@umbraci/jsmind 0.10.10 → 0.10.12
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/README.md +9 -11
- package/dist/jsmind.copy-paste.js +9 -0
- package/dist/jsmind.copy-paste.js.map +1 -0
- package/dist/jsmind.history.js.map +1 -1
- package/dist/jsmind.js +1 -1
- package/dist/jsmind.js.map +1 -1
- package/es/jsmind.copy-paste.js +9 -0
- package/es/jsmind.copy-paste.js.map +1 -0
- package/es/jsmind.history.js.map +1 -1
- package/es/jsmind.js +1 -1
- package/es/jsmind.js.map +1 -1
- package/lib/jsmind.copy-paste.js +9 -0
- package/lib/jsmind.copy-paste.js.map +1 -0
- package/lib/jsmind.history.js.map +1 -1
- package/lib/jsmind.js +1 -1
- package/lib/jsmind.js.map +1 -1
- package/package.json +1 -1
- package/types/generated/jsmind.d.ts +198 -7
- package/types/generated/jsmind.mind.d.ts +8 -0
- package/types/generated/jsmind.option.d.ts +5 -0
- package/types/generated/jsmind.view_provider.d.ts +27 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umbraci/jsmind",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.12",
|
|
4
4
|
"description": "jsMind is a pure javascript library for mindmap, it base on html5 canvas. jsMind was released under BSD license, you can embed it in any project, if only you observe the license.",
|
|
5
5
|
"main": "lib/jsmind.js",
|
|
6
6
|
"module": "es/jsmind.js",
|
|
@@ -78,6 +78,10 @@ declare class jsMind {
|
|
|
78
78
|
version: string;
|
|
79
79
|
initialized: boolean;
|
|
80
80
|
mind: Mind;
|
|
81
|
+
/** @type {'single'|'multi'|null} */
|
|
82
|
+
_selection_mode: "single" | "multi" | null;
|
|
83
|
+
/** @type {import('./jsmind.node.js').Node|null} */
|
|
84
|
+
_last_selected_node: import("./jsmind.node.js").Node | null;
|
|
81
85
|
/** @type {Array<(type: number, data: EventData) => void>} */
|
|
82
86
|
event_handles: Array<(type: number, data: EventData) => void>;
|
|
83
87
|
/** Initialize sub-systems and plugins. */
|
|
@@ -305,14 +309,11 @@ declare class jsMind {
|
|
|
305
309
|
*/
|
|
306
310
|
remove_node(node: string | import("./jsmind.node.js").Node): boolean;
|
|
307
311
|
/**
|
|
308
|
-
* Update
|
|
309
|
-
* @param {string} node_id
|
|
310
|
-
* @param {string|
|
|
312
|
+
* Update node topic text or multiple node properties.
|
|
313
|
+
* @param {string} node_id - The ID of the node to update
|
|
314
|
+
* @param {string|Partial<Pick<import('./jsmind.node.js').Node, 'topic' | 'data' | 'id' | 'index' | 'expanded' | 'direction'>>} topic_or_updates - Topic string for backward compatibility, or partial Node object for comprehensive updates
|
|
311
315
|
*/
|
|
312
|
-
update_node(node_id: string,
|
|
313
|
-
topic?: string;
|
|
314
|
-
data?: Record<string, any>;
|
|
315
|
-
}): void;
|
|
316
|
+
update_node(node_id: string, topic_or_updates: string | Partial<Pick<import("./jsmind.node.js").Node, "topic" | "data" | "id" | "index" | "expanded" | "direction">>): void;
|
|
316
317
|
/**
|
|
317
318
|
* Move a node and optionally change direction.
|
|
318
319
|
* @param {string} node_id
|
|
@@ -335,8 +336,24 @@ declare class jsMind {
|
|
|
335
336
|
* @returns {import('./jsmind.node.js').Node|null} Node instance or null
|
|
336
337
|
*/
|
|
337
338
|
get_selected_node(): import("./jsmind.node.js").Node | null;
|
|
339
|
+
/**
|
|
340
|
+
* Get all currently selected nodes.
|
|
341
|
+
* @returns {import('./jsmind.node.js').Node[]}
|
|
342
|
+
*/
|
|
343
|
+
get_selected_nodes(): import("./jsmind.node.js").Node[];
|
|
338
344
|
/** clear selection */
|
|
339
345
|
select_clear(): void;
|
|
346
|
+
/**
|
|
347
|
+
* Toggle multi-selection for a node (and optionally descendants).
|
|
348
|
+
* @param {string | import('./jsmind.node.js').Node} node
|
|
349
|
+
*/
|
|
350
|
+
toggle_subtree_selection(node: string | import("./jsmind.node.js").Node): void;
|
|
351
|
+
/**
|
|
352
|
+
* Determine whether a node is currently selected.
|
|
353
|
+
* @param {string | import('./jsmind.node.js').Node} node
|
|
354
|
+
* @returns {boolean}
|
|
355
|
+
*/
|
|
356
|
+
is_node_selected(node: string | import("./jsmind.node.js").Node): boolean;
|
|
340
357
|
/** @param {string | import('./jsmind.node.js').Node} node */
|
|
341
358
|
is_node_visible(node: string | import("./jsmind.node.js").Node): boolean;
|
|
342
359
|
/**
|
|
@@ -344,6 +361,180 @@ declare class jsMind {
|
|
|
344
361
|
* @param {string | import('./jsmind.node.js').Node} node
|
|
345
362
|
*/
|
|
346
363
|
scroll_node_to_center(node: string | import("./jsmind.node.js").Node): void;
|
|
364
|
+
/**
|
|
365
|
+
* Add nodes into the current selection set without clearing existing ones.
|
|
366
|
+
* @param {import('./jsmind.node.js').Node[]} nodes
|
|
367
|
+
* @returns {import('./jsmind.node.js').Node[]}
|
|
368
|
+
* @private
|
|
369
|
+
*/
|
|
370
|
+
/**
|
|
371
|
+
* @param {import('./jsmind.node.js').Node[]} nodes
|
|
372
|
+
* @param {{focusNode?: import('./jsmind.node.js').Node}=} options
|
|
373
|
+
* @private
|
|
374
|
+
*/
|
|
375
|
+
private _append_selection;
|
|
376
|
+
/**
|
|
377
|
+
* Remove nodes from the current selection set.
|
|
378
|
+
* @param {import('./jsmind.node.js').Node[]} nodes
|
|
379
|
+
* @returns {import('./jsmind.node.js').Node[]}
|
|
380
|
+
* @private
|
|
381
|
+
*/
|
|
382
|
+
private _remove_selection;
|
|
383
|
+
/**
|
|
384
|
+
* Deselect a node and all its descendants from the current selection.
|
|
385
|
+
* @param {string | import('./jsmind.node.js').Node} node
|
|
386
|
+
* @private
|
|
387
|
+
*/
|
|
388
|
+
private _deselect_subtree;
|
|
389
|
+
/**
|
|
390
|
+
* Clear all current selections and return the nodes that were cleared.
|
|
391
|
+
* @returns {import('./jsmind.node.js').Node[]}
|
|
392
|
+
* @private
|
|
393
|
+
*/
|
|
394
|
+
private _clear_selection_state;
|
|
395
|
+
/**
|
|
396
|
+
* Collect a node and optionally its descendants respecting filters.
|
|
397
|
+
* @param {import('./jsmind.node.js').Node} node
|
|
398
|
+
* @param {{includeChildren?:boolean, respectFilter?:boolean, skipRootFilter?:boolean}=} config
|
|
399
|
+
* @returns {import('./jsmind.node.js').Node[]}
|
|
400
|
+
* @private
|
|
401
|
+
*/
|
|
402
|
+
private _collect_subtree_nodes;
|
|
403
|
+
/**
|
|
404
|
+
* Ensure ancestors of provided nodes are also selected (up to first selected ancestor).
|
|
405
|
+
* @param {import('./jsmind.node.js').Node[]} nodes
|
|
406
|
+
* @param {import('./jsmind.node.js').Node=} focusNode
|
|
407
|
+
* @returns {import('./jsmind.node.js').Node[]}
|
|
408
|
+
* @private
|
|
409
|
+
*/
|
|
410
|
+
/**
|
|
411
|
+
* @param {import('./jsmind.node.js').Node[]} nodes
|
|
412
|
+
* @param {import('./jsmind.node.js').Node=} focusNode
|
|
413
|
+
* @param {{requireAncestorChainSelected?: boolean}=} options
|
|
414
|
+
* @returns {import('./jsmind.node.js').Node[]}
|
|
415
|
+
* @private
|
|
416
|
+
*/
|
|
417
|
+
private _ensure_ancestor_selection;
|
|
418
|
+
/**
|
|
419
|
+
* Get the configured selection filter callback, if any.
|
|
420
|
+
* @returns {((node: import('./jsmind.node.js').Node)=>boolean)|null}
|
|
421
|
+
* @private
|
|
422
|
+
*/
|
|
423
|
+
private _get_selection_filter;
|
|
424
|
+
/**
|
|
425
|
+
* Determine the multi-select mode based on event modifiers.
|
|
426
|
+
* Returns: null (single select), 'ctrl' (add/remove), 'shift' (range select)
|
|
427
|
+
* @param {MouseEvent} e
|
|
428
|
+
* @returns {null|'ctrl'|'shift'}
|
|
429
|
+
* @private
|
|
430
|
+
*/
|
|
431
|
+
private _get_multi_select_mode;
|
|
432
|
+
/**
|
|
433
|
+
* Toggle selection of a single node (add if not selected, remove if selected).
|
|
434
|
+
* Used for Ctrl+Click behavior.
|
|
435
|
+
* @param {string | import('./jsmind.node.js').Node} node_id
|
|
436
|
+
* @private
|
|
437
|
+
*/
|
|
438
|
+
private _toggle_node_selection;
|
|
439
|
+
/**
|
|
440
|
+
* Range select nodes for Shift+Click behavior.
|
|
441
|
+
* Logic:
|
|
442
|
+
* - If no nodes are currently selected: select all nodes under the clicked node
|
|
443
|
+
* - If nodes are already selected: select nodes in the range from first selected to clicked node
|
|
444
|
+
* @param {string | import('./jsmind.node.js').Node} node_id
|
|
445
|
+
* @private
|
|
446
|
+
*/
|
|
447
|
+
private _range_select_nodes;
|
|
448
|
+
/**
|
|
449
|
+
* Find all nodes between two nodes (for range selection).
|
|
450
|
+
* This includes both nodes and all nodes in between them in tree order.
|
|
451
|
+
* @param {import('./jsmind.node.js').Node} node1
|
|
452
|
+
* @param {import('./jsmind.node.js').Node} node2
|
|
453
|
+
* @returns {import('./jsmind.node.js').Node[]}
|
|
454
|
+
* @private
|
|
455
|
+
*/
|
|
456
|
+
private _find_nodes_between;
|
|
457
|
+
/**
|
|
458
|
+
* Check whether ancestor is an ancestor of node.
|
|
459
|
+
* @param {import('./jsmind.node.js').Node} ancestor
|
|
460
|
+
* @param {import('./jsmind.node.js').Node} node
|
|
461
|
+
* @returns {boolean}
|
|
462
|
+
* @private
|
|
463
|
+
*/
|
|
464
|
+
private _is_ancestor_of;
|
|
465
|
+
/**
|
|
466
|
+
* Return nodes along the ancestor->descendant chain, inclusive. If 'ancestor' is not actually
|
|
467
|
+
* an ancestor of 'descendant', returns empty array.
|
|
468
|
+
* @param {import('./jsmind.node.js').Node} ancestor
|
|
469
|
+
* @param {import('./jsmind.node.js').Node} descendant
|
|
470
|
+
* @returns {import('./jsmind.node.js').Node[]}
|
|
471
|
+
* @private
|
|
472
|
+
*/
|
|
473
|
+
private _get_path_nodes;
|
|
474
|
+
/**
|
|
475
|
+
* Find nearest selected ancestor of the given node from current selection set.
|
|
476
|
+
* @param {import('./jsmind.node.js').Node} node
|
|
477
|
+
* @returns {import('./jsmind.node.js').Node|null}
|
|
478
|
+
* @private
|
|
479
|
+
*/
|
|
480
|
+
private _find_nearest_selected_ancestor;
|
|
481
|
+
/**
|
|
482
|
+
* Find lowest common ancestor of two nodes.
|
|
483
|
+
* @param {import('./jsmind.node.js').Node} a
|
|
484
|
+
* @param {import('./jsmind.node.js').Node} b
|
|
485
|
+
* @returns {import('./jsmind.node.js').Node|null}
|
|
486
|
+
* @private
|
|
487
|
+
*/
|
|
488
|
+
private _find_lca;
|
|
489
|
+
/**
|
|
490
|
+
* Given a lowest common ancestor 'lca' and a descendant 'node',
|
|
491
|
+
* return the direct child of lca that lies on the path to node.
|
|
492
|
+
* Returns null if node is not a descendant of lca or node === lca.
|
|
493
|
+
* @param {import('./jsmind.node.js').Node} lca
|
|
494
|
+
* @param {import('./jsmind.node.js').Node} node
|
|
495
|
+
* @returns {import('./jsmind.node.js').Node|null}
|
|
496
|
+
* @private
|
|
497
|
+
*/
|
|
498
|
+
private _child_on_path;
|
|
499
|
+
/**
|
|
500
|
+
* From a list of nodes, remove those that are ancestors of any other node in the same list.
|
|
501
|
+
* Keeps only the deepest nodes so that we don't auto-select parents implicitly.
|
|
502
|
+
* @param {import('./jsmind.node.js').Node[]} nodes
|
|
503
|
+
* @returns {import('./jsmind.node.js').Node[]}
|
|
504
|
+
* @private
|
|
505
|
+
*/
|
|
506
|
+
private _remove_ancestor_nodes;
|
|
507
|
+
/**
|
|
508
|
+
* From a list of nodes, remove those that are descendants of any other node in the same list.
|
|
509
|
+
* Keeps only top-most nodes so that expanding subtrees covers full branches across siblings.
|
|
510
|
+
* @param {import('./jsmind.node.js').Node[]} nodes
|
|
511
|
+
* @returns {import('./jsmind.node.js').Node[]}
|
|
512
|
+
* @private
|
|
513
|
+
*/
|
|
514
|
+
private _remove_descendant_nodes;
|
|
515
|
+
/**
|
|
516
|
+
* Expand a set of base nodes with all their descendants (and themselves).
|
|
517
|
+
* @param {import('./jsmind.node.js').Node[]} nodes
|
|
518
|
+
* @param {{respectFilter?: boolean}=} opts
|
|
519
|
+
* @returns {Set<import('./jsmind.node.js').Node>}
|
|
520
|
+
* @private
|
|
521
|
+
*/
|
|
522
|
+
private _expand_with_descendants;
|
|
523
|
+
/**
|
|
524
|
+
* Promote parents into selection ONLY when all their direct children are selected
|
|
525
|
+
* AND at least one ancestor of that parent is already selected (in previous selection set).
|
|
526
|
+
* This avoids auto-selecting parents when only siblings are selected.
|
|
527
|
+
* @param {Set<import('./jsmind.node.js').Node>} set
|
|
528
|
+
* @returns {Set<import('./jsmind.node.js').Node>}
|
|
529
|
+
* @private
|
|
530
|
+
*/
|
|
531
|
+
private _promote_parents_when_children_selected;
|
|
532
|
+
/**
|
|
533
|
+
* Determine selection mode based on current selection size.
|
|
534
|
+
* @returns {'single'|'multi'|null}
|
|
535
|
+
* @private
|
|
536
|
+
*/
|
|
537
|
+
private _derive_selection_mode;
|
|
347
538
|
/**
|
|
348
539
|
* Find the previous sibling node of the given node.
|
|
349
540
|
*
|
|
@@ -9,6 +9,8 @@ export class Mind {
|
|
|
9
9
|
root: Node | null;
|
|
10
10
|
/** @type {Node | null} */
|
|
11
11
|
selected: Node | null;
|
|
12
|
+
/** @type {Set<Node>} */
|
|
13
|
+
selected_nodes: Set<Node>;
|
|
12
14
|
/** @type {Record<string, Node>} */
|
|
13
15
|
nodes: Record<string, Node>;
|
|
14
16
|
/**
|
|
@@ -106,6 +108,12 @@ export class Mind {
|
|
|
106
108
|
* @returns {boolean}
|
|
107
109
|
*/
|
|
108
110
|
remove_node(node: Node): boolean;
|
|
111
|
+
/**
|
|
112
|
+
* Remove a node and its subtree from the cached selection set.
|
|
113
|
+
* @param {Node} node
|
|
114
|
+
* @private
|
|
115
|
+
*/
|
|
116
|
+
private _purge_selection;
|
|
109
117
|
/**
|
|
110
118
|
* Put node into the map if id is not taken.
|
|
111
119
|
* @param {Node} node
|
|
@@ -60,6 +60,11 @@ export type JsMindRuntimeOptions = {
|
|
|
60
60
|
mapping?: Record<string, number | number[]>;
|
|
61
61
|
id_generator?: () => string;
|
|
62
62
|
};
|
|
63
|
+
selection?: {
|
|
64
|
+
enable_multi_select?: boolean;
|
|
65
|
+
include_descendants?: boolean;
|
|
66
|
+
filter?: (node: import("./jsmind.node.js").Node) => boolean;
|
|
67
|
+
};
|
|
63
68
|
fieldNames?: {
|
|
64
69
|
id?: string;
|
|
65
70
|
topic?: string;
|
|
@@ -71,6 +71,8 @@ export class ViewProvider {
|
|
|
71
71
|
h: number;
|
|
72
72
|
};
|
|
73
73
|
selected_node: import("./jsmind.node.js").Node;
|
|
74
|
+
/** @type {Map<string, import('./jsmind.node.js').Node>} */
|
|
75
|
+
multi_selected_nodes: Map<string, import("./jsmind.node.js").Node>;
|
|
74
76
|
editing_node: import("./jsmind.node.js").Node;
|
|
75
77
|
graph: {
|
|
76
78
|
view: ViewProvider;
|
|
@@ -253,6 +255,19 @@ export class ViewProvider {
|
|
|
253
255
|
select_node(node: import("./jsmind.node.js").Node | null): void;
|
|
254
256
|
/** Clear node selection. */
|
|
255
257
|
select_clear(): void;
|
|
258
|
+
/**
|
|
259
|
+
* Append nodes to the current selection without clearing existing ones.
|
|
260
|
+
* @param {import('./jsmind.node.js').Node[]} nodes
|
|
261
|
+
* @param {import('./jsmind.node.js').Node=} focus_node
|
|
262
|
+
*/
|
|
263
|
+
append_selected_nodes(nodes: import("./jsmind.node.js").Node[], focus_node?: import("./jsmind.node.js").Node | undefined): void;
|
|
264
|
+
/**
|
|
265
|
+
* Remove the provided nodes from selection state.
|
|
266
|
+
* @param {import('./jsmind.node.js').Node[]} nodes
|
|
267
|
+
*/
|
|
268
|
+
remove_selected_nodes(nodes: import("./jsmind.node.js").Node[]): void;
|
|
269
|
+
/** Clear all selections at once. */
|
|
270
|
+
clear_all_selected_nodes(): void;
|
|
256
271
|
/**
|
|
257
272
|
* Get currently editing node.
|
|
258
273
|
* @returns {import('./jsmind.node.js').Node|null} Currently editing node
|
|
@@ -329,6 +344,18 @@ export class ViewProvider {
|
|
|
329
344
|
restore_selected_node_custom_style(node: import("./jsmind.node.js").Node): void;
|
|
330
345
|
/** @param {import('./jsmind.node.js').Node} node */
|
|
331
346
|
clear_selected_node_custom_style(node: import("./jsmind.node.js").Node): void;
|
|
347
|
+
/**
|
|
348
|
+
* Mark the DOM/state for a selected node.
|
|
349
|
+
* @param {import('./jsmind.node.js').Node} node
|
|
350
|
+
* @private
|
|
351
|
+
*/
|
|
352
|
+
private _mark_node_selected;
|
|
353
|
+
/**
|
|
354
|
+
* Remove DOM/state selection for a node.
|
|
355
|
+
* @param {import('./jsmind.node.js').Node} node
|
|
356
|
+
* @private
|
|
357
|
+
*/
|
|
358
|
+
private _unmark_node_selected;
|
|
332
359
|
clear_lines(): void;
|
|
333
360
|
show_lines(): void;
|
|
334
361
|
/**
|