@dxos/react-ui-editor 0.3.9-main.2b2db7f → 0.3.9-main.2dc53cf
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 +298 -26
- 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.stories.d.ts +3 -2
- package/dist/types/src/components/Editor/Editor.stories.d.ts.map +1 -1
- package/dist/types/src/components/Markdown/Markdown.d.ts +2 -1
- package/dist/types/src/components/Markdown/Markdown.d.ts.map +1 -1
- package/dist/types/src/components/Markdown/Markdown.stories.d.ts +2 -1
- package/dist/types/src/components/Markdown/Markdown.stories.d.ts.map +1 -1
- package/dist/types/src/components/Markdown/markdownTheme.d.ts +2 -16
- package/dist/types/src/components/Markdown/markdownTheme.d.ts.map +1 -1
- package/dist/types/src/components/RichText/RichText.stories.d.ts +2 -1
- 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 +32 -0
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/extensions/hyperlink.d.ts +10 -0
- package/dist/types/src/components/TextEditor/extensions/hyperlink.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/extensions/hyperlinkTooltip.d.ts +9 -0
- package/dist/types/src/components/TextEditor/extensions/hyperlinkTooltip.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/extensions/index.d.ts +3 -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 +8 -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/package.json +20 -16
- package/src/automerge/automerge-plugin/index.ts +1 -1
- package/src/automerge/automerge.stories.tsx +1 -2
- package/src/components/Editor/Editor.stories.tsx +2 -0
- package/src/components/Markdown/Markdown.stories.tsx +2 -0
- package/src/components/Markdown/Markdown.tsx +6 -2
- package/src/components/Markdown/markdownTheme.ts +20 -16
- package/src/components/RichText/RichText.stories.tsx +2 -0
- package/src/components/TextEditor/TextEditor.stories.tsx +83 -0
- package/src/components/TextEditor/TextEditor.tsx +130 -0
- package/src/components/TextEditor/extensions/hyperlink.tsx +108 -0
- package/src/components/TextEditor/extensions/hyperlinkTooltip.tsx +76 -0
- package/src/components/TextEditor/extensions/index.ts +6 -0
- package/src/components/TextEditor/index.ts +11 -0
- package/src/components/TextEditor/theme.ts +33 -0
- package/src/components/index.ts +1 -0
|
@@ -0,0 +1,108 @@
|
|
|
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, or they would become part of CodeMirror’s editable content element.
|
|
19
|
+
// You should be able to introduce new contenteditable=true child elements inside of them.
|
|
20
|
+
|
|
21
|
+
const markdownLinkRegexp = /\[([^\]]+)]\(([^)]+)\)(!?)/gi;
|
|
22
|
+
|
|
23
|
+
const linkDecorator = () =>
|
|
24
|
+
new MatchDecorator({
|
|
25
|
+
regexp: markdownLinkRegexp, // regexp || defaultRegexp,
|
|
26
|
+
decorate: (add, from, to, match, view) => {
|
|
27
|
+
const [_, label, url] = match;
|
|
28
|
+
const p0 = {
|
|
29
|
+
start: from,
|
|
30
|
+
end: from + 1,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const p1 = {
|
|
34
|
+
start: from + 1,
|
|
35
|
+
end: from + 1 + label.length,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const p2 = {
|
|
39
|
+
start: p1.end,
|
|
40
|
+
end: p1.end + 1,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const p3 = {
|
|
44
|
+
start: p1.end + 1,
|
|
45
|
+
end: p1.end + 1 + url.length + 2,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// TODO(burdon): Wrap with tagName?
|
|
49
|
+
add(p0.start, p0.end, Decoration.mark({ class: 'cm-hyperlink-bracket' }));
|
|
50
|
+
add(p1.start, p1.end, Decoration.mark({ class: 'cm-hyperlink-label' }));
|
|
51
|
+
add(p2.start, p2.end, Decoration.mark({ class: 'cm-hyperlink-bracket' }));
|
|
52
|
+
add(p3.start, p3.end, Decoration.mark({ class: 'cm-hyperlink-url', __attributes: { style: 'display: none' } }));
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// const anchor = (dom: HTMLAnchorElement) => dom;
|
|
57
|
+
|
|
58
|
+
export function hyperLinkExtension() {
|
|
59
|
+
return ViewPlugin.fromClass(
|
|
60
|
+
class HyperLinkView {
|
|
61
|
+
readonly decorator: MatchDecorator;
|
|
62
|
+
decorations: DecorationSet;
|
|
63
|
+
constructor(view: EditorView) {
|
|
64
|
+
this.decorator = linkDecorator();
|
|
65
|
+
this.decorations = this.decorator.createDeco(view);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
update(update: ViewUpdate) {
|
|
69
|
+
if (update.docChanged || update.viewportChanged) {
|
|
70
|
+
this.decorations = this.decorator.updateDeco(update, this.decorations);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
decorations: (view) => view.decorations,
|
|
76
|
+
eventHandlers: {
|
|
77
|
+
// TODO(burdon): CLick to expand link.
|
|
78
|
+
// https://discuss.codemirror.net/t/decorator-iteration-on-click-event/4226/2
|
|
79
|
+
mousedown: function (event, view) {
|
|
80
|
+
const pos = view.posAtDOM(event.target as Node);
|
|
81
|
+
if (pos !== 0) {
|
|
82
|
+
console.log(this);
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
// TODO(burdon): Hide when cursor moves away.
|
|
86
|
+
keydown: (event, view) => {
|
|
87
|
+
view.update([]);
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export const hyperLinkStyle = EditorView.baseTheme({
|
|
95
|
+
'.cm-hyperlink-label': {
|
|
96
|
+
cursor: 'pointer',
|
|
97
|
+
textDecoration: 'underline',
|
|
98
|
+
},
|
|
99
|
+
'.cm-hyperlink-bracket': {
|
|
100
|
+
opacity: 0.2,
|
|
101
|
+
},
|
|
102
|
+
'.cm-hyperlink-url': {
|
|
103
|
+
opacity: 0.5,
|
|
104
|
+
fontFamily: 'monospace',
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
export const hyperlink: Extension = [hyperLinkExtension(), hyperLinkStyle];
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { hoverTooltip } from '@codemirror/view';
|
|
6
|
+
import { ArrowCircleUp } from '@phosphor-icons/react';
|
|
7
|
+
import React, { StrictMode } from 'react';
|
|
8
|
+
import { createRoot } from 'react-dom/client';
|
|
9
|
+
|
|
10
|
+
import { getSize, mx, tooltipContent } from '@dxos/react-ui-theme';
|
|
11
|
+
|
|
12
|
+
const markdownLinkRegexp = /\[([^\]]+)]\(([^)]+)\)/;
|
|
13
|
+
|
|
14
|
+
export type OnHyperlinkHover = (el: Element, url: string) => void;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* https://codemirror.net/examples/tooltip
|
|
18
|
+
* https://codemirror.net/docs/ref/#view.hoverTooltip
|
|
19
|
+
* https://github.com/codemirror/view/blob/main/src/tooltip.ts
|
|
20
|
+
*/
|
|
21
|
+
export const createHyperlinkTooltip = (onHover: OnHyperlinkHover, regexp = markdownLinkRegexp) =>
|
|
22
|
+
hoverTooltip((view, pos) => {
|
|
23
|
+
const { from, text } = view.state.doc.lineAt(pos);
|
|
24
|
+
const p = pos - from;
|
|
25
|
+
|
|
26
|
+
let idx = 0;
|
|
27
|
+
let match;
|
|
28
|
+
do {
|
|
29
|
+
match = text.substring(idx).match(regexp);
|
|
30
|
+
if (!match) {
|
|
31
|
+
match = undefined;
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
idx = text.indexOf(match[0]);
|
|
36
|
+
if (p >= idx && p < idx + match[0].length) {
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
idx += match[0].length;
|
|
41
|
+
} while (true);
|
|
42
|
+
|
|
43
|
+
if (!match) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const [, , url] = match ?? [];
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
pos: idx + from,
|
|
51
|
+
end: idx + from + match[0].length,
|
|
52
|
+
above: true,
|
|
53
|
+
create: () => {
|
|
54
|
+
const el = document.createElement('a');
|
|
55
|
+
el.innerText = '_'; // Required so doesn't collapse.
|
|
56
|
+
el.className = tooltipContent({}, 'mb-2 p-1');
|
|
57
|
+
el.setAttribute('target', '_blank');
|
|
58
|
+
el.setAttribute('href', url);
|
|
59
|
+
onHover(el, url);
|
|
60
|
+
return { dom: el };
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// TODO(burdon): Factor out (remove theme/icon deps)?
|
|
66
|
+
export const defaultHyperLinkTooltip = createHyperlinkTooltip((el, url) => {
|
|
67
|
+
const web = new URL(url);
|
|
68
|
+
createRoot(el).render(
|
|
69
|
+
<StrictMode>
|
|
70
|
+
<div className='flex gap-1 items-center'>
|
|
71
|
+
<ArrowCircleUp className={mx(getSize(6), 'text-blue-500')} />
|
|
72
|
+
<p className='pr-1'>{web.origin}</p>
|
|
73
|
+
</div>
|
|
74
|
+
</StrictMode>,
|
|
75
|
+
);
|
|
76
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
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
|
+
* https://codemirror.net/examples/styling
|
|
14
|
+
*/
|
|
15
|
+
// TODO(burdon): If given a "theme" suffix, `__docgen` properties are added to the object.
|
|
16
|
+
export const defaultStyles: {
|
|
17
|
+
[selector: string]: StyleSpec;
|
|
18
|
+
} = {
|
|
19
|
+
'&.cm-focused': {
|
|
20
|
+
outline: 'none',
|
|
21
|
+
},
|
|
22
|
+
'.cm-placeholder': {
|
|
23
|
+
fontFamily: get(tokens, 'fontFamily.body', []).join(','),
|
|
24
|
+
},
|
|
25
|
+
'& .cm-scroller': {
|
|
26
|
+
fontFamily: get(tokens, 'fontFamily.body', []).join(','),
|
|
27
|
+
overflow: 'visible',
|
|
28
|
+
},
|
|
29
|
+
'.cm-tooltip': {
|
|
30
|
+
backgroundColor: 'transparent',
|
|
31
|
+
border: 'none',
|
|
32
|
+
},
|
|
33
|
+
};
|
package/src/components/index.ts
CHANGED