@ship-ui/core 0.22.9 → 0.22.11

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,238 @@
1
+ import { NgTemplateOutlet } from '@angular/common';
2
+ import * as i0 from '@angular/core';
3
+ import { inject, ElementRef, Directive, model, input, contentChild, computed, output, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
4
+ import { ShipIcon } from '@ship-ui/core/ship-icon';
5
+ import { ShipSortable } from '@ship-ui/core/ship-sortable';
6
+
7
+ class ShipTreeOpenIcon {
8
+ constructor() {
9
+ this.el = inject((ElementRef));
10
+ }
11
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ShipTreeOpenIcon, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
12
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.0", type: ShipTreeOpenIcon, isStandalone: true, selector: "sh-icon[openIcon]", ngImport: i0 }); }
13
+ }
14
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ShipTreeOpenIcon, decorators: [{
15
+ type: Directive,
16
+ args: [{ selector: 'sh-icon[openIcon]', standalone: true }]
17
+ }] });
18
+ class ShipTreeClosedIcon {
19
+ constructor() {
20
+ this.el = inject((ElementRef));
21
+ }
22
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ShipTreeClosedIcon, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
23
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.0", type: ShipTreeClosedIcon, isStandalone: true, selector: "sh-icon[closedIcon]", ngImport: i0 }); }
24
+ }
25
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ShipTreeClosedIcon, decorators: [{
26
+ type: Directive,
27
+ args: [{ selector: 'sh-icon[closedIcon]', standalone: true }]
28
+ }] });
29
+ class ShipTreeItemIcon {
30
+ constructor() {
31
+ this.el = inject((ElementRef));
32
+ }
33
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ShipTreeItemIcon, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
34
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.0", type: ShipTreeItemIcon, isStandalone: true, selector: "sh-icon[itemIcon]", ngImport: i0 }); }
35
+ }
36
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ShipTreeItemIcon, decorators: [{
37
+ type: Directive,
38
+ args: [{ selector: 'sh-icon[itemIcon]', standalone: true }]
39
+ }] });
40
+ class ShipTree {
41
+ constructor() {
42
+ /** The flat list of all tree nodes. */
43
+ this.items = model([], /* @ts-ignore */
44
+ ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
45
+ /** Optional Writable/Readable WritableSignal manager created by createTreeSortableManager. */
46
+ this.sortableManager = input(null, /* @ts-ignore */
47
+ ...(ngDevMode ? [{ debugName: "sortableManager" }] : /* istanbul ignore next */ []));
48
+ /** Currently selected node ID. */
49
+ this.selectedId = model(null, /* @ts-ignore */
50
+ ...(ngDevMode ? [{ debugName: "selectedId" }] : /* istanbul ignore next */ []));
51
+ // Mappings
52
+ this.getId = input((item) => item.id, /* @ts-ignore */
53
+ ...(ngDevMode ? [{ debugName: "getId" }] : /* istanbul ignore next */ []));
54
+ this.getName = input((item) => item.name, /* @ts-ignore */
55
+ ...(ngDevMode ? [{ debugName: "getName" }] : /* istanbul ignore next */ []));
56
+ this.getParentId = input((item) => item.parentId, /* @ts-ignore */
57
+ ...(ngDevMode ? [{ debugName: "getParentId" }] : /* istanbul ignore next */ []));
58
+ this.isFolder = input((item) => item.type === 'dir', /* @ts-ignore */
59
+ ...(ngDevMode ? [{ debugName: "isFolder" }] : /* istanbul ignore next */ []));
60
+ this.getIsOpen = input((item) => !!item.isOpen, /* @ts-ignore */
61
+ ...(ngDevMode ? [{ debugName: "getIsOpen" }] : /* istanbul ignore next */ []));
62
+ this.setIsOpen = input((item, open) => {
63
+ item.isOpen = open;
64
+ }, /* @ts-ignore */
65
+ ...(ngDevMode ? [{ debugName: "setIsOpen" }] : /* istanbul ignore next */ []));
66
+ /** Function returning a custom icon name for a node. */
67
+ this.getIcon = input(() => null, /* @ts-ignore */
68
+ ...(ngDevMode ? [{ debugName: "getIcon" }] : /* istanbul ignore next */ []));
69
+ // Read projected icons via lightweight directives
70
+ this.openIconDir = contentChild(ShipTreeOpenIcon, /* @ts-ignore */
71
+ ...(ngDevMode ? [{ debugName: "openIconDir" }] : /* istanbul ignore next */ []));
72
+ this.closedIconDir = contentChild(ShipTreeClosedIcon, /* @ts-ignore */
73
+ ...(ngDevMode ? [{ debugName: "closedIconDir" }] : /* istanbul ignore next */ []));
74
+ this.itemIconDir = contentChild(ShipTreeItemIcon, /* @ts-ignore */
75
+ ...(ngDevMode ? [{ debugName: "itemIconDir" }] : /* istanbul ignore next */ []));
76
+ this.openIconName = computed(() => this.openIconDir()?.el.nativeElement.textContent?.trim() || null, /* @ts-ignore */
77
+ ...(ngDevMode ? [{ debugName: "openIconName" }] : /* istanbul ignore next */ []));
78
+ this.closedIconName = computed(() => this.closedIconDir()?.el.nativeElement.textContent?.trim() || null, /* @ts-ignore */
79
+ ...(ngDevMode ? [{ debugName: "closedIconName" }] : /* istanbul ignore next */ []));
80
+ this.itemIconName = computed(() => this.itemIconDir()?.el.nativeElement.textContent?.trim() || null, /* @ts-ignore */
81
+ ...(ngDevMode ? [{ debugName: "itemIconName" }] : /* istanbul ignore next */ []));
82
+ // Outputs
83
+ this.nodeClick = output();
84
+ this.nodeToggle = output();
85
+ this.nodeTemplate = contentChild('nodeTemplate', /* @ts-ignore */
86
+ ...(ngDevMode ? [{ debugName: "nodeTemplate" }] : /* istanbul ignore next */ []));
87
+ this.dirTemplate = contentChild('dirTemplate', /* @ts-ignore */
88
+ ...(ngDevMode ? [{ debugName: "dirTemplate" }] : /* istanbul ignore next */ []));
89
+ /** Filter the full flat array down to only visible nodes (whose parents are all expanded). */
90
+ this.visibleNodes = computed(() => {
91
+ const manager = this.sortableManager();
92
+ if (manager && typeof manager.visibleNodes === 'function') {
93
+ return manager.visibleNodes();
94
+ }
95
+ const list = this.items();
96
+ const visible = [];
97
+ const isNodeVisible = (node) => {
98
+ let currentParentId = this.getParentId()(node);
99
+ while (currentParentId !== null && currentParentId !== undefined) {
100
+ const parent = list.find((n) => this.getId()(n) === currentParentId);
101
+ if (!parent || !this.getIsOpen()(parent)) {
102
+ return false;
103
+ }
104
+ currentParentId = this.getParentId()(parent);
105
+ }
106
+ return true;
107
+ };
108
+ for (const node of list) {
109
+ const parentId = this.getParentId()(node);
110
+ if (parentId === null || parentId === undefined || isNodeVisible(node)) {
111
+ visible.push(node);
112
+ }
113
+ }
114
+ return visible;
115
+ }, /* @ts-ignore */
116
+ ...(ngDevMode ? [{ debugName: "visibleNodes" }] : /* istanbul ignore next */ []));
117
+ }
118
+ toggleNode(node, event) {
119
+ event.stopPropagation();
120
+ const open = !this.getIsOpen()(node);
121
+ this.items.update((list) => list.map((item) => {
122
+ if (this.getId()(item) === this.getId()(node)) {
123
+ const updated = { ...item };
124
+ this.setIsOpen()(updated, open);
125
+ return updated;
126
+ }
127
+ return item;
128
+ }));
129
+ this.nodeToggle.emit({ node, isOpen: open });
130
+ }
131
+ handleNodeClick(node, event) {
132
+ const target = event.target;
133
+ const isIconClick = !!target.closest('sh-icon') && !target.closest('.sh-tree-node-actions') && !target.closest('sh-tree-node-actions') && !target.closest('.caret-container');
134
+ if (isIconClick && this.isFolder()(node)) {
135
+ this.toggleNode(node, event);
136
+ }
137
+ else {
138
+ this.selectNode(node);
139
+ }
140
+ }
141
+ selectNode(node) {
142
+ this.selectedId.set(this.getId()(node));
143
+ this.nodeClick.emit(node);
144
+ }
145
+ getDepthArray(depth) {
146
+ return Array.from({ length: depth }, (_, i) => i);
147
+ }
148
+ getNodeDepth(node) {
149
+ const list = this.items();
150
+ let depth = 0;
151
+ let currentParentId = this.getParentId()(node);
152
+ while (currentParentId !== null && currentParentId !== undefined) {
153
+ const parent = list.find((n) => this.getId()(n) === currentParentId);
154
+ if (!parent)
155
+ break;
156
+ depth++;
157
+ currentParentId = this.getParentId()(parent);
158
+ }
159
+ return depth;
160
+ }
161
+ getNodeIcon(node) {
162
+ const customIcon = this.getIcon()(node);
163
+ if (customIcon)
164
+ return customIcon;
165
+ if (this.isFolder()(node)) {
166
+ const open = this.getIsOpen()(node);
167
+ return open ? this.openIconName() : this.closedIconName();
168
+ }
169
+ return this.itemIconName() || 'file';
170
+ }
171
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ShipTree, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
172
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.0", type: ShipTree, isStandalone: true, selector: "sh-tree", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, sortableManager: { classPropertyName: "sortableManager", publicName: "sortableManager", isSignal: true, isRequired: false, transformFunction: null }, selectedId: { classPropertyName: "selectedId", publicName: "selectedId", isSignal: true, isRequired: false, transformFunction: null }, getId: { classPropertyName: "getId", publicName: "getId", isSignal: true, isRequired: false, transformFunction: null }, getName: { classPropertyName: "getName", publicName: "getName", isSignal: true, isRequired: false, transformFunction: null }, getParentId: { classPropertyName: "getParentId", publicName: "getParentId", isSignal: true, isRequired: false, transformFunction: null }, isFolder: { classPropertyName: "isFolder", publicName: "isFolder", isSignal: true, isRequired: false, transformFunction: null }, getIsOpen: { classPropertyName: "getIsOpen", publicName: "getIsOpen", isSignal: true, isRequired: false, transformFunction: null }, setIsOpen: { classPropertyName: "setIsOpen", publicName: "setIsOpen", isSignal: true, isRequired: false, transformFunction: null }, getIcon: { classPropertyName: "getIcon", publicName: "getIcon", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { items: "itemsChange", selectedId: "selectedIdChange", nodeClick: "nodeClick", nodeToggle: "nodeToggle" }, host: { properties: { "class.sh-tree": "true" } }, queries: [{ propertyName: "openIconDir", first: true, predicate: ShipTreeOpenIcon, descendants: true, isSignal: true }, { propertyName: "closedIconDir", first: true, predicate: ShipTreeClosedIcon, descendants: true, isSignal: true }, { propertyName: "itemIconDir", first: true, predicate: ShipTreeItemIcon, descendants: true, isSignal: true }, { propertyName: "nodeTemplate", first: true, predicate: ["nodeTemplate"], descendants: true, isSignal: true }, { propertyName: "dirTemplate", first: true, predicate: ["dirTemplate"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (sortableManager()) {\n <div\n class=\"sh-tree-container\"\n [shSortable]=\"sortableManager()\"\n [treeItems]=\"visibleNodes()\"\n sortingMode=\"tree\"\n >\n @for (node of visibleNodes(); track getId()(node)) {\n <div\n class=\"sh-tree-node\"\n [class.is-folder]=\"isFolder()(node)\"\n [class.is-expanded]=\"getIsOpen()(node)\"\n [class.is-selected]=\"selectedId() === getId()(node)\"\n draggable=\"true\"\n [attr.sortable-dir]=\"isFolder()(node) ? 'true' : null\"\n [style.--tree-depth]=\"getNodeDepth(node)\"\n (click)=\"handleNodeClick(node, $event)\"\n >\n <!-- Indent guides -->\n @for (i of getDepthArray(getNodeDepth(node)); track i) {\n <div class=\"indent-guide\" [style.--guide-index]=\"i\"></div>\n }\n\n <!-- Folder Caret -->\n @if (isFolder()(node) && !dirTemplate()) {\n <span class=\"caret-container\">\n <button class=\"caret-btn\" (click)=\"toggleNode(node, $event)\" type=\"button\">\n @if (getIsOpen()(node)) {\n <sh-icon size=\"small\">caret-down</sh-icon>\n } @else {\n <sh-icon size=\"small\">caret-right</sh-icon>\n }\n </button>\n </span>\n }\n\n <!-- Node Icon -->\n @if (getNodeIcon(node)) {\n @if (getNodeIcon(node) === 'file') {\n <sh-icon class=\"node-icon\" [class.folder]=\"isFolder()(node)\" size=\"small\">file</sh-icon>\n } @else if (getNodeIcon(node) === 'folder') {\n <sh-icon class=\"node-icon\" [class.folder]=\"isFolder()(node)\" size=\"small\">folder</sh-icon>\n } @else if (getNodeIcon(node) === 'folder-open') {\n <sh-icon class=\"node-icon\" [class.folder]=\"isFolder()(node)\" size=\"small\">folder-open</sh-icon>\n } @else {\n <sh-icon class=\"node-icon\" [class.folder]=\"isFolder()(node)\" size=\"small\">\n {{ getNodeIcon(node) }}\n </sh-icon>\n }\n }\n\n <!-- Node Content -->\n <div class=\"node-content\">\n <ng-container *ngTemplateOutlet=\"isFolder()(node) && dirTemplate() ? dirTemplate() : (nodeTemplate() || defaultTemplate); context: { $implicit: node }\"></ng-container>\n </div>\n </div>\n } @empty {\n <div class=\"empty-state\">\n <ng-content select=\"[emptyState]\">\n No items in tree\n </ng-content>\n </div>\n }\n </div>\n} @else {\n <div class=\"sh-tree-container\">\n @for (node of visibleNodes(); track getId()(node)) {\n <div\n class=\"sh-tree-node\"\n [class.is-folder]=\"isFolder()(node)\"\n [class.is-expanded]=\"getIsOpen()(node)\"\n [class.is-selected]=\"selectedId() === getId()(node)\"\n [style.--tree-depth]=\"getNodeDepth(node)\"\n (click)=\"handleNodeClick(node, $event)\"\n >\n <!-- Indent guides -->\n @for (i of getDepthArray(getNodeDepth(node)); track i) {\n <div class=\"indent-guide\" [style.--guide-index]=\"i\"></div>\n }\n\n <!-- Folder Caret -->\n @if (isFolder()(node) && !dirTemplate()) {\n <span class=\"caret-container\">\n <button class=\"caret-btn\" (click)=\"toggleNode(node, $event)\" type=\"button\">\n @if (getIsOpen()(node)) {\n <sh-icon size=\"small\">caret-down</sh-icon>\n } @else {\n <sh-icon size=\"small\">caret-right</sh-icon>\n }\n </button>\n </span>\n }\n\n <!-- Node Icon -->\n @if (getNodeIcon(node)) {\n @if (getNodeIcon(node) === 'file') {\n <sh-icon class=\"node-icon\" [class.folder]=\"isFolder()(node)\" size=\"small\">file</sh-icon>\n } @else if (getNodeIcon(node) === 'folder') {\n <sh-icon class=\"node-icon\" [class.folder]=\"isFolder()(node)\" size=\"small\">folder</sh-icon>\n } @else if (getNodeIcon(node) === 'folder-open') {\n <sh-icon class=\"node-icon\" [class.folder]=\"isFolder()(node)\" size=\"small\">folder-open</sh-icon>\n } @else {\n <sh-icon class=\"node-icon\" [class.folder]=\"isFolder()(node)\" size=\"small\">\n {{ getNodeIcon(node) }}\n </sh-icon>\n }\n }\n\n <!-- Node Content -->\n <div class=\"node-content\">\n <ng-container *ngTemplateOutlet=\"isFolder()(node) && dirTemplate() ? dirTemplate() : (nodeTemplate() || defaultTemplate); context: { $implicit: node }\"></ng-container>\n </div>\n </div>\n } @empty {\n <div class=\"empty-state\">\n <ng-content select=\"[emptyState]\">\n No items in tree\n </ng-content>\n </div>\n }\n </div>\n}\n\n<ng-template #defaultTemplate let-node>\n <span class=\"node-name\">{{ getName()(node) }}</span>\n</ng-template>\n", styles: [".sh-sortable{position:relative}.sh-sortable.dragging [draggable],.sh-sortable.dragging .sortable-ghost{cursor:grabbing;transition:transform 40ms linear}.sh-sortable [draggable]{transform:translate(0);transition:none;z-index:2;-webkit-user-select:none;user-select:none}.sh-sortable:not(.sh-sortable-tree) [draggable]{background:var(--base-1)}.sh-sortable:not(.sh-sortable-tree) [draggable]:not(:has([sort-handle])){cursor:grab}.sh-sortable .sortable-ghost{opacity:.5;z-index:10}.sh-sortable.item-dragged-out .sortable-ghost{display:none}.sh-sortable [sort-handle]{cursor:grab}.sh-sortable.sh-sortable-tree [draggable]{transform:none!important;transition:none!important}.sh-sortable.sh-sortable-tree .drop-inside{background:var(--primary-3)!important;outline:2px solid var(--primary-8)!important;outline-offset:-2px;border-radius:var(--shape-1, 4px)}.sh-sortable.sh-sortable-tree .drop-inside .node-icon,.sh-sortable.sh-sortable-tree .drop-inside sh-tree-node .sh-tree-node-left sh-icon{color:var(--primary-8)!important}.sh-sortable.sh-sortable-tree .drop-before{position:relative;z-index:20}.sh-sortable.sh-sortable-tree .drop-before:before{content:\"\";position:absolute;top:-2px;left:calc(var(--tree-padding-left, 12px) + var(--tree-depth, 0) * var(--tree-indent-step, 12px));right:var(--tree-padding-right, 6px);height:6px;background-image:radial-gradient(circle at 3px 3px,var(--primary-8) 3px,transparent 3px),linear-gradient(to right,var(--primary-8),var(--primary-8));background-repeat:no-repeat,no-repeat;background-position:left center,6px center;background-size:6px 6px,100% 2px;z-index:10;pointer-events:none}.sh-sortable.sh-sortable-tree .drop-after{position:relative;z-index:20}.sh-sortable.sh-sortable-tree .drop-after:after{content:\"\";position:absolute;bottom:-2px;left:calc(var(--tree-padding-left, 12px) + var(--tree-depth, 0) * var(--tree-indent-step, 12px));right:var(--tree-padding-right, 6px);height:6px;background-image:radial-gradient(circle at 3px 3px,var(--primary-8) 3px,transparent 3px),linear-gradient(to right,var(--primary-8),var(--primary-8));background-repeat:no-repeat,no-repeat;background-position:left center,6px center;background-size:6px 6px,100% 2px;z-index:10;pointer-events:none}sh-tree{--tree-bg: var(--base-2);--tree-bc: var(--base-3);--tree-color: var(--base-12);--tree-hover-bg: var(--base-3);--tree-active-bg: var(--base-4);--tree-selected-bg: var(--base-4);--tree-guide-color: var(--base-4);--tree-caret-color: var(--base-9);--tree-caret-hover-color: var(--base-12);--tree-icon-color: var(--base-9);--tree-icon-folder-color: var(--primary-8);--tree-indent-step: .75rem;--tree-padding-left: .75rem;--tree-padding-right: .375rem;display:flex;flex-direction:column;width:100%;background:var(--tree-bg);border:1px solid var(--tree-bc);border-radius:var(--shape-2, .5rem);overflow:hidden;font-family:var(--font-family, sans-serif);box-shadow:var(--box-shadow-10)}sh-tree .sh-tree-container{flex:1;padding:.5rem 0;overflow-y:auto;position:relative}sh-tree .sh-tree-node{display:flex;align-items:center;height:32px;position:relative;cursor:pointer;-webkit-user-select:none;user-select:none;color:var(--tree-color);font:var(--paragraph-20, 14px sans-serif);gap:6px;transition:background .12s ease;padding-left:calc(var(--tree-padding-left) + var(--tree-depth, 0) * var(--tree-indent-step));padding-right:var(--tree-padding-right);background:var(--tree-bg)}sh-tree .sh-tree-node:hover{background:var(--tree-hover-bg)}sh-tree .sh-tree-node.is-selected{background:var(--tree-selected-bg)}sh-tree .sh-tree-node.sortable-ghost{opacity:.4;background:var(--tree-active-bg)}sh-tree .sh-tree-node .indent-guide{position:absolute;top:0;bottom:0;width:1px;border-left:1px solid var(--tree-guide-color);opacity:.5;left:calc(var(--tree-padding-left) + var(--tree-indent-step) / 2 + var(--guide-index, 0) * var(--tree-indent-step))}sh-tree .sh-tree-node .caret-container{display:flex;align-items:center;justify-content:center;width:var(--tree-indent-step);height:16px;z-index:2}sh-tree .sh-tree-node .caret-container:has(+.node-icon){position:absolute;opacity:0;z-index:3;width:var(--tree-indent-step)}sh-tree .sh-tree-node .caret-container .caret-btn{background:none;border:none;padding:0;cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--tree-caret-color);border-radius:var(--shape-1, 4px);width:100%;height:100%;position:relative;transition:background .15s ease,color .15s ease}sh-tree .sh-tree-node .caret-container .caret-btn:after{content:\"\";position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:24px;height:24px;z-index:1}sh-tree .sh-tree-node .caret-container .caret-btn:hover{color:var(--tree-caret-hover-color)}sh-tree .sh-tree-node .node-icon{color:var(--tree-icon-color);display:inline-flex;align-items:center;justify-content:center;width:var(--tree-indent-step)}sh-tree .sh-tree-node .node-icon.folder{color:var(--tree-icon-folder-color)}sh-tree .sh-tree-node .node-icon:has(~.node-content sh-tree-node sh-icon){display:none}sh-tree .sh-tree-node.is-folder sh-tree-node .sh-tree-node-left sh-icon{color:var(--tree-icon-folder-color)}sh-tree .sh-tree-node:not(.is-folder) sh-tree-node .sh-tree-node-left sh-icon{color:var(--tree-icon-color)}sh-tree .sh-tree-node .node-content{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;z-index:2;display:flex;align-items:center}sh-tree .sh-tree-node .node-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}sh-tree sh-tree-node{display:flex;align-items:center;width:100%;gap:.5rem;justify-content:space-between;min-width:0}sh-tree sh-tree-node .sh-tree-node-left{display:flex;align-items:center;gap:.25rem;padding-left:.25rem;overflow:hidden;min-width:0;flex:1}sh-tree sh-tree-node .sh-tree-node-left sh-icon{display:inline-flex;align-items:center;justify-content:center;width:var(--tree-indent-step)}sh-tree sh-tree-node .sh-tree-node-label{display:inline-block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}sh-tree sh-tree-node .sh-tree-node-label span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}sh-tree sh-tree-node .sh-tree-node-actions,sh-tree sh-tree-node sh-tree-node-actions{display:flex;align-items:center;gap:.5rem}sh-tree sh-tree-node .status-badge{font-size:10px;padding:2px 6px;border-radius:var(--shape-1, 4px);text-transform:uppercase;font-weight:600}sh-tree sh-tree-node .status-badge.success{background:var(--success-3, #e6f4ea);color:var(--success-8, #137333)}sh-tree sh-tree-node .status-badge.warning{background:var(--warning-3, #fef7e0);color:var(--warning-8, #b06000)}sh-tree sh-tree-node .status-badge.error{background:var(--error-3, #fce8e6);color:var(--error-8, #c5221f)}sh-tree sh-tree-node .delete-btn{background:none;border:none;padding:2px;cursor:pointer;color:var(--base-9);border-radius:var(--shape-1, 4px);display:flex;opacity:0;transition:opacity .15s ease;align-items:center;justify-content:center}sh-tree sh-tree-node .delete-btn:hover{background:var(--base-4);color:var(--error-8, #c5221f)}sh-tree .sh-tree-node:hover .delete-btn{opacity:1}sh-tree .empty-state{padding:32px;text-align:center;color:var(--tree-caret-color);font:var(--paragraph-20, 14px sans-serif)}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ShipIcon, selector: "sh-icon", inputs: ["color", "size"] }, { kind: "directive", type: ShipSortable, selector: "[shSortable]", inputs: ["shSortable", "sortableGroup", "sortingMode", "treeItems"], outputs: ["treeItemsChange", "sortDrop", "afterDrop", "crossDrop", "treeDrop"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
173
+ }
174
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ShipTree, decorators: [{
175
+ type: Component,
176
+ args: [{ selector: 'sh-tree', standalone: true, imports: [NgTemplateOutlet, ShipIcon, ShipSortable], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: {
177
+ '[class.sh-tree]': 'true',
178
+ }, template: "@if (sortableManager()) {\n <div\n class=\"sh-tree-container\"\n [shSortable]=\"sortableManager()\"\n [treeItems]=\"visibleNodes()\"\n sortingMode=\"tree\"\n >\n @for (node of visibleNodes(); track getId()(node)) {\n <div\n class=\"sh-tree-node\"\n [class.is-folder]=\"isFolder()(node)\"\n [class.is-expanded]=\"getIsOpen()(node)\"\n [class.is-selected]=\"selectedId() === getId()(node)\"\n draggable=\"true\"\n [attr.sortable-dir]=\"isFolder()(node) ? 'true' : null\"\n [style.--tree-depth]=\"getNodeDepth(node)\"\n (click)=\"handleNodeClick(node, $event)\"\n >\n <!-- Indent guides -->\n @for (i of getDepthArray(getNodeDepth(node)); track i) {\n <div class=\"indent-guide\" [style.--guide-index]=\"i\"></div>\n }\n\n <!-- Folder Caret -->\n @if (isFolder()(node) && !dirTemplate()) {\n <span class=\"caret-container\">\n <button class=\"caret-btn\" (click)=\"toggleNode(node, $event)\" type=\"button\">\n @if (getIsOpen()(node)) {\n <sh-icon size=\"small\">caret-down</sh-icon>\n } @else {\n <sh-icon size=\"small\">caret-right</sh-icon>\n }\n </button>\n </span>\n }\n\n <!-- Node Icon -->\n @if (getNodeIcon(node)) {\n @if (getNodeIcon(node) === 'file') {\n <sh-icon class=\"node-icon\" [class.folder]=\"isFolder()(node)\" size=\"small\">file</sh-icon>\n } @else if (getNodeIcon(node) === 'folder') {\n <sh-icon class=\"node-icon\" [class.folder]=\"isFolder()(node)\" size=\"small\">folder</sh-icon>\n } @else if (getNodeIcon(node) === 'folder-open') {\n <sh-icon class=\"node-icon\" [class.folder]=\"isFolder()(node)\" size=\"small\">folder-open</sh-icon>\n } @else {\n <sh-icon class=\"node-icon\" [class.folder]=\"isFolder()(node)\" size=\"small\">\n {{ getNodeIcon(node) }}\n </sh-icon>\n }\n }\n\n <!-- Node Content -->\n <div class=\"node-content\">\n <ng-container *ngTemplateOutlet=\"isFolder()(node) && dirTemplate() ? dirTemplate() : (nodeTemplate() || defaultTemplate); context: { $implicit: node }\"></ng-container>\n </div>\n </div>\n } @empty {\n <div class=\"empty-state\">\n <ng-content select=\"[emptyState]\">\n No items in tree\n </ng-content>\n </div>\n }\n </div>\n} @else {\n <div class=\"sh-tree-container\">\n @for (node of visibleNodes(); track getId()(node)) {\n <div\n class=\"sh-tree-node\"\n [class.is-folder]=\"isFolder()(node)\"\n [class.is-expanded]=\"getIsOpen()(node)\"\n [class.is-selected]=\"selectedId() === getId()(node)\"\n [style.--tree-depth]=\"getNodeDepth(node)\"\n (click)=\"handleNodeClick(node, $event)\"\n >\n <!-- Indent guides -->\n @for (i of getDepthArray(getNodeDepth(node)); track i) {\n <div class=\"indent-guide\" [style.--guide-index]=\"i\"></div>\n }\n\n <!-- Folder Caret -->\n @if (isFolder()(node) && !dirTemplate()) {\n <span class=\"caret-container\">\n <button class=\"caret-btn\" (click)=\"toggleNode(node, $event)\" type=\"button\">\n @if (getIsOpen()(node)) {\n <sh-icon size=\"small\">caret-down</sh-icon>\n } @else {\n <sh-icon size=\"small\">caret-right</sh-icon>\n }\n </button>\n </span>\n }\n\n <!-- Node Icon -->\n @if (getNodeIcon(node)) {\n @if (getNodeIcon(node) === 'file') {\n <sh-icon class=\"node-icon\" [class.folder]=\"isFolder()(node)\" size=\"small\">file</sh-icon>\n } @else if (getNodeIcon(node) === 'folder') {\n <sh-icon class=\"node-icon\" [class.folder]=\"isFolder()(node)\" size=\"small\">folder</sh-icon>\n } @else if (getNodeIcon(node) === 'folder-open') {\n <sh-icon class=\"node-icon\" [class.folder]=\"isFolder()(node)\" size=\"small\">folder-open</sh-icon>\n } @else {\n <sh-icon class=\"node-icon\" [class.folder]=\"isFolder()(node)\" size=\"small\">\n {{ getNodeIcon(node) }}\n </sh-icon>\n }\n }\n\n <!-- Node Content -->\n <div class=\"node-content\">\n <ng-container *ngTemplateOutlet=\"isFolder()(node) && dirTemplate() ? dirTemplate() : (nodeTemplate() || defaultTemplate); context: { $implicit: node }\"></ng-container>\n </div>\n </div>\n } @empty {\n <div class=\"empty-state\">\n <ng-content select=\"[emptyState]\">\n No items in tree\n </ng-content>\n </div>\n }\n </div>\n}\n\n<ng-template #defaultTemplate let-node>\n <span class=\"node-name\">{{ getName()(node) }}</span>\n</ng-template>\n", styles: [".sh-sortable{position:relative}.sh-sortable.dragging [draggable],.sh-sortable.dragging .sortable-ghost{cursor:grabbing;transition:transform 40ms linear}.sh-sortable [draggable]{transform:translate(0);transition:none;z-index:2;-webkit-user-select:none;user-select:none}.sh-sortable:not(.sh-sortable-tree) [draggable]{background:var(--base-1)}.sh-sortable:not(.sh-sortable-tree) [draggable]:not(:has([sort-handle])){cursor:grab}.sh-sortable .sortable-ghost{opacity:.5;z-index:10}.sh-sortable.item-dragged-out .sortable-ghost{display:none}.sh-sortable [sort-handle]{cursor:grab}.sh-sortable.sh-sortable-tree [draggable]{transform:none!important;transition:none!important}.sh-sortable.sh-sortable-tree .drop-inside{background:var(--primary-3)!important;outline:2px solid var(--primary-8)!important;outline-offset:-2px;border-radius:var(--shape-1, 4px)}.sh-sortable.sh-sortable-tree .drop-inside .node-icon,.sh-sortable.sh-sortable-tree .drop-inside sh-tree-node .sh-tree-node-left sh-icon{color:var(--primary-8)!important}.sh-sortable.sh-sortable-tree .drop-before{position:relative;z-index:20}.sh-sortable.sh-sortable-tree .drop-before:before{content:\"\";position:absolute;top:-2px;left:calc(var(--tree-padding-left, 12px) + var(--tree-depth, 0) * var(--tree-indent-step, 12px));right:var(--tree-padding-right, 6px);height:6px;background-image:radial-gradient(circle at 3px 3px,var(--primary-8) 3px,transparent 3px),linear-gradient(to right,var(--primary-8),var(--primary-8));background-repeat:no-repeat,no-repeat;background-position:left center,6px center;background-size:6px 6px,100% 2px;z-index:10;pointer-events:none}.sh-sortable.sh-sortable-tree .drop-after{position:relative;z-index:20}.sh-sortable.sh-sortable-tree .drop-after:after{content:\"\";position:absolute;bottom:-2px;left:calc(var(--tree-padding-left, 12px) + var(--tree-depth, 0) * var(--tree-indent-step, 12px));right:var(--tree-padding-right, 6px);height:6px;background-image:radial-gradient(circle at 3px 3px,var(--primary-8) 3px,transparent 3px),linear-gradient(to right,var(--primary-8),var(--primary-8));background-repeat:no-repeat,no-repeat;background-position:left center,6px center;background-size:6px 6px,100% 2px;z-index:10;pointer-events:none}sh-tree{--tree-bg: var(--base-2);--tree-bc: var(--base-3);--tree-color: var(--base-12);--tree-hover-bg: var(--base-3);--tree-active-bg: var(--base-4);--tree-selected-bg: var(--base-4);--tree-guide-color: var(--base-4);--tree-caret-color: var(--base-9);--tree-caret-hover-color: var(--base-12);--tree-icon-color: var(--base-9);--tree-icon-folder-color: var(--primary-8);--tree-indent-step: .75rem;--tree-padding-left: .75rem;--tree-padding-right: .375rem;display:flex;flex-direction:column;width:100%;background:var(--tree-bg);border:1px solid var(--tree-bc);border-radius:var(--shape-2, .5rem);overflow:hidden;font-family:var(--font-family, sans-serif);box-shadow:var(--box-shadow-10)}sh-tree .sh-tree-container{flex:1;padding:.5rem 0;overflow-y:auto;position:relative}sh-tree .sh-tree-node{display:flex;align-items:center;height:32px;position:relative;cursor:pointer;-webkit-user-select:none;user-select:none;color:var(--tree-color);font:var(--paragraph-20, 14px sans-serif);gap:6px;transition:background .12s ease;padding-left:calc(var(--tree-padding-left) + var(--tree-depth, 0) * var(--tree-indent-step));padding-right:var(--tree-padding-right);background:var(--tree-bg)}sh-tree .sh-tree-node:hover{background:var(--tree-hover-bg)}sh-tree .sh-tree-node.is-selected{background:var(--tree-selected-bg)}sh-tree .sh-tree-node.sortable-ghost{opacity:.4;background:var(--tree-active-bg)}sh-tree .sh-tree-node .indent-guide{position:absolute;top:0;bottom:0;width:1px;border-left:1px solid var(--tree-guide-color);opacity:.5;left:calc(var(--tree-padding-left) + var(--tree-indent-step) / 2 + var(--guide-index, 0) * var(--tree-indent-step))}sh-tree .sh-tree-node .caret-container{display:flex;align-items:center;justify-content:center;width:var(--tree-indent-step);height:16px;z-index:2}sh-tree .sh-tree-node .caret-container:has(+.node-icon){position:absolute;opacity:0;z-index:3;width:var(--tree-indent-step)}sh-tree .sh-tree-node .caret-container .caret-btn{background:none;border:none;padding:0;cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--tree-caret-color);border-radius:var(--shape-1, 4px);width:100%;height:100%;position:relative;transition:background .15s ease,color .15s ease}sh-tree .sh-tree-node .caret-container .caret-btn:after{content:\"\";position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:24px;height:24px;z-index:1}sh-tree .sh-tree-node .caret-container .caret-btn:hover{color:var(--tree-caret-hover-color)}sh-tree .sh-tree-node .node-icon{color:var(--tree-icon-color);display:inline-flex;align-items:center;justify-content:center;width:var(--tree-indent-step)}sh-tree .sh-tree-node .node-icon.folder{color:var(--tree-icon-folder-color)}sh-tree .sh-tree-node .node-icon:has(~.node-content sh-tree-node sh-icon){display:none}sh-tree .sh-tree-node.is-folder sh-tree-node .sh-tree-node-left sh-icon{color:var(--tree-icon-folder-color)}sh-tree .sh-tree-node:not(.is-folder) sh-tree-node .sh-tree-node-left sh-icon{color:var(--tree-icon-color)}sh-tree .sh-tree-node .node-content{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;z-index:2;display:flex;align-items:center}sh-tree .sh-tree-node .node-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}sh-tree sh-tree-node{display:flex;align-items:center;width:100%;gap:.5rem;justify-content:space-between;min-width:0}sh-tree sh-tree-node .sh-tree-node-left{display:flex;align-items:center;gap:.25rem;padding-left:.25rem;overflow:hidden;min-width:0;flex:1}sh-tree sh-tree-node .sh-tree-node-left sh-icon{display:inline-flex;align-items:center;justify-content:center;width:var(--tree-indent-step)}sh-tree sh-tree-node .sh-tree-node-label{display:inline-block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}sh-tree sh-tree-node .sh-tree-node-label span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}sh-tree sh-tree-node .sh-tree-node-actions,sh-tree sh-tree-node sh-tree-node-actions{display:flex;align-items:center;gap:.5rem}sh-tree sh-tree-node .status-badge{font-size:10px;padding:2px 6px;border-radius:var(--shape-1, 4px);text-transform:uppercase;font-weight:600}sh-tree sh-tree-node .status-badge.success{background:var(--success-3, #e6f4ea);color:var(--success-8, #137333)}sh-tree sh-tree-node .status-badge.warning{background:var(--warning-3, #fef7e0);color:var(--warning-8, #b06000)}sh-tree sh-tree-node .status-badge.error{background:var(--error-3, #fce8e6);color:var(--error-8, #c5221f)}sh-tree sh-tree-node .delete-btn{background:none;border:none;padding:2px;cursor:pointer;color:var(--base-9);border-radius:var(--shape-1, 4px);display:flex;opacity:0;transition:opacity .15s ease;align-items:center;justify-content:center}sh-tree sh-tree-node .delete-btn:hover{background:var(--base-4);color:var(--error-8, #c5221f)}sh-tree .sh-tree-node:hover .delete-btn{opacity:1}sh-tree .empty-state{padding:32px;text-align:center;color:var(--tree-caret-color);font:var(--paragraph-20, 14px sans-serif)}\n"] }]
179
+ }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }, { type: i0.Output, args: ["itemsChange"] }], sortableManager: [{ type: i0.Input, args: [{ isSignal: true, alias: "sortableManager", required: false }] }], selectedId: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedId", required: false }] }, { type: i0.Output, args: ["selectedIdChange"] }], getId: [{ type: i0.Input, args: [{ isSignal: true, alias: "getId", required: false }] }], getName: [{ type: i0.Input, args: [{ isSignal: true, alias: "getName", required: false }] }], getParentId: [{ type: i0.Input, args: [{ isSignal: true, alias: "getParentId", required: false }] }], isFolder: [{ type: i0.Input, args: [{ isSignal: true, alias: "isFolder", required: false }] }], getIsOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "getIsOpen", required: false }] }], setIsOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "setIsOpen", required: false }] }], getIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "getIcon", required: false }] }], openIconDir: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ShipTreeOpenIcon), { isSignal: true }] }], closedIconDir: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ShipTreeClosedIcon), { isSignal: true }] }], itemIconDir: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ShipTreeItemIcon), { isSignal: true }] }], nodeClick: [{ type: i0.Output, args: ["nodeClick"] }], nodeToggle: [{ type: i0.Output, args: ["nodeToggle"] }], nodeTemplate: [{ type: i0.ContentChild, args: ['nodeTemplate', { isSignal: true }] }], dirTemplate: [{ type: i0.ContentChild, args: ['dirTemplate', { isSignal: true }] }] } });
180
+ class ShipTreeNode {
181
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ShipTreeNode, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
182
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.0", type: ShipTreeNode, isStandalone: true, selector: "sh-tree-node", host: { properties: { "class.sh-tree-node-layout": "true" } }, ngImport: i0, template: `
183
+ <div class="sh-tree-node-left">
184
+ <ng-content select="sh-icon" />
185
+ <span class="sh-tree-node-label">
186
+ <ng-content />
187
+ </span>
188
+ </div>
189
+ <div class="sh-tree-node-actions">
190
+ <ng-content select="[actions], sh-tree-node-actions" />
191
+ </div>
192
+ `, isInline: true }); }
193
+ }
194
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ShipTreeNode, decorators: [{
195
+ type: Component,
196
+ args: [{
197
+ selector: 'sh-tree-node',
198
+ template: `
199
+ <div class="sh-tree-node-left">
200
+ <ng-content select="sh-icon" />
201
+ <span class="sh-tree-node-label">
202
+ <ng-content />
203
+ </span>
204
+ </div>
205
+ <div class="sh-tree-node-actions">
206
+ <ng-content select="[actions], sh-tree-node-actions" />
207
+ </div>
208
+ `,
209
+ host: {
210
+ '[class.sh-tree-node-layout]': 'true',
211
+ },
212
+ }]
213
+ }] });
214
+ class ShipTreeNodeActions {
215
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ShipTreeNodeActions, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
216
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.0", type: ShipTreeNodeActions, isStandalone: true, selector: "sh-tree-node-actions", host: { properties: { "class.sh-tree-node-actions": "true" } }, ngImport: i0, template: `
217
+ <ng-content />
218
+ `, isInline: true }); }
219
+ }
220
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.0", ngImport: i0, type: ShipTreeNodeActions, decorators: [{
221
+ type: Component,
222
+ args: [{
223
+ selector: 'sh-tree-node-actions',
224
+ template: `
225
+ <ng-content />
226
+ `,
227
+ host: {
228
+ '[class.sh-tree-node-actions]': 'true',
229
+ },
230
+ }]
231
+ }] });
232
+
233
+ /**
234
+ * Generated bundle index. Do not edit.
235
+ */
236
+
237
+ export { ShipTree, ShipTreeClosedIcon, ShipTreeItemIcon, ShipTreeNode, ShipTreeNodeActions, ShipTreeOpenIcon };
238
+ //# sourceMappingURL=ship-ui-core-ship-tree.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ship-ui-core-ship-tree.mjs","sources":["../../../projects/ship-ui/ship-tree/ship-tree.ts","../../../projects/ship-ui/ship-tree/ship-tree.html","../../../projects/ship-ui/ship-tree/ship-ui-core-ship-tree.ts"],"sourcesContent":["import { NgTemplateOutlet } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n computed,\n contentChild,\n Directive,\n ElementRef,\n inject,\n input,\n model,\n output,\n TemplateRef,\n ViewEncapsulation,\n} from '@angular/core';\nimport { ShipIcon } from '@ship-ui/core/ship-icon';\nimport { ShipSortable } from '@ship-ui/core/ship-sortable';\n\n@Directive({ selector: 'sh-icon[openIcon]', standalone: true })\nexport class ShipTreeOpenIcon {\n readonly el = inject(ElementRef<HTMLElement>);\n}\n\n@Directive({ selector: 'sh-icon[closedIcon]', standalone: true })\nexport class ShipTreeClosedIcon {\n readonly el = inject(ElementRef<HTMLElement>);\n}\n\n@Directive({ selector: 'sh-icon[itemIcon]', standalone: true })\nexport class ShipTreeItemIcon {\n readonly el = inject(ElementRef<HTMLElement>);\n}\n\n@Component({\n selector: 'sh-tree',\n standalone: true,\n imports: [NgTemplateOutlet, ShipIcon, ShipSortable],\n templateUrl: './ship-tree.html',\n styleUrl: './ship-tree.scss',\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[class.sh-tree]': 'true',\n },\n})\nexport class ShipTree {\n /** The flat list of all tree nodes. */\n readonly items = model<any[]>([]);\n\n /** Optional Writable/Readable WritableSignal manager created by createTreeSortableManager. */\n readonly sortableManager = input<any>(null);\n\n /** Currently selected node ID. */\n readonly selectedId = model<string | null>(null);\n\n // Mappings\n readonly getId = input<(item: any) => string>((item) => item.id);\n readonly getName = input<(item: any) => string>((item) => item.name);\n readonly getParentId = input<(item: any) => string | null>((item) => item.parentId);\n readonly isFolder = input<(item: any) => boolean>((item) => item.type === 'dir');\n readonly getIsOpen = input<(item: any) => boolean>((item) => !!item.isOpen);\n readonly setIsOpen = input<(item: any, isOpen: boolean) => void>((item, open) => {\n item.isOpen = open;\n });\n\n /** Function returning a custom icon name for a node. */\n readonly getIcon = input<(item: any) => string | null>(() => null);\n\n // Read projected icons via lightweight directives\n readonly openIconDir = contentChild(ShipTreeOpenIcon);\n readonly closedIconDir = contentChild(ShipTreeClosedIcon);\n readonly itemIconDir = contentChild(ShipTreeItemIcon);\n\n openIconName = computed(() => this.openIconDir()?.el.nativeElement.textContent?.trim() || null);\n closedIconName = computed(() => this.closedIconDir()?.el.nativeElement.textContent?.trim() || null);\n itemIconName = computed(() => this.itemIconDir()?.el.nativeElement.textContent?.trim() || null);\n\n // Outputs\n readonly nodeClick = output<any>();\n readonly nodeToggle = output<{ node: any; isOpen: boolean }>();\n\n readonly nodeTemplate = contentChild<TemplateRef<any>>('nodeTemplate');\n readonly dirTemplate = contentChild<TemplateRef<any>>('dirTemplate');\n\n /** Filter the full flat array down to only visible nodes (whose parents are all expanded). */\n visibleNodes = computed(() => {\n const manager = this.sortableManager();\n if (manager && typeof manager.visibleNodes === 'function') {\n return manager.visibleNodes();\n }\n\n const list = this.items();\n const visible: any[] = [];\n\n const isNodeVisible = (node: any): boolean => {\n let currentParentId = this.getParentId()(node);\n while (currentParentId !== null && currentParentId !== undefined) {\n const parent = list.find((n) => this.getId()(n) === currentParentId);\n if (!parent || !this.getIsOpen()(parent)) {\n return false;\n }\n currentParentId = this.getParentId()(parent);\n }\n return true;\n };\n\n for (const node of list) {\n const parentId = this.getParentId()(node);\n if (parentId === null || parentId === undefined || isNodeVisible(node)) {\n visible.push(node);\n }\n }\n return visible;\n });\n\n toggleNode(node: any, event: MouseEvent) {\n event.stopPropagation();\n const open = !this.getIsOpen()(node);\n\n this.items.update((list) =>\n list.map((item) => {\n if (this.getId()(item) === this.getId()(node)) {\n const updated = { ...item };\n this.setIsOpen()(updated, open);\n return updated;\n }\n return item;\n })\n );\n\n this.nodeToggle.emit({ node, isOpen: open });\n }\n\n handleNodeClick(node: any, event: MouseEvent) {\n const target = event.target as HTMLElement;\n const isIconClick = !!target.closest('sh-icon') && !target.closest('.sh-tree-node-actions') && !target.closest('sh-tree-node-actions') && !target.closest('.caret-container');\n\n if (isIconClick && this.isFolder()(node)) {\n this.toggleNode(node, event);\n } else {\n this.selectNode(node);\n }\n }\n\n selectNode(node: any) {\n this.selectedId.set(this.getId()(node));\n this.nodeClick.emit(node);\n }\n\n getDepthArray(depth: number): number[] {\n return Array.from({ length: depth }, (_, i) => i);\n }\n\n getNodeDepth(node: any): number {\n const list = this.items();\n let depth = 0;\n let currentParentId = this.getParentId()(node);\n while (currentParentId !== null && currentParentId !== undefined) {\n const parent = list.find((n) => this.getId()(n) === currentParentId);\n if (!parent) break;\n depth++;\n currentParentId = this.getParentId()(parent);\n }\n return depth;\n }\n\n getNodeIcon(node: any): string | null {\n const customIcon = this.getIcon()(node);\n if (customIcon) return customIcon;\n\n if (this.isFolder()(node)) {\n const open = this.getIsOpen()(node);\n return open ? this.openIconName() : this.closedIconName();\n }\n return this.itemIconName() || 'file';\n }\n}\n\n@Component({\n selector: 'sh-tree-node',\n template: `\n <div class=\"sh-tree-node-left\">\n <ng-content select=\"sh-icon\" />\n <span class=\"sh-tree-node-label\">\n <ng-content />\n </span>\n </div>\n <div class=\"sh-tree-node-actions\">\n <ng-content select=\"[actions], sh-tree-node-actions\" />\n </div>\n `,\n host: {\n '[class.sh-tree-node-layout]': 'true',\n },\n})\nexport class ShipTreeNode {}\n\n@Component({\n selector: 'sh-tree-node-actions',\n template: `\n <ng-content />\n `,\n host: {\n '[class.sh-tree-node-actions]': 'true',\n },\n})\nexport class ShipTreeNodeActions {}\n","@if (sortableManager()) {\n <div\n class=\"sh-tree-container\"\n [shSortable]=\"sortableManager()\"\n [treeItems]=\"visibleNodes()\"\n sortingMode=\"tree\"\n >\n @for (node of visibleNodes(); track getId()(node)) {\n <div\n class=\"sh-tree-node\"\n [class.is-folder]=\"isFolder()(node)\"\n [class.is-expanded]=\"getIsOpen()(node)\"\n [class.is-selected]=\"selectedId() === getId()(node)\"\n draggable=\"true\"\n [attr.sortable-dir]=\"isFolder()(node) ? 'true' : null\"\n [style.--tree-depth]=\"getNodeDepth(node)\"\n (click)=\"handleNodeClick(node, $event)\"\n >\n <!-- Indent guides -->\n @for (i of getDepthArray(getNodeDepth(node)); track i) {\n <div class=\"indent-guide\" [style.--guide-index]=\"i\"></div>\n }\n\n <!-- Folder Caret -->\n @if (isFolder()(node) && !dirTemplate()) {\n <span class=\"caret-container\">\n <button class=\"caret-btn\" (click)=\"toggleNode(node, $event)\" type=\"button\">\n @if (getIsOpen()(node)) {\n <sh-icon size=\"small\">caret-down</sh-icon>\n } @else {\n <sh-icon size=\"small\">caret-right</sh-icon>\n }\n </button>\n </span>\n }\n\n <!-- Node Icon -->\n @if (getNodeIcon(node)) {\n @if (getNodeIcon(node) === 'file') {\n <sh-icon class=\"node-icon\" [class.folder]=\"isFolder()(node)\" size=\"small\">file</sh-icon>\n } @else if (getNodeIcon(node) === 'folder') {\n <sh-icon class=\"node-icon\" [class.folder]=\"isFolder()(node)\" size=\"small\">folder</sh-icon>\n } @else if (getNodeIcon(node) === 'folder-open') {\n <sh-icon class=\"node-icon\" [class.folder]=\"isFolder()(node)\" size=\"small\">folder-open</sh-icon>\n } @else {\n <sh-icon class=\"node-icon\" [class.folder]=\"isFolder()(node)\" size=\"small\">\n {{ getNodeIcon(node) }}\n </sh-icon>\n }\n }\n\n <!-- Node Content -->\n <div class=\"node-content\">\n <ng-container *ngTemplateOutlet=\"isFolder()(node) && dirTemplate() ? dirTemplate() : (nodeTemplate() || defaultTemplate); context: { $implicit: node }\"></ng-container>\n </div>\n </div>\n } @empty {\n <div class=\"empty-state\">\n <ng-content select=\"[emptyState]\">\n No items in tree\n </ng-content>\n </div>\n }\n </div>\n} @else {\n <div class=\"sh-tree-container\">\n @for (node of visibleNodes(); track getId()(node)) {\n <div\n class=\"sh-tree-node\"\n [class.is-folder]=\"isFolder()(node)\"\n [class.is-expanded]=\"getIsOpen()(node)\"\n [class.is-selected]=\"selectedId() === getId()(node)\"\n [style.--tree-depth]=\"getNodeDepth(node)\"\n (click)=\"handleNodeClick(node, $event)\"\n >\n <!-- Indent guides -->\n @for (i of getDepthArray(getNodeDepth(node)); track i) {\n <div class=\"indent-guide\" [style.--guide-index]=\"i\"></div>\n }\n\n <!-- Folder Caret -->\n @if (isFolder()(node) && !dirTemplate()) {\n <span class=\"caret-container\">\n <button class=\"caret-btn\" (click)=\"toggleNode(node, $event)\" type=\"button\">\n @if (getIsOpen()(node)) {\n <sh-icon size=\"small\">caret-down</sh-icon>\n } @else {\n <sh-icon size=\"small\">caret-right</sh-icon>\n }\n </button>\n </span>\n }\n\n <!-- Node Icon -->\n @if (getNodeIcon(node)) {\n @if (getNodeIcon(node) === 'file') {\n <sh-icon class=\"node-icon\" [class.folder]=\"isFolder()(node)\" size=\"small\">file</sh-icon>\n } @else if (getNodeIcon(node) === 'folder') {\n <sh-icon class=\"node-icon\" [class.folder]=\"isFolder()(node)\" size=\"small\">folder</sh-icon>\n } @else if (getNodeIcon(node) === 'folder-open') {\n <sh-icon class=\"node-icon\" [class.folder]=\"isFolder()(node)\" size=\"small\">folder-open</sh-icon>\n } @else {\n <sh-icon class=\"node-icon\" [class.folder]=\"isFolder()(node)\" size=\"small\">\n {{ getNodeIcon(node) }}\n </sh-icon>\n }\n }\n\n <!-- Node Content -->\n <div class=\"node-content\">\n <ng-container *ngTemplateOutlet=\"isFolder()(node) && dirTemplate() ? dirTemplate() : (nodeTemplate() || defaultTemplate); context: { $implicit: node }\"></ng-container>\n </div>\n </div>\n } @empty {\n <div class=\"empty-state\">\n <ng-content select=\"[emptyState]\">\n No items in tree\n </ng-content>\n </div>\n }\n </div>\n}\n\n<ng-template #defaultTemplate let-node>\n <span class=\"node-name\">{{ getName()(node) }}</span>\n</ng-template>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;;MAmBa,gBAAgB,CAAA;AAD7B,IAAA,WAAA,GAAA;AAEW,QAAA,IAAA,CAAA,EAAE,GAAG,MAAM,EAAC,UAAuB,EAAC;AAC9C,IAAA;8GAFY,gBAAgB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;kGAAhB,gBAAgB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,mBAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA,CAAA;;2FAAhB,gBAAgB,EAAA,UAAA,EAAA,CAAA;kBAD5B,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA,EAAE,QAAQ,EAAE,mBAAmB,EAAE,UAAU,EAAE,IAAI,EAAE;;MAMjD,kBAAkB,CAAA;AAD/B,IAAA,WAAA,GAAA;AAEW,QAAA,IAAA,CAAA,EAAE,GAAG,MAAM,EAAC,UAAuB,EAAC;AAC9C,IAAA;8GAFY,kBAAkB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;kGAAlB,kBAAkB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,qBAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA,CAAA;;2FAAlB,kBAAkB,EAAA,UAAA,EAAA,CAAA;kBAD9B,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA,EAAE,QAAQ,EAAE,qBAAqB,EAAE,UAAU,EAAE,IAAI,EAAE;;MAMnD,gBAAgB,CAAA;AAD7B,IAAA,WAAA,GAAA;AAEW,QAAA,IAAA,CAAA,EAAE,GAAG,MAAM,EAAC,UAAuB,EAAC;AAC9C,IAAA;8GAFY,gBAAgB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;kGAAhB,gBAAgB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,mBAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA,CAAA;;2FAAhB,gBAAgB,EAAA,UAAA,EAAA,CAAA;kBAD5B,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA,EAAE,QAAQ,EAAE,mBAAmB,EAAE,UAAU,EAAE,IAAI,EAAE;;MAiBjD,QAAQ,CAAA;AAZrB,IAAA,WAAA,GAAA;;QAcW,IAAA,CAAA,KAAK,GAAG,KAAK,CAAQ,EAAE;kFAAC;;QAGxB,IAAA,CAAA,eAAe,GAAG,KAAK,CAAM,IAAI;4FAAC;;QAGlC,IAAA,CAAA,UAAU,GAAG,KAAK,CAAgB,IAAI;uFAAC;;QAGvC,IAAA,CAAA,KAAK,GAAG,KAAK,CAAwB,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE;kFAAC;QACvD,IAAA,CAAA,OAAO,GAAG,KAAK,CAAwB,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI;oFAAC;QAC3D,IAAA,CAAA,WAAW,GAAG,KAAK,CAA+B,CAAC,IAAI,KAAK,IAAI,CAAC,QAAQ;wFAAC;AAC1E,QAAA,IAAA,CAAA,QAAQ,GAAG,KAAK,CAAyB,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,KAAK;qFAAC;AACvE,QAAA,IAAA,CAAA,SAAS,GAAG,KAAK,CAAyB,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM;sFAAC;QAClE,IAAA,CAAA,SAAS,GAAG,KAAK,CAAuC,CAAC,IAAI,EAAE,IAAI,KAAI;AAC9E,YAAA,IAAI,CAAC,MAAM,GAAG,IAAI;QACpB,CAAC;sFAAC;;AAGO,QAAA,IAAA,CAAA,OAAO,GAAG,KAAK,CAA+B,MAAM,IAAI;oFAAC;;QAGzD,IAAA,CAAA,WAAW,GAAG,YAAY,CAAC,gBAAgB;wFAAC;QAC5C,IAAA,CAAA,aAAa,GAAG,YAAY,CAAC,kBAAkB;0FAAC;QAChD,IAAA,CAAA,WAAW,GAAG,YAAY,CAAC,gBAAgB;wFAAC;QAErD,IAAA,CAAA,YAAY,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,WAAW,EAAE,EAAE,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,IAAI;yFAAC;QAC/F,IAAA,CAAA,cAAc,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,aAAa,EAAE,EAAE,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,IAAI;2FAAC;QACnG,IAAA,CAAA,YAAY,GAAG,QAAQ,CAAC,MAAM,IAAI,CAAC,WAAW,EAAE,EAAE,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,IAAI;yFAAC;;QAGtF,IAAA,CAAA,SAAS,GAAG,MAAM,EAAO;QACzB,IAAA,CAAA,UAAU,GAAG,MAAM,EAAkC;QAErD,IAAA,CAAA,YAAY,GAAG,YAAY,CAAmB,cAAc;yFAAC;QAC7D,IAAA,CAAA,WAAW,GAAG,YAAY,CAAmB,aAAa;wFAAC;;AAGpE,QAAA,IAAA,CAAA,YAAY,GAAG,QAAQ,CAAC,MAAK;AAC3B,YAAA,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,EAAE;YACtC,IAAI,OAAO,IAAI,OAAO,OAAO,CAAC,YAAY,KAAK,UAAU,EAAE;AACzD,gBAAA,OAAO,OAAO,CAAC,YAAY,EAAE;YAC/B;AAEA,YAAA,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE;YACzB,MAAM,OAAO,GAAU,EAAE;AAEzB,YAAA,MAAM,aAAa,GAAG,CAAC,IAAS,KAAa;gBAC3C,IAAI,eAAe,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC;gBAC9C,OAAO,eAAe,KAAK,IAAI,IAAI,eAAe,KAAK,SAAS,EAAE;oBAChE,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,eAAe,CAAC;AACpE,oBAAA,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,EAAE;AACxC,wBAAA,OAAO,KAAK;oBACd;oBACA,eAAe,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC;gBAC9C;AACA,gBAAA,OAAO,IAAI;AACb,YAAA,CAAC;AAED,YAAA,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE;gBACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC;AACzC,gBAAA,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,SAAS,IAAI,aAAa,CAAC,IAAI,CAAC,EAAE;AACtE,oBAAA,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;gBACpB;YACF;AACA,YAAA,OAAO,OAAO;QAChB,CAAC;yFAAC;AA+DH,IAAA;IA7DC,UAAU,CAAC,IAAS,EAAE,KAAiB,EAAA;QACrC,KAAK,CAAC,eAAe,EAAE;QACvB,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC;AAEpC,QAAA,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,KACrB,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,KAAI;AAChB,YAAA,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE;AAC7C,gBAAA,MAAM,OAAO,GAAG,EAAE,GAAG,IAAI,EAAE;gBAC3B,IAAI,CAAC,SAAS,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC;AAC/B,gBAAA,OAAO,OAAO;YAChB;AACA,YAAA,OAAO,IAAI;QACb,CAAC,CAAC,CACH;AAED,QAAA,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC9C;IAEA,eAAe,CAAC,IAAS,EAAE,KAAiB,EAAA;AAC1C,QAAA,MAAM,MAAM,GAAG,KAAK,CAAC,MAAqB;AAC1C,QAAA,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,uBAAuB,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,sBAAsB,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAAC;QAE7K,IAAI,WAAW,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,EAAE;AACxC,YAAA,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC;QAC9B;aAAO;AACL,YAAA,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QACvB;IACF;AAEA,IAAA,UAAU,CAAC,IAAS,EAAA;AAClB,QAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC;AACvC,QAAA,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;IAC3B;AAEA,IAAA,aAAa,CAAC,KAAa,EAAA;AACzB,QAAA,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;IACnD;AAEA,IAAA,YAAY,CAAC,IAAS,EAAA;AACpB,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE;QACzB,IAAI,KAAK,GAAG,CAAC;QACb,IAAI,eAAe,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC;QAC9C,OAAO,eAAe,KAAK,IAAI,IAAI,eAAe,KAAK,SAAS,EAAE;YAChE,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,eAAe,CAAC;AACpE,YAAA,IAAI,CAAC,MAAM;gBAAE;AACb,YAAA,KAAK,EAAE;YACP,eAAe,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,MAAM,CAAC;QAC9C;AACA,QAAA,OAAO,KAAK;IACd;AAEA,IAAA,WAAW,CAAC,IAAS,EAAA;QACnB,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC;AACvC,QAAA,IAAI,UAAU;AAAE,YAAA,OAAO,UAAU;QAEjC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,EAAE;YACzB,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC;AACnC,YAAA,OAAO,IAAI,GAAG,IAAI,CAAC,YAAY,EAAE,GAAG,IAAI,CAAC,cAAc,EAAE;QAC3D;AACA,QAAA,OAAO,IAAI,CAAC,YAAY,EAAE,IAAI,MAAM;IACtC;8GAlIW,QAAQ,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;AAAR,IAAA,SAAA,IAAA,CAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,QAAQ,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,SAAA,EAAA,MAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,eAAA,EAAA,EAAA,iBAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,YAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,WAAA,EAAA,EAAA,iBAAA,EAAA,aAAA,EAAA,UAAA,EAAA,aAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,SAAA,EAAA,EAAA,iBAAA,EAAA,WAAA,EAAA,UAAA,EAAA,WAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,SAAA,EAAA,EAAA,iBAAA,EAAA,WAAA,EAAA,UAAA,EAAA,WAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,KAAA,EAAA,aAAA,EAAA,UAAA,EAAA,kBAAA,EAAA,SAAA,EAAA,WAAA,EAAA,UAAA,EAAA,YAAA,EAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,eAAA,EAAA,MAAA,EAAA,EAAA,EAAA,OAAA,EAAA,CAAA,EAAA,YAAA,EAAA,aAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAwBiB,gBAAgB,EAAA,WAAA,EAAA,IAAA,EAAA,QAAA,EAAA,IAAA,EAAA,EAAA,EAAA,YAAA,EAAA,eAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EACd,kBAAkB,EAAA,WAAA,EAAA,IAAA,EAAA,QAAA,EAAA,IAAA,EAAA,EAAA,EAAA,YAAA,EAAA,aAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EACpB,gBAAgB,EAAA,WAAA,EAAA,IAAA,EAAA,QAAA,EAAA,IAAA,EAAA,EAAA,EAAA,YAAA,EAAA,cAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAAA,CAAA,cAAA,CAAA,EAAA,WAAA,EAAA,IAAA,EAAA,QAAA,EAAA,IAAA,EAAA,EAAA,EAAA,YAAA,EAAA,aAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAAA,CAAA,aAAA,CAAA,EAAA,WAAA,EAAA,IAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECvEtD,23JA8HA,EAAA,MAAA,EAAA,CAAA,giOAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,ED1FY,gBAAgB,EAAA,QAAA,EAAA,oBAAA,EAAA,MAAA,EAAA,CAAA,yBAAA,EAAA,kBAAA,EAAA,0BAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,QAAQ,+EAAE,YAAY,EAAA,QAAA,EAAA,cAAA,EAAA,MAAA,EAAA,CAAA,YAAA,EAAA,eAAA,EAAA,aAAA,EAAA,WAAA,CAAA,EAAA,OAAA,EAAA,CAAA,iBAAA,EAAA,UAAA,EAAA,WAAA,EAAA,WAAA,EAAA,UAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,aAAA,EAAA,EAAA,CAAA,iBAAA,CAAA,IAAA,EAAA,CAAA,CAAA;;2FASvC,QAAQ,EAAA,UAAA,EAAA,CAAA;kBAZpB,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,SAAS,cACP,IAAI,EAAA,OAAA,EACP,CAAC,gBAAgB,EAAE,QAAQ,EAAE,YAAY,CAAC,EAAA,aAAA,EAGpC,iBAAiB,CAAC,IAAI,mBACpB,uBAAuB,CAAC,MAAM,EAAA,IAAA,EACzC;AACJ,wBAAA,iBAAiB,EAAE,MAAM;AAC1B,qBAAA,EAAA,QAAA,EAAA,23JAAA,EAAA,MAAA,EAAA,CAAA,giOAAA,CAAA,EAAA;AA0BmC,SAAA,CAAA,EAAA,cAAA,EAAA,EAAA,KAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,OAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,EAAA,CAAA,MAAA,EAAA,IAAA,EAAA,CAAA,aAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,UAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,YAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,EAAA,CAAA,MAAA,EAAA,IAAA,EAAA,CAAA,kBAAA,CAAA,EAAA,CAAA,EAAA,KAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,OAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,SAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,WAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,aAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,QAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,UAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,SAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,WAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,SAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,WAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,KAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,KAAA,EAAA,SAAA,EAAA,QAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA,EAAA,WAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,YAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,UAAA,CAAA,MAAA,gBAAgB,+FACd,kBAAkB,CAAA,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,WAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,YAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,UAAA,CAAA,MACpB,gBAAgB,CAAA,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,SAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,MAAA,EAAA,IAAA,EAAA,CAAA,WAAA,CAAA,EAAA,CAAA,EAAA,UAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,MAAA,EAAA,IAAA,EAAA,CAAA,YAAA,CAAA,EAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,EAAA,CAAA,YAAA,EAAA,IAAA,EAAA,CAUG,cAAc,wEACf,aAAa,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,EAAA,CAAA;MAiHxD,YAAY,CAAA;8GAAZ,YAAY,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;AAAZ,IAAA,SAAA,IAAA,CAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,YAAY,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,cAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,2BAAA,EAAA,MAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAfb;;;;;;;;;;AAUT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,CAAA;;2FAKU,YAAY,EAAA,UAAA,EAAA,CAAA;kBAjBxB,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,cAAc;AACxB,oBAAA,QAAQ,EAAE;;;;;;;;;;AAUT,EAAA,CAAA;AACD,oBAAA,IAAI,EAAE;AACJ,wBAAA,6BAA6B,EAAE,MAAM;AACtC,qBAAA;AACF,iBAAA;;MAYY,mBAAmB,CAAA;8GAAnB,mBAAmB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;AAAnB,IAAA,SAAA,IAAA,CAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,mBAAmB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,sBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,4BAAA,EAAA,MAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAPpB;;AAET,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,CAAA;;2FAKU,mBAAmB,EAAA,UAAA,EAAA,CAAA;kBAT/B,SAAS;AAAC,YAAA,IAAA,EAAA,CAAA;AACT,oBAAA,QAAQ,EAAE,sBAAsB;AAChC,oBAAA,QAAQ,EAAE;;AAET,EAAA,CAAA;AACD,oBAAA,IAAI,EAAE;AACJ,wBAAA,8BAA8B,EAAE,MAAM;AACvC,qBAAA;AACF,iBAAA;;;AE7MD;;AAEG;;;;"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ship-ui/core",
3
3
  "license": "MIT",
4
- "version": "0.22.9",
4
+ "version": "0.22.11",
5
5
  "peerDependencies": {
6
6
  "@angular/common": ">=20",
7
7
  "@angular/core": ">=20",
@@ -219,6 +219,10 @@
219
219
  "types": "./types/ship-ui-core-ship-toggle-card.d.ts",
220
220
  "default": "./fesm2022/ship-ui-core-ship-toggle-card.mjs"
221
221
  },
222
+ "./ship-tree": {
223
+ "types": "./types/ship-ui-core-ship-tree.d.ts",
224
+ "default": "./fesm2022/ship-ui-core-ship-tree.mjs"
225
+ },
222
226
  "./ship-virtual-scroll": {
223
227
  "types": "./types/ship-ui-core-ship-virtual-scroll.d.ts",
224
228
  "default": "./fesm2022/ship-ui-core-ship-virtual-scroll.mjs"
package/styles/index.scss CHANGED
@@ -51,5 +51,5 @@ $shipTable: true !default;
51
51
  $shipTabs: true !default;
52
52
  $shipFileUpload: true !default;
53
53
  $shipAccordion: true !default;
54
-
54
+ $shipTree: true !default;
55
55
 
@@ -2,7 +2,7 @@ import * as _angular_core from '@angular/core';
2
2
  import { ElementRef, TemplateRef, OutputEmitterRef, InputSignal, ComponentRef, EmbeddedViewRef, OutputRefSubscription, Type } from '@angular/core';
3
3
 
4
4
  type ShipDialogOptions = {
5
- class?: 'default' | 'type-b' | string;
5
+ class?: 'default' | 'type-b' | 'type-c' | string;
6
6
  width?: string;
7
7
  maxWidth?: string;
8
8
  height?: string;
@@ -18,7 +18,7 @@ declare class ShipDialog {
18
18
  options: _angular_core.InputSignal<Partial<ShipDialogOptions> | undefined>;
19
19
  closed: _angular_core.OutputEmitterRef<void>;
20
20
  defaultOptionMerge: _angular_core.Signal<{
21
- class: "default" | "type-b" | string;
21
+ class: "default" | "type-b" | "type-c" | string;
22
22
  width?: string;
23
23
  maxWidth?: string;
24
24
  height?: string;
@@ -18,6 +18,11 @@ type ShipDropEvent = {
18
18
  previousIndex: number;
19
19
  currentIndex: number;
20
20
  };
21
+ type ShipTreeDropEvent = {
22
+ previousIndex: number;
23
+ currentIndex: number;
24
+ position: 'before' | 'after' | 'inside';
25
+ };
21
26
  interface SortableManagerConfig {
22
27
  /**
23
28
  * If provided, this hook is evaluated before the Signals are modified.
@@ -32,11 +37,16 @@ declare class ShipSortable implements OnInit, OnDestroy {
32
37
  #private;
33
38
  shSortable: _angular_core.InputSignal<any>;
34
39
  sortableGroup: _angular_core.InputSignal<string | undefined>;
40
+ sortingMode: _angular_core.InputSignal<"list" | "grid" | "tree">;
41
+ treeItems: _angular_core.ModelSignal<any[]>;
35
42
  sortDrop: _angular_core.OutputEmitterRef<ShipDropEvent>;
36
43
  afterDrop: _angular_core.OutputEmitterRef<AfterDropResponse>;
37
44
  crossDrop: _angular_core.OutputEmitterRef<CrossDropResponse>;
45
+ treeDrop: _angular_core.OutputEmitterRef<ShipTreeDropEvent>;
38
46
  dragStartIndex: WritableSignal<number>;
39
47
  dragToIndex: WritableSignal<number>;
48
+ treeHoverIndex: WritableSignal<number>;
49
+ treeHoverPosition: WritableSignal<"before" | "after" | "inside" | null>;
40
50
  initialPositions: WritableSignal<{
41
51
  x: number;
42
52
  y: number;
@@ -63,10 +73,24 @@ declare class ShipSortable implements OnInit, OnDestroy {
63
73
  dragEnd(): void;
64
74
  ngOnDestroy(): void;
65
75
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<ShipSortable, never>;
66
- static ɵdir: _angular_core.ɵɵDirectiveDeclaration<ShipSortable, "[shSortable]", never, { "shSortable": { "alias": "shSortable"; "required": false; "isSignal": true; }; "sortableGroup": { "alias": "sortableGroup"; "required": false; "isSignal": true; }; }, { "sortDrop": "sortDrop"; "afterDrop": "afterDrop"; "crossDrop": "crossDrop"; }, never, never, true, never>;
76
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<ShipSortable, "[shSortable]", never, { "shSortable": { "alias": "shSortable"; "required": false; "isSignal": true; }; "sortableGroup": { "alias": "sortableGroup"; "required": false; "isSignal": true; }; "sortingMode": { "alias": "sortingMode"; "required": false; "isSignal": true; }; "treeItems": { "alias": "treeItems"; "required": false; "isSignal": true; }; }, { "treeItems": "treeItemsChange"; "sortDrop": "sortDrop"; "afterDrop": "afterDrop"; "crossDrop": "crossDrop"; "treeDrop": "treeDrop"; }, never, never, true, never>;
67
77
  }
68
78
  declare function moveIndex<T = any>(array: T[], event: Pick<ShipDropEvent, 'previousIndex' | 'currentIndex'> | Pick<AfterDropResponse, 'fromIndex' | 'toIndex'>): T[];
69
79
  declare function transferArrayItem<T = any>(currentArray: T[], targetArray: T[], currentIndex: number, targetIndex: number): void;
80
+ interface TreeSortableManagerConfig<T> {
81
+ getId?: (item: T) => string;
82
+ getParentId?: (item: T) => string | null;
83
+ setParentId?: (item: T, parentId: string | null) => void;
84
+ getDepth?: (item: T) => number;
85
+ setDepth?: (item: T, depth: number) => void;
86
+ isFolder?: (item: T) => boolean;
87
+ isOpen?: (item: T) => boolean;
88
+ setIsOpen?: (item: T, isOpen: boolean) => void;
89
+ }
90
+ declare function createTreeSortableManager<T>(nodesSignal: WritableSignal<T[]>, config?: TreeSortableManagerConfig<T>): {
91
+ visibleNodes: _angular_core.Signal<T[]>;
92
+ drop(event: ShipTreeDropEvent, visibleNodesList: T[]): void;
93
+ };
70
94
 
71
- export { ShipSortable, createSortableManager, moveIndex, transferArrayItem };
72
- export type { AfterDropResponse, CrossDropResponse, ShipDropEvent, SortableManagerConfig };
95
+ export { ShipSortable, createSortableManager, createTreeSortableManager, moveIndex, transferArrayItem };
96
+ export type { AfterDropResponse, CrossDropResponse, ShipDropEvent, ShipTreeDropEvent, SortableManagerConfig, TreeSortableManagerConfig };
@@ -16,10 +16,11 @@ interface ShipSpotlightServiceOptions {
16
16
  shortcut?: string;
17
17
  customFilter?: boolean;
18
18
  searchQuery?: string;
19
+ dialogClass?: string;
19
20
  }
20
21
  interface ShipSpotlightConfig {
21
22
  defaultItems?: ShipSpotlightItem[];
22
- enableShortcuts?: boolean;
23
+ enableGlobalEventListener?: boolean;
23
24
  }
24
25
  declare const SHIP_SPOTLIGHT_CONFIG: InjectionToken<ShipSpotlightConfig>;
25
26
  declare function provideShipSpotlight(config: ShipSpotlightConfig): Provider;
@@ -0,0 +1,67 @@
1
+ import * as _angular_core from '@angular/core';
2
+ import { ElementRef, TemplateRef } from '@angular/core';
3
+
4
+ declare class ShipTreeOpenIcon {
5
+ readonly el: ElementRef<any>;
6
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ShipTreeOpenIcon, never>;
7
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<ShipTreeOpenIcon, "sh-icon[openIcon]", never, {}, {}, never, never, true, never>;
8
+ }
9
+ declare class ShipTreeClosedIcon {
10
+ readonly el: ElementRef<any>;
11
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ShipTreeClosedIcon, never>;
12
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<ShipTreeClosedIcon, "sh-icon[closedIcon]", never, {}, {}, never, never, true, never>;
13
+ }
14
+ declare class ShipTreeItemIcon {
15
+ readonly el: ElementRef<any>;
16
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ShipTreeItemIcon, never>;
17
+ static ɵdir: _angular_core.ɵɵDirectiveDeclaration<ShipTreeItemIcon, "sh-icon[itemIcon]", never, {}, {}, never, never, true, never>;
18
+ }
19
+ declare class ShipTree {
20
+ /** The flat list of all tree nodes. */
21
+ readonly items: _angular_core.ModelSignal<any[]>;
22
+ /** Optional Writable/Readable WritableSignal manager created by createTreeSortableManager. */
23
+ readonly sortableManager: _angular_core.InputSignal<any>;
24
+ /** Currently selected node ID. */
25
+ readonly selectedId: _angular_core.ModelSignal<string | null>;
26
+ readonly getId: _angular_core.InputSignal<(item: any) => string>;
27
+ readonly getName: _angular_core.InputSignal<(item: any) => string>;
28
+ readonly getParentId: _angular_core.InputSignal<(item: any) => string | null>;
29
+ readonly isFolder: _angular_core.InputSignal<(item: any) => boolean>;
30
+ readonly getIsOpen: _angular_core.InputSignal<(item: any) => boolean>;
31
+ readonly setIsOpen: _angular_core.InputSignal<(item: any, isOpen: boolean) => void>;
32
+ /** Function returning a custom icon name for a node. */
33
+ readonly getIcon: _angular_core.InputSignal<(item: any) => string | null>;
34
+ readonly openIconDir: _angular_core.Signal<ShipTreeOpenIcon | undefined>;
35
+ readonly closedIconDir: _angular_core.Signal<ShipTreeClosedIcon | undefined>;
36
+ readonly itemIconDir: _angular_core.Signal<ShipTreeItemIcon | undefined>;
37
+ openIconName: _angular_core.Signal<any>;
38
+ closedIconName: _angular_core.Signal<any>;
39
+ itemIconName: _angular_core.Signal<any>;
40
+ readonly nodeClick: _angular_core.OutputEmitterRef<any>;
41
+ readonly nodeToggle: _angular_core.OutputEmitterRef<{
42
+ node: any;
43
+ isOpen: boolean;
44
+ }>;
45
+ readonly nodeTemplate: _angular_core.Signal<TemplateRef<any> | undefined>;
46
+ readonly dirTemplate: _angular_core.Signal<TemplateRef<any> | undefined>;
47
+ /** Filter the full flat array down to only visible nodes (whose parents are all expanded). */
48
+ visibleNodes: _angular_core.Signal<any>;
49
+ toggleNode(node: any, event: MouseEvent): void;
50
+ handleNodeClick(node: any, event: MouseEvent): void;
51
+ selectNode(node: any): void;
52
+ getDepthArray(depth: number): number[];
53
+ getNodeDepth(node: any): number;
54
+ getNodeIcon(node: any): string | null;
55
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ShipTree, never>;
56
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<ShipTree, "sh-tree", never, { "items": { "alias": "items"; "required": false; "isSignal": true; }; "sortableManager": { "alias": "sortableManager"; "required": false; "isSignal": true; }; "selectedId": { "alias": "selectedId"; "required": false; "isSignal": true; }; "getId": { "alias": "getId"; "required": false; "isSignal": true; }; "getName": { "alias": "getName"; "required": false; "isSignal": true; }; "getParentId": { "alias": "getParentId"; "required": false; "isSignal": true; }; "isFolder": { "alias": "isFolder"; "required": false; "isSignal": true; }; "getIsOpen": { "alias": "getIsOpen"; "required": false; "isSignal": true; }; "setIsOpen": { "alias": "setIsOpen"; "required": false; "isSignal": true; }; "getIcon": { "alias": "getIcon"; "required": false; "isSignal": true; }; }, { "items": "itemsChange"; "selectedId": "selectedIdChange"; "nodeClick": "nodeClick"; "nodeToggle": "nodeToggle"; }, ["openIconDir", "closedIconDir", "itemIconDir", "nodeTemplate", "dirTemplate"], ["[emptyState]", "[emptyState]"], true, never>;
57
+ }
58
+ declare class ShipTreeNode {
59
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ShipTreeNode, never>;
60
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<ShipTreeNode, "sh-tree-node", never, {}, {}, never, ["sh-icon", "*", "[actions], sh-tree-node-actions"], true, never>;
61
+ }
62
+ declare class ShipTreeNodeActions {
63
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ShipTreeNodeActions, never>;
64
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<ShipTreeNodeActions, "sh-tree-node-actions", never, {}, {}, never, ["*"], true, never>;
65
+ }
66
+
67
+ export { ShipTree, ShipTreeClosedIcon, ShipTreeItemIcon, ShipTreeNode, ShipTreeNodeActions, ShipTreeOpenIcon };