@tiptap/core 3.19.0 → 3.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +118 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +38 -2
- package/dist/index.d.ts +38 -2
- package/dist/index.js +118 -13
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/Editor.ts +8 -3
- package/src/Extendable.ts +24 -0
- package/src/ExtensionManager.ts +42 -1
- package/src/__tests__/transformPastedHTML.test.ts +575 -0
- package/src/helpers/getAttributesFromExtensions.ts +20 -1
- package/src/helpers/isMarkActive.ts +5 -0
- package/src/types.ts +11 -1
- package/src/utilities/mergeAttributes.ts +74 -26
package/dist/index.d.cts
CHANGED
|
@@ -719,6 +719,26 @@ interface ExtendableConfig<Options = any, Storage = any, Config extends Extensio
|
|
|
719
719
|
type: PMType;
|
|
720
720
|
parent: ParentConfig<Config>['addProseMirrorPlugins'];
|
|
721
721
|
}) => Plugin[];
|
|
722
|
+
/**
|
|
723
|
+
* This function transforms pasted HTML content before it's parsed.
|
|
724
|
+
* Extensions can use this to modify or clean up pasted HTML.
|
|
725
|
+
* The transformations are chained - each extension's transform receives
|
|
726
|
+
* the output from the previous extension's transform.
|
|
727
|
+
* @see https://tiptap.dev/docs/editor/guide/custom-extensions#transform-pasted-html
|
|
728
|
+
* @example
|
|
729
|
+
* transformPastedHTML(html) {
|
|
730
|
+
* // Remove all style attributes
|
|
731
|
+
* return html.replace(/style="[^"]*"/g, '')
|
|
732
|
+
* }
|
|
733
|
+
*/
|
|
734
|
+
transformPastedHTML?: (this: {
|
|
735
|
+
name: string;
|
|
736
|
+
options: Options;
|
|
737
|
+
storage: Storage;
|
|
738
|
+
editor: Editor;
|
|
739
|
+
type: PMType;
|
|
740
|
+
parent: ParentConfig<Config>['transformPastedHTML'];
|
|
741
|
+
}, html: string) => string;
|
|
722
742
|
/**
|
|
723
743
|
* This function adds additional extensions to the editor. This is useful for
|
|
724
744
|
* building extension kits.
|
|
@@ -1496,8 +1516,18 @@ type ExtensionAttribute = {
|
|
|
1496
1516
|
type GlobalAttributes = {
|
|
1497
1517
|
/**
|
|
1498
1518
|
* The node & mark types this attribute should be applied to.
|
|
1519
|
+
* Can be a specific array of type names, or a shorthand string:
|
|
1520
|
+
* - `'*'` applies to all nodes (excluding text) and all marks
|
|
1521
|
+
* - `'nodes'` applies to all nodes (excluding the built-in text node)
|
|
1522
|
+
* - `'marks'` applies to all marks
|
|
1523
|
+
* - `string[]` applies to specific node/mark types by name
|
|
1524
|
+
* @example
|
|
1525
|
+
* types: '*' // All nodes and marks
|
|
1526
|
+
* types: 'nodes' // All nodes
|
|
1527
|
+
* types: 'marks' // All marks
|
|
1528
|
+
* types: ['heading', 'paragraph'] // Specific types
|
|
1499
1529
|
*/
|
|
1500
|
-
types: string[];
|
|
1530
|
+
types: string[] | 'nodes' | 'marks' | '*';
|
|
1501
1531
|
/**
|
|
1502
1532
|
* The attributes to add to the node or mark types.
|
|
1503
1533
|
*/
|
|
@@ -2320,6 +2350,12 @@ declare class ExtensionManager {
|
|
|
2320
2350
|
* @returns A composed dispatch function
|
|
2321
2351
|
*/
|
|
2322
2352
|
dispatchTransaction(baseDispatch: (tr: Transaction) => void): (tr: Transaction) => void;
|
|
2353
|
+
/**
|
|
2354
|
+
* Get the composed transformPastedHTML function from all extensions.
|
|
2355
|
+
* @param baseTransform The base transform function (e.g. from the editor props)
|
|
2356
|
+
* @returns A composed transform function that chains all extension transforms
|
|
2357
|
+
*/
|
|
2358
|
+
transformPastedHTML(baseTransform?: (html: string, view?: any) => string): (html: string, view?: EditorView) => string;
|
|
2323
2359
|
get markViews(): Record<string, MarkViewConstructor>;
|
|
2324
2360
|
/**
|
|
2325
2361
|
* Go through all extensions, create extension storages & setup marks
|
|
@@ -3528,7 +3564,7 @@ declare class Editor extends EventEmitter<EditorEvents> {
|
|
|
3528
3564
|
*/
|
|
3529
3565
|
get isEditable(): boolean;
|
|
3530
3566
|
/**
|
|
3531
|
-
* Returns the editor
|
|
3567
|
+
* Returns the editor view.
|
|
3532
3568
|
*/
|
|
3533
3569
|
get view(): EditorView;
|
|
3534
3570
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -719,6 +719,26 @@ interface ExtendableConfig<Options = any, Storage = any, Config extends Extensio
|
|
|
719
719
|
type: PMType;
|
|
720
720
|
parent: ParentConfig<Config>['addProseMirrorPlugins'];
|
|
721
721
|
}) => Plugin[];
|
|
722
|
+
/**
|
|
723
|
+
* This function transforms pasted HTML content before it's parsed.
|
|
724
|
+
* Extensions can use this to modify or clean up pasted HTML.
|
|
725
|
+
* The transformations are chained - each extension's transform receives
|
|
726
|
+
* the output from the previous extension's transform.
|
|
727
|
+
* @see https://tiptap.dev/docs/editor/guide/custom-extensions#transform-pasted-html
|
|
728
|
+
* @example
|
|
729
|
+
* transformPastedHTML(html) {
|
|
730
|
+
* // Remove all style attributes
|
|
731
|
+
* return html.replace(/style="[^"]*"/g, '')
|
|
732
|
+
* }
|
|
733
|
+
*/
|
|
734
|
+
transformPastedHTML?: (this: {
|
|
735
|
+
name: string;
|
|
736
|
+
options: Options;
|
|
737
|
+
storage: Storage;
|
|
738
|
+
editor: Editor;
|
|
739
|
+
type: PMType;
|
|
740
|
+
parent: ParentConfig<Config>['transformPastedHTML'];
|
|
741
|
+
}, html: string) => string;
|
|
722
742
|
/**
|
|
723
743
|
* This function adds additional extensions to the editor. This is useful for
|
|
724
744
|
* building extension kits.
|
|
@@ -1496,8 +1516,18 @@ type ExtensionAttribute = {
|
|
|
1496
1516
|
type GlobalAttributes = {
|
|
1497
1517
|
/**
|
|
1498
1518
|
* The node & mark types this attribute should be applied to.
|
|
1519
|
+
* Can be a specific array of type names, or a shorthand string:
|
|
1520
|
+
* - `'*'` applies to all nodes (excluding text) and all marks
|
|
1521
|
+
* - `'nodes'` applies to all nodes (excluding the built-in text node)
|
|
1522
|
+
* - `'marks'` applies to all marks
|
|
1523
|
+
* - `string[]` applies to specific node/mark types by name
|
|
1524
|
+
* @example
|
|
1525
|
+
* types: '*' // All nodes and marks
|
|
1526
|
+
* types: 'nodes' // All nodes
|
|
1527
|
+
* types: 'marks' // All marks
|
|
1528
|
+
* types: ['heading', 'paragraph'] // Specific types
|
|
1499
1529
|
*/
|
|
1500
|
-
types: string[];
|
|
1530
|
+
types: string[] | 'nodes' | 'marks' | '*';
|
|
1501
1531
|
/**
|
|
1502
1532
|
* The attributes to add to the node or mark types.
|
|
1503
1533
|
*/
|
|
@@ -2320,6 +2350,12 @@ declare class ExtensionManager {
|
|
|
2320
2350
|
* @returns A composed dispatch function
|
|
2321
2351
|
*/
|
|
2322
2352
|
dispatchTransaction(baseDispatch: (tr: Transaction) => void): (tr: Transaction) => void;
|
|
2353
|
+
/**
|
|
2354
|
+
* Get the composed transformPastedHTML function from all extensions.
|
|
2355
|
+
* @param baseTransform The base transform function (e.g. from the editor props)
|
|
2356
|
+
* @returns A composed transform function that chains all extension transforms
|
|
2357
|
+
*/
|
|
2358
|
+
transformPastedHTML(baseTransform?: (html: string, view?: any) => string): (html: string, view?: EditorView) => string;
|
|
2323
2359
|
get markViews(): Record<string, MarkViewConstructor>;
|
|
2324
2360
|
/**
|
|
2325
2361
|
* Go through all extensions, create extension storages & setup marks
|
|
@@ -3528,7 +3564,7 @@ declare class Editor extends EventEmitter<EditorEvents> {
|
|
|
3528
3564
|
*/
|
|
3529
3565
|
get isEditable(): boolean;
|
|
3530
3566
|
/**
|
|
3531
|
-
* Returns the editor
|
|
3567
|
+
* Returns the editor view.
|
|
3532
3568
|
*/
|
|
3533
3569
|
get view(): EditorView;
|
|
3534
3570
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1348,6 +1348,9 @@ function getAttributesFromExtensions(extensions) {
|
|
|
1348
1348
|
keepOnSplit: true,
|
|
1349
1349
|
isRequired: false
|
|
1350
1350
|
};
|
|
1351
|
+
const nodeExtensionTypes = nodeExtensions.filter((ext) => ext.name !== "text").map((ext) => ext.name);
|
|
1352
|
+
const markExtensionTypes = markExtensions.map((ext) => ext.name);
|
|
1353
|
+
const allExtensionTypes = [...nodeExtensionTypes, ...markExtensionTypes];
|
|
1351
1354
|
extensions.forEach((extension) => {
|
|
1352
1355
|
const context = {
|
|
1353
1356
|
name: extension.name,
|
|
@@ -1365,7 +1368,19 @@ function getAttributesFromExtensions(extensions) {
|
|
|
1365
1368
|
}
|
|
1366
1369
|
const globalAttributes = addGlobalAttributes();
|
|
1367
1370
|
globalAttributes.forEach((globalAttribute) => {
|
|
1368
|
-
|
|
1371
|
+
let resolvedTypes;
|
|
1372
|
+
if (Array.isArray(globalAttribute.types)) {
|
|
1373
|
+
resolvedTypes = globalAttribute.types;
|
|
1374
|
+
} else if (globalAttribute.types === "*") {
|
|
1375
|
+
resolvedTypes = allExtensionTypes;
|
|
1376
|
+
} else if (globalAttribute.types === "nodes") {
|
|
1377
|
+
resolvedTypes = nodeExtensionTypes;
|
|
1378
|
+
} else if (globalAttribute.types === "marks") {
|
|
1379
|
+
resolvedTypes = markExtensionTypes;
|
|
1380
|
+
} else {
|
|
1381
|
+
resolvedTypes = [];
|
|
1382
|
+
}
|
|
1383
|
+
resolvedTypes.forEach((type) => {
|
|
1369
1384
|
Object.entries(globalAttribute.attributes).forEach(([name, attribute]) => {
|
|
1370
1385
|
extensionAttributes.push({
|
|
1371
1386
|
type,
|
|
@@ -1416,6 +1431,67 @@ function getAttributesFromExtensions(extensions) {
|
|
|
1416
1431
|
}
|
|
1417
1432
|
|
|
1418
1433
|
// src/utilities/mergeAttributes.ts
|
|
1434
|
+
function splitStyleDeclarations(styles) {
|
|
1435
|
+
const result = [];
|
|
1436
|
+
let current = "";
|
|
1437
|
+
let inSingleQuote = false;
|
|
1438
|
+
let inDoubleQuote = false;
|
|
1439
|
+
let parenDepth = 0;
|
|
1440
|
+
const length = styles.length;
|
|
1441
|
+
for (let i = 0; i < length; i += 1) {
|
|
1442
|
+
const char = styles[i];
|
|
1443
|
+
if (char === "'" && !inDoubleQuote) {
|
|
1444
|
+
inSingleQuote = !inSingleQuote;
|
|
1445
|
+
current += char;
|
|
1446
|
+
continue;
|
|
1447
|
+
}
|
|
1448
|
+
if (char === '"' && !inSingleQuote) {
|
|
1449
|
+
inDoubleQuote = !inDoubleQuote;
|
|
1450
|
+
current += char;
|
|
1451
|
+
continue;
|
|
1452
|
+
}
|
|
1453
|
+
if (!inSingleQuote && !inDoubleQuote) {
|
|
1454
|
+
if (char === "(") {
|
|
1455
|
+
parenDepth += 1;
|
|
1456
|
+
current += char;
|
|
1457
|
+
continue;
|
|
1458
|
+
}
|
|
1459
|
+
if (char === ")" && parenDepth > 0) {
|
|
1460
|
+
parenDepth -= 1;
|
|
1461
|
+
current += char;
|
|
1462
|
+
continue;
|
|
1463
|
+
}
|
|
1464
|
+
if (char === ";" && parenDepth === 0) {
|
|
1465
|
+
result.push(current);
|
|
1466
|
+
current = "";
|
|
1467
|
+
continue;
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
current += char;
|
|
1471
|
+
}
|
|
1472
|
+
if (current) {
|
|
1473
|
+
result.push(current);
|
|
1474
|
+
}
|
|
1475
|
+
return result;
|
|
1476
|
+
}
|
|
1477
|
+
function parseStyleEntries(styles) {
|
|
1478
|
+
const pairs = [];
|
|
1479
|
+
const declarations = splitStyleDeclarations(styles || "");
|
|
1480
|
+
const numDeclarations = declarations.length;
|
|
1481
|
+
for (let i = 0; i < numDeclarations; i += 1) {
|
|
1482
|
+
const declaration = declarations[i];
|
|
1483
|
+
const firstColonIndex = declaration.indexOf(":");
|
|
1484
|
+
if (firstColonIndex === -1) {
|
|
1485
|
+
continue;
|
|
1486
|
+
}
|
|
1487
|
+
const property = declaration.slice(0, firstColonIndex).trim();
|
|
1488
|
+
const value = declaration.slice(firstColonIndex + 1).trim();
|
|
1489
|
+
if (property && value) {
|
|
1490
|
+
pairs.push([property, value]);
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
return pairs;
|
|
1494
|
+
}
|
|
1419
1495
|
function mergeAttributes(...objects) {
|
|
1420
1496
|
return objects.filter((item) => !!item).reduce((items, item) => {
|
|
1421
1497
|
const mergedAttributes = { ...items };
|
|
@@ -1431,17 +1507,7 @@ function mergeAttributes(...objects) {
|
|
|
1431
1507
|
const insertClasses = valueClasses.filter((valueClass) => !existingClasses.includes(valueClass));
|
|
1432
1508
|
mergedAttributes[key] = [...existingClasses, ...insertClasses].join(" ");
|
|
1433
1509
|
} else if (key === "style") {
|
|
1434
|
-
const
|
|
1435
|
-
const existingStyles = mergedAttributes[key] ? mergedAttributes[key].split(";").map((style2) => style2.trim()).filter(Boolean) : [];
|
|
1436
|
-
const styleMap = /* @__PURE__ */ new Map();
|
|
1437
|
-
existingStyles.forEach((style2) => {
|
|
1438
|
-
const [property, val] = style2.split(":").map((part) => part.trim());
|
|
1439
|
-
styleMap.set(property, val);
|
|
1440
|
-
});
|
|
1441
|
-
newStyles.forEach((style2) => {
|
|
1442
|
-
const [property, val] = style2.split(":").map((part) => part.trim());
|
|
1443
|
-
styleMap.set(property, val);
|
|
1444
|
-
});
|
|
1510
|
+
const styleMap = new Map([...parseStyleEntries(mergedAttributes[key]), ...parseStyleEntries(value)]);
|
|
1445
1511
|
mergedAttributes[key] = Array.from(styleMap.entries()).map(([property, val]) => `${property}: ${val}`).join("; ");
|
|
1446
1512
|
} else {
|
|
1447
1513
|
mergedAttributes[key] = value;
|
|
@@ -1983,6 +2049,9 @@ function isMarkActive(state, typeOrName, attributes = {}) {
|
|
|
1983
2049
|
const from = $from.pos;
|
|
1984
2050
|
const to = $to.pos;
|
|
1985
2051
|
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
2052
|
+
if (type && node.inlineContent && !node.type.allowsMarkType(type)) {
|
|
2053
|
+
return false;
|
|
2054
|
+
}
|
|
1986
2055
|
if (!node.isText && !node.marks.length) {
|
|
1987
2056
|
return;
|
|
1988
2057
|
}
|
|
@@ -3683,6 +3752,39 @@ var ExtensionManager = class {
|
|
|
3683
3752
|
};
|
|
3684
3753
|
}, baseDispatch);
|
|
3685
3754
|
}
|
|
3755
|
+
/**
|
|
3756
|
+
* Get the composed transformPastedHTML function from all extensions.
|
|
3757
|
+
* @param baseTransform The base transform function (e.g. from the editor props)
|
|
3758
|
+
* @returns A composed transform function that chains all extension transforms
|
|
3759
|
+
*/
|
|
3760
|
+
transformPastedHTML(baseTransform) {
|
|
3761
|
+
const { editor } = this;
|
|
3762
|
+
const extensions = sortExtensions([...this.extensions]);
|
|
3763
|
+
return extensions.reduce(
|
|
3764
|
+
(transform, extension) => {
|
|
3765
|
+
const context = {
|
|
3766
|
+
name: extension.name,
|
|
3767
|
+
options: extension.options,
|
|
3768
|
+
storage: this.editor.extensionStorage[extension.name],
|
|
3769
|
+
editor,
|
|
3770
|
+
type: getSchemaTypeByName(extension.name, this.schema)
|
|
3771
|
+
};
|
|
3772
|
+
const extensionTransform = getExtensionField(
|
|
3773
|
+
extension,
|
|
3774
|
+
"transformPastedHTML",
|
|
3775
|
+
context
|
|
3776
|
+
);
|
|
3777
|
+
if (!extensionTransform) {
|
|
3778
|
+
return transform;
|
|
3779
|
+
}
|
|
3780
|
+
return (html, view) => {
|
|
3781
|
+
const transformedHtml = transform(html, view);
|
|
3782
|
+
return extensionTransform.call(context, transformedHtml);
|
|
3783
|
+
};
|
|
3784
|
+
},
|
|
3785
|
+
baseTransform || ((html) => html)
|
|
3786
|
+
);
|
|
3787
|
+
}
|
|
3686
3788
|
get markViews() {
|
|
3687
3789
|
const { editor } = this;
|
|
3688
3790
|
const { markExtensions } = splitExtensions(this.extensions);
|
|
@@ -4705,7 +4807,7 @@ var Editor = class extends EventEmitter {
|
|
|
4705
4807
|
return this.options.editable && this.view && this.view.editable;
|
|
4706
4808
|
}
|
|
4707
4809
|
/**
|
|
4708
|
-
* Returns the editor
|
|
4810
|
+
* Returns the editor view.
|
|
4709
4811
|
*/
|
|
4710
4812
|
get view() {
|
|
4711
4813
|
if (this.editorView) {
|
|
@@ -4874,6 +4976,8 @@ var Editor = class extends EventEmitter {
|
|
|
4874
4976
|
const { editorProps, enableExtensionDispatchTransaction } = this.options;
|
|
4875
4977
|
const baseDispatch = editorProps.dispatchTransaction || this.dispatchTransaction.bind(this);
|
|
4876
4978
|
const dispatch = enableExtensionDispatchTransaction ? this.extensionManager.dispatchTransaction(baseDispatch) : baseDispatch;
|
|
4979
|
+
const baseTransformPastedHTML = editorProps.transformPastedHTML;
|
|
4980
|
+
const transformPastedHTML = this.extensionManager.transformPastedHTML(baseTransformPastedHTML);
|
|
4877
4981
|
this.editorView = new EditorView(element, {
|
|
4878
4982
|
...editorProps,
|
|
4879
4983
|
attributes: {
|
|
@@ -4882,6 +4986,7 @@ var Editor = class extends EventEmitter {
|
|
|
4882
4986
|
...editorProps == null ? void 0 : editorProps.attributes
|
|
4883
4987
|
},
|
|
4884
4988
|
dispatchTransaction: dispatch,
|
|
4989
|
+
transformPastedHTML,
|
|
4885
4990
|
state: this.editorState,
|
|
4886
4991
|
markViews: this.extensionManager.markViews,
|
|
4887
4992
|
nodeViews: this.extensionManager.nodeViews
|