@dxos/react-ui-editor 0.6.5 → 0.6.6-staging.23d123d

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 (70) hide show
  1. package/dist/lib/browser/index.mjs +316 -324
  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/Toolbar/Toolbar.d.ts +5 -1
  5. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
  6. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +7 -0
  7. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
  8. package/dist/types/src/components/index.d.ts +0 -1
  9. package/dist/types/src/components/index.d.ts.map +1 -1
  10. package/dist/types/src/extensions/autocomplete.d.ts +2 -1
  11. package/dist/types/src/extensions/autocomplete.d.ts.map +1 -1
  12. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  13. package/dist/types/src/extensions/automerge/automerge.stories.d.ts +9 -2
  14. package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
  15. package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
  16. package/dist/types/src/extensions/awareness/awareness-provider.d.ts.map +1 -1
  17. package/dist/types/src/extensions/awareness/awareness.d.ts +6 -6
  18. package/dist/types/src/extensions/awareness/awareness.d.ts.map +1 -1
  19. package/dist/types/src/extensions/comments.d.ts +1 -2
  20. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  21. package/dist/types/src/extensions/cursor.d.ts +1 -1
  22. package/dist/types/src/extensions/cursor.d.ts.map +1 -1
  23. package/dist/types/src/extensions/debug.d.ts +3 -0
  24. package/dist/types/src/extensions/debug.d.ts.map +1 -0
  25. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  26. package/dist/types/src/extensions/index.d.ts +1 -0
  27. package/dist/types/src/extensions/index.d.ts.map +1 -1
  28. package/dist/types/src/extensions/markdown/action.d.ts +1 -1
  29. package/dist/types/src/extensions/markdown/action.d.ts.map +1 -1
  30. package/dist/types/src/extensions/modes.d.ts +7 -4
  31. package/dist/types/src/extensions/modes.d.ts.map +1 -1
  32. package/dist/types/src/hooks/{useTextEditor.stories.d.ts → InputMode.stories.d.ts} +9 -9
  33. package/dist/types/src/hooks/InputMode.stories.d.ts.map +1 -0
  34. package/dist/types/src/{components/TextEditor → hooks}/TextEditor.stories.d.ts +8 -16
  35. package/dist/types/src/hooks/TextEditor.stories.d.ts.map +1 -0
  36. package/dist/types/src/hooks/useTextEditor.d.ts +20 -3
  37. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  38. package/dist/types/src/themes/default.d.ts.map +1 -1
  39. package/dist/types/src/translations.d.ts +4 -0
  40. package/dist/types/src/translations.d.ts.map +1 -1
  41. package/package.json +27 -27
  42. package/src/components/Toolbar/Toolbar.stories.tsx +18 -8
  43. package/src/components/Toolbar/Toolbar.tsx +93 -3
  44. package/src/components/index.ts +0 -1
  45. package/src/extensions/autocomplete.ts +3 -3
  46. package/src/extensions/automerge/automerge.stories.tsx +25 -18
  47. package/src/extensions/automerge/automerge.ts +2 -0
  48. package/src/extensions/automerge/cursor.ts +3 -4
  49. package/src/extensions/awareness/awareness-provider.ts +2 -0
  50. package/src/extensions/awareness/awareness.ts +34 -30
  51. package/src/extensions/comments.ts +7 -14
  52. package/src/extensions/cursor.ts +1 -1
  53. package/src/extensions/debug.ts +15 -0
  54. package/src/extensions/factories.ts +19 -13
  55. package/src/extensions/index.ts +1 -0
  56. package/src/extensions/markdown/action.ts +1 -0
  57. package/src/extensions/modes.ts +9 -6
  58. package/src/hooks/{useTextEditor.stories.tsx → InputMode.stories.tsx} +41 -47
  59. package/src/{components/TextEditor → hooks}/TextEditor.stories.tsx +24 -30
  60. package/src/hooks/useTextEditor.ts +75 -23
  61. package/src/themes/default.ts +20 -4
  62. package/src/translations.ts +4 -0
  63. package/dist/types/src/components/TextEditor/TextEditor.d.ts +0 -34
  64. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +0 -1
  65. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +0 -1
  66. package/dist/types/src/components/TextEditor/index.d.ts +0 -2
  67. package/dist/types/src/components/TextEditor/index.d.ts.map +0 -1
  68. package/dist/types/src/hooks/useTextEditor.stories.d.ts.map +0 -1
  69. package/src/components/TextEditor/TextEditor.tsx +0 -184
  70. package/src/components/TextEditor/index.ts +0 -5
@@ -1,5 +1,5 @@
1
1
  import { type EditorView } from '@codemirror/view';
2
- export type ActionType = 'blockquote' | 'strong' | 'codeblock' | 'comment' | 'heading' | 'image' | 'emphasis' | 'code' | 'link' | 'list-bullet' | 'list-ordered' | 'list-task' | 'mention' | 'prompt' | 'strikethrough' | 'table';
2
+ export type ActionType = 'view-mode' | 'blockquote' | 'strong' | 'codeblock' | 'comment' | 'heading' | 'image' | 'emphasis' | 'code' | 'link' | 'list-bullet' | 'list-ordered' | 'list-task' | 'mention' | 'prompt' | 'strikethrough' | 'table';
3
3
  export type Action = {
4
4
  type: ActionType;
5
5
  data?: any;
@@ -1 +1 @@
1
- {"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../../../../../src/extensions/markdown/action.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAsBnD,MAAM,MAAM,UAAU,GAClB,YAAY,GACZ,QAAQ,GACR,WAAW,GACX,SAAS,GACT,SAAS,GACT,OAAO,GACP,UAAU,GACV,MAAM,GACN,MAAM,GACN,aAAa,GACb,cAAc,GACd,WAAW,GACX,SAAS,GACT,QAAQ,GACR,eAAe,GACf,OAAO,CAAC;AAEZ,MAAM,MAAM,MAAM,GAAG;IACnB,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;AAEvE,eAAO,MAAM,aAAa,EAAE,aA4D3B,CAAC"}
1
+ {"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../../../../../src/extensions/markdown/action.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAsBnD,MAAM,MAAM,UAAU,GAClB,WAAW,GACX,YAAY,GACZ,QAAQ,GACR,WAAW,GACX,SAAS,GACT,SAAS,GACT,OAAO,GACP,UAAU,GACV,MAAM,GACN,MAAM,GACN,aAAa,GACb,cAAc,GACd,WAAW,GACX,SAAS,GACT,QAAQ,GACR,eAAe,GACf,OAAO,CAAC;AAEZ,MAAM,MAAM,MAAM,GAAG;IACnB,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;AAEvE,eAAO,MAAM,aAAa,EAAE,aA4D3B,CAAC"}
@@ -1,12 +1,15 @@
1
1
  import { type Extension, Facet } from '@codemirror/state';
2
2
  export declare const focusEvent = "focus.container";
3
- export type EditorMode = 'default' | 'vim' | 'vscode' | undefined;
4
- export type EditorConfig = {
3
+ export declare const EditorViewModes: readonly ["preview", "readonly", "source"];
4
+ export type EditorViewMode = (typeof EditorViewModes)[number];
5
+ export declare const EditorInputModes: readonly ["default", "vim", "vscode"];
6
+ export type EditorInputMode = (typeof EditorInputModes)[number];
7
+ export type EditorInputConfig = {
5
8
  type: string;
6
9
  noTabster?: boolean;
7
10
  };
8
- export declare const editorMode: Facet<EditorConfig, EditorConfig>;
9
- export declare const EditorModes: {
11
+ export declare const editorInputMode: Facet<EditorInputConfig, EditorInputConfig>;
12
+ export declare const InputModeExtensions: {
10
13
  [mode: string]: Extension;
11
14
  };
12
15
  //# sourceMappingURL=modes.d.ts.map
@@ -1 +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;AAK1D,eAAO,MAAM,UAAU,oBAAoB,CAAC;AAE5C,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,SAAS,CAAC;AAElE,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;CAsBpD,CAAC"}
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;AAK1D,eAAO,MAAM,UAAU,oBAAoB,CAAC;AAE5C,eAAO,MAAM,eAAe,4CAA6C,CAAC;AAC1E,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAC9D,eAAO,MAAM,gBAAgB,uCAAwC,CAAC;AACtE,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,eAAe,6CAE1B,CAAC;AAEH,eAAO,MAAM,mBAAmB,EAAE;IAAE,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;CAsB5D,CAAC"}
@@ -1,15 +1,13 @@
1
1
  /// <reference types="react" />
2
2
  import '@dxosTheme';
3
+ import { type UseTextEditorProps } from './useTextEditor';
3
4
  type StoryProps = {
4
- autoFocus?: boolean;
5
5
  placeholder?: string;
6
- doc?: string;
7
6
  readonly?: boolean;
8
- };
7
+ } & UseTextEditorProps;
9
8
  declare const _default: {
10
9
  title: string;
11
10
  decorators: import("@storybook/react/*").Decorator[];
12
- render: (args: StoryProps) => JSX.Element;
13
11
  parameters: {
14
12
  translations: {
15
13
  'en-US': {
@@ -31,24 +29,26 @@ declare const _default: {
31
29
  'heading level label_zero': string;
32
30
  'heading level label_one': string;
33
31
  'heading level label_other': string;
32
+ 'view mode label': string;
33
+ 'preview mode label': string;
34
+ 'readonly mode label': string;
35
+ 'source mode label': string;
34
36
  };
35
37
  };
36
38
  }[];
37
39
  layout: string;
38
40
  };
41
+ render: ({ autoFocus, initialValue, placeholder, readonly }: StoryProps) => JSX.Element;
39
42
  };
40
43
  export default _default;
41
44
  export declare const Default: {
42
45
  render: () => JSX.Element;
43
46
  };
44
- export declare const Basic: {
45
- render: () => JSX.Element;
46
- };
47
47
  export declare const Markdown: {
48
48
  args: {
49
49
  autoFocus: boolean;
50
50
  placeholder: string;
51
- doc: string;
51
+ initialValue: string;
52
52
  };
53
53
  };
54
- //# sourceMappingURL=useTextEditor.stories.d.ts.map
54
+ //# sourceMappingURL=InputMode.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InputMode.stories.d.ts","sourceRoot":"","sources":["../../../../src/hooks/InputMode.stories.tsx"],"names":[],"mappings":";AAIA,OAAO,YAAY,CAAC;AASpB,OAAO,EAAiB,KAAK,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAgBzE,KAAK,UAAU,GAAG;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,kBAAkB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4EpF,wBAKE;AAEF,eAAO,MAAM,OAAO;;CAanB,CAAC;AAEF,eAAO,MAAM,QAAQ;;;;;;CAMpB,CAAC"}
@@ -1,28 +1,16 @@
1
+ /// <reference types="react" />
1
2
  import '@dxosTheme';
2
- import { type EditorView } from 'codemirror';
3
- import React from 'react';
4
- import { type TextEditorProps } from './TextEditor';
5
- import { type Comment } from '../../extensions';
3
+ import { type UseTextEditorProps } from './useTextEditor';
6
4
  type StoryProps = {
7
5
  id?: string;
8
6
  text?: string;
9
- comments?: Comment[];
10
7
  readonly?: boolean;
11
8
  placeholder?: string;
12
- } & Pick<TextEditorProps, 'selection' | 'extensions'>;
9
+ } & Pick<UseTextEditorProps, 'selection' | 'extensions'>;
13
10
  declare const _default: {
14
11
  title: string;
15
- component: React.ForwardRefExoticComponent<Pick<import("@codemirror/state").EditorStateConfig, "doc" | "selection" | "extensions"> & {
16
- id?: string | undefined;
17
- className?: string | undefined;
18
- autoFocus?: boolean | undefined;
19
- scrollTo?: import("@codemirror/state").StateEffect<unknown> | undefined;
20
- moveToEndOfLine?: boolean | undefined;
21
- debug?: boolean | undefined;
22
- dataTestId?: string | undefined;
23
- } & React.RefAttributes<EditorView | null>>;
24
12
  decorators: import("@storybook/react/*").Decorator[];
25
- render: ({ id, text, comments, extensions: _extensions, readonly, placeholder, ...props }: StoryProps) => JSX.Element;
13
+ render: ({ id, text, extensions: _extensions, readonly, placeholder, selection, }: StoryProps) => JSX.Element;
26
14
  parameters: {
27
15
  translations: {
28
16
  'en-US': {
@@ -44,6 +32,10 @@ declare const _default: {
44
32
  'heading level label_zero': string;
45
33
  'heading level label_one': string;
46
34
  'heading level label_other': string;
35
+ 'view mode label': string;
36
+ 'preview mode label': string;
37
+ 'readonly mode label': string;
38
+ 'source mode label': string;
47
39
  };
48
40
  };
49
41
  }[];
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TextEditor.stories.d.ts","sourceRoot":"","sources":["../../../../src/hooks/TextEditor.stories.tsx"],"names":[],"mappings":";AAIA,OAAO,YAAY,CAAC;AAmBpB,OAAO,EAAiB,KAAK,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AA0NzE,KAAK,UAAU,GAAG;IAChB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,IAAI,CAAC,kBAAkB,EAAE,WAAW,GAAG,YAAY,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCzD,wBAKE;AAeF,eAAO,MAAM,OAAO;;CAEnB,CAAC;AAEF,eAAO,MAAM,QAAQ;;CAEpB,CAAC;AAEF,eAAO,MAAM,YAAY;;CAExB,CAAC;AASF,eAAO,MAAM,KAAK;;CAEjB,CAAC;AAIF,eAAO,MAAM,SAAS;;CAUrB,CAAC;AAEF,eAAO,MAAM,mBAAmB;;CAE/B,CAAC;AAEF,eAAO,MAAM,KAAK;;CAEjB,CAAC;AAEF,eAAO,MAAM,KAAK;;CAEjB,CAAC;AAEF,eAAO,MAAM,IAAI;;CAEhB,CAAC;AAEF,eAAO,MAAM,KAAK;;CAIjB,CAAC;AAEF,eAAO,MAAM,KAAK;;CAEjB,CAAC;AAEF,eAAO,MAAM,YAAY;;CAYxB,CAAC;AAEF,eAAO,MAAM,YAAY;;CAWxB,CAAC;AAEF,eAAO,MAAM,OAAO;;CAWnB,CAAC;AAmDF,eAAO,MAAM,OAAO;;CAOnB,CAAC;AAEF,eAAO,MAAM,QAAQ;;CAsCpB,CAAC;AAEF,eAAO,MAAM,GAAG;;CAOf,CAAC;AAEF,eAAO,MAAM,WAAW;;CAEvB,CAAC;AAEF,eAAO,MAAM,GAAG;;CAaf,CAAC;AAIF,eAAO,MAAM,QAAQ;;CAgBpB,CAAC;AAEF,eAAO,MAAM,UAAU;;CAOtB,CAAC;AAEF,eAAO,MAAM,KAAK;;CAqBjB,CAAC"}
@@ -1,7 +1,8 @@
1
+ import { type EditorStateConfig, type StateEffect } from '@codemirror/state';
1
2
  import { EditorView } from '@codemirror/view';
2
3
  import { useFocusableGroup } from '@fluentui/react-tabster';
3
4
  import { type DependencyList, type KeyboardEventHandler, type RefObject } from 'react';
4
- import { type TextEditorProps } from '../components';
5
+ import { type MaybeFunction } from '@dxos/util';
5
6
  export type UseTextEditor = {
6
7
  parentRef: RefObject<HTMLDivElement>;
7
8
  view?: EditorView;
@@ -10,9 +11,25 @@ export type UseTextEditor = {
10
11
  onKeyUp: KeyboardEventHandler<HTMLDivElement>;
11
12
  };
12
13
  };
13
- export type UseTextEditorProps = Omit<TextEditorProps, 'moveToEndOfLine' | 'dataTestId'>;
14
+ export type CursorInfo = {
15
+ from: number;
16
+ to: number;
17
+ line: number;
18
+ lines: number;
19
+ length: number;
20
+ after?: string;
21
+ };
22
+ export type UseTextEditorProps = Pick<EditorStateConfig, 'selection' | 'extensions'> & {
23
+ id?: string;
24
+ initialValue?: string;
25
+ className?: string;
26
+ autoFocus?: boolean;
27
+ scrollTo?: StateEffect<unknown>;
28
+ moveToEndOfLine?: boolean;
29
+ debug?: boolean;
30
+ };
14
31
  /**
15
32
  * Hook for creating editor.
16
33
  */
17
- export declare const useTextEditor: (cb?: () => UseTextEditorProps, deps?: DependencyList) => UseTextEditor;
34
+ export declare const useTextEditor: (props?: MaybeFunction<UseTextEditorProps>, deps?: DependencyList) => UseTextEditor;
18
35
  //# sourceMappingURL=useTextEditor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useTextEditor.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useTextEditor.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,KAAK,SAAS,EAMf,MAAM,OAAO,CAAC;AAKf,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAIrD,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,EAAE,SAAS,CAAC,cAAc,CAAC,CAAC;IACrC,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,eAAe,EAAE,UAAU,CAAC,OAAO,iBAAiB,CAAC,GAAG;QACtD,QAAQ,EAAE,CAAC,CAAC;QACZ,OAAO,EAAE,oBAAoB,CAAC,cAAc,CAAC,CAAC;KAC/C,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,eAAe,EAAE,iBAAiB,GAAG,YAAY,CAAC,CAAC;AAEzF;;GAEG;AACH,eAAO,MAAM,aAAa,QAAQ,MAAM,kBAAkB,4BAA2C,aA+FpG,CAAC"}
1
+ {"version":3,"file":"useTextEditor.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useTextEditor.ts"],"names":[],"mappings":"AAIA,OAAO,EAAe,KAAK,iBAAiB,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC1F,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,KAAK,SAAS,EAMf,MAAM,OAAO,CAAC;AAIf,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAK5D,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,EAAE,SAAS,CAAC,cAAc,CAAC,CAAC;IACrC,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,eAAe,EAAE,UAAU,CAAC,OAAO,iBAAiB,CAAC,GAAG;QACtD,QAAQ,EAAE,CAAC,CAAC;QACZ,OAAO,EAAE,oBAAoB,CAAC,cAAc,CAAC,CAAC;KAC/C,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,iBAAiB,EAAE,WAAW,GAAG,YAAY,CAAC,GAAG;IACrF,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IAChC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAIF;;GAEG;AACH,eAAO,MAAM,aAAa,WACjB,cAAc,kBAAkB,CAAC,4BAEvC,aA6HF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"default.d.ts","sourceRoot":"","sources":["../../../../src/themes/default.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,WAAW,EAAU,MAAM,WAAW,CAAC;AAKrD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,YAAY,EAAE,WAgQ1B,CAAC"}
1
+ {"version":3,"file":"default.d.ts","sourceRoot":"","sources":["../../../../src/themes/default.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,WAAW,EAAU,MAAM,WAAW,CAAC;AAKrD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,YAAY,EAAE,WAgR1B,CAAC"}
@@ -19,6 +19,10 @@ declare const _default: {
19
19
  'heading level label_zero': string;
20
20
  'heading level label_one': string;
21
21
  'heading level label_other': string;
22
+ 'view mode label': string;
23
+ 'preview mode label': string;
24
+ 'readonly mode label': string;
25
+ 'source mode label': string;
22
26
  };
23
27
  };
24
28
  }[];
@@ -1 +1 @@
1
- {"version":3,"file":"translations.d.ts","sourceRoot":"","sources":["../../../src/translations.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,cAAc,oBAAoB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;AAEhD,wBAwBE"}
1
+ {"version":3,"file":"translations.d.ts","sourceRoot":"","sources":["../../../src/translations.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,cAAc,oBAAoB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEhD,wBA4BE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-editor",
3
- "version": "0.6.5",
3
+ "version": "0.6.6-staging.23d123d",
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",
@@ -21,7 +21,7 @@
21
21
  "src"
22
22
  ],
23
23
  "dependencies": {
24
- "@codemirror/autocomplete": "^6.17.0",
24
+ "@codemirror/autocomplete": "^6.18.0",
25
25
  "@codemirror/commands": "^6.6.0",
26
26
  "@codemirror/lang-javascript": "^6.2.2",
27
27
  "@codemirror/lang-markdown": "^6.2.5",
@@ -35,7 +35,7 @@
35
35
  "@fluentui/react-tabster": "^9.19.0",
36
36
  "@lezer/common": "^1.2.1",
37
37
  "@lezer/highlight": "^1.2.0",
38
- "@lezer/markdown": "^1.2.0",
38
+ "@lezer/markdown": "^1.3.0",
39
39
  "@radix-ui/react-context": "^1.0.0",
40
40
  "@replit/codemirror-vim": "^6.0.14",
41
41
  "@replit/codemirror-vscode-keymap": "^6.0.2",
@@ -47,19 +47,19 @@
47
47
  "lodash.sortby": "^4.7.0",
48
48
  "react-dropzone": "^14.2.3",
49
49
  "style-mod": "^4.1.0",
50
- "@dxos/async": "0.6.5",
51
- "@dxos/context": "0.6.5",
52
- "@dxos/debug": "0.6.5",
53
- "@dxos/display-name": "0.6.5",
54
- "@dxos/echo-schema": "0.6.5",
55
- "@dxos/automerge": "0.6.5",
56
- "@dxos/log": "0.6.5",
57
- "@dxos/invariant": "0.6.5",
58
- "@dxos/protocols": "0.6.5",
59
- "@dxos/react-ui-theme": "0.6.5",
60
- "@dxos/react-ui": "0.6.5",
61
- "@dxos/react-async": "0.6.5",
62
- "@dxos/util": "0.6.5"
50
+ "@dxos/async": "0.6.6-staging.23d123d",
51
+ "@dxos/context": "0.6.6-staging.23d123d",
52
+ "@dxos/automerge": "0.6.6-staging.23d123d",
53
+ "@dxos/debug": "0.6.6-staging.23d123d",
54
+ "@dxos/echo-schema": "0.6.6-staging.23d123d",
55
+ "@dxos/display-name": "0.6.6-staging.23d123d",
56
+ "@dxos/protocols": "0.6.6-staging.23d123d",
57
+ "@dxos/log": "0.6.6-staging.23d123d",
58
+ "@dxos/react-ui": "0.6.6-staging.23d123d",
59
+ "@dxos/invariant": "0.6.6-staging.23d123d",
60
+ "@dxos/react-async": "0.6.6-staging.23d123d",
61
+ "@dxos/react-ui-theme": "0.6.6-staging.23d123d",
62
+ "@dxos/util": "0.6.6-staging.23d123d"
63
63
  },
64
64
  "devDependencies": {
65
65
  "@phosphor-icons/react": "^2.1.5",
@@ -85,22 +85,22 @@
85
85
  "vite-plugin-top-level-await": "^1.4.1",
86
86
  "vite-plugin-wasm": "^3.3.0",
87
87
  "vitest": "^1.5.0",
88
- "@dxos/automerge": "0.6.5",
89
- "@dxos/config": "0.6.5",
90
- "@dxos/echo-signals": "0.6.5",
91
- "@dxos/keyboard": "0.6.5",
92
- "@dxos/echo-typegen": "0.6.5",
93
- "@dxos/react-ui": "0.6.5",
94
- "@dxos/random": "0.6.5",
95
- "@dxos/react-client": "0.6.5",
96
- "@dxos/storybook-utils": "0.6.5",
97
- "@braneframe/types": "0.6.5"
88
+ "@dxos/automerge": "0.6.6-staging.23d123d",
89
+ "@dxos/config": "0.6.6-staging.23d123d",
90
+ "@braneframe/types": "0.6.6-staging.23d123d",
91
+ "@dxos/echo-signals": "0.6.6-staging.23d123d",
92
+ "@dxos/echo-typegen": "0.6.6-staging.23d123d",
93
+ "@dxos/random": "0.6.6-staging.23d123d",
94
+ "@dxos/keyboard": "0.6.6-staging.23d123d",
95
+ "@dxos/react-ui": "0.6.6-staging.23d123d",
96
+ "@dxos/storybook-utils": "0.6.6-staging.23d123d",
97
+ "@dxos/react-client": "0.6.6-staging.23d123d"
98
98
  },
99
99
  "peerDependencies": {
100
100
  "@phosphor-icons/react": "^2.1.5",
101
101
  "react": "^18.0.0",
102
102
  "react-dom": "^18.0.0",
103
- "@dxos/react-client": "0.6.5"
103
+ "@dxos/react-client": "0.6.6-staging.23d123d"
104
104
  },
105
105
  "publishConfig": {
106
106
  "access": "public"
@@ -10,13 +10,14 @@ import { TextType } from '@braneframe/types';
10
10
  import { create } from '@dxos/echo-schema';
11
11
  import { PublicKey } from '@dxos/keys';
12
12
  import { faker } from '@dxos/random';
13
- import { createDocAccessor } from '@dxos/react-client/echo';
13
+ import { createDocAccessor, createEchoObject } from '@dxos/react-client/echo';
14
14
  import { Tooltip, useThemeContext } from '@dxos/react-ui';
15
15
  import { textBlockWidth } from '@dxos/react-ui-theme';
16
16
  import { withTheme } from '@dxos/storybook-utils';
17
17
 
18
18
  import { Toolbar } from './Toolbar';
19
19
  import {
20
+ type Action,
20
21
  type Comment,
21
22
  comments,
22
23
  createBasicExtensions,
@@ -24,6 +25,7 @@ import {
24
25
  createMarkdownExtensions,
25
26
  createThemeExtensions,
26
27
  decorateMarkdown,
28
+ type EditorViewMode,
27
29
  formattingKeymap,
28
30
  image,
29
31
  table,
@@ -37,15 +39,16 @@ faker.seed(101);
37
39
 
38
40
  const Story: FC<{ content: string }> = ({ content }) => {
39
41
  const { themeMode } = useThemeContext();
40
- const [text] = useState(create(TextType, { content }));
42
+ const [text] = useState(createEchoObject(create(TextType, { content })));
41
43
  const [formattingState, formattingObserver] = useFormattingState();
44
+ const [viewMode, setViewMode] = useState<EditorViewMode>('preview');
42
45
  const { parentRef, view } = useTextEditor(() => {
43
46
  return {
44
47
  id: text.id,
45
- doc: text.content,
48
+ initialValue: text.content,
46
49
  extensions: [
47
50
  formattingObserver,
48
- createBasicExtensions(),
51
+ createBasicExtensions({ readonly: viewMode === 'readonly' }),
49
52
  createMarkdownExtensions({ themeMode }),
50
53
  createThemeExtensions({ themeMode, slots: { editor: { className: 'p-2' } } }),
51
54
  createDataExtensions({ id: text.id, text: createDocAccessor(text, ['content']) }),
@@ -56,15 +59,21 @@ const Story: FC<{ content: string }> = ({ content }) => {
56
59
  return id;
57
60
  },
58
61
  }),
59
- decorateMarkdown(),
60
62
  formattingKeymap(),
61
63
  image(),
62
- table(),
64
+ ...(viewMode !== 'source' ? [decorateMarkdown(), table()] : []),
63
65
  ],
64
66
  };
65
- }, [text, formattingObserver, themeMode]);
67
+ }, [text, formattingObserver, viewMode, themeMode]);
66
68
 
67
- const handleAction = useActionHandler(view);
69
+ const handleToolbarAction = useActionHandler(view);
70
+ const handleAction = (action: Action) => {
71
+ if (action.type === 'view-mode') {
72
+ setViewMode(action.data);
73
+ } else {
74
+ handleToolbarAction?.(action);
75
+ }
76
+ };
68
77
 
69
78
  const [_comments, setComments] = useState<Comment[]>([]);
70
79
  useComments(view, text.id, _comments);
@@ -73,6 +82,7 @@ const Story: FC<{ content: string }> = ({ content }) => {
73
82
  <Tooltip.Provider>
74
83
  <div role='none' className='fixed inset-0 flex flex-col'>
75
84
  <Toolbar.Root onAction={handleAction} state={formattingState} classNames={textBlockWidth}>
85
+ <Toolbar.View mode={viewMode} />
76
86
  <Toolbar.Markdown />
77
87
  <Toolbar.Custom onUpload={async (file) => ({ url: file.name })} />
78
88
  <Toolbar.Separator />
@@ -26,6 +26,9 @@ import {
26
26
  TextItalic,
27
27
  CaretDown,
28
28
  Check,
29
+ PencilSimpleSlash,
30
+ MarkdownLogo,
31
+ PencilSimple,
29
32
  } from '@phosphor-icons/react';
30
33
  import { createContext } from '@radix-ui/react-context';
31
34
  import React, { type PropsWithChildren, useEffect, useRef, useState } from 'react';
@@ -45,7 +48,7 @@ import {
45
48
  } from '@dxos/react-ui';
46
49
  import { getSize } from '@dxos/react-ui-theme';
47
50
 
48
- import { type Action, type ActionType, type Formatting } from '../../extensions';
51
+ import { type EditorViewMode, type Action, type ActionType, type Formatting, EditorViewModes } from '../../extensions';
49
52
  import { translationKey } from '../../translations';
50
53
 
51
54
  const iconStyles = getSize(5);
@@ -60,7 +63,7 @@ const ToolbarSeparator = () => <div role='separator' className='grow' />;
60
63
 
61
64
  export type ToolbarProps = ThemedClassName<
62
65
  PropsWithChildren<{
63
- state: (Formatting & { comment?: boolean; selection?: boolean }) | undefined;
66
+ state: (Formatting & { comment?: boolean; mode?: EditorViewMode; selection?: boolean }) | undefined;
64
67
  onAction?: (action: Action) => void;
65
68
  }>
66
69
  >;
@@ -72,7 +75,10 @@ const ToolbarRoot = ({ children, onAction, classNames, state }: ToolbarProps) =>
72
75
  <ToolbarContextProvider onAction={onAction} state={state}>
73
76
  <DensityProvider density='fine'>
74
77
  <ElevationProvider elevation='chrome'>
75
- <NaturalToolbar.Root classNames={['is-full shrink-0 overflow-x-auto overflow-y-hidden p-1', classNames]}>
78
+ <NaturalToolbar.Root
79
+ classNames={['p-1 is-full shrink-0 overflow-x-auto overflow-y-hidden', classNames]}
80
+ style={{ contain: 'layout' }}
81
+ >
76
82
  {children}
77
83
  </NaturalToolbar.Root>
78
84
  </ElevationProvider>
@@ -134,6 +140,89 @@ const ToolbarButton = ({ Icon, children, ...props }: ToolbarButtonProps) => {
134
140
  );
135
141
  };
136
142
 
143
+ //
144
+ // View Mode
145
+ //
146
+
147
+ const ViewModeIcons: Record<EditorViewMode, Icon> = {
148
+ preview: PencilSimple,
149
+ readonly: PencilSimpleSlash,
150
+ source: MarkdownLogo,
151
+ };
152
+
153
+ const MarkdownView = ({ mode }: { mode: EditorViewMode }) => {
154
+ const { t } = useTranslation(translationKey);
155
+ const { onAction } = useToolbarContext('ViewMode');
156
+ const ModeIcon = ViewModeIcons[mode ?? 'preview'];
157
+ const suppressNextTooltip = useRef<boolean>(false);
158
+ const [tooltipOpen, setTooltipOpen] = useState<boolean>(false);
159
+ const [selectOpen, setSelectOpen] = useState<boolean>(false);
160
+ return (
161
+ <Tooltip.Root
162
+ open={tooltipOpen}
163
+ onOpenChange={(nextOpen) => {
164
+ if (nextOpen && suppressNextTooltip.current) {
165
+ suppressNextTooltip.current = false;
166
+ return setTooltipOpen(false);
167
+ } else {
168
+ return setTooltipOpen(nextOpen);
169
+ }
170
+ }}
171
+ >
172
+ {/* TODO(thure): `Select` encounters a ref error if used here (repro: select a heading, then select another
173
+ heading). Determine the root cause and fix or report to Radix. */}
174
+ <DropdownMenu.Root
175
+ open={selectOpen}
176
+ onOpenChange={(nextOpen: boolean) => {
177
+ if (!nextOpen) {
178
+ suppressNextTooltip.current = true;
179
+ }
180
+ return setSelectOpen(nextOpen);
181
+ }}
182
+ >
183
+ <Tooltip.Trigger asChild>
184
+ <NaturalToolbar.Button asChild>
185
+ <DropdownMenu.Trigger asChild>
186
+ <Button variant='ghost' classNames={buttonStyles}>
187
+ <span className='sr-only'>{t('mode label')}</span>
188
+ <ModeIcon className={iconStyles} />
189
+ <CaretDown />
190
+ </Button>
191
+ </DropdownMenu.Trigger>
192
+ </NaturalToolbar.Button>
193
+ </Tooltip.Trigger>
194
+ <DropdownMenu.Portal>
195
+ <DropdownMenu.Content classNames='is-min md:is-min' onCloseAutoFocus={(e) => e.preventDefault()}>
196
+ <DropdownMenu.Viewport>
197
+ {EditorViewModes.map((value) => {
198
+ const Icon = ViewModeIcons[value];
199
+ return (
200
+ <DropdownMenu.CheckboxItem
201
+ key={value}
202
+ checked={value === mode}
203
+ onClick={() => onAction?.({ type: 'view-mode', data: value })}
204
+ >
205
+ <Icon className={iconStyles} />
206
+ <span className='whitespace-nowrap grow'>{t(`${value} mode label`)}</span>
207
+ <Check className={value === mode ? 'visible' : 'invisible'} />
208
+ </DropdownMenu.CheckboxItem>
209
+ );
210
+ })}
211
+ </DropdownMenu.Viewport>
212
+ <DropdownMenu.Arrow />
213
+ </DropdownMenu.Content>
214
+ </DropdownMenu.Portal>
215
+ </DropdownMenu.Root>
216
+ <Tooltip.Portal>
217
+ <Tooltip.Content {...tooltipProps}>
218
+ {t('view mode label')}
219
+ <Tooltip.Arrow />
220
+ </Tooltip.Content>
221
+ </Tooltip.Portal>
222
+ </Tooltip.Root>
223
+ );
224
+ };
225
+
137
226
  //
138
227
  // Heading
139
228
  //
@@ -420,6 +509,7 @@ export const Toolbar = {
420
509
  Root: ToolbarRoot,
421
510
  Button: ToolbarToggleButton,
422
511
  Separator: ToolbarSeparator,
512
+ View: MarkdownView,
423
513
  Markdown: MarkdownStandard,
424
514
  Custom: MarkdownCustom,
425
515
  Actions: MarkdownActions,
@@ -2,5 +2,4 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- export * from './TextEditor';
6
5
  export * from './Toolbar';
@@ -19,13 +19,14 @@ import { keymap } from '@codemirror/view';
19
19
  export type AutocompleteResult = Completion;
20
20
 
21
21
  export type AutocompleteOptions = {
22
+ activateOnTyping?: boolean;
22
23
  onSearch: (text: string) => Completion[];
23
24
  };
24
25
 
25
26
  /**
26
27
  * Autocomplete extension.
27
28
  */
28
- export const autocomplete = ({ onSearch }: AutocompleteOptions) => {
29
+ export const autocomplete = ({ activateOnTyping, onSearch }: AutocompleteOptions) => {
29
30
  return [
30
31
  // https://codemirror.net/docs/ref/#view.keymap
31
32
  // https://discuss.codemirror.net/t/how-can-i-replace-the-default-autocompletion-keymap-v6/3322
@@ -35,7 +36,7 @@ export const autocomplete = ({ onSearch }: AutocompleteOptions) => {
35
36
  // https://codemirror.net/examples/autocompletion
36
37
  // https://codemirror.net/docs/ref/#autocomplete.autocompletion
37
38
  autocompletion({
38
- activateOnTyping: false,
39
+ activateOnTyping,
39
40
 
40
41
  // closeOnBlur: false,
41
42
  // defaultKeymap: false,
@@ -44,7 +45,6 @@ export const autocomplete = ({ onSearch }: AutocompleteOptions) => {
44
45
  tooltipClass: () => 'shadow rounded',
45
46
  }),
46
47
 
47
- // TODO(burdon): Option to create new page?
48
48
  // TODO(burdon): Optional decoration via addToOptions
49
49
  markdownLanguage.data.of({
50
50
  autocomplete: (context: CompletionContext): CompletionResult | null => {
@@ -12,15 +12,15 @@ import { Repo } from '@dxos/automerge/automerge-repo';
12
12
  import { BroadcastChannelNetworkAdapter } from '@dxos/automerge/automerge-repo-network-broadcastchannel';
13
13
  import { create, type Expando } from '@dxos/echo-schema';
14
14
  import { type PublicKey } from '@dxos/keys';
15
- import { Filter, DocAccessor, createDocAccessor, useSpace } from '@dxos/react-client/echo';
15
+ import { Filter, DocAccessor, createDocAccessor, useSpace, useQuery, type Space } from '@dxos/react-client/echo';
16
+ import { useIdentity, type Identity } from '@dxos/react-client/halo';
16
17
  import { ClientRepeater } from '@dxos/react-client/testing';
17
18
  import { useThemeContext } from '@dxos/react-ui';
18
19
  import { withTheme } from '@dxos/storybook-utils';
19
20
 
20
- import { automerge } from './automerge';
21
21
  import { useTextEditor } from '../../hooks';
22
22
  import translations from '../../translations';
23
- import { createBasicExtensions, createThemeExtensions } from '../factories';
23
+ import { createBasicExtensions, createDataExtensions, createThemeExtensions } from '../factories';
24
24
 
25
25
  const initialContent = 'Hello world!';
26
26
 
@@ -31,17 +31,26 @@ type TestObject = {
31
31
  type EditorProps = {
32
32
  source: DocAccessor;
33
33
  autoFocus?: boolean;
34
+ space?: Space;
35
+ identity?: Identity;
34
36
  };
35
37
 
36
- const Editor = ({ source, autoFocus }: EditorProps) => {
38
+ const Editor = ({ source, autoFocus, space, identity }: EditorProps) => {
37
39
  const { themeMode } = useThemeContext();
38
40
  const { parentRef } = useTextEditor(
39
41
  () => ({
40
- doc: DocAccessor.getValue(source),
42
+ initialValue: DocAccessor.getValue(source),
41
43
  extensions: [
42
44
  createBasicExtensions({ placeholder: 'Type here...' }),
43
- createThemeExtensions({ themeMode, slots: { editor: { className: 'w-full p-2 bg-white dark:bg-black' } } }),
44
- automerge(source),
45
+ createThemeExtensions({
46
+ themeMode,
47
+ slots: {
48
+ editor: { className: 'w-full bg-white dark:bg-black' },
49
+ // TODO(burdon): Sufficient padding so indicator isn't clipped.
50
+ content: { className: '!m-8' },
51
+ },
52
+ }),
53
+ createDataExtensions({ id: 'test', text: source, space, identity }),
45
54
  ],
46
55
  autoFocus,
47
56
  }),
@@ -94,25 +103,23 @@ export default {
94
103
  };
95
104
 
96
105
  const EchoStory = ({ spaceKey }: { spaceKey: PublicKey }) => {
106
+ const identity = useIdentity();
97
107
  const space = useSpace(spaceKey);
98
108
  const [source, setSource] = useState<DocAccessor>();
109
+ const objects = useQuery<Expando>(space, Filter.from({ type: 'test' }));
110
+
99
111
  useEffect(() => {
100
- setTimeout(async () => {
101
- if (space) {
102
- const { objects = [] } = await space.db.query<Expando>(Filter.from({ type: 'test' })).run();
103
- if (objects.length) {
104
- const source = createDocAccessor(objects[0].content, ['content']);
105
- setSource(source);
106
- }
107
- }
108
- });
109
- }, [space]);
112
+ if (!source && objects.length) {
113
+ const source = createDocAccessor(objects[0].content, ['content']);
114
+ setSource(source);
115
+ }
116
+ }, [objects, source]);
110
117
 
111
118
  if (!source) {
112
119
  return null;
113
120
  }
114
121
 
115
- return <Editor source={source} />;
122
+ return <Editor source={source} space={space} identity={identity ?? undefined} />;
116
123
  };
117
124
 
118
125
  export const Default = {};