@keenmate/svelte-treeview 4.2.0 → 4.3.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/README.md CHANGED
@@ -512,6 +512,7 @@ Without both requirements, no search indexing will occur.
512
512
  | `indexerBatchSize` | `number \| null` | `25` | Number of nodes to process per batch during search indexing |
513
513
  | `indexerTimeout` | `number \| null` | `50` | Maximum time (ms) to wait for idle callback before forcing indexing |
514
514
  | `shouldDisplayDebugInformation` | `boolean` | `false` | Show debug information panel with tree statistics and enable console debug logging for tree operations and async search indexing |
515
+ | `shouldDisplayContextMenuInDebugMode` | `boolean` | `false` | Display persistent context menu at fixed position for styling development |
515
516
 
516
517
  #### Event Handler Properties
517
518
  | Prop | Type | Default | Description |
@@ -732,6 +733,27 @@ Horizontal offset from cursor position (default: 8px).
732
733
  #### contextMenuYOffset
733
734
  Vertical offset from cursor position (default: 0px).
734
735
 
736
+ #### shouldDisplayContextMenuInDebugMode
737
+ When enabled, displays a persistent context menu at a fixed position for styling development (default: false).
738
+
739
+ ```svelte
740
+ <Tree
741
+ {data}
742
+ contextMenuCallback={createContextMenu}
743
+ shouldDisplayContextMenuInDebugMode={true}
744
+ shouldDisplayDebugInformation={true}
745
+ contextMenuXOffset={10}
746
+ contextMenuYOffset={5}
747
+ />
748
+ ```
749
+
750
+ **Debug Mode Features:**
751
+ - Shows context menu for the second node (or first if only one exists)
752
+ - Positions menu 200px right and 100px down from tree's top-left corner
753
+ - Persistent display - no need to right-click repeatedly
754
+ - Perfect for CSS styling and position testing
755
+ - Works with both callback-based and snippet-based context menus
756
+
735
757
  ## 🏗️ Data Structure
736
758
 
737
759
  The component expects hierarchical data with path-based organization:
@@ -69,7 +69,7 @@
69
69
  onNodeDragStart?: (node: LTreeNode<T>, event: DragEvent) => void;
70
70
  onNodeDragOver?: (node: LTreeNode<T>, event: DragEvent) => void;
71
71
  onNodeDrop?: (node: LTreeNode<T>, draggedNode: LTreeNode<T>, event: DragEvent) => void;
72
- contextMenuCallback?: (node: LTreeNode<T>) => ContextMenuItem[];
72
+ contextMenuCallback?: (node: LTreeNode<T>, closeMenuCallback: () => void) => ContextMenuItem[];
73
73
 
74
74
  // VISUALS
75
75
  bodyClass?: string | null | undefined;
@@ -178,6 +178,13 @@
178
178
  return tree?.searchNodes(searchText, searchOptions) || [];
179
179
  }
180
180
 
181
+ // svelte-ignore non_reactive_update
182
+ export function closeContextMenu() {
183
+ contextMenuVisible = false;
184
+ contextMenuNode = null;
185
+ isDebugMenuActive = false;
186
+ }
187
+
181
188
  export async function scrollToPath(
182
189
  path: string,
183
190
  options?: { expand?: boolean; highlight?: boolean; scrollOptions?: ScrollIntoViewOptions }
@@ -325,11 +332,6 @@
325
332
  isDebugMenuActive = false; // This is a user-triggered menu, not debug menu
326
333
  }
327
334
 
328
- function closeContextMenu() {
329
- contextMenuVisible = false;
330
- contextMenuNode = null;
331
- isDebugMenuActive = false;
332
- }
333
335
 
334
336
  function _onNodeDragStart(node: LTreeNode<T>, event: DragEvent) {
335
337
  draggedNode = node;
@@ -410,7 +412,7 @@
410
412
 
411
413
  const handleGlobalScroll = (event?: Event) => {
412
414
  if (shouldDisplayDebugInformation) {
413
- console.log('Scroll/wheel event detected, closing context menu', event?.type);
415
+ console.log(`[Tree ${treeId}] Scroll/wheel event detected, closing context menu`, event?.type);
414
416
  }
415
417
  closeContextMenu();
416
418
  };
@@ -436,21 +438,23 @@
436
438
 
437
439
  // Debug context menu - show context menu on second node for styling development
438
440
  let isDebugMenuActive = $state(false);
441
+ let treeContainerRef: HTMLDivElement;
439
442
 
440
443
  $effect(() => {
441
- if (shouldDisplayContextMenuInDebugMode && (contextMenu || contextMenuCallback) && tree?.tree && tree.tree.length > 1) {
442
- // Find the second node in the tree
443
- const secondNode = tree.tree[1];
444
- if (secondNode) {
445
- // Position the context menu at a fixed location for easy styling
446
- contextMenuNode = secondNode;
447
- contextMenuX = 200; // Fixed position for consistent styling work
448
- contextMenuY = 100;
444
+ if (shouldDisplayContextMenuInDebugMode && (contextMenu || contextMenuCallback) && tree?.tree && tree.tree.length > 0) {
445
+ // Use the first available node for the context menu data
446
+ const targetNode = tree.tree.length > 1 ? tree.tree[1] : tree.tree[0];
447
+ if (targetNode && treeContainerRef) {
448
+ // Position the context menu relative to the tree container
449
+ const treeRect = treeContainerRef.getBoundingClientRect();
450
+ contextMenuNode = targetNode;
451
+ contextMenuX = treeRect.left + 200; // 200px from tree's left edge
452
+ contextMenuY = treeRect.top + 100; // 100px from tree's top edge
449
453
  contextMenuVisible = true;
450
454
  isDebugMenuActive = true;
451
455
 
452
456
  if (shouldDisplayDebugInformation) {
453
- console.log('Debug context menu displayed for node:', secondNode.data);
457
+ console.log(`[Tree ${treeId}] Debug context menu displayed for node:`, targetNode.data, `at position (${contextMenuX}, ${contextMenuY})`);
454
458
  }
455
459
  }
456
460
  } else if (!shouldDisplayContextMenuInDebugMode && isDebugMenuActive) {
@@ -462,7 +466,7 @@
462
466
  });
463
467
  </script>
464
468
 
465
- <div>
469
+ <div bind:this={treeContainerRef}>
466
470
  {#if shouldDisplayDebugInformation}
467
471
  <div class="ltree-debug-info">
468
472
  <details>
@@ -527,15 +531,23 @@
527
531
  {#if contextMenuVisible && contextMenuNode}
528
532
  <div class="ltree-context-menu" style="left: {contextMenuX}px; top: {contextMenuY}px;">
529
533
  {#if contextMenuCallback}
530
- {@const menuItems = contextMenuCallback(contextMenuNode)}
534
+ {@const menuItems = contextMenuCallback(contextMenuNode, closeContextMenu)}
531
535
  {#each menuItems as item}
532
536
  {#if item.isDivider}
533
537
  <div class="ltree-context-menu-divider"></div>
534
538
  {:else}
535
539
  <div
536
- class="ltree-context-menu-item"
540
+ class="ltree-context-menu-item {item.className || ''}"
537
541
  class:ltree-context-menu-item-disabled={item.isDisabled}
538
- onclick={() => !item.isDisabled && item.callback()}
542
+ onclick={async () => {
543
+ if (!item.isDisabled) {
544
+ try {
545
+ await item.callback();
546
+ } catch (error) {
547
+ console.error('Context menu callback error:', error);
548
+ }
549
+ }
550
+ }}
539
551
  >
540
552
  {#if item.icon}
541
553
  <span class="ltree-context-menu-icon">{item.icon}</span>
@@ -43,7 +43,7 @@ declare function $$render<T>(): {
43
43
  onNodeDragStart?: (node: LTreeNode<T>, event: DragEvent) => void;
44
44
  onNodeDragOver?: (node: LTreeNode<T>, event: DragEvent) => void;
45
45
  onNodeDrop?: (node: LTreeNode<T>, draggedNode: LTreeNode<T>, event: DragEvent) => void;
46
- contextMenuCallback?: (node: LTreeNode<T>) => ContextMenuItem[];
46
+ contextMenuCallback?: (node: LTreeNode<T>, closeMenuCallback: () => void) => ContextMenuItem[];
47
47
  bodyClass?: string | null | undefined;
48
48
  selectedNodeClass?: string | null | undefined;
49
49
  dragOverNodeClass?: string | null | undefined;
@@ -62,6 +62,7 @@ declare function $$render<T>(): {
62
62
  collapseAll: (nodePath?: string | null | undefined) => void;
63
63
  filterNodes: (searchText: string, searchOptions?: SearchOptions) => void;
64
64
  searchNodes: (searchText: string | null | undefined, searchOptions?: SearchOptions) => LTreeNode<T>[];
65
+ closeContextMenu: () => void;
65
66
  scrollToPath: (path: string, options?: {
66
67
  expand?: boolean;
67
68
  highlight?: boolean;
@@ -84,6 +85,7 @@ declare class __sveltets_Render<T> {
84
85
  collapseAll: (nodePath?: string | null | undefined) => void;
85
86
  filterNodes: (searchText: string, searchOptions?: SearchOptions) => void;
86
87
  searchNodes: (searchText: string | null | undefined, searchOptions?: SearchOptions) => LTreeNode<T>[];
88
+ closeContextMenu: () => void;
87
89
  scrollToPath: (path: string, options?: {
88
90
  expand?: boolean;
89
91
  highlight?: boolean;
@@ -5,8 +5,9 @@ export interface ContextMenuItem {
5
5
  icon?: string;
6
6
  title: string;
7
7
  isDisabled?: boolean;
8
- callback: () => void;
8
+ callback: () => void | Promise<void>;
9
9
  isDivider?: boolean;
10
+ className?: string;
10
11
  }
11
12
  export interface InsertArrayResult<T> {
12
13
  successful: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keenmate/svelte-treeview",
3
- "version": "4.2.0",
3
+ "version": "4.3.1",
4
4
  "scripts": {
5
5
  "dev": "vite dev --port 17777",
6
6
  "build": "vite build && npm run prepack",