diff --git a/editors/editoria/src/layout/EditorElements.js b/editors/editoria/src/layout/EditorElements.js index e2e896a3663293f4913c2f9e692fd45ce1d3a1a2..d31d0839f3254b47f51b51fb181579e23e7b2429 100644 --- a/editors/editoria/src/layout/EditorElements.js +++ b/editors/editoria/src/layout/EditorElements.js @@ -30,7 +30,8 @@ export default css` h4 span::selection, code span::selection, custom-tag-block::selection, - custom-tag-inline::selection { + custom-tag-inline::selection, + figcaption span::selection, { background-color: transparent; } @@ -100,9 +101,36 @@ export default css` margin-right: 0; } - img { - cursor: default; - width: 100%; + figure { + display: table; + flex-direction: column; + margin-left: auto; + margin-right: auto; + + img { + cursor: default; + height: auto; + max-width: 100%; + width: auto; + } + + figcaption { + background: #e2ebff; + caption-side: bottom; + display: table-caption; + max-width: 100%; + min-height: 20px; + padding: 4px; + width: auto; + + &:focus { + outline: none; + } + &: before { + content: 'Caption : '; + font-weight: bold; + } + } } sup, diff --git a/wax-prosemirror-components/src/components/comments/CommentBubbleComponent.js b/wax-prosemirror-components/src/components/comments/CommentBubbleComponent.js index 9e9f9818b5293a9c99db74ff407d8481a6eff31b..4dce8b2db5390262770c03b70ea99106a8611299 100644 --- a/wax-prosemirror-components/src/components/comments/CommentBubbleComponent.js +++ b/wax-prosemirror-components/src/components/comments/CommentBubbleComponent.js @@ -41,7 +41,8 @@ const CommentBubbleComponent = ({ (node, from) => { if ( node.type.name === 'math_display' || - node.type.name === 'math_inline' + node.type.name === 'math_inline' || + node.type.name === 'image' ) { allowed = false; } diff --git a/wax-prosemirror-plugins/src/images/captionPlugin.js b/wax-prosemirror-plugins/src/images/captionPlugin.js index 6ae3b7a1d41764b68fde03877d624c0a5a3589d8..ea7acd9fffe6cd7aa57e5a9fa2c53de99fa6ddb0 100644 --- a/wax-prosemirror-plugins/src/images/captionPlugin.js +++ b/wax-prosemirror-plugins/src/images/captionPlugin.js @@ -1,18 +1,106 @@ -import { Decoration, DecorationSet } from 'prosemirror-view'; -import { Plugin, TextSelection } from 'prosemirror-state'; +import { DecorationSet } from 'prosemirror-view'; +import { Plugin, PluginKey, NodeSelection } from 'prosemirror-state'; const captionPlugin = key => new Plugin({ + key: new PluginKey(key), state: { init() { return DecorationSet.empty; }, - apply(tr, set) {}, + apply(tr, set) { + let setMap = set; + setMap = setMap.map(tr.mapping, tr.doc); + setMap = setMap.remove(set.find(null, null, spec => spec.id != null)); + return setMap; + }, }, props: { decorations(state) { return this.getState(state); }, + handleDOMEvents: { + mousedown(view, e) { + const captionPlugins = view.state.plugins.find(plugin => + plugin.key.startsWith('caption$'), + ); + + if ( + e.target.nodeName === 'IMG' && + e.target.parentNode.lastElementChild.nodeName !== 'FIGCAPTION' + ) { + let pos = view.posAtDOM(e.target); + const id = {}; + const { tr } = view.state; + pos += 1; + // insert figure caption node + view.dispatch( + tr + .replaceWith( + pos, + pos, + view.state.schema.nodes.figcaption.create({ + class: 'decoration', + dataContent: 'Caption : ', + }), + ) + .setMeta(captionPlugins, { + add: { id, pos }, + }), + ); + } else if (e.target.nodeName !== 'FIGCAPTION') { + const decorationelement = document.getElementsByTagName( + 'figcaption', + ); + const decorationLength = decorationelement.length; + + if (decorationLength) { + for (let i = 0; i < decorationLength; i += 1) { + if (!decorationelement[i].textContent.length) { + decorationelement[i].remove(); + } else if ( + decorationelement[i].parentElement.firstChild.tagName === + 'FIGCAPTION' + ) { + decorationelement[i].parentElement.remove(); + } + } + } + } + + if (e.target.nodeName === 'IMG') { + let pos = view.posAtDOM(e.target); + const { $from } = view.state.selection; + const same = $from.sharedDepth(pos); + if (same === 0) return false; + pos = $from.before(same); + view.dispatch( + view.state.tr.setSelection( + NodeSelection.create(view.state.doc, pos), + ), + ); + } + + return true; + }, + keyup(view, e) { + // delete caption if figure is deleted + if (e.key === 'Delete') { + const figcap = document.getElementsByTagName('figcaption'); + const figcapLength = figcap.length; + + if (figcapLength) { + for (let i = 0; i < figcapLength; i += 1) { + if ( + figcap[i].parentElement.firstChild.tagName === 'FIGCAPTION' + ) { + figcap[i].parentElement.remove(); + } + } + } + } + }, + }, }, }); diff --git a/wax-prosemirror-plugins/src/images/placeHolderPlugin.js b/wax-prosemirror-plugins/src/images/placeHolderPlugin.js index 7cbe9bdfd3695f1db32fa5f06385f11a8eab1aef..2d294a6977137a57782bd93a90ff2220efe21495 100644 --- a/wax-prosemirror-plugins/src/images/placeHolderPlugin.js +++ b/wax-prosemirror-plugins/src/images/placeHolderPlugin.js @@ -20,9 +20,9 @@ export default key => }); set = set.add(tr.doc, [deco]); } else if (action && action.remove) { - set = set.remove( - set.find(null, null, spec => spec.id === action.remove.id), - ); + // set = set.remove( + // set.find(null, null, spec => spec.id === action.remove.id), + // ); } return set; }, diff --git a/wax-prosemirror-schema/index.js b/wax-prosemirror-schema/index.js index 30b965c426c43930e2172e545a4fcd02b8357334..fd0c54754ca8ddb00116031f1adf81aa472b81b9 100644 --- a/wax-prosemirror-schema/index.js +++ b/wax-prosemirror-schema/index.js @@ -37,7 +37,7 @@ export { default as bulletListNode } from './src/nodes/bulletListNode'; export { default as listItemNode } from './src/nodes/listItemNode'; export { default as subTitleNode } from './src/nodes/subTitleNode'; export { default as figureNode } from './src/nodes/figureNode'; -export { default as figureCaptionNode } from './src/nodes/figureNode'; +export { default as figureCaptionNode } from './src/nodes/figureCaptionNode'; export { default as imageNode } from './src/nodes/imageNode'; export { default as headingNode } from './src/nodes/headingNode'; export { default as blockQuoteNode } from './src/nodes/blockQuoteNode'; diff --git a/wax-prosemirror-schema/src/nodes/figureCaptionNode.js b/wax-prosemirror-schema/src/nodes/figureCaptionNode.js index a4303d6b9aa04556b7c5631ec6f00e6d42f6ed9a..2b267b1d5ee18978aa9756f31ad2faaa5b73a1f0 100644 --- a/wax-prosemirror-schema/src/nodes/figureCaptionNode.js +++ b/wax-prosemirror-schema/src/nodes/figureCaptionNode.js @@ -1,11 +1,26 @@ const figureCaption = { content: 'inline*', group: 'figure', - marks: 'strong link', - parseDOM: [{ tag: 'figcaption' }], - toDOM(node) { - return ['figcaption', 0]; + draggable: false, + attrs: { + class: { default: '' }, + tabindex: { default: 0 }, + dataContent: { default: '' }, }, + toDOM: node => { + return ['figcaption', node.attrs, 0]; + }, + parseDOM: [ + { + tag: 'figcaption', + getAttrs(dom) { + return { + class: dom.getAttribute('class'), + dataContent: dom.getAttribute('dataContent'), + }; + }, + }, + ], }; export default figureCaption; diff --git a/wax-prosemirror-schema/src/nodes/imageNode.js b/wax-prosemirror-schema/src/nodes/imageNode.js index 40e38f316a572f8d2bf5318449ccb56045564103..34e825c63b235142d72dbbc7415174caca9dda80 100644 --- a/wax-prosemirror-schema/src/nodes/imageNode.js +++ b/wax-prosemirror-schema/src/nodes/imageNode.js @@ -1,6 +1,6 @@ import { SchemaHelpers } from 'wax-prosemirror-utilities'; + const image = { - // inline: true, attrs: { src: {}, alt: { default: null }, @@ -8,7 +8,7 @@ const image = { track: { default: [] }, }, group: 'figure', - draggable: true, + draggable: false, parseDOM: [ { tag: 'img[src]', @@ -26,12 +26,15 @@ const image = { ], toDOM(hook, next) { const attrs = {}; - let temp = ''; + if (hook.node.attrs.track && hook.node.attrs.track.length) { attrs['data-track'] = JSON.stringify(hook.node.attrs.track); attrs['data-id'] = hook.node.attrs.id; } + const { src, alt, title, id, track } = hook.node.attrs; + + // eslint-disable-next-line no-param-reassign hook.value = [ 'img', { src, alt, title, 'data-id': id, 'data-track': track }, diff --git a/wax-prosemirror-services/src/DisplayBlockLevel/HeadingService/Heading2.js b/wax-prosemirror-services/src/DisplayBlockLevel/HeadingService/Heading2.js index f29ab84670b3e5413ec6a3ba648ab8da80b76bcb..d48724224265e0f2b58fdc2e6d0f6fb0a326d83f 100644 --- a/wax-prosemirror-services/src/DisplayBlockLevel/HeadingService/Heading2.js +++ b/wax-prosemirror-services/src/DisplayBlockLevel/HeadingService/Heading2.js @@ -27,7 +27,12 @@ class Heading2 extends Tools { if (activeViewId !== 'main') return false; const { from, to } = state.selection; state.doc.nodesBetween(from, to, (node, pos) => { - if (node.type.name === 'list_item' || node.type.name === 'image') { + if ( + node.type.name === 'list_item' || + node.type.name === 'image' || + node.type.name === 'figure' || + node.type.name === 'figcaption' + ) { isActive = false; } }); diff --git a/wax-prosemirror-services/src/DisplayBlockLevel/HeadingService/Heading3.js b/wax-prosemirror-services/src/DisplayBlockLevel/HeadingService/Heading3.js index e2dd3d94ecd790c725fd28c363938c0fe2dcd00a..e79ea62f3f7e83468e372ff42be80904c1250a51 100644 --- a/wax-prosemirror-services/src/DisplayBlockLevel/HeadingService/Heading3.js +++ b/wax-prosemirror-services/src/DisplayBlockLevel/HeadingService/Heading3.js @@ -27,7 +27,12 @@ class Heading3 extends Tools { if (activeViewId !== 'main') return false; const { from, to } = state.selection; state.doc.nodesBetween(from, to, (node, pos) => { - if (node.type.name === 'list_item' || node.type.name === 'image') { + if ( + node.type.name === 'list_item' || + node.type.name === 'image' || + node.type.name === 'figure' || + node.type.name === 'figcaption' + ) { isActive = false; } }); diff --git a/wax-prosemirror-services/src/DisplayBlockLevel/HeadingService/Heading4.js b/wax-prosemirror-services/src/DisplayBlockLevel/HeadingService/Heading4.js index 3991590cf620ef5a844e0d16726dd36262ce5912..31df0dcb9296c62d307cb57542511d0e18de5103 100644 --- a/wax-prosemirror-services/src/DisplayBlockLevel/HeadingService/Heading4.js +++ b/wax-prosemirror-services/src/DisplayBlockLevel/HeadingService/Heading4.js @@ -27,7 +27,12 @@ class Heading4 extends Tools { if (activeViewId !== 'main') return false; const { from, to } = state.selection; state.doc.nodesBetween(from, to, (node, pos) => { - if (node.type.name === 'list_item' || node.type.name === 'image') { + if ( + node.type.name === 'list_item' || + node.type.name === 'image' || + node.type.name === 'figure' || + node.type.name === 'figcaption' + ) { isActive = false; } }); diff --git a/wax-prosemirror-services/src/ImageService/ImageService.js b/wax-prosemirror-services/src/ImageService/ImageService.js index c25501933b0a6552f2a703f722fe04c8343c4c8f..13b1612440ad896c18d1d7350cc2c484f1caf393 100644 --- a/wax-prosemirror-services/src/ImageService/ImageService.js +++ b/wax-prosemirror-services/src/ImageService/ImageService.js @@ -33,7 +33,10 @@ class ImageService extends Service { ); createNode({ figcaption: figureCaptionNode, - }); + } + // , + // { toWaxSchema: true }, + ); } } diff --git a/wax-prosemirror-services/src/ImageService/fileUpload.js b/wax-prosemirror-services/src/ImageService/fileUpload.js index 36a6040420141f93373cffb4021006c538b3d009..d0e5d61139f3793a8060c4e3c904f2b7b56d567e 100644 --- a/wax-prosemirror-services/src/ImageService/fileUpload.js +++ b/wax-prosemirror-services/src/ImageService/fileUpload.js @@ -34,6 +34,9 @@ export default (view, fileUpload, placeholderPlugin) => file => { .replaceWith( pos, pos, + // view.state.schema.nodes.image.create({ + // src: url, + // }), view.state.schema.nodes.image.create({ src: url, }),