diff --git a/editors/editoria/src/config/config.js b/editors/editoria/src/config/config.js index 75fe9444c1e4f86f5eb2a15d2df48ecb05d6fed1..7b2f7d53cfcc57eff48a949bc7932d266ba30742 100644 --- a/editors/editoria/src/config/config.js +++ b/editors/editoria/src/config/config.js @@ -32,6 +32,8 @@ import { FullScreenToolGroupService, SpecialCharactersService, SpecialCharactersToolGroupService, + HighlightService, + TextHighlightToolGroupServices, } from 'wax-prosemirror-services'; import { DefaultSchema } from 'wax-prosemirror-utilities'; @@ -54,6 +56,7 @@ export default { name: 'Annotations', more: ['Superscript', 'Subscript', 'SmallCaps'], }, + 'HighlightToolGroup', 'Notes', 'Lists', 'Images', @@ -134,5 +137,7 @@ export default { new FullScreenToolGroupService(), new SpecialCharactersService(), new SpecialCharactersToolGroupService(), + new HighlightService(), + new TextHighlightToolGroupServices(), ], }; diff --git a/editors/editoria/src/config/configMobile.js b/editors/editoria/src/config/configMobile.js index 84eb493e34f9d863a6fdefdb70a535a48484ec53..2ece02d823d24fc371148ab579058bc18913d598 100644 --- a/editors/editoria/src/config/configMobile.js +++ b/editors/editoria/src/config/configMobile.js @@ -26,6 +26,8 @@ import { TrackChangeToolGroupService, DisplayTextToolGroupService, BlockDropDownToolGroupService, + HighlightService, + TextHighlightToolGroupServices, } from 'wax-prosemirror-services'; import { WaxSelectionPlugin } from 'wax-prosemirror-plugins'; @@ -46,6 +48,7 @@ export default { name: 'Annotations', more: ['Superscript', 'Subscript', 'SmallCaps'], }, + 'HighlightToolGroup', 'BlockDropDown', 'Notes', 'Lists', @@ -96,5 +99,7 @@ export default { new TrackChangeToolGroupService(), new DisplayTextToolGroupService(), new BlockDropDownToolGroupService(), + new HighlightService(), + new TextHighlightToolGroupServices(), ], }; diff --git a/wax-prosemirror-components/index.js b/wax-prosemirror-components/index.js index 6203978ca814f55a64f55c3e2c5f5d7160c6266d..77d5da795d89ea16be527c60b192ca517c170fc1 100644 --- a/wax-prosemirror-components/index.js +++ b/wax-prosemirror-components/index.js @@ -17,3 +17,4 @@ export { default as BlockLevelTools } from './src/ui/tabs/BlockLevelTools'; export { default as FindAndReplaceTool } from './src/components/findAndReplace/FindAndReplaceTool'; export { default as FullScreen } from './src/components/FullScreen'; export { default as SpecialCharactersTool } from './src/components/specialCharacters/SpecialCharactersTool'; +export { default as TextHighlightingTool } from './src/components/textHighlight/TextHighlightingTool'; diff --git a/wax-prosemirror-components/src/components/textHighlight/TextHighlightComponent.js b/wax-prosemirror-components/src/components/textHighlight/TextHighlightComponent.js new file mode 100644 index 0000000000000000000000000000000000000000..04daf9c04a3564d0d0fa98ce8c901d6131344fb4 --- /dev/null +++ b/wax-prosemirror-components/src/components/textHighlight/TextHighlightComponent.js @@ -0,0 +1,81 @@ +import React, { useMemo, useState, useRef } from 'react'; +import ReactDOM from 'react-dom'; +import { v4 as uuidv4 } from 'uuid'; +import styled from 'styled-components'; +import { grid } from '@pubsweet/ui-toolkit'; +import useOnClickOutside from '../../helpers/useOnClickOutside'; + + +const Wrapper = styled.div` + width: 400px; + height: 250px; + overflow: hidden; + background: #fff; +`; + +const HighlightListComponent = styled.div` + height: 200px; + display: flex; + flex-direction: column; + overflow-y: scroll; + overflow-x: hidden; +`; + +const Highlighter = styled.div` + min-width: 25px; + height: 25px; + display: inline-grid; + cursor: pointer; + border-radius: 50%; + &:hover { + background: white; + } + span { + font-size: 16px; + text-align: center; + padding-top: 3px; + color: white; + } +`; + +const TextHighlightComponent = ({ view = { dispatch, state }, item, close }) => { + + const highlightDropDownOptions = [ + { name: 'red', value: 'background-color:red' }, + { name: 'blue', value: 'background-color:blue' }, + { name: 'yellow', value: 'background-color:yellow' }, + { name: 'black', value: 'background-color:black' }, + { name: 'transparent', value: 'background-color:transparent' }, + ]; + + const handleMouseDown = e => { + e.preventDefault(); + console.log("state@@@@ ", view.state, " ########dispatch ", view.dispatch); + item.run(view.state, view.dispatch); + }; + + const renderList = () => { + const lists = []; + + Object.keys(highlightDropDownOptions).forEach(function (key) { + lists.push( + <Highlighter onMouseDown={e => handleMouseDown(e)} key={uuidv4()} + title={highlightDropDownOptions[key].name} iconName={item.icon} + > + </Highlighter> + ); + }); + return <div>{lists}</div>; + }; + + + return ( + <Wrapper> + <HighlightListComponent>{renderList()}</HighlightListComponent> + </Wrapper> + ); +}; + +export default TextHighlightComponent; + + diff --git a/wax-prosemirror-components/src/components/textHighlight/TextHighlightingTool.js b/wax-prosemirror-components/src/components/textHighlight/TextHighlightingTool.js new file mode 100644 index 0000000000000000000000000000000000000000..2e8d47f8d408aff2122f7effaa4787887676d192 --- /dev/null +++ b/wax-prosemirror-components/src/components/textHighlight/TextHighlightingTool.js @@ -0,0 +1,128 @@ +import React, { useMemo, useState, useRef, useContext } from 'react'; +import styled from 'styled-components'; +import { grid } from '@pubsweet/ui-toolkit'; +import { v4 as uuidv4 } from 'uuid'; +import MenuButton from '../../ui/buttons/MenuButton'; +import useOnClickOutside from '../../helpers/useOnClickOutside'; +import { WaxContext } from 'wax-prosemirror-core'; + +const Wrapper = styled.div` + font-size: 0; + position: relative; + z-index: 2; +`; + +const DropWrapper = styled.div` + margin-top: ${grid(1)}; + position: absolute; + background: white; + top: 32px; + width: max-content; +`; +const TextHighlightComponent = styled.div` + display: flex; + flex-direction: column; + background:white + border:1px solid gray +`; +const Highlighter = styled.div` + min-width: 25px; + height: 25px; + margin:5px; + display: inline-grid; + cursor: pointer; + border:1px solid gray +`; + +const TextHighlightingTool = ({ view: { dispatch, state }, item }) => { + const { icon, title, select } = item; + const [isOpen, setIsOpen] = useState(false); + const ref = useRef(); + const { + view: { main }, + activeViewId, + activeView, + } = useContext(WaxContext); + + useOnClickOutside(ref, () => setIsOpen(false)); + let clicks = []; + let lastSaveColor; + let isApplied=false; + let timeout; + + const highlightDropDownOptions = [ + { name: 'red', value: 'color:red' }, + { name: 'blue', value: 'color:blue' }, + { name: 'yellow', value: 'color:yellow' }, + { name: 'black', value: 'color:black' }, + { name: 'green', value: 'color:green' }, + { name: 'gray', value: 'color:gray' }, + { name: 'orange', value: 'color:orange' }, + { name: 'brown', value: 'color:brown' }, + { name: 'aquamarine', value: 'color:aquamarine' }, + { name: 'transparent', value: 'color:transparent' }, + ]; + + const renderList = () => { + const lists = []; + + Object.keys(highlightDropDownOptions).forEach(function (key) { + lists.push( + <Highlighter onMouseDown={e => handleMouseDown(e)} key={uuidv4()} + title={highlightDropDownOptions[key].name} data-style={highlightDropDownOptions[key].value} style={{backgroundColor: highlightDropDownOptions[key].name}} + > + </Highlighter> + ); + }); + return <div>{lists}</div>; + }; + + const handleMouseDown = e => { + e.preventDefault(); + item.run(activeView.state,activeView.dispatch,e.target.getAttribute('style')); + lastSaveColor=e.target.getAttribute('style') + localStorage.setItem('lastColor',lastSaveColor) + let btnBackground=e.target.title; + localStorage.setItem('lastBgColor',btnBackground) + console.log(btnBackground); + setIsOpen(false); + } + const handleDblClk=()=>{ + console.log() + item.run(state, dispatch, localStorage.getItem('lastColor')); + } + + const isDisabled = !select(state, activeViewId, activeView); + + const MenuButtonComponent = useMemo( + () => ( + <Wrapper ref={ref} onDoubleClick={handleDblClk}> + <div style={{backgroundColor:localStorage.getItem('lastBgColor')!=undefined?localStorage.getItem('lastBgColor'):''}}> + <MenuButton + active={isOpen} + disabled={isDisabled} + iconName={icon} + onMouseDown={() => {setIsOpen(!isOpen)} + + } + title={title} + /> + </div> + {isOpen && ( + <DropWrapper> + <TextHighlightComponent key={uuidv4()} item={item} view={dispatch, state} + close={() => { + setIsOpen(false); + }}>{renderList()}</TextHighlightComponent> + </DropWrapper> + )} + </Wrapper> + ), + [isOpen,isDisabled], + ); + + return MenuButtonComponent; +}; + +export default TextHighlightingTool; + diff --git a/wax-prosemirror-components/src/icons/icons.js b/wax-prosemirror-components/src/icons/icons.js index 7797e4614aafb84e1715c8918b4aae72e4a18c91..e60e82864422c4d43cedf2b223c8d207a7f6985d 100644 --- a/wax-prosemirror-components/src/icons/icons.js +++ b/wax-prosemirror-components/src/icons/icons.js @@ -215,6 +215,21 @@ export default { </g> </Svg> ), + highlight: ({ className }) => ( + <Svg + className={className} + enable-background="new 0 0 24 24" + viewBox="0 0 24 24" + > + <g> + <rect fill="none" height="24" width="24" /> + <circle cx="12" cy="3.5" fill="none" r=".75" /> + <circle cx="12" cy="3.5" fill="none" r=".75" /> + <circle cx="12" cy="3.5" fill="none" r=".75" /> + <path d="M19,3h-4.18C14.4,1.84,13.3,1,12,1S9.6,1.84,9.18,3H5C4.86,3,4.73,3.01,4.6,3.04C4.21,3.12,3.86,3.32,3.59,3.59 c-0.18,0.18-0.33,0.4-0.43,0.64C3.06,4.46,3,4.72,3,5v14c0,0.27,0.06,0.54,0.16,0.78c0.1,0.24,0.25,0.45,0.43,0.64 c0.27,0.27,0.62,0.47,1.01,0.55C4.73,20.99,4.86,21,5,21h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3z M11,14.17l-1.41,1.42L6,12 l3.59-3.59L11,9.83L8.83,12L11,14.17z M12,4.25c-0.41,0-0.75-0.34-0.75-0.75S11.59,2.75,12,2.75s0.75,0.34,0.75,0.75 S12.41,4.25,12,4.25z M14.41,15.59L13,14.17L15.17,12L13,9.83l1.41-1.42L18,12L14.41,15.59z" /> + </g> + </Svg> + ), title: ({ className }) => ( <Svg className={className} viewBox="0 0 24 24"> <path d="M0 0h24v24H0V0z" fill="none" /> diff --git a/wax-prosemirror-schema/index.js b/wax-prosemirror-schema/index.js index 6e9de4854916088bdc7427bce55024fb193f5953..ebc8f12da6dae8574172f7fe41106c21f304829c 100644 --- a/wax-prosemirror-schema/index.js +++ b/wax-prosemirror-schema/index.js @@ -13,6 +13,7 @@ export { default as smallcapsMark } from './src/marks/smallcapsMark'; export { default as sourceMark } from './src/marks/sourceMark'; export { default as commentMark } from './src/marks/commentMark'; export { default as mathSelectMark } from './src/marks/mathSelectMark'; +export { default as highlightMark } from './src/marks/highlightMark'; /* LIST OF TRACK CHANGES MARKS */ diff --git a/wax-prosemirror-schema/src/marks/highlightMark.js b/wax-prosemirror-schema/src/marks/highlightMark.js new file mode 100644 index 0000000000000000000000000000000000000000..f45b5004486299f50ce5987b8a0cb0d2a45cb734 --- /dev/null +++ b/wax-prosemirror-schema/src/marks/highlightMark.js @@ -0,0 +1,42 @@ +// const highlight = { +// parseDOM: [ +// { tag: "highlight" }, +// { style: "color:red" }, +// ], +// toDOM: (hook, next) => { +// hook.value = [ +// "highlight", +// { +// style: "background-color:red" +// } +// ]; +// next(); +// } +// }; + + + const highlight = { + attrs: { + style: { default: null } + }, + inclusive: false, + parseDOM: [ + { + tag: "highlight", + getAttrs(hook, next) { + const style = hook.dom.getAttribute("style"); + Object.assign(hook, { + style: hook.dom.getAttribute("style"), + }); + next(); + } + } + ], + toDOM(hook, next) { + hook.value = ["highlight", hook.node.attrs, 0]; + next(); + } + }; + + export default highlight; + \ No newline at end of file diff --git a/wax-prosemirror-services/index.js b/wax-prosemirror-services/index.js index c41319ad44b9adad9e301931944f7fba38c98ca9..43d5477f24692adc3544bf2c07c503ebae2e8f4c 100644 --- a/wax-prosemirror-services/index.js +++ b/wax-prosemirror-services/index.js @@ -31,6 +31,7 @@ export { default as MathService } from './src/MathService/MathService'; export { default as FindAndReplaceService } from './src/FindAndReplaceService/FindAndReplaceService'; export { default as FullScreenService } from './src/FullScreenService/FullScreenService'; export { default as SpecialCharactersService } from './src/SpecialCharactersService/SpecialCharactersService'; +export { default as HighlightService } from './src/HighlightService/HightlightService'; /* ToolGroups @@ -50,3 +51,4 @@ export { default as BlockDropDownToolGroupService } from './src/WaxToolGroups/Bl export { default as TrackingAndEditingToolGroupService } from './src/WaxToolGroups/TrackingAndEditingToolGroupService/TrackingAndEditingToolGroupService'; export { default as FullScreenToolGroupService } from './src/WaxToolGroups/FullScreenToolGroupService/FullScreenToolGroupService'; export { default as SpecialCharactersToolGroupService } from './src/WaxToolGroups/SpecialCharactersToolGroupService/SpecialCharactersToolGroupService'; +export { default as TextHighlightToolGroupServices } from './src/WaxToolGroups/TextHighlightToolGroupService/TextHighlightToolGroupService'; \ No newline at end of file diff --git a/wax-prosemirror-services/src/HighlightService/HightlightService.js b/wax-prosemirror-services/src/HighlightService/HightlightService.js new file mode 100644 index 0000000000000000000000000000000000000000..f6963684330a7ac97d586131aa8b3c1f15c3a399 --- /dev/null +++ b/wax-prosemirror-services/src/HighlightService/HightlightService.js @@ -0,0 +1,19 @@ +import Service from "../Service"; +import { highlightMark } from "wax-prosemirror-schema"; +import TextHighlightTool from "./TextHighlightTool"; + +export default class HighlightService extends Service { + + register() { + this.container.bind('TextHighlightTool').to(TextHighlightTool); + const createMark = this.container.get('CreateMark'); + createMark( + { + highlight: highlightMark, + }, + { toWaxSchema: true }, + ); + } + +} + diff --git a/wax-prosemirror-services/src/HighlightService/TextHighlightTool.js b/wax-prosemirror-services/src/HighlightService/TextHighlightTool.js new file mode 100644 index 0000000000000000000000000000000000000000..496ee99ea99c3bee7d21b03ba6be6818b9f35b00 --- /dev/null +++ b/wax-prosemirror-services/src/HighlightService/TextHighlightTool.js @@ -0,0 +1,42 @@ +import { Commands } from 'wax-prosemirror-utilities'; + +import { injectable } from 'inversify'; +import { deleteSelection,toggleMark } from 'prosemirror-commands'; +import { TextHighlightingTool } from 'wax-prosemirror-components' +import { isEmpty } from 'lodash'; +import { render } from 'react-dom'; +import React from 'react'; +import Tools from '../lib/Tools'; +import { v4 as uuidv4 } from 'uuid'; + +@injectable() +class TextHighlightTool extends Tools { + + title ='Text Highlighter'; + icon ='highlight'; + name ='TextHighlightTool'; + + select = (state, activeViewId,activeView) => { + // return !activeView.state.selection.empty; + return window.getSelection().toString().trim().length == 0 ? false :true; + }; + + get run() { + return (state, dispatch,color) => { + const { + selection: { $from, $to }, + } = state; + color == "background-color: transparent;" ? dispatch(state.tr.removeMark($from.pos,$to.pos,state.schema.marks.highlight)) : dispatch(state.tr.addMark($from.pos,$to.pos,state.schema.marks.highlight.create({ style: color }))); + + }; + } + + renderTool(view) { + if (isEmpty(view)) return null; + return this._isDisplayed ? ( + <TextHighlightingTool key={uuidv4()} item={this.toJSON()} view={view} /> + ) : null; + } +} + +export default TextHighlightTool; \ No newline at end of file diff --git a/wax-prosemirror-services/src/WaxToolGroups/TextHighlightToolGroupService/HightlightToolGroup.js b/wax-prosemirror-services/src/WaxToolGroups/TextHighlightToolGroupService/HightlightToolGroup.js new file mode 100644 index 0000000000000000000000000000000000000000..44464c0012fd0470e1da5e66bf2b133a92e1f9bd --- /dev/null +++ b/wax-prosemirror-services/src/WaxToolGroups/TextHighlightToolGroupService/HightlightToolGroup.js @@ -0,0 +1,14 @@ +import { injectable, inject } from 'inversify'; +import ToolGroup from '../../lib/ToolGroup'; + +@injectable() +class HighlightToolGroup extends ToolGroup { + tools = []; + constructor(@inject('TextHighlightTool') texthighlight) { + + super(); + this.tools = [texthighlight]; + } +} + +export default HighlightToolGroup; \ No newline at end of file diff --git a/wax-prosemirror-services/src/WaxToolGroups/TextHighlightToolGroupService/TextHighlightToolGroupService.js b/wax-prosemirror-services/src/WaxToolGroups/TextHighlightToolGroupService/TextHighlightToolGroupService.js new file mode 100644 index 0000000000000000000000000000000000000000..b6f6ecceceb8d3a2627dfb5e18543787dbe39a17 --- /dev/null +++ b/wax-prosemirror-services/src/WaxToolGroups/TextHighlightToolGroupService/TextHighlightToolGroupService.js @@ -0,0 +1,11 @@ +import Service from '../../Service'; +import HighlightToolGroup from './HightlightToolGroup'; + +class TextHighlightToolGroupServices extends Service { + register(){ + + this.container.bind('HighlightToolGroup').to(HighlightToolGroup); + } +} + +export default TextHighlightToolGroupServices; \ No newline at end of file diff --git a/{ b/{ new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391