diff --git a/wax-prosemirror-components/index.js b/wax-prosemirror-components/index.js index a666ff4fe0a4ec2f2dce131f29bc5a706fbefac0..2f0a4dc7ed15649a9c2024bbd957a7d8bb1b33c8 100644 --- a/wax-prosemirror-components/index.js +++ b/wax-prosemirror-components/index.js @@ -2,7 +2,7 @@ export { default as Overlay } from './src/components/Overlay'; export { default as Button } from './src/components/Button'; export { default as icons } from './src/icons/icons'; export { default as TableDropDown } from './src/components/TableDropDown'; -export { default as ImageUpload } from './src/components/ImageUpload'; +export { default as ImageUpload } from './src/components/images/ImageUpload'; export { default as TitleButton } from './src/components/TitleButton'; export { default as LeftMenuTitle } from './src/components/LeftMenuTitle'; export { default as ToolGroupComponent } from './src/components/ToolGroupComponent'; diff --git a/wax-prosemirror-components/src/components/ImageUpload.js b/wax-prosemirror-components/src/components/images/ImageUpload.js similarity index 65% rename from wax-prosemirror-components/src/components/ImageUpload.js rename to wax-prosemirror-components/src/components/images/ImageUpload.js index 0ac2a71986dcd3b30d299dfc3204487ef5b97631..ef15bb56aaf820467b399f7776660fcb2026a841 100644 --- a/wax-prosemirror-components/src/components/ImageUpload.js +++ b/wax-prosemirror-components/src/components/images/ImageUpload.js @@ -3,7 +3,8 @@ import React, { useContext, useRef, useMemo } from 'react'; import { WaxContext } from 'wax-prosemirror-core'; import styled from 'styled-components'; -import MenuButton from '../ui/buttons/MenuButton'; +import MenuButton from '../../ui/buttons/MenuButton'; +import insertImage from './Upload'; const Wrapper = styled.div` input { @@ -13,12 +14,28 @@ const Wrapper = styled.div` const ImageUpload = ({ item, fileUpload, view }) => { const { + app, activeViewId, view: { main }, } = useContext(WaxContext); const inputRef = useRef(null); - const handleMouseDown = () => inputRef.current.click(); + const placeholderPlugin = app.PmPlugins.get('imagePlaceHolder'); + const imageServiceConfig = app.config.get('config.ImageServie'); + + const handleMouseDown = () => { + if (imageServiceConfig && imageServiceConfig.handleAssetManager) { + insertThroughFileMAnager(); + } else { + inputRef.current.click(); + } + }; + + async function insertThroughFileMAnager() { + const handler = imageServiceConfig.handleAssetManager; + const urls = await handler(); + insertImage(urls, view, placeholderPlugin); + } let isDisabled = !item.select(view.state, activeViewId); diff --git a/wax-prosemirror-components/src/components/images/Upload.js b/wax-prosemirror-components/src/components/images/Upload.js new file mode 100644 index 0000000000000000000000000000000000000000..faaacc0e55657b1e0b8886d0f002d225d625be08 --- /dev/null +++ b/wax-prosemirror-components/src/components/images/Upload.js @@ -0,0 +1,41 @@ +const findPlaceholder = (state, id, placeholderPlugin) => { + const decos = placeholderPlugin.getState(state); + const found = decos.find(null, null, spec => spec.id === id); + return found.length ? found[0].from : null; +}; + +const insertImage = (urls, view, placeholderPlugin) => { + for (let i = 0; i < urls.length; i += 1) { + const { state } = view; + + const id = {}; + + const { tr } = state; + if (!tr.selection.empty) tr.deleteSelection(); + + tr.setMeta(placeholderPlugin, { + add: { id, pos: tr.selection.from }, + }); + + view.dispatch(tr); + const pos = findPlaceholder(view.state, id, placeholderPlugin); + + if (pos == null) { + return; + } + + view.dispatch( + state.tr + .replaceWith( + pos, + pos, + view.state.schema.nodes.image.create({ + src: urls[i], + }), + ) + .setMeta(placeholderPlugin, { remove: { id } }), + ); + } +}; + +export default insertImage; diff --git a/wax-prosemirror-plugins/index.js b/wax-prosemirror-plugins/index.js index 1b52b4062d33b0d1d4c774ed6e93bf107140eabd..d0a198c76de0f10be29733fa77a2c6d8f9dbafa5 100644 --- a/wax-prosemirror-plugins/index.js +++ b/wax-prosemirror-plugins/index.js @@ -9,3 +9,4 @@ export { default as mathPlugin } from './src/math/math-plugin'; export { default as mathSelectPlugin } from './src/math/math-select'; export { default as FindAndReplacePlugin } from './src/findAndReplace/FindAndReplacePlugin'; export { default as PlaceHolderPlugin } from './src/images/placeHolderPlugin'; +export { default as captionPlugin } from './src/images/captionPlugin'; diff --git a/wax-prosemirror-plugins/src/images/captionPlugin.js b/wax-prosemirror-plugins/src/images/captionPlugin.js new file mode 100644 index 0000000000000000000000000000000000000000..fb6345e6283a576bc2a5e7155c2110c3f106ef99 --- /dev/null +++ b/wax-prosemirror-plugins/src/images/captionPlugin.js @@ -0,0 +1,21 @@ +import { Decoration, DecorationSet } from 'prosemirror-view'; +import { Plugin, TextSelection } from 'prosemirror-state'; + +const captionPlugin = key => + new Plugin({ + state: { + init() { + return DecorationSet.empty; + }, + apply(tr, set) { + console.log('in apply'); + }, + }, + props: { + decorations(state) { + return this.getState(state); + }, + }, + }); + +export default captionPlugin; diff --git a/wax-prosemirror-schema/index.js b/wax-prosemirror-schema/index.js index 56e61dcd0db6539a6eeb4bd365ea91a7c0d7a98d..f2e5d77d65a82c5abfe3dfacd0eb29f13854a366 100644 --- a/wax-prosemirror-schema/index.js +++ b/wax-prosemirror-schema/index.js @@ -35,6 +35,8 @@ export { default as orderedListNode } from './src/nodes/orderedListNode'; 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 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 new file mode 100644 index 0000000000000000000000000000000000000000..a4303d6b9aa04556b7c5631ec6f00e6d42f6ed9a --- /dev/null +++ b/wax-prosemirror-schema/src/nodes/figureCaptionNode.js @@ -0,0 +1,11 @@ +const figureCaption = { + content: 'inline*', + group: 'figure', + marks: 'strong link', + parseDOM: [{ tag: 'figcaption' }], + toDOM(node) { + return ['figcaption', 0]; + }, +}; + +export default figureCaption; diff --git a/wax-prosemirror-schema/src/nodes/figureNode.js b/wax-prosemirror-schema/src/nodes/figureNode.js new file mode 100644 index 0000000000000000000000000000000000000000..951d91c299c4e6327d3a6fcc2e69bf76a015285a --- /dev/null +++ b/wax-prosemirror-schema/src/nodes/figureNode.js @@ -0,0 +1,11 @@ +const figure = { + content: 'image* figcaption{0,1}', + group: 'block', + marks: '', + parseDOM: [{ tag: 'figure' }], + toDOM() { + return ['figure', 0]; + }, +}; + +export default figure; diff --git a/wax-prosemirror-schema/src/nodes/imageNode.js b/wax-prosemirror-schema/src/nodes/imageNode.js index 0e8fe0d9f35e5ff2ba8320cef178637fffa56bb7..d74314f435910d44cd2981a972cc0703794e417f 100644 --- a/wax-prosemirror-schema/src/nodes/imageNode.js +++ b/wax-prosemirror-schema/src/nodes/imageNode.js @@ -1,13 +1,13 @@ import { SchemaHelpers } from 'wax-prosemirror-utilities'; const image = { - inline: true, + // inline: true, attrs: { src: {}, alt: { default: null }, title: { default: null }, track: { default: [] }, }, - group: 'inline', + group: 'figure', draggable: true, parseDOM: [ { diff --git a/wax-prosemirror-services/src/ImageService/ImageService.js b/wax-prosemirror-services/src/ImageService/ImageService.js index b4dfad736bdccf80c5bfb0dd56b8b75f65671651..c25501933b0a6552f2a703f722fe04c8343c4c8f 100644 --- a/wax-prosemirror-services/src/ImageService/ImageService.js +++ b/wax-prosemirror-services/src/ImageService/ImageService.js @@ -1,26 +1,39 @@ -import { imageNode } from 'wax-prosemirror-schema'; -import { PlaceHolderPlugin } from 'wax-prosemirror-plugins'; +import { + imageNode, + figureCaptionNode, + figureNode, +} from 'wax-prosemirror-schema'; +import { PlaceHolderPlugin, captionPlugin } from 'wax-prosemirror-plugins'; import Service from '../Service'; import Image from './Image'; -const PLUGIN_KEY = 'imagePlaceHolder'; - class ImageService extends Service { name = 'ImageService'; boot() { - this.app.PmPlugins.add(PLUGIN_KEY, PlaceHolderPlugin(PLUGIN_KEY)); + this.app.PmPlugins.add( + 'imagePlaceHolder', + PlaceHolderPlugin('imagePlaceHolder'), + ); + this.app.PmPlugins.add('caption', captionPlugin('caption')); } register() { this.container.bind('Image').to(Image); const createNode = this.container.get('CreateNode'); + createNode({ + figure: figureNode, + }); + createNode( { image: imageNode, }, { toWaxSchema: true }, ); + createNode({ + figcaption: figureCaptionNode, + }); } } diff --git a/wax-prosemirror-services/src/ImageService/fileUpload.js b/wax-prosemirror-services/src/ImageService/fileUpload.js index aa33a6a9037112a8203bb4ccfe77dc8badb4e496..36a6040420141f93373cffb4021006c538b3d009 100644 --- a/wax-prosemirror-services/src/ImageService/fileUpload.js +++ b/wax-prosemirror-services/src/ImageService/fileUpload.js @@ -15,7 +15,7 @@ export default (view, fileUpload, placeholderPlugin) => file => { if (!tr.selection.empty) tr.deleteSelection(); tr.setMeta(placeholderPlugin, { - add: { id, pos: tr.selection.from } + add: { id, pos: tr.selection.from }, }); view.dispatch(tr); @@ -35,15 +35,15 @@ export default (view, fileUpload, placeholderPlugin) => file => { pos, pos, view.state.schema.nodes.image.create({ - src: url - }) + src: url, + }), ) - .setMeta(placeholderPlugin, { remove: { id } }) + .setMeta(placeholderPlugin, { remove: { id } }), ); }, () => { // On failure, just clean up the placeholder view.dispatch(tr.setMeta(placeholderPlugin, { remove: { id } })); - } + }, ); }; diff --git a/wax-prosemirror-services/src/NoteService/Editor.js b/wax-prosemirror-services/src/NoteService/Editor.js index 54faedc6aa7fdd18654dcd4dcbac796fbc3769e4..d8b7871bd1586f88a251eda49d5e0d60979a31d6 100644 --- a/wax-prosemirror-services/src/NoteService/Editor.js +++ b/wax-prosemirror-services/src/NoteService/Editor.js @@ -2,7 +2,7 @@ import React, { useEffect, useRef, useContext, useMemo } from 'react'; import { filter } from 'lodash'; import { EditorView } from 'prosemirror-view'; -import { EditorState, TextSelection } from 'prosemirror-state'; +import { EditorState } from 'prosemirror-state'; import { StepMap } from 'prosemirror-transform'; import { baseKeymap } from 'prosemirror-commands'; import { keymap } from 'prosemirror-keymap'; @@ -19,6 +19,7 @@ export default ({ node, view }) => { const noteId = node.attrs.id; let noteView; let clickInNote = false; + let typing = false; // eslint-disable-next-line react/destructuring-assignment const isEditable = context.view.main.props.editable(editable => { return editable; @@ -44,13 +45,9 @@ export default ({ node, view }) => { // the parent editor is focused. if (noteView.hasFocus()) noteView.focus(); }, - blur: view => { - view.dispatch( - view.state.tr.setSelection( - TextSelection.create(view.state.doc, 0), - ), - ); - }, + }, + handleTextInput: (editorView, from, to, text) => { + typing = true; }, transformPasted: slice => { return transformPasted(slice, noteView); @@ -105,6 +102,12 @@ export default ({ node, view }) => { context.updateView({}, noteId); }, 20); + const findReplace = context.app.PmPlugins.get('findAndReplacePlugin'); + const matches = findReplace.getState(noteView.state).allMatches; + if (matches.length > 0 && !typing && context.activeViewId === noteId) + context.updateView({}, noteId); + // UNTIL HERE + if (!tr.getMeta('fromOutside')) { const outerTr = view.state.tr; const offsetMap = StepMap.offset(noteFound[0].pos + 1); @@ -115,7 +118,7 @@ export default ({ node, view }) => { } if (outerTr.docChanged) - view.dispatch(outerTr.setMeta('outsideView', 'notes')); + view.dispatch(outerTr.setMeta('outsideView', noteId)); } };