From 98b022a2952dcd0e5ff73dea6c8b051331f01639 Mon Sep 17 00:00:00 2001 From: Deleephan <deleephan.sanjeevi@amnet-systems.com> Date: Tue, 2 Feb 2021 20:19:44 +0530 Subject: [PATCH] fix(custom-tag-block): fixed custom-tag-block --- editors/editoria/src/config/config.js | 7 + editors/editoria/src/config/configMobile.js | 7 + .../customtag/CustomTagBlockComponent.js | 44 ++++-- .../CustomTagInlineOverlayCompoment.js | 131 ++++++++++++++---- .../src/marks/customTagInlineMark.js | 6 +- .../CustomTagBlockTool.js | 12 +- 6 files changed, 160 insertions(+), 47 deletions(-) diff --git a/editors/editoria/src/config/config.js b/editors/editoria/src/config/config.js index b8fa8b711..9f5664137 100644 --- a/editors/editoria/src/config/config.js +++ b/editors/editoria/src/config/config.js @@ -130,6 +130,13 @@ export default { invisibles([hardBreak()]), WaxSelectionPlugin, ], + CustomTagService: { + tags: [ + { label: 'custom-tag-label-1', tagType: 'inline' }, + { label: 'custom-tag-label-2', tagType: 'inline' }, + { label: 'custom-tag-label-3', tagType: 'block' } + ] + }, // Always load first CommentsService and LinkService, //as it matters on how PM treats nodes and marks diff --git a/editors/editoria/src/config/configMobile.js b/editors/editoria/src/config/configMobile.js index ae449efe9..e024bf39e 100644 --- a/editors/editoria/src/config/configMobile.js +++ b/editors/editoria/src/config/configMobile.js @@ -89,6 +89,13 @@ export default { invisibles([hardBreak()]), WaxSelectionPlugin, ], + CustomTagService: { + tags: [ + { label: 'custom-tag-label-1', tagType: 'inline' }, + { label: 'custom-tag-label-2', tagType: 'inline' }, + { label: 'custom-tag-label-3', tagType: 'block' } + ] + }, // Always load first CommentsService and LinkService, //as it matters on how PM treats nodes and marks diff --git a/wax-prosemirror-components/src/components/customtag/CustomTagBlockComponent.js b/wax-prosemirror-components/src/components/customtag/CustomTagBlockComponent.js index 79b5a2bee..a09c91c56 100644 --- a/wax-prosemirror-components/src/components/customtag/CustomTagBlockComponent.js +++ b/wax-prosemirror-components/src/components/customtag/CustomTagBlockComponent.js @@ -1,7 +1,9 @@ -import React, { useContext, useMemo, useRef, useState } from 'react'; +import React, { useContext, useMemo, useRef, useState, useEffect } from 'react'; import styled from 'styled-components'; import { WaxContext } from 'wax-prosemirror-core'; import { Commands } from 'wax-prosemirror-utilities'; +import useDeepCompareEffect from 'use-deep-compare-effect'; +import { v4 as uuidv4 } from 'uuid'; const Wrapper = styled.div``; @@ -49,15 +51,18 @@ const StyledButton = styled.div``; -const CustomTagBlockComponent = (isIconClicked) => { +const CustomTagBlockComponent = (isIconClicked, item) => { const ref = useRef(); const [inputValue, setInputValue] = useState(''); const [tagName, setTagName] = useState(''); const localTagList = JSON.parse(localStorage.getItem('tagBlockList')); - const { view: { main }, activeView } = useContext(WaxContext); + const { app, view: { main }, activeView } = useContext(WaxContext); const { state, dispatch } = main; const { selection: { $from, $to } } = state; + const serviceConfig = app.config.get('config.CustomTagService'); + const [serviceList, setServiceList] = useState([]); + const onChangeTagName = (e) => { setTagName(e.target.value) @@ -68,24 +73,45 @@ const CustomTagBlockComponent = (isIconClicked) => { if (tagName === '') return; let tagNameList = []; if (localStorage.getItem('tagBlockList') === null) { - tagNameList.push(tagName); + tagNameList.push({ label: tagName, type: 'block' }); localStorage.setItem('tagBlockList', JSON.stringify(tagNameList)); } else { tagNameList = JSON.parse(localStorage.getItem('tagBlockList')); - tagNameList.push(tagName); + tagNameList.push({ label: tagName, type: 'block' }); localStorage.clear('tagBlockList'); localStorage.setItem('tagBlockList', JSON.stringify(tagNameList)); } setInputValue(' ') } - const onSelectTag = (e, item) => { - item = item.replace(/ /g, "-"); + const onSelectTag = (e, val) => { + val = val.replace(/ /g, "-"); + Commands.setBlockType(state.config.schema.nodes.customTagBlock, { - class: 'custom-tag-block ' + item + class: 'custom-tag-block ' + val })(state, dispatch); + + } + useDeepCompareEffect(() => { + let labels = []; + if (serviceConfig !== undefined) { + serviceConfig.tags.forEach(item => { + if (item.tagType === 'block') { + labels.push(item.label); + } + }); + } + if (localTagList !== null) { + localTagList.forEach(item => { + labels.push(item.label) + }) + } + setServiceList(labels); + }, [localTagList, serviceConfig]); + + return useMemo( () => ( <Wrapper> @@ -98,7 +124,7 @@ const CustomTagBlockComponent = (isIconClicked) => { <Add onClick={onClickAdd}>Add</Add> </FlexDiv>} - {localTagList !== null && localTagList.map((item, pos) => <ListStyle key={pos}> + {serviceList !== null && serviceList.map((item, pos) => <ListStyle key={uuidv4()}> <FlexDiv onClick={e => onSelectTag(e, item)} > <Box /> <StyledButton>{item}</StyledButton> diff --git a/wax-prosemirror-components/src/components/customtag/CustomTagInlineOverlayCompoment.js b/wax-prosemirror-components/src/components/customtag/CustomTagInlineOverlayCompoment.js index 41c68d19e..2154e717f 100644 --- a/wax-prosemirror-components/src/components/customtag/CustomTagInlineOverlayCompoment.js +++ b/wax-prosemirror-components/src/components/customtag/CustomTagInlineOverlayCompoment.js @@ -1,8 +1,10 @@ /* eslint react/prop-types: 0 */ -import React, { useRef, useEffect, useState, useContext } from 'react'; +import React, { useRef, useEffect, useState, useContext, Fragment } from 'react'; import styled from 'styled-components'; import { grid, th } from '@pubsweet/ui-toolkit'; import { WaxContext } from 'wax-prosemirror-core'; +import { DocumentHelpers } from 'wax-prosemirror-utilities'; +import { v4 as uuidv4 } from 'uuid'; const IconSVG = props => { const { className } = props; @@ -64,12 +66,22 @@ const ListStyle = styled.div` cursor: pointer; `; +const Flex = styled.div` + display: flex; + justify-content: space-between; +`; + +const DivWidth = styled.div` + width: 100%; +`; + + const CustomTagInlineOverlayComponent = ({ mark, setPosition, position }) => { const ref = useRef(null); const [tagName, setTagName] = useState(); const [inputValue, setInputValue] = useState(''); - const [selectedTagName, setSelectedTagName] = useState(''); + const [selectedTagName, setSelectedTagName] = useState([]); const localTagList = JSON.parse(localStorage.getItem('tagList')); const [isCustomTagInline, setCustomTag] = useState(JSON.parse(localStorage.getItem('isInline'))); const { view: { main } } = useContext(WaxContext); @@ -82,63 +94,124 @@ const CustomTagInlineOverlayComponent = ({ mark, setPosition, position }) => { } const onClickAdd = () => { + const localItem = localStorage.getItem('isInline'); let tagNameList = []; if (localStorage.getItem('tagList') === null) { - tagNameList.push(tagName); + tagNameList.push({ label: tagName, type: 'inline' }); localStorage.setItem('tagList', JSON.stringify(tagNameList)); } else { tagNameList = JSON.parse(localStorage.getItem('tagList')); - tagNameList.push(tagName); + tagNameList.push({ label: tagName, type: 'inline' }); localStorage.clear('tagList'); localStorage.setItem('tagList', JSON.stringify(tagNameList)); + localStorage.setItem('isInline', localItem); } - setInputValue(' ') + setInputValue(''); } - const onListClicked = (e, item) => { + const onListClicked = (item) => { + let tagNames = []; + let finalTag = []; + let isExist = false; + let classNames = 'custom-tag-inline '; + const mark = DocumentHelpers.findMark(state, state.schema.marks.customTagInline, true); + + tagNames.push(item); + if (mark.length > 0) { + mark.forEach(itemArtt => { + const classArray = itemArtt.attrs.class.split(' '); + classArray.forEach(classData => { + item.replace(/ /g, "-"); + if (classData === item.replace(/ /g, "-")) { + isExist = true; + } + }) + const parseArray = JSON.parse(itemArtt.attrs.tagNames) + classNames = itemArtt.attrs.class + ' ' + finalTag = tagNames.concat(parseArray); + }); + } else { + finalTag.push(item); + } + + if (isExist) return; + + setSelectedTagName(oldArray => [...oldArray, item]); + item = classNames + item.replace(/ /g, "-"); - setSelectedTagName(item); dispatch( state.tr.addMark( $from.pos, $to.pos, state.schema.marks.customTagInline.create({ - tagName: item, - class: 'custom-tag-inline', + tagNames: JSON.stringify(finalTag), + class: item }), ), ); + } - const onClickCancel = () => { - dispatch(state.tr.removeMark($from.pos, $to.pos, state.schema.marks.customTagInline)); - setSelectedTagName(''); + const onClickCancel = (tagName) => { + let classNames = ''; + let classArray = []; + let tagArray = []; + let finalTag = []; + const mark = DocumentHelpers.findMark(state, state.schema.marks.customTagInline, true); + + setSelectedTagName(finalTag) + tagName = tagName.replace(/ /g, "-"); + if (mark.length > 0) { + mark.forEach(itemArtt => { + classNames = '' + classArray = itemArtt.attrs.class.split(' '); + tagArray = JSON.parse(itemArtt.attrs.tagNames); + classArray.forEach(classData => { + if (classData !== tagName) { + classNames = classNames + ' ' + classData + } + }) + tagArray.forEach(tag => { + if (tag.replace(/ /g, "-") !== tagName) { + finalTag.push(tag); + } + }); + }); + setSelectedTagName(finalTag.filter((item, index) => finalTag.indexOf(item) === index)); + if (finalTag.length === 0) { + dispatch(state.tr.removeMark($from.pos, $to.pos, state.schema.marks.customTagInline)); + } else { + dispatch( + state.tr.addMark( + $from.pos, + $to.pos, + state.schema.marks.customTagInline.create({ + tagNames: JSON.stringify(finalTag), + class: classNames + }), + ), + ); + } + } + } useEffect(() => { - state.doc.nodesBetween( - $from.pos, $to.pos, - (node, from) => { - node.marks.forEach(item => { - setSelectedTagName(item.attrs.tagName !== undefined ? item.attrs.tagName : '' ); - }); - }, - ); setCustomTag(JSON.parse(localStorage.getItem('isInline'))); }); return isCustomTagInline === true ? ( <Wrapper> - {localTagList !== null && localTagList.map((item, pos) => <ListStyle key={pos}> - <div style={{ display: 'flex', justifyContent: 'space-between' }}> - <div onClick={e => onListClicked(e, item)}> {item} </div> - {selectedTagName === item && - <div onClick={onClickCancel}> - <Icon /> - </div> - } + {localTagList !== null && localTagList.map((item) => <ListStyle key={uuidv4()}> + <Flex> + <DivWidth onClick={e => onListClicked(item.label)}> {item.label} </DivWidth> - </div> + {selectedTagName.map(value => <Fragment key={uuidv4()}> + {value === item.label ? <span onClick={e => onClickCancel(item.label)}> + <Icon /> + </span> : ''} + </Fragment>)} + </Flex> </ListStyle>)} <CustomWrapper ref={ref}> <Input type="text" onChange={e => onChangeTagName(e)} value={inputValue} /> diff --git a/wax-prosemirror-schema/src/marks/customTagInlineMark.js b/wax-prosemirror-schema/src/marks/customTagInlineMark.js index bdff2c38e..d34e617f8 100644 --- a/wax-prosemirror-schema/src/marks/customTagInlineMark.js +++ b/wax-prosemirror-schema/src/marks/customTagInlineMark.js @@ -1,9 +1,9 @@ const customtagInline = { - excludes: 'cutomInline', + excludes: '', attrs: { class: { default: null }, - tagName: '' + tagNames: '', }, inclusive: false, parseDOM: [ @@ -12,7 +12,7 @@ const customtagInline = { getAttrs(hook, next) { Object.assign(hook, { class: hook.dom.getAttribute('class'), - tagname: hook.dom.getAttribute('tagName') + tagNames: hook.dom.getAttribute('tagNames'), }); next(); }, diff --git a/wax-prosemirror-services/src/CustomTagService/CustomTagBlockService/CustomTagBlockTool.js b/wax-prosemirror-services/src/CustomTagService/CustomTagBlockService/CustomTagBlockTool.js index 6f960c65d..8ae004422 100644 --- a/wax-prosemirror-services/src/CustomTagService/CustomTagBlockService/CustomTagBlockTool.js +++ b/wax-prosemirror-services/src/CustomTagService/CustomTagBlockService/CustomTagBlockTool.js @@ -1,17 +1,17 @@ import React from 'react'; // eslint-disable-next-line import/no-named-as-default,import/no-named-as-default-member import Tools from '../../lib/Tools'; -import { v4 as uuidv4 } from 'uuid'; -import { CustomTagBlockComponent } from 'wax-prosemirror-components'; class CustomTagBlockTool extends Tools { title = 'Custom Tag Block'; name = 'CustomTagBlock'; - renderTool(view) { - return ( - <CustomTagBlockComponent item={this.toJSON()} key={uuidv4()} view={view} /> - ); + get active() { + return state => { + return Commands.blockActive(state.config.schema.nodes.customTagBlock)( + state, + ); + }; } } -- GitLab