@platecms/delta-smart-text 0.13.0 → 1.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platecms/delta-smart-text",
3
- "version": "0.13.0",
3
+ "version": "1.2.0",
4
4
  "description": "Provides a component to display smart text by using slate.",
5
5
  "license": "UNLICENSED",
6
6
  "publishConfig": {
@@ -23,11 +23,11 @@
23
23
  "./index.css": "./index.css"
24
24
  },
25
25
  "dependencies": {
26
- "@platecms/delta-cast": "0.13.0",
27
- "@platecms/delta-plate-resource-notation": "0.13.0",
26
+ "@platecms/delta-cast": "1.2.0",
27
+ "@platecms/delta-plate-resource-notation": "1.2.0",
28
28
  "@graphql-codegen/cli": "5.0.7",
29
29
  "@graphql-typed-document-node/core": "3.2.0",
30
- "@platecms/delta-cast-util-to-plaintext": "0.13.0",
30
+ "@platecms/delta-cast-util-to-plaintext": "1.2.0",
31
31
  "@reduxjs/toolkit": "2.8.2",
32
32
  "class-transformer": "0.5.1",
33
33
  "graphql": "16.11.0",
@@ -8,16 +8,19 @@ import {
8
8
  } from "../react/store/editorSlice";
9
9
  import { store } from "../react/store/store";
10
10
  import { setState } from "../react/store/editorSlice";
11
- import {Descendant} from "slate";
12
11
  import watch from "redux-watch";
13
12
  import { ApolloClient, NormalizedCacheObject } from "@apollo/client";
14
13
  import { Organization } from "../../__generated__/graphql";
14
+ import { Root } from "@platecms/delta-cast";
15
+ import { toSlate } from "@platecms/delta-cast-util-to-slate";
16
+ import { fromSlate } from "@platecms/delta-cast-util-from-slate";
15
17
 
16
18
  let connector: DeltaSlateEditorConnector;
17
19
 
18
20
  const props = withDefaults(defineProps<{
19
21
  uuid?: string
20
- initialValue?: Descendant[]
22
+ current?: Root
23
+ initial?: Root
21
24
  context: HTMLElement | null
22
25
  client: ApolloClient<NormalizedCacheObject>
23
26
  organization: Organization | undefined
@@ -32,43 +35,51 @@ const props = withDefaults(defineProps<{
32
35
  ],
33
36
  })
34
37
 
35
- const tree = defineModel('tree')
36
-
37
- defineExpose({
38
- setValue: (value: Descendant[]) => {
39
- store.dispatch(setState({ uuid: props.uuid, state: { value, initialValue: value } }))
40
- },
41
- setInitialValue: (value: Descendant[]) => {
42
- store.dispatch(setInitialValue({ uuid: props.uuid, initialValue: value }))
43
- }
44
- })
38
+ const emit = defineEmits<{
39
+ (e: 'update:current', value: Root): void
40
+ }>()
45
41
 
46
42
  function mountReduxWatchers() {
47
43
  const treeWatcher = watch(() => selectById(store.getState(), props.uuid).value)
48
44
 
49
45
  store.subscribe(treeWatcher((newValue) => {
50
- tree.value = newValue
46
+ emit('update:current', fromSlate(newValue))
51
47
  }))
52
48
  }
53
49
 
50
+ function updateTree(value: Root) {
51
+ store.dispatch(setInitialValue({ uuid: props.uuid, initialValue: toSlate(valueOrDefault(value)) }))
52
+
53
+ }
54
+
55
+ function valueOrDefault(value: Root | undefined): Root {
56
+ return value ?? {
57
+ type: 'root',
58
+ children: [{
59
+ type: 'paragraph',
60
+ children: [{ type: 'text', value: '' }]
61
+ }]
62
+ }
63
+ }
64
+
54
65
  function setup() {
55
66
  connector = new DeltaSlateEditorConnector(document.getElementById(props.uuid)!, props.uuid, props.client, props.context, props.organization, props.isDisabled)
56
67
  connector.render()
57
68
 
58
- store.dispatch(setState({ uuid: props.uuid, state: { initialValue: props.initialValue, value: props.initialValue } }))
69
+ store.dispatch(setState({ uuid: props.uuid, state: { initialValue: toSlate(valueOrDefault(props.initial)), value: toSlate(valueOrDefault(props.current)) } }))
59
70
 
60
71
  mountReduxWatchers()
61
72
  }
62
73
 
74
+ defineExpose({
75
+ updateTree
76
+ })
77
+
63
78
  onMounted(() => {
64
79
  setup()
65
80
  })
66
- </script>
81
+ </script>
67
82
 
68
83
  <template>
69
- <div :id="uuid" />
84
+ <div :id="uuid" class="slate-editor"/>
70
85
  </template>
71
-
72
- <style scoped>
73
-
74
- </style>
@@ -1,5 +1,5 @@
1
1
  import React, { useCallback, useMemo, useRef, useState } from "react";
2
- import { BaseRange, Descendant, Editor, Range, Transforms, createEditor } from "slate";
2
+ import { BaseRange, Descendant, Editor, Node, Range, Transforms, createEditor } from "slate";
3
3
  import {
4
4
  Editable,
5
5
  ReactEditor,
@@ -18,7 +18,7 @@ import { store } from "../store/store";
18
18
  import isHotkey from "is-hotkey";
19
19
  import { toggleMark } from "../utils";
20
20
  import { HOTKEYS } from "../config/hotkeys";
21
- import { decorate, withContentValues } from "../plugins";
21
+ import { decorate, withContentValues, withSuggestions } from "../plugins";
22
22
  import { ContentAndFormatMenu } from "./menus/ContentAndFormatMenu";
23
23
  import "prismjs/components/prism-markdown";
24
24
  import watch from "redux-watch";
@@ -37,8 +37,9 @@ export function DeltaSlateEditor({
37
37
  }): React.ReactElement {
38
38
 
39
39
  const [initialValue] = useState<Descendant[]>(selectById(store.getState(), uuid)?.initialValue ?? []);
40
+
40
41
  const dispatch = useDispatch();
41
- const editor = useMemo<Editor & ReactEditor>(() => withContentValues(withReact(withHistory(createEditor()))), []);
42
+ const editor = useMemo<Editor & ReactEditor>(() => withContentValues(withSuggestions(withReact(withHistory(createEditor())))), []);
42
43
  const [lastKey, setLastKey] = useState<string | undefined>();
43
44
 
44
45
  const renderLeaf = useCallback((props: RenderLeafProps) => <Leaf {...props} />, []);
@@ -47,9 +48,9 @@ export function DeltaSlateEditor({
47
48
 
48
49
  const renderPlaceholder = useCallback(
49
50
  (props: RenderPlaceholderProps) => (
50
- <div {...props.attributes} className={"py-2"}>
51
+ <span {...props.attributes} className={"py-2.5"}>
51
52
  {props.children}
52
- </div>
53
+ </span>
53
54
  ),
54
55
  [],
55
56
  );
@@ -76,7 +77,7 @@ export function DeltaSlateEditor({
76
77
  Transforms.select(editor, []);
77
78
  Transforms.removeNodes(editor);
78
79
  Transforms.insertNodes(editor, newValue);
79
- }),
80
+ })
80
81
  );
81
82
 
82
83
  function onSlateChange(value: Descendant[]): void {
@@ -204,8 +205,11 @@ export function DeltaSlateEditor({
204
205
  }
205
206
  }
206
207
 
208
+ const hasInitialValue = Boolean(initialValue[0] && Node.string(initialValue[0]));
209
+
207
210
  return (
208
- <div className={"relative"}>
211
+ <div
212
+ className={"relative"}>
209
213
  <Slate editor={editor} initialValue={initialValue} onChange={onSlateChange}>
210
214
  <Toolbar uuid={uuid} context={context} />
211
215
  <Editable
@@ -214,7 +218,8 @@ export function DeltaSlateEditor({
214
218
  renderLeaf={renderLeaf}
215
219
  renderElement={renderElement}
216
220
  renderPlaceholder={renderPlaceholder}
217
- className={"outline-none py-2 max-h-32 overflow-y-auto overflow-x-hidden"}
221
+ style={{ position: 'relative', whiteSpace: 'pre-wrap', overflowWrap: 'break-word', minHeight: hasInitialValue ? 'auto' : '3.85rem' }}
222
+ className={"outline-none py-2 overflow-y-auto resize-y pr-2 "}
218
223
  onKeyDown={onKeyDown}
219
224
  autoFocus={!isDisabled}
220
225
  readOnly={isDisabled}
@@ -34,14 +34,16 @@ export class DeltaSlateEditorConnector {
34
34
  store.dispatch(setOrganization({ organization }));
35
35
  this.root = createRoot(targetEl);
36
36
  }
37
-
38
37
 
39
38
  public render(): void {
40
39
  this.root.render(
41
40
  <div>
42
41
  <ApolloProvider client={this.client}>
43
42
  <Provider store={store}>
44
- <Editor uuid={this.uuid} context={this.context} isDisabled={this.isDisabled ?? false} />
43
+ <Editor
44
+ uuid={this.uuid}
45
+ context={this.context}
46
+ isDisabled={this.isDisabled ?? false} />
45
47
  </Provider>
46
48
  </ApolloProvider>
47
49
  </div>,
@@ -6,6 +6,8 @@ import LinkElement from "./elements/LinkElement";
6
6
  import { Editor, Element as SlateElement, Transforms } from "slate";
7
7
  import { useSlate } from "slate-react";
8
8
  import { findElement } from "../utils";
9
+ import SuggestionElement from "./elements/SuggestionElement";
10
+ import ContentValueSuggestionElement from "./elements/ContentValueSuggestionElement";
9
11
 
10
12
  export function Element({
11
13
  attributes,
@@ -18,7 +20,6 @@ export function Element({
18
20
  element: DeltaElement;
19
21
  lastKey: string | undefined;
20
22
  }): React.ReactElement {
21
- const style = {};
22
23
  const editor = useSlate();
23
24
 
24
25
  useEffect(() => {
@@ -63,37 +64,37 @@ export function Element({
63
64
  switch (element.level) {
64
65
  case 1:
65
66
  return (
66
- <h1 style={style} {...attributes} className={"text-5xl"}>
67
+ <h1 {...attributes} className={"text-5xl"}>
67
68
  {children}
68
69
  </h1>
69
70
  );
70
71
  case 2:
71
72
  return (
72
- <h2 style={style} {...attributes} className={"text-4xl"}>
73
+ <h2 {...attributes} className={"text-4xl"}>
73
74
  {children}
74
75
  </h2>
75
76
  );
76
77
  case 3:
77
78
  return (
78
- <h3 style={style} {...attributes} className={"text-3xl"}>
79
+ <h3 {...attributes} className={"text-3xl"}>
79
80
  {children}
80
81
  </h3>
81
82
  );
82
83
  case 4:
83
84
  return (
84
- <h4 style={style} {...attributes} className={"text-2xl"}>
85
+ <h4 {...attributes} className={"text-2xl"}>
85
86
  {children}
86
87
  </h4>
87
88
  );
88
89
  case 5:
89
90
  return (
90
- <h5 style={style} {...attributes} className={"text-xl"}>
91
+ <h5 {...attributes} className={"text-xl"}>
91
92
  {children}
92
93
  </h5>
93
94
  );
94
95
  default:
95
96
  return (
96
- <h5 style={style} {...attributes} className={"text-xl"}>
97
+ <h5 {...attributes} className={"text-xl"}>
97
98
  {children}
98
99
  </h5>
99
100
  );
@@ -101,25 +102,25 @@ export function Element({
101
102
  case "list":
102
103
  if (element.ordered) {
103
104
  return (
104
- <ol style={style} {...attributes} className={"list-decimal pl-4"}>
105
+ <ol {...attributes} className={"list-decimal pl-4"}>
105
106
  {children}
106
107
  </ol>
107
108
  );
108
109
  }
109
110
  return (
110
- <ul style={style} {...attributes} className={"list-disc pl-4"}>
111
+ <ul {...attributes} className={"list-disc pl-4"}>
111
112
  {children}
112
113
  </ul>
113
114
  );
114
115
  case "blockquote":
115
116
  return (
116
- <blockquote style={style} {...attributes}>
117
+ <blockquote {...attributes}>
117
118
  {children}
118
119
  </blockquote>
119
120
  );
120
121
  case "listItem":
121
122
  return (
122
- <li style={style} {...attributes}>
123
+ <li {...attributes}>
123
124
  {children}
124
125
  </li>
125
126
  );
@@ -135,17 +136,30 @@ export function Element({
135
136
  {children}
136
137
  </ContentValueElement>
137
138
  );
139
+ case "contentValueSuggestion":
140
+ return (
141
+ <ContentValueSuggestionElement element={element} attributes={attributes}>
142
+ {children}
143
+ </ContentValueSuggestionElement>
144
+ );
138
145
  case "link":
139
146
  return (
140
147
  <LinkElement element={element} attributes={attributes}>
141
148
  {children}
142
149
  </LinkElement>
143
150
  );
151
+
152
+ case "suggestion":
153
+ return (
154
+ <SuggestionElement element={element} attributes={attributes}>
155
+ {children}
156
+ </SuggestionElement>
157
+ );
144
158
  default:
145
159
  return (
146
- <p style={style} {...attributes}>
160
+ <span {...attributes}>
147
161
  {children}
148
- </p>
162
+ </span>
149
163
  );
150
164
  }
151
165
  }
@@ -21,8 +21,8 @@ export function ContentValueElement({
21
21
  };
22
22
 
23
23
  return (
24
- <span className={`${selected ? "bg-[#EAEAFA]" : ""} border border-[#705ED9] py-1 rounded-l-sm`}>
25
- <span {...attributes} contentEditable={false} className={`border-[#705ED9] px-2 py-1.5 border-r-3`} style={style}>
24
+ <span className={`${selected ? "bg-[#EAEAFA]" : ""} inline-flex border-l-5 border-y-1 border-r-1 py-0.5 border-[#705ED9] rounded-sm px-1`}>
25
+ <span {...attributes} contentEditable={false} style={style}>
26
26
  {children}
27
27
  {element.root ? toPlaintext(element.root) : ""}
28
28
  </span>
@@ -0,0 +1,31 @@
1
+ import React, { HTMLAttributes, ReactNode } from "react";
2
+ import { ContentValueSuggestionElement as ContentValueSuggestionElementType } from "../../types";
3
+ import { toPlaintext } from "@platecms/delta-cast-util-to-plaintext";
4
+
5
+ export function ContentValueSuggestionElement({
6
+ attributes,
7
+ children,
8
+ element,
9
+ }: {
10
+ attributes: HTMLAttributes<HTMLElement>;
11
+ children: ReactNode;
12
+ element: ContentValueSuggestionElementType;
13
+ }): React.ReactElement {
14
+
15
+ const style: React.CSSProperties = {
16
+ fontWeight: element.children[0].bold ? "bold" : "normal",
17
+ fontStyle: element.children[0].italic ? "italic" : "normal",
18
+ textDecoration: `${element.children[0].underline ? "underline" : ""} ${element.children[0].strikethrough ? "line-through" : ""}`,
19
+ };
20
+
21
+ return (
22
+ <span className="inline-flex border-b-3 border-red-500 bg-red-50 px-1 leading-none">
23
+ <span {...attributes} contentEditable={false} style={style}>
24
+ {children}
25
+ {element.root ? toPlaintext(element.root) : ""}
26
+ </span>
27
+ </span>
28
+ );
29
+ }
30
+
31
+ export default ContentValueSuggestionElement;
@@ -0,0 +1,23 @@
1
+ import React, { HTMLAttributes, ReactNode } from "react";
2
+ import { Suggestion as SuggestionElementType } from "../../types";
3
+
4
+ export function SuggestionElement({
5
+ attributes,
6
+ children,
7
+ element,
8
+ }: {
9
+ attributes: HTMLAttributes<HTMLElement>;
10
+ children: ReactNode;
11
+ element: SuggestionElementType;
12
+ }): React.ReactElement {
13
+ return (
14
+ <span {...attributes} contentEditable={false}>
15
+ {children}
16
+ {element.suggested.map((node) => <span>
17
+ {node.type === 'text' ? node.value : ''}
18
+ </span>)}
19
+ </span>
20
+ );
21
+ }
22
+
23
+ export default SuggestionElement;
@@ -213,7 +213,7 @@ export function Toolbar({ uuid, context }: { uuid: string; context: HTMLElement
213
213
  className={"flex w-fit items-start gap-2 absolute z-10 right-0"}
214
214
  style={{
215
215
  opacity: show ? 1 : 0,
216
- zIndex: show ? 10 : -1,
216
+ zIndex: show ? 9999 : -1,
217
217
  left: `${left}px`,
218
218
  top: `${top}px`,
219
219
  transition: "opacity 0.5s ease-in-out, top 0.1s ease-in-out, left 0.1s ease-in-out, z-index 0.5s ease-in-out",
@@ -14,6 +14,19 @@ export function withContentValues(editor: Editor): Editor {
14
14
  return editor;
15
15
  }
16
16
 
17
+ export function withSuggestions(editor: Editor): Editor {
18
+ const { isInline, isVoid, markableVoid } = editor;
19
+
20
+ editor.isInline = (element): boolean =>
21
+ ['suggestion', 'contentValueSuggestion'].includes(element.type) ? true : isInline(element);
22
+
23
+ editor.isVoid = (element): boolean => (['suggestion', 'contentValueSuggestion'].includes(element.type) ? true : isVoid(element));
24
+
25
+ editor.markableVoid = (element): boolean => (['suggestion', 'contentValueSuggestion'].includes(element.type) ? true : markableVoid(element));
26
+
27
+ return editor;
28
+ }
29
+
17
30
  export function decorate([node, path]: NodeEntry): Range[] {
18
31
  const ranges: Range[] = [];
19
32
 
@@ -1,6 +1,6 @@
1
1
  import { BaseEditor } from "slate";
2
2
  import { ReactEditor } from "slate-react";
3
- import { Root } from "@platecms/delta-cast";
3
+ import { Content, Root } from "@platecms/delta-cast";
4
4
 
5
5
  declare module "slate" {
6
6
  interface CustomTypes {
@@ -15,14 +15,7 @@ export interface DeltaLeafMarkdown {
15
15
  }
16
16
 
17
17
  export type DeltaElement =
18
- | BlockquoteElement
19
- | CodeElement
20
- | ContentValueElement
21
- | HeadingElement
22
- | LinkElement
23
- | ListElement
24
- | ListItemElement
25
- | ParagraphElement;
18
+ BlockquoteElement | CodeElement | ContentValueElement | ContentValueSuggestionElement | HeadingElement | LinkElement | ListElement | ListItemElement | ParagraphElement | Suggestion;
26
19
 
27
20
  export type DeltaLeaf = DeltaLeafMarkdown & {
28
21
  text: string;
@@ -36,7 +29,7 @@ export type DeltaLeaf = DeltaLeafMarkdown & {
36
29
 
37
30
  export interface ParagraphElement {
38
31
  type: "paragraph";
39
- children: (ContentValueElement | DeltaLeaf | LinkElement)[];
32
+ children: (ContentValueElement | ContentValueSuggestionElement | DeltaLeaf | LinkElement | Suggestion)[];
40
33
  }
41
34
 
42
35
  export interface HeadingElement {
@@ -73,6 +66,17 @@ export interface ContentValueElement {
73
66
  children: DeltaLeaf[];
74
67
  }
75
68
 
69
+ /*
70
+ * This is a slate only element that does not exist inside CAST
71
+ * The element is only used to display a suggestion
72
+ **/
73
+ export interface ContentValueSuggestionElement {
74
+ type: "contentValueSuggestion";
75
+ prn: string;
76
+ root: Root | undefined;
77
+ children: DeltaLeaf[];
78
+ }
79
+
76
80
  export interface LinkElement {
77
81
  type: "link";
78
82
  url: string;
@@ -85,3 +89,12 @@ export interface LinkElement {
85
89
  anchor?: boolean;
86
90
  };
87
91
  }
92
+
93
+ export interface Suggestion {
94
+ type: "suggestion";
95
+ suggested: Content[];
96
+ original: Content[];
97
+ message: string;
98
+ method: string;
99
+ children: DeltaLeaf[];
100
+ }
@@ -1,10 +0,0 @@
1
- fragment plateMaintainerFragment on Subject {
2
- _id: id
3
- id
4
- name {
5
- first
6
- last
7
- }
8
- language
9
- email
10
- }
@@ -1,8 +0,0 @@
1
- fragment subjectFields on Subject {
2
- _id: id
3
- email
4
- name {
5
- first
6
- last
7
- }
8
- }