-
Yannis Barlas authored9dd07f53
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
MatchingContainerComponent.js 6.85 KiB
/* eslint-disable react/destructuring-assignment */
/* eslint-disable react/prop-types */
import React, { useContext, useEffect, useRef, useState } from 'react';
import { v4 as uuidv4 } from 'uuid';
import { WaxContext } from 'wax-prosemirror-core';
import { Icon } from 'wax-prosemirror-components';
import { DocumentHelpers } from 'wax-prosemirror-utilities';
import styled from 'styled-components';
import FeedbackComponent from './FeedbackComponent';
import ContainerEditor from './ContainerEditor';
const MatchingWrapper = styled.div`
display: flex;
flex-direction: column;
margin: 0px 38px 15px 38px;
margin-top: 10px;
`;
const MatchingContainer = styled.div`
border: 3px solid #f5f5f7;
margin-bottom: 30px;
padding: 10px;
`;
const QuestionWrapper = styled.div`
display: flex;
flex-direction: row;
width: 100%;
`;
const ActionButton = styled.button`
background: transparent;
border: none;
cursor: pointer;
height: 24px;
padding-left: 0;
`;
const StyledIconAction = styled(Icon)`
height: 24px;
width: 24px;
`;
const CreateOptions = styled.div`
display: flex;
flex-direction: column;
padding-bottom: 10px;
`;
const OptionArea = styled.div`
display: flex;
width: 100%;
ul {
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin: 0;
padding: 0;
li {
list-style-type: none;
padding-bottom: 7px;
padding-right: 7px;
span {
background: #535e76;
border-radius: 12px;
color: white;
padding: 3px 3px 3px 10px;
}
svg {
fill: white;
height: 16px;
width: 16px;
}
}
}
`;
const AddOption = styled.div`
display: flex;
input {
border: none;
border-bottom: 1px solid black;
&:focus {
outline: none;
}
::placeholder {
color: rgb(170, 170, 170);
font-style: italic;
}
}
button {
background: #fff;
border: 1px solid #535e76;
color: #535e76;
cursor: pointer;
margin-left: 20px;
padding: 4px 8px 4px 8px;
&:hover {
background: #535e76;
border: 1px solid #535e76;
color: #fff;
cursor: pointer;
margin-right: 20px;
padding: 4px 8px 4px 8px;
}
}
`;
export default ({ node, view, getPos }) => {
const context = useContext(WaxContext);
const {
pmViews: { main },
} = context;
const [options, setOptions] = useState(node.attrs.options);
const [optionText, setOptionText] = useState('');
const [addingOption, setAddingOption] = useState(false);
const addOptionRef = useRef(null);
const customProps = main.props.customValues;
const isEditable = main.props.editable(editable => {
return editable;
});
const readOnly = !isEditable;
useEffect(() => {
const allNodes = getNodes(main);
/* TEMP TO SAVE NODE OPTIONS TODO: SAVE IN CONTEXT OPTIONS */
saveInChildOptions(allNodes);
if (!addingOption) return;
allNodes.forEach(singleNode => {
if (singleNode.node.attrs.id === node.attrs.id) {
main.dispatch(
main.state.tr
.setMeta('addToHistory', false)
.setNodeMarkup(getPos(), undefined, {
...singleNode.node.attrs,
options,
}),
);
}
});
}, [options, JSON.stringify(context.pmViews.main.state)]);
const addOption = () => {
if (addOptionRef.current.value.trim() === '') return;
const obj = { label: addOptionRef.current.value, value: uuidv4() };
setOptions(prevOptions => [...prevOptions, obj]);
setAddingOption(true);
setTimeout(() => {
setAddingOption(false);
});
setOptionText('');
addOptionRef.current.focus();
};
const updateOptionText = () => {
setOptionText(addOptionRef.current.value);
};
const handleKeyDown = event => {
if (event.key === 'Enter' || event.which === 13) {
addOption();
}
};
const removeOption = value => {
setOptions(options.filter(option => option.value !== value));
setAddingOption(true);
setTimeout(() => {
setAddingOption(false);
});
};
const saveInChildOptions = allNodes => {
allNodes.forEach(singleNode => {
if (singleNode.node.attrs.id === node.attrs.id) {
singleNode.node.content.content.forEach(parentNodes => {
parentNodes.forEach(optionNode => {
if (optionNode.type.name === 'matching_option')
/* eslint-disable-next-line no-param-reassign */
optionNode.attrs.options = options;
});
});
}
});
};
return (
<MatchingWrapper>
<span>Matching</span>
<MatchingContainer className="matching">
<QuestionWrapper>
<ContainerEditor getPos={getPos} node={node} view={view} />
</QuestionWrapper>
{(!readOnly ||
(readOnly && !customProps.testMode && !customProps.showFeedBack)) && (
<CreateOptions>
<OptionArea>
{options.length > 0 && (
<ul>
<li>Options: </li>
{options.map((option, index) => {
return (
<li key={option.value}>
<span>
{option.label}
{!readOnly && (
<ActionButton
onClick={() => removeOption(option.value)}
type="button"
>
<StyledIconAction name="deleteOutlined" />
</ActionButton>
)}
</span>
</li>
);
})}
</ul>
)}
</OptionArea>
{!readOnly && (
<AddOption>
<input
onChange={updateOptionText}
onKeyPress={handleKeyDown}
placeholder="Type an option ..."
ref={addOptionRef}
type="text"
value={optionText}
/>
<button onClick={addOption} type="button">
Add Option
</button>
</AddOption>
)}
</CreateOptions>
)}
{(!(readOnly && !customProps.showFeedBack) ||
(readOnly && !customProps.testMode && !customProps.showFeedBack)) && (
<FeedbackComponent
getPos={getPos}
node={node}
readOnly={readOnly}
view={view}
/>
)}
</MatchingContainer>
</MatchingWrapper>
);
};
const getNodes = view => {
const allNodes = DocumentHelpers.findBlockNodes(view.state.doc);
const matchingContainerNodes = [];
allNodes.forEach(node => {
if (node.node.type.name === 'matching_container') {
matchingContainerNodes.push(node);
}
});
return matchingContainerNodes;
};