@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.
@@ -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
+ });
@@ -0,0 +1,8 @@
1
+ "use client";
2
+ "use client";
3
+ import {
4
+ useTiptapEditor
5
+ } from "../chunk-P55PLOHR.mjs";
6
+ export {
7
+ useTiptapEditor
8
+ };
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 import_state.NodeSelection) {
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,11 @@
1
+ "use client";
2
+ import {
3
+ getSelectedBlockNodes,
4
+ getSelectedNodesOfType,
5
+ selectionWithinConvertibleTypes
6
+ } from "./chunk-ID5T7EEZ.mjs";
7
+ export {
8
+ getSelectedBlockNodes,
9
+ getSelectedNodesOfType,
10
+ selectionWithinConvertibleTypes
11
+ };
@@ -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
+ });
@@ -0,0 +1,7 @@
1
+ "use client";
2
+ import {
3
+ updateNodesAttr
4
+ } from "./chunk-23NZ6BRW.mjs";
5
+ export {
6
+ updateNodesAttr
7
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kopexa/editor-utils",
3
- "version": "17.0.46",
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.22.7"
33
+ "@kopexa/theme": "17.23.0"
32
34
  },
33
35
  "dependencies": {
34
- "@tiptap/pm": "^3.20.0",
35
- "@tiptap/react": "^3.20.0",
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",