@dxos/react-ui-editor 0.6.9 → 0.6.10-main.e92b5eb

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.
@@ -145,7 +145,7 @@ const autoHideTags = new Set([
145
145
  type NumberingLevel = { type: string; from: number; to: number; level: number; number: number };
146
146
 
147
147
  const bulletListIndentationWidth = 24;
148
- const orderedListIndentationWidth = 32; // TODO(burdon): Make variable length based on number of digits.
148
+ const orderedListIndentationWidth = 36; // TODO(burdon): Make variable length based on number of digits.
149
149
 
150
150
  const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boolean) => {
151
151
  const deco = new RangeSetBuilder<Decoration>();
@@ -182,8 +182,9 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
182
182
  return listLevels[listLevels.length - 1];
183
183
  };
184
184
 
185
+ // const count = 0;
185
186
  const enterNode = (node: SyntaxNodeRef) => {
186
- // console.log('##', { node: node.name, from: node.from, to: node.to });
187
+ // console.log(`[${count++}]`, { node: node.name, from: node.from, to: node.to });
187
188
  switch (node.name) {
188
189
  // ATXHeading > HeaderMark > Paragraph
189
190
  // NOTE: Numbering requires processing the entire document since otherwise only the visible range will be
@@ -253,50 +254,55 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
253
254
  const list = getCurrentList();
254
255
  const width = list.type === 'OrderedList' ? orderedListIndentationWidth : bulletListIndentationWidth;
255
256
  const offset = ((list.level ?? 0) + 1) * width;
256
- const start = state.doc.lineAt(node.from);
257
+ const line = state.doc.lineAt(node.from);
258
+ if (node.from === line.to - 1) {
259
+ // Abort if only the hyphen is typed.
260
+ return false;
261
+ }
257
262
 
263
+ // Add line decoration to indent.
258
264
  deco.add(
259
- start.from,
260
- start.from,
265
+ line.from,
266
+ line.from,
261
267
  Decoration.line({
262
268
  class: 'cm-list-item',
263
269
  attributes: {
264
- // Subtract 0.25em to account for the space CM adds to Paragraph nodes following the ListItem.
265
- // Note: This makes the cursor appear to be left of the margin.
266
- style: `padding-left: ${offset}px; text-indent: calc(-${width}px - 0.25em);`,
270
+ style: `padding-left: ${offset}px; text-indent: -${width}px;`,
267
271
  },
268
272
  }),
269
273
  );
270
274
 
271
275
  // Remove indentation spaces.
272
- // TODO(burdon): Replace whitespace with atomic block. Parse ListMark inline.
273
- const line = state.doc.sliceString(start.from, node.to);
274
- const whitespace = line.match(/^ */)?.[0].length ?? 0;
276
+ const text = state.doc.sliceString(line.from, node.to);
277
+ const whitespace = text.match(/^ */)?.[0].length ?? 0;
275
278
  if (whitespace) {
276
- atomicDeco.add(start.from, start.from + whitespace, hide);
279
+ atomicDeco.add(line.from, line.from + whitespace, hide);
277
280
  }
278
281
 
279
- // const mark = node.node.firstChild!;
280
- // console.log(mark?.name);
281
- // if (mark?.name === 'ListMark') {}
282
282
  break;
283
283
  }
284
284
 
285
285
  case 'ListMark': {
286
286
  // Look-ahead for task marker.
287
- const task = tree.resolve(node.to + 1, 1).name === 'TaskMarker';
288
- if (task) {
289
- atomicDeco.add(node.from, node.to, hide);
287
+ const next = tree.resolve(node.to + 1, 1);
288
+ if (next?.name === 'TaskMarker') {
289
+ atomicDeco.add(node.from, node.to + 1, hide);
290
290
  break;
291
291
  }
292
292
 
293
- // TODO(burdon): Cursor stops for 1 character when moving back into number (but not dashes).
294
- // TODO(burdon): Option to make hierarchical; or a, b, c. etc.
295
293
  const list = getCurrentList();
296
- const label = list.type === 'OrderedList' ? `${++list.number}.` : '-';
294
+
295
+ // Abort unless followed by space.
296
+ const text = state.doc.sliceString(node.from, node.to + 1);
297
+ if (list.type === 'BulletList' && text[1] !== ' ') {
298
+ return false;
299
+ }
300
+
301
+ // TODO(burdon): Option to make hierarchical; or a), i), etc.
302
+ const label = list.type === 'OrderedList' ? `${++list.number}.` : '•';
297
303
  atomicDeco.add(
298
304
  node.from,
299
- node.to,
305
+ node.to + 1,
300
306
  Decoration.replace({
301
307
  widget: new TextWidget(
302
308
  label,
@@ -310,8 +316,7 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
310
316
  case 'TaskMarker': {
311
317
  if (!editingRange(state, node, focus)) {
312
318
  const checked = state.doc.sliceString(node.from + 1, node.to - 1) === 'x';
313
- atomicDeco.add(node.from - 2, node.from - 1, Decoration.mark({ class: 'cm-task-checkbox' }));
314
- atomicDeco.add(node.from, node.to, checked ? checkedTask : uncheckedTask);
319
+ atomicDeco.add(node.from, node.to + 1, checked ? checkedTask : uncheckedTask);
315
320
  }
316
321
  break;
317
322
  }
@@ -574,7 +579,7 @@ const formattingStyles = EditorView.baseTheme({
574
579
 
575
580
  '& .cm-task': {
576
581
  display: 'inline-block',
577
- width: `calc(${bulletListIndentationWidth}px - 0.25em)`,
582
+ width: `${bulletListIndentationWidth}px`,
578
583
  color: getToken('extend.colors.blue.500'),
579
584
  },
580
585
  '& .cm-task-checkbox': {
@@ -587,6 +592,7 @@ const formattingStyles = EditorView.baseTheme({
587
592
  '& .cm-list-mark': {
588
593
  display: 'inline-block',
589
594
  textAlign: 'right',
595
+ paddingRight: '0.5em',
590
596
  fontVariant: 'tabular-nums',
591
597
  },
592
598
  '& .cm-list-mark-bullet': {
@@ -9,6 +9,34 @@ import { parser } from '@lezer/markdown';
9
9
  import { describe, test } from '@dxos/test';
10
10
 
11
11
  describe('parser', () => {
12
+ // test.only('list-mark', () => {
13
+ // const newParser = parser.configure({
14
+ // parseBlock: [
15
+ // {
16
+ // name: 'ListItem',
17
+ // parse: (cx, line) => {
18
+ // console.log(`[${line.text}]`, cx.lineStart, line.text.length);
19
+ // // line.skipSpace(1);
20
+ // return true;
21
+ // },
22
+ // },
23
+ // ],
24
+ // });
25
+ //
26
+ // {
27
+ // const result = newParser.parse(' - ');
28
+ // testTree(result, 'Document(BulletList(ListItem(ListMark)))');
29
+ // }
30
+ // {
31
+ // const result = newParser.parse('-x');
32
+ // testTree(result, 'Document(Paragraph)');
33
+ // }
34
+ // {
35
+ // const result = newParser.parse('- x');
36
+ // testTree(result, 'Document(BulletList(ListItem(ListMark,Paragraph)))');
37
+ // }
38
+ // });
39
+
12
40
  // https://www.markdownguide.org/basic-syntax/#lists-1
13
41
  test('lists', () => {
14
42
  // Indented list must have 4 spaces.
@@ -25,6 +53,7 @@ describe('parser', () => {
25
53
  '1. one',
26
54
  ].join('\n'),
27
55
  );
56
+
28
57
  testTree(
29
58
  result,
30
59
  [
@@ -9,9 +9,9 @@ import { tailwindConfig, type TailwindConfig } from '@dxos/react-ui-theme';
9
9
  const tokens: TailwindConfig['theme'] = tailwindConfig({}).theme;
10
10
 
11
11
  /**
12
- * @deprecated
13
- * Replace with CSS vars.
12
+ * Returns the tailwind token value.
14
13
  */
14
+ // TODO(burdon): Replace with CSS vars.
15
15
  export const getToken = (path: string, defaultValue?: string | string[]): string => {
16
16
  const value = get(tokens, path, defaultValue);
17
17
  return value?.toString() ?? '';