@dxos/react-ui-editor 0.3.9-main.7cfe653 → 0.3.9-main.88fb1d9

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 (38) hide show
  1. package/dist/lib/browser/index.mjs +134 -14
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/automerge/automerge-plugin/index.d.ts +1 -1
  5. package/dist/types/src/automerge/automerge-plugin/index.d.ts.map +1 -1
  6. package/dist/types/src/automerge/automerge.stories.d.ts.map +1 -1
  7. package/dist/types/src/components/Editor/Editor.stories.d.ts +3 -2
  8. package/dist/types/src/components/Editor/Editor.stories.d.ts.map +1 -1
  9. package/dist/types/src/components/Markdown/Markdown.d.ts.map +1 -1
  10. package/dist/types/src/components/Markdown/Markdown.stories.d.ts +2 -1
  11. package/dist/types/src/components/Markdown/Markdown.stories.d.ts.map +1 -1
  12. package/dist/types/src/components/RichText/RichText.stories.d.ts +2 -1
  13. package/dist/types/src/components/RichText/RichText.stories.d.ts.map +1 -1
  14. package/dist/types/src/components/TextEditor/TextEditor.d.ts +39 -0
  15. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -0
  16. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +18 -0
  17. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -0
  18. package/dist/types/src/components/TextEditor/index.d.ts +5 -0
  19. package/dist/types/src/components/TextEditor/index.d.ts.map +1 -0
  20. package/dist/types/src/components/TextEditor/theme.d.ts +8 -0
  21. package/dist/types/src/components/TextEditor/theme.d.ts.map +1 -0
  22. package/dist/types/src/components/index.d.ts +1 -0
  23. package/dist/types/src/components/index.d.ts.map +1 -1
  24. package/dist/types/src/testing/replicator.d.ts.map +1 -1
  25. package/package.json +19 -17
  26. package/src/automerge/automerge-plugin/index.ts +1 -1
  27. package/src/automerge/automerge.stories.tsx +1 -2
  28. package/src/components/Editor/Editor.stories.tsx +4 -2
  29. package/src/components/Editor/Editor.tsx +2 -2
  30. package/src/components/Markdown/Markdown.stories.tsx +3 -0
  31. package/src/components/Markdown/Markdown.tsx +12 -8
  32. package/src/components/RichText/RichText.stories.tsx +2 -0
  33. package/src/components/TextEditor/TextEditor.stories.tsx +33 -0
  34. package/src/components/TextEditor/TextEditor.tsx +130 -0
  35. package/src/components/TextEditor/index.ts +9 -0
  36. package/src/components/TextEditor/theme.ts +29 -0
  37. package/src/components/index.ts +1 -0
  38. package/src/testing/replicator.ts +0 -1
@@ -0,0 +1,130 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { closeBrackets } from '@codemirror/autocomplete';
6
+ import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from '@codemirror/language';
7
+ import { EditorState, type Extension } from '@codemirror/state';
8
+ import { oneDarkHighlightStyle } from '@codemirror/theme-one-dark';
9
+ import { EditorView, placeholder } from '@codemirror/view';
10
+ import React, {
11
+ type KeyboardEvent,
12
+ forwardRef,
13
+ useEffect,
14
+ useImperativeHandle,
15
+ useState,
16
+ useCallback,
17
+ type HTMLAttributes,
18
+ } from 'react';
19
+ import { type StyleSpec } from 'style-mod';
20
+ import { yCollab } from 'y-codemirror.next';
21
+
22
+ import { useThemeContext } from '@dxos/react-ui';
23
+ import { YText } from '@dxos/text-model';
24
+
25
+ import { defaultStyles } from './theme';
26
+ import { type EditorModel, type EditorSlots } from '../../model';
27
+
28
+ export type CursorInfo = {
29
+ from: number;
30
+ to: number;
31
+ line: number;
32
+ lines: number;
33
+ after?: string;
34
+ };
35
+
36
+ export type TextEditorRef = {
37
+ editor: HTMLDivElement | null;
38
+ state?: EditorState;
39
+ view?: EditorView;
40
+ };
41
+
42
+ export type TextEditorProps = {
43
+ model?: EditorModel;
44
+ extensions?: Extension[];
45
+ theme?: {
46
+ [selector: string]: StyleSpec;
47
+ };
48
+ slots?: EditorSlots;
49
+ onKeyDown?: (event: KeyboardEvent, info: CursorInfo) => void;
50
+ } & Pick<HTMLAttributes<HTMLDivElement>, 'onBlur' | 'onFocus'>;
51
+
52
+ /**
53
+ * Simple text editor.
54
+ */
55
+ export const TextEditor = forwardRef<TextEditorRef, TextEditorProps>(
56
+ ({ model, extensions = [], theme = defaultStyles, slots = {}, onKeyDown, ...props }, forwardedRef) => {
57
+ const { id, content } = model ?? {};
58
+ const { themeMode } = useThemeContext();
59
+
60
+ const [parent, setParent] = useState<HTMLDivElement | null>(null);
61
+ const [state, setState] = useState<EditorState>();
62
+ const [view, setView] = useState<EditorView>();
63
+
64
+ // TODO(burdon): The ref may be instantiated before the view is created.
65
+ useImperativeHandle(
66
+ forwardedRef,
67
+ () => {
68
+ return {
69
+ editor: parent,
70
+ state,
71
+ view,
72
+ };
73
+ },
74
+ [view],
75
+ );
76
+
77
+ useEffect(() => {
78
+ if (!parent) {
79
+ return;
80
+ }
81
+
82
+ view?.destroy();
83
+
84
+ const state = EditorState.create({
85
+ doc: content?.toString(),
86
+ extensions: [
87
+ bracketMatching(),
88
+ closeBrackets(),
89
+ placeholder(slots.editor?.placeholder ?? ''),
90
+ EditorView.lineWrapping,
91
+
92
+ // Themes.
93
+ EditorView.theme(theme),
94
+ ...(themeMode === 'dark'
95
+ ? [syntaxHighlighting(oneDarkHighlightStyle)]
96
+ : [syntaxHighlighting(defaultHighlightStyle)]),
97
+
98
+ // Replication.
99
+ ...(content instanceof YText ? [yCollab(content, undefined)] : []),
100
+
101
+ // Custom.
102
+ ...extensions,
103
+ ],
104
+ });
105
+
106
+ setState(state);
107
+ setView(new EditorView({ state, parent }));
108
+
109
+ return () => {
110
+ view?.destroy();
111
+ setView(undefined);
112
+ setState(undefined);
113
+ };
114
+ }, [parent, content, themeMode]);
115
+
116
+ const handleKeyDown = useCallback(
117
+ (event: KeyboardEvent) => {
118
+ if (view) {
119
+ const { head, from, to } = view.state.selection.ranges[0];
120
+ const { number } = view.state.doc.lineAt(head);
121
+ const after = view.state.sliceDoc(from);
122
+ onKeyDown?.(event, { from, to, line: number, lines: view.state.doc.lines, after });
123
+ }
124
+ },
125
+ [view],
126
+ );
127
+
128
+ return <div key={id} ref={setParent} {...slots.root} onKeyDown={handleKeyDown} {...props} />;
129
+ },
130
+ );
@@ -0,0 +1,9 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ export { type StyleSpec } from 'style-mod';
6
+ export { tags } from '@lezer/highlight';
7
+
8
+ export * from './TextEditor';
9
+ export * from './theme';
@@ -0,0 +1,29 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import get from 'lodash.get';
6
+ import { type StyleSpec } from 'style-mod';
7
+
8
+ import { tailwindConfig, type TailwindConfig } from '@dxos/react-ui-theme';
9
+
10
+ const tokens: TailwindConfig['theme'] = tailwindConfig({}).theme;
11
+
12
+ /**
13
+ * https://codemirror.net/examples/styling
14
+ */
15
+ // TODO(burdon): If given a "theme" suffix, `__docgen` properties are added to the object.
16
+ export const defaultStyles: {
17
+ [selector: string]: StyleSpec;
18
+ } = {
19
+ '&.cm-focused': {
20
+ outline: 'none',
21
+ },
22
+ '.cm-placeholder': {
23
+ fontFamily: get(tokens, 'fontFamily.body', []).join(','),
24
+ },
25
+ '& .cm-scroller': {
26
+ fontFamily: get(tokens, 'fontFamily.body', []).join(','),
27
+ overflow: 'visible',
28
+ },
29
+ };
@@ -5,3 +5,4 @@
5
5
  export * from './Editor';
6
6
  export * from './Markdown';
7
7
  export * from './RichText';
8
+ export * from './TextEditor';
@@ -186,6 +186,5 @@ export type UseYjsModelOptions = {
186
186
 
187
187
  export const useYjsModel = ({ replicator, id, doc }: UseYjsModelOptions): EditorModel => {
188
188
  const peer = useMemo(() => replicator.createPeer(id, doc), [doc]);
189
-
190
189
  return peer;
191
190
  };