@crystallize/design-system 1.5.0 → 1.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 (43) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/index.css +85 -142
  3. package/dist/index.d.ts +7 -3
  4. package/dist/index.js +1526 -4411
  5. package/dist/index.mjs +1391 -4294
  6. package/package.json +19 -19
  7. package/src/iconography/document.tsx +19 -0
  8. package/src/iconography/folder.tsx +30 -0
  9. package/src/iconography/index.ts +8 -2
  10. package/src/iconography/product.tsx +42 -0
  11. package/src/rich-text-editor/model/crystallize-to-lexical.ts +1 -1
  12. package/src/rich-text-editor/model/lexical-to-crystallize.ts +2 -2
  13. package/src/rich-text-editor/model/to-text.test.ts +97 -0
  14. package/src/rich-text-editor/model/to-text.ts +47 -0
  15. package/src/rich-text-editor/nodes/BaseNodes.ts +0 -3
  16. package/src/rich-text-editor/plugins/ActionsPlugin/index.tsx +14 -3
  17. package/src/rich-text-editor/plugins/CodeActionMenuPlugin/index.tsx +1 -1
  18. package/src/rich-text-editor/plugins/ComponentPickerPlugin/index.tsx +5 -5
  19. package/src/rich-text-editor/plugins/DraggableBlockPlugin/index.tsx +29 -50
  20. package/src/rich-text-editor/plugins/MaxLengthPlugin/index.tsx +34 -23
  21. package/src/rich-text-editor/plugins/ToolbarPlugin/index.tsx +8 -8
  22. package/src/rich-text-editor/rich-text-editor.css +3 -3
  23. package/src/rich-text-editor/rich-text-editor.stories.tsx +9 -1
  24. package/src/rich-text-editor/rich-text-editor.tsx +30 -24
  25. package/src/rich-text-editor/tests/rich-text-editor-basic-rendering.test.tsx +1 -1
  26. package/src/rich-text-editor/tests/rich-text-editor-model-basics.test.tsx +1 -1
  27. package/src/rich-text-editor/tests/rich-text-editor-model-conversions.test.tsx +1 -1
  28. package/src/rich-text-editor/tests/rich-text-editor-text-formats.test.tsx +1 -1
  29. package/src/rich-text-editor/themes/{PlaygroundEditorTheme.css → CrystallizeRTEditorTheme.css} +81 -85
  30. package/src/rich-text-editor/themes/CrystallizeRTEditorTheme.ts +113 -0
  31. package/dist/draggable-block-menu-KKHDNKJA.svg +0 -1
  32. package/src/rich-text-editor/appSettings.ts +0 -28
  33. package/src/rich-text-editor/context/SettingsContext.tsx +0 -71
  34. package/src/rich-text-editor/context/SharedAutocompleteContext.tsx +0 -60
  35. package/src/rich-text-editor/nodes/AutocompleteNode.tsx +0 -96
  36. package/src/rich-text-editor/plugins/AutocompletePlugin/index.tsx +0 -2536
  37. package/src/rich-text-editor/themes/PlaygroundEditorTheme.ts +0 -113
  38. /package/src/rich-text-editor/{model → types}/crystallize-rich-text-types/code.ts +0 -0
  39. /package/src/rich-text-editor/{model → types}/crystallize-rich-text-types/headings.ts +0 -0
  40. /package/src/rich-text-editor/{model → types}/crystallize-rich-text-types/index.ts +0 -0
  41. /package/src/rich-text-editor/{model → types}/crystallize-rich-text-types/link.ts +0 -0
  42. /package/src/rich-text-editor/{model → types}/crystallize-rich-text-types/table.ts +0 -0
  43. /package/src/rich-text-editor/{types.ts → types/types.ts} +0 -0
@@ -6,10 +6,8 @@
6
6
  *
7
7
  */
8
8
  import './index.css';
9
-
10
- import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
11
- import {eventFiles} from '@lexical/rich-text';
12
- import {mergeRegister} from '@lexical/utils';
9
+ import * as React from 'react';
10
+ import { DragEvent as ReactDragEvent, useEffect, useRef, useState } from 'react';
13
11
  import {
14
12
  $getNearestNodeFromDOMNode,
15
13
  $getNodeByKey,
@@ -20,13 +18,14 @@ import {
20
18
  DROP_COMMAND,
21
19
  LexicalEditor,
22
20
  } from 'lexical';
23
- import * as React from 'react';
24
- import {DragEvent as ReactDragEvent, useEffect, useRef, useState} from 'react';
25
- import {createPortal} from 'react-dom';
21
+ import { createPortal } from 'react-dom';
22
+ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
23
+ import { eventFiles } from '@lexical/rich-text';
24
+ import { mergeRegister } from '@lexical/utils';
26
25
 
27
- import {isHTMLElement} from '../../utils/guard';
28
- import {Point} from '../../utils/point';
29
- import {Rect} from '../../utils/rect';
26
+ import { isHTMLElement } from '../../utils/guard';
27
+ import { Point } from '../../utils/point';
28
+ import { Rect } from '../../utils/rect';
30
29
 
31
30
  const SPACE = 4;
32
31
  const TARGET_LINE_HALF_HEIGHT = 2;
@@ -55,11 +54,7 @@ function getTopLevelNodeKeys(editor: LexicalEditor): string[] {
55
54
  return editor.getEditorState().read(() => $getRoot().getChildrenKeys());
56
55
  }
57
56
 
58
- function getBlockElement(
59
- anchorElem: HTMLElement,
60
- editor: LexicalEditor,
61
- event: MouseEvent,
62
- ): HTMLElement | null {
57
+ function getBlockElement(anchorElem: HTMLElement, editor: LexicalEditor, event: MouseEvent): HTMLElement | null {
63
58
  const anchorElementRect = anchorElem.getBoundingClientRect();
64
59
  const topLevelNodeKeys = getTopLevelNodeKeys(editor);
65
60
 
@@ -77,7 +72,7 @@ function getBlockElement(
77
72
  }
78
73
  const point = new Point(event.x, event.y);
79
74
  const domRect = Rect.fromDOM(elem);
80
- const {marginTop, marginBottom} = window.getComputedStyle(elem);
75
+ const { marginTop, marginBottom } = window.getComputedStyle(elem);
81
76
 
82
77
  const rect = domRect.generateNewRect({
83
78
  bottom: domRect.bottom + parseFloat(marginBottom),
@@ -88,7 +83,7 @@ function getBlockElement(
88
83
 
89
84
  const {
90
85
  result,
91
- reason: {isOnTopSide, isOnBottomSide},
86
+ reason: { isOnTopSide, isOnBottomSide },
92
87
  } = rect.contains(point);
93
88
 
94
89
  if (result) {
@@ -119,11 +114,7 @@ function isOnMenu(element: HTMLElement): boolean {
119
114
  return !!element.closest(`.${DRAGGABLE_BLOCK_MENU_CLASSNAME}`);
120
115
  }
121
116
 
122
- function setMenuPosition(
123
- targetElem: HTMLElement | null,
124
- floatingElem: HTMLElement,
125
- anchorElem: HTMLElement,
126
- ) {
117
+ function setMenuPosition(targetElem: HTMLElement | null, floatingElem: HTMLElement, anchorElem: HTMLElement) {
127
118
  if (!targetElem) {
128
119
  floatingElem.style.opacity = '0';
129
120
  floatingElem.style.transform = 'translate(-10000px, -10000px)';
@@ -136,9 +127,7 @@ function setMenuPosition(
136
127
  const anchorElementRect = anchorElem.getBoundingClientRect();
137
128
 
138
129
  const top =
139
- targetRect.top +
140
- (parseInt(targetStyle.lineHeight, 10) - floatingElemRect.height) / 2 -
141
- anchorElementRect.top;
130
+ targetRect.top + (parseInt(targetStyle.lineHeight, 10) - floatingElemRect.height) / 2 - anchorElementRect.top;
142
131
 
143
132
  const left = SPACE;
144
133
 
@@ -146,11 +135,8 @@ function setMenuPosition(
146
135
  floatingElem.style.transform = `translate(${left}px, ${top}px)`;
147
136
  }
148
137
 
149
- function setDragImage(
150
- dataTransfer: DataTransfer,
151
- draggableBlockElem: HTMLElement,
152
- ) {
153
- const {transform} = draggableBlockElem.style;
138
+ function setDragImage(dataTransfer: DataTransfer, draggableBlockElem: HTMLElement) {
139
+ const { transform } = draggableBlockElem.style;
154
140
 
155
141
  // Remove dragImage borders
156
142
  draggableBlockElem.style.transform = 'translateZ(0)';
@@ -168,10 +154,8 @@ function setTargetLine(
168
154
  anchorElem: HTMLElement,
169
155
  ) {
170
156
  const targetStyle = window.getComputedStyle(targetBlockElem);
171
- const {top: targetBlockElemTop, height: targetBlockElemHeight} =
172
- targetBlockElem.getBoundingClientRect();
173
- const {top: anchorTop, width: anchorWidth} =
174
- anchorElem.getBoundingClientRect();
157
+ const { top: targetBlockElemTop, height: targetBlockElemHeight } = targetBlockElem.getBoundingClientRect();
158
+ const { top: anchorTop, width: anchorWidth } = anchorElem.getBoundingClientRect();
175
159
 
176
160
  let lineTop = targetBlockElemTop;
177
161
  // At the bottom of the target
@@ -185,9 +169,7 @@ function setTargetLine(
185
169
  const left = TEXT_BOX_HORIZONTAL_PADDING - SPACE;
186
170
 
187
171
  targetLineElem.style.transform = `translate(${left}px, ${top}px)`;
188
- targetLineElem.style.width = `${
189
- anchorWidth - (TEXT_BOX_HORIZONTAL_PADDING - SPACE) * 2
190
- }px`;
172
+ targetLineElem.style.width = `${anchorWidth - (TEXT_BOX_HORIZONTAL_PADDING - SPACE) * 2}px`;
191
173
  targetLineElem.style.opacity = '.4';
192
174
  }
193
175
 
@@ -198,17 +180,12 @@ function hideTargetLine(targetLineElem: HTMLElement | null) {
198
180
  }
199
181
  }
200
182
 
201
- function useDraggableBlockMenu(
202
- editor: LexicalEditor,
203
- anchorElem: HTMLElement,
204
- isEditable: boolean,
205
- ): JSX.Element {
183
+ function useDraggableBlockMenu(editor: LexicalEditor, anchorElem: HTMLElement, isEditable: boolean): JSX.Element {
206
184
  const scrollerElem = anchorElem.parentElement;
207
185
 
208
186
  const menuRef = useRef<HTMLDivElement>(null);
209
187
  const targetLineRef = useRef<HTMLDivElement>(null);
210
- const [draggableBlockElem, setDraggableBlockElem] =
211
- useState<HTMLElement | null>(null);
188
+ const [draggableBlockElem, setDraggableBlockElem] = useState<HTMLElement | null>(null);
212
189
 
213
190
  useEffect(() => {
214
191
  function onMouseMove(event: MouseEvent) {
@@ -252,7 +229,7 @@ function useDraggableBlockMenu(
252
229
  if (isFileTransfer) {
253
230
  return false;
254
231
  }
255
- const {pageY, target} = event;
232
+ const { pageY, target } = event;
256
233
  if (!isHTMLElement(target)) {
257
234
  return false;
258
235
  }
@@ -272,7 +249,7 @@ function useDraggableBlockMenu(
272
249
  if (isFileTransfer) {
273
250
  return false;
274
251
  }
275
- const {target, dataTransfer, pageY} = event;
252
+ const { target, dataTransfer, pageY } = event;
276
253
  const dragData = dataTransfer?.getData(DRAG_DATA_FORMAT) || '';
277
254
  const draggedNode = $getNodeByKey(dragData);
278
255
  if (!draggedNode) {
@@ -292,7 +269,7 @@ function useDraggableBlockMenu(
292
269
  if (targetNode === draggedNode) {
293
270
  return true;
294
271
  }
295
- const {top, height} = targetBlockElem.getBoundingClientRect();
272
+ const { top, height } = targetBlockElem.getBoundingClientRect();
296
273
  const shouldInsertAfter = pageY - top > height / 2;
297
274
  if (shouldInsertAfter) {
298
275
  targetNode.insertAfter(draggedNode);
@@ -307,14 +284,14 @@ function useDraggableBlockMenu(
307
284
  return mergeRegister(
308
285
  editor.registerCommand(
309
286
  DRAGOVER_COMMAND,
310
- (event) => {
287
+ event => {
311
288
  return onDragover(event);
312
289
  },
313
290
  COMMAND_PRIORITY_LOW,
314
291
  ),
315
292
  editor.registerCommand(
316
293
  DROP_COMMAND,
317
- (event) => {
294
+ event => {
318
295
  return onDrop(event);
319
296
  },
320
297
  COMMAND_PRIORITY_HIGH,
@@ -324,6 +301,7 @@ function useDraggableBlockMenu(
324
301
 
325
302
  function onDragStart(event: ReactDragEvent<HTMLDivElement>): void {
326
303
  const dataTransfer = event.dataTransfer;
304
+
327
305
  if (!dataTransfer || !draggableBlockElem) {
328
306
  return;
329
307
  }
@@ -349,7 +327,8 @@ function useDraggableBlockMenu(
349
327
  ref={menuRef}
350
328
  draggable={true}
351
329
  onDragStart={onDragStart}
352
- onDragEnd={onDragEnd}>
330
+ onDragEnd={onDragEnd}
331
+ >
353
332
  <div className={isEditable ? 'icon' : ''} />
354
333
  </div>
355
334
  <div className="draggable-block-target-line" ref={targetLineRef} />
@@ -1,5 +1,5 @@
1
1
  import { useEffect } from 'react';
2
- import { $getSelection, $isRangeSelection, EditorState, RootNode } from 'lexical';
2
+ import { $getSelection, $isRangeSelection, EditorState } from 'lexical';
3
3
  /**
4
4
  * Copyright (c) Meta Platforms, Inc. and affiliates.
5
5
  *
@@ -12,36 +12,47 @@ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext
12
12
  import { trimTextContentFromAnchor } from '@lexical/selection';
13
13
  import { $restoreEditorState } from '@lexical/utils';
14
14
 
15
+ import { lexicalToCrystallizeRichText } from '../../model/lexical-to-crystallize';
16
+ import { toText } from '../../model/to-text';
17
+
15
18
  export function MaxLengthPlugin({ maxLength }: { maxLength: number }): null {
16
19
  const [editor] = useLexicalComposerContext();
17
20
 
18
21
  useEffect(() => {
19
22
  let lastRestoredEditorState: EditorState | null = null;
20
23
 
21
- return editor.registerNodeTransform(RootNode, (rootNode: RootNode) => {
22
- const selection = $getSelection();
23
- if (!$isRangeSelection(selection) || !selection.isCollapsed()) {
24
- return;
25
- }
26
- const prevEditorState = editor.getEditorState();
27
- const prevTextContent = prevEditorState.read(() => rootNode.getTextContent());
28
- const textContent = rootNode.getTextContent();
29
- if (prevTextContent !== textContent) {
30
- const textLength = textContent.length;
31
- const delCount = textLength - maxLength;
32
- const anchor = selection.anchor;
33
-
34
- if (delCount > 0) {
35
- // Restore the old editor state instead if the last
36
- // text content was already at the limit.
37
- if (prevTextContent.length === maxLength && lastRestoredEditorState !== prevEditorState) {
38
- lastRestoredEditorState = prevEditorState;
39
- $restoreEditorState(editor, prevEditorState);
40
- } else {
41
- trimTextContentFromAnchor(editor, anchor, delCount);
24
+ return editor.registerUpdateListener(({ editorState, prevEditorState }) => {
25
+ editor.update(() => {
26
+ const selection = $getSelection();
27
+ if (!$isRangeSelection(selection) || !selection.isCollapsed()) {
28
+ return;
29
+ }
30
+
31
+ const prevTextContent = toText(lexicalToCrystallizeRichText({ editorState: prevEditorState }));
32
+
33
+ // const editorState = rootNode.get
34
+ // createState
35
+ const textContent = toText(lexicalToCrystallizeRichText({ editorState }));
36
+
37
+ // const prevTextContent = prevEditorState.read(() => rootNode.getTextContent());
38
+ // const textContent = rootNode.getTextContent();
39
+ if (prevTextContent !== textContent) {
40
+ const textLength = textContent.length;
41
+ const delCount = textLength - maxLength;
42
+ const anchor = selection.anchor;
43
+
44
+ if (delCount > 0) {
45
+ // Restore the old editor state instead if the last
46
+ // text content was already at the limit.
47
+ if (prevTextContent.length === maxLength && lastRestoredEditorState !== prevEditorState) {
48
+ lastRestoredEditorState = prevEditorState;
49
+ $restoreEditorState(editor, prevEditorState);
50
+ } else {
51
+ trimTextContentFromAnchor(editor, anchor, delCount);
52
+ }
42
53
  }
43
54
  }
44
- }
55
+ });
45
56
  });
46
57
  }, [editor, maxLength]);
47
58
 
@@ -45,7 +45,7 @@ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext
45
45
  import { $isDecoratorBlockNode } from '@lexical/react/LexicalDecoratorBlockNode';
46
46
  import { INSERT_HORIZONTAL_RULE_COMMAND } from '@lexical/react/LexicalHorizontalRuleNode';
47
47
  import { $createHeadingNode, $createQuoteNode, $isHeadingNode, HeadingTagType } from '@lexical/rich-text';
48
- import { $selectAll, $setBlocksType_experimental } from '@lexical/selection';
48
+ import { $selectAll, $setBlocksType } from '@lexical/selection';
49
49
  import {
50
50
  $findMatchingParent,
51
51
  $getNearestBlockElementAncestorOrThrow,
@@ -58,7 +58,7 @@ import { Dialog } from '../../../dialog';
58
58
  import { DropdownMenu } from '../../../dropdown-menu';
59
59
  import { IconButton } from '../../../icon-button';
60
60
  import { Icon } from '../../../iconography';
61
- import type { CrystallizeRichTextActionMenuItem } from '../../types';
61
+ import type { CrystallizeRichTextActionMenuItem } from '../../types/types';
62
62
  import { IS_APPLE } from '../../utils/environment';
63
63
  import { getSelectedNode } from '../../utils/getSelectedNode';
64
64
  import { sanitizeUrl } from '../../utils/url';
@@ -111,7 +111,7 @@ function BlockFormatDropDown({
111
111
  editor.update(() => {
112
112
  const selection = $getSelection();
113
113
  if ($isRangeSelection(selection) || DEPRECATED_$isGridSelection(selection))
114
- $setBlocksType_experimental(selection, () => $createParagraphNode());
114
+ $setBlocksType(selection, () => $createParagraphNode());
115
115
  });
116
116
  }
117
117
  };
@@ -121,7 +121,7 @@ function BlockFormatDropDown({
121
121
  editor.update(() => {
122
122
  const selection = $getSelection();
123
123
  if ($isRangeSelection(selection) || DEPRECATED_$isGridSelection(selection)) {
124
- $setBlocksType_experimental(selection, () => $createHeadingNode(headingSize));
124
+ $setBlocksType(selection, () => $createHeadingNode(headingSize));
125
125
  }
126
126
  });
127
127
  }
@@ -148,13 +148,13 @@ function BlockFormatDropDown({
148
148
  editor.update(() => {
149
149
  const selection = $getSelection();
150
150
  if ($isRangeSelection(selection) || DEPRECATED_$isGridSelection(selection)) {
151
- $setBlocksType_experimental(selection, () => $createQuoteNode());
151
+ $setBlocksType(selection, () => $createQuoteNode());
152
152
  } else {
153
153
  /**
154
154
  * Will select the entire editor, in case it is not selected. This is added
155
155
  * to get the unit tests working
156
156
  */
157
- $setBlocksType_experimental($getRoot().select(), () => $createQuoteNode());
157
+ $setBlocksType($getRoot().select(), () => $createQuoteNode());
158
158
  }
159
159
  });
160
160
  }
@@ -167,7 +167,7 @@ function BlockFormatDropDown({
167
167
 
168
168
  if ($isRangeSelection(selection) || DEPRECATED_$isGridSelection(selection)) {
169
169
  if (selection.isCollapsed()) {
170
- $setBlocksType_experimental(selection, () => $createCodeNode());
170
+ $setBlocksType(selection, () => $createCodeNode());
171
171
  } else {
172
172
  const textContent = selection.getTextContent();
173
173
  const codeNode = $createCodeNode();
@@ -180,7 +180,7 @@ function BlockFormatDropDown({
180
180
  * Will select the entire editor, in case it is not selected. This is added
181
181
  * to get the unit tests working
182
182
  */
183
- $setBlocksType_experimental($getRoot().select(), () => $createCodeNode());
183
+ $setBlocksType($getRoot().select(), () => $createCodeNode());
184
184
  }
185
185
  });
186
186
  }
@@ -1333,17 +1333,17 @@
1333
1333
  z-index: 3;
1334
1334
  }
1335
1335
 
1336
- .TableNode__contentEditable .PlaygroundEditorTheme__paragraph {
1336
+ .TableNode__contentEditable .CrystallizeRTEditorTheme__paragraph {
1337
1337
  @apply mt-0;
1338
1338
  }
1339
1339
 
1340
- .PlaygroundEditorTheme__blockCursor {
1340
+ .CrystallizeRTEditorTheme__blockCursor {
1341
1341
  display: block;
1342
1342
  pointer-events: none;
1343
1343
  position: absolute;
1344
1344
  }
1345
1345
 
1346
- .PlaygroundEditorTheme__blockCursor:after {
1346
+ .CrystallizeRTEditorTheme__blockCursor:after {
1347
1347
  content: '';
1348
1348
  display: block;
1349
1349
  position: absolute;
@@ -1,8 +1,8 @@
1
1
  /* eslint-disable no-console */
2
2
  import type { Meta, StoryObj } from '@storybook/react';
3
3
 
4
- import type { CrystallizeRichText, CrystallizeRichTextNode } from './model/crystallize-rich-text-types';
5
4
  import { RichTextEditor } from './rich-text-editor';
5
+ import type { CrystallizeRichText, CrystallizeRichTextNode } from './types/crystallize-rich-text-types';
6
6
 
7
7
  const meta: Meta<typeof RichTextEditor> = {
8
8
  title: 'Components/RichTextEditor',
@@ -218,6 +218,14 @@ export const BlankSheet: Story = {
218
218
  },
219
219
  };
220
220
 
221
+ export const MaxLength: Story = {
222
+ args: {
223
+ initialData: [],
224
+ placeholder: 'You can only write 10 characters in here',
225
+ maxLength: 10,
226
+ },
227
+ };
228
+
221
229
  export const CodeInlined: Story = {
222
230
  args: {
223
231
  initialData: [
@@ -1,7 +1,6 @@
1
1
  import { ReactNode, useEffect, useRef, useState } from 'react';
2
2
  import type { EditorState } from 'lexical';
3
3
  import { AutoFocusPlugin } from '@lexical/react/LexicalAutoFocusPlugin';
4
- import { CharacterLimitPlugin } from '@lexical/react/LexicalCharacterLimitPlugin';
5
4
  import { ClearEditorPlugin } from '@lexical/react/LexicalClearEditorPlugin';
6
5
  import { LexicalComposer } from '@lexical/react/LexicalComposer';
7
6
  import LexicalErrorBoundary from '@lexical/react/LexicalErrorBoundary';
@@ -14,20 +13,18 @@ import { TabIndentationPlugin } from '@lexical/react/LexicalTabIndentationPlugin
14
13
  import { TablePlugin } from '@lexical/react/LexicalTablePlugin';
15
14
 
16
15
  import './rich-text-editor.css';
17
- import { useSettings } from './context/SettingsContext';
16
+ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
17
+
18
18
  import { SharedHistoryContext, useSharedHistoryContext } from './context/SharedHistoryContext';
19
- import type { CrystallizeRichText } from './model/crystallize-rich-text-types';
20
19
  import { composeInitialState } from './model/crystallize-to-lexical';
21
20
  import { lexicalToCrystallizeRichText } from './model/lexical-to-crystallize';
22
21
  import { BaseNodes } from './nodes/BaseNodes';
23
22
  import { BaseNodes as TableCellNodes } from './nodes/TableCellNodes';
24
23
  import AutoLinkPlugin from './plugins/AutoLinkPlugin';
25
- import AutocompletePlugin from './plugins/AutocompletePlugin';
26
24
  import CodeActionMenuPlugin from './plugins/CodeActionMenuPlugin';
27
25
  import CodeHighlightPlugin from './plugins/CodeHighlightPlugin';
28
26
  import ComponentPickerPlugin from './plugins/ComponentPickerPlugin';
29
27
  import DragDropPaste from './plugins/DragDropPastePlugin';
30
- import DraggableBlockPlugin from './plugins/DraggableBlockPlugin';
31
28
  import FloatingLinkEditorPlugin from './plugins/FloatingLinkEditorPlugin';
32
29
  import FloatingTextFormatToolbarPlugin from './plugins/FloatingTextFormatToolbarPlugin';
33
30
  import LinkPlugin from './plugins/LinkPlugin';
@@ -40,13 +37,21 @@ import TableCellActionMenuPlugin from './plugins/TableActionMenuPlugin';
40
37
  import TableCellResizer from './plugins/TableCellResizer';
41
38
  import { TableContext, TablePlugin as NewTablePlugin } from './plugins/TablePlugin';
42
39
  import ToolbarPlugin from './plugins/ToolbarPlugin';
43
- import PlaygroundEditorTheme from './themes/PlaygroundEditorTheme';
44
- import type { CrystallizeRichTextActionMenuItem } from './types';
40
+ import CrystallizeRTEditorTheme from './themes/CrystallizeRTEditorTheme';
41
+ import type { CrystallizeRichText } from './types/crystallize-rich-text-types';
42
+ import type { CrystallizeRichTextActionMenuItem } from './types/types';
45
43
  import ContentEditable from './ui/ContentEditable';
46
44
 
47
- // Types
48
- export type { CrystallizeRichText } from './model/crystallize-rich-text-types';
49
- export * from './types';
45
+ /**
46
+ * Currently disabling this as there is a conflict with someting within
47
+ * the app. @hk have done 15minutes of debugging without finding out what
48
+ * causes the conflict. We can re-visit this at some later time.
49
+ */
50
+ // import DraggableBlockPlugin from './plugins/DraggableBlockPlugin';
51
+
52
+ // Export types
53
+ export type { CrystallizeRichText } from './types/crystallize-rich-text-types';
54
+ export * from './types/types';
50
55
 
51
56
  type TRichTextBase = {
52
57
  placeholder?: string;
@@ -56,6 +61,8 @@ type TRichTextBase = {
56
61
  actionsMenuPrepend?: CrystallizeRichTextActionMenuItem[];
57
62
  actionsMenuAppend?: CrystallizeRichTextActionMenuItem[];
58
63
  slotPreContent?: ReactNode;
64
+ maxLength?: number;
65
+ editable?: boolean;
59
66
  };
60
67
 
61
68
  export function RichTextEditor({
@@ -64,7 +71,6 @@ export function RichTextEditor({
64
71
  ...rest
65
72
  }: TRichTextBase & {
66
73
  initialData?: CrystallizeRichText;
67
- editable?: boolean;
68
74
  }) {
69
75
  return (
70
76
  <LexicalComposer
@@ -75,7 +81,7 @@ export function RichTextEditor({
75
81
  onError: (error: Error) => {
76
82
  throw error;
77
83
  },
78
- theme: PlaygroundEditorTheme,
84
+ theme: CrystallizeRTEditorTheme,
79
85
  editorState: initialData
80
86
  ? composeInitialState({
81
87
  richText: initialData,
@@ -86,7 +92,7 @@ export function RichTextEditor({
86
92
  <SharedHistoryContext>
87
93
  <TableContext>
88
94
  <div className="c-rich-text-editor">
89
- <RichTextEditorWithoutContext {...rest} />
95
+ <RichTextEditorWithoutContext {...rest} editable={editable} />
90
96
  </div>
91
97
  </TableContext>
92
98
  </SharedHistoryContext>
@@ -102,17 +108,17 @@ function RichTextEditorWithoutContext({
102
108
  actionsMenuPrepend,
103
109
  actionsMenuAppend,
104
110
  slotPreContent,
111
+ maxLength,
112
+ editable,
105
113
  }: TRichTextBase): JSX.Element {
106
114
  const { historyState } = useSharedHistoryContext();
107
- const {
108
- settings: { isAutocomplete, isMaxLength, isCharLimit, isCharLimitUtf8 },
109
- } = useSettings();
110
115
  const placeholder = (
111
116
  <div className="text-[14px] font-normal text-gray-300-600 italic absolute overflow-hidden text-ellipsis top-0 left-6 right-10 select-none whitespace-nowrap inline-block pointer-events-none ">
112
117
  {placeholderText ?? ''}
113
118
  </div>
114
119
  );
115
120
 
121
+ const [editor] = useLexicalComposerContext();
116
122
  const [floatingAnchorElem, setFloatingAnchorElem] = useState<HTMLDivElement | null>(null);
117
123
  const [isSmallWidthViewport, setIsSmallWidthViewport] = useState<boolean>(false);
118
124
  const firstOnChangeTriggeredRef = useRef(!autoFocus);
@@ -123,6 +129,11 @@ function RichTextEditorWithoutContext({
123
129
  }
124
130
  };
125
131
 
132
+ // Listen for editable change
133
+ useEffect(() => {
134
+ editor.setEditable(editable || false);
135
+ }, [editable, editor]);
136
+
126
137
  // Deal with different window sizes
127
138
  useEffect(() => {
128
139
  const updateViewPortWidth = () => {
@@ -156,7 +167,7 @@ function RichTextEditorWithoutContext({
156
167
  <ToolbarPlugin actionsMenuPrepend={actionsMenuPrepend} actionsMenuAppend={actionsMenuAppend} />
157
168
  {slotPreContent}
158
169
  <div className="editor-container">
159
- {isMaxLength && <MaxLengthPlugin maxLength={30} />}
170
+ {maxLength != null ? <MaxLengthPlugin maxLength={maxLength} /> : null}
160
171
  <DragDropPaste />
161
172
  {!autoFocus ? null : <AutoFocusPlugin />}
162
173
  <ClearEditorPlugin />
@@ -190,7 +201,7 @@ function RichTextEditorWithoutContext({
190
201
  onError: (error: Error) => {
191
202
  throw error;
192
203
  },
193
- theme: PlaygroundEditorTheme,
204
+ theme: CrystallizeRTEditorTheme,
194
205
  }}
195
206
  >
196
207
  {!autoFocus ? <></> : <AutoFocusPlugin />}
@@ -210,18 +221,13 @@ function RichTextEditorWithoutContext({
210
221
  <TabIndentationPlugin />
211
222
  {floatingAnchorElem && !isSmallWidthViewport && (
212
223
  <>
213
- <DraggableBlockPlugin anchorElem={floatingAnchorElem} />
224
+ {/* <DraggableBlockPlugin anchorElem={floatingAnchorElem} /> */}
214
225
  <CodeActionMenuPlugin anchorElem={floatingAnchorElem} />
215
226
  <FloatingLinkEditorPlugin anchorElem={floatingAnchorElem} />
216
227
  <TableCellActionMenuPlugin anchorElem={floatingAnchorElem} />
217
228
  <FloatingTextFormatToolbarPlugin anchorElem={floatingAnchorElem} />
218
229
  </>
219
230
  )}
220
-
221
- {(isCharLimit || isCharLimitUtf8) && (
222
- <CharacterLimitPlugin charset={isCharLimit ? 'UTF-16' : 'UTF-8'} maxLength={5} />
223
- )}
224
- {isAutocomplete && <AutocompletePlugin />}
225
231
  </div>
226
232
  </>
227
233
  );
@@ -1,8 +1,8 @@
1
1
  /* eslint-disable no-console */
2
2
  import { render, screen } from '@testing-library/react';
3
3
 
4
- import type { CrystallizeRichText } from '../model/crystallize-rich-text-types';
5
4
  import { RichTextEditor } from '../rich-text-editor';
5
+ import type { CrystallizeRichText } from '../types/crystallize-rich-text-types';
6
6
  import { sleep } from './utils';
7
7
 
8
8
  const emptyParagraph: CrystallizeRichText = [{ kind: 'block', type: 'paragraph', textContent: 'rabbit' }];
@@ -1,7 +1,7 @@
1
1
  import { render } from '@testing-library/react';
2
2
 
3
- import type { CrystallizeRichText } from '../model/crystallize-rich-text-types';
4
3
  import { RichTextEditor } from '../rich-text-editor';
4
+ import type { CrystallizeRichText } from '../types/crystallize-rich-text-types';
5
5
  import { sleep } from './utils';
6
6
 
7
7
  describe('RichTextEditor model basics', () => {
@@ -1,7 +1,7 @@
1
1
  import { render } from '@testing-library/react';
2
2
 
3
- import type { CrystallizeRichText, CrystallizeRichTextNode } from '../model/crystallize-rich-text-types';
4
3
  import { RichTextEditor } from '../rich-text-editor';
4
+ import type { CrystallizeRichText, CrystallizeRichTextNode } from '../types/crystallize-rich-text-types';
5
5
  import { sleep } from './utils';
6
6
 
7
7
  /**
@@ -2,8 +2,8 @@
2
2
  import { render, screen } from '@testing-library/react';
3
3
  import userEvent from '@testing-library/user-event';
4
4
 
5
- import type { CrystallizeRichText } from '../model/crystallize-rich-text-types';
6
5
  import { RichTextEditor } from '../rich-text-editor';
6
+ import type { CrystallizeRichText } from '../types/crystallize-rich-text-types';
7
7
  import { selectTextInElement } from './utils';
8
8
 
9
9
  describe('RichTextEditor text formats', () => {