diff --git a/wax-prosemirror-components/src/components/images/ImageUpload.js b/wax-prosemirror-components/src/components/images/ImageUpload.js index 146f4cb4fac25291ef15336c29e0d98a140b243e..9371fbd4dad737b6af87674a96f221f05a969fd1 100644 --- a/wax-prosemirror-components/src/components/images/ImageUpload.js +++ b/wax-prosemirror-components/src/components/images/ImageUpload.js @@ -42,7 +42,7 @@ const ImageUpload = ({ item, fileUpload, view }) => { new TextSelection( main.state.tr.doc.resolve( nodeFound.pos + - 2 + + 1 + context.pmViews[activeViewId].state.selection.to, ), ), diff --git a/wax-prosemirror-core/index.js b/wax-prosemirror-core/index.js index 84a7b8e50fa3ec0e9b7b77b968bbdab5ef1fc225..87baa280e2c2b9c63879649e67ba05a193f1f46f 100644 --- a/wax-prosemirror-core/index.js +++ b/wax-prosemirror-core/index.js @@ -1,3 +1,4 @@ +export { default as Service } from './src/Service'; export { WaxContext, useInjection } from './src/WaxContext'; export { PortalContext } from './src/PortalContext'; export { default as ComponentPlugin } from './src/ComponentPlugin'; diff --git a/wax-prosemirror-core/src/Service.js b/wax-prosemirror-core/src/Service.js new file mode 100644 index 0000000000000000000000000000000000000000..2c40473caeab609db1a4a7cecbc8610122868380 --- /dev/null +++ b/wax-prosemirror-core/src/Service.js @@ -0,0 +1,17 @@ +export default class Service { + setApp(app) { + this.app = app; + } + + get container() { + return this.app.container; + } + + get config() { + return this.app.config.get(`config.${this.name}`) || this.app.config; + } + + get schema() { + return this.app.getSchema(); + } +} diff --git a/wax-prosemirror-core/src/WaxContext.js b/wax-prosemirror-core/src/WaxContext.js index d97ec379c28c616b9f6b18945d31ae29ab0c433d..3d1d3da3d61e82569f7e6462a24c7624bc66a3be 100644 --- a/wax-prosemirror-core/src/WaxContext.js +++ b/wax-prosemirror-core/src/WaxContext.js @@ -65,3 +65,21 @@ export const useInjection = identifier => { ? { instance: container.get(identifier) } : null; }; + +export class Service { + setApp(app) { + this.app = app; + } + + get container() { + return this.app.container; + } + + get config() { + return this.app.config.get(`config.${this.name}`) || this.app.config; + } + + get schema() { + return this.app.getSchema(); + } +} diff --git a/wax-prosemirror-core/src/WaxView.js b/wax-prosemirror-core/src/WaxView.js index 45382705d0e03614051b20fbd1dc7eb870a83d23..0e064d8ab70d29131e3b8d27c7fa980af42752a9 100644 --- a/wax-prosemirror-core/src/WaxView.js +++ b/wax-prosemirror-core/src/WaxView.js @@ -102,12 +102,13 @@ const WaxView = forwardRef((props, ref) => { 'main', ); if (debug) applyDevTools(view); - if (autoFocus && view) - setTimeout(() => { + setTimeout(() => { + if (autoFocus && view) { view.focus(); view.state.tr.insertText('', 0); view.dispatch(view.state.tr); - }, 500); + } + }, 500); return () => view.destroy(); } diff --git a/wax-prosemirror-schema/src/nodes/imageNode.js b/wax-prosemirror-schema/src/nodes/imageNode.js index f8ebb11b9180ffcb5f1154f3b8f1e13b9b11b6bf..6eb0f35f70106f32204b41333703546413675e5f 100644 --- a/wax-prosemirror-schema/src/nodes/imageNode.js +++ b/wax-prosemirror-schema/src/nodes/imageNode.js @@ -2,8 +2,9 @@ import { SchemaHelpers } from 'wax-prosemirror-utilities'; const image = { attrs: { + id: { default: '' }, src: {}, - alt: { default: null }, + alt: { default: '' }, title: { default: null }, // track: { default: [] }, fileid: { default: null }, diff --git a/wax-prosemirror-services/src/BaseService/BaseService.js b/wax-prosemirror-services/src/BaseService/BaseService.js index 83525bb3e3d7feb9249cf38c510d4c8972f028c1..44f8fd721104511885067283b5cded15e023a038 100644 --- a/wax-prosemirror-services/src/BaseService/BaseService.js +++ b/wax-prosemirror-services/src/BaseService/BaseService.js @@ -1,4 +1,4 @@ -import Service from '../Service'; +import { Service } from 'wax-prosemirror-core'; import BaseServices from './index'; class BaseService extends Service { diff --git a/wax-prosemirror-services/src/ImageService/AltComponent.js b/wax-prosemirror-services/src/ImageService/AltComponent.js new file mode 100644 index 0000000000000000000000000000000000000000..1dc2aebf532f9ea0be16c2a23a0706465d4303f1 --- /dev/null +++ b/wax-prosemirror-services/src/ImageService/AltComponent.js @@ -0,0 +1,82 @@ +/* eslint-disable react/prop-types */ +import React, { useContext, useLayoutEffect, useRef, useState } from 'react'; +import styled from 'styled-components'; +import { WaxContext } from 'wax-prosemirror-core'; + +export default ({ setPosition, position }) => { + const altRef = useRef(null); + const [altText, setAltText] = useState(''); + const context = useContext(WaxContext); + const { + activeView, + pmViews: { main }, + } = context; + + const isEditable = main.props.editable(editable => { + return editable; + }); + + const readOnly = !isEditable; + + const StyledInputAlt = styled.input` + background: #e2ebff; + border: none; + box-sizing: border-box; + width: 240px; + min-height: 20px; + padding: 4px; + + &:focus { + outline: none; + } + + &::placeholder { + color: black; + font-weight: bold; + } + `; + + useLayoutEffect(() => { + const WaxSurface = activeView.dom.getBoundingClientRect(); + const { selection } = activeView.state; + + if (!selection || !selection.node || !selection.node.attrs.id) return; + const imageId = selection.node.attrs.id; + const image = document.querySelector(`[data-id='${imageId}']`); + const imagePosition = image.getBoundingClientRect(); + const left = imagePosition.left - WaxSurface.left; + const top = imagePosition.bottom - WaxSurface.top - 22; + setPosition({ ...position, left, top }); + }, [position.left, position.top]); + + const altTextOnChange = () => { + const { selection } = activeView.state; + setAltText(altRef.current.value); + activeView.dispatch( + activeView.state.tr.setNodeMarkup(selection.from, undefined, { + ...selection.node.attrs, + alt: altRef.current.value, + }), + ); + }; + + if (!readOnly) { + return ( + <StyledInputAlt + autoFocus="autoFocus" + key="alt" + onChange={altTextOnChange} + placeholder="Alt Text" + ref={altRef} + type="text" + value={ + activeView.state.selection && + activeView.state.selection.node && + activeView.state.selection.node.attrs.alt !== '' + ? activeView.state.selection.node.attrs.alt + : altText + } + /> + ); + } +}; diff --git a/wax-prosemirror-services/src/ImageService/ImageService.js b/wax-prosemirror-services/src/ImageService/ImageService.js index d45d2074a100bc770ee5a7982205ced3b51eed92..bb0b6cd5f4f66aead334ac1c0701b6e93a7983ae 100644 --- a/wax-prosemirror-services/src/ImageService/ImageService.js +++ b/wax-prosemirror-services/src/ImageService/ImageService.js @@ -7,6 +7,7 @@ import { PlaceHolderPlugin, captionPlugin } from 'wax-prosemirror-plugins'; import Service from '../Service'; import Image from './Image'; import './image.css'; +import AltComponent from './AltComponent'; class ImageService extends Service { name = 'ImageService'; @@ -22,6 +23,7 @@ class ImageService extends Service { register() { this.container.bind('Image').to(Image); const createNode = this.container.get('CreateNode'); + const createOverlay = this.container.get('CreateOverlay'); createNode({ figure: figureNode, }); @@ -39,6 +41,18 @@ class ImageService extends Service { // , // { toWaxSchema: true }, ); + + createOverlay( + AltComponent, + {}, + { + nodeType: 'image', + findInParent: false, + markType: '', + followCursor: false, + selection: false, + }, + ); } } diff --git a/wax-prosemirror-services/src/ImageService/fileUpload.js b/wax-prosemirror-services/src/ImageService/fileUpload.js index d0e5d61139f3793a8060c4e3c904f2b7b56d567e..7dc77815556a1d370e7c54fb377ceb4320be9b4b 100644 --- a/wax-prosemirror-services/src/ImageService/fileUpload.js +++ b/wax-prosemirror-services/src/ImageService/fileUpload.js @@ -1,3 +1,5 @@ +import { v4 as uuidv4 } from 'uuid'; + const findPlaceholder = (state, id, placeholderPlugin) => { const decos = placeholderPlugin.getState(state); const found = decos.find(null, null, spec => spec.id === id); @@ -39,6 +41,7 @@ export default (view, fileUpload, placeholderPlugin) => file => { // }), view.state.schema.nodes.image.create({ src: url, + id: uuidv4(), }), ) .setMeta(placeholderPlugin, { remove: { id } }), diff --git a/wax-prosemirror-services/src/ImageService/image.css b/wax-prosemirror-services/src/ImageService/image.css index 420c8b2620bebb463a66824c103c536eff7d9fb8..311d2d329916462e52991f9df3a4129a4afb4ed1 100644 --- a/wax-prosemirror-services/src/ImageService/image.css +++ b/wax-prosemirror-services/src/ImageService/image.css @@ -30,4 +30,5 @@ figcaption:focus { figcaption:before { content: 'Caption: '; font-weight: bold; -} \ No newline at end of file +} + diff --git a/wax-prosemirror-services/src/MultipleChoiceQuestionService/MultipleChoiceSingleCorrectQuestionService/schema/questionSingleNode.js b/wax-prosemirror-services/src/MultipleChoiceQuestionService/MultipleChoiceSingleCorrectQuestionService/schema/questionSingleNode.js index 92912ce68d36c2b9dde81385f0f4a2156f144eee..4c1231444efe4cca40564e03d62510b6f583d7b4 100644 --- a/wax-prosemirror-services/src/MultipleChoiceQuestionService/MultipleChoiceSingleCorrectQuestionService/schema/questionSingleNode.js +++ b/wax-prosemirror-services/src/MultipleChoiceQuestionService/MultipleChoiceSingleCorrectQuestionService/schema/questionSingleNode.js @@ -4,7 +4,7 @@ const questionSingleNode = { class: { default: 'multiple-choice-question-single' }, }, group: 'block questions', - content: 'paragraph* bulletlist* orderedlist*', + content: 'block+', defining: true, // atom: true, diff --git a/wax-prosemirror-services/src/MultipleChoiceQuestionService/TrueFalseQuestionService/schema/questionTrueFalseNode.js b/wax-prosemirror-services/src/MultipleChoiceQuestionService/TrueFalseQuestionService/schema/questionTrueFalseNode.js index 992f9625f5da6f44beb4fcdd42ece1d1612bc3e2..fde65c6fdda9a1cd9025dd8f4905ef58ecf2b62c 100644 --- a/wax-prosemirror-services/src/MultipleChoiceQuestionService/TrueFalseQuestionService/schema/questionTrueFalseNode.js +++ b/wax-prosemirror-services/src/MultipleChoiceQuestionService/TrueFalseQuestionService/schema/questionTrueFalseNode.js @@ -4,7 +4,8 @@ const questionTrueFalseNode = { class: { default: 'true-false-question' }, }, group: 'block questions', - content: 'paragraph* bulletlist* orderedlist*', + // content: 'paragraph* bulletlist* orderedlist*', + content: 'block+', defining: true, // atom: true, diff --git a/wax-prosemirror-services/src/MultipleChoiceQuestionService/TrueFalseSingleCorrectQuestionService/schema/questionTrueFalseSingleNode.js b/wax-prosemirror-services/src/MultipleChoiceQuestionService/TrueFalseSingleCorrectQuestionService/schema/questionTrueFalseSingleNode.js index f4c1503664b27b546cab4c171f46ec3d62ac321f..ee82a9170f2edb7067c13a97986d1c9e0435a6de 100644 --- a/wax-prosemirror-services/src/MultipleChoiceQuestionService/TrueFalseSingleCorrectQuestionService/schema/questionTrueFalseSingleNode.js +++ b/wax-prosemirror-services/src/MultipleChoiceQuestionService/TrueFalseSingleCorrectQuestionService/schema/questionTrueFalseSingleNode.js @@ -4,7 +4,7 @@ const questionTrueFalseNode = { class: { default: 'true-false-question-single' }, }, group: 'block questions', - content: 'paragraph* bulletlist* orderedlist*', + content: 'block+', defining: true, // atom: true, diff --git a/wax-prosemirror-services/src/MultipleChoiceQuestionService/components/FeedbackComponent.js b/wax-prosemirror-services/src/MultipleChoiceQuestionService/components/FeedbackComponent.js index 5ff3ac5cb90ed2cb16f549f45f73e022791f4ddf..9de6f0ca48ca74626a494bb2448b048e05fccdd4 100644 --- a/wax-prosemirror-services/src/MultipleChoiceQuestionService/components/FeedbackComponent.js +++ b/wax-prosemirror-services/src/MultipleChoiceQuestionService/components/FeedbackComponent.js @@ -33,7 +33,6 @@ const FeedBackInput = styled.input` export default ({ node, view, getPos, readOnly }) => { const context = useContext(WaxContext); const { - app, pmViews: { main }, } = context; @@ -98,12 +97,12 @@ export default ({ node, view, getPos, readOnly }) => { <FeedBack> <FeedBackLabel>Feedback</FeedBackLabel> <FeedBackInput - readOnly={readOnly} onBlur={saveFeedBack} onChange={feedBackInput} onFocus={onFocus} onKeyDown={handleKeyDown} placeholder="Insert feedback" + readOnly={readOnly} ref={feedBackRef} type="text" value={feedBack} diff --git a/wax-prosemirror-services/src/MultipleChoiceQuestionService/components/QuestionEditorComponent.js b/wax-prosemirror-services/src/MultipleChoiceQuestionService/components/QuestionEditorComponent.js index 6c57c169b1bad3b44afc305416b5a32c37762700..abbe5724afa7f185037be528417fecb51c368a03 100644 --- a/wax-prosemirror-services/src/MultipleChoiceQuestionService/components/QuestionEditorComponent.js +++ b/wax-prosemirror-services/src/MultipleChoiceQuestionService/components/QuestionEditorComponent.js @@ -155,7 +155,7 @@ const QuestionEditorComponent = ({ node, view, getPos }) => { new TextSelection( main.state.tr.doc.resolve( getPos() + - 2 + + 1 + context.pmViews[questionId].state.selection.to, ), ), diff --git a/wax-prosemirror-services/src/MultipleChoiceQuestionService/schema/multipleChoiceContainerNode.js b/wax-prosemirror-services/src/MultipleChoiceQuestionService/schema/multipleChoiceContainerNode.js index 8e556bf26298a432513a1cc15d4a38b7b4c95631..35033cb0edf56181615d4bf2b4b288aa6f7d34bc 100644 --- a/wax-prosemirror-services/src/MultipleChoiceQuestionService/schema/multipleChoiceContainerNode.js +++ b/wax-prosemirror-services/src/MultipleChoiceQuestionService/schema/multipleChoiceContainerNode.js @@ -5,7 +5,7 @@ const multipleChoiceContainerNode = { }, group: 'block questions', atom: true, - content: 'block*', + content: 'block+', parseDOM: [ { tag: 'div.multiple-choice', diff --git a/wax-prosemirror-services/src/MultipleChoiceQuestionService/schema/multipleChoiceNode.js b/wax-prosemirror-services/src/MultipleChoiceQuestionService/schema/multipleChoiceNode.js index daf0168fab9ba5dfdcb57788072292fa1f4c7ec3..5eea97cc09e8e5255771803c2f4cc866d3306062 100644 --- a/wax-prosemirror-services/src/MultipleChoiceQuestionService/schema/multipleChoiceNode.js +++ b/wax-prosemirror-services/src/MultipleChoiceQuestionService/schema/multipleChoiceNode.js @@ -7,7 +7,7 @@ const multipleChoiceNode = { feedback: { default: '' }, }, group: 'block questions', - content: 'block*', + content: 'block+', defining: true, parseDOM: [ { diff --git a/wax-prosemirror-services/src/MultipleChoiceQuestionService/schema/questionNode.js b/wax-prosemirror-services/src/MultipleChoiceQuestionService/schema/questionNode.js index 99012468f07b19cce85d463e9b91e033ac715f93..05dc969d5c6fb9e79d10ef69f19fbf72b02d0250 100644 --- a/wax-prosemirror-services/src/MultipleChoiceQuestionService/schema/questionNode.js +++ b/wax-prosemirror-services/src/MultipleChoiceQuestionService/schema/questionNode.js @@ -4,7 +4,7 @@ const questionNode = { id: { default: '' }, }, group: 'block questions', - content: 'paragraph* bulletlist* orderedlist*', + content: 'block+', defining: true, parseDOM: [