@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.
Files changed (142) hide show
  1. package/.env.example +7 -0
  2. package/__generated__/fragment-masking.ts +87 -0
  3. package/__generated__/gql.ts +238 -0
  4. package/__generated__/graphql.ts +3441 -0
  5. package/__generated__/index.ts +2 -0
  6. package/codegen.config.ts +24 -0
  7. package/eslint.config.mjs +43 -0
  8. package/i18n.js +28 -0
  9. package/package.json +5 -5
  10. package/project.json +54 -0
  11. package/src/components/DeltaSlateEditor.vue +74 -0
  12. package/src/components/icon/FontAwesomeIcon.vue +21 -0
  13. package/src/graphql/apiTokens/apiTokens.fragments.gql +8 -0
  14. package/src/graphql/assets/assets.fragments.gql +10 -0
  15. package/src/graphql/blueprints/blueprints.fragments.gql +52 -0
  16. package/src/graphql/buildingBlockFieldFulfillments/buildingBlockFieldFullfillment.fragements.gql +6 -0
  17. package/src/graphql/buildingBlockFields/buildingBlockField.fragments.gql +8 -0
  18. package/src/graphql/buildingBlocks/buildingBlocks.fragments.gql +11 -0
  19. package/src/graphql/channels/channels.fragments.gql +9 -0
  20. package/src/graphql/contentExperiences/allContentExperiences.query.gql +24 -0
  21. package/src/graphql/contentExperiences/contentExperience.query.gql +20 -0
  22. package/src/graphql/contentExperiences/contentExperiences.fragments.gql +14 -0
  23. package/src/graphql/contentFields/contentFields.fragments.gql +7 -0
  24. package/src/graphql/contentItems/allContentItems.query.gql +48 -0
  25. package/src/graphql/contentItems/contentItems.fragments.gql +11 -0
  26. package/src/graphql/contentTypes/allContentTypes.query.gql +26 -0
  27. package/src/graphql/contentTypes/contentTypes.fragments.gql +11 -0
  28. package/src/graphql/contentValidations/contentValidationRule.fragments.gql +34 -0
  29. package/src/graphql/contentValues/allContentValues.query.gql +41 -0
  30. package/src/graphql/contentValues/contentValues.fragments.gql +9 -0
  31. package/src/graphql/contentValues/createContentValue.mutation.gql +17 -0
  32. package/src/graphql/experienceComponents/experienceComponent.fragments.gql +13 -0
  33. package/src/graphql/fragments.gql +6 -0
  34. package/src/graphql/gridDefinition/gridDefinition.fragments.gql +5 -0
  35. package/src/graphql/gridPlacements/gridPlacement.fragments.gql +7 -0
  36. package/src/graphql/grids/grid.fragments.gql +7 -0
  37. package/src/graphql/invitations/invitations.fragments.gql +7 -0
  38. package/src/graphql/organizations/organizations.fragments.gql +13 -0
  39. package/src/graphql/pathParts/pathParts.fragments.gql +19 -0
  40. package/src/graphql/plateMaintainers/plateMaintainer.fragements.gql +10 -0
  41. package/src/graphql/roleAssignments/roleAssignment.fragments.gql +9 -0
  42. package/src/graphql/roles/roles.fragments.gql +7 -0
  43. package/src/graphql/subject/subject.fragments.gql +8 -0
  44. package/src/graphql/tags/tags.fragments.gql +17 -0
  45. package/src/graphql/themes/themes.fragments.gql +8 -0
  46. package/src/index.css +1 -0
  47. package/src/index.ts +21 -0
  48. package/src/locales/en.json +52 -0
  49. package/src/locales/nl.json +52 -0
  50. package/src/react/components/DeltaSlateEditor.tsx +243 -0
  51. package/src/react/components/DeltaSlateEditorConnector.tsx +50 -0
  52. package/src/react/components/Element.spec.tsx +244 -0
  53. package/src/react/components/Element.tsx +151 -0
  54. package/src/react/components/FontAwesomeIcon.tsx +17 -0
  55. package/src/react/components/Leaf.spec.tsx +61 -0
  56. package/src/react/components/Leaf.tsx +22 -0
  57. package/src/react/components/elements/CodeElement.tsx +16 -0
  58. package/src/react/components/elements/ContentValueElement.tsx +33 -0
  59. package/src/react/components/elements/LinkElement.tsx +44 -0
  60. package/src/react/components/inputs/SearchInput.tsx +22 -0
  61. package/src/react/components/inputs/TextInput.tsx +30 -0
  62. package/src/react/components/menus/ContentAndFormatMenu.tsx +272 -0
  63. package/src/react/components/menus/ContentLibraryMenu.tsx +48 -0
  64. package/src/react/components/menus/ReusableContentMenu.tsx +190 -0
  65. package/src/react/components/menus/content/ContentItemsMenu.tsx +215 -0
  66. package/src/react/components/menus/content/ContentTypesMenu.tsx +129 -0
  67. package/src/react/components/menus/content/partials/ContentFieldMenuItem.tsx +11 -0
  68. package/src/react/components/menus/content/partials/ContentValueMenuItem.tsx +58 -0
  69. package/src/react/components/menus/link/AnchorInput.tsx +123 -0
  70. package/src/react/components/menus/link/LinkInput.tsx +195 -0
  71. package/src/react/components/menus/link/LinkMenu.spec.tsx +145 -0
  72. package/src/react/components/menus/link/LinkMenu.tsx +289 -0
  73. package/src/react/components/menus/partials/MenuButton.tsx +52 -0
  74. package/src/react/components/menus/partials/MenuContainer.tsx +9 -0
  75. package/src/react/components/menus/partials/MenuHeader.tsx +11 -0
  76. package/src/react/components/toolbar/Toolbar.tsx +249 -0
  77. package/src/react/components/toolbar/ToolbarBlockButton.tsx +31 -0
  78. package/src/react/components/toolbar/ToolbarHeadingDropdownButton.tsx +76 -0
  79. package/src/react/components/toolbar/ToolbarLinkButton.tsx +33 -0
  80. package/src/react/components/toolbar/ToolbarMarkButton.tsx +25 -0
  81. package/src/react/components/toolbar/content/ContentExtractToolbarButton.tsx +68 -0
  82. package/src/react/components/toolbar/content/ContentLibraryToolbarButton.tsx +43 -0
  83. package/src/react/components/toolbar/content/ContentToolbar.tsx +37 -0
  84. package/src/react/components/toolbar/link/ToolbarDisplayLink.tsx +36 -0
  85. package/src/react/components/toolbar/link/UnlinkButton.tsx +25 -0
  86. package/src/react/config/hotkeys.ts +8 -0
  87. package/src/react/plugins/index.ts +59 -0
  88. package/src/react/store/editorSlice.ts +124 -0
  89. package/src/react/store/store.ts +12 -0
  90. package/src/react/types.ts +87 -0
  91. package/src/react/utils/decorator.ts +61 -0
  92. package/src/react/utils/index.ts +110 -0
  93. package/src/vue-shims.d.ts +5 -0
  94. package/tsconfig.json +26 -0
  95. package/tsconfig.lib.json +25 -0
  96. package/tsconfig.spec.json +22 -0
  97. package/vite.config.ts +67 -0
  98. package/components/DeltaSlateEditor.vue.d.ts +0 -26
  99. package/index.cjs +0 -381
  100. package/index.css +0 -1
  101. package/index.d.ts +0 -12
  102. package/index.js +0 -49254
  103. package/react/components/DeltaSlateEditor.d.ts +0 -7
  104. package/react/components/DeltaSlateEditorConnector.d.ts +0 -12
  105. package/react/components/Element.d.ts +0 -8
  106. package/react/components/FontAwesomeIcon.d.ts +0 -6
  107. package/react/components/Leaf.d.ts +0 -3
  108. package/react/components/elements/CodeElement.d.ts +0 -8
  109. package/react/components/elements/ContentValueElement.d.ts +0 -8
  110. package/react/components/elements/LinkElement.d.ts +0 -8
  111. package/react/components/inputs/SearchInput.d.ts +0 -5
  112. package/react/components/inputs/TextInput.d.ts +0 -7
  113. package/react/components/menus/ContentAndFormatMenu.d.ts +0 -10
  114. package/react/components/menus/ContentLibraryMenu.d.ts +0 -4
  115. package/react/components/menus/ReusableContentMenu.d.ts +0 -3
  116. package/react/components/menus/content/ContentItemsMenu.d.ts +0 -5
  117. package/react/components/menus/content/ContentTypesMenu.d.ts +0 -6
  118. package/react/components/menus/content/partials/ContentFieldMenuItem.d.ts +0 -6
  119. package/react/components/menus/content/partials/ContentValueMenuItem.d.ts +0 -7
  120. package/react/components/menus/link/AnchorInput.d.ts +0 -8
  121. package/react/components/menus/link/LinkInput.d.ts +0 -11
  122. package/react/components/menus/link/LinkMenu.d.ts +0 -18
  123. package/react/components/menus/partials/MenuButton.d.ts +0 -7
  124. package/react/components/menus/partials/MenuContainer.d.ts +0 -4
  125. package/react/components/menus/partials/MenuHeader.d.ts +0 -5
  126. package/react/components/toolbar/Toolbar.d.ts +0 -6
  127. package/react/components/toolbar/ToolbarBlockButton.d.ts +0 -12
  128. package/react/components/toolbar/ToolbarHeadingDropdownButton.d.ts +0 -2
  129. package/react/components/toolbar/ToolbarLinkButton.d.ts +0 -6
  130. package/react/components/toolbar/ToolbarMarkButton.d.ts +0 -6
  131. package/react/components/toolbar/content/ContentExtractToolbarButton.d.ts +0 -2
  132. package/react/components/toolbar/content/ContentLibraryToolbarButton.d.ts +0 -5
  133. package/react/components/toolbar/content/ContentToolbar.d.ts +0 -4
  134. package/react/components/toolbar/link/ToolbarDisplayLink.d.ts +0 -2
  135. package/react/components/toolbar/link/UnlinkButton.d.ts +0 -2
  136. package/react/config/hotkeys.d.ts +0 -2
  137. package/react/plugins/index.d.ts +0 -3
  138. package/react/store/editorSlice.d.ts +0 -169
  139. package/react/store/store.d.ts +0 -5
  140. package/react/types.d.ts +0 -65
  141. package/react/utils/decorator.d.ts +0 -15
  142. package/react/utils/index.d.ts +0 -17
@@ -0,0 +1,13 @@
1
+ fragment experienceComponentFragment on ExperienceComponent {
2
+ _id: prn
3
+ prn
4
+ stage
5
+ name
6
+ isDraft
7
+ preview {
8
+ ...assetFragment
9
+ }
10
+ isGlobal
11
+ createdAt
12
+ updatedAt
13
+ }
@@ -0,0 +1,6 @@
1
+ fragment pagination on PageInfo {
2
+ endCursor
3
+ hasNextPage
4
+ hasPreviousPage
5
+ startCursor
6
+ }
@@ -0,0 +1,5 @@
1
+ fragment gridDefinitionFragment on GridDefinition {
2
+ _id: prn
3
+ prn
4
+ maxRows
5
+ }
@@ -0,0 +1,7 @@
1
+ fragment gridPlacementFragment on GridPlacement {
2
+ _id: prn
3
+ prn,
4
+ row
5
+ updatedAt
6
+ createdAt
7
+ }
@@ -0,0 +1,7 @@
1
+ fragment gridFragment on Grid {
2
+ _id: prn
3
+ prn,
4
+ rows
5
+ updatedAt
6
+ createdAt
7
+ }
@@ -0,0 +1,7 @@
1
+ fragment invitationFragment on Invitation {
2
+ _id: prn
3
+ prn
4
+ userEmail
5
+ createdAt
6
+ updatedAt
7
+ }
@@ -0,0 +1,13 @@
1
+ fragment organizationFragment on Organization {
2
+ _id: prn
3
+ prn
4
+ name
5
+ slug
6
+ logo {
7
+ ...assetFragment
8
+ }
9
+ logoUrl
10
+ seedStatus
11
+ createdAt
12
+ updatedAt
13
+ }
@@ -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,10 @@
1
+ fragment plateMaintainerFragment on Subject {
2
+ _id: id
3
+ id
4
+ name {
5
+ first
6
+ last
7
+ }
8
+ language
9
+ email
10
+ }
@@ -0,0 +1,9 @@
1
+ fragment roleAssignmentFragment on RoleAssignment {
2
+ _id: prn
3
+ prn
4
+ subject {
5
+ id
6
+ }
7
+ createdAt
8
+ updatedAt
9
+ }
@@ -0,0 +1,7 @@
1
+ fragment roleFragment on Role {
2
+ _id: prn
3
+ prn
4
+ name
5
+ createdAt
6
+ updatedAt
7
+ }
@@ -0,0 +1,8 @@
1
+ fragment subjectFields on Subject {
2
+ _id: id
3
+ email
4
+ name {
5
+ first
6
+ last
7
+ }
8
+ }
@@ -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
+ }
@@ -0,0 +1,8 @@
1
+ fragment themeFragment on Theme {
2
+ _id: prn
3
+ prn
4
+ name
5
+ repositoryUrl
6
+ createdAt
7
+ updatedAt
8
+ }
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
+ }