Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

@tiptap/extensions

Package Overview
Dependencies
Maintainers
6
Versions
101
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@tiptap/extensions - npm Package Compare versions

Comparing version
3.17.1
to
3.18.0
+11
-3
dist/index.cjs

@@ -30,3 +30,4 @@ "use strict";

TrailingNode: () => TrailingNode,
UndoRedo: () => UndoRedo
UndoRedo: () => UndoRedo,
preparePlaceholderAttribute: () => preparePlaceholderAttribute
});

@@ -241,2 +242,6 @@ module.exports = __toCommonJS(index_exports);

var import_view2 = require("@tiptap/pm/view");
var DEFAULT_DATA_ATTRIBUTE = "placeholder";
function preparePlaceholderAttribute(attr) {
return attr.replace(/\s+/g, "-").replace(/[^a-zA-Z0-9-]/g, "").replace(/^[0-9-]+/, "").replace(/^-+/, "").toLowerCase();
}
var Placeholder = import_core5.Extension.create({

@@ -248,2 +253,3 @@ name: "placeholder",

emptyNodeClass: "is-empty",
dataAttribute: DEFAULT_DATA_ATTRIBUTE,
placeholder: "Write something \u2026",

@@ -256,2 +262,3 @@ showOnlyWhenEditable: true,

addProseMirrorPlugins() {
const dataAttribute = this.options.dataAttribute ? `data-${preparePlaceholderAttribute(this.options.dataAttribute)}` : `data-${DEFAULT_DATA_ATTRIBUTE}`;
return [

@@ -279,3 +286,3 @@ new import_state3.Plugin({

class: classes.join(" "),
"data-placeholder": typeof this.options.placeholder === "function" ? this.options.placeholder({
[dataAttribute]: typeof this.options.placeholder === "function" ? this.options.placeholder({
editor: this.editor,

@@ -429,4 +436,5 @@ node,

TrailingNode,
UndoRedo
UndoRedo,
preparePlaceholderAttribute
});
//# sourceMappingURL=index.cjs.map

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/index.ts","../src/character-count/character-count.ts","../src/drop-cursor/drop-cursor.ts","../src/focus/focus.ts","../src/gap-cursor/gap-cursor.ts","../src/placeholder/placeholder.ts","../src/selection/selection.ts","../src/trailing-node/trailing-node.ts","../src/undo-redo/undo-redo.ts"],"sourcesContent":["export * from './character-count/index.js'\nexport * from './drop-cursor/index.js'\nexport * from './focus/index.js'\nexport * from './gap-cursor/index.js'\nexport * from './placeholder/index.js'\nexport * from './selection/index.js'\nexport * from './trailing-node/index.js'\nexport * from './undo-redo/index.js'\n","import { Extension } from '@tiptap/core'\nimport type { Node as ProseMirrorNode } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\n\nexport interface CharacterCountOptions {\n /**\n * The maximum number of characters that should be allowed. Defaults to `0`.\n * @default null\n * @example 180\n */\n limit: number | null | undefined\n /**\n * The mode by which the size is calculated. If set to `textSize`, the textContent of the document is used.\n * If set to `nodeSize`, the nodeSize of the document is used.\n * @default 'textSize'\n * @example 'textSize'\n */\n mode: 'textSize' | 'nodeSize'\n /**\n * The text counter function to use. Defaults to a simple character count.\n * @default (text) => text.length\n * @example (text) => [...new Intl.Segmenter().segment(text)].length\n */\n textCounter: (text: string) => number\n /**\n * The word counter function to use. Defaults to a simple word count.\n * @default (text) => text.split(' ').filter(word => word !== '').length\n * @example (text) => text.split(/\\s+/).filter(word => word !== '').length\n */\n wordCounter: (text: string) => number\n}\n\nexport interface CharacterCountStorage {\n /**\n * Get the number of characters for the current document.\n * @param options The options for the character count. (optional)\n * @param options.node The node to get the characters from. Defaults to the current document.\n * @param options.mode The mode by which the size is calculated. If set to `textSize`, the textContent of the document is used.\n */\n characters: (options?: { node?: ProseMirrorNode; mode?: 'textSize' | 'nodeSize' }) => number\n\n /**\n * Get the number of words for the current document.\n * @param options The options for the character count. (optional)\n * @param options.node The node to get the words from. Defaults to the current document.\n */\n words: (options?: { node?: ProseMirrorNode }) => number\n}\n\ndeclare module '@tiptap/core' {\n interface Storage {\n characterCount: CharacterCountStorage\n }\n}\n\n/**\n * This extension allows you to count the characters and words of your document.\n * @see https://tiptap.dev/api/extensions/character-count\n */\nexport const CharacterCount = Extension.create<CharacterCountOptions, CharacterCountStorage>({\n name: 'characterCount',\n\n addOptions() {\n return {\n limit: null,\n mode: 'textSize',\n textCounter: text => text.length,\n wordCounter: text => text.split(' ').filter(word => word !== '').length,\n }\n },\n\n addStorage() {\n return {\n characters: () => 0,\n words: () => 0,\n }\n },\n\n onBeforeCreate() {\n this.storage.characters = options => {\n const node = options?.node || this.editor.state.doc\n const mode = options?.mode || this.options.mode\n\n if (mode === 'textSize') {\n const text = node.textBetween(0, node.content.size, undefined, ' ')\n\n return this.options.textCounter(text)\n }\n\n return node.nodeSize\n }\n\n this.storage.words = options => {\n const node = options?.node || this.editor.state.doc\n const text = node.textBetween(0, node.content.size, ' ', ' ')\n\n return this.options.wordCounter(text)\n }\n },\n\n addProseMirrorPlugins() {\n let initialEvaluationDone = false\n\n return [\n new Plugin({\n key: new PluginKey('characterCount'),\n appendTransaction: (transactions, oldState, newState) => {\n if (initialEvaluationDone) {\n return\n }\n\n const limit = this.options.limit\n\n if (limit === null || limit === undefined || limit === 0) {\n initialEvaluationDone = true\n return\n }\n\n const initialContentSize = this.storage.characters({ node: newState.doc })\n\n if (initialContentSize > limit) {\n const over = initialContentSize - limit\n const from = 0\n const to = over\n\n console.warn(\n `[CharacterCount] Initial content exceeded limit of ${limit} characters. Content was automatically trimmed.`,\n )\n const tr = newState.tr.deleteRange(from, to)\n\n initialEvaluationDone = true\n return tr\n }\n\n initialEvaluationDone = true\n },\n filterTransaction: (transaction, state) => {\n const limit = this.options.limit\n\n // Nothing has changed or no limit is defined. Ignore it.\n if (!transaction.docChanged || limit === 0 || limit === null || limit === undefined) {\n return true\n }\n\n const oldSize = this.storage.characters({ node: state.doc })\n const newSize = this.storage.characters({ node: transaction.doc })\n\n // Everything is in the limit. Good.\n if (newSize <= limit) {\n return true\n }\n\n // The limit has already been exceeded but will be reduced.\n if (oldSize > limit && newSize > limit && newSize <= oldSize) {\n return true\n }\n\n // The limit has already been exceeded and will be increased further.\n if (oldSize > limit && newSize > limit && newSize > oldSize) {\n return false\n }\n\n const isPaste = transaction.getMeta('paste')\n\n // Block all exceeding transactions that were not pasted.\n if (!isPaste) {\n return false\n }\n\n // For pasted content, we try to remove the exceeding content.\n const pos = transaction.selection.$head.pos\n const over = newSize - limit\n const from = pos - over\n const to = pos\n\n // It’s probably a bad idea to mutate transactions within `filterTransaction`\n // but for now this is working fine.\n transaction.deleteRange(from, to)\n\n // In some situations, the limit will continue to be exceeded after trimming.\n // This happens e.g. when truncating within a complex node (e.g. table)\n // and ProseMirror has to close this node again.\n // If this is the case, we prevent the transaction completely.\n const updatedSize = this.storage.characters({ node: transaction.doc })\n\n if (updatedSize > limit) {\n return false\n }\n\n return true\n },\n }),\n ]\n },\n})\n","import { Extension } from '@tiptap/core'\nimport { dropCursor } from '@tiptap/pm/dropcursor'\n\nexport interface DropcursorOptions {\n /**\n * The color of the drop cursor. Use `false` to apply no color and rely only on class.\n * @default 'currentColor'\n * @example 'red'\n */\n color?: string | false\n\n /**\n * The width of the drop cursor\n * @default 1\n * @example 2\n */\n width: number | undefined\n\n /**\n * The class of the drop cursor\n * @default undefined\n * @example 'drop-cursor'\n */\n class: string | undefined\n}\n\n/**\n * This extension allows you to add a drop cursor to your editor.\n * A drop cursor is a line that appears when you drag and drop content\n * in-between nodes.\n * @see https://tiptap.dev/api/extensions/dropcursor\n */\nexport const Dropcursor = Extension.create<DropcursorOptions>({\n name: 'dropCursor',\n\n addOptions() {\n return {\n color: 'currentColor',\n width: 1,\n class: undefined,\n }\n },\n\n addProseMirrorPlugins() {\n return [dropCursor(this.options)]\n },\n})\n","import { Extension } from '@tiptap/core'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet } from '@tiptap/pm/view'\n\nexport interface FocusOptions {\n /**\n * The class name that should be added to the focused node.\n * @default 'has-focus'\n * @example 'is-focused'\n */\n className: string\n\n /**\n * The mode by which the focused node is determined.\n * - All: All nodes are marked as focused.\n * - Deepest: Only the deepest node is marked as focused.\n * - Shallowest: Only the shallowest node is marked as focused.\n *\n * @default 'all'\n * @example 'deepest'\n * @example 'shallowest'\n */\n mode: 'all' | 'deepest' | 'shallowest'\n}\n\n/**\n * This extension allows you to add a class to the focused node.\n * @see https://www.tiptap.dev/api/extensions/focus\n */\nexport const Focus = Extension.create<FocusOptions>({\n name: 'focus',\n\n addOptions() {\n return {\n className: 'has-focus',\n mode: 'all',\n }\n },\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: new PluginKey('focus'),\n props: {\n decorations: ({ doc, selection }) => {\n const { isEditable, isFocused } = this.editor\n const { anchor } = selection\n const decorations: Decoration[] = []\n\n if (!isEditable || !isFocused) {\n return DecorationSet.create(doc, [])\n }\n\n // Maximum Levels\n let maxLevels = 0\n\n if (this.options.mode === 'deepest') {\n doc.descendants((node, pos) => {\n if (node.isText) {\n return\n }\n\n const isCurrent = anchor >= pos && anchor <= pos + node.nodeSize - 1\n\n if (!isCurrent) {\n return false\n }\n\n maxLevels += 1\n })\n }\n\n // Loop through current\n let currentLevel = 0\n\n doc.descendants((node, pos) => {\n if (node.isText) {\n return false\n }\n\n const isCurrent = anchor >= pos && anchor <= pos + node.nodeSize - 1\n\n if (!isCurrent) {\n return false\n }\n\n currentLevel += 1\n\n const outOfScope =\n (this.options.mode === 'deepest' && maxLevels - currentLevel > 0) ||\n (this.options.mode === 'shallowest' && currentLevel > 1)\n\n if (outOfScope) {\n return this.options.mode === 'deepest'\n }\n\n decorations.push(\n Decoration.node(pos, pos + node.nodeSize, {\n class: this.options.className,\n }),\n )\n })\n\n return DecorationSet.create(doc, decorations)\n },\n },\n }),\n ]\n },\n})\n","import type { ParentConfig } from '@tiptap/core'\nimport { callOrReturn, Extension, getExtensionField } from '@tiptap/core'\nimport { gapCursor } from '@tiptap/pm/gapcursor'\n\ndeclare module '@tiptap/core' {\n interface NodeConfig<Options, Storage> {\n /**\n * A function to determine whether the gap cursor is allowed at the current position. Must return `true` or `false`.\n * @default null\n */\n allowGapCursor?:\n | boolean\n | null\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options>>['allowGapCursor']\n }) => boolean | null)\n }\n}\n\n/**\n * This extension allows you to add a gap cursor to your editor.\n * A gap cursor is a cursor that appears when you click on a place\n * where no content is present, for example inbetween nodes.\n * @see https://tiptap.dev/api/extensions/gapcursor\n */\nexport const Gapcursor = Extension.create({\n name: 'gapCursor',\n\n addProseMirrorPlugins() {\n return [gapCursor()]\n },\n\n extendNodeSchema(extension) {\n const context = {\n name: extension.name,\n options: extension.options,\n storage: extension.storage,\n }\n\n return {\n allowGapCursor: callOrReturn(getExtensionField(extension, 'allowGapCursor', context)) ?? null,\n }\n },\n})\n","import type { Editor } from '@tiptap/core'\nimport { Extension, isNodeEmpty } from '@tiptap/core'\nimport type { Node as ProsemirrorNode } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet } from '@tiptap/pm/view'\n\nexport interface PlaceholderOptions {\n /**\n * **The class name for the empty editor**\n * @default 'is-editor-empty'\n */\n emptyEditorClass: string\n\n /**\n * **The class name for empty nodes**\n * @default 'is-empty'\n */\n emptyNodeClass: string\n\n /**\n * **The placeholder content**\n *\n * You can use a function to return a dynamic placeholder or a string.\n * @default 'Write something …'\n */\n placeholder:\n | ((PlaceholderProps: { editor: Editor; node: ProsemirrorNode; pos: number; hasAnchor: boolean }) => string)\n | string\n\n /**\n * **Checks if the placeholder should be only shown when the editor is editable.**\n *\n * If true, the placeholder will only be shown when the editor is editable.\n * If false, the placeholder will always be shown.\n * @default true\n */\n showOnlyWhenEditable: boolean\n\n /**\n * **Checks if the placeholder should be only shown when the current node is empty.**\n *\n * If true, the placeholder will only be shown when the current node is empty.\n * If false, the placeholder will be shown when any node is empty.\n * @default true\n */\n showOnlyCurrent: boolean\n\n /**\n * **Controls if the placeholder should be shown for all descendents.**\n *\n * If true, the placeholder will be shown for all descendents.\n * If false, the placeholder will only be shown for the current node.\n * @default false\n */\n includeChildren: boolean\n}\n\n/**\n * This extension allows you to add a placeholder to your editor.\n * A placeholder is a text that appears when the editor or a node is empty.\n * @see https://www.tiptap.dev/api/extensions/placeholder\n */\nexport const Placeholder = Extension.create<PlaceholderOptions>({\n name: 'placeholder',\n\n addOptions() {\n return {\n emptyEditorClass: 'is-editor-empty',\n emptyNodeClass: 'is-empty',\n placeholder: 'Write something …',\n showOnlyWhenEditable: true,\n showOnlyCurrent: true,\n includeChildren: false,\n }\n },\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: new PluginKey('placeholder'),\n props: {\n decorations: ({ doc, selection }) => {\n const active = this.editor.isEditable || !this.options.showOnlyWhenEditable\n const { anchor } = selection\n const decorations: Decoration[] = []\n\n if (!active) {\n return null\n }\n\n const isEmptyDoc = this.editor.isEmpty\n\n doc.descendants((node, pos) => {\n const hasAnchor = anchor >= pos && anchor <= pos + node.nodeSize\n const isEmpty = !node.isLeaf && isNodeEmpty(node)\n\n if ((hasAnchor || !this.options.showOnlyCurrent) && isEmpty) {\n const classes = [this.options.emptyNodeClass]\n\n if (isEmptyDoc) {\n classes.push(this.options.emptyEditorClass)\n }\n\n const decoration = Decoration.node(pos, pos + node.nodeSize, {\n class: classes.join(' '),\n 'data-placeholder':\n typeof this.options.placeholder === 'function'\n ? this.options.placeholder({\n editor: this.editor,\n node,\n pos,\n hasAnchor,\n })\n : this.options.placeholder,\n })\n\n decorations.push(decoration)\n }\n\n return this.options.includeChildren\n })\n\n return DecorationSet.create(doc, decorations)\n },\n },\n }),\n ]\n },\n})\n","import { Extension, isNodeSelection } from '@tiptap/core'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet } from '@tiptap/pm/view'\n\nexport type SelectionOptions = {\n /**\n * The class name that should be added to the selected text.\n * @default 'selection'\n * @example 'is-selected'\n */\n className: string\n}\n\n/**\n * This extension allows you to add a class to the selected text.\n * @see https://www.tiptap.dev/api/extensions/selection\n */\nexport const Selection = Extension.create<SelectionOptions>({\n name: 'selection',\n\n addOptions() {\n return {\n className: 'selection',\n }\n },\n\n addProseMirrorPlugins() {\n const { editor, options } = this\n\n return [\n new Plugin({\n key: new PluginKey('selection'),\n props: {\n decorations(state) {\n if (\n state.selection.empty ||\n editor.isFocused ||\n !editor.isEditable ||\n isNodeSelection(state.selection) ||\n editor.view.dragging\n ) {\n return null\n }\n\n return DecorationSet.create(state.doc, [\n Decoration.inline(state.selection.from, state.selection.to, {\n class: options.className,\n }),\n ])\n },\n },\n }),\n ]\n },\n})\n\nexport default Selection\n","import { Extension } from '@tiptap/core'\nimport type { Node, NodeType } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\n\nfunction nodeEqualsType({ types, node }: { types: NodeType | NodeType[]; node: Node | null | undefined }) {\n return (node && Array.isArray(types) && types.includes(node.type)) || node?.type === types\n}\n\n/**\n * Extension based on:\n * - https://github.com/ueberdosis/tiptap/blob/v1/packages/tiptap-extensions/src/extensions/TrailingNode.js\n * - https://github.com/remirror/remirror/blob/e0f1bec4a1e8073ce8f5500d62193e52321155b9/packages/prosemirror-trailing-node/src/trailing-node-plugin.ts\n */\n\nexport interface TrailingNodeOptions {\n /**\n * The node type that should be inserted at the end of the document.\n * @note the node will always be added to the `notAfter` lists to\n * prevent an infinite loop.\n * @default undefined\n */\n node?: string\n /**\n * The node types after which the trailing node should not be inserted.\n * @default ['paragraph']\n */\n notAfter?: string | string[]\n}\n\n/**\n * This extension allows you to add an extra node at the end of the document.\n * @see https://www.tiptap.dev/api/extensions/trailing-node\n */\nexport const TrailingNode = Extension.create<TrailingNodeOptions>({\n name: 'trailingNode',\n\n addOptions() {\n return {\n node: undefined,\n notAfter: [],\n }\n },\n\n addProseMirrorPlugins() {\n const plugin = new PluginKey(this.name)\n const defaultNode =\n this.options.node || this.editor.schema.topNodeType.contentMatch.defaultType?.name || 'paragraph'\n\n const disabledNodes = Object.entries(this.editor.schema.nodes)\n .map(([, value]) => value)\n .filter(node => (this.options.notAfter || []).concat(defaultNode).includes(node.name))\n\n return [\n new Plugin({\n key: plugin,\n appendTransaction: (_, __, state) => {\n const { doc, tr, schema } = state\n const shouldInsertNodeAtEnd = plugin.getState(state)\n const endPosition = doc.content.size\n const type = schema.nodes[defaultNode]\n\n if (!shouldInsertNodeAtEnd) {\n return\n }\n\n return tr.insert(endPosition, type.create())\n },\n state: {\n init: (_, state) => {\n const lastNode = state.tr.doc.lastChild\n\n return !nodeEqualsType({ node: lastNode, types: disabledNodes })\n },\n apply: (tr, value) => {\n if (!tr.docChanged) {\n return value\n }\n\n // Ignore transactions from UniqueID extension to prevent infinite loops\n // when UniqueID adds IDs to newly inserted trailing nodes\n if (tr.getMeta('__uniqueIDTransaction')) {\n return value\n }\n\n const lastNode = tr.doc.lastChild\n\n return !nodeEqualsType({ node: lastNode, types: disabledNodes })\n },\n },\n }),\n ]\n },\n})\n","import { Extension } from '@tiptap/core'\nimport { history, redo, undo } from '@tiptap/pm/history'\n\nexport interface UndoRedoOptions {\n /**\n * The amount of history events that are collected before the oldest events are discarded.\n * @default 100\n * @example 50\n */\n depth: number\n\n /**\n * The delay (in milliseconds) between changes after which a new group should be started.\n * @default 500\n * @example 1000\n */\n newGroupDelay: number\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n undoRedo: {\n /**\n * Undo recent changes\n * @example editor.commands.undo()\n */\n undo: () => ReturnType\n /**\n * Reapply reverted changes\n * @example editor.commands.redo()\n */\n redo: () => ReturnType\n }\n }\n}\n\n/**\n * This extension allows you to undo and redo recent changes.\n * @see https://www.tiptap.dev/api/extensions/undo-redo\n *\n * **Important**: If the `@tiptap/extension-collaboration` package is used, make sure to remove\n * the `undo-redo` extension, as it is not compatible with the `collaboration` extension.\n *\n * `@tiptap/extension-collaboration` uses its own history implementation.\n */\nexport const UndoRedo = Extension.create<UndoRedoOptions>({\n name: 'undoRedo',\n\n addOptions() {\n return {\n depth: 100,\n newGroupDelay: 500,\n }\n },\n\n addCommands() {\n return {\n undo:\n () =>\n ({ state, dispatch }) => {\n return undo(state, dispatch)\n },\n redo:\n () =>\n ({ state, dispatch }) => {\n return redo(state, dispatch)\n },\n }\n },\n\n addProseMirrorPlugins() {\n return [history(this.options)]\n },\n\n addKeyboardShortcuts() {\n return {\n 'Mod-z': () => this.editor.commands.undo(),\n 'Shift-Mod-z': () => this.editor.commands.redo(),\n 'Mod-y': () => this.editor.commands.redo(),\n\n // Russian keyboard layouts\n 'Mod-я': () => this.editor.commands.undo(),\n 'Shift-Mod-я': () => this.editor.commands.redo(),\n }\n },\n})\n"],"mappings":"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","names":["import_core","import_core","import_state","import_core","import_core","import_state","import_view","import_core","import_state","import_view","import_core","import_state","import_core"]}
{"version":3,"sources":["../src/index.ts","../src/character-count/character-count.ts","../src/drop-cursor/drop-cursor.ts","../src/focus/focus.ts","../src/gap-cursor/gap-cursor.ts","../src/placeholder/placeholder.ts","../src/selection/selection.ts","../src/trailing-node/trailing-node.ts","../src/undo-redo/undo-redo.ts"],"sourcesContent":["export * from './character-count/index.js'\nexport * from './drop-cursor/index.js'\nexport * from './focus/index.js'\nexport * from './gap-cursor/index.js'\nexport * from './placeholder/index.js'\nexport * from './selection/index.js'\nexport * from './trailing-node/index.js'\nexport * from './undo-redo/index.js'\n","import { Extension } from '@tiptap/core'\nimport type { Node as ProseMirrorNode } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\n\nexport interface CharacterCountOptions {\n /**\n * The maximum number of characters that should be allowed. Defaults to `0`.\n * @default null\n * @example 180\n */\n limit: number | null | undefined\n /**\n * The mode by which the size is calculated. If set to `textSize`, the textContent of the document is used.\n * If set to `nodeSize`, the nodeSize of the document is used.\n * @default 'textSize'\n * @example 'textSize'\n */\n mode: 'textSize' | 'nodeSize'\n /**\n * The text counter function to use. Defaults to a simple character count.\n * @default (text) => text.length\n * @example (text) => [...new Intl.Segmenter().segment(text)].length\n */\n textCounter: (text: string) => number\n /**\n * The word counter function to use. Defaults to a simple word count.\n * @default (text) => text.split(' ').filter(word => word !== '').length\n * @example (text) => text.split(/\\s+/).filter(word => word !== '').length\n */\n wordCounter: (text: string) => number\n}\n\nexport interface CharacterCountStorage {\n /**\n * Get the number of characters for the current document.\n * @param options The options for the character count. (optional)\n * @param options.node The node to get the characters from. Defaults to the current document.\n * @param options.mode The mode by which the size is calculated. If set to `textSize`, the textContent of the document is used.\n */\n characters: (options?: { node?: ProseMirrorNode; mode?: 'textSize' | 'nodeSize' }) => number\n\n /**\n * Get the number of words for the current document.\n * @param options The options for the character count. (optional)\n * @param options.node The node to get the words from. Defaults to the current document.\n */\n words: (options?: { node?: ProseMirrorNode }) => number\n}\n\ndeclare module '@tiptap/core' {\n interface Storage {\n characterCount: CharacterCountStorage\n }\n}\n\n/**\n * This extension allows you to count the characters and words of your document.\n * @see https://tiptap.dev/api/extensions/character-count\n */\nexport const CharacterCount = Extension.create<CharacterCountOptions, CharacterCountStorage>({\n name: 'characterCount',\n\n addOptions() {\n return {\n limit: null,\n mode: 'textSize',\n textCounter: text => text.length,\n wordCounter: text => text.split(' ').filter(word => word !== '').length,\n }\n },\n\n addStorage() {\n return {\n characters: () => 0,\n words: () => 0,\n }\n },\n\n onBeforeCreate() {\n this.storage.characters = options => {\n const node = options?.node || this.editor.state.doc\n const mode = options?.mode || this.options.mode\n\n if (mode === 'textSize') {\n const text = node.textBetween(0, node.content.size, undefined, ' ')\n\n return this.options.textCounter(text)\n }\n\n return node.nodeSize\n }\n\n this.storage.words = options => {\n const node = options?.node || this.editor.state.doc\n const text = node.textBetween(0, node.content.size, ' ', ' ')\n\n return this.options.wordCounter(text)\n }\n },\n\n addProseMirrorPlugins() {\n let initialEvaluationDone = false\n\n return [\n new Plugin({\n key: new PluginKey('characterCount'),\n appendTransaction: (transactions, oldState, newState) => {\n if (initialEvaluationDone) {\n return\n }\n\n const limit = this.options.limit\n\n if (limit === null || limit === undefined || limit === 0) {\n initialEvaluationDone = true\n return\n }\n\n const initialContentSize = this.storage.characters({ node: newState.doc })\n\n if (initialContentSize > limit) {\n const over = initialContentSize - limit\n const from = 0\n const to = over\n\n console.warn(\n `[CharacterCount] Initial content exceeded limit of ${limit} characters. Content was automatically trimmed.`,\n )\n const tr = newState.tr.deleteRange(from, to)\n\n initialEvaluationDone = true\n return tr\n }\n\n initialEvaluationDone = true\n },\n filterTransaction: (transaction, state) => {\n const limit = this.options.limit\n\n // Nothing has changed or no limit is defined. Ignore it.\n if (!transaction.docChanged || limit === 0 || limit === null || limit === undefined) {\n return true\n }\n\n const oldSize = this.storage.characters({ node: state.doc })\n const newSize = this.storage.characters({ node: transaction.doc })\n\n // Everything is in the limit. Good.\n if (newSize <= limit) {\n return true\n }\n\n // The limit has already been exceeded but will be reduced.\n if (oldSize > limit && newSize > limit && newSize <= oldSize) {\n return true\n }\n\n // The limit has already been exceeded and will be increased further.\n if (oldSize > limit && newSize > limit && newSize > oldSize) {\n return false\n }\n\n const isPaste = transaction.getMeta('paste')\n\n // Block all exceeding transactions that were not pasted.\n if (!isPaste) {\n return false\n }\n\n // For pasted content, we try to remove the exceeding content.\n const pos = transaction.selection.$head.pos\n const over = newSize - limit\n const from = pos - over\n const to = pos\n\n // It’s probably a bad idea to mutate transactions within `filterTransaction`\n // but for now this is working fine.\n transaction.deleteRange(from, to)\n\n // In some situations, the limit will continue to be exceeded after trimming.\n // This happens e.g. when truncating within a complex node (e.g. table)\n // and ProseMirror has to close this node again.\n // If this is the case, we prevent the transaction completely.\n const updatedSize = this.storage.characters({ node: transaction.doc })\n\n if (updatedSize > limit) {\n return false\n }\n\n return true\n },\n }),\n ]\n },\n})\n","import { Extension } from '@tiptap/core'\nimport { dropCursor } from '@tiptap/pm/dropcursor'\n\nexport interface DropcursorOptions {\n /**\n * The color of the drop cursor. Use `false` to apply no color and rely only on class.\n * @default 'currentColor'\n * @example 'red'\n */\n color?: string | false\n\n /**\n * The width of the drop cursor\n * @default 1\n * @example 2\n */\n width: number | undefined\n\n /**\n * The class of the drop cursor\n * @default undefined\n * @example 'drop-cursor'\n */\n class: string | undefined\n}\n\n/**\n * This extension allows you to add a drop cursor to your editor.\n * A drop cursor is a line that appears when you drag and drop content\n * in-between nodes.\n * @see https://tiptap.dev/api/extensions/dropcursor\n */\nexport const Dropcursor = Extension.create<DropcursorOptions>({\n name: 'dropCursor',\n\n addOptions() {\n return {\n color: 'currentColor',\n width: 1,\n class: undefined,\n }\n },\n\n addProseMirrorPlugins() {\n return [dropCursor(this.options)]\n },\n})\n","import { Extension } from '@tiptap/core'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet } from '@tiptap/pm/view'\n\nexport interface FocusOptions {\n /**\n * The class name that should be added to the focused node.\n * @default 'has-focus'\n * @example 'is-focused'\n */\n className: string\n\n /**\n * The mode by which the focused node is determined.\n * - All: All nodes are marked as focused.\n * - Deepest: Only the deepest node is marked as focused.\n * - Shallowest: Only the shallowest node is marked as focused.\n *\n * @default 'all'\n * @example 'deepest'\n * @example 'shallowest'\n */\n mode: 'all' | 'deepest' | 'shallowest'\n}\n\n/**\n * This extension allows you to add a class to the focused node.\n * @see https://www.tiptap.dev/api/extensions/focus\n */\nexport const Focus = Extension.create<FocusOptions>({\n name: 'focus',\n\n addOptions() {\n return {\n className: 'has-focus',\n mode: 'all',\n }\n },\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: new PluginKey('focus'),\n props: {\n decorations: ({ doc, selection }) => {\n const { isEditable, isFocused } = this.editor\n const { anchor } = selection\n const decorations: Decoration[] = []\n\n if (!isEditable || !isFocused) {\n return DecorationSet.create(doc, [])\n }\n\n // Maximum Levels\n let maxLevels = 0\n\n if (this.options.mode === 'deepest') {\n doc.descendants((node, pos) => {\n if (node.isText) {\n return\n }\n\n const isCurrent = anchor >= pos && anchor <= pos + node.nodeSize - 1\n\n if (!isCurrent) {\n return false\n }\n\n maxLevels += 1\n })\n }\n\n // Loop through current\n let currentLevel = 0\n\n doc.descendants((node, pos) => {\n if (node.isText) {\n return false\n }\n\n const isCurrent = anchor >= pos && anchor <= pos + node.nodeSize - 1\n\n if (!isCurrent) {\n return false\n }\n\n currentLevel += 1\n\n const outOfScope =\n (this.options.mode === 'deepest' && maxLevels - currentLevel > 0) ||\n (this.options.mode === 'shallowest' && currentLevel > 1)\n\n if (outOfScope) {\n return this.options.mode === 'deepest'\n }\n\n decorations.push(\n Decoration.node(pos, pos + node.nodeSize, {\n class: this.options.className,\n }),\n )\n })\n\n return DecorationSet.create(doc, decorations)\n },\n },\n }),\n ]\n },\n})\n","import type { ParentConfig } from '@tiptap/core'\nimport { callOrReturn, Extension, getExtensionField } from '@tiptap/core'\nimport { gapCursor } from '@tiptap/pm/gapcursor'\n\ndeclare module '@tiptap/core' {\n interface NodeConfig<Options, Storage> {\n /**\n * A function to determine whether the gap cursor is allowed at the current position. Must return `true` or `false`.\n * @default null\n */\n allowGapCursor?:\n | boolean\n | null\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options>>['allowGapCursor']\n }) => boolean | null)\n }\n}\n\n/**\n * This extension allows you to add a gap cursor to your editor.\n * A gap cursor is a cursor that appears when you click on a place\n * where no content is present, for example inbetween nodes.\n * @see https://tiptap.dev/api/extensions/gapcursor\n */\nexport const Gapcursor = Extension.create({\n name: 'gapCursor',\n\n addProseMirrorPlugins() {\n return [gapCursor()]\n },\n\n extendNodeSchema(extension) {\n const context = {\n name: extension.name,\n options: extension.options,\n storage: extension.storage,\n }\n\n return {\n allowGapCursor: callOrReturn(getExtensionField(extension, 'allowGapCursor', context)) ?? null,\n }\n },\n})\n","import type { Editor } from '@tiptap/core'\nimport { Extension, isNodeEmpty } from '@tiptap/core'\nimport type { Node as ProsemirrorNode } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet } from '@tiptap/pm/view'\n\n/**\n * The default data attribute label\n */\nconst DEFAULT_DATA_ATTRIBUTE = 'placeholder'\n\n/**\n * Prepares the placeholder attribute by ensuring it is properly formatted.\n * @param attr - The placeholder attribute string.\n * @returns The prepared placeholder attribute string.\n */\nexport function preparePlaceholderAttribute(attr: string): string {\n return (\n attr\n // replace whitespace with dashes\n .replace(/\\s+/g, '-')\n // replace non-alphanumeric characters\n // or special chars like $, %, &, etc.\n // but not dashes\n .replace(/[^a-zA-Z0-9-]/g, '')\n // and replace any numeric character at the start\n .replace(/^[0-9-]+/, '')\n // and finally replace any stray, leading dashes\n .replace(/^-+/, '')\n .toLowerCase()\n )\n}\n\nexport interface PlaceholderOptions {\n /**\n * **The class name for the empty editor**\n * @default 'is-editor-empty'\n */\n emptyEditorClass: string\n\n /**\n * **The class name for empty nodes**\n * @default 'is-empty'\n */\n emptyNodeClass: string\n\n /**\n * **The data-attribute used for the placeholder label**\n * Will be prepended with `data-` and converted to kebab-case and cleaned of special characters.\n * @default 'placeholder'\n */\n dataAttribute: string\n\n /**\n * **The placeholder content**\n *\n * You can use a function to return a dynamic placeholder or a string.\n * @default 'Write something …'\n */\n placeholder:\n | ((PlaceholderProps: { editor: Editor; node: ProsemirrorNode; pos: number; hasAnchor: boolean }) => string)\n | string\n\n /**\n * **Checks if the placeholder should be only shown when the editor is editable.**\n *\n * If true, the placeholder will only be shown when the editor is editable.\n * If false, the placeholder will always be shown.\n * @default true\n */\n showOnlyWhenEditable: boolean\n\n /**\n * **Checks if the placeholder should be only shown when the current node is empty.**\n *\n * If true, the placeholder will only be shown when the current node is empty.\n * If false, the placeholder will be shown when any node is empty.\n * @default true\n */\n showOnlyCurrent: boolean\n\n /**\n * **Controls if the placeholder should be shown for all descendents.**\n *\n * If true, the placeholder will be shown for all descendents.\n * If false, the placeholder will only be shown for the current node.\n * @default false\n */\n includeChildren: boolean\n}\n\n/**\n * This extension allows you to add a placeholder to your editor.\n * A placeholder is a text that appears when the editor or a node is empty.\n * @see https://www.tiptap.dev/api/extensions/placeholder\n */\nexport const Placeholder = Extension.create<PlaceholderOptions>({\n name: 'placeholder',\n\n addOptions() {\n return {\n emptyEditorClass: 'is-editor-empty',\n emptyNodeClass: 'is-empty',\n dataAttribute: DEFAULT_DATA_ATTRIBUTE,\n placeholder: 'Write something …',\n showOnlyWhenEditable: true,\n showOnlyCurrent: true,\n includeChildren: false,\n }\n },\n\n addProseMirrorPlugins() {\n const dataAttribute = this.options.dataAttribute\n ? `data-${preparePlaceholderAttribute(this.options.dataAttribute)}`\n : `data-${DEFAULT_DATA_ATTRIBUTE}`\n\n return [\n new Plugin({\n key: new PluginKey('placeholder'),\n props: {\n decorations: ({ doc, selection }) => {\n const active = this.editor.isEditable || !this.options.showOnlyWhenEditable\n const { anchor } = selection\n const decorations: Decoration[] = []\n\n if (!active) {\n return null\n }\n\n const isEmptyDoc = this.editor.isEmpty\n\n doc.descendants((node, pos) => {\n const hasAnchor = anchor >= pos && anchor <= pos + node.nodeSize\n const isEmpty = !node.isLeaf && isNodeEmpty(node)\n\n if ((hasAnchor || !this.options.showOnlyCurrent) && isEmpty) {\n const classes = [this.options.emptyNodeClass]\n\n if (isEmptyDoc) {\n classes.push(this.options.emptyEditorClass)\n }\n\n const decoration = Decoration.node(pos, pos + node.nodeSize, {\n class: classes.join(' '),\n [dataAttribute]:\n typeof this.options.placeholder === 'function'\n ? this.options.placeholder({\n editor: this.editor,\n node,\n pos,\n hasAnchor,\n })\n : this.options.placeholder,\n })\n\n decorations.push(decoration)\n }\n\n return this.options.includeChildren\n })\n\n return DecorationSet.create(doc, decorations)\n },\n },\n }),\n ]\n },\n})\n","import { Extension, isNodeSelection } from '@tiptap/core'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet } from '@tiptap/pm/view'\n\nexport type SelectionOptions = {\n /**\n * The class name that should be added to the selected text.\n * @default 'selection'\n * @example 'is-selected'\n */\n className: string\n}\n\n/**\n * This extension allows you to add a class to the selected text.\n * @see https://www.tiptap.dev/api/extensions/selection\n */\nexport const Selection = Extension.create<SelectionOptions>({\n name: 'selection',\n\n addOptions() {\n return {\n className: 'selection',\n }\n },\n\n addProseMirrorPlugins() {\n const { editor, options } = this\n\n return [\n new Plugin({\n key: new PluginKey('selection'),\n props: {\n decorations(state) {\n if (\n state.selection.empty ||\n editor.isFocused ||\n !editor.isEditable ||\n isNodeSelection(state.selection) ||\n editor.view.dragging\n ) {\n return null\n }\n\n return DecorationSet.create(state.doc, [\n Decoration.inline(state.selection.from, state.selection.to, {\n class: options.className,\n }),\n ])\n },\n },\n }),\n ]\n },\n})\n\nexport default Selection\n","import { Extension } from '@tiptap/core'\nimport type { Node, NodeType } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\n\nfunction nodeEqualsType({ types, node }: { types: NodeType | NodeType[]; node: Node | null | undefined }) {\n return (node && Array.isArray(types) && types.includes(node.type)) || node?.type === types\n}\n\n/**\n * Extension based on:\n * - https://github.com/ueberdosis/tiptap/blob/v1/packages/tiptap-extensions/src/extensions/TrailingNode.js\n * - https://github.com/remirror/remirror/blob/e0f1bec4a1e8073ce8f5500d62193e52321155b9/packages/prosemirror-trailing-node/src/trailing-node-plugin.ts\n */\n\nexport interface TrailingNodeOptions {\n /**\n * The node type that should be inserted at the end of the document.\n * @note the node will always be added to the `notAfter` lists to\n * prevent an infinite loop.\n * @default undefined\n */\n node?: string\n /**\n * The node types after which the trailing node should not be inserted.\n * @default ['paragraph']\n */\n notAfter?: string | string[]\n}\n\n/**\n * This extension allows you to add an extra node at the end of the document.\n * @see https://www.tiptap.dev/api/extensions/trailing-node\n */\nexport const TrailingNode = Extension.create<TrailingNodeOptions>({\n name: 'trailingNode',\n\n addOptions() {\n return {\n node: undefined,\n notAfter: [],\n }\n },\n\n addProseMirrorPlugins() {\n const plugin = new PluginKey(this.name)\n const defaultNode =\n this.options.node || this.editor.schema.topNodeType.contentMatch.defaultType?.name || 'paragraph'\n\n const disabledNodes = Object.entries(this.editor.schema.nodes)\n .map(([, value]) => value)\n .filter(node => (this.options.notAfter || []).concat(defaultNode).includes(node.name))\n\n return [\n new Plugin({\n key: plugin,\n appendTransaction: (_, __, state) => {\n const { doc, tr, schema } = state\n const shouldInsertNodeAtEnd = plugin.getState(state)\n const endPosition = doc.content.size\n const type = schema.nodes[defaultNode]\n\n if (!shouldInsertNodeAtEnd) {\n return\n }\n\n return tr.insert(endPosition, type.create())\n },\n state: {\n init: (_, state) => {\n const lastNode = state.tr.doc.lastChild\n\n return !nodeEqualsType({ node: lastNode, types: disabledNodes })\n },\n apply: (tr, value) => {\n if (!tr.docChanged) {\n return value\n }\n\n // Ignore transactions from UniqueID extension to prevent infinite loops\n // when UniqueID adds IDs to newly inserted trailing nodes\n if (tr.getMeta('__uniqueIDTransaction')) {\n return value\n }\n\n const lastNode = tr.doc.lastChild\n\n return !nodeEqualsType({ node: lastNode, types: disabledNodes })\n },\n },\n }),\n ]\n },\n})\n","import { Extension } from '@tiptap/core'\nimport { history, redo, undo } from '@tiptap/pm/history'\n\nexport interface UndoRedoOptions {\n /**\n * The amount of history events that are collected before the oldest events are discarded.\n * @default 100\n * @example 50\n */\n depth: number\n\n /**\n * The delay (in milliseconds) between changes after which a new group should be started.\n * @default 500\n * @example 1000\n */\n newGroupDelay: number\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n undoRedo: {\n /**\n * Undo recent changes\n * @example editor.commands.undo()\n */\n undo: () => ReturnType\n /**\n * Reapply reverted changes\n * @example editor.commands.redo()\n */\n redo: () => ReturnType\n }\n }\n}\n\n/**\n * This extension allows you to undo and redo recent changes.\n * @see https://www.tiptap.dev/api/extensions/undo-redo\n *\n * **Important**: If the `@tiptap/extension-collaboration` package is used, make sure to remove\n * the `undo-redo` extension, as it is not compatible with the `collaboration` extension.\n *\n * `@tiptap/extension-collaboration` uses its own history implementation.\n */\nexport const UndoRedo = Extension.create<UndoRedoOptions>({\n name: 'undoRedo',\n\n addOptions() {\n return {\n depth: 100,\n newGroupDelay: 500,\n }\n },\n\n addCommands() {\n return {\n undo:\n () =>\n ({ state, dispatch }) => {\n return undo(state, dispatch)\n },\n redo:\n () =>\n ({ state, dispatch }) => {\n return redo(state, dispatch)\n },\n }\n },\n\n addProseMirrorPlugins() {\n return [history(this.options)]\n },\n\n addKeyboardShortcuts() {\n return {\n 'Mod-z': () => this.editor.commands.undo(),\n 'Shift-Mod-z': () => this.editor.commands.redo(),\n 'Mod-y': () => this.editor.commands.redo(),\n\n // Russian keyboard layouts\n 'Mod-я': () => this.editor.commands.undo(),\n 'Shift-Mod-я': () => this.editor.commands.redo(),\n }\n },\n})\n"],"mappings":"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","names":["import_core","import_core","import_state","import_core","import_core","import_state","import_view","import_core","import_state","import_view","import_core","import_state","import_core"]}

@@ -137,2 +137,8 @@ import { Extension, ParentConfig, Editor } from '@tiptap/core';

/**
* Prepares the placeholder attribute by ensuring it is properly formatted.
* @param attr - The placeholder attribute string.
* @returns The prepared placeholder attribute string.
*/
declare function preparePlaceholderAttribute(attr: string): string;
interface PlaceholderOptions {

@@ -150,2 +156,8 @@ /**

/**
* **The data-attribute used for the placeholder label**
* Will be prepended with `data-` and converted to kebab-case and cleaned of special characters.
* @default 'placeholder'
*/
dataAttribute: string;
/**
* **The placeholder content**

@@ -274,2 +286,2 @@ *

export { CharacterCount, type CharacterCountOptions, type CharacterCountStorage, Dropcursor, type DropcursorOptions, Focus, type FocusOptions, Gapcursor, Placeholder, type PlaceholderOptions, Selection, type SelectionOptions, TrailingNode, type TrailingNodeOptions, UndoRedo, type UndoRedoOptions };
export { CharacterCount, type CharacterCountOptions, type CharacterCountStorage, Dropcursor, type DropcursorOptions, Focus, type FocusOptions, Gapcursor, Placeholder, type PlaceholderOptions, Selection, type SelectionOptions, TrailingNode, type TrailingNodeOptions, UndoRedo, type UndoRedoOptions, preparePlaceholderAttribute };

@@ -137,2 +137,8 @@ import { Extension, ParentConfig, Editor } from '@tiptap/core';

/**
* Prepares the placeholder attribute by ensuring it is properly formatted.
* @param attr - The placeholder attribute string.
* @returns The prepared placeholder attribute string.
*/
declare function preparePlaceholderAttribute(attr: string): string;
interface PlaceholderOptions {

@@ -150,2 +156,8 @@ /**

/**
* **The data-attribute used for the placeholder label**
* Will be prepended with `data-` and converted to kebab-case and cleaned of special characters.
* @default 'placeholder'
*/
dataAttribute: string;
/**
* **The placeholder content**

@@ -274,2 +286,2 @@ *

export { CharacterCount, type CharacterCountOptions, type CharacterCountStorage, Dropcursor, type DropcursorOptions, Focus, type FocusOptions, Gapcursor, Placeholder, type PlaceholderOptions, Selection, type SelectionOptions, TrailingNode, type TrailingNodeOptions, UndoRedo, type UndoRedoOptions };
export { CharacterCount, type CharacterCountOptions, type CharacterCountStorage, Dropcursor, type DropcursorOptions, Focus, type FocusOptions, Gapcursor, Placeholder, type PlaceholderOptions, Selection, type SelectionOptions, TrailingNode, type TrailingNodeOptions, UndoRedo, type UndoRedoOptions, preparePlaceholderAttribute };

@@ -207,2 +207,6 @@ // src/character-count/character-count.ts

import { Decoration as Decoration2, DecorationSet as DecorationSet2 } from "@tiptap/pm/view";
var DEFAULT_DATA_ATTRIBUTE = "placeholder";
function preparePlaceholderAttribute(attr) {
return attr.replace(/\s+/g, "-").replace(/[^a-zA-Z0-9-]/g, "").replace(/^[0-9-]+/, "").replace(/^-+/, "").toLowerCase();
}
var Placeholder = Extension5.create({

@@ -214,2 +218,3 @@ name: "placeholder",

emptyNodeClass: "is-empty",
dataAttribute: DEFAULT_DATA_ATTRIBUTE,
placeholder: "Write something \u2026",

@@ -222,2 +227,3 @@ showOnlyWhenEditable: true,

addProseMirrorPlugins() {
const dataAttribute = this.options.dataAttribute ? `data-${preparePlaceholderAttribute(this.options.dataAttribute)}` : `data-${DEFAULT_DATA_ATTRIBUTE}`;
return [

@@ -245,3 +251,3 @@ new Plugin3({

class: classes.join(" "),
"data-placeholder": typeof this.options.placeholder === "function" ? this.options.placeholder({
[dataAttribute]: typeof this.options.placeholder === "function" ? this.options.placeholder({
editor: this.editor,

@@ -394,4 +400,5 @@ node,

TrailingNode,
UndoRedo
UndoRedo,
preparePlaceholderAttribute
};
//# sourceMappingURL=index.js.map

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/character-count/character-count.ts","../src/drop-cursor/drop-cursor.ts","../src/focus/focus.ts","../src/gap-cursor/gap-cursor.ts","../src/placeholder/placeholder.ts","../src/selection/selection.ts","../src/trailing-node/trailing-node.ts","../src/undo-redo/undo-redo.ts"],"sourcesContent":["import { Extension } from '@tiptap/core'\nimport type { Node as ProseMirrorNode } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\n\nexport interface CharacterCountOptions {\n /**\n * The maximum number of characters that should be allowed. Defaults to `0`.\n * @default null\n * @example 180\n */\n limit: number | null | undefined\n /**\n * The mode by which the size is calculated. If set to `textSize`, the textContent of the document is used.\n * If set to `nodeSize`, the nodeSize of the document is used.\n * @default 'textSize'\n * @example 'textSize'\n */\n mode: 'textSize' | 'nodeSize'\n /**\n * The text counter function to use. Defaults to a simple character count.\n * @default (text) => text.length\n * @example (text) => [...new Intl.Segmenter().segment(text)].length\n */\n textCounter: (text: string) => number\n /**\n * The word counter function to use. Defaults to a simple word count.\n * @default (text) => text.split(' ').filter(word => word !== '').length\n * @example (text) => text.split(/\\s+/).filter(word => word !== '').length\n */\n wordCounter: (text: string) => number\n}\n\nexport interface CharacterCountStorage {\n /**\n * Get the number of characters for the current document.\n * @param options The options for the character count. (optional)\n * @param options.node The node to get the characters from. Defaults to the current document.\n * @param options.mode The mode by which the size is calculated. If set to `textSize`, the textContent of the document is used.\n */\n characters: (options?: { node?: ProseMirrorNode; mode?: 'textSize' | 'nodeSize' }) => number\n\n /**\n * Get the number of words for the current document.\n * @param options The options for the character count. (optional)\n * @param options.node The node to get the words from. Defaults to the current document.\n */\n words: (options?: { node?: ProseMirrorNode }) => number\n}\n\ndeclare module '@tiptap/core' {\n interface Storage {\n characterCount: CharacterCountStorage\n }\n}\n\n/**\n * This extension allows you to count the characters and words of your document.\n * @see https://tiptap.dev/api/extensions/character-count\n */\nexport const CharacterCount = Extension.create<CharacterCountOptions, CharacterCountStorage>({\n name: 'characterCount',\n\n addOptions() {\n return {\n limit: null,\n mode: 'textSize',\n textCounter: text => text.length,\n wordCounter: text => text.split(' ').filter(word => word !== '').length,\n }\n },\n\n addStorage() {\n return {\n characters: () => 0,\n words: () => 0,\n }\n },\n\n onBeforeCreate() {\n this.storage.characters = options => {\n const node = options?.node || this.editor.state.doc\n const mode = options?.mode || this.options.mode\n\n if (mode === 'textSize') {\n const text = node.textBetween(0, node.content.size, undefined, ' ')\n\n return this.options.textCounter(text)\n }\n\n return node.nodeSize\n }\n\n this.storage.words = options => {\n const node = options?.node || this.editor.state.doc\n const text = node.textBetween(0, node.content.size, ' ', ' ')\n\n return this.options.wordCounter(text)\n }\n },\n\n addProseMirrorPlugins() {\n let initialEvaluationDone = false\n\n return [\n new Plugin({\n key: new PluginKey('characterCount'),\n appendTransaction: (transactions, oldState, newState) => {\n if (initialEvaluationDone) {\n return\n }\n\n const limit = this.options.limit\n\n if (limit === null || limit === undefined || limit === 0) {\n initialEvaluationDone = true\n return\n }\n\n const initialContentSize = this.storage.characters({ node: newState.doc })\n\n if (initialContentSize > limit) {\n const over = initialContentSize - limit\n const from = 0\n const to = over\n\n console.warn(\n `[CharacterCount] Initial content exceeded limit of ${limit} characters. Content was automatically trimmed.`,\n )\n const tr = newState.tr.deleteRange(from, to)\n\n initialEvaluationDone = true\n return tr\n }\n\n initialEvaluationDone = true\n },\n filterTransaction: (transaction, state) => {\n const limit = this.options.limit\n\n // Nothing has changed or no limit is defined. Ignore it.\n if (!transaction.docChanged || limit === 0 || limit === null || limit === undefined) {\n return true\n }\n\n const oldSize = this.storage.characters({ node: state.doc })\n const newSize = this.storage.characters({ node: transaction.doc })\n\n // Everything is in the limit. Good.\n if (newSize <= limit) {\n return true\n }\n\n // The limit has already been exceeded but will be reduced.\n if (oldSize > limit && newSize > limit && newSize <= oldSize) {\n return true\n }\n\n // The limit has already been exceeded and will be increased further.\n if (oldSize > limit && newSize > limit && newSize > oldSize) {\n return false\n }\n\n const isPaste = transaction.getMeta('paste')\n\n // Block all exceeding transactions that were not pasted.\n if (!isPaste) {\n return false\n }\n\n // For pasted content, we try to remove the exceeding content.\n const pos = transaction.selection.$head.pos\n const over = newSize - limit\n const from = pos - over\n const to = pos\n\n // It’s probably a bad idea to mutate transactions within `filterTransaction`\n // but for now this is working fine.\n transaction.deleteRange(from, to)\n\n // In some situations, the limit will continue to be exceeded after trimming.\n // This happens e.g. when truncating within a complex node (e.g. table)\n // and ProseMirror has to close this node again.\n // If this is the case, we prevent the transaction completely.\n const updatedSize = this.storage.characters({ node: transaction.doc })\n\n if (updatedSize > limit) {\n return false\n }\n\n return true\n },\n }),\n ]\n },\n})\n","import { Extension } from '@tiptap/core'\nimport { dropCursor } from '@tiptap/pm/dropcursor'\n\nexport interface DropcursorOptions {\n /**\n * The color of the drop cursor. Use `false` to apply no color and rely only on class.\n * @default 'currentColor'\n * @example 'red'\n */\n color?: string | false\n\n /**\n * The width of the drop cursor\n * @default 1\n * @example 2\n */\n width: number | undefined\n\n /**\n * The class of the drop cursor\n * @default undefined\n * @example 'drop-cursor'\n */\n class: string | undefined\n}\n\n/**\n * This extension allows you to add a drop cursor to your editor.\n * A drop cursor is a line that appears when you drag and drop content\n * in-between nodes.\n * @see https://tiptap.dev/api/extensions/dropcursor\n */\nexport const Dropcursor = Extension.create<DropcursorOptions>({\n name: 'dropCursor',\n\n addOptions() {\n return {\n color: 'currentColor',\n width: 1,\n class: undefined,\n }\n },\n\n addProseMirrorPlugins() {\n return [dropCursor(this.options)]\n },\n})\n","import { Extension } from '@tiptap/core'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet } from '@tiptap/pm/view'\n\nexport interface FocusOptions {\n /**\n * The class name that should be added to the focused node.\n * @default 'has-focus'\n * @example 'is-focused'\n */\n className: string\n\n /**\n * The mode by which the focused node is determined.\n * - All: All nodes are marked as focused.\n * - Deepest: Only the deepest node is marked as focused.\n * - Shallowest: Only the shallowest node is marked as focused.\n *\n * @default 'all'\n * @example 'deepest'\n * @example 'shallowest'\n */\n mode: 'all' | 'deepest' | 'shallowest'\n}\n\n/**\n * This extension allows you to add a class to the focused node.\n * @see https://www.tiptap.dev/api/extensions/focus\n */\nexport const Focus = Extension.create<FocusOptions>({\n name: 'focus',\n\n addOptions() {\n return {\n className: 'has-focus',\n mode: 'all',\n }\n },\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: new PluginKey('focus'),\n props: {\n decorations: ({ doc, selection }) => {\n const { isEditable, isFocused } = this.editor\n const { anchor } = selection\n const decorations: Decoration[] = []\n\n if (!isEditable || !isFocused) {\n return DecorationSet.create(doc, [])\n }\n\n // Maximum Levels\n let maxLevels = 0\n\n if (this.options.mode === 'deepest') {\n doc.descendants((node, pos) => {\n if (node.isText) {\n return\n }\n\n const isCurrent = anchor >= pos && anchor <= pos + node.nodeSize - 1\n\n if (!isCurrent) {\n return false\n }\n\n maxLevels += 1\n })\n }\n\n // Loop through current\n let currentLevel = 0\n\n doc.descendants((node, pos) => {\n if (node.isText) {\n return false\n }\n\n const isCurrent = anchor >= pos && anchor <= pos + node.nodeSize - 1\n\n if (!isCurrent) {\n return false\n }\n\n currentLevel += 1\n\n const outOfScope =\n (this.options.mode === 'deepest' && maxLevels - currentLevel > 0) ||\n (this.options.mode === 'shallowest' && currentLevel > 1)\n\n if (outOfScope) {\n return this.options.mode === 'deepest'\n }\n\n decorations.push(\n Decoration.node(pos, pos + node.nodeSize, {\n class: this.options.className,\n }),\n )\n })\n\n return DecorationSet.create(doc, decorations)\n },\n },\n }),\n ]\n },\n})\n","import type { ParentConfig } from '@tiptap/core'\nimport { callOrReturn, Extension, getExtensionField } from '@tiptap/core'\nimport { gapCursor } from '@tiptap/pm/gapcursor'\n\ndeclare module '@tiptap/core' {\n interface NodeConfig<Options, Storage> {\n /**\n * A function to determine whether the gap cursor is allowed at the current position. Must return `true` or `false`.\n * @default null\n */\n allowGapCursor?:\n | boolean\n | null\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options>>['allowGapCursor']\n }) => boolean | null)\n }\n}\n\n/**\n * This extension allows you to add a gap cursor to your editor.\n * A gap cursor is a cursor that appears when you click on a place\n * where no content is present, for example inbetween nodes.\n * @see https://tiptap.dev/api/extensions/gapcursor\n */\nexport const Gapcursor = Extension.create({\n name: 'gapCursor',\n\n addProseMirrorPlugins() {\n return [gapCursor()]\n },\n\n extendNodeSchema(extension) {\n const context = {\n name: extension.name,\n options: extension.options,\n storage: extension.storage,\n }\n\n return {\n allowGapCursor: callOrReturn(getExtensionField(extension, 'allowGapCursor', context)) ?? null,\n }\n },\n})\n","import type { Editor } from '@tiptap/core'\nimport { Extension, isNodeEmpty } from '@tiptap/core'\nimport type { Node as ProsemirrorNode } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet } from '@tiptap/pm/view'\n\nexport interface PlaceholderOptions {\n /**\n * **The class name for the empty editor**\n * @default 'is-editor-empty'\n */\n emptyEditorClass: string\n\n /**\n * **The class name for empty nodes**\n * @default 'is-empty'\n */\n emptyNodeClass: string\n\n /**\n * **The placeholder content**\n *\n * You can use a function to return a dynamic placeholder or a string.\n * @default 'Write something …'\n */\n placeholder:\n | ((PlaceholderProps: { editor: Editor; node: ProsemirrorNode; pos: number; hasAnchor: boolean }) => string)\n | string\n\n /**\n * **Checks if the placeholder should be only shown when the editor is editable.**\n *\n * If true, the placeholder will only be shown when the editor is editable.\n * If false, the placeholder will always be shown.\n * @default true\n */\n showOnlyWhenEditable: boolean\n\n /**\n * **Checks if the placeholder should be only shown when the current node is empty.**\n *\n * If true, the placeholder will only be shown when the current node is empty.\n * If false, the placeholder will be shown when any node is empty.\n * @default true\n */\n showOnlyCurrent: boolean\n\n /**\n * **Controls if the placeholder should be shown for all descendents.**\n *\n * If true, the placeholder will be shown for all descendents.\n * If false, the placeholder will only be shown for the current node.\n * @default false\n */\n includeChildren: boolean\n}\n\n/**\n * This extension allows you to add a placeholder to your editor.\n * A placeholder is a text that appears when the editor or a node is empty.\n * @see https://www.tiptap.dev/api/extensions/placeholder\n */\nexport const Placeholder = Extension.create<PlaceholderOptions>({\n name: 'placeholder',\n\n addOptions() {\n return {\n emptyEditorClass: 'is-editor-empty',\n emptyNodeClass: 'is-empty',\n placeholder: 'Write something …',\n showOnlyWhenEditable: true,\n showOnlyCurrent: true,\n includeChildren: false,\n }\n },\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: new PluginKey('placeholder'),\n props: {\n decorations: ({ doc, selection }) => {\n const active = this.editor.isEditable || !this.options.showOnlyWhenEditable\n const { anchor } = selection\n const decorations: Decoration[] = []\n\n if (!active) {\n return null\n }\n\n const isEmptyDoc = this.editor.isEmpty\n\n doc.descendants((node, pos) => {\n const hasAnchor = anchor >= pos && anchor <= pos + node.nodeSize\n const isEmpty = !node.isLeaf && isNodeEmpty(node)\n\n if ((hasAnchor || !this.options.showOnlyCurrent) && isEmpty) {\n const classes = [this.options.emptyNodeClass]\n\n if (isEmptyDoc) {\n classes.push(this.options.emptyEditorClass)\n }\n\n const decoration = Decoration.node(pos, pos + node.nodeSize, {\n class: classes.join(' '),\n 'data-placeholder':\n typeof this.options.placeholder === 'function'\n ? this.options.placeholder({\n editor: this.editor,\n node,\n pos,\n hasAnchor,\n })\n : this.options.placeholder,\n })\n\n decorations.push(decoration)\n }\n\n return this.options.includeChildren\n })\n\n return DecorationSet.create(doc, decorations)\n },\n },\n }),\n ]\n },\n})\n","import { Extension, isNodeSelection } from '@tiptap/core'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet } from '@tiptap/pm/view'\n\nexport type SelectionOptions = {\n /**\n * The class name that should be added to the selected text.\n * @default 'selection'\n * @example 'is-selected'\n */\n className: string\n}\n\n/**\n * This extension allows you to add a class to the selected text.\n * @see https://www.tiptap.dev/api/extensions/selection\n */\nexport const Selection = Extension.create<SelectionOptions>({\n name: 'selection',\n\n addOptions() {\n return {\n className: 'selection',\n }\n },\n\n addProseMirrorPlugins() {\n const { editor, options } = this\n\n return [\n new Plugin({\n key: new PluginKey('selection'),\n props: {\n decorations(state) {\n if (\n state.selection.empty ||\n editor.isFocused ||\n !editor.isEditable ||\n isNodeSelection(state.selection) ||\n editor.view.dragging\n ) {\n return null\n }\n\n return DecorationSet.create(state.doc, [\n Decoration.inline(state.selection.from, state.selection.to, {\n class: options.className,\n }),\n ])\n },\n },\n }),\n ]\n },\n})\n\nexport default Selection\n","import { Extension } from '@tiptap/core'\nimport type { Node, NodeType } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\n\nfunction nodeEqualsType({ types, node }: { types: NodeType | NodeType[]; node: Node | null | undefined }) {\n return (node && Array.isArray(types) && types.includes(node.type)) || node?.type === types\n}\n\n/**\n * Extension based on:\n * - https://github.com/ueberdosis/tiptap/blob/v1/packages/tiptap-extensions/src/extensions/TrailingNode.js\n * - https://github.com/remirror/remirror/blob/e0f1bec4a1e8073ce8f5500d62193e52321155b9/packages/prosemirror-trailing-node/src/trailing-node-plugin.ts\n */\n\nexport interface TrailingNodeOptions {\n /**\n * The node type that should be inserted at the end of the document.\n * @note the node will always be added to the `notAfter` lists to\n * prevent an infinite loop.\n * @default undefined\n */\n node?: string\n /**\n * The node types after which the trailing node should not be inserted.\n * @default ['paragraph']\n */\n notAfter?: string | string[]\n}\n\n/**\n * This extension allows you to add an extra node at the end of the document.\n * @see https://www.tiptap.dev/api/extensions/trailing-node\n */\nexport const TrailingNode = Extension.create<TrailingNodeOptions>({\n name: 'trailingNode',\n\n addOptions() {\n return {\n node: undefined,\n notAfter: [],\n }\n },\n\n addProseMirrorPlugins() {\n const plugin = new PluginKey(this.name)\n const defaultNode =\n this.options.node || this.editor.schema.topNodeType.contentMatch.defaultType?.name || 'paragraph'\n\n const disabledNodes = Object.entries(this.editor.schema.nodes)\n .map(([, value]) => value)\n .filter(node => (this.options.notAfter || []).concat(defaultNode).includes(node.name))\n\n return [\n new Plugin({\n key: plugin,\n appendTransaction: (_, __, state) => {\n const { doc, tr, schema } = state\n const shouldInsertNodeAtEnd = plugin.getState(state)\n const endPosition = doc.content.size\n const type = schema.nodes[defaultNode]\n\n if (!shouldInsertNodeAtEnd) {\n return\n }\n\n return tr.insert(endPosition, type.create())\n },\n state: {\n init: (_, state) => {\n const lastNode = state.tr.doc.lastChild\n\n return !nodeEqualsType({ node: lastNode, types: disabledNodes })\n },\n apply: (tr, value) => {\n if (!tr.docChanged) {\n return value\n }\n\n // Ignore transactions from UniqueID extension to prevent infinite loops\n // when UniqueID adds IDs to newly inserted trailing nodes\n if (tr.getMeta('__uniqueIDTransaction')) {\n return value\n }\n\n const lastNode = tr.doc.lastChild\n\n return !nodeEqualsType({ node: lastNode, types: disabledNodes })\n },\n },\n }),\n ]\n },\n})\n","import { Extension } from '@tiptap/core'\nimport { history, redo, undo } from '@tiptap/pm/history'\n\nexport interface UndoRedoOptions {\n /**\n * The amount of history events that are collected before the oldest events are discarded.\n * @default 100\n * @example 50\n */\n depth: number\n\n /**\n * The delay (in milliseconds) between changes after which a new group should be started.\n * @default 500\n * @example 1000\n */\n newGroupDelay: number\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n undoRedo: {\n /**\n * Undo recent changes\n * @example editor.commands.undo()\n */\n undo: () => ReturnType\n /**\n * Reapply reverted changes\n * @example editor.commands.redo()\n */\n redo: () => ReturnType\n }\n }\n}\n\n/**\n * This extension allows you to undo and redo recent changes.\n * @see https://www.tiptap.dev/api/extensions/undo-redo\n *\n * **Important**: If the `@tiptap/extension-collaboration` package is used, make sure to remove\n * the `undo-redo` extension, as it is not compatible with the `collaboration` extension.\n *\n * `@tiptap/extension-collaboration` uses its own history implementation.\n */\nexport const UndoRedo = Extension.create<UndoRedoOptions>({\n name: 'undoRedo',\n\n addOptions() {\n return {\n depth: 100,\n newGroupDelay: 500,\n }\n },\n\n addCommands() {\n return {\n undo:\n () =>\n ({ state, dispatch }) => {\n return undo(state, dispatch)\n },\n redo:\n () =>\n ({ state, dispatch }) => {\n return redo(state, dispatch)\n },\n }\n },\n\n addProseMirrorPlugins() {\n return [history(this.options)]\n },\n\n addKeyboardShortcuts() {\n return {\n 'Mod-z': () => this.editor.commands.undo(),\n 'Shift-Mod-z': () => this.editor.commands.redo(),\n 'Mod-y': () => this.editor.commands.redo(),\n\n // Russian keyboard layouts\n 'Mod-я': () => this.editor.commands.undo(),\n 'Shift-Mod-я': () => this.editor.commands.redo(),\n }\n },\n})\n"],"mappings":"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","names":["Extension","Extension","Plugin","PluginKey","Extension","Extension","Plugin","PluginKey","Decoration","DecorationSet","Extension","Plugin","PluginKey","Decoration","DecorationSet","Extension","Plugin","PluginKey","Extension"]}
{"version":3,"sources":["../src/character-count/character-count.ts","../src/drop-cursor/drop-cursor.ts","../src/focus/focus.ts","../src/gap-cursor/gap-cursor.ts","../src/placeholder/placeholder.ts","../src/selection/selection.ts","../src/trailing-node/trailing-node.ts","../src/undo-redo/undo-redo.ts"],"sourcesContent":["import { Extension } from '@tiptap/core'\nimport type { Node as ProseMirrorNode } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\n\nexport interface CharacterCountOptions {\n /**\n * The maximum number of characters that should be allowed. Defaults to `0`.\n * @default null\n * @example 180\n */\n limit: number | null | undefined\n /**\n * The mode by which the size is calculated. If set to `textSize`, the textContent of the document is used.\n * If set to `nodeSize`, the nodeSize of the document is used.\n * @default 'textSize'\n * @example 'textSize'\n */\n mode: 'textSize' | 'nodeSize'\n /**\n * The text counter function to use. Defaults to a simple character count.\n * @default (text) => text.length\n * @example (text) => [...new Intl.Segmenter().segment(text)].length\n */\n textCounter: (text: string) => number\n /**\n * The word counter function to use. Defaults to a simple word count.\n * @default (text) => text.split(' ').filter(word => word !== '').length\n * @example (text) => text.split(/\\s+/).filter(word => word !== '').length\n */\n wordCounter: (text: string) => number\n}\n\nexport interface CharacterCountStorage {\n /**\n * Get the number of characters for the current document.\n * @param options The options for the character count. (optional)\n * @param options.node The node to get the characters from. Defaults to the current document.\n * @param options.mode The mode by which the size is calculated. If set to `textSize`, the textContent of the document is used.\n */\n characters: (options?: { node?: ProseMirrorNode; mode?: 'textSize' | 'nodeSize' }) => number\n\n /**\n * Get the number of words for the current document.\n * @param options The options for the character count. (optional)\n * @param options.node The node to get the words from. Defaults to the current document.\n */\n words: (options?: { node?: ProseMirrorNode }) => number\n}\n\ndeclare module '@tiptap/core' {\n interface Storage {\n characterCount: CharacterCountStorage\n }\n}\n\n/**\n * This extension allows you to count the characters and words of your document.\n * @see https://tiptap.dev/api/extensions/character-count\n */\nexport const CharacterCount = Extension.create<CharacterCountOptions, CharacterCountStorage>({\n name: 'characterCount',\n\n addOptions() {\n return {\n limit: null,\n mode: 'textSize',\n textCounter: text => text.length,\n wordCounter: text => text.split(' ').filter(word => word !== '').length,\n }\n },\n\n addStorage() {\n return {\n characters: () => 0,\n words: () => 0,\n }\n },\n\n onBeforeCreate() {\n this.storage.characters = options => {\n const node = options?.node || this.editor.state.doc\n const mode = options?.mode || this.options.mode\n\n if (mode === 'textSize') {\n const text = node.textBetween(0, node.content.size, undefined, ' ')\n\n return this.options.textCounter(text)\n }\n\n return node.nodeSize\n }\n\n this.storage.words = options => {\n const node = options?.node || this.editor.state.doc\n const text = node.textBetween(0, node.content.size, ' ', ' ')\n\n return this.options.wordCounter(text)\n }\n },\n\n addProseMirrorPlugins() {\n let initialEvaluationDone = false\n\n return [\n new Plugin({\n key: new PluginKey('characterCount'),\n appendTransaction: (transactions, oldState, newState) => {\n if (initialEvaluationDone) {\n return\n }\n\n const limit = this.options.limit\n\n if (limit === null || limit === undefined || limit === 0) {\n initialEvaluationDone = true\n return\n }\n\n const initialContentSize = this.storage.characters({ node: newState.doc })\n\n if (initialContentSize > limit) {\n const over = initialContentSize - limit\n const from = 0\n const to = over\n\n console.warn(\n `[CharacterCount] Initial content exceeded limit of ${limit} characters. Content was automatically trimmed.`,\n )\n const tr = newState.tr.deleteRange(from, to)\n\n initialEvaluationDone = true\n return tr\n }\n\n initialEvaluationDone = true\n },\n filterTransaction: (transaction, state) => {\n const limit = this.options.limit\n\n // Nothing has changed or no limit is defined. Ignore it.\n if (!transaction.docChanged || limit === 0 || limit === null || limit === undefined) {\n return true\n }\n\n const oldSize = this.storage.characters({ node: state.doc })\n const newSize = this.storage.characters({ node: transaction.doc })\n\n // Everything is in the limit. Good.\n if (newSize <= limit) {\n return true\n }\n\n // The limit has already been exceeded but will be reduced.\n if (oldSize > limit && newSize > limit && newSize <= oldSize) {\n return true\n }\n\n // The limit has already been exceeded and will be increased further.\n if (oldSize > limit && newSize > limit && newSize > oldSize) {\n return false\n }\n\n const isPaste = transaction.getMeta('paste')\n\n // Block all exceeding transactions that were not pasted.\n if (!isPaste) {\n return false\n }\n\n // For pasted content, we try to remove the exceeding content.\n const pos = transaction.selection.$head.pos\n const over = newSize - limit\n const from = pos - over\n const to = pos\n\n // It’s probably a bad idea to mutate transactions within `filterTransaction`\n // but for now this is working fine.\n transaction.deleteRange(from, to)\n\n // In some situations, the limit will continue to be exceeded after trimming.\n // This happens e.g. when truncating within a complex node (e.g. table)\n // and ProseMirror has to close this node again.\n // If this is the case, we prevent the transaction completely.\n const updatedSize = this.storage.characters({ node: transaction.doc })\n\n if (updatedSize > limit) {\n return false\n }\n\n return true\n },\n }),\n ]\n },\n})\n","import { Extension } from '@tiptap/core'\nimport { dropCursor } from '@tiptap/pm/dropcursor'\n\nexport interface DropcursorOptions {\n /**\n * The color of the drop cursor. Use `false` to apply no color and rely only on class.\n * @default 'currentColor'\n * @example 'red'\n */\n color?: string | false\n\n /**\n * The width of the drop cursor\n * @default 1\n * @example 2\n */\n width: number | undefined\n\n /**\n * The class of the drop cursor\n * @default undefined\n * @example 'drop-cursor'\n */\n class: string | undefined\n}\n\n/**\n * This extension allows you to add a drop cursor to your editor.\n * A drop cursor is a line that appears when you drag and drop content\n * in-between nodes.\n * @see https://tiptap.dev/api/extensions/dropcursor\n */\nexport const Dropcursor = Extension.create<DropcursorOptions>({\n name: 'dropCursor',\n\n addOptions() {\n return {\n color: 'currentColor',\n width: 1,\n class: undefined,\n }\n },\n\n addProseMirrorPlugins() {\n return [dropCursor(this.options)]\n },\n})\n","import { Extension } from '@tiptap/core'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet } from '@tiptap/pm/view'\n\nexport interface FocusOptions {\n /**\n * The class name that should be added to the focused node.\n * @default 'has-focus'\n * @example 'is-focused'\n */\n className: string\n\n /**\n * The mode by which the focused node is determined.\n * - All: All nodes are marked as focused.\n * - Deepest: Only the deepest node is marked as focused.\n * - Shallowest: Only the shallowest node is marked as focused.\n *\n * @default 'all'\n * @example 'deepest'\n * @example 'shallowest'\n */\n mode: 'all' | 'deepest' | 'shallowest'\n}\n\n/**\n * This extension allows you to add a class to the focused node.\n * @see https://www.tiptap.dev/api/extensions/focus\n */\nexport const Focus = Extension.create<FocusOptions>({\n name: 'focus',\n\n addOptions() {\n return {\n className: 'has-focus',\n mode: 'all',\n }\n },\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: new PluginKey('focus'),\n props: {\n decorations: ({ doc, selection }) => {\n const { isEditable, isFocused } = this.editor\n const { anchor } = selection\n const decorations: Decoration[] = []\n\n if (!isEditable || !isFocused) {\n return DecorationSet.create(doc, [])\n }\n\n // Maximum Levels\n let maxLevels = 0\n\n if (this.options.mode === 'deepest') {\n doc.descendants((node, pos) => {\n if (node.isText) {\n return\n }\n\n const isCurrent = anchor >= pos && anchor <= pos + node.nodeSize - 1\n\n if (!isCurrent) {\n return false\n }\n\n maxLevels += 1\n })\n }\n\n // Loop through current\n let currentLevel = 0\n\n doc.descendants((node, pos) => {\n if (node.isText) {\n return false\n }\n\n const isCurrent = anchor >= pos && anchor <= pos + node.nodeSize - 1\n\n if (!isCurrent) {\n return false\n }\n\n currentLevel += 1\n\n const outOfScope =\n (this.options.mode === 'deepest' && maxLevels - currentLevel > 0) ||\n (this.options.mode === 'shallowest' && currentLevel > 1)\n\n if (outOfScope) {\n return this.options.mode === 'deepest'\n }\n\n decorations.push(\n Decoration.node(pos, pos + node.nodeSize, {\n class: this.options.className,\n }),\n )\n })\n\n return DecorationSet.create(doc, decorations)\n },\n },\n }),\n ]\n },\n})\n","import type { ParentConfig } from '@tiptap/core'\nimport { callOrReturn, Extension, getExtensionField } from '@tiptap/core'\nimport { gapCursor } from '@tiptap/pm/gapcursor'\n\ndeclare module '@tiptap/core' {\n interface NodeConfig<Options, Storage> {\n /**\n * A function to determine whether the gap cursor is allowed at the current position. Must return `true` or `false`.\n * @default null\n */\n allowGapCursor?:\n | boolean\n | null\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options>>['allowGapCursor']\n }) => boolean | null)\n }\n}\n\n/**\n * This extension allows you to add a gap cursor to your editor.\n * A gap cursor is a cursor that appears when you click on a place\n * where no content is present, for example inbetween nodes.\n * @see https://tiptap.dev/api/extensions/gapcursor\n */\nexport const Gapcursor = Extension.create({\n name: 'gapCursor',\n\n addProseMirrorPlugins() {\n return [gapCursor()]\n },\n\n extendNodeSchema(extension) {\n const context = {\n name: extension.name,\n options: extension.options,\n storage: extension.storage,\n }\n\n return {\n allowGapCursor: callOrReturn(getExtensionField(extension, 'allowGapCursor', context)) ?? null,\n }\n },\n})\n","import type { Editor } from '@tiptap/core'\nimport { Extension, isNodeEmpty } from '@tiptap/core'\nimport type { Node as ProsemirrorNode } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet } from '@tiptap/pm/view'\n\n/**\n * The default data attribute label\n */\nconst DEFAULT_DATA_ATTRIBUTE = 'placeholder'\n\n/**\n * Prepares the placeholder attribute by ensuring it is properly formatted.\n * @param attr - The placeholder attribute string.\n * @returns The prepared placeholder attribute string.\n */\nexport function preparePlaceholderAttribute(attr: string): string {\n return (\n attr\n // replace whitespace with dashes\n .replace(/\\s+/g, '-')\n // replace non-alphanumeric characters\n // or special chars like $, %, &, etc.\n // but not dashes\n .replace(/[^a-zA-Z0-9-]/g, '')\n // and replace any numeric character at the start\n .replace(/^[0-9-]+/, '')\n // and finally replace any stray, leading dashes\n .replace(/^-+/, '')\n .toLowerCase()\n )\n}\n\nexport interface PlaceholderOptions {\n /**\n * **The class name for the empty editor**\n * @default 'is-editor-empty'\n */\n emptyEditorClass: string\n\n /**\n * **The class name for empty nodes**\n * @default 'is-empty'\n */\n emptyNodeClass: string\n\n /**\n * **The data-attribute used for the placeholder label**\n * Will be prepended with `data-` and converted to kebab-case and cleaned of special characters.\n * @default 'placeholder'\n */\n dataAttribute: string\n\n /**\n * **The placeholder content**\n *\n * You can use a function to return a dynamic placeholder or a string.\n * @default 'Write something …'\n */\n placeholder:\n | ((PlaceholderProps: { editor: Editor; node: ProsemirrorNode; pos: number; hasAnchor: boolean }) => string)\n | string\n\n /**\n * **Checks if the placeholder should be only shown when the editor is editable.**\n *\n * If true, the placeholder will only be shown when the editor is editable.\n * If false, the placeholder will always be shown.\n * @default true\n */\n showOnlyWhenEditable: boolean\n\n /**\n * **Checks if the placeholder should be only shown when the current node is empty.**\n *\n * If true, the placeholder will only be shown when the current node is empty.\n * If false, the placeholder will be shown when any node is empty.\n * @default true\n */\n showOnlyCurrent: boolean\n\n /**\n * **Controls if the placeholder should be shown for all descendents.**\n *\n * If true, the placeholder will be shown for all descendents.\n * If false, the placeholder will only be shown for the current node.\n * @default false\n */\n includeChildren: boolean\n}\n\n/**\n * This extension allows you to add a placeholder to your editor.\n * A placeholder is a text that appears when the editor or a node is empty.\n * @see https://www.tiptap.dev/api/extensions/placeholder\n */\nexport const Placeholder = Extension.create<PlaceholderOptions>({\n name: 'placeholder',\n\n addOptions() {\n return {\n emptyEditorClass: 'is-editor-empty',\n emptyNodeClass: 'is-empty',\n dataAttribute: DEFAULT_DATA_ATTRIBUTE,\n placeholder: 'Write something …',\n showOnlyWhenEditable: true,\n showOnlyCurrent: true,\n includeChildren: false,\n }\n },\n\n addProseMirrorPlugins() {\n const dataAttribute = this.options.dataAttribute\n ? `data-${preparePlaceholderAttribute(this.options.dataAttribute)}`\n : `data-${DEFAULT_DATA_ATTRIBUTE}`\n\n return [\n new Plugin({\n key: new PluginKey('placeholder'),\n props: {\n decorations: ({ doc, selection }) => {\n const active = this.editor.isEditable || !this.options.showOnlyWhenEditable\n const { anchor } = selection\n const decorations: Decoration[] = []\n\n if (!active) {\n return null\n }\n\n const isEmptyDoc = this.editor.isEmpty\n\n doc.descendants((node, pos) => {\n const hasAnchor = anchor >= pos && anchor <= pos + node.nodeSize\n const isEmpty = !node.isLeaf && isNodeEmpty(node)\n\n if ((hasAnchor || !this.options.showOnlyCurrent) && isEmpty) {\n const classes = [this.options.emptyNodeClass]\n\n if (isEmptyDoc) {\n classes.push(this.options.emptyEditorClass)\n }\n\n const decoration = Decoration.node(pos, pos + node.nodeSize, {\n class: classes.join(' '),\n [dataAttribute]:\n typeof this.options.placeholder === 'function'\n ? this.options.placeholder({\n editor: this.editor,\n node,\n pos,\n hasAnchor,\n })\n : this.options.placeholder,\n })\n\n decorations.push(decoration)\n }\n\n return this.options.includeChildren\n })\n\n return DecorationSet.create(doc, decorations)\n },\n },\n }),\n ]\n },\n})\n","import { Extension, isNodeSelection } from '@tiptap/core'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet } from '@tiptap/pm/view'\n\nexport type SelectionOptions = {\n /**\n * The class name that should be added to the selected text.\n * @default 'selection'\n * @example 'is-selected'\n */\n className: string\n}\n\n/**\n * This extension allows you to add a class to the selected text.\n * @see https://www.tiptap.dev/api/extensions/selection\n */\nexport const Selection = Extension.create<SelectionOptions>({\n name: 'selection',\n\n addOptions() {\n return {\n className: 'selection',\n }\n },\n\n addProseMirrorPlugins() {\n const { editor, options } = this\n\n return [\n new Plugin({\n key: new PluginKey('selection'),\n props: {\n decorations(state) {\n if (\n state.selection.empty ||\n editor.isFocused ||\n !editor.isEditable ||\n isNodeSelection(state.selection) ||\n editor.view.dragging\n ) {\n return null\n }\n\n return DecorationSet.create(state.doc, [\n Decoration.inline(state.selection.from, state.selection.to, {\n class: options.className,\n }),\n ])\n },\n },\n }),\n ]\n },\n})\n\nexport default Selection\n","import { Extension } from '@tiptap/core'\nimport type { Node, NodeType } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\n\nfunction nodeEqualsType({ types, node }: { types: NodeType | NodeType[]; node: Node | null | undefined }) {\n return (node && Array.isArray(types) && types.includes(node.type)) || node?.type === types\n}\n\n/**\n * Extension based on:\n * - https://github.com/ueberdosis/tiptap/blob/v1/packages/tiptap-extensions/src/extensions/TrailingNode.js\n * - https://github.com/remirror/remirror/blob/e0f1bec4a1e8073ce8f5500d62193e52321155b9/packages/prosemirror-trailing-node/src/trailing-node-plugin.ts\n */\n\nexport interface TrailingNodeOptions {\n /**\n * The node type that should be inserted at the end of the document.\n * @note the node will always be added to the `notAfter` lists to\n * prevent an infinite loop.\n * @default undefined\n */\n node?: string\n /**\n * The node types after which the trailing node should not be inserted.\n * @default ['paragraph']\n */\n notAfter?: string | string[]\n}\n\n/**\n * This extension allows you to add an extra node at the end of the document.\n * @see https://www.tiptap.dev/api/extensions/trailing-node\n */\nexport const TrailingNode = Extension.create<TrailingNodeOptions>({\n name: 'trailingNode',\n\n addOptions() {\n return {\n node: undefined,\n notAfter: [],\n }\n },\n\n addProseMirrorPlugins() {\n const plugin = new PluginKey(this.name)\n const defaultNode =\n this.options.node || this.editor.schema.topNodeType.contentMatch.defaultType?.name || 'paragraph'\n\n const disabledNodes = Object.entries(this.editor.schema.nodes)\n .map(([, value]) => value)\n .filter(node => (this.options.notAfter || []).concat(defaultNode).includes(node.name))\n\n return [\n new Plugin({\n key: plugin,\n appendTransaction: (_, __, state) => {\n const { doc, tr, schema } = state\n const shouldInsertNodeAtEnd = plugin.getState(state)\n const endPosition = doc.content.size\n const type = schema.nodes[defaultNode]\n\n if (!shouldInsertNodeAtEnd) {\n return\n }\n\n return tr.insert(endPosition, type.create())\n },\n state: {\n init: (_, state) => {\n const lastNode = state.tr.doc.lastChild\n\n return !nodeEqualsType({ node: lastNode, types: disabledNodes })\n },\n apply: (tr, value) => {\n if (!tr.docChanged) {\n return value\n }\n\n // Ignore transactions from UniqueID extension to prevent infinite loops\n // when UniqueID adds IDs to newly inserted trailing nodes\n if (tr.getMeta('__uniqueIDTransaction')) {\n return value\n }\n\n const lastNode = tr.doc.lastChild\n\n return !nodeEqualsType({ node: lastNode, types: disabledNodes })\n },\n },\n }),\n ]\n },\n})\n","import { Extension } from '@tiptap/core'\nimport { history, redo, undo } from '@tiptap/pm/history'\n\nexport interface UndoRedoOptions {\n /**\n * The amount of history events that are collected before the oldest events are discarded.\n * @default 100\n * @example 50\n */\n depth: number\n\n /**\n * The delay (in milliseconds) between changes after which a new group should be started.\n * @default 500\n * @example 1000\n */\n newGroupDelay: number\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n undoRedo: {\n /**\n * Undo recent changes\n * @example editor.commands.undo()\n */\n undo: () => ReturnType\n /**\n * Reapply reverted changes\n * @example editor.commands.redo()\n */\n redo: () => ReturnType\n }\n }\n}\n\n/**\n * This extension allows you to undo and redo recent changes.\n * @see https://www.tiptap.dev/api/extensions/undo-redo\n *\n * **Important**: If the `@tiptap/extension-collaboration` package is used, make sure to remove\n * the `undo-redo` extension, as it is not compatible with the `collaboration` extension.\n *\n * `@tiptap/extension-collaboration` uses its own history implementation.\n */\nexport const UndoRedo = Extension.create<UndoRedoOptions>({\n name: 'undoRedo',\n\n addOptions() {\n return {\n depth: 100,\n newGroupDelay: 500,\n }\n },\n\n addCommands() {\n return {\n undo:\n () =>\n ({ state, dispatch }) => {\n return undo(state, dispatch)\n },\n redo:\n () =>\n ({ state, dispatch }) => {\n return redo(state, dispatch)\n },\n }\n },\n\n addProseMirrorPlugins() {\n return [history(this.options)]\n },\n\n addKeyboardShortcuts() {\n return {\n 'Mod-z': () => this.editor.commands.undo(),\n 'Shift-Mod-z': () => this.editor.commands.redo(),\n 'Mod-y': () => this.editor.commands.redo(),\n\n // Russian keyboard layouts\n 'Mod-я': () => this.editor.commands.undo(),\n 'Shift-Mod-я': () => this.editor.commands.redo(),\n }\n },\n})\n"],"mappings":"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","names":["Extension","Extension","Plugin","PluginKey","Extension","Extension","Plugin","PluginKey","Decoration","DecorationSet","Extension","Plugin","PluginKey","Decoration","DecorationSet","Extension","Plugin","PluginKey","Extension"]}

@@ -23,3 +23,4 @@ "use strict";

__export(index_exports, {
Placeholder: () => Placeholder
Placeholder: () => Placeholder,
preparePlaceholderAttribute: () => preparePlaceholderAttribute
});

@@ -32,2 +33,6 @@ module.exports = __toCommonJS(index_exports);

var import_view = require("@tiptap/pm/view");
var DEFAULT_DATA_ATTRIBUTE = "placeholder";
function preparePlaceholderAttribute(attr) {
return attr.replace(/\s+/g, "-").replace(/[^a-zA-Z0-9-]/g, "").replace(/^[0-9-]+/, "").replace(/^-+/, "").toLowerCase();
}
var Placeholder = import_core.Extension.create({

@@ -39,2 +44,3 @@ name: "placeholder",

emptyNodeClass: "is-empty",
dataAttribute: DEFAULT_DATA_ATTRIBUTE,
placeholder: "Write something \u2026",

@@ -47,2 +53,3 @@ showOnlyWhenEditable: true,

addProseMirrorPlugins() {
const dataAttribute = this.options.dataAttribute ? `data-${preparePlaceholderAttribute(this.options.dataAttribute)}` : `data-${DEFAULT_DATA_ATTRIBUTE}`;
return [

@@ -70,3 +77,3 @@ new import_state.Plugin({

class: classes.join(" "),
"data-placeholder": typeof this.options.placeholder === "function" ? this.options.placeholder({
[dataAttribute]: typeof this.options.placeholder === "function" ? this.options.placeholder({
editor: this.editor,

@@ -91,4 +98,5 @@ node,

0 && (module.exports = {
Placeholder
Placeholder,
preparePlaceholderAttribute
});
//# sourceMappingURL=index.cjs.map

@@ -1,1 +0,1 @@

{"version":3,"sources":["../../src/placeholder/index.ts","../../src/placeholder/placeholder.ts"],"sourcesContent":["export * from './placeholder.js'\n","import type { Editor } from '@tiptap/core'\nimport { Extension, isNodeEmpty } from '@tiptap/core'\nimport type { Node as ProsemirrorNode } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet } from '@tiptap/pm/view'\n\nexport interface PlaceholderOptions {\n /**\n * **The class name for the empty editor**\n * @default 'is-editor-empty'\n */\n emptyEditorClass: string\n\n /**\n * **The class name for empty nodes**\n * @default 'is-empty'\n */\n emptyNodeClass: string\n\n /**\n * **The placeholder content**\n *\n * You can use a function to return a dynamic placeholder or a string.\n * @default 'Write something …'\n */\n placeholder:\n | ((PlaceholderProps: { editor: Editor; node: ProsemirrorNode; pos: number; hasAnchor: boolean }) => string)\n | string\n\n /**\n * **Checks if the placeholder should be only shown when the editor is editable.**\n *\n * If true, the placeholder will only be shown when the editor is editable.\n * If false, the placeholder will always be shown.\n * @default true\n */\n showOnlyWhenEditable: boolean\n\n /**\n * **Checks if the placeholder should be only shown when the current node is empty.**\n *\n * If true, the placeholder will only be shown when the current node is empty.\n * If false, the placeholder will be shown when any node is empty.\n * @default true\n */\n showOnlyCurrent: boolean\n\n /**\n * **Controls if the placeholder should be shown for all descendents.**\n *\n * If true, the placeholder will be shown for all descendents.\n * If false, the placeholder will only be shown for the current node.\n * @default false\n */\n includeChildren: boolean\n}\n\n/**\n * This extension allows you to add a placeholder to your editor.\n * A placeholder is a text that appears when the editor or a node is empty.\n * @see https://www.tiptap.dev/api/extensions/placeholder\n */\nexport const Placeholder = Extension.create<PlaceholderOptions>({\n name: 'placeholder',\n\n addOptions() {\n return {\n emptyEditorClass: 'is-editor-empty',\n emptyNodeClass: 'is-empty',\n placeholder: 'Write something …',\n showOnlyWhenEditable: true,\n showOnlyCurrent: true,\n includeChildren: false,\n }\n },\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: new PluginKey('placeholder'),\n props: {\n decorations: ({ doc, selection }) => {\n const active = this.editor.isEditable || !this.options.showOnlyWhenEditable\n const { anchor } = selection\n const decorations: Decoration[] = []\n\n if (!active) {\n return null\n }\n\n const isEmptyDoc = this.editor.isEmpty\n\n doc.descendants((node, pos) => {\n const hasAnchor = anchor >= pos && anchor <= pos + node.nodeSize\n const isEmpty = !node.isLeaf && isNodeEmpty(node)\n\n if ((hasAnchor || !this.options.showOnlyCurrent) && isEmpty) {\n const classes = [this.options.emptyNodeClass]\n\n if (isEmptyDoc) {\n classes.push(this.options.emptyEditorClass)\n }\n\n const decoration = Decoration.node(pos, pos + node.nodeSize, {\n class: classes.join(' '),\n 'data-placeholder':\n typeof this.options.placeholder === 'function'\n ? this.options.placeholder({\n editor: this.editor,\n node,\n pos,\n hasAnchor,\n })\n : this.options.placeholder,\n })\n\n decorations.push(decoration)\n }\n\n return this.options.includeChildren\n })\n\n return DecorationSet.create(doc, decorations)\n },\n },\n }),\n ]\n },\n})\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACCA,kBAAuC;AAEvC,mBAAkC;AAClC,kBAA0C;AA0DnC,IAAM,cAAc,sBAAU,OAA2B;AAAA,EAC9D,MAAM;AAAA,EAEN,aAAa;AACX,WAAO;AAAA,MACL,kBAAkB;AAAA,MAClB,gBAAgB;AAAA,MAChB,aAAa;AAAA,MACb,sBAAsB;AAAA,MACtB,iBAAiB;AAAA,MACjB,iBAAiB;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,wBAAwB;AACtB,WAAO;AAAA,MACL,IAAI,oBAAO;AAAA,QACT,KAAK,IAAI,uBAAU,aAAa;AAAA,QAChC,OAAO;AAAA,UACL,aAAa,CAAC,EAAE,KAAK,UAAU,MAAM;AACnC,kBAAM,SAAS,KAAK,OAAO,cAAc,CAAC,KAAK,QAAQ;AACvD,kBAAM,EAAE,OAAO,IAAI;AACnB,kBAAM,cAA4B,CAAC;AAEnC,gBAAI,CAAC,QAAQ;AACX,qBAAO;AAAA,YACT;AAEA,kBAAM,aAAa,KAAK,OAAO;AAE/B,gBAAI,YAAY,CAAC,MAAM,QAAQ;AAC7B,oBAAM,YAAY,UAAU,OAAO,UAAU,MAAM,KAAK;AACxD,oBAAM,UAAU,CAAC,KAAK,cAAU,yBAAY,IAAI;AAEhD,mBAAK,aAAa,CAAC,KAAK,QAAQ,oBAAoB,SAAS;AAC3D,sBAAM,UAAU,CAAC,KAAK,QAAQ,cAAc;AAE5C,oBAAI,YAAY;AACd,0BAAQ,KAAK,KAAK,QAAQ,gBAAgB;AAAA,gBAC5C;AAEA,sBAAM,aAAa,uBAAW,KAAK,KAAK,MAAM,KAAK,UAAU;AAAA,kBAC3D,OAAO,QAAQ,KAAK,GAAG;AAAA,kBACvB,oBACE,OAAO,KAAK,QAAQ,gBAAgB,aAChC,KAAK,QAAQ,YAAY;AAAA,oBACvB,QAAQ,KAAK;AAAA,oBACb;AAAA,oBACA;AAAA,oBACA;AAAA,kBACF,CAAC,IACD,KAAK,QAAQ;AAAA,gBACrB,CAAC;AAED,4BAAY,KAAK,UAAU;AAAA,cAC7B;AAEA,qBAAO,KAAK,QAAQ;AAAA,YACtB,CAAC;AAED,mBAAO,0BAAc,OAAO,KAAK,WAAW;AAAA,UAC9C;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;","names":[]}
{"version":3,"sources":["../../src/placeholder/index.ts","../../src/placeholder/placeholder.ts"],"sourcesContent":["export * from './placeholder.js'\n","import type { Editor } from '@tiptap/core'\nimport { Extension, isNodeEmpty } from '@tiptap/core'\nimport type { Node as ProsemirrorNode } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet } from '@tiptap/pm/view'\n\n/**\n * The default data attribute label\n */\nconst DEFAULT_DATA_ATTRIBUTE = 'placeholder'\n\n/**\n * Prepares the placeholder attribute by ensuring it is properly formatted.\n * @param attr - The placeholder attribute string.\n * @returns The prepared placeholder attribute string.\n */\nexport function preparePlaceholderAttribute(attr: string): string {\n return (\n attr\n // replace whitespace with dashes\n .replace(/\\s+/g, '-')\n // replace non-alphanumeric characters\n // or special chars like $, %, &, etc.\n // but not dashes\n .replace(/[^a-zA-Z0-9-]/g, '')\n // and replace any numeric character at the start\n .replace(/^[0-9-]+/, '')\n // and finally replace any stray, leading dashes\n .replace(/^-+/, '')\n .toLowerCase()\n )\n}\n\nexport interface PlaceholderOptions {\n /**\n * **The class name for the empty editor**\n * @default 'is-editor-empty'\n */\n emptyEditorClass: string\n\n /**\n * **The class name for empty nodes**\n * @default 'is-empty'\n */\n emptyNodeClass: string\n\n /**\n * **The data-attribute used for the placeholder label**\n * Will be prepended with `data-` and converted to kebab-case and cleaned of special characters.\n * @default 'placeholder'\n */\n dataAttribute: string\n\n /**\n * **The placeholder content**\n *\n * You can use a function to return a dynamic placeholder or a string.\n * @default 'Write something …'\n */\n placeholder:\n | ((PlaceholderProps: { editor: Editor; node: ProsemirrorNode; pos: number; hasAnchor: boolean }) => string)\n | string\n\n /**\n * **Checks if the placeholder should be only shown when the editor is editable.**\n *\n * If true, the placeholder will only be shown when the editor is editable.\n * If false, the placeholder will always be shown.\n * @default true\n */\n showOnlyWhenEditable: boolean\n\n /**\n * **Checks if the placeholder should be only shown when the current node is empty.**\n *\n * If true, the placeholder will only be shown when the current node is empty.\n * If false, the placeholder will be shown when any node is empty.\n * @default true\n */\n showOnlyCurrent: boolean\n\n /**\n * **Controls if the placeholder should be shown for all descendents.**\n *\n * If true, the placeholder will be shown for all descendents.\n * If false, the placeholder will only be shown for the current node.\n * @default false\n */\n includeChildren: boolean\n}\n\n/**\n * This extension allows you to add a placeholder to your editor.\n * A placeholder is a text that appears when the editor or a node is empty.\n * @see https://www.tiptap.dev/api/extensions/placeholder\n */\nexport const Placeholder = Extension.create<PlaceholderOptions>({\n name: 'placeholder',\n\n addOptions() {\n return {\n emptyEditorClass: 'is-editor-empty',\n emptyNodeClass: 'is-empty',\n dataAttribute: DEFAULT_DATA_ATTRIBUTE,\n placeholder: 'Write something …',\n showOnlyWhenEditable: true,\n showOnlyCurrent: true,\n includeChildren: false,\n }\n },\n\n addProseMirrorPlugins() {\n const dataAttribute = this.options.dataAttribute\n ? `data-${preparePlaceholderAttribute(this.options.dataAttribute)}`\n : `data-${DEFAULT_DATA_ATTRIBUTE}`\n\n return [\n new Plugin({\n key: new PluginKey('placeholder'),\n props: {\n decorations: ({ doc, selection }) => {\n const active = this.editor.isEditable || !this.options.showOnlyWhenEditable\n const { anchor } = selection\n const decorations: Decoration[] = []\n\n if (!active) {\n return null\n }\n\n const isEmptyDoc = this.editor.isEmpty\n\n doc.descendants((node, pos) => {\n const hasAnchor = anchor >= pos && anchor <= pos + node.nodeSize\n const isEmpty = !node.isLeaf && isNodeEmpty(node)\n\n if ((hasAnchor || !this.options.showOnlyCurrent) && isEmpty) {\n const classes = [this.options.emptyNodeClass]\n\n if (isEmptyDoc) {\n classes.push(this.options.emptyEditorClass)\n }\n\n const decoration = Decoration.node(pos, pos + node.nodeSize, {\n class: classes.join(' '),\n [dataAttribute]:\n typeof this.options.placeholder === 'function'\n ? this.options.placeholder({\n editor: this.editor,\n node,\n pos,\n hasAnchor,\n })\n : this.options.placeholder,\n })\n\n decorations.push(decoration)\n }\n\n return this.options.includeChildren\n })\n\n return DecorationSet.create(doc, decorations)\n },\n },\n }),\n ]\n },\n})\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACCA,kBAAuC;AAEvC,mBAAkC;AAClC,kBAA0C;AAK1C,IAAM,yBAAyB;AAOxB,SAAS,4BAA4B,MAAsB;AAChE,SACE,KAEG,QAAQ,QAAQ,GAAG,EAInB,QAAQ,kBAAkB,EAAE,EAE5B,QAAQ,YAAY,EAAE,EAEtB,QAAQ,OAAO,EAAE,EACjB,YAAY;AAEnB;AAiEO,IAAM,cAAc,sBAAU,OAA2B;AAAA,EAC9D,MAAM;AAAA,EAEN,aAAa;AACX,WAAO;AAAA,MACL,kBAAkB;AAAA,MAClB,gBAAgB;AAAA,MAChB,eAAe;AAAA,MACf,aAAa;AAAA,MACb,sBAAsB;AAAA,MACtB,iBAAiB;AAAA,MACjB,iBAAiB;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,wBAAwB;AACtB,UAAM,gBAAgB,KAAK,QAAQ,gBAC/B,QAAQ,4BAA4B,KAAK,QAAQ,aAAa,CAAC,KAC/D,QAAQ,sBAAsB;AAElC,WAAO;AAAA,MACL,IAAI,oBAAO;AAAA,QACT,KAAK,IAAI,uBAAU,aAAa;AAAA,QAChC,OAAO;AAAA,UACL,aAAa,CAAC,EAAE,KAAK,UAAU,MAAM;AACnC,kBAAM,SAAS,KAAK,OAAO,cAAc,CAAC,KAAK,QAAQ;AACvD,kBAAM,EAAE,OAAO,IAAI;AACnB,kBAAM,cAA4B,CAAC;AAEnC,gBAAI,CAAC,QAAQ;AACX,qBAAO;AAAA,YACT;AAEA,kBAAM,aAAa,KAAK,OAAO;AAE/B,gBAAI,YAAY,CAAC,MAAM,QAAQ;AAC7B,oBAAM,YAAY,UAAU,OAAO,UAAU,MAAM,KAAK;AACxD,oBAAM,UAAU,CAAC,KAAK,cAAU,yBAAY,IAAI;AAEhD,mBAAK,aAAa,CAAC,KAAK,QAAQ,oBAAoB,SAAS;AAC3D,sBAAM,UAAU,CAAC,KAAK,QAAQ,cAAc;AAE5C,oBAAI,YAAY;AACd,0BAAQ,KAAK,KAAK,QAAQ,gBAAgB;AAAA,gBAC5C;AAEA,sBAAM,aAAa,uBAAW,KAAK,KAAK,MAAM,KAAK,UAAU;AAAA,kBAC3D,OAAO,QAAQ,KAAK,GAAG;AAAA,kBACvB,CAAC,aAAa,GACZ,OAAO,KAAK,QAAQ,gBAAgB,aAChC,KAAK,QAAQ,YAAY;AAAA,oBACvB,QAAQ,KAAK;AAAA,oBACb;AAAA,oBACA;AAAA,oBACA;AAAA,kBACF,CAAC,IACD,KAAK,QAAQ;AAAA,gBACrB,CAAC;AAED,4BAAY,KAAK,UAAU;AAAA,cAC7B;AAEA,qBAAO,KAAK,QAAQ;AAAA,YACtB,CAAC;AAED,mBAAO,0BAAc,OAAO,KAAK,WAAW;AAAA,UAC9C;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;","names":[]}
import { Editor, Extension } from '@tiptap/core';
import { Node } from '@tiptap/pm/model';
/**
* Prepares the placeholder attribute by ensuring it is properly formatted.
* @param attr - The placeholder attribute string.
* @returns The prepared placeholder attribute string.
*/
declare function preparePlaceholderAttribute(attr: string): string;
interface PlaceholderOptions {

@@ -16,2 +22,8 @@ /**

/**
* **The data-attribute used for the placeholder label**
* Will be prepended with `data-` and converted to kebab-case and cleaned of special characters.
* @default 'placeholder'
*/
dataAttribute: string;
/**
* **The placeholder content**

@@ -60,2 +72,2 @@ *

export { Placeholder, type PlaceholderOptions };
export { Placeholder, type PlaceholderOptions, preparePlaceholderAttribute };
import { Editor, Extension } from '@tiptap/core';
import { Node } from '@tiptap/pm/model';
/**
* Prepares the placeholder attribute by ensuring it is properly formatted.
* @param attr - The placeholder attribute string.
* @returns The prepared placeholder attribute string.
*/
declare function preparePlaceholderAttribute(attr: string): string;
interface PlaceholderOptions {

@@ -16,2 +22,8 @@ /**

/**
* **The data-attribute used for the placeholder label**
* Will be prepended with `data-` and converted to kebab-case and cleaned of special characters.
* @default 'placeholder'
*/
dataAttribute: string;
/**
* **The placeholder content**

@@ -60,2 +72,2 @@ *

export { Placeholder, type PlaceholderOptions };
export { Placeholder, type PlaceholderOptions, preparePlaceholderAttribute };

@@ -5,2 +5,6 @@ // src/placeholder/placeholder.ts

import { Decoration, DecorationSet } from "@tiptap/pm/view";
var DEFAULT_DATA_ATTRIBUTE = "placeholder";
function preparePlaceholderAttribute(attr) {
return attr.replace(/\s+/g, "-").replace(/[^a-zA-Z0-9-]/g, "").replace(/^[0-9-]+/, "").replace(/^-+/, "").toLowerCase();
}
var Placeholder = Extension.create({

@@ -12,2 +16,3 @@ name: "placeholder",

emptyNodeClass: "is-empty",
dataAttribute: DEFAULT_DATA_ATTRIBUTE,
placeholder: "Write something \u2026",

@@ -20,2 +25,3 @@ showOnlyWhenEditable: true,

addProseMirrorPlugins() {
const dataAttribute = this.options.dataAttribute ? `data-${preparePlaceholderAttribute(this.options.dataAttribute)}` : `data-${DEFAULT_DATA_ATTRIBUTE}`;
return [

@@ -43,3 +49,3 @@ new Plugin({

class: classes.join(" "),
"data-placeholder": typeof this.options.placeholder === "function" ? this.options.placeholder({
[dataAttribute]: typeof this.options.placeholder === "function" ? this.options.placeholder({
editor: this.editor,

@@ -63,4 +69,5 @@ node,

export {
Placeholder
Placeholder,
preparePlaceholderAttribute
};
//# sourceMappingURL=index.js.map

@@ -1,1 +0,1 @@

{"version":3,"sources":["../../src/placeholder/placeholder.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { Extension, isNodeEmpty } from '@tiptap/core'\nimport type { Node as ProsemirrorNode } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet } from '@tiptap/pm/view'\n\nexport interface PlaceholderOptions {\n /**\n * **The class name for the empty editor**\n * @default 'is-editor-empty'\n */\n emptyEditorClass: string\n\n /**\n * **The class name for empty nodes**\n * @default 'is-empty'\n */\n emptyNodeClass: string\n\n /**\n * **The placeholder content**\n *\n * You can use a function to return a dynamic placeholder or a string.\n * @default 'Write something …'\n */\n placeholder:\n | ((PlaceholderProps: { editor: Editor; node: ProsemirrorNode; pos: number; hasAnchor: boolean }) => string)\n | string\n\n /**\n * **Checks if the placeholder should be only shown when the editor is editable.**\n *\n * If true, the placeholder will only be shown when the editor is editable.\n * If false, the placeholder will always be shown.\n * @default true\n */\n showOnlyWhenEditable: boolean\n\n /**\n * **Checks if the placeholder should be only shown when the current node is empty.**\n *\n * If true, the placeholder will only be shown when the current node is empty.\n * If false, the placeholder will be shown when any node is empty.\n * @default true\n */\n showOnlyCurrent: boolean\n\n /**\n * **Controls if the placeholder should be shown for all descendents.**\n *\n * If true, the placeholder will be shown for all descendents.\n * If false, the placeholder will only be shown for the current node.\n * @default false\n */\n includeChildren: boolean\n}\n\n/**\n * This extension allows you to add a placeholder to your editor.\n * A placeholder is a text that appears when the editor or a node is empty.\n * @see https://www.tiptap.dev/api/extensions/placeholder\n */\nexport const Placeholder = Extension.create<PlaceholderOptions>({\n name: 'placeholder',\n\n addOptions() {\n return {\n emptyEditorClass: 'is-editor-empty',\n emptyNodeClass: 'is-empty',\n placeholder: 'Write something …',\n showOnlyWhenEditable: true,\n showOnlyCurrent: true,\n includeChildren: false,\n }\n },\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: new PluginKey('placeholder'),\n props: {\n decorations: ({ doc, selection }) => {\n const active = this.editor.isEditable || !this.options.showOnlyWhenEditable\n const { anchor } = selection\n const decorations: Decoration[] = []\n\n if (!active) {\n return null\n }\n\n const isEmptyDoc = this.editor.isEmpty\n\n doc.descendants((node, pos) => {\n const hasAnchor = anchor >= pos && anchor <= pos + node.nodeSize\n const isEmpty = !node.isLeaf && isNodeEmpty(node)\n\n if ((hasAnchor || !this.options.showOnlyCurrent) && isEmpty) {\n const classes = [this.options.emptyNodeClass]\n\n if (isEmptyDoc) {\n classes.push(this.options.emptyEditorClass)\n }\n\n const decoration = Decoration.node(pos, pos + node.nodeSize, {\n class: classes.join(' '),\n 'data-placeholder':\n typeof this.options.placeholder === 'function'\n ? this.options.placeholder({\n editor: this.editor,\n node,\n pos,\n hasAnchor,\n })\n : this.options.placeholder,\n })\n\n decorations.push(decoration)\n }\n\n return this.options.includeChildren\n })\n\n return DecorationSet.create(doc, decorations)\n },\n },\n }),\n ]\n },\n})\n"],"mappings":";AACA,SAAS,WAAW,mBAAmB;AAEvC,SAAS,QAAQ,iBAAiB;AAClC,SAAS,YAAY,qBAAqB;AA0DnC,IAAM,cAAc,UAAU,OAA2B;AAAA,EAC9D,MAAM;AAAA,EAEN,aAAa;AACX,WAAO;AAAA,MACL,kBAAkB;AAAA,MAClB,gBAAgB;AAAA,MAChB,aAAa;AAAA,MACb,sBAAsB;AAAA,MACtB,iBAAiB;AAAA,MACjB,iBAAiB;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,wBAAwB;AACtB,WAAO;AAAA,MACL,IAAI,OAAO;AAAA,QACT,KAAK,IAAI,UAAU,aAAa;AAAA,QAChC,OAAO;AAAA,UACL,aAAa,CAAC,EAAE,KAAK,UAAU,MAAM;AACnC,kBAAM,SAAS,KAAK,OAAO,cAAc,CAAC,KAAK,QAAQ;AACvD,kBAAM,EAAE,OAAO,IAAI;AACnB,kBAAM,cAA4B,CAAC;AAEnC,gBAAI,CAAC,QAAQ;AACX,qBAAO;AAAA,YACT;AAEA,kBAAM,aAAa,KAAK,OAAO;AAE/B,gBAAI,YAAY,CAAC,MAAM,QAAQ;AAC7B,oBAAM,YAAY,UAAU,OAAO,UAAU,MAAM,KAAK;AACxD,oBAAM,UAAU,CAAC,KAAK,UAAU,YAAY,IAAI;AAEhD,mBAAK,aAAa,CAAC,KAAK,QAAQ,oBAAoB,SAAS;AAC3D,sBAAM,UAAU,CAAC,KAAK,QAAQ,cAAc;AAE5C,oBAAI,YAAY;AACd,0BAAQ,KAAK,KAAK,QAAQ,gBAAgB;AAAA,gBAC5C;AAEA,sBAAM,aAAa,WAAW,KAAK,KAAK,MAAM,KAAK,UAAU;AAAA,kBAC3D,OAAO,QAAQ,KAAK,GAAG;AAAA,kBACvB,oBACE,OAAO,KAAK,QAAQ,gBAAgB,aAChC,KAAK,QAAQ,YAAY;AAAA,oBACvB,QAAQ,KAAK;AAAA,oBACb;AAAA,oBACA;AAAA,oBACA;AAAA,kBACF,CAAC,IACD,KAAK,QAAQ;AAAA,gBACrB,CAAC;AAED,4BAAY,KAAK,UAAU;AAAA,cAC7B;AAEA,qBAAO,KAAK,QAAQ;AAAA,YACtB,CAAC;AAED,mBAAO,cAAc,OAAO,KAAK,WAAW;AAAA,UAC9C;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;","names":[]}
{"version":3,"sources":["../../src/placeholder/placeholder.ts"],"sourcesContent":["import type { Editor } from '@tiptap/core'\nimport { Extension, isNodeEmpty } from '@tiptap/core'\nimport type { Node as ProsemirrorNode } from '@tiptap/pm/model'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet } from '@tiptap/pm/view'\n\n/**\n * The default data attribute label\n */\nconst DEFAULT_DATA_ATTRIBUTE = 'placeholder'\n\n/**\n * Prepares the placeholder attribute by ensuring it is properly formatted.\n * @param attr - The placeholder attribute string.\n * @returns The prepared placeholder attribute string.\n */\nexport function preparePlaceholderAttribute(attr: string): string {\n return (\n attr\n // replace whitespace with dashes\n .replace(/\\s+/g, '-')\n // replace non-alphanumeric characters\n // or special chars like $, %, &, etc.\n // but not dashes\n .replace(/[^a-zA-Z0-9-]/g, '')\n // and replace any numeric character at the start\n .replace(/^[0-9-]+/, '')\n // and finally replace any stray, leading dashes\n .replace(/^-+/, '')\n .toLowerCase()\n )\n}\n\nexport interface PlaceholderOptions {\n /**\n * **The class name for the empty editor**\n * @default 'is-editor-empty'\n */\n emptyEditorClass: string\n\n /**\n * **The class name for empty nodes**\n * @default 'is-empty'\n */\n emptyNodeClass: string\n\n /**\n * **The data-attribute used for the placeholder label**\n * Will be prepended with `data-` and converted to kebab-case and cleaned of special characters.\n * @default 'placeholder'\n */\n dataAttribute: string\n\n /**\n * **The placeholder content**\n *\n * You can use a function to return a dynamic placeholder or a string.\n * @default 'Write something …'\n */\n placeholder:\n | ((PlaceholderProps: { editor: Editor; node: ProsemirrorNode; pos: number; hasAnchor: boolean }) => string)\n | string\n\n /**\n * **Checks if the placeholder should be only shown when the editor is editable.**\n *\n * If true, the placeholder will only be shown when the editor is editable.\n * If false, the placeholder will always be shown.\n * @default true\n */\n showOnlyWhenEditable: boolean\n\n /**\n * **Checks if the placeholder should be only shown when the current node is empty.**\n *\n * If true, the placeholder will only be shown when the current node is empty.\n * If false, the placeholder will be shown when any node is empty.\n * @default true\n */\n showOnlyCurrent: boolean\n\n /**\n * **Controls if the placeholder should be shown for all descendents.**\n *\n * If true, the placeholder will be shown for all descendents.\n * If false, the placeholder will only be shown for the current node.\n * @default false\n */\n includeChildren: boolean\n}\n\n/**\n * This extension allows you to add a placeholder to your editor.\n * A placeholder is a text that appears when the editor or a node is empty.\n * @see https://www.tiptap.dev/api/extensions/placeholder\n */\nexport const Placeholder = Extension.create<PlaceholderOptions>({\n name: 'placeholder',\n\n addOptions() {\n return {\n emptyEditorClass: 'is-editor-empty',\n emptyNodeClass: 'is-empty',\n dataAttribute: DEFAULT_DATA_ATTRIBUTE,\n placeholder: 'Write something …',\n showOnlyWhenEditable: true,\n showOnlyCurrent: true,\n includeChildren: false,\n }\n },\n\n addProseMirrorPlugins() {\n const dataAttribute = this.options.dataAttribute\n ? `data-${preparePlaceholderAttribute(this.options.dataAttribute)}`\n : `data-${DEFAULT_DATA_ATTRIBUTE}`\n\n return [\n new Plugin({\n key: new PluginKey('placeholder'),\n props: {\n decorations: ({ doc, selection }) => {\n const active = this.editor.isEditable || !this.options.showOnlyWhenEditable\n const { anchor } = selection\n const decorations: Decoration[] = []\n\n if (!active) {\n return null\n }\n\n const isEmptyDoc = this.editor.isEmpty\n\n doc.descendants((node, pos) => {\n const hasAnchor = anchor >= pos && anchor <= pos + node.nodeSize\n const isEmpty = !node.isLeaf && isNodeEmpty(node)\n\n if ((hasAnchor || !this.options.showOnlyCurrent) && isEmpty) {\n const classes = [this.options.emptyNodeClass]\n\n if (isEmptyDoc) {\n classes.push(this.options.emptyEditorClass)\n }\n\n const decoration = Decoration.node(pos, pos + node.nodeSize, {\n class: classes.join(' '),\n [dataAttribute]:\n typeof this.options.placeholder === 'function'\n ? this.options.placeholder({\n editor: this.editor,\n node,\n pos,\n hasAnchor,\n })\n : this.options.placeholder,\n })\n\n decorations.push(decoration)\n }\n\n return this.options.includeChildren\n })\n\n return DecorationSet.create(doc, decorations)\n },\n },\n }),\n ]\n },\n})\n"],"mappings":";AACA,SAAS,WAAW,mBAAmB;AAEvC,SAAS,QAAQ,iBAAiB;AAClC,SAAS,YAAY,qBAAqB;AAK1C,IAAM,yBAAyB;AAOxB,SAAS,4BAA4B,MAAsB;AAChE,SACE,KAEG,QAAQ,QAAQ,GAAG,EAInB,QAAQ,kBAAkB,EAAE,EAE5B,QAAQ,YAAY,EAAE,EAEtB,QAAQ,OAAO,EAAE,EACjB,YAAY;AAEnB;AAiEO,IAAM,cAAc,UAAU,OAA2B;AAAA,EAC9D,MAAM;AAAA,EAEN,aAAa;AACX,WAAO;AAAA,MACL,kBAAkB;AAAA,MAClB,gBAAgB;AAAA,MAChB,eAAe;AAAA,MACf,aAAa;AAAA,MACb,sBAAsB;AAAA,MACtB,iBAAiB;AAAA,MACjB,iBAAiB;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,wBAAwB;AACtB,UAAM,gBAAgB,KAAK,QAAQ,gBAC/B,QAAQ,4BAA4B,KAAK,QAAQ,aAAa,CAAC,KAC/D,QAAQ,sBAAsB;AAElC,WAAO;AAAA,MACL,IAAI,OAAO;AAAA,QACT,KAAK,IAAI,UAAU,aAAa;AAAA,QAChC,OAAO;AAAA,UACL,aAAa,CAAC,EAAE,KAAK,UAAU,MAAM;AACnC,kBAAM,SAAS,KAAK,OAAO,cAAc,CAAC,KAAK,QAAQ;AACvD,kBAAM,EAAE,OAAO,IAAI;AACnB,kBAAM,cAA4B,CAAC;AAEnC,gBAAI,CAAC,QAAQ;AACX,qBAAO;AAAA,YACT;AAEA,kBAAM,aAAa,KAAK,OAAO;AAE/B,gBAAI,YAAY,CAAC,MAAM,QAAQ;AAC7B,oBAAM,YAAY,UAAU,OAAO,UAAU,MAAM,KAAK;AACxD,oBAAM,UAAU,CAAC,KAAK,UAAU,YAAY,IAAI;AAEhD,mBAAK,aAAa,CAAC,KAAK,QAAQ,oBAAoB,SAAS;AAC3D,sBAAM,UAAU,CAAC,KAAK,QAAQ,cAAc;AAE5C,oBAAI,YAAY;AACd,0BAAQ,KAAK,KAAK,QAAQ,gBAAgB;AAAA,gBAC5C;AAEA,sBAAM,aAAa,WAAW,KAAK,KAAK,MAAM,KAAK,UAAU;AAAA,kBAC3D,OAAO,QAAQ,KAAK,GAAG;AAAA,kBACvB,CAAC,aAAa,GACZ,OAAO,KAAK,QAAQ,gBAAgB,aAChC,KAAK,QAAQ,YAAY;AAAA,oBACvB,QAAQ,KAAK;AAAA,oBACb;AAAA,oBACA;AAAA,oBACA;AAAA,kBACF,CAAC,IACD,KAAK,QAAQ;AAAA,gBACrB,CAAC;AAED,4BAAY,KAAK,UAAU;AAAA,cAC7B;AAEA,qBAAO,KAAK,QAAQ;AAAA,YACtB,CAAC;AAED,mBAAO,cAAc,OAAO,KAAK,WAAW;AAAA,UAC9C;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;","names":[]}
{
"name": "@tiptap/extensions",
"description": "various extensions for tiptap",
"version": "3.17.1",
"version": "3.18.0",
"homepage": "https://tiptap.dev",

@@ -98,8 +98,8 @@ "keywords": [

"devDependencies": {
"@tiptap/core": "^3.17.1",
"@tiptap/pm": "^3.17.1"
"@tiptap/pm": "^3.18.0",
"@tiptap/core": "^3.18.0"
},
"peerDependencies": {
"@tiptap/core": "^3.17.1",
"@tiptap/pm": "^3.17.1"
"@tiptap/core": "^3.18.0",
"@tiptap/pm": "^3.18.0"
},

@@ -106,0 +106,0 @@ "repository": {

@@ -7,2 +7,29 @@ import type { Editor } from '@tiptap/core'

/**
* The default data attribute label
*/
const DEFAULT_DATA_ATTRIBUTE = 'placeholder'
/**
* Prepares the placeholder attribute by ensuring it is properly formatted.
* @param attr - The placeholder attribute string.
* @returns The prepared placeholder attribute string.
*/
export function preparePlaceholderAttribute(attr: string): string {
return (
attr
// replace whitespace with dashes
.replace(/\s+/g, '-')
// replace non-alphanumeric characters
// or special chars like $, %, &, etc.
// but not dashes
.replace(/[^a-zA-Z0-9-]/g, '')
// and replace any numeric character at the start
.replace(/^[0-9-]+/, '')
// and finally replace any stray, leading dashes
.replace(/^-+/, '')
.toLowerCase()
)
}
export interface PlaceholderOptions {

@@ -22,2 +49,9 @@ /**

/**
* **The data-attribute used for the placeholder label**
* Will be prepended with `data-` and converted to kebab-case and cleaned of special characters.
* @default 'placeholder'
*/
dataAttribute: string
/**
* **The placeholder content**

@@ -72,2 +106,3 @@ *

emptyNodeClass: 'is-empty',
dataAttribute: DEFAULT_DATA_ATTRIBUTE,
placeholder: 'Write something …',

@@ -81,2 +116,6 @@ showOnlyWhenEditable: true,

addProseMirrorPlugins() {
const dataAttribute = this.options.dataAttribute
? `data-${preparePlaceholderAttribute(this.options.dataAttribute)}`
: `data-${DEFAULT_DATA_ATTRIBUTE}`
return [

@@ -110,3 +149,3 @@ new Plugin({

class: classes.join(' '),
'data-placeholder':
[dataAttribute]:
typeof this.options.placeholder === 'function'

@@ -113,0 +152,0 @@ ? this.options.placeholder({