Skip to content
Snippets Groups Projects
Commit a859c9e6 authored by chris's avatar chris
Browse files

new prompt node

parent 8a8fa96a
No related branches found
No related tags found
1 merge request!475Essay question
import { QuestionsNodeView } from 'wax-prosemirror-core';
export default class EssayPromptNodeView extends QuestionsNodeView {
constructor(
node,
view,
getPos,
decorations,
createPortal,
Component,
context,
) {
super(node, view, getPos, decorations, createPortal, Component, context);
this.node = node;
this.outerView = view;
this.getPos = getPos;
this.context = context;
}
static name() {
return 'essay_prompt';
}
stopEvent(event) {
const innerView = this.context.pmViews[this.node.attrs.id];
return innerView && innerView.dom.contains(event.target);
}
}
...@@ -76,11 +76,11 @@ class EssayQuestion extends Tools { ...@@ -76,11 +76,11 @@ class EssayQuestion extends Tools {
if (!wrapping) return false; if (!wrapping) return false;
tr.wrap(range, wrapping); tr.wrap(range, wrapping);
// const map = tr.mapping.maps[0]; const map = tr.mapping.maps[0];
// let newPos = 0; let newPos = 0;
// map.forEach((_from, _to, _newFrom, newTo) => { map.forEach((_from, _to, _newFrom, newTo) => {
// newPos = newTo; newPos = newTo;
// }); });
tr.setSelection(TextSelection.create(tr.doc, range.$to.pos)); tr.setSelection(TextSelection.create(tr.doc, range.$to.pos));
...@@ -88,17 +88,26 @@ class EssayQuestion extends Tools { ...@@ -88,17 +88,26 @@ class EssayQuestion extends Tools {
{ id: uuidv4() }, { id: uuidv4() },
Fragment.empty, Fragment.empty,
); );
const essayPrompt = main.state.config.schema.nodes.essay_prompt.create(
{ id: uuidv4() },
Fragment.empty,
);
const essayAnswer = main.state.config.schema.nodes.essay_answer.create( const essayAnswer = main.state.config.schema.nodes.essay_answer.create(
{ id: uuidv4() }, { id: uuidv4() },
Fragment.empty, Fragment.empty,
); );
tr.replaceSelectionWith(essayQuestion); tr.replaceSelectionWith(essayQuestion);
tr.setSelection(TextSelection.create(tr.doc, newPos));
tr.replaceSelectionWith(essayPrompt);
tr.setSelection(TextSelection.create(tr.doc, newPos + 1));
tr.replaceSelectionWith(essayAnswer); tr.replaceSelectionWith(essayAnswer);
dispatch(tr); dispatch(tr);
setTimeout(() => { setTimeout(() => {
createEmptyParagraph(context, essayAnswer.attrs.id); createEmptyParagraph(context, essayAnswer.attrs.id);
createEmptyParagraph(context, essayPrompt.attrs.id);
createEmptyParagraph(context, essayQuestion.attrs.id); createEmptyParagraph(context, essayQuestion.attrs.id);
}, 50); }, 50);
}; };
......
import { Service } from 'wax-prosemirror-core'; import { Service } from 'wax-prosemirror-core';
import EssayQuestion from './EssayQuestion'; import EssayQuestion from './EssayQuestion';
import essayContainerNode from './schema/essayContainerNode'; import essayContainerNode from './schema/essayContainerNode';
import essayPromptNode from './schema/essayPromptNode';
import essayQuestionNode from './schema/essayQuestionNode'; import essayQuestionNode from './schema/essayQuestionNode';
import essayAnswerNode from './schema/essayAnswerNode'; import essayAnswerNode from './schema/essayAnswerNode';
import EssayQuestionComponent from './components/EssayQuestionComponent'; import EssayQuestionComponent from './components/EssayQuestionComponent';
import EssayPromptComponent from './components/EssayPromptComponent';
import EssayAnswerComponent from './components/EssayAnswerComponent'; import EssayAnswerComponent from './components/EssayAnswerComponent';
import EssayQuestionNodeView from './EssayQuestionNodeView'; import EssayQuestionNodeView from './EssayQuestionNodeView';
import EssayPromptNodeView from './EssayPromptNodeView';
import EssayAnswerNodeView from './EssayAnswerNodeView'; import EssayAnswerNodeView from './EssayAnswerNodeView';
import './essay.css'; import './essay.css';
...@@ -23,6 +26,10 @@ class EssayService extends Service { ...@@ -23,6 +26,10 @@ class EssayService extends Service {
essay_question: essayQuestionNode, essay_question: essayQuestionNode,
}); });
createNode({
essay_prompt: essayPromptNode,
});
createNode({ createNode({
essay_answer: essayAnswerNode, essay_answer: essayAnswerNode,
}); });
...@@ -33,6 +40,12 @@ class EssayService extends Service { ...@@ -33,6 +40,12 @@ class EssayService extends Service {
context: this.app, context: this.app,
}); });
addPortal({
nodeView: EssayPromptNodeView,
component: EssayPromptComponent,
context: this.app,
});
addPortal({ addPortal({
nodeView: EssayAnswerNodeView, nodeView: EssayAnswerNodeView,
component: EssayAnswerComponent, component: EssayAnswerComponent,
......
import React, { useContext, useRef, useEffect } from 'react';
import styled from 'styled-components';
import { EditorView } from 'prosemirror-view';
import { EditorState, TextSelection, NodeSelection } from 'prosemirror-state';
import { StepMap } from 'prosemirror-transform';
import { keymap } from 'prosemirror-keymap';
import { baseKeymap, chainCommands } from 'prosemirror-commands';
import { undo, redo } from 'prosemirror-history';
import { WaxContext } from 'wax-prosemirror-core';
import {
splitListItem,
liftListItem,
sinkListItem,
} from 'prosemirror-schema-list';
import Placeholder from '../../MultipleChoiceQuestionService/plugins/placeholder';
const EditorWrapper = styled.div`
border: none;
display: flex;
flex: 2 1 auto;
justify-content: left;
opacity: ${props => (props.editable ? 1 : 0.4)};
cursor: ${props => (props.editable ? 'default' : 'not-allowed')};
.ProseMirror {
white-space: break-spaces;
width: 100%;
word-wrap: break-word;
&:focus {
outline: none;
}
p.empty-node:first-child::before {
content: attr(data-content);
}
.empty-node::before {
color: rgb(170, 170, 170);
float: left;
font-style: italic;
height: 0px;
pointer-events: none;
}
}
`;
const EssayPromptComponent = ({ node, view, getPos }) => {
const editorRef = useRef();
const context = useContext(WaxContext);
const {
app,
pmViews: { main },
} = context;
let essayPromptView;
const questionId = node.attrs.id;
const customProps = main.props.customValues;
const { testMode } = customProps;
let finalPlugins = [];
const createKeyBindings = () => {
const keys = getKeys();
Object.keys(baseKeymap).forEach(key => {
if (keys[key]) {
keys[key] = chainCommands(keys[key], baseKeymap[key]);
} else {
keys[key] = baseKeymap[key];
}
});
return keys;
};
const pressEnter = (state, dispatch) => {
if (state.selection.node && state.selection.node.type.name === 'image') {
const { $from, to } = state.selection;
const same = $from.sharedDepth(to);
const pos = $from.before(same);
dispatch(state.tr.setSelection(NodeSelection.create(state.doc, pos)));
return true;
}
// LISTS
if (splitListItem(state.schema.nodes.list_item)(state)) {
splitListItem(state.schema.nodes.list_item)(state, dispatch);
return true;
}
return false;
};
const getKeys = () => {
return {
'Mod-z': () => undo(view.state, view.dispatch),
'Mod-y': () => redo(view.state, view.dispatch),
'Mod-[': liftListItem(view.state.schema.nodes.list_item),
'Mod-]': sinkListItem(view.state.schema.nodes.list_item),
Enter: pressEnter,
};
};
const plugins = [keymap(createKeyBindings()), ...app.getPlugins()];
const createPlaceholder = placeholder => {
return Placeholder({
content: placeholder,
});
};
finalPlugins = finalPlugins.concat([
createPlaceholder('Type your essay prompt'),
...plugins,
]);
useEffect(() => {
essayPromptView = new EditorView(
{
mount: editorRef.current,
},
{
editable: () => testMode,
state: EditorState.create({
doc: node,
plugins: finalPlugins,
}),
dispatchTransaction,
disallowedTools: ['MultipleChoice'],
handleDOMEvents: {
mousedown: () => {
context.updateView({}, questionId);
main.dispatch(
main.state.tr
.setMeta('outsideView', questionId)
.setSelection(
new TextSelection(
main.state.tr.doc.resolve(
getPos() +
2 +
context.pmViews[questionId].state.selection.to,
),
),
),
);
context.updateView({}, questionId);
if (essayPromptView.hasFocus()) essayPromptView.focus();
},
blur: (editorView, event) => {
if (essayPromptView && event.relatedTarget === null) {
essayPromptView.focus();
}
},
},
attributes: {
spellcheck: 'false',
},
},
);
// Set Each note into Wax's Context
context.updateView(
{
[questionId]: essayPromptView,
},
questionId,
);
if (essayPromptView.hasFocus()) essayPromptView.focus();
}, []);
const dispatchTransaction = tr => {
const outerTr = main.state.tr;
main.dispatch(outerTr.setMeta('outsideView', questionId));
const { state, transactions } = essayPromptView.state.applyTransaction(tr);
context.updateView({}, questionId);
essayPromptView.updateState(state);
if (!tr.getMeta('fromOutside')) {
const offsetMap = StepMap.offset(getPos() + 1);
for (let i = 0; i < transactions.length; i++) {
const { steps } = transactions[i];
for (let j = 0; j < steps.length; j++)
outerTr.step(steps[j].map(offsetMap));
}
if (outerTr.docChanged)
main.dispatch(outerTr.setMeta('outsideView', questionId));
}
};
return (
<EditorWrapper editable={testMode}>
<div ref={editorRef} />
</EditorWrapper>
);
};
export default EssayPromptComponent;
import { v4 as uuidv4 } from 'uuid';
const essayPromptNode = {
attrs: {
class: { default: 'essay-prompt' },
id: { default: uuidv4() },
},
group: 'block questions',
content: 'block*',
defining: true,
parseDOM: [
{
tag: 'div.essay-prompt',
getAttrs(dom) {
return {
id: dom.getAttribute('id'),
class: dom.getAttribute('class'),
};
},
},
],
toDOM: node => ['div', node.attrs, 0],
};
export default essayPromptNode;
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment