@dxos/react-ui-editor 0.4.2 → 0.4.3-main.085f4d3

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 (49) hide show
  1. package/dist/lib/browser/index.mjs +290 -212
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts +23 -0
  5. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/components/TextEditor/TextEditor.d.ts +0 -3
  7. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
  8. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +20 -0
  9. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
  10. package/dist/types/src/components/TextEditor/automerge.stories.d.ts +22 -2
  11. package/dist/types/src/components/TextEditor/automerge.stories.d.ts.map +1 -1
  12. package/dist/types/src/components/TextEditor/hooks.stories.d.ts +20 -0
  13. package/dist/types/src/components/TextEditor/hooks.stories.d.ts.map +1 -1
  14. package/dist/types/src/components/Toolbar/Toolbar.d.ts +7 -10
  15. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
  16. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +23 -6
  17. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
  18. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  19. package/dist/types/src/extensions/automerge/{semaphore.d.ts → sync.d.ts} +6 -5
  20. package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -0
  21. package/dist/types/src/extensions/index.d.ts +1 -0
  22. package/dist/types/src/extensions/index.d.ts.map +1 -1
  23. package/dist/types/src/extensions/modes.d.ts +11 -0
  24. package/dist/types/src/extensions/modes.d.ts.map +1 -0
  25. package/dist/types/src/index.d.ts +3 -3
  26. package/dist/types/src/index.d.ts.map +1 -1
  27. package/dist/types/src/styles/index.d.ts +1 -0
  28. package/dist/types/src/styles/index.d.ts.map +1 -1
  29. package/dist/types/src/styles/layout.d.ts +2 -0
  30. package/dist/types/src/styles/layout.d.ts.map +1 -0
  31. package/dist/types/src/translations.d.ts +20 -0
  32. package/dist/types/src/translations.d.ts.map +1 -0
  33. package/package.json +25 -25
  34. package/src/components/TextEditor/MarkdownEditor.stories.tsx +23 -17
  35. package/src/components/TextEditor/TextEditor.stories.tsx +11 -14
  36. package/src/components/TextEditor/TextEditor.tsx +32 -53
  37. package/src/components/TextEditor/automerge.stories.tsx +38 -44
  38. package/src/components/TextEditor/hooks.stories.tsx +15 -16
  39. package/src/components/Toolbar/Toolbar.stories.tsx +16 -12
  40. package/src/components/Toolbar/Toolbar.tsx +156 -129
  41. package/src/extensions/automerge/automerge.ts +5 -21
  42. package/src/extensions/automerge/{semaphore.ts → sync.ts} +32 -34
  43. package/src/extensions/index.ts +1 -0
  44. package/src/extensions/modes.ts +36 -0
  45. package/src/index.ts +4 -3
  46. package/src/styles/index.ts +1 -0
  47. package/src/styles/layout.ts +6 -0
  48. package/src/translations.ts +25 -0
  49. package/dist/types/src/extensions/automerge/semaphore.d.ts.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../../../../src/extensions/automerge/sync.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAInD,OAAO,EAGL,KAAK,UAAU,EACf,KAAK,KAAK,EAIX,MAAM,QAAQ,CAAC;AAIhB;;GAEG;AACH,qBAAa,MAAM;IAKf,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IALzB,OAAO,CAAC,QAAQ,CAAS;gBAIN,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,UAAU,CAAC,KAAK,CAAC;IAG5C,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO;IAe3C,cAAc,CAAC,IAAI,EAAE,UAAU;IAa/B,iBAAiB,CAAC,IAAI,EAAE,UAAU;CAenC"}
@@ -9,6 +9,7 @@ export * from './cursor';
9
9
  export * from './listener';
10
10
  export * from './markdown';
11
11
  export * from './mention';
12
+ export * from './modes';
12
13
  export * from './outliner';
13
14
  export * from './typewriter';
14
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/extensions/index.ts"],"names":[],"mappings":"AAIA,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/extensions/index.ts"],"names":[],"mappings":"AAIA,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { type Extension, Facet } from '@codemirror/state';
2
+ export type EditorMode = 'default' | 'vim' | undefined;
3
+ export type EditorConfig = {
4
+ type: string;
5
+ noTabster?: boolean;
6
+ };
7
+ export declare const editorMode: Facet<EditorConfig, EditorConfig>;
8
+ export declare const EditorModes: {
9
+ [mode: string]: Extension;
10
+ };
11
+ //# sourceMappingURL=modes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modes.d.ts","sourceRoot":"","sources":["../../../../src/extensions/modes.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAI1D,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,KAAK,GAAG,SAAS,CAAC;AAEvD,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,UAAU,mCAErB,CAAC;AAEH,eAAO,MAAM,WAAW,EAAE;IAAE,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;CAgBpD,CAAC"}
@@ -1,11 +1,11 @@
1
+ export { type Extension, type EditorState } from '@codemirror/state';
2
+ export { type EditorView, keymap } from '@codemirror/view';
1
3
  export { TextKind } from '@dxos/protocols/proto/dxos/echo/model/text';
2
4
  export { Doc, YText, YXmlFragment } from '@dxos/text-model';
3
- export { type Extension } from '@codemirror/state';
4
- export { type EditorView, keymap } from '@codemirror/view';
5
5
  export * from './components';
6
6
  export * from './extensions';
7
7
  export * from './hooks';
8
- export { getToken } from './styles';
8
+ export { getToken, editorWithToolbarLayout } from './styles';
9
9
  export * from './themes';
10
10
  export * from './util';
11
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,QAAQ,EAAE,MAAM,4CAA4C,CAAC;AACtE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE3D,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE3D,OAAO,EAAE,QAAQ,EAAE,MAAM,4CAA4C,CAAC;AACtE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAE5D,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAC7D,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC"}
@@ -1,4 +1,5 @@
1
1
  export * from './cursors';
2
2
  export * from './markdown';
3
3
  export * from './tokens';
4
+ export * from './layout';
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/styles/index.ts"],"names":[],"mappings":"AAIA,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/styles/index.ts"],"names":[],"mappings":"AAIA,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const editorWithToolbarLayout = "fixed inset-0 grid grid-cols-1 grid-rows-[min-content_1fr] justify-center content-start overflow-hidden";
2
+ //# sourceMappingURL=layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../../src/styles/layout.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,uBAAuB,4GACuE,CAAC"}
@@ -0,0 +1,20 @@
1
+ export declare const translationKey = "react-ui-editor";
2
+ declare const _default: {
3
+ 'en-US': {
4
+ "react-ui-editor": {
5
+ 'strong label': string;
6
+ 'emphasis label': string;
7
+ 'strikethrough label': string;
8
+ 'code label': string;
9
+ 'bullet label': string;
10
+ 'ordered label': string;
11
+ 'task label': string;
12
+ 'blockquote label': string;
13
+ 'codeblock label': string;
14
+ 'comment label': string;
15
+ 'heading label': string;
16
+ };
17
+ };
18
+ }[];
19
+ export default _default;
20
+ //# sourceMappingURL=translations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translations.d.ts","sourceRoot":"","sources":["../../../src/translations.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,cAAc,oBAAoB,CAAC;;;;;;;;;;;;;;;;;;AAEhD,wBAkBE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-editor",
3
- "version": "0.4.2",
3
+ "version": "0.4.3-main.085f4d3",
4
4
  "description": "Document editing experience within a DXOS shell.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -36,23 +36,23 @@
36
36
  "lodash.pick": "^4.4.0",
37
37
  "lodash.sortby": "^4.7.0",
38
38
  "style-mod": "^4.1.0",
39
- "@dxos/async": "0.4.2",
40
- "@dxos/context": "0.4.2",
41
- "@dxos/display-name": "0.4.2",
42
- "@dxos/debug": "0.4.2",
43
- "@dxos/echo-schema": "0.4.2",
44
- "@dxos/log": "0.4.2",
45
- "@dxos/invariant": "0.4.2",
46
- "@dxos/protocols": "0.4.2",
47
- "@dxos/react-ui": "0.4.2",
48
- "@dxos/react-async": "0.4.2",
49
- "@dxos/automerge": "0.4.2",
50
- "@dxos/react-ui-theme": "0.4.2",
51
- "@dxos/text-model": "0.4.2",
52
- "@dxos/util": "0.4.2"
39
+ "@dxos/async": "0.4.3-main.085f4d3",
40
+ "@dxos/context": "0.4.3-main.085f4d3",
41
+ "@dxos/automerge": "0.4.3-main.085f4d3",
42
+ "@dxos/debug": "0.4.3-main.085f4d3",
43
+ "@dxos/display-name": "0.4.3-main.085f4d3",
44
+ "@dxos/echo-schema": "0.4.3-main.085f4d3",
45
+ "@dxos/invariant": "0.4.3-main.085f4d3",
46
+ "@dxos/log": "0.4.3-main.085f4d3",
47
+ "@dxos/protocols": "0.4.3-main.085f4d3",
48
+ "@dxos/react-ui": "0.4.3-main.085f4d3",
49
+ "@dxos/react-async": "0.4.3-main.085f4d3",
50
+ "@dxos/react-ui-theme": "0.4.3-main.085f4d3",
51
+ "@dxos/text-model": "0.4.3-main.085f4d3",
52
+ "@dxos/util": "0.4.3-main.085f4d3"
53
53
  },
54
54
  "devDependencies": {
55
- "@automerge/automerge-repo-network-broadcastchannel": "^1.1.0",
55
+ "@automerge/automerge-repo-network-broadcastchannel": "^1.1.1",
56
56
  "@phosphor-icons/react": "^2.0.5",
57
57
  "@preact/signals-react": "^1.3.6",
58
58
  "@testing-library/dom": "^8.17.1",
@@ -76,20 +76,20 @@
76
76
  "vite-plugin-top-level-await": "^1.4.1",
77
77
  "vite-plugin-wasm": "^3.3.0",
78
78
  "vitest": "^1.2.1",
79
- "@dxos/config": "0.4.2",
80
- "@dxos/echo-typegen": "0.4.2",
81
- "@dxos/echo-signals": "0.4.2",
82
- "@dxos/random": "0.4.2",
83
- "@dxos/keyboard": "0.4.2",
84
- "@dxos/react-client": "0.4.2",
85
- "@dxos/react-ui": "0.4.2",
86
- "@dxos/storybook-utils": "0.4.2"
79
+ "@dxos/config": "0.4.3-main.085f4d3",
80
+ "@dxos/echo-signals": "0.4.3-main.085f4d3",
81
+ "@dxos/echo-typegen": "0.4.3-main.085f4d3",
82
+ "@dxos/keyboard": "0.4.3-main.085f4d3",
83
+ "@dxos/random": "0.4.3-main.085f4d3",
84
+ "@dxos/react-ui": "0.4.3-main.085f4d3",
85
+ "@dxos/react-client": "0.4.3-main.085f4d3",
86
+ "@dxos/storybook-utils": "0.4.3-main.085f4d3"
87
87
  },
88
88
  "peerDependencies": {
89
89
  "@phosphor-icons/react": "^2.0.5",
90
90
  "react": "^18.2.0",
91
91
  "react-dom": "^18.2.0",
92
- "@dxos/react-client": "0.4.2"
92
+ "@dxos/react-client": "0.4.3-main.085f4d3"
93
93
  },
94
94
  "publishConfig": {
95
95
  "access": "public"
@@ -3,7 +3,6 @@
3
3
  //
4
4
 
5
5
  import '@dxosTheme';
6
-
7
6
  import { type EditorView } from '@codemirror/view';
8
7
  import { ArrowSquareOut } from '@phosphor-icons/react';
9
8
  import defaultsDeep from 'lodash.defaultsdeep';
@@ -14,7 +13,7 @@ import { TextObject } from '@dxos/echo-schema';
14
13
  import { keySymbols, parseShortcut } from '@dxos/keyboard';
15
14
  import { PublicKey } from '@dxos/keys';
16
15
  import { faker } from '@dxos/random';
17
- import { fixedInsetFlexLayout, getSize, groupSurface, mx } from '@dxos/react-ui-theme';
16
+ import { getSize, mx, textBlockWidth } from '@dxos/react-ui-theme';
18
17
  import { withTheme } from '@dxos/storybook-utils';
19
18
 
20
19
  import { MarkdownEditor, type TextEditorProps } from './TextEditor';
@@ -37,8 +36,10 @@ import {
37
36
  useComments,
38
37
  formatting,
39
38
  annotations,
39
+ EditorModes,
40
40
  } from '../../extensions';
41
41
  import { type Comment, useTextModel } from '../../hooks';
42
+ import translations from '../../translations';
42
43
 
43
44
  faker.seed(101);
44
45
 
@@ -224,21 +225,16 @@ const Story = ({ text, comments, placeholder = 'New document.', ...props }: Stor
224
225
  }
225
226
 
226
227
  return (
227
- <div className={mx(fixedInsetFlexLayout, groupSurface)}>
228
- <div className='flex h-full justify-center'>
229
- <div className='flex flex-col h-full w-[800px] overflow-y-auto'>
230
- <MarkdownEditor
231
- ref={view}
232
- model={model}
233
- placeholder={placeholder}
234
- slots={{
235
- editor: { className: 'bs-full p-2 bg-white dark:bg-black' },
236
- }}
237
- {...props}
238
- />
239
- </div>
240
- </div>
241
- </div>
228
+ <MarkdownEditor
229
+ ref={view}
230
+ model={model}
231
+ placeholder={placeholder}
232
+ slots={{
233
+ root: { className: mx(textBlockWidth, 'min-bs-dvh') },
234
+ editor: { className: 'min-bs-dvh p-2 bg-white dark:bg-black' },
235
+ }}
236
+ {...props}
237
+ />
242
238
  );
243
239
  };
244
240
 
@@ -247,6 +243,7 @@ export default {
247
243
  component: MarkdownEditor,
248
244
  decorators: [withTheme],
249
245
  render: Story,
246
+ parameters: { translations, layout: 'fullscreen' },
250
247
  };
251
248
 
252
249
  const defaults = [
@@ -393,6 +390,15 @@ export const HorizontalRule = {
393
390
  ),
394
391
  };
395
392
 
393
+ export const Vim = {
394
+ render: () => (
395
+ <Story
396
+ text={str('# Vim Mode', '', 'The distant future. The year 2000.', '', text.paragraphs)}
397
+ extensions={[defaults, EditorModes.vim]}
398
+ />
399
+ ),
400
+ };
401
+
396
402
  export const Annotations = {
397
403
  render: () => <Story text={str('# Annotations', '', large)} extensions={[annotations({ match: /volup/gi })]} />,
398
404
  };
@@ -7,12 +7,13 @@ import '@dxosTheme';
7
7
  import React, { useState } from 'react';
8
8
 
9
9
  import { TextObject } from '@dxos/echo-schema';
10
- import { fixedInsetFlexLayout, groupSurface, mx } from '@dxos/react-ui-theme';
10
+ import { mx, textBlockWidth } from '@dxos/react-ui-theme';
11
11
  import { withTheme } from '@dxos/storybook-utils';
12
12
 
13
13
  import { TextEditor, type TextEditorProps } from './TextEditor';
14
14
  import { listener } from '../../extensions';
15
15
  import { useTextModel } from '../../hooks';
16
+ import translations from '../../translations';
16
17
 
17
18
  const initialText = [
18
19
  '# TextEditor',
@@ -36,19 +37,14 @@ const Story = ({
36
37
  }
37
38
 
38
39
  return (
39
- <div className={mx(fixedInsetFlexLayout, groupSurface)}>
40
- <div className='flex justify-center'>
41
- <div className='flex flex-col w-[800px] overflow-y-auto'>
42
- <TextEditor
43
- model={model}
44
- slots={{
45
- editor: { className: 'bs-full p-2 bg-white dark:bg-black' },
46
- }}
47
- {...props}
48
- />
49
- </div>
50
- </div>
51
- </div>
40
+ <TextEditor
41
+ model={model}
42
+ slots={{
43
+ root: { className: mx(textBlockWidth, 'min-bs-dvh') },
44
+ editor: { className: 'min-bs-dvh p-2 bg-white dark:bg-black' },
45
+ }}
46
+ {...props}
47
+ />
52
48
  );
53
49
  };
54
50
 
@@ -57,6 +53,7 @@ export default {
57
53
  component: TextEditor,
58
54
  decorators: [withTheme],
59
55
  render: Story,
56
+ parameters: { translations, layout: 'fullscreen' },
60
57
  };
61
58
 
62
59
  export const Default = {
@@ -4,13 +4,12 @@
4
4
 
5
5
  import { EditorState, type Extension, type StateEffect } from '@codemirror/state';
6
6
  import { EditorView } from '@codemirror/view';
7
- // import { useFocusableGroup } from '@fluentui/react-tabster';
8
- import { vim } from '@replit/codemirror-vim';
7
+ import { useFocusableGroup } from '@fluentui/react-tabster';
9
8
  import defaultsDeep from 'lodash.defaultsdeep';
10
9
  import React, {
11
10
  type ComponentProps,
12
- type KeyboardEvent,
13
11
  forwardRef,
12
+ type KeyboardEventHandler,
14
13
  useCallback,
15
14
  useEffect,
16
15
  useImperativeHandle,
@@ -20,19 +19,15 @@ import React, {
20
19
 
21
20
  import { log } from '@dxos/log';
22
21
  import { useThemeContext } from '@dxos/react-ui';
23
- import { focusRing, mx } from '@dxos/react-ui-theme';
22
+ import { focusRing } from '@dxos/react-ui-theme';
24
23
  import { isNotFalsy } from '@dxos/util';
25
24
 
26
- import { createBasicBundle, createMarkdownExtensions } from '../../extensions';
25
+ import { createBasicBundle, createMarkdownExtensions, editorMode } from '../../extensions';
27
26
  import { type EditorModel } from '../../hooks';
28
27
  import { type ThemeStyles } from '../../styles';
29
28
  import { defaultTheme, markdownTheme, textTheme } from '../../themes';
30
29
  import { logChanges } from '../../util';
31
30
 
32
- // TODO(burdon): Change to enum?
33
- export const EditorModes = ['default', 'vim'] as const;
34
- export type EditorMode = (typeof EditorModes)[number];
35
-
36
31
  export type CursorInfo = {
37
32
  from: number;
38
33
  to: number;
@@ -52,7 +47,6 @@ export type TextEditorSlots = {
52
47
  };
53
48
  };
54
49
 
55
- // TODO(burdon): Spellcheck?
56
50
  export type TextEditorProps = {
57
51
  model: EditorModel; // TODO(burdon): Optional (e.g., just provide content if readonly).
58
52
  readonly?: boolean; // TODO(burdon): Move into model.
@@ -60,7 +54,6 @@ export type TextEditorProps = {
60
54
  lineWrapping?: boolean;
61
55
  scrollTo?: StateEffect<any>; // TODO(burdon): Restore scroll position: scrollTo EditorView.scrollSnapshot().
62
56
  selection?: { anchor: number; head?: number };
63
- editorMode?: EditorMode; // TODO(burdon): Factor out.
64
57
  placeholder?: string;
65
58
  theme?: ThemeStyles;
66
59
  slots?: TextEditorSlots;
@@ -74,26 +67,10 @@ export type TextEditorProps = {
74
67
  // TODO(burdon): Replace with useTextEditor.
75
68
  export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
76
69
  (
77
- {
78
- model,
79
- readonly,
80
- autoFocus,
81
- scrollTo,
82
- selection,
83
- editorMode,
84
- theme,
85
- slots = defaultSlots,
86
- extensions = [],
87
- debug,
88
- },
70
+ { model, readonly, autoFocus, scrollTo, selection, theme, slots = defaultSlots, extensions = [], debug },
89
71
  forwardedRef,
90
72
  ) => {
91
- // TODO(burdon): Hook causes error even if properties are not spread into div.
92
- // Uncaught TypeError: Cannot read properties of undefined (reading 'relatedTarget')
93
- // Uses event.detail, which is deprecated (not event.details). At runtime the event has a property `details`.
94
- // https://github.com/microsoft/tabster/blob/master/src/State/FocusedElement.ts#L348 (e.detail.relatedTarget)
95
- // https://github.com/microsoft/keyborg/blob/49e49b2c3ba0a5f6cc518ac46825d7551def8109/src/FocusEvent.ts#L58
96
- // const tabsterDOMAttribute = useFocusableGroup({ tabBehavior: 'limited' });
73
+ const tabsterDOMAttribute = useFocusableGroup({ tabBehavior: 'limited' });
97
74
  const { themeMode } = useThemeContext();
98
75
 
99
76
  const rootRef = useRef<HTMLDivElement>(null);
@@ -143,6 +120,15 @@ export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
143
120
  EditorView.editorAttributes.of({ class: slots.editor?.className ?? '' }),
144
121
  EditorView.contentAttributes.of({ class: slots.content?.className ?? '' }),
145
122
 
123
+ // Focus.
124
+ EditorView.updateListener.of((update) => {
125
+ update.transactions.forEach((transaction) => {
126
+ if (transaction.isUserEvent('focus.container')) {
127
+ rootRef.current?.focus();
128
+ }
129
+ });
130
+ }),
131
+
146
132
  // State.
147
133
  EditorView.editable.of(!readonly),
148
134
  EditorState.readOnly.of(!!readonly),
@@ -151,9 +137,6 @@ export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
151
137
  // NOTE: This must come before user extensions.
152
138
  model.extension,
153
139
 
154
- // TODO(burdon): Factor out? (Requires special handling for Escape/Enter below).
155
- editorMode === 'vim' && vim(),
156
-
157
140
  // Custom.
158
141
  ...extensions,
159
142
  ].filter(isNotFalsy),
@@ -180,44 +163,40 @@ export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
180
163
  view?.destroy();
181
164
  setView(newView);
182
165
 
166
+ // Remove tabster attribute (rely on custom keymap).
167
+ if (state.facet(editorMode).noTabster) {
168
+ rootRef.current.removeAttribute('data-tabster');
169
+ }
170
+
183
171
  return () => {
184
172
  newView?.destroy();
185
173
  setView(null);
186
174
  };
187
- }, [rootRef, model, readonly, editorMode, themeMode]);
175
+ }, [rootRef, model, readonly, themeMode]);
188
176
 
189
- // Handles tab/focus.
190
- // Pressing Escape focuses the outer div (to support tab navigation); pressing Enter refocuses the editor.
191
- // TODO(burdon): Convert to keymap?
192
- const handleKeyUp = useCallback(
193
- (event: KeyboardEvent) => {
194
- const { key, altKey, shiftKey, metaKey, ctrlKey } = event;
177
+ // Focus editor on Enter (e.g., when tabbing to this component).
178
+ const handleKeyUp = useCallback<KeyboardEventHandler<HTMLDivElement>>(
179
+ (event) => {
180
+ const { key } = event;
195
181
  switch (key) {
196
182
  case 'Enter': {
197
183
  view?.focus();
198
184
  break;
199
185
  }
200
-
201
- case 'Escape': {
202
- if (editorMode === 'vim' && (altKey || shiftKey || metaKey || ctrlKey)) {
203
- view?.focus();
204
- }
205
- break;
206
- }
207
186
  }
208
187
  },
209
- [view, editorMode],
188
+ [view],
210
189
  );
211
190
 
212
191
  return (
213
192
  <div
214
- key={model.id}
215
193
  role='none'
194
+ ref={rootRef}
195
+ key={model.id}
216
196
  tabIndex={0}
217
- {...slots.root}
218
- // {...(editorMode !== 'vim' && tabsterDOMAttribute)}
219
197
  onKeyUp={handleKeyUp}
220
- ref={rootRef}
198
+ {...slots.root}
199
+ {...tabsterDOMAttribute}
221
200
  />
222
201
  );
223
202
  },
@@ -259,10 +238,10 @@ export const MarkdownEditor = forwardRef<EditorView, TextEditorProps>(
259
238
 
260
239
  export const defaultSlots: TextEditorSlots = {
261
240
  root: {
262
- className: mx('grow', focusRing),
241
+ className: focusRing,
263
242
  },
264
243
  editor: {
265
- className: 'bs-full',
244
+ className: 'min-bs-full',
266
245
  },
267
246
  };
268
247
 
@@ -5,11 +5,10 @@
5
5
  import '@dxosTheme';
6
6
 
7
7
  import { BroadcastChannelNetworkAdapter } from '@automerge/automerge-repo-network-broadcastchannel';
8
- import { EditorView } from '@codemirror/view';
9
- import '@preact/signals-react'; // Register react integration
10
- import { basicSetup } from 'codemirror';
8
+ import '@preact/signals-react';
9
+ import { EditorView } from '@codemirror/view'; // Register react integration.
11
10
  import get from 'lodash.get';
12
- import React, { useEffect, useRef, useState } from 'react';
11
+ import React, { useEffect, useState } from 'react';
13
12
 
14
13
  import { type Prop } from '@dxos/automerge/automerge';
15
14
  import { Repo, type DocHandle } from '@dxos/automerge/automerge-repo';
@@ -21,38 +20,42 @@ import { ClientRepeater } from '@dxos/react-client/testing';
21
20
  import { withTheme } from '@dxos/storybook-utils';
22
21
 
23
22
  import { MarkdownEditor } from './TextEditor';
24
- import { type IDocHandle, automerge, awareness } from '../../extensions';
25
- import { useTextModel } from '../../hooks';
23
+ import { type IDocHandle, automerge, awareness, createBasicBundle } from '../../extensions';
24
+ import { useTextEditor, useTextModel } from '../../hooks';
25
+ import { defaultTheme } from '../../themes';
26
+ import translations from '../../translations';
26
27
 
27
- // TODO(burdon): Move to components.
28
+ const initialContent = 'Hello world!';
29
+
30
+ type TestObject = {
31
+ text: string;
32
+ };
28
33
 
29
34
  type EditorProps = {
30
35
  handle: IDocHandle;
31
- path: Prop[];
36
+ path?: Prop[];
32
37
  };
33
38
 
34
- const Editor = ({ handle, path }: EditorProps) => {
35
- const containerRef = useRef<HTMLDivElement | null>(null);
36
- const editorRoot = useRef<EditorView>();
37
-
38
- useEffect(() => {
39
- const view = (editorRoot.current = new EditorView({
40
- doc: get(handle.docSync()!, path),
41
- extensions: [basicSetup, automerge({ handle, path }), awareness()],
42
- parent: containerRef.current as any,
43
- }));
44
-
45
- return () => {
46
- view.destroy();
47
- };
48
- }, []);
39
+ const Editor = ({ handle, path = ['text'] }: EditorProps) => {
40
+ const { parentRef } = useTextEditor({
41
+ autoFocus: true,
42
+ doc: get(handle.docSync()!, path),
43
+ extensions: [
44
+ //
45
+ EditorView.baseTheme(defaultTheme),
46
+ EditorView.editorAttributes.of({ class: 'p-2 bg-white' }),
47
+ createBasicBundle({ placeholder: 'Type here...' }),
48
+ automerge({ handle, path }),
49
+ awareness(),
50
+ ],
51
+ });
49
52
 
50
- return <div className='codemirror-editor' ref={containerRef} onKeyDown={(evt) => evt.stopPropagation()} />;
53
+ return <div ref={parentRef} />;
51
54
  };
52
55
 
53
56
  const Story = () => {
54
- const [object1, setObject1] = useState<DocHandle<any> | null>(null);
55
- const [object2, setObject2] = useState<DocHandle<any> | null>(null);
57
+ const [object1, setObject1] = useState<DocHandle<TestObject>>();
58
+ const [object2, setObject2] = useState<DocHandle<TestObject>>();
56
59
 
57
60
  useEffect(() => {
58
61
  queueMicrotask(async () => {
@@ -64,8 +67,8 @@ const Story = () => {
64
67
  });
65
68
 
66
69
  const object1 = repo1.create();
67
- object1.change((doc: any) => {
68
- doc.text = 'Hello world!';
70
+ object1.change((doc: TestObject) => {
71
+ doc.text = initialContent;
69
72
  });
70
73
 
71
74
  const object2 = repo2.find(object1.url);
@@ -81,13 +84,9 @@ const Story = () => {
81
84
  }
82
85
 
83
86
  return (
84
- <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', width: '100vw' }}>
85
- <div>
86
- <Editor handle={object1} path={['text']} />
87
- </div>
88
- <div>
89
- <Editor handle={object2} path={['text']} />
90
- </div>
87
+ <div role='none' className='grid grid-cols-2 bs-full is-full gap-2'>
88
+ <Editor handle={object1} path={['text']} />
89
+ <Editor handle={object2} path={['text']} />
91
90
  </div>
92
91
  );
93
92
  };
@@ -96,6 +95,7 @@ export default {
96
95
  title: 'react-ui-editor/Automerge',
97
96
  component: Editor,
98
97
  render: () => <Story />,
98
+ parameters: { translations, layout: 'fullscreen' },
99
99
  };
100
100
 
101
101
  export const Default = {};
@@ -115,16 +115,11 @@ const EchoStory = ({ spaceKey }: { spaceKey: PublicKey }) => {
115
115
  return null;
116
116
  }
117
117
 
118
- return (
119
- <div className='flex overflow-hidden'>
120
- <div className='flex flex-col w-[800px] overflow-y-scroll'>
121
- <MarkdownEditor model={model} />
122
- </div>
123
- </div>
124
- );
118
+ return <MarkdownEditor model={model} />;
125
119
  };
126
120
 
127
121
  export const WithEcho = {
122
+ decorators: [withTheme],
128
123
  render: () => {
129
124
  return (
130
125
  <ClientRepeater
@@ -134,7 +129,7 @@ export const WithEcho = {
134
129
  space.db.add(
135
130
  new Expando({
136
131
  type: 'test',
137
- content: new TextObject('Hello world!'),
132
+ content: new TextObject(initialContent),
138
133
  }),
139
134
  );
140
135
  }}
@@ -142,5 +137,4 @@ export const WithEcho = {
142
137
  />
143
138
  );
144
139
  },
145
- decorators: [withTheme],
146
140
  };