@dxos/react-ui-editor 0.4.8-main.beadd4b → 0.4.8-main.c1580f6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/index.mjs +129 -61
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.d.ts +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
- package/dist/types/src/extensions/comments.d.ts.map +1 -1
- package/dist/types/src/extensions/factories.d.ts +3 -0
- package/dist/types/src/extensions/factories.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/formatting.d.ts +3 -3
- package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
- package/dist/types/src/extensions/modes.d.ts +1 -1
- package/dist/types/src/extensions/modes.d.ts.map +1 -1
- package/dist/types/src/hooks/useDocAccessor.d.ts +4 -2
- package/dist/types/src/hooks/useDocAccessor.d.ts.map +1 -1
- package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
- package/dist/types/src/themes/default.d.ts.map +1 -1
- package/package.json +25 -24
- package/src/components/TextEditor/TextEditor.tsx +2 -2
- package/src/components/Toolbar/Toolbar.stories.tsx +23 -25
- package/src/components/Toolbar/Toolbar.tsx +18 -6
- package/src/extensions/automerge/automerge.ts +4 -4
- package/src/extensions/comments.ts +2 -2
- package/src/extensions/factories.ts +30 -7
- package/src/extensions/markdown/bundle.ts +8 -11
- package/src/extensions/markdown/formatting.ts +15 -11
- package/src/extensions/modes.ts +8 -1
- package/src/hooks/useDocAccessor.ts +8 -2
- package/src/hooks/useTextEditor.ts +3 -0
- package/src/themes/default.ts +24 -8
|
@@ -16,17 +16,17 @@ import { withTheme } from '@dxos/storybook-utils';
|
|
|
16
16
|
import { Toolbar } from './Toolbar';
|
|
17
17
|
import {
|
|
18
18
|
type Comment,
|
|
19
|
-
decorateMarkdown,
|
|
20
19
|
comments,
|
|
21
20
|
createBasicExtensions,
|
|
22
21
|
createDataExtensions,
|
|
22
|
+
createMarkdownExtensions,
|
|
23
23
|
createThemeExtensions,
|
|
24
|
+
decorateMarkdown,
|
|
24
25
|
formattingKeymap,
|
|
26
|
+
image,
|
|
25
27
|
table,
|
|
26
28
|
useComments,
|
|
27
29
|
useFormattingState,
|
|
28
|
-
createMarkdownExtensions,
|
|
29
|
-
image,
|
|
30
30
|
} from '../../extensions';
|
|
31
31
|
import { useActionHandler, useDocAccessor, useTextEditor } from '../../hooks';
|
|
32
32
|
import translations from '../../translations';
|
|
@@ -34,15 +34,17 @@ import translations from '../../translations';
|
|
|
34
34
|
faker.seed(101);
|
|
35
35
|
|
|
36
36
|
const Story: FC<{ content: string }> = ({ content }) => {
|
|
37
|
+
console.log('STORY');
|
|
37
38
|
const { themeMode } = useThemeContext();
|
|
38
39
|
const [item] = useState({ text: new TextObject(content) });
|
|
39
40
|
const { id, doc, accessor } = useDocAccessor(item.text);
|
|
40
41
|
const [formattingState, formattingObserver] = useFormattingState();
|
|
41
|
-
const { parentRef, view } = useTextEditor(
|
|
42
|
-
|
|
42
|
+
const { parentRef, view } = useTextEditor(() => {
|
|
43
|
+
return {
|
|
43
44
|
id,
|
|
44
45
|
doc,
|
|
45
46
|
extensions: [
|
|
47
|
+
formattingObserver,
|
|
46
48
|
createBasicExtensions(),
|
|
47
49
|
createMarkdownExtensions({ themeMode }),
|
|
48
50
|
createThemeExtensions({ themeMode, slots: { editor: { className: 'p-2' } } }),
|
|
@@ -58,40 +60,36 @@ const Story: FC<{ content: string }> = ({ content }) => {
|
|
|
58
60
|
formattingKeymap(),
|
|
59
61
|
image(),
|
|
60
62
|
table(),
|
|
61
|
-
formattingObserver,
|
|
62
63
|
],
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
};
|
|
65
|
+
}, [id, accessor, formattingObserver, themeMode]);
|
|
66
|
+
|
|
67
|
+
const handleAction = useActionHandler(view);
|
|
66
68
|
|
|
67
69
|
const [_comments, setComments] = useState<Comment[]>([]);
|
|
68
70
|
useComments(view, id, _comments);
|
|
69
71
|
|
|
70
|
-
const handleAction = useActionHandler(view);
|
|
71
|
-
|
|
72
72
|
return (
|
|
73
|
-
<
|
|
74
|
-
<
|
|
75
|
-
<Toolbar.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
73
|
+
<Tooltip.Provider>
|
|
74
|
+
<div role='none' className='fixed inset-0 flex flex-col'>
|
|
75
|
+
<Toolbar.Root onAction={handleAction} state={formattingState} classNames={textBlockWidth}>
|
|
76
|
+
<Toolbar.Markdown />
|
|
77
|
+
<Toolbar.Custom onUpload={async (file) => ({ url: file.name })} />
|
|
78
|
+
<Toolbar.Separator />
|
|
79
|
+
<Toolbar.Actions />
|
|
80
|
+
</Toolbar.Root>
|
|
81
|
+
<div ref={parentRef} className={textBlockWidth} />
|
|
82
|
+
</div>
|
|
83
|
+
</Tooltip.Provider>
|
|
82
84
|
);
|
|
83
85
|
};
|
|
84
86
|
|
|
85
87
|
export default {
|
|
86
88
|
title: 'react-ui-editor/Toolbar',
|
|
87
89
|
component: Toolbar,
|
|
88
|
-
render: (args: any) => (
|
|
89
|
-
<Tooltip.Provider>
|
|
90
|
-
<Story {...args} />
|
|
91
|
-
</Tooltip.Provider>
|
|
92
|
-
),
|
|
93
90
|
decorators: [withTheme],
|
|
94
91
|
parameters: { translations, layout: 'fullscreen' },
|
|
92
|
+
render: (args: any) => <Story {...args} />,
|
|
95
93
|
};
|
|
96
94
|
|
|
97
95
|
const content = [
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
ListChecks,
|
|
14
14
|
ListNumbers,
|
|
15
15
|
Paragraph,
|
|
16
|
+
Quotes,
|
|
16
17
|
TextStrikethrough,
|
|
17
18
|
Table,
|
|
18
19
|
TextB,
|
|
@@ -23,7 +24,6 @@ import {
|
|
|
23
24
|
TextHFive,
|
|
24
25
|
TextHSix,
|
|
25
26
|
TextItalic,
|
|
26
|
-
Quotes,
|
|
27
27
|
} from '@phosphor-icons/react';
|
|
28
28
|
import { createContext } from '@radix-ui/react-context';
|
|
29
29
|
import React, { type PropsWithChildren, useEffect, useRef, useState } from 'react';
|
|
@@ -33,9 +33,9 @@ import {
|
|
|
33
33
|
DensityProvider,
|
|
34
34
|
ElevationProvider,
|
|
35
35
|
Select,
|
|
36
|
-
type ThemedClassName,
|
|
37
36
|
Toolbar as NaturalToolbar,
|
|
38
37
|
Tooltip,
|
|
38
|
+
type ThemedClassName,
|
|
39
39
|
type ToolbarToggleGroupItemProps as NaturalToolbarToggleGroupItemProps,
|
|
40
40
|
type ToolbarButtonProps as NaturalToolbarButtonProps,
|
|
41
41
|
useTranslation,
|
|
@@ -83,7 +83,7 @@ export type Action = {
|
|
|
83
83
|
|
|
84
84
|
export type ToolbarProps = ThemedClassName<
|
|
85
85
|
PropsWithChildren<{
|
|
86
|
-
state: Formatting |
|
|
86
|
+
state: Formatting | undefined;
|
|
87
87
|
onAction?: (action: Action) => void;
|
|
88
88
|
}>
|
|
89
89
|
>;
|
|
@@ -355,6 +355,7 @@ const MarkdownCustom = ({ onUpload }: MarkdownCustomOptions = {}) => {
|
|
|
355
355
|
'image/*': ['.jpg', '.jpeg', '.png', '.gif'],
|
|
356
356
|
},
|
|
357
357
|
});
|
|
358
|
+
|
|
358
359
|
useEffect(() => {
|
|
359
360
|
if (onUpload && acceptedFiles.length) {
|
|
360
361
|
setTimeout(async () => {
|
|
@@ -388,9 +389,20 @@ const MarkdownActions = () => {
|
|
|
388
389
|
const { onAction } = useToolbarContext('MarkdownStyles');
|
|
389
390
|
const { t } = useTranslation(translationKey);
|
|
390
391
|
return (
|
|
391
|
-
|
|
392
|
-
{
|
|
393
|
-
|
|
392
|
+
<>
|
|
393
|
+
{/* TODO(burdon): Toggle readonly state. */}
|
|
394
|
+
{/* <ToolbarButton value='comment' Icon={BookOpenText} onClick={() => onAction?.({ type: 'comment' })}> */}
|
|
395
|
+
{/* {t('comment label')} */}
|
|
396
|
+
{/* </ToolbarButton> */}
|
|
397
|
+
<ToolbarButton
|
|
398
|
+
value='comment'
|
|
399
|
+
Icon={ChatText}
|
|
400
|
+
data-testid='editor.toolbar.comment'
|
|
401
|
+
onClick={() => onAction?.({ type: 'comment' })}
|
|
402
|
+
>
|
|
403
|
+
{t('comment label')}
|
|
404
|
+
</ToolbarButton>
|
|
405
|
+
</>
|
|
394
406
|
);
|
|
395
407
|
};
|
|
396
408
|
|
|
@@ -60,16 +60,16 @@ export const automerge = (accessor: DocAccessor): Extension => {
|
|
|
60
60
|
ViewPlugin.fromClass(
|
|
61
61
|
class {
|
|
62
62
|
constructor(private readonly _view: EditorView) {
|
|
63
|
-
accessor.handle.addListener('change', this._handleChange
|
|
63
|
+
accessor.handle.addListener('change', this._handleChange);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
destroy() {
|
|
67
|
-
accessor.handle.removeListener('change', this._handleChange
|
|
67
|
+
accessor.handle.removeListener('change', this._handleChange);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
_handleChange() {
|
|
70
|
+
readonly _handleChange = () => {
|
|
71
71
|
syncer.reconcile(this._view, false);
|
|
72
|
-
}
|
|
72
|
+
};
|
|
73
73
|
},
|
|
74
74
|
),
|
|
75
75
|
|
|
@@ -508,14 +508,14 @@ export const focusComment = (view: EditorView, id: string, center = true) => {
|
|
|
508
508
|
/**
|
|
509
509
|
* Update comments state field.
|
|
510
510
|
*/
|
|
511
|
-
export const useComments = (view: EditorView | null | undefined, id: string, comments
|
|
511
|
+
export const useComments = (view: EditorView | null | undefined, id: string, comments?: Comment[]) => {
|
|
512
512
|
useEffect(() => {
|
|
513
513
|
if (view) {
|
|
514
514
|
// Check same document.
|
|
515
515
|
// NOTE: Hook might be called before editor state is updated.
|
|
516
516
|
if (id === view.state.facet(documentId)) {
|
|
517
517
|
view.dispatch({
|
|
518
|
-
effects: setComments.of({ id, comments }),
|
|
518
|
+
effects: setComments.of({ id, comments: comments ?? [] }),
|
|
519
519
|
});
|
|
520
520
|
}
|
|
521
521
|
}
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { closeBrackets } from '@codemirror/autocomplete';
|
|
6
|
-
import { history } from '@codemirror/commands';
|
|
5
|
+
import { closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete';
|
|
6
|
+
import { history, historyKeymap, indentWithTab, standardKeymap } from '@codemirror/commands';
|
|
7
7
|
import { bracketMatching } from '@codemirror/language';
|
|
8
|
+
import { searchKeymap } from '@codemirror/search';
|
|
8
9
|
import { EditorState, type Extension } from '@codemirror/state';
|
|
9
10
|
import {
|
|
10
11
|
EditorView,
|
|
@@ -12,6 +13,8 @@ import {
|
|
|
12
13
|
drawSelection,
|
|
13
14
|
dropCursor,
|
|
14
15
|
highlightActiveLine,
|
|
16
|
+
highlightSpecialChars,
|
|
17
|
+
keymap,
|
|
15
18
|
lineNumbers,
|
|
16
19
|
placeholder,
|
|
17
20
|
scrollPastEnd,
|
|
@@ -32,8 +35,6 @@ import { awareness, SpaceAwarenessProvider } from './awareness';
|
|
|
32
35
|
import { type ThemeStyles } from '../styles';
|
|
33
36
|
import { defaultTheme } from '../themes';
|
|
34
37
|
|
|
35
|
-
// TODO(burdon): Move into extensions folder.
|
|
36
|
-
|
|
37
38
|
//
|
|
38
39
|
// Basic
|
|
39
40
|
//
|
|
@@ -41,8 +42,8 @@ import { defaultTheme } from '../themes';
|
|
|
41
42
|
/**
|
|
42
43
|
* https://codemirror.net/docs/extensions
|
|
43
44
|
* https://github.com/codemirror/basic-setup
|
|
45
|
+
* https://github.com/codemirror/basic-setup/blob/main/src/codemirror.ts
|
|
44
46
|
*/
|
|
45
|
-
// TODO(burdon): Reconcile with createMarkdownExtensions.
|
|
46
47
|
export type BasicExtensionsOptions = {
|
|
47
48
|
allowMultipleSelections?: boolean;
|
|
48
49
|
bracketMatching?: boolean;
|
|
@@ -53,21 +54,25 @@ export type BasicExtensionsOptions = {
|
|
|
53
54
|
editable?: boolean;
|
|
54
55
|
highlightActiveLine?: boolean;
|
|
55
56
|
history?: boolean;
|
|
57
|
+
indentWithTab?: boolean;
|
|
56
58
|
lineNumbers?: boolean;
|
|
57
59
|
lineWrapping?: boolean;
|
|
58
60
|
placeholder?: string;
|
|
59
61
|
readonly?: boolean;
|
|
62
|
+
search?: boolean;
|
|
60
63
|
scrollPastEnd?: boolean;
|
|
61
64
|
tabSize?: number;
|
|
62
65
|
};
|
|
63
66
|
|
|
64
67
|
const defaults: BasicExtensionsOptions = {
|
|
68
|
+
allowMultipleSelections: true,
|
|
65
69
|
bracketMatching: true,
|
|
66
70
|
closeBrackets: true,
|
|
67
71
|
drawSelection: true,
|
|
68
72
|
editable: true,
|
|
69
73
|
history: true,
|
|
70
74
|
lineWrapping: true,
|
|
75
|
+
search: true,
|
|
71
76
|
};
|
|
72
77
|
|
|
73
78
|
export const createBasicExtensions = (_props?: BasicExtensionsOptions): Extension => {
|
|
@@ -85,6 +90,7 @@ export const createBasicExtensions = (_props?: BasicExtensionsOptions): Extensio
|
|
|
85
90
|
props.dropCursor && dropCursor(),
|
|
86
91
|
props.drawSelection && drawSelection(),
|
|
87
92
|
props.highlightActiveLine && highlightActiveLine(),
|
|
93
|
+
highlightSpecialChars(),
|
|
88
94
|
props.history && history(),
|
|
89
95
|
props.lineNumbers && lineNumbers(),
|
|
90
96
|
props.lineWrapping && EditorView.lineWrapping,
|
|
@@ -92,6 +98,24 @@ export const createBasicExtensions = (_props?: BasicExtensionsOptions): Extensio
|
|
|
92
98
|
props.readonly && [EditorState.readOnly.of(true), EditorView.editable.of(false)],
|
|
93
99
|
props.scrollPastEnd && scrollPastEnd(),
|
|
94
100
|
props.tabSize && EditorState.tabSize.of(props.tabSize),
|
|
101
|
+
|
|
102
|
+
// https://codemirror.net/docs/ref/#view.KeyBinding
|
|
103
|
+
keymap.of(
|
|
104
|
+
[
|
|
105
|
+
// https://codemirror.net/docs/ref/#commands.standardKeymap
|
|
106
|
+
...standardKeymap,
|
|
107
|
+
|
|
108
|
+
// https://codemirror.net/docs/ref/#commands.indentWithTab
|
|
109
|
+
props.indentWithTab && indentWithTab,
|
|
110
|
+
|
|
111
|
+
// https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap
|
|
112
|
+
...(props.closeBrackets ? closeBracketsKeymap : []),
|
|
113
|
+
// https://codemirror.net/docs/ref/#commands.historyKeymap
|
|
114
|
+
...(props.history ? historyKeymap : []),
|
|
115
|
+
// https://codemirror.net/docs/ref/#search.searchKeymap
|
|
116
|
+
...(props.search ? searchKeymap : []),
|
|
117
|
+
].filter(isNotFalsy),
|
|
118
|
+
),
|
|
95
119
|
].filter(isNotFalsy);
|
|
96
120
|
};
|
|
97
121
|
|
|
@@ -135,12 +159,11 @@ export const createThemeExtensions = ({ theme, themeMode, slots: _slots }: Theme
|
|
|
135
159
|
|
|
136
160
|
export type DataExtensionsProps = {
|
|
137
161
|
id: string;
|
|
138
|
-
text: DocAccessor;
|
|
162
|
+
text: DocAccessor;
|
|
139
163
|
space?: Space;
|
|
140
164
|
identity?: Identity | null;
|
|
141
165
|
};
|
|
142
166
|
|
|
143
|
-
// TODO(burdon): Factor out automerge defs and extension (not hook).
|
|
144
167
|
// TODO(burdon): Move out of react-ui-editor (remove echo deps).
|
|
145
168
|
export const createDataExtensions = ({ id, text, space, identity }: DataExtensionsProps): Extension[] => {
|
|
146
169
|
const extensions: Extension[] = [automerge(text)];
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { completionKeymap } from '@codemirror/autocomplete';
|
|
6
|
+
import { defaultKeymap, indentWithTab } from '@codemirror/commands';
|
|
7
7
|
import { markdownLanguage, markdown } from '@codemirror/lang-markdown';
|
|
8
8
|
import { defaultHighlightStyle, syntaxHighlighting } from '@codemirror/language';
|
|
9
9
|
import { languages } from '@codemirror/language-data';
|
|
10
|
-
import {
|
|
10
|
+
import { lintKeymap } from '@codemirror/lint';
|
|
11
11
|
import { type Extension } from '@codemirror/state';
|
|
12
12
|
import { oneDarkHighlightStyle } from '@codemirror/theme-one-dark';
|
|
13
13
|
import { keymap } from '@codemirror/view';
|
|
@@ -60,14 +60,11 @@ export const createMarkdownExtensions = ({ themeMode }: MarkdownBundleOptions =
|
|
|
60
60
|
keymap.of([
|
|
61
61
|
// https://codemirror.net/docs/ref/#commands.indentWithTab
|
|
62
62
|
indentWithTab,
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
...
|
|
67
|
-
|
|
68
|
-
...searchKeymap,
|
|
69
|
-
// https://codemirror.net/docs/ref/#commands.standardKeymap
|
|
70
|
-
...standardKeymap,
|
|
63
|
+
|
|
64
|
+
// https://codemirror.net/docs/ref/#commands.defaultKeymap
|
|
65
|
+
...defaultKeymap,
|
|
66
|
+
...completionKeymap,
|
|
67
|
+
...lintKeymap,
|
|
71
68
|
]),
|
|
72
69
|
];
|
|
73
70
|
};
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
} from '@codemirror/state';
|
|
16
16
|
import { EditorView, keymap } from '@codemirror/view';
|
|
17
17
|
import { type SyntaxNodeRef, type SyntaxNode } from '@lezer/common';
|
|
18
|
-
import {
|
|
18
|
+
import { useMemo, useState } from 'react';
|
|
19
19
|
|
|
20
20
|
// Markdown refs:
|
|
21
21
|
// https://github.github.com/gfm
|
|
@@ -56,7 +56,7 @@ export type Formatting = {
|
|
|
56
56
|
listStyle: null | 'ordered' | 'bullet' | 'task';
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
-
export const
|
|
59
|
+
export const formattingEquals = (a: Formatting, b: Formatting) =>
|
|
60
60
|
a.blockType === b.blockType &&
|
|
61
61
|
a.strong === b.strong &&
|
|
62
62
|
a.emphasis === b.emphasis &&
|
|
@@ -1061,7 +1061,7 @@ export const getFormatting = (state: EditorState): Formatting => {
|
|
|
1061
1061
|
const inline: (boolean | null)[] = [null, null, null, null];
|
|
1062
1062
|
let link: boolean = false;
|
|
1063
1063
|
let blockQuote: boolean | null = null;
|
|
1064
|
-
// False indicates mixed list styles
|
|
1064
|
+
// False indicates mixed list styles.
|
|
1065
1065
|
let listStyle: Formatting['listStyle'] | null | false = null;
|
|
1066
1066
|
|
|
1067
1067
|
// Track block context for list/blockquote handling.
|
|
@@ -1220,21 +1220,25 @@ export const getFormatting = (state: EditorState): Formatting => {
|
|
|
1220
1220
|
};
|
|
1221
1221
|
|
|
1222
1222
|
/**
|
|
1223
|
-
* Hook
|
|
1223
|
+
* Hook provides an extension to compute the current formatting state.
|
|
1224
1224
|
*/
|
|
1225
|
-
export const useFormattingState = (): [Formatting |
|
|
1226
|
-
const [state, setState] = useState<Formatting
|
|
1225
|
+
export const useFormattingState = (): [Formatting | undefined, Extension] => {
|
|
1226
|
+
const [state, setState] = useState<Formatting>();
|
|
1227
|
+
|
|
1227
1228
|
const observer = useMemo(
|
|
1228
1229
|
() =>
|
|
1229
1230
|
EditorView.updateListener.of((update) => {
|
|
1230
1231
|
if (update.docChanged || update.selectionSet) {
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1232
|
+
setState((prevState) => {
|
|
1233
|
+
const newState = getFormatting(update.state);
|
|
1234
|
+
if (!prevState || !formattingEquals(prevState, newState)) {
|
|
1235
|
+
return newState;
|
|
1236
|
+
}
|
|
1237
|
+
return prevState;
|
|
1238
|
+
});
|
|
1235
1239
|
}
|
|
1236
1240
|
}),
|
|
1237
|
-
[],
|
|
1241
|
+
[setState],
|
|
1238
1242
|
);
|
|
1239
1243
|
|
|
1240
1244
|
return [state, observer];
|
package/src/extensions/modes.ts
CHANGED
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
import { type Extension, Facet } from '@codemirror/state';
|
|
6
6
|
import { keymap } from '@codemirror/view';
|
|
7
7
|
import { vim } from '@replit/codemirror-vim';
|
|
8
|
+
import { vscodeKeymap } from '@replit/codemirror-vscode-keymap';
|
|
8
9
|
|
|
9
10
|
export const focusEvent = 'focus.container';
|
|
10
11
|
|
|
11
|
-
export type EditorMode = 'default' | 'vim' | undefined;
|
|
12
|
+
export type EditorMode = 'default' | 'vim' | 'vscode' | undefined;
|
|
12
13
|
|
|
13
14
|
export type EditorConfig = {
|
|
14
15
|
type: string;
|
|
@@ -21,7 +22,13 @@ export const editorMode = Facet.define<EditorConfig, EditorConfig>({
|
|
|
21
22
|
|
|
22
23
|
export const EditorModes: { [mode: string]: Extension } = {
|
|
23
24
|
default: [],
|
|
25
|
+
vscode: [
|
|
26
|
+
// https://github.com/replit/codemirror-vscode-keymap
|
|
27
|
+
editorMode.of({ type: 'vscode' }),
|
|
28
|
+
keymap.of(vscodeKeymap),
|
|
29
|
+
],
|
|
24
30
|
vim: [
|
|
31
|
+
// https://github.com/replit/codemirror-vim
|
|
25
32
|
vim(),
|
|
26
33
|
editorMode.of({ type: 'vim', noTabster: true }),
|
|
27
34
|
keymap.of([
|
|
@@ -4,11 +4,17 @@
|
|
|
4
4
|
|
|
5
5
|
import { useMemo } from 'react';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
createDocAccessor,
|
|
9
|
+
type DocAccessor,
|
|
10
|
+
getTextContent,
|
|
11
|
+
type TextObject,
|
|
12
|
+
type EchoReactiveObject,
|
|
13
|
+
} from '@dxos/echo-schema';
|
|
8
14
|
|
|
9
15
|
// TODO(burdon): Factor out.
|
|
10
16
|
export const useDocAccessor = <T = any>(
|
|
11
|
-
text: TextObject
|
|
17
|
+
text: TextObject | EchoReactiveObject<{ content: string }>,
|
|
12
18
|
): { id: string; doc: string | undefined; accessor: DocAccessor<T> } => {
|
|
13
19
|
return useMemo(
|
|
14
20
|
() => ({
|
|
@@ -33,6 +33,8 @@ export const useTextEditor = (cb: () => UseTextEditorProps = () => ({}), deps: D
|
|
|
33
33
|
useEffect(() => {
|
|
34
34
|
let view: EditorView;
|
|
35
35
|
if (parentRef.current) {
|
|
36
|
+
log('create', { id });
|
|
37
|
+
|
|
36
38
|
// https://codemirror.net/docs/ref/#state.EditorStateConfig
|
|
37
39
|
const state = EditorState.create({
|
|
38
40
|
doc,
|
|
@@ -69,6 +71,7 @@ export const useTextEditor = (cb: () => UseTextEditorProps = () => ({}), deps: D
|
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
return () => {
|
|
74
|
+
log('destroy', { id });
|
|
72
75
|
view?.destroy();
|
|
73
76
|
};
|
|
74
77
|
}, deps);
|
package/src/themes/default.ts
CHANGED
|
@@ -236,28 +236,44 @@ export const defaultTheme: ThemeStyles = {
|
|
|
236
236
|
* </div>
|
|
237
237
|
* </div
|
|
238
238
|
*/
|
|
239
|
-
'.cm-panels': {
|
|
240
|
-
border: `1px solid ${get(tokens, 'extend.colors.neutral.200')}`,
|
|
241
|
-
},
|
|
239
|
+
'.cm-panels': {},
|
|
242
240
|
'.cm-panel': {
|
|
243
|
-
background: get(tokens, 'extend.colors.neutral.50'),
|
|
244
241
|
fontFamily: get(tokens, 'fontFamily.body', []).join(','),
|
|
245
242
|
},
|
|
243
|
+
'.cm-panel input[type=checkbox]': {
|
|
244
|
+
marginRight: '0.4rem !important',
|
|
245
|
+
},
|
|
246
|
+
'&light .cm-panel': {
|
|
247
|
+
background: get(tokens, 'extend.colors.neutral.50'),
|
|
248
|
+
},
|
|
249
|
+
'&dark .cm-panel': {
|
|
250
|
+
background: get(tokens, 'extend.colors.neutral.850'),
|
|
251
|
+
},
|
|
246
252
|
'.cm-button': {
|
|
247
253
|
margin: '4px',
|
|
248
254
|
fontFamily: get(tokens, 'fontFamily.body', []).join(','),
|
|
249
|
-
background: get(tokens, 'extend.colors.neutral.100'),
|
|
250
255
|
backgroundImage: 'none',
|
|
251
256
|
border: 'none',
|
|
257
|
+
'&:active': {
|
|
258
|
+
backgroundImage: 'none',
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
'&light .cm-button': {
|
|
262
|
+
background: get(tokens, 'extend.colors.neutral.100'),
|
|
252
263
|
'&:hover': {
|
|
253
264
|
background: get(tokens, 'extend.colors.neutral.200'),
|
|
254
265
|
},
|
|
255
266
|
'&:active': {
|
|
256
267
|
background: get(tokens, 'extend.colors.neutral.300'),
|
|
257
|
-
backgroundImage: 'none',
|
|
258
268
|
},
|
|
259
269
|
},
|
|
260
|
-
'.cm-
|
|
261
|
-
|
|
270
|
+
'&dark .cm-button': {
|
|
271
|
+
background: get(tokens, 'extend.colors.neutral.800'),
|
|
272
|
+
'&:hover': {
|
|
273
|
+
background: get(tokens, 'extend.colors.neutral.700'),
|
|
274
|
+
},
|
|
275
|
+
'&:active': {
|
|
276
|
+
background: get(tokens, 'extend.colors.neutral.600'),
|
|
277
|
+
},
|
|
262
278
|
},
|
|
263
279
|
};
|