gdpr audit implemented, email log, vollmachten, pdf delete cancel data privacy and vollmachten, removed message no id card in engergy car, and other contracts that are not telecom contracts, added insert counter for engery
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025, Tiptap GmbH
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
# @tiptap/extensions
|
||||
|
||||
[](https://www.npmjs.com/package/@tiptap/extension-focus)
|
||||
[](https://npmcharts.com/compare/tiptap?minimal=true)
|
||||
[](https://www.npmjs.com/package/@tiptap/extension-focus)
|
||||
[](https://github.com/sponsors/ueberdosis)
|
||||
|
||||
## Introduction
|
||||
|
||||
Tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) – a toolkit for building rich text WYSIWYG editors, which is already in use at many well-known companies such as _New York Times_, _The Guardian_ or _Atlassian_.
|
||||
|
||||
## Official Documentation
|
||||
|
||||
Documentation can be found on the [Tiptap website](https://tiptap.dev).
|
||||
|
||||
## License
|
||||
|
||||
Tiptap is open sourced software licensed under the [MIT license](https://github.com/ueberdosis/tiptap/blob/main/LICENSE.md).
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
"use strict";
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
|
||||
// src/character-count/index.ts
|
||||
var index_exports = {};
|
||||
__export(index_exports, {
|
||||
CharacterCount: () => CharacterCount
|
||||
});
|
||||
module.exports = __toCommonJS(index_exports);
|
||||
|
||||
// src/character-count/character-count.ts
|
||||
var import_core = require("@tiptap/core");
|
||||
var import_state = require("@tiptap/pm/state");
|
||||
var CharacterCount = import_core.Extension.create({
|
||||
name: "characterCount",
|
||||
addOptions() {
|
||||
return {
|
||||
limit: null,
|
||||
mode: "textSize",
|
||||
textCounter: (text) => text.length,
|
||||
wordCounter: (text) => text.split(" ").filter((word) => word !== "").length
|
||||
};
|
||||
},
|
||||
addStorage() {
|
||||
return {
|
||||
characters: () => 0,
|
||||
words: () => 0
|
||||
};
|
||||
},
|
||||
onBeforeCreate() {
|
||||
this.storage.characters = (options) => {
|
||||
const node = (options == null ? void 0 : options.node) || this.editor.state.doc;
|
||||
const mode = (options == null ? void 0 : options.mode) || this.options.mode;
|
||||
if (mode === "textSize") {
|
||||
const text = node.textBetween(0, node.content.size, void 0, " ");
|
||||
return this.options.textCounter(text);
|
||||
}
|
||||
return node.nodeSize;
|
||||
};
|
||||
this.storage.words = (options) => {
|
||||
const node = (options == null ? void 0 : options.node) || this.editor.state.doc;
|
||||
const text = node.textBetween(0, node.content.size, " ", " ");
|
||||
return this.options.wordCounter(text);
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
let initialEvaluationDone = false;
|
||||
return [
|
||||
new import_state.Plugin({
|
||||
key: new import_state.PluginKey("characterCount"),
|
||||
appendTransaction: (transactions, oldState, newState) => {
|
||||
if (initialEvaluationDone) {
|
||||
return;
|
||||
}
|
||||
const limit = this.options.limit;
|
||||
if (limit === null || limit === void 0 || limit === 0) {
|
||||
initialEvaluationDone = true;
|
||||
return;
|
||||
}
|
||||
const initialContentSize = this.storage.characters({ node: newState.doc });
|
||||
if (initialContentSize > limit) {
|
||||
const over = initialContentSize - limit;
|
||||
const from = 0;
|
||||
const to = over;
|
||||
console.warn(
|
||||
`[CharacterCount] Initial content exceeded limit of ${limit} characters. Content was automatically trimmed.`
|
||||
);
|
||||
const tr = newState.tr.deleteRange(from, to);
|
||||
initialEvaluationDone = true;
|
||||
return tr;
|
||||
}
|
||||
initialEvaluationDone = true;
|
||||
},
|
||||
filterTransaction: (transaction, state) => {
|
||||
const limit = this.options.limit;
|
||||
if (!transaction.docChanged || limit === 0 || limit === null || limit === void 0) {
|
||||
return true;
|
||||
}
|
||||
const oldSize = this.storage.characters({ node: state.doc });
|
||||
const newSize = this.storage.characters({ node: transaction.doc });
|
||||
if (newSize <= limit) {
|
||||
return true;
|
||||
}
|
||||
if (oldSize > limit && newSize > limit && newSize <= oldSize) {
|
||||
return true;
|
||||
}
|
||||
if (oldSize > limit && newSize > limit && newSize > oldSize) {
|
||||
return false;
|
||||
}
|
||||
const isPaste = transaction.getMeta("paste");
|
||||
if (!isPaste) {
|
||||
return false;
|
||||
}
|
||||
const pos = transaction.selection.$head.pos;
|
||||
const over = newSize - limit;
|
||||
const from = pos - over;
|
||||
const to = pos;
|
||||
transaction.deleteRange(from, to);
|
||||
const updatedSize = this.storage.characters({ node: transaction.doc });
|
||||
if (updatedSize > limit) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
});
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
CharacterCount
|
||||
});
|
||||
//# sourceMappingURL=index.cjs.map
|
||||
+1
File diff suppressed because one or more lines are too long
+62
@@ -0,0 +1,62 @@
|
||||
import { Extension } from '@tiptap/core';
|
||||
import { Node } from '@tiptap/pm/model';
|
||||
|
||||
interface CharacterCountOptions {
|
||||
/**
|
||||
* The maximum number of characters that should be allowed. Defaults to `0`.
|
||||
* @default null
|
||||
* @example 180
|
||||
*/
|
||||
limit: number | null | undefined;
|
||||
/**
|
||||
* The mode by which the size is calculated. If set to `textSize`, the textContent of the document is used.
|
||||
* If set to `nodeSize`, the nodeSize of the document is used.
|
||||
* @default 'textSize'
|
||||
* @example 'textSize'
|
||||
*/
|
||||
mode: 'textSize' | 'nodeSize';
|
||||
/**
|
||||
* The text counter function to use. Defaults to a simple character count.
|
||||
* @default (text) => text.length
|
||||
* @example (text) => [...new Intl.Segmenter().segment(text)].length
|
||||
*/
|
||||
textCounter: (text: string) => number;
|
||||
/**
|
||||
* The word counter function to use. Defaults to a simple word count.
|
||||
* @default (text) => text.split(' ').filter(word => word !== '').length
|
||||
* @example (text) => text.split(/\s+/).filter(word => word !== '').length
|
||||
*/
|
||||
wordCounter: (text: string) => number;
|
||||
}
|
||||
interface CharacterCountStorage {
|
||||
/**
|
||||
* Get the number of characters for the current document.
|
||||
* @param options The options for the character count. (optional)
|
||||
* @param options.node The node to get the characters from. Defaults to the current document.
|
||||
* @param options.mode The mode by which the size is calculated. If set to `textSize`, the textContent of the document is used.
|
||||
*/
|
||||
characters: (options?: {
|
||||
node?: Node;
|
||||
mode?: 'textSize' | 'nodeSize';
|
||||
}) => number;
|
||||
/**
|
||||
* Get the number of words for the current document.
|
||||
* @param options The options for the character count. (optional)
|
||||
* @param options.node The node to get the words from. Defaults to the current document.
|
||||
*/
|
||||
words: (options?: {
|
||||
node?: Node;
|
||||
}) => number;
|
||||
}
|
||||
declare module '@tiptap/core' {
|
||||
interface Storage {
|
||||
characterCount: CharacterCountStorage;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This extension allows you to count the characters and words of your document.
|
||||
* @see https://tiptap.dev/api/extensions/character-count
|
||||
*/
|
||||
declare const CharacterCount: Extension<CharacterCountOptions, CharacterCountStorage>;
|
||||
|
||||
export { CharacterCount, type CharacterCountOptions, type CharacterCountStorage };
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
import { Extension } from '@tiptap/core';
|
||||
import { Node } from '@tiptap/pm/model';
|
||||
|
||||
interface CharacterCountOptions {
|
||||
/**
|
||||
* The maximum number of characters that should be allowed. Defaults to `0`.
|
||||
* @default null
|
||||
* @example 180
|
||||
*/
|
||||
limit: number | null | undefined;
|
||||
/**
|
||||
* The mode by which the size is calculated. If set to `textSize`, the textContent of the document is used.
|
||||
* If set to `nodeSize`, the nodeSize of the document is used.
|
||||
* @default 'textSize'
|
||||
* @example 'textSize'
|
||||
*/
|
||||
mode: 'textSize' | 'nodeSize';
|
||||
/**
|
||||
* The text counter function to use. Defaults to a simple character count.
|
||||
* @default (text) => text.length
|
||||
* @example (text) => [...new Intl.Segmenter().segment(text)].length
|
||||
*/
|
||||
textCounter: (text: string) => number;
|
||||
/**
|
||||
* The word counter function to use. Defaults to a simple word count.
|
||||
* @default (text) => text.split(' ').filter(word => word !== '').length
|
||||
* @example (text) => text.split(/\s+/).filter(word => word !== '').length
|
||||
*/
|
||||
wordCounter: (text: string) => number;
|
||||
}
|
||||
interface CharacterCountStorage {
|
||||
/**
|
||||
* Get the number of characters for the current document.
|
||||
* @param options The options for the character count. (optional)
|
||||
* @param options.node The node to get the characters from. Defaults to the current document.
|
||||
* @param options.mode The mode by which the size is calculated. If set to `textSize`, the textContent of the document is used.
|
||||
*/
|
||||
characters: (options?: {
|
||||
node?: Node;
|
||||
mode?: 'textSize' | 'nodeSize';
|
||||
}) => number;
|
||||
/**
|
||||
* Get the number of words for the current document.
|
||||
* @param options The options for the character count. (optional)
|
||||
* @param options.node The node to get the words from. Defaults to the current document.
|
||||
*/
|
||||
words: (options?: {
|
||||
node?: Node;
|
||||
}) => number;
|
||||
}
|
||||
declare module '@tiptap/core' {
|
||||
interface Storage {
|
||||
characterCount: CharacterCountStorage;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This extension allows you to count the characters and words of your document.
|
||||
* @see https://tiptap.dev/api/extensions/character-count
|
||||
*/
|
||||
declare const CharacterCount: Extension<CharacterCountOptions, CharacterCountStorage>;
|
||||
|
||||
export { CharacterCount, type CharacterCountOptions, type CharacterCountStorage };
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
// src/character-count/character-count.ts
|
||||
import { Extension } from "@tiptap/core";
|
||||
import { Plugin, PluginKey } from "@tiptap/pm/state";
|
||||
var CharacterCount = Extension.create({
|
||||
name: "characterCount",
|
||||
addOptions() {
|
||||
return {
|
||||
limit: null,
|
||||
mode: "textSize",
|
||||
textCounter: (text) => text.length,
|
||||
wordCounter: (text) => text.split(" ").filter((word) => word !== "").length
|
||||
};
|
||||
},
|
||||
addStorage() {
|
||||
return {
|
||||
characters: () => 0,
|
||||
words: () => 0
|
||||
};
|
||||
},
|
||||
onBeforeCreate() {
|
||||
this.storage.characters = (options) => {
|
||||
const node = (options == null ? void 0 : options.node) || this.editor.state.doc;
|
||||
const mode = (options == null ? void 0 : options.mode) || this.options.mode;
|
||||
if (mode === "textSize") {
|
||||
const text = node.textBetween(0, node.content.size, void 0, " ");
|
||||
return this.options.textCounter(text);
|
||||
}
|
||||
return node.nodeSize;
|
||||
};
|
||||
this.storage.words = (options) => {
|
||||
const node = (options == null ? void 0 : options.node) || this.editor.state.doc;
|
||||
const text = node.textBetween(0, node.content.size, " ", " ");
|
||||
return this.options.wordCounter(text);
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
let initialEvaluationDone = false;
|
||||
return [
|
||||
new Plugin({
|
||||
key: new PluginKey("characterCount"),
|
||||
appendTransaction: (transactions, oldState, newState) => {
|
||||
if (initialEvaluationDone) {
|
||||
return;
|
||||
}
|
||||
const limit = this.options.limit;
|
||||
if (limit === null || limit === void 0 || limit === 0) {
|
||||
initialEvaluationDone = true;
|
||||
return;
|
||||
}
|
||||
const initialContentSize = this.storage.characters({ node: newState.doc });
|
||||
if (initialContentSize > limit) {
|
||||
const over = initialContentSize - limit;
|
||||
const from = 0;
|
||||
const to = over;
|
||||
console.warn(
|
||||
`[CharacterCount] Initial content exceeded limit of ${limit} characters. Content was automatically trimmed.`
|
||||
);
|
||||
const tr = newState.tr.deleteRange(from, to);
|
||||
initialEvaluationDone = true;
|
||||
return tr;
|
||||
}
|
||||
initialEvaluationDone = true;
|
||||
},
|
||||
filterTransaction: (transaction, state) => {
|
||||
const limit = this.options.limit;
|
||||
if (!transaction.docChanged || limit === 0 || limit === null || limit === void 0) {
|
||||
return true;
|
||||
}
|
||||
const oldSize = this.storage.characters({ node: state.doc });
|
||||
const newSize = this.storage.characters({ node: transaction.doc });
|
||||
if (newSize <= limit) {
|
||||
return true;
|
||||
}
|
||||
if (oldSize > limit && newSize > limit && newSize <= oldSize) {
|
||||
return true;
|
||||
}
|
||||
if (oldSize > limit && newSize > limit && newSize > oldSize) {
|
||||
return false;
|
||||
}
|
||||
const isPaste = transaction.getMeta("paste");
|
||||
if (!isPaste) {
|
||||
return false;
|
||||
}
|
||||
const pos = transaction.selection.$head.pos;
|
||||
const over = newSize - limit;
|
||||
const from = pos - over;
|
||||
const to = pos;
|
||||
transaction.deleteRange(from, to);
|
||||
const updatedSize = this.storage.characters({ node: transaction.doc });
|
||||
if (updatedSize > limit) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
});
|
||||
export {
|
||||
CharacterCount
|
||||
};
|
||||
//# sourceMappingURL=index.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+47
@@ -0,0 +1,47 @@
|
||||
"use strict";
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
|
||||
// src/drop-cursor/index.ts
|
||||
var index_exports = {};
|
||||
__export(index_exports, {
|
||||
Dropcursor: () => Dropcursor
|
||||
});
|
||||
module.exports = __toCommonJS(index_exports);
|
||||
|
||||
// src/drop-cursor/drop-cursor.ts
|
||||
var import_core = require("@tiptap/core");
|
||||
var import_dropcursor = require("@tiptap/pm/dropcursor");
|
||||
var Dropcursor = import_core.Extension.create({
|
||||
name: "dropCursor",
|
||||
addOptions() {
|
||||
return {
|
||||
color: "currentColor",
|
||||
width: 1,
|
||||
class: void 0
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
return [(0, import_dropcursor.dropCursor)(this.options)];
|
||||
}
|
||||
});
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
Dropcursor
|
||||
});
|
||||
//# sourceMappingURL=index.cjs.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../src/drop-cursor/index.ts","../../src/drop-cursor/drop-cursor.ts"],"sourcesContent":["export * from './drop-cursor.js'\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"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,kBAA0B;AAC1B,wBAA2B;AA+BpB,IAAM,aAAa,sBAAU,OAA0B;AAAA,EAC5D,MAAM;AAAA,EAEN,aAAa;AACX,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,wBAAwB;AACtB,WAAO,KAAC,8BAAW,KAAK,OAAO,CAAC;AAAA,EAClC;AACF,CAAC;","names":[]}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import { Extension } from '@tiptap/core';
|
||||
|
||||
interface DropcursorOptions {
|
||||
/**
|
||||
* The color of the drop cursor. Use `false` to apply no color and rely only on class.
|
||||
* @default 'currentColor'
|
||||
* @example 'red'
|
||||
*/
|
||||
color?: string | false;
|
||||
/**
|
||||
* The width of the drop cursor
|
||||
* @default 1
|
||||
* @example 2
|
||||
*/
|
||||
width: number | undefined;
|
||||
/**
|
||||
* The class of the drop cursor
|
||||
* @default undefined
|
||||
* @example 'drop-cursor'
|
||||
*/
|
||||
class: string | undefined;
|
||||
}
|
||||
/**
|
||||
* This extension allows you to add a drop cursor to your editor.
|
||||
* A drop cursor is a line that appears when you drag and drop content
|
||||
* in-between nodes.
|
||||
* @see https://tiptap.dev/api/extensions/dropcursor
|
||||
*/
|
||||
declare const Dropcursor: Extension<DropcursorOptions, any>;
|
||||
|
||||
export { Dropcursor, type DropcursorOptions };
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import { Extension } from '@tiptap/core';
|
||||
|
||||
interface DropcursorOptions {
|
||||
/**
|
||||
* The color of the drop cursor. Use `false` to apply no color and rely only on class.
|
||||
* @default 'currentColor'
|
||||
* @example 'red'
|
||||
*/
|
||||
color?: string | false;
|
||||
/**
|
||||
* The width of the drop cursor
|
||||
* @default 1
|
||||
* @example 2
|
||||
*/
|
||||
width: number | undefined;
|
||||
/**
|
||||
* The class of the drop cursor
|
||||
* @default undefined
|
||||
* @example 'drop-cursor'
|
||||
*/
|
||||
class: string | undefined;
|
||||
}
|
||||
/**
|
||||
* This extension allows you to add a drop cursor to your editor.
|
||||
* A drop cursor is a line that appears when you drag and drop content
|
||||
* in-between nodes.
|
||||
* @see https://tiptap.dev/api/extensions/dropcursor
|
||||
*/
|
||||
declare const Dropcursor: Extension<DropcursorOptions, any>;
|
||||
|
||||
export { Dropcursor, type DropcursorOptions };
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// src/drop-cursor/drop-cursor.ts
|
||||
import { Extension } from "@tiptap/core";
|
||||
import { dropCursor } from "@tiptap/pm/dropcursor";
|
||||
var Dropcursor = Extension.create({
|
||||
name: "dropCursor",
|
||||
addOptions() {
|
||||
return {
|
||||
color: "currentColor",
|
||||
width: 1,
|
||||
class: void 0
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
return [dropCursor(this.options)];
|
||||
}
|
||||
});
|
||||
export {
|
||||
Dropcursor
|
||||
};
|
||||
//# sourceMappingURL=index.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../src/drop-cursor/drop-cursor.ts"],"sourcesContent":["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"],"mappings":";AAAA,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AA+BpB,IAAM,aAAa,UAAU,OAA0B;AAAA,EAC5D,MAAM;AAAA,EAEN,aAAa;AACX,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,wBAAwB;AACtB,WAAO,CAAC,WAAW,KAAK,OAAO,CAAC;AAAA,EAClC;AACF,CAAC;","names":[]}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
"use strict";
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
|
||||
// src/focus/index.ts
|
||||
var index_exports = {};
|
||||
__export(index_exports, {
|
||||
Focus: () => Focus
|
||||
});
|
||||
module.exports = __toCommonJS(index_exports);
|
||||
|
||||
// src/focus/focus.ts
|
||||
var import_core = require("@tiptap/core");
|
||||
var import_state = require("@tiptap/pm/state");
|
||||
var import_view = require("@tiptap/pm/view");
|
||||
var Focus = import_core.Extension.create({
|
||||
name: "focus",
|
||||
addOptions() {
|
||||
return {
|
||||
className: "has-focus",
|
||||
mode: "all"
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
return [
|
||||
new import_state.Plugin({
|
||||
key: new import_state.PluginKey("focus"),
|
||||
props: {
|
||||
decorations: ({ doc, selection }) => {
|
||||
const { isEditable, isFocused } = this.editor;
|
||||
const { anchor } = selection;
|
||||
const decorations = [];
|
||||
if (!isEditable || !isFocused) {
|
||||
return import_view.DecorationSet.create(doc, []);
|
||||
}
|
||||
let maxLevels = 0;
|
||||
if (this.options.mode === "deepest") {
|
||||
doc.descendants((node, pos) => {
|
||||
if (node.isText) {
|
||||
return;
|
||||
}
|
||||
const isCurrent = anchor >= pos && anchor <= pos + node.nodeSize - 1;
|
||||
if (!isCurrent) {
|
||||
return false;
|
||||
}
|
||||
maxLevels += 1;
|
||||
});
|
||||
}
|
||||
let currentLevel = 0;
|
||||
doc.descendants((node, pos) => {
|
||||
if (node.isText) {
|
||||
return false;
|
||||
}
|
||||
const isCurrent = anchor >= pos && anchor <= pos + node.nodeSize - 1;
|
||||
if (!isCurrent) {
|
||||
return false;
|
||||
}
|
||||
currentLevel += 1;
|
||||
const outOfScope = this.options.mode === "deepest" && maxLevels - currentLevel > 0 || this.options.mode === "shallowest" && currentLevel > 1;
|
||||
if (outOfScope) {
|
||||
return this.options.mode === "deepest";
|
||||
}
|
||||
decorations.push(
|
||||
import_view.Decoration.node(pos, pos + node.nodeSize, {
|
||||
class: this.options.className
|
||||
})
|
||||
);
|
||||
});
|
||||
return import_view.DecorationSet.create(doc, decorations);
|
||||
}
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
});
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
Focus
|
||||
});
|
||||
//# sourceMappingURL=index.cjs.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../src/focus/index.ts","../../src/focus/focus.ts"],"sourcesContent":["export * from './focus.js'\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"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,kBAA0B;AAC1B,mBAAkC;AAClC,kBAA0C;AA2BnC,IAAM,QAAQ,sBAAU,OAAqB;AAAA,EAClD,MAAM;AAAA,EAEN,aAAa;AACX,WAAO;AAAA,MACL,WAAW;AAAA,MACX,MAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,wBAAwB;AACtB,WAAO;AAAA,MACL,IAAI,oBAAO;AAAA,QACT,KAAK,IAAI,uBAAU,OAAO;AAAA,QAC1B,OAAO;AAAA,UACL,aAAa,CAAC,EAAE,KAAK,UAAU,MAAM;AACnC,kBAAM,EAAE,YAAY,UAAU,IAAI,KAAK;AACvC,kBAAM,EAAE,OAAO,IAAI;AACnB,kBAAM,cAA4B,CAAC;AAEnC,gBAAI,CAAC,cAAc,CAAC,WAAW;AAC7B,qBAAO,0BAAc,OAAO,KAAK,CAAC,CAAC;AAAA,YACrC;AAGA,gBAAI,YAAY;AAEhB,gBAAI,KAAK,QAAQ,SAAS,WAAW;AACnC,kBAAI,YAAY,CAAC,MAAM,QAAQ;AAC7B,oBAAI,KAAK,QAAQ;AACf;AAAA,gBACF;AAEA,sBAAM,YAAY,UAAU,OAAO,UAAU,MAAM,KAAK,WAAW;AAEnE,oBAAI,CAAC,WAAW;AACd,yBAAO;AAAA,gBACT;AAEA,6BAAa;AAAA,cACf,CAAC;AAAA,YACH;AAGA,gBAAI,eAAe;AAEnB,gBAAI,YAAY,CAAC,MAAM,QAAQ;AAC7B,kBAAI,KAAK,QAAQ;AACf,uBAAO;AAAA,cACT;AAEA,oBAAM,YAAY,UAAU,OAAO,UAAU,MAAM,KAAK,WAAW;AAEnE,kBAAI,CAAC,WAAW;AACd,uBAAO;AAAA,cACT;AAEA,8BAAgB;AAEhB,oBAAM,aACH,KAAK,QAAQ,SAAS,aAAa,YAAY,eAAe,KAC9D,KAAK,QAAQ,SAAS,gBAAgB,eAAe;AAExD,kBAAI,YAAY;AACd,uBAAO,KAAK,QAAQ,SAAS;AAAA,cAC/B;AAEA,0BAAY;AAAA,gBACV,uBAAW,KAAK,KAAK,MAAM,KAAK,UAAU;AAAA,kBACxC,OAAO,KAAK,QAAQ;AAAA,gBACtB,CAAC;AAAA,cACH;AAAA,YACF,CAAC;AAED,mBAAO,0BAAc,OAAO,KAAK,WAAW;AAAA,UAC9C;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;","names":[]}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
import { Extension } from '@tiptap/core';
|
||||
|
||||
interface FocusOptions {
|
||||
/**
|
||||
* The class name that should be added to the focused node.
|
||||
* @default 'has-focus'
|
||||
* @example 'is-focused'
|
||||
*/
|
||||
className: string;
|
||||
/**
|
||||
* The mode by which the focused node is determined.
|
||||
* - All: All nodes are marked as focused.
|
||||
* - Deepest: Only the deepest node is marked as focused.
|
||||
* - Shallowest: Only the shallowest node is marked as focused.
|
||||
*
|
||||
* @default 'all'
|
||||
* @example 'deepest'
|
||||
* @example 'shallowest'
|
||||
*/
|
||||
mode: 'all' | 'deepest' | 'shallowest';
|
||||
}
|
||||
/**
|
||||
* This extension allows you to add a class to the focused node.
|
||||
* @see https://www.tiptap.dev/api/extensions/focus
|
||||
*/
|
||||
declare const Focus: Extension<FocusOptions, any>;
|
||||
|
||||
export { Focus, type FocusOptions };
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
import { Extension } from '@tiptap/core';
|
||||
|
||||
interface FocusOptions {
|
||||
/**
|
||||
* The class name that should be added to the focused node.
|
||||
* @default 'has-focus'
|
||||
* @example 'is-focused'
|
||||
*/
|
||||
className: string;
|
||||
/**
|
||||
* The mode by which the focused node is determined.
|
||||
* - All: All nodes are marked as focused.
|
||||
* - Deepest: Only the deepest node is marked as focused.
|
||||
* - Shallowest: Only the shallowest node is marked as focused.
|
||||
*
|
||||
* @default 'all'
|
||||
* @example 'deepest'
|
||||
* @example 'shallowest'
|
||||
*/
|
||||
mode: 'all' | 'deepest' | 'shallowest';
|
||||
}
|
||||
/**
|
||||
* This extension allows you to add a class to the focused node.
|
||||
* @see https://www.tiptap.dev/api/extensions/focus
|
||||
*/
|
||||
declare const Focus: Extension<FocusOptions, any>;
|
||||
|
||||
export { Focus, type FocusOptions };
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
// src/focus/focus.ts
|
||||
import { Extension } from "@tiptap/core";
|
||||
import { Plugin, PluginKey } from "@tiptap/pm/state";
|
||||
import { Decoration, DecorationSet } from "@tiptap/pm/view";
|
||||
var Focus = Extension.create({
|
||||
name: "focus",
|
||||
addOptions() {
|
||||
return {
|
||||
className: "has-focus",
|
||||
mode: "all"
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
return [
|
||||
new Plugin({
|
||||
key: new PluginKey("focus"),
|
||||
props: {
|
||||
decorations: ({ doc, selection }) => {
|
||||
const { isEditable, isFocused } = this.editor;
|
||||
const { anchor } = selection;
|
||||
const decorations = [];
|
||||
if (!isEditable || !isFocused) {
|
||||
return DecorationSet.create(doc, []);
|
||||
}
|
||||
let maxLevels = 0;
|
||||
if (this.options.mode === "deepest") {
|
||||
doc.descendants((node, pos) => {
|
||||
if (node.isText) {
|
||||
return;
|
||||
}
|
||||
const isCurrent = anchor >= pos && anchor <= pos + node.nodeSize - 1;
|
||||
if (!isCurrent) {
|
||||
return false;
|
||||
}
|
||||
maxLevels += 1;
|
||||
});
|
||||
}
|
||||
let currentLevel = 0;
|
||||
doc.descendants((node, pos) => {
|
||||
if (node.isText) {
|
||||
return false;
|
||||
}
|
||||
const isCurrent = anchor >= pos && anchor <= pos + node.nodeSize - 1;
|
||||
if (!isCurrent) {
|
||||
return false;
|
||||
}
|
||||
currentLevel += 1;
|
||||
const outOfScope = this.options.mode === "deepest" && maxLevels - currentLevel > 0 || this.options.mode === "shallowest" && currentLevel > 1;
|
||||
if (outOfScope) {
|
||||
return this.options.mode === "deepest";
|
||||
}
|
||||
decorations.push(
|
||||
Decoration.node(pos, pos + node.nodeSize, {
|
||||
class: this.options.className
|
||||
})
|
||||
);
|
||||
});
|
||||
return DecorationSet.create(doc, decorations);
|
||||
}
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
});
|
||||
export {
|
||||
Focus
|
||||
};
|
||||
//# sourceMappingURL=index.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../src/focus/focus.ts"],"sourcesContent":["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"],"mappings":";AAAA,SAAS,iBAAiB;AAC1B,SAAS,QAAQ,iBAAiB;AAClC,SAAS,YAAY,qBAAqB;AA2BnC,IAAM,QAAQ,UAAU,OAAqB;AAAA,EAClD,MAAM;AAAA,EAEN,aAAa;AACX,WAAO;AAAA,MACL,WAAW;AAAA,MACX,MAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,wBAAwB;AACtB,WAAO;AAAA,MACL,IAAI,OAAO;AAAA,QACT,KAAK,IAAI,UAAU,OAAO;AAAA,QAC1B,OAAO;AAAA,UACL,aAAa,CAAC,EAAE,KAAK,UAAU,MAAM;AACnC,kBAAM,EAAE,YAAY,UAAU,IAAI,KAAK;AACvC,kBAAM,EAAE,OAAO,IAAI;AACnB,kBAAM,cAA4B,CAAC;AAEnC,gBAAI,CAAC,cAAc,CAAC,WAAW;AAC7B,qBAAO,cAAc,OAAO,KAAK,CAAC,CAAC;AAAA,YACrC;AAGA,gBAAI,YAAY;AAEhB,gBAAI,KAAK,QAAQ,SAAS,WAAW;AACnC,kBAAI,YAAY,CAAC,MAAM,QAAQ;AAC7B,oBAAI,KAAK,QAAQ;AACf;AAAA,gBACF;AAEA,sBAAM,YAAY,UAAU,OAAO,UAAU,MAAM,KAAK,WAAW;AAEnE,oBAAI,CAAC,WAAW;AACd,yBAAO;AAAA,gBACT;AAEA,6BAAa;AAAA,cACf,CAAC;AAAA,YACH;AAGA,gBAAI,eAAe;AAEnB,gBAAI,YAAY,CAAC,MAAM,QAAQ;AAC7B,kBAAI,KAAK,QAAQ;AACf,uBAAO;AAAA,cACT;AAEA,oBAAM,YAAY,UAAU,OAAO,UAAU,MAAM,KAAK,WAAW;AAEnE,kBAAI,CAAC,WAAW;AACd,uBAAO;AAAA,cACT;AAEA,8BAAgB;AAEhB,oBAAM,aACH,KAAK,QAAQ,SAAS,aAAa,YAAY,eAAe,KAC9D,KAAK,QAAQ,SAAS,gBAAgB,eAAe;AAExD,kBAAI,YAAY;AACd,uBAAO,KAAK,QAAQ,SAAS;AAAA,cAC/B;AAEA,0BAAY;AAAA,gBACV,WAAW,KAAK,KAAK,MAAM,KAAK,UAAU;AAAA,kBACxC,OAAO,KAAK,QAAQ;AAAA,gBACtB,CAAC;AAAA,cACH;AAAA,YACF,CAAC;AAED,mBAAO,cAAc,OAAO,KAAK,WAAW;AAAA,UAC9C;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;","names":[]}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
"use strict";
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
|
||||
// src/gap-cursor/index.ts
|
||||
var index_exports = {};
|
||||
__export(index_exports, {
|
||||
Gapcursor: () => Gapcursor
|
||||
});
|
||||
module.exports = __toCommonJS(index_exports);
|
||||
|
||||
// src/gap-cursor/gap-cursor.ts
|
||||
var import_core = require("@tiptap/core");
|
||||
var import_gapcursor = require("@tiptap/pm/gapcursor");
|
||||
var Gapcursor = import_core.Extension.create({
|
||||
name: "gapCursor",
|
||||
addProseMirrorPlugins() {
|
||||
return [(0, import_gapcursor.gapCursor)()];
|
||||
},
|
||||
extendNodeSchema(extension) {
|
||||
var _a;
|
||||
const context = {
|
||||
name: extension.name,
|
||||
options: extension.options,
|
||||
storage: extension.storage
|
||||
};
|
||||
return {
|
||||
allowGapCursor: (_a = (0, import_core.callOrReturn)((0, import_core.getExtensionField)(extension, "allowGapCursor", context))) != null ? _a : null
|
||||
};
|
||||
}
|
||||
});
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
Gapcursor
|
||||
});
|
||||
//# sourceMappingURL=index.cjs.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../src/gap-cursor/index.ts","../../src/gap-cursor/gap-cursor.ts"],"sourcesContent":["export * from './gap-cursor.js'\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"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACCA,kBAA2D;AAC3D,uBAA0B;AA0BnB,IAAM,YAAY,sBAAU,OAAO;AAAA,EACxC,MAAM;AAAA,EAEN,wBAAwB;AACtB,WAAO,KAAC,4BAAU,CAAC;AAAA,EACrB;AAAA,EAEA,iBAAiB,WAAW;AAnC9B;AAoCI,UAAM,UAAU;AAAA,MACd,MAAM,UAAU;AAAA,MAChB,SAAS,UAAU;AAAA,MACnB,SAAS,UAAU;AAAA,IACrB;AAEA,WAAO;AAAA,MACL,iBAAgB,uCAAa,+BAAkB,WAAW,kBAAkB,OAAO,CAAC,MAApE,YAAyE;AAAA,IAC3F;AAAA,EACF;AACF,CAAC;","names":[]}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { ParentConfig, Extension } from '@tiptap/core';
|
||||
|
||||
declare module '@tiptap/core' {
|
||||
interface NodeConfig<Options, Storage> {
|
||||
/**
|
||||
* A function to determine whether the gap cursor is allowed at the current position. Must return `true` or `false`.
|
||||
* @default null
|
||||
*/
|
||||
allowGapCursor?: boolean | null | ((this: {
|
||||
name: string;
|
||||
options: Options;
|
||||
storage: Storage;
|
||||
parent: ParentConfig<NodeConfig<Options>>['allowGapCursor'];
|
||||
}) => boolean | null);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This extension allows you to add a gap cursor to your editor.
|
||||
* A gap cursor is a cursor that appears when you click on a place
|
||||
* where no content is present, for example inbetween nodes.
|
||||
* @see https://tiptap.dev/api/extensions/gapcursor
|
||||
*/
|
||||
declare const Gapcursor: Extension<any, any>;
|
||||
|
||||
export { Gapcursor };
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { ParentConfig, Extension } from '@tiptap/core';
|
||||
|
||||
declare module '@tiptap/core' {
|
||||
interface NodeConfig<Options, Storage> {
|
||||
/**
|
||||
* A function to determine whether the gap cursor is allowed at the current position. Must return `true` or `false`.
|
||||
* @default null
|
||||
*/
|
||||
allowGapCursor?: boolean | null | ((this: {
|
||||
name: string;
|
||||
options: Options;
|
||||
storage: Storage;
|
||||
parent: ParentConfig<NodeConfig<Options>>['allowGapCursor'];
|
||||
}) => boolean | null);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This extension allows you to add a gap cursor to your editor.
|
||||
* A gap cursor is a cursor that appears when you click on a place
|
||||
* where no content is present, for example inbetween nodes.
|
||||
* @see https://tiptap.dev/api/extensions/gapcursor
|
||||
*/
|
||||
declare const Gapcursor: Extension<any, any>;
|
||||
|
||||
export { Gapcursor };
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
// src/gap-cursor/gap-cursor.ts
|
||||
import { callOrReturn, Extension, getExtensionField } from "@tiptap/core";
|
||||
import { gapCursor } from "@tiptap/pm/gapcursor";
|
||||
var Gapcursor = Extension.create({
|
||||
name: "gapCursor",
|
||||
addProseMirrorPlugins() {
|
||||
return [gapCursor()];
|
||||
},
|
||||
extendNodeSchema(extension) {
|
||||
var _a;
|
||||
const context = {
|
||||
name: extension.name,
|
||||
options: extension.options,
|
||||
storage: extension.storage
|
||||
};
|
||||
return {
|
||||
allowGapCursor: (_a = callOrReturn(getExtensionField(extension, "allowGapCursor", context))) != null ? _a : null
|
||||
};
|
||||
}
|
||||
});
|
||||
export {
|
||||
Gapcursor
|
||||
};
|
||||
//# sourceMappingURL=index.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../src/gap-cursor/gap-cursor.ts"],"sourcesContent":["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"],"mappings":";AACA,SAAS,cAAc,WAAW,yBAAyB;AAC3D,SAAS,iBAAiB;AA0BnB,IAAM,YAAY,UAAU,OAAO;AAAA,EACxC,MAAM;AAAA,EAEN,wBAAwB;AACtB,WAAO,CAAC,UAAU,CAAC;AAAA,EACrB;AAAA,EAEA,iBAAiB,WAAW;AAnC9B;AAoCI,UAAM,UAAU;AAAA,MACd,MAAM,UAAU;AAAA,MAChB,SAAS,UAAU;AAAA,MACnB,SAAS,UAAU;AAAA,IACrB;AAEA,WAAO;AAAA,MACL,iBAAgB,kBAAa,kBAAkB,WAAW,kBAAkB,OAAO,CAAC,MAApE,YAAyE;AAAA,IAC3F;AAAA,EACF;AACF,CAAC;","names":[]}
|
||||
+434
@@ -0,0 +1,434 @@
|
||||
"use strict";
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
|
||||
// src/index.ts
|
||||
var index_exports = {};
|
||||
__export(index_exports, {
|
||||
CharacterCount: () => CharacterCount,
|
||||
Dropcursor: () => Dropcursor,
|
||||
Focus: () => Focus,
|
||||
Gapcursor: () => Gapcursor,
|
||||
Placeholder: () => Placeholder,
|
||||
Selection: () => Selection,
|
||||
TrailingNode: () => TrailingNode,
|
||||
UndoRedo: () => UndoRedo,
|
||||
preparePlaceholderAttribute: () => preparePlaceholderAttribute
|
||||
});
|
||||
module.exports = __toCommonJS(index_exports);
|
||||
|
||||
// src/character-count/character-count.ts
|
||||
var import_core = require("@tiptap/core");
|
||||
var import_state = require("@tiptap/pm/state");
|
||||
var CharacterCount = import_core.Extension.create({
|
||||
name: "characterCount",
|
||||
addOptions() {
|
||||
return {
|
||||
limit: null,
|
||||
mode: "textSize",
|
||||
textCounter: (text) => text.length,
|
||||
wordCounter: (text) => text.split(" ").filter((word) => word !== "").length
|
||||
};
|
||||
},
|
||||
addStorage() {
|
||||
return {
|
||||
characters: () => 0,
|
||||
words: () => 0
|
||||
};
|
||||
},
|
||||
onBeforeCreate() {
|
||||
this.storage.characters = (options) => {
|
||||
const node = (options == null ? void 0 : options.node) || this.editor.state.doc;
|
||||
const mode = (options == null ? void 0 : options.mode) || this.options.mode;
|
||||
if (mode === "textSize") {
|
||||
const text = node.textBetween(0, node.content.size, void 0, " ");
|
||||
return this.options.textCounter(text);
|
||||
}
|
||||
return node.nodeSize;
|
||||
};
|
||||
this.storage.words = (options) => {
|
||||
const node = (options == null ? void 0 : options.node) || this.editor.state.doc;
|
||||
const text = node.textBetween(0, node.content.size, " ", " ");
|
||||
return this.options.wordCounter(text);
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
let initialEvaluationDone = false;
|
||||
return [
|
||||
new import_state.Plugin({
|
||||
key: new import_state.PluginKey("characterCount"),
|
||||
appendTransaction: (transactions, oldState, newState) => {
|
||||
if (initialEvaluationDone) {
|
||||
return;
|
||||
}
|
||||
const limit = this.options.limit;
|
||||
if (limit === null || limit === void 0 || limit === 0) {
|
||||
initialEvaluationDone = true;
|
||||
return;
|
||||
}
|
||||
const initialContentSize = this.storage.characters({ node: newState.doc });
|
||||
if (initialContentSize > limit) {
|
||||
const over = initialContentSize - limit;
|
||||
const from = 0;
|
||||
const to = over;
|
||||
console.warn(
|
||||
`[CharacterCount] Initial content exceeded limit of ${limit} characters. Content was automatically trimmed.`
|
||||
);
|
||||
const tr = newState.tr.deleteRange(from, to);
|
||||
initialEvaluationDone = true;
|
||||
return tr;
|
||||
}
|
||||
initialEvaluationDone = true;
|
||||
},
|
||||
filterTransaction: (transaction, state) => {
|
||||
const limit = this.options.limit;
|
||||
if (!transaction.docChanged || limit === 0 || limit === null || limit === void 0) {
|
||||
return true;
|
||||
}
|
||||
const oldSize = this.storage.characters({ node: state.doc });
|
||||
const newSize = this.storage.characters({ node: transaction.doc });
|
||||
if (newSize <= limit) {
|
||||
return true;
|
||||
}
|
||||
if (oldSize > limit && newSize > limit && newSize <= oldSize) {
|
||||
return true;
|
||||
}
|
||||
if (oldSize > limit && newSize > limit && newSize > oldSize) {
|
||||
return false;
|
||||
}
|
||||
const isPaste = transaction.getMeta("paste");
|
||||
if (!isPaste) {
|
||||
return false;
|
||||
}
|
||||
const pos = transaction.selection.$head.pos;
|
||||
const over = newSize - limit;
|
||||
const from = pos - over;
|
||||
const to = pos;
|
||||
transaction.deleteRange(from, to);
|
||||
const updatedSize = this.storage.characters({ node: transaction.doc });
|
||||
if (updatedSize > limit) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
// src/drop-cursor/drop-cursor.ts
|
||||
var import_core2 = require("@tiptap/core");
|
||||
var import_dropcursor = require("@tiptap/pm/dropcursor");
|
||||
var Dropcursor = import_core2.Extension.create({
|
||||
name: "dropCursor",
|
||||
addOptions() {
|
||||
return {
|
||||
color: "currentColor",
|
||||
width: 1,
|
||||
class: void 0
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
return [(0, import_dropcursor.dropCursor)(this.options)];
|
||||
}
|
||||
});
|
||||
|
||||
// src/focus/focus.ts
|
||||
var import_core3 = require("@tiptap/core");
|
||||
var import_state2 = require("@tiptap/pm/state");
|
||||
var import_view = require("@tiptap/pm/view");
|
||||
var Focus = import_core3.Extension.create({
|
||||
name: "focus",
|
||||
addOptions() {
|
||||
return {
|
||||
className: "has-focus",
|
||||
mode: "all"
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
return [
|
||||
new import_state2.Plugin({
|
||||
key: new import_state2.PluginKey("focus"),
|
||||
props: {
|
||||
decorations: ({ doc, selection }) => {
|
||||
const { isEditable, isFocused } = this.editor;
|
||||
const { anchor } = selection;
|
||||
const decorations = [];
|
||||
if (!isEditable || !isFocused) {
|
||||
return import_view.DecorationSet.create(doc, []);
|
||||
}
|
||||
let maxLevels = 0;
|
||||
if (this.options.mode === "deepest") {
|
||||
doc.descendants((node, pos) => {
|
||||
if (node.isText) {
|
||||
return;
|
||||
}
|
||||
const isCurrent = anchor >= pos && anchor <= pos + node.nodeSize - 1;
|
||||
if (!isCurrent) {
|
||||
return false;
|
||||
}
|
||||
maxLevels += 1;
|
||||
});
|
||||
}
|
||||
let currentLevel = 0;
|
||||
doc.descendants((node, pos) => {
|
||||
if (node.isText) {
|
||||
return false;
|
||||
}
|
||||
const isCurrent = anchor >= pos && anchor <= pos + node.nodeSize - 1;
|
||||
if (!isCurrent) {
|
||||
return false;
|
||||
}
|
||||
currentLevel += 1;
|
||||
const outOfScope = this.options.mode === "deepest" && maxLevels - currentLevel > 0 || this.options.mode === "shallowest" && currentLevel > 1;
|
||||
if (outOfScope) {
|
||||
return this.options.mode === "deepest";
|
||||
}
|
||||
decorations.push(
|
||||
import_view.Decoration.node(pos, pos + node.nodeSize, {
|
||||
class: this.options.className
|
||||
})
|
||||
);
|
||||
});
|
||||
return import_view.DecorationSet.create(doc, decorations);
|
||||
}
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
// src/gap-cursor/gap-cursor.ts
|
||||
var import_core4 = require("@tiptap/core");
|
||||
var import_gapcursor = require("@tiptap/pm/gapcursor");
|
||||
var Gapcursor = import_core4.Extension.create({
|
||||
name: "gapCursor",
|
||||
addProseMirrorPlugins() {
|
||||
return [(0, import_gapcursor.gapCursor)()];
|
||||
},
|
||||
extendNodeSchema(extension) {
|
||||
var _a;
|
||||
const context = {
|
||||
name: extension.name,
|
||||
options: extension.options,
|
||||
storage: extension.storage
|
||||
};
|
||||
return {
|
||||
allowGapCursor: (_a = (0, import_core4.callOrReturn)((0, import_core4.getExtensionField)(extension, "allowGapCursor", context))) != null ? _a : null
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// src/placeholder/placeholder.ts
|
||||
var import_core5 = require("@tiptap/core");
|
||||
var import_state3 = require("@tiptap/pm/state");
|
||||
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({
|
||||
name: "placeholder",
|
||||
addOptions() {
|
||||
return {
|
||||
emptyEditorClass: "is-editor-empty",
|
||||
emptyNodeClass: "is-empty",
|
||||
dataAttribute: DEFAULT_DATA_ATTRIBUTE,
|
||||
placeholder: "Write something \u2026",
|
||||
showOnlyWhenEditable: true,
|
||||
showOnlyCurrent: true,
|
||||
includeChildren: false
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
const dataAttribute = this.options.dataAttribute ? `data-${preparePlaceholderAttribute(this.options.dataAttribute)}` : `data-${DEFAULT_DATA_ATTRIBUTE}`;
|
||||
return [
|
||||
new import_state3.Plugin({
|
||||
key: new import_state3.PluginKey("placeholder"),
|
||||
props: {
|
||||
decorations: ({ doc, selection }) => {
|
||||
const active = this.editor.isEditable || !this.options.showOnlyWhenEditable;
|
||||
const { anchor } = selection;
|
||||
const decorations = [];
|
||||
if (!active) {
|
||||
return null;
|
||||
}
|
||||
const isEmptyDoc = this.editor.isEmpty;
|
||||
doc.descendants((node, pos) => {
|
||||
const hasAnchor = anchor >= pos && anchor <= pos + node.nodeSize;
|
||||
const isEmpty = !node.isLeaf && (0, import_core5.isNodeEmpty)(node);
|
||||
if ((hasAnchor || !this.options.showOnlyCurrent) && isEmpty) {
|
||||
const classes = [this.options.emptyNodeClass];
|
||||
if (isEmptyDoc) {
|
||||
classes.push(this.options.emptyEditorClass);
|
||||
}
|
||||
const decoration = import_view2.Decoration.node(pos, pos + node.nodeSize, {
|
||||
class: classes.join(" "),
|
||||
[dataAttribute]: typeof this.options.placeholder === "function" ? this.options.placeholder({
|
||||
editor: this.editor,
|
||||
node,
|
||||
pos,
|
||||
hasAnchor
|
||||
}) : this.options.placeholder
|
||||
});
|
||||
decorations.push(decoration);
|
||||
}
|
||||
return this.options.includeChildren;
|
||||
});
|
||||
return import_view2.DecorationSet.create(doc, decorations);
|
||||
}
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
// src/selection/selection.ts
|
||||
var import_core6 = require("@tiptap/core");
|
||||
var import_state4 = require("@tiptap/pm/state");
|
||||
var import_view3 = require("@tiptap/pm/view");
|
||||
var Selection = import_core6.Extension.create({
|
||||
name: "selection",
|
||||
addOptions() {
|
||||
return {
|
||||
className: "selection"
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
const { editor, options } = this;
|
||||
return [
|
||||
new import_state4.Plugin({
|
||||
key: new import_state4.PluginKey("selection"),
|
||||
props: {
|
||||
decorations(state) {
|
||||
if (state.selection.empty || editor.isFocused || !editor.isEditable || (0, import_core6.isNodeSelection)(state.selection) || editor.view.dragging) {
|
||||
return null;
|
||||
}
|
||||
return import_view3.DecorationSet.create(state.doc, [
|
||||
import_view3.Decoration.inline(state.selection.from, state.selection.to, {
|
||||
class: options.className
|
||||
})
|
||||
]);
|
||||
}
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
// src/trailing-node/trailing-node.ts
|
||||
var import_core7 = require("@tiptap/core");
|
||||
var import_state5 = require("@tiptap/pm/state");
|
||||
function nodeEqualsType({ types, node }) {
|
||||
return node && Array.isArray(types) && types.includes(node.type) || (node == null ? void 0 : node.type) === types;
|
||||
}
|
||||
var TrailingNode = import_core7.Extension.create({
|
||||
name: "trailingNode",
|
||||
addOptions() {
|
||||
return {
|
||||
node: void 0,
|
||||
notAfter: []
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
var _a;
|
||||
const plugin = new import_state5.PluginKey(this.name);
|
||||
const defaultNode = this.options.node || ((_a = this.editor.schema.topNodeType.contentMatch.defaultType) == null ? void 0 : _a.name) || "paragraph";
|
||||
const disabledNodes = Object.entries(this.editor.schema.nodes).map(([, value]) => value).filter((node) => (this.options.notAfter || []).concat(defaultNode).includes(node.name));
|
||||
return [
|
||||
new import_state5.Plugin({
|
||||
key: plugin,
|
||||
appendTransaction: (_, __, state) => {
|
||||
const { doc, tr, schema } = state;
|
||||
const shouldInsertNodeAtEnd = plugin.getState(state);
|
||||
const endPosition = doc.content.size;
|
||||
const type = schema.nodes[defaultNode];
|
||||
if (!shouldInsertNodeAtEnd) {
|
||||
return;
|
||||
}
|
||||
return tr.insert(endPosition, type.create());
|
||||
},
|
||||
state: {
|
||||
init: (_, state) => {
|
||||
const lastNode = state.tr.doc.lastChild;
|
||||
return !nodeEqualsType({ node: lastNode, types: disabledNodes });
|
||||
},
|
||||
apply: (tr, value) => {
|
||||
if (!tr.docChanged) {
|
||||
return value;
|
||||
}
|
||||
if (tr.getMeta("__uniqueIDTransaction")) {
|
||||
return value;
|
||||
}
|
||||
const lastNode = tr.doc.lastChild;
|
||||
return !nodeEqualsType({ node: lastNode, types: disabledNodes });
|
||||
}
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
// src/undo-redo/undo-redo.ts
|
||||
var import_core8 = require("@tiptap/core");
|
||||
var import_history = require("@tiptap/pm/history");
|
||||
var UndoRedo = import_core8.Extension.create({
|
||||
name: "undoRedo",
|
||||
addOptions() {
|
||||
return {
|
||||
depth: 100,
|
||||
newGroupDelay: 500
|
||||
};
|
||||
},
|
||||
addCommands() {
|
||||
return {
|
||||
undo: () => ({ state, dispatch }) => {
|
||||
return (0, import_history.undo)(state, dispatch);
|
||||
},
|
||||
redo: () => ({ state, dispatch }) => {
|
||||
return (0, import_history.redo)(state, dispatch);
|
||||
}
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
return [(0, import_history.history)(this.options)];
|
||||
},
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
"Mod-z": () => this.editor.commands.undo(),
|
||||
"Shift-Mod-z": () => this.editor.commands.redo(),
|
||||
"Mod-y": () => this.editor.commands.redo(),
|
||||
// Russian keyboard layouts
|
||||
"Mod-\u044F": () => this.editor.commands.undo(),
|
||||
"Shift-Mod-\u044F": () => this.editor.commands.redo()
|
||||
};
|
||||
}
|
||||
});
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
CharacterCount,
|
||||
Dropcursor,
|
||||
Focus,
|
||||
Gapcursor,
|
||||
Placeholder,
|
||||
Selection,
|
||||
TrailingNode,
|
||||
UndoRedo,
|
||||
preparePlaceholderAttribute
|
||||
});
|
||||
//# sourceMappingURL=index.cjs.map
|
||||
+1
File diff suppressed because one or more lines are too long
+284
@@ -0,0 +1,284 @@
|
||||
import { Extension, ParentConfig, Editor } from '@tiptap/core';
|
||||
import { Node } from '@tiptap/pm/model';
|
||||
|
||||
interface CharacterCountOptions {
|
||||
/**
|
||||
* The maximum number of characters that should be allowed. Defaults to `0`.
|
||||
* @default null
|
||||
* @example 180
|
||||
*/
|
||||
limit: number | null | undefined;
|
||||
/**
|
||||
* The mode by which the size is calculated. If set to `textSize`, the textContent of the document is used.
|
||||
* If set to `nodeSize`, the nodeSize of the document is used.
|
||||
* @default 'textSize'
|
||||
* @example 'textSize'
|
||||
*/
|
||||
mode: 'textSize' | 'nodeSize';
|
||||
/**
|
||||
* The text counter function to use. Defaults to a simple character count.
|
||||
* @default (text) => text.length
|
||||
* @example (text) => [...new Intl.Segmenter().segment(text)].length
|
||||
*/
|
||||
textCounter: (text: string) => number;
|
||||
/**
|
||||
* The word counter function to use. Defaults to a simple word count.
|
||||
* @default (text) => text.split(' ').filter(word => word !== '').length
|
||||
* @example (text) => text.split(/\s+/).filter(word => word !== '').length
|
||||
*/
|
||||
wordCounter: (text: string) => number;
|
||||
}
|
||||
interface CharacterCountStorage {
|
||||
/**
|
||||
* Get the number of characters for the current document.
|
||||
* @param options The options for the character count. (optional)
|
||||
* @param options.node The node to get the characters from. Defaults to the current document.
|
||||
* @param options.mode The mode by which the size is calculated. If set to `textSize`, the textContent of the document is used.
|
||||
*/
|
||||
characters: (options?: {
|
||||
node?: Node;
|
||||
mode?: 'textSize' | 'nodeSize';
|
||||
}) => number;
|
||||
/**
|
||||
* Get the number of words for the current document.
|
||||
* @param options The options for the character count. (optional)
|
||||
* @param options.node The node to get the words from. Defaults to the current document.
|
||||
*/
|
||||
words: (options?: {
|
||||
node?: Node;
|
||||
}) => number;
|
||||
}
|
||||
declare module '@tiptap/core' {
|
||||
interface Storage {
|
||||
characterCount: CharacterCountStorage;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This extension allows you to count the characters and words of your document.
|
||||
* @see https://tiptap.dev/api/extensions/character-count
|
||||
*/
|
||||
declare const CharacterCount: Extension<CharacterCountOptions, CharacterCountStorage>;
|
||||
|
||||
interface DropcursorOptions {
|
||||
/**
|
||||
* The color of the drop cursor. Use `false` to apply no color and rely only on class.
|
||||
* @default 'currentColor'
|
||||
* @example 'red'
|
||||
*/
|
||||
color?: string | false;
|
||||
/**
|
||||
* The width of the drop cursor
|
||||
* @default 1
|
||||
* @example 2
|
||||
*/
|
||||
width: number | undefined;
|
||||
/**
|
||||
* The class of the drop cursor
|
||||
* @default undefined
|
||||
* @example 'drop-cursor'
|
||||
*/
|
||||
class: string | undefined;
|
||||
}
|
||||
/**
|
||||
* This extension allows you to add a drop cursor to your editor.
|
||||
* A drop cursor is a line that appears when you drag and drop content
|
||||
* in-between nodes.
|
||||
* @see https://tiptap.dev/api/extensions/dropcursor
|
||||
*/
|
||||
declare const Dropcursor: Extension<DropcursorOptions, any>;
|
||||
|
||||
interface FocusOptions {
|
||||
/**
|
||||
* The class name that should be added to the focused node.
|
||||
* @default 'has-focus'
|
||||
* @example 'is-focused'
|
||||
*/
|
||||
className: string;
|
||||
/**
|
||||
* The mode by which the focused node is determined.
|
||||
* - All: All nodes are marked as focused.
|
||||
* - Deepest: Only the deepest node is marked as focused.
|
||||
* - Shallowest: Only the shallowest node is marked as focused.
|
||||
*
|
||||
* @default 'all'
|
||||
* @example 'deepest'
|
||||
* @example 'shallowest'
|
||||
*/
|
||||
mode: 'all' | 'deepest' | 'shallowest';
|
||||
}
|
||||
/**
|
||||
* This extension allows you to add a class to the focused node.
|
||||
* @see https://www.tiptap.dev/api/extensions/focus
|
||||
*/
|
||||
declare const Focus: Extension<FocusOptions, any>;
|
||||
|
||||
declare module '@tiptap/core' {
|
||||
interface NodeConfig<Options, Storage> {
|
||||
/**
|
||||
* A function to determine whether the gap cursor is allowed at the current position. Must return `true` or `false`.
|
||||
* @default null
|
||||
*/
|
||||
allowGapCursor?: boolean | null | ((this: {
|
||||
name: string;
|
||||
options: Options;
|
||||
storage: Storage;
|
||||
parent: ParentConfig<NodeConfig<Options>>['allowGapCursor'];
|
||||
}) => boolean | null);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This extension allows you to add a gap cursor to your editor.
|
||||
* A gap cursor is a cursor that appears when you click on a place
|
||||
* where no content is present, for example inbetween nodes.
|
||||
* @see https://tiptap.dev/api/extensions/gapcursor
|
||||
*/
|
||||
declare const Gapcursor: Extension<any, any>;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
/**
|
||||
* **The class name for the empty editor**
|
||||
* @default 'is-editor-empty'
|
||||
*/
|
||||
emptyEditorClass: string;
|
||||
/**
|
||||
* **The class name for empty nodes**
|
||||
* @default 'is-empty'
|
||||
*/
|
||||
emptyNodeClass: string;
|
||||
/**
|
||||
* **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**
|
||||
*
|
||||
* You can use a function to return a dynamic placeholder or a string.
|
||||
* @default 'Write something …'
|
||||
*/
|
||||
placeholder: ((PlaceholderProps: {
|
||||
editor: Editor;
|
||||
node: Node;
|
||||
pos: number;
|
||||
hasAnchor: boolean;
|
||||
}) => string) | string;
|
||||
/**
|
||||
* **Checks if the placeholder should be only shown when the editor is editable.**
|
||||
*
|
||||
* If true, the placeholder will only be shown when the editor is editable.
|
||||
* If false, the placeholder will always be shown.
|
||||
* @default true
|
||||
*/
|
||||
showOnlyWhenEditable: boolean;
|
||||
/**
|
||||
* **Checks if the placeholder should be only shown when the current node is empty.**
|
||||
*
|
||||
* If true, the placeholder will only be shown when the current node is empty.
|
||||
* If false, the placeholder will be shown when any node is empty.
|
||||
* @default true
|
||||
*/
|
||||
showOnlyCurrent: boolean;
|
||||
/**
|
||||
* **Controls if the placeholder should be shown for all descendents.**
|
||||
*
|
||||
* If true, the placeholder will be shown for all descendents.
|
||||
* If false, the placeholder will only be shown for the current node.
|
||||
* @default false
|
||||
*/
|
||||
includeChildren: boolean;
|
||||
}
|
||||
/**
|
||||
* This extension allows you to add a placeholder to your editor.
|
||||
* A placeholder is a text that appears when the editor or a node is empty.
|
||||
* @see https://www.tiptap.dev/api/extensions/placeholder
|
||||
*/
|
||||
declare const Placeholder: Extension<PlaceholderOptions, any>;
|
||||
|
||||
type SelectionOptions = {
|
||||
/**
|
||||
* The class name that should be added to the selected text.
|
||||
* @default 'selection'
|
||||
* @example 'is-selected'
|
||||
*/
|
||||
className: string;
|
||||
};
|
||||
/**
|
||||
* This extension allows you to add a class to the selected text.
|
||||
* @see https://www.tiptap.dev/api/extensions/selection
|
||||
*/
|
||||
declare const Selection: Extension<SelectionOptions, any>;
|
||||
|
||||
/**
|
||||
* Extension based on:
|
||||
* - https://github.com/ueberdosis/tiptap/blob/v1/packages/tiptap-extensions/src/extensions/TrailingNode.js
|
||||
* - https://github.com/remirror/remirror/blob/e0f1bec4a1e8073ce8f5500d62193e52321155b9/packages/prosemirror-trailing-node/src/trailing-node-plugin.ts
|
||||
*/
|
||||
interface TrailingNodeOptions {
|
||||
/**
|
||||
* The node type that should be inserted at the end of the document.
|
||||
* @note the node will always be added to the `notAfter` lists to
|
||||
* prevent an infinite loop.
|
||||
* @default undefined
|
||||
*/
|
||||
node?: string;
|
||||
/**
|
||||
* The node types after which the trailing node should not be inserted.
|
||||
* @default ['paragraph']
|
||||
*/
|
||||
notAfter?: string | string[];
|
||||
}
|
||||
/**
|
||||
* This extension allows you to add an extra node at the end of the document.
|
||||
* @see https://www.tiptap.dev/api/extensions/trailing-node
|
||||
*/
|
||||
declare const TrailingNode: Extension<TrailingNodeOptions, any>;
|
||||
|
||||
interface UndoRedoOptions {
|
||||
/**
|
||||
* The amount of history events that are collected before the oldest events are discarded.
|
||||
* @default 100
|
||||
* @example 50
|
||||
*/
|
||||
depth: number;
|
||||
/**
|
||||
* The delay (in milliseconds) between changes after which a new group should be started.
|
||||
* @default 500
|
||||
* @example 1000
|
||||
*/
|
||||
newGroupDelay: number;
|
||||
}
|
||||
declare module '@tiptap/core' {
|
||||
interface Commands<ReturnType> {
|
||||
undoRedo: {
|
||||
/**
|
||||
* Undo recent changes
|
||||
* @example editor.commands.undo()
|
||||
*/
|
||||
undo: () => ReturnType;
|
||||
/**
|
||||
* Reapply reverted changes
|
||||
* @example editor.commands.redo()
|
||||
*/
|
||||
redo: () => ReturnType;
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This extension allows you to undo and redo recent changes.
|
||||
* @see https://www.tiptap.dev/api/extensions/undo-redo
|
||||
*
|
||||
* **Important**: If the `@tiptap/extension-collaboration` package is used, make sure to remove
|
||||
* the `undo-redo` extension, as it is not compatible with the `collaboration` extension.
|
||||
*
|
||||
* `@tiptap/extension-collaboration` uses its own history implementation.
|
||||
*/
|
||||
declare const UndoRedo: Extension<UndoRedoOptions, any>;
|
||||
|
||||
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 };
|
||||
+284
@@ -0,0 +1,284 @@
|
||||
import { Extension, ParentConfig, Editor } from '@tiptap/core';
|
||||
import { Node } from '@tiptap/pm/model';
|
||||
|
||||
interface CharacterCountOptions {
|
||||
/**
|
||||
* The maximum number of characters that should be allowed. Defaults to `0`.
|
||||
* @default null
|
||||
* @example 180
|
||||
*/
|
||||
limit: number | null | undefined;
|
||||
/**
|
||||
* The mode by which the size is calculated. If set to `textSize`, the textContent of the document is used.
|
||||
* If set to `nodeSize`, the nodeSize of the document is used.
|
||||
* @default 'textSize'
|
||||
* @example 'textSize'
|
||||
*/
|
||||
mode: 'textSize' | 'nodeSize';
|
||||
/**
|
||||
* The text counter function to use. Defaults to a simple character count.
|
||||
* @default (text) => text.length
|
||||
* @example (text) => [...new Intl.Segmenter().segment(text)].length
|
||||
*/
|
||||
textCounter: (text: string) => number;
|
||||
/**
|
||||
* The word counter function to use. Defaults to a simple word count.
|
||||
* @default (text) => text.split(' ').filter(word => word !== '').length
|
||||
* @example (text) => text.split(/\s+/).filter(word => word !== '').length
|
||||
*/
|
||||
wordCounter: (text: string) => number;
|
||||
}
|
||||
interface CharacterCountStorage {
|
||||
/**
|
||||
* Get the number of characters for the current document.
|
||||
* @param options The options for the character count. (optional)
|
||||
* @param options.node The node to get the characters from. Defaults to the current document.
|
||||
* @param options.mode The mode by which the size is calculated. If set to `textSize`, the textContent of the document is used.
|
||||
*/
|
||||
characters: (options?: {
|
||||
node?: Node;
|
||||
mode?: 'textSize' | 'nodeSize';
|
||||
}) => number;
|
||||
/**
|
||||
* Get the number of words for the current document.
|
||||
* @param options The options for the character count. (optional)
|
||||
* @param options.node The node to get the words from. Defaults to the current document.
|
||||
*/
|
||||
words: (options?: {
|
||||
node?: Node;
|
||||
}) => number;
|
||||
}
|
||||
declare module '@tiptap/core' {
|
||||
interface Storage {
|
||||
characterCount: CharacterCountStorage;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This extension allows you to count the characters and words of your document.
|
||||
* @see https://tiptap.dev/api/extensions/character-count
|
||||
*/
|
||||
declare const CharacterCount: Extension<CharacterCountOptions, CharacterCountStorage>;
|
||||
|
||||
interface DropcursorOptions {
|
||||
/**
|
||||
* The color of the drop cursor. Use `false` to apply no color and rely only on class.
|
||||
* @default 'currentColor'
|
||||
* @example 'red'
|
||||
*/
|
||||
color?: string | false;
|
||||
/**
|
||||
* The width of the drop cursor
|
||||
* @default 1
|
||||
* @example 2
|
||||
*/
|
||||
width: number | undefined;
|
||||
/**
|
||||
* The class of the drop cursor
|
||||
* @default undefined
|
||||
* @example 'drop-cursor'
|
||||
*/
|
||||
class: string | undefined;
|
||||
}
|
||||
/**
|
||||
* This extension allows you to add a drop cursor to your editor.
|
||||
* A drop cursor is a line that appears when you drag and drop content
|
||||
* in-between nodes.
|
||||
* @see https://tiptap.dev/api/extensions/dropcursor
|
||||
*/
|
||||
declare const Dropcursor: Extension<DropcursorOptions, any>;
|
||||
|
||||
interface FocusOptions {
|
||||
/**
|
||||
* The class name that should be added to the focused node.
|
||||
* @default 'has-focus'
|
||||
* @example 'is-focused'
|
||||
*/
|
||||
className: string;
|
||||
/**
|
||||
* The mode by which the focused node is determined.
|
||||
* - All: All nodes are marked as focused.
|
||||
* - Deepest: Only the deepest node is marked as focused.
|
||||
* - Shallowest: Only the shallowest node is marked as focused.
|
||||
*
|
||||
* @default 'all'
|
||||
* @example 'deepest'
|
||||
* @example 'shallowest'
|
||||
*/
|
||||
mode: 'all' | 'deepest' | 'shallowest';
|
||||
}
|
||||
/**
|
||||
* This extension allows you to add a class to the focused node.
|
||||
* @see https://www.tiptap.dev/api/extensions/focus
|
||||
*/
|
||||
declare const Focus: Extension<FocusOptions, any>;
|
||||
|
||||
declare module '@tiptap/core' {
|
||||
interface NodeConfig<Options, Storage> {
|
||||
/**
|
||||
* A function to determine whether the gap cursor is allowed at the current position. Must return `true` or `false`.
|
||||
* @default null
|
||||
*/
|
||||
allowGapCursor?: boolean | null | ((this: {
|
||||
name: string;
|
||||
options: Options;
|
||||
storage: Storage;
|
||||
parent: ParentConfig<NodeConfig<Options>>['allowGapCursor'];
|
||||
}) => boolean | null);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This extension allows you to add a gap cursor to your editor.
|
||||
* A gap cursor is a cursor that appears when you click on a place
|
||||
* where no content is present, for example inbetween nodes.
|
||||
* @see https://tiptap.dev/api/extensions/gapcursor
|
||||
*/
|
||||
declare const Gapcursor: Extension<any, any>;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
/**
|
||||
* **The class name for the empty editor**
|
||||
* @default 'is-editor-empty'
|
||||
*/
|
||||
emptyEditorClass: string;
|
||||
/**
|
||||
* **The class name for empty nodes**
|
||||
* @default 'is-empty'
|
||||
*/
|
||||
emptyNodeClass: string;
|
||||
/**
|
||||
* **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**
|
||||
*
|
||||
* You can use a function to return a dynamic placeholder or a string.
|
||||
* @default 'Write something …'
|
||||
*/
|
||||
placeholder: ((PlaceholderProps: {
|
||||
editor: Editor;
|
||||
node: Node;
|
||||
pos: number;
|
||||
hasAnchor: boolean;
|
||||
}) => string) | string;
|
||||
/**
|
||||
* **Checks if the placeholder should be only shown when the editor is editable.**
|
||||
*
|
||||
* If true, the placeholder will only be shown when the editor is editable.
|
||||
* If false, the placeholder will always be shown.
|
||||
* @default true
|
||||
*/
|
||||
showOnlyWhenEditable: boolean;
|
||||
/**
|
||||
* **Checks if the placeholder should be only shown when the current node is empty.**
|
||||
*
|
||||
* If true, the placeholder will only be shown when the current node is empty.
|
||||
* If false, the placeholder will be shown when any node is empty.
|
||||
* @default true
|
||||
*/
|
||||
showOnlyCurrent: boolean;
|
||||
/**
|
||||
* **Controls if the placeholder should be shown for all descendents.**
|
||||
*
|
||||
* If true, the placeholder will be shown for all descendents.
|
||||
* If false, the placeholder will only be shown for the current node.
|
||||
* @default false
|
||||
*/
|
||||
includeChildren: boolean;
|
||||
}
|
||||
/**
|
||||
* This extension allows you to add a placeholder to your editor.
|
||||
* A placeholder is a text that appears when the editor or a node is empty.
|
||||
* @see https://www.tiptap.dev/api/extensions/placeholder
|
||||
*/
|
||||
declare const Placeholder: Extension<PlaceholderOptions, any>;
|
||||
|
||||
type SelectionOptions = {
|
||||
/**
|
||||
* The class name that should be added to the selected text.
|
||||
* @default 'selection'
|
||||
* @example 'is-selected'
|
||||
*/
|
||||
className: string;
|
||||
};
|
||||
/**
|
||||
* This extension allows you to add a class to the selected text.
|
||||
* @see https://www.tiptap.dev/api/extensions/selection
|
||||
*/
|
||||
declare const Selection: Extension<SelectionOptions, any>;
|
||||
|
||||
/**
|
||||
* Extension based on:
|
||||
* - https://github.com/ueberdosis/tiptap/blob/v1/packages/tiptap-extensions/src/extensions/TrailingNode.js
|
||||
* - https://github.com/remirror/remirror/blob/e0f1bec4a1e8073ce8f5500d62193e52321155b9/packages/prosemirror-trailing-node/src/trailing-node-plugin.ts
|
||||
*/
|
||||
interface TrailingNodeOptions {
|
||||
/**
|
||||
* The node type that should be inserted at the end of the document.
|
||||
* @note the node will always be added to the `notAfter` lists to
|
||||
* prevent an infinite loop.
|
||||
* @default undefined
|
||||
*/
|
||||
node?: string;
|
||||
/**
|
||||
* The node types after which the trailing node should not be inserted.
|
||||
* @default ['paragraph']
|
||||
*/
|
||||
notAfter?: string | string[];
|
||||
}
|
||||
/**
|
||||
* This extension allows you to add an extra node at the end of the document.
|
||||
* @see https://www.tiptap.dev/api/extensions/trailing-node
|
||||
*/
|
||||
declare const TrailingNode: Extension<TrailingNodeOptions, any>;
|
||||
|
||||
interface UndoRedoOptions {
|
||||
/**
|
||||
* The amount of history events that are collected before the oldest events are discarded.
|
||||
* @default 100
|
||||
* @example 50
|
||||
*/
|
||||
depth: number;
|
||||
/**
|
||||
* The delay (in milliseconds) between changes after which a new group should be started.
|
||||
* @default 500
|
||||
* @example 1000
|
||||
*/
|
||||
newGroupDelay: number;
|
||||
}
|
||||
declare module '@tiptap/core' {
|
||||
interface Commands<ReturnType> {
|
||||
undoRedo: {
|
||||
/**
|
||||
* Undo recent changes
|
||||
* @example editor.commands.undo()
|
||||
*/
|
||||
undo: () => ReturnType;
|
||||
/**
|
||||
* Reapply reverted changes
|
||||
* @example editor.commands.redo()
|
||||
*/
|
||||
redo: () => ReturnType;
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This extension allows you to undo and redo recent changes.
|
||||
* @see https://www.tiptap.dev/api/extensions/undo-redo
|
||||
*
|
||||
* **Important**: If the `@tiptap/extension-collaboration` package is used, make sure to remove
|
||||
* the `undo-redo` extension, as it is not compatible with the `collaboration` extension.
|
||||
*
|
||||
* `@tiptap/extension-collaboration` uses its own history implementation.
|
||||
*/
|
||||
declare const UndoRedo: Extension<UndoRedoOptions, any>;
|
||||
|
||||
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 };
|
||||
+399
@@ -0,0 +1,399 @@
|
||||
// src/character-count/character-count.ts
|
||||
import { Extension } from "@tiptap/core";
|
||||
import { Plugin, PluginKey } from "@tiptap/pm/state";
|
||||
var CharacterCount = Extension.create({
|
||||
name: "characterCount",
|
||||
addOptions() {
|
||||
return {
|
||||
limit: null,
|
||||
mode: "textSize",
|
||||
textCounter: (text) => text.length,
|
||||
wordCounter: (text) => text.split(" ").filter((word) => word !== "").length
|
||||
};
|
||||
},
|
||||
addStorage() {
|
||||
return {
|
||||
characters: () => 0,
|
||||
words: () => 0
|
||||
};
|
||||
},
|
||||
onBeforeCreate() {
|
||||
this.storage.characters = (options) => {
|
||||
const node = (options == null ? void 0 : options.node) || this.editor.state.doc;
|
||||
const mode = (options == null ? void 0 : options.mode) || this.options.mode;
|
||||
if (mode === "textSize") {
|
||||
const text = node.textBetween(0, node.content.size, void 0, " ");
|
||||
return this.options.textCounter(text);
|
||||
}
|
||||
return node.nodeSize;
|
||||
};
|
||||
this.storage.words = (options) => {
|
||||
const node = (options == null ? void 0 : options.node) || this.editor.state.doc;
|
||||
const text = node.textBetween(0, node.content.size, " ", " ");
|
||||
return this.options.wordCounter(text);
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
let initialEvaluationDone = false;
|
||||
return [
|
||||
new Plugin({
|
||||
key: new PluginKey("characterCount"),
|
||||
appendTransaction: (transactions, oldState, newState) => {
|
||||
if (initialEvaluationDone) {
|
||||
return;
|
||||
}
|
||||
const limit = this.options.limit;
|
||||
if (limit === null || limit === void 0 || limit === 0) {
|
||||
initialEvaluationDone = true;
|
||||
return;
|
||||
}
|
||||
const initialContentSize = this.storage.characters({ node: newState.doc });
|
||||
if (initialContentSize > limit) {
|
||||
const over = initialContentSize - limit;
|
||||
const from = 0;
|
||||
const to = over;
|
||||
console.warn(
|
||||
`[CharacterCount] Initial content exceeded limit of ${limit} characters. Content was automatically trimmed.`
|
||||
);
|
||||
const tr = newState.tr.deleteRange(from, to);
|
||||
initialEvaluationDone = true;
|
||||
return tr;
|
||||
}
|
||||
initialEvaluationDone = true;
|
||||
},
|
||||
filterTransaction: (transaction, state) => {
|
||||
const limit = this.options.limit;
|
||||
if (!transaction.docChanged || limit === 0 || limit === null || limit === void 0) {
|
||||
return true;
|
||||
}
|
||||
const oldSize = this.storage.characters({ node: state.doc });
|
||||
const newSize = this.storage.characters({ node: transaction.doc });
|
||||
if (newSize <= limit) {
|
||||
return true;
|
||||
}
|
||||
if (oldSize > limit && newSize > limit && newSize <= oldSize) {
|
||||
return true;
|
||||
}
|
||||
if (oldSize > limit && newSize > limit && newSize > oldSize) {
|
||||
return false;
|
||||
}
|
||||
const isPaste = transaction.getMeta("paste");
|
||||
if (!isPaste) {
|
||||
return false;
|
||||
}
|
||||
const pos = transaction.selection.$head.pos;
|
||||
const over = newSize - limit;
|
||||
const from = pos - over;
|
||||
const to = pos;
|
||||
transaction.deleteRange(from, to);
|
||||
const updatedSize = this.storage.characters({ node: transaction.doc });
|
||||
if (updatedSize > limit) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
// src/drop-cursor/drop-cursor.ts
|
||||
import { Extension as Extension2 } from "@tiptap/core";
|
||||
import { dropCursor } from "@tiptap/pm/dropcursor";
|
||||
var Dropcursor = Extension2.create({
|
||||
name: "dropCursor",
|
||||
addOptions() {
|
||||
return {
|
||||
color: "currentColor",
|
||||
width: 1,
|
||||
class: void 0
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
return [dropCursor(this.options)];
|
||||
}
|
||||
});
|
||||
|
||||
// src/focus/focus.ts
|
||||
import { Extension as Extension3 } from "@tiptap/core";
|
||||
import { Plugin as Plugin2, PluginKey as PluginKey2 } from "@tiptap/pm/state";
|
||||
import { Decoration, DecorationSet } from "@tiptap/pm/view";
|
||||
var Focus = Extension3.create({
|
||||
name: "focus",
|
||||
addOptions() {
|
||||
return {
|
||||
className: "has-focus",
|
||||
mode: "all"
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
return [
|
||||
new Plugin2({
|
||||
key: new PluginKey2("focus"),
|
||||
props: {
|
||||
decorations: ({ doc, selection }) => {
|
||||
const { isEditable, isFocused } = this.editor;
|
||||
const { anchor } = selection;
|
||||
const decorations = [];
|
||||
if (!isEditable || !isFocused) {
|
||||
return DecorationSet.create(doc, []);
|
||||
}
|
||||
let maxLevels = 0;
|
||||
if (this.options.mode === "deepest") {
|
||||
doc.descendants((node, pos) => {
|
||||
if (node.isText) {
|
||||
return;
|
||||
}
|
||||
const isCurrent = anchor >= pos && anchor <= pos + node.nodeSize - 1;
|
||||
if (!isCurrent) {
|
||||
return false;
|
||||
}
|
||||
maxLevels += 1;
|
||||
});
|
||||
}
|
||||
let currentLevel = 0;
|
||||
doc.descendants((node, pos) => {
|
||||
if (node.isText) {
|
||||
return false;
|
||||
}
|
||||
const isCurrent = anchor >= pos && anchor <= pos + node.nodeSize - 1;
|
||||
if (!isCurrent) {
|
||||
return false;
|
||||
}
|
||||
currentLevel += 1;
|
||||
const outOfScope = this.options.mode === "deepest" && maxLevels - currentLevel > 0 || this.options.mode === "shallowest" && currentLevel > 1;
|
||||
if (outOfScope) {
|
||||
return this.options.mode === "deepest";
|
||||
}
|
||||
decorations.push(
|
||||
Decoration.node(pos, pos + node.nodeSize, {
|
||||
class: this.options.className
|
||||
})
|
||||
);
|
||||
});
|
||||
return DecorationSet.create(doc, decorations);
|
||||
}
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
// src/gap-cursor/gap-cursor.ts
|
||||
import { callOrReturn, Extension as Extension4, getExtensionField } from "@tiptap/core";
|
||||
import { gapCursor } from "@tiptap/pm/gapcursor";
|
||||
var Gapcursor = Extension4.create({
|
||||
name: "gapCursor",
|
||||
addProseMirrorPlugins() {
|
||||
return [gapCursor()];
|
||||
},
|
||||
extendNodeSchema(extension) {
|
||||
var _a;
|
||||
const context = {
|
||||
name: extension.name,
|
||||
options: extension.options,
|
||||
storage: extension.storage
|
||||
};
|
||||
return {
|
||||
allowGapCursor: (_a = callOrReturn(getExtensionField(extension, "allowGapCursor", context))) != null ? _a : null
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// src/placeholder/placeholder.ts
|
||||
import { Extension as Extension5, isNodeEmpty } from "@tiptap/core";
|
||||
import { Plugin as Plugin3, PluginKey as PluginKey3 } from "@tiptap/pm/state";
|
||||
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({
|
||||
name: "placeholder",
|
||||
addOptions() {
|
||||
return {
|
||||
emptyEditorClass: "is-editor-empty",
|
||||
emptyNodeClass: "is-empty",
|
||||
dataAttribute: DEFAULT_DATA_ATTRIBUTE,
|
||||
placeholder: "Write something \u2026",
|
||||
showOnlyWhenEditable: true,
|
||||
showOnlyCurrent: true,
|
||||
includeChildren: false
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
const dataAttribute = this.options.dataAttribute ? `data-${preparePlaceholderAttribute(this.options.dataAttribute)}` : `data-${DEFAULT_DATA_ATTRIBUTE}`;
|
||||
return [
|
||||
new Plugin3({
|
||||
key: new PluginKey3("placeholder"),
|
||||
props: {
|
||||
decorations: ({ doc, selection }) => {
|
||||
const active = this.editor.isEditable || !this.options.showOnlyWhenEditable;
|
||||
const { anchor } = selection;
|
||||
const decorations = [];
|
||||
if (!active) {
|
||||
return null;
|
||||
}
|
||||
const isEmptyDoc = this.editor.isEmpty;
|
||||
doc.descendants((node, pos) => {
|
||||
const hasAnchor = anchor >= pos && anchor <= pos + node.nodeSize;
|
||||
const isEmpty = !node.isLeaf && isNodeEmpty(node);
|
||||
if ((hasAnchor || !this.options.showOnlyCurrent) && isEmpty) {
|
||||
const classes = [this.options.emptyNodeClass];
|
||||
if (isEmptyDoc) {
|
||||
classes.push(this.options.emptyEditorClass);
|
||||
}
|
||||
const decoration = Decoration2.node(pos, pos + node.nodeSize, {
|
||||
class: classes.join(" "),
|
||||
[dataAttribute]: typeof this.options.placeholder === "function" ? this.options.placeholder({
|
||||
editor: this.editor,
|
||||
node,
|
||||
pos,
|
||||
hasAnchor
|
||||
}) : this.options.placeholder
|
||||
});
|
||||
decorations.push(decoration);
|
||||
}
|
||||
return this.options.includeChildren;
|
||||
});
|
||||
return DecorationSet2.create(doc, decorations);
|
||||
}
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
// src/selection/selection.ts
|
||||
import { Extension as Extension6, isNodeSelection } from "@tiptap/core";
|
||||
import { Plugin as Plugin4, PluginKey as PluginKey4 } from "@tiptap/pm/state";
|
||||
import { Decoration as Decoration3, DecorationSet as DecorationSet3 } from "@tiptap/pm/view";
|
||||
var Selection = Extension6.create({
|
||||
name: "selection",
|
||||
addOptions() {
|
||||
return {
|
||||
className: "selection"
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
const { editor, options } = this;
|
||||
return [
|
||||
new Plugin4({
|
||||
key: new PluginKey4("selection"),
|
||||
props: {
|
||||
decorations(state) {
|
||||
if (state.selection.empty || editor.isFocused || !editor.isEditable || isNodeSelection(state.selection) || editor.view.dragging) {
|
||||
return null;
|
||||
}
|
||||
return DecorationSet3.create(state.doc, [
|
||||
Decoration3.inline(state.selection.from, state.selection.to, {
|
||||
class: options.className
|
||||
})
|
||||
]);
|
||||
}
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
// src/trailing-node/trailing-node.ts
|
||||
import { Extension as Extension7 } from "@tiptap/core";
|
||||
import { Plugin as Plugin5, PluginKey as PluginKey5 } from "@tiptap/pm/state";
|
||||
function nodeEqualsType({ types, node }) {
|
||||
return node && Array.isArray(types) && types.includes(node.type) || (node == null ? void 0 : node.type) === types;
|
||||
}
|
||||
var TrailingNode = Extension7.create({
|
||||
name: "trailingNode",
|
||||
addOptions() {
|
||||
return {
|
||||
node: void 0,
|
||||
notAfter: []
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
var _a;
|
||||
const plugin = new PluginKey5(this.name);
|
||||
const defaultNode = this.options.node || ((_a = this.editor.schema.topNodeType.contentMatch.defaultType) == null ? void 0 : _a.name) || "paragraph";
|
||||
const disabledNodes = Object.entries(this.editor.schema.nodes).map(([, value]) => value).filter((node) => (this.options.notAfter || []).concat(defaultNode).includes(node.name));
|
||||
return [
|
||||
new Plugin5({
|
||||
key: plugin,
|
||||
appendTransaction: (_, __, state) => {
|
||||
const { doc, tr, schema } = state;
|
||||
const shouldInsertNodeAtEnd = plugin.getState(state);
|
||||
const endPosition = doc.content.size;
|
||||
const type = schema.nodes[defaultNode];
|
||||
if (!shouldInsertNodeAtEnd) {
|
||||
return;
|
||||
}
|
||||
return tr.insert(endPosition, type.create());
|
||||
},
|
||||
state: {
|
||||
init: (_, state) => {
|
||||
const lastNode = state.tr.doc.lastChild;
|
||||
return !nodeEqualsType({ node: lastNode, types: disabledNodes });
|
||||
},
|
||||
apply: (tr, value) => {
|
||||
if (!tr.docChanged) {
|
||||
return value;
|
||||
}
|
||||
if (tr.getMeta("__uniqueIDTransaction")) {
|
||||
return value;
|
||||
}
|
||||
const lastNode = tr.doc.lastChild;
|
||||
return !nodeEqualsType({ node: lastNode, types: disabledNodes });
|
||||
}
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
// src/undo-redo/undo-redo.ts
|
||||
import { Extension as Extension8 } from "@tiptap/core";
|
||||
import { history, redo, undo } from "@tiptap/pm/history";
|
||||
var UndoRedo = Extension8.create({
|
||||
name: "undoRedo",
|
||||
addOptions() {
|
||||
return {
|
||||
depth: 100,
|
||||
newGroupDelay: 500
|
||||
};
|
||||
},
|
||||
addCommands() {
|
||||
return {
|
||||
undo: () => ({ state, dispatch }) => {
|
||||
return undo(state, dispatch);
|
||||
},
|
||||
redo: () => ({ state, dispatch }) => {
|
||||
return redo(state, dispatch);
|
||||
}
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
return [history(this.options)];
|
||||
},
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
"Mod-z": () => this.editor.commands.undo(),
|
||||
"Shift-Mod-z": () => this.editor.commands.redo(),
|
||||
"Mod-y": () => this.editor.commands.redo(),
|
||||
// Russian keyboard layouts
|
||||
"Mod-\u044F": () => this.editor.commands.undo(),
|
||||
"Shift-Mod-\u044F": () => this.editor.commands.redo()
|
||||
};
|
||||
}
|
||||
});
|
||||
export {
|
||||
CharacterCount,
|
||||
Dropcursor,
|
||||
Focus,
|
||||
Gapcursor,
|
||||
Placeholder,
|
||||
Selection,
|
||||
TrailingNode,
|
||||
UndoRedo,
|
||||
preparePlaceholderAttribute
|
||||
};
|
||||
//# sourceMappingURL=index.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+96
@@ -0,0 +1,96 @@
|
||||
"use strict";
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
|
||||
// src/placeholder/index.ts
|
||||
var index_exports = {};
|
||||
__export(index_exports, {
|
||||
Placeholder: () => Placeholder,
|
||||
preparePlaceholderAttribute: () => preparePlaceholderAttribute
|
||||
});
|
||||
module.exports = __toCommonJS(index_exports);
|
||||
|
||||
// src/placeholder/placeholder.ts
|
||||
var import_core = require("@tiptap/core");
|
||||
var import_state = require("@tiptap/pm/state");
|
||||
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({
|
||||
name: "placeholder",
|
||||
addOptions() {
|
||||
return {
|
||||
emptyEditorClass: "is-editor-empty",
|
||||
emptyNodeClass: "is-empty",
|
||||
dataAttribute: DEFAULT_DATA_ATTRIBUTE,
|
||||
placeholder: "Write something \u2026",
|
||||
showOnlyWhenEditable: true,
|
||||
showOnlyCurrent: true,
|
||||
includeChildren: false
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
const dataAttribute = this.options.dataAttribute ? `data-${preparePlaceholderAttribute(this.options.dataAttribute)}` : `data-${DEFAULT_DATA_ATTRIBUTE}`;
|
||||
return [
|
||||
new import_state.Plugin({
|
||||
key: new import_state.PluginKey("placeholder"),
|
||||
props: {
|
||||
decorations: ({ doc, selection }) => {
|
||||
const active = this.editor.isEditable || !this.options.showOnlyWhenEditable;
|
||||
const { anchor } = selection;
|
||||
const decorations = [];
|
||||
if (!active) {
|
||||
return null;
|
||||
}
|
||||
const isEmptyDoc = this.editor.isEmpty;
|
||||
doc.descendants((node, pos) => {
|
||||
const hasAnchor = anchor >= pos && anchor <= pos + node.nodeSize;
|
||||
const isEmpty = !node.isLeaf && (0, import_core.isNodeEmpty)(node);
|
||||
if ((hasAnchor || !this.options.showOnlyCurrent) && isEmpty) {
|
||||
const classes = [this.options.emptyNodeClass];
|
||||
if (isEmptyDoc) {
|
||||
classes.push(this.options.emptyEditorClass);
|
||||
}
|
||||
const decoration = import_view.Decoration.node(pos, pos + node.nodeSize, {
|
||||
class: classes.join(" "),
|
||||
[dataAttribute]: typeof this.options.placeholder === "function" ? this.options.placeholder({
|
||||
editor: this.editor,
|
||||
node,
|
||||
pos,
|
||||
hasAnchor
|
||||
}) : this.options.placeholder
|
||||
});
|
||||
decorations.push(decoration);
|
||||
}
|
||||
return this.options.includeChildren;
|
||||
});
|
||||
return import_view.DecorationSet.create(doc, decorations);
|
||||
}
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
});
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
Placeholder,
|
||||
preparePlaceholderAttribute
|
||||
});
|
||||
//# sourceMappingURL=index.cjs.map
|
||||
+1
File diff suppressed because one or more lines are too long
+71
@@ -0,0 +1,71 @@
|
||||
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 {
|
||||
/**
|
||||
* **The class name for the empty editor**
|
||||
* @default 'is-editor-empty'
|
||||
*/
|
||||
emptyEditorClass: string;
|
||||
/**
|
||||
* **The class name for empty nodes**
|
||||
* @default 'is-empty'
|
||||
*/
|
||||
emptyNodeClass: string;
|
||||
/**
|
||||
* **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**
|
||||
*
|
||||
* You can use a function to return a dynamic placeholder or a string.
|
||||
* @default 'Write something …'
|
||||
*/
|
||||
placeholder: ((PlaceholderProps: {
|
||||
editor: Editor;
|
||||
node: Node;
|
||||
pos: number;
|
||||
hasAnchor: boolean;
|
||||
}) => string) | string;
|
||||
/**
|
||||
* **Checks if the placeholder should be only shown when the editor is editable.**
|
||||
*
|
||||
* If true, the placeholder will only be shown when the editor is editable.
|
||||
* If false, the placeholder will always be shown.
|
||||
* @default true
|
||||
*/
|
||||
showOnlyWhenEditable: boolean;
|
||||
/**
|
||||
* **Checks if the placeholder should be only shown when the current node is empty.**
|
||||
*
|
||||
* If true, the placeholder will only be shown when the current node is empty.
|
||||
* If false, the placeholder will be shown when any node is empty.
|
||||
* @default true
|
||||
*/
|
||||
showOnlyCurrent: boolean;
|
||||
/**
|
||||
* **Controls if the placeholder should be shown for all descendents.**
|
||||
*
|
||||
* If true, the placeholder will be shown for all descendents.
|
||||
* If false, the placeholder will only be shown for the current node.
|
||||
* @default false
|
||||
*/
|
||||
includeChildren: boolean;
|
||||
}
|
||||
/**
|
||||
* This extension allows you to add a placeholder to your editor.
|
||||
* A placeholder is a text that appears when the editor or a node is empty.
|
||||
* @see https://www.tiptap.dev/api/extensions/placeholder
|
||||
*/
|
||||
declare const Placeholder: Extension<PlaceholderOptions, any>;
|
||||
|
||||
export { Placeholder, type PlaceholderOptions, preparePlaceholderAttribute };
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
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 {
|
||||
/**
|
||||
* **The class name for the empty editor**
|
||||
* @default 'is-editor-empty'
|
||||
*/
|
||||
emptyEditorClass: string;
|
||||
/**
|
||||
* **The class name for empty nodes**
|
||||
* @default 'is-empty'
|
||||
*/
|
||||
emptyNodeClass: string;
|
||||
/**
|
||||
* **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**
|
||||
*
|
||||
* You can use a function to return a dynamic placeholder or a string.
|
||||
* @default 'Write something …'
|
||||
*/
|
||||
placeholder: ((PlaceholderProps: {
|
||||
editor: Editor;
|
||||
node: Node;
|
||||
pos: number;
|
||||
hasAnchor: boolean;
|
||||
}) => string) | string;
|
||||
/**
|
||||
* **Checks if the placeholder should be only shown when the editor is editable.**
|
||||
*
|
||||
* If true, the placeholder will only be shown when the editor is editable.
|
||||
* If false, the placeholder will always be shown.
|
||||
* @default true
|
||||
*/
|
||||
showOnlyWhenEditable: boolean;
|
||||
/**
|
||||
* **Checks if the placeholder should be only shown when the current node is empty.**
|
||||
*
|
||||
* If true, the placeholder will only be shown when the current node is empty.
|
||||
* If false, the placeholder will be shown when any node is empty.
|
||||
* @default true
|
||||
*/
|
||||
showOnlyCurrent: boolean;
|
||||
/**
|
||||
* **Controls if the placeholder should be shown for all descendents.**
|
||||
*
|
||||
* If true, the placeholder will be shown for all descendents.
|
||||
* If false, the placeholder will only be shown for the current node.
|
||||
* @default false
|
||||
*/
|
||||
includeChildren: boolean;
|
||||
}
|
||||
/**
|
||||
* This extension allows you to add a placeholder to your editor.
|
||||
* A placeholder is a text that appears when the editor or a node is empty.
|
||||
* @see https://www.tiptap.dev/api/extensions/placeholder
|
||||
*/
|
||||
declare const Placeholder: Extension<PlaceholderOptions, any>;
|
||||
|
||||
export { Placeholder, type PlaceholderOptions, preparePlaceholderAttribute };
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
// src/placeholder/placeholder.ts
|
||||
import { Extension, isNodeEmpty } from "@tiptap/core";
|
||||
import { Plugin, PluginKey } from "@tiptap/pm/state";
|
||||
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({
|
||||
name: "placeholder",
|
||||
addOptions() {
|
||||
return {
|
||||
emptyEditorClass: "is-editor-empty",
|
||||
emptyNodeClass: "is-empty",
|
||||
dataAttribute: DEFAULT_DATA_ATTRIBUTE,
|
||||
placeholder: "Write something \u2026",
|
||||
showOnlyWhenEditable: true,
|
||||
showOnlyCurrent: true,
|
||||
includeChildren: false
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
const dataAttribute = this.options.dataAttribute ? `data-${preparePlaceholderAttribute(this.options.dataAttribute)}` : `data-${DEFAULT_DATA_ATTRIBUTE}`;
|
||||
return [
|
||||
new Plugin({
|
||||
key: new PluginKey("placeholder"),
|
||||
props: {
|
||||
decorations: ({ doc, selection }) => {
|
||||
const active = this.editor.isEditable || !this.options.showOnlyWhenEditable;
|
||||
const { anchor } = selection;
|
||||
const decorations = [];
|
||||
if (!active) {
|
||||
return null;
|
||||
}
|
||||
const isEmptyDoc = this.editor.isEmpty;
|
||||
doc.descendants((node, pos) => {
|
||||
const hasAnchor = anchor >= pos && anchor <= pos + node.nodeSize;
|
||||
const isEmpty = !node.isLeaf && isNodeEmpty(node);
|
||||
if ((hasAnchor || !this.options.showOnlyCurrent) && isEmpty) {
|
||||
const classes = [this.options.emptyNodeClass];
|
||||
if (isEmptyDoc) {
|
||||
classes.push(this.options.emptyEditorClass);
|
||||
}
|
||||
const decoration = Decoration.node(pos, pos + node.nodeSize, {
|
||||
class: classes.join(" "),
|
||||
[dataAttribute]: typeof this.options.placeholder === "function" ? this.options.placeholder({
|
||||
editor: this.editor,
|
||||
node,
|
||||
pos,
|
||||
hasAnchor
|
||||
}) : this.options.placeholder
|
||||
});
|
||||
decorations.push(decoration);
|
||||
}
|
||||
return this.options.includeChildren;
|
||||
});
|
||||
return DecorationSet.create(doc, decorations);
|
||||
}
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
});
|
||||
export {
|
||||
Placeholder,
|
||||
preparePlaceholderAttribute
|
||||
};
|
||||
//# sourceMappingURL=index.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+63
@@ -0,0 +1,63 @@
|
||||
"use strict";
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
|
||||
// src/selection/index.ts
|
||||
var index_exports = {};
|
||||
__export(index_exports, {
|
||||
Selection: () => Selection
|
||||
});
|
||||
module.exports = __toCommonJS(index_exports);
|
||||
|
||||
// src/selection/selection.ts
|
||||
var import_core = require("@tiptap/core");
|
||||
var import_state = require("@tiptap/pm/state");
|
||||
var import_view = require("@tiptap/pm/view");
|
||||
var Selection = import_core.Extension.create({
|
||||
name: "selection",
|
||||
addOptions() {
|
||||
return {
|
||||
className: "selection"
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
const { editor, options } = this;
|
||||
return [
|
||||
new import_state.Plugin({
|
||||
key: new import_state.PluginKey("selection"),
|
||||
props: {
|
||||
decorations(state) {
|
||||
if (state.selection.empty || editor.isFocused || !editor.isEditable || (0, import_core.isNodeSelection)(state.selection) || editor.view.dragging) {
|
||||
return null;
|
||||
}
|
||||
return import_view.DecorationSet.create(state.doc, [
|
||||
import_view.Decoration.inline(state.selection.from, state.selection.to, {
|
||||
class: options.className
|
||||
})
|
||||
]);
|
||||
}
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
});
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
Selection
|
||||
});
|
||||
//# sourceMappingURL=index.cjs.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../src/selection/index.ts","../../src/selection/selection.ts"],"sourcesContent":["export * from './selection.js'\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"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,kBAA2C;AAC3C,mBAAkC;AAClC,kBAA0C;AAenC,IAAM,YAAY,sBAAU,OAAyB;AAAA,EAC1D,MAAM;AAAA,EAEN,aAAa;AACX,WAAO;AAAA,MACL,WAAW;AAAA,IACb;AAAA,EACF;AAAA,EAEA,wBAAwB;AACtB,UAAM,EAAE,QAAQ,QAAQ,IAAI;AAE5B,WAAO;AAAA,MACL,IAAI,oBAAO;AAAA,QACT,KAAK,IAAI,uBAAU,WAAW;AAAA,QAC9B,OAAO;AAAA,UACL,YAAY,OAAO;AACjB,gBACE,MAAM,UAAU,SAChB,OAAO,aACP,CAAC,OAAO,kBACR,6BAAgB,MAAM,SAAS,KAC/B,OAAO,KAAK,UACZ;AACA,qBAAO;AAAA,YACT;AAEA,mBAAO,0BAAc,OAAO,MAAM,KAAK;AAAA,cACrC,uBAAW,OAAO,MAAM,UAAU,MAAM,MAAM,UAAU,IAAI;AAAA,gBAC1D,OAAO,QAAQ;AAAA,cACjB,CAAC;AAAA,YACH,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;","names":[]}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import { Extension } from '@tiptap/core';
|
||||
|
||||
type SelectionOptions = {
|
||||
/**
|
||||
* The class name that should be added to the selected text.
|
||||
* @default 'selection'
|
||||
* @example 'is-selected'
|
||||
*/
|
||||
className: string;
|
||||
};
|
||||
/**
|
||||
* This extension allows you to add a class to the selected text.
|
||||
* @see https://www.tiptap.dev/api/extensions/selection
|
||||
*/
|
||||
declare const Selection: Extension<SelectionOptions, any>;
|
||||
|
||||
export { Selection, type SelectionOptions };
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import { Extension } from '@tiptap/core';
|
||||
|
||||
type SelectionOptions = {
|
||||
/**
|
||||
* The class name that should be added to the selected text.
|
||||
* @default 'selection'
|
||||
* @example 'is-selected'
|
||||
*/
|
||||
className: string;
|
||||
};
|
||||
/**
|
||||
* This extension allows you to add a class to the selected text.
|
||||
* @see https://www.tiptap.dev/api/extensions/selection
|
||||
*/
|
||||
declare const Selection: Extension<SelectionOptions, any>;
|
||||
|
||||
export { Selection, type SelectionOptions };
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
// src/selection/selection.ts
|
||||
import { Extension, isNodeSelection } from "@tiptap/core";
|
||||
import { Plugin, PluginKey } from "@tiptap/pm/state";
|
||||
import { Decoration, DecorationSet } from "@tiptap/pm/view";
|
||||
var Selection = Extension.create({
|
||||
name: "selection",
|
||||
addOptions() {
|
||||
return {
|
||||
className: "selection"
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
const { editor, options } = this;
|
||||
return [
|
||||
new Plugin({
|
||||
key: new PluginKey("selection"),
|
||||
props: {
|
||||
decorations(state) {
|
||||
if (state.selection.empty || editor.isFocused || !editor.isEditable || isNodeSelection(state.selection) || editor.view.dragging) {
|
||||
return null;
|
||||
}
|
||||
return DecorationSet.create(state.doc, [
|
||||
Decoration.inline(state.selection.from, state.selection.to, {
|
||||
class: options.className
|
||||
})
|
||||
]);
|
||||
}
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
});
|
||||
export {
|
||||
Selection
|
||||
};
|
||||
//# sourceMappingURL=index.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../src/selection/selection.ts"],"sourcesContent":["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"],"mappings":";AAAA,SAAS,WAAW,uBAAuB;AAC3C,SAAS,QAAQ,iBAAiB;AAClC,SAAS,YAAY,qBAAqB;AAenC,IAAM,YAAY,UAAU,OAAyB;AAAA,EAC1D,MAAM;AAAA,EAEN,aAAa;AACX,WAAO;AAAA,MACL,WAAW;AAAA,IACb;AAAA,EACF;AAAA,EAEA,wBAAwB;AACtB,UAAM,EAAE,QAAQ,QAAQ,IAAI;AAE5B,WAAO;AAAA,MACL,IAAI,OAAO;AAAA,QACT,KAAK,IAAI,UAAU,WAAW;AAAA,QAC9B,OAAO;AAAA,UACL,YAAY,OAAO;AACjB,gBACE,MAAM,UAAU,SAChB,OAAO,aACP,CAAC,OAAO,cACR,gBAAgB,MAAM,SAAS,KAC/B,OAAO,KAAK,UACZ;AACA,qBAAO;AAAA,YACT;AAEA,mBAAO,cAAc,OAAO,MAAM,KAAK;AAAA,cACrC,WAAW,OAAO,MAAM,UAAU,MAAM,MAAM,UAAU,IAAI;AAAA,gBAC1D,OAAO,QAAQ;AAAA,cACjB,CAAC;AAAA,YACH,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;","names":[]}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
"use strict";
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
|
||||
// src/trailing-node/index.ts
|
||||
var index_exports = {};
|
||||
__export(index_exports, {
|
||||
TrailingNode: () => TrailingNode
|
||||
});
|
||||
module.exports = __toCommonJS(index_exports);
|
||||
|
||||
// src/trailing-node/trailing-node.ts
|
||||
var import_core = require("@tiptap/core");
|
||||
var import_state = require("@tiptap/pm/state");
|
||||
function nodeEqualsType({ types, node }) {
|
||||
return node && Array.isArray(types) && types.includes(node.type) || (node == null ? void 0 : node.type) === types;
|
||||
}
|
||||
var TrailingNode = import_core.Extension.create({
|
||||
name: "trailingNode",
|
||||
addOptions() {
|
||||
return {
|
||||
node: void 0,
|
||||
notAfter: []
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
var _a;
|
||||
const plugin = new import_state.PluginKey(this.name);
|
||||
const defaultNode = this.options.node || ((_a = this.editor.schema.topNodeType.contentMatch.defaultType) == null ? void 0 : _a.name) || "paragraph";
|
||||
const disabledNodes = Object.entries(this.editor.schema.nodes).map(([, value]) => value).filter((node) => (this.options.notAfter || []).concat(defaultNode).includes(node.name));
|
||||
return [
|
||||
new import_state.Plugin({
|
||||
key: plugin,
|
||||
appendTransaction: (_, __, state) => {
|
||||
const { doc, tr, schema } = state;
|
||||
const shouldInsertNodeAtEnd = plugin.getState(state);
|
||||
const endPosition = doc.content.size;
|
||||
const type = schema.nodes[defaultNode];
|
||||
if (!shouldInsertNodeAtEnd) {
|
||||
return;
|
||||
}
|
||||
return tr.insert(endPosition, type.create());
|
||||
},
|
||||
state: {
|
||||
init: (_, state) => {
|
||||
const lastNode = state.tr.doc.lastChild;
|
||||
return !nodeEqualsType({ node: lastNode, types: disabledNodes });
|
||||
},
|
||||
apply: (tr, value) => {
|
||||
if (!tr.docChanged) {
|
||||
return value;
|
||||
}
|
||||
if (tr.getMeta("__uniqueIDTransaction")) {
|
||||
return value;
|
||||
}
|
||||
const lastNode = tr.doc.lastChild;
|
||||
return !nodeEqualsType({ node: lastNode, types: disabledNodes });
|
||||
}
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
});
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
TrailingNode
|
||||
});
|
||||
//# sourceMappingURL=index.cjs.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../src/trailing-node/index.ts","../../src/trailing-node/trailing-node.ts"],"sourcesContent":["export * from './trailing-node.js'\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"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,kBAA0B;AAE1B,mBAAkC;AAElC,SAAS,eAAe,EAAE,OAAO,KAAK,GAAoE;AACxG,SAAQ,QAAQ,MAAM,QAAQ,KAAK,KAAK,MAAM,SAAS,KAAK,IAAI,MAAM,6BAAM,UAAS;AACvF;AA2BO,IAAM,eAAe,sBAAU,OAA4B;AAAA,EAChE,MAAM;AAAA,EAEN,aAAa;AACX,WAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU,CAAC;AAAA,IACb;AAAA,EACF;AAAA,EAEA,wBAAwB;AA3C1B;AA4CI,UAAM,SAAS,IAAI,uBAAU,KAAK,IAAI;AACtC,UAAM,cACJ,KAAK,QAAQ,UAAQ,UAAK,OAAO,OAAO,YAAY,aAAa,gBAA5C,mBAAyD,SAAQ;AAExF,UAAM,gBAAgB,OAAO,QAAQ,KAAK,OAAO,OAAO,KAAK,EAC1D,IAAI,CAAC,CAAC,EAAE,KAAK,MAAM,KAAK,EACxB,OAAO,WAAS,KAAK,QAAQ,YAAY,CAAC,GAAG,OAAO,WAAW,EAAE,SAAS,KAAK,IAAI,CAAC;AAEvF,WAAO;AAAA,MACL,IAAI,oBAAO;AAAA,QACT,KAAK;AAAA,QACL,mBAAmB,CAAC,GAAG,IAAI,UAAU;AACnC,gBAAM,EAAE,KAAK,IAAI,OAAO,IAAI;AAC5B,gBAAM,wBAAwB,OAAO,SAAS,KAAK;AACnD,gBAAM,cAAc,IAAI,QAAQ;AAChC,gBAAM,OAAO,OAAO,MAAM,WAAW;AAErC,cAAI,CAAC,uBAAuB;AAC1B;AAAA,UACF;AAEA,iBAAO,GAAG,OAAO,aAAa,KAAK,OAAO,CAAC;AAAA,QAC7C;AAAA,QACA,OAAO;AAAA,UACL,MAAM,CAAC,GAAG,UAAU;AAClB,kBAAM,WAAW,MAAM,GAAG,IAAI;AAE9B,mBAAO,CAAC,eAAe,EAAE,MAAM,UAAU,OAAO,cAAc,CAAC;AAAA,UACjE;AAAA,UACA,OAAO,CAAC,IAAI,UAAU;AACpB,gBAAI,CAAC,GAAG,YAAY;AAClB,qBAAO;AAAA,YACT;AAIA,gBAAI,GAAG,QAAQ,uBAAuB,GAAG;AACvC,qBAAO;AAAA,YACT;AAEA,kBAAM,WAAW,GAAG,IAAI;AAExB,mBAAO,CAAC,eAAe,EAAE,MAAM,UAAU,OAAO,cAAc,CAAC;AAAA,UACjE;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;","names":[]}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
import { Extension } from '@tiptap/core';
|
||||
|
||||
/**
|
||||
* Extension based on:
|
||||
* - https://github.com/ueberdosis/tiptap/blob/v1/packages/tiptap-extensions/src/extensions/TrailingNode.js
|
||||
* - https://github.com/remirror/remirror/blob/e0f1bec4a1e8073ce8f5500d62193e52321155b9/packages/prosemirror-trailing-node/src/trailing-node-plugin.ts
|
||||
*/
|
||||
interface TrailingNodeOptions {
|
||||
/**
|
||||
* The node type that should be inserted at the end of the document.
|
||||
* @note the node will always be added to the `notAfter` lists to
|
||||
* prevent an infinite loop.
|
||||
* @default undefined
|
||||
*/
|
||||
node?: string;
|
||||
/**
|
||||
* The node types after which the trailing node should not be inserted.
|
||||
* @default ['paragraph']
|
||||
*/
|
||||
notAfter?: string | string[];
|
||||
}
|
||||
/**
|
||||
* This extension allows you to add an extra node at the end of the document.
|
||||
* @see https://www.tiptap.dev/api/extensions/trailing-node
|
||||
*/
|
||||
declare const TrailingNode: Extension<TrailingNodeOptions, any>;
|
||||
|
||||
export { TrailingNode, type TrailingNodeOptions };
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
import { Extension } from '@tiptap/core';
|
||||
|
||||
/**
|
||||
* Extension based on:
|
||||
* - https://github.com/ueberdosis/tiptap/blob/v1/packages/tiptap-extensions/src/extensions/TrailingNode.js
|
||||
* - https://github.com/remirror/remirror/blob/e0f1bec4a1e8073ce8f5500d62193e52321155b9/packages/prosemirror-trailing-node/src/trailing-node-plugin.ts
|
||||
*/
|
||||
interface TrailingNodeOptions {
|
||||
/**
|
||||
* The node type that should be inserted at the end of the document.
|
||||
* @note the node will always be added to the `notAfter` lists to
|
||||
* prevent an infinite loop.
|
||||
* @default undefined
|
||||
*/
|
||||
node?: string;
|
||||
/**
|
||||
* The node types after which the trailing node should not be inserted.
|
||||
* @default ['paragraph']
|
||||
*/
|
||||
notAfter?: string | string[];
|
||||
}
|
||||
/**
|
||||
* This extension allows you to add an extra node at the end of the document.
|
||||
* @see https://www.tiptap.dev/api/extensions/trailing-node
|
||||
*/
|
||||
declare const TrailingNode: Extension<TrailingNodeOptions, any>;
|
||||
|
||||
export { TrailingNode, type TrailingNodeOptions };
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
// src/trailing-node/trailing-node.ts
|
||||
import { Extension } from "@tiptap/core";
|
||||
import { Plugin, PluginKey } from "@tiptap/pm/state";
|
||||
function nodeEqualsType({ types, node }) {
|
||||
return node && Array.isArray(types) && types.includes(node.type) || (node == null ? void 0 : node.type) === types;
|
||||
}
|
||||
var TrailingNode = Extension.create({
|
||||
name: "trailingNode",
|
||||
addOptions() {
|
||||
return {
|
||||
node: void 0,
|
||||
notAfter: []
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
var _a;
|
||||
const plugin = new PluginKey(this.name);
|
||||
const defaultNode = this.options.node || ((_a = this.editor.schema.topNodeType.contentMatch.defaultType) == null ? void 0 : _a.name) || "paragraph";
|
||||
const disabledNodes = Object.entries(this.editor.schema.nodes).map(([, value]) => value).filter((node) => (this.options.notAfter || []).concat(defaultNode).includes(node.name));
|
||||
return [
|
||||
new Plugin({
|
||||
key: plugin,
|
||||
appendTransaction: (_, __, state) => {
|
||||
const { doc, tr, schema } = state;
|
||||
const shouldInsertNodeAtEnd = plugin.getState(state);
|
||||
const endPosition = doc.content.size;
|
||||
const type = schema.nodes[defaultNode];
|
||||
if (!shouldInsertNodeAtEnd) {
|
||||
return;
|
||||
}
|
||||
return tr.insert(endPosition, type.create());
|
||||
},
|
||||
state: {
|
||||
init: (_, state) => {
|
||||
const lastNode = state.tr.doc.lastChild;
|
||||
return !nodeEqualsType({ node: lastNode, types: disabledNodes });
|
||||
},
|
||||
apply: (tr, value) => {
|
||||
if (!tr.docChanged) {
|
||||
return value;
|
||||
}
|
||||
if (tr.getMeta("__uniqueIDTransaction")) {
|
||||
return value;
|
||||
}
|
||||
const lastNode = tr.doc.lastChild;
|
||||
return !nodeEqualsType({ node: lastNode, types: disabledNodes });
|
||||
}
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
});
|
||||
export {
|
||||
TrailingNode
|
||||
};
|
||||
//# sourceMappingURL=index.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../src/trailing-node/trailing-node.ts"],"sourcesContent":["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"],"mappings":";AAAA,SAAS,iBAAiB;AAE1B,SAAS,QAAQ,iBAAiB;AAElC,SAAS,eAAe,EAAE,OAAO,KAAK,GAAoE;AACxG,SAAQ,QAAQ,MAAM,QAAQ,KAAK,KAAK,MAAM,SAAS,KAAK,IAAI,MAAM,6BAAM,UAAS;AACvF;AA2BO,IAAM,eAAe,UAAU,OAA4B;AAAA,EAChE,MAAM;AAAA,EAEN,aAAa;AACX,WAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU,CAAC;AAAA,IACb;AAAA,EACF;AAAA,EAEA,wBAAwB;AA3C1B;AA4CI,UAAM,SAAS,IAAI,UAAU,KAAK,IAAI;AACtC,UAAM,cACJ,KAAK,QAAQ,UAAQ,UAAK,OAAO,OAAO,YAAY,aAAa,gBAA5C,mBAAyD,SAAQ;AAExF,UAAM,gBAAgB,OAAO,QAAQ,KAAK,OAAO,OAAO,KAAK,EAC1D,IAAI,CAAC,CAAC,EAAE,KAAK,MAAM,KAAK,EACxB,OAAO,WAAS,KAAK,QAAQ,YAAY,CAAC,GAAG,OAAO,WAAW,EAAE,SAAS,KAAK,IAAI,CAAC;AAEvF,WAAO;AAAA,MACL,IAAI,OAAO;AAAA,QACT,KAAK;AAAA,QACL,mBAAmB,CAAC,GAAG,IAAI,UAAU;AACnC,gBAAM,EAAE,KAAK,IAAI,OAAO,IAAI;AAC5B,gBAAM,wBAAwB,OAAO,SAAS,KAAK;AACnD,gBAAM,cAAc,IAAI,QAAQ;AAChC,gBAAM,OAAO,OAAO,MAAM,WAAW;AAErC,cAAI,CAAC,uBAAuB;AAC1B;AAAA,UACF;AAEA,iBAAO,GAAG,OAAO,aAAa,KAAK,OAAO,CAAC;AAAA,QAC7C;AAAA,QACA,OAAO;AAAA,UACL,MAAM,CAAC,GAAG,UAAU;AAClB,kBAAM,WAAW,MAAM,GAAG,IAAI;AAE9B,mBAAO,CAAC,eAAe,EAAE,MAAM,UAAU,OAAO,cAAc,CAAC;AAAA,UACjE;AAAA,UACA,OAAO,CAAC,IAAI,UAAU;AACpB,gBAAI,CAAC,GAAG,YAAY;AAClB,qBAAO;AAAA,YACT;AAIA,gBAAI,GAAG,QAAQ,uBAAuB,GAAG;AACvC,qBAAO;AAAA,YACT;AAEA,kBAAM,WAAW,GAAG,IAAI;AAExB,mBAAO,CAAC,eAAe,EAAE,MAAM,UAAU,OAAO,cAAc,CAAC;AAAA,UACjE;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;","names":[]}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
"use strict";
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
|
||||
// src/undo-redo/index.ts
|
||||
var index_exports = {};
|
||||
__export(index_exports, {
|
||||
UndoRedo: () => UndoRedo
|
||||
});
|
||||
module.exports = __toCommonJS(index_exports);
|
||||
|
||||
// src/undo-redo/undo-redo.ts
|
||||
var import_core = require("@tiptap/core");
|
||||
var import_history = require("@tiptap/pm/history");
|
||||
var UndoRedo = import_core.Extension.create({
|
||||
name: "undoRedo",
|
||||
addOptions() {
|
||||
return {
|
||||
depth: 100,
|
||||
newGroupDelay: 500
|
||||
};
|
||||
},
|
||||
addCommands() {
|
||||
return {
|
||||
undo: () => ({ state, dispatch }) => {
|
||||
return (0, import_history.undo)(state, dispatch);
|
||||
},
|
||||
redo: () => ({ state, dispatch }) => {
|
||||
return (0, import_history.redo)(state, dispatch);
|
||||
}
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
return [(0, import_history.history)(this.options)];
|
||||
},
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
"Mod-z": () => this.editor.commands.undo(),
|
||||
"Shift-Mod-z": () => this.editor.commands.redo(),
|
||||
"Mod-y": () => this.editor.commands.redo(),
|
||||
// Russian keyboard layouts
|
||||
"Mod-\u044F": () => this.editor.commands.undo(),
|
||||
"Shift-Mod-\u044F": () => this.editor.commands.redo()
|
||||
};
|
||||
}
|
||||
});
|
||||
// Annotate the CommonJS export names for ESM import in node:
|
||||
0 && (module.exports = {
|
||||
UndoRedo
|
||||
});
|
||||
//# sourceMappingURL=index.cjs.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../src/undo-redo/index.ts","../../src/undo-redo/undo-redo.ts"],"sourcesContent":["export * from './undo-redo.js'\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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,kBAA0B;AAC1B,qBAAoC;AA4C7B,IAAM,WAAW,sBAAU,OAAwB;AAAA,EACxD,MAAM;AAAA,EAEN,aAAa;AACX,WAAO;AAAA,MACL,OAAO;AAAA,MACP,eAAe;AAAA,IACjB;AAAA,EACF;AAAA,EAEA,cAAc;AACZ,WAAO;AAAA,MACL,MACE,MACA,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,mBAAO,qBAAK,OAAO,QAAQ;AAAA,MAC7B;AAAA,MACF,MACE,MACA,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,mBAAO,qBAAK,OAAO,QAAQ;AAAA,MAC7B;AAAA,IACJ;AAAA,EACF;AAAA,EAEA,wBAAwB;AACtB,WAAO,KAAC,wBAAQ,KAAK,OAAO,CAAC;AAAA,EAC/B;AAAA,EAEA,uBAAuB;AACrB,WAAO;AAAA,MACL,SAAS,MAAM,KAAK,OAAO,SAAS,KAAK;AAAA,MACzC,eAAe,MAAM,KAAK,OAAO,SAAS,KAAK;AAAA,MAC/C,SAAS,MAAM,KAAK,OAAO,SAAS,KAAK;AAAA;AAAA,MAGzC,cAAS,MAAM,KAAK,OAAO,SAAS,KAAK;AAAA,MACzC,oBAAe,MAAM,KAAK,OAAO,SAAS,KAAK;AAAA,IACjD;AAAA,EACF;AACF,CAAC;","names":[]}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
import { Extension } from '@tiptap/core';
|
||||
|
||||
interface UndoRedoOptions {
|
||||
/**
|
||||
* The amount of history events that are collected before the oldest events are discarded.
|
||||
* @default 100
|
||||
* @example 50
|
||||
*/
|
||||
depth: number;
|
||||
/**
|
||||
* The delay (in milliseconds) between changes after which a new group should be started.
|
||||
* @default 500
|
||||
* @example 1000
|
||||
*/
|
||||
newGroupDelay: number;
|
||||
}
|
||||
declare module '@tiptap/core' {
|
||||
interface Commands<ReturnType> {
|
||||
undoRedo: {
|
||||
/**
|
||||
* Undo recent changes
|
||||
* @example editor.commands.undo()
|
||||
*/
|
||||
undo: () => ReturnType;
|
||||
/**
|
||||
* Reapply reverted changes
|
||||
* @example editor.commands.redo()
|
||||
*/
|
||||
redo: () => ReturnType;
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This extension allows you to undo and redo recent changes.
|
||||
* @see https://www.tiptap.dev/api/extensions/undo-redo
|
||||
*
|
||||
* **Important**: If the `@tiptap/extension-collaboration` package is used, make sure to remove
|
||||
* the `undo-redo` extension, as it is not compatible with the `collaboration` extension.
|
||||
*
|
||||
* `@tiptap/extension-collaboration` uses its own history implementation.
|
||||
*/
|
||||
declare const UndoRedo: Extension<UndoRedoOptions, any>;
|
||||
|
||||
export { UndoRedo, type UndoRedoOptions };
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
import { Extension } from '@tiptap/core';
|
||||
|
||||
interface UndoRedoOptions {
|
||||
/**
|
||||
* The amount of history events that are collected before the oldest events are discarded.
|
||||
* @default 100
|
||||
* @example 50
|
||||
*/
|
||||
depth: number;
|
||||
/**
|
||||
* The delay (in milliseconds) between changes after which a new group should be started.
|
||||
* @default 500
|
||||
* @example 1000
|
||||
*/
|
||||
newGroupDelay: number;
|
||||
}
|
||||
declare module '@tiptap/core' {
|
||||
interface Commands<ReturnType> {
|
||||
undoRedo: {
|
||||
/**
|
||||
* Undo recent changes
|
||||
* @example editor.commands.undo()
|
||||
*/
|
||||
undo: () => ReturnType;
|
||||
/**
|
||||
* Reapply reverted changes
|
||||
* @example editor.commands.redo()
|
||||
*/
|
||||
redo: () => ReturnType;
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This extension allows you to undo and redo recent changes.
|
||||
* @see https://www.tiptap.dev/api/extensions/undo-redo
|
||||
*
|
||||
* **Important**: If the `@tiptap/extension-collaboration` package is used, make sure to remove
|
||||
* the `undo-redo` extension, as it is not compatible with the `collaboration` extension.
|
||||
*
|
||||
* `@tiptap/extension-collaboration` uses its own history implementation.
|
||||
*/
|
||||
declare const UndoRedo: Extension<UndoRedoOptions, any>;
|
||||
|
||||
export { UndoRedo, type UndoRedoOptions };
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
// src/undo-redo/undo-redo.ts
|
||||
import { Extension } from "@tiptap/core";
|
||||
import { history, redo, undo } from "@tiptap/pm/history";
|
||||
var UndoRedo = Extension.create({
|
||||
name: "undoRedo",
|
||||
addOptions() {
|
||||
return {
|
||||
depth: 100,
|
||||
newGroupDelay: 500
|
||||
};
|
||||
},
|
||||
addCommands() {
|
||||
return {
|
||||
undo: () => ({ state, dispatch }) => {
|
||||
return undo(state, dispatch);
|
||||
},
|
||||
redo: () => ({ state, dispatch }) => {
|
||||
return redo(state, dispatch);
|
||||
}
|
||||
};
|
||||
},
|
||||
addProseMirrorPlugins() {
|
||||
return [history(this.options)];
|
||||
},
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
"Mod-z": () => this.editor.commands.undo(),
|
||||
"Shift-Mod-z": () => this.editor.commands.redo(),
|
||||
"Mod-y": () => this.editor.commands.redo(),
|
||||
// Russian keyboard layouts
|
||||
"Mod-\u044F": () => this.editor.commands.undo(),
|
||||
"Shift-Mod-\u044F": () => this.editor.commands.redo()
|
||||
};
|
||||
}
|
||||
});
|
||||
export {
|
||||
UndoRedo
|
||||
};
|
||||
//# sourceMappingURL=index.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["../../src/undo-redo/undo-redo.ts"],"sourcesContent":["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":";AAAA,SAAS,iBAAiB;AAC1B,SAAS,SAAS,MAAM,YAAY;AA4C7B,IAAM,WAAW,UAAU,OAAwB;AAAA,EACxD,MAAM;AAAA,EAEN,aAAa;AACX,WAAO;AAAA,MACL,OAAO;AAAA,MACP,eAAe;AAAA,IACjB;AAAA,EACF;AAAA,EAEA,cAAc;AACZ,WAAO;AAAA,MACL,MACE,MACA,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,eAAO,KAAK,OAAO,QAAQ;AAAA,MAC7B;AAAA,MACF,MACE,MACA,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,eAAO,KAAK,OAAO,QAAQ;AAAA,MAC7B;AAAA,IACJ;AAAA,EACF;AAAA,EAEA,wBAAwB;AACtB,WAAO,CAAC,QAAQ,KAAK,OAAO,CAAC;AAAA,EAC/B;AAAA,EAEA,uBAAuB;AACrB,WAAO;AAAA,MACL,SAAS,MAAM,KAAK,OAAO,SAAS,KAAK;AAAA,MACzC,eAAe,MAAM,KAAK,OAAO,SAAS,KAAK;AAAA,MAC/C,SAAS,MAAM,KAAK,OAAO,SAAS,KAAK;AAAA;AAAA,MAGzC,cAAS,MAAM,KAAK,OAAO,SAAS,KAAK;AAAA,MACzC,oBAAe,MAAM,KAAK,OAAO,SAAS,KAAK;AAAA,IACjD;AAAA,EACF;AACF,CAAC;","names":[]}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
{
|
||||
"name": "@tiptap/extensions",
|
||||
"description": "various extensions for tiptap",
|
||||
"version": "3.19.0",
|
||||
"homepage": "https://tiptap.dev",
|
||||
"keywords": [
|
||||
"tiptap",
|
||||
"tiptap extension"
|
||||
],
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": {
|
||||
"import": "./dist/index.d.ts",
|
||||
"require": "./dist/index.d.cts"
|
||||
},
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.cjs"
|
||||
},
|
||||
"./character-count": {
|
||||
"types": {
|
||||
"import": "./dist/character-count/index.d.ts",
|
||||
"require": "./dist/character-count/index.d.cts"
|
||||
},
|
||||
"import": "./dist/character-count/index.js",
|
||||
"require": "./dist/character-count/index.cjs"
|
||||
},
|
||||
"./drop-cursor": {
|
||||
"types": {
|
||||
"import": "./dist/drop-cursor/index.d.ts",
|
||||
"require": "./dist/drop-cursor/index.d.cts"
|
||||
},
|
||||
"import": "./dist/drop-cursor/index.js",
|
||||
"require": "./dist/drop-cursor/index.cjs"
|
||||
},
|
||||
"./focus": {
|
||||
"types": {
|
||||
"import": "./dist/focus/index.d.ts",
|
||||
"require": "./dist/focus/index.d.cts"
|
||||
},
|
||||
"import": "./dist/focus/index.js",
|
||||
"require": "./dist/focus/index.cjs"
|
||||
},
|
||||
"./gap-cursor": {
|
||||
"types": {
|
||||
"import": "./dist/gap-cursor/index.d.ts",
|
||||
"require": "./dist/gap-cursor/index.d.cts"
|
||||
},
|
||||
"import": "./dist/gap-cursor/index.js",
|
||||
"require": "./dist/gap-cursor/index.cjs"
|
||||
},
|
||||
"./undo-redo": {
|
||||
"types": {
|
||||
"import": "./dist/undo-redo/index.d.ts",
|
||||
"require": "./dist/undo-redo/index.d.cts"
|
||||
},
|
||||
"import": "./dist/undo-redo/index.js",
|
||||
"require": "./dist/undo-redo/index.cjs"
|
||||
},
|
||||
"./placeholder": {
|
||||
"types": {
|
||||
"import": "./dist/placeholder/index.d.ts",
|
||||
"require": "./dist/placeholder/index.d.cts"
|
||||
},
|
||||
"import": "./dist/placeholder/index.js",
|
||||
"require": "./dist/placeholder/index.cjs"
|
||||
},
|
||||
"./selection": {
|
||||
"types": {
|
||||
"import": "./dist/selection/index.d.ts",
|
||||
"require": "./dist/selection/index.d.cts"
|
||||
},
|
||||
"import": "./dist/selection/index.js",
|
||||
"require": "./dist/selection/index.cjs"
|
||||
},
|
||||
"./trailing-node": {
|
||||
"types": {
|
||||
"import": "./dist/trailing-node/index.d.ts",
|
||||
"require": "./dist/trailing-node/index.d.cts"
|
||||
},
|
||||
"import": "./dist/trailing-node/index.js",
|
||||
"require": "./dist/trailing-node/index.cjs"
|
||||
}
|
||||
},
|
||||
"main": "dist/index.cjs",
|
||||
"module": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": [
|
||||
"src",
|
||||
"dist"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@tiptap/core": "^3.19.0",
|
||||
"@tiptap/pm": "^3.19.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^3.19.0",
|
||||
"@tiptap/pm": "^3.19.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ueberdosis/tiptap",
|
||||
"directory": "packages/extension"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsup",
|
||||
"lint": "prettier ./src/ --check && eslint --cache --quiet --no-error-on-unmatched-pattern ./src/"
|
||||
}
|
||||
}
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
import { Extension } from '@tiptap/core'
|
||||
import type { Node as ProseMirrorNode } from '@tiptap/pm/model'
|
||||
import { Plugin, PluginKey } from '@tiptap/pm/state'
|
||||
|
||||
export interface CharacterCountOptions {
|
||||
/**
|
||||
* The maximum number of characters that should be allowed. Defaults to `0`.
|
||||
* @default null
|
||||
* @example 180
|
||||
*/
|
||||
limit: number | null | undefined
|
||||
/**
|
||||
* The mode by which the size is calculated. If set to `textSize`, the textContent of the document is used.
|
||||
* If set to `nodeSize`, the nodeSize of the document is used.
|
||||
* @default 'textSize'
|
||||
* @example 'textSize'
|
||||
*/
|
||||
mode: 'textSize' | 'nodeSize'
|
||||
/**
|
||||
* The text counter function to use. Defaults to a simple character count.
|
||||
* @default (text) => text.length
|
||||
* @example (text) => [...new Intl.Segmenter().segment(text)].length
|
||||
*/
|
||||
textCounter: (text: string) => number
|
||||
/**
|
||||
* The word counter function to use. Defaults to a simple word count.
|
||||
* @default (text) => text.split(' ').filter(word => word !== '').length
|
||||
* @example (text) => text.split(/\s+/).filter(word => word !== '').length
|
||||
*/
|
||||
wordCounter: (text: string) => number
|
||||
}
|
||||
|
||||
export interface CharacterCountStorage {
|
||||
/**
|
||||
* Get the number of characters for the current document.
|
||||
* @param options The options for the character count. (optional)
|
||||
* @param options.node The node to get the characters from. Defaults to the current document.
|
||||
* @param options.mode The mode by which the size is calculated. If set to `textSize`, the textContent of the document is used.
|
||||
*/
|
||||
characters: (options?: { node?: ProseMirrorNode; mode?: 'textSize' | 'nodeSize' }) => number
|
||||
|
||||
/**
|
||||
* Get the number of words for the current document.
|
||||
* @param options The options for the character count. (optional)
|
||||
* @param options.node The node to get the words from. Defaults to the current document.
|
||||
*/
|
||||
words: (options?: { node?: ProseMirrorNode }) => number
|
||||
}
|
||||
|
||||
declare module '@tiptap/core' {
|
||||
interface Storage {
|
||||
characterCount: CharacterCountStorage
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This extension allows you to count the characters and words of your document.
|
||||
* @see https://tiptap.dev/api/extensions/character-count
|
||||
*/
|
||||
export const CharacterCount = Extension.create<CharacterCountOptions, CharacterCountStorage>({
|
||||
name: 'characterCount',
|
||||
|
||||
addOptions() {
|
||||
return {
|
||||
limit: null,
|
||||
mode: 'textSize',
|
||||
textCounter: text => text.length,
|
||||
wordCounter: text => text.split(' ').filter(word => word !== '').length,
|
||||
}
|
||||
},
|
||||
|
||||
addStorage() {
|
||||
return {
|
||||
characters: () => 0,
|
||||
words: () => 0,
|
||||
}
|
||||
},
|
||||
|
||||
onBeforeCreate() {
|
||||
this.storage.characters = options => {
|
||||
const node = options?.node || this.editor.state.doc
|
||||
const mode = options?.mode || this.options.mode
|
||||
|
||||
if (mode === 'textSize') {
|
||||
const text = node.textBetween(0, node.content.size, undefined, ' ')
|
||||
|
||||
return this.options.textCounter(text)
|
||||
}
|
||||
|
||||
return node.nodeSize
|
||||
}
|
||||
|
||||
this.storage.words = options => {
|
||||
const node = options?.node || this.editor.state.doc
|
||||
const text = node.textBetween(0, node.content.size, ' ', ' ')
|
||||
|
||||
return this.options.wordCounter(text)
|
||||
}
|
||||
},
|
||||
|
||||
addProseMirrorPlugins() {
|
||||
let initialEvaluationDone = false
|
||||
|
||||
return [
|
||||
new Plugin({
|
||||
key: new PluginKey('characterCount'),
|
||||
appendTransaction: (transactions, oldState, newState) => {
|
||||
if (initialEvaluationDone) {
|
||||
return
|
||||
}
|
||||
|
||||
const limit = this.options.limit
|
||||
|
||||
if (limit === null || limit === undefined || limit === 0) {
|
||||
initialEvaluationDone = true
|
||||
return
|
||||
}
|
||||
|
||||
const initialContentSize = this.storage.characters({ node: newState.doc })
|
||||
|
||||
if (initialContentSize > limit) {
|
||||
const over = initialContentSize - limit
|
||||
const from = 0
|
||||
const to = over
|
||||
|
||||
console.warn(
|
||||
`[CharacterCount] Initial content exceeded limit of ${limit} characters. Content was automatically trimmed.`,
|
||||
)
|
||||
const tr = newState.tr.deleteRange(from, to)
|
||||
|
||||
initialEvaluationDone = true
|
||||
return tr
|
||||
}
|
||||
|
||||
initialEvaluationDone = true
|
||||
},
|
||||
filterTransaction: (transaction, state) => {
|
||||
const limit = this.options.limit
|
||||
|
||||
// Nothing has changed or no limit is defined. Ignore it.
|
||||
if (!transaction.docChanged || limit === 0 || limit === null || limit === undefined) {
|
||||
return true
|
||||
}
|
||||
|
||||
const oldSize = this.storage.characters({ node: state.doc })
|
||||
const newSize = this.storage.characters({ node: transaction.doc })
|
||||
|
||||
// Everything is in the limit. Good.
|
||||
if (newSize <= limit) {
|
||||
return true
|
||||
}
|
||||
|
||||
// The limit has already been exceeded but will be reduced.
|
||||
if (oldSize > limit && newSize > limit && newSize <= oldSize) {
|
||||
return true
|
||||
}
|
||||
|
||||
// The limit has already been exceeded and will be increased further.
|
||||
if (oldSize > limit && newSize > limit && newSize > oldSize) {
|
||||
return false
|
||||
}
|
||||
|
||||
const isPaste = transaction.getMeta('paste')
|
||||
|
||||
// Block all exceeding transactions that were not pasted.
|
||||
if (!isPaste) {
|
||||
return false
|
||||
}
|
||||
|
||||
// For pasted content, we try to remove the exceeding content.
|
||||
const pos = transaction.selection.$head.pos
|
||||
const over = newSize - limit
|
||||
const from = pos - over
|
||||
const to = pos
|
||||
|
||||
// It’s probably a bad idea to mutate transactions within `filterTransaction`
|
||||
// but for now this is working fine.
|
||||
transaction.deleteRange(from, to)
|
||||
|
||||
// In some situations, the limit will continue to be exceeded after trimming.
|
||||
// This happens e.g. when truncating within a complex node (e.g. table)
|
||||
// and ProseMirror has to close this node again.
|
||||
// If this is the case, we prevent the transaction completely.
|
||||
const updatedSize = this.storage.characters({ node: transaction.doc })
|
||||
|
||||
if (updatedSize > limit) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
}),
|
||||
]
|
||||
},
|
||||
})
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from './character-count.js'
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
import { Extension } from '@tiptap/core'
|
||||
import { dropCursor } from '@tiptap/pm/dropcursor'
|
||||
|
||||
export interface DropcursorOptions {
|
||||
/**
|
||||
* The color of the drop cursor. Use `false` to apply no color and rely only on class.
|
||||
* @default 'currentColor'
|
||||
* @example 'red'
|
||||
*/
|
||||
color?: string | false
|
||||
|
||||
/**
|
||||
* The width of the drop cursor
|
||||
* @default 1
|
||||
* @example 2
|
||||
*/
|
||||
width: number | undefined
|
||||
|
||||
/**
|
||||
* The class of the drop cursor
|
||||
* @default undefined
|
||||
* @example 'drop-cursor'
|
||||
*/
|
||||
class: string | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* This extension allows you to add a drop cursor to your editor.
|
||||
* A drop cursor is a line that appears when you drag and drop content
|
||||
* in-between nodes.
|
||||
* @see https://tiptap.dev/api/extensions/dropcursor
|
||||
*/
|
||||
export const Dropcursor = Extension.create<DropcursorOptions>({
|
||||
name: 'dropCursor',
|
||||
|
||||
addOptions() {
|
||||
return {
|
||||
color: 'currentColor',
|
||||
width: 1,
|
||||
class: undefined,
|
||||
}
|
||||
},
|
||||
|
||||
addProseMirrorPlugins() {
|
||||
return [dropCursor(this.options)]
|
||||
},
|
||||
})
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from './drop-cursor.js'
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
import { Extension } from '@tiptap/core'
|
||||
import { Plugin, PluginKey } from '@tiptap/pm/state'
|
||||
import { Decoration, DecorationSet } from '@tiptap/pm/view'
|
||||
|
||||
export interface FocusOptions {
|
||||
/**
|
||||
* The class name that should be added to the focused node.
|
||||
* @default 'has-focus'
|
||||
* @example 'is-focused'
|
||||
*/
|
||||
className: string
|
||||
|
||||
/**
|
||||
* The mode by which the focused node is determined.
|
||||
* - All: All nodes are marked as focused.
|
||||
* - Deepest: Only the deepest node is marked as focused.
|
||||
* - Shallowest: Only the shallowest node is marked as focused.
|
||||
*
|
||||
* @default 'all'
|
||||
* @example 'deepest'
|
||||
* @example 'shallowest'
|
||||
*/
|
||||
mode: 'all' | 'deepest' | 'shallowest'
|
||||
}
|
||||
|
||||
/**
|
||||
* This extension allows you to add a class to the focused node.
|
||||
* @see https://www.tiptap.dev/api/extensions/focus
|
||||
*/
|
||||
export const Focus = Extension.create<FocusOptions>({
|
||||
name: 'focus',
|
||||
|
||||
addOptions() {
|
||||
return {
|
||||
className: 'has-focus',
|
||||
mode: 'all',
|
||||
}
|
||||
},
|
||||
|
||||
addProseMirrorPlugins() {
|
||||
return [
|
||||
new Plugin({
|
||||
key: new PluginKey('focus'),
|
||||
props: {
|
||||
decorations: ({ doc, selection }) => {
|
||||
const { isEditable, isFocused } = this.editor
|
||||
const { anchor } = selection
|
||||
const decorations: Decoration[] = []
|
||||
|
||||
if (!isEditable || !isFocused) {
|
||||
return DecorationSet.create(doc, [])
|
||||
}
|
||||
|
||||
// Maximum Levels
|
||||
let maxLevels = 0
|
||||
|
||||
if (this.options.mode === 'deepest') {
|
||||
doc.descendants((node, pos) => {
|
||||
if (node.isText) {
|
||||
return
|
||||
}
|
||||
|
||||
const isCurrent = anchor >= pos && anchor <= pos + node.nodeSize - 1
|
||||
|
||||
if (!isCurrent) {
|
||||
return false
|
||||
}
|
||||
|
||||
maxLevels += 1
|
||||
})
|
||||
}
|
||||
|
||||
// Loop through current
|
||||
let currentLevel = 0
|
||||
|
||||
doc.descendants((node, pos) => {
|
||||
if (node.isText) {
|
||||
return false
|
||||
}
|
||||
|
||||
const isCurrent = anchor >= pos && anchor <= pos + node.nodeSize - 1
|
||||
|
||||
if (!isCurrent) {
|
||||
return false
|
||||
}
|
||||
|
||||
currentLevel += 1
|
||||
|
||||
const outOfScope =
|
||||
(this.options.mode === 'deepest' && maxLevels - currentLevel > 0) ||
|
||||
(this.options.mode === 'shallowest' && currentLevel > 1)
|
||||
|
||||
if (outOfScope) {
|
||||
return this.options.mode === 'deepest'
|
||||
}
|
||||
|
||||
decorations.push(
|
||||
Decoration.node(pos, pos + node.nodeSize, {
|
||||
class: this.options.className,
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
return DecorationSet.create(doc, decorations)
|
||||
},
|
||||
},
|
||||
}),
|
||||
]
|
||||
},
|
||||
})
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from './focus.js'
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
import type { ParentConfig } from '@tiptap/core'
|
||||
import { callOrReturn, Extension, getExtensionField } from '@tiptap/core'
|
||||
import { gapCursor } from '@tiptap/pm/gapcursor'
|
||||
|
||||
declare module '@tiptap/core' {
|
||||
interface NodeConfig<Options, Storage> {
|
||||
/**
|
||||
* A function to determine whether the gap cursor is allowed at the current position. Must return `true` or `false`.
|
||||
* @default null
|
||||
*/
|
||||
allowGapCursor?:
|
||||
| boolean
|
||||
| null
|
||||
| ((this: {
|
||||
name: string
|
||||
options: Options
|
||||
storage: Storage
|
||||
parent: ParentConfig<NodeConfig<Options>>['allowGapCursor']
|
||||
}) => boolean | null)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This extension allows you to add a gap cursor to your editor.
|
||||
* A gap cursor is a cursor that appears when you click on a place
|
||||
* where no content is present, for example inbetween nodes.
|
||||
* @see https://tiptap.dev/api/extensions/gapcursor
|
||||
*/
|
||||
export const Gapcursor = Extension.create({
|
||||
name: 'gapCursor',
|
||||
|
||||
addProseMirrorPlugins() {
|
||||
return [gapCursor()]
|
||||
},
|
||||
|
||||
extendNodeSchema(extension) {
|
||||
const context = {
|
||||
name: extension.name,
|
||||
options: extension.options,
|
||||
storage: extension.storage,
|
||||
}
|
||||
|
||||
return {
|
||||
allowGapCursor: callOrReturn(getExtensionField(extension, 'allowGapCursor', context)) ?? null,
|
||||
}
|
||||
},
|
||||
})
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from './gap-cursor.js'
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
export * from './character-count/index.js'
|
||||
export * from './drop-cursor/index.js'
|
||||
export * from './focus/index.js'
|
||||
export * from './gap-cursor/index.js'
|
||||
export * from './placeholder/index.js'
|
||||
export * from './selection/index.js'
|
||||
export * from './trailing-node/index.js'
|
||||
export * from './undo-redo/index.js'
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from './placeholder.js'
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
import type { Editor } from '@tiptap/core'
|
||||
import { Extension, isNodeEmpty } from '@tiptap/core'
|
||||
import type { Node as ProsemirrorNode } from '@tiptap/pm/model'
|
||||
import { Plugin, PluginKey } from '@tiptap/pm/state'
|
||||
import { Decoration, DecorationSet } from '@tiptap/pm/view'
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
/**
|
||||
* **The class name for the empty editor**
|
||||
* @default 'is-editor-empty'
|
||||
*/
|
||||
emptyEditorClass: string
|
||||
|
||||
/**
|
||||
* **The class name for empty nodes**
|
||||
* @default 'is-empty'
|
||||
*/
|
||||
emptyNodeClass: string
|
||||
|
||||
/**
|
||||
* **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**
|
||||
*
|
||||
* You can use a function to return a dynamic placeholder or a string.
|
||||
* @default 'Write something …'
|
||||
*/
|
||||
placeholder:
|
||||
| ((PlaceholderProps: { editor: Editor; node: ProsemirrorNode; pos: number; hasAnchor: boolean }) => string)
|
||||
| string
|
||||
|
||||
/**
|
||||
* **Checks if the placeholder should be only shown when the editor is editable.**
|
||||
*
|
||||
* If true, the placeholder will only be shown when the editor is editable.
|
||||
* If false, the placeholder will always be shown.
|
||||
* @default true
|
||||
*/
|
||||
showOnlyWhenEditable: boolean
|
||||
|
||||
/**
|
||||
* **Checks if the placeholder should be only shown when the current node is empty.**
|
||||
*
|
||||
* If true, the placeholder will only be shown when the current node is empty.
|
||||
* If false, the placeholder will be shown when any node is empty.
|
||||
* @default true
|
||||
*/
|
||||
showOnlyCurrent: boolean
|
||||
|
||||
/**
|
||||
* **Controls if the placeholder should be shown for all descendents.**
|
||||
*
|
||||
* If true, the placeholder will be shown for all descendents.
|
||||
* If false, the placeholder will only be shown for the current node.
|
||||
* @default false
|
||||
*/
|
||||
includeChildren: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* This extension allows you to add a placeholder to your editor.
|
||||
* A placeholder is a text that appears when the editor or a node is empty.
|
||||
* @see https://www.tiptap.dev/api/extensions/placeholder
|
||||
*/
|
||||
export const Placeholder = Extension.create<PlaceholderOptions>({
|
||||
name: 'placeholder',
|
||||
|
||||
addOptions() {
|
||||
return {
|
||||
emptyEditorClass: 'is-editor-empty',
|
||||
emptyNodeClass: 'is-empty',
|
||||
dataAttribute: DEFAULT_DATA_ATTRIBUTE,
|
||||
placeholder: 'Write something …',
|
||||
showOnlyWhenEditable: true,
|
||||
showOnlyCurrent: true,
|
||||
includeChildren: false,
|
||||
}
|
||||
},
|
||||
|
||||
addProseMirrorPlugins() {
|
||||
const dataAttribute = this.options.dataAttribute
|
||||
? `data-${preparePlaceholderAttribute(this.options.dataAttribute)}`
|
||||
: `data-${DEFAULT_DATA_ATTRIBUTE}`
|
||||
|
||||
return [
|
||||
new Plugin({
|
||||
key: new PluginKey('placeholder'),
|
||||
props: {
|
||||
decorations: ({ doc, selection }) => {
|
||||
const active = this.editor.isEditable || !this.options.showOnlyWhenEditable
|
||||
const { anchor } = selection
|
||||
const decorations: Decoration[] = []
|
||||
|
||||
if (!active) {
|
||||
return null
|
||||
}
|
||||
|
||||
const isEmptyDoc = this.editor.isEmpty
|
||||
|
||||
doc.descendants((node, pos) => {
|
||||
const hasAnchor = anchor >= pos && anchor <= pos + node.nodeSize
|
||||
const isEmpty = !node.isLeaf && isNodeEmpty(node)
|
||||
|
||||
if ((hasAnchor || !this.options.showOnlyCurrent) && isEmpty) {
|
||||
const classes = [this.options.emptyNodeClass]
|
||||
|
||||
if (isEmptyDoc) {
|
||||
classes.push(this.options.emptyEditorClass)
|
||||
}
|
||||
|
||||
const decoration = Decoration.node(pos, pos + node.nodeSize, {
|
||||
class: classes.join(' '),
|
||||
[dataAttribute]:
|
||||
typeof this.options.placeholder === 'function'
|
||||
? this.options.placeholder({
|
||||
editor: this.editor,
|
||||
node,
|
||||
pos,
|
||||
hasAnchor,
|
||||
})
|
||||
: this.options.placeholder,
|
||||
})
|
||||
|
||||
decorations.push(decoration)
|
||||
}
|
||||
|
||||
return this.options.includeChildren
|
||||
})
|
||||
|
||||
return DecorationSet.create(doc, decorations)
|
||||
},
|
||||
},
|
||||
}),
|
||||
]
|
||||
},
|
||||
})
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from './selection.js'
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
import { Extension, isNodeSelection } from '@tiptap/core'
|
||||
import { Plugin, PluginKey } from '@tiptap/pm/state'
|
||||
import { Decoration, DecorationSet } from '@tiptap/pm/view'
|
||||
|
||||
export type SelectionOptions = {
|
||||
/**
|
||||
* The class name that should be added to the selected text.
|
||||
* @default 'selection'
|
||||
* @example 'is-selected'
|
||||
*/
|
||||
className: string
|
||||
}
|
||||
|
||||
/**
|
||||
* This extension allows you to add a class to the selected text.
|
||||
* @see https://www.tiptap.dev/api/extensions/selection
|
||||
*/
|
||||
export const Selection = Extension.create<SelectionOptions>({
|
||||
name: 'selection',
|
||||
|
||||
addOptions() {
|
||||
return {
|
||||
className: 'selection',
|
||||
}
|
||||
},
|
||||
|
||||
addProseMirrorPlugins() {
|
||||
const { editor, options } = this
|
||||
|
||||
return [
|
||||
new Plugin({
|
||||
key: new PluginKey('selection'),
|
||||
props: {
|
||||
decorations(state) {
|
||||
if (
|
||||
state.selection.empty ||
|
||||
editor.isFocused ||
|
||||
!editor.isEditable ||
|
||||
isNodeSelection(state.selection) ||
|
||||
editor.view.dragging
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
return DecorationSet.create(state.doc, [
|
||||
Decoration.inline(state.selection.from, state.selection.to, {
|
||||
class: options.className,
|
||||
}),
|
||||
])
|
||||
},
|
||||
},
|
||||
}),
|
||||
]
|
||||
},
|
||||
})
|
||||
|
||||
export default Selection
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from './trailing-node.js'
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
import { Extension } from '@tiptap/core'
|
||||
import type { Node, NodeType } from '@tiptap/pm/model'
|
||||
import { Plugin, PluginKey } from '@tiptap/pm/state'
|
||||
|
||||
function nodeEqualsType({ types, node }: { types: NodeType | NodeType[]; node: Node | null | undefined }) {
|
||||
return (node && Array.isArray(types) && types.includes(node.type)) || node?.type === types
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension based on:
|
||||
* - https://github.com/ueberdosis/tiptap/blob/v1/packages/tiptap-extensions/src/extensions/TrailingNode.js
|
||||
* - https://github.com/remirror/remirror/blob/e0f1bec4a1e8073ce8f5500d62193e52321155b9/packages/prosemirror-trailing-node/src/trailing-node-plugin.ts
|
||||
*/
|
||||
|
||||
export interface TrailingNodeOptions {
|
||||
/**
|
||||
* The node type that should be inserted at the end of the document.
|
||||
* @note the node will always be added to the `notAfter` lists to
|
||||
* prevent an infinite loop.
|
||||
* @default undefined
|
||||
*/
|
||||
node?: string
|
||||
/**
|
||||
* The node types after which the trailing node should not be inserted.
|
||||
* @default ['paragraph']
|
||||
*/
|
||||
notAfter?: string | string[]
|
||||
}
|
||||
|
||||
/**
|
||||
* This extension allows you to add an extra node at the end of the document.
|
||||
* @see https://www.tiptap.dev/api/extensions/trailing-node
|
||||
*/
|
||||
export const TrailingNode = Extension.create<TrailingNodeOptions>({
|
||||
name: 'trailingNode',
|
||||
|
||||
addOptions() {
|
||||
return {
|
||||
node: undefined,
|
||||
notAfter: [],
|
||||
}
|
||||
},
|
||||
|
||||
addProseMirrorPlugins() {
|
||||
const plugin = new PluginKey(this.name)
|
||||
const defaultNode =
|
||||
this.options.node || this.editor.schema.topNodeType.contentMatch.defaultType?.name || 'paragraph'
|
||||
|
||||
const disabledNodes = Object.entries(this.editor.schema.nodes)
|
||||
.map(([, value]) => value)
|
||||
.filter(node => (this.options.notAfter || []).concat(defaultNode).includes(node.name))
|
||||
|
||||
return [
|
||||
new Plugin({
|
||||
key: plugin,
|
||||
appendTransaction: (_, __, state) => {
|
||||
const { doc, tr, schema } = state
|
||||
const shouldInsertNodeAtEnd = plugin.getState(state)
|
||||
const endPosition = doc.content.size
|
||||
const type = schema.nodes[defaultNode]
|
||||
|
||||
if (!shouldInsertNodeAtEnd) {
|
||||
return
|
||||
}
|
||||
|
||||
return tr.insert(endPosition, type.create())
|
||||
},
|
||||
state: {
|
||||
init: (_, state) => {
|
||||
const lastNode = state.tr.doc.lastChild
|
||||
|
||||
return !nodeEqualsType({ node: lastNode, types: disabledNodes })
|
||||
},
|
||||
apply: (tr, value) => {
|
||||
if (!tr.docChanged) {
|
||||
return value
|
||||
}
|
||||
|
||||
// Ignore transactions from UniqueID extension to prevent infinite loops
|
||||
// when UniqueID adds IDs to newly inserted trailing nodes
|
||||
if (tr.getMeta('__uniqueIDTransaction')) {
|
||||
return value
|
||||
}
|
||||
|
||||
const lastNode = tr.doc.lastChild
|
||||
|
||||
return !nodeEqualsType({ node: lastNode, types: disabledNodes })
|
||||
},
|
||||
},
|
||||
}),
|
||||
]
|
||||
},
|
||||
})
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from './undo-redo.js'
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
import { Extension } from '@tiptap/core'
|
||||
import { history, redo, undo } from '@tiptap/pm/history'
|
||||
|
||||
export interface UndoRedoOptions {
|
||||
/**
|
||||
* The amount of history events that are collected before the oldest events are discarded.
|
||||
* @default 100
|
||||
* @example 50
|
||||
*/
|
||||
depth: number
|
||||
|
||||
/**
|
||||
* The delay (in milliseconds) between changes after which a new group should be started.
|
||||
* @default 500
|
||||
* @example 1000
|
||||
*/
|
||||
newGroupDelay: number
|
||||
}
|
||||
|
||||
declare module '@tiptap/core' {
|
||||
interface Commands<ReturnType> {
|
||||
undoRedo: {
|
||||
/**
|
||||
* Undo recent changes
|
||||
* @example editor.commands.undo()
|
||||
*/
|
||||
undo: () => ReturnType
|
||||
/**
|
||||
* Reapply reverted changes
|
||||
* @example editor.commands.redo()
|
||||
*/
|
||||
redo: () => ReturnType
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This extension allows you to undo and redo recent changes.
|
||||
* @see https://www.tiptap.dev/api/extensions/undo-redo
|
||||
*
|
||||
* **Important**: If the `@tiptap/extension-collaboration` package is used, make sure to remove
|
||||
* the `undo-redo` extension, as it is not compatible with the `collaboration` extension.
|
||||
*
|
||||
* `@tiptap/extension-collaboration` uses its own history implementation.
|
||||
*/
|
||||
export const UndoRedo = Extension.create<UndoRedoOptions>({
|
||||
name: 'undoRedo',
|
||||
|
||||
addOptions() {
|
||||
return {
|
||||
depth: 100,
|
||||
newGroupDelay: 500,
|
||||
}
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {
|
||||
undo:
|
||||
() =>
|
||||
({ state, dispatch }) => {
|
||||
return undo(state, dispatch)
|
||||
},
|
||||
redo:
|
||||
() =>
|
||||
({ state, dispatch }) => {
|
||||
return redo(state, dispatch)
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
addProseMirrorPlugins() {
|
||||
return [history(this.options)]
|
||||
},
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
'Mod-z': () => this.editor.commands.undo(),
|
||||
'Shift-Mod-z': () => this.editor.commands.redo(),
|
||||
'Mod-y': () => this.editor.commands.redo(),
|
||||
|
||||
// Russian keyboard layouts
|
||||
'Mod-я': () => this.editor.commands.undo(),
|
||||
'Shift-Mod-я': () => this.editor.commands.redo(),
|
||||
}
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user