@kopexa/editor-utils 17.0.46 → 17.1.0
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/chunk-23NZ6BRW.mjs +27 -0
- package/dist/chunk-ID5T7EEZ.mjs +90 -0
- package/dist/chunk-P55PLOHR.mjs +34 -0
- package/dist/hooks/use-tiptap-editor.d.mts +20 -0
- package/dist/hooks/use-tiptap-editor.d.ts +20 -0
- package/dist/hooks/use-tiptap-editor.js +58 -0
- package/dist/hooks/use-tiptap-editor.mjs +8 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +145 -3
- package/dist/index.mjs +17 -1
- package/dist/selection.d.mts +22 -0
- package/dist/selection.d.ts +22 -0
- package/dist/selection.js +111 -0
- package/dist/selection.mjs +11 -0
- package/dist/update-node-attrs.d.mts +16 -0
- package/dist/update-node-attrs.d.ts +16 -0
- package/dist/update-node-attrs.js +50 -0
- package/dist/update-node-attrs.mjs +7 -0
- package/package.json +6 -6
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/update-node-attrs.ts
|
|
4
|
+
function updateNodesAttr(tr, targets, attrName, next) {
|
|
5
|
+
if (!targets.length) return false;
|
|
6
|
+
let changed = false;
|
|
7
|
+
for (const { pos } of targets) {
|
|
8
|
+
const currentNode = tr.doc.nodeAt(pos);
|
|
9
|
+
if (!currentNode) continue;
|
|
10
|
+
const prevValue = currentNode.attrs[attrName];
|
|
11
|
+
const resolvedNext = typeof next === "function" ? next(prevValue) : next;
|
|
12
|
+
if (prevValue === resolvedNext) continue;
|
|
13
|
+
const nextAttrs = { ...currentNode.attrs };
|
|
14
|
+
if (resolvedNext === void 0) {
|
|
15
|
+
delete nextAttrs[attrName];
|
|
16
|
+
} else {
|
|
17
|
+
nextAttrs[attrName] = resolvedNext;
|
|
18
|
+
}
|
|
19
|
+
tr.setNodeMarkup(pos, void 0, nextAttrs);
|
|
20
|
+
changed = true;
|
|
21
|
+
}
|
|
22
|
+
return changed;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export {
|
|
26
|
+
updateNodesAttr
|
|
27
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/selection.ts
|
|
4
|
+
import {
|
|
5
|
+
AllSelection,
|
|
6
|
+
NodeSelection,
|
|
7
|
+
TextSelection
|
|
8
|
+
} from "@tiptap/pm/state";
|
|
9
|
+
import { CellSelection, cellAround } from "@tiptap/pm/tables";
|
|
10
|
+
import { findParentNodeClosestToPos } from "@tiptap/react";
|
|
11
|
+
function getSelectedBlockNodes(editor) {
|
|
12
|
+
const { doc } = editor.state;
|
|
13
|
+
const { from, to } = editor.state.selection;
|
|
14
|
+
const blocks = [];
|
|
15
|
+
const seen = /* @__PURE__ */ new Set();
|
|
16
|
+
doc.nodesBetween(from, to, (node, pos) => {
|
|
17
|
+
if (!node.isBlock) return;
|
|
18
|
+
if (!seen.has(pos)) {
|
|
19
|
+
seen.add(pos);
|
|
20
|
+
blocks.push(node);
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
});
|
|
24
|
+
return blocks;
|
|
25
|
+
}
|
|
26
|
+
function getSelectedNodesOfType(selection, allowedNodeTypes) {
|
|
27
|
+
const results = [];
|
|
28
|
+
const allowed = new Set(allowedNodeTypes);
|
|
29
|
+
if (selection instanceof CellSelection) {
|
|
30
|
+
selection.forEachCell((node, pos) => {
|
|
31
|
+
if (allowed.has(node.type.name)) {
|
|
32
|
+
results.push({ node, pos });
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
return results;
|
|
36
|
+
}
|
|
37
|
+
if (selection instanceof NodeSelection) {
|
|
38
|
+
const { node, from: pos } = selection;
|
|
39
|
+
if (node && allowed.has(node.type.name)) {
|
|
40
|
+
results.push({ node, pos });
|
|
41
|
+
}
|
|
42
|
+
return results;
|
|
43
|
+
}
|
|
44
|
+
const { $anchor } = selection;
|
|
45
|
+
const cell = cellAround($anchor);
|
|
46
|
+
if (cell) {
|
|
47
|
+
const cellNode = selection.$anchor.doc.nodeAt(cell.pos);
|
|
48
|
+
if (cellNode && allowed.has(cellNode.type.name)) {
|
|
49
|
+
results.push({ node: cellNode, pos: cell.pos });
|
|
50
|
+
return results;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const parentNode = findParentNodeClosestToPos(
|
|
54
|
+
$anchor,
|
|
55
|
+
(node) => allowed.has(node.type.name)
|
|
56
|
+
);
|
|
57
|
+
if (parentNode) {
|
|
58
|
+
results.push({ node: parentNode.node, pos: parentNode.pos });
|
|
59
|
+
}
|
|
60
|
+
return results;
|
|
61
|
+
}
|
|
62
|
+
function selectionWithinConvertibleTypes(editor, types = []) {
|
|
63
|
+
var _a, _b;
|
|
64
|
+
if (!editor || types.length === 0) return false;
|
|
65
|
+
const { state } = editor;
|
|
66
|
+
const { selection } = state;
|
|
67
|
+
const allowed = new Set(types);
|
|
68
|
+
if (selection instanceof NodeSelection) {
|
|
69
|
+
const nodeType = (_b = (_a = selection.node) == null ? void 0 : _a.type) == null ? void 0 : _b.name;
|
|
70
|
+
return !!nodeType && allowed.has(nodeType);
|
|
71
|
+
}
|
|
72
|
+
if (selection instanceof TextSelection || selection instanceof AllSelection) {
|
|
73
|
+
let valid = true;
|
|
74
|
+
state.doc.nodesBetween(selection.from, selection.to, (node) => {
|
|
75
|
+
if (node.isTextblock && !allowed.has(node.type.name)) {
|
|
76
|
+
valid = false;
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
return valid;
|
|
80
|
+
});
|
|
81
|
+
return valid;
|
|
82
|
+
}
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export {
|
|
87
|
+
getSelectedBlockNodes,
|
|
88
|
+
getSelectedNodesOfType,
|
|
89
|
+
selectionWithinConvertibleTypes
|
|
90
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/hooks/use-tiptap-editor.ts
|
|
4
|
+
import { useCurrentEditor, useEditorState } from "@tiptap/react";
|
|
5
|
+
import { useMemo } from "react";
|
|
6
|
+
function useTiptapEditor(providedEditor) {
|
|
7
|
+
const { editor: coreEditor } = useCurrentEditor();
|
|
8
|
+
const mainEditor = useMemo(
|
|
9
|
+
() => providedEditor || coreEditor,
|
|
10
|
+
[providedEditor, coreEditor]
|
|
11
|
+
);
|
|
12
|
+
const editorState = useEditorState({
|
|
13
|
+
editor: mainEditor,
|
|
14
|
+
selector(context) {
|
|
15
|
+
if (!context.editor) {
|
|
16
|
+
return {
|
|
17
|
+
editor: null,
|
|
18
|
+
editorState: void 0,
|
|
19
|
+
canCommand: void 0
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
editor: context.editor,
|
|
24
|
+
editorState: context.editor.state,
|
|
25
|
+
canCommand: context.editor.can
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
return editorState || { editor: null };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export {
|
|
33
|
+
useTiptapEditor
|
|
34
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Editor } from '@tiptap/react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Hook that provides access to a Tiptap editor instance.
|
|
5
|
+
*
|
|
6
|
+
* Accepts an optional editor instance directly, or falls back to retrieving
|
|
7
|
+
* the editor from the Tiptap context if available. This allows components
|
|
8
|
+
* to work both when given an editor directly and when used within a Tiptap
|
|
9
|
+
* editor context.
|
|
10
|
+
*
|
|
11
|
+
* @param providedEditor - Optional editor instance to use instead of the context editor
|
|
12
|
+
* @returns The provided editor or the editor from context, whichever is available
|
|
13
|
+
*/
|
|
14
|
+
declare function useTiptapEditor(providedEditor?: Editor | null): {
|
|
15
|
+
editor: Editor | null;
|
|
16
|
+
editorState?: Editor["state"];
|
|
17
|
+
canCommand?: Editor["can"];
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export { useTiptapEditor };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Editor } from '@tiptap/react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Hook that provides access to a Tiptap editor instance.
|
|
5
|
+
*
|
|
6
|
+
* Accepts an optional editor instance directly, or falls back to retrieving
|
|
7
|
+
* the editor from the Tiptap context if available. This allows components
|
|
8
|
+
* to work both when given an editor directly and when used within a Tiptap
|
|
9
|
+
* editor context.
|
|
10
|
+
*
|
|
11
|
+
* @param providedEditor - Optional editor instance to use instead of the context editor
|
|
12
|
+
* @returns The provided editor or the editor from context, whichever is available
|
|
13
|
+
*/
|
|
14
|
+
declare function useTiptapEditor(providedEditor?: Editor | null): {
|
|
15
|
+
editor: Editor | null;
|
|
16
|
+
editorState?: Editor["state"];
|
|
17
|
+
canCommand?: Editor["can"];
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export { useTiptapEditor };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
"use client";
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
|
+
|
|
22
|
+
// src/hooks/use-tiptap-editor.ts
|
|
23
|
+
var use_tiptap_editor_exports = {};
|
|
24
|
+
__export(use_tiptap_editor_exports, {
|
|
25
|
+
useTiptapEditor: () => useTiptapEditor
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(use_tiptap_editor_exports);
|
|
28
|
+
var import_react = require("@tiptap/react");
|
|
29
|
+
var import_react2 = require("react");
|
|
30
|
+
function useTiptapEditor(providedEditor) {
|
|
31
|
+
const { editor: coreEditor } = (0, import_react.useCurrentEditor)();
|
|
32
|
+
const mainEditor = (0, import_react2.useMemo)(
|
|
33
|
+
() => providedEditor || coreEditor,
|
|
34
|
+
[providedEditor, coreEditor]
|
|
35
|
+
);
|
|
36
|
+
const editorState = (0, import_react.useEditorState)({
|
|
37
|
+
editor: mainEditor,
|
|
38
|
+
selector(context) {
|
|
39
|
+
if (!context.editor) {
|
|
40
|
+
return {
|
|
41
|
+
editor: null,
|
|
42
|
+
editorState: void 0,
|
|
43
|
+
canCommand: void 0
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
editor: context.editor,
|
|
48
|
+
editorState: context.editor.state,
|
|
49
|
+
canCommand: context.editor.can
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
return editorState || { editor: null };
|
|
54
|
+
}
|
|
55
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
56
|
+
0 && (module.exports = {
|
|
57
|
+
useTiptapEditor
|
|
58
|
+
});
|
package/dist/index.d.mts
CHANGED
|
@@ -2,6 +2,9 @@ import * as _tiptap_core from '@tiptap/core';
|
|
|
2
2
|
import { Node, Attrs } from '@tiptap/pm/model';
|
|
3
3
|
import { Selection } from '@tiptap/pm/state';
|
|
4
4
|
import { Editor } from '@tiptap/react';
|
|
5
|
+
export { useTiptapEditor } from './hooks/use-tiptap-editor.mjs';
|
|
6
|
+
export { getSelectedBlockNodes, getSelectedNodesOfType, selectionWithinConvertibleTypes } from './selection.mjs';
|
|
7
|
+
export { updateNodesAttr } from './update-node-attrs.mjs';
|
|
5
8
|
|
|
6
9
|
type OverflowPosition = "none" | "top" | "bottom" | "both";
|
|
7
10
|
declare function findParentNodeOfType(selection: Selection, typeName: string): {
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,9 @@ import * as _tiptap_core from '@tiptap/core';
|
|
|
2
2
|
import { Node, Attrs } from '@tiptap/pm/model';
|
|
3
3
|
import { Selection } from '@tiptap/pm/state';
|
|
4
4
|
import { Editor } from '@tiptap/react';
|
|
5
|
+
export { useTiptapEditor } from './hooks/use-tiptap-editor.js';
|
|
6
|
+
export { getSelectedBlockNodes, getSelectedNodesOfType, selectionWithinConvertibleTypes } from './selection.js';
|
|
7
|
+
export { updateNodesAttr } from './update-node-attrs.js';
|
|
5
8
|
|
|
6
9
|
type OverflowPosition = "none" | "top" | "bottom" | "both";
|
|
7
10
|
declare function findParentNodeOfType(selection: Selection, typeName: string): {
|
package/dist/index.js
CHANGED
|
@@ -27,16 +27,153 @@ __export(index_exports, {
|
|
|
27
27
|
getActiveMarkAttrs: () => getActiveMarkAttrs,
|
|
28
28
|
getEditorExtension: () => getEditorExtension,
|
|
29
29
|
getElementOverflowPosition: () => getElementOverflowPosition,
|
|
30
|
+
getSelectedBlockNodes: () => getSelectedBlockNodes,
|
|
31
|
+
getSelectedNodesOfType: () => getSelectedNodesOfType,
|
|
30
32
|
hasContentAbove: () => hasContentAbove,
|
|
31
33
|
isEmptyNode: () => isEmptyNode,
|
|
32
34
|
isExtensionAvailable: () => isExtensionAvailable,
|
|
33
35
|
isMarkInSchema: () => isMarkInSchema,
|
|
34
36
|
isNodeInSchema: () => isNodeInSchema,
|
|
35
37
|
isNodeTypeSelected: () => isNodeTypeSelected,
|
|
36
|
-
isValidPosition: () => isValidPosition
|
|
38
|
+
isValidPosition: () => isValidPosition,
|
|
39
|
+
selectionWithinConvertibleTypes: () => selectionWithinConvertibleTypes,
|
|
40
|
+
updateNodesAttr: () => updateNodesAttr,
|
|
41
|
+
useTiptapEditor: () => useTiptapEditor
|
|
37
42
|
});
|
|
38
43
|
module.exports = __toCommonJS(index_exports);
|
|
44
|
+
var import_state2 = require("@tiptap/pm/state");
|
|
45
|
+
|
|
46
|
+
// src/hooks/use-tiptap-editor.ts
|
|
47
|
+
var import_react = require("@tiptap/react");
|
|
48
|
+
var import_react2 = require("react");
|
|
49
|
+
function useTiptapEditor(providedEditor) {
|
|
50
|
+
const { editor: coreEditor } = (0, import_react.useCurrentEditor)();
|
|
51
|
+
const mainEditor = (0, import_react2.useMemo)(
|
|
52
|
+
() => providedEditor || coreEditor,
|
|
53
|
+
[providedEditor, coreEditor]
|
|
54
|
+
);
|
|
55
|
+
const editorState = (0, import_react.useEditorState)({
|
|
56
|
+
editor: mainEditor,
|
|
57
|
+
selector(context) {
|
|
58
|
+
if (!context.editor) {
|
|
59
|
+
return {
|
|
60
|
+
editor: null,
|
|
61
|
+
editorState: void 0,
|
|
62
|
+
canCommand: void 0
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
editor: context.editor,
|
|
67
|
+
editorState: context.editor.state,
|
|
68
|
+
canCommand: context.editor.can
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
return editorState || { editor: null };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// src/selection.ts
|
|
39
76
|
var import_state = require("@tiptap/pm/state");
|
|
77
|
+
var import_tables = require("@tiptap/pm/tables");
|
|
78
|
+
var import_react3 = require("@tiptap/react");
|
|
79
|
+
function getSelectedBlockNodes(editor) {
|
|
80
|
+
const { doc } = editor.state;
|
|
81
|
+
const { from, to } = editor.state.selection;
|
|
82
|
+
const blocks = [];
|
|
83
|
+
const seen = /* @__PURE__ */ new Set();
|
|
84
|
+
doc.nodesBetween(from, to, (node, pos) => {
|
|
85
|
+
if (!node.isBlock) return;
|
|
86
|
+
if (!seen.has(pos)) {
|
|
87
|
+
seen.add(pos);
|
|
88
|
+
blocks.push(node);
|
|
89
|
+
}
|
|
90
|
+
return false;
|
|
91
|
+
});
|
|
92
|
+
return blocks;
|
|
93
|
+
}
|
|
94
|
+
function getSelectedNodesOfType(selection, allowedNodeTypes) {
|
|
95
|
+
const results = [];
|
|
96
|
+
const allowed = new Set(allowedNodeTypes);
|
|
97
|
+
if (selection instanceof import_tables.CellSelection) {
|
|
98
|
+
selection.forEachCell((node, pos) => {
|
|
99
|
+
if (allowed.has(node.type.name)) {
|
|
100
|
+
results.push({ node, pos });
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
return results;
|
|
104
|
+
}
|
|
105
|
+
if (selection instanceof import_state.NodeSelection) {
|
|
106
|
+
const { node, from: pos } = selection;
|
|
107
|
+
if (node && allowed.has(node.type.name)) {
|
|
108
|
+
results.push({ node, pos });
|
|
109
|
+
}
|
|
110
|
+
return results;
|
|
111
|
+
}
|
|
112
|
+
const { $anchor } = selection;
|
|
113
|
+
const cell = (0, import_tables.cellAround)($anchor);
|
|
114
|
+
if (cell) {
|
|
115
|
+
const cellNode = selection.$anchor.doc.nodeAt(cell.pos);
|
|
116
|
+
if (cellNode && allowed.has(cellNode.type.name)) {
|
|
117
|
+
results.push({ node: cellNode, pos: cell.pos });
|
|
118
|
+
return results;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const parentNode = (0, import_react3.findParentNodeClosestToPos)(
|
|
122
|
+
$anchor,
|
|
123
|
+
(node) => allowed.has(node.type.name)
|
|
124
|
+
);
|
|
125
|
+
if (parentNode) {
|
|
126
|
+
results.push({ node: parentNode.node, pos: parentNode.pos });
|
|
127
|
+
}
|
|
128
|
+
return results;
|
|
129
|
+
}
|
|
130
|
+
function selectionWithinConvertibleTypes(editor, types = []) {
|
|
131
|
+
var _a, _b;
|
|
132
|
+
if (!editor || types.length === 0) return false;
|
|
133
|
+
const { state } = editor;
|
|
134
|
+
const { selection } = state;
|
|
135
|
+
const allowed = new Set(types);
|
|
136
|
+
if (selection instanceof import_state.NodeSelection) {
|
|
137
|
+
const nodeType = (_b = (_a = selection.node) == null ? void 0 : _a.type) == null ? void 0 : _b.name;
|
|
138
|
+
return !!nodeType && allowed.has(nodeType);
|
|
139
|
+
}
|
|
140
|
+
if (selection instanceof import_state.TextSelection || selection instanceof import_state.AllSelection) {
|
|
141
|
+
let valid = true;
|
|
142
|
+
state.doc.nodesBetween(selection.from, selection.to, (node) => {
|
|
143
|
+
if (node.isTextblock && !allowed.has(node.type.name)) {
|
|
144
|
+
valid = false;
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
return valid;
|
|
148
|
+
});
|
|
149
|
+
return valid;
|
|
150
|
+
}
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// src/update-node-attrs.ts
|
|
155
|
+
function updateNodesAttr(tr, targets, attrName, next) {
|
|
156
|
+
if (!targets.length) return false;
|
|
157
|
+
let changed = false;
|
|
158
|
+
for (const { pos } of targets) {
|
|
159
|
+
const currentNode = tr.doc.nodeAt(pos);
|
|
160
|
+
if (!currentNode) continue;
|
|
161
|
+
const prevValue = currentNode.attrs[attrName];
|
|
162
|
+
const resolvedNext = typeof next === "function" ? next(prevValue) : next;
|
|
163
|
+
if (prevValue === resolvedNext) continue;
|
|
164
|
+
const nextAttrs = { ...currentNode.attrs };
|
|
165
|
+
if (resolvedNext === void 0) {
|
|
166
|
+
delete nextAttrs[attrName];
|
|
167
|
+
} else {
|
|
168
|
+
nextAttrs[attrName] = resolvedNext;
|
|
169
|
+
}
|
|
170
|
+
tr.setNodeMarkup(pos, void 0, nextAttrs);
|
|
171
|
+
changed = true;
|
|
172
|
+
}
|
|
173
|
+
return changed;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// src/index.ts
|
|
40
177
|
function findParentNodeOfType(selection, typeName) {
|
|
41
178
|
let depth = selection.$anchor.depth;
|
|
42
179
|
while (depth > 0) {
|
|
@@ -117,7 +254,7 @@ function isNodeTypeSelected(editor, types = []) {
|
|
|
117
254
|
const { state } = editor;
|
|
118
255
|
const { selection } = state;
|
|
119
256
|
if (selection.empty) return false;
|
|
120
|
-
if (selection instanceof
|
|
257
|
+
if (selection instanceof import_state2.NodeSelection) {
|
|
121
258
|
const node = selection.node;
|
|
122
259
|
return node ? types.includes(node.type.name) : false;
|
|
123
260
|
}
|
|
@@ -184,11 +321,16 @@ function findSelectionPosition(params) {
|
|
|
184
321
|
getActiveMarkAttrs,
|
|
185
322
|
getEditorExtension,
|
|
186
323
|
getElementOverflowPosition,
|
|
324
|
+
getSelectedBlockNodes,
|
|
325
|
+
getSelectedNodesOfType,
|
|
187
326
|
hasContentAbove,
|
|
188
327
|
isEmptyNode,
|
|
189
328
|
isExtensionAvailable,
|
|
190
329
|
isMarkInSchema,
|
|
191
330
|
isNodeInSchema,
|
|
192
331
|
isNodeTypeSelected,
|
|
193
|
-
isValidPosition
|
|
332
|
+
isValidPosition,
|
|
333
|
+
selectionWithinConvertibleTypes,
|
|
334
|
+
updateNodesAttr,
|
|
335
|
+
useTiptapEditor
|
|
194
336
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import {
|
|
3
|
+
getSelectedBlockNodes,
|
|
4
|
+
getSelectedNodesOfType,
|
|
5
|
+
selectionWithinConvertibleTypes
|
|
6
|
+
} from "./chunk-ID5T7EEZ.mjs";
|
|
7
|
+
import {
|
|
8
|
+
updateNodesAttr
|
|
9
|
+
} from "./chunk-23NZ6BRW.mjs";
|
|
10
|
+
import {
|
|
11
|
+
useTiptapEditor
|
|
12
|
+
} from "./chunk-P55PLOHR.mjs";
|
|
2
13
|
|
|
3
14
|
// src/index.ts
|
|
4
15
|
import { NodeSelection } from "@tiptap/pm/state";
|
|
@@ -148,11 +159,16 @@ export {
|
|
|
148
159
|
getActiveMarkAttrs,
|
|
149
160
|
getEditorExtension,
|
|
150
161
|
getElementOverflowPosition,
|
|
162
|
+
getSelectedBlockNodes,
|
|
163
|
+
getSelectedNodesOfType,
|
|
151
164
|
hasContentAbove,
|
|
152
165
|
isEmptyNode,
|
|
153
166
|
isExtensionAvailable,
|
|
154
167
|
isMarkInSchema,
|
|
155
168
|
isNodeInSchema,
|
|
156
169
|
isNodeTypeSelected,
|
|
157
|
-
isValidPosition
|
|
170
|
+
isValidPosition,
|
|
171
|
+
selectionWithinConvertibleTypes,
|
|
172
|
+
updateNodesAttr,
|
|
173
|
+
useTiptapEditor
|
|
158
174
|
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Node } from '@tiptap/pm/model';
|
|
2
|
+
import { Selection } from '@tiptap/pm/state';
|
|
3
|
+
import { Editor, NodeWithPos } from '@tiptap/react';
|
|
4
|
+
|
|
5
|
+
declare function getSelectedBlockNodes(editor: Editor): Node[];
|
|
6
|
+
/**
|
|
7
|
+
* Retrieves all nodes of specified types from the current selection.
|
|
8
|
+
* @param selection The current editor selection
|
|
9
|
+
* @param allowedNodeTypes An array of node type names to look for (e.g., ["image", "table"])
|
|
10
|
+
* @returns An array of objects containing the node and its position
|
|
11
|
+
*/
|
|
12
|
+
declare function getSelectedNodesOfType(selection: Selection, allowedNodeTypes: string[]): NodeWithPos[];
|
|
13
|
+
/**
|
|
14
|
+
* Check whether the current selection is fully within nodes
|
|
15
|
+
* whose type names are in the provided `types` list.
|
|
16
|
+
*
|
|
17
|
+
* - NodeSelection → checks the selected node.
|
|
18
|
+
* - Text/AllSelection → ensures all textblocks within [from, to) are allowed.
|
|
19
|
+
*/
|
|
20
|
+
declare function selectionWithinConvertibleTypes(editor: Editor, types?: string[]): boolean;
|
|
21
|
+
|
|
22
|
+
export { getSelectedBlockNodes, getSelectedNodesOfType, selectionWithinConvertibleTypes };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Node } from '@tiptap/pm/model';
|
|
2
|
+
import { Selection } from '@tiptap/pm/state';
|
|
3
|
+
import { Editor, NodeWithPos } from '@tiptap/react';
|
|
4
|
+
|
|
5
|
+
declare function getSelectedBlockNodes(editor: Editor): Node[];
|
|
6
|
+
/**
|
|
7
|
+
* Retrieves all nodes of specified types from the current selection.
|
|
8
|
+
* @param selection The current editor selection
|
|
9
|
+
* @param allowedNodeTypes An array of node type names to look for (e.g., ["image", "table"])
|
|
10
|
+
* @returns An array of objects containing the node and its position
|
|
11
|
+
*/
|
|
12
|
+
declare function getSelectedNodesOfType(selection: Selection, allowedNodeTypes: string[]): NodeWithPos[];
|
|
13
|
+
/**
|
|
14
|
+
* Check whether the current selection is fully within nodes
|
|
15
|
+
* whose type names are in the provided `types` list.
|
|
16
|
+
*
|
|
17
|
+
* - NodeSelection → checks the selected node.
|
|
18
|
+
* - Text/AllSelection → ensures all textblocks within [from, to) are allowed.
|
|
19
|
+
*/
|
|
20
|
+
declare function selectionWithinConvertibleTypes(editor: Editor, types?: string[]): boolean;
|
|
21
|
+
|
|
22
|
+
export { getSelectedBlockNodes, getSelectedNodesOfType, selectionWithinConvertibleTypes };
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/selection.ts
|
|
22
|
+
var selection_exports = {};
|
|
23
|
+
__export(selection_exports, {
|
|
24
|
+
getSelectedBlockNodes: () => getSelectedBlockNodes,
|
|
25
|
+
getSelectedNodesOfType: () => getSelectedNodesOfType,
|
|
26
|
+
selectionWithinConvertibleTypes: () => selectionWithinConvertibleTypes
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(selection_exports);
|
|
29
|
+
var import_state = require("@tiptap/pm/state");
|
|
30
|
+
var import_tables = require("@tiptap/pm/tables");
|
|
31
|
+
var import_react = require("@tiptap/react");
|
|
32
|
+
function getSelectedBlockNodes(editor) {
|
|
33
|
+
const { doc } = editor.state;
|
|
34
|
+
const { from, to } = editor.state.selection;
|
|
35
|
+
const blocks = [];
|
|
36
|
+
const seen = /* @__PURE__ */ new Set();
|
|
37
|
+
doc.nodesBetween(from, to, (node, pos) => {
|
|
38
|
+
if (!node.isBlock) return;
|
|
39
|
+
if (!seen.has(pos)) {
|
|
40
|
+
seen.add(pos);
|
|
41
|
+
blocks.push(node);
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
});
|
|
45
|
+
return blocks;
|
|
46
|
+
}
|
|
47
|
+
function getSelectedNodesOfType(selection, allowedNodeTypes) {
|
|
48
|
+
const results = [];
|
|
49
|
+
const allowed = new Set(allowedNodeTypes);
|
|
50
|
+
if (selection instanceof import_tables.CellSelection) {
|
|
51
|
+
selection.forEachCell((node, pos) => {
|
|
52
|
+
if (allowed.has(node.type.name)) {
|
|
53
|
+
results.push({ node, pos });
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
return results;
|
|
57
|
+
}
|
|
58
|
+
if (selection instanceof import_state.NodeSelection) {
|
|
59
|
+
const { node, from: pos } = selection;
|
|
60
|
+
if (node && allowed.has(node.type.name)) {
|
|
61
|
+
results.push({ node, pos });
|
|
62
|
+
}
|
|
63
|
+
return results;
|
|
64
|
+
}
|
|
65
|
+
const { $anchor } = selection;
|
|
66
|
+
const cell = (0, import_tables.cellAround)($anchor);
|
|
67
|
+
if (cell) {
|
|
68
|
+
const cellNode = selection.$anchor.doc.nodeAt(cell.pos);
|
|
69
|
+
if (cellNode && allowed.has(cellNode.type.name)) {
|
|
70
|
+
results.push({ node: cellNode, pos: cell.pos });
|
|
71
|
+
return results;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const parentNode = (0, import_react.findParentNodeClosestToPos)(
|
|
75
|
+
$anchor,
|
|
76
|
+
(node) => allowed.has(node.type.name)
|
|
77
|
+
);
|
|
78
|
+
if (parentNode) {
|
|
79
|
+
results.push({ node: parentNode.node, pos: parentNode.pos });
|
|
80
|
+
}
|
|
81
|
+
return results;
|
|
82
|
+
}
|
|
83
|
+
function selectionWithinConvertibleTypes(editor, types = []) {
|
|
84
|
+
var _a, _b;
|
|
85
|
+
if (!editor || types.length === 0) return false;
|
|
86
|
+
const { state } = editor;
|
|
87
|
+
const { selection } = state;
|
|
88
|
+
const allowed = new Set(types);
|
|
89
|
+
if (selection instanceof import_state.NodeSelection) {
|
|
90
|
+
const nodeType = (_b = (_a = selection.node) == null ? void 0 : _a.type) == null ? void 0 : _b.name;
|
|
91
|
+
return !!nodeType && allowed.has(nodeType);
|
|
92
|
+
}
|
|
93
|
+
if (selection instanceof import_state.TextSelection || selection instanceof import_state.AllSelection) {
|
|
94
|
+
let valid = true;
|
|
95
|
+
state.doc.nodesBetween(selection.from, selection.to, (node) => {
|
|
96
|
+
if (node.isTextblock && !allowed.has(node.type.name)) {
|
|
97
|
+
valid = false;
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
return valid;
|
|
101
|
+
});
|
|
102
|
+
return valid;
|
|
103
|
+
}
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
107
|
+
0 && (module.exports = {
|
|
108
|
+
getSelectedBlockNodes,
|
|
109
|
+
getSelectedNodesOfType,
|
|
110
|
+
selectionWithinConvertibleTypes
|
|
111
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Transaction } from '@tiptap/pm/state';
|
|
2
|
+
import { NodeWithPos } from '@tiptap/react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Update a single attribute on multiple nodes.
|
|
6
|
+
*
|
|
7
|
+
* @param tr - The transaction to mutate
|
|
8
|
+
* @param targets - Array of { node, pos }
|
|
9
|
+
* @param attrName - Attribute key to update
|
|
10
|
+
* @param next - New value OR updater function receiving previous value
|
|
11
|
+
* Pass `undefined` to remove the attribute.
|
|
12
|
+
* @returns true if at least one node was updated, false otherwise
|
|
13
|
+
*/
|
|
14
|
+
declare function updateNodesAttr<A extends string = string, V = unknown>(tr: Transaction, targets: readonly NodeWithPos[], attrName: A, next: V | ((prev: V | undefined) => V | undefined)): boolean;
|
|
15
|
+
|
|
16
|
+
export { updateNodesAttr };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Transaction } from '@tiptap/pm/state';
|
|
2
|
+
import { NodeWithPos } from '@tiptap/react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Update a single attribute on multiple nodes.
|
|
6
|
+
*
|
|
7
|
+
* @param tr - The transaction to mutate
|
|
8
|
+
* @param targets - Array of { node, pos }
|
|
9
|
+
* @param attrName - Attribute key to update
|
|
10
|
+
* @param next - New value OR updater function receiving previous value
|
|
11
|
+
* Pass `undefined` to remove the attribute.
|
|
12
|
+
* @returns true if at least one node was updated, false otherwise
|
|
13
|
+
*/
|
|
14
|
+
declare function updateNodesAttr<A extends string = string, V = unknown>(tr: Transaction, targets: readonly NodeWithPos[], attrName: A, next: V | ((prev: V | undefined) => V | undefined)): boolean;
|
|
15
|
+
|
|
16
|
+
export { updateNodesAttr };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/update-node-attrs.ts
|
|
22
|
+
var update_node_attrs_exports = {};
|
|
23
|
+
__export(update_node_attrs_exports, {
|
|
24
|
+
updateNodesAttr: () => updateNodesAttr
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(update_node_attrs_exports);
|
|
27
|
+
function updateNodesAttr(tr, targets, attrName, next) {
|
|
28
|
+
if (!targets.length) return false;
|
|
29
|
+
let changed = false;
|
|
30
|
+
for (const { pos } of targets) {
|
|
31
|
+
const currentNode = tr.doc.nodeAt(pos);
|
|
32
|
+
if (!currentNode) continue;
|
|
33
|
+
const prevValue = currentNode.attrs[attrName];
|
|
34
|
+
const resolvedNext = typeof next === "function" ? next(prevValue) : next;
|
|
35
|
+
if (prevValue === resolvedNext) continue;
|
|
36
|
+
const nextAttrs = { ...currentNode.attrs };
|
|
37
|
+
if (resolvedNext === void 0) {
|
|
38
|
+
delete nextAttrs[attrName];
|
|
39
|
+
} else {
|
|
40
|
+
nextAttrs[attrName] = resolvedNext;
|
|
41
|
+
}
|
|
42
|
+
tr.setNodeMarkup(pos, void 0, nextAttrs);
|
|
43
|
+
changed = true;
|
|
44
|
+
}
|
|
45
|
+
return changed;
|
|
46
|
+
}
|
|
47
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
48
|
+
0 && (module.exports = {
|
|
49
|
+
updateNodesAttr
|
|
50
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kopexa/editor-utils",
|
|
3
|
-
"version": "17.0
|
|
3
|
+
"version": "17.1.0",
|
|
4
4
|
"description": "utility components for our editor",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"editor-utils"
|
|
@@ -25,16 +25,16 @@
|
|
|
25
25
|
"url": "https://github.com/kopexa-grc/sight/issues"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
|
+
"@tiptap/pm": "^3.20.0",
|
|
29
|
+
"@tiptap/react": "^3.20.0",
|
|
28
30
|
"motion": ">=12.23.6",
|
|
29
31
|
"react": ">=19.0.0-rc.0",
|
|
30
32
|
"react-dom": ">=19.0.0-rc.0",
|
|
31
|
-
"@kopexa/theme": "17.
|
|
33
|
+
"@kopexa/theme": "17.23.0"
|
|
32
34
|
},
|
|
33
35
|
"dependencies": {
|
|
34
|
-
"@
|
|
35
|
-
"@
|
|
36
|
-
"@kopexa/react-utils": "17.0.46",
|
|
37
|
-
"@kopexa/shared-utils": "17.0.46"
|
|
36
|
+
"@kopexa/react-utils": "17.0.49",
|
|
37
|
+
"@kopexa/shared-utils": "17.0.49"
|
|
38
38
|
},
|
|
39
39
|
"clean-package": "../../../clean-package.config.json",
|
|
40
40
|
"module": "dist/index.mjs",
|