mirror of
https://github.com/ueberdosis/tiptap.git
synced 2025-06-12 12:43:48 +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]
|
node-version: [16]
|
||||||
|
|
||||||
steps:
|
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 }}
|
- name: Load cached dependencies
|
||||||
uses: actions/setup-node@v3.5.1
|
uses: actions/cache@v3.0.11
|
||||||
with:
|
id: cache
|
||||||
node-version: ${{ matrix.node-version }}
|
with:
|
||||||
|
path: |
|
||||||
|
**/node_modules
|
||||||
|
/home/runner/.cache/Cypress
|
||||||
|
key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
|
||||||
|
|
||||||
- name: Load cached dependencies
|
- name: Install dependencies
|
||||||
uses: actions/cache@v3.0.11
|
id: install-dependencies
|
||||||
id: cache
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
with:
|
run: npm install
|
||||||
path: |
|
|
||||||
**/node_modules
|
|
||||||
/home/runner/.cache/Cypress
|
|
||||||
key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
|
|
||||||
|
|
||||||
- name: Install dependencies
|
# - name: Fix code style linting errors
|
||||||
id: install-dependencies
|
# id: lint-fix
|
||||||
if: steps.cache.outputs.cache-hit != 'true'
|
# run: npm run lint:fix
|
||||||
run: npm install
|
# 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
|
- name: Lint code
|
||||||
# id: lint-fix
|
id: lint
|
||||||
# run: npm run lint:fix
|
run: npm run lint
|
||||||
# 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
|
- name: Send Slack notifications
|
||||||
id: lint
|
uses: act10ns/slack@v1
|
||||||
run: npm run lint
|
if: failure()
|
||||||
|
with:
|
||||||
- name: Send Slack notifications
|
status: ${{ job.status }}
|
||||||
uses: act10ns/slack@v1
|
steps: ${{ toJson(steps) }}
|
||||||
if: failure()
|
channel: '#tiptap-notifications'
|
||||||
with:
|
|
||||||
status: ${{ job.status }}
|
|
||||||
steps: ${{ toJson(steps) }}
|
|
||||||
channel: '#tiptap-notifications'
|
|
||||||
|
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -79,48 +78,55 @@ jobs:
|
|||||||
node-version: [16]
|
node-version: [16]
|
||||||
|
|
||||||
steps:
|
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 }}
|
- name: Install dependencies
|
||||||
uses: actions/setup-node@v3.5.1
|
id: install-dependencies
|
||||||
with:
|
run: npm install
|
||||||
node-version: ${{ matrix.node-version }}
|
|
||||||
|
|
||||||
- name: Run tests with Cypress
|
- name: Try to build the packages
|
||||||
id: cypress
|
id: build-packages
|
||||||
uses: cypress-io/github-action@v4.2.0
|
run: npm run build:pm
|
||||||
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 screenshots (on failure only)
|
- name: Run tests with Cypress
|
||||||
uses: actions/upload-artifact@v3.1.0
|
id: cypress
|
||||||
if: failure()
|
uses: cypress-io/github-action@v4.2.0
|
||||||
with:
|
with:
|
||||||
name: cypress-screenshots
|
cache-key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
|
||||||
path: tests/cypress/screenshots
|
start: npm run start
|
||||||
retention-days: 7
|
wait-on: 'http://localhost:3000'
|
||||||
|
project: ./tests
|
||||||
|
browser: chrome
|
||||||
|
quiet: true
|
||||||
|
|
||||||
- name: Export screen recordings (on failure only)
|
- name: Export screenshots (on failure only)
|
||||||
uses: actions/upload-artifact@v3.1.0
|
uses: actions/upload-artifact@v3.1.0
|
||||||
if: failure()
|
if: failure()
|
||||||
with:
|
with:
|
||||||
name: cypress-videos
|
name: cypress-screenshots
|
||||||
path: tests/cypress/videos
|
path: tests/cypress/screenshots
|
||||||
retention-days: 7
|
retention-days: 7
|
||||||
|
|
||||||
- name: Send Slack notifications
|
- name: Export screen recordings (on failure only)
|
||||||
uses: act10ns/slack@v1
|
uses: actions/upload-artifact@v3.1.0
|
||||||
if: failure()
|
if: failure()
|
||||||
with:
|
with:
|
||||||
status: ${{ job.status }}
|
name: cypress-videos
|
||||||
steps: ${{ toJson(steps) }}
|
path: tests/cypress/videos
|
||||||
channel: '#tiptap-notifications'
|
retention-days: 7
|
||||||
|
|
||||||
|
- name: Send Slack notifications
|
||||||
|
uses: act10ns/slack@v1
|
||||||
|
if: failure()
|
||||||
|
with:
|
||||||
|
status: ${{ job.status }}
|
||||||
|
steps: ${{ toJson(steps) }}
|
||||||
|
channel: '#tiptap-notifications'
|
||||||
|
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -135,36 +141,35 @@ jobs:
|
|||||||
node-version: [16]
|
node-version: [16]
|
||||||
|
|
||||||
steps:
|
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 }}
|
- name: Load cached dependencies
|
||||||
uses: actions/setup-node@v3.5.1
|
uses: actions/cache@v3.0.11
|
||||||
with:
|
id: cache
|
||||||
node-version: ${{ matrix.node-version }}
|
with:
|
||||||
|
path: |
|
||||||
|
**/node_modules
|
||||||
|
/home/runner/.cache/Cypress
|
||||||
|
key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
|
||||||
|
|
||||||
- name: Load cached dependencies
|
- name: Install dependencies
|
||||||
uses: actions/cache@v3.0.11
|
id: install-dependencies
|
||||||
id: cache
|
if: steps.cache.outputs.cache-hit != 'true'
|
||||||
with:
|
run: npm install
|
||||||
path: |
|
|
||||||
**/node_modules
|
|
||||||
/home/runner/.cache/Cypress
|
|
||||||
key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Try to build the packages
|
||||||
id: install-dependencies
|
id: build-packages
|
||||||
if: steps.cache.outputs.cache-hit != 'true'
|
run: npm run build:ci
|
||||||
run: npm install
|
|
||||||
|
|
||||||
- name: Try to build the packages
|
- name: Send Slack notifications
|
||||||
id: build-packages
|
uses: act10ns/slack@v1
|
||||||
run: npm run build:ci
|
if: failure()
|
||||||
|
with:
|
||||||
- name: Send Slack notifications
|
status: ${{ job.status }}
|
||||||
uses: act10ns/slack@v1
|
steps: ${{ toJson(steps) }}
|
||||||
if: failure()
|
channel: '#tiptap-notifications'
|
||||||
with:
|
|
||||||
status: ${{ job.status }}
|
|
||||||
steps: ${{ toJson(steps) }}
|
|
||||||
channel: '#tiptap-notifications'
|
|
||||||
|
@ -10,7 +10,7 @@ prosemirror-keymap
|
|||||||
prosemirror-model
|
prosemirror-model
|
||||||
prosemirror-schema-list
|
prosemirror-schema-list
|
||||||
prosemirror-state
|
prosemirror-state
|
||||||
@tiptap/prosemirror-tables
|
prosemirror-tables
|
||||||
prosemirror-transform
|
prosemirror-transform
|
||||||
prosemirror-view
|
prosemirror-view
|
||||||
react
|
react
|
||||||
|
2
demos/package-lock.json
generated
2
demos/package-lock.json
generated
@ -33,7 +33,7 @@
|
|||||||
"sass": "^1.49.7",
|
"sass": "^1.49.7",
|
||||||
"svelte": "^3.49.0",
|
"svelte": "^3.49.0",
|
||||||
"tailwindcss": "^2.2.19",
|
"tailwindcss": "^2.2.19",
|
||||||
"typescript": "^4.5.5",
|
"typescript": "4.7.4",
|
||||||
"uuid": "^8.3.2",
|
"uuid": "^8.3.2",
|
||||||
"vite": "^2.9.13",
|
"vite": "^2.9.13",
|
||||||
"vite-plugin-checker": "^0.3.4",
|
"vite-plugin-checker": "^0.3.4",
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
"sass": "^1.49.7",
|
"sass": "^1.49.7",
|
||||||
"svelte": "^3.49.0",
|
"svelte": "^3.49.0",
|
||||||
"tailwindcss": "^2.2.19",
|
"tailwindcss": "^2.2.19",
|
||||||
"typescript": "^4.5.5",
|
"typescript": "4.7.4",
|
||||||
"uuid": "^8.3.2",
|
"uuid": "^8.3.2",
|
||||||
"vite": "^2.9.13",
|
"vite": "^2.9.13",
|
||||||
"vite-plugin-checker": "^0.3.4",
|
"vite-plugin-checker": "^0.3.4",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Extension } from '@tiptap/core'
|
import { Extension } from '@tiptap/core'
|
||||||
import { Plugin } from 'prosemirror-state'
|
import { Plugin } from '@tiptap/pm/state'
|
||||||
|
|
||||||
import findColors from './findColors'
|
import findColors from './findColors'
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Node } from 'prosemirror-model'
|
import { Node } from '@tiptap/pm/model'
|
||||||
import { Decoration, DecorationSet } from 'prosemirror-view'
|
import { Decoration, DecorationSet } from '@tiptap/pm/view'
|
||||||
|
|
||||||
export default function (doc: Node): DecorationSet {
|
export default function (doc: Node): DecorationSet {
|
||||||
const hexColor = /(#[0-9a-f]{3,6})\b/gi
|
const hexColor = /(#[0-9a-f]{3,6})\b/gi
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Extension } from '@tiptap/core'
|
import { Extension } from '@tiptap/core'
|
||||||
import { Plugin } from 'prosemirror-state'
|
import { Plugin } from '@tiptap/pm/state'
|
||||||
|
|
||||||
import findColors from './findColors'
|
import findColors from './findColors'
|
||||||
|
|
||||||
@ -14,9 +14,7 @@ export const ColorHighlighter = Extension.create({
|
|||||||
return findColors(doc)
|
return findColors(doc)
|
||||||
},
|
},
|
||||||
apply(transaction, oldState) {
|
apply(transaction, oldState) {
|
||||||
return transaction.docChanged
|
return transaction.docChanged ? findColors(transaction.doc) : oldState
|
||||||
? findColors(transaction.doc)
|
|
||||||
: oldState
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { Node } from 'prosemirror-model'
|
import { Node } from '@tiptap/pm/model'
|
||||||
import { Decoration, DecorationSet } from 'prosemirror-view'
|
import { Decoration, DecorationSet } from '@tiptap/pm/view'
|
||||||
|
|
||||||
export default function (doc: Node): DecorationSet {
|
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[] = []
|
const decorations: Decoration[] = []
|
||||||
|
|
||||||
doc.descendants((node, position) => {
|
doc.descendants((node, position) => {
|
||||||
@ -10,20 +10,18 @@ export default function (doc: Node): DecorationSet {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
Array
|
Array.from(node.text.matchAll(hexColor)).forEach(match => {
|
||||||
.from(node.text.matchAll(hexColor))
|
const color = match[0]
|
||||||
.forEach(match => {
|
const index = match.index || 0
|
||||||
const color = match[0]
|
const from = position + index
|
||||||
const index = match.index || 0
|
const to = from + color.length
|
||||||
const from = position + index
|
const decoration = Decoration.inline(from, to, {
|
||||||
const to = from + color.length
|
class: 'color',
|
||||||
const decoration = Decoration.inline(from, to, {
|
style: `--color: ${color}`,
|
||||||
class: 'color',
|
|
||||||
style: `--color: ${color}`,
|
|
||||||
})
|
|
||||||
|
|
||||||
decorations.push(decoration)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
decorations.push(decoration)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return DecorationSet.create(doc, decorations)
|
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 * as Y from 'yjs'
|
||||||
|
|
||||||
import { AnnotationState } from './AnnotationState'
|
import { AnnotationState } from './AnnotationState'
|
||||||
@ -8,10 +8,10 @@ export const AnnotationPluginKey = new PluginKey('annotation')
|
|||||||
export interface AnnotationPluginOptions {
|
export interface AnnotationPluginOptions {
|
||||||
HTMLAttributes: {
|
HTMLAttributes: {
|
||||||
[key: string]: any
|
[key: string]: any
|
||||||
},
|
}
|
||||||
onUpdate: (items: [any?]) => {},
|
onUpdate: (items: [any?]) => {}
|
||||||
map: Y.Map<any>,
|
map: Y.Map<any>
|
||||||
instance: string,
|
instance: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AnnotationPlugin = (options: AnnotationPluginOptions) => new Plugin({
|
export const AnnotationPlugin = (options: AnnotationPluginOptions) => new Plugin({
|
||||||
@ -39,9 +39,7 @@ export const AnnotationPlugin = (options: AnnotationPluginOptions) => new Plugin
|
|||||||
return decorations
|
return decorations
|
||||||
}
|
}
|
||||||
|
|
||||||
const annotations = this
|
const annotations = this.getState(state).annotationsAt(selection.from)
|
||||||
.getState(state)
|
|
||||||
.annotationsAt(selection.from)
|
|
||||||
|
|
||||||
options.onUpdate(annotations)
|
options.onUpdate(annotations)
|
||||||
|
|
||||||
|
@ -1,18 +1,26 @@
|
|||||||
import { EditorState, Transaction } from 'prosemirror-state'
|
import { EditorState, Transaction } from '@tiptap/pm/state'
|
||||||
import { Decoration, DecorationSet } from 'prosemirror-view'
|
import { Decoration, DecorationSet } from '@tiptap/pm/view'
|
||||||
import { absolutePositionToRelativePosition, relativePositionToAbsolutePosition, ySyncPluginKey } from 'y-prosemirror'
|
import {
|
||||||
|
absolutePositionToRelativePosition,
|
||||||
|
relativePositionToAbsolutePosition,
|
||||||
|
ySyncPluginKey,
|
||||||
|
} from 'y-prosemirror'
|
||||||
import * as Y from 'yjs'
|
import * as Y from 'yjs'
|
||||||
|
|
||||||
import { AnnotationItem } from './AnnotationItem'
|
import { AnnotationItem } from './AnnotationItem'
|
||||||
import { AnnotationPluginKey } from './AnnotationPlugin'
|
import { AnnotationPluginKey } from './AnnotationPlugin'
|
||||||
import { AddAnnotationAction, DeleteAnnotationAction, UpdateAnnotationAction } from './collaboration-annotation'
|
import {
|
||||||
|
AddAnnotationAction,
|
||||||
|
DeleteAnnotationAction,
|
||||||
|
UpdateAnnotationAction,
|
||||||
|
} from './collaboration-annotation'
|
||||||
|
|
||||||
export interface AnnotationStateOptions {
|
export interface AnnotationStateOptions {
|
||||||
HTMLAttributes: {
|
HTMLAttributes: {
|
||||||
[key: string]: any
|
[key: string]: any
|
||||||
},
|
}
|
||||||
map: Y.Map<any>,
|
map: Y.Map<any>
|
||||||
instance: string,
|
instance: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AnnotationState {
|
export class AnnotationState {
|
||||||
@ -93,14 +101,27 @@ export class AnnotationState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line
|
// 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) {
|
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(
|
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) {
|
apply(transaction: Transaction, state: EditorState) {
|
||||||
// Add/Remove annotations
|
// 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) {
|
if (action && action.type) {
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { mergeAttributes, Node } from '@tiptap/core'
|
import { mergeAttributes, Node } from '@tiptap/core'
|
||||||
import { Plugin } from 'prosemirror-state'
|
import { Plugin } from '@tiptap/pm/state'
|
||||||
|
|
||||||
export const Figure = Node.create({
|
export const Figure = Node.create({
|
||||||
name: 'figure',
|
name: 'figure',
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Extension } from '@tiptap/core'
|
import { Extension } from '@tiptap/core'
|
||||||
import { NodeSelection, Plugin } from 'prosemirror-state'
|
import { NodeSelection, Plugin } from '@tiptap/pm/state'
|
||||||
import { __serializeForClipboard as serializeForClipboard } from 'prosemirror-view'
|
import { __serializeForClipboard as serializeForClipboard } from '@tiptap/pm/view'
|
||||||
|
|
||||||
function removeNode(node) {
|
function removeNode(node) {
|
||||||
node.parentNode.removeChild(node)
|
node.parentNode.removeChild(node)
|
||||||
@ -25,7 +25,8 @@ export default Extension.create({
|
|||||||
node = node.node
|
node = node.node
|
||||||
|
|
||||||
while (node && node.parentNode) {
|
while (node && node.parentNode) {
|
||||||
if (node.parentNode?.classList?.contains('ProseMirror')) { // todo
|
if (node.parentNode?.classList?.contains('ProseMirror')) {
|
||||||
|
// todo
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +132,8 @@ export default Extension.create({
|
|||||||
if (node) {
|
if (node) {
|
||||||
node = node.node
|
node = node.node
|
||||||
while (node && node.parentNode) {
|
while (node && node.parentNode) {
|
||||||
if (node.parentNode?.classList?.contains('ProseMirror')) { // todo
|
if (node.parentNode?.classList?.contains('ProseMirror')) {
|
||||||
|
// todo
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
node = node.parentNode
|
node = node.parentNode
|
||||||
@ -145,7 +147,7 @@ export default Extension.create({
|
|||||||
const rect = absoluteRect(node)
|
const rect = absoluteRect(node)
|
||||||
const win = node.ownerDocument.defaultView
|
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.left += win.pageXOffset
|
||||||
rect.width = `${WIDTH}px`
|
rect.width = `${WIDTH}px`
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Extension } from '@tiptap/core'
|
import { Extension } from '@tiptap/core'
|
||||||
import { Node as ProsemirrorNode } from 'prosemirror-model'
|
import { Node as ProsemirrorNode } from '@tiptap/pm/model'
|
||||||
import { Plugin, PluginKey, TextSelection } from 'prosemirror-state'
|
import { Plugin, PluginKey, TextSelection } from '@tiptap/pm/state'
|
||||||
import { Decoration, DecorationSet } from 'prosemirror-view'
|
import { Decoration, DecorationSet } from '@tiptap/pm/view'
|
||||||
|
|
||||||
import LinterPlugin, { Result as Issue } from './LinterPlugin'
|
import LinterPlugin, { Result as Issue } from './LinterPlugin'
|
||||||
|
|
||||||
@ -22,9 +22,11 @@ function renderIcon(issue: Issue) {
|
|||||||
function runAllLinterPlugins(doc: ProsemirrorNode, plugins: Array<typeof LinterPlugin>) {
|
function runAllLinterPlugins(doc: ProsemirrorNode, plugins: Array<typeof LinterPlugin>) {
|
||||||
const decorations: [any?] = []
|
const decorations: [any?] = []
|
||||||
|
|
||||||
const results = plugins.map(RegisteredLinterPlugin => {
|
const results = plugins
|
||||||
return new RegisteredLinterPlugin(doc).scan().getResults()
|
.map(RegisteredLinterPlugin => {
|
||||||
}).flat()
|
return new RegisteredLinterPlugin(doc).scan().getResults()
|
||||||
|
})
|
||||||
|
.flat()
|
||||||
|
|
||||||
results.forEach(issue => {
|
results.forEach(issue => {
|
||||||
decorations.push(
|
decorations.push(
|
||||||
@ -39,7 +41,7 @@ function runAllLinterPlugins(doc: ProsemirrorNode, plugins: Array<typeof LinterP
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface LinterOptions {
|
export interface LinterOptions {
|
||||||
plugins: Array<typeof LinterPlugin>,
|
plugins: Array<typeof LinterPlugin>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Linter = Extension.create<LinterOptions>({
|
export const Linter = Extension.create<LinterOptions>({
|
||||||
@ -62,9 +64,7 @@ export const Linter = Extension.create<LinterOptions>({
|
|||||||
return runAllLinterPlugins(doc, plugins)
|
return runAllLinterPlugins(doc, plugins)
|
||||||
},
|
},
|
||||||
apply(transaction, oldState) {
|
apply(transaction, oldState) {
|
||||||
return transaction.docChanged
|
return transaction.docChanged ? runAllLinterPlugins(transaction.doc, plugins) : oldState
|
||||||
? runAllLinterPlugins(transaction.doc, plugins)
|
|
||||||
: oldState
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
@ -72,7 +72,7 @@ export const Linter = Extension.create<LinterOptions>({
|
|||||||
return this.getState(state)
|
return this.getState(state)
|
||||||
},
|
},
|
||||||
handleClick(view, _, event) {
|
handleClick(view, _, event) {
|
||||||
const target = (event.target as IconDivElement)
|
const target = event.target as IconDivElement
|
||||||
|
|
||||||
if (/lint-icon/.test(target.className) && target.issue) {
|
if (/lint-icon/.test(target.className) && target.issue) {
|
||||||
const { from, to } = target.issue
|
const { from, to } = target.issue
|
||||||
@ -89,7 +89,7 @@ export const Linter = Extension.create<LinterOptions>({
|
|||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
handleDoubleClick(view, _, event) {
|
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) {
|
if (/lint-icon/.test((event.target as HTMLElement).className) && target.issue) {
|
||||||
const prob = 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 {
|
export interface Result {
|
||||||
message: string,
|
message: string
|
||||||
from: number,
|
from: number
|
||||||
to: number,
|
to: number
|
||||||
fix?: Function
|
fix?: Function
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { EditorView } from 'prosemirror-view'
|
import { EditorView } from '@tiptap/pm/view'
|
||||||
|
|
||||||
import LinterPlugin, { Result as Issue } from '../LinterPlugin'
|
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'
|
import LinterPlugin, { Result as Issue } from '../LinterPlugin'
|
||||||
|
|
||||||
@ -7,13 +7,7 @@ export class Punctuation extends LinterPlugin {
|
|||||||
|
|
||||||
fix(replacement: any) {
|
fix(replacement: any) {
|
||||||
return function ({ state, dispatch }: EditorView, issue: Issue) {
|
return function ({ state, dispatch }: EditorView, issue: Issue) {
|
||||||
dispatch(
|
dispatch(state.tr.replaceWith(issue.from, issue.to, state.schema.text(replacement)))
|
||||||
state.tr.replaceWith(
|
|
||||||
issue.from,
|
|
||||||
issue.to,
|
|
||||||
state.schema.text(replacement),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Extension } from '@tiptap/core'
|
import { Extension } from '@tiptap/core'
|
||||||
import { Plugin, PluginKey } from 'prosemirror-state'
|
import { Plugin, PluginKey } from '@tiptap/pm/state'
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
function nodeEqualsType({ types, node }) {
|
function nodeEqualsType({ types, node }) {
|
||||||
@ -13,8 +13,8 @@ function nodeEqualsType({ types, node }) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export interface TrailingNodeOptions {
|
export interface TrailingNodeOptions {
|
||||||
node: string,
|
node: string
|
||||||
notAfter: string[],
|
notAfter: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TrailingNode = Extension.create<TrailingNodeOptions>({
|
export const TrailingNode = Extension.create<TrailingNodeOptions>({
|
||||||
@ -23,9 +23,7 @@ export const TrailingNode = Extension.create<TrailingNodeOptions>({
|
|||||||
addOptions() {
|
addOptions() {
|
||||||
return {
|
return {
|
||||||
node: 'paragraph',
|
node: 'paragraph',
|
||||||
notAfter: [
|
notAfter: ['paragraph'],
|
||||||
'paragraph',
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -11,8 +11,30 @@ import {
|
|||||||
} from 'path'
|
} from 'path'
|
||||||
import { v4 as uuid } from 'uuid'
|
import { v4 as uuid } from 'uuid'
|
||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
|
|
||||||
// import checker from 'vite-plugin-checker'
|
// 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')
|
const includeDependencies = fs.readFileSync('./includeDependencies.txt')
|
||||||
.toString()
|
.toString()
|
||||||
.replace(/\r\n/g, '\n')
|
.replace(/\r\n/g, '\n')
|
||||||
@ -271,12 +293,6 @@ export default defineConfig({
|
|||||||
],
|
],
|
||||||
|
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: [
|
alias: getPackageDependencies(),
|
||||||
...fg.sync('../packages/*', { onlyDirectories: true })
|
|
||||||
.map(name => name.replace('../packages/', ''))
|
|
||||||
.map(name => {
|
|
||||||
return { find: `@tiptap/${name}`, replacement: resolve(`../packages/${name}/src/index.ts`) }
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -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:
|
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
|
```js
|
||||||
import { exitCode } from 'prosemirror-commands'
|
import { exitCode } from '@tiptap/pm/commands'
|
||||||
|
|
||||||
export default () => ({ state, dispatch }) => {
|
export default () => ({ state, dispatch }) => {
|
||||||
return exitCode(state, dispatch)
|
return exitCode(state, dispatch)
|
||||||
|
@ -112,7 +112,7 @@ Alternatively you can pass a ProseMirror `PluginKey`.
|
|||||||
```js
|
```js
|
||||||
import { Editor } from '@tiptap/core'
|
import { Editor } from '@tiptap/core'
|
||||||
import BubbleMenu from '@tiptap/extension-bubble-menu'
|
import BubbleMenu from '@tiptap/extension-bubble-menu'
|
||||||
import { PluginKey } from 'prosemirror-state'
|
import { PluginKey } from '@tiptap/pm/state'
|
||||||
|
|
||||||
new Editor({
|
new Editor({
|
||||||
extensions: [
|
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
|
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.
|
This extension requires the [`Collaboration`](/api/extensions/collaboration) extension.
|
||||||
|
|
||||||
## Settings
|
## 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
|
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
|
## Settings
|
||||||
|
|
||||||
### document
|
### document
|
||||||
|
@ -4,6 +4,7 @@ icon: drag-drop-line
|
|||||||
---
|
---
|
||||||
|
|
||||||
# Dropcursor
|
# Dropcursor
|
||||||
|
|
||||||
[](https://www.npmjs.com/package/@tiptap/extension-dropcursor)
|
[](https://www.npmjs.com/package/@tiptap/extension-dropcursor)
|
||||||
[](https://npmcharts.com/compare/@tiptap/extension-dropcursor?minimal=true)
|
[](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.
|
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
|
## Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install @tiptap/extension-dropcursor
|
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
|
## Settings
|
||||||
|
|
||||||
### color
|
### color
|
||||||
|
|
||||||
Color of the dropcursor.
|
Color of the dropcursor.
|
||||||
|
|
||||||
Default: `'currentColor'`
|
Default: `'currentColor'`
|
||||||
|
|
||||||
```js
|
```js
|
||||||
Dropcursor.configure({
|
Dropcursor.configure({
|
||||||
color: '#ff0000'
|
color: '#ff0000',
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
### width
|
### width
|
||||||
|
|
||||||
Width of the dropcursor.
|
Width of the dropcursor.
|
||||||
|
|
||||||
Default: `1`
|
Default: `1`
|
||||||
@ -45,6 +45,7 @@ Dropcursor.configure({
|
|||||||
```
|
```
|
||||||
|
|
||||||
### class
|
### class
|
||||||
|
|
||||||
One or multiple CSS classes that should be applied to the dropcursor.
|
One or multiple CSS classes that should be applied to the dropcursor.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
@ -54,7 +55,9 @@ Dropcursor.configure({
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Source code
|
## Source code
|
||||||
|
|
||||||
[packages/extension-dropcursor/](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-dropcursor/)
|
[packages/extension-dropcursor/](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-dropcursor/)
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
https://embed.tiptap.dev/preview/Extensions/Dropcursor
|
https://embed.tiptap.dev/preview/Extensions/Dropcursor
|
||||||
|
@ -100,7 +100,7 @@ Alternatively you can pass a ProseMirror `PluginKey`.
|
|||||||
```js
|
```js
|
||||||
import { Editor } from '@tiptap/core'
|
import { Editor } from '@tiptap/core'
|
||||||
import FloatingMenu from '@tiptap/extension-floating-menu'
|
import FloatingMenu from '@tiptap/extension-floating-menu'
|
||||||
import { PluginKey } from 'prosemirror-state'
|
import { PluginKey } from '@tiptap/pm/state'
|
||||||
|
|
||||||
new Editor({
|
new Editor({
|
||||||
extensions: [
|
extensions: [
|
||||||
|
@ -4,6 +4,7 @@ icon: space
|
|||||||
---
|
---
|
||||||
|
|
||||||
# Gapcursor
|
# Gapcursor
|
||||||
|
|
||||||
[](https://www.npmjs.com/package/@tiptap/extension-gapcursor)
|
[](https://www.npmjs.com/package/@tiptap/extension-gapcursor)
|
||||||
[](https://npmcharts.com/compare/@tiptap/extension-gapcursor?minimal=true)
|
[](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.
|
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
|
## Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install @tiptap/extension-gapcursor
|
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
|
## Source code
|
||||||
|
|
||||||
[packages/extension-gapcursor/](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-gapcursor/)
|
[packages/extension-gapcursor/](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-gapcursor/)
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
https://embed.tiptap.dev/preview/Extensions/Gapcursor
|
https://embed.tiptap.dev/preview/Extensions/Gapcursor
|
||||||
|
@ -4,23 +4,22 @@ icon: history-line
|
|||||||
---
|
---
|
||||||
|
|
||||||
# History
|
# History
|
||||||
|
|
||||||
[](https://www.npmjs.com/package/@tiptap/extension-history)
|
[](https://www.npmjs.com/package/@tiptap/extension-history)
|
||||||
[](https://npmcharts.com/compare/@tiptap/extension-history?minimal=true)
|
[](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.
|
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
|
## Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install @tiptap/extension-history
|
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
|
## Settings
|
||||||
|
|
||||||
### depth
|
### depth
|
||||||
|
|
||||||
The amount of history events that are collected before the oldest events are discarded. Defaults to 100.
|
The amount of history events that are collected before the oldest events are discarded. Defaults to 100.
|
||||||
|
|
||||||
Default: `100`
|
Default: `100`
|
||||||
@ -32,6 +31,7 @@ History.configure({
|
|||||||
```
|
```
|
||||||
|
|
||||||
### newGroupDelay
|
### 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.
|
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`
|
Default: `500`
|
||||||
@ -45,12 +45,15 @@ History.configure({
|
|||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
### undo()
|
### undo()
|
||||||
|
|
||||||
Undo the last change.
|
Undo the last change.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
editor.commands.undo()
|
editor.commands.undo()
|
||||||
```
|
```
|
||||||
|
|
||||||
### redo()
|
### redo()
|
||||||
|
|
||||||
Redo the last change.
|
Redo the last change.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
@ -58,13 +61,16 @@ editor.commands.redo()
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Keyboard shortcuts
|
## Keyboard shortcuts
|
||||||
|
|
||||||
| Command | Windows/Linux | macOS |
|
| Command | Windows/Linux | macOS |
|
||||||
| ------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
|
| ------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
|
||||||
| undo() | `Control` `Z`<br>`Control` `я` | `Cmd` `Z`<br>`Cmd` `я` |
|
| 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` `я` |
|
| redo() | `Shift` `Control` `Z`<br>`Control` `Y`<br>`Shift` `Control` `я` | `Shift` `Cmd` `Z`<br>`Cmd` `Y`<br>`Shift` `Cmd` `я` |
|
||||||
|
|
||||||
## Source code
|
## Source code
|
||||||
|
|
||||||
[packages/extension-history/](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-history/)
|
[packages/extension-history/](https://github.com/ueberdosis/tiptap/blob/main/packages/extension-history/)
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
https://embed.tiptap.dev/preview/Extensions/History
|
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
|
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
|
## Included extensions
|
||||||
|
|
||||||
### Nodes
|
### Nodes
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Introduction
|
# 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
|
### 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.
|
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
|
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:
|
Now, create a new Y document, and register it with Tiptap:
|
||||||
|
|
||||||
```js
|
```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
|
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:
|
And then register the WebSocket provider with Tiptap:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
@ -293,7 +285,7 @@ server.listen()
|
|||||||
## Pitfalls
|
## Pitfalls
|
||||||
|
|
||||||
### Schema updates
|
### 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.
|
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
|
### 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:
|
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.
|
You can wrap existing ProseMirror plugins in Tiptap extensions like shown in the example below.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
import { history } from 'prosemirror-history'
|
import { history } from '@tiptap/pm/history'
|
||||||
|
|
||||||
const History = Extension.create({
|
const History = Extension.create({
|
||||||
addProseMirrorPlugins() {
|
addProseMirrorPlugins() {
|
||||||
@ -550,7 +550,7 @@ Or you can add them to a Tiptap extension like shown in the below example.
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
import { Extension } from '@tiptap/core'
|
import { Extension } from '@tiptap/core'
|
||||||
import { Plugin, PluginKey } from 'prosemirror-state'
|
import { Plugin, PluginKey } from '@tiptap/pm/state'
|
||||||
|
|
||||||
export const EventHandler = Extension.create({
|
export const EventHandler = Extension.create({
|
||||||
name: 'eventHandler',
|
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
|
# Installation
|
||||||
|
|
||||||
## Introduction
|
## 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.
|
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
|
## Base Setup
|
||||||
<!-- * [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)
|
|
||||||
|
|
||||||
:::warning Are you using Yarn, pNPM, npm 6 or less?
|
To get started you will need to install `@tiptap/core`, `@tiptap/pm` and `@tiptap/starter-kit` in your project like this:
|
||||||
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.
|
|
||||||
:::
|
```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
|
### 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
|
## 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
|
```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.
|
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
|
## 3. Initialize the editor
|
||||||
@ -74,7 +70,7 @@ document.addEventListener('alpine:init', () => {
|
|||||||
onSelectionUpdate({ editor }) {
|
onSelectionUpdate({ editor }) {
|
||||||
_this.updatedAt = Date.now()
|
_this.updatedAt = Date.now()
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
isLoaded() {
|
isLoaded() {
|
||||||
return editor
|
return editor
|
||||||
@ -91,9 +87,9 @@ document.addEventListener('alpine:init', () => {
|
|||||||
toggleItalic() {
|
toggleItalic() {
|
||||||
editor.chain().toggleItalic().focus().run()
|
editor.chain().toggleItalic().focus().run()
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
window.Alpine = Alpine
|
window.Alpine = Alpine
|
||||||
Alpine.start()
|
Alpine.start()
|
||||||
|
@ -25,16 +25,12 @@ cd my-tiptap-project
|
|||||||
```
|
```
|
||||||
|
|
||||||
## 2. Install the dependencies
|
## 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
|
```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.
|
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
|
## 3. Create a new component
|
||||||
@ -57,7 +53,7 @@ const Tiptap = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Tiptap;
|
export default Tiptap
|
||||||
```
|
```
|
||||||
|
|
||||||
## 4. Add it to your app
|
## 4. Add it to your app
|
||||||
|
@ -26,16 +26,12 @@ cd my-tiptap-project
|
|||||||
```
|
```
|
||||||
|
|
||||||
## 2. Install the dependencies
|
## 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
|
```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.
|
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
|
## 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.
|
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
|
```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.
|
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
|
#### 3. Create a new component
|
||||||
|
@ -28,16 +28,12 @@ npm run dev
|
|||||||
```
|
```
|
||||||
|
|
||||||
## 2. Install the dependencies
|
## 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
|
```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.
|
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
|
## 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.
|
You are using plain JavaScript or a framework that is not listed here? No worries, we provide everything you need.
|
||||||
|
|
||||||
## 1. Install the dependencies
|
## 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.
|
The StarterKit doesn’t include all, but the most common extensions.
|
||||||
|
|
||||||
```bash
|
```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
|
## 2. Add some markup
|
||||||
Add the following HTML where you want the editor to be mounted:
|
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
|
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.
|
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
|
## 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
|
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.
|
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
|
## 3. Create a new component
|
||||||
|
@ -10,7 +10,7 @@ tableOfContents: true
|
|||||||
[](https://www.npmjs.com/package/@tiptap/core)
|
[](https://www.npmjs.com/package/@tiptap/core)
|
||||||
[](https://github.com/sponsors/ueberdosis)
|
[](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.
|
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:
|
items:
|
||||||
- title: Configuration
|
- title: Configuration
|
||||||
link: /guide/configuration
|
link: /guide/configuration
|
||||||
|
- title: ProseMirror
|
||||||
|
link: /guide/prosemirror
|
||||||
- title: Menus
|
- title: Menus
|
||||||
link: /guide/menus
|
link: /guide/menus
|
||||||
- title: Styling
|
- 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": "eslint --quiet --no-error-on-unmatched-pattern ./",
|
||||||
"lint:fix": "eslint --fix --quiet --no-error-on-unmatched-pattern ./",
|
"lint:fix": "eslint --fix --quiet --no-error-on-unmatched-pattern ./",
|
||||||
"lint:staged": "lint-staged",
|
"lint:staged": "lint-staged",
|
||||||
"test:open": "cypress open --project tests",
|
"test:open": "npm run build:pm && cypress open --project tests",
|
||||||
"test": "cypress run --project tests",
|
"test": "npm run build:pm && cypress run --project tests",
|
||||||
"build": "npm run clean:packages && lerna run build",
|
"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:demos": "npm --prefix ./demos run build:demos",
|
||||||
"build:ci": "npm run build",
|
"build:ci": "npm run build",
|
||||||
"release:major": "lerna version major --force-publish",
|
"release:major": "lerna version major --force-publish",
|
||||||
@ -26,9 +27,10 @@
|
|||||||
"release:patch:pre": "lerna version prepatch --force-publish",
|
"release:patch:pre": "lerna version prepatch --force-publish",
|
||||||
"release:pre": "lerna version prerelease --force-publish",
|
"release:pre": "lerna version prerelease --force-publish",
|
||||||
"publish": "npm run build:packages && lerna exec --since --no-private -- npm publish --access public",
|
"publish": "npm run build:packages && lerna exec --since --no-private -- npm publish --access public",
|
||||||
"pack": "lerna exec -- npm pack",
|
"pack": "npm run clean:packs && lerna exec -- npm pack",
|
||||||
"clean:packages": "rm -rf ./packages/*/dist",
|
"clean:packages": "rm -rf ./packages/*/dist && rm -rf ./packages/pm/*/dist",
|
||||||
"reset": "npm run clean:packages && rm -rf ./**/.cache && rm -rf ./**/node_modules && rm -rf ./package-lock.json && npm install",
|
"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"
|
"prepare": "husky install"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -56,7 +58,7 @@
|
|||||||
"lerna": "^5.5.1",
|
"lerna": "^5.5.1",
|
||||||
"lint-staged": "^13.0.3",
|
"lint-staged": "^13.0.3",
|
||||||
"minimist": "^1.2.5",
|
"minimist": "^1.2.5",
|
||||||
"ts-loader": "^9.2.6",
|
"ts-loader": "9.3.1",
|
||||||
"tsup": "^6.5.0",
|
"tsup": "^6.5.0",
|
||||||
"typescript": "4.7.4",
|
"typescript": "4.7.4",
|
||||||
"webpack": "^5.68.0"
|
"webpack": "^5.68.0"
|
||||||
|
@ -31,22 +31,10 @@
|
|||||||
"dist"
|
"dist"
|
||||||
],
|
],
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"prosemirror-commands": "^1.3.1",
|
"@tiptap/pm": "^2.0.0-beta.209"
|
||||||
"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"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"prosemirror-commands": "^1.3.1",
|
"@tiptap/pm": "^2.0.0-beta.209"
|
||||||
"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"
|
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -1,27 +1,19 @@
|
|||||||
import { EditorState, Transaction } from 'prosemirror-state'
|
import { EditorState, Transaction } from '@tiptap/pm/state'
|
||||||
|
|
||||||
import { Editor } from './Editor'
|
import { Editor } from './Editor'
|
||||||
import { createChainableState } from './helpers/createChainableState'
|
import { createChainableState } from './helpers/createChainableState'
|
||||||
import {
|
import {
|
||||||
AnyCommands,
|
AnyCommands, CanCommands, ChainedCommands, CommandProps, SingleCommands,
|
||||||
CanCommands,
|
|
||||||
ChainedCommands,
|
|
||||||
CommandProps,
|
|
||||||
SingleCommands,
|
|
||||||
} from './types'
|
} from './types'
|
||||||
|
|
||||||
export class CommandManager {
|
export class CommandManager {
|
||||||
|
|
||||||
editor: Editor
|
editor: Editor
|
||||||
|
|
||||||
rawCommands: AnyCommands
|
rawCommands: AnyCommands
|
||||||
|
|
||||||
customState?: EditorState
|
customState?: EditorState
|
||||||
|
|
||||||
constructor(props: {
|
constructor(props: { editor: Editor; state?: EditorState }) {
|
||||||
editor: Editor,
|
|
||||||
state?: EditorState,
|
|
||||||
}) {
|
|
||||||
this.editor = props.editor
|
this.editor = props.editor
|
||||||
this.rawCommands = this.editor.extensionManager.commands
|
this.rawCommands = this.editor.extensionManager.commands
|
||||||
this.customState = props.state
|
this.customState = props.state
|
||||||
@ -41,9 +33,8 @@ export class CommandManager {
|
|||||||
const { tr } = state
|
const { tr } = state
|
||||||
const props = this.buildProps(tr)
|
const props = this.buildProps(tr)
|
||||||
|
|
||||||
return Object.fromEntries(Object
|
return Object.fromEntries(
|
||||||
.entries(rawCommands)
|
Object.entries(rawCommands).map(([name, command]) => {
|
||||||
.map(([name, command]) => {
|
|
||||||
const method = (...args: any[]) => {
|
const method = (...args: any[]) => {
|
||||||
const callback = command(...args)(props)
|
const callback = command(...args)(props)
|
||||||
|
|
||||||
@ -55,7 +46,8 @@ export class CommandManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return [name, method]
|
return [name, method]
|
||||||
})) as unknown as SingleCommands
|
}),
|
||||||
|
) as unknown as SingleCommands
|
||||||
}
|
}
|
||||||
|
|
||||||
get chain(): () => ChainedCommands {
|
get chain(): () => ChainedCommands {
|
||||||
@ -87,18 +79,20 @@ export class CommandManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const chain = {
|
const chain = {
|
||||||
...Object.fromEntries(Object.entries(rawCommands).map(([name, command]) => {
|
...Object.fromEntries(
|
||||||
const chainedCommand = (...args: never[]) => {
|
Object.entries(rawCommands).map(([name, command]) => {
|
||||||
const props = this.buildProps(tr, shouldDispatch)
|
const chainedCommand = (...args: never[]) => {
|
||||||
const callback = command(...args)(props)
|
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,
|
run,
|
||||||
} as unknown as ChainedCommands
|
} as unknown as ChainedCommands
|
||||||
|
|
||||||
@ -110,11 +104,11 @@ export class CommandManager {
|
|||||||
const dispatch = false
|
const dispatch = false
|
||||||
const tr = startTr || state.tr
|
const tr = startTr || state.tr
|
||||||
const props = this.buildProps(tr, dispatch)
|
const props = this.buildProps(tr, dispatch)
|
||||||
const formattedCommands = Object.fromEntries(Object
|
const formattedCommands = Object.fromEntries(
|
||||||
.entries(rawCommands)
|
Object.entries(rawCommands).map(([name, command]) => {
|
||||||
.map(([name, command]) => {
|
|
||||||
return [name, (...args: never[]) => command(...args)({ ...props, dispatch: undefined })]
|
return [name, (...args: never[]) => command(...args)({ ...props, dispatch: undefined })]
|
||||||
})) as unknown as SingleCommands
|
}),
|
||||||
|
) as unknown as SingleCommands
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...formattedCommands,
|
...formattedCommands,
|
||||||
@ -138,21 +132,18 @@ export class CommandManager {
|
|||||||
state,
|
state,
|
||||||
transaction: tr,
|
transaction: tr,
|
||||||
}),
|
}),
|
||||||
dispatch: shouldDispatch
|
dispatch: shouldDispatch ? () => undefined : undefined,
|
||||||
? () => undefined
|
|
||||||
: undefined,
|
|
||||||
chain: () => this.createChain(tr),
|
chain: () => this.createChain(tr),
|
||||||
can: () => this.createCan(tr),
|
can: () => this.createCan(tr),
|
||||||
get commands() {
|
get commands() {
|
||||||
return Object.fromEntries(Object
|
return Object.fromEntries(
|
||||||
.entries(rawCommands)
|
Object.entries(rawCommands).map(([name, command]) => {
|
||||||
.map(([name, command]) => {
|
|
||||||
return [name, (...args: never[]) => command(...args)(props)]
|
return [name, (...args: never[]) => command(...args)(props)]
|
||||||
})) as unknown as SingleCommands
|
}),
|
||||||
|
) as unknown as SingleCommands
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return props
|
return props
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
import { MarkType, NodeType, Schema } from 'prosemirror-model'
|
import { MarkType, NodeType, Schema } from '@tiptap/pm/model'
|
||||||
import {
|
import {
|
||||||
EditorState,
|
EditorState, Plugin, PluginKey, Transaction,
|
||||||
Plugin,
|
} from '@tiptap/pm/state'
|
||||||
PluginKey,
|
import { EditorView } from '@tiptap/pm/view'
|
||||||
Transaction,
|
|
||||||
} from 'prosemirror-state'
|
|
||||||
import { EditorView } from 'prosemirror-view'
|
|
||||||
|
|
||||||
import { CommandManager } from './CommandManager'
|
import { CommandManager } from './CommandManager'
|
||||||
import { EventEmitter } from './EventEmitter'
|
import { EventEmitter } from './EventEmitter'
|
||||||
@ -39,7 +36,6 @@ export interface HTMLElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Editor extends EventEmitter<EditorEvents> {
|
export class Editor extends EventEmitter<EditorEvents> {
|
||||||
|
|
||||||
private commandManager!: CommandManager
|
private commandManager!: CommandManager
|
||||||
|
|
||||||
public extensionManager!: ExtensionManager
|
public extensionManager!: ExtensionManager
|
||||||
@ -182,9 +178,7 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|||||||
// since plugins are applied after creating the view
|
// since plugins are applied after creating the view
|
||||||
// `editable` is always `true` for one tick.
|
// `editable` is always `true` for one tick.
|
||||||
// that’s why we also have to check for `options.editable`
|
// that’s why we also have to check for `options.editable`
|
||||||
return this.options.editable
|
return this.options.editable && this.view && this.view.editable
|
||||||
&& this.view
|
|
||||||
&& this.view.editable
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -200,7 +194,10 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|||||||
* @param plugin A ProseMirror plugin
|
* @param plugin A ProseMirror plugin
|
||||||
* @param handlePlugins Control how to merge the plugin into the existing plugins.
|
* @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)
|
const plugins = isFunction(handlePlugins)
|
||||||
? handlePlugins(plugin, [...this.state.plugins])
|
? handlePlugins(plugin, [...this.state.plugins])
|
||||||
: [...this.state.plugins, plugin]
|
: [...this.state.plugins, plugin]
|
||||||
@ -220,10 +217,8 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const name = typeof nameOrPluginKey === 'string'
|
// @ts-ignore
|
||||||
? `${nameOrPluginKey}$`
|
const name = typeof nameOrPluginKey === 'string' ? `${nameOrPluginKey}$` : nameOrPluginKey.key
|
||||||
// @ts-ignore
|
|
||||||
: nameOrPluginKey.key
|
|
||||||
|
|
||||||
const state = this.state.reconfigure({
|
const state = this.state.reconfigure({
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@ -237,9 +232,7 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|||||||
* Creates an extension manager.
|
* Creates an extension manager.
|
||||||
*/
|
*/
|
||||||
private createExtensionManager(): void {
|
private createExtensionManager(): void {
|
||||||
const coreExtensions = this.options.enableCoreExtensions
|
const coreExtensions = this.options.enableCoreExtensions ? Object.values(extensions) : []
|
||||||
? Object.values(extensions)
|
|
||||||
: []
|
|
||||||
const allExtensions = [...coreExtensions, ...this.options.extensions].filter(extension => {
|
const allExtensions = [...coreExtensions, ...this.options.extensions].filter(extension => {
|
||||||
return ['extension', 'node', 'mark'].includes(extension?.type)
|
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 name Name of the node or mark
|
||||||
* @param attributes Attributes of the node or mark
|
* @param attributes Attributes of the node or mark
|
||||||
*/
|
*/
|
||||||
public isActive(name: string, attributes?: {}): boolean;
|
public isActive(name: string, attributes?: {}): boolean
|
||||||
public isActive(attributes: {}): boolean;
|
public isActive(attributes: {}): boolean
|
||||||
public isActive(nameOrAttributes: string, attributesOrUndefined?: {}): boolean {
|
public isActive(nameOrAttributes: string, attributesOrUndefined?: {}): boolean {
|
||||||
const name = typeof nameOrAttributes === 'string'
|
const name = typeof nameOrAttributes === 'string' ? nameOrAttributes : null
|
||||||
? nameOrAttributes
|
|
||||||
: null
|
|
||||||
|
|
||||||
const attributes = typeof nameOrAttributes === 'string'
|
const attributes = typeof nameOrAttributes === 'string' ? attributesOrUndefined : nameOrAttributes
|
||||||
? attributesOrUndefined
|
|
||||||
: nameOrAttributes
|
|
||||||
|
|
||||||
return isActive(this.state, name, attributes)
|
return isActive(this.state, name, attributes)
|
||||||
}
|
}
|
||||||
@ -429,13 +418,10 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|||||||
* Get the document as text.
|
* Get the document as text.
|
||||||
*/
|
*/
|
||||||
public getText(options?: {
|
public getText(options?: {
|
||||||
blockSeparator?: string,
|
blockSeparator?: string
|
||||||
textSerializers?: Record<string, TextSerializer>,
|
textSerializers?: Record<string, TextSerializer>
|
||||||
}): string {
|
}): string {
|
||||||
const {
|
const { blockSeparator = '\n\n', textSerializers = {} } = options || {}
|
||||||
blockSeparator = '\n\n',
|
|
||||||
textSerializers = {},
|
|
||||||
} = options || {}
|
|
||||||
|
|
||||||
return getText(this.state.doc, {
|
return getText(this.state.doc, {
|
||||||
blockSeparator,
|
blockSeparator,
|
||||||
@ -459,7 +445,9 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|||||||
* @deprecated
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
public getCharacterCount(): number {
|
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
|
return this.state.doc.content.size - 2
|
||||||
}
|
}
|
||||||
@ -484,5 +472,4 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return !this.view?.docView
|
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 { ExtensionConfig } from '.'
|
||||||
import { Editor } from './Editor'
|
import { Editor } from './Editor'
|
||||||
@ -20,245 +20,265 @@ import { mergeDeep } from './utilities/mergeDeep'
|
|||||||
|
|
||||||
declare module '@tiptap/core' {
|
declare module '@tiptap/core' {
|
||||||
interface ExtensionConfig<Options = any, Storage = any> {
|
interface ExtensionConfig<Options = any, Storage = any> {
|
||||||
[key: string]: any;
|
[key: string]: any
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name
|
* Name
|
||||||
*/
|
*/
|
||||||
name: string,
|
name: string
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Priority
|
* Priority
|
||||||
*/
|
*/
|
||||||
priority?: number,
|
priority?: number
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default options
|
* Default options
|
||||||
*/
|
*/
|
||||||
defaultOptions?: Options,
|
defaultOptions?: Options
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default Options
|
* Default Options
|
||||||
*/
|
*/
|
||||||
addOptions?: (this: {
|
addOptions?: (this: {
|
||||||
name: string,
|
name: string
|
||||||
parent: Exclude<ParentConfig<ExtensionConfig<Options, Storage>>['addOptions'], undefined>,
|
parent: Exclude<ParentConfig<ExtensionConfig<Options, Storage>>['addOptions'], undefined>
|
||||||
}) => Options,
|
}) => Options
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default Storage
|
* Default Storage
|
||||||
*/
|
*/
|
||||||
addStorage?: (this: {
|
addStorage?: (this: {
|
||||||
name: string,
|
name: string
|
||||||
options: Options,
|
options: Options
|
||||||
parent: Exclude<ParentConfig<ExtensionConfig<Options, Storage>>['addStorage'], undefined>,
|
parent: Exclude<ParentConfig<ExtensionConfig<Options, Storage>>['addStorage'], undefined>
|
||||||
}) => Storage,
|
}) => Storage
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Global attributes
|
* Global attributes
|
||||||
*/
|
*/
|
||||||
addGlobalAttributes?: (this: {
|
addGlobalAttributes?: (this: {
|
||||||
name: string,
|
name: string
|
||||||
options: Options,
|
options: Options
|
||||||
storage: Storage,
|
storage: Storage
|
||||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addGlobalAttributes'],
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addGlobalAttributes']
|
||||||
}) => GlobalAttributes | {},
|
}) => GlobalAttributes | {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Raw
|
* Raw
|
||||||
*/
|
*/
|
||||||
addCommands?: (this: {
|
addCommands?: (this: {
|
||||||
name: string,
|
name: string
|
||||||
options: Options,
|
options: Options
|
||||||
storage: Storage,
|
storage: Storage
|
||||||
editor: Editor,
|
editor: Editor
|
||||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addCommands'],
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addCommands']
|
||||||
}) => Partial<RawCommands>,
|
}) => Partial<RawCommands>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keyboard shortcuts
|
* Keyboard shortcuts
|
||||||
*/
|
*/
|
||||||
addKeyboardShortcuts?: (this: {
|
addKeyboardShortcuts?: (this: {
|
||||||
name: string,
|
name: string
|
||||||
options: Options,
|
options: Options
|
||||||
storage: Storage,
|
storage: Storage
|
||||||
editor: Editor,
|
editor: Editor
|
||||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addKeyboardShortcuts'],
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addKeyboardShortcuts']
|
||||||
}) => {
|
}) => {
|
||||||
[key: string]: KeyboardShortcutCommand,
|
[key: string]: KeyboardShortcutCommand
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Input rules
|
* Input rules
|
||||||
*/
|
*/
|
||||||
addInputRules?: (this: {
|
addInputRules?: (this: {
|
||||||
name: string,
|
name: string
|
||||||
options: Options,
|
options: Options
|
||||||
storage: Storage,
|
storage: Storage
|
||||||
editor: Editor,
|
editor: Editor
|
||||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addInputRules'],
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addInputRules']
|
||||||
}) => InputRule[],
|
}) => InputRule[]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Paste rules
|
* Paste rules
|
||||||
*/
|
*/
|
||||||
addPasteRules?: (this: {
|
addPasteRules?: (this: {
|
||||||
name: string,
|
name: string
|
||||||
options: Options,
|
options: Options
|
||||||
storage: Storage,
|
storage: Storage
|
||||||
editor: Editor,
|
editor: Editor
|
||||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addPasteRules'],
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addPasteRules']
|
||||||
}) => PasteRule[],
|
}) => PasteRule[]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ProseMirror plugins
|
* ProseMirror plugins
|
||||||
*/
|
*/
|
||||||
addProseMirrorPlugins?: (this: {
|
addProseMirrorPlugins?: (this: {
|
||||||
name: string,
|
name: string
|
||||||
options: Options,
|
options: Options
|
||||||
storage: Storage,
|
storage: Storage
|
||||||
editor: Editor,
|
editor: Editor
|
||||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addProseMirrorPlugins'],
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addProseMirrorPlugins']
|
||||||
}) => Plugin[],
|
}) => Plugin[]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extensions
|
* Extensions
|
||||||
*/
|
*/
|
||||||
addExtensions?: (this: {
|
addExtensions?: (this: {
|
||||||
name: string,
|
name: string
|
||||||
options: Options,
|
options: Options
|
||||||
storage: Storage,
|
storage: Storage
|
||||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addExtensions'],
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['addExtensions']
|
||||||
}) => Extensions,
|
}) => Extensions
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extend Node Schema
|
* Extend Node Schema
|
||||||
*/
|
*/
|
||||||
extendNodeSchema?: ((
|
extendNodeSchema?:
|
||||||
this: {
|
| ((
|
||||||
name: string,
|
this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['extendNodeSchema'],
|
storage: Storage
|
||||||
},
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['extendNodeSchema']
|
||||||
extension: Node,
|
},
|
||||||
) => Record<string, any>) | null,
|
extension: Node,
|
||||||
|
) => Record<string, any>)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extend Mark Schema
|
* Extend Mark Schema
|
||||||
*/
|
*/
|
||||||
extendMarkSchema?: ((
|
extendMarkSchema?:
|
||||||
this: {
|
| ((
|
||||||
name: string,
|
this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['extendMarkSchema'],
|
storage: Storage
|
||||||
},
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['extendMarkSchema']
|
||||||
extension: Mark,
|
},
|
||||||
) => Record<string, any>) | null,
|
extension: Mark,
|
||||||
|
) => Record<string, any>)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The editor is not ready yet.
|
* The editor is not ready yet.
|
||||||
*/
|
*/
|
||||||
onBeforeCreate?: ((this: {
|
onBeforeCreate?:
|
||||||
name: string,
|
| ((this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
editor: Editor,
|
storage: Storage
|
||||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onBeforeCreate'],
|
editor: Editor
|
||||||
}) => void) | null,
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onBeforeCreate']
|
||||||
|
}) => void)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The editor is ready.
|
* The editor is ready.
|
||||||
*/
|
*/
|
||||||
onCreate?: ((this: {
|
onCreate?:
|
||||||
name: string,
|
| ((this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
editor: Editor,
|
storage: Storage
|
||||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onCreate'],
|
editor: Editor
|
||||||
}) => void) | null,
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onCreate']
|
||||||
|
}) => void)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The content has changed.
|
* The content has changed.
|
||||||
*/
|
*/
|
||||||
onUpdate?: ((this: {
|
onUpdate?:
|
||||||
name: string,
|
| ((this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
editor: Editor,
|
storage: Storage
|
||||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onUpdate'],
|
editor: Editor
|
||||||
}) => void) | null,
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onUpdate']
|
||||||
|
}) => void)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The selection has changed.
|
* The selection has changed.
|
||||||
*/
|
*/
|
||||||
onSelectionUpdate?: ((this: {
|
onSelectionUpdate?:
|
||||||
name: string,
|
| ((this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
editor: Editor,
|
storage: Storage
|
||||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onSelectionUpdate'],
|
editor: Editor
|
||||||
}) => void) | null,
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onSelectionUpdate']
|
||||||
|
}) => void)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The editor state has changed.
|
* The editor state has changed.
|
||||||
*/
|
*/
|
||||||
onTransaction?: ((
|
onTransaction?:
|
||||||
this: {
|
| ((
|
||||||
name: string,
|
this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
editor: Editor,
|
storage: Storage
|
||||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onTransaction'],
|
editor: Editor
|
||||||
},
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onTransaction']
|
||||||
props: {
|
},
|
||||||
transaction: Transaction,
|
props: {
|
||||||
},
|
transaction: Transaction
|
||||||
) => void) | null,
|
},
|
||||||
|
) => void)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The editor is focused.
|
* The editor is focused.
|
||||||
*/
|
*/
|
||||||
onFocus?: ((
|
onFocus?:
|
||||||
this: {
|
| ((
|
||||||
name: string,
|
this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
editor: Editor,
|
storage: Storage
|
||||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onFocus'],
|
editor: Editor
|
||||||
},
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onFocus']
|
||||||
props: {
|
},
|
||||||
event: FocusEvent,
|
props: {
|
||||||
},
|
event: FocusEvent
|
||||||
) => void) | null,
|
},
|
||||||
|
) => void)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The editor isn’t focused anymore.
|
* The editor isn’t focused anymore.
|
||||||
*/
|
*/
|
||||||
onBlur?: ((
|
onBlur?:
|
||||||
this: {
|
| ((
|
||||||
name: string,
|
this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
editor: Editor,
|
storage: Storage
|
||||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onBlur'],
|
editor: Editor
|
||||||
},
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onBlur']
|
||||||
props: {
|
},
|
||||||
event: FocusEvent,
|
props: {
|
||||||
},
|
event: FocusEvent
|
||||||
) => void) | null,
|
},
|
||||||
|
) => void)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The editor is destroyed.
|
* The editor is destroyed.
|
||||||
*/
|
*/
|
||||||
onDestroy?: ((this: {
|
onDestroy?:
|
||||||
name: string,
|
| ((this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
editor: Editor,
|
storage: Storage
|
||||||
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onDestroy'],
|
editor: Editor
|
||||||
}) => void) | null,
|
parent: ParentConfig<ExtensionConfig<Options, Storage>>['onDestroy']
|
||||||
|
}) => void)
|
||||||
|
| null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,30 +309,28 @@ export class Extension<Options = any, Storage = any> {
|
|||||||
this.name = this.config.name
|
this.name = this.config.name
|
||||||
|
|
||||||
if (config.defaultOptions) {
|
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
|
// TODO: remove `addOptions` fallback
|
||||||
this.options = this.config.defaultOptions
|
this.options = this.config.defaultOptions
|
||||||
|
|
||||||
if (this.config.addOptions) {
|
if (this.config.addOptions) {
|
||||||
this.options = callOrReturn(getExtensionField<AnyConfig['addOptions']>(
|
this.options = callOrReturn(
|
||||||
this,
|
getExtensionField<AnyConfig['addOptions']>(this, 'addOptions', {
|
||||||
'addOptions',
|
|
||||||
{
|
|
||||||
name: this.name,
|
name: this.name,
|
||||||
},
|
}),
|
||||||
))
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.storage = callOrReturn(getExtensionField<AnyConfig['addStorage']>(
|
this.storage = callOrReturn(
|
||||||
this,
|
getExtensionField<AnyConfig['addStorage']>(this, 'addStorage', {
|
||||||
'addStorage',
|
|
||||||
{
|
|
||||||
name: this.name,
|
name: this.name,
|
||||||
options: this.options,
|
options: this.options,
|
||||||
},
|
}),
|
||||||
)) || {}
|
) || {}
|
||||||
}
|
}
|
||||||
|
|
||||||
static create<O = any, S = any>(config: Partial<ExtensionConfig<O, S>> = {}) {
|
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.options = mergeDeep(this.options as Record<string, any>, options) as Options
|
||||||
|
|
||||||
extension.storage = callOrReturn(getExtensionField<AnyConfig['addStorage']>(
|
extension.storage = callOrReturn(
|
||||||
extension,
|
getExtensionField<AnyConfig['addStorage']>(extension, 'addStorage', {
|
||||||
'addStorage',
|
|
||||||
{
|
|
||||||
name: extension.name,
|
name: extension.name,
|
||||||
options: extension.options,
|
options: extension.options,
|
||||||
},
|
}),
|
||||||
))
|
)
|
||||||
|
|
||||||
return extension
|
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)
|
const extension = new Extension<ExtendedOptions, ExtendedStorage>(extendedConfig)
|
||||||
|
|
||||||
extension.parent = this
|
extension.parent = this
|
||||||
|
|
||||||
this.child = extension
|
this.child = extension
|
||||||
|
|
||||||
extension.name = extendedConfig.name
|
extension.name = extendedConfig.name ? extendedConfig.name : extension.parent.name
|
||||||
? extendedConfig.name
|
|
||||||
: extension.parent.name
|
|
||||||
|
|
||||||
if (extendedConfig.defaultOptions) {
|
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.options = callOrReturn(
|
||||||
extension,
|
getExtensionField<AnyConfig['addOptions']>(extension, 'addOptions', {
|
||||||
'addOptions',
|
|
||||||
{
|
|
||||||
name: extension.name,
|
name: extension.name,
|
||||||
},
|
}),
|
||||||
))
|
)
|
||||||
|
|
||||||
extension.storage = callOrReturn(getExtensionField<AnyConfig['addStorage']>(
|
extension.storage = callOrReturn(
|
||||||
extension,
|
getExtensionField<AnyConfig['addStorage']>(extension, 'addStorage', {
|
||||||
'addStorage',
|
|
||||||
{
|
|
||||||
name: extension.name,
|
name: extension.name,
|
||||||
options: extension.options,
|
options: extension.options,
|
||||||
},
|
}),
|
||||||
))
|
)
|
||||||
|
|
||||||
return extension
|
return extension
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { keymap } from 'prosemirror-keymap'
|
import { keymap } from '@tiptap/pm/keymap'
|
||||||
import { Node as ProsemirrorNode, Schema } from 'prosemirror-model'
|
import { Node as ProsemirrorNode, Schema } from '@tiptap/pm/model'
|
||||||
import { Plugin } from 'prosemirror-state'
|
import { Plugin } from '@tiptap/pm/state'
|
||||||
import { Decoration, EditorView } from 'prosemirror-view'
|
import { Decoration, EditorView } from '@tiptap/pm/view'
|
||||||
|
|
||||||
import { Mark, NodeConfig } from '.'
|
import { Mark, NodeConfig } from '.'
|
||||||
import { Editor } from './Editor'
|
import { Editor } from './Editor'
|
||||||
@ -20,7 +20,6 @@ import { callOrReturn } from './utilities/callOrReturn'
|
|||||||
import { findDuplicates } from './utilities/findDuplicates'
|
import { findDuplicates } from './utilities/findDuplicates'
|
||||||
|
|
||||||
export class ExtensionManager {
|
export class ExtensionManager {
|
||||||
|
|
||||||
editor: Editor
|
editor: Editor
|
||||||
|
|
||||||
schema: Schema
|
schema: Schema
|
||||||
@ -64,21 +63,13 @@ export class ExtensionManager {
|
|||||||
this.editor.on('beforeCreate', onBeforeCreate)
|
this.editor.on('beforeCreate', onBeforeCreate)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onCreate = getExtensionField<AnyConfig['onCreate']>(
|
const onCreate = getExtensionField<AnyConfig['onCreate']>(extension, 'onCreate', context)
|
||||||
extension,
|
|
||||||
'onCreate',
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (onCreate) {
|
if (onCreate) {
|
||||||
this.editor.on('create', onCreate)
|
this.editor.on('create', onCreate)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onUpdate = getExtensionField<AnyConfig['onUpdate']>(
|
const onUpdate = getExtensionField<AnyConfig['onUpdate']>(extension, 'onUpdate', context)
|
||||||
extension,
|
|
||||||
'onUpdate',
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (onUpdate) {
|
if (onUpdate) {
|
||||||
this.editor.on('update', onUpdate)
|
this.editor.on('update', onUpdate)
|
||||||
@ -104,31 +95,19 @@ export class ExtensionManager {
|
|||||||
this.editor.on('transaction', onTransaction)
|
this.editor.on('transaction', onTransaction)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onFocus = getExtensionField<AnyConfig['onFocus']>(
|
const onFocus = getExtensionField<AnyConfig['onFocus']>(extension, 'onFocus', context)
|
||||||
extension,
|
|
||||||
'onFocus',
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (onFocus) {
|
if (onFocus) {
|
||||||
this.editor.on('focus', onFocus)
|
this.editor.on('focus', onFocus)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onBlur = getExtensionField<AnyConfig['onBlur']>(
|
const onBlur = getExtensionField<AnyConfig['onBlur']>(extension, 'onBlur', context)
|
||||||
extension,
|
|
||||||
'onBlur',
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (onBlur) {
|
if (onBlur) {
|
||||||
this.editor.on('blur', onBlur)
|
this.editor.on('blur', onBlur)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onDestroy = getExtensionField<AnyConfig['onDestroy']>(
|
const onDestroy = getExtensionField<AnyConfig['onDestroy']>(extension, 'onDestroy', context)
|
||||||
extension,
|
|
||||||
'onDestroy',
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (onDestroy) {
|
if (onDestroy) {
|
||||||
this.editor.on('destroy', onDestroy)
|
this.editor.on('destroy', onDestroy)
|
||||||
@ -141,38 +120,41 @@ export class ExtensionManager {
|
|||||||
const duplicatedNames = findDuplicates(resolvedExtensions.map(extension => extension.name))
|
const duplicatedNames = findDuplicates(resolvedExtensions.map(extension => extension.name))
|
||||||
|
|
||||||
if (duplicatedNames.length) {
|
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
|
return resolvedExtensions
|
||||||
}
|
}
|
||||||
|
|
||||||
static flatten(extensions: Extensions): Extensions {
|
static flatten(extensions: Extensions): Extensions {
|
||||||
return extensions
|
return (
|
||||||
.map(extension => {
|
extensions
|
||||||
const context = {
|
.map(extension => {
|
||||||
name: extension.name,
|
const context = {
|
||||||
options: extension.options,
|
name: extension.name,
|
||||||
storage: extension.storage,
|
options: extension.options,
|
||||||
}
|
storage: extension.storage,
|
||||||
|
}
|
||||||
|
|
||||||
const addExtensions = getExtensionField<AnyConfig['addExtensions']>(
|
const addExtensions = getExtensionField<AnyConfig['addExtensions']>(
|
||||||
extension,
|
|
||||||
'addExtensions',
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (addExtensions) {
|
|
||||||
return [
|
|
||||||
extension,
|
extension,
|
||||||
...this.flatten(addExtensions()),
|
'addExtensions',
|
||||||
]
|
context,
|
||||||
}
|
)
|
||||||
|
|
||||||
return extension
|
if (addExtensions) {
|
||||||
})
|
return [extension, ...this.flatten(addExtensions())]
|
||||||
// `Infinity` will break TypeScript so we set a number that is probably high enough
|
}
|
||||||
.flat(10)
|
|
||||||
|
return extension
|
||||||
|
})
|
||||||
|
// `Infinity` will break TypeScript so we set a number that is probably high enough
|
||||||
|
.flat(10)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
static sort(extensions: Extensions): Extensions {
|
static sort(extensions: Extensions): Extensions {
|
||||||
@ -256,16 +238,14 @@ export class ExtensionManager {
|
|||||||
|
|
||||||
// bind exit handling
|
// bind exit handling
|
||||||
if (extension.type === 'mark' && extension.config.exitable) {
|
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) {
|
if (addKeyboardShortcuts) {
|
||||||
const bindings = Object.fromEntries(
|
const bindings = Object.fromEntries(
|
||||||
Object
|
Object.entries(addKeyboardShortcuts()).map(([shortcut, method]) => {
|
||||||
.entries(addKeyboardShortcuts())
|
return [shortcut, () => method({ editor })]
|
||||||
.map(([shortcut, method]) => {
|
}),
|
||||||
return [shortcut, () => method({ editor })]
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
defaultBindings = { ...defaultBindings, ...bindings }
|
defaultBindings = { ...defaultBindings, ...bindings }
|
||||||
@ -332,46 +312,50 @@ export class ExtensionManager {
|
|||||||
const { editor } = this
|
const { editor } = this
|
||||||
const { nodeExtensions } = splitExtensions(this.extensions)
|
const { nodeExtensions } = splitExtensions(this.extensions)
|
||||||
|
|
||||||
return Object.fromEntries(nodeExtensions
|
return Object.fromEntries(
|
||||||
.filter(extension => !!getExtensionField(extension, 'addNodeView'))
|
nodeExtensions
|
||||||
.map(extension => {
|
.filter(extension => !!getExtensionField(extension, 'addNodeView'))
|
||||||
const extensionAttributes = this.attributes.filter(attribute => attribute.type === extension.name)
|
.map(extension => {
|
||||||
const context = {
|
const extensionAttributes = this.attributes.filter(
|
||||||
name: extension.name,
|
attribute => attribute.type === extension.name,
|
||||||
options: extension.options,
|
)
|
||||||
storage: extension.storage,
|
const context = {
|
||||||
editor,
|
name: extension.name,
|
||||||
type: getNodeType(extension.name, this.schema),
|
options: extension.options,
|
||||||
}
|
storage: extension.storage,
|
||||||
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()({
|
|
||||||
editor,
|
editor,
|
||||||
node,
|
type: getNodeType(extension.name, this.schema),
|
||||||
getPos,
|
}
|
||||||
decorations,
|
const addNodeView = getExtensionField<NodeConfig['addNodeView']>(
|
||||||
HTMLAttributes,
|
|
||||||
extension,
|
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 { CommandManager } from './CommandManager'
|
||||||
import { Editor } from './Editor'
|
import { Editor } from './Editor'
|
||||||
@ -14,46 +14,47 @@ import {
|
|||||||
import { isRegExp } from './utilities/isRegExp'
|
import { isRegExp } from './utilities/isRegExp'
|
||||||
|
|
||||||
export type InputRuleMatch = {
|
export type InputRuleMatch = {
|
||||||
index: number,
|
index: number
|
||||||
text: string,
|
text: string
|
||||||
replaceWith?: string,
|
replaceWith?: string
|
||||||
match?: RegExpMatchArray,
|
match?: RegExpMatchArray
|
||||||
data?: Record<string, any>,
|
data?: Record<string, any>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type InputRuleFinder =
|
export type InputRuleFinder = RegExp | ((text: string) => InputRuleMatch | null)
|
||||||
| RegExp
|
|
||||||
| ((text: string) => InputRuleMatch | null)
|
|
||||||
|
|
||||||
export class InputRule {
|
export class InputRule {
|
||||||
find: InputRuleFinder
|
find: InputRuleFinder
|
||||||
|
|
||||||
handler: (props: {
|
handler: (props: {
|
||||||
state: EditorState,
|
state: EditorState
|
||||||
range: Range,
|
range: Range
|
||||||
match: ExtendedRegExpMatchArray,
|
match: ExtendedRegExpMatchArray
|
||||||
commands: SingleCommands,
|
commands: SingleCommands
|
||||||
chain: () => ChainedCommands,
|
chain: () => ChainedCommands
|
||||||
can: () => CanCommands,
|
can: () => CanCommands
|
||||||
}) => void | null
|
}) => void | null
|
||||||
|
|
||||||
constructor(config: {
|
constructor(config: {
|
||||||
find: InputRuleFinder,
|
find: InputRuleFinder
|
||||||
handler: (props: {
|
handler: (props: {
|
||||||
state: EditorState,
|
state: EditorState
|
||||||
range: Range,
|
range: Range
|
||||||
match: ExtendedRegExpMatchArray,
|
match: ExtendedRegExpMatchArray
|
||||||
commands: SingleCommands,
|
commands: SingleCommands
|
||||||
chain: () => ChainedCommands,
|
chain: () => ChainedCommands
|
||||||
can: () => CanCommands,
|
can: () => CanCommands
|
||||||
}) => void | null,
|
}) => void | null
|
||||||
}) {
|
}) {
|
||||||
this.find = config.find
|
this.find = config.find
|
||||||
this.handler = config.handler
|
this.handler = config.handler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const inputRuleMatcherHandler = (text: string, find: InputRuleFinder): ExtendedRegExpMatchArray | null => {
|
const inputRuleMatcherHandler = (
|
||||||
|
text: string,
|
||||||
|
find: InputRuleFinder,
|
||||||
|
): ExtendedRegExpMatchArray | null => {
|
||||||
if (isRegExp(find)) {
|
if (isRegExp(find)) {
|
||||||
return find.exec(text)
|
return find.exec(text)
|
||||||
}
|
}
|
||||||
@ -72,7 +73,9 @@ const inputRuleMatcherHandler = (text: string, find: InputRuleFinder): ExtendedR
|
|||||||
|
|
||||||
if (inputRuleMatch.replaceWith) {
|
if (inputRuleMatch.replaceWith) {
|
||||||
if (!inputRuleMatch.text.includes(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)
|
result.push(inputRuleMatch.replaceWith)
|
||||||
@ -82,20 +85,15 @@ const inputRuleMatcherHandler = (text: string, find: InputRuleFinder): ExtendedR
|
|||||||
}
|
}
|
||||||
|
|
||||||
function run(config: {
|
function run(config: {
|
||||||
editor: Editor,
|
editor: Editor
|
||||||
from: number,
|
from: number
|
||||||
to: number,
|
to: number
|
||||||
text: string,
|
text: string
|
||||||
rules: InputRule[],
|
rules: InputRule[]
|
||||||
plugin: Plugin,
|
plugin: Plugin
|
||||||
}): boolean {
|
}): boolean {
|
||||||
const {
|
const {
|
||||||
editor,
|
editor, from, to, text, rules, plugin,
|
||||||
from,
|
|
||||||
to,
|
|
||||||
text,
|
|
||||||
rules,
|
|
||||||
plugin,
|
|
||||||
} = config
|
} = config
|
||||||
const { view } = editor
|
const { view } = editor
|
||||||
|
|
||||||
@ -179,7 +177,7 @@ function run(config: {
|
|||||||
* input that matches any of the given rules to trigger the rule’s
|
* input that matches any of the given rules to trigger the rule’s
|
||||||
* action.
|
* action.
|
||||||
*/
|
*/
|
||||||
export function inputRulesPlugin(props: { editor: Editor, rules: InputRule[] }): Plugin {
|
export function inputRulesPlugin(props: { editor: Editor; rules: InputRule[] }): Plugin {
|
||||||
const { editor, rules } = props
|
const { editor, rules } = props
|
||||||
const plugin = new Plugin({
|
const plugin = new Plugin({
|
||||||
state: {
|
state: {
|
||||||
@ -193,9 +191,7 @@ export function inputRulesPlugin(props: { editor: Editor, rules: InputRule[] }):
|
|||||||
return stored
|
return stored
|
||||||
}
|
}
|
||||||
|
|
||||||
return tr.selectionSet || tr.docChanged
|
return tr.selectionSet || tr.docChanged ? null : prev
|
||||||
? null
|
|
||||||
: prev
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
DOMOutputSpec,
|
DOMOutputSpec, Mark as ProseMirrorMark, MarkSpec, MarkType,
|
||||||
Mark as ProseMirrorMark,
|
} from '@tiptap/pm/model'
|
||||||
MarkSpec,
|
import { Plugin, Transaction } from '@tiptap/pm/state'
|
||||||
MarkType,
|
|
||||||
} from 'prosemirror-model'
|
|
||||||
import { Plugin, Transaction } from 'prosemirror-state'
|
|
||||||
|
|
||||||
import { MarkConfig } from '.'
|
import { MarkConfig } from '.'
|
||||||
import { Editor } from './Editor'
|
import { Editor } from './Editor'
|
||||||
@ -26,358 +23,386 @@ import { mergeDeep } from './utilities/mergeDeep'
|
|||||||
|
|
||||||
declare module '@tiptap/core' {
|
declare module '@tiptap/core' {
|
||||||
export interface MarkConfig<Options = any, Storage = any> {
|
export interface MarkConfig<Options = any, Storage = any> {
|
||||||
[key: string]: any;
|
[key: string]: any
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name
|
* Name
|
||||||
*/
|
*/
|
||||||
name: string,
|
name: string
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Priority
|
* Priority
|
||||||
*/
|
*/
|
||||||
priority?: number,
|
priority?: number
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default options
|
* Default options
|
||||||
*/
|
*/
|
||||||
defaultOptions?: Options,
|
defaultOptions?: Options
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default Options
|
* Default Options
|
||||||
*/
|
*/
|
||||||
addOptions?: (this: {
|
addOptions?: (this: {
|
||||||
name: string,
|
name: string
|
||||||
parent: Exclude<ParentConfig<MarkConfig<Options, Storage>>['addOptions'], undefined>,
|
parent: Exclude<ParentConfig<MarkConfig<Options, Storage>>['addOptions'], undefined>
|
||||||
}) => Options,
|
}) => Options
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default Storage
|
* Default Storage
|
||||||
*/
|
*/
|
||||||
addStorage?: (this: {
|
addStorage?: (this: {
|
||||||
name: string,
|
name: string
|
||||||
options: Options,
|
options: Options
|
||||||
parent: Exclude<ParentConfig<MarkConfig<Options, Storage>>['addStorage'], undefined>,
|
parent: Exclude<ParentConfig<MarkConfig<Options, Storage>>['addStorage'], undefined>
|
||||||
}) => Storage,
|
}) => Storage
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Global attributes
|
* Global attributes
|
||||||
*/
|
*/
|
||||||
addGlobalAttributes?: (this: {
|
addGlobalAttributes?: (this: {
|
||||||
name: string,
|
name: string
|
||||||
options: Options,
|
options: Options
|
||||||
storage: Storage,
|
storage: Storage
|
||||||
parent: ParentConfig<MarkConfig<Options, Storage>>['addGlobalAttributes'],
|
parent: ParentConfig<MarkConfig<Options, Storage>>['addGlobalAttributes']
|
||||||
}) => GlobalAttributes | {},
|
}) => GlobalAttributes | {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Raw
|
* Raw
|
||||||
*/
|
*/
|
||||||
addCommands?: (this: {
|
addCommands?: (this: {
|
||||||
name: string,
|
name: string
|
||||||
options: Options,
|
options: Options
|
||||||
storage: Storage,
|
storage: Storage
|
||||||
editor: Editor,
|
editor: Editor
|
||||||
type: MarkType,
|
type: MarkType
|
||||||
parent: ParentConfig<MarkConfig<Options, Storage>>['addCommands'],
|
parent: ParentConfig<MarkConfig<Options, Storage>>['addCommands']
|
||||||
}) => Partial<RawCommands>,
|
}) => Partial<RawCommands>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keyboard shortcuts
|
* Keyboard shortcuts
|
||||||
*/
|
*/
|
||||||
addKeyboardShortcuts?: (this: {
|
addKeyboardShortcuts?: (this: {
|
||||||
name: string,
|
name: string
|
||||||
options: Options,
|
options: Options
|
||||||
storage: Storage,
|
storage: Storage
|
||||||
editor: Editor,
|
editor: Editor
|
||||||
type: MarkType,
|
type: MarkType
|
||||||
parent: ParentConfig<MarkConfig<Options, Storage>>['addKeyboardShortcuts'],
|
parent: ParentConfig<MarkConfig<Options, Storage>>['addKeyboardShortcuts']
|
||||||
}) => {
|
}) => {
|
||||||
[key: string]: KeyboardShortcutCommand,
|
[key: string]: KeyboardShortcutCommand
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Input rules
|
* Input rules
|
||||||
*/
|
*/
|
||||||
addInputRules?: (this: {
|
addInputRules?: (this: {
|
||||||
name: string,
|
name: string
|
||||||
options: Options,
|
options: Options
|
||||||
storage: Storage,
|
storage: Storage
|
||||||
editor: Editor,
|
editor: Editor
|
||||||
type: MarkType,
|
type: MarkType
|
||||||
parent: ParentConfig<MarkConfig<Options, Storage>>['addInputRules'],
|
parent: ParentConfig<MarkConfig<Options, Storage>>['addInputRules']
|
||||||
}) => InputRule[],
|
}) => InputRule[]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Paste rules
|
* Paste rules
|
||||||
*/
|
*/
|
||||||
addPasteRules?: (this: {
|
addPasteRules?: (this: {
|
||||||
name: string,
|
name: string
|
||||||
options: Options,
|
options: Options
|
||||||
storage: Storage,
|
storage: Storage
|
||||||
editor: Editor,
|
editor: Editor
|
||||||
type: MarkType,
|
type: MarkType
|
||||||
parent: ParentConfig<MarkConfig<Options, Storage>>['addPasteRules'],
|
parent: ParentConfig<MarkConfig<Options, Storage>>['addPasteRules']
|
||||||
}) => PasteRule[],
|
}) => PasteRule[]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ProseMirror plugins
|
* ProseMirror plugins
|
||||||
*/
|
*/
|
||||||
addProseMirrorPlugins?: (this: {
|
addProseMirrorPlugins?: (this: {
|
||||||
name: string,
|
name: string
|
||||||
options: Options,
|
options: Options
|
||||||
storage: Storage,
|
storage: Storage
|
||||||
editor: Editor,
|
editor: Editor
|
||||||
type: MarkType,
|
type: MarkType
|
||||||
parent: ParentConfig<MarkConfig<Options, Storage>>['addProseMirrorPlugins'],
|
parent: ParentConfig<MarkConfig<Options, Storage>>['addProseMirrorPlugins']
|
||||||
}) => Plugin[],
|
}) => Plugin[]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extensions
|
* Extensions
|
||||||
*/
|
*/
|
||||||
addExtensions?: (this: {
|
addExtensions?: (this: {
|
||||||
name: string,
|
name: string
|
||||||
options: Options,
|
options: Options
|
||||||
storage: Storage,
|
storage: Storage
|
||||||
parent: ParentConfig<MarkConfig<Options, Storage>>['addExtensions'],
|
parent: ParentConfig<MarkConfig<Options, Storage>>['addExtensions']
|
||||||
}) => Extensions,
|
}) => Extensions
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extend Node Schema
|
* Extend Node Schema
|
||||||
*/
|
*/
|
||||||
extendNodeSchema?: ((
|
extendNodeSchema?:
|
||||||
this: {
|
| ((
|
||||||
name: string,
|
this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
parent: ParentConfig<MarkConfig<Options, Storage>>['extendNodeSchema'],
|
storage: Storage
|
||||||
},
|
parent: ParentConfig<MarkConfig<Options, Storage>>['extendNodeSchema']
|
||||||
extension: Node,
|
},
|
||||||
) => Record<string, any>) | null,
|
extension: Node,
|
||||||
|
) => Record<string, any>)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extend Mark Schema
|
* Extend Mark Schema
|
||||||
*/
|
*/
|
||||||
extendMarkSchema?: ((
|
extendMarkSchema?:
|
||||||
this: {
|
| ((
|
||||||
name: string,
|
this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
parent: ParentConfig<MarkConfig<Options, Storage>>['extendMarkSchema'],
|
storage: Storage
|
||||||
},
|
parent: ParentConfig<MarkConfig<Options, Storage>>['extendMarkSchema']
|
||||||
extension: Mark,
|
},
|
||||||
) => Record<string, any>) | null,
|
extension: Mark,
|
||||||
|
) => Record<string, any>)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The editor is not ready yet.
|
* The editor is not ready yet.
|
||||||
*/
|
*/
|
||||||
onBeforeCreate?: ((this: {
|
onBeforeCreate?:
|
||||||
name: string,
|
| ((this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
editor: Editor,
|
storage: Storage
|
||||||
type: MarkType,
|
editor: Editor
|
||||||
parent: ParentConfig<MarkConfig<Options, Storage>>['onBeforeCreate'],
|
type: MarkType
|
||||||
}) => void) | null,
|
parent: ParentConfig<MarkConfig<Options, Storage>>['onBeforeCreate']
|
||||||
|
}) => void)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The editor is ready.
|
* The editor is ready.
|
||||||
*/
|
*/
|
||||||
onCreate?: ((this: {
|
onCreate?:
|
||||||
name: string,
|
| ((this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
editor: Editor,
|
storage: Storage
|
||||||
type: MarkType,
|
editor: Editor
|
||||||
parent: ParentConfig<MarkConfig<Options, Storage>>['onCreate'],
|
type: MarkType
|
||||||
}) => void) | null,
|
parent: ParentConfig<MarkConfig<Options, Storage>>['onCreate']
|
||||||
|
}) => void)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The content has changed.
|
* The content has changed.
|
||||||
*/
|
*/
|
||||||
onUpdate?: ((this: {
|
onUpdate?:
|
||||||
name: string,
|
| ((this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
editor: Editor,
|
storage: Storage
|
||||||
type: MarkType,
|
editor: Editor
|
||||||
parent: ParentConfig<MarkConfig<Options, Storage>>['onUpdate'],
|
type: MarkType
|
||||||
}) => void) | null,
|
parent: ParentConfig<MarkConfig<Options, Storage>>['onUpdate']
|
||||||
|
}) => void)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The selection has changed.
|
* The selection has changed.
|
||||||
*/
|
*/
|
||||||
onSelectionUpdate?: ((this: {
|
onSelectionUpdate?:
|
||||||
name: string,
|
| ((this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
editor: Editor,
|
storage: Storage
|
||||||
type: MarkType,
|
editor: Editor
|
||||||
parent: ParentConfig<MarkConfig<Options, Storage>>['onSelectionUpdate'],
|
type: MarkType
|
||||||
}) => void) | null,
|
parent: ParentConfig<MarkConfig<Options, Storage>>['onSelectionUpdate']
|
||||||
|
}) => void)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The editor state has changed.
|
* The editor state has changed.
|
||||||
*/
|
*/
|
||||||
onTransaction?: ((
|
onTransaction?:
|
||||||
this: {
|
| ((
|
||||||
name: string,
|
this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
editor: Editor,
|
storage: Storage
|
||||||
type: MarkType,
|
editor: Editor
|
||||||
parent: ParentConfig<MarkConfig<Options, Storage>>['onTransaction'],
|
type: MarkType
|
||||||
},
|
parent: ParentConfig<MarkConfig<Options, Storage>>['onTransaction']
|
||||||
props: {
|
},
|
||||||
transaction: Transaction,
|
props: {
|
||||||
},
|
transaction: Transaction
|
||||||
) => void) | null,
|
},
|
||||||
|
) => void)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The editor is focused.
|
* The editor is focused.
|
||||||
*/
|
*/
|
||||||
onFocus?: ((
|
onFocus?:
|
||||||
this: {
|
| ((
|
||||||
name: string,
|
this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
editor: Editor,
|
storage: Storage
|
||||||
type: MarkType,
|
editor: Editor
|
||||||
parent: ParentConfig<MarkConfig<Options, Storage>>['onFocus'],
|
type: MarkType
|
||||||
},
|
parent: ParentConfig<MarkConfig<Options, Storage>>['onFocus']
|
||||||
props: {
|
},
|
||||||
event: FocusEvent,
|
props: {
|
||||||
},
|
event: FocusEvent
|
||||||
) => void) | null,
|
},
|
||||||
|
) => void)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The editor isn’t focused anymore.
|
* The editor isn’t focused anymore.
|
||||||
*/
|
*/
|
||||||
onBlur?: ((
|
onBlur?:
|
||||||
this: {
|
| ((
|
||||||
name: string,
|
this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
editor: Editor,
|
storage: Storage
|
||||||
type: MarkType,
|
editor: Editor
|
||||||
parent: ParentConfig<MarkConfig<Options, Storage>>['onBlur'],
|
type: MarkType
|
||||||
},
|
parent: ParentConfig<MarkConfig<Options, Storage>>['onBlur']
|
||||||
props: {
|
},
|
||||||
event: FocusEvent,
|
props: {
|
||||||
},
|
event: FocusEvent
|
||||||
) => void) | null,
|
},
|
||||||
|
) => void)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The editor is destroyed.
|
* The editor is destroyed.
|
||||||
*/
|
*/
|
||||||
onDestroy?: ((this: {
|
onDestroy?:
|
||||||
name: string,
|
| ((this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
editor: Editor,
|
storage: Storage
|
||||||
type: MarkType,
|
editor: Editor
|
||||||
parent: ParentConfig<MarkConfig<Options, Storage>>['onDestroy'],
|
type: MarkType
|
||||||
}) => void) | null,
|
parent: ParentConfig<MarkConfig<Options, Storage>>['onDestroy']
|
||||||
|
}) => void)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keep mark after split node
|
* Keep mark after split node
|
||||||
*/
|
*/
|
||||||
keepOnSplit?: boolean | (() => boolean),
|
keepOnSplit?: boolean | (() => boolean)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inclusive
|
* Inclusive
|
||||||
*/
|
*/
|
||||||
inclusive?: MarkSpec['inclusive'] | ((this: {
|
inclusive?:
|
||||||
name: string,
|
| MarkSpec['inclusive']
|
||||||
options: Options,
|
| ((this: {
|
||||||
storage: Storage,
|
name: string
|
||||||
parent: ParentConfig<MarkConfig<Options, Storage>>['inclusive'],
|
options: Options
|
||||||
}) => MarkSpec['inclusive']),
|
storage: Storage
|
||||||
|
parent: ParentConfig<MarkConfig<Options, Storage>>['inclusive']
|
||||||
|
}) => MarkSpec['inclusive'])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Excludes
|
* Excludes
|
||||||
*/
|
*/
|
||||||
excludes?: MarkSpec['excludes'] | ((this: {
|
excludes?:
|
||||||
name: string,
|
| MarkSpec['excludes']
|
||||||
options: Options,
|
| ((this: {
|
||||||
storage: Storage,
|
name: string
|
||||||
parent: ParentConfig<MarkConfig<Options, Storage>>['excludes'],
|
options: Options
|
||||||
}) => MarkSpec['excludes']),
|
storage: Storage
|
||||||
|
parent: ParentConfig<MarkConfig<Options, Storage>>['excludes']
|
||||||
|
}) => MarkSpec['excludes'])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks this Mark as exitable
|
* Marks this Mark as exitable
|
||||||
*/
|
*/
|
||||||
exitable?: boolean | (() => boolean),
|
exitable?: boolean | (() => boolean)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Group
|
* Group
|
||||||
*/
|
*/
|
||||||
group?: MarkSpec['group'] | ((this: {
|
group?:
|
||||||
name: string,
|
| MarkSpec['group']
|
||||||
options: Options,
|
| ((this: {
|
||||||
storage: Storage,
|
name: string
|
||||||
parent: ParentConfig<MarkConfig<Options, Storage>>['group'],
|
options: Options
|
||||||
}) => MarkSpec['group']),
|
storage: Storage
|
||||||
|
parent: ParentConfig<MarkConfig<Options, Storage>>['group']
|
||||||
|
}) => MarkSpec['group'])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spanning
|
* Spanning
|
||||||
*/
|
*/
|
||||||
spanning?: MarkSpec['spanning'] | ((this: {
|
spanning?:
|
||||||
name: string,
|
| MarkSpec['spanning']
|
||||||
options: Options,
|
| ((this: {
|
||||||
storage: Storage,
|
name: string
|
||||||
parent: ParentConfig<MarkConfig<Options, Storage>>['spanning'],
|
options: Options
|
||||||
}) => MarkSpec['spanning']),
|
storage: Storage
|
||||||
|
parent: ParentConfig<MarkConfig<Options, Storage>>['spanning']
|
||||||
|
}) => MarkSpec['spanning'])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Code
|
* Code
|
||||||
*/
|
*/
|
||||||
code?: boolean | ((this: {
|
code?:
|
||||||
name: string,
|
| boolean
|
||||||
options: Options,
|
| ((this: {
|
||||||
storage: Storage,
|
name: string
|
||||||
parent: ParentConfig<MarkConfig<Options, Storage>>['code'],
|
options: Options
|
||||||
}) => boolean),
|
storage: Storage
|
||||||
|
parent: ParentConfig<MarkConfig<Options, Storage>>['code']
|
||||||
|
}) => boolean)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse HTML
|
* Parse HTML
|
||||||
*/
|
*/
|
||||||
parseHTML?: (
|
parseHTML?: (this: {
|
||||||
this: {
|
name: string
|
||||||
name: string,
|
options: Options
|
||||||
options: Options,
|
storage: Storage
|
||||||
storage: Storage,
|
parent: ParentConfig<MarkConfig<Options, Storage>>['parseHTML']
|
||||||
parent: ParentConfig<MarkConfig<Options, Storage>>['parseHTML'],
|
}) => MarkSpec['parseDOM']
|
||||||
},
|
|
||||||
) => MarkSpec['parseDOM'],
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render HTML
|
* Render HTML
|
||||||
*/
|
*/
|
||||||
renderHTML?: ((
|
renderHTML?:
|
||||||
this: {
|
| ((
|
||||||
name: string,
|
this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
parent: ParentConfig<MarkConfig<Options, Storage>>['renderHTML'],
|
storage: Storage
|
||||||
},
|
parent: ParentConfig<MarkConfig<Options, Storage>>['renderHTML']
|
||||||
props: {
|
},
|
||||||
mark: ProseMirrorMark,
|
props: {
|
||||||
HTMLAttributes: Record<string, any>,
|
mark: ProseMirrorMark
|
||||||
},
|
HTMLAttributes: Record<string, any>
|
||||||
) => DOMOutputSpec) | null,
|
},
|
||||||
|
) => DOMOutputSpec)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attributes
|
* Attributes
|
||||||
*/
|
*/
|
||||||
addAttributes?: (
|
addAttributes?: (this: {
|
||||||
this: {
|
name: string
|
||||||
name: string,
|
options: Options
|
||||||
options: Options,
|
storage: Storage
|
||||||
storage: Storage,
|
parent: ParentConfig<MarkConfig<Options, Storage>>['addAttributes']
|
||||||
parent: ParentConfig<MarkConfig<Options, Storage>>['addAttributes'],
|
}) => Attributes | {}
|
||||||
},
|
|
||||||
) => Attributes | {},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,30 +433,28 @@ export class Mark<Options = any, Storage = any> {
|
|||||||
this.name = this.config.name
|
this.name = this.config.name
|
||||||
|
|
||||||
if (config.defaultOptions) {
|
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
|
// TODO: remove `addOptions` fallback
|
||||||
this.options = this.config.defaultOptions
|
this.options = this.config.defaultOptions
|
||||||
|
|
||||||
if (this.config.addOptions) {
|
if (this.config.addOptions) {
|
||||||
this.options = callOrReturn(getExtensionField<AnyConfig['addOptions']>(
|
this.options = callOrReturn(
|
||||||
this,
|
getExtensionField<AnyConfig['addOptions']>(this, 'addOptions', {
|
||||||
'addOptions',
|
|
||||||
{
|
|
||||||
name: this.name,
|
name: this.name,
|
||||||
},
|
}),
|
||||||
))
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.storage = callOrReturn(getExtensionField<AnyConfig['addStorage']>(
|
this.storage = callOrReturn(
|
||||||
this,
|
getExtensionField<AnyConfig['addStorage']>(this, 'addStorage', {
|
||||||
'addStorage',
|
|
||||||
{
|
|
||||||
name: this.name,
|
name: this.name,
|
||||||
options: this.options,
|
options: this.options,
|
||||||
},
|
}),
|
||||||
)) || {}
|
) || {}
|
||||||
}
|
}
|
||||||
|
|
||||||
static create<O = any, S = any>(config: Partial<MarkConfig<O, S>> = {}) {
|
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.options = mergeDeep(this.options as Record<string, any>, options) as Options
|
||||||
|
|
||||||
extension.storage = callOrReturn(getExtensionField<AnyConfig['addStorage']>(
|
extension.storage = callOrReturn(
|
||||||
extension,
|
getExtensionField<AnyConfig['addStorage']>(extension, 'addStorage', {
|
||||||
'addStorage',
|
|
||||||
{
|
|
||||||
name: extension.name,
|
name: extension.name,
|
||||||
options: extension.options,
|
options: extension.options,
|
||||||
},
|
}),
|
||||||
))
|
)
|
||||||
|
|
||||||
return extension
|
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)
|
const extension = new Mark<ExtendedOptions, ExtendedStorage>(extendedConfig)
|
||||||
|
|
||||||
extension.parent = this
|
extension.parent = this
|
||||||
|
|
||||||
this.child = extension
|
this.child = extension
|
||||||
|
|
||||||
extension.name = extendedConfig.name
|
extension.name = extendedConfig.name ? extendedConfig.name : extension.parent.name
|
||||||
? extendedConfig.name
|
|
||||||
: extension.parent.name
|
|
||||||
|
|
||||||
if (extendedConfig.defaultOptions) {
|
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.options = callOrReturn(
|
||||||
extension,
|
getExtensionField<AnyConfig['addOptions']>(extension, 'addOptions', {
|
||||||
'addOptions',
|
|
||||||
{
|
|
||||||
name: extension.name,
|
name: extension.name,
|
||||||
},
|
}),
|
||||||
))
|
)
|
||||||
|
|
||||||
extension.storage = callOrReturn(getExtensionField<AnyConfig['addStorage']>(
|
extension.storage = callOrReturn(
|
||||||
extension,
|
getExtensionField<AnyConfig['addStorage']>(extension, 'addStorage', {
|
||||||
'addStorage',
|
|
||||||
{
|
|
||||||
name: extension.name,
|
name: extension.name,
|
||||||
options: extension.options,
|
options: extension.options,
|
||||||
},
|
}),
|
||||||
))
|
)
|
||||||
|
|
||||||
return extension
|
return extension
|
||||||
}
|
}
|
||||||
|
|
||||||
static handleExit({
|
static handleExit({ editor, mark }: { editor: Editor; mark: Mark }) {
|
||||||
editor,
|
|
||||||
mark,
|
|
||||||
}: {
|
|
||||||
editor: Editor
|
|
||||||
mark: Mark
|
|
||||||
}) {
|
|
||||||
const { tr } = editor.state
|
const { tr } = editor.state
|
||||||
const currentPos = editor.state.selection.$from
|
const currentPos = editor.state.selection.$from
|
||||||
const isAtEnd = currentPos.pos === currentPos.end()
|
const isAtEnd = currentPos.pos === currentPos.end()
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
DOMOutputSpec,
|
DOMOutputSpec, Node as ProseMirrorNode, NodeSpec, NodeType,
|
||||||
Node as ProseMirrorNode,
|
} from '@tiptap/pm/model'
|
||||||
NodeSpec,
|
import { Plugin, Transaction } from '@tiptap/pm/state'
|
||||||
NodeType,
|
|
||||||
} from 'prosemirror-model'
|
|
||||||
import { Plugin, Transaction } from 'prosemirror-state'
|
|
||||||
|
|
||||||
import { NodeConfig } from '.'
|
import { NodeConfig } from '.'
|
||||||
import { Editor } from './Editor'
|
import { Editor } from './Editor'
|
||||||
@ -26,443 +23,487 @@ import { mergeDeep } from './utilities/mergeDeep'
|
|||||||
|
|
||||||
declare module '@tiptap/core' {
|
declare module '@tiptap/core' {
|
||||||
interface NodeConfig<Options = any, Storage = any> {
|
interface NodeConfig<Options = any, Storage = any> {
|
||||||
[key: string]: any;
|
[key: string]: any
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name
|
* Name
|
||||||
*/
|
*/
|
||||||
name: string,
|
name: string
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Priority
|
* Priority
|
||||||
*/
|
*/
|
||||||
priority?: number,
|
priority?: number
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default options
|
* Default options
|
||||||
*/
|
*/
|
||||||
defaultOptions?: Options,
|
defaultOptions?: Options
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default Options
|
* Default Options
|
||||||
*/
|
*/
|
||||||
addOptions?: (this: {
|
addOptions?: (this: {
|
||||||
name: string,
|
name: string
|
||||||
parent: Exclude<ParentConfig<NodeConfig<Options, Storage>>['addOptions'], undefined>,
|
parent: Exclude<ParentConfig<NodeConfig<Options, Storage>>['addOptions'], undefined>
|
||||||
}) => Options,
|
}) => Options
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default Storage
|
* Default Storage
|
||||||
*/
|
*/
|
||||||
addStorage?: (this: {
|
addStorage?: (this: {
|
||||||
name: string,
|
name: string
|
||||||
options: Options,
|
options: Options
|
||||||
parent: Exclude<ParentConfig<NodeConfig<Options, Storage>>['addStorage'], undefined>,
|
parent: Exclude<ParentConfig<NodeConfig<Options, Storage>>['addStorage'], undefined>
|
||||||
}) => Storage,
|
}) => Storage
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Global attributes
|
* Global attributes
|
||||||
*/
|
*/
|
||||||
addGlobalAttributes?: (this: {
|
addGlobalAttributes?: (this: {
|
||||||
name: string,
|
name: string
|
||||||
options: Options,
|
options: Options
|
||||||
storage: Storage,
|
storage: Storage
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['addGlobalAttributes'],
|
parent: ParentConfig<NodeConfig<Options, Storage>>['addGlobalAttributes']
|
||||||
}) => GlobalAttributes | {},
|
}) => GlobalAttributes | {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Raw
|
* Raw
|
||||||
*/
|
*/
|
||||||
addCommands?: (this: {
|
addCommands?: (this: {
|
||||||
name: string,
|
name: string
|
||||||
options: Options,
|
options: Options
|
||||||
storage: Storage,
|
storage: Storage
|
||||||
editor: Editor,
|
editor: Editor
|
||||||
type: NodeType,
|
type: NodeType
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['addCommands'],
|
parent: ParentConfig<NodeConfig<Options, Storage>>['addCommands']
|
||||||
}) => Partial<RawCommands>,
|
}) => Partial<RawCommands>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keyboard shortcuts
|
* Keyboard shortcuts
|
||||||
*/
|
*/
|
||||||
addKeyboardShortcuts?: (this: {
|
addKeyboardShortcuts?: (this: {
|
||||||
name: string,
|
name: string
|
||||||
options: Options,
|
options: Options
|
||||||
storage: Storage,
|
storage: Storage
|
||||||
editor: Editor,
|
editor: Editor
|
||||||
type: NodeType,
|
type: NodeType
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['addKeyboardShortcuts'],
|
parent: ParentConfig<NodeConfig<Options, Storage>>['addKeyboardShortcuts']
|
||||||
}) => {
|
}) => {
|
||||||
[key: string]: KeyboardShortcutCommand,
|
[key: string]: KeyboardShortcutCommand
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Input rules
|
* Input rules
|
||||||
*/
|
*/
|
||||||
addInputRules?: (this: {
|
addInputRules?: (this: {
|
||||||
name: string,
|
name: string
|
||||||
options: Options,
|
options: Options
|
||||||
storage: Storage,
|
storage: Storage
|
||||||
editor: Editor,
|
editor: Editor
|
||||||
type: NodeType,
|
type: NodeType
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['addInputRules'],
|
parent: ParentConfig<NodeConfig<Options, Storage>>['addInputRules']
|
||||||
}) => InputRule[],
|
}) => InputRule[]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Paste rules
|
* Paste rules
|
||||||
*/
|
*/
|
||||||
addPasteRules?: (this: {
|
addPasteRules?: (this: {
|
||||||
name: string,
|
name: string
|
||||||
options: Options,
|
options: Options
|
||||||
storage: Storage,
|
storage: Storage
|
||||||
editor: Editor,
|
editor: Editor
|
||||||
type: NodeType,
|
type: NodeType
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['addPasteRules'],
|
parent: ParentConfig<NodeConfig<Options, Storage>>['addPasteRules']
|
||||||
}) => PasteRule[],
|
}) => PasteRule[]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ProseMirror plugins
|
* ProseMirror plugins
|
||||||
*/
|
*/
|
||||||
addProseMirrorPlugins?: (this: {
|
addProseMirrorPlugins?: (this: {
|
||||||
name: string,
|
name: string
|
||||||
options: Options,
|
options: Options
|
||||||
storage: Storage,
|
storage: Storage
|
||||||
editor: Editor,
|
editor: Editor
|
||||||
type: NodeType,
|
type: NodeType
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['addProseMirrorPlugins'],
|
parent: ParentConfig<NodeConfig<Options, Storage>>['addProseMirrorPlugins']
|
||||||
}) => Plugin[],
|
}) => Plugin[]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extensions
|
* Extensions
|
||||||
*/
|
*/
|
||||||
addExtensions?: (this: {
|
addExtensions?: (this: {
|
||||||
name: string,
|
name: string
|
||||||
options: Options,
|
options: Options
|
||||||
storage: Storage,
|
storage: Storage
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['addExtensions'],
|
parent: ParentConfig<NodeConfig<Options, Storage>>['addExtensions']
|
||||||
}) => Extensions,
|
}) => Extensions
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extend Node Schema
|
* Extend Node Schema
|
||||||
*/
|
*/
|
||||||
extendNodeSchema?: ((
|
extendNodeSchema?:
|
||||||
this: {
|
| ((
|
||||||
name: string,
|
this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['extendNodeSchema'],
|
storage: Storage
|
||||||
},
|
parent: ParentConfig<NodeConfig<Options, Storage>>['extendNodeSchema']
|
||||||
extension: Node,
|
},
|
||||||
) => Record<string, any>) | null,
|
extension: Node,
|
||||||
|
) => Record<string, any>)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extend Mark Schema
|
* Extend Mark Schema
|
||||||
*/
|
*/
|
||||||
extendMarkSchema?: ((
|
extendMarkSchema?:
|
||||||
this: {
|
| ((
|
||||||
name: string,
|
this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['extendMarkSchema'],
|
storage: Storage
|
||||||
},
|
parent: ParentConfig<NodeConfig<Options, Storage>>['extendMarkSchema']
|
||||||
extension: Node,
|
},
|
||||||
) => Record<string, any>) | null,
|
extension: Node,
|
||||||
|
) => Record<string, any>)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The editor is not ready yet.
|
* The editor is not ready yet.
|
||||||
*/
|
*/
|
||||||
onBeforeCreate?: ((this: {
|
onBeforeCreate?:
|
||||||
name: string,
|
| ((this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
editor: Editor,
|
storage: Storage
|
||||||
type: NodeType,
|
editor: Editor
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['onBeforeCreate'],
|
type: NodeType
|
||||||
}) => void) | null,
|
parent: ParentConfig<NodeConfig<Options, Storage>>['onBeforeCreate']
|
||||||
|
}) => void)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The editor is ready.
|
* The editor is ready.
|
||||||
*/
|
*/
|
||||||
onCreate?: ((this: {
|
onCreate?:
|
||||||
name: string,
|
| ((this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
editor: Editor,
|
storage: Storage
|
||||||
type: NodeType,
|
editor: Editor
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['onCreate'],
|
type: NodeType
|
||||||
}) => void) | null,
|
parent: ParentConfig<NodeConfig<Options, Storage>>['onCreate']
|
||||||
|
}) => void)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The content has changed.
|
* The content has changed.
|
||||||
*/
|
*/
|
||||||
onUpdate?: ((this: {
|
onUpdate?:
|
||||||
name: string,
|
| ((this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
editor: Editor,
|
storage: Storage
|
||||||
type: NodeType,
|
editor: Editor
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['onUpdate'],
|
type: NodeType
|
||||||
}) => void) | null,
|
parent: ParentConfig<NodeConfig<Options, Storage>>['onUpdate']
|
||||||
|
}) => void)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The selection has changed.
|
* The selection has changed.
|
||||||
*/
|
*/
|
||||||
onSelectionUpdate?: ((this: {
|
onSelectionUpdate?:
|
||||||
name: string,
|
| ((this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
editor: Editor,
|
storage: Storage
|
||||||
type: NodeType,
|
editor: Editor
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['onSelectionUpdate'],
|
type: NodeType
|
||||||
}) => void) | null,
|
parent: ParentConfig<NodeConfig<Options, Storage>>['onSelectionUpdate']
|
||||||
|
}) => void)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The editor state has changed.
|
* The editor state has changed.
|
||||||
*/
|
*/
|
||||||
onTransaction?: ((
|
onTransaction?:
|
||||||
this: {
|
| ((
|
||||||
name: string,
|
this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
editor: Editor,
|
storage: Storage
|
||||||
type: NodeType,
|
editor: Editor
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['onTransaction'],
|
type: NodeType
|
||||||
},
|
parent: ParentConfig<NodeConfig<Options, Storage>>['onTransaction']
|
||||||
props: {
|
},
|
||||||
transaction: Transaction,
|
props: {
|
||||||
},
|
transaction: Transaction
|
||||||
) => void) | null,
|
},
|
||||||
|
) => void)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The editor is focused.
|
* The editor is focused.
|
||||||
*/
|
*/
|
||||||
onFocus?: ((
|
onFocus?:
|
||||||
this: {
|
| ((
|
||||||
name: string,
|
this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
editor: Editor,
|
storage: Storage
|
||||||
type: NodeType,
|
editor: Editor
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['onFocus'],
|
type: NodeType
|
||||||
},
|
parent: ParentConfig<NodeConfig<Options, Storage>>['onFocus']
|
||||||
props: {
|
},
|
||||||
event: FocusEvent,
|
props: {
|
||||||
},
|
event: FocusEvent
|
||||||
) => void) | null,
|
},
|
||||||
|
) => void)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The editor isn’t focused anymore.
|
* The editor isn’t focused anymore.
|
||||||
*/
|
*/
|
||||||
onBlur?: ((
|
onBlur?:
|
||||||
this: {
|
| ((
|
||||||
name: string,
|
this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
editor: Editor,
|
storage: Storage
|
||||||
type: NodeType,
|
editor: Editor
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['onBlur'],
|
type: NodeType
|
||||||
},
|
parent: ParentConfig<NodeConfig<Options, Storage>>['onBlur']
|
||||||
props: {
|
},
|
||||||
event: FocusEvent,
|
props: {
|
||||||
},
|
event: FocusEvent
|
||||||
) => void) | null,
|
},
|
||||||
|
) => void)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The editor is destroyed.
|
* The editor is destroyed.
|
||||||
*/
|
*/
|
||||||
onDestroy?: ((this: {
|
onDestroy?:
|
||||||
name: string,
|
| ((this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
editor: Editor,
|
storage: Storage
|
||||||
type: NodeType,
|
editor: Editor
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['onDestroy'],
|
type: NodeType
|
||||||
}) => void) | null,
|
parent: ParentConfig<NodeConfig<Options, Storage>>['onDestroy']
|
||||||
|
}) => void)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Node View
|
* Node View
|
||||||
*/
|
*/
|
||||||
addNodeView?: ((this: {
|
addNodeView?:
|
||||||
name: string,
|
| ((this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
editor: Editor,
|
storage: Storage
|
||||||
type: NodeType,
|
editor: Editor
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['addNodeView'],
|
type: NodeType
|
||||||
}) => NodeViewRenderer) | null,
|
parent: ParentConfig<NodeConfig<Options, Storage>>['addNodeView']
|
||||||
|
}) => NodeViewRenderer)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TopNode
|
* TopNode
|
||||||
*/
|
*/
|
||||||
topNode?: boolean,
|
topNode?: boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Content
|
* Content
|
||||||
*/
|
*/
|
||||||
content?: NodeSpec['content'] | ((this: {
|
content?:
|
||||||
name: string,
|
| NodeSpec['content']
|
||||||
options: Options,
|
| ((this: {
|
||||||
storage: Storage,
|
name: string
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['content'],
|
options: Options
|
||||||
}) => NodeSpec['content']),
|
storage: Storage
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['content']
|
||||||
|
}) => NodeSpec['content'])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks
|
* Marks
|
||||||
*/
|
*/
|
||||||
marks?: NodeSpec['marks'] | ((this: {
|
marks?:
|
||||||
name: string,
|
| NodeSpec['marks']
|
||||||
options: Options,
|
| ((this: {
|
||||||
storage: Storage,
|
name: string
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['marks'],
|
options: Options
|
||||||
}) => NodeSpec['marks']),
|
storage: Storage
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['marks']
|
||||||
|
}) => NodeSpec['marks'])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Group
|
* Group
|
||||||
*/
|
*/
|
||||||
group?: NodeSpec['group'] | ((this: {
|
group?:
|
||||||
name: string,
|
| NodeSpec['group']
|
||||||
options: Options,
|
| ((this: {
|
||||||
storage: Storage,
|
name: string
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['group'],
|
options: Options
|
||||||
}) => NodeSpec['group']),
|
storage: Storage
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['group']
|
||||||
|
}) => NodeSpec['group'])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inline
|
* Inline
|
||||||
*/
|
*/
|
||||||
inline?: NodeSpec['inline'] | ((this: {
|
inline?:
|
||||||
name: string,
|
| NodeSpec['inline']
|
||||||
options: Options,
|
| ((this: {
|
||||||
storage: Storage,
|
name: string
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['inline'],
|
options: Options
|
||||||
}) => NodeSpec['inline']),
|
storage: Storage
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['inline']
|
||||||
|
}) => NodeSpec['inline'])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Atom
|
* Atom
|
||||||
*/
|
*/
|
||||||
atom?: NodeSpec['atom'] | ((this: {
|
atom?:
|
||||||
name: string,
|
| NodeSpec['atom']
|
||||||
options: Options,
|
| ((this: {
|
||||||
storage: Storage,
|
name: string
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['atom'],
|
options: Options
|
||||||
}) => NodeSpec['atom']),
|
storage: Storage
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['atom']
|
||||||
|
}) => NodeSpec['atom'])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Selectable
|
* Selectable
|
||||||
*/
|
*/
|
||||||
selectable?: NodeSpec['selectable'] | ((this: {
|
selectable?:
|
||||||
name: string,
|
| NodeSpec['selectable']
|
||||||
options: Options,
|
| ((this: {
|
||||||
storage: Storage,
|
name: string
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['selectable'],
|
options: Options
|
||||||
}) => NodeSpec['selectable']),
|
storage: Storage
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['selectable']
|
||||||
|
}) => NodeSpec['selectable'])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draggable
|
* Draggable
|
||||||
*/
|
*/
|
||||||
draggable?: NodeSpec['draggable'] | ((this: {
|
draggable?:
|
||||||
name: string,
|
| NodeSpec['draggable']
|
||||||
options: Options,
|
| ((this: {
|
||||||
storage: Storage,
|
name: string
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['draggable'],
|
options: Options
|
||||||
}) => NodeSpec['draggable']),
|
storage: Storage
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['draggable']
|
||||||
|
}) => NodeSpec['draggable'])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Code
|
* Code
|
||||||
*/
|
*/
|
||||||
code?: NodeSpec['code'] | ((this: {
|
code?:
|
||||||
name: string,
|
| NodeSpec['code']
|
||||||
options: Options,
|
| ((this: {
|
||||||
storage: Storage,
|
name: string
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['code'],
|
options: Options
|
||||||
}) => NodeSpec['code']),
|
storage: Storage
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['code']
|
||||||
|
}) => NodeSpec['code'])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whitespace
|
* Whitespace
|
||||||
*/
|
*/
|
||||||
whitespace?: NodeSpec['whitespace'] | ((this: {
|
whitespace?:
|
||||||
name: string,
|
| NodeSpec['whitespace']
|
||||||
options: Options,
|
| ((this: {
|
||||||
storage: Storage,
|
name: string
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['whitespace'],
|
options: Options
|
||||||
}) => NodeSpec['whitespace']),
|
storage: Storage
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['whitespace']
|
||||||
|
}) => NodeSpec['whitespace'])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defining
|
* Defining
|
||||||
*/
|
*/
|
||||||
defining?: NodeSpec['defining'] | ((this: {
|
defining?:
|
||||||
name: string,
|
| NodeSpec['defining']
|
||||||
options: Options,
|
| ((this: {
|
||||||
storage: Storage,
|
name: string
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['defining'],
|
options: Options
|
||||||
}) => NodeSpec['defining']),
|
storage: Storage
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['defining']
|
||||||
|
}) => NodeSpec['defining'])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Isolating
|
* Isolating
|
||||||
*/
|
*/
|
||||||
isolating?: NodeSpec['isolating'] | ((this: {
|
isolating?:
|
||||||
name: string,
|
| NodeSpec['isolating']
|
||||||
options: Options,
|
| ((this: {
|
||||||
storage: Storage,
|
name: string
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['isolating'],
|
options: Options
|
||||||
}) => NodeSpec['isolating']),
|
storage: Storage
|
||||||
|
parent: ParentConfig<NodeConfig<Options, Storage>>['isolating']
|
||||||
|
}) => NodeSpec['isolating'])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse HTML
|
* Parse HTML
|
||||||
*/
|
*/
|
||||||
parseHTML?: (
|
parseHTML?: (this: {
|
||||||
this: {
|
name: string
|
||||||
name: string,
|
options: Options
|
||||||
options: Options,
|
storage: Storage
|
||||||
storage: Storage,
|
parent: ParentConfig<NodeConfig<Options, Storage>>['parseHTML']
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['parseHTML'],
|
}) => NodeSpec['parseDOM']
|
||||||
},
|
|
||||||
) => NodeSpec['parseDOM'],
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render HTML
|
* Render HTML
|
||||||
*/
|
*/
|
||||||
renderHTML?: ((
|
renderHTML?:
|
||||||
this: {
|
| ((
|
||||||
name: string,
|
this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['renderHTML'],
|
storage: Storage
|
||||||
},
|
parent: ParentConfig<NodeConfig<Options, Storage>>['renderHTML']
|
||||||
props: {
|
},
|
||||||
node: ProseMirrorNode,
|
props: {
|
||||||
HTMLAttributes: Record<string, any>,
|
node: ProseMirrorNode
|
||||||
}
|
HTMLAttributes: Record<string, any>
|
||||||
) => DOMOutputSpec) | null,
|
},
|
||||||
|
) => DOMOutputSpec)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render Text
|
* Render Text
|
||||||
*/
|
*/
|
||||||
renderText?: ((
|
renderText?:
|
||||||
this: {
|
| ((
|
||||||
name: string,
|
this: {
|
||||||
options: Options,
|
name: string
|
||||||
storage: Storage,
|
options: Options
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['renderText'],
|
storage: Storage
|
||||||
},
|
parent: ParentConfig<NodeConfig<Options, Storage>>['renderText']
|
||||||
props: {
|
},
|
||||||
node: ProseMirrorNode,
|
props: {
|
||||||
pos: number,
|
node: ProseMirrorNode
|
||||||
parent: ProseMirrorNode,
|
pos: number
|
||||||
index: number,
|
parent: ProseMirrorNode
|
||||||
}
|
index: number
|
||||||
) => string) | null,
|
},
|
||||||
|
) => string)
|
||||||
|
| null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add Attributes
|
* Add Attributes
|
||||||
*/
|
*/
|
||||||
addAttributes?: (
|
addAttributes?: (this: {
|
||||||
this: {
|
name: string
|
||||||
name: string,
|
options: Options
|
||||||
options: Options,
|
storage: Storage
|
||||||
storage: Storage,
|
parent: ParentConfig<NodeConfig<Options, Storage>>['addAttributes']
|
||||||
parent: ParentConfig<NodeConfig<Options, Storage>>['addAttributes'],
|
}) => Attributes | {}
|
||||||
},
|
|
||||||
) => Attributes | {},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -493,30 +534,28 @@ export class Node<Options = any, Storage = any> {
|
|||||||
this.name = this.config.name
|
this.name = this.config.name
|
||||||
|
|
||||||
if (config.defaultOptions) {
|
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
|
// TODO: remove `addOptions` fallback
|
||||||
this.options = this.config.defaultOptions
|
this.options = this.config.defaultOptions
|
||||||
|
|
||||||
if (this.config.addOptions) {
|
if (this.config.addOptions) {
|
||||||
this.options = callOrReturn(getExtensionField<AnyConfig['addOptions']>(
|
this.options = callOrReturn(
|
||||||
this,
|
getExtensionField<AnyConfig['addOptions']>(this, 'addOptions', {
|
||||||
'addOptions',
|
|
||||||
{
|
|
||||||
name: this.name,
|
name: this.name,
|
||||||
},
|
}),
|
||||||
))
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.storage = callOrReturn(getExtensionField<AnyConfig['addStorage']>(
|
this.storage = callOrReturn(
|
||||||
this,
|
getExtensionField<AnyConfig['addStorage']>(this, 'addStorage', {
|
||||||
'addStorage',
|
|
||||||
{
|
|
||||||
name: this.name,
|
name: this.name,
|
||||||
options: this.options,
|
options: this.options,
|
||||||
},
|
}),
|
||||||
)) || {}
|
) || {}
|
||||||
}
|
}
|
||||||
|
|
||||||
static create<O = any, S = any>(config: Partial<NodeConfig<O, S>> = {}) {
|
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.options = mergeDeep(this.options as Record<string, any>, options) as Options
|
||||||
|
|
||||||
extension.storage = callOrReturn(getExtensionField<AnyConfig['addStorage']>(
|
extension.storage = callOrReturn(
|
||||||
extension,
|
getExtensionField<AnyConfig['addStorage']>(extension, 'addStorage', {
|
||||||
'addStorage',
|
|
||||||
{
|
|
||||||
name: extension.name,
|
name: extension.name,
|
||||||
options: extension.options,
|
options: extension.options,
|
||||||
},
|
}),
|
||||||
))
|
)
|
||||||
|
|
||||||
return extension
|
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)
|
const extension = new Node<ExtendedOptions, ExtendedStorage>(extendedConfig)
|
||||||
|
|
||||||
extension.parent = this
|
extension.parent = this
|
||||||
|
|
||||||
this.child = extension
|
this.child = extension
|
||||||
|
|
||||||
extension.name = extendedConfig.name
|
extension.name = extendedConfig.name ? extendedConfig.name : extension.parent.name
|
||||||
? extendedConfig.name
|
|
||||||
: extension.parent.name
|
|
||||||
|
|
||||||
if (extendedConfig.defaultOptions) {
|
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.options = callOrReturn(
|
||||||
extension,
|
getExtensionField<AnyConfig['addOptions']>(extension, 'addOptions', {
|
||||||
'addOptions',
|
|
||||||
{
|
|
||||||
name: extension.name,
|
name: extension.name,
|
||||||
},
|
}),
|
||||||
))
|
)
|
||||||
|
|
||||||
extension.storage = callOrReturn(getExtensionField<AnyConfig['addStorage']>(
|
extension.storage = callOrReturn(
|
||||||
extension,
|
getExtensionField<AnyConfig['addStorage']>(extension, 'addStorage', {
|
||||||
'addStorage',
|
|
||||||
{
|
|
||||||
name: extension.name,
|
name: extension.name,
|
||||||
options: extension.options,
|
options: extension.options,
|
||||||
},
|
}),
|
||||||
))
|
)
|
||||||
|
|
||||||
return extension
|
return extension
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Node as ProseMirrorNode } from 'prosemirror-model'
|
import { Node as ProseMirrorNode } from '@tiptap/pm/model'
|
||||||
import { NodeSelection } from 'prosemirror-state'
|
import { NodeSelection } from '@tiptap/pm/state'
|
||||||
import { Decoration, NodeView as ProseMirrorNodeView } from 'prosemirror-view'
|
import { Decoration, NodeView as ProseMirrorNodeView } from '@tiptap/pm/view'
|
||||||
|
|
||||||
import { Editor as CoreEditor } from './Editor'
|
import { Editor as CoreEditor } from './Editor'
|
||||||
import { Node } from './Node'
|
import { Node } from './Node'
|
||||||
@ -12,7 +12,6 @@ export class NodeView<
|
|||||||
Editor extends CoreEditor = CoreEditor,
|
Editor extends CoreEditor = CoreEditor,
|
||||||
Options extends NodeViewRendererOptions = NodeViewRendererOptions,
|
Options extends NodeViewRendererOptions = NodeViewRendererOptions,
|
||||||
> implements ProseMirrorNodeView {
|
> implements ProseMirrorNodeView {
|
||||||
|
|
||||||
component: Component
|
component: Component
|
||||||
|
|
||||||
editor: Editor
|
editor: Editor
|
||||||
@ -59,7 +58,7 @@ export class NodeView<
|
|||||||
|
|
||||||
onDragStart(event: DragEvent) {
|
onDragStart(event: DragEvent) {
|
||||||
const { view } = this.editor
|
const { view } = this.editor
|
||||||
const target = (event.target as HTMLElement)
|
const target = event.target as HTMLElement
|
||||||
|
|
||||||
// get the drag handle element
|
// get the drag handle element
|
||||||
// `closest` is not available for text nodes so we may have to use its parent
|
// `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.parentElement?.closest('[data-drag-handle]')
|
||||||
: target.closest('[data-drag-handle]')
|
: target.closest('[data-drag-handle]')
|
||||||
|
|
||||||
if (
|
if (!this.dom || this.contentDOM?.contains(target) || !dragHandle) {
|
||||||
!this.dom
|
|
||||||
|| this.contentDOM?.contains(target)
|
|
||||||
|| !dragHandle
|
|
||||||
) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,7 +105,7 @@ export class NodeView<
|
|||||||
return this.options.stopEvent({ event })
|
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)
|
const isInElement = this.dom.contains(target) && !this.contentDOM?.contains(target)
|
||||||
|
|
||||||
// any event from child nodes should be handled by ProseMirror
|
// any event from child nodes should be handled by ProseMirror
|
||||||
@ -119,8 +114,7 @@ export class NodeView<
|
|||||||
}
|
}
|
||||||
|
|
||||||
const isDropEvent = event.type === 'drop'
|
const isDropEvent = event.type === 'drop'
|
||||||
const isInput = ['INPUT', 'BUTTON', 'SELECT', 'TEXTAREA'].includes(target.tagName)
|
const isInput = ['INPUT', 'BUTTON', 'SELECT', 'TEXTAREA'].includes(target.tagName) || target.isContentEditable
|
||||||
|| target.isContentEditable
|
|
||||||
|
|
||||||
// any input event within node views should be ignored by ProseMirror
|
// any input event within node views should be ignored by ProseMirror
|
||||||
if (isInput && !isDropEvent) {
|
if (isInput && !isDropEvent) {
|
||||||
@ -152,19 +146,26 @@ export class NodeView<
|
|||||||
// we have to store that dragging started
|
// we have to store that dragging started
|
||||||
if (isDraggable && isEditable && !isDragging && isClickEvent) {
|
if (isDraggable && isEditable && !isDragging && isClickEvent) {
|
||||||
const dragHandle = target.closest('[data-drag-handle]')
|
const dragHandle = target.closest('[data-drag-handle]')
|
||||||
const isValidDragHandle = dragHandle
|
const isValidDragHandle = dragHandle && (this.dom === dragHandle || this.dom.contains(dragHandle))
|
||||||
&& (this.dom === dragHandle || (this.dom.contains(dragHandle)))
|
|
||||||
|
|
||||||
if (isValidDragHandle) {
|
if (isValidDragHandle) {
|
||||||
this.isDragging = true
|
this.isDragging = true
|
||||||
|
|
||||||
document.addEventListener('dragend', () => {
|
document.addEventListener(
|
||||||
this.isDragging = false
|
'dragend',
|
||||||
}, { once: true })
|
() => {
|
||||||
|
this.isDragging = false
|
||||||
|
},
|
||||||
|
{ once: true },
|
||||||
|
)
|
||||||
|
|
||||||
document.addEventListener('mouseup', () => {
|
document.addEventListener(
|
||||||
this.isDragging = false
|
'mouseup',
|
||||||
}, { once: true })
|
() => {
|
||||||
|
this.isDragging = false
|
||||||
|
},
|
||||||
|
{ once: true },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +184,7 @@ export class NodeView<
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
ignoreMutation(mutation: MutationRecord | { type: 'selection', target: Element }) {
|
ignoreMutation(mutation: MutationRecord | { type: 'selection'; target: Element }) {
|
||||||
if (!this.dom || !this.contentDOM) {
|
if (!this.dom || !this.contentDOM) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { EditorState, Plugin } from 'prosemirror-state'
|
import { EditorState, Plugin } from '@tiptap/pm/state'
|
||||||
|
|
||||||
import { CommandManager } from './CommandManager'
|
import { CommandManager } from './CommandManager'
|
||||||
import { Editor } from './Editor'
|
import { Editor } from './Editor'
|
||||||
@ -14,46 +14,47 @@ import { isNumber } from './utilities/isNumber'
|
|||||||
import { isRegExp } from './utilities/isRegExp'
|
import { isRegExp } from './utilities/isRegExp'
|
||||||
|
|
||||||
export type PasteRuleMatch = {
|
export type PasteRuleMatch = {
|
||||||
index: number,
|
index: number
|
||||||
text: string,
|
text: string
|
||||||
replaceWith?: string,
|
replaceWith?: string
|
||||||
match?: RegExpMatchArray,
|
match?: RegExpMatchArray
|
||||||
data?: Record<string, any>,
|
data?: Record<string, any>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PasteRuleFinder =
|
export type PasteRuleFinder = RegExp | ((text: string) => PasteRuleMatch[] | null | undefined)
|
||||||
| RegExp
|
|
||||||
| ((text: string) => PasteRuleMatch[] | null | undefined)
|
|
||||||
|
|
||||||
export class PasteRule {
|
export class PasteRule {
|
||||||
find: PasteRuleFinder
|
find: PasteRuleFinder
|
||||||
|
|
||||||
handler: (props: {
|
handler: (props: {
|
||||||
state: EditorState,
|
state: EditorState
|
||||||
range: Range,
|
range: Range
|
||||||
match: ExtendedRegExpMatchArray,
|
match: ExtendedRegExpMatchArray
|
||||||
commands: SingleCommands,
|
commands: SingleCommands
|
||||||
chain: () => ChainedCommands,
|
chain: () => ChainedCommands
|
||||||
can: () => CanCommands,
|
can: () => CanCommands
|
||||||
}) => void | null
|
}) => void | null
|
||||||
|
|
||||||
constructor(config: {
|
constructor(config: {
|
||||||
find: PasteRuleFinder,
|
find: PasteRuleFinder
|
||||||
handler: (props: {
|
handler: (props: {
|
||||||
state: EditorState,
|
state: EditorState
|
||||||
range: Range,
|
range: Range
|
||||||
match: ExtendedRegExpMatchArray,
|
match: ExtendedRegExpMatchArray
|
||||||
commands: SingleCommands,
|
commands: SingleCommands
|
||||||
chain: () => ChainedCommands,
|
chain: () => ChainedCommands
|
||||||
can: () => CanCommands,
|
can: () => CanCommands
|
||||||
}) => void | null,
|
}) => void | null
|
||||||
}) {
|
}) {
|
||||||
this.find = config.find
|
this.find = config.find
|
||||||
this.handler = config.handler
|
this.handler = config.handler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const pasteRuleMatcherHandler = (text: string, find: PasteRuleFinder): ExtendedRegExpMatchArray[] => {
|
const pasteRuleMatcherHandler = (
|
||||||
|
text: string,
|
||||||
|
find: PasteRuleFinder,
|
||||||
|
): ExtendedRegExpMatchArray[] => {
|
||||||
if (isRegExp(find)) {
|
if (isRegExp(find)) {
|
||||||
return [...text.matchAll(find)]
|
return [...text.matchAll(find)]
|
||||||
}
|
}
|
||||||
@ -73,7 +74,9 @@ const pasteRuleMatcherHandler = (text: string, find: PasteRuleFinder): ExtendedR
|
|||||||
|
|
||||||
if (pasteRuleMatch.replaceWith) {
|
if (pasteRuleMatch.replaceWith) {
|
||||||
if (!pasteRuleMatch.text.includes(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)
|
result.push(pasteRuleMatch.replaceWith)
|
||||||
@ -84,18 +87,14 @@ const pasteRuleMatcherHandler = (text: string, find: PasteRuleFinder): ExtendedR
|
|||||||
}
|
}
|
||||||
|
|
||||||
function run(config: {
|
function run(config: {
|
||||||
editor: Editor,
|
editor: Editor
|
||||||
state: EditorState,
|
state: EditorState
|
||||||
from: number,
|
from: number
|
||||||
to: number,
|
to: number
|
||||||
rule: PasteRule,
|
rule: PasteRule
|
||||||
}): boolean {
|
}): boolean {
|
||||||
const {
|
const {
|
||||||
editor,
|
editor, state, from, to, rule,
|
||||||
state,
|
|
||||||
from,
|
|
||||||
to,
|
|
||||||
rule,
|
|
||||||
} = config
|
} = config
|
||||||
|
|
||||||
const { commands, chain, can } = new CommandManager({
|
const { commands, chain, can } = new CommandManager({
|
||||||
@ -112,12 +111,7 @@ function run(config: {
|
|||||||
|
|
||||||
const resolvedFrom = Math.max(from, pos)
|
const resolvedFrom = Math.max(from, pos)
|
||||||
const resolvedTo = Math.min(to, pos + node.content.size)
|
const resolvedTo = Math.min(to, pos + node.content.size)
|
||||||
const textToMatch = node.textBetween(
|
const textToMatch = node.textBetween(resolvedFrom - pos, resolvedTo - pos, undefined, '\ufffc')
|
||||||
resolvedFrom - pos,
|
|
||||||
resolvedTo - pos,
|
|
||||||
undefined,
|
|
||||||
'\ufffc',
|
|
||||||
)
|
|
||||||
|
|
||||||
const matches = pasteRuleMatcherHandler(textToMatch, rule.find)
|
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
|
* text that matches any of the given rules to trigger the rule’s
|
||||||
* action.
|
* action.
|
||||||
*/
|
*/
|
||||||
export function pasteRulesPlugin(props: { editor: Editor, rules: PasteRule[] }): Plugin[] {
|
export function pasteRulesPlugin(props: { editor: Editor; rules: PasteRule[] }): Plugin[] {
|
||||||
const { editor, rules } = props
|
const { editor, rules } = props
|
||||||
let dragSourceElement: Element | null = null
|
let dragSourceElement: Element | null = null
|
||||||
let isPastedFromProseMirror = false
|
let isPastedFromProseMirror = false
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
import { Transaction } from 'prosemirror-state'
|
import { Transaction } from '@tiptap/pm/state'
|
||||||
|
|
||||||
export interface TrackerResult {
|
export interface TrackerResult {
|
||||||
position: number,
|
position: number
|
||||||
deleted: boolean,
|
deleted: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Tracker {
|
export class Tracker {
|
||||||
|
|
||||||
transaction: Transaction
|
transaction: Transaction
|
||||||
|
|
||||||
currentStep: number
|
currentStep: number
|
||||||
@ -22,9 +21,7 @@ export class Tracker {
|
|||||||
const mappedPosition = this.transaction.steps
|
const mappedPosition = this.transaction.steps
|
||||||
.slice(this.currentStep)
|
.slice(this.currentStep)
|
||||||
.reduce((newPosition, step) => {
|
.reduce((newPosition, step) => {
|
||||||
const mapResult = step
|
const mapResult = step.getMap().mapResult(newPosition)
|
||||||
.getMap()
|
|
||||||
.mapResult(newPosition)
|
|
||||||
|
|
||||||
if (mapResult.deleted) {
|
if (mapResult.deleted) {
|
||||||
deleted = true
|
deleted = true
|
||||||
@ -38,5 +35,4 @@ export class Tracker {
|
|||||||
deleted,
|
deleted,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { liftTarget } from 'prosemirror-transform'
|
import { liftTarget } from '@tiptap/pm/transform'
|
||||||
|
|
||||||
import { RawCommands } from '../types'
|
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'
|
import { RawCommands } from '../types'
|
||||||
|
|
||||||
@ -8,7 +8,7 @@ declare module '@tiptap/core' {
|
|||||||
/**
|
/**
|
||||||
* Create a paragraph nearby.
|
* 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 { getNodeType } from '../helpers/getNodeType'
|
||||||
import { RawCommands } from '../types'
|
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'
|
import { RawCommands } from '../types'
|
||||||
|
|
||||||
@ -8,7 +8,7 @@ declare module '@tiptap/core' {
|
|||||||
/**
|
/**
|
||||||
* Delete the selection, if there is one.
|
* 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'
|
import { RawCommands } from '../types'
|
||||||
|
|
||||||
@ -8,7 +8,7 @@ declare module '@tiptap/core' {
|
|||||||
/**
|
/**
|
||||||
* Exit from a code block.
|
* Exit from a code block.
|
||||||
*/
|
*/
|
||||||
exitCode: () => ReturnType,
|
exitCode: () => ReturnType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { MarkType } from 'prosemirror-model'
|
import { MarkType } from '@tiptap/pm/model'
|
||||||
import { TextSelection } from 'prosemirror-state'
|
import { TextSelection } from '@tiptap/pm/state'
|
||||||
|
|
||||||
import { getMarkRange } from '../helpers/getMarkRange'
|
import { getMarkRange } from '../helpers/getMarkRange'
|
||||||
import { getMarkType } from '../helpers/getMarkType'
|
import { getMarkType } from '../helpers/getMarkType'
|
||||||
@ -11,7 +11,10 @@ declare module '@tiptap/core' {
|
|||||||
/**
|
/**
|
||||||
* Extends the text selection to the current mark.
|
* 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'
|
import { Content, RawCommands } from '../types'
|
||||||
|
|
||||||
@ -11,14 +11,18 @@ declare module '@tiptap/core' {
|
|||||||
insertContent: (
|
insertContent: (
|
||||||
value: Content,
|
value: Content,
|
||||||
options?: {
|
options?: {
|
||||||
parseOptions?: ParseOptions,
|
parseOptions?: ParseOptions
|
||||||
updateSelection?: boolean,
|
updateSelection?: boolean
|
||||||
},
|
},
|
||||||
) => ReturnType,
|
) => ReturnType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const insertContent: RawCommands['insertContent'] = (value, options) => ({ tr, commands }) => {
|
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 { createNodeFromContent } from '../helpers/createNodeFromContent'
|
||||||
import { selectionToInsertionEnd } from '../helpers/selectionToInsertionEnd'
|
import { selectionToInsertionEnd } from '../helpers/selectionToInsertionEnd'
|
||||||
import {
|
import { Content, Range, RawCommands } from '../types'
|
||||||
Content,
|
|
||||||
Range,
|
|
||||||
RawCommands,
|
|
||||||
} from '../types'
|
|
||||||
|
|
||||||
declare module '@tiptap/core' {
|
declare module '@tiptap/core' {
|
||||||
interface Commands<ReturnType> {
|
interface Commands<ReturnType> {
|
||||||
@ -18,10 +14,10 @@ declare module '@tiptap/core' {
|
|||||||
position: number | Range,
|
position: number | Range,
|
||||||
value: Content,
|
value: Content,
|
||||||
options?: {
|
options?: {
|
||||||
parseOptions?: ParseOptions,
|
parseOptions?: ParseOptions
|
||||||
updateSelection?: boolean,
|
updateSelection?: boolean
|
||||||
},
|
},
|
||||||
) => ReturnType,
|
) => ReturnType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -50,27 +46,19 @@ export const insertContentAt: RawCommands['insertContentAt'] = (position, value,
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
let { from, to } = typeof position === 'number'
|
let { from, to } = typeof position === 'number' ? { from: position, to: position } : position
|
||||||
? { from: position, to: position }
|
|
||||||
: position
|
|
||||||
|
|
||||||
let isOnlyTextContent = true
|
let isOnlyTextContent = true
|
||||||
let isOnlyBlockContent = true
|
let isOnlyBlockContent = true
|
||||||
const nodes = isFragment(content)
|
const nodes = isFragment(content) ? content : [content]
|
||||||
? content
|
|
||||||
: [content]
|
|
||||||
|
|
||||||
nodes.forEach(node => {
|
nodes.forEach(node => {
|
||||||
// check if added node is valid
|
// check if added node is valid
|
||||||
node.check()
|
node.check()
|
||||||
|
|
||||||
isOnlyTextContent = isOnlyTextContent
|
isOnlyTextContent = isOnlyTextContent ? node.isText && node.marks.length === 0 : false
|
||||||
? node.isText && node.marks.length === 0
|
|
||||||
: false
|
|
||||||
|
|
||||||
isOnlyBlockContent = isOnlyBlockContent
|
isOnlyBlockContent = isOnlyBlockContent ? node.isBlock : false
|
||||||
? node.isBlock
|
|
||||||
: false
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// check if we can replace the wrapping node by
|
// 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
|
// instead of inserting the image below the paragraph
|
||||||
if (from === to && isOnlyBlockContent) {
|
if (from === to && isOnlyBlockContent) {
|
||||||
const { parent } = tr.doc.resolve(from)
|
const { parent } = tr.doc.resolve(from)
|
||||||
const isEmptyTextBlock = parent.isTextblock
|
const isEmptyTextBlock = parent.isTextblock && !parent.type.spec.code && !parent.childCount
|
||||||
&& !parent.type.spec.code
|
|
||||||
&& !parent.childCount
|
|
||||||
|
|
||||||
if (isEmptyTextBlock) {
|
if (isEmptyTextBlock) {
|
||||||
from -= 1
|
from -= 1
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import {
|
import {
|
||||||
joinBackward as originalJoinBackward, joinDown as originalJoinDown, joinForward as originalJoinForward, joinUp as originalJoinUp,
|
joinBackward as originalJoinBackward,
|
||||||
} from 'prosemirror-commands'
|
joinDown as originalJoinDown,
|
||||||
|
joinForward as originalJoinForward,
|
||||||
|
joinUp as originalJoinUp,
|
||||||
|
} from '@tiptap/pm/commands'
|
||||||
|
|
||||||
import { RawCommands } from '../types'
|
import { RawCommands } from '../types'
|
||||||
|
|
||||||
@ -10,25 +13,25 @@ declare module '@tiptap/core' {
|
|||||||
/**
|
/**
|
||||||
* Join two nodes Up.
|
* Join two nodes Up.
|
||||||
*/
|
*/
|
||||||
joinUp: () => ReturnType,
|
joinUp: () => ReturnType
|
||||||
}
|
}
|
||||||
joinDown: {
|
joinDown: {
|
||||||
/**
|
/**
|
||||||
* Join two nodes Down.
|
* Join two nodes Down.
|
||||||
*/
|
*/
|
||||||
joinDown: () => ReturnType,
|
joinDown: () => ReturnType
|
||||||
}
|
}
|
||||||
joinBackward: {
|
joinBackward: {
|
||||||
/**
|
/**
|
||||||
* Join two nodes Backwards.
|
* Join two nodes Backwards.
|
||||||
*/
|
*/
|
||||||
joinBackward: () => ReturnType,
|
joinBackward: () => ReturnType
|
||||||
}
|
}
|
||||||
joinForward: {
|
joinForward: {
|
||||||
/**
|
/**
|
||||||
* Join two nodes Forwards.
|
* Join two nodes Forwards.
|
||||||
*/
|
*/
|
||||||
joinForward: () => ReturnType,
|
joinForward: () => ReturnType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { lift as originalLift } from 'prosemirror-commands'
|
import { lift as originalLift } from '@tiptap/pm/commands'
|
||||||
import { NodeType } from 'prosemirror-model'
|
import { NodeType } from '@tiptap/pm/model'
|
||||||
|
|
||||||
import { getNodeType } from '../helpers/getNodeType'
|
import { getNodeType } from '../helpers/getNodeType'
|
||||||
import { isNodeActive } from '../helpers/isNodeActive'
|
import { isNodeActive } from '../helpers/isNodeActive'
|
||||||
@ -11,7 +11,7 @@ declare module '@tiptap/core' {
|
|||||||
/**
|
/**
|
||||||
* Removes an existing wrap.
|
* 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'
|
import { RawCommands } from '../types'
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { NodeType } from 'prosemirror-model'
|
import { NodeType } from '@tiptap/pm/model'
|
||||||
import { liftListItem as originalLiftListItem } from 'prosemirror-schema-list'
|
import { liftListItem as originalLiftListItem } from '@tiptap/pm/schema-list'
|
||||||
|
|
||||||
import { getNodeType } from '../helpers/getNodeType'
|
import { getNodeType } from '../helpers/getNodeType'
|
||||||
import { RawCommands } from '../types'
|
import { RawCommands } from '../types'
|
||||||
@ -10,7 +10,7 @@ declare module '@tiptap/core' {
|
|||||||
/**
|
/**
|
||||||
* Lift the list item into a wrapping list.
|
* 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'
|
import { RawCommands } from '../types'
|
||||||
|
|
||||||
@ -8,7 +8,7 @@ declare module '@tiptap/core' {
|
|||||||
/**
|
/**
|
||||||
* Add a newline character in code.
|
* 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 { getMarkType } from '../helpers/getMarkType'
|
||||||
import { getNodeType } from '../helpers/getNodeType'
|
import { getNodeType } from '../helpers/getNodeType'
|
||||||
@ -12,7 +12,10 @@ declare module '@tiptap/core' {
|
|||||||
/**
|
/**
|
||||||
* Resets some node attributes to the default value.
|
* 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
|
let markType: MarkType | null = null
|
||||||
|
|
||||||
const schemaType = getSchemaTypeNameByName(
|
const schemaType = getSchemaTypeNameByName(
|
||||||
typeof typeOrName === 'string'
|
typeof typeOrName === 'string' ? typeOrName : typeOrName.name,
|
||||||
? typeOrName
|
|
||||||
: typeOrName.name,
|
|
||||||
state.schema,
|
state.schema,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -50,7 +51,11 @@ export const resetAttributes: RawCommands['resetAttributes'] = (typeOrName, attr
|
|||||||
if (markType && node.marks.length) {
|
if (markType && node.marks.length) {
|
||||||
node.marks.forEach(mark => {
|
node.marks.forEach(mark => {
|
||||||
if (markType === mark.type) {
|
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'
|
import { RawCommands } from '../types'
|
||||||
|
|
||||||
@ -8,7 +8,7 @@ declare module '@tiptap/core' {
|
|||||||
/**
|
/**
|
||||||
* Select a node backward.
|
* 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'
|
import { RawCommands } from '../types'
|
||||||
|
|
||||||
@ -8,7 +8,7 @@ declare module '@tiptap/core' {
|
|||||||
/**
|
/**
|
||||||
* Select a node forward.
|
* 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'
|
import { RawCommands } from '../types'
|
||||||
|
|
||||||
@ -8,7 +8,7 @@ declare module '@tiptap/core' {
|
|||||||
/**
|
/**
|
||||||
* Select the parent node.
|
* Select the parent node.
|
||||||
*/
|
*/
|
||||||
selectParentNode: () => ReturnType,
|
selectParentNode: () => ReturnType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
// TODO: add types to @types/prosemirror-commands
|
// 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'
|
import { RawCommands } from '../types'
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ declare module '@tiptap/core' {
|
|||||||
/**
|
/**
|
||||||
* Moves the cursor to the end of current text block.
|
* Moves the cursor to the end of current text block.
|
||||||
*/
|
*/
|
||||||
selectTextblockEnd: () => ReturnType,
|
selectTextblockEnd: () => ReturnType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
// TODO: add types to @types/prosemirror-commands
|
// 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'
|
import { RawCommands } from '../types'
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ declare module '@tiptap/core' {
|
|||||||
/**
|
/**
|
||||||
* Moves the cursor to the start of current text block.
|
* 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 { createDocument } from '../helpers/createDocument'
|
||||||
import { Content, RawCommands } from '../types'
|
import { Content, RawCommands } from '../types'
|
||||||
@ -13,7 +13,7 @@ declare module '@tiptap/core' {
|
|||||||
content: Content,
|
content: Content,
|
||||||
emitUpdate?: boolean,
|
emitUpdate?: boolean,
|
||||||
parseOptions?: ParseOptions,
|
parseOptions?: ParseOptions,
|
||||||
) => ReturnType,
|
) => ReturnType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -23,8 +23,7 @@ export const setContent: RawCommands['setContent'] = (content, emitUpdate = fals
|
|||||||
const document = createDocument(content, editor.schema, parseOptions)
|
const document = createDocument(content, editor.schema, parseOptions)
|
||||||
|
|
||||||
if (dispatch) {
|
if (dispatch) {
|
||||||
tr.replaceWith(0, doc.content.size, document)
|
tr.replaceWith(0, doc.content.size, document).setMeta('preventUpdate', !emitUpdate)
|
||||||
.setMeta('preventUpdate', !emitUpdate)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { MarkType, ResolvedPos } from 'prosemirror-model'
|
import { MarkType, ResolvedPos } from '@tiptap/pm/model'
|
||||||
import { EditorState, Transaction } from 'prosemirror-state'
|
import { EditorState, Transaction } from '@tiptap/pm/state'
|
||||||
|
|
||||||
import { isTextSelection } from '../helpers'
|
import { isTextSelection } from '../helpers'
|
||||||
import { getMarkAttributes } from '../helpers/getMarkAttributes'
|
import { getMarkAttributes } from '../helpers/getMarkAttributes'
|
||||||
@ -12,7 +12,7 @@ declare module '@tiptap/core' {
|
|||||||
/**
|
/**
|
||||||
* Add a mark with new attributes.
|
* 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()
|
const currentMarks = state.storedMarks ?? cursor.marks()
|
||||||
|
|
||||||
// There can be no current marks that exclude the new mark
|
// 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
|
const { ranges } = selection
|
||||||
|
|
||||||
return ranges.some(({ $from, $to }) => {
|
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) => {
|
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
|
// 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) {
|
if (node.isInline) {
|
||||||
const parentAllowsMarkType = !parent || parent.type.allowsMarkType(newMarkType)
|
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
|
someNodeSupportsMark = parentAllowsMarkType && currentMarksAllowMarkType
|
||||||
}
|
}
|
||||||
@ -54,7 +60,6 @@ function canSetMark(state: EditorState, tr: Transaction, newMarkType: MarkType)
|
|||||||
|
|
||||||
return someNodeSupportsMark
|
return someNodeSupportsMark
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
export const setMark: RawCommands['setMark'] = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
|
export const setMark: RawCommands['setMark'] = (typeOrName, attributes = {}) => ({ tr, state, dispatch }) => {
|
||||||
const { selection } = tr
|
const { selection } = tr
|
||||||
@ -65,10 +70,12 @@ export const setMark: RawCommands['setMark'] = (typeOrName, attributes = {}) =>
|
|||||||
if (empty) {
|
if (empty) {
|
||||||
const oldAttributes = getMarkAttributes(state, type)
|
const oldAttributes = getMarkAttributes(state, type)
|
||||||
|
|
||||||
tr.addStoredMark(type.create({
|
tr.addStoredMark(
|
||||||
...oldAttributes,
|
type.create({
|
||||||
...attributes,
|
...oldAttributes,
|
||||||
}))
|
...attributes,
|
||||||
|
}),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
ranges.forEach(range => {
|
ranges.forEach(range => {
|
||||||
const from = range.$from.pos
|
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
|
// we know that we have to merge its attributes
|
||||||
// otherwise we add a fresh new mark
|
// otherwise we add a fresh new mark
|
||||||
if (someHasMark) {
|
if (someHasMark) {
|
||||||
|
|
||||||
node.marks.forEach(mark => {
|
node.marks.forEach(mark => {
|
||||||
if (type === mark.type) {
|
if (type === mark.type) {
|
||||||
tr.addMark(trimmedFrom, trimmedTo, type.create({
|
tr.addMark(
|
||||||
...mark.attrs,
|
trimmedFrom,
|
||||||
...attributes,
|
trimmedTo,
|
||||||
}))
|
type.create({
|
||||||
|
...mark.attrs,
|
||||||
|
...attributes,
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { setBlockType } from 'prosemirror-commands'
|
import { setBlockType } from '@tiptap/pm/commands'
|
||||||
import { NodeType } from 'prosemirror-model'
|
import { NodeType } from '@tiptap/pm/model'
|
||||||
|
|
||||||
import { getNodeType } from '../helpers/getNodeType'
|
import { getNodeType } from '../helpers/getNodeType'
|
||||||
import { RawCommands } from '../types'
|
import { RawCommands } from '../types'
|
||||||
@ -10,7 +10,7 @@ declare module '@tiptap/core' {
|
|||||||
/**
|
/**
|
||||||
* Replace a given range with a node.
|
* 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 false
|
||||||
}
|
}
|
||||||
|
|
||||||
return chain()
|
return (
|
||||||
|
chain()
|
||||||
// try to convert node to default node if needed
|
// try to convert node to default node if needed
|
||||||
.command(({ commands }) => {
|
.command(({ commands }) => {
|
||||||
const canSetBlock = setBlockType(type, attributes)(state)
|
const canSetBlock = setBlockType(type, attributes)(state)
|
||||||
|
|
||||||
if (canSetBlock) {
|
if (canSetBlock) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return commands.clearNodes()
|
return commands.clearNodes()
|
||||||
})
|
})
|
||||||
.command(({ state: updatedState }) => {
|
.command(({ state: updatedState }) => {
|
||||||
return setBlockType(type, attributes)(updatedState, dispatch)
|
return setBlockType(type, attributes)(updatedState, dispatch)
|
||||||
})
|
})
|
||||||
.run()
|
.run()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { NodeSelection } from 'prosemirror-state'
|
import { NodeSelection } from '@tiptap/pm/state'
|
||||||
|
|
||||||
import { RawCommands } from '../types'
|
import { RawCommands } from '../types'
|
||||||
import { minMax } from '../utilities/minMax'
|
import { minMax } from '../utilities/minMax'
|
||||||
@ -9,7 +9,7 @@ declare module '@tiptap/core' {
|
|||||||
/**
|
/**
|
||||||
* Creates a NodeSelection.
|
* 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 { Range, RawCommands } from '../types'
|
||||||
import { minMax } from '../utilities/minMax'
|
import { minMax } from '../utilities/minMax'
|
||||||
@ -9,7 +9,7 @@ declare module '@tiptap/core' {
|
|||||||
/**
|
/**
|
||||||
* Creates a TextSelection.
|
* 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 }) => {
|
export const setTextSelection: RawCommands['setTextSelection'] = position => ({ tr, dispatch }) => {
|
||||||
if (dispatch) {
|
if (dispatch) {
|
||||||
const { doc } = tr
|
const { doc } = tr
|
||||||
const { from, to } = typeof position === 'number'
|
const { from, to } = typeof position === 'number' ? { from: position, to: position } : position
|
||||||
? { from: position, to: position }
|
|
||||||
: position
|
|
||||||
const minPos = TextSelection.atStart(doc).from
|
const minPos = TextSelection.atStart(doc).from
|
||||||
const maxPos = TextSelection.atEnd(doc).to
|
const maxPos = TextSelection.atEnd(doc).to
|
||||||
const resolvedFrom = minMax(from, minPos, maxPos)
|
const resolvedFrom = minMax(from, minPos, maxPos)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { NodeType } from 'prosemirror-model'
|
import { NodeType } from '@tiptap/pm/model'
|
||||||
import { sinkListItem as originalSinkListItem } from 'prosemirror-schema-list'
|
import { sinkListItem as originalSinkListItem } from '@tiptap/pm/schema-list'
|
||||||
|
|
||||||
import { getNodeType } from '../helpers/getNodeType'
|
import { getNodeType } from '../helpers/getNodeType'
|
||||||
import { RawCommands } from '../types'
|
import { RawCommands } from '../types'
|
||||||
@ -10,7 +10,7 @@ declare module '@tiptap/core' {
|
|||||||
/**
|
/**
|
||||||
* Sink the list item down into an inner list.
|
* 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 { EditorState, NodeSelection, TextSelection } from '@tiptap/pm/state'
|
||||||
import { canSplit } from 'prosemirror-transform'
|
import { canSplit } from '@tiptap/pm/transform'
|
||||||
|
|
||||||
import { defaultBlockAt } from '../helpers/defaultBlockAt'
|
import { defaultBlockAt } from '../helpers/defaultBlockAt'
|
||||||
import { getSplittedAttributes } from '../helpers/getSplittedAttributes'
|
import { getSplittedAttributes } from '../helpers/getSplittedAttributes'
|
||||||
import { RawCommands } from '../types'
|
import { RawCommands } from '../types'
|
||||||
|
|
||||||
function ensureMarks(state: EditorState, splittableMarks?: string[]) {
|
function ensureMarks(state: EditorState, splittableMarks?: string[]) {
|
||||||
const marks = state.storedMarks
|
const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks())
|
||||||
|| (state.selection.$to.parentOffset && state.selection.$from.marks())
|
|
||||||
|
|
||||||
if (marks) {
|
if (marks) {
|
||||||
const filteredMarks = marks.filter(mark => splittableMarks?.includes(mark.type.name))
|
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.
|
* 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 } = {}) => ({
|
export const splitBlock: RawCommands['splitBlock'] = ({ keepMarks = true } = {}) => ({
|
||||||
tr,
|
tr, state, dispatch, editor,
|
||||||
state,
|
|
||||||
dispatch,
|
|
||||||
editor,
|
|
||||||
}) => {
|
}) => {
|
||||||
const { selection, doc } = tr
|
const { selection, doc } = tr
|
||||||
const { $from, $to } = selection
|
const { $from, $to } = selection
|
||||||
@ -74,37 +70,36 @@ export const splitBlock: RawCommands['splitBlock'] = ({ keepMarks = true } = {})
|
|||||||
: defaultBlockAt($from.node(-1).contentMatchAt($from.indexAfter(-1)))
|
: defaultBlockAt($from.node(-1).contentMatchAt($from.indexAfter(-1)))
|
||||||
|
|
||||||
let types = atEnd && deflt
|
let types = atEnd && deflt
|
||||||
? [{
|
? [
|
||||||
type: deflt,
|
{
|
||||||
attrs: newAttributes,
|
type: deflt,
|
||||||
}]
|
attrs: newAttributes,
|
||||||
|
},
|
||||||
|
]
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
let can = canSplit(tr.doc, tr.mapping.map($from.pos), 1, types)
|
let can = canSplit(tr.doc, tr.mapping.map($from.pos), 1, types)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!types
|
!types
|
||||||
&& !can
|
&& !can
|
||||||
&& canSplit(tr.doc, tr.mapping.map($from.pos), 1, deflt ? [{ type: deflt }] : undefined)
|
&& canSplit(tr.doc, tr.mapping.map($from.pos), 1, deflt ? [{ type: deflt }] : undefined)
|
||||||
) {
|
) {
|
||||||
can = true
|
can = true
|
||||||
types = deflt
|
types = deflt
|
||||||
? [{
|
? [
|
||||||
type: deflt,
|
{
|
||||||
attrs: newAttributes,
|
type: deflt,
|
||||||
}]
|
attrs: newAttributes,
|
||||||
|
},
|
||||||
|
]
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
if (can) {
|
if (can) {
|
||||||
tr.split(tr.mapping.map($from.pos), 1, types)
|
tr.split(tr.mapping.map($from.pos), 1, types)
|
||||||
|
|
||||||
if (
|
if (deflt && !atEnd && !$from.parentOffset && $from.parent.type !== deflt) {
|
||||||
deflt
|
|
||||||
&& !atEnd
|
|
||||||
&& !$from.parentOffset
|
|
||||||
&& $from.parent.type !== deflt
|
|
||||||
) {
|
|
||||||
const first = tr.mapping.map($from.before())
|
const first = tr.mapping.map($from.before())
|
||||||
const $first = tr.doc.resolve(first)
|
const $first = tr.doc.resolve(first)
|
||||||
|
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
Fragment,
|
Fragment, Node as ProseMirrorNode, NodeType, Slice,
|
||||||
Node as ProseMirrorNode,
|
} from '@tiptap/pm/model'
|
||||||
NodeType,
|
import { TextSelection } from '@tiptap/pm/state'
|
||||||
Slice,
|
import { canSplit } from '@tiptap/pm/transform'
|
||||||
} from 'prosemirror-model'
|
|
||||||
import { TextSelection } from 'prosemirror-state'
|
|
||||||
import { canSplit } from 'prosemirror-transform'
|
|
||||||
|
|
||||||
import { getNodeType } from '../helpers/getNodeType'
|
import { getNodeType } from '../helpers/getNodeType'
|
||||||
import { getSplittedAttributes } from '../helpers/getSplittedAttributes'
|
import { getSplittedAttributes } from '../helpers/getSplittedAttributes'
|
||||||
@ -17,7 +14,7 @@ declare module '@tiptap/core' {
|
|||||||
/**
|
/**
|
||||||
* Splits one list item into two list items.
|
* 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
|
// @ts-ignore
|
||||||
// eslint-disable-next-line
|
// 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)) {
|
if ((node && node.isBlock) || $from.depth < 2 || !$from.sameParent($to)) {
|
||||||
return false
|
return false
|
||||||
@ -50,8 +47,8 @@ export const splitListItem: RawCommands['splitListItem'] = typeOrName => ({
|
|||||||
// command handle lifting.
|
// command handle lifting.
|
||||||
if (
|
if (
|
||||||
$from.depth === 2
|
$from.depth === 2
|
||||||
|| $from.node(-3).type !== type
|
|| $from.node(-3).type !== type
|
||||||
|| $from.index(-2) !== $from.node(-2).childCount - 1
|
|| $from.index(-2) !== $from.node(-2).childCount - 1
|
||||||
) {
|
) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -59,11 +56,7 @@ export const splitListItem: RawCommands['splitListItem'] = typeOrName => ({
|
|||||||
if (dispatch) {
|
if (dispatch) {
|
||||||
let wrap = Fragment.empty
|
let wrap = Fragment.empty
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
const depthBefore = $from.index(-1)
|
const depthBefore = $from.index(-1) ? 1 : $from.index(-2) ? 2 : 3
|
||||||
? 1
|
|
||||||
: $from.index(-2)
|
|
||||||
? 2
|
|
||||||
: 3
|
|
||||||
|
|
||||||
// Build a fragment containing empty versions of the structure
|
// Build a fragment containing empty versions of the structure
|
||||||
// from the outer list item to the parent node of the cursor
|
// 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
|
// eslint-disable-next-line
|
||||||
const depthAfter = $from.indexAfter(-1) < $from.node(-2).childCount
|
const depthAfter = $from.indexAfter(-1) < $from.node(-2).childCount ? 1 : $from.indexAfter(-2) < $from.node(-3).childCount ? 2 : 3
|
||||||
? 1
|
|
||||||
: $from.indexAfter(-2) < $from.node(-3).childCount
|
|
||||||
? 2
|
|
||||||
: 3
|
|
||||||
|
|
||||||
// Add a second list item with an empty default start node
|
// Add a second list item with an empty default start node
|
||||||
const newNextTypeAttributes = getSplittedAttributes(
|
const newNextTypeAttributes = getSplittedAttributes(
|
||||||
@ -114,9 +103,7 @@ export const splitListItem: RawCommands['splitListItem'] = typeOrName => ({
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextType = $to.pos === $from.end()
|
const nextType = $to.pos === $from.end() ? grandParent.contentMatchAt(0).defaultType : null
|
||||||
? grandParent.contentMatchAt(0).defaultType
|
|
||||||
: null
|
|
||||||
|
|
||||||
const newTypeAttributes = getSplittedAttributes(
|
const newTypeAttributes = getSplittedAttributes(
|
||||||
extensionAttributes,
|
extensionAttributes,
|
||||||
@ -132,7 +119,10 @@ export const splitListItem: RawCommands['splitListItem'] = typeOrName => ({
|
|||||||
tr.delete($from.pos, $to.pos)
|
tr.delete($from.pos, $to.pos)
|
||||||
|
|
||||||
const types = nextType
|
const types = nextType
|
||||||
? [{ type, attrs: newTypeAttributes }, { type: nextType, attrs: newNextTypeAttributes }]
|
? [
|
||||||
|
{ type, attrs: newTypeAttributes },
|
||||||
|
{ type: nextType, attrs: newNextTypeAttributes },
|
||||||
|
]
|
||||||
: [{ type, attrs: newTypeAttributes }]
|
: [{ type, attrs: newTypeAttributes }]
|
||||||
|
|
||||||
if (!canSplit(tr.doc, $from.pos, 2)) {
|
if (!canSplit(tr.doc, $from.pos, 2)) {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { NodeType } from 'prosemirror-model'
|
import { NodeType } from '@tiptap/pm/model'
|
||||||
import { Transaction } from 'prosemirror-state'
|
import { Transaction } from '@tiptap/pm/state'
|
||||||
import { canJoin } from 'prosemirror-transform'
|
import { canJoin } from '@tiptap/pm/transform'
|
||||||
|
|
||||||
import { findParentNode } from '../helpers/findParentNode'
|
import { findParentNode } from '../helpers/findParentNode'
|
||||||
import { getNodeType } from '../helpers/getNodeType'
|
import { getNodeType } from '../helpers/getNodeType'
|
||||||
@ -21,8 +21,7 @@ const joinListBackwards = (tr: Transaction, listType: NodeType): boolean => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const nodeBefore = tr.doc.nodeAt(before)
|
const nodeBefore = tr.doc.nodeAt(before)
|
||||||
const canJoinBackwards = list.node.type === nodeBefore?.type
|
const canJoinBackwards = list.node.type === nodeBefore?.type && canJoin(tr.doc, list.pos)
|
||||||
&& canJoin(tr.doc, list.pos)
|
|
||||||
|
|
||||||
if (!canJoinBackwards) {
|
if (!canJoinBackwards) {
|
||||||
return true
|
return true
|
||||||
@ -47,8 +46,7 @@ const joinListForwards = (tr: Transaction, listType: NodeType): boolean => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const nodeAfter = tr.doc.nodeAt(after)
|
const nodeAfter = tr.doc.nodeAt(after)
|
||||||
const canJoinForwards = list.node.type === nodeAfter?.type
|
const canJoinForwards = list.node.type === nodeAfter?.type && canJoin(tr.doc, after)
|
||||||
&& canJoin(tr.doc, after)
|
|
||||||
|
|
||||||
if (!canJoinForwards) {
|
if (!canJoinForwards) {
|
||||||
return true
|
return true
|
||||||
@ -65,7 +63,10 @@ declare module '@tiptap/core' {
|
|||||||
/**
|
/**
|
||||||
* Toggle between different list types.
|
* 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
|
// change list type
|
||||||
if (
|
if (
|
||||||
isList(parentList.node.type.name, extensions)
|
isList(parentList.node.type.name, extensions)
|
||||||
&& listType.validContent(parentList.node.content)
|
&& listType.validContent(parentList.node.content)
|
||||||
&& dispatch
|
&& dispatch
|
||||||
) {
|
) {
|
||||||
return chain()
|
return chain()
|
||||||
.command(() => {
|
.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
|
// try to convert node to default node if needed
|
||||||
.command(() => {
|
.command(() => {
|
||||||
const canWrapInList = can().wrapInList(listType)
|
const canWrapInList = can().wrapInList(listType)
|
||||||
|
|
||||||
if (canWrapInList) {
|
if (canWrapInList) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return commands.clearNodes()
|
return commands.clearNodes()
|
||||||
})
|
})
|
||||||
.wrapInList(listType)
|
.wrapInList(listType)
|
||||||
.command(() => joinListBackwards(tr, listType))
|
.command(() => joinListBackwards(tr, listType))
|
||||||
.command(() => joinListForwards(tr, listType))
|
.command(() => joinListForwards(tr, listType))
|
||||||
.run()
|
.run()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { MarkType } from 'prosemirror-model'
|
import { MarkType } from '@tiptap/pm/model'
|
||||||
|
|
||||||
import { getMarkType } from '../helpers/getMarkType'
|
import { getMarkType } from '../helpers/getMarkType'
|
||||||
import { isMarkActive } from '../helpers/isMarkActive'
|
import { isMarkActive } from '../helpers/isMarkActive'
|
||||||
@ -17,9 +17,9 @@ declare module '@tiptap/core' {
|
|||||||
/**
|
/**
|
||||||
* Removes the mark even across the current selection. Defaults to `false`.
|
* 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 { getNodeType } from '../helpers/getNodeType'
|
||||||
import { isNodeActive } from '../helpers/isNodeActive'
|
import { isNodeActive } from '../helpers/isNodeActive'
|
||||||
@ -10,7 +10,11 @@ declare module '@tiptap/core' {
|
|||||||
/**
|
/**
|
||||||
* Toggle a node with another node.
|
* 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 { getNodeType } from '../helpers/getNodeType'
|
||||||
import { isNodeActive } from '../helpers/isNodeActive'
|
import { isNodeActive } from '../helpers/isNodeActive'
|
||||||
@ -10,7 +10,7 @@ declare module '@tiptap/core' {
|
|||||||
/**
|
/**
|
||||||
* Wraps nodes in another node, or removes an existing wrap.
|
* 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 { getMarkRange } from '../helpers/getMarkRange'
|
||||||
import { getMarkType } from '../helpers/getMarkType'
|
import { getMarkType } from '../helpers/getMarkType'
|
||||||
@ -16,9 +16,9 @@ declare module '@tiptap/core' {
|
|||||||
/**
|
/**
|
||||||
* Removes the mark even across the current selection. Defaults to `false`.
|
* 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 { getMarkType } from '../helpers/getMarkType'
|
||||||
import { getNodeType } from '../helpers/getNodeType'
|
import { getNodeType } from '../helpers/getNodeType'
|
||||||
@ -11,7 +11,10 @@ declare module '@tiptap/core' {
|
|||||||
/**
|
/**
|
||||||
* Update attributes of a node or mark.
|
* 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
|
let markType: MarkType | null = null
|
||||||
|
|
||||||
const schemaType = getSchemaTypeNameByName(
|
const schemaType = getSchemaTypeNameByName(
|
||||||
typeof typeOrName === 'string'
|
typeof typeOrName === 'string' ? typeOrName : typeOrName.name,
|
||||||
? typeOrName
|
|
||||||
: typeOrName.name,
|
|
||||||
state.schema,
|
state.schema,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -58,10 +59,14 @@ export const updateAttributes: RawCommands['updateAttributes'] = (typeOrName, at
|
|||||||
const trimmedFrom = Math.max(pos, from)
|
const trimmedFrom = Math.max(pos, from)
|
||||||
const trimmedTo = Math.min(pos + node.nodeSize, to)
|
const trimmedTo = Math.min(pos + node.nodeSize, to)
|
||||||
|
|
||||||
tr.addMark(trimmedFrom, trimmedTo, markType.create({
|
tr.addMark(
|
||||||
...mark.attrs,
|
trimmedFrom,
|
||||||
...attributes,
|
trimmedTo,
|
||||||
}))
|
markType.create({
|
||||||
|
...mark.attrs,
|
||||||
|
...attributes,
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { wrapIn as originalWrapIn } from 'prosemirror-commands'
|
import { wrapIn as originalWrapIn } from '@tiptap/pm/commands'
|
||||||
import { NodeType } from 'prosemirror-model'
|
import { NodeType } from '@tiptap/pm/model'
|
||||||
|
|
||||||
import { getNodeType } from '../helpers/getNodeType'
|
import { getNodeType } from '../helpers/getNodeType'
|
||||||
import { RawCommands } from '../types'
|
import { RawCommands } from '../types'
|
||||||
@ -10,7 +10,7 @@ declare module '@tiptap/core' {
|
|||||||
/**
|
/**
|
||||||
* Wraps nodes in another node.
|
* 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 { NodeType } from '@tiptap/pm/model'
|
||||||
import { wrapInList as originalWrapInList } from 'prosemirror-schema-list'
|
import { wrapInList as originalWrapInList } from '@tiptap/pm/schema-list'
|
||||||
|
|
||||||
import { getNodeType } from '../helpers/getNodeType'
|
import { getNodeType } from '../helpers/getNodeType'
|
||||||
import { RawCommands } from '../types'
|
import { RawCommands } from '../types'
|
||||||
@ -10,7 +10,7 @@ declare module '@tiptap/core' {
|
|||||||
/**
|
/**
|
||||||
* Wrap a node in a list.
|
* 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 { Extension } from '../Extension'
|
||||||
import { getTextBetween } from '../helpers/getTextBetween'
|
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'
|
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'
|
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 { CommandManager } from '../CommandManager'
|
||||||
import { Extension } from '../Extension'
|
import { Extension } from '../Extension'
|
||||||
@ -19,12 +19,7 @@ export const Keymap = Extension.create({
|
|||||||
const { pos, parent } = $anchor
|
const { pos, parent } = $anchor
|
||||||
const isAtStart = Selection.atStart(doc).from === pos
|
const isAtStart = Selection.atStart(doc).from === pos
|
||||||
|
|
||||||
if (
|
if (!empty || !isAtStart || !parent.type.isTextblock || parent.textContent.length) {
|
||||||
!empty
|
|
||||||
|| !isAtStart
|
|
||||||
|| !parent.type.isTextblock
|
|
||||||
|| parent.textContent.length
|
|
||||||
) {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Plugin, PluginKey } from 'prosemirror-state'
|
import { Plugin, PluginKey } from '@tiptap/pm/state'
|
||||||
|
|
||||||
import { Extension } from '../Extension'
|
import { Extension } from '../Extension'
|
||||||
|
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
import { Node as ProseMirrorNode } from 'prosemirror-model'
|
import { Node as ProseMirrorNode } from '@tiptap/pm/model'
|
||||||
import { Transaction } from 'prosemirror-state'
|
import { Transaction } from '@tiptap/pm/state'
|
||||||
import { Transform } from 'prosemirror-transform'
|
import { Transform } from '@tiptap/pm/transform'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new `Transform` based on all steps of the passed transactions.
|
* 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)
|
const transform = new Transform(oldDoc)
|
||||||
|
|
||||||
transactions.forEach(transaction => {
|
transactions.forEach(transaction => {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { EditorState, Transaction } from 'prosemirror-state'
|
import { EditorState, Transaction } from '@tiptap/pm/state'
|
||||||
|
|
||||||
export function createChainableState(config: {
|
export function createChainableState(config: {
|
||||||
transaction: Transaction,
|
transaction: Transaction
|
||||||
state: EditorState,
|
state: EditorState
|
||||||
}): EditorState {
|
}): EditorState {
|
||||||
const { state, transaction } = config
|
const { state, transaction } = config
|
||||||
let { selection } = transaction
|
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 { Content } from '../types'
|
||||||
import { createNodeFromContent } from './createNodeFromContent'
|
import { createNodeFromContent } from './createNodeFromContent'
|
||||||
|
@ -4,14 +4,14 @@ import {
|
|||||||
Node as ProseMirrorNode,
|
Node as ProseMirrorNode,
|
||||||
ParseOptions,
|
ParseOptions,
|
||||||
Schema,
|
Schema,
|
||||||
} from 'prosemirror-model'
|
} from '@tiptap/pm/model'
|
||||||
|
|
||||||
import { Content } from '../types'
|
import { Content } from '../types'
|
||||||
import { elementFromString } from '../utilities/elementFromString'
|
import { elementFromString } from '../utilities/elementFromString'
|
||||||
|
|
||||||
export type CreateNodeFromContentOptions = {
|
export type CreateNodeFromContentOptions = {
|
||||||
slice?: boolean,
|
slice?: boolean
|
||||||
parseOptions?: ParseOptions,
|
parseOptions?: ParseOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createNodeFromContent(
|
export function createNodeFromContent(
|
||||||
@ -33,13 +33,7 @@ export function createNodeFromContent(
|
|||||||
|
|
||||||
return schema.nodeFromJSON(content)
|
return schema.nodeFromJSON(content)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(
|
console.warn('[tiptap warn]: Invalid content.', 'Passed value:', content, 'Error:', error)
|
||||||
'[tiptap warn]: Invalid content.',
|
|
||||||
'Passed value:',
|
|
||||||
content,
|
|
||||||
'Error:',
|
|
||||||
error,
|
|
||||||
)
|
|
||||||
|
|
||||||
return createNodeFromContent('', schema, options)
|
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