mirror of
https://github.com/ueberdosis/tiptap.git
synced 2024-11-24 03:31:47 +08:00
feat(pm): new prosemirror package for dependency resolving
* chore:(core): migrate to tsup * chore: migrate blockquote and bold to tsup * chore: migrated bubble-menu and bullet-list to tsup * chore: migrated more packages to tsup * chore: migrate code and character extensions to tsup * chore: update package.json to simplify build for all packages * chore: move all packages to tsup as a build process * chore: change ci build task * feat(pm): add prosemirror meta package * rfix: resolve issues with build paths & export mappings * docs: update documentation to include notes for @tiptap/pm * chore(pm): update tsconfig * chore(packages): update packages * fix(pm): add package export infos & fix dependencies * chore(general): start moving to pm package as deps * chore: move to tiptap pm package internally * fix(demos): fix demos working with new pm package * fix(tables): fix tables package * fix(tables): fix tables package * chore(demos): pinned typescript version * chore: remove unnecessary tsconfig * chore: fix netlify build * fix(demos): fix package resolving for pm packages * fix(tests): fix package resolving for pm packages * fix(tests): fix package resolving for pm packages * chore(tests): fix tests not running correctly after pm package * chore(pm): add files to files array * chore: update build workflow * chore(tests): increase timeout time back to 12s * chore(docs): update docs * chore(docs): update installation guides & pm information to docs * chore(docs): add link to prosemirror docs * fix(vue-3): add missing build step * chore(docs): comment out cdn link * chore(docs): remove semicolons from docs * chore(docs): remove unnecessary installation note * chore(docs): remove unnecessary installation note
This commit is contained in:
parent
0ecb5a8df8
commit
f387ad3dd4
209
.github/workflows/build.yml
vendored
209
.github/workflows/build.yml
vendored
@ -23,50 +23,49 @@ jobs:
|
||||
node-version: [16]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3.0.2
|
||||
|
||||
- uses: actions/checkout@v3.0.2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3.5.1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3.5.1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Load cached dependencies
|
||||
uses: actions/cache@v3.0.11
|
||||
id: cache
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
/home/runner/.cache/Cypress
|
||||
key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
|
||||
|
||||
- name: Load cached dependencies
|
||||
uses: actions/cache@v3.0.11
|
||||
id: cache
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
/home/runner/.cache/Cypress
|
||||
key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
|
||||
- name: Install dependencies
|
||||
id: install-dependencies
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: npm install
|
||||
|
||||
- name: Install dependencies
|
||||
id: install-dependencies
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: npm install
|
||||
# - name: Fix code style linting errors
|
||||
# id: lint-fix
|
||||
# run: npm run lint:fix
|
||||
# continue-on-error: true
|
||||
#
|
||||
# - name: Commit fixed linting errors
|
||||
# id: commit
|
||||
# uses: stefanzweifel/git-auto-commit-action@v4
|
||||
# with:
|
||||
# commit_message: "ci: fix code style linting errors"
|
||||
|
||||
# - name: Fix code style linting errors
|
||||
# id: lint-fix
|
||||
# run: npm run lint:fix
|
||||
# continue-on-error: true
|
||||
#
|
||||
# - name: Commit fixed linting errors
|
||||
# id: commit
|
||||
# uses: stefanzweifel/git-auto-commit-action@v4
|
||||
# with:
|
||||
# commit_message: "ci: fix code style linting errors"
|
||||
- name: Lint code
|
||||
id: lint
|
||||
run: npm run lint
|
||||
|
||||
- name: Lint code
|
||||
id: lint
|
||||
run: npm run lint
|
||||
|
||||
- name: Send Slack notifications
|
||||
uses: act10ns/slack@v1
|
||||
if: failure()
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
steps: ${{ toJson(steps) }}
|
||||
channel: '#tiptap-notifications'
|
||||
- name: Send Slack notifications
|
||||
uses: act10ns/slack@v1
|
||||
if: failure()
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
steps: ${{ toJson(steps) }}
|
||||
channel: '#tiptap-notifications'
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
@ -79,48 +78,55 @@ jobs:
|
||||
node-version: [16]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3.0.2
|
||||
|
||||
- uses: actions/checkout@v3.0.2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3.5.1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3.5.1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Install dependencies
|
||||
id: install-dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Run tests with Cypress
|
||||
id: cypress
|
||||
uses: cypress-io/github-action@v4.2.0
|
||||
with:
|
||||
cache-key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
|
||||
start: npm run start
|
||||
wait-on: 'http://localhost:3000'
|
||||
project: ./tests
|
||||
browser: chrome
|
||||
quiet: true
|
||||
- name: Try to build the packages
|
||||
id: build-packages
|
||||
run: npm run build:pm
|
||||
|
||||
- name: Export screenshots (on failure only)
|
||||
uses: actions/upload-artifact@v3.1.0
|
||||
if: failure()
|
||||
with:
|
||||
name: cypress-screenshots
|
||||
path: tests/cypress/screenshots
|
||||
retention-days: 7
|
||||
- name: Run tests with Cypress
|
||||
id: cypress
|
||||
uses: cypress-io/github-action@v4.2.0
|
||||
with:
|
||||
cache-key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
|
||||
start: npm run start
|
||||
wait-on: 'http://localhost:3000'
|
||||
project: ./tests
|
||||
browser: chrome
|
||||
quiet: true
|
||||
|
||||
- name: Export screen recordings (on failure only)
|
||||
uses: actions/upload-artifact@v3.1.0
|
||||
if: failure()
|
||||
with:
|
||||
name: cypress-videos
|
||||
path: tests/cypress/videos
|
||||
retention-days: 7
|
||||
- name: Export screenshots (on failure only)
|
||||
uses: actions/upload-artifact@v3.1.0
|
||||
if: failure()
|
||||
with:
|
||||
name: cypress-screenshots
|
||||
path: tests/cypress/screenshots
|
||||
retention-days: 7
|
||||
|
||||
- name: Send Slack notifications
|
||||
uses: act10ns/slack@v1
|
||||
if: failure()
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
steps: ${{ toJson(steps) }}
|
||||
channel: '#tiptap-notifications'
|
||||
- name: Export screen recordings (on failure only)
|
||||
uses: actions/upload-artifact@v3.1.0
|
||||
if: failure()
|
||||
with:
|
||||
name: cypress-videos
|
||||
path: tests/cypress/videos
|
||||
retention-days: 7
|
||||
|
||||
- name: Send Slack notifications
|
||||
uses: act10ns/slack@v1
|
||||
if: failure()
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
steps: ${{ toJson(steps) }}
|
||||
channel: '#tiptap-notifications'
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
@ -135,36 +141,35 @@ jobs:
|
||||
node-version: [16]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3.0.2
|
||||
|
||||
- uses: actions/checkout@v3.0.2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3.5.1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3.5.1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Load cached dependencies
|
||||
uses: actions/cache@v3.0.11
|
||||
id: cache
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
/home/runner/.cache/Cypress
|
||||
key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
|
||||
|
||||
- name: Load cached dependencies
|
||||
uses: actions/cache@v3.0.11
|
||||
id: cache
|
||||
with:
|
||||
path: |
|
||||
**/node_modules
|
||||
/home/runner/.cache/Cypress
|
||||
key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
|
||||
- name: Install dependencies
|
||||
id: install-dependencies
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: npm install
|
||||
|
||||
- name: Install dependencies
|
||||
id: install-dependencies
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: npm install
|
||||
- name: Try to build the packages
|
||||
id: build-packages
|
||||
run: npm run build:ci
|
||||
|
||||
- name: Try to build the packages
|
||||
id: build-packages
|
||||
run: npm run build:ci
|
||||
|
||||
- name: Send Slack notifications
|
||||
uses: act10ns/slack@v1
|
||||
if: failure()
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
steps: ${{ toJson(steps) }}
|
||||
channel: '#tiptap-notifications'
|
||||
- name: Send Slack notifications
|
||||
uses: act10ns/slack@v1
|
||||
if: failure()
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
steps: ${{ toJson(steps) }}
|
||||
channel: '#tiptap-notifications'
|
||||
|
@ -10,7 +10,7 @@ prosemirror-keymap
|
||||
prosemirror-model
|
||||
prosemirror-schema-list
|
||||
prosemirror-state
|
||||
@tiptap/prosemirror-tables
|
||||
prosemirror-tables
|
||||
prosemirror-transform
|
||||
prosemirror-view
|
||||
react
|
||||
|
2
demos/package-lock.json
generated
2
demos/package-lock.json
generated
@ -33,7 +33,7 @@
|
||||
"sass": "^1.49.7",
|
||||
"svelte": "^3.49.0",
|
||||
"tailwindcss": "^2.2.19",
|
||||
"typescript": "^4.5.5",
|
||||
"typescript": "4.7.4",
|
||||
"uuid": "^8.3.2",
|
||||
"vite": "^2.9.13",
|
||||
"vite-plugin-checker": "^0.3.4",
|
||||
|
@ -34,7 +34,7 @@
|
||||
"sass": "^1.49.7",
|
||||
"svelte": "^3.49.0",
|
||||
"tailwindcss": "^2.2.19",
|
||||
"typescript": "^4.5.5",
|
||||
"typescript": "4.7.4",
|
||||
"uuid": "^8.3.2",
|
||||
"vite": "^2.9.13",
|
||||
"vite-plugin-checker": "^0.3.4",
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Extension } from '@tiptap/core'
|
||||
import { Plugin } from 'prosemirror-state'
|
||||
import { Plugin } from '@tiptap/pm/state'
|
||||
|
||||
import findColors from './findColors'
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Node } from 'prosemirror-model'
|
||||
import { Decoration, DecorationSet } from 'prosemirror-view'
|
||||
import { Node } from '@tiptap/pm/model'
|
||||
import { Decoration, DecorationSet } from '@tiptap/pm/view'
|
||||
|
||||
export default function (doc: Node): DecorationSet {
|
||||
const hexColor = /(#[0-9a-f]{3,6})\b/gi
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Extension } from '@tiptap/core'
|
||||
import { Plugin } from 'prosemirror-state'
|
||||
import { Plugin } from '@tiptap/pm/state'
|
||||
|
||||
import findColors from './findColors'
|
||||
|
||||
@ -14,9 +14,7 @@ export const ColorHighlighter = Extension.create({
|
||||
return findColors(doc)
|
||||
},
|
||||
apply(transaction, oldState) {
|
||||
return transaction.docChanged
|
||||
? findColors(transaction.doc)
|
||||
: oldState
|
||||
return transaction.docChanged ? findColors(transaction.doc) : oldState
|
||||
},
|
||||
},
|
||||
props: {
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { Node } from 'prosemirror-model'
|
||||
import { Decoration, DecorationSet } from 'prosemirror-view'
|
||||
import { Node } from '@tiptap/pm/model'
|
||||
import { Decoration, DecorationSet } from '@tiptap/pm/view'
|
||||
|
||||
export default function (doc: Node): DecorationSet {
|
||||
const hexColor = /(#[0-9a-f]{3,6})\b/ig
|
||||
const hexColor = /(#[0-9a-f]{3,6})\b/gi
|
||||
const decorations: Decoration[] = []
|
||||
|
||||
doc.descendants((node, position) => {
|
||||
@ -10,20 +10,18 @@ export default function (doc: Node): DecorationSet {
|
||||
return
|
||||
}
|
||||
|
||||
Array
|
||||
.from(node.text.matchAll(hexColor))
|
||||
.forEach(match => {
|
||||
const color = match[0]
|
||||
const index = match.index || 0
|
||||
const from = position + index
|
||||
const to = from + color.length
|
||||
const decoration = Decoration.inline(from, to, {
|
||||
class: 'color',
|
||||
style: `--color: ${color}`,
|
||||
})
|
||||
|
||||
decorations.push(decoration)
|
||||
Array.from(node.text.matchAll(hexColor)).forEach(match => {
|
||||
const color = match[0]
|
||||
const index = match.index || 0
|
||||
const from = position + index
|
||||
const to = from + color.length
|
||||
const decoration = Decoration.inline(from, to, {
|
||||
class: 'color',
|
||||
style: `--color: ${color}`,
|
||||
})
|
||||
|
||||
decorations.push(decoration)
|
||||
})
|
||||
})
|
||||
|
||||
return DecorationSet.create(doc, decorations)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Plugin, PluginKey } from 'prosemirror-state'
|
||||
import { Plugin, PluginKey } from '@tiptap/pm/state'
|
||||
import * as Y from 'yjs'
|
||||
|
||||
import { AnnotationState } from './AnnotationState'
|
||||
@ -8,10 +8,10 @@ export const AnnotationPluginKey = new PluginKey('annotation')
|
||||
export interface AnnotationPluginOptions {
|
||||
HTMLAttributes: {
|
||||
[key: string]: any
|
||||
},
|
||||
onUpdate: (items: [any?]) => {},
|
||||
map: Y.Map<any>,
|
||||
instance: string,
|
||||
}
|
||||
onUpdate: (items: [any?]) => {}
|
||||
map: Y.Map<any>
|
||||
instance: string
|
||||
}
|
||||
|
||||
export const AnnotationPlugin = (options: AnnotationPluginOptions) => new Plugin({
|
||||
@ -39,9 +39,7 @@ export const AnnotationPlugin = (options: AnnotationPluginOptions) => new Plugin
|
||||
return decorations
|
||||
}
|
||||
|
||||
const annotations = this
|
||||
.getState(state)
|
||||
.annotationsAt(selection.from)
|
||||
const annotations = this.getState(state).annotationsAt(selection.from)
|
||||
|
||||
options.onUpdate(annotations)
|
||||
|
||||
|
@ -1,18 +1,26 @@
|
||||
import { EditorState, Transaction } from 'prosemirror-state'
|
||||
import { Decoration, DecorationSet } from 'prosemirror-view'
|
||||
import { absolutePositionToRelativePosition, relativePositionToAbsolutePosition, ySyncPluginKey } from 'y-prosemirror'
|
||||
import { EditorState, Transaction } from '@tiptap/pm/state'
|
||||
import { Decoration, DecorationSet } from '@tiptap/pm/view'
|
||||
import {
|
||||
absolutePositionToRelativePosition,
|
||||
relativePositionToAbsolutePosition,
|
||||
ySyncPluginKey,
|
||||
} from 'y-prosemirror'
|
||||
import * as Y from 'yjs'
|
||||
|
||||
import { AnnotationItem } from './AnnotationItem'
|
||||
import { AnnotationPluginKey } from './AnnotationPlugin'
|
||||
import { AddAnnotationAction, DeleteAnnotationAction, UpdateAnnotationAction } from './collaboration-annotation'
|
||||
import {
|
||||
AddAnnotationAction,
|
||||
DeleteAnnotationAction,
|
||||
UpdateAnnotationAction,
|
||||
} from './collaboration-annotation'
|
||||
|
||||
export interface AnnotationStateOptions {
|
||||
HTMLAttributes: {
|
||||
[key: string]: any
|
||||
},
|
||||
map: Y.Map<any>,
|
||||
instance: string,
|
||||
}
|
||||
map: Y.Map<any>
|
||||
instance: string
|
||||
}
|
||||
|
||||
export class AnnotationState {
|
||||
@ -93,14 +101,27 @@ export class AnnotationState {
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
console.log(`[${this.options.instance}] Decoration.inline()`, from, to, HTMLAttributes, { id, data: annotation.data })
|
||||
console.log(`[${this.options.instance}] Decoration.inline()`, from, to, HTMLAttributes, {
|
||||
id,
|
||||
data: annotation.data,
|
||||
})
|
||||
|
||||
if (from === to) {
|
||||
console.warn(`[${this.options.instance}] corrupt decoration `, annotation.from, from, annotation.to, to)
|
||||
console.warn(
|
||||
`[${this.options.instance}] corrupt decoration `,
|
||||
annotation.from,
|
||||
from,
|
||||
annotation.to,
|
||||
to,
|
||||
)
|
||||
}
|
||||
|
||||
decorations.push(
|
||||
Decoration.inline(from, to, HTMLAttributes, { id, data: annotation.data, inclusiveEnd: true }),
|
||||
Decoration.inline(from, to, HTMLAttributes, {
|
||||
id,
|
||||
data: annotation.data,
|
||||
inclusiveEnd: true,
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
@ -109,7 +130,10 @@ export class AnnotationState {
|
||||
|
||||
apply(transaction: Transaction, state: EditorState) {
|
||||
// Add/Remove annotations
|
||||
const action = transaction.getMeta(AnnotationPluginKey) as AddAnnotationAction | UpdateAnnotationAction | DeleteAnnotationAction
|
||||
const action = transaction.getMeta(AnnotationPluginKey) as
|
||||
| AddAnnotationAction
|
||||
| UpdateAnnotationAction
|
||||
| DeleteAnnotationAction
|
||||
|
||||
if (action && action.type) {
|
||||
// eslint-disable-next-line
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { mergeAttributes, Node } from '@tiptap/core'
|
||||
import { Plugin } from 'prosemirror-state'
|
||||
import { Plugin } from '@tiptap/pm/state'
|
||||
|
||||
export const Figure = Node.create({
|
||||
name: 'figure',
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Extension } from '@tiptap/core'
|
||||
import { NodeSelection, Plugin } from 'prosemirror-state'
|
||||
import { __serializeForClipboard as serializeForClipboard } from 'prosemirror-view'
|
||||
import { NodeSelection, Plugin } from '@tiptap/pm/state'
|
||||
import { __serializeForClipboard as serializeForClipboard } from '@tiptap/pm/view'
|
||||
|
||||
function removeNode(node) {
|
||||
node.parentNode.removeChild(node)
|
||||
@ -25,7 +25,8 @@ export default Extension.create({
|
||||
node = node.node
|
||||
|
||||
while (node && node.parentNode) {
|
||||
if (node.parentNode?.classList?.contains('ProseMirror')) { // todo
|
||||
if (node.parentNode?.classList?.contains('ProseMirror')) {
|
||||
// todo
|
||||
break
|
||||
}
|
||||
|
||||
@ -131,7 +132,8 @@ export default Extension.create({
|
||||
if (node) {
|
||||
node = node.node
|
||||
while (node && node.parentNode) {
|
||||
if (node.parentNode?.classList?.contains('ProseMirror')) { // todo
|
||||
if (node.parentNode?.classList?.contains('ProseMirror')) {
|
||||
// todo
|
||||
break
|
||||
}
|
||||
node = node.parentNode
|
||||
@ -145,7 +147,7 @@ export default Extension.create({
|
||||
const rect = absoluteRect(node)
|
||||
const win = node.ownerDocument.defaultView
|
||||
|
||||
rect.top += win.pageYOffset + ((lineHeight - 24) / 2) + top
|
||||
rect.top += win.pageYOffset + (lineHeight - 24) / 2 + top
|
||||
rect.left += win.pageXOffset
|
||||
rect.width = `${WIDTH}px`
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Extension } from '@tiptap/core'
|
||||
import { Node as ProsemirrorNode } from 'prosemirror-model'
|
||||
import { Plugin, PluginKey, TextSelection } from 'prosemirror-state'
|
||||
import { Decoration, DecorationSet } from 'prosemirror-view'
|
||||
import { Node as ProsemirrorNode } from '@tiptap/pm/model'
|
||||
import { Plugin, PluginKey, TextSelection } from '@tiptap/pm/state'
|
||||
import { Decoration, DecorationSet } from '@tiptap/pm/view'
|
||||
|
||||
import LinterPlugin, { Result as Issue } from './LinterPlugin'
|
||||
|
||||
@ -22,9 +22,11 @@ function renderIcon(issue: Issue) {
|
||||
function runAllLinterPlugins(doc: ProsemirrorNode, plugins: Array<typeof LinterPlugin>) {
|
||||
const decorations: [any?] = []
|
||||
|
||||
const results = plugins.map(RegisteredLinterPlugin => {
|
||||
return new RegisteredLinterPlugin(doc).scan().getResults()
|
||||
}).flat()
|
||||
const results = plugins
|
||||
.map(RegisteredLinterPlugin => {
|
||||
return new RegisteredLinterPlugin(doc).scan().getResults()
|
||||
})
|
||||
.flat()
|
||||
|
||||
results.forEach(issue => {
|
||||
decorations.push(
|
||||
@ -39,7 +41,7 @@ function runAllLinterPlugins(doc: ProsemirrorNode, plugins: Array<typeof LinterP
|
||||
}
|
||||
|
||||
export interface LinterOptions {
|
||||
plugins: Array<typeof LinterPlugin>,
|
||||
plugins: Array<typeof LinterPlugin>
|
||||
}
|
||||
|
||||
export const Linter = Extension.create<LinterOptions>({
|
||||
@ -62,9 +64,7 @@ export const Linter = Extension.create<LinterOptions>({
|
||||
return runAllLinterPlugins(doc, plugins)
|
||||
},
|
||||
apply(transaction, oldState) {
|
||||
return transaction.docChanged
|
||||
? runAllLinterPlugins(transaction.doc, plugins)
|
||||
: oldState
|
||||
return transaction.docChanged ? runAllLinterPlugins(transaction.doc, plugins) : oldState
|
||||
},
|
||||
},
|
||||
props: {
|
||||
@ -72,7 +72,7 @@ export const Linter = Extension.create<LinterOptions>({
|
||||
return this.getState(state)
|
||||
},
|
||||
handleClick(view, _, event) {
|
||||
const target = (event.target as IconDivElement)
|
||||
const target = event.target as IconDivElement
|
||||
|
||||
if (/lint-icon/.test(target.className) && target.issue) {
|
||||
const { from, to } = target.issue
|
||||
@ -89,7 +89,7 @@ export const Linter = Extension.create<LinterOptions>({
|
||||
return false
|
||||
},
|
||||
handleDoubleClick(view, _, event) {
|
||||
const target = (event.target as IconDivElement)
|
||||
const target = event.target as IconDivElement
|
||||
|
||||
if (/lint-icon/.test((event.target as HTMLElement).className) && target.issue) {
|
||||
const prob = target.issue
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { Node as ProsemirrorNode } from 'prosemirror-model'
|
||||
import { Node as ProsemirrorNode } from '@tiptap/pm/model'
|
||||
|
||||
export interface Result {
|
||||
message: string,
|
||||
from: number,
|
||||
to: number,
|
||||
message: string
|
||||
from: number
|
||||
to: number
|
||||
fix?: Function
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { EditorView } from 'prosemirror-view'
|
||||
import { EditorView } from '@tiptap/pm/view'
|
||||
|
||||
import LinterPlugin, { Result as Issue } from '../LinterPlugin'
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { EditorView } from 'prosemirror-view'
|
||||
import { EditorView } from '@tiptap/pm/view'
|
||||
|
||||
import LinterPlugin, { Result as Issue } from '../LinterPlugin'
|
||||
|
||||
@ -7,13 +7,7 @@ export class Punctuation extends LinterPlugin {
|
||||
|
||||
fix(replacement: any) {
|
||||
return function ({ state, dispatch }: EditorView, issue: Issue) {
|
||||
dispatch(
|
||||
state.tr.replaceWith(
|
||||
issue.from,
|
||||
issue.to,
|
||||
state.schema.text(replacement),
|
||||
),
|
||||
)
|
||||
dispatch(state.tr.replaceWith(issue.from, issue.to, state.schema.text(replacement)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Extension } from '@tiptap/core'
|
||||
import { Plugin, PluginKey } from 'prosemirror-state'
|
||||
import { Plugin, PluginKey } from '@tiptap/pm/state'
|
||||
|
||||
// @ts-ignore
|
||||
function nodeEqualsType({ types, node }) {
|
||||
@ -13,8 +13,8 @@ function nodeEqualsType({ types, node }) {
|
||||
*/
|
||||
|
||||
export interface TrailingNodeOptions {
|
||||
node: string,
|
||||
notAfter: string[],
|
||||
node: string
|
||||
notAfter: string[]
|
||||
}
|
||||
|
||||
export const TrailingNode = Extension.create<TrailingNodeOptions>({
|
||||
@ -23,9 +23,7 @@ export const TrailingNode = Extension.create<TrailingNodeOptions>({
|
||||
addOptions() {
|
||||
return {
|
||||
node: 'paragraph',
|
||||
notAfter: [
|
||||
'paragraph',
|
||||
],
|
||||
notAfter: ['paragraph'],
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -11,8 +11,30 @@ import {
|
||||
} from 'path'
|
||||
import { v4 as uuid } from 'uuid'
|
||||
import { defineConfig } from 'vite'
|
||||
|
||||
// import checker from 'vite-plugin-checker'
|
||||
|
||||
const getPackageDependencies = () => {
|
||||
const paths: Array<{ find: string, replacement: any }> = []
|
||||
|
||||
fg.sync('../packages/*', { onlyDirectories: true })
|
||||
.map(name => name.replace('../packages/', ''))
|
||||
.forEach(name => {
|
||||
if (name === 'pm') {
|
||||
fg.sync(`../packages/${name}/*`, { onlyDirectories: true })
|
||||
.forEach(subName => {
|
||||
const subPkgName = subName.replace(`../packages/${name}/`, '')
|
||||
|
||||
paths.push({ find: `@tiptap/${name}/${subPkgName}`, replacement: resolve(`../packages/${name}/${subPkgName}/index.ts`) })
|
||||
})
|
||||
} else {
|
||||
paths.push({ find: `@tiptap/${name}`, replacement: resolve(`../packages/${name}/src/index.ts`) })
|
||||
}
|
||||
})
|
||||
|
||||
return paths
|
||||
}
|
||||
|
||||
const includeDependencies = fs.readFileSync('./includeDependencies.txt')
|
||||
.toString()
|
||||
.replace(/\r\n/g, '\n')
|
||||
@ -271,12 +293,6 @@ export default defineConfig({
|
||||
],
|
||||
|
||||
resolve: {
|
||||
alias: [
|
||||
...fg.sync('../packages/*', { onlyDirectories: true })
|
||||
.map(name => name.replace('../packages/', ''))
|
||||
.map(name => {
|
||||
return { find: `@tiptap/${name}`, replacement: resolve(`../packages/${name}/src/index.ts`) }
|
||||
}),
|
||||
],
|
||||
alias: getPackageDependencies(),
|
||||
},
|
||||
})
|
||||
|
@ -120,7 +120,7 @@ addCommands() {
|
||||
If you’re just wrapping a plain ProseMirror command, you’ll need to pass `dispatch` anyway. Then there’s also no need to check it:
|
||||
|
||||
```js
|
||||
import { exitCode } from 'prosemirror-commands'
|
||||
import { exitCode } from '@tiptap/pm/commands'
|
||||
|
||||
export default () => ({ state, dispatch }) => {
|
||||
return exitCode(state, dispatch)
|
||||
|
@ -112,7 +112,7 @@ Alternatively you can pass a ProseMirror `PluginKey`.
|
||||
```js
|
||||
import { Editor } from '@tiptap/core'
|
||||
import BubbleMenu from '@tiptap/extension-bubble-menu'
|
||||
import { PluginKey } from 'prosemirror-state'
|
||||
import { PluginKey } from '@tiptap/pm/state'
|
||||
|
||||
new Editor({
|
||||
extensions: [
|
||||
|
@ -20,10 +20,6 @@ We kindly ask you to [sponsor our work](/sponsor) when using this extension in p
|
||||
npm install @tiptap/extension-collaboration-cursor
|
||||
```
|
||||
|
||||
:::warning Are you using Yarn, pNPM, npm 6 or less?
|
||||
Unfortunately your package manager does not install peer dependencies automatically and you have to install them by your own. Please [see here](https://tiptap.dev/installation/peer-dependencies#tiptapextension-collaboration-cursor) which packages are needed and how to install them.
|
||||
:::
|
||||
|
||||
This extension requires the [`Collaboration`](/api/extensions/collaboration) extension.
|
||||
|
||||
## Settings
|
||||
|
@ -20,10 +20,6 @@ We kindly ask you to [sponsor our work](/sponsor) when using this extension in p
|
||||
npm install @tiptap/extension-collaboration yjs y-websocket
|
||||
```
|
||||
|
||||
:::warning Are you using Yarn, pNPM, npm 6 or less?
|
||||
Unfortunately your package manager does not install peer dependencies automatically and you have to install them by your own. Please [see here](https://tiptap.dev/installation/peer-dependencies#tiptapextension-collaboration) which packages are needed and how to install them.
|
||||
:::
|
||||
|
||||
## Settings
|
||||
|
||||
### document
|
||||
|
@ -4,6 +4,7 @@ icon: drag-drop-line
|
||||
---
|
||||
|
||||
# Dropcursor
|
||||
|
||||
[![Version](https://img.shields.io/npm/v/@tiptap/extension-dropcursor.svg?label=version)](https://www.npmjs.com/package/@tiptap/extension-dropcursor)
|
||||
[![Downloads](https://img.shields.io/npm/dm/@tiptap/extension-dropcursor.svg)](https://npmcharts.com/compare/@tiptap/extension-dropcursor?minimal=true)
|
||||
|
||||
@ -12,28 +13,27 @@ This extension loads the [ProseMirror Dropcursor plugin](https://github.com/Pros
|
||||
Note that Tiptap is headless, but the dropcursor needs CSS for its appearance. There are settings for the color and width, and you’re free to add a custom CSS class.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @tiptap/extension-dropcursor
|
||||
```
|
||||
|
||||
:::warning Are you using Yarn, pNPM, npm 6 or less?
|
||||
Unfortunately your package manager does not install peer dependencies automatically and you have to install them by your own. Please [see here](https://tiptap.dev/installation/peer-dependencies#tiptapextension-dropcursor) which packages are needed and how to install them.
|
||||
:::
|
||||
|
||||
## Settings
|
||||
|
||||
### color
|
||||
|
||||
Color of the dropcursor.
|
||||
|
||||
Default: `'currentColor'`
|
||||
|
||||
```js
|
||||
Dropcursor.configure({
|
||||
color: '#ff0000'
|
||||
color: '#ff0000',
|
||||
})
|
||||
```
|
||||
|
||||
### width
|
||||
|
||||
Width of the dropcursor.
|
||||
|
||||
Default: `1`
|
||||
@ -45,6 +45,7 @@ Dropcursor.configure({
|
||||
```
|
||||
|
||||
### class
|
||||
|
||||
One or multiple CSS classes that should be applied to the dropcursor.
|
||||
|
||||
```js
|
||||
@ -54,7 +55,9 @@ Dropcursor.configure({
|
||||
```
|
||||
|
||||
## Source code
|
||||
|
||||
[packages/extension-dropcursor/](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-dropcursor/)
|
||||
|
||||
## Usage
|
||||
|
||||
https://embed.tiptap.dev/preview/Extensions/Dropcursor
|
||||
|
@ -100,7 +100,7 @@ Alternatively you can pass a ProseMirror `PluginKey`.
|
||||
```js
|
||||
import { Editor } from '@tiptap/core'
|
||||
import FloatingMenu from '@tiptap/extension-floating-menu'
|
||||
import { PluginKey } from 'prosemirror-state'
|
||||
import { PluginKey } from '@tiptap/pm/state'
|
||||
|
||||
new Editor({
|
||||
extensions: [
|
||||
|
@ -4,6 +4,7 @@ icon: space
|
||||
---
|
||||
|
||||
# Gapcursor
|
||||
|
||||
[![Version](https://img.shields.io/npm/v/@tiptap/extension-gapcursor.svg?label=version)](https://www.npmjs.com/package/@tiptap/extension-gapcursor)
|
||||
[![Downloads](https://img.shields.io/npm/dm/@tiptap/extension-gapcursor.svg)](https://npmcharts.com/compare/@tiptap/extension-gapcursor?minimal=true)
|
||||
|
||||
@ -12,16 +13,15 @@ This extension loads the [ProseMirror Gapcursor plugin](https://github.com/Prose
|
||||
Note that Tiptap is headless, but the gapcursor needs CSS for its appearance. The [default CSS](https://github.com/ueberdosis/tiptap/tree/main/packages/core/src/style.ts) is loaded through the Editor class.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @tiptap/extension-gapcursor
|
||||
```
|
||||
|
||||
:::warning Are you using Yarn, pNPM, npm 6 or less?
|
||||
Unfortunately your package manager does not install peer dependencies automatically and you have to install them by your own. Please [see here](https://tiptap.dev/installation/peer-dependencies#tiptapextension-gapcursor) which packages are needed and how to install them.
|
||||
:::
|
||||
|
||||
## Source code
|
||||
|
||||
[packages/extension-gapcursor/](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-gapcursor/)
|
||||
|
||||
## Usage
|
||||
|
||||
https://embed.tiptap.dev/preview/Extensions/Gapcursor
|
||||
|
@ -4,23 +4,22 @@ icon: history-line
|
||||
---
|
||||
|
||||
# History
|
||||
|
||||
[![Version](https://img.shields.io/npm/v/@tiptap/extension-history.svg?label=version)](https://www.npmjs.com/package/@tiptap/extension-history)
|
||||
[![Downloads](https://img.shields.io/npm/dm/@tiptap/extension-history.svg)](https://npmcharts.com/compare/@tiptap/extension-history?minimal=true)
|
||||
|
||||
This extension provides history support. All changes to the document will be tracked and can be removed with `undo`. Undone changes can be applied with `redo` again.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @tiptap/extension-history
|
||||
```
|
||||
|
||||
:::warning Are you using Yarn, pNPM, npm 6 or less?
|
||||
Unfortunately your package manager does not install peer dependencies automatically and you have to install them by your own. Please [see here](https://tiptap.dev/installation/peer-dependencies#tiptapextension-history) which packages are needed and how to install them.
|
||||
:::
|
||||
|
||||
## Settings
|
||||
|
||||
### depth
|
||||
|
||||
The amount of history events that are collected before the oldest events are discarded. Defaults to 100.
|
||||
|
||||
Default: `100`
|
||||
@ -32,6 +31,7 @@ History.configure({
|
||||
```
|
||||
|
||||
### newGroupDelay
|
||||
|
||||
The delay between changes after which a new group should be started (in milliseconds). When changes aren’t adjacent, a new group is always started.
|
||||
|
||||
Default: `500`
|
||||
@ -45,12 +45,15 @@ History.configure({
|
||||
## Commands
|
||||
|
||||
### undo()
|
||||
|
||||
Undo the last change.
|
||||
|
||||
```js
|
||||
editor.commands.undo()
|
||||
```
|
||||
|
||||
### redo()
|
||||
|
||||
Redo the last change.
|
||||
|
||||
```js
|
||||
@ -58,13 +61,16 @@ editor.commands.redo()
|
||||
```
|
||||
|
||||
## Keyboard shortcuts
|
||||
|
||||
| Command | Windows/Linux | macOS |
|
||||
| ------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
|
||||
| undo() | `Control` `Z`<br>`Control` `я` | `Cmd` `Z`<br>`Cmd` `я` |
|
||||
| redo() | `Shift` `Control` `Z`<br>`Control` `Y`<br>`Shift` `Control` `я` | `Shift` `Cmd` `Z`<br>`Cmd` `Y`<br>`Shift` `Cmd` `я` |
|
||||
|
||||
## Source code
|
||||
|
||||
[packages/extension-history/](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-history/)
|
||||
|
||||
## Usage
|
||||
|
||||
https://embed.tiptap.dev/preview/Extensions/History
|
||||
|
@ -14,10 +14,6 @@ The `StarterKit` is a collection of the most popular Tiptap extensions. If you
|
||||
npm install @tiptap/starter-kit
|
||||
```
|
||||
|
||||
:::warning Are you using Yarn, pNPM, npm 6 or less?
|
||||
Unfortunately your package manager does not install peer dependencies automatically and you have to install them by your own. Please [see here](https://tiptap.dev/installation/peer-dependencies#tiptapstarter-kit) which packages are needed and how to install them.
|
||||
:::
|
||||
|
||||
## Included extensions
|
||||
|
||||
### Nodes
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Introduction
|
||||
tiptap is a friendly wrapper around [ProseMirror](https://ProseMirror.net). Although Tiptap tries to hide most of the complexity of ProseMirror, it’s built on top of its APIs and we recommend you to read through the [ProseMirror Guide](https://ProseMirror.net/docs/guide/) for advanced usage.
|
||||
Tiptap is a friendly wrapper around [ProseMirror](https://ProseMirror.net). Although Tiptap tries to hide most of the complexity of ProseMirror, it’s built on top of its APIs and we recommend you to read through the [ProseMirror Guide](https://ProseMirror.net/docs/guide/) for advanced usage.
|
||||
|
||||
### Structure
|
||||
ProseMirror works with a strict [Schema](/api/schema), which defines the allowed structure of a document. A document is a tree of headings, paragraphs and others elements, so called nodes. Marks can be attached to a node, e. g. to emphasize part of it. [Commands](/api/commands) change that document programmatically.
|
||||
|
@ -28,10 +28,6 @@ First, install the dependencies:
|
||||
npm install @tiptap/extension-collaboration yjs y-webrtc
|
||||
```
|
||||
|
||||
:::warning Are you using Yarn, pNPM, npm 6 or less?
|
||||
Unfortunately your package manager does not install peer dependencies automatically and you have to install them by your own. Please [see here](https://tiptap.dev/installation/peer-dependencies#tiptapextension-collaboration) which packages are needed and how to install them.
|
||||
:::
|
||||
|
||||
Now, create a new Y document, and register it with Tiptap:
|
||||
|
||||
```js
|
||||
@ -78,10 +74,6 @@ For the client, the example is nearly the same, only the provider is different.
|
||||
npm install @tiptap/extension-collaboration @hocuspocus/provider
|
||||
```
|
||||
|
||||
:::warning Are you using Yarn, pNPM, npm 6 or less?
|
||||
Unfortunately your package manager does not install peer dependencies automatically and you have to install them by your own. Please [see here](https://tiptap.dev/installation/peer-dependencies#tiptapextension-collaboration) which packages are needed and how to install them.
|
||||
:::
|
||||
|
||||
And then register the WebSocket provider with Tiptap:
|
||||
|
||||
```js
|
||||
@ -293,7 +285,7 @@ server.listen()
|
||||
## Pitfalls
|
||||
|
||||
### Schema updates
|
||||
tiptap is very strict with the [schema](/api/schema), that means, if you add something that’s not allowed according to the configured schema it’ll be thrown away. That can lead to a strange behaviour when multiple clients with different schemas share changes to a document.
|
||||
Tiptap is very strict with the [schema](/api/schema), that means, if you add something that’s not allowed according to the configured schema it’ll be thrown away. That can lead to a strange behaviour when multiple clients with different schemas share changes to a document.
|
||||
|
||||
Let’s say you added an editor to your app and the first people use it already. They have all a loaded instance of Tiptap with all default extensions, and therefor a schema that only allows those. But you want to add task lists in the next update, so you add the extension and deploy again.
|
||||
|
||||
|
@ -114,7 +114,7 @@ const awesomeness = editor.storage.customExtension.awesomeness
|
||||
```
|
||||
|
||||
### Schema
|
||||
tiptap works with a strict schema, which configures how the content can be structured, nested, how it behaves and many more things. You [can change all aspects of the schema](/api/schema) for existing extensions. Let’s walk through a few common use cases.
|
||||
Tiptap works with a strict schema, which configures how the content can be structured, nested, how it behaves and many more things. You [can change all aspects of the schema](/api/schema) for existing extensions. Let’s walk through a few common use cases.
|
||||
|
||||
The default `Blockquote` extension can wrap other nodes, like headings. If you want to allow nothing but paragraphs in your blockquotes, set the `content` attribute accordingly:
|
||||
|
||||
@ -531,7 +531,7 @@ After all, Tiptap is built on ProseMirror and ProseMirror has a pretty powerful
|
||||
You can wrap existing ProseMirror plugins in Tiptap extensions like shown in the example below.
|
||||
|
||||
```js
|
||||
import { history } from 'prosemirror-history'
|
||||
import { history } from '@tiptap/pm/history'
|
||||
|
||||
const History = Extension.create({
|
||||
addProseMirrorPlugins() {
|
||||
@ -550,7 +550,7 @@ Or you can add them to a Tiptap extension like shown in the below example.
|
||||
|
||||
```js
|
||||
import { Extension } from '@tiptap/core'
|
||||
import { Plugin, PluginKey } from 'prosemirror-state'
|
||||
import { Plugin, PluginKey } from '@tiptap/pm/state'
|
||||
|
||||
export const EventHandler = Extension.create({
|
||||
name: 'eventHandler',
|
||||
|
39
docs/guide/prosemirror.md
Normal file
39
docs/guide/prosemirror.md
Normal file
@ -0,0 +1,39 @@
|
||||
---
|
||||
tableOfContents: true
|
||||
---
|
||||
|
||||
# Accessing ProseMirror internals
|
||||
|
||||
The ProseMirror internals are packaged in the `@tiptap/pm` package that you need to install with `npm install @tiptap/pm`. If you already have this done you can skip the following step.
|
||||
|
||||
```bash
|
||||
npm i @tiptap/pm
|
||||
```
|
||||
|
||||
After that you can access all internal ProseMirror packages like this:
|
||||
|
||||
```js
|
||||
// this example loads the EditorState class from the ProseMirror state package
|
||||
import { EditorState } from '@tiptap/pm/state'
|
||||
```
|
||||
|
||||
The following packages are available:
|
||||
|
||||
- `@tiptap/pm/changeset`
|
||||
- `@tiptap/pm/collab`
|
||||
- `@tiptap/pm/commands`
|
||||
- `@tiptap/pm/dropcursor`
|
||||
- `@tiptap/pm/gapcursor`
|
||||
- `@tiptap/pm/history`
|
||||
- `@tiptap/pm/inputrules`
|
||||
- `@tiptap/pm/keymap`
|
||||
- `@tiptap/pm/markdown`
|
||||
- `@tiptap/pm/menu`
|
||||
- `@tiptap/pm/model`
|
||||
- `@tiptap/pm/schema-basic`
|
||||
- `@tiptap/pm/schema-list`
|
||||
- `@tiptap/pm/state`
|
||||
- `@tiptap/pm/tables`
|
||||
- `@tiptap/pm/trailing-node`
|
||||
- `@tiptap/pm/transform`
|
||||
- `@tiptap/pm/view`
|
@ -5,24 +5,33 @@ tableOfContents: true
|
||||
# Installation
|
||||
|
||||
## Introduction
|
||||
|
||||
Tiptap is framework-agnostic and even works with Vanilla JavaScript (if that’s your thing). The following integration guides help you integrating Tiptap in your JavaScript project.
|
||||
|
||||
## Integration guides
|
||||
<!-- * [CDN](/installation/cdn) -->
|
||||
* [Vanilla JavaScript](/installation/vanilla-javascript)
|
||||
* [React](/installation/react)
|
||||
* [Next.js](/installation/nextjs)
|
||||
* [Vue 3](/installation/vue3)
|
||||
* [Vue 2](/installation/vue2)
|
||||
* [Nuxt.js](/installation/nuxt)
|
||||
* [Svelte](/installation/svelte)
|
||||
* [Alpine.js](/installation/alpine)
|
||||
* [PHP](/installation/php)
|
||||
## Base Setup
|
||||
|
||||
:::warning Are you using Yarn, pNPM, npm 6 or less?
|
||||
Unfortunately your package manager does not install peer dependencies automatically and you have to install them by your own. Please [read this](https://tiptap.dev/installation/peer-dependencies) to understand what is needed in that case.
|
||||
:::
|
||||
To get started you will need to install `@tiptap/core`, `@tiptap/pm` and `@tiptap/starter-kit` in your project like this:
|
||||
|
||||
```bash
|
||||
npm install @tiptap/core @tiptap/pm @tiptap/starter-kit
|
||||
```
|
||||
|
||||
After that, you can start using Tiptap in your project. To integrate it into your framework, please follow the guides below.
|
||||
|
||||
## Integration guides
|
||||
|
||||
- [Vanilla JavaScript](/installation/vanilla-javascript)
|
||||
- [React](/installation/react)
|
||||
- [Next.js](/installation/nextjs)
|
||||
- [Vue 3](/installation/vue3)
|
||||
- [Vue 2](/installation/vue2)
|
||||
- [Nuxt.js](/installation/nuxt)
|
||||
- [Svelte](/installation/svelte)
|
||||
- [Alpine.js](/installation/alpine)
|
||||
- [PHP](/installation/php)
|
||||
<!-- [CDN](/installation/cdn)-->
|
||||
|
||||
### Community efforts
|
||||
* [Angular](https://github.com/sibiraj-s/ngx-tiptap)
|
||||
* [SolidJS](https://github.com/LXSMNSYC/solid-tiptap)
|
||||
|
||||
- [Angular](https://github.com/sibiraj-s/ngx-tiptap)
|
||||
- [SolidJS](https://github.com/LXSMNSYC/solid-tiptap)
|
||||
|
@ -28,16 +28,12 @@ npm run dev
|
||||
|
||||
## 2. Install the dependencies
|
||||
|
||||
Okay, enough of the boring boilerplate work. Let’s finally install Tiptap! For the following example you’ll need `alpinejs`, the `@tiptap/core` package and the `@tiptap/starter-kit` which has the most common extensions to get started quickly.
|
||||
Okay, enough of the boring boilerplate work. Let’s finally install Tiptap! For the following example you’ll need `alpinejs`, the `@tiptap/core` package, the `@tiptap/pm` package and the `@tiptap/starter-kit` which has the most common extensions to get started quickly.
|
||||
|
||||
```bash
|
||||
npm install alpinejs @tiptap/core @tiptap/starter-kit
|
||||
npm install alpinejs @tiptap/core @tiptap/pm @tiptap/starter-kit
|
||||
```
|
||||
|
||||
:::warning Are you using Yarn, pNPM, npm 6 or less?
|
||||
Unfortunately your package manager does not install peer dependencies automatically and you have to install them by your own. Please check the following links to find out what dependencies are needed and how to install them: [@tiptap/core](https://tiptap.dev/installation/peer-dependencies#tiptapcore), [@tiptap/starter-kit](https://tiptap.dev/installation/peer-dependencies#tiptapstarter-kit)
|
||||
:::
|
||||
|
||||
If you followed step 1, you can now start your project with `npm run dev`, and open [http://localhost:5173](http://localhost:5173) in your favorite browser. This might be different, if you’re working with an existing project.
|
||||
|
||||
## 3. Initialize the editor
|
||||
@ -74,7 +70,7 @@ document.addEventListener('alpine:init', () => {
|
||||
onSelectionUpdate({ editor }) {
|
||||
_this.updatedAt = Date.now()
|
||||
}
|
||||
});
|
||||
})
|
||||
},
|
||||
isLoaded() {
|
||||
return editor
|
||||
@ -91,9 +87,9 @@ document.addEventListener('alpine:init', () => {
|
||||
toggleItalic() {
|
||||
editor.chain().toggleItalic().focus().run()
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
window.Alpine = Alpine
|
||||
Alpine.start()
|
||||
|
@ -25,16 +25,12 @@ cd my-tiptap-project
|
||||
```
|
||||
|
||||
## 2. Install the dependencies
|
||||
Now that we have a standard boilerplate set up we can get started on getting Tiptap up and running! For this we will need to install two packages: `@tiptap/react` and `@tiptap/starter-kit` which includes all the extensions you need to get started quickly.
|
||||
Now that we have a standard boilerplate set up we can get started on getting Tiptap up and running! For this we will need to install three packages: `@tiptap/react`, `@tiptap/pm` and `@tiptap/starter-kit` which includes all the extensions you need to get started quickly.
|
||||
|
||||
```bash
|
||||
npm install @tiptap/react @tiptap/starter-kit
|
||||
npm install @tiptap/react @tiptap/pm @tiptap/starter-kit
|
||||
```
|
||||
|
||||
:::warning Are you using Yarn, pNPM, npm 6 or less?
|
||||
Unfortunately your package manager does not install peer dependencies automatically and you have to install them by your own. Please check the following links to find out what dependencies are needed and how to install them: [@tiptap/core](https://tiptap.dev/installation/peer-dependencies#tiptapcore), [@tiptap/starter-kit](https://tiptap.dev/installation/peer-dependencies#tiptapstarter-kit)
|
||||
:::
|
||||
|
||||
If you followed step 1 and 2, you can now start your project with `npm run dev`, and open [http://localhost:3000/](http://localhost:3000/) in your favorite browser. This might be different, if you’re working with an existing project.
|
||||
|
||||
## 3. Create a new component
|
||||
@ -57,7 +53,7 @@ const Tiptap = () => {
|
||||
)
|
||||
}
|
||||
|
||||
export default Tiptap;
|
||||
export default Tiptap
|
||||
```
|
||||
|
||||
## 4. Add it to your app
|
||||
|
@ -26,16 +26,12 @@ cd my-tiptap-project
|
||||
```
|
||||
|
||||
## 2. Install the dependencies
|
||||
Okay, enough of the boring boilerplate work. Let’s finally install Tiptap! For the following example you’ll need the `@tiptap/vue-2` package, with a few components, and `@tiptap/starter-kit` which has the most common extensions to get started quickly.
|
||||
Okay, enough of the boring boilerplate work. Let’s finally install Tiptap! For the following example you’ll need the `@tiptap/vue-2` package with a few components, the `@tiptap/pm` package, and `@tiptap/starter-kit` which has the most common extensions to get started quickly.
|
||||
|
||||
```bash
|
||||
npm install @tiptap/vue-2 @tiptap/starter-kit
|
||||
npm install @tiptap/vue-2 @tiptap/pm @tiptap/starter-kit
|
||||
```
|
||||
|
||||
:::warning Are you using Yarn, pNPM, npm 6 or less?
|
||||
Unfortunately your package manager does not install peer dependencies automatically and you have to install them by your own. Please check the following links to find out what dependencies are needed and how to install them: [@tiptap/core](https://tiptap.dev/installation/peer-dependencies#tiptapcore), [@tiptap/starter-kit](https://tiptap.dev/installation/peer-dependencies#tiptapstarter-kit)
|
||||
:::
|
||||
|
||||
If you followed step 1 and 2, you can now start your project with `npm run serve`, and open [http://localhost:8080/](http://localhost:8080/) in your favorite browser. This might be different, if you’re working with an existing project.
|
||||
|
||||
## 3. Create a new component
|
||||
|
@ -1,80 +0,0 @@
|
||||
---
|
||||
tableOfContents: true
|
||||
---
|
||||
|
||||
# Peer dependencies
|
||||
|
||||
## Introduction
|
||||
With the release of version 2.0.0-beta.205 we introduced peer dependencies. Most packages require the installation of peer dependencies.
|
||||
|
||||
## Why peer dependencies
|
||||
In the past it has happened that users installed ProseMirror or Yjs packages to develope their own extensions, which had a different version than the ones included in Tiptap. This has caused version clashes.
|
||||
|
||||
## How to install
|
||||
|
||||
### NPM 7 or higher
|
||||
If you are using NPM 7 or higher, you can ignore the following notes. NPM installs peer dependencies automatically and no further action is required.
|
||||
|
||||
### Yarn, pNPM, npm 6 or less
|
||||
|
||||
#### @tiptap/core
|
||||
|
||||
| Package manager | Command |
|
||||
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Yarn | `yarn add prosemirror-commands prosemirror-keymap prosemirror-model prosemirror-schema-list prosemirror-state prosemirror-transform prosemirror-view` |
|
||||
| pNPM | `pnpm install prosemirror-commands prosemirror-keymap prosemirror-model prosemirror-schema-list prosemirror-state prosemirror-transform prosemirror-view` |
|
||||
| npm 6 or less | `npm install prosemirror-commands prosemirror-keymap prosemirror-model prosemirror-schema-list prosemirror-state prosemirror-transform prosemirror-view` |
|
||||
|
||||
|
||||
#### @tiptap/starter-kit
|
||||
|
||||
| Package manager | Command |
|
||||
| ------------------ | ---------------------------------------------------------------------------------------------------------- |
|
||||
| Yarn | `yarn add prosemirror-history prosemirror-dropcursor prosemirror-gapcursor` |
|
||||
| pNPM | `pnpm install prosemirror-history prosemirror-dropcursor prosemirror-gapcursor` |
|
||||
| npm 6 or less | `npm install prosemirror-history prosemirror-dropcursor prosemirror-gapcursor` |
|
||||
|
||||
|
||||
#### @tiptap/extension-history
|
||||
|
||||
| Package manager | Command |
|
||||
| ------------------ | ---------------------------------------------------------------------------------------------------------- |
|
||||
| Yarn | `yarn add prosemirror-history` |
|
||||
| pNPM | `pnpm install prosemirror-history` |
|
||||
| npm 6 or less | `npm install prosemirror-history` |
|
||||
|
||||
|
||||
#### @tiptap/extension-gapcursor
|
||||
|
||||
| Package manager | Command |
|
||||
| ------------------ | ---------------------------------------------------------------------------------------------------------- |
|
||||
| Yarn | `yarn add prosemirror-gapcursor` |
|
||||
| pNPM | `pnpm install prosemirror-gapcursor` |
|
||||
| npm 6 or less | `npm install prosemirror-gapcursor` |
|
||||
|
||||
|
||||
#### @tiptap/extension-dropcursor
|
||||
|
||||
| Package manager | Command |
|
||||
| ------------------ | ---------------------------------------------------------------------------------------------------------- |
|
||||
| Yarn | `yarn add prosemirror-dropcursor` |
|
||||
| pNPM | `pnpm install prosemirror-dropcursor` |
|
||||
| npm 6 or less | `npm install prosemirror-dropcursor` |
|
||||
|
||||
|
||||
#### @tiptap/extension-collaboration
|
||||
|
||||
| Package manager | Command |
|
||||
| ------------------ | ---------------------------------------------------------------------------------------------------------- |
|
||||
| Yarn | `yarn add y-prosemirror` |
|
||||
| pNPM | `pnpm install y-prosemirror` |
|
||||
| npm 6 or less | `npm install y-prosemirror` |
|
||||
|
||||
|
||||
#### @tiptap/extension-collaboration-cursor
|
||||
|
||||
| Package manager | Command |
|
||||
| ------------------ | ---------------------------------------------------------------------------------------------------------- |
|
||||
| Yarn | `yarn add y-prosemirror` |
|
||||
| pNPM | `pnpm install y-prosemirror` |
|
||||
| npm 6 or less | `npm install y-prosemirror` |
|
@ -37,13 +37,9 @@ cd my-tiptap-project
|
||||
Time to install the `@tiptap/react` package and our [`StarterKit`](/api/extensions/starter-kit), which has the most popular extensions to get started quickly.
|
||||
|
||||
```bash
|
||||
npm install @tiptap/react @tiptap/starter-kit
|
||||
npm install @tiptap/react @tiptap/pm @tiptap/starter-kit
|
||||
```
|
||||
|
||||
:::warning Are you using Yarn, pNPM, npm 6 or less?
|
||||
Unfortunately your package manager does not install peer dependencies automatically and you have to install them by your own. Please check the following links to find out what dependencies are needed and how to install them: [@tiptap/core](https://tiptap.dev/installation/peer-dependencies#tiptapcore), [@tiptap/starter-kit](https://tiptap.dev/installation/peer-dependencies#tiptapstarter-kit)
|
||||
:::
|
||||
|
||||
If you followed step 1 and 2, you can now start your project with `npm run start`, and open [http://localhost:3000](http://localhost:3000) in your browser.
|
||||
|
||||
#### 3. Create a new component
|
||||
|
@ -28,16 +28,12 @@ npm run dev
|
||||
```
|
||||
|
||||
## 2. Install the dependencies
|
||||
Okay, enough of the boring boilerplate work. Let’s finally install Tiptap! For the following example you’ll need the `@tiptap/core` package, with a few components, and `@tiptap/starter-kit` which has the most common extensions to get started quickly.
|
||||
Okay, enough of the boring boilerplate work. Let’s finally install Tiptap! For the following example you’ll need the `@tiptap/core` package, with a few components, `@tiptap/pm` and `@tiptap/starter-kit` which has the most common extensions to get started quickly.
|
||||
|
||||
```bash
|
||||
npm install @tiptap/core @tiptap/starter-kit
|
||||
npm install @tiptap/core @tiptap/pm @tiptap/starter-kit
|
||||
```
|
||||
|
||||
:::warning Are you using Yarn, pNPM, npm 6 or less?
|
||||
Unfortunately your package manager does not install peer dependencies automatically and you have to install them by your own. Please check the following links to find out what dependencies are needed and how to install them: [@tiptap/core](https://tiptap.dev/installation/peer-dependencies#tiptapcore), [@tiptap/starter-kit](https://tiptap.dev/installation/peer-dependencies#tiptapstarter-kit)
|
||||
:::
|
||||
|
||||
If you followed step 1 and 2, you can now start your project with `npm run dev`, and open [http://localhost:3000/](http://localhost:3000/) in your favorite browser. This might be different, if you’re working with an existing project.
|
||||
|
||||
## 3. Create a new component
|
||||
|
@ -9,18 +9,14 @@ tableOfContents: true
|
||||
You are using plain JavaScript or a framework that is not listed here? No worries, we provide everything you need.
|
||||
|
||||
## 1. Install the dependencies
|
||||
For the following example you will need `@tiptap/core` (the actual editor) and `@tiptap/starter-kit`.
|
||||
For the following example you will need `@tiptap/core` (the actual editor), `@tiptap/pm` (the ProseMirror library) and `@tiptap/starter-kit`.
|
||||
|
||||
The StarterKit doesn’t include all, but the most common extensions.
|
||||
|
||||
```bash
|
||||
npm install @tiptap/core @tiptap/starter-kit
|
||||
npm install @tiptap/core @tiptap/pm @tiptap/starter-kit
|
||||
```
|
||||
|
||||
:::warning Are you using Yarn, pNPM, npm 6 or less?
|
||||
Unfortunately your package manager does not install peer dependencies automatically and you have to install them by your own. Please check the following links to find out what dependencies are needed and how to install them: [@tiptap/core](https://tiptap.dev/installation/peer-dependencies#tiptapcore), [@tiptap/starter-kit](https://tiptap.dev/installation/peer-dependencies#tiptapstarter-kit)
|
||||
:::
|
||||
|
||||
## 2. Add some markup
|
||||
Add the following HTML where you want the editor to be mounted:
|
||||
|
||||
|
@ -33,10 +33,6 @@ Okay, enough of the boring boilerplate work. Let’s finally install Tiptap! For
|
||||
npm install @tiptap/vue-2 @tiptap/starter-kit
|
||||
```
|
||||
|
||||
:::warning Are you using Yarn, pNPM, npm 6 or less?
|
||||
Unfortunately your package manager does not install peer dependencies automatically and you have to install them by your own. Please check the following links to find out what dependencies are needed and how to install them: [@tiptap/core](https://tiptap.dev/installation/peer-dependencies#tiptapcore), [@tiptap/starter-kit](https://tiptap.dev/installation/peer-dependencies#tiptapstarter-kit)
|
||||
:::
|
||||
|
||||
If you followed step 1 and 2, you can now start your project with `npm run dev`, and open [http://localhost:8080](http://localhost:8080) in your favorite browser. This might be different, if you’re working with an existing project.
|
||||
|
||||
## 3. Create a new component
|
||||
|
@ -33,10 +33,6 @@ Okay, enough of the boring boilerplate work. Let’s finally install Tiptap! For
|
||||
npm install @tiptap/vue-3 @tiptap/starter-kit
|
||||
```
|
||||
|
||||
:::warning Are you using Yarn, pNPM, npm 6 or less?
|
||||
Unfortunately your package manager does not install peer dependencies automatically and you have to install them by your own. Please check the following links to find out what dependencies are needed and how to install them: [@tiptap/core](https://tiptap.dev/installation/peer-dependencies#tiptapcore), [@tiptap/starter-kit](https://tiptap.dev/installation/peer-dependencies#tiptapstarter-kit)
|
||||
:::
|
||||
|
||||
If you followed step 1 and 2, you can now start your project with `npm run serve`, and open [http://localhost:8080](http://localhost:8080) in your favorite browser. This might be different, if you’re working with an existing project.
|
||||
|
||||
## 3. Create a new component
|
||||
|
@ -10,7 +10,7 @@ tableOfContents: true
|
||||
[![License](https://img.shields.io/npm/l/@tiptap/core.svg)](https://www.npmjs.com/package/@tiptap/core)
|
||||
[![Sponsor](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub)](https://github.com/sponsors/ueberdosis)
|
||||
|
||||
tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) – a toolkit for building rich text WYSIWYG editors, which is already in use at many well-known companies such as *New York Times*, *The Guardian* or *Atlassian*.
|
||||
Tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) – a toolkit for building rich text WYSIWYG editors, which is already in use at many well-known companies such as *New York Times*, *The Guardian* or *Atlassian*.
|
||||
|
||||
Create exactly the rich text editor you want out of customizable building blocks. Tiptap comes with sensible defaults, a lot of extensions and a friendly API to customize every aspect. It’s backed by a welcoming community, open source, and free.
|
||||
|
||||
|
@ -120,6 +120,8 @@
|
||||
items:
|
||||
- title: Configuration
|
||||
link: /guide/configuration
|
||||
- title: ProseMirror
|
||||
link: /guide/prosemirror
|
||||
- title: Menus
|
||||
link: /guide/menus
|
||||
- title: Styling
|
||||
|
863
package-lock.json
generated
863
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
@ -13,9 +13,10 @@
|
||||
"lint": "eslint --quiet --no-error-on-unmatched-pattern ./",
|
||||
"lint:fix": "eslint --fix --quiet --no-error-on-unmatched-pattern ./",
|
||||
"lint:staged": "lint-staged",
|
||||
"test:open": "cypress open --project tests",
|
||||
"test": "cypress run --project tests",
|
||||
"build": "npm run clean:packages && lerna run build",
|
||||
"test:open": "npm run build:pm && cypress open --project tests",
|
||||
"test": "npm run build:pm && cypress run --project tests",
|
||||
"build": "npm run clean:packages && npm run clean:packs && lerna run build",
|
||||
"build:pm": "npm --prefix ./packages/pm run build",
|
||||
"build:demos": "npm --prefix ./demos run build:demos",
|
||||
"build:ci": "npm run build",
|
||||
"release:major": "lerna version major --force-publish",
|
||||
@ -26,9 +27,10 @@
|
||||
"release:patch:pre": "lerna version prepatch --force-publish",
|
||||
"release:pre": "lerna version prerelease --force-publish",
|
||||
"publish": "npm run build:packages && lerna exec --since --no-private -- npm publish --access public",
|
||||
"pack": "lerna exec -- npm pack",
|
||||
"clean:packages": "rm -rf ./packages/*/dist",
|
||||
"reset": "npm run clean:packages && rm -rf ./**/.cache && rm -rf ./**/node_modules && rm -rf ./package-lock.json && npm install",
|
||||
"pack": "npm run clean:packs && lerna exec -- npm pack",
|
||||
"clean:packages": "rm -rf ./packages/*/dist && rm -rf ./packages/pm/*/dist",
|
||||
"clean:packs": "rm -rf ./packages/*/*.tgz",
|
||||
"reset": "npm run clean:packages && npm run clean:packs && rm -rf ./**/.cache && rm -rf ./**/node_modules && rm -rf ./package-lock.json && npm install",
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -56,7 +58,7 @@
|
||||
"lerna": "^5.5.1",
|
||||
"lint-staged": "^13.0.3",
|
||||
"minimist": "^1.2.5",
|
||||
"ts-loader": "^9.2.6",
|
||||
"ts-loader": "9.3.1",
|
||||
"tsup": "^6.5.0",
|
||||
"typescript": "4.7.4",
|
||||
"webpack": "^5.68.0"
|
||||
|
@ -31,22 +31,10 @@
|
||||
"dist"
|
||||
],
|
||||
"peerDependencies": {
|
||||
"prosemirror-commands": "^1.3.1",
|
||||
"prosemirror-keymap": "^1.2.0",
|
||||
"prosemirror-model": "^1.18.1",
|
||||
"prosemirror-schema-list": "^1.2.2",
|
||||
"prosemirror-state": "^1.4.1",
|
||||
"prosemirror-transform": "^1.7.0",
|
||||
"prosemirror-view": "^1.28.2"
|
||||
"@tiptap/pm": "^2.0.0-beta.209"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prosemirror-commands": "^1.3.1",
|
||||
"prosemirror-keymap": "^1.2.0",
|
||||
"prosemirror-model": "^1.18.1",
|
||||
"prosemirror-schema-list": "^1.2.2",
|
||||
"prosemirror-state": "^1.4.1",
|
||||
"prosemirror-transform": "^1.7.0",
|
||||
"prosemirror-view": "^1.28.2"
|
||||
"@tiptap/pm": "^2.0.0-beta.209"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -1,27 +1,19 @@
|
||||
import { EditorState, Transaction } from 'prosemirror-state'
|
||||
import { EditorState, Transaction } from '@tiptap/pm/state'
|
||||
|
||||
import { Editor } from './Editor'
|
||||
import { createChainableState } from './helpers/createChainableState'
|
||||
import {
|
||||
AnyCommands,
|
||||
CanCommands,
|
||||
ChainedCommands,
|
||||
CommandProps,
|
||||
SingleCommands,
|
||||
AnyCommands, CanCommands, ChainedCommands, CommandProps, SingleCommands,
|
||||
} from './types'
|
||||
|
||||
export class CommandManager {
|
||||
|
||||
editor: Editor
|
||||
|
||||
rawCommands: AnyCommands
|
||||
|
||||
customState?: EditorState
|
||||
|
||||
constructor(props: {
|
||||
editor: Editor,
|
||||
state?: EditorState,
|
||||
}) {
|
||||
constructor(props: { editor: Editor; state?: EditorState }) {
|
||||
this.editor = props.editor
|
||||
this.rawCommands = this.editor.extensionManager.commands
|
||||
this.customState = props.state
|
||||
@ -41,9 +33,8 @@ export class CommandManager {
|
||||
const { tr } = state
|
||||
const props = this.buildProps(tr)
|
||||
|
||||
return Object.fromEntries(Object
|
||||
.entries(rawCommands)
|
||||
.map(([name, command]) => {
|
||||
return Object.fromEntries(
|
||||
Object.entries(rawCommands).map(([name, command]) => {
|
||||
const method = (...args: any[]) => {
|
||||
const callback = command(...args)(props)
|
||||
|
||||
@ -55,7 +46,8 @@ export class CommandManager {
|
||||
}
|
||||
|
||||
return [name, method]
|
||||
})) as unknown as SingleCommands
|
||||
}),
|
||||
) as unknown as SingleCommands
|
||||
}
|
||||
|
||||
get chain(): () => ChainedCommands {
|
||||
@ -87,18 +79,20 @@ export class CommandManager {
|
||||
}
|
||||
|
||||
const chain = {
|
||||
...Object.fromEntries(Object.entries(rawCommands).map(([name, command]) => {
|
||||
const chainedCommand = (...args: never[]) => {
|
||||
const props = this.buildProps(tr, shouldDispatch)
|
||||
const callback = command(...args)(props)
|
||||
...Object.fromEntries(
|
||||
Object.entries(rawCommands).map(([name, command]) => {
|
||||
const chainedCommand = (...args: never[]) => {
|
||||
const props = this.buildProps(tr, shouldDispatch)
|
||||
const callback = command(...args)(props)
|
||||
|
||||
callbacks.push(callback)
|
||||
callbacks.push(callback)
|
||||
|
||||
return chain
|
||||
}
|
||||
return chain
|
||||
}
|
||||
|
||||
return [name, chainedCommand]
|
||||
})),
|
||||
return [name, chainedCommand]
|
||||
}),
|
||||
),
|
||||
run,
|
||||
} as unknown as ChainedCommands
|
||||
|
||||
@ -110,11 +104,11 @@ export class CommandManager {
|
||||
const dispatch = false
|
||||
const tr = startTr || state.tr
|
||||
const props = this.buildProps(tr, dispatch)
|
||||
const formattedCommands = Object.fromEntries(Object
|
||||
.entries(rawCommands)
|
||||
.map(([name, command]) => {
|
||||
const formattedCommands = Object.fromEntries(
|
||||
Object.entries(rawCommands).map(([name, command]) => {
|
||||
return [name, (...args: never[]) => command(...args)({ ...props, dispatch: undefined })]
|
||||
})) as unknown as SingleCommands
|
||||
}),
|
||||
) as unknown as SingleCommands
|
||||
|
||||
return {
|
||||
...formattedCommands,
|
||||
@ -138,21 +132,18 @@ export class CommandManager {
|
||||
state,
|
||||
transaction: tr,
|
||||
}),
|
||||
dispatch: shouldDispatch
|
||||
? () => undefined
|
||||
: undefined,
|
||||
dispatch: shouldDispatch ? () => undefined : undefined,
|
||||
chain: () => this.createChain(tr),
|
||||
can: () => this.createCan(tr),
|
||||
get commands() {
|
||||
return Object.fromEntries(Object
|
||||
.entries(rawCommands)
|
||||
.map(([name, command]) => {
|
||||
return Object.fromEntries(
|
||||
Object.entries(rawCommands).map(([name, command]) => {
|
||||
return [name, (...args: never[]) => command(...args)(props)]
|
||||
})) as unknown as SingleCommands
|
||||
}),
|
||||
) as unknown as SingleCommands
|
||||
},
|
||||
}
|
||||
|
||||
return props
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,11 +1,8 @@
|
||||
import { MarkType, NodeType, Schema } from 'prosemirror-model'
|
||||
import { MarkType, NodeType, Schema } from '@tiptap/pm/model'
|
||||
import {
|
||||
EditorState,
|
||||
Plugin,
|
||||
PluginKey,
|
||||
Transaction,
|
||||
} from 'prosemirror-state'
|
||||
import { EditorView } from 'prosemirror-view'
|
||||
EditorState, Plugin, PluginKey, Transaction,
|
||||
} from '@tiptap/pm/state'
|
||||
import { EditorView } from '@tiptap/pm/view'
|
||||
|
||||
import { CommandManager } from './CommandManager'
|
||||
import { EventEmitter } from './EventEmitter'
|
||||
@ -39,7 +36,6 @@ export interface HTMLElement {
|
||||
}
|
||||
|
||||
export class Editor extends EventEmitter<EditorEvents> {
|
||||
|
||||
private commandManager!: CommandManager
|
||||
|
||||
public extensionManager!: ExtensionManager
|
||||
@ -182,9 +178,7 @@ export class Editor extends EventEmitter<EditorEvents> {
|
||||
// since plugins are applied after creating the view
|
||||
// `editable` is always `true` for one tick.
|
||||
// that’s why we also have to check for `options.editable`
|
||||
return this.options.editable
|
||||
&& this.view
|
||||
&& this.view.editable
|
||||
return this.options.editable && this.view && this.view.editable
|
||||
}
|
||||
|
||||
/**
|
||||
@ -200,7 +194,10 @@ export class Editor extends EventEmitter<EditorEvents> {
|
||||
* @param plugin A ProseMirror plugin
|
||||
* @param handlePlugins Control how to merge the plugin into the existing plugins.
|
||||
*/
|
||||
public registerPlugin(plugin: Plugin, handlePlugins?: (newPlugin: Plugin, plugins: Plugin[]) => Plugin[]): void {
|
||||
public registerPlugin(
|
||||
plugin: Plugin,
|
||||
handlePlugins?: (newPlugin: Plugin, plugins: Plugin[]) => Plugin[],
|
||||
): void {
|
||||
const plugins = isFunction(handlePlugins)
|
||||
? handlePlugins(plugin, [...this.state.plugins])
|
||||
: [...this.state.plugins, plugin]
|
||||
@ -220,10 +217,8 @@ export class Editor extends EventEmitter<EditorEvents> {
|
||||
return
|
||||
}
|
||||
|
||||
const name = typeof nameOrPluginKey === 'string'
|
||||
? `${nameOrPluginKey}$`
|
||||
// @ts-ignore
|
||||
: nameOrPluginKey.key
|
||||
// @ts-ignore
|
||||
const name = typeof nameOrPluginKey === 'string' ? `${nameOrPluginKey}$` : nameOrPluginKey.key
|
||||
|
||||
const state = this.state.reconfigure({
|
||||
// @ts-ignore
|
||||
@ -237,9 +232,7 @@ export class Editor extends EventEmitter<EditorEvents> {
|
||||
* Creates an extension manager.
|
||||
*/
|
||||
private createExtensionManager(): void {
|
||||
const coreExtensions = this.options.enableCoreExtensions
|
||||
? Object.values(extensions)
|
||||
: []
|
||||
const coreExtensions = this.options.enableCoreExtensions ? Object.values(extensions) : []
|
||||
const allExtensions = [...coreExtensions, ...this.options.extensions].filter(extension => {
|
||||
return ['extension', 'node', 'mark'].includes(extension?.type)
|
||||
})
|
||||
@ -397,16 +390,12 @@ export class Editor extends EventEmitter<EditorEvents> {
|
||||
* @param name Name of the node or mark
|
||||
* @param attributes Attributes of the node or mark
|
||||
*/
|
||||
public isActive(name: string, attributes?: {}): boolean;
|
||||
public isActive(attributes: {}): boolean;
|
||||
public isActive(name: string, attributes?: {}): boolean
|
||||
public isActive(attributes: {}): boolean
|
||||
public isActive(nameOrAttributes: string, attributesOrUndefined?: {}): boolean {
|
||||
const name = typeof nameOrAttributes === 'string'
|
||||
? nameOrAttributes
|
||||
: null
|
||||
const name = typeof nameOrAttributes === 'string' ? nameOrAttributes : null
|
||||
|
||||
const attributes = typeof nameOrAttributes === 'string'
|
||||
? attributesOrUndefined
|
||||
: nameOrAttributes
|
||||
const attributes = typeof nameOrAttributes === 'string' ? attributesOrUndefined : nameOrAttributes
|
||||
|
||||
return isActive(this.state, name, attributes)
|
||||
}
|
||||
@ -429,13 +418,10 @@ export class Editor extends EventEmitter<EditorEvents> {
|
||||
* Get the document as text.
|
||||
*/
|
||||
public getText(options?: {
|
||||
blockSeparator?: string,
|
||||
textSerializers?: Record<string, TextSerializer>,
|
||||
blockSeparator?: string
|
||||
textSerializers?: Record<string, TextSerializer>
|
||||
}): string {
|
||||
const {
|
||||
blockSeparator = '\n\n',
|
||||
textSerializers = {},
|
||||
} = options || {}
|
||||
const { blockSeparator = '\n\n', textSerializers = {} } = options || {}
|
||||
|
||||
return getText(this.state.doc, {
|
||||
blockSeparator,
|
||||
@ -459,7 +445,9 @@ export class Editor extends EventEmitter<EditorEvents> {
|
||||
* @deprecated
|
||||
*/
|
||||
public getCharacterCount(): number {
|
||||
console.warn('[tiptap warn]: "editor.getCharacterCount()" is deprecated. Please use "editor.storage.characterCount.characters()" instead.')
|
||||
console.warn(
|
||||
'[tiptap warn]: "editor.getCharacterCount()" is deprecated. Please use "editor.storage.characterCount.characters()" instead.',
|
||||
)
|
||||
|
||||
return this.state.doc.content.size - 2
|
||||
}
|
||||
@ -484,5 +472,4 @@ export class Editor extends EventEmitter<EditorEvents> {
|
||||
// @ts-ignore
|
||||
return !this.view?.docView
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Plugin, Transaction } from 'prosemirror-state'
|
||||
import { Plugin, Transaction } from '@tiptap/pm/state'
|
||||
|
||||
import { ExtensionConfig } from '.'
|
||||
import { Editor } from './Editor'
|
||||
@ -20,245 +20,265 @@ import { mergeDeep } from './utilities/mergeDeep'
|
||||
|
||||
declare module '@tiptap/core' {
|
||||
interface ExtensionConfig<Options = any, Storage = any> {
|
||||
[key: string]: any;
|
||||
[key: string]: any
|
||||
|
||||
/**
|
||||
* Name
|
||||
*/
|
||||
name: string,
|
||||
name: string
|
||||
|
||||
/**
|
||||
* Priority
|
||||
*/
|
||||
priority?: number,
|
||||
priority?: number
|
||||
|
||||
/**
|
||||
* Default options
|
||||
*/
|
||||
defaultOptions?: Options,
|
||||
defaultOptions?: Options
|
||||
|
||||
/**
|
||||
* Default Options
|
||||
*/
|
||||
addOptions?: (this: {
|
||||
name: string,
|
||||
parent: Exclude<ParentConfig<ExtensionConfig<Options, Storage>>['addOptions'], undefined>,
|
||||
}) => Options,
|
||||
name: string
|
||||
parent: Exclude<ParentConfig<ExtensionConfig<Options, Storage>>['addOptions'], undefined>
|
||||
}) => Options
|
||||
|
||||
/**
|
||||
* Default Storage
|
||||
*/
|
||||
addStorage?: (this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
parent: Exclude<ParentConfig<ExtensionConfig<Options, Storage>>['addStorage'], undefined>,
|
||||
}) => Storage,
|
||||
name: string
|
||||
options: Options
|
||||
parent: Exclude<ParentConfig<ExtensionConfig<Options, Storage>>['addStorage'], undefined>
|
||||
}) => Storage
|
||||
|
||||
/**
|
||||
* Global attributes
|
||||
*/
|
||||
addGlobalAttributes?: (this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addGlobalAttributes'],
|
||||
}) => GlobalAttributes | {},
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addGlobalAttributes']
|
||||
}) => GlobalAttributes | {}
|
||||
|
||||
/**
|
||||
* Raw
|
||||
*/
|
||||
addCommands?: (this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addCommands'],
|
||||
}) => Partial<RawCommands>,
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addCommands']
|
||||
}) => Partial<RawCommands>
|
||||
|
||||
/**
|
||||
* Keyboard shortcuts
|
||||
*/
|
||||
addKeyboardShortcuts?: (this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addKeyboardShortcuts'],
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addKeyboardShortcuts']
|
||||
}) => {
|
||||
[key: string]: KeyboardShortcutCommand,
|
||||
},
|
||||
[key: string]: KeyboardShortcutCommand
|
||||
}
|
||||
|
||||
/**
|
||||
* Input rules
|
||||
*/
|
||||
addInputRules?: (this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addInputRules'],
|
||||
}) => InputRule[],
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addInputRules']
|
||||
}) => InputRule[]
|
||||
|
||||
/**
|
||||
* Paste rules
|
||||
*/
|
||||
addPasteRules?: (this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addPasteRules'],
|
||||
}) => PasteRule[],
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addPasteRules']
|
||||
}) => PasteRule[]
|
||||
|
||||
/**
|
||||
* ProseMirror plugins
|
||||
*/
|
||||
addProseMirrorPlugins?: (this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addProseMirrorPlugins'],
|
||||
}) => Plugin[],
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addProseMirrorPlugins']
|
||||
}) => Plugin[]
|
||||
|
||||
/**
|
||||
* Extensions
|
||||
*/
|
||||
addExtensions?: (this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addExtensions'],
|
||||
}) => Extensions,
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addExtensions']
|
||||
}) => Extensions
|
||||
|
||||
/**
|
||||
* Extend Node Schema
|
||||
*/
|
||||
extendNodeSchema?: ((
|
||||
this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['extendNodeSchema'],
|
||||
},
|
||||
extension: Node,
|
||||
) => Record<string, any>) | null,
|
||||
extendNodeSchema?:
|
||||
| ((
|
||||
this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['extendNodeSchema']
|
||||
},
|
||||
extension: Node,
|
||||
) => Record<string, any>)
|
||||
| null
|
||||
|
||||
/**
|
||||
* Extend Mark Schema
|
||||
*/
|
||||
extendMarkSchema?: ((
|
||||
this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['extendMarkSchema'],
|
||||
},
|
||||
extension: Mark,
|
||||
) => Record<string, any>) | null,
|
||||
extendMarkSchema?:
|
||||
| ((
|
||||
this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['extendMarkSchema']
|
||||
},
|
||||
extension: Mark,
|
||||
) => Record<string, any>)
|
||||
| null
|
||||
|
||||
/**
|
||||
* The editor is not ready yet.
|
||||
*/
|
||||
onBeforeCreate?: ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onBeforeCreate'],
|
||||
}) => void) | null,
|
||||
onBeforeCreate?:
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onBeforeCreate']
|
||||
}) => void)
|
||||
| null
|
||||
|
||||
/**
|
||||
* The editor is ready.
|
||||
*/
|
||||
onCreate?: ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onCreate'],
|
||||
}) => void) | null,
|
||||
onCreate?:
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onCreate']
|
||||
}) => void)
|
||||
| null
|
||||
|
||||
/**
|
||||
* The content has changed.
|
||||
*/
|
||||
onUpdate?: ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onUpdate'],
|
||||
}) => void) | null,
|
||||
onUpdate?:
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onUpdate']
|
||||
}) => void)
|
||||
| null
|
||||
|
||||
/**
|
||||
* The selection has changed.
|
||||
*/
|
||||
onSelectionUpdate?: ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onSelectionUpdate'],
|
||||
}) => void) | null,
|
||||
onSelectionUpdate?:
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onSelectionUpdate']
|
||||
}) => void)
|
||||
| null
|
||||
|
||||
/**
|
||||
* The editor state has changed.
|
||||
*/
|
||||
onTransaction?: ((
|
||||
this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onTransaction'],
|
||||
},
|
||||
props: {
|
||||
transaction: Transaction,
|
||||
},
|
||||
) => void) | null,
|
||||
onTransaction?:
|
||||
| ((
|
||||
this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onTransaction']
|
||||
},
|
||||
props: {
|
||||
transaction: Transaction
|
||||
},
|
||||
) => void)
|
||||
| null
|
||||
|
||||
/**
|
||||
* The editor is focused.
|
||||
*/
|
||||
onFocus?: ((
|
||||
this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onFocus'],
|
||||
},
|
||||
props: {
|
||||
event: FocusEvent,
|
||||
},
|
||||
) => void) | null,
|
||||
onFocus?:
|
||||
| ((
|
||||
this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onFocus']
|
||||
},
|
||||
props: {
|
||||
event: FocusEvent
|
||||
},
|
||||
) => void)
|
||||
| null
|
||||
|
||||
/**
|
||||
* The editor isn’t focused anymore.
|
||||
*/
|
||||
onBlur?: ((
|
||||
this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onBlur'],
|
||||
},
|
||||
props: {
|
||||
event: FocusEvent,
|
||||
},
|
||||
) => void) | null,
|
||||
onBlur?:
|
||||
| ((
|
||||
this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onBlur']
|
||||
},
|
||||
props: {
|
||||
event: FocusEvent
|
||||
},
|
||||
) => void)
|
||||
| null
|
||||
|
||||
/**
|
||||
* The editor is destroyed.
|
||||
*/
|
||||
onDestroy?: ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onDestroy'],
|
||||
}) => void) | null,
|
||||
onDestroy?:
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onDestroy']
|
||||
}) => void)
|
||||
| null
|
||||
}
|
||||
}
|
||||
|
||||
@ -289,30 +309,28 @@ export class Extension<Options = any, Storage = any> {
|
||||
this.name = this.config.name
|
||||
|
||||
if (config.defaultOptions) {
|
||||
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`)
|
||||
console.warn(
|
||||
`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`,
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: remove `addOptions` fallback
|
||||
this.options = this.config.defaultOptions
|
||||
|
||||
if (this.config.addOptions) {
|
||||
this.options = callOrReturn(getExtensionField<AnyConfig['addOptions']>(
|
||||
this,
|
||||
'addOptions',
|
||||
{
|
||||
this.options = callOrReturn(
|
||||
getExtensionField<AnyConfig['addOptions']>(this, 'addOptions', {
|
||||
name: this.name,
|
||||
},
|
||||
))
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
this.storage = callOrReturn(getExtensionField<AnyConfig['addStorage']>(
|
||||
this,
|
||||
'addStorage',
|
||||
{
|
||||
this.storage = callOrReturn(
|
||||
getExtensionField<AnyConfig['addStorage']>(this, 'addStorage', {
|
||||
name: this.name,
|
||||
options: this.options,
|
||||
},
|
||||
)) || {}
|
||||
}),
|
||||
) || {}
|
||||
}
|
||||
|
||||
static create<O = any, S = any>(config: Partial<ExtensionConfig<O, S>> = {}) {
|
||||
@ -326,49 +344,45 @@ export class Extension<Options = any, Storage = any> {
|
||||
|
||||
extension.options = mergeDeep(this.options as Record<string, any>, options) as Options
|
||||
|
||||
extension.storage = callOrReturn(getExtensionField<AnyConfig['addStorage']>(
|
||||
extension,
|
||||
'addStorage',
|
||||
{
|
||||
extension.storage = callOrReturn(
|
||||
getExtensionField<AnyConfig['addStorage']>(extension, 'addStorage', {
|
||||
name: extension.name,
|
||||
options: extension.options,
|
||||
},
|
||||
))
|
||||
}),
|
||||
)
|
||||
|
||||
return extension
|
||||
}
|
||||
|
||||
extend<ExtendedOptions = Options, ExtendedStorage = Storage>(extendedConfig: Partial<ExtensionConfig<ExtendedOptions, ExtendedStorage>> = {}) {
|
||||
extend<ExtendedOptions = Options, ExtendedStorage = Storage>(
|
||||
extendedConfig: Partial<ExtensionConfig<ExtendedOptions, ExtendedStorage>> = {},
|
||||
) {
|
||||
const extension = new Extension<ExtendedOptions, ExtendedStorage>(extendedConfig)
|
||||
|
||||
extension.parent = this
|
||||
|
||||
this.child = extension
|
||||
|
||||
extension.name = extendedConfig.name
|
||||
? extendedConfig.name
|
||||
: extension.parent.name
|
||||
extension.name = extendedConfig.name ? extendedConfig.name : extension.parent.name
|
||||
|
||||
if (extendedConfig.defaultOptions) {
|
||||
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`)
|
||||
console.warn(
|
||||
`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`,
|
||||
)
|
||||
}
|
||||
|
||||
extension.options = callOrReturn(getExtensionField<AnyConfig['addOptions']>(
|
||||
extension,
|
||||
'addOptions',
|
||||
{
|
||||
extension.options = callOrReturn(
|
||||
getExtensionField<AnyConfig['addOptions']>(extension, 'addOptions', {
|
||||
name: extension.name,
|
||||
},
|
||||
))
|
||||
}),
|
||||
)
|
||||
|
||||
extension.storage = callOrReturn(getExtensionField<AnyConfig['addStorage']>(
|
||||
extension,
|
||||
'addStorage',
|
||||
{
|
||||
extension.storage = callOrReturn(
|
||||
getExtensionField<AnyConfig['addStorage']>(extension, 'addStorage', {
|
||||
name: extension.name,
|
||||
options: extension.options,
|
||||
},
|
||||
))
|
||||
}),
|
||||
)
|
||||
|
||||
return extension
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { keymap } from 'prosemirror-keymap'
|
||||
import { Node as ProsemirrorNode, Schema } from 'prosemirror-model'
|
||||
import { Plugin } from 'prosemirror-state'
|
||||
import { Decoration, EditorView } from 'prosemirror-view'
|
||||
import { keymap } from '@tiptap/pm/keymap'
|
||||
import { Node as ProsemirrorNode, Schema } from '@tiptap/pm/model'
|
||||
import { Plugin } from '@tiptap/pm/state'
|
||||
import { Decoration, EditorView } from '@tiptap/pm/view'
|
||||
|
||||
import { Mark, NodeConfig } from '.'
|
||||
import { Editor } from './Editor'
|
||||
@ -20,7 +20,6 @@ import { callOrReturn } from './utilities/callOrReturn'
|
||||
import { findDuplicates } from './utilities/findDuplicates'
|
||||
|
||||
export class ExtensionManager {
|
||||
|
||||
editor: Editor
|
||||
|
||||
schema: Schema
|
||||
@ -64,21 +63,13 @@ export class ExtensionManager {
|
||||
this.editor.on('beforeCreate', onBeforeCreate)
|
||||
}
|
||||
|
||||
const onCreate = getExtensionField<AnyConfig['onCreate']>(
|
||||
extension,
|
||||
'onCreate',
|
||||
context,
|
||||
)
|
||||
const onCreate = getExtensionField<AnyConfig['onCreate']>(extension, 'onCreate', context)
|
||||
|
||||
if (onCreate) {
|
||||
this.editor.on('create', onCreate)
|
||||
}
|
||||
|
||||
const onUpdate = getExtensionField<AnyConfig['onUpdate']>(
|
||||
extension,
|
||||
'onUpdate',
|
||||
context,
|
||||
)
|
||||
const onUpdate = getExtensionField<AnyConfig['onUpdate']>(extension, 'onUpdate', context)
|
||||
|
||||
if (onUpdate) {
|
||||
this.editor.on('update', onUpdate)
|
||||
@ -104,31 +95,19 @@ export class ExtensionManager {
|
||||
this.editor.on('transaction', onTransaction)
|
||||
}
|
||||
|
||||
const onFocus = getExtensionField<AnyConfig['onFocus']>(
|
||||
extension,
|
||||
'onFocus',
|
||||
context,
|
||||
)
|
||||
const onFocus = getExtensionField<AnyConfig['onFocus']>(extension, 'onFocus', context)
|
||||
|
||||
if (onFocus) {
|
||||
this.editor.on('focus', onFocus)
|
||||
}
|
||||
|
||||
const onBlur = getExtensionField<AnyConfig['onBlur']>(
|
||||
extension,
|
||||
'onBlur',
|
||||
context,
|
||||
)
|
||||
const onBlur = getExtensionField<AnyConfig['onBlur']>(extension, 'onBlur', context)
|
||||
|
||||
if (onBlur) {
|
||||
this.editor.on('blur', onBlur)
|
||||
}
|
||||
|
||||
const onDestroy = getExtensionField<AnyConfig['onDestroy']>(
|
||||
extension,
|
||||
'onDestroy',
|
||||
context,
|
||||
)
|
||||
const onDestroy = getExtensionField<AnyConfig['onDestroy']>(extension, 'onDestroy', context)
|
||||
|
||||
if (onDestroy) {
|
||||
this.editor.on('destroy', onDestroy)
|
||||
@ -141,38 +120,41 @@ export class ExtensionManager {
|
||||
const duplicatedNames = findDuplicates(resolvedExtensions.map(extension => extension.name))
|
||||
|
||||
if (duplicatedNames.length) {
|
||||
console.warn(`[tiptap warn]: Duplicate extension names found: [${duplicatedNames.map(item => `'${item}'`).join(', ')}]. This can lead to issues.`)
|
||||
console.warn(
|
||||
`[tiptap warn]: Duplicate extension names found: [${duplicatedNames
|
||||
.map(item => `'${item}'`)
|
||||
.join(', ')}]. This can lead to issues.`,
|
||||
)
|
||||
}
|
||||
|
||||
return resolvedExtensions
|
||||
}
|
||||
|
||||
static flatten(extensions: Extensions): Extensions {
|
||||
return extensions
|
||||
.map(extension => {
|
||||
const context = {
|
||||
name: extension.name,
|
||||
options: extension.options,
|
||||
storage: extension.storage,
|
||||
}
|
||||
return (
|
||||
extensions
|
||||
.map(extension => {
|
||||
const context = {
|
||||
name: extension.name,
|
||||
options: extension.options,
|
||||
storage: extension.storage,
|
||||
}
|
||||
|
||||
const addExtensions = getExtensionField<AnyConfig['addExtensions']>(
|
||||
extension,
|
||||
'addExtensions',
|
||||
context,
|
||||
)
|
||||
|
||||
if (addExtensions) {
|
||||
return [
|
||||
const addExtensions = getExtensionField<AnyConfig['addExtensions']>(
|
||||
extension,
|
||||
...this.flatten(addExtensions()),
|
||||
]
|
||||
}
|
||||
'addExtensions',
|
||||
context,
|
||||
)
|
||||
|
||||
return extension
|
||||
})
|
||||
// `Infinity` will break TypeScript so we set a number that is probably high enough
|
||||
.flat(10)
|
||||
if (addExtensions) {
|
||||
return [extension, ...this.flatten(addExtensions())]
|
||||
}
|
||||
|
||||
return extension
|
||||
})
|
||||
// `Infinity` will break TypeScript so we set a number that is probably high enough
|
||||
.flat(10)
|
||||
)
|
||||
}
|
||||
|
||||
static sort(extensions: Extensions): Extensions {
|
||||
@ -256,16 +238,14 @@ export class ExtensionManager {
|
||||
|
||||
// bind exit handling
|
||||
if (extension.type === 'mark' && extension.config.exitable) {
|
||||
defaultBindings.ArrowRight = () => Mark.handleExit({ editor, mark: (extension as Mark) })
|
||||
defaultBindings.ArrowRight = () => Mark.handleExit({ editor, mark: extension as Mark })
|
||||
}
|
||||
|
||||
if (addKeyboardShortcuts) {
|
||||
const bindings = Object.fromEntries(
|
||||
Object
|
||||
.entries(addKeyboardShortcuts())
|
||||
.map(([shortcut, method]) => {
|
||||
return [shortcut, () => method({ editor })]
|
||||
}),
|
||||
Object.entries(addKeyboardShortcuts()).map(([shortcut, method]) => {
|
||||
return [shortcut, () => method({ editor })]
|
||||
}),
|
||||
)
|
||||
|
||||
defaultBindings = { ...defaultBindings, ...bindings }
|
||||
@ -332,46 +312,50 @@ export class ExtensionManager {
|
||||
const { editor } = this
|
||||
const { nodeExtensions } = splitExtensions(this.extensions)
|
||||
|
||||
return Object.fromEntries(nodeExtensions
|
||||
.filter(extension => !!getExtensionField(extension, 'addNodeView'))
|
||||
.map(extension => {
|
||||
const extensionAttributes = this.attributes.filter(attribute => attribute.type === extension.name)
|
||||
const context = {
|
||||
name: extension.name,
|
||||
options: extension.options,
|
||||
storage: extension.storage,
|
||||
editor,
|
||||
type: getNodeType(extension.name, this.schema),
|
||||
}
|
||||
const addNodeView = getExtensionField<NodeConfig['addNodeView']>(
|
||||
extension,
|
||||
'addNodeView',
|
||||
context,
|
||||
)
|
||||
|
||||
if (!addNodeView) {
|
||||
return []
|
||||
}
|
||||
|
||||
const nodeview = (
|
||||
node: ProsemirrorNode,
|
||||
view: EditorView,
|
||||
getPos: (() => number) | boolean,
|
||||
decorations: Decoration[],
|
||||
) => {
|
||||
const HTMLAttributes = getRenderedAttributes(node, extensionAttributes)
|
||||
|
||||
return addNodeView()({
|
||||
return Object.fromEntries(
|
||||
nodeExtensions
|
||||
.filter(extension => !!getExtensionField(extension, 'addNodeView'))
|
||||
.map(extension => {
|
||||
const extensionAttributes = this.attributes.filter(
|
||||
attribute => attribute.type === extension.name,
|
||||
)
|
||||
const context = {
|
||||
name: extension.name,
|
||||
options: extension.options,
|
||||
storage: extension.storage,
|
||||
editor,
|
||||
node,
|
||||
getPos,
|
||||
decorations,
|
||||
HTMLAttributes,
|
||||
type: getNodeType(extension.name, this.schema),
|
||||
}
|
||||
const addNodeView = getExtensionField<NodeConfig['addNodeView']>(
|
||||
extension,
|
||||
})
|
||||
}
|
||||
'addNodeView',
|
||||
context,
|
||||
)
|
||||
|
||||
return [extension.name, nodeview]
|
||||
}))
|
||||
if (!addNodeView) {
|
||||
return []
|
||||
}
|
||||
|
||||
const nodeview = (
|
||||
node: ProsemirrorNode,
|
||||
view: EditorView,
|
||||
getPos: (() => number) | boolean,
|
||||
decorations: Decoration[],
|
||||
) => {
|
||||
const HTMLAttributes = getRenderedAttributes(node, extensionAttributes)
|
||||
|
||||
return addNodeView()({
|
||||
editor,
|
||||
node,
|
||||
getPos,
|
||||
decorations,
|
||||
HTMLAttributes,
|
||||
extension,
|
||||
})
|
||||
}
|
||||
|
||||
return [extension.name, nodeview]
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { EditorState, Plugin, TextSelection } from 'prosemirror-state'
|
||||
import { EditorState, Plugin, TextSelection } from '@tiptap/pm/state'
|
||||
|
||||
import { CommandManager } from './CommandManager'
|
||||
import { Editor } from './Editor'
|
||||
@ -14,46 +14,47 @@ import {
|
||||
import { isRegExp } from './utilities/isRegExp'
|
||||
|
||||
export type InputRuleMatch = {
|
||||
index: number,
|
||||
text: string,
|
||||
replaceWith?: string,
|
||||
match?: RegExpMatchArray,
|
||||
data?: Record<string, any>,
|
||||
index: number
|
||||
text: string
|
||||
replaceWith?: string
|
||||
match?: RegExpMatchArray
|
||||
data?: Record<string, any>
|
||||
}
|
||||
|
||||
export type InputRuleFinder =
|
||||
| RegExp
|
||||
| ((text: string) => InputRuleMatch | null)
|
||||
export type InputRuleFinder = RegExp | ((text: string) => InputRuleMatch | null)
|
||||
|
||||
export class InputRule {
|
||||
find: InputRuleFinder
|
||||
|
||||
handler: (props: {
|
||||
state: EditorState,
|
||||
range: Range,
|
||||
match: ExtendedRegExpMatchArray,
|
||||
commands: SingleCommands,
|
||||
chain: () => ChainedCommands,
|
||||
can: () => CanCommands,
|
||||
state: EditorState
|
||||
range: Range
|
||||
match: ExtendedRegExpMatchArray
|
||||
commands: SingleCommands
|
||||
chain: () => ChainedCommands
|
||||
can: () => CanCommands
|
||||
}) => void | null
|
||||
|
||||
constructor(config: {
|
||||
find: InputRuleFinder,
|
||||
find: InputRuleFinder
|
||||
handler: (props: {
|
||||
state: EditorState,
|
||||
range: Range,
|
||||
match: ExtendedRegExpMatchArray,
|
||||
commands: SingleCommands,
|
||||
chain: () => ChainedCommands,
|
||||
can: () => CanCommands,
|
||||
}) => void | null,
|
||||
state: EditorState
|
||||
range: Range
|
||||
match: ExtendedRegExpMatchArray
|
||||
commands: SingleCommands
|
||||
chain: () => ChainedCommands
|
||||
can: () => CanCommands
|
||||
}) => void | null
|
||||
}) {
|
||||
this.find = config.find
|
||||
this.handler = config.handler
|
||||
}
|
||||
}
|
||||
|
||||
const inputRuleMatcherHandler = (text: string, find: InputRuleFinder): ExtendedRegExpMatchArray | null => {
|
||||
const inputRuleMatcherHandler = (
|
||||
text: string,
|
||||
find: InputRuleFinder,
|
||||
): ExtendedRegExpMatchArray | null => {
|
||||
if (isRegExp(find)) {
|
||||
return find.exec(text)
|
||||
}
|
||||
@ -72,7 +73,9 @@ const inputRuleMatcherHandler = (text: string, find: InputRuleFinder): ExtendedR
|
||||
|
||||
if (inputRuleMatch.replaceWith) {
|
||||
if (!inputRuleMatch.text.includes(inputRuleMatch.replaceWith)) {
|
||||
console.warn('[tiptap warn]: "inputRuleMatch.replaceWith" must be part of "inputRuleMatch.text".')
|
||||
console.warn(
|
||||
'[tiptap warn]: "inputRuleMatch.replaceWith" must be part of "inputRuleMatch.text".',
|
||||
)
|
||||
}
|
||||
|
||||
result.push(inputRuleMatch.replaceWith)
|
||||
@ -82,20 +85,15 @@ const inputRuleMatcherHandler = (text: string, find: InputRuleFinder): ExtendedR
|
||||
}
|
||||
|
||||
function run(config: {
|
||||
editor: Editor,
|
||||
from: number,
|
||||
to: number,
|
||||
text: string,
|
||||
rules: InputRule[],
|
||||
plugin: Plugin,
|
||||
editor: Editor
|
||||
from: number
|
||||
to: number
|
||||
text: string
|
||||
rules: InputRule[]
|
||||
plugin: Plugin
|
||||
}): boolean {
|
||||
const {
|
||||
editor,
|
||||
from,
|
||||
to,
|
||||
text,
|
||||
rules,
|
||||
plugin,
|
||||
editor, from, to, text, rules, plugin,
|
||||
} = config
|
||||
const { view } = editor
|
||||
|
||||
@ -179,7 +177,7 @@ function run(config: {
|
||||
* input that matches any of the given rules to trigger the rule’s
|
||||
* action.
|
||||
*/
|
||||
export function inputRulesPlugin(props: { editor: Editor, rules: InputRule[] }): Plugin {
|
||||
export function inputRulesPlugin(props: { editor: Editor; rules: InputRule[] }): Plugin {
|
||||
const { editor, rules } = props
|
||||
const plugin = new Plugin({
|
||||
state: {
|
||||
@ -193,9 +191,7 @@ export function inputRulesPlugin(props: { editor: Editor, rules: InputRule[] }):
|
||||
return stored
|
||||
}
|
||||
|
||||
return tr.selectionSet || tr.docChanged
|
||||
? null
|
||||
: prev
|
||||
return tr.selectionSet || tr.docChanged ? null : prev
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -1,10 +1,7 @@
|
||||
import {
|
||||
DOMOutputSpec,
|
||||
Mark as ProseMirrorMark,
|
||||
MarkSpec,
|
||||
MarkType,
|
||||
} from 'prosemirror-model'
|
||||
import { Plugin, Transaction } from 'prosemirror-state'
|
||||
DOMOutputSpec, Mark as ProseMirrorMark, MarkSpec, MarkType,
|
||||
} from '@tiptap/pm/model'
|
||||
import { Plugin, Transaction } from '@tiptap/pm/state'
|
||||
|
||||
import { MarkConfig } from '.'
|
||||
import { Editor } from './Editor'
|
||||
@ -26,358 +23,386 @@ import { mergeDeep } from './utilities/mergeDeep'
|
||||
|
||||
declare module '@tiptap/core' {
|
||||
export interface MarkConfig<Options = any, Storage = any> {
|
||||
[key: string]: any;
|
||||
[key: string]: any
|
||||
|
||||
/**
|
||||
* Name
|
||||
*/
|
||||
name: string,
|
||||
name: string
|
||||
|
||||
/**
|
||||
* Priority
|
||||
*/
|
||||
priority?: number,
|
||||
priority?: number
|
||||
|
||||
/**
|
||||
* Default options
|
||||
*/
|
||||
defaultOptions?: Options,
|
||||
defaultOptions?: Options
|
||||
|
||||
/**
|
||||
* Default Options
|
||||
*/
|
||||
addOptions?: (this: {
|
||||
name: string,
|
||||
parent: Exclude<ParentConfig<MarkConfig<Options, Storage>>['addOptions'], undefined>,
|
||||
}) => Options,
|
||||
name: string
|
||||
parent: Exclude<ParentConfig<MarkConfig<Options, Storage>>['addOptions'], undefined>
|
||||
}) => Options
|
||||
|
||||
/**
|
||||
* Default Storage
|
||||
*/
|
||||
addStorage?: (this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
parent: Exclude<ParentConfig<MarkConfig<Options, Storage>>['addStorage'], undefined>,
|
||||
}) => Storage,
|
||||
name: string
|
||||
options: Options
|
||||
parent: Exclude<ParentConfig<MarkConfig<Options, Storage>>['addStorage'], undefined>
|
||||
}) => Storage
|
||||
|
||||
/**
|
||||
* Global attributes
|
||||
*/
|
||||
addGlobalAttributes?: (this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['addGlobalAttributes'],
|
||||
}) => GlobalAttributes | {},
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['addGlobalAttributes']
|
||||
}) => GlobalAttributes | {}
|
||||
|
||||
/**
|
||||
* Raw
|
||||
*/
|
||||
addCommands?: (this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
type: MarkType,
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['addCommands'],
|
||||
}) => Partial<RawCommands>,
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
type: MarkType
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['addCommands']
|
||||
}) => Partial<RawCommands>
|
||||
|
||||
/**
|
||||
* Keyboard shortcuts
|
||||
*/
|
||||
addKeyboardShortcuts?: (this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
type: MarkType,
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['addKeyboardShortcuts'],
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
type: MarkType
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['addKeyboardShortcuts']
|
||||
}) => {
|
||||
[key: string]: KeyboardShortcutCommand,
|
||||
},
|
||||
[key: string]: KeyboardShortcutCommand
|
||||
}
|
||||
|
||||
/**
|
||||
* Input rules
|
||||
*/
|
||||
addInputRules?: (this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
type: MarkType,
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['addInputRules'],
|
||||
}) => InputRule[],
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
type: MarkType
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['addInputRules']
|
||||
}) => InputRule[]
|
||||
|
||||
/**
|
||||
* Paste rules
|
||||
*/
|
||||
addPasteRules?: (this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
type: MarkType,
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['addPasteRules'],
|
||||
}) => PasteRule[],
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
type: MarkType
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['addPasteRules']
|
||||
}) => PasteRule[]
|
||||
|
||||
/**
|
||||
* ProseMirror plugins
|
||||
*/
|
||||
addProseMirrorPlugins?: (this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
type: MarkType,
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['addProseMirrorPlugins'],
|
||||
}) => Plugin[],
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
type: MarkType
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['addProseMirrorPlugins']
|
||||
}) => Plugin[]
|
||||
|
||||
/**
|
||||
* Extensions
|
||||
*/
|
||||
addExtensions?: (this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['addExtensions'],
|
||||
}) => Extensions,
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['addExtensions']
|
||||
}) => Extensions
|
||||
|
||||
/**
|
||||
* Extend Node Schema
|
||||
*/
|
||||
extendNodeSchema?: ((
|
||||
this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['extendNodeSchema'],
|
||||
},
|
||||
extension: Node,
|
||||
) => Record<string, any>) | null,
|
||||
extendNodeSchema?:
|
||||
| ((
|
||||
this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['extendNodeSchema']
|
||||
},
|
||||
extension: Node,
|
||||
) => Record<string, any>)
|
||||
| null
|
||||
|
||||
/**
|
||||
* Extend Mark Schema
|
||||
*/
|
||||
extendMarkSchema?: ((
|
||||
this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['extendMarkSchema'],
|
||||
},
|
||||
extension: Mark,
|
||||
) => Record<string, any>) | null,
|
||||
extendMarkSchema?:
|
||||
| ((
|
||||
this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['extendMarkSchema']
|
||||
},
|
||||
extension: Mark,
|
||||
) => Record<string, any>)
|
||||
| null
|
||||
|
||||
/**
|
||||
* The editor is not ready yet.
|
||||
*/
|
||||
onBeforeCreate?: ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
type: MarkType,
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['onBeforeCreate'],
|
||||
}) => void) | null,
|
||||
onBeforeCreate?:
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
type: MarkType
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['onBeforeCreate']
|
||||
}) => void)
|
||||
| null
|
||||
|
||||
/**
|
||||
* The editor is ready.
|
||||
*/
|
||||
onCreate?: ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
type: MarkType,
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['onCreate'],
|
||||
}) => void) | null,
|
||||
onCreate?:
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
type: MarkType
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['onCreate']
|
||||
}) => void)
|
||||
| null
|
||||
|
||||
/**
|
||||
* The content has changed.
|
||||
*/
|
||||
onUpdate?: ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
type: MarkType,
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['onUpdate'],
|
||||
}) => void) | null,
|
||||
onUpdate?:
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
type: MarkType
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['onUpdate']
|
||||
}) => void)
|
||||
| null
|
||||
|
||||
/**
|
||||
* The selection has changed.
|
||||
*/
|
||||
onSelectionUpdate?: ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
type: MarkType,
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['onSelectionUpdate'],
|
||||
}) => void) | null,
|
||||
onSelectionUpdate?:
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
type: MarkType
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['onSelectionUpdate']
|
||||
}) => void)
|
||||
| null
|
||||
|
||||
/**
|
||||
* The editor state has changed.
|
||||
*/
|
||||
onTransaction?: ((
|
||||
this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
type: MarkType,
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['onTransaction'],
|
||||
},
|
||||
props: {
|
||||
transaction: Transaction,
|
||||
},
|
||||
) => void) | null,
|
||||
onTransaction?:
|
||||
| ((
|
||||
this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
type: MarkType
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['onTransaction']
|
||||
},
|
||||
props: {
|
||||
transaction: Transaction
|
||||
},
|
||||
) => void)
|
||||
| null
|
||||
|
||||
/**
|
||||
* The editor is focused.
|
||||
*/
|
||||
onFocus?: ((
|
||||
this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
type: MarkType,
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['onFocus'],
|
||||
},
|
||||
props: {
|
||||
event: FocusEvent,
|
||||
},
|
||||
) => void) | null,
|
||||
onFocus?:
|
||||
| ((
|
||||
this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
type: MarkType
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['onFocus']
|
||||
},
|
||||
props: {
|
||||
event: FocusEvent
|
||||
},
|
||||
) => void)
|
||||
| null
|
||||
|
||||
/**
|
||||
* The editor isn’t focused anymore.
|
||||
*/
|
||||
onBlur?: ((
|
||||
this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
type: MarkType,
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['onBlur'],
|
||||
},
|
||||
props: {
|
||||
event: FocusEvent,
|
||||
},
|
||||
) => void) | null,
|
||||
onBlur?:
|
||||
| ((
|
||||
this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
type: MarkType
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['onBlur']
|
||||
},
|
||||
props: {
|
||||
event: FocusEvent
|
||||
},
|
||||
) => void)
|
||||
| null
|
||||
|
||||
/**
|
||||
* The editor is destroyed.
|
||||
*/
|
||||
onDestroy?: ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
type: MarkType,
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['onDestroy'],
|
||||
}) => void) | null,
|
||||
onDestroy?:
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
type: MarkType
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['onDestroy']
|
||||
}) => void)
|
||||
| null
|
||||
|
||||
/**
|
||||
* Keep mark after split node
|
||||
*/
|
||||
keepOnSplit?: boolean | (() => boolean),
|
||||
keepOnSplit?: boolean | (() => boolean)
|
||||
|
||||
/**
|
||||
* Inclusive
|
||||
*/
|
||||
inclusive?: MarkSpec['inclusive'] | ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['inclusive'],
|
||||
}) => MarkSpec['inclusive']),
|
||||
inclusive?:
|
||||
| MarkSpec['inclusive']
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['inclusive']
|
||||
}) => MarkSpec['inclusive'])
|
||||
|
||||
/**
|
||||
* Excludes
|
||||
*/
|
||||
excludes?: MarkSpec['excludes'] | ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['excludes'],
|
||||
}) => MarkSpec['excludes']),
|
||||
excludes?:
|
||||
| MarkSpec['excludes']
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['excludes']
|
||||
}) => MarkSpec['excludes'])
|
||||
|
||||
/**
|
||||
* Marks this Mark as exitable
|
||||
*/
|
||||
exitable?: boolean | (() => boolean),
|
||||
exitable?: boolean | (() => boolean)
|
||||
|
||||
/**
|
||||
* Group
|
||||
*/
|
||||
group?: MarkSpec['group'] | ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['group'],
|
||||
}) => MarkSpec['group']),
|
||||
group?:
|
||||
| MarkSpec['group']
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['group']
|
||||
}) => MarkSpec['group'])
|
||||
|
||||
/**
|
||||
* Spanning
|
||||
*/
|
||||
spanning?: MarkSpec['spanning'] | ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['spanning'],
|
||||
}) => MarkSpec['spanning']),
|
||||
spanning?:
|
||||
| MarkSpec['spanning']
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['spanning']
|
||||
}) => MarkSpec['spanning'])
|
||||
|
||||
/**
|
||||
* Code
|
||||
*/
|
||||
code?: boolean | ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['code'],
|
||||
}) => boolean),
|
||||
code?:
|
||||
| boolean
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['code']
|
||||
}) => boolean)
|
||||
|
||||
/**
|
||||
* Parse HTML
|
||||
*/
|
||||
parseHTML?: (
|
||||
this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['parseHTML'],
|
||||
},
|
||||
) => MarkSpec['parseDOM'],
|
||||
parseHTML?: (this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['parseHTML']
|
||||
}) => MarkSpec['parseDOM']
|
||||
|
||||
/**
|
||||
* Render HTML
|
||||
*/
|
||||
renderHTML?: ((
|
||||
this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['renderHTML'],
|
||||
},
|
||||
props: {
|
||||
mark: ProseMirrorMark,
|
||||
HTMLAttributes: Record<string, any>,
|
||||
},
|
||||
) => DOMOutputSpec) | null,
|
||||
renderHTML?:
|
||||
| ((
|
||||
this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['renderHTML']
|
||||
},
|
||||
props: {
|
||||
mark: ProseMirrorMark
|
||||
HTMLAttributes: Record<string, any>
|
||||
},
|
||||
) => DOMOutputSpec)
|
||||
| null
|
||||
|
||||
/**
|
||||
* Attributes
|
||||
*/
|
||||
addAttributes?: (
|
||||
this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['addAttributes'],
|
||||
},
|
||||
) => Attributes | {},
|
||||
addAttributes?: (this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<MarkConfig<Options, Storage>>['addAttributes']
|
||||
}) => Attributes | {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -408,30 +433,28 @@ export class Mark<Options = any, Storage = any> {
|
||||
this.name = this.config.name
|
||||
|
||||
if (config.defaultOptions) {
|
||||
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`)
|
||||
console.warn(
|
||||
`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`,
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: remove `addOptions` fallback
|
||||
this.options = this.config.defaultOptions
|
||||
|
||||
if (this.config.addOptions) {
|
||||
this.options = callOrReturn(getExtensionField<AnyConfig['addOptions']>(
|
||||
this,
|
||||
'addOptions',
|
||||
{
|
||||
this.options = callOrReturn(
|
||||
getExtensionField<AnyConfig['addOptions']>(this, 'addOptions', {
|
||||
name: this.name,
|
||||
},
|
||||
))
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
this.storage = callOrReturn(getExtensionField<AnyConfig['addStorage']>(
|
||||
this,
|
||||
'addStorage',
|
||||
{
|
||||
this.storage = callOrReturn(
|
||||
getExtensionField<AnyConfig['addStorage']>(this, 'addStorage', {
|
||||
name: this.name,
|
||||
options: this.options,
|
||||
},
|
||||
)) || {}
|
||||
}),
|
||||
) || {}
|
||||
}
|
||||
|
||||
static create<O = any, S = any>(config: Partial<MarkConfig<O, S>> = {}) {
|
||||
@ -445,60 +468,50 @@ export class Mark<Options = any, Storage = any> {
|
||||
|
||||
extension.options = mergeDeep(this.options as Record<string, any>, options) as Options
|
||||
|
||||
extension.storage = callOrReturn(getExtensionField<AnyConfig['addStorage']>(
|
||||
extension,
|
||||
'addStorage',
|
||||
{
|
||||
extension.storage = callOrReturn(
|
||||
getExtensionField<AnyConfig['addStorage']>(extension, 'addStorage', {
|
||||
name: extension.name,
|
||||
options: extension.options,
|
||||
},
|
||||
))
|
||||
}),
|
||||
)
|
||||
|
||||
return extension
|
||||
}
|
||||
|
||||
extend<ExtendedOptions = Options, ExtendedStorage = Storage>(extendedConfig: Partial<MarkConfig<ExtendedOptions, ExtendedStorage>> = {}) {
|
||||
extend<ExtendedOptions = Options, ExtendedStorage = Storage>(
|
||||
extendedConfig: Partial<MarkConfig<ExtendedOptions, ExtendedStorage>> = {},
|
||||
) {
|
||||
const extension = new Mark<ExtendedOptions, ExtendedStorage>(extendedConfig)
|
||||
|
||||
extension.parent = this
|
||||
|
||||
this.child = extension
|
||||
|
||||
extension.name = extendedConfig.name
|
||||
? extendedConfig.name
|
||||
: extension.parent.name
|
||||
extension.name = extendedConfig.name ? extendedConfig.name : extension.parent.name
|
||||
|
||||
if (extendedConfig.defaultOptions) {
|
||||
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`)
|
||||
console.warn(
|
||||
`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`,
|
||||
)
|
||||
}
|
||||
|
||||
extension.options = callOrReturn(getExtensionField<AnyConfig['addOptions']>(
|
||||
extension,
|
||||
'addOptions',
|
||||
{
|
||||
extension.options = callOrReturn(
|
||||
getExtensionField<AnyConfig['addOptions']>(extension, 'addOptions', {
|
||||
name: extension.name,
|
||||
},
|
||||
))
|
||||
}),
|
||||
)
|
||||
|
||||
extension.storage = callOrReturn(getExtensionField<AnyConfig['addStorage']>(
|
||||
extension,
|
||||
'addStorage',
|
||||
{
|
||||
extension.storage = callOrReturn(
|
||||
getExtensionField<AnyConfig['addStorage']>(extension, 'addStorage', {
|
||||
name: extension.name,
|
||||
options: extension.options,
|
||||
},
|
||||
))
|
||||
}),
|
||||
)
|
||||
|
||||
return extension
|
||||
}
|
||||
|
||||
static handleExit({
|
||||
editor,
|
||||
mark,
|
||||
}: {
|
||||
editor: Editor
|
||||
mark: Mark
|
||||
}) {
|
||||
static handleExit({ editor, mark }: { editor: Editor; mark: Mark }) {
|
||||
const { tr } = editor.state
|
||||
const currentPos = editor.state.selection.$from
|
||||
const isAtEnd = currentPos.pos === currentPos.end()
|
||||
|
@ -1,10 +1,7 @@
|
||||
import {
|
||||
DOMOutputSpec,
|
||||
Node as ProseMirrorNode,
|
||||
NodeSpec,
|
||||
NodeType,
|
||||
} from 'prosemirror-model'
|
||||
import { Plugin, Transaction } from 'prosemirror-state'
|
||||
DOMOutputSpec, Node as ProseMirrorNode, NodeSpec, NodeType,
|
||||
} from '@tiptap/pm/model'
|
||||
import { Plugin, Transaction } from '@tiptap/pm/state'
|
||||
|
||||
import { NodeConfig } from '.'
|
||||
import { Editor } from './Editor'
|
||||
@ -26,443 +23,487 @@ import { mergeDeep } from './utilities/mergeDeep'
|
||||
|
||||
declare module '@tiptap/core' {
|
||||
interface NodeConfig<Options = any, Storage = any> {
|
||||
[key: string]: any;
|
||||
[key: string]: any
|
||||
|
||||
/**
|
||||
* Name
|
||||
*/
|
||||
name: string,
|
||||
name: string
|
||||
|
||||
/**
|
||||
* Priority
|
||||
*/
|
||||
priority?: number,
|
||||
priority?: number
|
||||
|
||||
/**
|
||||
* Default options
|
||||
*/
|
||||
defaultOptions?: Options,
|
||||
defaultOptions?: Options
|
||||
|
||||
/**
|
||||
* Default Options
|
||||
*/
|
||||
addOptions?: (this: {
|
||||
name: string,
|
||||
parent: Exclude<ParentConfig<NodeConfig<Options, Storage>>['addOptions'], undefined>,
|
||||
}) => Options,
|
||||
name: string
|
||||
parent: Exclude<ParentConfig<NodeConfig<Options, Storage>>['addOptions'], undefined>
|
||||
}) => Options
|
||||
|
||||
/**
|
||||
* Default Storage
|
||||
*/
|
||||
addStorage?: (this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
parent: Exclude<ParentConfig<NodeConfig<Options, Storage>>['addStorage'], undefined>,
|
||||
}) => Storage,
|
||||
name: string
|
||||
options: Options
|
||||
parent: Exclude<ParentConfig<NodeConfig<Options, Storage>>['addStorage'], undefined>
|
||||
}) => Storage
|
||||
|
||||
/**
|
||||
* Global attributes
|
||||
*/
|
||||
addGlobalAttributes?: (this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['addGlobalAttributes'],
|
||||
}) => GlobalAttributes | {},
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['addGlobalAttributes']
|
||||
}) => GlobalAttributes | {}
|
||||
|
||||
/**
|
||||
* Raw
|
||||
*/
|
||||
addCommands?: (this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['addCommands'],
|
||||
}) => Partial<RawCommands>,
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
type: NodeType
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['addCommands']
|
||||
}) => Partial<RawCommands>
|
||||
|
||||
/**
|
||||
* Keyboard shortcuts
|
||||
*/
|
||||
addKeyboardShortcuts?: (this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['addKeyboardShortcuts'],
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
type: NodeType
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['addKeyboardShortcuts']
|
||||
}) => {
|
||||
[key: string]: KeyboardShortcutCommand,
|
||||
},
|
||||
[key: string]: KeyboardShortcutCommand
|
||||
}
|
||||
|
||||
/**
|
||||
* Input rules
|
||||
*/
|
||||
addInputRules?: (this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['addInputRules'],
|
||||
}) => InputRule[],
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
type: NodeType
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['addInputRules']
|
||||
}) => InputRule[]
|
||||
|
||||
/**
|
||||
* Paste rules
|
||||
*/
|
||||
addPasteRules?: (this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['addPasteRules'],
|
||||
}) => PasteRule[],
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
type: NodeType
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['addPasteRules']
|
||||
}) => PasteRule[]
|
||||
|
||||
/**
|
||||
* ProseMirror plugins
|
||||
*/
|
||||
addProseMirrorPlugins?: (this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['addProseMirrorPlugins'],
|
||||
}) => Plugin[],
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
type: NodeType
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['addProseMirrorPlugins']
|
||||
}) => Plugin[]
|
||||
|
||||
/**
|
||||
* Extensions
|
||||
*/
|
||||
addExtensions?: (this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['addExtensions'],
|
||||
}) => Extensions,
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['addExtensions']
|
||||
}) => Extensions
|
||||
|
||||
/**
|
||||
* Extend Node Schema
|
||||
*/
|
||||
extendNodeSchema?: ((
|
||||
this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['extendNodeSchema'],
|
||||
},
|
||||
extension: Node,
|
||||
) => Record<string, any>) | null,
|
||||
extendNodeSchema?:
|
||||
| ((
|
||||
this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['extendNodeSchema']
|
||||
},
|
||||
extension: Node,
|
||||
) => Record<string, any>)
|
||||
| null
|
||||
|
||||
/**
|
||||
* Extend Mark Schema
|
||||
*/
|
||||
extendMarkSchema?: ((
|
||||
this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['extendMarkSchema'],
|
||||
},
|
||||
extension: Node,
|
||||
) => Record<string, any>) | null,
|
||||
extendMarkSchema?:
|
||||
| ((
|
||||
this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['extendMarkSchema']
|
||||
},
|
||||
extension: Node,
|
||||
) => Record<string, any>)
|
||||
| null
|
||||
|
||||
/**
|
||||
* The editor is not ready yet.
|
||||
*/
|
||||
onBeforeCreate?: ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['onBeforeCreate'],
|
||||
}) => void) | null,
|
||||
onBeforeCreate?:
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
type: NodeType
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['onBeforeCreate']
|
||||
}) => void)
|
||||
| null
|
||||
|
||||
/**
|
||||
* The editor is ready.
|
||||
*/
|
||||
onCreate?: ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['onCreate'],
|
||||
}) => void) | null,
|
||||
onCreate?:
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
type: NodeType
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['onCreate']
|
||||
}) => void)
|
||||
| null
|
||||
|
||||
/**
|
||||
* The content has changed.
|
||||
*/
|
||||
onUpdate?: ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['onUpdate'],
|
||||
}) => void) | null,
|
||||
onUpdate?:
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
type: NodeType
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['onUpdate']
|
||||
}) => void)
|
||||
| null
|
||||
|
||||
/**
|
||||
* The selection has changed.
|
||||
*/
|
||||
onSelectionUpdate?: ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['onSelectionUpdate'],
|
||||
}) => void) | null,
|
||||
onSelectionUpdate?:
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
type: NodeType
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['onSelectionUpdate']
|
||||
}) => void)
|
||||
| null
|
||||
|
||||
/**
|
||||
* The editor state has changed.
|
||||
*/
|
||||
onTransaction?: ((
|
||||
this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['onTransaction'],
|
||||
},
|
||||
props: {
|
||||
transaction: Transaction,
|
||||
},
|
||||
) => void) | null,
|
||||
onTransaction?:
|
||||
| ((
|
||||
this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
type: NodeType
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['onTransaction']
|
||||
},
|
||||
props: {
|
||||
transaction: Transaction
|
||||
},
|
||||
) => void)
|
||||
| null
|
||||
|
||||
/**
|
||||
* The editor is focused.
|
||||
*/
|
||||
onFocus?: ((
|
||||
this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['onFocus'],
|
||||
},
|
||||
props: {
|
||||
event: FocusEvent,
|
||||
},
|
||||
) => void) | null,
|
||||
onFocus?:
|
||||
| ((
|
||||
this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
type: NodeType
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['onFocus']
|
||||
},
|
||||
props: {
|
||||
event: FocusEvent
|
||||
},
|
||||
) => void)
|
||||
| null
|
||||
|
||||
/**
|
||||
* The editor isn’t focused anymore.
|
||||
*/
|
||||
onBlur?: ((
|
||||
this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['onBlur'],
|
||||
},
|
||||
props: {
|
||||
event: FocusEvent,
|
||||
},
|
||||
) => void) | null,
|
||||
onBlur?:
|
||||
| ((
|
||||
this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
type: NodeType
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['onBlur']
|
||||
},
|
||||
props: {
|
||||
event: FocusEvent
|
||||
},
|
||||
) => void)
|
||||
| null
|
||||
|
||||
/**
|
||||
* The editor is destroyed.
|
||||
*/
|
||||
onDestroy?: ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['onDestroy'],
|
||||
}) => void) | null,
|
||||
onDestroy?:
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
type: NodeType
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['onDestroy']
|
||||
}) => void)
|
||||
| null
|
||||
|
||||
/**
|
||||
* Node View
|
||||
*/
|
||||
addNodeView?: ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
editor: Editor,
|
||||
type: NodeType,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['addNodeView'],
|
||||
}) => NodeViewRenderer) | null,
|
||||
addNodeView?:
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
editor: Editor
|
||||
type: NodeType
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['addNodeView']
|
||||
}) => NodeViewRenderer)
|
||||
| null
|
||||
|
||||
/**
|
||||
* TopNode
|
||||
*/
|
||||
topNode?: boolean,
|
||||
topNode?: boolean
|
||||
|
||||
/**
|
||||
* Content
|
||||
*/
|
||||
content?: NodeSpec['content'] | ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['content'],
|
||||
}) => NodeSpec['content']),
|
||||
content?:
|
||||
| NodeSpec['content']
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['content']
|
||||
}) => NodeSpec['content'])
|
||||
|
||||
/**
|
||||
* Marks
|
||||
*/
|
||||
marks?: NodeSpec['marks'] | ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['marks'],
|
||||
}) => NodeSpec['marks']),
|
||||
marks?:
|
||||
| NodeSpec['marks']
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['marks']
|
||||
}) => NodeSpec['marks'])
|
||||
|
||||
/**
|
||||
* Group
|
||||
*/
|
||||
group?: NodeSpec['group'] | ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['group'],
|
||||
}) => NodeSpec['group']),
|
||||
group?:
|
||||
| NodeSpec['group']
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['group']
|
||||
}) => NodeSpec['group'])
|
||||
|
||||
/**
|
||||
* Inline
|
||||
*/
|
||||
inline?: NodeSpec['inline'] | ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['inline'],
|
||||
}) => NodeSpec['inline']),
|
||||
inline?:
|
||||
| NodeSpec['inline']
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['inline']
|
||||
}) => NodeSpec['inline'])
|
||||
|
||||
/**
|
||||
* Atom
|
||||
*/
|
||||
atom?: NodeSpec['atom'] | ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['atom'],
|
||||
}) => NodeSpec['atom']),
|
||||
atom?:
|
||||
| NodeSpec['atom']
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['atom']
|
||||
}) => NodeSpec['atom'])
|
||||
|
||||
/**
|
||||
* Selectable
|
||||
*/
|
||||
selectable?: NodeSpec['selectable'] | ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['selectable'],
|
||||
}) => NodeSpec['selectable']),
|
||||
selectable?:
|
||||
| NodeSpec['selectable']
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['selectable']
|
||||
}) => NodeSpec['selectable'])
|
||||
|
||||
/**
|
||||
* Draggable
|
||||
*/
|
||||
draggable?: NodeSpec['draggable'] | ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['draggable'],
|
||||
}) => NodeSpec['draggable']),
|
||||
draggable?:
|
||||
| NodeSpec['draggable']
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['draggable']
|
||||
}) => NodeSpec['draggable'])
|
||||
|
||||
/**
|
||||
* Code
|
||||
*/
|
||||
code?: NodeSpec['code'] | ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['code'],
|
||||
}) => NodeSpec['code']),
|
||||
code?:
|
||||
| NodeSpec['code']
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['code']
|
||||
}) => NodeSpec['code'])
|
||||
|
||||
/**
|
||||
* Whitespace
|
||||
*/
|
||||
whitespace?: NodeSpec['whitespace'] | ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['whitespace'],
|
||||
}) => NodeSpec['whitespace']),
|
||||
whitespace?:
|
||||
| NodeSpec['whitespace']
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['whitespace']
|
||||
}) => NodeSpec['whitespace'])
|
||||
|
||||
/**
|
||||
* Defining
|
||||
*/
|
||||
defining?: NodeSpec['defining'] | ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['defining'],
|
||||
}) => NodeSpec['defining']),
|
||||
defining?:
|
||||
| NodeSpec['defining']
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['defining']
|
||||
}) => NodeSpec['defining'])
|
||||
|
||||
/**
|
||||
* Isolating
|
||||
*/
|
||||
isolating?: NodeSpec['isolating'] | ((this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['isolating'],
|
||||
}) => NodeSpec['isolating']),
|
||||
isolating?:
|
||||
| NodeSpec['isolating']
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['isolating']
|
||||
}) => NodeSpec['isolating'])
|
||||
|
||||
/**
|
||||
* Parse HTML
|
||||
*/
|
||||
parseHTML?: (
|
||||
this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['parseHTML'],
|
||||
},
|
||||
) => NodeSpec['parseDOM'],
|
||||
parseHTML?: (this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['parseHTML']
|
||||
}) => NodeSpec['parseDOM']
|
||||
|
||||
/**
|
||||
* Render HTML
|
||||
*/
|
||||
renderHTML?: ((
|
||||
this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['renderHTML'],
|
||||
},
|
||||
props: {
|
||||
node: ProseMirrorNode,
|
||||
HTMLAttributes: Record<string, any>,
|
||||
}
|
||||
) => DOMOutputSpec) | null,
|
||||
renderHTML?:
|
||||
| ((
|
||||
this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['renderHTML']
|
||||
},
|
||||
props: {
|
||||
node: ProseMirrorNode
|
||||
HTMLAttributes: Record<string, any>
|
||||
},
|
||||
) => DOMOutputSpec)
|
||||
| null
|
||||
|
||||
/**
|
||||
* Render Text
|
||||
*/
|
||||
renderText?: ((
|
||||
this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['renderText'],
|
||||
},
|
||||
props: {
|
||||
node: ProseMirrorNode,
|
||||
pos: number,
|
||||
parent: ProseMirrorNode,
|
||||
index: number,
|
||||
}
|
||||
) => string) | null,
|
||||
renderText?:
|
||||
| ((
|
||||
this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['renderText']
|
||||
},
|
||||
props: {
|
||||
node: ProseMirrorNode
|
||||
pos: number
|
||||
parent: ProseMirrorNode
|
||||
index: number
|
||||
},
|
||||
) => string)
|
||||
| null
|
||||
|
||||
/**
|
||||
* Add Attributes
|
||||
*/
|
||||
addAttributes?: (
|
||||
this: {
|
||||
name: string,
|
||||
options: Options,
|
||||
storage: Storage,
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['addAttributes'],
|
||||
},
|
||||
) => Attributes | {},
|
||||
addAttributes?: (this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<NodeConfig<Options, Storage>>['addAttributes']
|
||||
}) => Attributes | {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -493,30 +534,28 @@ export class Node<Options = any, Storage = any> {
|
||||
this.name = this.config.name
|
||||
|
||||
if (config.defaultOptions) {
|
||||
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`)
|
||||
console.warn(
|
||||
`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`,
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: remove `addOptions` fallback
|
||||
this.options = this.config.defaultOptions
|
||||
|
||||
if (this.config.addOptions) {
|
||||
this.options = callOrReturn(getExtensionField<AnyConfig['addOptions']>(
|
||||
this,
|
||||
'addOptions',
|
||||
{
|
||||
this.options = callOrReturn(
|
||||
getExtensionField<AnyConfig['addOptions']>(this, 'addOptions', {
|
||||
name: this.name,
|
||||
},
|
||||
))
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
this.storage = callOrReturn(getExtensionField<AnyConfig['addStorage']>(
|
||||
this,
|
||||
'addStorage',
|
||||
{
|
||||
this.storage = callOrReturn(
|
||||
getExtensionField<AnyConfig['addStorage']>(this, 'addStorage', {
|
||||
name: this.name,
|
||||
options: this.options,
|
||||
},
|
||||
)) || {}
|
||||
}),
|
||||
) || {}
|
||||
}
|
||||
|
||||
static create<O = any, S = any>(config: Partial<NodeConfig<O, S>> = {}) {
|
||||
@ -530,49 +569,45 @@ export class Node<Options = any, Storage = any> {
|
||||
|
||||
extension.options = mergeDeep(this.options as Record<string, any>, options) as Options
|
||||
|
||||
extension.storage = callOrReturn(getExtensionField<AnyConfig['addStorage']>(
|
||||
extension,
|
||||
'addStorage',
|
||||
{
|
||||
extension.storage = callOrReturn(
|
||||
getExtensionField<AnyConfig['addStorage']>(extension, 'addStorage', {
|
||||
name: extension.name,
|
||||
options: extension.options,
|
||||
},
|
||||
))
|
||||
}),
|
||||
)
|
||||
|
||||
return extension
|
||||
}
|
||||
|
||||
extend<ExtendedOptions = Options, ExtendedStorage = Storage>(extendedConfig: Partial<NodeConfig<ExtendedOptions, ExtendedStorage>> = {}) {
|
||||
extend<ExtendedOptions = Options, ExtendedStorage = Storage>(
|
||||
extendedConfig: Partial<NodeConfig<ExtendedOptions, ExtendedStorage>> = {},
|
||||
) {
|
||||
const extension = new Node<ExtendedOptions, ExtendedStorage>(extendedConfig)
|
||||
|
||||
extension.parent = this
|
||||
|
||||
this.child = extension
|
||||
|
||||
extension.name = extendedConfig.name
|
||||
? extendedConfig.name
|
||||
: extension.parent.name
|
||||
extension.name = extendedConfig.name ? extendedConfig.name : extension.parent.name
|
||||
|
||||
if (extendedConfig.defaultOptions) {
|
||||
console.warn(`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`)
|
||||
console.warn(
|
||||
`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${extension.name}".`,
|
||||
)
|
||||
}
|
||||
|
||||
extension.options = callOrReturn(getExtensionField<AnyConfig['addOptions']>(
|
||||
extension,
|
||||
'addOptions',
|
||||
{
|
||||
extension.options = callOrReturn(
|
||||
getExtensionField<AnyConfig['addOptions']>(extension, 'addOptions', {
|
||||
name: extension.name,
|
||||
},
|
||||
))
|
||||
}),
|
||||
)
|
||||
|
||||
extension.storage = callOrReturn(getExtensionField<AnyConfig['addStorage']>(
|
||||
extension,
|
||||
'addStorage',
|
||||
{
|
||||
extension.storage = callOrReturn(
|
||||
getExtensionField<AnyConfig['addStorage']>(extension, 'addStorage', {
|
||||
name: extension.name,
|
||||
options: extension.options,
|
||||
},
|
||||
))
|
||||
}),
|
||||
)
|
||||
|
||||
return extension
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Node as ProseMirrorNode } from 'prosemirror-model'
|
||||
import { NodeSelection } from 'prosemirror-state'
|
||||
import { Decoration, NodeView as ProseMirrorNodeView } from 'prosemirror-view'
|
||||
import { Node as ProseMirrorNode } from '@tiptap/pm/model'
|
||||
import { NodeSelection } from '@tiptap/pm/state'
|
||||
import { Decoration, NodeView as ProseMirrorNodeView } from '@tiptap/pm/view'
|
||||
|
||||
import { Editor as CoreEditor } from './Editor'
|
||||
import { Node } from './Node'
|
||||
@ -12,7 +12,6 @@ export class NodeView<
|
||||
Editor extends CoreEditor = CoreEditor,
|
||||
Options extends NodeViewRendererOptions = NodeViewRendererOptions,
|
||||
> implements ProseMirrorNodeView {
|
||||
|
||||
component: Component
|
||||
|
||||
editor: Editor
|
||||
@ -59,7 +58,7 @@ export class NodeView<
|
||||
|
||||
onDragStart(event: DragEvent) {
|
||||
const { view } = this.editor
|
||||
const target = (event.target as HTMLElement)
|
||||
const target = event.target as HTMLElement
|
||||
|
||||
// get the drag handle element
|
||||
// `closest` is not available for text nodes so we may have to use its parent
|
||||
@ -67,11 +66,7 @@ export class NodeView<
|
||||
? target.parentElement?.closest('[data-drag-handle]')
|
||||
: target.closest('[data-drag-handle]')
|
||||
|
||||
if (
|
||||
!this.dom
|
||||
|| this.contentDOM?.contains(target)
|
||||
|| !dragHandle
|
||||
) {
|
||||
if (!this.dom || this.contentDOM?.contains(target) || !dragHandle) {
|
||||
return
|
||||
}
|
||||
|
||||
@ -110,7 +105,7 @@ export class NodeView<
|
||||
return this.options.stopEvent({ event })
|
||||
}
|
||||
|
||||
const target = (event.target as HTMLElement)
|
||||
const target = event.target as HTMLElement
|
||||
const isInElement = this.dom.contains(target) && !this.contentDOM?.contains(target)
|
||||
|
||||
// any event from child nodes should be handled by ProseMirror
|
||||
@ -119,8 +114,7 @@ export class NodeView<
|
||||
}
|
||||
|
||||
const isDropEvent = event.type === 'drop'
|
||||
const isInput = ['INPUT', 'BUTTON', 'SELECT', 'TEXTAREA'].includes(target.tagName)
|
||||
|| target.isContentEditable
|
||||
const isInput = ['INPUT', 'BUTTON', 'SELECT', 'TEXTAREA'].includes(target.tagName) || target.isContentEditable
|
||||
|
||||
// any input event within node views should be ignored by ProseMirror
|
||||
if (isInput && !isDropEvent) {
|
||||
@ -152,19 +146,26 @@ export class NodeView<
|
||||
// we have to store that dragging started
|
||||
if (isDraggable && isEditable && !isDragging && isClickEvent) {
|
||||
const dragHandle = target.closest('[data-drag-handle]')
|
||||
const isValidDragHandle = dragHandle
|
||||
&& (this.dom === dragHandle || (this.dom.contains(dragHandle)))
|
||||
const isValidDragHandle = dragHandle && (this.dom === dragHandle || this.dom.contains(dragHandle))
|
||||
|
||||
if (isValidDragHandle) {
|
||||
this.isDragging = true
|
||||
|
||||
document.addEventListener('dragend', () => {
|
||||
this.isDragging = false
|
||||
}, { once: true })
|
||||
document.addEventListener(
|
||||
'dragend',
|
||||
() => {
|
||||
this.isDragging = false
|
||||
},
|
||||
{ once: true },
|
||||
)
|
||||
|
||||
document.addEventListener('mouseup', () => {
|
||||
this.isDragging = false
|
||||
}, { once: true })
|
||||
document.addEventListener(
|
||||
'mouseup',
|
||||
() => {
|
||||
this.isDragging = false
|
||||
},
|
||||
{ once: true },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,7 +184,7 @@ export class NodeView<
|
||||
return true
|
||||
}
|
||||
|
||||
ignoreMutation(mutation: MutationRecord | { type: 'selection', target: Element }) {
|
||||
ignoreMutation(mutation: MutationRecord | { type: 'selection'; target: Element }) {
|
||||
if (!this.dom || !this.contentDOM) {
|
||||
return true
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { EditorState, Plugin } from 'prosemirror-state'
|
||||
import { EditorState, Plugin } from '@tiptap/pm/state'
|
||||
|
||||
import { CommandManager } from './CommandManager'
|
||||
import { Editor } from './Editor'
|
||||
@ -14,46 +14,47 @@ import { isNumber } from './utilities/isNumber'
|
||||
import { isRegExp } from './utilities/isRegExp'
|
||||
|
||||
export type PasteRuleMatch = {
|
||||
index: number,
|
||||
text: string,
|
||||
replaceWith?: string,
|
||||
match?: RegExpMatchArray,
|
||||
data?: Record<string, any>,
|
||||
index: number
|
||||
text: string
|
||||
replaceWith?: string
|
||||
match?: RegExpMatchArray
|
||||
data?: Record<string, any>
|
||||
}
|
||||
|
||||
export type PasteRuleFinder =
|
||||
| RegExp
|
||||
| ((text: string) => PasteRuleMatch[] | null | undefined)
|
||||
export type PasteRuleFinder = RegExp | ((text: string) => PasteRuleMatch[] | null | undefined)
|
||||
|
||||
export class PasteRule {
|
||||
find: PasteRuleFinder
|
||||
|
||||
handler: (props: {
|
||||
state: EditorState,
|
||||
range: Range,
|
||||
match: ExtendedRegExpMatchArray,
|
||||
commands: SingleCommands,
|
||||
chain: () => ChainedCommands,
|
||||
can: () => CanCommands,
|
||||
state: EditorState
|
||||
range: Range
|
||||
match: ExtendedRegExpMatchArray
|
||||
commands: SingleCommands
|
||||
chain: () => ChainedCommands
|
||||
can: () => CanCommands
|
||||
}) => void | null
|
||||
|
||||
constructor(config: {
|
||||
find: PasteRuleFinder,
|
||||
find: PasteRuleFinder
|
||||
handler: (props: {
|
||||
state: EditorState,
|
||||
range: Range,
|
||||
match: ExtendedRegExpMatchArray,
|
||||
commands: SingleCommands,
|
||||
chain: () => ChainedCommands,
|
||||
can: () => CanCommands,
|
||||
}) => void | null,
|
||||
state: EditorState
|
||||
range: Range
|
||||
match: ExtendedRegExpMatchArray
|
||||
commands: SingleCommands
|
||||
chain: () => ChainedCommands
|
||||
can: () => CanCommands
|
||||
}) => void | null
|
||||
}) {
|
||||
this.find = config.find
|
||||
this.handler = config.handler
|
||||
}
|
||||
}
|
||||
|
||||
const pasteRuleMatcherHandler = (text: string, find: PasteRuleFinder): ExtendedRegExpMatchArray[] => {
|
||||
const pasteRuleMatcherHandler = (
|
||||
text: string,
|
||||
find: PasteRuleFinder,
|
||||
): ExtendedRegExpMatchArray[] => {
|
||||
if (isRegExp(find)) {
|
||||
return [...text.matchAll(find)]
|
||||
}
|
||||
@ -73,7 +74,9 @@ const pasteRuleMatcherHandler = (text: string, find: PasteRuleFinder): ExtendedR
|
||||
|
||||
if (pasteRuleMatch.replaceWith) {
|
||||
if (!pasteRuleMatch.text.includes(pasteRuleMatch.replaceWith)) {
|
||||
console.warn('[tiptap warn]: "pasteRuleMatch.replaceWith" must be part of "pasteRuleMatch.text".')
|
||||
console.warn(
|
||||
'[tiptap warn]: "pasteRuleMatch.replaceWith" must be part of "pasteRuleMatch.text".',
|
||||
)
|
||||
}
|
||||
|
||||
result.push(pasteRuleMatch.replaceWith)
|
||||
@ -84,18 +87,14 @@ const pasteRuleMatcherHandler = (text: string, find: PasteRuleFinder): ExtendedR
|
||||
}
|
||||
|
||||
function run(config: {
|
||||
editor: Editor,
|
||||
state: EditorState,
|
||||
from: number,
|
||||
to: number,
|
||||
rule: PasteRule,
|
||||
editor: Editor
|
||||
state: EditorState
|
||||
from: number
|
||||
to: number
|
||||
rule: PasteRule
|
||||
}): boolean {
|
||||
const {
|
||||
editor,
|
||||
state,
|
||||
from,
|
||||
to,
|
||||
rule,
|
||||
editor, state, from, to, rule,
|
||||
} = config
|
||||
|
||||
const { commands, chain, can } = new CommandManager({
|
||||
@ -112,12 +111,7 @@ function run(config: {
|
||||
|
||||
const resolvedFrom = Math.max(from, pos)
|
||||
const resolvedTo = Math.min(to, pos + node.content.size)
|
||||
const textToMatch = node.textBetween(
|
||||
resolvedFrom - pos,
|
||||
resolvedTo - pos,
|
||||
undefined,
|
||||
'\ufffc',
|
||||
)
|
||||
const textToMatch = node.textBetween(resolvedFrom - pos, resolvedTo - pos, undefined, '\ufffc')
|
||||
|
||||
const matches = pasteRuleMatcherHandler(textToMatch, rule.find)
|
||||
|
||||
@ -156,7 +150,7 @@ function run(config: {
|
||||
* text that matches any of the given rules to trigger the rule’s
|
||||
* action.
|
||||
*/
|
||||
export function pasteRulesPlugin(props: { editor: Editor, rules: PasteRule[] }): Plugin[] {
|
||||
export function pasteRulesPlugin(props: { editor: Editor; rules: PasteRule[] }): Plugin[] {
|
||||
const { editor, rules } = props
|
||||
let dragSourceElement: Element | null = null
|
||||
let isPastedFromProseMirror = false
|
||||
|
@ -1,12 +1,11 @@
|
||||
import { Transaction } from 'prosemirror-state'
|
||||
import { Transaction } from '@tiptap/pm/state'
|
||||
|
||||
export interface TrackerResult {
|
||||
position: number,
|
||||
deleted: boolean,
|
||||
position: number
|
||||
deleted: boolean
|
||||
}
|
||||
|
||||
export class Tracker {
|
||||
|
||||
transaction: Transaction
|
||||
|
||||
currentStep: number
|
||||
@ -22,9 +21,7 @@ export class Tracker {
|
||||
const mappedPosition = this.transaction.steps
|
||||
.slice(this.currentStep)
|
||||
.reduce((newPosition, step) => {
|
||||
const mapResult = step
|
||||
.getMap()
|
||||
.mapResult(newPosition)
|
||||
const mapResult = step.getMap().mapResult(newPosition)
|
||||
|
||||
if (mapResult.deleted) {
|
||||
deleted = true
|
||||
@ -38,5 +35,4 @@ export class Tracker {
|
||||
deleted,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { liftTarget } from 'prosemirror-transform'
|
||||
import { liftTarget } from '@tiptap/pm/transform'
|
||||
|
||||
import { RawCommands } from '../types'
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { createParagraphNear as originalCreateParagraphNear } from 'prosemirror-commands'
|
||||
import { createParagraphNear as originalCreateParagraphNear } from '@tiptap/pm/commands'
|
||||
|
||||
import { RawCommands } from '../types'
|
||||
|
||||
@ -8,7 +8,7 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Create a paragraph nearby.
|
||||
*/
|
||||
createParagraphNear: () => ReturnType,
|
||||
createParagraphNear: () => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { NodeType } from 'prosemirror-model'
|
||||
import { NodeType } from '@tiptap/pm/model'
|
||||
|
||||
import { getNodeType } from '../helpers/getNodeType'
|
||||
import { RawCommands } from '../types'
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { deleteSelection as originalDeleteSelection } from 'prosemirror-commands'
|
||||
import { deleteSelection as originalDeleteSelection } from '@tiptap/pm/commands'
|
||||
|
||||
import { RawCommands } from '../types'
|
||||
|
||||
@ -8,7 +8,7 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Delete the selection, if there is one.
|
||||
*/
|
||||
deleteSelection: () => ReturnType,
|
||||
deleteSelection: () => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { exitCode as originalExitCode } from 'prosemirror-commands'
|
||||
import { exitCode as originalExitCode } from '@tiptap/pm/commands'
|
||||
|
||||
import { RawCommands } from '../types'
|
||||
|
||||
@ -8,7 +8,7 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Exit from a code block.
|
||||
*/
|
||||
exitCode: () => ReturnType,
|
||||
exitCode: () => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { MarkType } from 'prosemirror-model'
|
||||
import { TextSelection } from 'prosemirror-state'
|
||||
import { MarkType } from '@tiptap/pm/model'
|
||||
import { TextSelection } from '@tiptap/pm/state'
|
||||
|
||||
import { getMarkRange } from '../helpers/getMarkRange'
|
||||
import { getMarkType } from '../helpers/getMarkType'
|
||||
@ -11,7 +11,10 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Extends the text selection to the current mark.
|
||||
*/
|
||||
extendMarkRange: (typeOrName: string | MarkType, attributes?: Record<string, any>) => ReturnType,
|
||||
extendMarkRange: (
|
||||
typeOrName: string | MarkType,
|
||||
attributes?: Record<string, any>,
|
||||
) => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ParseOptions } from 'prosemirror-model'
|
||||
import { ParseOptions } from '@tiptap/pm/model'
|
||||
|
||||
import { Content, RawCommands } from '../types'
|
||||
|
||||
@ -11,14 +11,18 @@ declare module '@tiptap/core' {
|
||||
insertContent: (
|
||||
value: Content,
|
||||
options?: {
|
||||
parseOptions?: ParseOptions,
|
||||
updateSelection?: boolean,
|
||||
parseOptions?: ParseOptions
|
||||
updateSelection?: boolean
|
||||
},
|
||||
) => ReturnType,
|
||||
) => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const insertContent: RawCommands['insertContent'] = (value, options) => ({ tr, commands }) => {
|
||||
return commands.insertContentAt({ from: tr.selection.from, to: tr.selection.to }, value, options)
|
||||
return commands.insertContentAt(
|
||||
{ from: tr.selection.from, to: tr.selection.to },
|
||||
value,
|
||||
options,
|
||||
)
|
||||
}
|
||||
|
@ -1,12 +1,8 @@
|
||||
import { Fragment, Node as ProseMirrorNode, ParseOptions } from 'prosemirror-model'
|
||||
import { Fragment, Node as ProseMirrorNode, ParseOptions } from '@tiptap/pm/model'
|
||||
|
||||
import { createNodeFromContent } from '../helpers/createNodeFromContent'
|
||||
import { selectionToInsertionEnd } from '../helpers/selectionToInsertionEnd'
|
||||
import {
|
||||
Content,
|
||||
Range,
|
||||
RawCommands,
|
||||
} from '../types'
|
||||
import { Content, Range, RawCommands } from '../types'
|
||||
|
||||
declare module '@tiptap/core' {
|
||||
interface Commands<ReturnType> {
|
||||
@ -18,10 +14,10 @@ declare module '@tiptap/core' {
|
||||
position: number | Range,
|
||||
value: Content,
|
||||
options?: {
|
||||
parseOptions?: ParseOptions,
|
||||
updateSelection?: boolean,
|
||||
parseOptions?: ParseOptions
|
||||
updateSelection?: boolean
|
||||
},
|
||||
) => ReturnType,
|
||||
) => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -50,27 +46,19 @@ export const insertContentAt: RawCommands['insertContentAt'] = (position, value,
|
||||
return true
|
||||
}
|
||||
|
||||
let { from, to } = typeof position === 'number'
|
||||
? { from: position, to: position }
|
||||
: position
|
||||
let { from, to } = typeof position === 'number' ? { from: position, to: position } : position
|
||||
|
||||
let isOnlyTextContent = true
|
||||
let isOnlyBlockContent = true
|
||||
const nodes = isFragment(content)
|
||||
? content
|
||||
: [content]
|
||||
const nodes = isFragment(content) ? content : [content]
|
||||
|
||||
nodes.forEach(node => {
|
||||
// check if added node is valid
|
||||
node.check()
|
||||
|
||||
isOnlyTextContent = isOnlyTextContent
|
||||
? node.isText && node.marks.length === 0
|
||||
: false
|
||||
isOnlyTextContent = isOnlyTextContent ? node.isText && node.marks.length === 0 : false
|
||||
|
||||
isOnlyBlockContent = isOnlyBlockContent
|
||||
? node.isBlock
|
||||
: false
|
||||
isOnlyBlockContent = isOnlyBlockContent ? node.isBlock : false
|
||||
})
|
||||
|
||||
// check if we can replace the wrapping node by
|
||||
@ -80,9 +68,7 @@ export const insertContentAt: RawCommands['insertContentAt'] = (position, value,
|
||||
// instead of inserting the image below the paragraph
|
||||
if (from === to && isOnlyBlockContent) {
|
||||
const { parent } = tr.doc.resolve(from)
|
||||
const isEmptyTextBlock = parent.isTextblock
|
||||
&& !parent.type.spec.code
|
||||
&& !parent.childCount
|
||||
const isEmptyTextBlock = parent.isTextblock && !parent.type.spec.code && !parent.childCount
|
||||
|
||||
if (isEmptyTextBlock) {
|
||||
from -= 1
|
||||
|
@ -1,6 +1,9 @@
|
||||
import {
|
||||
joinBackward as originalJoinBackward, joinDown as originalJoinDown, joinForward as originalJoinForward, joinUp as originalJoinUp,
|
||||
} from 'prosemirror-commands'
|
||||
joinBackward as originalJoinBackward,
|
||||
joinDown as originalJoinDown,
|
||||
joinForward as originalJoinForward,
|
||||
joinUp as originalJoinUp,
|
||||
} from '@tiptap/pm/commands'
|
||||
|
||||
import { RawCommands } from '../types'
|
||||
|
||||
@ -10,25 +13,25 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Join two nodes Up.
|
||||
*/
|
||||
joinUp: () => ReturnType,
|
||||
joinUp: () => ReturnType
|
||||
}
|
||||
joinDown: {
|
||||
/**
|
||||
* Join two nodes Down.
|
||||
*/
|
||||
joinDown: () => ReturnType,
|
||||
joinDown: () => ReturnType
|
||||
}
|
||||
joinBackward: {
|
||||
/**
|
||||
* Join two nodes Backwards.
|
||||
*/
|
||||
joinBackward: () => ReturnType,
|
||||
joinBackward: () => ReturnType
|
||||
}
|
||||
joinForward: {
|
||||
/**
|
||||
* Join two nodes Forwards.
|
||||
*/
|
||||
joinForward: () => ReturnType,
|
||||
joinForward: () => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { lift as originalLift } from 'prosemirror-commands'
|
||||
import { NodeType } from 'prosemirror-model'
|
||||
import { lift as originalLift } from '@tiptap/pm/commands'
|
||||
import { NodeType } from '@tiptap/pm/model'
|
||||
|
||||
import { getNodeType } from '../helpers/getNodeType'
|
||||
import { isNodeActive } from '../helpers/isNodeActive'
|
||||
@ -11,7 +11,7 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Removes an existing wrap.
|
||||
*/
|
||||
lift: (typeOrName: string | NodeType, attributes?: Record<string, any>) => ReturnType,
|
||||
lift: (typeOrName: string | NodeType, attributes?: Record<string, any>) => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { liftEmptyBlock as originalLiftEmptyBlock } from 'prosemirror-commands'
|
||||
import { liftEmptyBlock as originalLiftEmptyBlock } from '@tiptap/pm/commands'
|
||||
|
||||
import { RawCommands } from '../types'
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { NodeType } from 'prosemirror-model'
|
||||
import { liftListItem as originalLiftListItem } from 'prosemirror-schema-list'
|
||||
import { NodeType } from '@tiptap/pm/model'
|
||||
import { liftListItem as originalLiftListItem } from '@tiptap/pm/schema-list'
|
||||
|
||||
import { getNodeType } from '../helpers/getNodeType'
|
||||
import { RawCommands } from '../types'
|
||||
@ -10,7 +10,7 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Lift the list item into a wrapping list.
|
||||
*/
|
||||
liftListItem: (typeOrName: string | NodeType) => ReturnType,
|
||||
liftListItem: (typeOrName: string | NodeType) => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { newlineInCode as originalNewlineInCode } from 'prosemirror-commands'
|
||||
import { newlineInCode as originalNewlineInCode } from '@tiptap/pm/commands'
|
||||
|
||||
import { RawCommands } from '../types'
|
||||
|
||||
@ -8,7 +8,7 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Add a newline character in code.
|
||||
*/
|
||||
newlineInCode: () => ReturnType,
|
||||
newlineInCode: () => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { MarkType, NodeType } from 'prosemirror-model'
|
||||
import { MarkType, NodeType } from '@tiptap/pm/model'
|
||||
|
||||
import { getMarkType } from '../helpers/getMarkType'
|
||||
import { getNodeType } from '../helpers/getNodeType'
|
||||
@ -12,7 +12,10 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Resets some node attributes to the default value.
|
||||
*/
|
||||
resetAttributes: (typeOrName: string | NodeType | MarkType, attributes: string | string[]) => ReturnType,
|
||||
resetAttributes: (
|
||||
typeOrName: string | NodeType | MarkType,
|
||||
attributes: string | string[],
|
||||
) => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -22,9 +25,7 @@ export const resetAttributes: RawCommands['resetAttributes'] = (typeOrName, attr
|
||||
let markType: MarkType | null = null
|
||||
|
||||
const schemaType = getSchemaTypeNameByName(
|
||||
typeof typeOrName === 'string'
|
||||
? typeOrName
|
||||
: typeOrName.name,
|
||||
typeof typeOrName === 'string' ? typeOrName : typeOrName.name,
|
||||
state.schema,
|
||||
)
|
||||
|
||||
@ -50,7 +51,11 @@ export const resetAttributes: RawCommands['resetAttributes'] = (typeOrName, attr
|
||||
if (markType && node.marks.length) {
|
||||
node.marks.forEach(mark => {
|
||||
if (markType === mark.type) {
|
||||
tr.addMark(pos, pos + node.nodeSize, markType.create(deleteProps(mark.attrs, attributes)))
|
||||
tr.addMark(
|
||||
pos,
|
||||
pos + node.nodeSize,
|
||||
markType.create(deleteProps(mark.attrs, attributes)),
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { selectNodeBackward as originalSelectNodeBackward } from 'prosemirror-commands'
|
||||
import { selectNodeBackward as originalSelectNodeBackward } from '@tiptap/pm/commands'
|
||||
|
||||
import { RawCommands } from '../types'
|
||||
|
||||
@ -8,7 +8,7 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Select a node backward.
|
||||
*/
|
||||
selectNodeBackward: () => ReturnType,
|
||||
selectNodeBackward: () => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { selectNodeForward as originalSelectNodeForward } from 'prosemirror-commands'
|
||||
import { selectNodeForward as originalSelectNodeForward } from '@tiptap/pm/commands'
|
||||
|
||||
import { RawCommands } from '../types'
|
||||
|
||||
@ -8,7 +8,7 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Select a node forward.
|
||||
*/
|
||||
selectNodeForward: () => ReturnType,
|
||||
selectNodeForward: () => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { selectParentNode as originalSelectParentNode } from 'prosemirror-commands'
|
||||
import { selectParentNode as originalSelectParentNode } from '@tiptap/pm/commands'
|
||||
|
||||
import { RawCommands } from '../types'
|
||||
|
||||
@ -8,7 +8,7 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Select the parent node.
|
||||
*/
|
||||
selectParentNode: () => ReturnType,
|
||||
selectParentNode: () => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
// @ts-ignore
|
||||
// TODO: add types to @types/prosemirror-commands
|
||||
import { selectTextblockEnd as originalSelectTextblockEnd } from 'prosemirror-commands'
|
||||
import { selectTextblockEnd as originalSelectTextblockEnd } from '@tiptap/pm/commands'
|
||||
|
||||
import { RawCommands } from '../types'
|
||||
|
||||
@ -10,7 +10,7 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Moves the cursor to the end of current text block.
|
||||
*/
|
||||
selectTextblockEnd: () => ReturnType,
|
||||
selectTextblockEnd: () => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
// @ts-ignore
|
||||
// TODO: add types to @types/prosemirror-commands
|
||||
import { selectTextblockStart as originalSelectTextblockStart } from 'prosemirror-commands'
|
||||
import { selectTextblockStart as originalSelectTextblockStart } from '@tiptap/pm/commands'
|
||||
|
||||
import { RawCommands } from '../types'
|
||||
|
||||
@ -10,7 +10,7 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Moves the cursor to the start of current text block.
|
||||
*/
|
||||
selectTextblockStart: () => ReturnType,
|
||||
selectTextblockStart: () => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ParseOptions } from 'prosemirror-model'
|
||||
import { ParseOptions } from '@tiptap/pm/model'
|
||||
|
||||
import { createDocument } from '../helpers/createDocument'
|
||||
import { Content, RawCommands } from '../types'
|
||||
@ -13,7 +13,7 @@ declare module '@tiptap/core' {
|
||||
content: Content,
|
||||
emitUpdate?: boolean,
|
||||
parseOptions?: ParseOptions,
|
||||
) => ReturnType,
|
||||
) => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -23,8 +23,7 @@ export const setContent: RawCommands['setContent'] = (content, emitUpdate = fals
|
||||
const document = createDocument(content, editor.schema, parseOptions)
|
||||
|
||||
if (dispatch) {
|
||||
tr.replaceWith(0, doc.content.size, document)
|
||||
.setMeta('preventUpdate', !emitUpdate)
|
||||
tr.replaceWith(0, doc.content.size, document).setMeta('preventUpdate', !emitUpdate)
|
||||
}
|
||||
|
||||
return true
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { MarkType, ResolvedPos } from 'prosemirror-model'
|
||||
import { EditorState, Transaction } from 'prosemirror-state'
|
||||
import { MarkType, ResolvedPos } from '@tiptap/pm/model'
|
||||
import { EditorState, Transaction } from '@tiptap/pm/state'
|
||||
|
||||
import { isTextSelection } from '../helpers'
|
||||
import { getMarkAttributes } from '../helpers/getMarkAttributes'
|
||||
@ -12,7 +12,7 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Add a mark with new attributes.
|
||||
*/
|
||||
setMark: (typeOrName: string | MarkType, attributes?: Record<string, any>) => ReturnType,
|
||||
setMark: (typeOrName: string | MarkType, attributes?: Record<string, any>) => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -29,13 +29,18 @@ function canSetMark(state: EditorState, tr: Transaction, newMarkType: MarkType)
|
||||
const currentMarks = state.storedMarks ?? cursor.marks()
|
||||
|
||||
// There can be no current marks that exclude the new mark
|
||||
return !!newMarkType.isInSet(currentMarks) || !currentMarks.some(mark => mark.type.excludes(newMarkType))
|
||||
return (
|
||||
!!newMarkType.isInSet(currentMarks)
|
||||
|| !currentMarks.some(mark => mark.type.excludes(newMarkType))
|
||||
)
|
||||
}
|
||||
|
||||
const { ranges } = selection
|
||||
|
||||
return ranges.some(({ $from, $to }) => {
|
||||
let someNodeSupportsMark = $from.depth === 0 ? state.doc.inlineContent && state.doc.type.allowsMarkType(newMarkType) : false
|
||||
let someNodeSupportsMark = $from.depth === 0
|
||||
? state.doc.inlineContent && state.doc.type.allowsMarkType(newMarkType)
|
||||
: false
|
||||
|
||||
state.doc.nodesBetween($from.pos, $to.pos, (node, _pos, parent) => {
|
||||
// If we already found a mark that we can enable, return false to bypass the remaining search
|
||||
@ -45,7 +50,8 @@ function canSetMark(state: EditorState, tr: Transaction, newMarkType: MarkType)
|
||||
|
||||
if (node.isInline) {
|
||||
const parentAllowsMarkType = !parent || parent.type.allowsMarkType(newMarkType)
|
||||
const currentMarksAllowMarkType = !!newMarkType.isInSet(node.marks) || !node.marks.some(otherMark => otherMark.type.excludes(newMarkType))
|
||||
const currentMarksAllowMarkType = !!newMarkType.isInSet(node.marks)
|
||||
|| !node.marks.some(otherMark => otherMark.type.excludes(newMarkType))
|
||||
|
||||
someNodeSupportsMark = parentAllowsMarkType && currentMarksAllowMarkType
|
||||
}
|
||||
@ -54,7 +60,6 @@ function canSetMark(state: EditorState, tr: Transaction, newMarkType: MarkType)
|
||||
|
||||
return someNodeSupportsMark
|
||||
})
|
||||
|
||||
}
|
||||
export const setMark: RawCommands['setMark'] = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
|
||||
const { selection } = tr
|
||||
@ -65,10 +70,12 @@ export const setMark: RawCommands['setMark'] = (typeOrName, attributes = {}) =>
|
||||
if (empty) {
|
||||
const oldAttributes = getMarkAttributes(state, type)
|
||||
|
||||
tr.addStoredMark(type.create({
|
||||
...oldAttributes,
|
||||
...attributes,
|
||||
}))
|
||||
tr.addStoredMark(
|
||||
type.create({
|
||||
...oldAttributes,
|
||||
...attributes,
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
ranges.forEach(range => {
|
||||
const from = range.$from.pos
|
||||
@ -83,13 +90,16 @@ export const setMark: RawCommands['setMark'] = (typeOrName, attributes = {}) =>
|
||||
// we know that we have to merge its attributes
|
||||
// otherwise we add a fresh new mark
|
||||
if (someHasMark) {
|
||||
|
||||
node.marks.forEach(mark => {
|
||||
if (type === mark.type) {
|
||||
tr.addMark(trimmedFrom, trimmedTo, type.create({
|
||||
...mark.attrs,
|
||||
...attributes,
|
||||
}))
|
||||
tr.addMark(
|
||||
trimmedFrom,
|
||||
trimmedTo,
|
||||
type.create({
|
||||
...mark.attrs,
|
||||
...attributes,
|
||||
}),
|
||||
)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { setBlockType } from 'prosemirror-commands'
|
||||
import { NodeType } from 'prosemirror-model'
|
||||
import { setBlockType } from '@tiptap/pm/commands'
|
||||
import { NodeType } from '@tiptap/pm/model'
|
||||
|
||||
import { getNodeType } from '../helpers/getNodeType'
|
||||
import { RawCommands } from '../types'
|
||||
@ -10,7 +10,7 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Replace a given range with a node.
|
||||
*/
|
||||
setNode: (typeOrName: string | NodeType, attributes?: Record<string, any>) => ReturnType,
|
||||
setNode: (typeOrName: string | NodeType, attributes?: Record<string, any>) => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -25,19 +25,21 @@ export const setNode: RawCommands['setNode'] = (typeOrName, attributes = {}) =>
|
||||
return false
|
||||
}
|
||||
|
||||
return chain()
|
||||
return (
|
||||
chain()
|
||||
// try to convert node to default node if needed
|
||||
.command(({ commands }) => {
|
||||
const canSetBlock = setBlockType(type, attributes)(state)
|
||||
.command(({ commands }) => {
|
||||
const canSetBlock = setBlockType(type, attributes)(state)
|
||||
|
||||
if (canSetBlock) {
|
||||
return true
|
||||
}
|
||||
if (canSetBlock) {
|
||||
return true
|
||||
}
|
||||
|
||||
return commands.clearNodes()
|
||||
})
|
||||
.command(({ state: updatedState }) => {
|
||||
return setBlockType(type, attributes)(updatedState, dispatch)
|
||||
})
|
||||
.run()
|
||||
return commands.clearNodes()
|
||||
})
|
||||
.command(({ state: updatedState }) => {
|
||||
return setBlockType(type, attributes)(updatedState, dispatch)
|
||||
})
|
||||
.run()
|
||||
)
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { NodeSelection } from 'prosemirror-state'
|
||||
import { NodeSelection } from '@tiptap/pm/state'
|
||||
|
||||
import { RawCommands } from '../types'
|
||||
import { minMax } from '../utilities/minMax'
|
||||
@ -9,7 +9,7 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Creates a NodeSelection.
|
||||
*/
|
||||
setNodeSelection: (position: number) => ReturnType,
|
||||
setNodeSelection: (position: number) => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { TextSelection } from 'prosemirror-state'
|
||||
import { TextSelection } from '@tiptap/pm/state'
|
||||
|
||||
import { Range, RawCommands } from '../types'
|
||||
import { minMax } from '../utilities/minMax'
|
||||
@ -9,7 +9,7 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Creates a TextSelection.
|
||||
*/
|
||||
setTextSelection: (position: number | Range) => ReturnType,
|
||||
setTextSelection: (position: number | Range) => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -17,9 +17,7 @@ declare module '@tiptap/core' {
|
||||
export const setTextSelection: RawCommands['setTextSelection'] = position => ({ tr, dispatch }) => {
|
||||
if (dispatch) {
|
||||
const { doc } = tr
|
||||
const { from, to } = typeof position === 'number'
|
||||
? { from: position, to: position }
|
||||
: position
|
||||
const { from, to } = typeof position === 'number' ? { from: position, to: position } : position
|
||||
const minPos = TextSelection.atStart(doc).from
|
||||
const maxPos = TextSelection.atEnd(doc).to
|
||||
const resolvedFrom = minMax(from, minPos, maxPos)
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { NodeType } from 'prosemirror-model'
|
||||
import { sinkListItem as originalSinkListItem } from 'prosemirror-schema-list'
|
||||
import { NodeType } from '@tiptap/pm/model'
|
||||
import { sinkListItem as originalSinkListItem } from '@tiptap/pm/schema-list'
|
||||
|
||||
import { getNodeType } from '../helpers/getNodeType'
|
||||
import { RawCommands } from '../types'
|
||||
@ -10,7 +10,7 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Sink the list item down into an inner list.
|
||||
*/
|
||||
sinkListItem: (typeOrName: string | NodeType) => ReturnType,
|
||||
sinkListItem: (typeOrName: string | NodeType) => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,12 @@
|
||||
import { EditorState, NodeSelection, TextSelection } from 'prosemirror-state'
|
||||
import { canSplit } from 'prosemirror-transform'
|
||||
import { EditorState, NodeSelection, TextSelection } from '@tiptap/pm/state'
|
||||
import { canSplit } from '@tiptap/pm/transform'
|
||||
|
||||
import { defaultBlockAt } from '../helpers/defaultBlockAt'
|
||||
import { getSplittedAttributes } from '../helpers/getSplittedAttributes'
|
||||
import { RawCommands } from '../types'
|
||||
|
||||
function ensureMarks(state: EditorState, splittableMarks?: string[]) {
|
||||
const marks = state.storedMarks
|
||||
|| (state.selection.$to.parentOffset && state.selection.$from.marks())
|
||||
const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks())
|
||||
|
||||
if (marks) {
|
||||
const filteredMarks = marks.filter(mark => splittableMarks?.includes(mark.type.name))
|
||||
@ -22,16 +21,13 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Forks a new node from an existing node.
|
||||
*/
|
||||
splitBlock: (options?: { keepMarks?: boolean }) => ReturnType,
|
||||
splitBlock: (options?: { keepMarks?: boolean }) => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const splitBlock: RawCommands['splitBlock'] = ({ keepMarks = true } = {}) => ({
|
||||
tr,
|
||||
state,
|
||||
dispatch,
|
||||
editor,
|
||||
tr, state, dispatch, editor,
|
||||
}) => {
|
||||
const { selection, doc } = tr
|
||||
const { $from, $to } = selection
|
||||
@ -74,37 +70,36 @@ export const splitBlock: RawCommands['splitBlock'] = ({ keepMarks = true } = {})
|
||||
: defaultBlockAt($from.node(-1).contentMatchAt($from.indexAfter(-1)))
|
||||
|
||||
let types = atEnd && deflt
|
||||
? [{
|
||||
type: deflt,
|
||||
attrs: newAttributes,
|
||||
}]
|
||||
? [
|
||||
{
|
||||
type: deflt,
|
||||
attrs: newAttributes,
|
||||
},
|
||||
]
|
||||
: undefined
|
||||
|
||||
let can = canSplit(tr.doc, tr.mapping.map($from.pos), 1, types)
|
||||
|
||||
if (
|
||||
!types
|
||||
&& !can
|
||||
&& canSplit(tr.doc, tr.mapping.map($from.pos), 1, deflt ? [{ type: deflt }] : undefined)
|
||||
&& !can
|
||||
&& canSplit(tr.doc, tr.mapping.map($from.pos), 1, deflt ? [{ type: deflt }] : undefined)
|
||||
) {
|
||||
can = true
|
||||
types = deflt
|
||||
? [{
|
||||
type: deflt,
|
||||
attrs: newAttributes,
|
||||
}]
|
||||
? [
|
||||
{
|
||||
type: deflt,
|
||||
attrs: newAttributes,
|
||||
},
|
||||
]
|
||||
: undefined
|
||||
}
|
||||
|
||||
if (can) {
|
||||
tr.split(tr.mapping.map($from.pos), 1, types)
|
||||
|
||||
if (
|
||||
deflt
|
||||
&& !atEnd
|
||||
&& !$from.parentOffset
|
||||
&& $from.parent.type !== deflt
|
||||
) {
|
||||
if (deflt && !atEnd && !$from.parentOffset && $from.parent.type !== deflt) {
|
||||
const first = tr.mapping.map($from.before())
|
||||
const $first = tr.doc.resolve(first)
|
||||
|
||||
|
@ -1,11 +1,8 @@
|
||||
import {
|
||||
Fragment,
|
||||
Node as ProseMirrorNode,
|
||||
NodeType,
|
||||
Slice,
|
||||
} from 'prosemirror-model'
|
||||
import { TextSelection } from 'prosemirror-state'
|
||||
import { canSplit } from 'prosemirror-transform'
|
||||
Fragment, Node as ProseMirrorNode, NodeType, Slice,
|
||||
} from '@tiptap/pm/model'
|
||||
import { TextSelection } from '@tiptap/pm/state'
|
||||
import { canSplit } from '@tiptap/pm/transform'
|
||||
|
||||
import { getNodeType } from '../helpers/getNodeType'
|
||||
import { getSplittedAttributes } from '../helpers/getSplittedAttributes'
|
||||
@ -17,7 +14,7 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Splits one list item into two list items.
|
||||
*/
|
||||
splitListItem: (typeOrName: string | NodeType) => ReturnType,
|
||||
splitListItem: (typeOrName: string | NodeType) => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -30,7 +27,7 @@ export const splitListItem: RawCommands['splitListItem'] = typeOrName => ({
|
||||
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line
|
||||
const node: ProseMirrorNode = state.selection.node
|
||||
const node: ProseMirrorNode = state.selection.node
|
||||
|
||||
if ((node && node.isBlock) || $from.depth < 2 || !$from.sameParent($to)) {
|
||||
return false
|
||||
@ -50,8 +47,8 @@ export const splitListItem: RawCommands['splitListItem'] = typeOrName => ({
|
||||
// command handle lifting.
|
||||
if (
|
||||
$from.depth === 2
|
||||
|| $from.node(-3).type !== type
|
||||
|| $from.index(-2) !== $from.node(-2).childCount - 1
|
||||
|| $from.node(-3).type !== type
|
||||
|| $from.index(-2) !== $from.node(-2).childCount - 1
|
||||
) {
|
||||
return false
|
||||
}
|
||||
@ -59,11 +56,7 @@ export const splitListItem: RawCommands['splitListItem'] = typeOrName => ({
|
||||
if (dispatch) {
|
||||
let wrap = Fragment.empty
|
||||
// eslint-disable-next-line
|
||||
const depthBefore = $from.index(-1)
|
||||
? 1
|
||||
: $from.index(-2)
|
||||
? 2
|
||||
: 3
|
||||
const depthBefore = $from.index(-1) ? 1 : $from.index(-2) ? 2 : 3
|
||||
|
||||
// Build a fragment containing empty versions of the structure
|
||||
// from the outer list item to the parent node of the cursor
|
||||
@ -72,11 +65,7 @@ export const splitListItem: RawCommands['splitListItem'] = typeOrName => ({
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
const depthAfter = $from.indexAfter(-1) < $from.node(-2).childCount
|
||||
? 1
|
||||
: $from.indexAfter(-2) < $from.node(-3).childCount
|
||||
? 2
|
||||
: 3
|
||||
const depthAfter = $from.indexAfter(-1) < $from.node(-2).childCount ? 1 : $from.indexAfter(-2) < $from.node(-3).childCount ? 2 : 3
|
||||
|
||||
// Add a second list item with an empty default start node
|
||||
const newNextTypeAttributes = getSplittedAttributes(
|
||||
@ -114,9 +103,7 @@ export const splitListItem: RawCommands['splitListItem'] = typeOrName => ({
|
||||
return true
|
||||
}
|
||||
|
||||
const nextType = $to.pos === $from.end()
|
||||
? grandParent.contentMatchAt(0).defaultType
|
||||
: null
|
||||
const nextType = $to.pos === $from.end() ? grandParent.contentMatchAt(0).defaultType : null
|
||||
|
||||
const newTypeAttributes = getSplittedAttributes(
|
||||
extensionAttributes,
|
||||
@ -132,7 +119,10 @@ export const splitListItem: RawCommands['splitListItem'] = typeOrName => ({
|
||||
tr.delete($from.pos, $to.pos)
|
||||
|
||||
const types = nextType
|
||||
? [{ type, attrs: newTypeAttributes }, { type: nextType, attrs: newNextTypeAttributes }]
|
||||
? [
|
||||
{ type, attrs: newTypeAttributes },
|
||||
{ type: nextType, attrs: newNextTypeAttributes },
|
||||
]
|
||||
: [{ type, attrs: newTypeAttributes }]
|
||||
|
||||
if (!canSplit(tr.doc, $from.pos, 2)) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { NodeType } from 'prosemirror-model'
|
||||
import { Transaction } from 'prosemirror-state'
|
||||
import { canJoin } from 'prosemirror-transform'
|
||||
import { NodeType } from '@tiptap/pm/model'
|
||||
import { Transaction } from '@tiptap/pm/state'
|
||||
import { canJoin } from '@tiptap/pm/transform'
|
||||
|
||||
import { findParentNode } from '../helpers/findParentNode'
|
||||
import { getNodeType } from '../helpers/getNodeType'
|
||||
@ -21,8 +21,7 @@ const joinListBackwards = (tr: Transaction, listType: NodeType): boolean => {
|
||||
}
|
||||
|
||||
const nodeBefore = tr.doc.nodeAt(before)
|
||||
const canJoinBackwards = list.node.type === nodeBefore?.type
|
||||
&& canJoin(tr.doc, list.pos)
|
||||
const canJoinBackwards = list.node.type === nodeBefore?.type && canJoin(tr.doc, list.pos)
|
||||
|
||||
if (!canJoinBackwards) {
|
||||
return true
|
||||
@ -47,8 +46,7 @@ const joinListForwards = (tr: Transaction, listType: NodeType): boolean => {
|
||||
}
|
||||
|
||||
const nodeAfter = tr.doc.nodeAt(after)
|
||||
const canJoinForwards = list.node.type === nodeAfter?.type
|
||||
&& canJoin(tr.doc, after)
|
||||
const canJoinForwards = list.node.type === nodeAfter?.type && canJoin(tr.doc, after)
|
||||
|
||||
if (!canJoinForwards) {
|
||||
return true
|
||||
@ -65,7 +63,10 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Toggle between different list types.
|
||||
*/
|
||||
toggleList: (listTypeOrName: string | NodeType, itemTypeOrName: string | NodeType) => ReturnType,
|
||||
toggleList: (
|
||||
listTypeOrName: string | NodeType,
|
||||
itemTypeOrName: string | NodeType,
|
||||
) => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -95,8 +96,8 @@ export const toggleList: RawCommands['toggleList'] = (listTypeOrName, itemTypeOr
|
||||
// change list type
|
||||
if (
|
||||
isList(parentList.node.type.name, extensions)
|
||||
&& listType.validContent(parentList.node.content)
|
||||
&& dispatch
|
||||
&& listType.validContent(parentList.node.content)
|
||||
&& dispatch
|
||||
) {
|
||||
return chain()
|
||||
.command(() => {
|
||||
@ -110,19 +111,21 @@ export const toggleList: RawCommands['toggleList'] = (listTypeOrName, itemTypeOr
|
||||
}
|
||||
}
|
||||
|
||||
return chain()
|
||||
return (
|
||||
chain()
|
||||
// try to convert node to default node if needed
|
||||
.command(() => {
|
||||
const canWrapInList = can().wrapInList(listType)
|
||||
.command(() => {
|
||||
const canWrapInList = can().wrapInList(listType)
|
||||
|
||||
if (canWrapInList) {
|
||||
return true
|
||||
}
|
||||
if (canWrapInList) {
|
||||
return true
|
||||
}
|
||||
|
||||
return commands.clearNodes()
|
||||
})
|
||||
.wrapInList(listType)
|
||||
.command(() => joinListBackwards(tr, listType))
|
||||
.command(() => joinListForwards(tr, listType))
|
||||
.run()
|
||||
return commands.clearNodes()
|
||||
})
|
||||
.wrapInList(listType)
|
||||
.command(() => joinListBackwards(tr, listType))
|
||||
.command(() => joinListForwards(tr, listType))
|
||||
.run()
|
||||
)
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { MarkType } from 'prosemirror-model'
|
||||
import { MarkType } from '@tiptap/pm/model'
|
||||
|
||||
import { getMarkType } from '../helpers/getMarkType'
|
||||
import { isMarkActive } from '../helpers/isMarkActive'
|
||||
@ -17,9 +17,9 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Removes the mark even across the current selection. Defaults to `false`.
|
||||
*/
|
||||
extendEmptyMarkRange?: boolean,
|
||||
extendEmptyMarkRange?: boolean
|
||||
},
|
||||
) => ReturnType,
|
||||
) => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { NodeType } from 'prosemirror-model'
|
||||
import { NodeType } from '@tiptap/pm/model'
|
||||
|
||||
import { getNodeType } from '../helpers/getNodeType'
|
||||
import { isNodeActive } from '../helpers/isNodeActive'
|
||||
@ -10,7 +10,11 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Toggle a node with another node.
|
||||
*/
|
||||
toggleNode: (typeOrName: string | NodeType, toggleTypeOrName: string | NodeType, attributes?: Record<string, any>) => ReturnType,
|
||||
toggleNode: (
|
||||
typeOrName: string | NodeType,
|
||||
toggleTypeOrName: string | NodeType,
|
||||
attributes?: Record<string, any>,
|
||||
) => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { NodeType } from 'prosemirror-model'
|
||||
import { NodeType } from '@tiptap/pm/model'
|
||||
|
||||
import { getNodeType } from '../helpers/getNodeType'
|
||||
import { isNodeActive } from '../helpers/isNodeActive'
|
||||
@ -10,7 +10,7 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Wraps nodes in another node, or removes an existing wrap.
|
||||
*/
|
||||
toggleWrap: (typeOrName: string | NodeType, attributes?: Record<string, any>) => ReturnType,
|
||||
toggleWrap: (typeOrName: string | NodeType, attributes?: Record<string, any>) => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { MarkType } from 'prosemirror-model'
|
||||
import { MarkType } from '@tiptap/pm/model'
|
||||
|
||||
import { getMarkRange } from '../helpers/getMarkRange'
|
||||
import { getMarkType } from '../helpers/getMarkType'
|
||||
@ -16,9 +16,9 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Removes the mark even across the current selection. Defaults to `false`.
|
||||
*/
|
||||
extendEmptyMarkRange?: boolean,
|
||||
extendEmptyMarkRange?: boolean
|
||||
},
|
||||
) => ReturnType,
|
||||
) => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { MarkType, NodeType } from 'prosemirror-model'
|
||||
import { MarkType, NodeType } from '@tiptap/pm/model'
|
||||
|
||||
import { getMarkType } from '../helpers/getMarkType'
|
||||
import { getNodeType } from '../helpers/getNodeType'
|
||||
@ -11,7 +11,10 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Update attributes of a node or mark.
|
||||
*/
|
||||
updateAttributes: (typeOrName: string | NodeType | MarkType, attributes: Record<string, any>) => ReturnType,
|
||||
updateAttributes: (
|
||||
typeOrName: string | NodeType | MarkType,
|
||||
attributes: Record<string, any>,
|
||||
) => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -21,9 +24,7 @@ export const updateAttributes: RawCommands['updateAttributes'] = (typeOrName, at
|
||||
let markType: MarkType | null = null
|
||||
|
||||
const schemaType = getSchemaTypeNameByName(
|
||||
typeof typeOrName === 'string'
|
||||
? typeOrName
|
||||
: typeOrName.name,
|
||||
typeof typeOrName === 'string' ? typeOrName : typeOrName.name,
|
||||
state.schema,
|
||||
)
|
||||
|
||||
@ -58,10 +59,14 @@ export const updateAttributes: RawCommands['updateAttributes'] = (typeOrName, at
|
||||
const trimmedFrom = Math.max(pos, from)
|
||||
const trimmedTo = Math.min(pos + node.nodeSize, to)
|
||||
|
||||
tr.addMark(trimmedFrom, trimmedTo, markType.create({
|
||||
...mark.attrs,
|
||||
...attributes,
|
||||
}))
|
||||
tr.addMark(
|
||||
trimmedFrom,
|
||||
trimmedTo,
|
||||
markType.create({
|
||||
...mark.attrs,
|
||||
...attributes,
|
||||
}),
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { wrapIn as originalWrapIn } from 'prosemirror-commands'
|
||||
import { NodeType } from 'prosemirror-model'
|
||||
import { wrapIn as originalWrapIn } from '@tiptap/pm/commands'
|
||||
import { NodeType } from '@tiptap/pm/model'
|
||||
|
||||
import { getNodeType } from '../helpers/getNodeType'
|
||||
import { RawCommands } from '../types'
|
||||
@ -10,7 +10,7 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Wraps nodes in another node.
|
||||
*/
|
||||
wrapIn: (typeOrName: string | NodeType, attributes?: Record<string, any>) => ReturnType,
|
||||
wrapIn: (typeOrName: string | NodeType, attributes?: Record<string, any>) => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { NodeType } from 'prosemirror-model'
|
||||
import { wrapInList as originalWrapInList } from 'prosemirror-schema-list'
|
||||
import { NodeType } from '@tiptap/pm/model'
|
||||
import { wrapInList as originalWrapInList } from '@tiptap/pm/schema-list'
|
||||
|
||||
import { getNodeType } from '../helpers/getNodeType'
|
||||
import { RawCommands } from '../types'
|
||||
@ -10,7 +10,7 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Wrap a node in a list.
|
||||
*/
|
||||
wrapInList: (typeOrName: string | NodeType, attributes?: Record<string, any>) => ReturnType,
|
||||
wrapInList: (typeOrName: string | NodeType, attributes?: Record<string, any>) => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Plugin, PluginKey } from 'prosemirror-state'
|
||||
import { Plugin, PluginKey } from '@tiptap/pm/state'
|
||||
|
||||
import { Extension } from '../Extension'
|
||||
import { getTextBetween } from '../helpers/getTextBetween'
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Plugin, PluginKey } from 'prosemirror-state'
|
||||
import { Plugin, PluginKey } from '@tiptap/pm/state'
|
||||
|
||||
import { Extension } from '../Extension'
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Plugin, PluginKey } from 'prosemirror-state'
|
||||
import { Plugin, PluginKey } from '@tiptap/pm/state'
|
||||
|
||||
import { Extension } from '../Extension'
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Plugin, PluginKey, Selection } from 'prosemirror-state'
|
||||
import { Plugin, PluginKey, Selection } from '@tiptap/pm/state'
|
||||
|
||||
import { CommandManager } from '../CommandManager'
|
||||
import { Extension } from '../Extension'
|
||||
@ -19,12 +19,7 @@ export const Keymap = Extension.create({
|
||||
const { pos, parent } = $anchor
|
||||
const isAtStart = Selection.atStart(doc).from === pos
|
||||
|
||||
if (
|
||||
!empty
|
||||
|| !isAtStart
|
||||
|| !parent.type.isTextblock
|
||||
|| parent.textContent.length
|
||||
) {
|
||||
if (!empty || !isAtStart || !parent.type.isTextblock || parent.textContent.length) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Plugin, PluginKey } from 'prosemirror-state'
|
||||
import { Plugin, PluginKey } from '@tiptap/pm/state'
|
||||
|
||||
import { Extension } from '../Extension'
|
||||
|
||||
|
@ -1,11 +1,14 @@
|
||||
import { Node as ProseMirrorNode } from 'prosemirror-model'
|
||||
import { Transaction } from 'prosemirror-state'
|
||||
import { Transform } from 'prosemirror-transform'
|
||||
import { Node as ProseMirrorNode } from '@tiptap/pm/model'
|
||||
import { Transaction } from '@tiptap/pm/state'
|
||||
import { Transform } from '@tiptap/pm/transform'
|
||||
|
||||
/**
|
||||
* Returns a new `Transform` based on all steps of the passed transactions.
|
||||
*/
|
||||
export function combineTransactionSteps(oldDoc: ProseMirrorNode, transactions: Transaction[]): Transform {
|
||||
export function combineTransactionSteps(
|
||||
oldDoc: ProseMirrorNode,
|
||||
transactions: Transaction[],
|
||||
): Transform {
|
||||
const transform = new Transform(oldDoc)
|
||||
|
||||
transactions.forEach(transaction => {
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { EditorState, Transaction } from 'prosemirror-state'
|
||||
import { EditorState, Transaction } from '@tiptap/pm/state'
|
||||
|
||||
export function createChainableState(config: {
|
||||
transaction: Transaction,
|
||||
state: EditorState,
|
||||
transaction: Transaction
|
||||
state: EditorState
|
||||
}): EditorState {
|
||||
const { state, transaction } = config
|
||||
let { selection } = transaction
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Node as ProseMirrorNode, ParseOptions, Schema } from 'prosemirror-model'
|
||||
import { Node as ProseMirrorNode, ParseOptions, Schema } from '@tiptap/pm/model'
|
||||
|
||||
import { Content } from '../types'
|
||||
import { createNodeFromContent } from './createNodeFromContent'
|
||||
|
@ -4,14 +4,14 @@ import {
|
||||
Node as ProseMirrorNode,
|
||||
ParseOptions,
|
||||
Schema,
|
||||
} from 'prosemirror-model'
|
||||
} from '@tiptap/pm/model'
|
||||
|
||||
import { Content } from '../types'
|
||||
import { elementFromString } from '../utilities/elementFromString'
|
||||
|
||||
export type CreateNodeFromContentOptions = {
|
||||
slice?: boolean,
|
||||
parseOptions?: ParseOptions,
|
||||
slice?: boolean
|
||||
parseOptions?: ParseOptions
|
||||
}
|
||||
|
||||
export function createNodeFromContent(
|
||||
@ -33,13 +33,7 @@ export function createNodeFromContent(
|
||||
|
||||
return schema.nodeFromJSON(content)
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
'[tiptap warn]: Invalid content.',
|
||||
'Passed value:',
|
||||
content,
|
||||
'Error:',
|
||||
error,
|
||||
)
|
||||
console.warn('[tiptap warn]: Invalid content.', 'Passed value:', content, 'Error:', error)
|
||||
|
||||
return createNodeFromContent('', schema, options)
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user