@dxos/react-ui-editor 0.4.10-main.fa5a270 → 0.4.10-main.fd8ea31
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 +707 -694
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/automerge.d.ts +1 -1
- package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/automerge.stories.d.ts +1 -1
- package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/cursor.d.ts +2 -2
- package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/sync.d.ts +1 -1
- package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/update-automerge.d.ts +1 -1
- package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
- package/dist/types/src/extensions/factories.d.ts +2 -3
- package/dist/types/src/extensions/factories.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/formatting.d.ts +1 -1
- package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/table.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +0 -1
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/package.json +30 -29
- package/src/components/TextEditor/TextEditor.stories.tsx +21 -18
- package/src/components/TextEditor/TextEditor.tsx +6 -3
- package/src/components/Toolbar/Toolbar.stories.tsx +10 -9
- package/src/extensions/automerge/automerge.stories.tsx +7 -6
- package/src/extensions/automerge/automerge.ts +1 -1
- package/src/extensions/automerge/cursor.ts +4 -37
- package/src/extensions/automerge/sync.ts +1 -1
- package/src/extensions/automerge/update-automerge.ts +1 -1
- package/src/extensions/factories.ts +3 -4
- package/src/extensions/markdown/decorate.ts +90 -62
- package/src/extensions/markdown/formatting.ts +42 -35
- package/src/extensions/markdown/highlight.ts +3 -1
- package/src/extensions/markdown/table.ts +7 -6
- package/src/hooks/index.ts +0 -1
- package/src/hooks/useTextEditor.ts +1 -1
- package/dist/types/src/hooks/useDocAccessor.d.ts +0 -9
- package/dist/types/src/hooks/useDocAccessor.d.ts.map +0 -1
- package/src/hooks/useDocAccessor.ts +0 -27
|
@@ -81,6 +81,7 @@ 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.
|
|
84
85
|
const editingRange = (state: EditorState, range: { from: number; to: number }, focus: boolean) => {
|
|
85
86
|
const {
|
|
86
87
|
readOnly,
|
|
@@ -103,76 +104,103 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
|
|
|
103
104
|
from,
|
|
104
105
|
to,
|
|
105
106
|
enter: (node) => {
|
|
106
|
-
|
|
107
|
-
const editing = editingRange(state, node, focus);
|
|
107
|
+
switch (node.name) {
|
|
108
108
|
// FencedCode > CodeMark > [CodeInfo] > CodeText > CodeMark
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
+
}
|
|
121
128
|
}
|
|
129
|
+
return false;
|
|
122
130
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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(
|
|
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,
|
|
148
143
|
marks[1].from,
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
144
|
+
Decoration.mark({
|
|
145
|
+
tagName: 'a',
|
|
146
|
+
attributes: {
|
|
147
|
+
class: 'cm-link',
|
|
148
|
+
href: url,
|
|
149
|
+
rel: 'noreferrer',
|
|
150
|
+
target: '_blank',
|
|
151
|
+
},
|
|
152
|
+
}),
|
|
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);
|
|
154
172
|
}
|
|
173
|
+
break;
|
|
155
174
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
175
|
+
|
|
176
|
+
case 'HorizontalRule': {
|
|
177
|
+
if (!editingRange(state, node, focus)) {
|
|
178
|
+
deco.add(node.from, node.to, horizontalRule);
|
|
179
|
+
}
|
|
180
|
+
break;
|
|
161
181
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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;
|
|
165
190
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
atomicDeco.add(node.from, node.to, checked ? checkedTask : uncheckedTask);
|
|
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;
|
|
172
196
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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
|
+
}
|
|
176
204
|
}
|
|
177
205
|
}
|
|
178
206
|
},
|
|
@@ -224,8 +252,6 @@ export const decorateMarkdown = (options: DecorateOptions = {}) => {
|
|
|
224
252
|
}
|
|
225
253
|
}
|
|
226
254
|
|
|
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.
|
|
229
255
|
scheduleUpdate(view: EditorView) {
|
|
230
256
|
this.clearUpdate();
|
|
231
257
|
this.pendingUpdate = setTimeout(() => {
|
|
@@ -304,4 +330,6 @@ const formattingStyles = EditorView.baseTheme({
|
|
|
304
330
|
marginLeft: '4px',
|
|
305
331
|
marginRight: '4px',
|
|
306
332
|
},
|
|
333
|
+
|
|
334
|
+
// '& .cm-list-item > span:nth-child(2)': {},
|
|
307
335
|
});
|
|
@@ -83,9 +83,8 @@ export enum List {
|
|
|
83
83
|
// Headings
|
|
84
84
|
//
|
|
85
85
|
|
|
86
|
-
export const setHeading =
|
|
87
|
-
(
|
|
88
|
-
({ state, dispatch }) => {
|
|
86
|
+
export const setHeading = (level: number): StateCommand => {
|
|
87
|
+
return ({ state, dispatch }) => {
|
|
89
88
|
const {
|
|
90
89
|
selection: { ranges },
|
|
91
90
|
doc,
|
|
@@ -151,14 +150,14 @@ export const setHeading =
|
|
|
151
150
|
);
|
|
152
151
|
return true;
|
|
153
152
|
};
|
|
153
|
+
};
|
|
154
154
|
|
|
155
155
|
//
|
|
156
156
|
// Styles
|
|
157
157
|
//
|
|
158
158
|
|
|
159
|
-
export const setStyle =
|
|
160
|
-
(
|
|
161
|
-
({ state, dispatch }) => {
|
|
159
|
+
export const setStyle = (type: Inline, enable: boolean): StateCommand => {
|
|
160
|
+
return ({ state, dispatch }) => {
|
|
162
161
|
const marker = inlineMarkerText(type);
|
|
163
162
|
const changes = state.changeByRange((range) => {
|
|
164
163
|
// Special case for markers directly around the cursor, which will often not be parsed as valid styling.
|
|
@@ -322,14 +321,14 @@ export const setStyle =
|
|
|
322
321
|
|
|
323
322
|
return true;
|
|
324
323
|
};
|
|
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 =
|
|
331
|
-
(
|
|
332
|
-
(arg) => {
|
|
330
|
+
export const toggleStyle = (style: Inline): StateCommand => {
|
|
331
|
+
return (arg) => {
|
|
333
332
|
const form = getFormatting(arg.state);
|
|
334
333
|
return setStyle(
|
|
335
334
|
style,
|
|
@@ -342,6 +341,7 @@ export const toggleStyle =
|
|
|
342
341
|
: !form.code,
|
|
343
342
|
)(arg);
|
|
344
343
|
};
|
|
344
|
+
};
|
|
345
345
|
|
|
346
346
|
export const toggleStrong = toggleStyle(Inline.Strong);
|
|
347
347
|
export const toggleEmphasis = toggleStyle(Inline.Emphasis);
|
|
@@ -469,10 +469,9 @@ 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 =
|
|
474
|
-
({
|
|
475
|
-
({ state, dispatch }) => {
|
|
472
|
+
// Add link markup around the selection.
|
|
473
|
+
export const addLink = ({ url, image }: { url?: string; image?: boolean } = {}): StateCommand => {
|
|
474
|
+
return ({ state, dispatch }) => {
|
|
476
475
|
const changes = state.changeByRange((range) => {
|
|
477
476
|
let { from, to } = range;
|
|
478
477
|
const cutStyles: SyntaxNode[] = [];
|
|
@@ -519,7 +518,7 @@ export const addLink =
|
|
|
519
518
|
}
|
|
520
519
|
|
|
521
520
|
const changes: ChangeSpec[] = [];
|
|
522
|
-
// Some changes must be moved to end of change array so that they are applied in the right order
|
|
521
|
+
// Some changes must be moved to end of change array so that they are applied in the right order.
|
|
523
522
|
const changesAfter: ChangeSpec[] = [];
|
|
524
523
|
// Clear existing links.
|
|
525
524
|
removeLinkInner(from, to, changesAfter, state);
|
|
@@ -551,28 +550,34 @@ export const addLink =
|
|
|
551
550
|
range: EditorSelection.cursor(changeSet.mapPos(to, 1) - cursorOffset - (url ? url.length + 2 : 0)),
|
|
552
551
|
};
|
|
553
552
|
});
|
|
553
|
+
|
|
554
554
|
if (changes.changes.empty) {
|
|
555
555
|
return false;
|
|
556
556
|
}
|
|
557
557
|
|
|
558
|
-
dispatch(
|
|
558
|
+
dispatch(
|
|
559
|
+
state.update(changes, {
|
|
560
|
+
userEvent: 'format.link.add',
|
|
561
|
+
scrollIntoView: true,
|
|
562
|
+
}),
|
|
563
|
+
);
|
|
559
564
|
return true;
|
|
560
565
|
};
|
|
566
|
+
};
|
|
561
567
|
|
|
562
568
|
//
|
|
563
569
|
// Lists
|
|
564
570
|
//
|
|
565
571
|
|
|
566
|
-
export const addList =
|
|
567
|
-
(
|
|
568
|
-
({ state, dispatch }) => {
|
|
572
|
+
export const addList = (type: List): StateCommand => {
|
|
573
|
+
return ({ state, dispatch }) => {
|
|
569
574
|
let lastBlock = -1;
|
|
570
575
|
let counter = 1;
|
|
571
576
|
let first = true;
|
|
572
577
|
let parentColumn: number | null = null;
|
|
573
|
-
const blocks: { node: SyntaxNode; counter: number; parentColumn: number | null }[] = [];
|
|
574
578
|
|
|
575
579
|
// Scan the syntax tree to locate textblocks that can be wrapped.
|
|
580
|
+
const blocks: { node: SyntaxNode; counter: number; parentColumn: number | null }[] = [];
|
|
576
581
|
for (const { from, to } of state.selection.ranges) {
|
|
577
582
|
syntaxTree(state).iterate({
|
|
578
583
|
from,
|
|
@@ -604,6 +609,7 @@ export const addList =
|
|
|
604
609
|
if (node.from === lastBlock) {
|
|
605
610
|
return;
|
|
606
611
|
}
|
|
612
|
+
|
|
607
613
|
lastBlock = node.from;
|
|
608
614
|
blocks.push({ node: node.node, counter, parentColumn });
|
|
609
615
|
counter++;
|
|
@@ -638,7 +644,6 @@ export const addList =
|
|
|
638
644
|
scrollIntoView: true,
|
|
639
645
|
}),
|
|
640
646
|
);
|
|
641
|
-
|
|
642
647
|
return true;
|
|
643
648
|
}
|
|
644
649
|
|
|
@@ -680,7 +685,7 @@ export const addList =
|
|
|
680
685
|
}
|
|
681
686
|
|
|
682
687
|
changes.push({ from: nodeFrom, insert: mark });
|
|
683
|
-
// Add indentation for the other lines in this block
|
|
688
|
+
// Add indentation for the other lines in this block.
|
|
684
689
|
while (line.to < node.to) {
|
|
685
690
|
line = state.doc.lineAt(line.to + 1);
|
|
686
691
|
const open = /^[\s>]*/.exec(line.text)![0].length;
|
|
@@ -700,7 +705,7 @@ export const addList =
|
|
|
700
705
|
renumberListItems(next.firstChild, last.counter + 1, changes, state.doc);
|
|
701
706
|
}
|
|
702
707
|
}
|
|
703
|
-
|
|
708
|
+
('Oeswe');
|
|
704
709
|
const changeSet = state.changes(changes);
|
|
705
710
|
dispatch(
|
|
706
711
|
state.update({
|
|
@@ -712,10 +717,10 @@ export const addList =
|
|
|
712
717
|
);
|
|
713
718
|
return true;
|
|
714
719
|
};
|
|
720
|
+
};
|
|
715
721
|
|
|
716
|
-
export const removeList =
|
|
717
|
-
(
|
|
718
|
-
({ state, dispatch }) => {
|
|
722
|
+
export const removeList = (type: List): StateCommand => {
|
|
723
|
+
return ({ state, dispatch }) => {
|
|
719
724
|
let lastBlock = -1;
|
|
720
725
|
const changes: ChangeSpec[] = [];
|
|
721
726
|
const stack: string[] = [];
|
|
@@ -771,15 +776,16 @@ export const removeList =
|
|
|
771
776
|
dispatch(state.update({ changes, userEvent: 'format.list.remove', scrollIntoView: true }));
|
|
772
777
|
return true;
|
|
773
778
|
};
|
|
779
|
+
};
|
|
774
780
|
|
|
775
|
-
export const toggleList =
|
|
776
|
-
(
|
|
777
|
-
(target) => {
|
|
781
|
+
export const toggleList = (type: List): StateCommand => {
|
|
782
|
+
return (target) => {
|
|
778
783
|
const formatting = getFormatting(target.state);
|
|
779
784
|
const active =
|
|
780
785
|
formatting.listStyle === (type === List.Bullet ? 'bullet' : type === List.Ordered ? 'ordered' : 'task');
|
|
781
786
|
return (active ? removeList(type) : addList(type))(target);
|
|
782
787
|
};
|
|
788
|
+
};
|
|
783
789
|
|
|
784
790
|
const renumberListItems = (item: SyntaxNode | null, counter: number, changes: ChangeSpec[], doc: Text) => {
|
|
785
791
|
for (; item; item = item.nextSibling) {
|
|
@@ -800,9 +806,8 @@ const renumberListItems = (item: SyntaxNode | null, counter: number, changes: Ch
|
|
|
800
806
|
// Block quotes
|
|
801
807
|
//
|
|
802
808
|
|
|
803
|
-
export const setBlockquote =
|
|
804
|
-
(
|
|
805
|
-
({ state, dispatch }) => {
|
|
809
|
+
export const setBlockquote = (enable: boolean): StateCommand => {
|
|
810
|
+
return ({ state, dispatch }) => {
|
|
806
811
|
const lines: Line[] = [];
|
|
807
812
|
let lastBlock = -1;
|
|
808
813
|
for (const { from, to } of state.selection.ranges) {
|
|
@@ -874,6 +879,7 @@ export const setBlockquote =
|
|
|
874
879
|
);
|
|
875
880
|
return true;
|
|
876
881
|
};
|
|
882
|
+
};
|
|
877
883
|
|
|
878
884
|
export const addBlockquote = setBlockquote(true);
|
|
879
885
|
|
|
@@ -944,9 +950,10 @@ export const addCodeblock: StateCommand = (target) => {
|
|
|
944
950
|
};
|
|
945
951
|
|
|
946
952
|
export const removeCodeblock: StateCommand = ({ state, dispatch }) => {
|
|
947
|
-
const changes: ChangeSpec[] = [];
|
|
948
953
|
let lastBlock = -1;
|
|
949
|
-
|
|
954
|
+
|
|
955
|
+
// Find all code blocks, remove their markup.
|
|
956
|
+
const changes: ChangeSpec[] = [];
|
|
950
957
|
for (const { from, to } of state.selection.ranges) {
|
|
951
958
|
syntaxTree(state).iterate({
|
|
952
959
|
from,
|
|
@@ -965,7 +972,7 @@ export const removeCodeblock: StateCommand = ({ state, dispatch }) => {
|
|
|
965
972
|
});
|
|
966
973
|
}
|
|
967
974
|
} else {
|
|
968
|
-
// Indented code block
|
|
975
|
+
// Indented code block.
|
|
969
976
|
const column = node.from - firstLine.from;
|
|
970
977
|
for (let line = firstLine; ; line = state.doc.line(line.number + 1)) {
|
|
971
978
|
changes.push({ from: line.from + column - 4, to: line.from + column });
|
|
@@ -1050,7 +1057,7 @@ const Textblocks: { [name: string]: NonNullable<Formatting['blockType']> } = {
|
|
|
1050
1057
|
};
|
|
1051
1058
|
|
|
1052
1059
|
/**
|
|
1053
|
-
* Query
|
|
1060
|
+
* Query the editor state for the active formatting at the selection.
|
|
1054
1061
|
*/
|
|
1055
1062
|
export const getFormatting = (state: EditorState): Formatting => {
|
|
1056
1063
|
// These will track the formatting we've seen so far.
|
|
@@ -183,7 +183,9 @@ export const markdownHighlightStyle = (readonly?: boolean) => {
|
|
|
183
183
|
],
|
|
184
184
|
{
|
|
185
185
|
scope: markdownLanguage,
|
|
186
|
-
all: {
|
|
186
|
+
all: {
|
|
187
|
+
fontFamily: getToken('fontFamily.body', []).join(','),
|
|
188
|
+
},
|
|
187
189
|
},
|
|
188
190
|
);
|
|
189
191
|
};
|
|
@@ -15,7 +15,6 @@ 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
|
-
|
|
19
18
|
// TODO(burdon): Advanced formatting (left/right/center).
|
|
20
19
|
// TODO(burdon): Editor to auto balance columns.
|
|
21
20
|
|
|
@@ -82,19 +81,21 @@ const update = (state: EditorState, options: TableOptions) => {
|
|
|
82
81
|
});
|
|
83
82
|
|
|
84
83
|
tables.forEach((table) => {
|
|
85
|
-
const
|
|
86
|
-
if (
|
|
84
|
+
const replace = state.readOnly || cursor < table.from || cursor > table.to;
|
|
85
|
+
if (replace) {
|
|
87
86
|
builder.add(
|
|
88
87
|
table.from,
|
|
89
88
|
table.to,
|
|
90
89
|
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' }));
|
|
94
98
|
}
|
|
95
|
-
|
|
96
|
-
// Add class for styling.
|
|
97
|
-
builder.add(table.from, table.to, Decoration.mark({ class: 'cm-table' }));
|
|
98
99
|
});
|
|
99
100
|
|
|
100
101
|
return builder.finish();
|
package/src/hooks/index.ts
CHANGED
|
@@ -36,9 +36,9 @@ export const useTextEditor = (cb: () => UseTextEditorProps = () => ({}), deps: D
|
|
|
36
36
|
log('create', { id });
|
|
37
37
|
|
|
38
38
|
// https://codemirror.net/docs/ref/#state.EditorStateConfig
|
|
39
|
+
// NOTE: Don't set selection here in case it is invalid (and crashes the state); dispatch below.
|
|
39
40
|
const state = EditorState.create({
|
|
40
41
|
doc,
|
|
41
|
-
selection,
|
|
42
42
|
extensions: [
|
|
43
43
|
id && documentId.of(id),
|
|
44
44
|
// TODO(burdon): Doesn't catch errors in keymap functions.
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { type DocAccessor, type TextObject, type EchoReactiveObject } from '@dxos/echo-schema';
|
|
2
|
-
export declare const useDocAccessor: <T = any>(text: TextObject | EchoReactiveObject<{
|
|
3
|
-
content: string;
|
|
4
|
-
}>) => {
|
|
5
|
-
id: string;
|
|
6
|
-
doc: string | undefined;
|
|
7
|
-
accessor: DocAccessor<T>;
|
|
8
|
-
};
|
|
9
|
-
//# sourceMappingURL=useDocAccessor.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useDocAccessor.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useDocAccessor.ts"],"names":[],"mappings":"AAMA,OAAO,EAEL,KAAK,WAAW,EAEhB,KAAK,UAAU,EACf,KAAK,kBAAkB,EACxB,MAAM,mBAAmB,CAAC;AAG3B,eAAO,MAAM,cAAc,kBACnB,UAAU,GAAG,mBAAmB;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;QACnD,MAAM;SAAO,MAAM,GAAG,SAAS;;CASvC,CAAC"}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2024 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { useMemo } from 'react';
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
createDocAccessor,
|
|
9
|
-
type DocAccessor,
|
|
10
|
-
getTextContent,
|
|
11
|
-
type TextObject,
|
|
12
|
-
type EchoReactiveObject,
|
|
13
|
-
} from '@dxos/echo-schema';
|
|
14
|
-
|
|
15
|
-
// TODO(burdon): Factor out.
|
|
16
|
-
export const useDocAccessor = <T = any>(
|
|
17
|
-
text: TextObject | EchoReactiveObject<{ content: string }>,
|
|
18
|
-
): { id: string; doc: string | undefined; accessor: DocAccessor<T> } => {
|
|
19
|
-
return useMemo(
|
|
20
|
-
() => ({
|
|
21
|
-
id: text.id,
|
|
22
|
-
doc: getTextContent(text),
|
|
23
|
-
accessor: createDocAccessor<T>(text),
|
|
24
|
-
}),
|
|
25
|
-
[text],
|
|
26
|
-
);
|
|
27
|
-
};
|