React context implementation for Tiptap (#4192)

* feat(react): add react context implementation

* chore(docs): updated react docs & demos for new context

* chore(docs): added slot docs

* chore(docs): fix typo

* chore(react): use correct editor package

* fix typo in react installation docs

* update react typings to latest version

* fix types

---------

Co-authored-by: bdbch <dominik@bdbch.com>
This commit is contained in:
bdbch 2023-07-11 18:20:43 +02:00 committed by GitHub
parent e661bbbbc9
commit d689e2d9c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 252 additions and 139 deletions

View File

@ -3,11 +3,13 @@ import './styles.scss'
import { Color } from '@tiptap/extension-color'
import ListItem from '@tiptap/extension-list-item'
import TextStyle from '@tiptap/extension-text-style'
import { EditorContent, useEditor } from '@tiptap/react'
import { EditorProvider, useCurrentEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import React from 'react'
const MenuBar = ({ editor }) => {
const MenuBar = () => {
const { editor } = useCurrentEditor()
if (!editor) {
return null
}
@ -178,58 +180,54 @@ const MenuBar = ({ editor }) => {
)
}
export default () => {
const editor = useEditor({
extensions: [
Color.configure({ types: [TextStyle.name, ListItem.name] }),
TextStyle.configure({ types: [ListItem.name] }),
StarterKit.configure({
bulletList: {
keepMarks: true,
keepAttributes: false, // TODO : Making this as `false` becase marks are not preserved when I try to preserve attrs, awaiting a bit of help
},
orderedList: {
keepMarks: true,
keepAttributes: false, // TODO : Making this as `false` becase marks are not preserved when I try to preserve attrs, awaiting a bit of help
},
}),
],
content: `
<h2>
Hi there,
</h2>
<p>
this is a <em>basic</em> example of <strong>tiptap</strong>. Sure, there are all kind of basic text styles youd probably expect from a text editor. But wait until you see the lists:
</p>
<ul>
<li>
Thats a bullet list with one
</li>
<li>
or two list items.
</li>
</ul>
<p>
Isnt that great? And all of that is editable. But wait, theres more. Lets try a code block:
</p>
<pre><code class="language-css">body {
display: none;
}</code></pre>
<p>
I know, I know, this is impressive. Its only the tip of the iceberg though. Give it a try and click a little bit around. Dont forget to check the other examples too.
</p>
<blockquote>
Wow, thats amazing. Good work, boy! 👏
<br />
Mom
</blockquote>
`,
})
const extensions = [
Color.configure({ types: [TextStyle.name, ListItem.name] }),
TextStyle.configure({ types: [ListItem.name] }),
StarterKit.configure({
bulletList: {
keepMarks: true,
keepAttributes: false, // TODO : Making this as `false` becase marks are not preserved when I try to preserve attrs, awaiting a bit of help
},
orderedList: {
keepMarks: true,
keepAttributes: false, // TODO : Making this as `false` becase marks are not preserved when I try to preserve attrs, awaiting a bit of help
},
}),
]
const content = `
<h2>
Hi there,
</h2>
<p>
this is a <em>basic</em> example of <strong>tiptap</strong>. Sure, there are all kind of basic text styles youd probably expect from a text editor. But wait until you see the lists:
</p>
<ul>
<li>
Thats a bullet list with one
</li>
<li>
or two list items.
</li>
</ul>
<p>
Isnt that great? And all of that is editable. But wait, theres more. Lets try a code block:
</p>
<pre><code class="language-css">body {
display: none;
}</code></pre>
<p>
I know, I know, this is impressive. Its only the tip of the iceberg though. Give it a try and click a little bit around. Dont forget to check the other examples too.
</p>
<blockquote>
Wow, thats amazing. Good work, boy! 👏
<br />
Mom
</blockquote>
`
export default () => {
return (
<div>
<MenuBar editor={editor} />
<EditorContent editor={editor} />
</div>
<EditorProvider slotBefore={<MenuBar />} extensions={extensions} content={content}></EditorProvider>
)
}

View File

@ -47,19 +47,54 @@ To actually start using Tiptap we need to create a new component. Lets call i
```jsx
// src/Tiptap.jsx
import { useEditor, EditorContent } from '@tiptap/react'
import { EditorProvider, FloatingMenu, BubbleMenu } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
// define your extension array
const extensions = [
StarterKit,
]
const content = '<p>Hello World!</p>'
const Tiptap = () => {
return (
<EditorProvider extensions={extensions} content={content}>
<FloatingMenu>This is the floating menu</FloatingMenu>
<BubbleMenu>This is the bubble menu</BubbleMenu>
</EditorProvider>
)
}
export default Tiptap
```
**Important Note**: You can always use the `useEditor` hook if you want to avoid using the Editor context.
```jsx
// src/Tiptap.jsx
import { useEditor, EditorContent FloatingMenu, BubbleMenu } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
// define your extension array
const extensions = [
StarterKit,
]
const content = '<p>Hello World!</p>'
const Tiptap = () => {
const editor = useEditor({
extensions: [
StarterKit,
],
content: '<p>Hello World!</p>',
extensions,
content,
})
return (
<EditorContent editor={editor} />
<>
<EditorContent editor={editor} />
<FloatingMenu editor={editor}>This is the floating menu</FloatingMenu>
<BubbleMenu editor={editor}>This is the bubble menu</BubbleMenu>
</>
)
}
@ -83,9 +118,41 @@ const App = () => {
export default App
```
#### 5. Consume the Editor context in child components
If you use the `EditorProvider` to setup your Tiptap editor, you can now easily access your editor instance from any child component using the `useCurrentEditor` hook.
```jsx
import { useCurrentEditor } from '@tiptap/react'
const EditorJSONPreview = () => {
const { editor } = useCurrentEditor()
return (
<pre>
{JSON.stringify(editor.getJSON(), null, 2)}
</pre>
)
}
```
**Important**: This won't work if you use the `useEditor` hook to setup your editor.
You should now see a pretty barebones example of Tiptap in your browser.
#### 5. The complete setup (optional)
#### 6. Add before or after slots
Since the EditorContent component is rendered by the `EditorProvider` component, we now can't directly define where to render before or after content of our editor. For that we can use the `slotBefore` & `slotAfter` props on the `EditorProvider` component.
```jsx
<EditorProvider
extensions={extensions}
content={content}
slotBefore={<MyEditorToolbar />}
slotAfter={<MyEditorFooter />}
/>
```
#### 7. The complete setup (optional)
Ready to add more? Below is a demo that shows how you could set up a basic toolbar. Feel free to take it and start customizing it to your needs:
https://embed.tiptap.dev/preview/Examples/Default

90
package-lock.json generated
View File

@ -23,6 +23,8 @@
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-commonjs": "^21.0.1",
"@rollup/plugin-node-resolve": "^13.1.3",
"@types/react": "^18.2.14",
"@types/react-dom": "^18.2.6",
"@typescript-eslint/eslint-plugin": "^5.10.2",
"@typescript-eslint/parser": "^5.10.2",
"babel-loader": "^8.2.3",
@ -509,7 +511,6 @@
},
"node_modules/@babel/parser": {
"version": "7.18.5",
"dev": true,
"license": "MIT",
"bin": {
"parser": "bin/babel-parser.js"
@ -5047,9 +5048,10 @@
"license": "MIT"
},
"node_modules/@types/react": {
"version": "18.0.14",
"version": "18.2.14",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.14.tgz",
"integrity": "sha512-A0zjq+QN/O0Kpe30hA1GidzyFjatVvrpIvWLxD+xv67Vt91TWWgco9IvrJBkeyHm1trGaFS/FSGqPlhyeZRm0g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/prop-types": "*",
"@types/scheduler": "*",
@ -5057,9 +5059,10 @@
}
},
"node_modules/@types/react-dom": {
"version": "18.0.5",
"version": "18.2.6",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.6.tgz",
"integrity": "sha512-2et4PDvg6PVCyS7fuTc4gPoksV58bW0RwSxWKcPRcHZf0PRUGq03TKcD/rUHe3azfV6/5/biUBJw+HhCQjaP0A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/react": "*"
}
@ -5357,7 +5360,6 @@
},
"node_modules/@vue/compiler-core": {
"version": "3.2.37",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.16.4",
@ -5368,7 +5370,6 @@
},
"node_modules/@vue/compiler-dom": {
"version": "3.2.37",
"dev": true,
"license": "MIT",
"dependencies": {
"@vue/compiler-core": "3.2.37",
@ -5377,7 +5378,6 @@
},
"node_modules/@vue/compiler-sfc": {
"version": "3.2.37",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.16.4",
@ -5394,7 +5394,6 @@
},
"node_modules/@vue/compiler-ssr": {
"version": "3.2.37",
"dev": true,
"license": "MIT",
"dependencies": {
"@vue/compiler-dom": "3.2.37",
@ -5408,7 +5407,6 @@
},
"node_modules/@vue/reactivity": {
"version": "3.2.37",
"dev": true,
"license": "MIT",
"dependencies": {
"@vue/shared": "3.2.37"
@ -5416,7 +5414,6 @@
},
"node_modules/@vue/reactivity-transform": {
"version": "3.2.37",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.16.4",
@ -5428,7 +5425,6 @@
},
"node_modules/@vue/runtime-core": {
"version": "3.2.37",
"dev": true,
"license": "MIT",
"dependencies": {
"@vue/reactivity": "3.2.37",
@ -5437,7 +5433,6 @@
},
"node_modules/@vue/runtime-dom": {
"version": "3.2.37",
"dev": true,
"license": "MIT",
"dependencies": {
"@vue/runtime-core": "3.2.37",
@ -5447,12 +5442,10 @@
},
"node_modules/@vue/runtime-dom/node_modules/csstype": {
"version": "2.6.20",
"dev": true,
"license": "MIT"
},
"node_modules/@vue/server-renderer": {
"version": "3.2.37",
"dev": true,
"license": "MIT",
"dependencies": {
"@vue/compiler-ssr": "3.2.37",
@ -5464,7 +5457,6 @@
},
"node_modules/@vue/shared": {
"version": "3.2.37",
"dev": true,
"license": "MIT"
},
"node_modules/@webassemblyjs/ast": {
@ -8954,7 +8946,6 @@
},
"node_modules/estree-walker": {
"version": "2.0.2",
"dev": true,
"license": "MIT"
},
"node_modules/esutils": {
@ -12708,7 +12699,6 @@
},
"node_modules/magic-string": {
"version": "0.25.9",
"dev": true,
"license": "MIT",
"dependencies": {
"sourcemap-codec": "^1.4.8"
@ -13498,7 +13488,6 @@
},
"node_modules/nanoid": {
"version": "3.3.4",
"dev": true,
"license": "MIT",
"bin": {
"nanoid": "bin/nanoid.cjs"
@ -15363,7 +15352,6 @@
},
"node_modules/picocolors": {
"version": "1.0.0",
"dev": true,
"license": "ISC"
},
"node_modules/picomatch": {
@ -15472,7 +15460,6 @@
},
"node_modules/postcss": {
"version": "8.4.14",
"dev": true,
"funding": [
{
"type": "opencollective",
@ -17091,7 +17078,6 @@
},
"node_modules/source-map": {
"version": "0.6.1",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
@ -17099,7 +17085,6 @@
},
"node_modules/source-map-js": {
"version": "1.0.2",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
@ -17127,7 +17112,6 @@
},
"node_modules/sourcemap-codec": {
"version": "1.4.8",
"dev": true,
"license": "MIT"
},
"node_modules/spdx-correct": {
@ -18875,7 +18859,6 @@
},
"node_modules/vue": {
"version": "3.2.37",
"dev": true,
"license": "MIT",
"dependencies": {
"@vue/compiler-dom": "3.2.37",
@ -20308,8 +20291,8 @@
"devDependencies": {
"@tiptap/core": "^2.1.0-rc.11",
"@tiptap/pm": "^2.1.0-rc.11",
"@types/react": "^18.0.1",
"@types/react-dom": "^18.0.0",
"@types/react": "^18.2.14",
"@types/react-dom": "^18.2.6",
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
@ -20709,8 +20692,7 @@
}
},
"@babel/parser": {
"version": "7.18.5",
"dev": true
"version": "7.18.5"
},
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
"version": "7.17.12",
@ -23980,8 +23962,8 @@
"@tiptap/extension-bubble-menu": "^2.1.0-rc.11",
"@tiptap/extension-floating-menu": "^2.1.0-rc.11",
"@tiptap/pm": "^2.1.0-rc.11",
"@types/react": "^18.0.1",
"@types/react-dom": "^18.0.0",
"@types/react": "^18.2.14",
"@types/react-dom": "^18.2.6",
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
@ -24025,7 +24007,7 @@
"@tiptap/extension-floating-menu": "^2.1.0-rc.11",
"@tiptap/pm": "^2.1.0-rc.11",
"vue": "^2.6.0",
"vue-ts-types": "*"
"vue-ts-types": "^1.6.0"
},
"dependencies": {
"vue": {
@ -24150,7 +24132,9 @@
"dev": true
},
"@types/react": {
"version": "18.0.14",
"version": "18.2.14",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.14.tgz",
"integrity": "sha512-A0zjq+QN/O0Kpe30hA1GidzyFjatVvrpIvWLxD+xv67Vt91TWWgco9IvrJBkeyHm1trGaFS/FSGqPlhyeZRm0g==",
"dev": true,
"requires": {
"@types/prop-types": "*",
@ -24159,7 +24143,9 @@
}
},
"@types/react-dom": {
"version": "18.0.5",
"version": "18.2.6",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.6.tgz",
"integrity": "sha512-2et4PDvg6PVCyS7fuTc4gPoksV58bW0RwSxWKcPRcHZf0PRUGq03TKcD/rUHe3azfV6/5/biUBJw+HhCQjaP0A==",
"dev": true,
"requires": {
"@types/react": "*"
@ -24332,7 +24318,6 @@
},
"@vue/compiler-core": {
"version": "3.2.37",
"dev": true,
"requires": {
"@babel/parser": "^7.16.4",
"@vue/shared": "3.2.37",
@ -24342,7 +24327,6 @@
},
"@vue/compiler-dom": {
"version": "3.2.37",
"dev": true,
"requires": {
"@vue/compiler-core": "3.2.37",
"@vue/shared": "3.2.37"
@ -24350,7 +24334,6 @@
},
"@vue/compiler-sfc": {
"version": "3.2.37",
"dev": true,
"requires": {
"@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.37",
@ -24366,7 +24349,6 @@
},
"@vue/compiler-ssr": {
"version": "3.2.37",
"dev": true,
"requires": {
"@vue/compiler-dom": "3.2.37",
"@vue/shared": "3.2.37"
@ -24378,14 +24360,12 @@
},
"@vue/reactivity": {
"version": "3.2.37",
"dev": true,
"requires": {
"@vue/shared": "3.2.37"
}
},
"@vue/reactivity-transform": {
"version": "3.2.37",
"dev": true,
"requires": {
"@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.37",
@ -24396,7 +24376,6 @@
},
"@vue/runtime-core": {
"version": "3.2.37",
"dev": true,
"requires": {
"@vue/reactivity": "3.2.37",
"@vue/shared": "3.2.37"
@ -24404,7 +24383,6 @@
},
"@vue/runtime-dom": {
"version": "3.2.37",
"dev": true,
"requires": {
"@vue/runtime-core": "3.2.37",
"@vue/shared": "3.2.37",
@ -24412,22 +24390,19 @@
},
"dependencies": {
"csstype": {
"version": "2.6.20",
"dev": true
"version": "2.6.20"
}
}
},
"@vue/server-renderer": {
"version": "3.2.37",
"dev": true,
"requires": {
"@vue/compiler-ssr": "3.2.37",
"@vue/shared": "3.2.37"
}
},
"@vue/shared": {
"version": "3.2.37",
"dev": true
"version": "3.2.37"
},
"@webassemblyjs/ast": {
"version": "1.11.1",
@ -26790,8 +26765,7 @@
"dev": true
},
"estree-walker": {
"version": "2.0.2",
"dev": true
"version": "2.0.2"
},
"esutils": {
"version": "2.0.3",
@ -29328,7 +29302,6 @@
},
"magic-string": {
"version": "0.25.9",
"dev": true,
"requires": {
"sourcemap-codec": "^1.4.8"
}
@ -29923,8 +29896,7 @@
}
},
"nanoid": {
"version": "3.3.4",
"dev": true
"version": "3.3.4"
},
"natural-compare": {
"version": "1.4.0",
@ -31296,8 +31268,7 @@
"dev": true
},
"picocolors": {
"version": "1.0.0",
"dev": true
"version": "1.0.0"
},
"picomatch": {
"version": "2.3.1"
@ -31358,7 +31329,6 @@
},
"postcss": {
"version": "8.4.14",
"dev": true,
"requires": {
"nanoid": "^3.3.4",
"picocolors": "^1.0.0",
@ -32470,12 +32440,10 @@
}
},
"source-map": {
"version": "0.6.1",
"dev": true
"version": "0.6.1"
},
"source-map-js": {
"version": "1.0.2",
"dev": true
"version": "1.0.2"
},
"source-map-resolve": {
"version": "0.6.0",
@ -32496,8 +32464,7 @@
}
},
"sourcemap-codec": {
"version": "1.4.8",
"dev": true
"version": "1.4.8"
},
"spdx-correct": {
"version": "3.1.1",
@ -33679,7 +33646,6 @@
},
"vue": {
"version": "3.2.37",
"dev": true,
"requires": {
"@vue/compiler-dom": "3.2.37",
"@vue/compiler-sfc": "3.2.37",

View File

@ -50,6 +50,8 @@
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-commonjs": "^21.0.1",
"@rollup/plugin-node-resolve": "^13.1.3",
"@types/react": "^18.2.14",
"@types/react-dom": "^18.2.6",
"@typescript-eslint/eslint-plugin": "^5.10.2",
"@typescript-eslint/parser": "^5.10.2",
"babel-loader": "^8.2.3",

View File

@ -35,8 +35,8 @@
"devDependencies": {
"@tiptap/core": "^2.1.0-rc.11",
"@tiptap/pm": "^2.1.0-rc.11",
"@types/react": "^18.0.1",
"@types/react-dom": "^18.0.0",
"@types/react": "^18.2.14",
"@types/react-dom": "^18.2.6",
"react": "^18.0.0",
"react-dom": "^18.0.0"
},

View File

@ -1,9 +1,11 @@
import { BubbleMenuPlugin, BubbleMenuPluginProps } from '@tiptap/extension-bubble-menu'
import React, { useEffect, useState } from 'react'
import { useCurrentEditor } from './Context.js'
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
export type BubbleMenuProps = Omit<Optional<BubbleMenuPluginProps, 'pluginKey'>, 'element'> & {
export type BubbleMenuProps = Omit<Optional<BubbleMenuPluginProps, 'pluginKey' | 'editor'>, 'element'> & {
className?: string;
children: React.ReactNode;
updateDelay?: number;
@ -11,13 +13,14 @@ export type BubbleMenuProps = Omit<Optional<BubbleMenuPluginProps, 'pluginKey'>,
export const BubbleMenu = (props: BubbleMenuProps) => {
const [element, setElement] = useState<HTMLDivElement | null>(null)
const { editor: currentEditor } = useCurrentEditor()
useEffect(() => {
if (!element) {
return
}
if (props.editor.isDestroyed) {
if (props.editor?.isDestroyed || currentEditor?.isDestroyed) {
return
}
@ -25,18 +28,25 @@ export const BubbleMenu = (props: BubbleMenuProps) => {
pluginKey = 'bubbleMenu', editor, tippyOptions = {}, updateDelay, shouldShow = null,
} = props
const menuEditor = editor || currentEditor
if (!menuEditor) {
console.warn('BubbleMenu component is not rendered inside of an editor component or does not have editor prop.')
return
}
const plugin = BubbleMenuPlugin({
updateDelay,
editor,
editor: menuEditor,
element,
pluginKey,
shouldShow,
tippyOptions,
})
editor.registerPlugin(plugin)
return () => editor.unregisterPlugin(pluginKey)
}, [props.editor, element])
menuEditor.registerPlugin(plugin)
return () => menuEditor.unregisterPlugin(pluginKey)
}, [props.editor, currentEditor, element])
return (
<div ref={setElement} className={props.className} style={{ visibility: 'hidden' }}>

View File

@ -0,0 +1,47 @@
import { EditorOptions } from '@tiptap/core'
import React, { createContext, ReactNode, useContext } from 'react'
import { Editor } from './Editor.js'
import { EditorContent } from './EditorContent.js'
import { useEditor } from './useEditor.js'
export type EditorContextValue = {
editor: Editor | null;
}
export const EditorContext = createContext<EditorContextValue>({
editor: null,
})
export const EditorConsumer = EditorContext.Consumer
export const useCurrentEditor = () => useContext(EditorContext)
export type EditorProviderProps = {
children: ReactNode;
slotBefore?: ReactNode;
slotAfter?: ReactNode;
} & Partial<EditorOptions>
export const EditorProvider = ({
children, slotAfter, slotBefore, ...editorOptions
}: EditorProviderProps) => {
const editor = useEditor(editorOptions)
if (!editor) {
return null
}
return (
<EditorContext.Provider value={{ editor }}>
{slotBefore}
<EditorConsumer>
{({ editor: currentEditor }) => (
<EditorContent editor={currentEditor} />
)}
</EditorConsumer>
{children}
{slotAfter}
</EditorContext.Provider>
)
}

View File

@ -3,22 +3,25 @@ import React, {
useEffect, useState,
} from 'react'
import { useCurrentEditor } from './Context.js'
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>
export type FloatingMenuProps = Omit<Optional<FloatingMenuPluginProps, 'pluginKey'>, 'element'> & {
export type FloatingMenuProps = Omit<Optional<FloatingMenuPluginProps, 'pluginKey' | 'editor'>, 'element'> & {
className?: string,
children: React.ReactNode
}
export const FloatingMenu = (props: FloatingMenuProps) => {
const [element, setElement] = useState<HTMLDivElement | null>(null)
const { editor: currentEditor } = useCurrentEditor()
useEffect(() => {
if (!element) {
return
}
if (props.editor.isDestroyed) {
if (props.editor?.isDestroyed || currentEditor?.isDestroyed) {
return
}
@ -29,18 +32,26 @@ export const FloatingMenu = (props: FloatingMenuProps) => {
shouldShow = null,
} = props
const menuEditor = editor || currentEditor
if (!menuEditor) {
console.warn('FloatingMenu component is not rendered inside of an editor component or does not have editor prop.')
return
}
const plugin = FloatingMenuPlugin({
pluginKey,
editor,
editor: menuEditor,
element,
tippyOptions,
shouldShow,
})
editor.registerPlugin(plugin)
return () => editor.unregisterPlugin(pluginKey)
menuEditor.registerPlugin(plugin)
return () => menuEditor.unregisterPlugin(pluginKey)
}, [
props.editor,
currentEditor,
element,
])

View File

@ -1,4 +1,5 @@
export * from './BubbleMenu.js'
export * from './Context.js'
export { Editor } from './Editor.js'
export * from './EditorContent.js'
export * from './FloatingMenu.js'

View File

@ -30,8 +30,12 @@ module.exports = on => {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
loader: 'ts-loader',
exclude: /node_modules/,
options: {
// tsconfig:
configFile: path.resolve(__dirname, '..', 'tsconfig.json'),
},
},
{
test: /\.jsx?$/,

View File

@ -4,11 +4,18 @@
"strict": false,
"noEmit": false,
"sourceMap": false,
"types": ["cypress"],
"types": ["cypress", "react", "react-dom"],
"paths": {
"@tiptap/*": ["packages/*/dist", "packages/*/src"],
"@tiptap/pm/*": ["../../pm/*/dist"]
}
},
"typeRoots": ["../../node_modules/@types"]
},
"include": ["./*/*.ts", "../../**/*.ts"]
"include": ["./*/*.ts", "../../**/*.ts"],
"exclude": [
"../../packages/react",
"../../packages/vue-2",
"../../packages/vue-3",
"../../packages/extension-code-block-lowlight"
]
}