@doist/typist 14.1.1 → 15.0.0-next.1
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/CHANGELOG.md +6 -0
- package/README.md +4 -0
- package/dist/components/typist-editor.d.ts +11 -18
- package/dist/components/typist-editor.d.ts.map +1 -1
- package/dist/components/typist-editor.js +54 -50
- package/dist/components/typist-editor.js.map +1 -1
- package/dist/extensions/core/view-event-handlers.d.ts +1 -12
- package/dist/extensions/core/view-event-handlers.d.ts.map +1 -1
- package/dist/extensions/core/view-event-handlers.js +13 -3
- package/dist/extensions/core/view-event-handlers.js.map +1 -1
- package/dist/extensions/rich-text/rich-text-kit.d.ts +9 -0
- package/dist/extensions/rich-text/rich-text-kit.d.ts.map +1 -1
- package/dist/extensions/rich-text/rich-text-kit.js +2 -0
- package/dist/extensions/rich-text/rich-text-kit.js.map +1 -1
- package/dist/extensions/shared/paste-html-table-as-string.js +1 -0
- package/dist/extensions/shared/paste-html-table-as-string.js.map +1 -1
- package/package.json +2 -1
- package/dist/hooks/use-editor.js +0 -59
- package/dist/hooks/use-editor.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## [14.1.2](https://github.com/Doist/typist/compare/v14.1.1...v14.1.2) (2026-06-18)
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
|
|
5
|
+
* **rich-text:** Degrade pasted HTML tables to string when tables unavailable ([#1386](https://github.com/Doist/typist/issues/1386)) ([f2111fd](https://github.com/Doist/typist/commit/f2111fd340102739ec201aeb1f193d9ecf65b103))
|
|
6
|
+
|
|
1
7
|
## [14.1.1](https://github.com/Doist/typist/compare/v14.1.0...v14.1.1) (2026-06-17)
|
|
2
8
|
|
|
3
9
|
### Bug Fixes
|
package/README.md
CHANGED
|
@@ -48,6 +48,10 @@ function TypistEditorContainer({ content }) {
|
|
|
48
48
|
}
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
+
> **Note**
|
|
52
|
+
>
|
|
53
|
+
> `content`, `extensions`, and `placeholder` are initialization-only: they're read when the editor first mounts, and later changes to these props are ignored. See the [Component Lifecycle](https://typist.doist.dev/?path=/docs/documentation-tips-tricks-component-lifecycle--docs) documentation for how to handle runtime changes.
|
|
54
|
+
|
|
51
55
|
If you're looking for additional documentation, in-depth examples, or a live demo, please check out our [Storybook](https://typist.doist.dev/).
|
|
52
56
|
|
|
53
57
|
## Resources
|
|
@@ -69,6 +69,9 @@ type TypistEditorProps = {
|
|
|
69
69
|
className?: string;
|
|
70
70
|
/**
|
|
71
71
|
* The initial Markdown content for the editor.
|
|
72
|
+
*
|
|
73
|
+
* This value is only used when the editor is first mounted, subsequent changes to this prop are
|
|
74
|
+
* ignored. Use `editor.commands.setContent()` to update content at runtime.
|
|
72
75
|
*/
|
|
73
76
|
content?: string;
|
|
74
77
|
/**
|
|
@@ -80,61 +83,51 @@ type TypistEditorProps = {
|
|
|
80
83
|
*/
|
|
81
84
|
editable?: boolean;
|
|
82
85
|
/**
|
|
83
|
-
* The list of
|
|
86
|
+
* The list of extensions to initialize the editor with.
|
|
84
87
|
*
|
|
85
|
-
*
|
|
88
|
+
* This value is only used when the editor is first mounted, subsequent changes to this prop are
|
|
89
|
+
* ignored. Extensions that depend on data that changes over time should read the current value
|
|
90
|
+
* at the moment it's needed from a mutable source you update from outside (a ref or a store),
|
|
91
|
+
* rather than capturing the value when the extension is created.
|
|
86
92
|
*/
|
|
87
93
|
extensions: Extensions;
|
|
88
94
|
/**
|
|
89
95
|
* A short hint that gives users an idea what can be entered in the editor.
|
|
96
|
+
*
|
|
97
|
+
* This value is only used when the editor is first mounted, subsequent changes to this prop are
|
|
98
|
+
* ignored.
|
|
90
99
|
*/
|
|
91
100
|
placeholder?: string;
|
|
92
101
|
/**
|
|
93
102
|
* The event handler that is fired before the editor view is created.
|
|
94
|
-
*
|
|
95
|
-
* You may consider wrapping this prop with `useCallback` to prevent unnecessary re-renders.
|
|
96
103
|
*/
|
|
97
104
|
onBeforeCreate?: (props: BeforeCreateProps) => void;
|
|
98
105
|
/**
|
|
99
106
|
* The event handler that is fired when the editor view is ready.
|
|
100
|
-
*
|
|
101
|
-
* You may consider wrapping this prop with `useCallback` to prevent unnecessary re-renders.
|
|
102
107
|
*/
|
|
103
108
|
onCreate?: (props: CreateProps) => void;
|
|
104
109
|
/**
|
|
105
110
|
* The event handler that is fired when the editor content has changed.
|
|
106
|
-
*
|
|
107
|
-
* You may consider wrapping this prop with `useCallback` to prevent unnecessary re-renders.
|
|
108
111
|
*/
|
|
109
112
|
onUpdate?: (props: UpdateProps) => void;
|
|
110
113
|
/**
|
|
111
114
|
* The event handler that is fired when the editor selection has changed.
|
|
112
|
-
*
|
|
113
|
-
* You may consider wrapping this prop with `useCallback` to prevent unnecessary re-renders.
|
|
114
115
|
*/
|
|
115
116
|
onSelectionUpdate?: (props: SelectionUpdateProps) => void;
|
|
116
117
|
/**
|
|
117
118
|
* The event handler that is fired when the editor state has changed.
|
|
118
|
-
*
|
|
119
|
-
* You may consider wrapping this prop with `useCallback` to prevent unnecessary re-renders.
|
|
120
119
|
*/
|
|
121
120
|
onTransaction?: (props: TransacationProps) => void;
|
|
122
121
|
/**
|
|
123
122
|
* The event handler that is fired when the editor view gains focus.
|
|
124
|
-
*
|
|
125
|
-
* You may consider wrapping this prop with `useCallback` to prevent unnecessary re-renders.
|
|
126
123
|
*/
|
|
127
124
|
onFocus?: (props: FocusProps) => void;
|
|
128
125
|
/**
|
|
129
126
|
* The event handler that is fired when the editor view loses focus.
|
|
130
|
-
*
|
|
131
|
-
* You may consider wrapping this prop with `useCallback` to prevent unnecessary re-renders.
|
|
132
127
|
*/
|
|
133
128
|
onBlur?: (props: BlurProps) => void;
|
|
134
129
|
/**
|
|
135
130
|
* The event handler that is fired when the editor view is being destroyed.
|
|
136
|
-
*
|
|
137
|
-
* You may consider wrapping this prop with `useCallback` to prevent unnecessary re-renders.
|
|
138
131
|
*/
|
|
139
132
|
onDestroy?: (props: DestroyProps) => void;
|
|
140
133
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typist-editor.d.ts","names":[],"sources":["../../src/components/typist-editor.tsx"],"mappings":";;;;;;;;
|
|
1
|
+
{"version":3,"file":"typist-editor.d.ts","names":[],"sources":["../../src/components/typist-editor.tsx"],"mappings":";;;;;;;;AAeiD;;KAM5C,eAAA;EAIgB;;;EAAjB,SAAA,QAAiB,MAAA;EAYF;;;EAPf,WAAA;EAKA;;;EAAA,2BAAA,GACI,QAAA,aACC,UAAA,QAAkB,2BAAA;AAAA;AAA2B;AAAA;;AAAA,KAMjD,iBAAA,GAAoB,YAAY;;AAAA;AAAA;KAKhC,WAAA,GAAc,YAAY;;;AAAA;KAK1B,WAAA,GAAc,YAAA,aAAyB,IAAA,CAAK,eAAA;;;;KAK5C,oBAAA,GAAuB,YAAY;;;;KAKnC,iBAAA,GAAoB,YAAY;;;;KAKhC,UAAA,GAAa,YAAY;AAfkC;;;AAAA,KAoB3D,SAAA,GAAY,YAAY;AAfW;AAAA;;AAAA,KAoBnC,YAAA,GAAe,YAAY;;AAfK;AAAA;;KAqBhC,iBAAA;EAhByB;AAAA;AAAA;EAoB1B,SAAA;;;AAfyB;EAoBzB,SAAA;EAfa;;;AAAe;AAAA;;EAuB5B,OAAA;EAKmB;;;EAAnB,gBAAA,GAAmB,SAAA;EAsCA;;;EAjCnB,QAAA;EAqDiB;;;;;;;;EA3CjB,UAAA,EAAY,UAAA;EAjCZ;;;;;;EAyCA,WAAA;EARY;;;EAaZ,cAAA,IAAkB,KAAA,EAAO,iBAAA;EAAP;;;EAKlB,QAAA,IAAY,KAAA,EAAO,WAAA;EAKnB;;;EAAA,QAAA,IAAY,KAAA,EAAO,WAAA;EAKS;;;EAA5B,iBAAA,IAAqB,KAAA,EAAO,oBAAA;EAKX;;;EAAjB,aAAA,IAAiB,KAAA,EAAO,iBAAA;EAUxB;;;EALA,OAAA,IAAW,KAAA,EAAO,UAAA;EAUE;;;EALpB,MAAA,IAAU,KAAA,EAAO,SAAA;EAYU;;;EAP3B,SAAA,IAAa,KAAA,EAAO,YAAA;EAqBpB;;;;;EAdA,kBAAA,GAAqB,KAAA,CAAM,cAAA;EAwBf;;AAAwB;AAAA;;EAjBpC,YAAA,GAAe,KAAA,CAAM,cAAA;EAwBP;;;;;EAjBd,iBAAA,GAAoB,KAAA,CAAM,cAAA;EAiBZ;;;EAZd,OAAA,GAAU,wBAAA;;;;EAKV,SAAA,GAAY,wBAAA;AAAA;;;;;cAOV,YAAA,kBAAY,yBAAA,CAAA,iBAAA,mBAAA,aAAA,CAAA,eAAA"}
|
|
@@ -2,13 +2,12 @@ import { isMultilineDocument, isPlainTextDocument } from "../helpers/schema.js";
|
|
|
2
2
|
import { getHTMLSerializerInstance } from "../serializers/html/html.js";
|
|
3
3
|
import { ExtraEditorCommands } from "../extensions/core/extra-editor-commands/extra-editor-commands.js";
|
|
4
4
|
import { ViewEventHandlers } from "../extensions/core/view-event-handlers.js";
|
|
5
|
-
import { useEditor } from "../hooks/use-editor.js";
|
|
6
5
|
import { getMarkdownSerializerInstance } from "../serializers/markdown/markdown.js";
|
|
7
6
|
import { getAllNodesAttributesByType, resolveContentSelection } from "./typist-editor.helper.js";
|
|
8
|
-
import { forwardRef, useCallback, useImperativeHandle, useMemo } from "react";
|
|
7
|
+
import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useState } from "react";
|
|
9
8
|
import { getSchema } from "@tiptap/core";
|
|
10
9
|
import { Placeholder } from "@tiptap/extension-placeholder";
|
|
11
|
-
import { EditorContent } from "@tiptap/react";
|
|
10
|
+
import { EditorContent, useEditor } from "@tiptap/react";
|
|
12
11
|
import { jsx } from "react/jsx-runtime";
|
|
13
12
|
//#region src/components/typist-editor.tsx
|
|
14
13
|
/**
|
|
@@ -16,21 +15,26 @@ import { jsx } from "react/jsx-runtime";
|
|
|
16
15
|
* top of the amazing [Tiptap](https://tiptap.dev/) library.
|
|
17
16
|
*/
|
|
18
17
|
const TypistEditor = forwardRef(function TypistEditor({ autoFocus, className, content = "", contentSelection, editable = true, extensions, placeholder, onBeforeCreate, onCreate, onUpdate, onSelectionUpdate, onTransaction, onFocus, onBlur, onDestroy, "aria-describedby": ariaDescribedBy, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, onClick, onKeyDown }, ref) {
|
|
18
|
+
const [initialExtensions] = useState(() => extensions);
|
|
19
|
+
const [initialContent] = useState(() => content);
|
|
20
|
+
const [initialPlaceholder] = useState(() => placeholder);
|
|
21
|
+
const [initialOnClick] = useState(() => onClick);
|
|
22
|
+
const [initialOnKeyDown] = useState(() => onKeyDown);
|
|
19
23
|
const allExtensions = useMemo(function initializeExtensions() {
|
|
20
24
|
return [
|
|
21
|
-
...
|
|
25
|
+
...initialPlaceholder ? [Placeholder.configure({ placeholder: initialPlaceholder })] : [],
|
|
22
26
|
ExtraEditorCommands,
|
|
23
27
|
ViewEventHandlers.configure({
|
|
24
|
-
onClick,
|
|
25
|
-
onKeyDown
|
|
28
|
+
onClick: initialOnClick,
|
|
29
|
+
onKeyDown: initialOnKeyDown
|
|
26
30
|
}),
|
|
27
|
-
...
|
|
31
|
+
...initialExtensions
|
|
28
32
|
];
|
|
29
33
|
}, [
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
initialExtensions,
|
|
35
|
+
initialPlaceholder,
|
|
36
|
+
initialOnClick,
|
|
37
|
+
initialOnKeyDown
|
|
34
38
|
]);
|
|
35
39
|
const schema = useMemo(function generateProseMirrorSchema() {
|
|
36
40
|
return getSchema(allExtensions);
|
|
@@ -42,23 +46,8 @@ const TypistEditor = forwardRef(function TypistEditor({ autoFocus, className, co
|
|
|
42
46
|
return getMarkdownSerializerInstance(schema);
|
|
43
47
|
}, [schema]);
|
|
44
48
|
const htmlContent = useMemo(function generateHTMLContent() {
|
|
45
|
-
return htmlSerializer.serialize(
|
|
46
|
-
}, [
|
|
47
|
-
const ariaAttributes = useMemo(function initializeAriaAttributes() {
|
|
48
|
-
return {
|
|
49
|
-
"aria-readonly": String(!editable),
|
|
50
|
-
"aria-multiline": String(isMultilineDocument(schema)),
|
|
51
|
-
...ariaDescribedBy ? { "aria-describedby": ariaDescribedBy } : {},
|
|
52
|
-
...ariaLabel ? { "aria-label": ariaLabel } : {},
|
|
53
|
-
...ariaLabelledBy ? { "aria-labelledby": ariaLabelledBy } : {}
|
|
54
|
-
};
|
|
55
|
-
}, [
|
|
56
|
-
ariaDescribedBy,
|
|
57
|
-
ariaLabel,
|
|
58
|
-
ariaLabelledBy,
|
|
59
|
-
editable,
|
|
60
|
-
schema
|
|
61
|
-
]);
|
|
49
|
+
return htmlSerializer.serialize(initialContent);
|
|
50
|
+
}, [initialContent, htmlSerializer]);
|
|
62
51
|
const handleCreate = useCallback(function handleCreate(props) {
|
|
63
52
|
const { view } = props.editor;
|
|
64
53
|
if (autoFocus && contentSelection) view.dispatch(view.state.tr.setSelection(resolveContentSelection(view.state.doc, contentSelection)).scrollIntoView());
|
|
@@ -68,18 +57,36 @@ const TypistEditor = forwardRef(function TypistEditor({ autoFocus, className, co
|
|
|
68
57
|
contentSelection,
|
|
69
58
|
onCreate
|
|
70
59
|
]);
|
|
60
|
+
const editorProps = useMemo(function initializeEditorProps() {
|
|
61
|
+
return { attributes: {
|
|
62
|
+
"data-typist-editor": "true",
|
|
63
|
+
...isPlainTextDocument(schema) ? { "data-plain-text": "true" } : { "data-rich-text": "true" },
|
|
64
|
+
"aria-readonly": String(!editable),
|
|
65
|
+
"aria-multiline": String(isMultilineDocument(schema)),
|
|
66
|
+
...ariaDescribedBy ? { "aria-describedby": ariaDescribedBy } : {},
|
|
67
|
+
...ariaLabel ? { "aria-label": ariaLabel } : {},
|
|
68
|
+
...ariaLabelledBy ? { "aria-labelledby": ariaLabelledBy } : {},
|
|
69
|
+
role: "textbox"
|
|
70
|
+
} };
|
|
71
|
+
}, [
|
|
72
|
+
schema,
|
|
73
|
+
editable,
|
|
74
|
+
ariaDescribedBy,
|
|
75
|
+
ariaLabel,
|
|
76
|
+
ariaLabelledBy
|
|
77
|
+
]);
|
|
78
|
+
const parseOptions = useMemo(function initializeParseOptions() {
|
|
79
|
+
return { preserveWhitespace: isPlainTextDocument(schema) };
|
|
80
|
+
}, [schema]);
|
|
71
81
|
const editor = useEditor({
|
|
72
82
|
autofocus: autoFocus ? "end" : false,
|
|
73
83
|
content: htmlContent,
|
|
74
84
|
editable,
|
|
75
|
-
editorProps
|
|
76
|
-
"data-typist-editor": "true",
|
|
77
|
-
...isPlainTextDocument(schema) ? { "data-plain-text": "true" } : { "data-rich-text": "true" },
|
|
78
|
-
role: "textbox",
|
|
79
|
-
...ariaAttributes
|
|
80
|
-
} },
|
|
85
|
+
editorProps,
|
|
81
86
|
extensions: allExtensions,
|
|
82
|
-
parseOptions
|
|
87
|
+
parseOptions,
|
|
88
|
+
immediatelyRender: true,
|
|
89
|
+
shouldRerenderOnTransaction: false,
|
|
83
90
|
...onBeforeCreate ? { onBeforeCreate } : {},
|
|
84
91
|
onCreate: handleCreate,
|
|
85
92
|
...onUpdate ? { onUpdate(props) {
|
|
@@ -95,22 +102,19 @@ const TypistEditor = forwardRef(function TypistEditor({ autoFocus, className, co
|
|
|
95
102
|
...onFocus ? { onFocus } : {},
|
|
96
103
|
...onBlur ? { onBlur } : {},
|
|
97
104
|
...onDestroy ? { onDestroy } : {}
|
|
105
|
+
});
|
|
106
|
+
useEffect(function syncEditableState() {
|
|
107
|
+
editor.setEditable(editable);
|
|
108
|
+
}, [editor, editable]);
|
|
109
|
+
useEffect(function syncViewEventHandlers() {
|
|
110
|
+
editor.storage.viewEventHandlers.setHandlers({
|
|
111
|
+
onClick,
|
|
112
|
+
onKeyDown
|
|
113
|
+
});
|
|
98
114
|
}, [
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
editable,
|
|
103
|
-
handleCreate,
|
|
104
|
-
htmlContent,
|
|
105
|
-
markdownSerializer,
|
|
106
|
-
onBeforeCreate,
|
|
107
|
-
onBlur,
|
|
108
|
-
onDestroy,
|
|
109
|
-
onFocus,
|
|
110
|
-
onSelectionUpdate,
|
|
111
|
-
onTransaction,
|
|
112
|
-
onUpdate,
|
|
113
|
-
schema
|
|
115
|
+
editor,
|
|
116
|
+
onClick,
|
|
117
|
+
onKeyDown
|
|
114
118
|
]);
|
|
115
119
|
useImperativeHandle(ref, function exposeHelperFunctionsToParent() {
|
|
116
120
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typist-editor.js","names":[],"sources":["../../src/components/typist-editor.tsx"],"sourcesContent":["import { forwardRef, useCallback, useImperativeHandle, useMemo } from 'react'\n\nimport { getSchema } from '@tiptap/core'\nimport { Placeholder } from '@tiptap/extension-placeholder'\nimport { EditorContent } from '@tiptap/react'\n\nimport { ExtraEditorCommands } from '../extensions/core/extra-editor-commands/extra-editor-commands'\nimport { ViewEventHandlers, ViewEventHandlersOptions } from '../extensions/core/view-event-handlers'\nimport { isMultilineDocument, isPlainTextDocument } from '../helpers/schema'\nimport { useEditor } from '../hooks/use-editor'\nimport { getHTMLSerializerInstance } from '../serializers/html/html'\nimport { getMarkdownSerializerInstance } from '../serializers/markdown/markdown'\n\nimport { getAllNodesAttributesByType, resolveContentSelection } from './typist-editor.helper'\n\nimport type { Editor as CoreEditor, EditorEvents, Extensions } from '@tiptap/core'\nimport type { Selection } from '@tiptap/pm/state'\n\n/**\n * The forwarded ref that describes the helper methods that the `TypistEditor` parent component\n * will have access to.\n */\ntype TypistEditorRef = {\n /**\n * Returns the `Editor` instance associated to the `TypistEditor` component.\n */\n getEditor: () => CoreEditor\n\n /**\n * Returns the current editor document output as Markdown.\n */\n getMarkdown: () => string\n\n /**\n * Returns the attributes of a given node type for all the nodes in the editor document.\n */\n getAllNodesAttributesByType: (\n nodeType: string,\n ) => ReturnType<typeof getAllNodesAttributesByType>\n}\n\n/**\n * The properties that describe the `beforeCreate` editor event.\n */\ntype BeforeCreateProps = EditorEvents['beforeCreate']\n\n/**\n * The properties that describe the `create` editor event.\n */\ntype CreateProps = EditorEvents['create']\n\n/**\n * The properties that describe the `update` editor event.\n */\ntype UpdateProps = EditorEvents['update'] & Pick<TypistEditorRef, 'getMarkdown'>\n\n/**\n * The properties that describe the `selectionUpdate` editor event.\n */\ntype SelectionUpdateProps = EditorEvents['selectionUpdate']\n\n/**\n * The properties that describe the `transaction` editor event.\n */\ntype TransacationProps = EditorEvents['transaction']\n\n/**\n * The properties that describe the `focus` editor event.\n */\ntype FocusProps = EditorEvents['focus']\n\n/**\n * The properties that describe the `blur` editor event.\n */\ntype BlurProps = EditorEvents['blur']\n\n/**\n * The properties that describe the `destroy` editor event.\n */\ntype DestroyProps = EditorEvents['destroy']\n\n/**\n * The properties available to represent an instance of the `TypistEditor` component, including\n * the supported WAI-ARIA 1.1 attributes.\n */\ntype TypistEditorProps = {\n /**\n * Auto focus the editor to the end of the document on initialization.\n */\n autoFocus?: boolean\n\n /**\n * The CSS class for the container surrounding the editor DOM element.\n */\n className?: string\n\n /**\n * The initial Markdown content for the editor.\n */\n content?: string\n\n /**\n * The initial content selection (only applied if `autoFocus` is `true`).\n */\n contentSelection?: Selection\n\n /**\n * Determines if users can write into the editor.\n */\n editable?: boolean\n\n /**\n * The list of required extensions to initialize the editor.\n *\n * You may consider wrapping this prop with `useMemo` to prevent unnecessary re-renders.\n */\n extensions: Extensions\n\n /**\n * A short hint that gives users an idea what can be entered in the editor.\n */\n placeholder?: string\n\n /**\n * The event handler that is fired before the editor view is created.\n *\n * You may consider wrapping this prop with `useCallback` to prevent unnecessary re-renders.\n */\n onBeforeCreate?: (props: BeforeCreateProps) => void\n\n /**\n * The event handler that is fired when the editor view is ready.\n *\n * You may consider wrapping this prop with `useCallback` to prevent unnecessary re-renders.\n */\n onCreate?: (props: CreateProps) => void\n\n /**\n * The event handler that is fired when the editor content has changed.\n *\n * You may consider wrapping this prop with `useCallback` to prevent unnecessary re-renders.\n */\n onUpdate?: (props: UpdateProps) => void\n\n /**\n * The event handler that is fired when the editor selection has changed.\n *\n * You may consider wrapping this prop with `useCallback` to prevent unnecessary re-renders.\n */\n onSelectionUpdate?: (props: SelectionUpdateProps) => void\n\n /**\n * The event handler that is fired when the editor state has changed.\n *\n * You may consider wrapping this prop with `useCallback` to prevent unnecessary re-renders.\n */\n onTransaction?: (props: TransacationProps) => void\n\n /**\n * The event handler that is fired when the editor view gains focus.\n *\n * You may consider wrapping this prop with `useCallback` to prevent unnecessary re-renders.\n */\n onFocus?: (props: FocusProps) => void\n\n /**\n * The event handler that is fired when the editor view loses focus.\n *\n * You may consider wrapping this prop with `useCallback` to prevent unnecessary re-renders.\n */\n onBlur?: (props: BlurProps) => void\n\n /**\n * The event handler that is fired when the editor view is being destroyed.\n *\n * You may consider wrapping this prop with `useCallback` to prevent unnecessary re-renders.\n */\n onDestroy?: (props: DestroyProps) => void\n\n /**\n * Identifies the element (or elements) that describes the object.\n *\n * @see aria-labelledby\n */\n 'aria-describedby'?: React.AriaAttributes['aria-describedby']\n\n /**\n * Defines a string value that labels the current element.\n *\n * @see aria-labelledby.\n */\n 'aria-label'?: React.AriaAttributes['aria-label']\n\n /**\n * Identifies the element (or elements) that labels the current element.\n *\n * @see aria-describedby.\n */\n 'aria-labelledby'?: React.AriaAttributes['aria-labelledby']\n\n /**\n * The event handler that processes `click` events in the editor.\n */\n onClick?: ViewEventHandlersOptions['onClick']\n\n /**\n * The event handler that processes `keydown` events in the editor.\n */\n onKeyDown?: ViewEventHandlersOptions['onKeyDown']\n}\n\n/**\n * The `TypistEditor` component represents a plain-text or a rich-text editing control, built on\n * top of the amazing [Tiptap](https://tiptap.dev/) library.\n */\nconst TypistEditor = forwardRef<TypistEditorRef, TypistEditorProps>(function TypistEditor(\n {\n autoFocus,\n className,\n content = '',\n contentSelection,\n editable = true,\n extensions,\n placeholder,\n onBeforeCreate,\n onCreate,\n onUpdate,\n onSelectionUpdate,\n onTransaction,\n onFocus,\n onBlur,\n onDestroy,\n 'aria-describedby': ariaDescribedBy,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n onClick,\n onKeyDown,\n },\n ref,\n) {\n const allExtensions = useMemo(\n function initializeExtensions() {\n return [\n ...(placeholder\n ? [\n Placeholder.configure({\n placeholder,\n }),\n ]\n : []),\n ExtraEditorCommands,\n ViewEventHandlers.configure({\n onClick,\n onKeyDown,\n }),\n // Always register external extensions at the end so they get a higher priority and\n // are loaded earlier (necessary to override behaviors from built-in extensions)\n ...extensions,\n ]\n },\n [extensions, onClick, onKeyDown, placeholder],\n )\n\n const schema = useMemo(\n function generateProseMirrorSchema() {\n return getSchema(allExtensions)\n },\n [allExtensions],\n )\n\n const htmlSerializer = useMemo(\n function initializeHTMLSerializer() {\n return getHTMLSerializerInstance(schema)\n },\n [schema],\n )\n\n const markdownSerializer = useMemo(\n function initializeMarkdownSerializer() {\n return getMarkdownSerializerInstance(schema)\n },\n [schema],\n )\n\n const htmlContent = useMemo(\n function generateHTMLContent() {\n return htmlSerializer.serialize(content)\n },\n [content, htmlSerializer],\n )\n\n const ariaAttributes = useMemo(\n function initializeAriaAttributes() {\n return {\n 'aria-readonly': String(!editable),\n 'aria-multiline': String(isMultilineDocument(schema)),\n ...(ariaDescribedBy ? { 'aria-describedby': ariaDescribedBy } : {}),\n ...(ariaLabel ? { 'aria-label': ariaLabel } : {}),\n ...(ariaLabelledBy ? { 'aria-labelledby': ariaLabelledBy } : {}),\n }\n },\n [ariaDescribedBy, ariaLabel, ariaLabelledBy, editable, schema],\n )\n\n const handleCreate = useCallback(\n function handleCreate(props: CreateProps) {\n const { view } = props.editor\n\n // Apply a selection to the document if one was given and `autoFocus` is `true`\n if (autoFocus && contentSelection) {\n view.dispatch(\n view.state.tr\n .setSelection(resolveContentSelection(view.state.doc, contentSelection))\n .scrollIntoView(),\n )\n }\n\n // Invoke the user `onCreate` handle after all internal initializations\n onCreate?.(props)\n },\n [autoFocus, contentSelection, onCreate],\n )\n\n const editor = useEditor(\n {\n autofocus: autoFocus ? 'end' : false,\n content: htmlContent,\n editable,\n editorProps: {\n attributes: {\n 'data-typist-editor': 'true',\n ...(isPlainTextDocument(schema)\n ? { 'data-plain-text': 'true' }\n : { 'data-rich-text': 'true' }),\n role: 'textbox',\n ...ariaAttributes,\n },\n },\n extensions: allExtensions,\n parseOptions: {\n preserveWhitespace: isPlainTextDocument(schema),\n },\n ...(onBeforeCreate ? { onBeforeCreate } : {}),\n onCreate: handleCreate,\n ...(onUpdate\n ? {\n onUpdate(props) {\n onUpdate({\n ...props,\n getMarkdown() {\n return markdownSerializer.serialize(props.editor.getHTML())\n },\n })\n },\n }\n : {}),\n ...(onSelectionUpdate ? { onSelectionUpdate } : {}),\n ...(onTransaction ? { onTransaction } : {}),\n ...(onFocus ? { onFocus } : {}),\n ...(onBlur ? { onBlur } : {}),\n ...(onDestroy ? { onDestroy } : {}),\n },\n [\n allExtensions,\n ariaAttributes,\n autoFocus,\n editable,\n handleCreate,\n htmlContent,\n markdownSerializer,\n onBeforeCreate,\n onBlur,\n onDestroy,\n onFocus,\n onSelectionUpdate,\n onTransaction,\n onUpdate,\n schema,\n ],\n )\n\n useImperativeHandle(\n ref,\n function exposeHelperFunctionsToParent() {\n return {\n getEditor() {\n return editor\n },\n getMarkdown() {\n return markdownSerializer.serialize(editor.getHTML())\n },\n getAllNodesAttributesByType(nodeType) {\n return getAllNodesAttributesByType(editor.state.doc, nodeType)\n },\n }\n },\n [editor, markdownSerializer],\n )\n\n return <EditorContent className={className} editor={editor} />\n})\n\nexport { TypistEditor }\n\nexport type {\n BeforeCreateProps,\n BlurProps,\n CreateProps,\n DestroyProps,\n FocusProps,\n SelectionUpdateProps,\n TransacationProps,\n TypistEditorProps,\n TypistEditorRef,\n UpdateProps,\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"typist-editor.js","names":[],"sources":["../../src/components/typist-editor.tsx"],"sourcesContent":["import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useState } from 'react'\n\nimport { getSchema } from '@tiptap/core'\nimport { Placeholder } from '@tiptap/extension-placeholder'\nimport { EditorContent, useEditor } from '@tiptap/react'\n\nimport { ExtraEditorCommands } from '../extensions/core/extra-editor-commands/extra-editor-commands'\nimport { ViewEventHandlers, ViewEventHandlersOptions } from '../extensions/core/view-event-handlers'\nimport { isMultilineDocument, isPlainTextDocument } from '../helpers/schema'\nimport { getHTMLSerializerInstance } from '../serializers/html/html'\nimport { getMarkdownSerializerInstance } from '../serializers/markdown/markdown'\n\nimport { getAllNodesAttributesByType, resolveContentSelection } from './typist-editor.helper'\n\nimport type { Editor as CoreEditor, EditorEvents, Extensions } from '@tiptap/core'\nimport type { Selection } from '@tiptap/pm/state'\n\n/**\n * The forwarded ref that describes the helper methods that the `TypistEditor` parent component\n * will have access to.\n */\ntype TypistEditorRef = {\n /**\n * Returns the `Editor` instance associated to the `TypistEditor` component.\n */\n getEditor: () => CoreEditor\n\n /**\n * Returns the current editor document output as Markdown.\n */\n getMarkdown: () => string\n\n /**\n * Returns the attributes of a given node type for all the nodes in the editor document.\n */\n getAllNodesAttributesByType: (\n nodeType: string,\n ) => ReturnType<typeof getAllNodesAttributesByType>\n}\n\n/**\n * The properties that describe the `beforeCreate` editor event.\n */\ntype BeforeCreateProps = EditorEvents['beforeCreate']\n\n/**\n * The properties that describe the `create` editor event.\n */\ntype CreateProps = EditorEvents['create']\n\n/**\n * The properties that describe the `update` editor event.\n */\ntype UpdateProps = EditorEvents['update'] & Pick<TypistEditorRef, 'getMarkdown'>\n\n/**\n * The properties that describe the `selectionUpdate` editor event.\n */\ntype SelectionUpdateProps = EditorEvents['selectionUpdate']\n\n/**\n * The properties that describe the `transaction` editor event.\n */\ntype TransacationProps = EditorEvents['transaction']\n\n/**\n * The properties that describe the `focus` editor event.\n */\ntype FocusProps = EditorEvents['focus']\n\n/**\n * The properties that describe the `blur` editor event.\n */\ntype BlurProps = EditorEvents['blur']\n\n/**\n * The properties that describe the `destroy` editor event.\n */\ntype DestroyProps = EditorEvents['destroy']\n\n/**\n * The properties available to represent an instance of the `TypistEditor` component, including\n * the supported WAI-ARIA 1.1 attributes.\n */\ntype TypistEditorProps = {\n /**\n * Auto focus the editor to the end of the document on initialization.\n */\n autoFocus?: boolean\n\n /**\n * The CSS class for the container surrounding the editor DOM element.\n */\n className?: string\n\n /**\n * The initial Markdown content for the editor.\n *\n * This value is only used when the editor is first mounted, subsequent changes to this prop are\n * ignored. Use `editor.commands.setContent()` to update content at runtime.\n */\n content?: string\n\n /**\n * The initial content selection (only applied if `autoFocus` is `true`).\n */\n contentSelection?: Selection\n\n /**\n * Determines if users can write into the editor.\n */\n editable?: boolean\n\n /**\n * The list of extensions to initialize the editor with.\n *\n * This value is only used when the editor is first mounted, subsequent changes to this prop are\n * ignored. Extensions that depend on data that changes over time should read the current value\n * at the moment it's needed from a mutable source you update from outside (a ref or a store),\n * rather than capturing the value when the extension is created.\n */\n extensions: Extensions\n\n /**\n * A short hint that gives users an idea what can be entered in the editor.\n *\n * This value is only used when the editor is first mounted, subsequent changes to this prop are\n * ignored.\n */\n placeholder?: string\n\n /**\n * The event handler that is fired before the editor view is created.\n */\n onBeforeCreate?: (props: BeforeCreateProps) => void\n\n /**\n * The event handler that is fired when the editor view is ready.\n */\n onCreate?: (props: CreateProps) => void\n\n /**\n * The event handler that is fired when the editor content has changed.\n */\n onUpdate?: (props: UpdateProps) => void\n\n /**\n * The event handler that is fired when the editor selection has changed.\n */\n onSelectionUpdate?: (props: SelectionUpdateProps) => void\n\n /**\n * The event handler that is fired when the editor state has changed.\n */\n onTransaction?: (props: TransacationProps) => void\n\n /**\n * The event handler that is fired when the editor view gains focus.\n */\n onFocus?: (props: FocusProps) => void\n\n /**\n * The event handler that is fired when the editor view loses focus.\n */\n onBlur?: (props: BlurProps) => void\n\n /**\n * The event handler that is fired when the editor view is being destroyed.\n */\n onDestroy?: (props: DestroyProps) => void\n\n /**\n * Identifies the element (or elements) that describes the object.\n *\n * @see aria-labelledby\n */\n 'aria-describedby'?: React.AriaAttributes['aria-describedby']\n\n /**\n * Defines a string value that labels the current element.\n *\n * @see aria-labelledby.\n */\n 'aria-label'?: React.AriaAttributes['aria-label']\n\n /**\n * Identifies the element (or elements) that labels the current element.\n *\n * @see aria-describedby.\n */\n 'aria-labelledby'?: React.AriaAttributes['aria-labelledby']\n\n /**\n * The event handler that processes `click` events in the editor.\n */\n onClick?: ViewEventHandlersOptions['onClick']\n\n /**\n * The event handler that processes `keydown` events in the editor.\n */\n onKeyDown?: ViewEventHandlersOptions['onKeyDown']\n}\n\n/**\n * The `TypistEditor` component represents a plain-text or a rich-text editing control, built on\n * top of the amazing [Tiptap](https://tiptap.dev/) library.\n */\nconst TypistEditor = forwardRef<TypistEditorRef, TypistEditorProps>(function TypistEditor(\n {\n autoFocus,\n className,\n content = '',\n contentSelection,\n editable = true,\n extensions,\n placeholder,\n onBeforeCreate,\n onCreate,\n onUpdate,\n onSelectionUpdate,\n onTransaction,\n onFocus,\n onBlur,\n onDestroy,\n 'aria-describedby': ariaDescribedBy,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n onClick,\n onKeyDown,\n },\n ref,\n) {\n // Extensions and content are captured once at mount. Subsequent changes to these props are\n // ignored because recreating the editor is expensive and unnecessary for runtime updates.\n // Use `editor.commands.setContent()` to update content, and design extensions to handle\n // dynamic data internally (e.g., via stores or refs) rather than requiring reconfiguration.\n const [initialExtensions] = useState(() => extensions)\n const [initialContent] = useState(() => content)\n const [initialPlaceholder] = useState(() => placeholder)\n\n // Capture the initial click and keydown handlers so they are configured on the extension from\n // the first render. Unlike content and extensions, they stay reactive, so later changes are\n // synced at runtime.\n const [initialOnClick] = useState(() => onClick)\n const [initialOnKeyDown] = useState(() => onKeyDown)\n\n const allExtensions = useMemo(\n function initializeExtensions() {\n return [\n ...(initialPlaceholder\n ? [\n Placeholder.configure({\n placeholder: initialPlaceholder,\n }),\n ]\n : []),\n ExtraEditorCommands,\n ViewEventHandlers.configure({\n onClick: initialOnClick,\n onKeyDown: initialOnKeyDown,\n }),\n // Always register external extensions at the end so they get a higher priority and\n // are loaded earlier (necessary to override behaviors from built-in extensions)\n ...initialExtensions,\n ]\n },\n [initialExtensions, initialPlaceholder, initialOnClick, initialOnKeyDown],\n )\n\n const schema = useMemo(\n function generateProseMirrorSchema() {\n return getSchema(allExtensions)\n },\n [allExtensions],\n )\n\n const htmlSerializer = useMemo(\n function initializeHTMLSerializer() {\n return getHTMLSerializerInstance(schema)\n },\n [schema],\n )\n\n const markdownSerializer = useMemo(\n function initializeMarkdownSerializer() {\n return getMarkdownSerializerInstance(schema)\n },\n [schema],\n )\n\n const htmlContent = useMemo(\n function generateHTMLContent() {\n return htmlSerializer.serialize(initialContent)\n },\n [initialContent, htmlSerializer],\n )\n\n const handleCreate = useCallback(\n function handleCreate(props: CreateProps) {\n const { view } = props.editor\n\n // Apply a selection to the document if one was given and `autoFocus` is `true`\n if (autoFocus && contentSelection) {\n view.dispatch(\n view.state.tr\n .setSelection(resolveContentSelection(view.state.doc, contentSelection))\n .scrollIntoView(),\n )\n }\n\n // Invoke the user `onCreate` handle after all internal initializations\n onCreate?.(props)\n },\n [autoFocus, contentSelection, onCreate],\n )\n\n // Keep these option objects memoized so they preserve a stable reference across renders. The\n // built-in `useEditor` compares options by reference and reconfigures the editor in place when\n // they differ, so passing inline objects would trigger that work on every parent re-render.\n const editorProps = useMemo(\n function initializeEditorProps() {\n return {\n attributes: {\n 'data-typist-editor': 'true',\n ...(isPlainTextDocument(schema)\n ? { 'data-plain-text': 'true' }\n : { 'data-rich-text': 'true' }),\n 'aria-readonly': String(!editable),\n 'aria-multiline': String(isMultilineDocument(schema)),\n ...(ariaDescribedBy ? { 'aria-describedby': ariaDescribedBy } : {}),\n ...(ariaLabel ? { 'aria-label': ariaLabel } : {}),\n ...(ariaLabelledBy ? { 'aria-labelledby': ariaLabelledBy } : {}),\n role: 'textbox',\n },\n }\n },\n [schema, editable, ariaDescribedBy, ariaLabel, ariaLabelledBy],\n )\n\n const parseOptions = useMemo(\n function initializeParseOptions() {\n return {\n preserveWhitespace: isPlainTextDocument(schema),\n }\n },\n [schema],\n )\n\n const editor = useEditor({\n autofocus: autoFocus ? 'end' : false,\n content: htmlContent,\n editable,\n editorProps,\n extensions: allExtensions,\n parseOptions,\n\n // Tiptap's `useEditor` returns `null` by default on the first render to support SSR.\n // Typist has no need for SSR, so we opt into immediate rendering to guarantee a\n // non-null editor instance from the first render.\n immediatelyRender: true,\n\n // Opt-out of the legacy behavior that re-renders the component on every ProseMirror\n // transaction (e.g. keystrokes, selection changes). Typist doesn't read reactive editor\n // state during render, so these re-renders would be wasteful. Consumers that need\n // reactive state (e.g. toolbars) should subscribe directly via `useSyncExternalStore`.\n shouldRerenderOnTransaction: false,\n\n ...(onBeforeCreate ? { onBeforeCreate } : {}),\n onCreate: handleCreate,\n ...(onUpdate\n ? {\n onUpdate(props) {\n onUpdate({\n ...props,\n getMarkdown() {\n return markdownSerializer.serialize(props.editor.getHTML())\n },\n })\n },\n }\n : {}),\n ...(onSelectionUpdate ? { onSelectionUpdate } : {}),\n ...(onTransaction ? { onTransaction } : {}),\n ...(onFocus ? { onFocus } : {}),\n ...(onBlur ? { onBlur } : {}),\n ...(onDestroy ? { onDestroy } : {}),\n })\n\n useEffect(\n function syncEditableState() {\n editor.setEditable(editable)\n },\n [editor, editable],\n )\n\n // The editor is created once, so push the latest handlers into the extension as they change\n useEffect(\n function syncViewEventHandlers() {\n editor.storage.viewEventHandlers.setHandlers({ onClick, onKeyDown })\n },\n [editor, onClick, onKeyDown],\n )\n\n useImperativeHandle(\n ref,\n function exposeHelperFunctionsToParent() {\n return {\n getEditor() {\n return editor\n },\n getMarkdown() {\n return markdownSerializer.serialize(editor.getHTML())\n },\n getAllNodesAttributesByType(nodeType) {\n return getAllNodesAttributesByType(editor.state.doc, nodeType)\n },\n }\n },\n [editor, markdownSerializer],\n )\n\n return <EditorContent className={className} editor={editor} />\n})\n\nexport { TypistEditor }\n\nexport type {\n BeforeCreateProps,\n BlurProps,\n CreateProps,\n DestroyProps,\n FocusProps,\n SelectionUpdateProps,\n TransacationProps,\n TypistEditorProps,\n TypistEditorRef,\n UpdateProps,\n}\n"],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ import { EditorView } from "@tiptap/pm/view";
|
|
|
3
3
|
|
|
4
4
|
//#region src/extensions/core/view-event-handlers.d.ts
|
|
5
5
|
/**
|
|
6
|
-
* The
|
|
6
|
+
* The set of view event handlers that can be provided to the editor.
|
|
7
7
|
*
|
|
8
8
|
* If more view handlers are needed, please look into the available event handlers in
|
|
9
9
|
* [`prosemirror-view`](https://prosemirror.net/docs/ref/#view.Props), and add them below.
|
|
@@ -18,17 +18,6 @@ type ViewEventHandlersOptions = {
|
|
|
18
18
|
*/
|
|
19
19
|
onKeyDown?: (event: KeyboardEvent, view: EditorView) => boolean | void;
|
|
20
20
|
};
|
|
21
|
-
/**
|
|
22
|
-
* The `ViewEventHandlers` extension allows handling of various ProseMirror view events.
|
|
23
|
-
*
|
|
24
|
-
* The various event-handling functions may all return `true` to indicate that they handled the
|
|
25
|
-
* given event. The view will then take care to call `preventDefault` on the event, except with
|
|
26
|
-
* `handleDOMEvents`, where the handler itself is responsible for that. Return `false` or
|
|
27
|
-
* `undefined` for the default event handler to be called.
|
|
28
|
-
*
|
|
29
|
-
* These event handlers should be used sparingly, please consider if a reusable extension would be
|
|
30
|
-
* more appropriate for your use case.
|
|
31
|
-
*/
|
|
32
21
|
//#endregion
|
|
33
22
|
export type { ViewEventHandlersOptions };
|
|
34
23
|
//# sourceMappingURL=view-event-handlers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"view-event-handlers.d.ts","names":[],"sources":["../../../src/extensions/core/view-event-handlers.ts"],"mappings":";;;;;;AAKiD;;;;KAQ5C,wBAAA;EASmB;;;EALpB,OAAA,IAAW,KAAA,EAAO,UAAA,EAAY,IAAA,EAAM,UAAA,EAAY,GAAA;EAAhD;;;EAKA,SAAA,IAAa,KAAA,EAAO,aAAA,EAAe,IAAA,EAAM,UAAA;AAAA
|
|
1
|
+
{"version":3,"file":"view-event-handlers.d.ts","names":[],"sources":["../../../src/extensions/core/view-event-handlers.ts"],"mappings":";;;;;;AAKiD;;;;KAQ5C,wBAAA;EASmB;;;EALpB,OAAA,IAAW,KAAA,EAAO,UAAA,EAAY,IAAA,EAAM,UAAA,EAAY,GAAA;EAAhD;;;EAKA,SAAA,IAAa,KAAA,EAAO,aAAA,EAAe,IAAA,EAAM,UAAA;AAAA"}
|
|
@@ -16,16 +16,26 @@ import { Plugin, PluginKey } from "@tiptap/pm/state";
|
|
|
16
16
|
const ViewEventHandlers = Extension.create({
|
|
17
17
|
name: "viewEventHandlers",
|
|
18
18
|
priority: 105,
|
|
19
|
+
addStorage() {
|
|
20
|
+
return {
|
|
21
|
+
onClick: this.options.onClick,
|
|
22
|
+
onKeyDown: this.options.onKeyDown,
|
|
23
|
+
setHandlers(handlers) {
|
|
24
|
+
this.onClick = handlers.onClick;
|
|
25
|
+
this.onKeyDown = handlers.onKeyDown;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
},
|
|
19
29
|
addProseMirrorPlugins() {
|
|
20
|
-
const {
|
|
30
|
+
const { editor } = this;
|
|
21
31
|
return [new Plugin({
|
|
22
32
|
key: new PluginKey("viewEventHandlers"),
|
|
23
33
|
props: {
|
|
24
34
|
handleClick(view, pos, event) {
|
|
25
|
-
return
|
|
35
|
+
return editor.storage.viewEventHandlers.onClick?.(event, view, pos) || false;
|
|
26
36
|
},
|
|
27
37
|
handleKeyDown(view, event) {
|
|
28
|
-
return
|
|
38
|
+
return editor.storage.viewEventHandlers.onKeyDown?.(event, view) || false;
|
|
29
39
|
}
|
|
30
40
|
}
|
|
31
41
|
})];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"view-event-handlers.js","names":[],"sources":["../../../src/extensions/core/view-event-handlers.ts"],"sourcesContent":["import { Extension } from '@tiptap/core'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\n\nimport { VIEW_EVENT_HANDLERS_PRIORITY } from '../../constants/extension-priorities'\n\nimport type { EditorView } from '@tiptap/pm/view'\n\n/**\n * The
|
|
1
|
+
{"version":3,"file":"view-event-handlers.js","names":[],"sources":["../../../src/extensions/core/view-event-handlers.ts"],"sourcesContent":["import { Extension } from '@tiptap/core'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\n\nimport { VIEW_EVENT_HANDLERS_PRIORITY } from '../../constants/extension-priorities'\n\nimport type { EditorView } from '@tiptap/pm/view'\n\n/**\n * The set of view event handlers that can be provided to the editor.\n *\n * If more view handlers are needed, please look into the available event handlers in\n * [`prosemirror-view`](https://prosemirror.net/docs/ref/#view.Props), and add them below.\n */\ntype ViewEventHandlersOptions = {\n /**\n * Called when the editor is clicked, after `handleClickOn` handlers have been called.\n */\n onClick?: (event: MouseEvent, view: EditorView, pos: number) => boolean | void\n\n /**\n * Called when the editor receives a `keydown` event.\n */\n onKeyDown?: (event: KeyboardEvent, view: EditorView) => boolean | void\n}\n\ntype ViewEventHandlersStorage = ViewEventHandlersOptions & {\n /**\n * Updates the handlers the plugin invokes. The editor component calls this as its handler\n * props change, since the editor and its plugins are created only once.\n */\n setHandlers: (handlers: ViewEventHandlersOptions) => void\n}\n\n/**\n * The `ViewEventHandlers` extension allows handling of various ProseMirror view events.\n *\n * The various event-handling functions may all return `true` to indicate that they handled the\n * given event. The view will then take care to call `preventDefault` on the event, except with\n * `handleDOMEvents`, where the handler itself is responsible for that. Return `false` or\n * `undefined` for the default event handler to be called.\n *\n * These event handlers should be used sparingly, please consider if a reusable extension would be\n * more appropriate for your use case.\n */\nconst ViewEventHandlers = Extension.create<ViewEventHandlersOptions, ViewEventHandlersStorage>({\n name: 'viewEventHandlers',\n priority: VIEW_EVENT_HANDLERS_PRIORITY,\n addStorage() {\n return {\n onClick: this.options.onClick,\n onKeyDown: this.options.onKeyDown,\n setHandlers(handlers) {\n this.onClick = handlers.onClick\n this.onKeyDown = handlers.onKeyDown\n },\n }\n },\n addProseMirrorPlugins() {\n const { editor } = this\n\n return [\n new Plugin({\n key: new PluginKey('viewEventHandlers'),\n props: {\n handleClick(view, pos, event) {\n return editor.storage.viewEventHandlers.onClick?.(event, view, pos) || false\n },\n handleKeyDown(view, event) {\n return editor.storage.viewEventHandlers.onKeyDown?.(event, view) || false\n },\n },\n }),\n ]\n },\n})\n\nexport { ViewEventHandlers }\n\nexport type { ViewEventHandlersOptions }\n"],"mappings":";;;;;;;;;;;;;;;AA4CA,MAAM,oBAAoB,UAAU,OAA2D;CAC3F,MAAM;CACN,UAAA;CACA,aAAa;EACT,OAAO;GACH,SAAS,KAAK,QAAQ;GACtB,WAAW,KAAK,QAAQ;GACxB,YAAY,UAAU;IAClB,KAAK,UAAU,SAAS;IACxB,KAAK,YAAY,SAAS;GAC9B;EACJ;CACJ;CACA,wBAAwB;EACpB,MAAM,EAAE,WAAW;EAEnB,OAAO,CACH,IAAI,OAAO;GACP,KAAK,IAAI,UAAU,mBAAmB;GACtC,OAAO;IACH,YAAY,MAAM,KAAK,OAAO;KAC1B,OAAO,OAAO,QAAQ,kBAAkB,UAAU,OAAO,MAAM,GAAG,KAAK;IAC3E;IACA,cAAc,MAAM,OAAO;KACvB,OAAO,OAAO,QAAQ,kBAAkB,YAAY,OAAO,IAAI,KAAK;IACxE;GACJ;EACJ,CAAC,CACL;CACJ;AACJ,CAAC"}
|
|
@@ -105,6 +105,15 @@ type RichTextKitOptions = {
|
|
|
105
105
|
* Set to `false` to disable the `PasteEmojis` extension.
|
|
106
106
|
*/
|
|
107
107
|
pasteEmojis: false;
|
|
108
|
+
/**
|
|
109
|
+
* Set to `false` to disable the `PasteHTMLTableAsString` extension, which converts pasted HTML
|
|
110
|
+
* tables (e.g. from spreadsheets or GitHub Flavored Markdown) into space-separated paragraphs.
|
|
111
|
+
*
|
|
112
|
+
* This extension is only registered when native table support is unavailable (i.e. `table` is
|
|
113
|
+
* `false`, or the document is singleline). When tables are enabled, pasted tables become native
|
|
114
|
+
* tables instead, so this option has no effect.
|
|
115
|
+
*/
|
|
116
|
+
pasteHTMLTableAsString: false;
|
|
108
117
|
/**
|
|
109
118
|
* Set to `false` to disable the `PasteMarkdown` extension.
|
|
110
119
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rich-text-kit.d.ts","names":[],"sources":["../../../src/extensions/rich-text/rich-text-kit.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"rich-text-kit.d.ts","names":[],"sources":["../../../src/extensions/rich-text/rich-text-kit.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAyD6D;KAKxD,kBAAA;;;;EAID,UAAA,EAAY,OAAA,CAAQ,iBAAA;EAKd;;;EAAN,IAAA,EAAM,OAAA,CAAQ,WAAA;EAUR;;;EALN,UAAA,EAAY,OAAA,CAAQ,yBAAA;EAeV;;;EAVV,IAAA,EAAM,OAAA,CAAQ,WAAA;EAyBH;;;EApBX,SAAA,EAAW,OAAA,CAAQ,gBAAA;EA8BV;;;EAzBT,QAAA,EAAU,OAAA,CAAQ,uBAAA;EAmCX;;;EA9BP,UAAA,EAAY,OAAA,CAAQ,iBAAA;EAwCd;;;EAnCN,SAAA;EA6CY;;;EAxCZ,SAAA,EAAW,OAAA,CAAQ,gBAAA;EAkDR;;;EA7CX,OAAA,EAAS,OAAA,CAAQ,sBAAA;EAqFV;;;EAhFP,OAAA,EAAS,OAAA,CAAQ,cAAA;EAlDL;;;EAuDZ,cAAA,EAAgB,OAAA,CAAQ,qBAAA;EAlDV;;;EAuDd,KAAA,EAAO,OAAA,CAAQ,oBAAA;EA7Cf;;;EAkDA,MAAA,EAAQ,OAAA,CAAQ,aAAA;EA7CL;;;EAkDX,IAAA,EAAM,OAAA,CAAQ,mBAAA;EA7CI;;;EAkDlB,QAAA,EAAU,OAAA,CAAQ,eAAA;EAxClB;;;EA6CA,UAAA,EAAY,OAAA,CAAQ,iBAAA;EAnCpB;;;EAwCA,WAAA,EAAa,OAAA,CAAQ,0BAAA;EAnCZ;;;EAwCT,SAAA,EAAW,OAAA,CAAQ,gBAAA;EAnCK;;;EAwCxB,WAAA;EA9BA;;;;;;;;EAwCA,sBAAA;EAzBA;;;EA8BA,aAAA;EAzBa;;;EA8Bb,mBAAA;EAzBmB;;;EA8BnB,MAAA,EAAQ,OAAA,CAAQ,4BAAA;EALhB;;;;;;;;EAeA,KAAA,EAAO,OAAA,CAAQ,oBAAA;EAUL;AAAA;AAAA;EALV,IAAA;;;AAaa;EARb,UAAA;AAAA;;;;;;cAQE,WAAA,EAAW,SAAA,CAAA,kBAAA"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import "../../constants/extension-priorities.js";
|
|
2
2
|
import { CopyMarkdownSource } from "../shared/copy-markdown-source.js";
|
|
3
|
+
import { PasteHTMLTableAsString } from "../shared/paste-html-table-as-string.js";
|
|
3
4
|
import { PasteSinglelineText } from "../shared/paste-singleline-text.js";
|
|
4
5
|
import { BoldAndItalics } from "./bold-and-italics.js";
|
|
5
6
|
import { CurvenoteCodemark } from "./curvenote-codemark.js";
|
|
@@ -70,6 +71,7 @@ const RichTextKit = Extension.create({
|
|
|
70
71
|
if (this.options.strike !== false) extensions.push(RichTextStrikethrough.configure(this.options?.strike));
|
|
71
72
|
const isSinglelineDocument = this.options.document !== false && this.options.document?.multiline === false;
|
|
72
73
|
if (this.options.table !== false && !isSinglelineDocument) extensions.push(RichTextTable.configure(this.options?.table), TableRow, TableHeader.extend({ content: "paragraph" }), TableCell.extend({ content: "paragraph" }));
|
|
74
|
+
else if (this.options.pasteHTMLTableAsString !== false) extensions.push(PasteHTMLTableAsString);
|
|
73
75
|
if (this.options.text !== false) extensions.push(Text);
|
|
74
76
|
if (this.options.typography !== false) extensions.push(Typography);
|
|
75
77
|
return extensions;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rich-text-kit.js","names":[],"sources":["../../../src/extensions/rich-text/rich-text-kit.ts"],"sourcesContent":["import { Extension } from '@tiptap/core'\nimport { Blockquote } from '@tiptap/extension-blockquote'\nimport { Bold } from '@tiptap/extension-bold'\nimport { CodeBlock } from '@tiptap/extension-code-block'\nimport { Dropcursor } from '@tiptap/extension-dropcursor'\nimport { Gapcursor } from '@tiptap/extension-gapcursor'\nimport { HardBreak } from '@tiptap/extension-hard-break'\nimport { History } from '@tiptap/extension-history'\nimport { HorizontalRule } from '@tiptap/extension-horizontal-rule'\nimport { Italic } from '@tiptap/extension-italic'\nimport { ListItem } from '@tiptap/extension-list-item'\nimport { ListKeymap } from '@tiptap/extension-list-keymap'\nimport { Paragraph } from '@tiptap/extension-paragraph'\nimport { TableCell } from '@tiptap/extension-table-cell'\nimport { TableHeader } from '@tiptap/extension-table-header'\nimport { TableRow } from '@tiptap/extension-table-row'\nimport { Text } from '@tiptap/extension-text'\nimport { Typography } from '@tiptap/extension-typography'\n\nimport { BLOCKQUOTE_EXTENSION_PRIORITY } from '../../constants/extension-priorities'\nimport { CopyMarkdownSource } from '../shared/copy-markdown-source'\nimport { PasteSinglelineText } from '../shared/paste-singleline-text'\n\nimport { BoldAndItalics } from './bold-and-italics'\nimport { CurvenoteCodemark } from './curvenote-codemark'\nimport { PasteEmojis } from './paste-emojis'\nimport { PasteMarkdown } from './paste-markdown'\nimport { RichTextBulletList } from './rich-text-bullet-list'\nimport { RichTextCode } from './rich-text-code'\nimport { RichTextDocument } from './rich-text-document'\nimport { RichTextHeading, RichTextHeadingOptions } from './rich-text-heading'\nimport { RichTextImage } from './rich-text-image'\nimport { RichTextLink } from './rich-text-link'\nimport { RichTextOrderedList } from './rich-text-ordered-list'\nimport { RichTextStrikethrough } from './rich-text-strikethrough'\nimport { RichTextTable } from './rich-text-table'\n\nimport type { Extensions } from '@tiptap/core'\nimport type { BlockquoteOptions } from '@tiptap/extension-blockquote'\nimport type { BoldOptions } from '@tiptap/extension-bold'\nimport type { CodeOptions } from '@tiptap/extension-code'\nimport type { CodeBlockOptions } from '@tiptap/extension-code-block'\nimport type { DropcursorOptions } from '@tiptap/extension-dropcursor'\nimport type { HardBreakOptions } from '@tiptap/extension-hard-break'\nimport type { HistoryOptions } from '@tiptap/extension-history'\nimport type { HorizontalRuleOptions } from '@tiptap/extension-horizontal-rule'\nimport type { ItalicOptions } from '@tiptap/extension-italic'\nimport type { ListItemOptions } from '@tiptap/extension-list-item'\nimport type { ListKeymapOptions } from '@tiptap/extension-list-keymap'\nimport type { ParagraphOptions } from '@tiptap/extension-paragraph'\nimport type { RichTextBulletListOptions } from './rich-text-bullet-list'\nimport type { RichTextDocumentOptions } from './rich-text-document'\nimport type { RichTextImageOptions } from './rich-text-image'\nimport type { RichTextLinkOptions } from './rich-text-link'\nimport type { RichTextOrderedListOptions } from './rich-text-ordered-list'\nimport type { RichTextStrikethroughOptions } from './rich-text-strikethrough'\nimport type { RichTextTableOptions } from './rich-text-table'\n\n/**\n * The options available to customize the `RichTextKit` extension.\n */\ntype RichTextKitOptions = {\n /**\n * Set options for the `Blockquote` extension, or `false` to disable.\n */\n blockquote: Partial<BlockquoteOptions> | false\n\n /**\n * Set options for the `Bold` extension, or `false` to disable.\n */\n bold: Partial<BoldOptions> | false\n\n /**\n * Set options for the `BulletList` extension, or `false` to disable.\n */\n bulletList: Partial<RichTextBulletListOptions> | false\n\n /**\n * Set options for the `Code` extension, or `false` to disable.\n */\n code: Partial<CodeOptions> | false\n\n /**\n * Set options for the `CodeBlock` extension, or `false` to disable.\n */\n codeBlock: Partial<CodeBlockOptions> | false\n\n /**\n * Set options for the `Document` extension, or `false` to disable.\n */\n document: Partial<RichTextDocumentOptions> | false\n\n /**\n * Set options for the `Dropcursor` extension, or `false` to disable.\n */\n dropCursor: Partial<DropcursorOptions> | false\n\n /**\n * Set to `false` to disable the `Gapcursor` extension.\n */\n gapCursor: false\n\n /**\n * Set options for the `HardBreak` extension, or `false` to disable.\n */\n hardBreak: Partial<HardBreakOptions> | false\n\n /**\n * Set options for the `Heading` extension, or `false` to disable.\n */\n heading: Partial<RichTextHeadingOptions> | false\n\n /**\n * Set options for the `History` extension, or `false` to disable.\n */\n history: Partial<HistoryOptions> | false\n\n /**\n * Set options for the `HorizontalRule` extension, or `false` to disable.\n */\n horizontalRule: Partial<HorizontalRuleOptions> | false\n\n /**\n * Set options for the `Image` extension, or `false` to disable.\n */\n image: Partial<RichTextImageOptions> | false\n\n /**\n * Set options for the `Italic` extension, or `false` to disable.\n */\n italic: Partial<ItalicOptions> | false\n\n /**\n * Set options for the `Link` extension, or `false` to disable.\n */\n link: Partial<RichTextLinkOptions> | false\n\n /**\n * Set options for the `ListItem` extension, or `false` to disable.\n */\n listItem: Partial<ListItemOptions> | false\n\n /**\n * Set options for the `ListKeymap` extension, or `false` to disable.\n */\n listKeymap: Partial<ListKeymapOptions> | false\n\n /**\n * Set options for the `OrderedList` extension, or `false` to disable.\n */\n orderedList: Partial<RichTextOrderedListOptions> | false\n\n /**\n * Set options for the `Paragraph` extension, or `false` to disable.\n */\n paragraph: Partial<ParagraphOptions> | false\n\n /**\n * Set to `false` to disable the `PasteEmojis` extension.\n */\n pasteEmojis: false\n\n /**\n * Set to `false` to disable the `PasteMarkdown` extension.\n */\n pasteMarkdown: false\n\n /**\n * Set to `false` to disable the `PasteSinglelineText` extension.\n */\n pasteSinglelineText: false\n\n /**\n * Set options for the `Strike` extension, or `false` to disable.\n */\n strike: Partial<RichTextStrikethroughOptions> | false\n\n /**\n * Set options for the `Table` extension, or `false` to disable.\n *\n * Note that table cells are restricted to a single paragraph of content, since the GFM table\n * syntax cannot represent multiple blocks within a cell (which also means that tables require\n * a `paragraph` node in the editor schema). Additionally, tables are always disabled in\n * singleline documents, since they serialize to multiple lines of Markdown.\n */\n table: Partial<RichTextTableOptions> | false\n\n /**\n * Set to `false` to disable the `Text` extension.\n */\n text: false\n\n /**\n * Set to `false` to disable the `Typography` extension.\n */\n typography: false\n}\n\n/**\n * The `RichTextKit` extension is a collection of the minimal required extensions to have a full\n * WYSIWYG text editor working. This extension is based on the official `StarterKit` extension\n * implementation, allowing almost every extension to be customized or disabled.\n */\nconst RichTextKit = Extension.create<RichTextKitOptions>({\n name: 'richTextKit',\n addExtensions() {\n const extensions: Extensions = []\n\n if (this.options.blockquote !== false) {\n extensions.push(\n Blockquote.extend({\n priority: BLOCKQUOTE_EXTENSION_PRIORITY,\n }).configure(this.options?.blockquote),\n )\n }\n\n if (this.options.bold !== false) {\n extensions.push(Bold.configure(this.options?.bold))\n }\n\n if (this.options.bulletList !== false) {\n extensions.push(RichTextBulletList.configure(this.options?.bulletList))\n }\n\n if (this.options.code !== false) {\n extensions.push(\n RichTextCode.configure(this.options?.code),\n\n // Enhances the Code extension capabilities with additional features\n CurvenoteCodemark,\n )\n }\n\n if (this.options.codeBlock !== false) {\n extensions.push(CodeBlock.configure(this.options?.codeBlock))\n }\n\n if (this.options.document !== false) {\n extensions.push(\n RichTextDocument.configure(this.options?.document),\n\n // Supports copying the underlying Markdown source to the clipboard\n CopyMarkdownSource.configure({\n keyboardShortcut: 'Mod-Shift-c',\n }),\n )\n\n if (this.options?.pasteEmojis !== false) {\n // Supports pasting HTML image emojis as unicode characters\n extensions.push(PasteEmojis)\n }\n\n if (this.options?.pasteMarkdown !== false) {\n // Supports pasting Markdown content as HTML into the editor\n extensions.push(PasteMarkdown)\n }\n\n if (\n this.options?.document?.multiline === false &&\n this.options?.pasteSinglelineText !== false\n ) {\n // Supports pasting multiple lines into a singleline editor, by joining all the\n // pasted lines together\n extensions.push(PasteSinglelineText)\n }\n }\n\n if (this.options.dropCursor !== false) {\n extensions.push(Dropcursor.configure(this.options?.dropCursor))\n }\n\n if (this.options.gapCursor !== false) {\n extensions.push(Gapcursor)\n }\n\n if (this.options.hardBreak !== false) {\n extensions.push(HardBreak.configure(this.options?.hardBreak))\n }\n\n if (this.options.heading !== false) {\n extensions.push(RichTextHeading.configure(this.options?.heading))\n }\n\n if (this.options.history !== false) {\n extensions.push(History.configure(this.options?.history))\n }\n\n if (this.options.horizontalRule !== false) {\n extensions.push(HorizontalRule.configure(this.options?.horizontalRule))\n }\n\n if (this.options.image !== false) {\n extensions.push(RichTextImage.configure(this.options?.image))\n }\n\n if (this.options.italic !== false) {\n extensions.push(Italic.configure(this.options?.italic))\n }\n\n if (this.options.bold !== false && this.options.italic !== false) {\n extensions.push(BoldAndItalics)\n }\n\n if (this.options.link !== false) {\n extensions.push(RichTextLink.configure(this.options?.link))\n }\n\n if (this.options.listItem !== false) {\n extensions.push(ListItem.configure(this.options?.listItem))\n }\n\n if (this.options.listKeymap !== false) {\n extensions.push(ListKeymap.configure(this.options?.listKeymap))\n }\n\n if (this.options.orderedList !== false) {\n extensions.push(RichTextOrderedList.configure(this.options?.orderedList))\n }\n\n if (this.options.paragraph !== false) {\n extensions.push(Paragraph.configure(this.options?.paragraph))\n }\n\n if (this.options.strike !== false) {\n extensions.push(RichTextStrikethrough.configure(this.options?.strike))\n }\n\n // Tables are not supported in singleline documents because, although a table is a single\n // block node (which would technically fit the singleline schema), it serializes to\n // multiple lines of Markdown, breaking the singleline contract\n const isSinglelineDocument =\n this.options.document !== false && this.options.document?.multiline === false\n\n if (this.options.table !== false && !isSinglelineDocument) {\n extensions.push(\n RichTextTable.configure(this.options?.table),\n TableRow,\n\n // Restrict cells to a single paragraph (instead of one or more blocks), since the\n // GFM table syntax cannot represent multiple blocks within a cell, ensuring the\n // editor cannot produce tables that lose content when serialized to Markdown\n TableHeader.extend({ content: 'paragraph' }),\n TableCell.extend({ content: 'paragraph' }),\n )\n }\n\n if (this.options.text !== false) {\n extensions.push(Text)\n }\n\n if (this.options.typography !== false) {\n extensions.push(Typography)\n }\n\n return extensions\n },\n})\n\nexport { RichTextKit }\n\nexport type { RichTextKitOptions }\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2MA,MAAM,cAAc,UAAU,OAA2B;CACrD,MAAM;CACN,gBAAgB;EACZ,MAAM,aAAyB,CAAC;EAEhC,IAAI,KAAK,QAAQ,eAAe,OAC5B,WAAW,KACP,WAAW,OAAO,EACd,UAAA,IACJ,CAAC,CAAC,CAAC,UAAU,KAAK,SAAS,UAAU,CACzC;EAGJ,IAAI,KAAK,QAAQ,SAAS,OACtB,WAAW,KAAK,KAAK,UAAU,KAAK,SAAS,IAAI,CAAC;EAGtD,IAAI,KAAK,QAAQ,eAAe,OAC5B,WAAW,KAAK,mBAAmB,UAAU,KAAK,SAAS,UAAU,CAAC;EAG1E,IAAI,KAAK,QAAQ,SAAS,OACtB,WAAW,KACP,aAAa,UAAU,KAAK,SAAS,IAAI,GAGzC,iBACJ;EAGJ,IAAI,KAAK,QAAQ,cAAc,OAC3B,WAAW,KAAK,UAAU,UAAU,KAAK,SAAS,SAAS,CAAC;EAGhE,IAAI,KAAK,QAAQ,aAAa,OAAO;GACjC,WAAW,KACP,iBAAiB,UAAU,KAAK,SAAS,QAAQ,GAGjD,mBAAmB,UAAU,EACzB,kBAAkB,cACtB,CAAC,CACL;GAEA,IAAI,KAAK,SAAS,gBAAgB,OAE9B,WAAW,KAAK,WAAW;GAG/B,IAAI,KAAK,SAAS,kBAAkB,OAEhC,WAAW,KAAK,aAAa;GAGjC,IACI,KAAK,SAAS,UAAU,cAAc,SACtC,KAAK,SAAS,wBAAwB,OAItC,WAAW,KAAK,mBAAmB;EAE3C;EAEA,IAAI,KAAK,QAAQ,eAAe,OAC5B,WAAW,KAAK,WAAW,UAAU,KAAK,SAAS,UAAU,CAAC;EAGlE,IAAI,KAAK,QAAQ,cAAc,OAC3B,WAAW,KAAK,SAAS;EAG7B,IAAI,KAAK,QAAQ,cAAc,OAC3B,WAAW,KAAK,UAAU,UAAU,KAAK,SAAS,SAAS,CAAC;EAGhE,IAAI,KAAK,QAAQ,YAAY,OACzB,WAAW,KAAK,gBAAgB,UAAU,KAAK,SAAS,OAAO,CAAC;EAGpE,IAAI,KAAK,QAAQ,YAAY,OACzB,WAAW,KAAK,QAAQ,UAAU,KAAK,SAAS,OAAO,CAAC;EAG5D,IAAI,KAAK,QAAQ,mBAAmB,OAChC,WAAW,KAAK,eAAe,UAAU,KAAK,SAAS,cAAc,CAAC;EAG1E,IAAI,KAAK,QAAQ,UAAU,OACvB,WAAW,KAAK,cAAc,UAAU,KAAK,SAAS,KAAK,CAAC;EAGhE,IAAI,KAAK,QAAQ,WAAW,OACxB,WAAW,KAAK,OAAO,UAAU,KAAK,SAAS,MAAM,CAAC;EAG1D,IAAI,KAAK,QAAQ,SAAS,SAAS,KAAK,QAAQ,WAAW,OACvD,WAAW,KAAK,cAAc;EAGlC,IAAI,KAAK,QAAQ,SAAS,OACtB,WAAW,KAAK,aAAa,UAAU,KAAK,SAAS,IAAI,CAAC;EAG9D,IAAI,KAAK,QAAQ,aAAa,OAC1B,WAAW,KAAK,SAAS,UAAU,KAAK,SAAS,QAAQ,CAAC;EAG9D,IAAI,KAAK,QAAQ,eAAe,OAC5B,WAAW,KAAK,WAAW,UAAU,KAAK,SAAS,UAAU,CAAC;EAGlE,IAAI,KAAK,QAAQ,gBAAgB,OAC7B,WAAW,KAAK,oBAAoB,UAAU,KAAK,SAAS,WAAW,CAAC;EAG5E,IAAI,KAAK,QAAQ,cAAc,OAC3B,WAAW,KAAK,UAAU,UAAU,KAAK,SAAS,SAAS,CAAC;EAGhE,IAAI,KAAK,QAAQ,WAAW,OACxB,WAAW,KAAK,sBAAsB,UAAU,KAAK,SAAS,MAAM,CAAC;EAMzE,MAAM,uBACF,KAAK,QAAQ,aAAa,SAAS,KAAK,QAAQ,UAAU,cAAc;EAE5E,IAAI,KAAK,QAAQ,UAAU,SAAS,CAAC,sBACjC,WAAW,KACP,cAAc,UAAU,KAAK,SAAS,KAAK,GAC3C,UAKA,YAAY,OAAO,EAAE,SAAS,YAAY,CAAC,GAC3C,UAAU,OAAO,EAAE,SAAS,YAAY,CAAC,CAC7C;EAGJ,IAAI,KAAK,QAAQ,SAAS,OACtB,WAAW,KAAK,IAAI;EAGxB,IAAI,KAAK,QAAQ,eAAe,OAC5B,WAAW,KAAK,UAAU;EAG9B,OAAO;CACX;AACJ,CAAC"}
|
|
1
|
+
{"version":3,"file":"rich-text-kit.js","names":[],"sources":["../../../src/extensions/rich-text/rich-text-kit.ts"],"sourcesContent":["import { Extension } from '@tiptap/core'\nimport { Blockquote } from '@tiptap/extension-blockquote'\nimport { Bold } from '@tiptap/extension-bold'\nimport { CodeBlock } from '@tiptap/extension-code-block'\nimport { Dropcursor } from '@tiptap/extension-dropcursor'\nimport { Gapcursor } from '@tiptap/extension-gapcursor'\nimport { HardBreak } from '@tiptap/extension-hard-break'\nimport { History } from '@tiptap/extension-history'\nimport { HorizontalRule } from '@tiptap/extension-horizontal-rule'\nimport { Italic } from '@tiptap/extension-italic'\nimport { ListItem } from '@tiptap/extension-list-item'\nimport { ListKeymap } from '@tiptap/extension-list-keymap'\nimport { Paragraph } from '@tiptap/extension-paragraph'\nimport { TableCell } from '@tiptap/extension-table-cell'\nimport { TableHeader } from '@tiptap/extension-table-header'\nimport { TableRow } from '@tiptap/extension-table-row'\nimport { Text } from '@tiptap/extension-text'\nimport { Typography } from '@tiptap/extension-typography'\n\nimport { BLOCKQUOTE_EXTENSION_PRIORITY } from '../../constants/extension-priorities'\nimport { CopyMarkdownSource } from '../shared/copy-markdown-source'\nimport { PasteHTMLTableAsString } from '../shared/paste-html-table-as-string'\nimport { PasteSinglelineText } from '../shared/paste-singleline-text'\n\nimport { BoldAndItalics } from './bold-and-italics'\nimport { CurvenoteCodemark } from './curvenote-codemark'\nimport { PasteEmojis } from './paste-emojis'\nimport { PasteMarkdown } from './paste-markdown'\nimport { RichTextBulletList } from './rich-text-bullet-list'\nimport { RichTextCode } from './rich-text-code'\nimport { RichTextDocument } from './rich-text-document'\nimport { RichTextHeading, RichTextHeadingOptions } from './rich-text-heading'\nimport { RichTextImage } from './rich-text-image'\nimport { RichTextLink } from './rich-text-link'\nimport { RichTextOrderedList } from './rich-text-ordered-list'\nimport { RichTextStrikethrough } from './rich-text-strikethrough'\nimport { RichTextTable } from './rich-text-table'\n\nimport type { Extensions } from '@tiptap/core'\nimport type { BlockquoteOptions } from '@tiptap/extension-blockquote'\nimport type { BoldOptions } from '@tiptap/extension-bold'\nimport type { CodeOptions } from '@tiptap/extension-code'\nimport type { CodeBlockOptions } from '@tiptap/extension-code-block'\nimport type { DropcursorOptions } from '@tiptap/extension-dropcursor'\nimport type { HardBreakOptions } from '@tiptap/extension-hard-break'\nimport type { HistoryOptions } from '@tiptap/extension-history'\nimport type { HorizontalRuleOptions } from '@tiptap/extension-horizontal-rule'\nimport type { ItalicOptions } from '@tiptap/extension-italic'\nimport type { ListItemOptions } from '@tiptap/extension-list-item'\nimport type { ListKeymapOptions } from '@tiptap/extension-list-keymap'\nimport type { ParagraphOptions } from '@tiptap/extension-paragraph'\nimport type { RichTextBulletListOptions } from './rich-text-bullet-list'\nimport type { RichTextDocumentOptions } from './rich-text-document'\nimport type { RichTextImageOptions } from './rich-text-image'\nimport type { RichTextLinkOptions } from './rich-text-link'\nimport type { RichTextOrderedListOptions } from './rich-text-ordered-list'\nimport type { RichTextStrikethroughOptions } from './rich-text-strikethrough'\nimport type { RichTextTableOptions } from './rich-text-table'\n\n/**\n * The options available to customize the `RichTextKit` extension.\n */\ntype RichTextKitOptions = {\n /**\n * Set options for the `Blockquote` extension, or `false` to disable.\n */\n blockquote: Partial<BlockquoteOptions> | false\n\n /**\n * Set options for the `Bold` extension, or `false` to disable.\n */\n bold: Partial<BoldOptions> | false\n\n /**\n * Set options for the `BulletList` extension, or `false` to disable.\n */\n bulletList: Partial<RichTextBulletListOptions> | false\n\n /**\n * Set options for the `Code` extension, or `false` to disable.\n */\n code: Partial<CodeOptions> | false\n\n /**\n * Set options for the `CodeBlock` extension, or `false` to disable.\n */\n codeBlock: Partial<CodeBlockOptions> | false\n\n /**\n * Set options for the `Document` extension, or `false` to disable.\n */\n document: Partial<RichTextDocumentOptions> | false\n\n /**\n * Set options for the `Dropcursor` extension, or `false` to disable.\n */\n dropCursor: Partial<DropcursorOptions> | false\n\n /**\n * Set to `false` to disable the `Gapcursor` extension.\n */\n gapCursor: false\n\n /**\n * Set options for the `HardBreak` extension, or `false` to disable.\n */\n hardBreak: Partial<HardBreakOptions> | false\n\n /**\n * Set options for the `Heading` extension, or `false` to disable.\n */\n heading: Partial<RichTextHeadingOptions> | false\n\n /**\n * Set options for the `History` extension, or `false` to disable.\n */\n history: Partial<HistoryOptions> | false\n\n /**\n * Set options for the `HorizontalRule` extension, or `false` to disable.\n */\n horizontalRule: Partial<HorizontalRuleOptions> | false\n\n /**\n * Set options for the `Image` extension, or `false` to disable.\n */\n image: Partial<RichTextImageOptions> | false\n\n /**\n * Set options for the `Italic` extension, or `false` to disable.\n */\n italic: Partial<ItalicOptions> | false\n\n /**\n * Set options for the `Link` extension, or `false` to disable.\n */\n link: Partial<RichTextLinkOptions> | false\n\n /**\n * Set options for the `ListItem` extension, or `false` to disable.\n */\n listItem: Partial<ListItemOptions> | false\n\n /**\n * Set options for the `ListKeymap` extension, or `false` to disable.\n */\n listKeymap: Partial<ListKeymapOptions> | false\n\n /**\n * Set options for the `OrderedList` extension, or `false` to disable.\n */\n orderedList: Partial<RichTextOrderedListOptions> | false\n\n /**\n * Set options for the `Paragraph` extension, or `false` to disable.\n */\n paragraph: Partial<ParagraphOptions> | false\n\n /**\n * Set to `false` to disable the `PasteEmojis` extension.\n */\n pasteEmojis: false\n\n /**\n * Set to `false` to disable the `PasteHTMLTableAsString` extension, which converts pasted HTML\n * tables (e.g. from spreadsheets or GitHub Flavored Markdown) into space-separated paragraphs.\n *\n * This extension is only registered when native table support is unavailable (i.e. `table` is\n * `false`, or the document is singleline). When tables are enabled, pasted tables become native\n * tables instead, so this option has no effect.\n */\n pasteHTMLTableAsString: false\n\n /**\n * Set to `false` to disable the `PasteMarkdown` extension.\n */\n pasteMarkdown: false\n\n /**\n * Set to `false` to disable the `PasteSinglelineText` extension.\n */\n pasteSinglelineText: false\n\n /**\n * Set options for the `Strike` extension, or `false` to disable.\n */\n strike: Partial<RichTextStrikethroughOptions> | false\n\n /**\n * Set options for the `Table` extension, or `false` to disable.\n *\n * Note that table cells are restricted to a single paragraph of content, since the GFM table\n * syntax cannot represent multiple blocks within a cell (which also means that tables require\n * a `paragraph` node in the editor schema). Additionally, tables are always disabled in\n * singleline documents, since they serialize to multiple lines of Markdown.\n */\n table: Partial<RichTextTableOptions> | false\n\n /**\n * Set to `false` to disable the `Text` extension.\n */\n text: false\n\n /**\n * Set to `false` to disable the `Typography` extension.\n */\n typography: false\n}\n\n/**\n * The `RichTextKit` extension is a collection of the minimal required extensions to have a full\n * WYSIWYG text editor working. This extension is based on the official `StarterKit` extension\n * implementation, allowing almost every extension to be customized or disabled.\n */\nconst RichTextKit = Extension.create<RichTextKitOptions>({\n name: 'richTextKit',\n addExtensions() {\n const extensions: Extensions = []\n\n if (this.options.blockquote !== false) {\n extensions.push(\n Blockquote.extend({\n priority: BLOCKQUOTE_EXTENSION_PRIORITY,\n }).configure(this.options?.blockquote),\n )\n }\n\n if (this.options.bold !== false) {\n extensions.push(Bold.configure(this.options?.bold))\n }\n\n if (this.options.bulletList !== false) {\n extensions.push(RichTextBulletList.configure(this.options?.bulletList))\n }\n\n if (this.options.code !== false) {\n extensions.push(\n RichTextCode.configure(this.options?.code),\n\n // Enhances the Code extension capabilities with additional features\n CurvenoteCodemark,\n )\n }\n\n if (this.options.codeBlock !== false) {\n extensions.push(CodeBlock.configure(this.options?.codeBlock))\n }\n\n if (this.options.document !== false) {\n extensions.push(\n RichTextDocument.configure(this.options?.document),\n\n // Supports copying the underlying Markdown source to the clipboard\n CopyMarkdownSource.configure({\n keyboardShortcut: 'Mod-Shift-c',\n }),\n )\n\n if (this.options?.pasteEmojis !== false) {\n // Supports pasting HTML image emojis as unicode characters\n extensions.push(PasteEmojis)\n }\n\n if (this.options?.pasteMarkdown !== false) {\n // Supports pasting Markdown content as HTML into the editor\n extensions.push(PasteMarkdown)\n }\n\n if (\n this.options?.document?.multiline === false &&\n this.options?.pasteSinglelineText !== false\n ) {\n // Supports pasting multiple lines into a singleline editor, by joining all the\n // pasted lines together\n extensions.push(PasteSinglelineText)\n }\n }\n\n if (this.options.dropCursor !== false) {\n extensions.push(Dropcursor.configure(this.options?.dropCursor))\n }\n\n if (this.options.gapCursor !== false) {\n extensions.push(Gapcursor)\n }\n\n if (this.options.hardBreak !== false) {\n extensions.push(HardBreak.configure(this.options?.hardBreak))\n }\n\n if (this.options.heading !== false) {\n extensions.push(RichTextHeading.configure(this.options?.heading))\n }\n\n if (this.options.history !== false) {\n extensions.push(History.configure(this.options?.history))\n }\n\n if (this.options.horizontalRule !== false) {\n extensions.push(HorizontalRule.configure(this.options?.horizontalRule))\n }\n\n if (this.options.image !== false) {\n extensions.push(RichTextImage.configure(this.options?.image))\n }\n\n if (this.options.italic !== false) {\n extensions.push(Italic.configure(this.options?.italic))\n }\n\n if (this.options.bold !== false && this.options.italic !== false) {\n extensions.push(BoldAndItalics)\n }\n\n if (this.options.link !== false) {\n extensions.push(RichTextLink.configure(this.options?.link))\n }\n\n if (this.options.listItem !== false) {\n extensions.push(ListItem.configure(this.options?.listItem))\n }\n\n if (this.options.listKeymap !== false) {\n extensions.push(ListKeymap.configure(this.options?.listKeymap))\n }\n\n if (this.options.orderedList !== false) {\n extensions.push(RichTextOrderedList.configure(this.options?.orderedList))\n }\n\n if (this.options.paragraph !== false) {\n extensions.push(Paragraph.configure(this.options?.paragraph))\n }\n\n if (this.options.strike !== false) {\n extensions.push(RichTextStrikethrough.configure(this.options?.strike))\n }\n\n // Tables are not supported in singleline documents because, although a table is a single\n // block node (which would technically fit the singleline schema), it serializes to\n // multiple lines of Markdown, breaking the singleline contract\n const isSinglelineDocument =\n this.options.document !== false && this.options.document?.multiline === false\n\n if (this.options.table !== false && !isSinglelineDocument) {\n extensions.push(\n RichTextTable.configure(this.options?.table),\n TableRow,\n\n // Restrict cells to a single paragraph (instead of one or more blocks), since the\n // GFM table syntax cannot represent multiple blocks within a cell, ensuring the\n // editor cannot produce tables that lose content when serialized to Markdown\n TableHeader.extend({ content: 'paragraph' }),\n TableCell.extend({ content: 'paragraph' }),\n )\n } else if (this.options.pasteHTMLTableAsString !== false) {\n // When native tables aren't available (disabled, or a singleline document), gracefully\n // degrade pasted HTML tables (from spreadsheets or GFM) into space-separated paragraphs\n // instead of letting the cell contents run together. This mirrors the `PlainTextKit`,\n // which always registers this extension since it never supports native tables\n extensions.push(PasteHTMLTableAsString)\n }\n\n if (this.options.text !== false) {\n extensions.push(Text)\n }\n\n if (this.options.typography !== false) {\n extensions.push(Typography)\n }\n\n return extensions\n },\n})\n\nexport { RichTextKit }\n\nexport type { RichTextKitOptions }\n"],"mappings":"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"}
|
|
@@ -7,6 +7,7 @@ import { Plugin, PluginKey } from "@tiptap/pm/state";
|
|
|
7
7
|
* Transforms pasted HTML by converting tables to paragraphs while preserving surrounding content.
|
|
8
8
|
*/
|
|
9
9
|
function transformPastedHTML(html) {
|
|
10
|
+
if (!/<table[\s>]/i.test(html)) return html;
|
|
10
11
|
const body = parseHtmlToElement(html);
|
|
11
12
|
const tables = body.querySelectorAll("table");
|
|
12
13
|
if (tables.length === 0) return html;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paste-html-table-as-string.js","names":[],"sources":["../../../src/extensions/shared/paste-html-table-as-string.ts"],"sourcesContent":["import { Extension } from '@tiptap/core'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\n\nimport { PASTE_HTML_TABLE_AS_STRING_EXTENSION_PRIORITY } from '../../constants/extension-priorities'\nimport { parseHtmlToElement } from '../../helpers/dom'\n\n/**\n * Transforms pasted HTML by converting tables to paragraphs while preserving surrounding content.\n */\nfunction transformPastedHTML(html: string): string {\n const body = parseHtmlToElement(html)\n const tables = body.querySelectorAll('table')\n\n if (tables.length === 0) {\n return html\n }\n\n for (const table of Array.from(tables)) {\n if (!table.rows) {\n continue\n }\n\n // Convert table rows to paragraphs (using innerHTML to preserve formatting)\n const paragraphs = Array.from(table.rows)\n .map((row) =>\n Array.from(row.cells)\n .map((cell) => {\n // Unwrap paragraphs but preserve inline formatting\n const cellParagraphs = cell.querySelectorAll('p')\n\n for (const p of Array.from(cellParagraphs)) {\n p.replaceWith(...Array.from(p.childNodes))\n }\n\n return cell.innerHTML\n })\n .join(' '),\n )\n .filter((row) => row.trim().length > 0)\n .map((row) => {\n const p = document.createElement('p')\n p.innerHTML = row\n return p\n })\n\n table.replaceWith(...paragraphs)\n }\n\n return body.innerHTML\n}\n\n/**\n * The `PasteHTMLTableAsString` extension adds the ability to paste a table copied from a spreadsheet\n * web app (e.g., Google Sheets, Microsoft Excel), along with tables rendered by GitHub Flavored\n * Markdown (GFM), into a plain-text editor.\n *\n * Since plain-text documents cannot represent tables, this extension simply pastes the table as a\n * string of paragraphs (one paragraph per row), with each cell separated by a space character.\n *\n * Lastly, please note that formatting is lost when the copied table comes from Google Sheets or\n * Microsoft Excel, because unfortunately, these apps style the cell contents using CSS.\n */\nconst PasteHTMLTableAsString = Extension.create({\n name: 'pasteHTMLTableAsString',\n priority: PASTE_HTML_TABLE_AS_STRING_EXTENSION_PRIORITY,\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: new PluginKey('pasteHTMLTableAsString'),\n props: {\n transformPastedHTML,\n },\n }),\n ]\n },\n})\n\nexport { PasteHTMLTableAsString, transformPastedHTML }\n"],"mappings":";;;;;;;;AASA,SAAS,oBAAoB,MAAsB;
|
|
1
|
+
{"version":3,"file":"paste-html-table-as-string.js","names":[],"sources":["../../../src/extensions/shared/paste-html-table-as-string.ts"],"sourcesContent":["import { Extension } from '@tiptap/core'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\n\nimport { PASTE_HTML_TABLE_AS_STRING_EXTENSION_PRIORITY } from '../../constants/extension-priorities'\nimport { parseHtmlToElement } from '../../helpers/dom'\n\n/**\n * Transforms pasted HTML by converting tables to paragraphs while preserving surrounding content.\n */\nfunction transformPastedHTML(html: string): string {\n // Cheap guard so non-table HTML pastes skip the full `DOMParser` parse below, which matters now\n // that this extension is also registered for singleline and `table: false` rich-text editors.\n if (!/<table[\\s>]/i.test(html)) {\n return html\n }\n\n const body = parseHtmlToElement(html)\n const tables = body.querySelectorAll('table')\n\n if (tables.length === 0) {\n return html\n }\n\n for (const table of Array.from(tables)) {\n if (!table.rows) {\n continue\n }\n\n // Convert table rows to paragraphs (using innerHTML to preserve formatting)\n const paragraphs = Array.from(table.rows)\n .map((row) =>\n Array.from(row.cells)\n .map((cell) => {\n // Unwrap paragraphs but preserve inline formatting\n const cellParagraphs = cell.querySelectorAll('p')\n\n for (const p of Array.from(cellParagraphs)) {\n p.replaceWith(...Array.from(p.childNodes))\n }\n\n return cell.innerHTML\n })\n .join(' '),\n )\n .filter((row) => row.trim().length > 0)\n .map((row) => {\n const p = document.createElement('p')\n p.innerHTML = row\n return p\n })\n\n table.replaceWith(...paragraphs)\n }\n\n return body.innerHTML\n}\n\n/**\n * The `PasteHTMLTableAsString` extension adds the ability to paste a table copied from a spreadsheet\n * web app (e.g., Google Sheets, Microsoft Excel), along with tables rendered by GitHub Flavored\n * Markdown (GFM), into a plain-text editor.\n *\n * Since plain-text documents cannot represent tables, this extension simply pastes the table as a\n * string of paragraphs (one paragraph per row), with each cell separated by a space character.\n *\n * Lastly, please note that formatting is lost when the copied table comes from Google Sheets or\n * Microsoft Excel, because unfortunately, these apps style the cell contents using CSS.\n */\nconst PasteHTMLTableAsString = Extension.create({\n name: 'pasteHTMLTableAsString',\n priority: PASTE_HTML_TABLE_AS_STRING_EXTENSION_PRIORITY,\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: new PluginKey('pasteHTMLTableAsString'),\n props: {\n transformPastedHTML,\n },\n }),\n ]\n },\n})\n\nexport { PasteHTMLTableAsString, transformPastedHTML }\n"],"mappings":";;;;;;;;AASA,SAAS,oBAAoB,MAAsB;CAG/C,IAAI,CAAC,eAAe,KAAK,IAAI,GACzB,OAAO;CAGX,MAAM,OAAO,mBAAmB,IAAI;CACpC,MAAM,SAAS,KAAK,iBAAiB,OAAO;CAE5C,IAAI,OAAO,WAAW,GAClB,OAAO;CAGX,KAAK,MAAM,SAAS,MAAM,KAAK,MAAM,GAAG;EACpC,IAAI,CAAC,MAAM,MACP;EAIJ,MAAM,aAAa,MAAM,KAAK,MAAM,IAAI,CAAC,CACpC,KAAK,QACF,MAAM,KAAK,IAAI,KAAK,CAAC,CAChB,KAAK,SAAS;GAEX,MAAM,iBAAiB,KAAK,iBAAiB,GAAG;GAEhD,KAAK,MAAM,KAAK,MAAM,KAAK,cAAc,GACrC,EAAE,YAAY,GAAG,MAAM,KAAK,EAAE,UAAU,CAAC;GAG7C,OAAO,KAAK;EAChB,CAAC,CAAC,CACD,KAAK,GAAG,CACjB,CAAC,CACA,QAAQ,QAAQ,IAAI,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC,CACtC,KAAK,QAAQ;GACV,MAAM,IAAI,SAAS,cAAc,GAAG;GACpC,EAAE,YAAY;GACd,OAAO;EACX,CAAC;EAEL,MAAM,YAAY,GAAG,UAAU;CACnC;CAEA,OAAO,KAAK;AAChB;;;;;;;;;;;;AAaA,MAAM,yBAAyB,UAAU,OAAO;CAC5C,MAAM;CACN,UAAU;CACV,wBAAwB;EACpB,OAAO,CACH,IAAI,OAAO;GACP,KAAK,IAAI,UAAU,wBAAwB;GAC3C,OAAO,EACH,oBACJ;EACJ,CAAC,CACL;CACJ;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@doist/typist",
|
|
3
3
|
"description": "The mighty Tiptap-based rich-text editor React component that powers Doist products.",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "15.0.0-next.1",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://typist.doist.dev/",
|
|
7
7
|
"repository": {
|
|
@@ -121,6 +121,7 @@
|
|
|
121
121
|
"@testing-library/dom": "10.4.1",
|
|
122
122
|
"@testing-library/jest-dom": "6.9.1",
|
|
123
123
|
"@testing-library/react": "16.3.2",
|
|
124
|
+
"@testing-library/user-event": "14.6.1",
|
|
124
125
|
"@types/hast": "3.0.4",
|
|
125
126
|
"@types/lodash-es": "4.17.12",
|
|
126
127
|
"@types/react": "18.3.31",
|
package/dist/hooks/use-editor.js
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useState } from "react";
|
|
2
|
-
import { Editor } from "@tiptap/react";
|
|
3
|
-
//#region src/hooks/use-editor.ts
|
|
4
|
-
function stateChanger(state) {
|
|
5
|
-
return (state + 1) % Number.MAX_SAFE_INTEGER;
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* This is a copy of the `useRerender` hook from `@react-hookz/web`, which is a utility hook that
|
|
9
|
-
* returns a function that can be called to force a re-render of the component.
|
|
10
|
-
*
|
|
11
|
-
* Turns out we don't have the need to use any of the other hooks from `@react-hookz/web`, which is
|
|
12
|
-
* a peer dependency that often introduces breaking changes, causing upgrade issues across our
|
|
13
|
-
* projects.
|
|
14
|
-
*/
|
|
15
|
-
function useRerender() {
|
|
16
|
-
const [, setState] = useState(0);
|
|
17
|
-
return useCallback(() => {
|
|
18
|
-
setState(stateChanger);
|
|
19
|
-
}, []);
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* This is a fork of the official `useEditor` hook with one key difference, which is to prevent a
|
|
23
|
-
* `null` Editor object instance from being returned on the first render.
|
|
24
|
-
*
|
|
25
|
-
* This change was once fixed in the `@tiptap/react` package, but was reverted because it didn't
|
|
26
|
-
* have support for server-side rendering ([ref](https://github.com/ueberdosis/tiptap/pull/2282)),
|
|
27
|
-
* which is a problem we don't currently have.
|
|
28
|
-
*
|
|
29
|
-
* @param options The options to configure the editor component with.
|
|
30
|
-
* @param dependencies If present, re-create the editor instance if the values in the list change.
|
|
31
|
-
*
|
|
32
|
-
* @returns A new editor instance with the given options.
|
|
33
|
-
*/
|
|
34
|
-
function useEditor(options = {}, dependencies = []) {
|
|
35
|
-
const [editor, setEditor] = useState(() => new Editor(options));
|
|
36
|
-
const forceRerender = useRerender();
|
|
37
|
-
useEffect(function initializeEditorInstance() {
|
|
38
|
-
let instance;
|
|
39
|
-
if (editor.isDestroyed) {
|
|
40
|
-
instance = new Editor(options);
|
|
41
|
-
setEditor(instance);
|
|
42
|
-
} else instance = editor;
|
|
43
|
-
instance.on("transaction", () => {
|
|
44
|
-
requestAnimationFrame(() => {
|
|
45
|
-
requestAnimationFrame(() => {
|
|
46
|
-
if (!instance.isDestroyed) forceRerender();
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
return function destroyEditorInstance() {
|
|
51
|
-
instance.destroy();
|
|
52
|
-
};
|
|
53
|
-
}, dependencies);
|
|
54
|
-
return editor;
|
|
55
|
-
}
|
|
56
|
-
//#endregion
|
|
57
|
-
export { useEditor };
|
|
58
|
-
|
|
59
|
-
//# sourceMappingURL=use-editor.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"use-editor.js","names":[],"sources":["../../src/hooks/use-editor.ts"],"sourcesContent":["import { DependencyList, useCallback, useEffect, useState } from 'react'\n\nimport { Editor } from '@tiptap/react'\n\nimport type { EditorOptions } from '@tiptap/core'\n\nfunction stateChanger(state: number) {\n return (state + 1) % Number.MAX_SAFE_INTEGER\n}\n\n/**\n * This is a copy of the `useRerender` hook from `@react-hookz/web`, which is a utility hook that\n * returns a function that can be called to force a re-render of the component.\n *\n * Turns out we don't have the need to use any of the other hooks from `@react-hookz/web`, which is\n * a peer dependency that often introduces breaking changes, causing upgrade issues across our\n * projects.\n */\nfunction useRerender(): () => void {\n const [, setState] = useState(0)\n\n return useCallback(() => {\n setState(stateChanger)\n }, [])\n}\n\n/**\n * This is a fork of the official `useEditor` hook with one key difference, which is to prevent a\n * `null` Editor object instance from being returned on the first render.\n *\n * This change was once fixed in the `@tiptap/react` package, but was reverted because it didn't\n * have support for server-side rendering ([ref](https://github.com/ueberdosis/tiptap/pull/2282)),\n * which is a problem we don't currently have.\n *\n * @param options The options to configure the editor component with.\n * @param dependencies If present, re-create the editor instance if the values in the list change.\n *\n * @returns A new editor instance with the given options.\n */\nfunction useEditor(\n options: Partial<EditorOptions> = {},\n dependencies: DependencyList = [],\n): Editor {\n const [editor, setEditor] = useState<Editor>(() => new Editor(options))\n\n const forceRerender = useRerender()\n\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n useEffect(\n function initializeEditorInstance() {\n let instance: Editor\n\n if (editor.isDestroyed) {\n instance = new Editor(options)\n setEditor(instance)\n } else {\n instance = editor\n }\n\n instance.on('transaction', () => {\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n if (!instance.isDestroyed) {\n forceRerender()\n }\n })\n })\n })\n\n return function destroyEditorInstance() {\n instance.destroy()\n }\n },\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n dependencies,\n )\n\n return editor\n}\n\nexport { useEditor }\n"],"mappings":";;;AAMA,SAAS,aAAa,OAAe;CACjC,QAAQ,QAAQ,KAAK,OAAO;AAChC;;;;;;;;;AAUA,SAAS,cAA0B;CAC/B,MAAM,GAAG,YAAY,SAAS,CAAC;CAE/B,OAAO,kBAAkB;EACrB,SAAS,YAAY;CACzB,GAAG,CAAC,CAAC;AACT;;;;;;;;;;;;;;AAeA,SAAS,UACL,UAAkC,CAAC,GACnC,eAA+B,CAAC,GAC1B;CACN,MAAM,CAAC,QAAQ,aAAa,eAAuB,IAAI,OAAO,OAAO,CAAC;CAEtE,MAAM,gBAAgB,YAAY;CAGlC,UACI,SAAS,2BAA2B;EAChC,IAAI;EAEJ,IAAI,OAAO,aAAa;GACpB,WAAW,IAAI,OAAO,OAAO;GAC7B,UAAU,QAAQ;EACtB,OACI,WAAW;EAGf,SAAS,GAAG,qBAAqB;GAC7B,4BAA4B;IACxB,4BAA4B;KACxB,IAAI,CAAC,SAAS,aACV,cAAc;IAEtB,CAAC;GACL,CAAC;EACL,CAAC;EAED,OAAO,SAAS,wBAAwB;GACpC,SAAS,QAAQ;EACrB;CACJ,GAEA,YACJ;CAEA,OAAO;AACX"}
|