diff --git a/editors/editoria/src/config/config.js b/editors/editoria/src/config/config.js index 98c1e8ec7edda8d6d54e6d5241ed9204d9430cde..5cb0d881c029af2b65f86a5cfd7d933443f8b4f4 100644 --- a/editors/editoria/src/config/config.js +++ b/editors/editoria/src/config/config.js @@ -40,6 +40,8 @@ import { TrackOptionsService, TrackOptionsToolGroupService, TrackCommentOptionsToolGroupService, + CustomTagInlineService, + CustomTagInlineToolGroupService, } from 'wax-prosemirror-services'; import { DefaultSchema } from 'wax-prosemirror-utilities'; @@ -74,6 +76,7 @@ export default { }, 'HighlightToolGroup', 'TransformToolGroup', + 'CustomTagInline', 'Notes', 'Lists', 'Images', @@ -167,5 +170,7 @@ export default { new TransformToolGroupService(), new TrackOptionsToolGroupService(), new TrackCommentOptionsToolGroupService(), + new CustomTagInlineToolGroupService(), + new CustomTagInlineService(), ], }; diff --git a/editors/editoria/src/config/configMobile.js b/editors/editoria/src/config/configMobile.js index 45eb49d4720104d145838676f563c449efca07e8..ee50909dcbe902400e4b28a8ad41b33f28f77c06 100644 --- a/editors/editoria/src/config/configMobile.js +++ b/editors/editoria/src/config/configMobile.js @@ -31,6 +31,8 @@ import { BottomInfoService, TransformService, TransformToolGroupService, + CustomTagInlineService, + CustomTagInlineToolGroupService, } from 'wax-prosemirror-services'; import { WaxSelectionPlugin } from 'wax-prosemirror-plugins'; @@ -58,6 +60,7 @@ export default { }, 'HighlightToolGroup', 'TransformToolGroup', + 'CustomTagInline', 'BlockDropDown', 'Notes', 'Lists', @@ -119,5 +122,7 @@ export default { new BottomInfoService(), new TransformService(), new TransformToolGroupService(), + new CustomTagInlineToolGroupService(), + new CustomTagInlineService(), ], }; diff --git a/editors/editoria/src/layout/EditorElements.js b/editors/editoria/src/layout/EditorElements.js index 9dca9e2d9084fd2e1c95e2c5463027da7aa01ae6..6ce1100e80c7edf061480642c13d6de5102fe896 100644 --- a/editors/editoria/src/layout/EditorElements.js +++ b/editors/editoria/src/layout/EditorElements.js @@ -389,4 +389,28 @@ export default css` math-inline.math-select .math-render { padding-top: 2px; } + + .custom-tag-inline { + font-weight: 500; + } + + .custom-tag-inline:before { + content: ' | '; + color: #006f19; + font-weight: 600; + margin-left: 0 + } + + .custom-tag-inline:after { + content: ' | '; + display: inline; + color: #006f19; + font-weight: 600; + } + + .custom-tag-inline:hover { + cursor: pointer; + color: #006f19; + } + `; diff --git a/wax-prosemirror-components/index.js b/wax-prosemirror-components/index.js index 2f0a4dc7ed15649a9c2024bbd957a7d8bb1b33c8..f2065c92ed7efbd95c2482b93afcd62dc272134f 100644 --- a/wax-prosemirror-components/index.js +++ b/wax-prosemirror-components/index.js @@ -23,3 +23,5 @@ export { default as EditorInfoTool } from './src/components/EditorInfo/CounterIn export { default as TransformCaseComponent } from './src/components/transformCase/TransformCaseComponent'; export { default as EditingSuggestingDropDown } from './src/components/editingSuggesting/EditingSuggestingDropDown'; export { default as TrackChangeOptionsTool } from './src/components/trackChanges/TrackChangeOptionsTool'; +export { default as CustomTagInlineComponent } from './src/components/customtag/CustomTagInlineComponent'; +export { default as CustomTagInlineOverlayComponent } from './src/components/customtag/CustomTagInlineOverlayCompoment'; diff --git a/wax-prosemirror-components/src/components/customtag/CustomTagInlineComponent.js b/wax-prosemirror-components/src/components/customtag/CustomTagInlineComponent.js new file mode 100644 index 0000000000000000000000000000000000000000..bed62d4458c963d11a1291d06389bca95be5e3f3 --- /dev/null +++ b/wax-prosemirror-components/src/components/customtag/CustomTagInlineComponent.js @@ -0,0 +1,40 @@ +import React, { useContext, useMemo, useRef, useState } from 'react'; +import styled from 'styled-components'; +import MenuButton from '../../ui/buttons/MenuButton'; + +const Wrapper = styled.div` + font-size: 0; + position: relative; + z-index: 2; +`; + + +const CustomTagInlineComponent = ({ view: { state }, item }) => { + const { icon, title } = item; + const localInline = JSON.parse(localStorage.getItem('isInline')) + const [isOpen, setIsOpen] = useState((localInline !== null && localInline !== false) ? true : false); + const ref = useRef(); + + const onClickIcon = () => { + setIsOpen(isOpen === true ? false : true); + localStorage.setItem('isInline', isOpen === true ? false : true); + } + + return useMemo( + () => ( + <Wrapper ref={ref}> + <div onClick={onClickIcon}> + <MenuButton + active={isOpen} + iconName={icon} + disabled={false} + title={title} + /> + </div> + </Wrapper> + ), + [isOpen], + ); +}; + +export default CustomTagInlineComponent; diff --git a/wax-prosemirror-components/src/components/customtag/CustomTagInlineOverlayCompoment.js b/wax-prosemirror-components/src/components/customtag/CustomTagInlineOverlayCompoment.js new file mode 100644 index 0000000000000000000000000000000000000000..41c68d19ea63d70977fdd4cebabcfd86412cdc23 --- /dev/null +++ b/wax-prosemirror-components/src/components/customtag/CustomTagInlineOverlayCompoment.js @@ -0,0 +1,155 @@ +/* eslint react/prop-types: 0 */ +import React, { useRef, useEffect, useState, useContext } from 'react'; +import styled from 'styled-components'; +import { grid, th } from '@pubsweet/ui-toolkit'; +import { WaxContext } from 'wax-prosemirror-core'; + +const IconSVG = props => { + const { className } = props; + return ( + <svg version="1.1" viewBox="0 0 512.001 512.001" className={className}> + <path d="M284.286,256.002L506.143,34.144c7.811-7.811,7.811-20.475,0-28.285c-7.811-7.81-20.475-7.811-28.285,0L256,227.717 + L34.143,5.859c-7.811-7.811-20.475-7.811-28.285,0c-7.81,7.811-7.811,20.475,0,28.285l221.857,221.857L5.858,477.859 + c-7.811,7.811-7.811,20.475,0,28.285c3.905,3.905,9.024,5.857,14.143,5.857c5.119,0,10.237-1.952,14.143-5.857L256,284.287 + l221.857,221.857c3.905,3.905,9.024,5.857,14.143,5.857s10.237-1.952,14.143-5.857c7.811-7.811,7.811-20.475,0-28.285 + L284.286,256.002z"/> + </svg> + ); +}; + +const Icon = styled(IconSVG)` + height: 10px; + width: 10px; + fill: ${th('colorPrimary')}; +`; + +const Wrapper = styled.div` + background: #fff; + background: #fff; + border-radius: 1.03093% / 8%; + box-shadow: rgba(9, 30, 66, 0.25) 0px 4px 8px 0px, + rgba(9, 30, 66, 0.31) 0px 0px 1px 0px; + transform-origin: 50% 50% 0px; + padding: ${grid(2)} ${grid(1)} ${grid(2)} ${grid(2)}; +`; + +const CustomWrapper = styled.div` + display: inline-block; + width: 200px; + margin-right: 12px; +`; + +const Input = styled.input` + width: calc(100% - 8px); + outline: none; + :focus { + outline: none; + } +`; + +const ButtonGroup = styled.div` + display: inline-block; +`; + +const StyledButton = styled.button` + margin-right: 10px; + background: ${th('colorPrimary')}; + cursor: pointer; + color: #fff; +`; + +const ListStyle = styled.div` + padding: 2px 3px; + margin: 5px 7px 7px 0px; + cursor: pointer; +`; + +const CustomTagInlineOverlayComponent = ({ mark, setPosition, position }) => { + + const ref = useRef(null); + const [tagName, setTagName] = useState(); + const [inputValue, setInputValue] = 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); + const { state, dispatch } = main; + const { selection: { $from, $to } } = state; + + const onChangeTagName = (e) => { + setTagName(e.target.value) + setInputValue(e.target.value) + } + + const onClickAdd = () => { + let tagNameList = []; + if (localStorage.getItem('tagList') === null) { + tagNameList.push(tagName); + localStorage.setItem('tagList', JSON.stringify(tagNameList)); + } else { + tagNameList = JSON.parse(localStorage.getItem('tagList')); + tagNameList.push(tagName); + localStorage.clear('tagList'); + localStorage.setItem('tagList', JSON.stringify(tagNameList)); + } + setInputValue(' ') + } + + const onListClicked = (e, item) => { + + setSelectedTagName(item); + dispatch( + state.tr.addMark( + $from.pos, + $to.pos, + state.schema.marks.customTagInline.create({ + tagName: item, + class: 'custom-tag-inline', + }), + ), + ); + } + + const onClickCancel = () => { + dispatch(state.tr.removeMark($from.pos, $to.pos, state.schema.marks.customTagInline)); + setSelectedTagName(''); + } + + 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> + } + + </div> + </ListStyle>)} + <CustomWrapper ref={ref}> + <Input type="text" onChange={e => onChangeTagName(e)} value={inputValue} /> + </CustomWrapper> + + <ButtonGroup> + <StyledButton type="button" onClick={onClickAdd}> + Add + </StyledButton> + </ButtonGroup> + </Wrapper> + ) : null; +}; +export default CustomTagInlineOverlayComponent; diff --git a/wax-prosemirror-components/src/icons/icons.js b/wax-prosemirror-components/src/icons/icons.js index 002c1972f9fcdd69b088f4b680c6bfeec82845cb..057c29057defe3f6f339e7f0e640394891eb4191 100644 --- a/wax-prosemirror-components/src/icons/icons.js +++ b/wax-prosemirror-components/src/icons/icons.js @@ -386,4 +386,11 @@ export default { <polygon points="21.663,8.742 26.08,6.938 21.663,5.135 21.663,6.376 15.637,6.376 15.637,6.625 15.637,7.253 15.637,7.501 21.663,7.501" /> </Svg> ), + cutomInline: ({ className }) => ( + <Svg className={className} + viewBox="0 0 426.66667 426.66667" fill="none" + > + <path d="m405.332031 192h-170.664062v-170.667969c0-11.773437-9.558594-21.332031-21.335938-21.332031-11.773437 0-21.332031 9.558594-21.332031 21.332031v170.667969h-170.667969c-11.773437 0-21.332031 9.558594-21.332031 21.332031 0 11.777344 9.558594 21.335938 21.332031 21.335938h170.667969v170.664062c0 11.777344 9.558594 21.335938 21.332031 21.335938 11.777344 0 21.335938-9.558594 21.335938-21.335938v-170.664062h170.664062c11.777344 0 21.335938-9.558594 21.335938-21.335938 0-11.773437-9.558594-21.332031-21.335938-21.332031zm0 0" /> + </Svg> + ) }; diff --git a/wax-prosemirror-schema/index.js b/wax-prosemirror-schema/index.js index f2e5d77d65a82c5abfe3dfacd0eb29f13854a366..47d639e735d433a568dbdf4b8f714533ea0842f1 100644 --- a/wax-prosemirror-schema/index.js +++ b/wax-prosemirror-schema/index.js @@ -15,6 +15,7 @@ export { default as commentMark } from './src/marks/commentMark'; export { default as mathSelectMark } from './src/marks/mathSelectMark'; export { default as highlightMark } from './src/marks/highlightMark'; export { default as transformMark } from './src/marks/transformMark'; +export { default as customtagInlineMark } from './src/marks/customTagInlineMark'; /* LIST OF TRACK CHANGES MARKS */ diff --git a/wax-prosemirror-schema/src/marks/customTagInlineMark.js b/wax-prosemirror-schema/src/marks/customTagInlineMark.js new file mode 100644 index 0000000000000000000000000000000000000000..bdff2c38e18c77d95bf147d39d3f0ff0181f1b6b --- /dev/null +++ b/wax-prosemirror-schema/src/marks/customTagInlineMark.js @@ -0,0 +1,27 @@ +const customtagInline = { + excludes: 'cutomInline', + + attrs: { + class: { default: null }, + tagName: '' + }, + inclusive: false, + parseDOM: [ + { + tag: 'custom-tag-inline', + getAttrs(hook, next) { + Object.assign(hook, { + class: hook.dom.getAttribute('class'), + tagname: hook.dom.getAttribute('tagName') + }); + next(); + }, + }, + ], + toDOM(hook, next) { + hook.value = ['custom-tag-inline', hook.node.attrs, 0]; // eslint-disable-line no-param-reassign + next(); + }, +}; + +export default customtagInline; diff --git a/wax-prosemirror-services/index.js b/wax-prosemirror-services/index.js index 363fddbb6b51a3c2398548ffc3fda51b3498e975..9c834f1c4f0b5cce76e6de830806e59a22e24084 100644 --- a/wax-prosemirror-services/index.js +++ b/wax-prosemirror-services/index.js @@ -36,6 +36,7 @@ export { default as BottomInfoService } from './src/BottomInfoService/BottomInfo export { default as TransformService } from './src/TransformService/TransformService'; export { default as EditingSuggestingService } from './src/EditingSuggestingService/EditingSuggestingService'; export { default as TrackOptionsService } from './src/TrackOptionsService/TrackOptionsService'; +export { default as CustomTagInlineService } from './src/CustomTagService/CustomTagInlineService/CustomTagInlineService'; /* ToolGroups @@ -59,3 +60,4 @@ export { default as EditorInfoToolGroupServices } from './src/WaxToolGroups/Bott export { default as TransformToolGroupService } from './src/WaxToolGroups/TransformToolGroupService/TransformToolGroupService'; export { default as TrackOptionsToolGroupService } from './src/WaxToolGroups/TrackOptionsToolGroupService/TrackOptionsToolGroupService'; export { default as TrackCommentOptionsToolGroupService } from './src/WaxToolGroups/TrackCommentOptionsToolGroupService/TrackCommentOptionsToolGroupService'; +export { default as CustomTagInlineToolGroupService } from './src/WaxToolGroups/CustomTagToolGroupService/CustomTagInlineToolGroupService/CustomTagInlineToolGroupService'; diff --git a/wax-prosemirror-services/src/CustomTagService/CustomTagInlineService/CustomTagInlineService.js b/wax-prosemirror-services/src/CustomTagService/CustomTagInlineService/CustomTagInlineService.js new file mode 100644 index 0000000000000000000000000000000000000000..1e913ce5f8cfa7f0fd2ef3692c3bae7a42347274 --- /dev/null +++ b/wax-prosemirror-services/src/CustomTagService/CustomTagInlineService/CustomTagInlineService.js @@ -0,0 +1,33 @@ +import CustomTagInlineTool from './CustomTagInlineTool'; +import Service from '../../Service'; +import { CustomTagInlineOverlayComponent } from 'wax-prosemirror-components'; +import { customtagInlineMark } from 'wax-prosemirror-schema'; +import { WaxContext } from 'wax-prosemirror-core'; + +export default class CustomTagInlineService extends Service { + + boot() { + const createOverlay = this.container.get('CreateOverlay'); + + createOverlay( + CustomTagInlineOverlayComponent, + {}, + { + followCursor: false, + selection: true, + }, + ); + } + + + register() { + this.container.bind('CustomTagInlineTool').to(CustomTagInlineTool); + const createMark = this.container.get('CreateMark'); + createMark( + { + customTagInline: customtagInlineMark, + }, + { toWaxSchema: true }, + ); + } +} \ No newline at end of file diff --git a/wax-prosemirror-services/src/CustomTagService/CustomTagInlineService/CustomTagInlineTool.js b/wax-prosemirror-services/src/CustomTagService/CustomTagInlineService/CustomTagInlineTool.js new file mode 100644 index 0000000000000000000000000000000000000000..109043b913e385b75da0ff1ba2056f061f9aa1b6 --- /dev/null +++ b/wax-prosemirror-services/src/CustomTagService/CustomTagInlineService/CustomTagInlineTool.js @@ -0,0 +1,19 @@ +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 CustomTagInlineComponent from '../../../../wax-prosemirror-components/src/components/customtag/CustomTagInlineComponent' + +class CustomTagInLineTool extends Tools { + title = 'Custom Tag Inline'; + icon = 'cutomInline'; + name = 'CustomTagInline'; + + renderTool(view) { + return ( + <CustomTagInlineComponent item={this.toJSON()} key={uuidv4()} view={view} /> + ); + } +} + +export default CustomTagInLineTool; diff --git a/wax-prosemirror-services/src/WaxToolGroups/CustomTagToolGroupService/CustomTagInlineToolGroupService/CustomTagInlineToolGroup.js b/wax-prosemirror-services/src/WaxToolGroups/CustomTagToolGroupService/CustomTagInlineToolGroupService/CustomTagInlineToolGroup.js new file mode 100644 index 0000000000000000000000000000000000000000..97cded570c7089b59aba24368ce5764d738b2af5 --- /dev/null +++ b/wax-prosemirror-services/src/WaxToolGroups/CustomTagToolGroupService/CustomTagInlineToolGroupService/CustomTagInlineToolGroup.js @@ -0,0 +1,14 @@ +import {injectable, inject} from 'inversify'; +import ToolGroup from '../../../lib/ToolGroup'; + +@injectable() +class CustomTagInlineToolGroup extends ToolGroup { + tools = []; + + constructor(@inject('CustomTagInlineTool') customTagInline) { + super(); + this.tools = [customTagInline]; + } +} + +export default CustomTagInlineToolGroup; \ No newline at end of file diff --git a/wax-prosemirror-services/src/WaxToolGroups/CustomTagToolGroupService/CustomTagInlineToolGroupService/CustomTagInlineToolGroupService.js b/wax-prosemirror-services/src/WaxToolGroups/CustomTagToolGroupService/CustomTagInlineToolGroupService/CustomTagInlineToolGroupService.js new file mode 100644 index 0000000000000000000000000000000000000000..1f50851081e10da9af27d34440f920651e22ac15 --- /dev/null +++ b/wax-prosemirror-services/src/WaxToolGroups/CustomTagToolGroupService/CustomTagInlineToolGroupService/CustomTagInlineToolGroupService.js @@ -0,0 +1,10 @@ +import Service from '../../../Service'; +import CustomTagInlineToolGroup from './CustomTagInlineToolGroup'; + +class CustomTagInlineToolGroupService extends Service { + register() { + this.container.bind('CustomTagInline').to(CustomTagInlineToolGroup); + } +} + +export default CustomTagInlineToolGroupService;