@dxos/react-ui-editor 0.6.7 → 0.6.8-main.3be982f
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 +1019 -796
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/{hooks/InputMode.stories.d.ts → InputMode.stories.d.ts} +6 -4
- package/dist/types/src/InputMode.stories.d.ts.map +1 -0
- package/dist/types/src/{hooks/TextEditor.stories.d.ts → TextEditor.stories.d.ts} +43 -26
- package/dist/types/src/TextEditor.stories.d.ts.map +1 -0
- package/dist/types/src/components/Toolbar/Toolbar.d.ts +9 -9
- package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/types/src/defaults.d.ts +10 -0
- package/dist/types/src/defaults.d.ts.map +1 -0
- package/dist/types/src/extensions/autocomplete.d.ts +2 -2
- package/dist/types/src/extensions/autocomplete.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/automerge.stories.d.ts +6 -4
- package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
- package/dist/types/src/extensions/blast.d.ts.map +1 -1
- package/dist/types/src/extensions/command/state.d.ts +1 -1
- package/dist/types/src/extensions/command/state.d.ts.map +1 -1
- package/dist/types/src/extensions/comments.d.ts.map +1 -1
- package/dist/types/src/extensions/factories.d.ts.map +1 -1
- package/dist/types/src/extensions/folding.d.ts +7 -0
- package/dist/types/src/extensions/folding.d.ts.map +1 -0
- package/dist/types/src/extensions/index.d.ts +1 -0
- package/dist/types/src/extensions/index.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/decorate.d.ts +5 -1
- package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/formatting.d.ts +9 -9
- package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/image.d.ts +1 -1
- package/dist/types/src/extensions/markdown/image.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/link-paste.d.ts +6 -0
- package/dist/types/src/extensions/markdown/link-paste.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/link-paste.test.d.ts +2 -0
- package/dist/types/src/extensions/markdown/link-paste.test.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/parser.test.d.ts +2 -0
- package/dist/types/src/extensions/markdown/parser.test.d.ts.map +1 -0
- package/dist/types/src/extensions/state.d.ts.map +1 -1
- package/dist/types/src/extensions/util/error.d.ts +2 -0
- package/dist/types/src/extensions/util/error.d.ts.map +1 -0
- package/dist/types/src/extensions/util/index.d.ts +3 -1
- package/dist/types/src/extensions/util/index.d.ts.map +1 -1
- package/dist/types/src/extensions/util/overlap.d.ts.map +1 -1
- package/dist/types/src/extensions/util/react.d.ts +3 -0
- package/dist/types/src/extensions/util/react.d.ts.map +1 -0
- package/dist/types/src/hooks/useActionHandler.d.ts +1 -1
- package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/styles/index.d.ts +1 -1
- package/dist/types/src/styles/index.d.ts.map +1 -1
- package/dist/types/src/styles/markdown.d.ts +0 -1
- package/dist/types/src/styles/markdown.d.ts.map +1 -1
- package/dist/types/src/styles/theme.d.ts +36 -0
- package/dist/types/src/styles/theme.d.ts.map +1 -0
- package/dist/types/src/styles/tokens.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +2 -0
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/util.d.ts.map +1 -1
- package/package.json +26 -29
- package/src/{hooks/InputMode.stories.tsx → InputMode.stories.tsx} +6 -11
- package/src/{hooks/TextEditor.stories.tsx → TextEditor.stories.tsx} +139 -92
- package/src/components/Toolbar/Toolbar.tsx +17 -9
- package/src/defaults.ts +28 -0
- package/src/extensions/autocomplete.ts +24 -18
- package/src/extensions/automerge/automerge.stories.tsx +4 -6
- package/src/extensions/comments.ts +4 -0
- package/src/extensions/factories.ts +3 -2
- package/src/extensions/folding.tsx +34 -0
- package/src/extensions/index.ts +1 -0
- package/src/extensions/markdown/bundle.ts +1 -1
- package/src/extensions/markdown/decorate.ts +359 -129
- package/src/extensions/markdown/formatting.ts +10 -12
- package/src/extensions/markdown/image.ts +3 -1
- package/src/extensions/markdown/link-paste.test.ts +28 -0
- package/src/extensions/markdown/link-paste.ts +104 -0
- package/src/extensions/markdown/parser.test.ts +47 -0
- package/src/extensions/markdown/table.ts +21 -24
- package/src/extensions/util/error.ts +15 -0
- package/src/extensions/util/index.ts +3 -1
- package/src/extensions/util/overlap.ts +1 -0
- package/src/extensions/util/react.tsx +15 -0
- package/src/hooks/useTextEditor.ts +1 -1
- package/src/index.ts +2 -2
- package/src/styles/index.ts +1 -1
- package/src/styles/markdown.ts +4 -3
- package/src/{themes/default.ts → styles/theme.ts} +51 -43
- package/src/styles/tokens.ts +0 -1
- package/src/translations.ts +2 -0
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +0 -57
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +0 -1
- package/dist/types/src/extensions/markdown/linkPaste.d.ts +0 -16
- package/dist/types/src/extensions/markdown/linkPaste.d.ts.map +0 -1
- package/dist/types/src/extensions/markdown/linkPaste.test.d.ts +0 -2
- package/dist/types/src/extensions/markdown/linkPaste.test.d.ts.map +0 -1
- package/dist/types/src/hooks/InputMode.stories.d.ts.map +0 -1
- package/dist/types/src/hooks/TextEditor.stories.d.ts.map +0 -1
- package/dist/types/src/styles/layout.d.ts +0 -4
- package/dist/types/src/styles/layout.d.ts.map +0 -1
- package/dist/types/src/themes/default.d.ts +0 -14
- package/dist/types/src/themes/default.d.ts.map +0 -1
- package/dist/types/src/themes/index.d.ts +0 -2
- package/dist/types/src/themes/index.d.ts.map +0 -1
- package/src/components/Toolbar/Toolbar.stories.tsx +0 -119
- package/src/extensions/markdown/linkPaste.test.ts +0 -45
- package/src/extensions/markdown/linkPaste.ts +0 -113
- package/src/styles/layout.ts +0 -9
- package/src/themes/index.ts +0 -5
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/// <reference types="react" />
|
|
2
|
-
import '@dxosTheme';
|
|
3
|
-
declare const _default: {
|
|
4
|
-
title: string;
|
|
5
|
-
component: {
|
|
6
|
-
Root: ({ children, onAction, classNames, state }: import("./Toolbar").ToolbarProps) => JSX.Element;
|
|
7
|
-
Button: ({ Icon, children, ...props }: Omit<import("@radix-ui/react-toggle-group").ToggleGroupItemProps, "className"> & import("@dxos/react-ui").ButtonProps & {
|
|
8
|
-
Icon: import("@phosphor-icons/react").Icon;
|
|
9
|
-
}) => JSX.Element;
|
|
10
|
-
Separator: () => JSX.Element;
|
|
11
|
-
View: ({ mode }: {
|
|
12
|
-
mode: "source" | "preview" | "readonly";
|
|
13
|
-
}) => JSX.Element;
|
|
14
|
-
Markdown: () => JSX.Element;
|
|
15
|
-
Custom: ({ onUpload }?: import("./Toolbar").MarkdownCustomOptions) => JSX.Element;
|
|
16
|
-
Actions: () => JSX.Element;
|
|
17
|
-
};
|
|
18
|
-
decorators: import("@storybook/react/*").Decorator[];
|
|
19
|
-
parameters: {
|
|
20
|
-
translations: {
|
|
21
|
-
'en-US': {
|
|
22
|
-
"react-ui-editor": {
|
|
23
|
-
'strong label': string;
|
|
24
|
-
'emphasis label': string;
|
|
25
|
-
'strikethrough label': string;
|
|
26
|
-
'code label': string;
|
|
27
|
-
'link label': string;
|
|
28
|
-
'list-bullet label': string;
|
|
29
|
-
'list-ordered label': string;
|
|
30
|
-
'list-task label': string;
|
|
31
|
-
'blockquote label': string;
|
|
32
|
-
'codeblock label': string;
|
|
33
|
-
'comment label': string;
|
|
34
|
-
'image label': string;
|
|
35
|
-
'heading label': string;
|
|
36
|
-
'table label': string;
|
|
37
|
-
'heading level label_zero': string;
|
|
38
|
-
'heading level label_one': string;
|
|
39
|
-
'heading level label_other': string;
|
|
40
|
-
'view mode label': string;
|
|
41
|
-
'preview mode label': string;
|
|
42
|
-
'readonly mode label': string;
|
|
43
|
-
'source mode label': string;
|
|
44
|
-
};
|
|
45
|
-
};
|
|
46
|
-
}[];
|
|
47
|
-
layout: string;
|
|
48
|
-
};
|
|
49
|
-
render: (args: any) => JSX.Element;
|
|
50
|
-
};
|
|
51
|
-
export default _default;
|
|
52
|
-
export declare const Default: {
|
|
53
|
-
args: {
|
|
54
|
-
content: string;
|
|
55
|
-
};
|
|
56
|
-
};
|
|
57
|
-
//# sourceMappingURL=Toolbar.stories.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Toolbar.stories.d.ts","sourceRoot":"","sources":["../../../../../src/components/Toolbar/Toolbar.stories.tsx"],"names":[],"mappings":";AAIA,OAAO,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBAiGH,GAAG;;AALpB,wBAME;AAYF,eAAO,MAAM,OAAO;;;;CAInB,CAAC"}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { type EditorState, Transaction } from '@codemirror/state';
|
|
2
|
-
import { type EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view';
|
|
3
|
-
export declare const formatUrlForDisplay: (url: string) => string;
|
|
4
|
-
export declare const linkPastePlugin: ViewPlugin<{
|
|
5
|
-
view: EditorView;
|
|
6
|
-
update(update: ViewUpdate): void;
|
|
7
|
-
handleInputRead(view: EditorView, tr: Transaction): void;
|
|
8
|
-
/**
|
|
9
|
-
* Determines if a given position is within a code block.
|
|
10
|
-
* Traverses the syntax tree upwards from the position,
|
|
11
|
-
* checking for CodeBlock or FencedCode nodes.
|
|
12
|
-
*/
|
|
13
|
-
isInCodeBlock(state: EditorState, pos: number): boolean;
|
|
14
|
-
createLinkTransaction(state: EditorState, from: number, to: number, url: string, text: string): Transaction;
|
|
15
|
-
}>;
|
|
16
|
-
//# sourceMappingURL=linkPaste.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"linkPaste.d.ts","sourceRoot":"","sources":["../../../../../src/extensions/markdown/linkPaste.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,EAAE,KAAK,UAAU,EAAE,UAAU,EAAE,KAAK,UAAU,EAAoB,MAAM,kBAAkB,CAAC;AAYlG,eAAO,MAAM,mBAAmB,QAAS,MAAM,KAAG,MAYjD,CAAC;AA0BF,eAAO,MAAM,eAAe;UAElB,UAAU;mBAMD,UAAU;0BASH,UAAU,MAAM,WAAW;IAgBjD;;;;OAIG;yBACkB,WAAW,OAAO,MAAM,GAAG,OAAO;iCAY1B,WAAW,QAAQ,MAAM,MAAM,MAAM,OAAO,MAAM,QAAQ,MAAM,GAAG,WAAW;EAO9G,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"linkPaste.test.d.ts","sourceRoot":"","sources":["../../../../../src/extensions/markdown/linkPaste.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"InputMode.stories.d.ts","sourceRoot":"","sources":["../../../../src/hooks/InputMode.stories.tsx"],"names":[],"mappings":";AAIA,OAAO,YAAY,CAAC;AASpB,OAAO,EAAiB,KAAK,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAgBzE,KAAK,UAAU,GAAG;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,kBAAkB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4EpF,wBAKE;AAEF,eAAO,MAAM,OAAO;;CAanB,CAAC;AAEF,eAAO,MAAM,QAAQ;;;;;;CAMpB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TextEditor.stories.d.ts","sourceRoot":"","sources":["../../../../src/hooks/TextEditor.stories.tsx"],"names":[],"mappings":";AAIA,OAAO,YAAY,CAAC;AAmBpB,OAAO,EAAiB,KAAK,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AA0NzE,KAAK,UAAU,GAAG;IAChB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG,IAAI,CAAC,kBAAkB,EAAE,WAAW,GAAG,YAAY,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCzD,wBAKE;AAeF,eAAO,MAAM,OAAO;;CAEnB,CAAC;AAEF,eAAO,MAAM,QAAQ;;CAEpB,CAAC;AAEF,eAAO,MAAM,YAAY;;CAExB,CAAC;AASF,eAAO,MAAM,KAAK;;CAEjB,CAAC;AAIF,eAAO,MAAM,SAAS;;CAUrB,CAAC;AAEF,eAAO,MAAM,mBAAmB;;CAE/B,CAAC;AAEF,eAAO,MAAM,KAAK;;CAEjB,CAAC;AAEF,eAAO,MAAM,KAAK;;CAEjB,CAAC;AAEF,eAAO,MAAM,IAAI;;CAEhB,CAAC;AAEF,eAAO,MAAM,KAAK;;CAIjB,CAAC;AAEF,eAAO,MAAM,KAAK;;CAEjB,CAAC;AAEF,eAAO,MAAM,YAAY;;CAYxB,CAAC;AAEF,eAAO,MAAM,YAAY;;CAWxB,CAAC;AAEF,eAAO,MAAM,OAAO;;CAWnB,CAAC;AAmDF,eAAO,MAAM,OAAO;;CAOnB,CAAC;AAEF,eAAO,MAAM,QAAQ;;CAsCpB,CAAC;AAEF,eAAO,MAAM,GAAG;;CAOf,CAAC;AAEF,eAAO,MAAM,WAAW;;CAEvB,CAAC;AAEF,eAAO,MAAM,GAAG;;CAaf,CAAC;AAIF,eAAO,MAAM,QAAQ;;CAgBpB,CAAC;AAEF,eAAO,MAAM,UAAU;;CAOtB,CAAC;AAEF,eAAO,MAAM,KAAK;;CAqBjB,CAAC"}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
export declare const editorWithToolbarLayout = "grid grid-cols-1 grid-rows-[min-content_1fr] data-[toolbar=disabled]:grid-rows-[1fr] justify-center content-start overflow-hidden";
|
|
2
|
-
export declare const editorFillLayoutRoot = "bs-full relative";
|
|
3
|
-
export declare const editorFillLayoutEditor = "!absolute inset-0";
|
|
4
|
-
//# sourceMappingURL=layout.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../../src/styles/layout.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,uBAAuB,sIACiG,CAAC;AAEtI,eAAO,MAAM,oBAAoB,qBAAqB,CAAC;AACvD,eAAO,MAAM,sBAAsB,sBAAsB,CAAC"}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { type ThemeStyles } from '../styles';
|
|
2
|
-
/**
|
|
3
|
-
* Minimal styles.
|
|
4
|
-
* https://codemirror.net/examples/styling
|
|
5
|
-
*
|
|
6
|
-
* Examples:
|
|
7
|
-
* - https://github.com/codemirror/view/blob/main/src/theme.ts
|
|
8
|
-
* - https://github.com/codemirror/theme-one-dark/blob/main/src/one-dark.ts
|
|
9
|
-
*
|
|
10
|
-
* NOTE: Use one of '&', '&light', and '&dark' prefix to scope instance.
|
|
11
|
-
* NOTE: `light` and `dark` selectors are preprocessed by CodeMirror and can only be in the base theme.
|
|
12
|
-
*/
|
|
13
|
-
export declare const defaultTheme: ThemeStyles;
|
|
14
|
-
//# sourceMappingURL=default.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"default.d.ts","sourceRoot":"","sources":["../../../../src/themes/default.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,WAAW,EAAU,MAAM,WAAW,CAAC;AAKrD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,YAAY,EAAE,WAgR1B,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/themes/index.ts"],"names":[],"mappings":"AAIA,cAAc,WAAW,CAAC"}
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2024 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import '@dxosTheme';
|
|
6
|
-
|
|
7
|
-
import React, { type FC, useState } from 'react';
|
|
8
|
-
|
|
9
|
-
import { TextType } from '@braneframe/types';
|
|
10
|
-
import { create } from '@dxos/echo-schema';
|
|
11
|
-
import { PublicKey } from '@dxos/keys';
|
|
12
|
-
import { faker } from '@dxos/random';
|
|
13
|
-
import { createDocAccessor, createEchoObject } from '@dxos/react-client/echo';
|
|
14
|
-
import { Tooltip, useThemeContext } from '@dxos/react-ui';
|
|
15
|
-
import { textBlockWidth } from '@dxos/react-ui-theme';
|
|
16
|
-
import { withTheme } from '@dxos/storybook-utils';
|
|
17
|
-
|
|
18
|
-
import { Toolbar } from './Toolbar';
|
|
19
|
-
import {
|
|
20
|
-
type Action,
|
|
21
|
-
type Comment,
|
|
22
|
-
comments,
|
|
23
|
-
createBasicExtensions,
|
|
24
|
-
createDataExtensions,
|
|
25
|
-
createMarkdownExtensions,
|
|
26
|
-
createThemeExtensions,
|
|
27
|
-
decorateMarkdown,
|
|
28
|
-
type EditorViewMode,
|
|
29
|
-
formattingKeymap,
|
|
30
|
-
image,
|
|
31
|
-
table,
|
|
32
|
-
useComments,
|
|
33
|
-
useFormattingState,
|
|
34
|
-
} from '../../extensions';
|
|
35
|
-
import { useActionHandler, useTextEditor } from '../../hooks';
|
|
36
|
-
import translations from '../../translations';
|
|
37
|
-
|
|
38
|
-
faker.seed(101);
|
|
39
|
-
|
|
40
|
-
const Story: FC<{ content: string }> = ({ content }) => {
|
|
41
|
-
const { themeMode } = useThemeContext();
|
|
42
|
-
const [text] = useState(createEchoObject(create(TextType, { content })));
|
|
43
|
-
const [formattingState, formattingObserver] = useFormattingState();
|
|
44
|
-
const [viewMode, setViewMode] = useState<EditorViewMode>('preview');
|
|
45
|
-
const { parentRef, view } = useTextEditor(() => {
|
|
46
|
-
return {
|
|
47
|
-
id: text.id,
|
|
48
|
-
initialValue: text.content,
|
|
49
|
-
extensions: [
|
|
50
|
-
formattingObserver,
|
|
51
|
-
createBasicExtensions({ readonly: viewMode === 'readonly' }),
|
|
52
|
-
createMarkdownExtensions({ themeMode }),
|
|
53
|
-
createThemeExtensions({ themeMode, slots: { editor: { className: 'p-2' } } }),
|
|
54
|
-
createDataExtensions({ id: text.id, text: createDocAccessor(text, ['content']) }),
|
|
55
|
-
comments({
|
|
56
|
-
onCreate: ({ cursor }) => {
|
|
57
|
-
const id = PublicKey.random().toHex();
|
|
58
|
-
setComments((comments) => [...comments, { id, cursor }]);
|
|
59
|
-
return id;
|
|
60
|
-
},
|
|
61
|
-
}),
|
|
62
|
-
formattingKeymap(),
|
|
63
|
-
image(),
|
|
64
|
-
...(viewMode !== 'source' ? [decorateMarkdown(), table()] : []),
|
|
65
|
-
],
|
|
66
|
-
};
|
|
67
|
-
}, [text, formattingObserver, viewMode, themeMode]);
|
|
68
|
-
|
|
69
|
-
const handleToolbarAction = useActionHandler(view);
|
|
70
|
-
const handleAction = (action: Action) => {
|
|
71
|
-
if (action.type === 'view-mode') {
|
|
72
|
-
setViewMode(action.data);
|
|
73
|
-
} else {
|
|
74
|
-
handleToolbarAction?.(action);
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
const [_comments, setComments] = useState<Comment[]>([]);
|
|
79
|
-
useComments(view, text.id, _comments);
|
|
80
|
-
|
|
81
|
-
return (
|
|
82
|
-
<Tooltip.Provider>
|
|
83
|
-
<div role='none' className='fixed inset-0 flex flex-col'>
|
|
84
|
-
<Toolbar.Root onAction={handleAction} state={formattingState} classNames={textBlockWidth}>
|
|
85
|
-
<Toolbar.View mode={viewMode} />
|
|
86
|
-
<Toolbar.Markdown />
|
|
87
|
-
<Toolbar.Custom onUpload={async (file) => ({ url: file.name })} />
|
|
88
|
-
<Toolbar.Separator />
|
|
89
|
-
<Toolbar.Actions />
|
|
90
|
-
</Toolbar.Root>
|
|
91
|
-
<div ref={parentRef} className={textBlockWidth} />
|
|
92
|
-
</div>
|
|
93
|
-
</Tooltip.Provider>
|
|
94
|
-
);
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
export default {
|
|
98
|
-
title: 'react-ui-editor/Toolbar',
|
|
99
|
-
component: Toolbar,
|
|
100
|
-
decorators: [withTheme],
|
|
101
|
-
parameters: { translations, layout: 'fullscreen' },
|
|
102
|
-
render: (args: any) => <Story {...args} />,
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
const content = [
|
|
106
|
-
'# Demo',
|
|
107
|
-
'',
|
|
108
|
-
'The editor supports **Markdown** styles.',
|
|
109
|
-
'',
|
|
110
|
-
faker.lorem.paragraph({ min: 5, max: 8 }),
|
|
111
|
-
'',
|
|
112
|
-
'',
|
|
113
|
-
].join('\n');
|
|
114
|
-
|
|
115
|
-
export const Default = {
|
|
116
|
-
args: {
|
|
117
|
-
content,
|
|
118
|
-
},
|
|
119
|
-
};
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2024 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { expect } from 'chai';
|
|
6
|
-
|
|
7
|
-
import { describe, test } from '@dxos/test';
|
|
8
|
-
|
|
9
|
-
import { formatUrlForDisplay } from './linkPaste';
|
|
10
|
-
|
|
11
|
-
const testCases = [
|
|
12
|
-
{ input: 'https://www.example.com', expected: 'example.com' },
|
|
13
|
-
{ input: 'http://example.com', expected: 'example.com' },
|
|
14
|
-
{ input: 'https://example.com/', expected: 'example.com' },
|
|
15
|
-
{ input: 'https://www.example.com/', expected: 'example.com' },
|
|
16
|
-
{ input: 'https://example.com/test', expected: 'example.com/test' },
|
|
17
|
-
{ input: 'https://www.example.com/test', expected: 'example.com/test' },
|
|
18
|
-
{ input: 'http://example.com/test', expected: 'example.com/test' },
|
|
19
|
-
{
|
|
20
|
-
input: 'https://example.com/some/path?name=value&another_name=another_value',
|
|
21
|
-
expected: 'example.com/some/path?name=value&anot...',
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
input: 'https://www.example.com/some/path?name=value&another_name=another_value',
|
|
25
|
-
expected: 'example.com/some/path?name=value&anot...',
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
input: 'http://example.com/some/path?name=value&another_name=another_value',
|
|
29
|
-
expected: 'example.com/some/path?name=value&anot...',
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
input: 'https://www.example.com?name=value&another_name=another_value',
|
|
33
|
-
expected: 'example.com?name=value&anot...',
|
|
34
|
-
},
|
|
35
|
-
{ input: 'http://example.com?name=value&another_name=another_value', expected: 'example.com?name=value&anot...' },
|
|
36
|
-
{ input: 'https://example.com?name=value', expected: 'example.com?name=value' },
|
|
37
|
-
{ input: 'http://example.com?name=value', expected: 'example.com?name=value' },
|
|
38
|
-
{ input: 'https://www.example.com?name=value', expected: 'example.com?name=value' },
|
|
39
|
-
{ input: 'ftp://example.com', expected: 'ftp://example.com' },
|
|
40
|
-
];
|
|
41
|
-
|
|
42
|
-
describe('formatUrlForDisplay', () =>
|
|
43
|
-
testCases.forEach(({ input, expected }) =>
|
|
44
|
-
test(`formats ${input} into ${expected}`, () => expect(formatUrlForDisplay(input)).equals(expected)),
|
|
45
|
-
));
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2024 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
import { syntaxTree } from '@codemirror/language';
|
|
5
|
-
import { type EditorState, Transaction } from '@codemirror/state';
|
|
6
|
-
import { type EditorView, ViewPlugin, type ViewUpdate, type PluginValue } from '@codemirror/view';
|
|
7
|
-
import { type SyntaxNode } from '@lezer/common';
|
|
8
|
-
|
|
9
|
-
const VALID_PROTOCOLS = ['http:', 'https:', 'mailto:', 'tel:'];
|
|
10
|
-
|
|
11
|
-
const createTextLink = (text: string, url: string): string => `[${text}](${url})`;
|
|
12
|
-
|
|
13
|
-
const createUrlLink = (url: string): string => {
|
|
14
|
-
const displayUrl = formatUrlForDisplay(url);
|
|
15
|
-
return `[${displayUrl}](${url})`;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export const formatUrlForDisplay = (url: string): string => {
|
|
19
|
-
// Remove protocol (http:// or https://)
|
|
20
|
-
let formattedUrl = url.replace(/^https?:\/\//, '');
|
|
21
|
-
|
|
22
|
-
// NOTE(Zan): Consult: https://github.com/dxos/dxos/issues/7331 before changing this.
|
|
23
|
-
// Remove 'www.' if at the beginning of the URL
|
|
24
|
-
formattedUrl = formattedUrl.replace(/^www\./, '');
|
|
25
|
-
|
|
26
|
-
// Remove trailing slash if the URL ends with `.com/`
|
|
27
|
-
formattedUrl = formattedUrl.replace(/\.com\/$/, '.com');
|
|
28
|
-
|
|
29
|
-
return truncateQueryParams(formattedUrl);
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const truncateQueryParams = (url: string, maxQueryLength: number = 15): string => {
|
|
33
|
-
const [urlBase, queryString] = url.split('?');
|
|
34
|
-
if (!queryString) {
|
|
35
|
-
return urlBase;
|
|
36
|
-
}
|
|
37
|
-
if (queryString.length > maxQueryLength) {
|
|
38
|
-
const truncatedQuery = queryString.slice(0, maxQueryLength) + '...';
|
|
39
|
-
return `${urlBase}?${truncatedQuery}`;
|
|
40
|
-
} else {
|
|
41
|
-
return `${urlBase}?${queryString}`;
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const isValidUrl = (str: string) => {
|
|
46
|
-
try {
|
|
47
|
-
const url = new URL(str);
|
|
48
|
-
return VALID_PROTOCOLS.includes(url.protocol);
|
|
49
|
-
} catch (e) {
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const onNextUpdate = (callback: () => void) => setTimeout(callback, 0);
|
|
55
|
-
|
|
56
|
-
export const linkPastePlugin = ViewPlugin.fromClass(
|
|
57
|
-
class implements PluginValue {
|
|
58
|
-
view: EditorView;
|
|
59
|
-
|
|
60
|
-
constructor(view: EditorView) {
|
|
61
|
-
this.view = view;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
update(update: ViewUpdate) {
|
|
65
|
-
for (const tr of update.transactions) {
|
|
66
|
-
const event = tr.annotation(Transaction.userEvent);
|
|
67
|
-
if (event === 'input.paste') {
|
|
68
|
-
this.handleInputRead(this.view, tr);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
handleInputRead(view: EditorView, tr: Transaction) {
|
|
74
|
-
const changes = tr.changes;
|
|
75
|
-
if (changes.empty) {
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
changes.iterChangedRanges((fromA, toA, fromB, toB) => {
|
|
79
|
-
const insertedText = view.state.sliceDoc(fromB, toB);
|
|
80
|
-
if (isValidUrl(insertedText) && !this.isInCodeBlock(view.state, fromB)) {
|
|
81
|
-
const replacedText = tr.startState.sliceDoc(fromA, toA);
|
|
82
|
-
onNextUpdate(() => {
|
|
83
|
-
view.dispatch(this.createLinkTransaction(view.state, fromA, toB, insertedText, replacedText));
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Determines if a given position is within a code block.
|
|
91
|
-
* Traverses the syntax tree upwards from the position,
|
|
92
|
-
* checking for CodeBlock or FencedCode nodes.
|
|
93
|
-
*/
|
|
94
|
-
isInCodeBlock(state: EditorState, pos: number): boolean {
|
|
95
|
-
const tree = syntaxTree(state);
|
|
96
|
-
let node: SyntaxNode | null = tree.resolveInner(pos, -1);
|
|
97
|
-
while (node) {
|
|
98
|
-
if (node.name.includes('Code') || node.name.includes('FencedCode')) {
|
|
99
|
-
return true;
|
|
100
|
-
}
|
|
101
|
-
node = node.parent;
|
|
102
|
-
}
|
|
103
|
-
return false;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
createLinkTransaction(state: EditorState, from: number, to: number, url: string, text: string): Transaction {
|
|
107
|
-
const linkText = text.trim() ? createTextLink(text, url) : createUrlLink(url);
|
|
108
|
-
return state.update({
|
|
109
|
-
changes: { from, to, insert: linkText },
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
},
|
|
113
|
-
);
|
package/src/styles/layout.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2024 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
export const editorWithToolbarLayout =
|
|
6
|
-
'grid grid-cols-1 grid-rows-[min-content_1fr] data-[toolbar=disabled]:grid-rows-[1fr] justify-center content-start overflow-hidden';
|
|
7
|
-
|
|
8
|
-
export const editorFillLayoutRoot = 'bs-full relative';
|
|
9
|
-
export const editorFillLayoutEditor = '!absolute inset-0';
|