@dxos/react-ui-editor 0.6.11-staging.e6894a4 → 0.6.12-main.15a606f
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 +127 -74
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/InputMode.stories.d.ts.map +1 -1
- package/dist/types/src/TextEditor.stories.d.ts +10 -2
- package/dist/types/src/TextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/defaults.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/automerge.stories.d.ts +0 -1
- package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/automerge.test.d.ts.map +1 -1
- package/dist/types/src/extensions/factories.d.ts +5 -1
- package/dist/types/src/extensions/factories.d.ts.map +1 -1
- package/dist/types/src/extensions/folding.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/formatting.test.d.ts.map +1 -1
- package/dist/types/src/extensions/util/react.d.ts +4 -0
- package/dist/types/src/extensions/util/react.d.ts.map +1 -1
- package/dist/types/src/hooks/useTextEditor.d.ts +2 -2
- package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
- package/dist/types/src/styles/markdown.d.ts.map +1 -1
- package/dist/types/src/styles/theme.d.ts.map +1 -1
- package/package.json +32 -27
- package/src/InputMode.stories.tsx +8 -10
- package/src/TextEditor.stories.tsx +61 -34
- package/src/defaults.ts +3 -3
- package/src/extensions/automerge/automerge.stories.tsx +5 -6
- package/src/extensions/automerge/{automerge.spec.tsx → automerge.test.tsx} +1 -0
- package/src/extensions/automerge/automerge.ts +1 -1
- package/src/extensions/factories.ts +15 -4
- package/src/extensions/folding.tsx +17 -4
- package/src/extensions/markdown/bundle.ts +1 -5
- package/src/extensions/markdown/changes.test.ts +1 -3
- package/src/extensions/markdown/decorate.ts +40 -23
- package/src/extensions/markdown/formatting.test.ts +1 -3
- package/src/extensions/markdown/parser.test.ts +1 -2
- package/src/extensions/util/react.tsx +15 -0
- package/src/hooks/useTextEditor.ts +3 -5
- package/src/styles/markdown.ts +0 -2
- package/src/styles/theme.ts +13 -9
- package/dist/types/src/extensions/automerge/automerge.spec.d.ts +0 -2
- package/dist/types/src/extensions/automerge/automerge.spec.d.ts.map +0 -1
- package/src/extensions/automerge/automerge.test.ts +0 -13
@@ -17,6 +17,19 @@ import { table } from './table';
|
|
17
17
|
import { theme, type HeadingLevel } from '../../styles';
|
18
18
|
import { wrapWithCatch } from '../util';
|
19
19
|
|
20
|
+
/**
|
21
|
+
* Unicode characters.
|
22
|
+
* NOTE: Depends on font.
|
23
|
+
* https://www.compart.com/en/unicode (nice resource).
|
24
|
+
* https://en.wikipedia.org/wiki/List_of_Unicode_characters
|
25
|
+
*/
|
26
|
+
const Unicode = {
|
27
|
+
emDash: '\u2014',
|
28
|
+
bullet: '\u2022',
|
29
|
+
bulletSmall: '\u2219',
|
30
|
+
bulletSquare: '\u2b1d',
|
31
|
+
};
|
32
|
+
|
20
33
|
//
|
21
34
|
// Widgets
|
22
35
|
//
|
@@ -67,12 +80,18 @@ class CheckboxWidget extends WidgetType {
|
|
67
80
|
input.setAttribute('disabled', 'true');
|
68
81
|
} else {
|
69
82
|
input.onmousedown = (event: Event) => {
|
70
|
-
|
71
|
-
const
|
72
|
-
|
83
|
+
// Could be beginning of line.
|
84
|
+
const line = view.state.doc.lineAt(view.posAtDOM(span));
|
85
|
+
const text = view.state.sliceDoc(line.from, line.to);
|
86
|
+
const match = text.match(/^\s*- (\[[xX ]]).*/);
|
87
|
+
if (match) {
|
88
|
+
const [, checked] = match;
|
89
|
+
const pos = line.from + text.indexOf(checked);
|
90
|
+
this._checked = checked !== '[ ]';
|
73
91
|
view.dispatch({
|
74
92
|
changes: { from: pos + 1, to: pos + 2, insert: this._checked ? ' ' : 'x' },
|
75
93
|
});
|
94
|
+
|
76
95
|
event.preventDefault();
|
77
96
|
}
|
78
97
|
};
|
@@ -246,17 +265,20 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
|
|
246
265
|
}
|
247
266
|
|
248
267
|
case 'ListItem': {
|
268
|
+
const line = state.doc.lineAt(node.from);
|
269
|
+
|
249
270
|
// Set indentation.
|
250
271
|
const list = getCurrentListLevel();
|
251
272
|
const width = list.type === 'OrderedList' ? orderedListIndentationWidth : bulletListIndentationWidth;
|
252
273
|
const offset = ((list.level ?? 0) + 1) * width;
|
253
|
-
const line = state.doc.lineAt(node.from);
|
254
274
|
if (node.from === line.to - 1) {
|
255
275
|
// Abort if only the hyphen is typed.
|
256
276
|
return false;
|
257
277
|
}
|
258
278
|
|
259
|
-
// Add line decoration for continuation indent.
|
279
|
+
// Add line decoration for the continuation indent.
|
280
|
+
// TODO(burdon): Bug if indentation is more than one indentation unit (e.g., 4 spaces) from the previous line.
|
281
|
+
|
260
282
|
deco.add(
|
261
283
|
line.from,
|
262
284
|
line.from,
|
@@ -268,32 +290,26 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
|
|
268
290
|
}),
|
269
291
|
);
|
270
292
|
|
271
|
-
// Remove indentation spaces.
|
272
|
-
const text = state.doc.sliceString(line.from, node.to);
|
273
|
-
const whitespace = text.match(/^ */)?.[0].length ?? 0;
|
274
|
-
if (whitespace) {
|
275
|
-
atomicDeco.add(line.from, line.from + whitespace, hide);
|
276
|
-
}
|
277
|
-
|
278
293
|
break;
|
279
294
|
}
|
280
295
|
|
281
296
|
case 'ListMark': {
|
297
|
+
const list = getCurrentListLevel();
|
298
|
+
|
282
299
|
// Look-ahead for task marker.
|
283
|
-
// NOTE: Requires space to exist (otherwise
|
300
|
+
// NOTE: Requires space to exist (otherwise the text is parsed as the start of a link).
|
284
301
|
const next = tree.resolve(node.to + 1, 1);
|
285
302
|
if (next?.name === 'TaskMarker') {
|
286
|
-
atomicDeco.add(node.from, node.to + 1, hide);
|
287
303
|
break;
|
288
304
|
}
|
289
305
|
|
290
|
-
const list = getCurrentListLevel();
|
291
|
-
|
292
306
|
// TODO(burdon): Option to make hierarchical; or a), i), etc.
|
293
|
-
const label = list.type === 'OrderedList' ? `${++list.number}.` :
|
307
|
+
const label = list.type === 'OrderedList' ? `${++list.number}.` : Unicode.bulletSmall;
|
308
|
+
const line = state.doc.lineAt(node.from);
|
309
|
+
const to = state.doc.sliceString(node.to, node.to + 1) === ' ' ? node.to + 1 : node.to;
|
294
310
|
atomicDeco.add(
|
295
|
-
|
296
|
-
|
311
|
+
line.from,
|
312
|
+
to,
|
297
313
|
Decoration.replace({
|
298
314
|
widget: new TextWidget(
|
299
315
|
label,
|
@@ -305,10 +321,11 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
|
|
305
321
|
}
|
306
322
|
|
307
323
|
case 'TaskMarker': {
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
324
|
+
const checked = state.doc.sliceString(node.from + 1, node.to - 1) === 'x';
|
325
|
+
// Check if the next character is a space and if so, include it in the replacement.
|
326
|
+
const line = state.doc.lineAt(node.from);
|
327
|
+
const to = state.doc.sliceString(node.to, node.to + 1) === ' ' ? node.to + 1 : node.to;
|
328
|
+
atomicDeco.add(line.from, to, checked ? checkedTask : uncheckedTask);
|
312
329
|
break;
|
313
330
|
}
|
314
331
|
|
@@ -4,9 +4,7 @@
|
|
4
4
|
|
5
5
|
import { markdownLanguage } from '@codemirror/lang-markdown';
|
6
6
|
import { EditorState, type StateCommand } from '@codemirror/state';
|
7
|
-
import { expect } from '
|
8
|
-
|
9
|
-
import { describe, test } from '@dxos/test';
|
7
|
+
import { describe, expect, test } from 'vitest';
|
10
8
|
|
11
9
|
import {
|
12
10
|
addBlockquote,
|
@@ -5,8 +5,7 @@
|
|
5
5
|
// @ts-ignore
|
6
6
|
import { testTree } from '@lezer/generator/test';
|
7
7
|
import { parser } from '@lezer/markdown';
|
8
|
-
|
9
|
-
import { describe, test } from '@dxos/test';
|
8
|
+
import { describe, test } from 'vitest';
|
10
9
|
|
11
10
|
describe('parser', () => {
|
12
11
|
// test.only('list-mark', () => {
|
@@ -8,6 +8,21 @@ import { createRoot } from 'react-dom/client';
|
|
8
8
|
import { ThemeProvider } from '@dxos/react-ui';
|
9
9
|
import { defaultTx } from '@dxos/react-ui-theme';
|
10
10
|
|
11
|
+
export type ElementOptions = {
|
12
|
+
className?: string;
|
13
|
+
};
|
14
|
+
|
15
|
+
export const createElement = (tag: string, options?: ElementOptions, children?: ReactNode): HTMLElement => {
|
16
|
+
const el = document.createElement(tag);
|
17
|
+
if (options?.className) {
|
18
|
+
el.className = options.className;
|
19
|
+
}
|
20
|
+
if (children) {
|
21
|
+
el.append(...(Array.isArray(children) ? children : [children]));
|
22
|
+
}
|
23
|
+
return el;
|
24
|
+
};
|
25
|
+
|
11
26
|
export const renderRoot = (root: HTMLElement, node: ReactNode): HTMLElement => {
|
12
27
|
createRoot(root).render(<ThemeProvider tx={defaultTx}>{node}</ThemeProvider>);
|
13
28
|
return root;
|
@@ -17,7 +17,7 @@ import {
|
|
17
17
|
} from 'react';
|
18
18
|
|
19
19
|
import { log } from '@dxos/log';
|
20
|
-
import { isNotFalsy, type
|
20
|
+
import { getProviderValue, isNotFalsy, type MaybeProvider } from '@dxos/util';
|
21
21
|
|
22
22
|
import { createEditorStateTransaction, documentId, editorInputMode, type EditorSelection } from '../extensions';
|
23
23
|
import { logChanges } from '../util';
|
@@ -57,13 +57,11 @@ let instanceCount = 0;
|
|
57
57
|
* Hook for creating editor.
|
58
58
|
*/
|
59
59
|
export const useTextEditor = (
|
60
|
-
props:
|
60
|
+
props: MaybeProvider<UseTextEditorProps> = {},
|
61
61
|
deps: DependencyList = [],
|
62
62
|
): UseTextEditor => {
|
63
63
|
const { id, initialValue, extensions, autoFocus, scrollTo, selection, moveToEndOfLine, debug } =
|
64
|
-
useMemo<UseTextEditorProps>(() =>
|
65
|
-
return typeof props === 'function' ? props() : props;
|
66
|
-
}, deps ?? []);
|
64
|
+
useMemo<UseTextEditorProps>(() => getProviderValue(props), deps ?? []);
|
67
65
|
|
68
66
|
// NOTE: Increments by 2 in strict mode.
|
69
67
|
const [instanceId] = useState(() => `text-editor-${++instanceCount}`);
|
package/src/styles/markdown.ts
CHANGED
@@ -16,8 +16,6 @@ const headings: Record<HeadingLevel, string> = {
|
|
16
16
|
6: 'text-md',
|
17
17
|
};
|
18
18
|
|
19
|
-
// TODO(burdon): Define theme as facet (used in multiple extensions).
|
20
|
-
// TODO(burdon): Organize theme styles for widgets.
|
21
19
|
export const theme = {
|
22
20
|
mark: 'opacity-50',
|
23
21
|
code: 'font-mono !no-underline text-neutral-700 dark:text-neutral-300',
|
package/src/styles/theme.ts
CHANGED
@@ -73,16 +73,19 @@ export const defaultTheme: ThemeStyles = {
|
|
73
73
|
* NOTE: Gutters should have the same top margin as the content.
|
74
74
|
*/
|
75
75
|
'.cm-gutters': {
|
76
|
-
background: '
|
76
|
+
background: 'var(--surface-bg)',
|
77
77
|
},
|
78
78
|
'.cm-gutter': {},
|
79
|
+
'.cm-gutter.cm-lineNumbers .cm-gutterElement': {
|
80
|
+
minWidth: '40px',
|
81
|
+
alignContent: 'center',
|
82
|
+
},
|
83
|
+
/**
|
84
|
+
* Height is set to match the corresponding line.
|
85
|
+
*/
|
79
86
|
'.cm-gutterElement': {
|
87
|
+
alignItems: 'center',
|
80
88
|
fontSize: '16px',
|
81
|
-
lineHeight: 1.5,
|
82
|
-
},
|
83
|
-
|
84
|
-
'.cm-lineNumbers': {
|
85
|
-
minWidth: '36px',
|
86
89
|
},
|
87
90
|
|
88
91
|
/**
|
@@ -92,7 +95,7 @@ export const defaultTheme: ThemeStyles = {
|
|
92
95
|
paddingInline: 0,
|
93
96
|
},
|
94
97
|
'.cm-activeLine': {
|
95
|
-
background: 'var(--dx-
|
98
|
+
background: 'var(--dx-cmActiveLine)',
|
96
99
|
},
|
97
100
|
|
98
101
|
/**
|
@@ -198,13 +201,14 @@ export const defaultTheme: ThemeStyles = {
|
|
198
201
|
* </div>
|
199
202
|
* </div
|
200
203
|
*/
|
201
|
-
// TODO(burdon):
|
204
|
+
// TODO(burdon): Implement custom panel (with icon buttons).
|
202
205
|
'.cm-panels': {},
|
203
206
|
'.cm-panel': {
|
204
207
|
fontFamily: fontBody,
|
205
|
-
backgroundColor: 'var(--
|
208
|
+
backgroundColor: 'var(--surface-bg)',
|
206
209
|
},
|
207
210
|
'.cm-panel input, .cm-panel button, .cm-panel label': {
|
211
|
+
color: 'var(--dx-subdued)',
|
208
212
|
fontFamily: fontBody,
|
209
213
|
fontSize: '14px',
|
210
214
|
all: 'unset',
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"automerge.spec.d.ts","sourceRoot":"","sources":["../../../../../src/extensions/automerge/automerge.spec.tsx"],"names":[],"mappings":""}
|