@dxos/react-ui-editor 0.4.10-main.0302e13 → 0.4.10-main.308cc0e

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.
@@ -81,7 +81,6 @@ const horizontalRule = Decoration.replace({ widget: new HorizontalRuleWidget() }
81
81
  const checkedTask = Decoration.replace({ widget: new CheckboxWidget(true) });
82
82
  const uncheckedTask = Decoration.replace({ widget: new CheckboxWidget(false) });
83
83
 
84
- // Check if cursor is inside text.
85
84
  const editingRange = (state: EditorState, range: { from: number; to: number }, focus: boolean) => {
86
85
  const {
87
86
  readOnly,
@@ -104,103 +103,76 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
104
103
  from,
105
104
  to,
106
105
  enter: (node) => {
107
- switch (node.name) {
106
+ if (node.name === 'FencedCode') {
107
+ const editing = editingRange(state, node, focus);
108
108
  // FencedCode > CodeMark > [CodeInfo] > CodeText > CodeMark
109
- case 'FencedCode': {
110
- const editing = editingRange(state, node, focus);
111
- for (const block of view.viewportLineBlocks) {
112
- if (block.to < node.from) {
113
- continue;
114
- }
115
- if (block.from > node.to) {
116
- break;
117
- }
118
- const first = block.from <= node.from;
119
- const last = block.to >= node.to && /^(\s>)*```$/.test(state.doc.sliceString(block.from, block.to));
120
- deco.add(
121
- block.from,
122
- block.from,
123
- first ? fencedCodeLineFirst : last ? fencedCodeLineLast : fencedCodeLine,
124
- );
125
- if (!editing && (first || last)) {
126
- atomicDeco.add(block.from, block.to, hide);
127
- }
109
+ for (const block of view.viewportLineBlocks) {
110
+ if (block.to < node.from) {
111
+ continue;
112
+ }
113
+ if (block.from > node.to) {
114
+ break;
115
+ }
116
+ const first = block.from <= node.from;
117
+ const last = block.to >= node.to && /^(\s>)*```$/.test(state.doc.sliceString(block.from, block.to));
118
+ deco.add(block.from, block.from, first ? fencedCodeLineFirst : last ? fencedCodeLineLast : fencedCodeLine);
119
+ if (!editing && (first || last)) {
120
+ atomicDeco.add(block.from, block.to, hide);
128
121
  }
129
- return false;
130
122
  }
131
-
132
- case 'Link': {
133
- const marks = node.node.getChildren('LinkMark');
134
- const urlNode = node.node.getChild('URL');
135
- const editing = editingRange(state, node, focus);
136
- if (urlNode && marks.length >= 2) {
137
- const url = state.sliceDoc(urlNode.from, urlNode.to);
138
- if (!editing) {
139
- atomicDeco.add(node.from, marks[0].to, hide);
140
- }
141
- deco.add(
142
- marks[0].to,
123
+ return false;
124
+ } else if (node.name === 'Link') {
125
+ const marks = node.node.getChildren('LinkMark');
126
+ const urlNode = node.node.getChild('URL');
127
+ const editing = editingRange(state, node, focus);
128
+ if (urlNode && marks.length >= 2) {
129
+ const url = state.sliceDoc(urlNode.from, urlNode.to);
130
+ if (!editing) {
131
+ atomicDeco.add(node.from, marks[0].to, hide);
132
+ }
133
+ deco.add(
134
+ marks[0].to,
135
+ marks[1].from,
136
+ Decoration.mark({
137
+ tagName: 'a',
138
+ attributes: {
139
+ class: 'cm-link',
140
+ href: url,
141
+ rel: 'noreferrer',
142
+ target: '_blank',
143
+ },
144
+ }),
145
+ );
146
+ if (!editing) {
147
+ atomicDeco.add(
143
148
  marks[1].from,
144
- Decoration.mark({
145
- tagName: 'a',
146
- attributes: {
147
- class: 'cm-link',
148
- href: url,
149
- rel: 'noreferrer',
150
- target: '_blank',
151
- },
152
- }),
149
+ node.to,
150
+ options.renderLinkButton
151
+ ? Decoration.replace({ widget: new LinkButton(url, options.renderLinkButton) })
152
+ : hide,
153
153
  );
154
- if (!editing) {
155
- atomicDeco.add(
156
- marks[1].from,
157
- node.to,
158
- options.renderLinkButton
159
- ? Decoration.replace({ widget: new LinkButton(url, options.renderLinkButton) })
160
- : hide,
161
- );
162
- }
163
- }
164
- break;
165
- }
166
-
167
- case 'HeaderMark': {
168
- const parent = node.node.parent!;
169
- if (/^ATX/.test(parent.name) && !editingRange(state, state.doc.lineAt(node.from), focus)) {
170
- const next = state.doc.sliceString(node.to, node.to + 1);
171
- atomicDeco.add(node.from, node.to + (next === ' ' ? 1 : 0), hide);
172
154
  }
173
- break;
174
155
  }
175
-
176
- case 'HorizontalRule': {
177
- if (!editingRange(state, node, focus)) {
178
- deco.add(node.from, node.to, horizontalRule);
179
- }
180
- break;
156
+ } else if (node.name === 'HeaderMark') {
157
+ const parent = node.node.parent!;
158
+ if (/^ATX/.test(parent.name) && !editingRange(state, state.doc.lineAt(node.from), focus)) {
159
+ const next = state.doc.sliceString(node.to, node.to + 1);
160
+ atomicDeco.add(node.from, node.to + (next === ' ' ? 1 : 0), hide);
181
161
  }
182
-
183
- case 'TaskMarker': {
184
- if (!editingRange(state, node, focus)) {
185
- const checked = state.doc.sliceString(node.from + 1, node.to - 1) === 'x';
186
- atomicDeco.add(node.from - 2, node.from - 1, Decoration.mark({ class: 'cm-task' }));
187
- atomicDeco.add(node.from, node.to, checked ? checkedTask : uncheckedTask);
188
- }
189
- break;
162
+ } else if (node.name === 'HorizontalRule') {
163
+ if (!editingRange(state, node, focus)) {
164
+ deco.add(node.from, node.to, horizontalRule);
190
165
  }
191
-
192
- case 'ListItem': {
193
- const start = state.doc.lineAt(node.from);
194
- deco.add(start.from, start.from, Decoration.line({ class: 'cm-list-item' }));
195
- break;
166
+ } else if (node.name === 'TaskMarker') {
167
+ // Check if cursor is inside text.
168
+ if (!editingRange(state, node, focus)) {
169
+ const checked = state.doc.sliceString(node.from + 1, node.to - 1) === 'x';
170
+ atomicDeco.add(node.from - 2, node.from - 1, Decoration.mark({ class: 'cm-task' }));
171
+ atomicDeco.add(node.from, node.to, checked ? checkedTask : uncheckedTask);
196
172
  }
197
-
198
- default: {
199
- if (MarksByParent.has(node.name)) {
200
- if (!editingRange(state, node.node.parent!, focus)) {
201
- atomicDeco.add(node.from, node.to, hide);
202
- }
203
- }
173
+ } else if (MarksByParent.has(node.name)) {
174
+ if (!editingRange(state, node.node.parent!, focus)) {
175
+ atomicDeco.add(node.from, node.to, hide);
204
176
  }
205
177
  }
206
178
  },
@@ -252,6 +224,8 @@ export const decorateMarkdown = (options: DecorateOptions = {}) => {
252
224
  }
253
225
  }
254
226
 
227
+ // TODO(burdon): BUG: If the cursor is at the end of a link at the end of a line,
228
+ // the cursor will float in space or be in the wrong position after the decoration is applied.
255
229
  scheduleUpdate(view: EditorView) {
256
230
  this.clearUpdate();
257
231
  this.pendingUpdate = setTimeout(() => {
@@ -330,6 +304,4 @@ const formattingStyles = EditorView.baseTheme({
330
304
  marginLeft: '4px',
331
305
  marginRight: '4px',
332
306
  },
333
-
334
- // '& .cm-list-item > span:nth-child(2)': {},
335
307
  });
@@ -83,8 +83,9 @@ export enum List {
83
83
  // Headings
84
84
  //
85
85
 
86
- export const setHeading = (level: number): StateCommand => {
87
- return ({ state, dispatch }) => {
86
+ export const setHeading =
87
+ (level: number): StateCommand =>
88
+ ({ state, dispatch }) => {
88
89
  const {
89
90
  selection: { ranges },
90
91
  doc,
@@ -150,14 +151,14 @@ export const setHeading = (level: number): StateCommand => {
150
151
  );
151
152
  return true;
152
153
  };
153
- };
154
154
 
155
155
  //
156
156
  // Styles
157
157
  //
158
158
 
159
- export const setStyle = (type: Inline, enable: boolean): StateCommand => {
160
- return ({ state, dispatch }) => {
159
+ export const setStyle =
160
+ (type: Inline, enable: boolean): StateCommand =>
161
+ ({ state, dispatch }) => {
161
162
  const marker = inlineMarkerText(type);
162
163
  const changes = state.changeByRange((range) => {
163
164
  // Special case for markers directly around the cursor, which will often not be parsed as valid styling.
@@ -321,14 +322,14 @@ export const setStyle = (type: Inline, enable: boolean): StateCommand => {
321
322
 
322
323
  return true;
323
324
  };
324
- };
325
325
 
326
326
  export const addStyle = (style: Inline): StateCommand => setStyle(style, true);
327
327
 
328
328
  export const removeStyle = (style: Inline): StateCommand => setStyle(style, false);
329
329
 
330
- export const toggleStyle = (style: Inline): StateCommand => {
331
- return (arg) => {
330
+ export const toggleStyle =
331
+ (style: Inline): StateCommand =>
332
+ (arg) => {
332
333
  const form = getFormatting(arg.state);
333
334
  return setStyle(
334
335
  style,
@@ -341,7 +342,6 @@ export const toggleStyle = (style: Inline): StateCommand => {
341
342
  : !form.code,
342
343
  )(arg);
343
344
  };
344
- };
345
345
 
346
346
  export const toggleStrong = toggleStyle(Inline.Strong);
347
347
  export const toggleEmphasis = toggleStyle(Inline.Emphasis);
@@ -469,9 +469,10 @@ export const removeLink: StateCommand = ({ state, dispatch }) => {
469
469
  return true;
470
470
  };
471
471
 
472
- // Add link markup around the selection.
473
- export const addLink = ({ url, image }: { url?: string; image?: boolean } = {}): StateCommand => {
474
- return ({ state, dispatch }) => {
472
+ // Add link markup around the selection
473
+ export const addLink =
474
+ ({ url, image }: { url?: string; image?: boolean } = {}): StateCommand =>
475
+ ({ state, dispatch }) => {
475
476
  const changes = state.changeByRange((range) => {
476
477
  let { from, to } = range;
477
478
  const cutStyles: SyntaxNode[] = [];
@@ -518,7 +519,7 @@ export const addLink = ({ url, image }: { url?: string; image?: boolean } = {}):
518
519
  }
519
520
 
520
521
  const changes: ChangeSpec[] = [];
521
- // Some changes must be moved to end of change array so that they are applied in the right order.
522
+ // Some changes must be moved to end of change array so that they are applied in the right order
522
523
  const changesAfter: ChangeSpec[] = [];
523
524
  // Clear existing links.
524
525
  removeLinkInner(from, to, changesAfter, state);
@@ -550,34 +551,28 @@ export const addLink = ({ url, image }: { url?: string; image?: boolean } = {}):
550
551
  range: EditorSelection.cursor(changeSet.mapPos(to, 1) - cursorOffset - (url ? url.length + 2 : 0)),
551
552
  };
552
553
  });
553
-
554
554
  if (changes.changes.empty) {
555
555
  return false;
556
556
  }
557
557
 
558
- dispatch(
559
- state.update(changes, {
560
- userEvent: 'format.link.add',
561
- scrollIntoView: true,
562
- }),
563
- );
558
+ dispatch(state.update(changes, { userEvent: 'format.link.add', scrollIntoView: true }));
564
559
  return true;
565
560
  };
566
- };
567
561
 
568
562
  //
569
563
  // Lists
570
564
  //
571
565
 
572
- export const addList = (type: List): StateCommand => {
573
- return ({ state, dispatch }) => {
566
+ export const addList =
567
+ (type: List): StateCommand =>
568
+ ({ state, dispatch }) => {
574
569
  let lastBlock = -1;
575
570
  let counter = 1;
576
571
  let first = true;
577
572
  let parentColumn: number | null = null;
573
+ const blocks: { node: SyntaxNode; counter: number; parentColumn: number | null }[] = [];
578
574
 
579
575
  // Scan the syntax tree to locate textblocks that can be wrapped.
580
- const blocks: { node: SyntaxNode; counter: number; parentColumn: number | null }[] = [];
581
576
  for (const { from, to } of state.selection.ranges) {
582
577
  syntaxTree(state).iterate({
583
578
  from,
@@ -609,7 +604,6 @@ export const addList = (type: List): StateCommand => {
609
604
  if (node.from === lastBlock) {
610
605
  return;
611
606
  }
612
-
613
607
  lastBlock = node.from;
614
608
  blocks.push({ node: node.node, counter, parentColumn });
615
609
  counter++;
@@ -644,6 +638,7 @@ export const addList = (type: List): StateCommand => {
644
638
  scrollIntoView: true,
645
639
  }),
646
640
  );
641
+
647
642
  return true;
648
643
  }
649
644
 
@@ -685,7 +680,7 @@ export const addList = (type: List): StateCommand => {
685
680
  }
686
681
 
687
682
  changes.push({ from: nodeFrom, insert: mark });
688
- // Add indentation for the other lines in this block.
683
+ // Add indentation for the other lines in this block
689
684
  while (line.to < node.to) {
690
685
  line = state.doc.lineAt(line.to + 1);
691
686
  const open = /^[\s>]*/.exec(line.text)![0].length;
@@ -705,7 +700,7 @@ export const addList = (type: List): StateCommand => {
705
700
  renumberListItems(next.firstChild, last.counter + 1, changes, state.doc);
706
701
  }
707
702
  }
708
- ('Oeswe');
703
+
709
704
  const changeSet = state.changes(changes);
710
705
  dispatch(
711
706
  state.update({
@@ -717,10 +712,10 @@ export const addList = (type: List): StateCommand => {
717
712
  );
718
713
  return true;
719
714
  };
720
- };
721
715
 
722
- export const removeList = (type: List): StateCommand => {
723
- return ({ state, dispatch }) => {
716
+ export const removeList =
717
+ (type: List): StateCommand =>
718
+ ({ state, dispatch }) => {
724
719
  let lastBlock = -1;
725
720
  const changes: ChangeSpec[] = [];
726
721
  const stack: string[] = [];
@@ -776,16 +771,15 @@ export const removeList = (type: List): StateCommand => {
776
771
  dispatch(state.update({ changes, userEvent: 'format.list.remove', scrollIntoView: true }));
777
772
  return true;
778
773
  };
779
- };
780
774
 
781
- export const toggleList = (type: List): StateCommand => {
782
- return (target) => {
775
+ export const toggleList =
776
+ (type: List): StateCommand =>
777
+ (target) => {
783
778
  const formatting = getFormatting(target.state);
784
779
  const active =
785
780
  formatting.listStyle === (type === List.Bullet ? 'bullet' : type === List.Ordered ? 'ordered' : 'task');
786
781
  return (active ? removeList(type) : addList(type))(target);
787
782
  };
788
- };
789
783
 
790
784
  const renumberListItems = (item: SyntaxNode | null, counter: number, changes: ChangeSpec[], doc: Text) => {
791
785
  for (; item; item = item.nextSibling) {
@@ -806,8 +800,9 @@ const renumberListItems = (item: SyntaxNode | null, counter: number, changes: Ch
806
800
  // Block quotes
807
801
  //
808
802
 
809
- export const setBlockquote = (enable: boolean): StateCommand => {
810
- return ({ state, dispatch }) => {
803
+ export const setBlockquote =
804
+ (enable: boolean): StateCommand =>
805
+ ({ state, dispatch }) => {
811
806
  const lines: Line[] = [];
812
807
  let lastBlock = -1;
813
808
  for (const { from, to } of state.selection.ranges) {
@@ -879,7 +874,6 @@ export const setBlockquote = (enable: boolean): StateCommand => {
879
874
  );
880
875
  return true;
881
876
  };
882
- };
883
877
 
884
878
  export const addBlockquote = setBlockquote(true);
885
879
 
@@ -950,10 +944,9 @@ export const addCodeblock: StateCommand = (target) => {
950
944
  };
951
945
 
952
946
  export const removeCodeblock: StateCommand = ({ state, dispatch }) => {
953
- let lastBlock = -1;
954
-
955
- // Find all code blocks, remove their markup.
956
947
  const changes: ChangeSpec[] = [];
948
+ let lastBlock = -1;
949
+ // Find all code blocks, remove their markup
957
950
  for (const { from, to } of state.selection.ranges) {
958
951
  syntaxTree(state).iterate({
959
952
  from,
@@ -972,7 +965,7 @@ export const removeCodeblock: StateCommand = ({ state, dispatch }) => {
972
965
  });
973
966
  }
974
967
  } else {
975
- // Indented code block.
968
+ // Indented code block
976
969
  const column = node.from - firstLine.from;
977
970
  for (let line = firstLine; ; line = state.doc.line(line.number + 1)) {
978
971
  changes.push({ from: line.from + column - 4, to: line.from + column });
@@ -1057,7 +1050,7 @@ const Textblocks: { [name: string]: NonNullable<Formatting['blockType']> } = {
1057
1050
  };
1058
1051
 
1059
1052
  /**
1060
- * Query the editor state for the active formatting at the selection.
1053
+ * Query an editor state for the active formatting at the selection.
1061
1054
  */
1062
1055
  export const getFormatting = (state: EditorState): Formatting => {
1063
1056
  // These will track the formatting we've seen so far.
@@ -183,9 +183,7 @@ export const markdownHighlightStyle = (readonly?: boolean) => {
183
183
  ],
184
184
  {
185
185
  scope: markdownLanguage,
186
- all: {
187
- fontFamily: getToken('fontFamily.body', []).join(','),
188
- },
186
+ all: { fontFamily: getToken('fontFamily.body', []).join(',') },
189
187
  },
190
188
  );
191
189
  };
@@ -15,6 +15,7 @@ import { Decoration, EditorView, WidgetType } from '@codemirror/view';
15
15
 
16
16
  // TODO(burdon): Snippet to create basic table.
17
17
  // https://codemirror.net/docs/ref/#autocomplete.snippet
18
+
18
19
  // TODO(burdon): Advanced formatting (left/right/center).
19
20
  // TODO(burdon): Editor to auto balance columns.
20
21
 
@@ -81,21 +82,19 @@ const update = (state: EditorState, options: TableOptions) => {
81
82
  });
82
83
 
83
84
  tables.forEach((table) => {
84
- const replace = state.readOnly || cursor < table.from || cursor > table.to;
85
- if (replace) {
85
+ const hide = state.readOnly || cursor < table.from || cursor > table.to;
86
+ if (hide) {
86
87
  builder.add(
87
88
  table.from,
88
89
  table.to,
89
90
  Decoration.replace({
90
- block: true,
91
91
  widget: new TableWidget(table),
92
92
  }),
93
93
  );
94
- } else {
95
- // Add class for styling.
96
- // TODO(burdon): Apply to each line?
97
- builder.add(table.from, table.to, Decoration.mark({ class: 'cm-table' }));
98
94
  }
95
+
96
+ // Add class for styling.
97
+ builder.add(table.from, table.to, Decoration.mark({ class: 'cm-table' }));
99
98
  });
100
99
 
101
100
  return builder.finish();