@dxos/react-ui-editor 0.3.9-main.325dd0a → 0.3.9-main.35264cb
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 -2
- 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 +2 -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 -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 +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 +4 -2
- package/src/components/Editor/Editor.tsx +6 -3
- package/src/components/Markdown/Markdown.stories.tsx +15 -1
- package/src/components/Markdown/Markdown.tsx +63 -30
- package/src/components/Markdown/markdownTheme.ts +32 -17
- package/src/components/RichText/RichText.stories.tsx +2 -0
- 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
|
@@ -261,10 +261,7 @@ import { tailwindConfig } from "@dxos/react-ui-theme";
|
|
|
261
261
|
var tokens = tailwindConfig({}).theme;
|
|
262
262
|
|
|
263
263
|
// packages/ui/react-ui-editor/src/components/Markdown/markdownTheme.ts
|
|
264
|
-
var ivory = "#abb2bf";
|
|
265
264
|
var malibu = "#61afef";
|
|
266
|
-
var highlightBackground = "#2c313a";
|
|
267
|
-
var tooltipBackground = "#353a42";
|
|
268
265
|
var cursor = "#ffffff";
|
|
269
266
|
var monospace = get(tokens, "fontFamily.mono", [
|
|
270
267
|
"monospace"
|
|
@@ -307,24 +304,28 @@ var markdownTheme = {
|
|
|
307
304
|
// border: 'none',
|
|
308
305
|
// color: '#ddd'
|
|
309
306
|
// },
|
|
310
|
-
".
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
},
|
|
314
|
-
".dark & .cm-tooltip .cm-tooltip-arrow:before": {
|
|
315
|
-
borderTopColor: "transparent",
|
|
316
|
-
borderBottomColor: "transparent"
|
|
317
|
-
},
|
|
318
|
-
".dark & .cm-tooltip .cm-tooltip-arrow:after": {
|
|
319
|
-
borderTopColor: tooltipBackground,
|
|
320
|
-
borderBottomColor: tooltipBackground
|
|
321
|
-
},
|
|
322
|
-
".dark & .cm-tooltip-autocomplete": {
|
|
323
|
-
"& > ul > li[aria-selected]": {
|
|
324
|
-
backgroundColor: highlightBackground,
|
|
325
|
-
color: ivory
|
|
326
|
-
}
|
|
307
|
+
".cm-tooltip": {
|
|
308
|
+
backgroundColor: "transparent",
|
|
309
|
+
border: "none"
|
|
327
310
|
},
|
|
311
|
+
// '.dark & .cm-tooltip': {
|
|
312
|
+
// border: 'none',
|
|
313
|
+
// backgroundColor: tooltipBackground,
|
|
314
|
+
// },
|
|
315
|
+
// '.dark & .cm-tooltip .cm-tooltip-arrow:before': {
|
|
316
|
+
// borderTopColor: 'transparent',
|
|
317
|
+
// borderBottomColor: 'transparent',
|
|
318
|
+
// },
|
|
319
|
+
// '.dark & .cm-tooltip .cm-tooltip-arrow:after': {
|
|
320
|
+
// borderTopColor: tooltipBackground,
|
|
321
|
+
// borderBottomColor: tooltipBackground,
|
|
322
|
+
// },
|
|
323
|
+
// '.dark & .cm-tooltip-autocomplete': {
|
|
324
|
+
// '& > ul > li[aria-selected]': {
|
|
325
|
+
// backgroundColor: highlightBackground,
|
|
326
|
+
// color: ivory,
|
|
327
|
+
// },
|
|
328
|
+
// },
|
|
328
329
|
"&.cm-focused": {
|
|
329
330
|
outline: "none"
|
|
330
331
|
},
|
|
@@ -396,6 +397,13 @@ var markdownTheme = {
|
|
|
396
397
|
"& .cm-yLineSelection": {
|
|
397
398
|
margin: "0"
|
|
398
399
|
},
|
|
400
|
+
".cm-link": {
|
|
401
|
+
color: "rgb(20 89 208)",
|
|
402
|
+
textDecorationLine: "underline",
|
|
403
|
+
textDecorationThickness: "1px",
|
|
404
|
+
textUnderlineOffset: "2px",
|
|
405
|
+
borderRadius: ".125rem"
|
|
406
|
+
},
|
|
399
407
|
...Object.keys(get(tokens, "extend.fontSize", {})).reduce((acc, fontSize) => {
|
|
400
408
|
const height = get(tokens, [
|
|
401
409
|
"extend",
|
|
@@ -416,7 +424,7 @@ var markdownTheme = {
|
|
|
416
424
|
return acc;
|
|
417
425
|
}, {})
|
|
418
426
|
};
|
|
419
|
-
var
|
|
427
|
+
var markdownHighlightStyle = HighlightStyle.define([
|
|
420
428
|
{
|
|
421
429
|
tag: [
|
|
422
430
|
tags.keyword,
|
|
@@ -541,7 +549,7 @@ var EditorModes = [
|
|
|
541
549
|
"default",
|
|
542
550
|
"vim"
|
|
543
551
|
];
|
|
544
|
-
var MarkdownEditor = /* @__PURE__ */ forwardRef(({ model, slots = {},
|
|
552
|
+
var MarkdownEditor = /* @__PURE__ */ forwardRef(({ model, slots = {}, editorMode, extensions = [], onChange }, forwardedRef) => {
|
|
545
553
|
const { id, content, provider, peer } = model ?? {};
|
|
546
554
|
const { themeMode } = useThemeContext();
|
|
547
555
|
const tabsterDOMAttribute = useFocusableGroup({
|
|
@@ -593,49 +601,56 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ model, slots = {}, onChange,
|
|
|
593
601
|
const state2 = EditorState.create({
|
|
594
602
|
doc: content?.toString(),
|
|
595
603
|
extensions: [
|
|
596
|
-
// Based on https://github.com/codemirror/dev/issues/44#issuecomment-789093799
|
|
604
|
+
// Based on https://github.com/codemirror/dev/issues/44#issuecomment-789093799
|
|
597
605
|
listenChangesExtension,
|
|
598
|
-
|
|
599
|
-
vim()
|
|
600
|
-
] : [],
|
|
606
|
+
// TODO(burdon): Create custom bundle to make this class more reusable and retire simpler TextEditor component.
|
|
601
607
|
// All of https://github.com/codemirror/basic-setup minus line numbers and fold gutter.
|
|
608
|
+
// https://codemirror.net/docs/ref/#codemirror.basicSetup
|
|
609
|
+
autocompletion(),
|
|
610
|
+
bracketMatching(),
|
|
611
|
+
closeBrackets(),
|
|
612
|
+
crosshairCursor(),
|
|
613
|
+
dropCursor(),
|
|
614
|
+
drawSelection(),
|
|
615
|
+
highlightActiveLine(),
|
|
602
616
|
highlightActiveLineGutter(),
|
|
617
|
+
highlightSelectionMatches(),
|
|
603
618
|
highlightSpecialChars(),
|
|
604
619
|
history(),
|
|
605
|
-
drawSelection(),
|
|
606
|
-
dropCursor(),
|
|
607
|
-
EditorState.allowMultipleSelections.of(true),
|
|
608
620
|
indentOnInput(),
|
|
621
|
+
placeholder(slots.editor?.placeholder ?? ""),
|
|
622
|
+
rectangularSelection(),
|
|
609
623
|
syntaxHighlighting(defaultHighlightStyle, {
|
|
610
624
|
fallback: true
|
|
611
625
|
}),
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
autocompletion(),
|
|
615
|
-
rectangularSelection(),
|
|
616
|
-
crosshairCursor(),
|
|
617
|
-
highlightActiveLine(),
|
|
618
|
-
highlightSelectionMatches(),
|
|
619
|
-
placeholder(slots.editor?.placeholder ?? ""),
|
|
626
|
+
EditorState.allowMultipleSelections.of(true),
|
|
627
|
+
EditorView.lineWrapping,
|
|
620
628
|
keymap.of([
|
|
621
629
|
...closeBracketsKeymap,
|
|
630
|
+
...completionKeymap,
|
|
622
631
|
...defaultKeymap,
|
|
623
|
-
...searchKeymap,
|
|
624
|
-
...historyKeymap,
|
|
625
632
|
...foldKeymap,
|
|
626
|
-
...
|
|
633
|
+
...historyKeymap,
|
|
627
634
|
...lintKeymap,
|
|
635
|
+
...searchKeymap,
|
|
628
636
|
indentWithTab
|
|
629
637
|
]),
|
|
630
|
-
|
|
631
|
-
//
|
|
638
|
+
// Main extension.
|
|
639
|
+
// https://github.com/codemirror/lang-markdown
|
|
632
640
|
markdown({
|
|
633
641
|
base: markdownLanguage2,
|
|
634
642
|
codeLanguages: languages,
|
|
635
643
|
extensions: [
|
|
644
|
+
// TODO(burdon): This seems to upgrade the parser.
|
|
645
|
+
// GitHub flavored markdown bundle: Table, TaskList, Strikethrough, and Autolink.
|
|
646
|
+
// https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown
|
|
647
|
+
// https://github.github.com/gfm
|
|
648
|
+
// GFM,
|
|
649
|
+
// Custom styling.
|
|
636
650
|
markdownTagsExtension
|
|
637
651
|
]
|
|
638
652
|
}),
|
|
653
|
+
// Theme.
|
|
639
654
|
EditorView.theme({
|
|
640
655
|
...markdownTheme,
|
|
641
656
|
...slots.editor?.markdownTheme
|
|
@@ -646,11 +661,19 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ model, slots = {}, onChange,
|
|
|
646
661
|
syntaxHighlighting(defaultHighlightStyle)
|
|
647
662
|
],
|
|
648
663
|
// TODO(thure): All but one rule here apply to both themes; rename or refactor.
|
|
649
|
-
syntaxHighlighting(
|
|
650
|
-
//
|
|
664
|
+
syntaxHighlighting(markdownHighlightStyle),
|
|
665
|
+
// Settings.
|
|
666
|
+
...editorMode === "vim" ? [
|
|
667
|
+
vim()
|
|
668
|
+
] : [],
|
|
669
|
+
// TODO(burdon): Move to extensions.
|
|
670
|
+
// Replication and awareness (incl. remote selection).
|
|
671
|
+
// https://codemirror.net/docs/ref/#collab
|
|
651
672
|
...content instanceof YText ? [
|
|
652
673
|
yCollab(content, provider?.awareness)
|
|
653
|
-
] : []
|
|
674
|
+
] : [],
|
|
675
|
+
// Custom.
|
|
676
|
+
...extensions
|
|
654
677
|
]
|
|
655
678
|
});
|
|
656
679
|
setState(state2);
|
|
@@ -671,14 +694,17 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ model, slots = {}, onChange,
|
|
|
671
694
|
themeMode,
|
|
672
695
|
editorMode
|
|
673
696
|
]);
|
|
674
|
-
const handleKeyUp = useCallback((
|
|
697
|
+
const handleKeyUp = useCallback((event) => {
|
|
698
|
+
const { key, altKey, shiftKey, metaKey, ctrlKey } = event;
|
|
675
699
|
switch (key) {
|
|
676
|
-
case "Enter":
|
|
700
|
+
case "Enter": {
|
|
677
701
|
view?.contentDOM.focus();
|
|
678
702
|
break;
|
|
679
|
-
|
|
703
|
+
}
|
|
704
|
+
case "Escape": {
|
|
680
705
|
editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey) && parent?.focus();
|
|
681
706
|
break;
|
|
707
|
+
}
|
|
682
708
|
}
|
|
683
709
|
}, [
|
|
684
710
|
view,
|
|
@@ -686,11 +712,11 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ model, slots = {}, onChange,
|
|
|
686
712
|
]);
|
|
687
713
|
return /* @__PURE__ */ React.createElement("div", {
|
|
688
714
|
tabIndex: 0,
|
|
715
|
+
ref: setParent,
|
|
689
716
|
key: id,
|
|
690
717
|
...slots.root,
|
|
691
|
-
onKeyUp: handleKeyUp,
|
|
692
718
|
...editorMode !== "vim" ? tabsterDOMAttribute : {},
|
|
693
|
-
|
|
719
|
+
onKeyUp: handleKeyUp
|
|
694
720
|
});
|
|
695
721
|
});
|
|
696
722
|
|
|
@@ -706,7 +732,7 @@ import StarterKit from "@tiptap/starter-kit";
|
|
|
706
732
|
import React2, { forwardRef as forwardRef2, useImperativeHandle as useImperativeHandle2, useMemo as useMemo3 } from "react";
|
|
707
733
|
import { generateName as generateName2 } from "@dxos/display-name";
|
|
708
734
|
import { mx as mx2 } from "@dxos/react-ui-theme";
|
|
709
|
-
var useEditor = ({ model, placeholder:
|
|
735
|
+
var useEditor = ({ model, placeholder: placeholder3 = "Enter text\u2026", slots = {} }) => {
|
|
710
736
|
const extensions = useMemo3(() => [
|
|
711
737
|
StarterKit.configure({
|
|
712
738
|
// Extensions
|
|
@@ -811,7 +837,7 @@ var useEditor = ({ model, placeholder: placeholder2 = "Enter text\u2026", slots
|
|
|
811
837
|
})
|
|
812
838
|
] : [],
|
|
813
839
|
Placeholder.configure({
|
|
814
|
-
placeholder:
|
|
840
|
+
placeholder: placeholder3,
|
|
815
841
|
emptyEditorClass: "before:content-[attr(data-placeholder)] before:absolute opacity-50 cursor-text"
|
|
816
842
|
})
|
|
817
843
|
], [
|
|
@@ -842,8 +868,11 @@ var RichTextEditor = /* @__PURE__ */ forwardRef2((props, ref) => {
|
|
|
842
868
|
});
|
|
843
869
|
|
|
844
870
|
// packages/ui/react-ui-editor/src/components/Editor/Editor.tsx
|
|
845
|
-
var Editor = /* @__PURE__ */ memo(/* @__PURE__ */ forwardRef3(({ slots, ...
|
|
846
|
-
const model = useTextModel(
|
|
871
|
+
var Editor = /* @__PURE__ */ memo(/* @__PURE__ */ forwardRef3(({ slots, ...params }, forwardedRef) => {
|
|
872
|
+
const model = useTextModel(params);
|
|
873
|
+
if (!model) {
|
|
874
|
+
return null;
|
|
875
|
+
}
|
|
847
876
|
if (model?.content instanceof YXmlFragment) {
|
|
848
877
|
return /* @__PURE__ */ React3.createElement(RichTextEditor, {
|
|
849
878
|
ref: forwardedRef,
|
|
@@ -859,18 +888,345 @@ var Editor = /* @__PURE__ */ memo(/* @__PURE__ */ forwardRef3(({ slots, ...optio
|
|
|
859
888
|
}
|
|
860
889
|
}));
|
|
861
890
|
|
|
891
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/index.ts
|
|
892
|
+
import { tags as tags2 } from "@lezer/highlight";
|
|
893
|
+
|
|
894
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
895
|
+
import { closeBrackets as closeBrackets2 } from "@codemirror/autocomplete";
|
|
896
|
+
import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
|
|
897
|
+
import { EditorState as EditorState2 } from "@codemirror/state";
|
|
898
|
+
import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
|
|
899
|
+
import { EditorView as EditorView2, placeholder as placeholder2 } from "@codemirror/view";
|
|
900
|
+
import React4, { forwardRef as forwardRef4, useEffect as useEffect2, useImperativeHandle as useImperativeHandle3, useState as useState2, useCallback as useCallback2 } from "react";
|
|
901
|
+
import { yCollab as yCollab2 } from "y-codemirror.next";
|
|
902
|
+
import { useThemeContext as useThemeContext2 } from "@dxos/react-ui";
|
|
903
|
+
import { YText as YText2 } from "@dxos/text-model";
|
|
904
|
+
|
|
905
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/theme.ts
|
|
906
|
+
import get2 from "lodash.get";
|
|
907
|
+
import { tailwindConfig as tailwindConfig2 } from "@dxos/react-ui-theme";
|
|
908
|
+
var tokens2 = tailwindConfig2({}).theme;
|
|
909
|
+
var defaultStyles = {
|
|
910
|
+
"&.cm-focused": {
|
|
911
|
+
outline: "none"
|
|
912
|
+
},
|
|
913
|
+
".cm-scroller": {
|
|
914
|
+
fontFamily: get2(tokens2, "fontFamily.body", []).join(","),
|
|
915
|
+
overflow: "visible"
|
|
916
|
+
},
|
|
917
|
+
".cm-tooltip": {
|
|
918
|
+
backgroundColor: "transparent",
|
|
919
|
+
border: "none"
|
|
920
|
+
},
|
|
921
|
+
".cm-link": {
|
|
922
|
+
color: get2(tokens2, "extend.colors.primary.500"),
|
|
923
|
+
textDecoration: "underline"
|
|
924
|
+
}
|
|
925
|
+
};
|
|
926
|
+
|
|
927
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
928
|
+
var TextEditor = /* @__PURE__ */ forwardRef4(({ model, extensions = [], theme = defaultStyles, slots = {}, onKeyDown, ...props }, forwardedRef) => {
|
|
929
|
+
const { id, content } = model ?? {};
|
|
930
|
+
const { themeMode } = useThemeContext2();
|
|
931
|
+
const [parent, setParent] = useState2(null);
|
|
932
|
+
const [state, setState] = useState2();
|
|
933
|
+
const [view, setView] = useState2();
|
|
934
|
+
useImperativeHandle3(forwardedRef, () => {
|
|
935
|
+
return {
|
|
936
|
+
editor: parent,
|
|
937
|
+
state,
|
|
938
|
+
view
|
|
939
|
+
};
|
|
940
|
+
}, [
|
|
941
|
+
view
|
|
942
|
+
]);
|
|
943
|
+
useEffect2(() => {
|
|
944
|
+
if (!parent) {
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
view?.destroy();
|
|
948
|
+
const state2 = EditorState2.create({
|
|
949
|
+
doc: content?.toString(),
|
|
950
|
+
extensions: [
|
|
951
|
+
bracketMatching2(),
|
|
952
|
+
closeBrackets2(),
|
|
953
|
+
placeholder2(slots.editor?.placeholder ?? ""),
|
|
954
|
+
EditorView2.lineWrapping,
|
|
955
|
+
// Themes.
|
|
956
|
+
EditorView2.theme(theme),
|
|
957
|
+
...themeMode === "dark" ? [
|
|
958
|
+
syntaxHighlighting2(oneDarkHighlightStyle2)
|
|
959
|
+
] : [
|
|
960
|
+
syntaxHighlighting2(defaultHighlightStyle2)
|
|
961
|
+
],
|
|
962
|
+
// Replication.
|
|
963
|
+
// TODO(burdon): Move to default extensions.
|
|
964
|
+
...content instanceof YText2 ? [
|
|
965
|
+
yCollab2(content, void 0)
|
|
966
|
+
] : [],
|
|
967
|
+
// Custom.
|
|
968
|
+
...extensions
|
|
969
|
+
]
|
|
970
|
+
});
|
|
971
|
+
setState(state2);
|
|
972
|
+
setView(new EditorView2({
|
|
973
|
+
state: state2,
|
|
974
|
+
parent
|
|
975
|
+
}));
|
|
976
|
+
return () => {
|
|
977
|
+
view?.destroy();
|
|
978
|
+
setView(void 0);
|
|
979
|
+
setState(void 0);
|
|
980
|
+
};
|
|
981
|
+
}, [
|
|
982
|
+
parent,
|
|
983
|
+
content,
|
|
984
|
+
themeMode
|
|
985
|
+
]);
|
|
986
|
+
const handleKeyDown = useCallback2((event) => {
|
|
987
|
+
if (view) {
|
|
988
|
+
const { head, from, to } = view.state.selection.ranges[0];
|
|
989
|
+
const { number } = view.state.doc.lineAt(head);
|
|
990
|
+
const after = view.state.sliceDoc(from);
|
|
991
|
+
onKeyDown?.(event, {
|
|
992
|
+
from,
|
|
993
|
+
to,
|
|
994
|
+
line: number,
|
|
995
|
+
lines: view.state.doc.lines,
|
|
996
|
+
after
|
|
997
|
+
});
|
|
998
|
+
}
|
|
999
|
+
}, [
|
|
1000
|
+
view
|
|
1001
|
+
]);
|
|
1002
|
+
return /* @__PURE__ */ React4.createElement("div", {
|
|
1003
|
+
key: id,
|
|
1004
|
+
ref: setParent,
|
|
1005
|
+
...slots.root,
|
|
1006
|
+
onKeyDown: handleKeyDown,
|
|
1007
|
+
...props
|
|
1008
|
+
});
|
|
1009
|
+
});
|
|
1010
|
+
|
|
1011
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/hyperlinkDecoration.ts
|
|
1012
|
+
import { syntaxTree } from "@codemirror/language";
|
|
1013
|
+
import { RangeSetBuilder, StateField as StateField2 } from "@codemirror/state";
|
|
1014
|
+
import { Decoration, EditorView as EditorView3, WidgetType } from "@codemirror/view";
|
|
1015
|
+
var Link = class extends WidgetType {
|
|
1016
|
+
constructor(_anchor, _text, _url) {
|
|
1017
|
+
super();
|
|
1018
|
+
this._anchor = _anchor;
|
|
1019
|
+
this._text = _text;
|
|
1020
|
+
this._url = _url;
|
|
1021
|
+
}
|
|
1022
|
+
eq(other) {
|
|
1023
|
+
return this._text === other.text && this._url === other.url;
|
|
1024
|
+
}
|
|
1025
|
+
toDOM(view) {
|
|
1026
|
+
const link = document.createElement("a");
|
|
1027
|
+
link.setAttribute("class", "cm-link");
|
|
1028
|
+
link.textContent = this._text;
|
|
1029
|
+
if (this._url) {
|
|
1030
|
+
link.setAttribute("target", "_blank");
|
|
1031
|
+
link.href = this._url;
|
|
1032
|
+
} else {
|
|
1033
|
+
link.onclick = () => {
|
|
1034
|
+
view.dispatch({
|
|
1035
|
+
selection: {
|
|
1036
|
+
anchor: this._anchor
|
|
1037
|
+
}
|
|
1038
|
+
});
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
return link;
|
|
1042
|
+
}
|
|
1043
|
+
};
|
|
1044
|
+
var hyperlinkDecoration = ({ link } = {}) => {
|
|
1045
|
+
return StateField2.define({
|
|
1046
|
+
create: (state) => update(state, link),
|
|
1047
|
+
update: (_, tr) => update(tr.state, link),
|
|
1048
|
+
provide: (field) => EditorView3.decorations.from(field)
|
|
1049
|
+
});
|
|
1050
|
+
};
|
|
1051
|
+
var update = (state, link) => {
|
|
1052
|
+
const builder = new RangeSetBuilder();
|
|
1053
|
+
const cursor2 = state.selection.main.head;
|
|
1054
|
+
syntaxTree(state).iterate({
|
|
1055
|
+
enter: (node) => {
|
|
1056
|
+
if (cursor2 < node.from || cursor2 > node.to) {
|
|
1057
|
+
if (node.name === "Link") {
|
|
1058
|
+
const marks = node.node.getChildren("LinkMark");
|
|
1059
|
+
const text = marks.length >= 2 ? state.sliceDoc(marks[0].to, marks[1].from) : "";
|
|
1060
|
+
const urlNode = node.node.getChild("URL");
|
|
1061
|
+
const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
|
|
1062
|
+
builder.add(node.from, node.to, Decoration.replace({
|
|
1063
|
+
widget: new Link(node.from, text, link ? url : void 0)
|
|
1064
|
+
}));
|
|
1065
|
+
return false;
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
return true;
|
|
1069
|
+
}
|
|
1070
|
+
});
|
|
1071
|
+
return builder.finish();
|
|
1072
|
+
};
|
|
1073
|
+
|
|
1074
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/hyperlinkTooltip.tsx
|
|
1075
|
+
import { hoverTooltip } from "@codemirror/view";
|
|
1076
|
+
import { tooltipContent } from "@dxos/react-ui-theme";
|
|
1077
|
+
var markdownLinkRegexp = /\[([^\]]+)]\(([^)]+)\)/;
|
|
1078
|
+
var createHyperlinkTooltip = (onHover, regexp = markdownLinkRegexp) => hoverTooltip((view, pos) => {
|
|
1079
|
+
const { from, text } = view.state.doc.lineAt(pos);
|
|
1080
|
+
const p = pos - from;
|
|
1081
|
+
let idx = 0;
|
|
1082
|
+
let match;
|
|
1083
|
+
do {
|
|
1084
|
+
match = text.substring(idx).match(regexp);
|
|
1085
|
+
if (!match) {
|
|
1086
|
+
match = void 0;
|
|
1087
|
+
break;
|
|
1088
|
+
}
|
|
1089
|
+
idx = text.indexOf(match[0]);
|
|
1090
|
+
if (p >= idx && p < idx + match[0].length) {
|
|
1091
|
+
break;
|
|
1092
|
+
}
|
|
1093
|
+
idx += match[0].length;
|
|
1094
|
+
} while (true);
|
|
1095
|
+
if (!match) {
|
|
1096
|
+
return null;
|
|
1097
|
+
}
|
|
1098
|
+
const [, , url] = match ?? [];
|
|
1099
|
+
return {
|
|
1100
|
+
pos: idx + from,
|
|
1101
|
+
end: idx + from + match[0].length,
|
|
1102
|
+
above: true,
|
|
1103
|
+
create: () => {
|
|
1104
|
+
const el = document.createElement("a");
|
|
1105
|
+
el.innerText = "_";
|
|
1106
|
+
el.className = tooltipContent({}, "mb-2 p-1");
|
|
1107
|
+
el.setAttribute("target", "_blank");
|
|
1108
|
+
el.setAttribute("href", url);
|
|
1109
|
+
onHover(el, url);
|
|
1110
|
+
return {
|
|
1111
|
+
dom: el
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
};
|
|
1115
|
+
});
|
|
1116
|
+
|
|
1117
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/hyperlinkWidget.tsx
|
|
1118
|
+
import { Decoration as Decoration2, EditorView as EditorView4, MatchDecorator, ViewPlugin } from "@codemirror/view";
|
|
1119
|
+
var markdownLinkRegexp2 = /\[([^\]]+)]\(([^)]+)\)(!?)/gi;
|
|
1120
|
+
var linkDecorator = () => new MatchDecorator({
|
|
1121
|
+
regexp: markdownLinkRegexp2,
|
|
1122
|
+
decorate: (add, from, to, match, view) => {
|
|
1123
|
+
const [_, label, url] = match;
|
|
1124
|
+
const p0 = {
|
|
1125
|
+
start: from,
|
|
1126
|
+
end: from + 1
|
|
1127
|
+
};
|
|
1128
|
+
const p1 = {
|
|
1129
|
+
start: from + 1,
|
|
1130
|
+
end: from + 1 + label.length
|
|
1131
|
+
};
|
|
1132
|
+
const p2 = {
|
|
1133
|
+
start: p1.end,
|
|
1134
|
+
end: p1.end + 1
|
|
1135
|
+
};
|
|
1136
|
+
const p3 = {
|
|
1137
|
+
start: p1.end + 1,
|
|
1138
|
+
end: p1.end + 1 + url.length + 2
|
|
1139
|
+
};
|
|
1140
|
+
add(p0.start, p0.end, Decoration2.mark({
|
|
1141
|
+
class: "cm-hyperlink-bracket",
|
|
1142
|
+
_attributes: {
|
|
1143
|
+
style: "display: none"
|
|
1144
|
+
}
|
|
1145
|
+
}));
|
|
1146
|
+
add(p1.start, p1.end, Decoration2.mark({
|
|
1147
|
+
class: "cm-hyperlink-label"
|
|
1148
|
+
}));
|
|
1149
|
+
add(p2.start, p2.end, Decoration2.mark({
|
|
1150
|
+
class: "cm-hyperlink-bracket",
|
|
1151
|
+
_attributes: {
|
|
1152
|
+
style: "display: none"
|
|
1153
|
+
}
|
|
1154
|
+
}));
|
|
1155
|
+
add(p3.start, p3.end, Decoration2.mark({
|
|
1156
|
+
class: "cm-hyperlink-url",
|
|
1157
|
+
_attributes: {
|
|
1158
|
+
style: "display: none"
|
|
1159
|
+
}
|
|
1160
|
+
}));
|
|
1161
|
+
}
|
|
1162
|
+
});
|
|
1163
|
+
function hyperLinkExtension() {
|
|
1164
|
+
return ViewPlugin.fromClass(class HyperLinkView {
|
|
1165
|
+
constructor(view) {
|
|
1166
|
+
this.decorator = linkDecorator();
|
|
1167
|
+
this.decorations = this.decorator.createDeco(view);
|
|
1168
|
+
}
|
|
1169
|
+
update(update2) {
|
|
1170
|
+
if (update2.docChanged || update2.viewportChanged) {
|
|
1171
|
+
this.decorations = this.decorator.updateDeco(update2, this.decorations);
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
}, {
|
|
1175
|
+
decorations: (view) => view.decorations,
|
|
1176
|
+
eventHandlers: {
|
|
1177
|
+
// TODO(burdon): CLick to expand link.
|
|
1178
|
+
// https://discuss.codemirror.net/t/decorator-iteration-on-click-event/4226/2
|
|
1179
|
+
mousedown: function(event, view) {
|
|
1180
|
+
const pos = view.posAtDOM(event.target);
|
|
1181
|
+
if (pos !== 0) {
|
|
1182
|
+
console.log(this);
|
|
1183
|
+
}
|
|
1184
|
+
},
|
|
1185
|
+
// TODO(burdon): Hide when cursor moves away.
|
|
1186
|
+
keydown: (event, view) => {
|
|
1187
|
+
view.update([]);
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
var hyperlinkStyle = EditorView4.baseTheme({
|
|
1193
|
+
".cm-hyperlink-label": {
|
|
1194
|
+
cursor: "pointer",
|
|
1195
|
+
textDecoration: "underline"
|
|
1196
|
+
},
|
|
1197
|
+
".cm-hyperlink-bracket": {
|
|
1198
|
+
opacity: 0.2
|
|
1199
|
+
},
|
|
1200
|
+
".cm-hyperlink-url": {
|
|
1201
|
+
opacity: 0.5,
|
|
1202
|
+
fontFamily: "monospace"
|
|
1203
|
+
}
|
|
1204
|
+
});
|
|
1205
|
+
var hyperlinkWidget = [
|
|
1206
|
+
hyperLinkExtension(),
|
|
1207
|
+
hyperlinkStyle
|
|
1208
|
+
];
|
|
1209
|
+
|
|
862
1210
|
// packages/ui/react-ui-editor/src/index.ts
|
|
863
1211
|
import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
|
|
864
|
-
import { Doc, YText as
|
|
1212
|
+
import { Doc, YText as YText3, YXmlFragment as YXmlFragment2 } from "@dxos/text-model";
|
|
865
1213
|
export {
|
|
866
1214
|
Doc,
|
|
867
1215
|
Editor,
|
|
868
1216
|
EditorModes,
|
|
869
1217
|
MarkdownEditor,
|
|
870
1218
|
RichTextEditor,
|
|
1219
|
+
TextEditor,
|
|
871
1220
|
TextKind,
|
|
872
|
-
|
|
1221
|
+
YText3 as YText,
|
|
873
1222
|
YXmlFragment2 as YXmlFragment,
|
|
1223
|
+
createHyperlinkTooltip,
|
|
1224
|
+
defaultStyles,
|
|
1225
|
+
hyperLinkExtension,
|
|
1226
|
+
hyperlinkDecoration,
|
|
1227
|
+
hyperlinkStyle,
|
|
1228
|
+
hyperlinkWidget,
|
|
1229
|
+
tags2 as tags,
|
|
874
1230
|
useTextModel
|
|
875
1231
|
};
|
|
876
1232
|
//# sourceMappingURL=index.mjs.map
|