@platecms/delta-smart-text 0.4.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +7 -0
- package/__generated__/fragment-masking.ts +87 -0
- package/__generated__/gql.ts +238 -0
- package/__generated__/graphql.ts +3441 -0
- package/__generated__/index.ts +2 -0
- package/codegen.config.ts +24 -0
- package/eslint.config.mjs +43 -0
- package/i18n.js +28 -0
- package/package.json +5 -5
- package/project.json +54 -0
- package/src/components/DeltaSlateEditor.vue +74 -0
- package/src/components/icon/FontAwesomeIcon.vue +21 -0
- package/src/graphql/apiTokens/apiTokens.fragments.gql +8 -0
- package/src/graphql/assets/assets.fragments.gql +10 -0
- package/src/graphql/blueprints/blueprints.fragments.gql +52 -0
- package/src/graphql/buildingBlockFieldFulfillments/buildingBlockFieldFullfillment.fragements.gql +6 -0
- package/src/graphql/buildingBlockFields/buildingBlockField.fragments.gql +8 -0
- package/src/graphql/buildingBlocks/buildingBlocks.fragments.gql +11 -0
- package/src/graphql/channels/channels.fragments.gql +9 -0
- package/src/graphql/contentExperiences/allContentExperiences.query.gql +24 -0
- package/src/graphql/contentExperiences/contentExperience.query.gql +20 -0
- package/src/graphql/contentExperiences/contentExperiences.fragments.gql +14 -0
- package/src/graphql/contentFields/contentFields.fragments.gql +7 -0
- package/src/graphql/contentItems/allContentItems.query.gql +48 -0
- package/src/graphql/contentItems/contentItems.fragments.gql +11 -0
- package/src/graphql/contentTypes/allContentTypes.query.gql +26 -0
- package/src/graphql/contentTypes/contentTypes.fragments.gql +11 -0
- package/src/graphql/contentValidations/contentValidationRule.fragments.gql +34 -0
- package/src/graphql/contentValues/allContentValues.query.gql +41 -0
- package/src/graphql/contentValues/contentValues.fragments.gql +9 -0
- package/src/graphql/contentValues/createContentValue.mutation.gql +17 -0
- package/src/graphql/experienceComponents/experienceComponent.fragments.gql +13 -0
- package/src/graphql/fragments.gql +6 -0
- package/src/graphql/gridDefinition/gridDefinition.fragments.gql +5 -0
- package/src/graphql/gridPlacements/gridPlacement.fragments.gql +7 -0
- package/src/graphql/grids/grid.fragments.gql +7 -0
- package/src/graphql/invitations/invitations.fragments.gql +7 -0
- package/src/graphql/organizations/organizations.fragments.gql +13 -0
- package/src/graphql/pathParts/pathParts.fragments.gql +19 -0
- package/src/graphql/plateMaintainers/plateMaintainer.fragements.gql +10 -0
- package/src/graphql/roleAssignments/roleAssignment.fragments.gql +9 -0
- package/src/graphql/roles/roles.fragments.gql +7 -0
- package/src/graphql/subject/subject.fragments.gql +8 -0
- package/src/graphql/tags/tags.fragments.gql +17 -0
- package/src/graphql/themes/themes.fragments.gql +8 -0
- package/src/index.css +1 -0
- package/src/index.ts +21 -0
- package/src/locales/en.json +52 -0
- package/src/locales/nl.json +52 -0
- package/src/react/components/DeltaSlateEditor.tsx +243 -0
- package/src/react/components/DeltaSlateEditorConnector.tsx +50 -0
- package/src/react/components/Element.spec.tsx +244 -0
- package/src/react/components/Element.tsx +151 -0
- package/src/react/components/FontAwesomeIcon.tsx +17 -0
- package/src/react/components/Leaf.spec.tsx +61 -0
- package/src/react/components/Leaf.tsx +22 -0
- package/src/react/components/elements/CodeElement.tsx +16 -0
- package/src/react/components/elements/ContentValueElement.tsx +33 -0
- package/src/react/components/elements/LinkElement.tsx +44 -0
- package/src/react/components/inputs/SearchInput.tsx +22 -0
- package/src/react/components/inputs/TextInput.tsx +30 -0
- package/src/react/components/menus/ContentAndFormatMenu.tsx +272 -0
- package/src/react/components/menus/ContentLibraryMenu.tsx +48 -0
- package/src/react/components/menus/ReusableContentMenu.tsx +190 -0
- package/src/react/components/menus/content/ContentItemsMenu.tsx +215 -0
- package/src/react/components/menus/content/ContentTypesMenu.tsx +129 -0
- package/src/react/components/menus/content/partials/ContentFieldMenuItem.tsx +11 -0
- package/src/react/components/menus/content/partials/ContentValueMenuItem.tsx +58 -0
- package/src/react/components/menus/link/AnchorInput.tsx +123 -0
- package/src/react/components/menus/link/LinkInput.tsx +195 -0
- package/src/react/components/menus/link/LinkMenu.spec.tsx +145 -0
- package/src/react/components/menus/link/LinkMenu.tsx +289 -0
- package/src/react/components/menus/partials/MenuButton.tsx +52 -0
- package/src/react/components/menus/partials/MenuContainer.tsx +9 -0
- package/src/react/components/menus/partials/MenuHeader.tsx +11 -0
- package/src/react/components/toolbar/Toolbar.tsx +249 -0
- package/src/react/components/toolbar/ToolbarBlockButton.tsx +31 -0
- package/src/react/components/toolbar/ToolbarHeadingDropdownButton.tsx +76 -0
- package/src/react/components/toolbar/ToolbarLinkButton.tsx +33 -0
- package/src/react/components/toolbar/ToolbarMarkButton.tsx +25 -0
- package/src/react/components/toolbar/content/ContentExtractToolbarButton.tsx +68 -0
- package/src/react/components/toolbar/content/ContentLibraryToolbarButton.tsx +43 -0
- package/src/react/components/toolbar/content/ContentToolbar.tsx +37 -0
- package/src/react/components/toolbar/link/ToolbarDisplayLink.tsx +36 -0
- package/src/react/components/toolbar/link/UnlinkButton.tsx +25 -0
- package/src/react/config/hotkeys.ts +8 -0
- package/src/react/plugins/index.ts +59 -0
- package/src/react/store/editorSlice.ts +124 -0
- package/src/react/store/store.ts +12 -0
- package/src/react/types.ts +87 -0
- package/src/react/utils/decorator.ts +61 -0
- package/src/react/utils/index.ts +110 -0
- package/src/vue-shims.d.ts +5 -0
- package/tsconfig.json +26 -0
- package/tsconfig.lib.json +25 -0
- package/tsconfig.spec.json +22 -0
- package/vite.config.ts +67 -0
- package/components/DeltaSlateEditor.vue.d.ts +0 -26
- package/index.cjs +0 -381
- package/index.css +0 -1
- package/index.d.ts +0 -12
- package/index.js +0 -49254
- package/react/components/DeltaSlateEditor.d.ts +0 -7
- package/react/components/DeltaSlateEditorConnector.d.ts +0 -12
- package/react/components/Element.d.ts +0 -8
- package/react/components/FontAwesomeIcon.d.ts +0 -6
- package/react/components/Leaf.d.ts +0 -3
- package/react/components/elements/CodeElement.d.ts +0 -8
- package/react/components/elements/ContentValueElement.d.ts +0 -8
- package/react/components/elements/LinkElement.d.ts +0 -8
- package/react/components/inputs/SearchInput.d.ts +0 -5
- package/react/components/inputs/TextInput.d.ts +0 -7
- package/react/components/menus/ContentAndFormatMenu.d.ts +0 -10
- package/react/components/menus/ContentLibraryMenu.d.ts +0 -4
- package/react/components/menus/ReusableContentMenu.d.ts +0 -3
- package/react/components/menus/content/ContentItemsMenu.d.ts +0 -5
- package/react/components/menus/content/ContentTypesMenu.d.ts +0 -6
- package/react/components/menus/content/partials/ContentFieldMenuItem.d.ts +0 -6
- package/react/components/menus/content/partials/ContentValueMenuItem.d.ts +0 -7
- package/react/components/menus/link/AnchorInput.d.ts +0 -8
- package/react/components/menus/link/LinkInput.d.ts +0 -11
- package/react/components/menus/link/LinkMenu.d.ts +0 -18
- package/react/components/menus/partials/MenuButton.d.ts +0 -7
- package/react/components/menus/partials/MenuContainer.d.ts +0 -4
- package/react/components/menus/partials/MenuHeader.d.ts +0 -5
- package/react/components/toolbar/Toolbar.d.ts +0 -6
- package/react/components/toolbar/ToolbarBlockButton.d.ts +0 -12
- package/react/components/toolbar/ToolbarHeadingDropdownButton.d.ts +0 -2
- package/react/components/toolbar/ToolbarLinkButton.d.ts +0 -6
- package/react/components/toolbar/ToolbarMarkButton.d.ts +0 -6
- package/react/components/toolbar/content/ContentExtractToolbarButton.d.ts +0 -2
- package/react/components/toolbar/content/ContentLibraryToolbarButton.d.ts +0 -5
- package/react/components/toolbar/content/ContentToolbar.d.ts +0 -4
- package/react/components/toolbar/link/ToolbarDisplayLink.d.ts +0 -2
- package/react/components/toolbar/link/UnlinkButton.d.ts +0 -2
- package/react/config/hotkeys.d.ts +0 -2
- package/react/plugins/index.d.ts +0 -3
- package/react/store/editorSlice.d.ts +0 -169
- package/react/store/store.d.ts +0 -5
- package/react/types.d.ts +0 -65
- package/react/utils/decorator.d.ts +0 -15
- package/react/utils/index.d.ts +0 -17
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
fragment pathPartFragment on PathPart {
|
|
2
|
+
_id: prn
|
|
3
|
+
prn
|
|
4
|
+
slug
|
|
5
|
+
path
|
|
6
|
+
createdAt
|
|
7
|
+
updatedAt
|
|
8
|
+
channel {
|
|
9
|
+
prn
|
|
10
|
+
}
|
|
11
|
+
hasContentExperience
|
|
12
|
+
amountOfChildren
|
|
13
|
+
amountOfChildrenWithContentExperiences
|
|
14
|
+
amountOfDescendants
|
|
15
|
+
amountOfDescendantsWithContentExperiences
|
|
16
|
+
contentExperience {
|
|
17
|
+
prn
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
fragment tagFragment on Tag {
|
|
2
|
+
_id: prn
|
|
3
|
+
prn
|
|
4
|
+
name
|
|
5
|
+
path
|
|
6
|
+
color
|
|
7
|
+
visibility
|
|
8
|
+
forceVisibilityOnDescendants
|
|
9
|
+
stage
|
|
10
|
+
amountOfChildren
|
|
11
|
+
amountOfContentExperiences
|
|
12
|
+
amountOfContentItems
|
|
13
|
+
hasForcedVisibility
|
|
14
|
+
nestingLevel
|
|
15
|
+
createdAt
|
|
16
|
+
updatedAt
|
|
17
|
+
}
|
package/src/index.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import "tailwindcss";
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { App } from "vue";
|
|
2
|
+
import DeltaSlateEditorComponent from "./components/DeltaSlateEditor.vue";
|
|
3
|
+
import "./index.css";
|
|
4
|
+
import "../i18n";
|
|
5
|
+
|
|
6
|
+
//Editor types
|
|
7
|
+
export type * from "./react/types";
|
|
8
|
+
|
|
9
|
+
export type DeltaSlateEditor = typeof DeltaSlateEditorComponent;
|
|
10
|
+
|
|
11
|
+
declare module "@vue/runtime-core" {
|
|
12
|
+
export interface GlobalComponents {
|
|
13
|
+
DeltaSlateEditor: DeltaSlateEditor;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const smartText = {
|
|
18
|
+
install(app: App): void {
|
|
19
|
+
app.component("DeltaSlateEditor", DeltaSlateEditorComponent);
|
|
20
|
+
},
|
|
21
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"general": {
|
|
3
|
+
"word": {
|
|
4
|
+
"cancel": "Cancel",
|
|
5
|
+
"save": "Save",
|
|
6
|
+
"load_more": "Load more",
|
|
7
|
+
"back_to": "Back to"
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
"smart_text": {
|
|
11
|
+
"placeholder": "Enter some text or use the / command",
|
|
12
|
+
"toolbar": {
|
|
13
|
+
"link": {
|
|
14
|
+
"title": "Link",
|
|
15
|
+
"enter_link": "Enter a link",
|
|
16
|
+
"new_tab": "Open link in new tab",
|
|
17
|
+
"add_link": "Add link",
|
|
18
|
+
"select_section": "Select a section",
|
|
19
|
+
"select_section_description": "Select a content block to link to a specific section",
|
|
20
|
+
"no_results_found": "No results found",
|
|
21
|
+
"enter_display_text": "Enter a display text",
|
|
22
|
+
"display_text": "Display text",
|
|
23
|
+
"row": "Row",
|
|
24
|
+
"remove_anchor": "Remove anchor"
|
|
25
|
+
},
|
|
26
|
+
"content": {
|
|
27
|
+
"smart_content": "Smart content",
|
|
28
|
+
"content_library": "Content library",
|
|
29
|
+
"content_types": "Content types",
|
|
30
|
+
"no_content_types_found": "No content types found",
|
|
31
|
+
"search_content_types": "Search content types",
|
|
32
|
+
"reusable_content": "Reusable content",
|
|
33
|
+
"no_reusable_content_found": "No reusable content found",
|
|
34
|
+
"content_extract": "Save the selected text as reusable content for easy reuse across your site."
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"content_and_format": {
|
|
38
|
+
"paragraph": "Paragraph",
|
|
39
|
+
"heading": "Heading",
|
|
40
|
+
"bulleted_list": "Bulleted list",
|
|
41
|
+
"numbered_list": "Numbered list",
|
|
42
|
+
"quote": "Quote",
|
|
43
|
+
"code": "Code",
|
|
44
|
+
"image": "Image",
|
|
45
|
+
"video": "Video",
|
|
46
|
+
"audio": "Audio",
|
|
47
|
+
"link": "Link",
|
|
48
|
+
"format": "Format",
|
|
49
|
+
"content": "Content"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"general": {
|
|
3
|
+
"word": {
|
|
4
|
+
"cancel": "Annuleren",
|
|
5
|
+
"save": "Opslaan",
|
|
6
|
+
"load_more": "Meer laden",
|
|
7
|
+
"back_to": "Terug naar"
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
"smart_text": {
|
|
11
|
+
"placeholder": "Voer tekst in of gebruik de / commando",
|
|
12
|
+
"toolbar": {
|
|
13
|
+
"link": {
|
|
14
|
+
"title": "Link",
|
|
15
|
+
"enter_link": "Voer een link in",
|
|
16
|
+
"new_tab": "Open link in nieuw tabblad",
|
|
17
|
+
"add_link": "Link toevoegen",
|
|
18
|
+
"select_section": "Selecteer een sectie",
|
|
19
|
+
"select_section_description": "Selecteer een content blok om naar een specifieke sectie te linken",
|
|
20
|
+
"no_results_found": "Geen resultaten gevonden",
|
|
21
|
+
"enter_display_text": "Voer een weergavenaam in",
|
|
22
|
+
"display_text": "Weergavenaam",
|
|
23
|
+
"row": "Rij",
|
|
24
|
+
"remove_anchor": "Verwijder anchor"
|
|
25
|
+
},
|
|
26
|
+
"content_library": {
|
|
27
|
+
"smart_content": "Smart content",
|
|
28
|
+
"content_library": "Content library",
|
|
29
|
+
"content_types": "Content types",
|
|
30
|
+
"no_content_types_found": "Geen content types gevonden",
|
|
31
|
+
"search_content_types": "Zoek content types",
|
|
32
|
+
"reusable_content": "Hergebruikbare content",
|
|
33
|
+
"no_reusable_content_found": "Geen hergebruikbare content gevonden",
|
|
34
|
+
"content_extract": "Sla de geselecteerde tekst op als hergebruikbare content voor eenvoudige hergebruik op je site."
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"content_and_format": {
|
|
38
|
+
"paragraph": "Paragraaf",
|
|
39
|
+
"heading": "Kop",
|
|
40
|
+
"bulleted_list": "Lijst met opsommingtekens",
|
|
41
|
+
"numbered_list": "Genummerde lijst",
|
|
42
|
+
"quote": "Citaat",
|
|
43
|
+
"code": "Code",
|
|
44
|
+
"image": "Afbeelding",
|
|
45
|
+
"video": "Video",
|
|
46
|
+
"audio": "Audio",
|
|
47
|
+
"link": "Link",
|
|
48
|
+
"format": "Format",
|
|
49
|
+
"content": "Content"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import React, { useCallback, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { BaseRange, Descendant, Editor, Range, Transforms, createEditor } from "slate";
|
|
3
|
+
import {
|
|
4
|
+
Editable,
|
|
5
|
+
ReactEditor,
|
|
6
|
+
RenderElementProps,
|
|
7
|
+
RenderLeafProps,
|
|
8
|
+
RenderPlaceholderProps,
|
|
9
|
+
Slate,
|
|
10
|
+
withReact,
|
|
11
|
+
} from "slate-react";
|
|
12
|
+
import { useDispatch } from "react-redux";
|
|
13
|
+
import { selectById, setState } from "../store/editorSlice";
|
|
14
|
+
import { Toolbar } from "./toolbar/Toolbar";
|
|
15
|
+
import { Leaf } from "./Leaf";
|
|
16
|
+
import { Element } from "./Element";
|
|
17
|
+
import { store } from "../store/store";
|
|
18
|
+
import isHotkey from "is-hotkey";
|
|
19
|
+
import { toggleMark } from "../utils";
|
|
20
|
+
import { HOTKEYS } from "../config/hotkeys";
|
|
21
|
+
import { decorate, withContentValues } from "../plugins";
|
|
22
|
+
import { ContentAndFormatMenu } from "./menus/ContentAndFormatMenu";
|
|
23
|
+
import "prismjs/components/prism-markdown";
|
|
24
|
+
import watch from "redux-watch";
|
|
25
|
+
import { LinkMenu } from "./menus/link/LinkMenu";
|
|
26
|
+
import { HistoryEditor, withHistory } from "slate-history";
|
|
27
|
+
import { useTranslation } from "react-i18next";
|
|
28
|
+
|
|
29
|
+
export function DeltaSlateEditor({
|
|
30
|
+
uuid,
|
|
31
|
+
context,
|
|
32
|
+
isDisabled,
|
|
33
|
+
}: {
|
|
34
|
+
uuid: string;
|
|
35
|
+
context: HTMLElement | null;
|
|
36
|
+
isDisabled: boolean;
|
|
37
|
+
}): React.ReactElement {
|
|
38
|
+
|
|
39
|
+
const [initialValue] = useState<Descendant[]>(selectById(store.getState(), uuid)?.initialValue ?? []);
|
|
40
|
+
const dispatch = useDispatch();
|
|
41
|
+
const editor = useMemo<Editor & ReactEditor>(() => withContentValues(withReact(withHistory(createEditor()))), []);
|
|
42
|
+
const [lastKey, setLastKey] = useState<string | undefined>();
|
|
43
|
+
|
|
44
|
+
const renderLeaf = useCallback((props: RenderLeafProps) => <Leaf {...props} />, []);
|
|
45
|
+
|
|
46
|
+
const renderElement = useCallback((props: RenderElementProps) => <Element {...props} lastKey={lastKey} />, [lastKey]);
|
|
47
|
+
|
|
48
|
+
const renderPlaceholder = useCallback(
|
|
49
|
+
(props: RenderPlaceholderProps) => (
|
|
50
|
+
<div {...props.attributes} className={"py-2"}>
|
|
51
|
+
{props.children}
|
|
52
|
+
</div>
|
|
53
|
+
),
|
|
54
|
+
[],
|
|
55
|
+
);
|
|
56
|
+
const [searchInContentAndFormatMenuValue, setSearchInContentAndFormatMenuValue] = useState<string | null>(null);
|
|
57
|
+
|
|
58
|
+
function updateShowLinkMenu(): void {
|
|
59
|
+
setShowLinkMenu(true);
|
|
60
|
+
isDisabled = true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const [showLinkMenu, setShowLinkMenu] = useState(false);
|
|
64
|
+
|
|
65
|
+
const { t: translate } = useTranslation();
|
|
66
|
+
|
|
67
|
+
const initialValueWatcher = watch(() => selectById(store.getState(), uuid).initialValue);
|
|
68
|
+
|
|
69
|
+
store.subscribe(
|
|
70
|
+
initialValueWatcher((newValue: Descendant[]) => {
|
|
71
|
+
/*
|
|
72
|
+
* This is a very cumbersomne way to do this and should and could probably be improved by using the correct Editor/Transforms methods.
|
|
73
|
+
* For now, this is the only way due to time constraints. It simply selects the entire context of editor and removes and inserts the new nodes
|
|
74
|
+
* TODO: improve and refactor
|
|
75
|
+
**/
|
|
76
|
+
Transforms.select(editor, []);
|
|
77
|
+
Transforms.removeNodes(editor);
|
|
78
|
+
Transforms.insertNodes(editor, newValue);
|
|
79
|
+
}),
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
function onSlateChange(value: Descendant[]): void {
|
|
83
|
+
handleAstChange(value);
|
|
84
|
+
dispatch(setState({ uuid, state: { target: getInsertCommandSelectionTarget() } }));
|
|
85
|
+
|
|
86
|
+
const { selection } = editor;
|
|
87
|
+
if (selection && Range.isCollapsed(selection)) {
|
|
88
|
+
const [start] = Range.edges(selection);
|
|
89
|
+
const wordBefore = Editor.before(editor, start, { unit: "word" });
|
|
90
|
+
const before = wordBefore && Editor.before(editor, wordBefore);
|
|
91
|
+
const beforeRange = before && Editor.range(editor, before, start);
|
|
92
|
+
const beforeText = beforeRange && Editor.string(editor, beforeRange);
|
|
93
|
+
const beforeMatch = beforeText?.match(/^\/(\w+)$/u);
|
|
94
|
+
const newSearchValue = beforeMatch ? beforeMatch[1] : null;
|
|
95
|
+
setSearchInContentAndFormatMenuValue(newSearchValue);
|
|
96
|
+
} else {
|
|
97
|
+
setSearchInContentAndFormatMenuValue(null);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function handleAstChange(value: Descendant[]): void {
|
|
102
|
+
const isAstChange = editor.operations.some((operation) => operation.type !== "set_selection");
|
|
103
|
+
if (isAstChange) {
|
|
104
|
+
dispatch(setState({ uuid, state: { value } }));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Returns undefined, /[any string without whitespace] or just a /, depending on where the cursor (|) is
|
|
109
|
+
// located.
|
|
110
|
+
// Return undefined if there is a selection (range).
|
|
111
|
+
function getInsertCommandSelectionTarget(): BaseRange | undefined {
|
|
112
|
+
const { selection } = editor;
|
|
113
|
+
|
|
114
|
+
//Check if the user has not selected range
|
|
115
|
+
if (!selection || !Range.isCollapsed(selection)) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const [selectionStart] = Range.edges(selection);
|
|
120
|
+
|
|
121
|
+
const charBefore = Editor.before(editor, selectionStart, { unit: "character" });
|
|
122
|
+
|
|
123
|
+
const realCharBefore =
|
|
124
|
+
charBefore &&
|
|
125
|
+
Editor.string(editor, Editor.range(editor, charBefore, { ...charBefore, offset: charBefore.offset + 1 }));
|
|
126
|
+
|
|
127
|
+
// Edge case: |/ then return undefined
|
|
128
|
+
// Edge case: | is at the beginning of the line, then return undefined
|
|
129
|
+
if (realCharBefore === " ") {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Check if the character before the selection start is a '/'
|
|
134
|
+
// Edge case: /| then return /
|
|
135
|
+
// Edge case: / | then return undefined
|
|
136
|
+
if (charBefore && realCharBefore === "/") {
|
|
137
|
+
const wordAfterCharBefore = Editor.after(editor, charBefore, { unit: "word" });
|
|
138
|
+
|
|
139
|
+
//Check if the char after the '/' is a space
|
|
140
|
+
if (
|
|
141
|
+
wordAfterCharBefore &&
|
|
142
|
+
Editor.string(editor, Editor.range(editor, charBefore, wordAfterCharBefore)).includes(" ")
|
|
143
|
+
) {
|
|
144
|
+
return Editor.range(editor, charBefore, { ...charBefore, offset: charBefore.offset + 1 });
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return Editor.range(editor, charBefore, wordAfterCharBefore);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Check if the word before the selection start is a command
|
|
151
|
+
// Edge case: /jun|gle then return /jungle
|
|
152
|
+
// Edge case: / jun|gle then return undefined
|
|
153
|
+
const wordBefore = Editor.before(editor, selectionStart, { unit: "word" });
|
|
154
|
+
|
|
155
|
+
if (!wordBefore) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const charBeforeWordBefore = Editor.before(editor, wordBefore, { unit: "character" });
|
|
160
|
+
|
|
161
|
+
if (charBeforeWordBefore && Editor.string(editor, Editor.range(editor, charBeforeWordBefore, wordBefore)) === "/") {
|
|
162
|
+
return Editor.range(editor, charBeforeWordBefore, Editor.after(editor, wordBefore, { unit: "word" }));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const contentAndFormatMenuRef = useRef<{
|
|
167
|
+
escapeContentAndFormatMenu: () => void;
|
|
168
|
+
} | null>(null);
|
|
169
|
+
|
|
170
|
+
function onKeyDown(event: React.KeyboardEvent): void {
|
|
171
|
+
setLastKey(event.key);
|
|
172
|
+
for (const hotkey in HOTKEYS) {
|
|
173
|
+
if (isHotkey(hotkey, event)) {
|
|
174
|
+
event.preventDefault();
|
|
175
|
+
const mark = HOTKEYS[hotkey];
|
|
176
|
+
toggleMark(editor, mark);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const { selection } = editor;
|
|
180
|
+
if (selection && Range.isCollapsed(selection)) {
|
|
181
|
+
const { nativeEvent } = event;
|
|
182
|
+
|
|
183
|
+
if (isHotkey("left", nativeEvent)) {
|
|
184
|
+
event.preventDefault();
|
|
185
|
+
Transforms.move(editor, { unit: "offset", reverse: true });
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (isHotkey("right", nativeEvent)) {
|
|
189
|
+
event.preventDefault();
|
|
190
|
+
Transforms.move(editor, { unit: "offset" });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (isHotkey("escape", event)) {
|
|
194
|
+
contentAndFormatMenuRef.current?.escapeContentAndFormatMenu();
|
|
195
|
+
}
|
|
196
|
+
if ((event.ctrlKey || event.metaKey) && event.key === "z") {
|
|
197
|
+
event.preventDefault();
|
|
198
|
+
|
|
199
|
+
if (event.shiftKey) {
|
|
200
|
+
HistoryEditor.redo(editor as unknown as HistoryEditor);
|
|
201
|
+
} else {
|
|
202
|
+
HistoryEditor.undo(editor as unknown as HistoryEditor);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return (
|
|
208
|
+
<div className={"relative"}>
|
|
209
|
+
<Slate editor={editor} initialValue={initialValue} onChange={onSlateChange}>
|
|
210
|
+
<Toolbar uuid={uuid} context={context} />
|
|
211
|
+
<Editable
|
|
212
|
+
placeholder={translate("smart_text.placeholder")}
|
|
213
|
+
decorate={useCallback(decorate, [])}
|
|
214
|
+
renderLeaf={renderLeaf}
|
|
215
|
+
renderElement={renderElement}
|
|
216
|
+
renderPlaceholder={renderPlaceholder}
|
|
217
|
+
className={"outline-none py-2 max-h-32 overflow-y-auto overflow-x-hidden"}
|
|
218
|
+
onKeyDown={onKeyDown}
|
|
219
|
+
autoFocus={!isDisabled}
|
|
220
|
+
readOnly={isDisabled}
|
|
221
|
+
/>
|
|
222
|
+
{!isDisabled && (
|
|
223
|
+
<ContentAndFormatMenu
|
|
224
|
+
uuid={uuid}
|
|
225
|
+
ref={contentAndFormatMenuRef}
|
|
226
|
+
openLinkMenu={() => updateShowLinkMenu()}
|
|
227
|
+
linkMenuIsOpen={showLinkMenu}
|
|
228
|
+
searchValue={searchInContentAndFormatMenuValue}
|
|
229
|
+
/>
|
|
230
|
+
)}
|
|
231
|
+
<LinkMenu
|
|
232
|
+
menuClass={"top-full -translate-y-3"}
|
|
233
|
+
buttonText={translate("smart_text.toolbar.link.add_link")}
|
|
234
|
+
showLinkTextInput={true}
|
|
235
|
+
onClose={() => setShowLinkMenu(false)}
|
|
236
|
+
showMenu={showLinkMenu}
|
|
237
|
+
/>
|
|
238
|
+
</Slate>
|
|
239
|
+
</div>
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export default DeltaSlateEditor;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// ./scr/components/DeltaSlateEditorConnector.tsx
|
|
2
|
+
import Editor from "./DeltaSlateEditor";
|
|
3
|
+
import { Root, createRoot } from "react-dom/client";
|
|
4
|
+
import { Provider } from "react-redux";
|
|
5
|
+
import { store } from "../store/store";
|
|
6
|
+
import { setOrganization, setState } from "../store/editorSlice";
|
|
7
|
+
import { ApolloClient, ApolloProvider, NormalizedCacheObject } from "@apollo/client";
|
|
8
|
+
import { Organization } from "../../../__generated__/graphql";
|
|
9
|
+
import "../../../i18n";
|
|
10
|
+
|
|
11
|
+
export class DeltaSlateEditorConnector {
|
|
12
|
+
private readonly root: Root;
|
|
13
|
+
private readonly uuid: string;
|
|
14
|
+
private readonly context: HTMLElement | null;
|
|
15
|
+
private readonly client: ApolloClient<NormalizedCacheObject>;
|
|
16
|
+
private readonly organization: Organization | undefined;
|
|
17
|
+
private readonly isDisabled?: boolean;
|
|
18
|
+
|
|
19
|
+
public constructor(
|
|
20
|
+
targetEl: HTMLElement,
|
|
21
|
+
uuid: string,
|
|
22
|
+
client: ApolloClient<NormalizedCacheObject>,
|
|
23
|
+
context: HTMLElement | null,
|
|
24
|
+
organization: Organization | undefined,
|
|
25
|
+
isDisabled?: boolean,
|
|
26
|
+
) {
|
|
27
|
+
this.uuid = uuid;
|
|
28
|
+
this.context = context;
|
|
29
|
+
this.client = client;
|
|
30
|
+
this.organization = organization;
|
|
31
|
+
this.isDisabled = isDisabled;
|
|
32
|
+
|
|
33
|
+
store.dispatch(setState({ uuid }));
|
|
34
|
+
store.dispatch(setOrganization({ organization }));
|
|
35
|
+
this.root = createRoot(targetEl);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
public render(): void {
|
|
40
|
+
this.root.render(
|
|
41
|
+
<div>
|
|
42
|
+
<ApolloProvider client={this.client}>
|
|
43
|
+
<Provider store={store}>
|
|
44
|
+
<Editor uuid={this.uuid} context={this.context} isDisabled={this.isDisabled ?? false} />
|
|
45
|
+
</Provider>
|
|
46
|
+
</ApolloProvider>
|
|
47
|
+
</div>,
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}
|