@dxos/react-ui-editor 0.3.9-main.dd8d283 → 0.3.9-main.de60860
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 +412 -56
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/automerge/automerge-plugin/index.d.ts +1 -1
- package/dist/types/src/automerge/automerge-plugin/index.d.ts.map +1 -1
- package/dist/types/src/automerge/automerge.stories.d.ts.map +1 -1
- package/dist/types/src/components/Editor/Editor.d.ts +0 -1
- package/dist/types/src/components/Editor/Editor.d.ts.map +1 -1
- package/dist/types/src/components/Editor/Editor.stories.d.ts +3 -8
- package/dist/types/src/components/Editor/Editor.stories.d.ts.map +1 -1
- package/dist/types/src/components/Markdown/Markdown.d.ts +4 -2
- package/dist/types/src/components/Markdown/Markdown.d.ts.map +1 -1
- package/dist/types/src/components/Markdown/Markdown.stories.d.ts +1 -1
- package/dist/types/src/components/Markdown/Markdown.stories.d.ts.map +1 -1
- package/dist/types/src/components/Markdown/markdownTheme.d.ts +10 -17
- package/dist/types/src/components/Markdown/markdownTheme.d.ts.map +1 -1
- package/dist/types/src/components/RichText/RichText.stories.d.ts +2 -3
- package/dist/types/src/components/RichText/RichText.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.d.ts +39 -0
- package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +18 -0
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/extensions/hyperlinkDecoration.d.ts +11 -0
- package/dist/types/src/components/TextEditor/extensions/hyperlinkDecoration.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/extensions/hyperlinkTooltip.d.ts +8 -0
- package/dist/types/src/components/TextEditor/extensions/hyperlinkTooltip.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/extensions/hyperlinkWidget.d.ts +10 -0
- package/dist/types/src/components/TextEditor/extensions/hyperlinkWidget.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/extensions/index.d.ts +4 -0
- package/dist/types/src/components/TextEditor/extensions/index.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/index.d.ts +6 -0
- package/dist/types/src/components/TextEditor/index.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/theme.d.ts +9 -0
- package/dist/types/src/components/TextEditor/theme.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +1 -0
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/testing/replicator.d.ts.map +1 -1
- package/package.json +21 -17
- package/src/automerge/automerge-plugin/index.ts +1 -1
- package/src/automerge/automerge.stories.tsx +1 -2
- package/src/components/Editor/Editor.stories.tsx +7 -7
- package/src/components/Editor/Editor.tsx +6 -3
- package/src/components/Markdown/Markdown.stories.tsx +39 -25
- package/src/components/Markdown/Markdown.tsx +63 -30
- package/src/components/Markdown/markdownTheme.ts +32 -17
- package/src/components/RichText/RichText.stories.tsx +37 -33
- package/src/components/TextEditor/TextEditor.stories.tsx +72 -0
- package/src/components/TextEditor/TextEditor.tsx +131 -0
- package/src/components/TextEditor/extensions/hyperlinkDecoration.ts +99 -0
- package/src/components/TextEditor/extensions/hyperlinkTooltip.tsx +61 -0
- package/src/components/TextEditor/extensions/hyperlinkWidget.tsx +119 -0
- package/src/components/TextEditor/extensions/index.ts +7 -0
- package/src/components/TextEditor/index.ts +11 -0
- package/src/components/TextEditor/theme.ts +35 -0
- package/src/components/index.ts +1 -0
- package/src/testing/replicator.ts +0 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { hoverTooltip } from '@codemirror/view';
|
|
6
|
+
|
|
7
|
+
import { tooltipContent } from '@dxos/react-ui-theme';
|
|
8
|
+
|
|
9
|
+
const markdownLinkRegexp = /\[([^\]]+)]\(([^)]+)\)/;
|
|
10
|
+
|
|
11
|
+
export type OnHyperlinkHover = (el: Element, url: string) => void;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* https://codemirror.net/examples/tooltip
|
|
15
|
+
* https://codemirror.net/docs/ref/#view.hoverTooltip
|
|
16
|
+
* https://github.com/codemirror/view/blob/main/src/tooltip.ts
|
|
17
|
+
*/
|
|
18
|
+
// TODO(burdon): Create config object.
|
|
19
|
+
export const createHyperlinkTooltip = (onHover: OnHyperlinkHover, regexp = markdownLinkRegexp) =>
|
|
20
|
+
hoverTooltip((view, pos) => {
|
|
21
|
+
const { from, text } = view.state.doc.lineAt(pos);
|
|
22
|
+
const p = pos - from;
|
|
23
|
+
|
|
24
|
+
let idx = 0;
|
|
25
|
+
let match;
|
|
26
|
+
do {
|
|
27
|
+
match = text.substring(idx).match(regexp);
|
|
28
|
+
if (!match) {
|
|
29
|
+
match = undefined;
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
idx = text.indexOf(match[0]);
|
|
34
|
+
if (p >= idx && p < idx + match[0].length) {
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
idx += match[0].length;
|
|
39
|
+
} while (true);
|
|
40
|
+
|
|
41
|
+
if (!match) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const [, , url] = match ?? [];
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
pos: idx + from,
|
|
49
|
+
end: idx + from + match[0].length,
|
|
50
|
+
above: true,
|
|
51
|
+
create: () => {
|
|
52
|
+
const el = document.createElement('a');
|
|
53
|
+
el.innerText = '_'; // Required so doesn't collapse.
|
|
54
|
+
el.className = tooltipContent({}, 'mb-2 p-1');
|
|
55
|
+
el.setAttribute('target', '_blank');
|
|
56
|
+
el.setAttribute('href', url);
|
|
57
|
+
onHover(el, url);
|
|
58
|
+
return { dom: el };
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
});
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
// Adapted from: https://github.com/uiwjs/react-codemirror/blob/master/extensions/hyper-link/src/index.ts
|
|
4
|
+
//
|
|
5
|
+
|
|
6
|
+
import { type Extension } from '@codemirror/state';
|
|
7
|
+
import {
|
|
8
|
+
Decoration,
|
|
9
|
+
type DecorationSet,
|
|
10
|
+
EditorView,
|
|
11
|
+
MatchDecorator,
|
|
12
|
+
ViewPlugin,
|
|
13
|
+
type ViewUpdate,
|
|
14
|
+
} from '@codemirror/view';
|
|
15
|
+
|
|
16
|
+
// Notes: Can't edit widget content (or cursor nav through them).
|
|
17
|
+
// - https://discuss.codemirror.net/t/focusing-inputs-within-widgets/5178/7
|
|
18
|
+
// Widgets intentionally always get set to contenteditable=false,
|
|
19
|
+
// or they would become part of CodeMirror’s editable content element.
|
|
20
|
+
// You should be able to introduce new contenteditable=true child elements inside of them.
|
|
21
|
+
|
|
22
|
+
const markdownLinkRegexp = /\[([^\]]+)]\(([^)]+)\)(!?)/gi;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Custom link decorator.
|
|
26
|
+
* NOTE: The default markdown extension formats links by default.
|
|
27
|
+
* @deprecated
|
|
28
|
+
*/
|
|
29
|
+
const linkDecorator = () =>
|
|
30
|
+
new MatchDecorator({
|
|
31
|
+
regexp: markdownLinkRegexp,
|
|
32
|
+
decorate: (add, from, to, match, view) => {
|
|
33
|
+
const [_, label, url] = match;
|
|
34
|
+
const p0 = {
|
|
35
|
+
start: from,
|
|
36
|
+
end: from + 1,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const p1 = {
|
|
40
|
+
start: from + 1,
|
|
41
|
+
end: from + 1 + label.length,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const p2 = {
|
|
45
|
+
start: p1.end,
|
|
46
|
+
end: p1.end + 1,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const p3 = {
|
|
50
|
+
start: p1.end + 1,
|
|
51
|
+
end: p1.end + 1 + url.length + 2,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
add(
|
|
55
|
+
p0.start,
|
|
56
|
+
p0.end,
|
|
57
|
+
Decoration.mark({ class: 'cm-hyperlink-bracket', _attributes: { style: 'display: none' } }),
|
|
58
|
+
);
|
|
59
|
+
add(p1.start, p1.end, Decoration.mark({ class: 'cm-hyperlink-label' }));
|
|
60
|
+
add(
|
|
61
|
+
p2.start,
|
|
62
|
+
p2.end,
|
|
63
|
+
Decoration.mark({ class: 'cm-hyperlink-bracket', _attributes: { style: 'display: none' } }),
|
|
64
|
+
);
|
|
65
|
+
add(p3.start, p3.end, Decoration.mark({ class: 'cm-hyperlink-url', _attributes: { style: 'display: none' } }));
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
export function hyperLinkExtension() {
|
|
70
|
+
return ViewPlugin.fromClass(
|
|
71
|
+
class HyperLinkView {
|
|
72
|
+
readonly decorator: MatchDecorator;
|
|
73
|
+
decorations: DecorationSet;
|
|
74
|
+
constructor(view: EditorView) {
|
|
75
|
+
this.decorator = linkDecorator();
|
|
76
|
+
this.decorations = this.decorator.createDeco(view);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
update(update: ViewUpdate) {
|
|
80
|
+
if (update.docChanged || update.viewportChanged) {
|
|
81
|
+
this.decorations = this.decorator.updateDeco(update, this.decorations);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
decorations: (view) => view.decorations,
|
|
87
|
+
eventHandlers: {
|
|
88
|
+
// TODO(burdon): CLick to expand link.
|
|
89
|
+
// https://discuss.codemirror.net/t/decorator-iteration-on-click-event/4226/2
|
|
90
|
+
mousedown: function (event, view) {
|
|
91
|
+
const pos = view.posAtDOM(event.target as Node);
|
|
92
|
+
if (pos !== 0) {
|
|
93
|
+
console.log(this);
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
// TODO(burdon): Hide when cursor moves away.
|
|
97
|
+
keydown: (event, view) => {
|
|
98
|
+
view.update([]);
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export const hyperlinkStyle = EditorView.baseTheme({
|
|
106
|
+
'.cm-hyperlink-label': {
|
|
107
|
+
cursor: 'pointer',
|
|
108
|
+
textDecoration: 'underline',
|
|
109
|
+
},
|
|
110
|
+
'.cm-hyperlink-bracket': {
|
|
111
|
+
opacity: 0.2,
|
|
112
|
+
},
|
|
113
|
+
'.cm-hyperlink-url': {
|
|
114
|
+
opacity: 0.5,
|
|
115
|
+
fontFamily: 'monospace',
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
export const hyperlinkWidget: Extension = [hyperLinkExtension(), hyperlinkStyle];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import get from 'lodash.get';
|
|
6
|
+
import { type StyleSpec } from 'style-mod';
|
|
7
|
+
|
|
8
|
+
import { tailwindConfig, type TailwindConfig } from '@dxos/react-ui-theme';
|
|
9
|
+
|
|
10
|
+
const tokens: TailwindConfig['theme'] = tailwindConfig({}).theme;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Minimal styles.
|
|
14
|
+
* https://codemirror.net/examples/styling
|
|
15
|
+
*/
|
|
16
|
+
// TODO(burdon): If given a "theme" suffix, `__docgen` properties are added to the object.
|
|
17
|
+
export const defaultStyles: {
|
|
18
|
+
[selector: string]: StyleSpec;
|
|
19
|
+
} = {
|
|
20
|
+
'&.cm-focused': {
|
|
21
|
+
outline: 'none',
|
|
22
|
+
},
|
|
23
|
+
'.cm-scroller': {
|
|
24
|
+
fontFamily: get(tokens, 'fontFamily.body', []).join(','),
|
|
25
|
+
overflow: 'visible',
|
|
26
|
+
},
|
|
27
|
+
'.cm-tooltip': {
|
|
28
|
+
backgroundColor: 'transparent',
|
|
29
|
+
border: 'none',
|
|
30
|
+
},
|
|
31
|
+
'.cm-link': {
|
|
32
|
+
color: get(tokens, 'extend.colors.primary.500'),
|
|
33
|
+
textDecoration: 'underline',
|
|
34
|
+
},
|
|
35
|
+
};
|
package/src/components/index.ts
CHANGED