@akcelik/strct 0.22.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1546,46 +1546,133 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
1546
1546
 
1547
1547
  /**
1548
1548
  * Surface container. Compose with the header / block / footer pieces:
1549
- * <strct-card>
1550
- * <strct-card-header>Title</strct-card-header>
1551
- * <strct-card-block>...</strct-card-block>
1549
+ *
1550
+ * <strct-card status="warning" collapsible [(collapsed)]="hidden">
1551
+ * <strct-card-header icon="host">Host hv-02</strct-card-header>
1552
+ * <strct-card-block>…</strct-card-block>
1552
1553
  * <strct-card-footer><button strct-button>OK</button></strct-card-footer>
1553
1554
  * </strct-card>
1555
+ *
1556
+ * Rich options (all opt-in; defaults preserve the plain card):
1557
+ * - `status` — tone rail on the leading edge (same language as alert / hero).
1558
+ * - `interactive` — hover lift + accent border for clickable cards.
1559
+ * - `selected` — accent ring for picker layouts.
1560
+ * - `dense` — tighter paddings for dashboards.
1561
+ * - `loading` — indeterminate top bar + `aria-busy` (reduced-motion safe).
1562
+ * - `collapsible` + two-way `collapsed` — the header gains a chevron toggle;
1563
+ * block and footer hide while collapsed.
1554
1564
  */
1555
1565
  class StrctCard {
1566
+ /** Tone rail on the leading edge. */
1567
+ status = input('neutral', ...(ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []));
1568
+ /** Hover lift + accent border for clickable cards (style-only affordance). */
1569
+ interactive = input(false, { ...(ngDevMode ? { debugName: "interactive" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1570
+ /** Accent ring — for picker / multi-select card layouts. */
1571
+ selected = input(false, { ...(ngDevMode ? { debugName: "selected" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1572
+ /** Tighter paddings for dense dashboards. */
1573
+ dense = input(false, { ...(ngDevMode ? { debugName: "dense" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1574
+ /** Indeterminate top bar + aria-busy; body/footer dim and ignore input. */
1575
+ loading = input(false, { ...(ngDevMode ? { debugName: "loading" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1576
+ /** Allow collapsing to just the header (a chevron appears in the header). */
1577
+ collapsible = input(false, { ...(ngDevMode ? { debugName: "collapsible" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1578
+ /** Collapsed state (two-way; only meaningful with `collapsible`). */
1579
+ collapsed = model(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
1580
+ toggle() {
1581
+ this.collapsed.update((v) => !v);
1582
+ }
1556
1583
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctCard, deps: [], target: i0.ɵɵFactoryTarget.Component });
1557
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.16", type: StrctCard, isStandalone: true, selector: "strct-card", ngImport: i0, template: `<ng-content />`, isInline: true, styles: [":host{display:block;background:var(--bg-1);border:1px solid var(--b2);border-radius:var(--radius-lg);box-shadow:var(--sh);overflow:hidden}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1584
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.16", type: StrctCard, isStandalone: true, selector: "strct-card", inputs: { status: { classPropertyName: "status", publicName: "status", isSignal: true, isRequired: false, transformFunction: null }, interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null }, dense: { classPropertyName: "dense", publicName: "dense", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { collapsed: "collapsedChange" }, host: { properties: { "class.strct-card--accent": "status() === 'accent'", "class.strct-card--success": "status() === 'success'", "class.strct-card--warning": "status() === 'warning'", "class.strct-card--critical": "status() === 'critical'", "class.strct-card--interactive": "interactive()", "class.strct-card--selected": "selected()", "class.strct-card--dense": "dense()", "class.strct-card--loading": "loading()", "class.strct-card--collapsed": "collapsible() && collapsed()", "attr.aria-busy": "loading() ? \"true\" : null" }, classAttribute: "strct-card" }, ngImport: i0, template: `<ng-content />`, isInline: true, styles: [".strct-card{position:relative;display:block;background:var(--bg-1);border:1px solid var(--b2);border-radius:var(--radius-lg);box-shadow:var(--sh);overflow:hidden}.strct-card--accent{border-inline-start:3px solid var(--acc)}.strct-card--success{border-inline-start:3px solid var(--success)}.strct-card--warning{border-inline-start:3px solid var(--warning)}.strct-card--critical{border-inline-start:3px solid var(--critical)}.strct-card--interactive{cursor:pointer;transition:border-color .14s ease,transform .14s ease,box-shadow .14s ease}.strct-card--interactive:hover{border-color:var(--acc50);transform:translateY(-1px);box-shadow:var(--shh)}.strct-card--interactive:focus-within{border-color:var(--acc)}.strct-card--selected{border-color:var(--acc);box-shadow:0 0 0 1px var(--acc),var(--sh)}.strct-card--dense{--strct-card-px: var(--space-3);--strct-card-py: var(--space-2);--strct-card-pb: var(--space-3)}.strct-card--loading:before{content:\"\";position:absolute;top:0;left:0;height:2px;width:40%;background:var(--acc);animation:strct-card-load 1.1s ease-in-out infinite;z-index:1}.strct-card--loading>strct-card-block,.strct-card--loading>strct-card-footer{opacity:.55;pointer-events:none}@keyframes strct-card-load{0%{transform:translate(-100%)}to{transform:translate(280%)}}.strct-card--collapsed>strct-card-block,.strct-card--collapsed>strct-card-footer{display:none}.strct-card--collapsed>strct-card-header{border-bottom:0}@media(prefers-reduced-motion:reduce){.strct-card--interactive{transition:none}.strct-card--interactive:hover{transform:none}.strct-card--loading:before{animation:none;width:100%;opacity:.4}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1558
1585
  }
1559
1586
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctCard, decorators: [{
1560
1587
  type: Component,
1561
- args: [{ selector: 'strct-card', changeDetection: ChangeDetectionStrategy.OnPush, template: `<ng-content />`, styles: [":host{display:block;background:var(--bg-1);border:1px solid var(--b2);border-radius:var(--radius-lg);box-shadow:var(--sh);overflow:hidden}\n"] }]
1562
- }] });
1588
+ args: [{ selector: 'strct-card', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: `<ng-content />`, host: {
1589
+ class: 'strct-card',
1590
+ '[class.strct-card--accent]': "status() === 'accent'",
1591
+ '[class.strct-card--success]': "status() === 'success'",
1592
+ '[class.strct-card--warning]': "status() === 'warning'",
1593
+ '[class.strct-card--critical]': "status() === 'critical'",
1594
+ '[class.strct-card--interactive]': 'interactive()',
1595
+ '[class.strct-card--selected]': 'selected()',
1596
+ '[class.strct-card--dense]': 'dense()',
1597
+ '[class.strct-card--loading]': 'loading()',
1598
+ '[class.strct-card--collapsed]': 'collapsible() && collapsed()',
1599
+ '[attr.aria-busy]': 'loading() ? "true" : null',
1600
+ }, styles: [".strct-card{position:relative;display:block;background:var(--bg-1);border:1px solid var(--b2);border-radius:var(--radius-lg);box-shadow:var(--sh);overflow:hidden}.strct-card--accent{border-inline-start:3px solid var(--acc)}.strct-card--success{border-inline-start:3px solid var(--success)}.strct-card--warning{border-inline-start:3px solid var(--warning)}.strct-card--critical{border-inline-start:3px solid var(--critical)}.strct-card--interactive{cursor:pointer;transition:border-color .14s ease,transform .14s ease,box-shadow .14s ease}.strct-card--interactive:hover{border-color:var(--acc50);transform:translateY(-1px);box-shadow:var(--shh)}.strct-card--interactive:focus-within{border-color:var(--acc)}.strct-card--selected{border-color:var(--acc);box-shadow:0 0 0 1px var(--acc),var(--sh)}.strct-card--dense{--strct-card-px: var(--space-3);--strct-card-py: var(--space-2);--strct-card-pb: var(--space-3)}.strct-card--loading:before{content:\"\";position:absolute;top:0;left:0;height:2px;width:40%;background:var(--acc);animation:strct-card-load 1.1s ease-in-out infinite;z-index:1}.strct-card--loading>strct-card-block,.strct-card--loading>strct-card-footer{opacity:.55;pointer-events:none}@keyframes strct-card-load{0%{transform:translate(-100%)}to{transform:translate(280%)}}.strct-card--collapsed>strct-card-block,.strct-card--collapsed>strct-card-footer{display:none}.strct-card--collapsed>strct-card-header{border-bottom:0}@media(prefers-reduced-motion:reduce){.strct-card--interactive{transition:none}.strct-card--interactive:hover{transform:none}.strct-card--loading:before{animation:none;width:100%;opacity:.4}}\n"] }]
1601
+ }], propDecorators: { status: [{ type: i0.Input, args: [{ isSignal: true, alias: "status", required: false }] }], interactive: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactive", required: false }] }], selected: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }], dense: [{ type: i0.Input, args: [{ isSignal: true, alias: "dense", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], collapsible: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsible", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }, { type: i0.Output, args: ["collapsedChange"] }] } });
1563
1602
  /** Header section of a {@link StrctCard}. */
1564
1603
  class StrctCardHeader {
1604
+ /** Optional leading icon (StrctIcon set). */
1605
+ icon = input('', ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
1606
+ /** Accessible labels for the collapse toggle (localizable). */
1607
+ collapseLabel = input('Collapse', ...(ngDevMode ? [{ debugName: "collapseLabel" }] : /* istanbul ignore next */ []));
1608
+ expandLabel = input('Expand', ...(ngDevMode ? [{ debugName: "expandLabel" }] : /* istanbul ignore next */ []));
1609
+ card = inject(StrctCard, { optional: true });
1565
1610
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctCardHeader, deps: [], target: i0.ɵɵFactoryTarget.Component });
1566
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.16", type: StrctCardHeader, isStandalone: true, selector: "strct-card-header", ngImport: i0, template: `<ng-content />`, isInline: true, styles: [":host{display:flex;align-items:center;justify-content:space-between;gap:var(--space-2);padding:var(--space-3) var(--space-4);border-bottom:1px solid var(--b1);font-size:13px;font-weight:600;color:var(--t1)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1611
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctCardHeader, isStandalone: true, selector: "strct-card-header", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, collapseLabel: { classPropertyName: "collapseLabel", publicName: "collapseLabel", isSignal: true, isRequired: false, transformFunction: null }, expandLabel: { classPropertyName: "expandLabel", publicName: "expandLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "strct-card__header" }, ngImport: i0, template: `
1612
+ @if (icon()) {
1613
+ <strct-icon class="strct-card__hicon" [name]="icon()" [size]="16" [strokeWidth]="1.4" />
1614
+ }
1615
+ <ng-content />
1616
+ @if (card?.collapsible()) {
1617
+ <button
1618
+ type="button"
1619
+ class="strct-card__collapse"
1620
+ [attr.aria-expanded]="!card!.collapsed()"
1621
+ [attr.aria-label]="card!.collapsed() ? expandLabel() : collapseLabel()"
1622
+ (click)="card!.toggle()"
1623
+ >
1624
+ <strct-icon
1625
+ name="chevronDown"
1626
+ [size]="14"
1627
+ [strokeWidth]="1.6"
1628
+ [class.strct-card__chev--collapsed]="card!.collapsed()"
1629
+ />
1630
+ </button>
1631
+ }
1632
+ `, isInline: true, styles: [".strct-card__header{display:flex;align-items:center;justify-content:space-between;gap:var(--space-2);padding:var(--strct-card-py, var(--space-3)) var(--strct-card-px, var(--space-4));border-bottom:1px solid var(--b1);font-size:13px;font-weight:600;color:var(--t1)}.strct-card__hicon{color:var(--t2);flex-shrink:0}.strct-card__collapse{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;margin:-4px;margin-inline-start:0;padding:0;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--t3);cursor:pointer;flex-shrink:0}.strct-card__collapse:hover{color:var(--t1);background:var(--bg-3)}.strct-card__collapse:focus-visible{outline:none;box-shadow:0 0 0 2px var(--acc50)}.strct-card__collapse strct-icon{transition:transform .15s ease}.strct-card__chev--collapsed{transform:rotate(-90deg)}@media(prefers-reduced-motion:reduce){.strct-card__collapse strct-icon{transition:none}}\n"], dependencies: [{ kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "size", "strokeWidth", "badge", "ariaLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1567
1633
  }
1568
1634
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctCardHeader, decorators: [{
1569
1635
  type: Component,
1570
- args: [{ selector: 'strct-card-header', changeDetection: ChangeDetectionStrategy.OnPush, template: `<ng-content />`, styles: [":host{display:flex;align-items:center;justify-content:space-between;gap:var(--space-2);padding:var(--space-3) var(--space-4);border-bottom:1px solid var(--b1);font-size:13px;font-weight:600;color:var(--t1)}\n"] }]
1571
- }] });
1636
+ args: [{ selector: 'strct-card-header', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [StrctIcon], template: `
1637
+ @if (icon()) {
1638
+ <strct-icon class="strct-card__hicon" [name]="icon()" [size]="16" [strokeWidth]="1.4" />
1639
+ }
1640
+ <ng-content />
1641
+ @if (card?.collapsible()) {
1642
+ <button
1643
+ type="button"
1644
+ class="strct-card__collapse"
1645
+ [attr.aria-expanded]="!card!.collapsed()"
1646
+ [attr.aria-label]="card!.collapsed() ? expandLabel() : collapseLabel()"
1647
+ (click)="card!.toggle()"
1648
+ >
1649
+ <strct-icon
1650
+ name="chevronDown"
1651
+ [size]="14"
1652
+ [strokeWidth]="1.6"
1653
+ [class.strct-card__chev--collapsed]="card!.collapsed()"
1654
+ />
1655
+ </button>
1656
+ }
1657
+ `, host: { class: 'strct-card__header' }, styles: [".strct-card__header{display:flex;align-items:center;justify-content:space-between;gap:var(--space-2);padding:var(--strct-card-py, var(--space-3)) var(--strct-card-px, var(--space-4));border-bottom:1px solid var(--b1);font-size:13px;font-weight:600;color:var(--t1)}.strct-card__hicon{color:var(--t2);flex-shrink:0}.strct-card__collapse{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;margin:-4px;margin-inline-start:0;padding:0;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--t3);cursor:pointer;flex-shrink:0}.strct-card__collapse:hover{color:var(--t1);background:var(--bg-3)}.strct-card__collapse:focus-visible{outline:none;box-shadow:0 0 0 2px var(--acc50)}.strct-card__collapse strct-icon{transition:transform .15s ease}.strct-card__chev--collapsed{transform:rotate(-90deg)}@media(prefers-reduced-motion:reduce){.strct-card__collapse strct-icon{transition:none}}\n"] }]
1658
+ }], propDecorators: { icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], collapseLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapseLabel", required: false }] }], expandLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandLabel", required: false }] }] } });
1572
1659
  /** Body section of a {@link StrctCard}. */
1573
1660
  class StrctCardBlock {
1574
1661
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctCardBlock, deps: [], target: i0.ɵɵFactoryTarget.Component });
1575
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.16", type: StrctCardBlock, isStandalone: true, selector: "strct-card-block", ngImport: i0, template: `<ng-content />`, isInline: true, styles: [":host{display:block;padding:var(--space-4);color:var(--t2);font-size:13px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1662
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.16", type: StrctCardBlock, isStandalone: true, selector: "strct-card-block", host: { classAttribute: "strct-card__block" }, ngImport: i0, template: `<ng-content />`, isInline: true, styles: [".strct-card__block{display:block;padding:var(--strct-card-pb, var(--space-4)) var(--strct-card-px, var(--space-4));color:var(--t2);font-size:13px}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1576
1663
  }
1577
1664
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctCardBlock, decorators: [{
1578
1665
  type: Component,
1579
- args: [{ selector: 'strct-card-block', changeDetection: ChangeDetectionStrategy.OnPush, template: `<ng-content />`, styles: [":host{display:block;padding:var(--space-4);color:var(--t2);font-size:13px}\n"] }]
1666
+ args: [{ selector: 'strct-card-block', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: `<ng-content />`, host: { class: 'strct-card__block' }, styles: [".strct-card__block{display:block;padding:var(--strct-card-pb, var(--space-4)) var(--strct-card-px, var(--space-4));color:var(--t2);font-size:13px}\n"] }]
1580
1667
  }] });
1581
1668
  /** Footer section of a {@link StrctCard}. */
1582
1669
  class StrctCardFooter {
1583
1670
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctCardFooter, deps: [], target: i0.ɵɵFactoryTarget.Component });
1584
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.16", type: StrctCardFooter, isStandalone: true, selector: "strct-card-footer", ngImport: i0, template: `<ng-content />`, isInline: true, styles: [":host{display:flex;align-items:center;justify-content:flex-end;gap:var(--space-2);padding:var(--space-3) var(--space-4);border-top:1px solid var(--b1);background:var(--bg-2)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1671
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.16", type: StrctCardFooter, isStandalone: true, selector: "strct-card-footer", host: { classAttribute: "strct-card__footer" }, ngImport: i0, template: `<ng-content />`, isInline: true, styles: [".strct-card__footer{display:flex;align-items:center;justify-content:flex-end;gap:var(--space-2);padding:var(--strct-card-py, var(--space-3)) var(--strct-card-px, var(--space-4));border-top:1px solid var(--b1);background:var(--bg-2)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1585
1672
  }
1586
1673
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctCardFooter, decorators: [{
1587
1674
  type: Component,
1588
- args: [{ selector: 'strct-card-footer', changeDetection: ChangeDetectionStrategy.OnPush, template: `<ng-content />`, styles: [":host{display:flex;align-items:center;justify-content:flex-end;gap:var(--space-2);padding:var(--space-3) var(--space-4);border-top:1px solid var(--b1);background:var(--bg-2)}\n"] }]
1675
+ args: [{ selector: 'strct-card-footer', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: `<ng-content />`, host: { class: 'strct-card__footer' }, styles: [".strct-card__footer{display:flex;align-items:center;justify-content:flex-end;gap:var(--space-2);padding:var(--strct-card-py, var(--space-3)) var(--strct-card-px, var(--space-4));border-top:1px solid var(--b1);background:var(--bg-2)}\n"] }]
1589
1676
  }] });
1590
1677
 
1591
1678
  /** Vertical container for `<strct-accordion-panel>` items. */
@@ -2194,6 +2281,8 @@ class StrctTreeNode {
2194
2281
  }, ...(ngDevMode ? [{ debugName: "menuItems" }] : /* istanbul ignore next */ []));
2195
2282
  /** The owning `<strct-tree>` (null only for standalone nodes outside a tree). */
2196
2283
  tree = inject(forwardRef(() => StrctTree), { optional: true });
2284
+ /** Node icon scales with the owning tree's density (16 native → 18 comfortable). */
2285
+ iconSize = computed(() => (this.tree?.density() === 'comfortable' ? 18 : 16), ...(ngDevMode ? [{ debugName: "iconSize" }] : /* istanbul ignore next */ []));
2197
2286
  /** Parent tree node (element-injector ancestor); drives aria-level + ArrowLeft. */
2198
2287
  parent = inject(StrctTreeNode, { optional: true, skipSelf: true });
2199
2288
  /** 1-based depth, exposed as aria-level. */
@@ -2311,7 +2400,7 @@ class StrctTreeNode {
2311
2400
  <strct-icon
2312
2401
  class="strct-tnode__icon"
2313
2402
  [name]="displayIcon()!"
2314
- [size]="16"
2403
+ [size]="iconSize()"
2315
2404
  [strokeWidth]="1.3"
2316
2405
  [badge]="displayBadge()"
2317
2406
  />
@@ -2377,7 +2466,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
2377
2466
  <strct-icon
2378
2467
  class="strct-tnode__icon"
2379
2468
  [name]="displayIcon()!"
2380
- [size]="16"
2469
+ [size]="iconSize()"
2381
2470
  [strokeWidth]="1.3"
2382
2471
  [badge]="displayBadge()"
2383
2472
  />
@@ -2414,6 +2503,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
2414
2503
  class StrctTree {
2415
2504
  /** Data-driven node list; when set, projected content is ignored. */
2416
2505
  nodes = input(null, ...(ngDevMode ? [{ debugName: "nodes" }] : /* istanbul ignore next */ []));
2506
+ /**
2507
+ * Row density. `compact` (default) is the dense inventory layout —
2508
+ * 13px text / 16px icons; `comfortable` relaxes to 14px text / 18px icons
2509
+ * with taller rows for touch-friendly or low-density consoles.
2510
+ */
2511
+ density = input('compact', ...(ngDevMode ? [{ debugName: "density" }] : /* istanbul ignore next */ []));
2417
2512
  /** Per-node right-click menu resolver. */
2418
2513
  nodeMenu = input(null, ...(ngDevMode ? [{ debugName: "nodeMenu" }] : /* istanbul ignore next */ []));
2419
2514
  /** Emitted when any data-driven node is clicked. */
@@ -2563,7 +2658,7 @@ class StrctTree {
2563
2658
  this.expandedChange.emit([...this.currentSet()]);
2564
2659
  }
2565
2660
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctTree, deps: [], target: i0.ɵɵFactoryTarget.Component });
2566
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctTree, isStandalone: true, selector: "strct-tree", inputs: { nodes: { classPropertyName: "nodes", publicName: "nodes", isSignal: true, isRequired: false, transformFunction: null }, nodeMenu: { classPropertyName: "nodeMenu", publicName: "nodeMenu", isSignal: true, isRequired: false, transformFunction: null }, expandedIds: { classPropertyName: "expandedIds", publicName: "expandedIds", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { nodeActivated: "nodeActivated", nodeMenuSelect: "nodeMenuSelect", expandedIds: "expandedIdsChange", expandedChange: "expandedChange", nodeToggled: "nodeToggled" }, host: { attributes: { "role": "tree" }, classAttribute: "strct-tree" }, ngImport: i0, template: `
2661
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.16", type: StrctTree, isStandalone: true, selector: "strct-tree", inputs: { nodes: { classPropertyName: "nodes", publicName: "nodes", isSignal: true, isRequired: false, transformFunction: null }, density: { classPropertyName: "density", publicName: "density", isSignal: true, isRequired: false, transformFunction: null }, nodeMenu: { classPropertyName: "nodeMenu", publicName: "nodeMenu", isSignal: true, isRequired: false, transformFunction: null }, expandedIds: { classPropertyName: "expandedIds", publicName: "expandedIds", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { nodeActivated: "nodeActivated", nodeMenuSelect: "nodeMenuSelect", expandedIds: "expandedIdsChange", expandedChange: "expandedChange", nodeToggled: "nodeToggled" }, host: { attributes: { "role": "tree" }, properties: { "class.strct-tree--comfortable": "density() === 'comfortable'" }, classAttribute: "strct-tree" }, ngImport: i0, template: `
2567
2662
  @if (nodes(); as ns) {
2568
2663
  @for (n of ns; track n.id ?? n.label) {
2569
2664
  <strct-tree-node
@@ -2576,7 +2671,7 @@ class StrctTree {
2576
2671
  } @else {
2577
2672
  <ng-content />
2578
2673
  }
2579
- `, isInline: true, styles: [".strct-tree{display:block}\n"], dependencies: [{ kind: "component", type: StrctTreeNode, selector: "strct-tree-node", inputs: ["node", "label", "icon", "badge", "active", "expanded", "nodeMenu"], outputs: ["expandedChange", "activated", "nodeActivated", "nodeMenuSelect"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
2674
+ `, isInline: true, styles: [".strct-tree{display:block}.strct-tree--comfortable .strct-tnode__row{gap:7px;padding:6px 10px;border-radius:6px;font-size:14px}.strct-tree--comfortable .strct-tnode__chevron{margin:-4px;margin-inline-end:-9px}.strct-tree--comfortable .strct-tnode__spacer{width:16px}.strct-tree--comfortable .strct-tnode__children{margin-inline-start:19px}\n"], dependencies: [{ kind: "component", type: StrctTreeNode, selector: "strct-tree-node", inputs: ["node", "label", "icon", "badge", "active", "expanded", "nodeMenu"], outputs: ["expandedChange", "activated", "nodeActivated", "nodeMenuSelect"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
2580
2675
  }
2581
2676
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImport: i0, type: StrctTree, decorators: [{
2582
2677
  type: Component,
@@ -2593,8 +2688,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.16", ngImpo
2593
2688
  } @else {
2594
2689
  <ng-content />
2595
2690
  }
2596
- `, host: { class: 'strct-tree', role: 'tree' }, styles: [".strct-tree{display:block}\n"] }]
2597
- }], propDecorators: { nodes: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodes", required: false }] }], nodeMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodeMenu", required: false }] }], nodeActivated: [{ type: i0.Output, args: ["nodeActivated"] }], nodeMenuSelect: [{ type: i0.Output, args: ["nodeMenuSelect"] }], expandedIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedIds", required: false }] }, { type: i0.Output, args: ["expandedIdsChange"] }], expandedChange: [{ type: i0.Output, args: ["expandedChange"] }], nodeToggled: [{ type: i0.Output, args: ["nodeToggled"] }] } });
2691
+ `, host: {
2692
+ class: 'strct-tree',
2693
+ role: 'tree',
2694
+ '[class.strct-tree--comfortable]': "density() === 'comfortable'",
2695
+ }, styles: [".strct-tree{display:block}.strct-tree--comfortable .strct-tnode__row{gap:7px;padding:6px 10px;border-radius:6px;font-size:14px}.strct-tree--comfortable .strct-tnode__chevron{margin:-4px;margin-inline-end:-9px}.strct-tree--comfortable .strct-tnode__spacer{width:16px}.strct-tree--comfortable .strct-tnode__children{margin-inline-start:19px}\n"] }]
2696
+ }], propDecorators: { nodes: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodes", required: false }] }], density: [{ type: i0.Input, args: [{ isSignal: true, alias: "density", required: false }] }], nodeMenu: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodeMenu", required: false }] }], nodeActivated: [{ type: i0.Output, args: ["nodeActivated"] }], nodeMenuSelect: [{ type: i0.Output, args: ["nodeMenuSelect"] }], expandedIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedIds", required: false }] }, { type: i0.Output, args: ["expandedIdsChange"] }], expandedChange: [{ type: i0.Output, args: ["expandedChange"] }], nodeToggled: [{ type: i0.Output, args: ["nodeToggled"] }] } });
2598
2697
 
2599
2698
  let modalCounter = 0;
2600
2699
  // Body scroll-lock shared across any number of simultaneously open modals.