@tolle_/tolle-ui 0.0.30-beta → 0.0.31-beta

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.
@@ -10,9 +10,12 @@ import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
10
10
  import { autoUpdate, computePosition, offset, flip, shift, size } from '@floating-ui/dom';
11
11
  import { Subject, Subscription, BehaviorSubject } from 'rxjs';
12
12
  import { format, startOfWeek, startOfMonth, endOfWeek, endOfMonth, eachDayOfInterval, subMonths, subYears, addMonths, addYears, setMonth, setYear, isSameDay, isToday, isSameMonth, isBefore, startOfDay, parse, isValid, isWithinInterval } from 'date-fns';
13
+ import { trigger, state, style, transition, animate } from '@angular/animations';
13
14
  import * as i1$1 from '@angular/cdk/overlay';
14
15
  import { OverlayConfig } from '@angular/cdk/overlay';
15
16
  import { ComponentPortal } from '@angular/cdk/portal';
17
+ import * as i1$2 from '@angular/router';
18
+ import { RouterModule } from '@angular/router';
16
19
 
17
20
  function cn(...inputs) {
18
21
  return twMerge(clsx(inputs));
@@ -4167,21 +4170,42 @@ class AccordionItemComponent {
4167
4170
  <button
4168
4171
  type="button"
4169
4172
  (click)="toggle()"
4170
- class="flex flex-1 items-center justify-between py-4 font-medium transition-all [&[data-state=open]>i]:rotate-180"
4173
+ [attr.aria-expanded]="isOpen"
4171
4174
  [attr.data-state]="isOpen ? 'open' : 'closed'"
4175
+ class="flex flex-1 items-center justify-between py-4 font-medium transition-all group [&[data-state=open]>i]:rotate-180"
4172
4176
  >
4173
- <span class="text-left">{{ title }}</span>
4174
- <i class="ri-arrow-down-s-line text-muted-foreground transition-transform"></i>
4177
+ <span class="text-left group-hover:underline">{{ title }}</span>
4178
+ <i class="ri-arrow-down-s-line text-muted-foreground text-lg transition-transform duration-200 hover:no-underline"></i>
4175
4179
  </button>
4176
4180
 
4177
4181
  <div
4178
- *ngIf="isOpen"
4179
- class="pb-4 text-sm text-muted-foreground overflow-hidden"
4180
- >
4181
- <ng-content></ng-content>
4182
+ [@expandCollapse]="isOpen ? 'expanded' : 'collapsed'"
4183
+ class="overflow-hidden">
4184
+ <div class="pb-4 pt-0 text-sm text-muted-foreground">
4185
+ <ng-content></ng-content>
4186
+ </div>
4182
4187
  </div>
4183
4188
  </div>
4184
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
4189
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }], animations: [
4190
+ trigger('expandCollapse', [
4191
+ state('collapsed', style({
4192
+ height: '0px',
4193
+ opacity: '0',
4194
+ overflow: 'hidden',
4195
+ visibility: 'hidden'
4196
+ })),
4197
+ state('expanded', style({
4198
+ height: '*', // "Star" means actual content height
4199
+ opacity: '1',
4200
+ overflow: 'hidden',
4201
+ visibility: 'visible'
4202
+ })),
4203
+ // Use cubic-bezier to match Tailwind/shadcn-ui default ease
4204
+ transition('collapsed <=> expanded', [
4205
+ animate('300ms cubic-bezier(0.87, 0, 0.13, 1)')
4206
+ ])
4207
+ ])
4208
+ ] });
4185
4209
  }
4186
4210
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AccordionItemComponent, decorators: [{
4187
4211
  type: Component,
@@ -4189,23 +4213,45 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
4189
4213
  selector: 'tolle-accordion-item',
4190
4214
  standalone: true,
4191
4215
  imports: [CommonModule],
4216
+ animations: [
4217
+ trigger('expandCollapse', [
4218
+ state('collapsed', style({
4219
+ height: '0px',
4220
+ opacity: '0',
4221
+ overflow: 'hidden',
4222
+ visibility: 'hidden'
4223
+ })),
4224
+ state('expanded', style({
4225
+ height: '*', // "Star" means actual content height
4226
+ opacity: '1',
4227
+ overflow: 'hidden',
4228
+ visibility: 'visible'
4229
+ })),
4230
+ // Use cubic-bezier to match Tailwind/shadcn-ui default ease
4231
+ transition('collapsed <=> expanded', [
4232
+ animate('300ms cubic-bezier(0.87, 0, 0.13, 1)')
4233
+ ])
4234
+ ])
4235
+ ],
4192
4236
  template: `
4193
4237
  <div [class]="cn('flex flex-col border-b border-border', class)">
4194
4238
  <button
4195
4239
  type="button"
4196
4240
  (click)="toggle()"
4197
- class="flex flex-1 items-center justify-between py-4 font-medium transition-all [&[data-state=open]>i]:rotate-180"
4241
+ [attr.aria-expanded]="isOpen"
4198
4242
  [attr.data-state]="isOpen ? 'open' : 'closed'"
4243
+ class="flex flex-1 items-center justify-between py-4 font-medium transition-all group [&[data-state=open]>i]:rotate-180"
4199
4244
  >
4200
- <span class="text-left">{{ title }}</span>
4201
- <i class="ri-arrow-down-s-line text-muted-foreground transition-transform"></i>
4245
+ <span class="text-left group-hover:underline">{{ title }}</span>
4246
+ <i class="ri-arrow-down-s-line text-muted-foreground text-lg transition-transform duration-200 hover:no-underline"></i>
4202
4247
  </button>
4203
4248
 
4204
4249
  <div
4205
- *ngIf="isOpen"
4206
- class="pb-4 text-sm text-muted-foreground overflow-hidden"
4207
- >
4208
- <ng-content></ng-content>
4250
+ [@expandCollapse]="isOpen ? 'expanded' : 'collapsed'"
4251
+ class="overflow-hidden">
4252
+ <div class="pb-4 pt-0 text-sm text-muted-foreground">
4253
+ <ng-content></ng-content>
4254
+ </div>
4209
4255
  </div>
4210
4256
  </div>
4211
4257
  `
@@ -4223,31 +4269,36 @@ class AccordionComponent {
4223
4269
  class = '';
4224
4270
  items;
4225
4271
  ngAfterContentInit() {
4272
+ // 1. Assign IDs and Listeners on load
4273
+ this.initItems();
4274
+ // 2. Re-init if items change dynamically (optional but good for robustness)
4275
+ this.items.changes.subscribe(() => this.initItems());
4276
+ }
4277
+ initItems() {
4226
4278
  this.items.forEach((item, index) => {
4227
- // Assign unique ID if none provided
4279
+ // Auto-assign ID if missing
4228
4280
  if (item.id === undefined)
4229
- item.id = index;
4230
- // Hook into the item's toggle event
4281
+ item.id = `accordion-item-${index}`;
4282
+ // Set up the toggle bridge
4231
4283
  item.onToggle = (id) => this.handleToggle(id);
4232
4284
  });
4233
4285
  }
4234
4286
  handleToggle(selectedId) {
4235
4287
  this.items.forEach(item => {
4236
4288
  if (item.id === selectedId) {
4289
+ // Toggle the clicked item
4237
4290
  item.isOpen = !item.isOpen;
4238
4291
  }
4239
- else {
4240
- // If type is 'single', close all other items
4241
- if (this.type === 'single') {
4242
- item.isOpen = false;
4243
- }
4292
+ else if (this.type === 'single') {
4293
+ // Close others if in single mode
4294
+ item.isOpen = false;
4244
4295
  }
4245
4296
  });
4246
4297
  }
4247
4298
  cn = cn;
4248
4299
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AccordionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
4249
4300
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: AccordionComponent, isStandalone: true, selector: "tolle-accordion", inputs: { type: "type", class: "class" }, queries: [{ propertyName: "items", predicate: AccordionItemComponent }], ngImport: i0, template: `
4250
- <div [class]="cn('w-full border-t border-border', class)">
4301
+ <div [class]="cn('w-full', class)">
4251
4302
  <ng-content></ng-content>
4252
4303
  </div>
4253
4304
  `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }] });
@@ -4257,9 +4308,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
4257
4308
  args: [{
4258
4309
  selector: 'tolle-accordion',
4259
4310
  standalone: true,
4260
- imports: [CommonModule],
4311
+ imports: [CommonModule], // No AccordionItemComponent import needed here if projected via ng-content
4261
4312
  template: `
4262
- <div [class]="cn('w-full border-t border-border', class)">
4313
+ <div [class]="cn('w-full', class)">
4263
4314
  <ng-content></ng-content>
4264
4315
  </div>
4265
4316
  `
@@ -5471,13 +5522,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
5471
5522
  type: Input
5472
5523
  }] } });
5473
5524
 
5474
- const alertVariants = cva("relative w-full rounded-lg border p-4 transition-all duration-300 [&>i~div]:pl-7 [&>i]:absolute [&>i]:left-4 [&>i]:top-4 [&>i]:text-foreground", {
5525
+ const alertVariants = cva("relative w-full rounded-lg border p-4 [&>i+div]:translate-y-[-3px] [&>i]:absolute [&>i]:left-4 [&>i]:top-4 [&>i]:text-foreground [&>i~div]:pl-7", {
5475
5526
  variants: {
5476
5527
  variant: {
5477
5528
  default: "bg-background text-foreground",
5478
- destructive: "border-destructive/50 text-destructive dark:border-destructive [&>i]:text-destructive",
5479
- success: "border-emerald-500/50 text-emerald-700 dark:text-emerald-400 [&>i]:text-emerald-600",
5480
- warning: "border-amber-500/50 text-amber-700 dark:text-amber-400 [&>i]:text-amber-600",
5529
+ destructive: "border-red-500/50 text-red-500 dark:border-red-500 [&>i]:text-red-500",
5530
+ success: "border-emerald-500/50 text-emerald-700 dark:border-emerald-500 [&>i]:text-emerald-600 dark:text-emerald-400",
5531
+ warning: "border-amber-500/50 text-amber-700 dark:border-amber-500 [&>i]:text-amber-600 dark:text-amber-400",
5532
+ info: "border-blue-500/50 text-blue-700 dark:border-blue-500 [&>i]:text-blue-600 dark:text-blue-400",
5481
5533
  },
5482
5534
  },
5483
5535
  defaultVariants: {
@@ -5491,24 +5543,18 @@ class AlertComponent {
5491
5543
  dismissible = false;
5492
5544
  onClose = new EventEmitter();
5493
5545
  dismissed = false;
5494
- isDismissing = false;
5495
5546
  alertVariants = alertVariants;
5496
5547
  cn = cn;
5497
5548
  dismiss() {
5498
- this.isDismissing = true;
5499
- // Wait for animation to finish before removing from DOM
5500
- setTimeout(() => {
5501
- this.dismissed = true;
5502
- this.onClose.emit();
5503
- }, 300);
5549
+ this.dismissed = true;
5550
+ this.onClose.emit();
5504
5551
  }
5505
5552
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AlertComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5506
5553
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.14", type: AlertComponent, isStandalone: true, selector: "tolle-alert", inputs: { variant: "variant", title: "title", class: "class", dismissible: "dismissible" }, outputs: { onClose: "onClose" }, ngImport: i0, template: `
5507
5554
  <div
5508
5555
  *ngIf="!dismissed"
5556
+ @fade
5509
5557
  [class]="cn(alertVariants({ variant }), class)"
5510
- [class.opacity-0]="isDismissing"
5511
- [class.scale-95]="isDismissing"
5512
5558
  role="alert"
5513
5559
  >
5514
5560
  <ng-content select="[icon]"></ng-content>
@@ -5516,22 +5562,30 @@ class AlertComponent {
5516
5562
  <button
5517
5563
  *ngIf="dismissible"
5518
5564
  (click)="dismiss()"
5519
- class="absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 group-hover:opacity-100"
5520
- [class.opacity-100]="dismissible"
5565
+ class="absolute right-2 top-2 rounded-md p-1 opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
5566
+ type="button"
5521
5567
  >
5522
5568
  <i class="ri-close-line text-lg"></i>
5569
+ <span class="sr-only">Close</span>
5523
5570
  </button>
5524
5571
 
5525
5572
  <div>
5526
5573
  <h5 *ngIf="title" class="mb-1 font-medium leading-none tracking-tight">
5527
5574
  {{ title }}
5528
5575
  </h5>
5529
- <div class="text-sm [&_p]:leading-relaxed">
5576
+ <div class="text-sm [&_p]:leading-relaxed opacity-90">
5530
5577
  <ng-content></ng-content>
5531
5578
  </div>
5532
5579
  </div>
5533
5580
  </div>
5534
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
5581
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], animations: [
5582
+ trigger('fade', [
5583
+ transition(':leave', [
5584
+ style({ opacity: 1, transform: 'scale(1)' }),
5585
+ animate('300ms ease-in-out', style({ opacity: 0, transform: 'scale(0.95)', height: 0, margin: 0, padding: 0 }))
5586
+ ])
5587
+ ])
5588
+ ] });
5535
5589
  }
5536
5590
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: AlertComponent, decorators: [{
5537
5591
  type: Component,
@@ -5539,12 +5593,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
5539
5593
  selector: 'tolle-alert',
5540
5594
  standalone: true,
5541
5595
  imports: [CommonModule],
5596
+ animations: [
5597
+ trigger('fade', [
5598
+ transition(':leave', [
5599
+ style({ opacity: 1, transform: 'scale(1)' }),
5600
+ animate('300ms ease-in-out', style({ opacity: 0, transform: 'scale(0.95)', height: 0, margin: 0, padding: 0 }))
5601
+ ])
5602
+ ])
5603
+ ],
5542
5604
  template: `
5543
5605
  <div
5544
5606
  *ngIf="!dismissed"
5607
+ @fade
5545
5608
  [class]="cn(alertVariants({ variant }), class)"
5546
- [class.opacity-0]="isDismissing"
5547
- [class.scale-95]="isDismissing"
5548
5609
  role="alert"
5549
5610
  >
5550
5611
  <ng-content select="[icon]"></ng-content>
@@ -5552,22 +5613,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
5552
5613
  <button
5553
5614
  *ngIf="dismissible"
5554
5615
  (click)="dismiss()"
5555
- class="absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 group-hover:opacity-100"
5556
- [class.opacity-100]="dismissible"
5616
+ class="absolute right-2 top-2 rounded-md p-1 opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
5617
+ type="button"
5557
5618
  >
5558
5619
  <i class="ri-close-line text-lg"></i>
5620
+ <span class="sr-only">Close</span>
5559
5621
  </button>
5560
5622
 
5561
5623
  <div>
5562
5624
  <h5 *ngIf="title" class="mb-1 font-medium leading-none tracking-tight">
5563
5625
  {{ title }}
5564
5626
  </h5>
5565
- <div class="text-sm [&_p]:leading-relaxed">
5627
+ <div class="text-sm [&_p]:leading-relaxed opacity-90">
5566
5628
  <ng-content></ng-content>
5567
5629
  </div>
5568
5630
  </div>
5569
5631
  </div>
5570
- `,
5632
+ `
5571
5633
  }]
5572
5634
  }], propDecorators: { variant: [{
5573
5635
  type: Input
@@ -6630,6 +6692,433 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
6630
6692
  args: ['itemEls']
6631
6693
  }] } });
6632
6694
 
6695
+ class SidebarComponent {
6696
+ items = [];
6697
+ collapsed = false;
6698
+ class = '';
6699
+ expandedParents = new Set();
6700
+ expandedChildren = new Set();
6701
+ cn = cn;
6702
+ ngOnChanges(changes) {
6703
+ if (changes['collapsed'] && this.collapsed) {
6704
+ this.collapseAll();
6705
+ return;
6706
+ }
6707
+ if (changes['items']) {
6708
+ this.initializeState();
6709
+ }
6710
+ }
6711
+ // --- STATE INIT ---
6712
+ initializeState() {
6713
+ this.collapseAll();
6714
+ if (!this.items)
6715
+ return;
6716
+ this.items.forEach((group, gIndex) => {
6717
+ group.items.forEach((item, iIndex) => {
6718
+ const parentId = this.getParentId(group, item, gIndex, iIndex);
6719
+ if (item.expanded) {
6720
+ this.expandedParents.add(parentId);
6721
+ }
6722
+ if (item.items) {
6723
+ item.items.forEach((subItem, sIndex) => {
6724
+ if (subItem.expanded) {
6725
+ const childId = this.getChildId(group, item, subItem, gIndex, iIndex, sIndex);
6726
+ this.expandedChildren.add(childId);
6727
+ this.expandedParents.add(parentId);
6728
+ }
6729
+ });
6730
+ }
6731
+ });
6732
+ });
6733
+ }
6734
+ // --- ID GENERATORS (Now using Group Index correctly) ---
6735
+ getGroupId(group, index) {
6736
+ return group.id || group.title || `g-${index}`;
6737
+ }
6738
+ getParentId(group, item, gIndex, iIndex) {
6739
+ const groupId = this.getGroupId(group, gIndex);
6740
+ const itemId = item.id || item.title || `p-${iIndex}`;
6741
+ return `${groupId}__${itemId}`;
6742
+ }
6743
+ getChildId(group, parent, subItem, gIndex, iIndex, sIndex) {
6744
+ const parentId = this.getParentId(group, parent, gIndex, iIndex);
6745
+ const subItemId = subItem.id || subItem.title || `c-${sIndex}`;
6746
+ return `${parentId}__${subItemId}`;
6747
+ }
6748
+ // --- ACTIONS ---
6749
+ toggleParent(group, item, gIndex, iIndex) {
6750
+ if (this.collapsed)
6751
+ return;
6752
+ const id = this.getParentId(group, item, gIndex, iIndex);
6753
+ // Create new Set reference to ensure Change Detection triggers
6754
+ const newSet = new Set(this.expandedParents);
6755
+ if (newSet.has(id)) {
6756
+ newSet.delete(id);
6757
+ }
6758
+ else {
6759
+ newSet.add(id);
6760
+ }
6761
+ this.expandedParents = newSet;
6762
+ }
6763
+ toggleChild(group, parent, subItem, gIndex, iIndex, sIndex) {
6764
+ const id = this.getChildId(group, parent, subItem, gIndex, iIndex, sIndex);
6765
+ // Create new Set reference
6766
+ const newSet = new Set(this.expandedChildren);
6767
+ if (newSet.has(id)) {
6768
+ newSet.delete(id);
6769
+ }
6770
+ else {
6771
+ newSet.add(id);
6772
+ }
6773
+ this.expandedChildren = newSet;
6774
+ }
6775
+ collapseAll() {
6776
+ this.expandedParents = new Set();
6777
+ this.expandedChildren = new Set();
6778
+ }
6779
+ // --- TEMPLATE HELPERS ---
6780
+ isParentExpanded(group, item, gIndex, iIndex) {
6781
+ return this.expandedParents.has(this.getParentId(group, item, gIndex, iIndex));
6782
+ }
6783
+ isChildExpanded(group, parent, subItem, gIndex, iIndex, sIndex) {
6784
+ return this.expandedChildren.has(this.getChildId(group, parent, subItem, gIndex, iIndex, sIndex));
6785
+ }
6786
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SidebarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6787
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: SidebarComponent, isStandalone: true, selector: "tolle-sidebar", inputs: { items: "items", collapsed: "collapsed", class: "class" }, usesOnChanges: true, ngImport: i0, template: `
6788
+ <aside [class]="cn(
6789
+ 'flex flex-col h-full border-r bg-background transition-[width] duration-300 ease-in-out shrink-0 overflow-hidden',
6790
+ collapsed ? 'w-16' : 'w-64',
6791
+ class
6792
+ )">
6793
+ <div class="flex h-14 shrink-0 items-center border-b px-3 overflow-hidden whitespace-nowrap">
6794
+ <ng-content select="[header]"></ng-content>
6795
+ </div>
6796
+
6797
+ <div class="flex-1 min-h-0 overflow-y-auto overflow-x-hidden py-4 px-3 custom-scrollbar">
6798
+
6799
+ @for (group of items; track group.id || group.title || $index; let gIndex = $index) {
6800
+ <div class="mb-6">
6801
+
6802
+ <div [class]="cn(
6803
+ 'mb-2 px-2 text-xs font-semibold uppercase tracking-wider text-muted-foreground/70 transition-all duration-200 overflow-hidden',
6804
+ collapsed ? 'opacity-0 h-0 mb-0 px-0' : 'opacity-100 h-auto'
6805
+ )">
6806
+ {{ group.title }}
6807
+ </div>
6808
+
6809
+ <div class="flex flex-col gap-1">
6810
+ @for (item of group.items; track item.id || item.title || $index; let i = $index) {
6811
+
6812
+ @if (item.items && item.items.length) {
6813
+ <div class="relative">
6814
+ <button
6815
+ type="button"
6816
+ (click)="toggleParent(group, item, gIndex, i)"
6817
+ [disabled]="collapsed"
6818
+ [attr.aria-expanded]="isParentExpanded(group, item, gIndex, i)"
6819
+ [class]="cn(
6820
+ 'group w-full relative flex items-center rounded-md px-3 py-2 text-sm font-medium transition-colors',
6821
+ 'hover:bg-accent hover:text-accent-foreground',
6822
+ isParentExpanded(group, item, gIndex, i) ? 'text-foreground bg-accent/50' : 'text-muted-foreground',
6823
+ collapsed ? 'justify-center' : 'justify-start'
6824
+ )"
6825
+ >
6826
+ <i [class]="cn(
6827
+ item.icon || 'ri-circle-fill',
6828
+ 'h-4 w-4 text-lg shrink-0 transition-transform',
6829
+ !collapsed && 'group-hover:scale-110'
6830
+ )"></i>
6831
+
6832
+ <span [class]="cn(
6833
+ 'truncate transition-all duration-300',
6834
+ collapsed ? 'opacity-0 w-0 ml-0' : 'opacity-100 w-auto ml-3'
6835
+ )">
6836
+ {{ item.title }}
6837
+ </span>
6838
+
6839
+ @if (!collapsed) {
6840
+ <i [class]="cn(
6841
+ 'ri-arrow-down-s-line ml-auto transition-transform duration-300',
6842
+ isParentExpanded(group, item, gIndex, i) ? 'rotate-180' : ''
6843
+ )"></i>
6844
+ }
6845
+ </button>
6846
+
6847
+ <div [class]="cn(
6848
+ 'grid transition-[grid-template-rows] duration-300 ease-in-out',
6849
+ isParentExpanded(group, item, gIndex, i) && !collapsed ? 'grid-rows-[1fr] opacity-100' : 'grid-rows-[0fr] opacity-0'
6850
+ )">
6851
+ <div class="overflow-hidden">
6852
+ <div class="flex flex-col gap-1 mt-1 ml-4 border-l border-border/50 pl-2">
6853
+
6854
+ @for (subItem of item.items; track subItem.id || subItem.title || $index; let j = $index) {
6855
+
6856
+ @if (subItem.items && subItem.items.length) {
6857
+ <div class="relative">
6858
+ <button
6859
+ type="button"
6860
+ (click)="toggleChild(group, item, subItem, gIndex, i, j); $event.stopPropagation()"
6861
+ [class]="cn(
6862
+ 'group w-full relative flex items-center rounded-md px-3 py-1.5 text-sm font-medium transition-colors',
6863
+ 'hover:bg-accent hover:text-accent-foreground text-muted-foreground',
6864
+ isChildExpanded(group, item, subItem, gIndex, i, j) ? 'text-foreground bg-accent/30' : ''
6865
+ )"
6866
+ >
6867
+ <span class="w-1.5 h-1.5 rounded-full bg-border shrink-0"></span>
6868
+ <span class="ml-2 truncate">{{ subItem.title }}</span>
6869
+ <i [class]="cn(
6870
+ 'ri-arrow-right-s-line ml-auto transition-transform duration-300 text-xs',
6871
+ isChildExpanded(group, item, subItem, gIndex, i, j) ? 'rotate-90' : ''
6872
+ )"></i>
6873
+ </button>
6874
+
6875
+ <div [class]="cn(
6876
+ 'grid transition-[grid-template-rows] duration-300 ease-in-out',
6877
+ isChildExpanded(group, item, subItem, gIndex, i, j) ? 'grid-rows-[1fr] opacity-100' : 'grid-rows-[0fr] opacity-0'
6878
+ )">
6879
+ <div class="overflow-hidden">
6880
+ <div class="flex flex-col gap-1 mt-1 ml-4 border-l border-border/30 pl-2">
6881
+ @for (grandChild of subItem.items; track grandChild.title) {
6882
+ <a
6883
+ [routerLink]="grandChild.url"
6884
+ routerLinkActive="bg-accent/50 text-accent-foreground"
6885
+ [routerLinkActiveOptions]="{ exact: true }"
6886
+ class="flex items-center gap-2 hover:no-underline rounded-md px-3 py-1.5 text-sm transition-colors hover:bg-accent hover:text-accent-foreground text-muted-foreground whitespace-nowrap"
6887
+ >
6888
+ <span class="w-1.5 h-1.5 rounded-full bg-border/70 shrink-0"></span>
6889
+ <span class="truncate">{{ grandChild.title }}</span>
6890
+ </a>
6891
+ }
6892
+ </div>
6893
+ </div>
6894
+ </div>
6895
+ </div>
6896
+ }
6897
+ @else {
6898
+ <a
6899
+ [routerLink]="subItem.url"
6900
+ routerLinkActive="bg-accent/50 text-accent-foreground"
6901
+ [routerLinkActiveOptions]="{ exact: true }"
6902
+ class="flex items-center gap-2 hover:no-underline rounded-md px-3 py-1.5 text-sm transition-colors hover:bg-accent hover:text-accent-foreground text-muted-foreground whitespace-nowrap"
6903
+ >
6904
+ <span class="w-1.5 h-1.5 rounded-full bg-border shrink-0"></span>
6905
+ <span class="truncate">{{ subItem.title }}</span>
6906
+ </a>
6907
+ }
6908
+ }
6909
+ </div>
6910
+ </div>
6911
+ </div>
6912
+ </div>
6913
+ }
6914
+ @else {
6915
+ <a
6916
+ [routerLink]="item.url"
6917
+ routerLinkActive="bg-primary/10 text-primary"
6918
+ [routerLinkActiveOptions]="{ exact: true }"
6919
+ [class]="cn(
6920
+ 'group relative hover:no-underline flex items-center rounded-md px-3 py-2 text-sm font-medium transition-colors',
6921
+ 'hover:bg-accent hover:text-accent-foreground text-muted-foreground',
6922
+ collapsed ? 'justify-center' : 'justify-start'
6923
+ )"
6924
+ >
6925
+ <i [class]="cn(
6926
+ item.icon || 'ri-circle-fill',
6927
+ 'h-4 w-4 text-lg shrink-0 transition-transform',
6928
+ !collapsed && 'group-hover:scale-110'
6929
+ )"></i>
6930
+ <span [class]="cn(
6931
+ 'truncate transition-all duration-300',
6932
+ collapsed ? 'opacity-0 w-0 ml-0' : 'opacity-100 w-auto ml-3'
6933
+ )">
6934
+ {{ item.title }}
6935
+ </span>
6936
+ </a>
6937
+ }
6938
+ }
6939
+ </div>
6940
+ </div>
6941
+ }
6942
+ </div>
6943
+
6944
+ <div class="border-t shrink-0 p-3 overflow-hidden whitespace-nowrap">
6945
+ <ng-content select="[footer]"></ng-content>
6946
+ </div>
6947
+ </aside>
6948
+ `, isInline: true, styles: [":host{display:block;height:100%}.custom-scrollbar{scrollbar-width:thin;scrollbar-color:rgba(156,163,175,.3) transparent}.custom-scrollbar::-webkit-scrollbar{width:4px}.custom-scrollbar::-webkit-scrollbar-track{background:transparent}.custom-scrollbar::-webkit-scrollbar-thumb{background-color:#9ca3af4d;border-radius:20px}button:disabled{cursor:not-allowed;opacity:.6}*{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ml-4{margin-left:1rem}.border-l{border-left-width:1px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i1$2.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }] });
6949
+ }
6950
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: SidebarComponent, decorators: [{
6951
+ type: Component,
6952
+ args: [{ selector: 'tolle-sidebar', standalone: true, imports: [CommonModule, RouterModule], template: `
6953
+ <aside [class]="cn(
6954
+ 'flex flex-col h-full border-r bg-background transition-[width] duration-300 ease-in-out shrink-0 overflow-hidden',
6955
+ collapsed ? 'w-16' : 'w-64',
6956
+ class
6957
+ )">
6958
+ <div class="flex h-14 shrink-0 items-center border-b px-3 overflow-hidden whitespace-nowrap">
6959
+ <ng-content select="[header]"></ng-content>
6960
+ </div>
6961
+
6962
+ <div class="flex-1 min-h-0 overflow-y-auto overflow-x-hidden py-4 px-3 custom-scrollbar">
6963
+
6964
+ @for (group of items; track group.id || group.title || $index; let gIndex = $index) {
6965
+ <div class="mb-6">
6966
+
6967
+ <div [class]="cn(
6968
+ 'mb-2 px-2 text-xs font-semibold uppercase tracking-wider text-muted-foreground/70 transition-all duration-200 overflow-hidden',
6969
+ collapsed ? 'opacity-0 h-0 mb-0 px-0' : 'opacity-100 h-auto'
6970
+ )">
6971
+ {{ group.title }}
6972
+ </div>
6973
+
6974
+ <div class="flex flex-col gap-1">
6975
+ @for (item of group.items; track item.id || item.title || $index; let i = $index) {
6976
+
6977
+ @if (item.items && item.items.length) {
6978
+ <div class="relative">
6979
+ <button
6980
+ type="button"
6981
+ (click)="toggleParent(group, item, gIndex, i)"
6982
+ [disabled]="collapsed"
6983
+ [attr.aria-expanded]="isParentExpanded(group, item, gIndex, i)"
6984
+ [class]="cn(
6985
+ 'group w-full relative flex items-center rounded-md px-3 py-2 text-sm font-medium transition-colors',
6986
+ 'hover:bg-accent hover:text-accent-foreground',
6987
+ isParentExpanded(group, item, gIndex, i) ? 'text-foreground bg-accent/50' : 'text-muted-foreground',
6988
+ collapsed ? 'justify-center' : 'justify-start'
6989
+ )"
6990
+ >
6991
+ <i [class]="cn(
6992
+ item.icon || 'ri-circle-fill',
6993
+ 'h-4 w-4 text-lg shrink-0 transition-transform',
6994
+ !collapsed && 'group-hover:scale-110'
6995
+ )"></i>
6996
+
6997
+ <span [class]="cn(
6998
+ 'truncate transition-all duration-300',
6999
+ collapsed ? 'opacity-0 w-0 ml-0' : 'opacity-100 w-auto ml-3'
7000
+ )">
7001
+ {{ item.title }}
7002
+ </span>
7003
+
7004
+ @if (!collapsed) {
7005
+ <i [class]="cn(
7006
+ 'ri-arrow-down-s-line ml-auto transition-transform duration-300',
7007
+ isParentExpanded(group, item, gIndex, i) ? 'rotate-180' : ''
7008
+ )"></i>
7009
+ }
7010
+ </button>
7011
+
7012
+ <div [class]="cn(
7013
+ 'grid transition-[grid-template-rows] duration-300 ease-in-out',
7014
+ isParentExpanded(group, item, gIndex, i) && !collapsed ? 'grid-rows-[1fr] opacity-100' : 'grid-rows-[0fr] opacity-0'
7015
+ )">
7016
+ <div class="overflow-hidden">
7017
+ <div class="flex flex-col gap-1 mt-1 ml-4 border-l border-border/50 pl-2">
7018
+
7019
+ @for (subItem of item.items; track subItem.id || subItem.title || $index; let j = $index) {
7020
+
7021
+ @if (subItem.items && subItem.items.length) {
7022
+ <div class="relative">
7023
+ <button
7024
+ type="button"
7025
+ (click)="toggleChild(group, item, subItem, gIndex, i, j); $event.stopPropagation()"
7026
+ [class]="cn(
7027
+ 'group w-full relative flex items-center rounded-md px-3 py-1.5 text-sm font-medium transition-colors',
7028
+ 'hover:bg-accent hover:text-accent-foreground text-muted-foreground',
7029
+ isChildExpanded(group, item, subItem, gIndex, i, j) ? 'text-foreground bg-accent/30' : ''
7030
+ )"
7031
+ >
7032
+ <span class="w-1.5 h-1.5 rounded-full bg-border shrink-0"></span>
7033
+ <span class="ml-2 truncate">{{ subItem.title }}</span>
7034
+ <i [class]="cn(
7035
+ 'ri-arrow-right-s-line ml-auto transition-transform duration-300 text-xs',
7036
+ isChildExpanded(group, item, subItem, gIndex, i, j) ? 'rotate-90' : ''
7037
+ )"></i>
7038
+ </button>
7039
+
7040
+ <div [class]="cn(
7041
+ 'grid transition-[grid-template-rows] duration-300 ease-in-out',
7042
+ isChildExpanded(group, item, subItem, gIndex, i, j) ? 'grid-rows-[1fr] opacity-100' : 'grid-rows-[0fr] opacity-0'
7043
+ )">
7044
+ <div class="overflow-hidden">
7045
+ <div class="flex flex-col gap-1 mt-1 ml-4 border-l border-border/30 pl-2">
7046
+ @for (grandChild of subItem.items; track grandChild.title) {
7047
+ <a
7048
+ [routerLink]="grandChild.url"
7049
+ routerLinkActive="bg-accent/50 text-accent-foreground"
7050
+ [routerLinkActiveOptions]="{ exact: true }"
7051
+ class="flex items-center gap-2 hover:no-underline rounded-md px-3 py-1.5 text-sm transition-colors hover:bg-accent hover:text-accent-foreground text-muted-foreground whitespace-nowrap"
7052
+ >
7053
+ <span class="w-1.5 h-1.5 rounded-full bg-border/70 shrink-0"></span>
7054
+ <span class="truncate">{{ grandChild.title }}</span>
7055
+ </a>
7056
+ }
7057
+ </div>
7058
+ </div>
7059
+ </div>
7060
+ </div>
7061
+ }
7062
+ @else {
7063
+ <a
7064
+ [routerLink]="subItem.url"
7065
+ routerLinkActive="bg-accent/50 text-accent-foreground"
7066
+ [routerLinkActiveOptions]="{ exact: true }"
7067
+ class="flex items-center gap-2 hover:no-underline rounded-md px-3 py-1.5 text-sm transition-colors hover:bg-accent hover:text-accent-foreground text-muted-foreground whitespace-nowrap"
7068
+ >
7069
+ <span class="w-1.5 h-1.5 rounded-full bg-border shrink-0"></span>
7070
+ <span class="truncate">{{ subItem.title }}</span>
7071
+ </a>
7072
+ }
7073
+ }
7074
+ </div>
7075
+ </div>
7076
+ </div>
7077
+ </div>
7078
+ }
7079
+ @else {
7080
+ <a
7081
+ [routerLink]="item.url"
7082
+ routerLinkActive="bg-primary/10 text-primary"
7083
+ [routerLinkActiveOptions]="{ exact: true }"
7084
+ [class]="cn(
7085
+ 'group relative hover:no-underline flex items-center rounded-md px-3 py-2 text-sm font-medium transition-colors',
7086
+ 'hover:bg-accent hover:text-accent-foreground text-muted-foreground',
7087
+ collapsed ? 'justify-center' : 'justify-start'
7088
+ )"
7089
+ >
7090
+ <i [class]="cn(
7091
+ item.icon || 'ri-circle-fill',
7092
+ 'h-4 w-4 text-lg shrink-0 transition-transform',
7093
+ !collapsed && 'group-hover:scale-110'
7094
+ )"></i>
7095
+ <span [class]="cn(
7096
+ 'truncate transition-all duration-300',
7097
+ collapsed ? 'opacity-0 w-0 ml-0' : 'opacity-100 w-auto ml-3'
7098
+ )">
7099
+ {{ item.title }}
7100
+ </span>
7101
+ </a>
7102
+ }
7103
+ }
7104
+ </div>
7105
+ </div>
7106
+ }
7107
+ </div>
7108
+
7109
+ <div class="border-t shrink-0 p-3 overflow-hidden whitespace-nowrap">
7110
+ <ng-content select="[footer]"></ng-content>
7111
+ </div>
7112
+ </aside>
7113
+ `, styles: [":host{display:block;height:100%}.custom-scrollbar{scrollbar-width:thin;scrollbar-color:rgba(156,163,175,.3) transparent}.custom-scrollbar::-webkit-scrollbar{width:4px}.custom-scrollbar::-webkit-scrollbar-track{background:transparent}.custom-scrollbar::-webkit-scrollbar-thumb{background-color:#9ca3af4d;border-radius:20px}button:disabled{cursor:not-allowed;opacity:.6}*{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ml-4{margin-left:1rem}.border-l{border-left-width:1px}\n"] }]
7114
+ }], propDecorators: { items: [{
7115
+ type: Input
7116
+ }], collapsed: [{
7117
+ type: Input
7118
+ }], class: [{
7119
+ type: Input
7120
+ }] } });
7121
+
6633
7122
  /*
6634
7123
  * Public API Surface of tolle
6635
7124
  */
@@ -6638,5 +7127,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
6638
7127
  * Generated bundle index. Do not edit.
6639
7128
  */
6640
7129
 
6641
- export { AccordionComponent, AccordionItemComponent, AlertComponent, AvatarComponent, AvatarFallbackComponent, BadgeComponent, BreadcrumbComponent, BreadcrumbItemComponent, BreadcrumbLinkComponent, BreadcrumbSeparatorComponent, ButtonComponent, ButtonGroupComponent, CalendarComponent, CardComponent, CardContentComponent, CardFooterComponent, CardHeaderComponent, CardTitleComponent, CheckboxComponent, DataTableComponent, DatePickerComponent, DateRangePickerComponent, DropdownItemComponent, DropdownLabelComponent, DropdownMenuComponent, DropdownSeparatorComponent, DropdownTriggerDirective, EmptyStateComponent, InputComponent, MaskedInputComponent, Modal, ModalComponent, ModalRef, ModalService, ModalStackService, MultiSelectComponent, OtpComponent, OtpGroupComponent, OtpSlotComponent, PaginationComponent, PopoverComponent, PopoverContentComponent, RadioGroupComponent, RadioItemComponent, RangeCalendarComponent, SegmentedComponent, SelectComponent, SelectGroupComponent, SelectItemComponent, SelectSeparatorComponent, SkeletonComponent, SwitchComponent, TOLLE_CONFIG, TextareaComponent, ThemeService, ToastContainerComponent, ToastService, TolleCellDirective, TooltipDirective, cn, provideTolleConfig };
7130
+ export { AccordionComponent, AccordionItemComponent, AlertComponent, AvatarComponent, AvatarFallbackComponent, BadgeComponent, BreadcrumbComponent, BreadcrumbItemComponent, BreadcrumbLinkComponent, BreadcrumbSeparatorComponent, ButtonComponent, ButtonGroupComponent, CalendarComponent, CardComponent, CardContentComponent, CardFooterComponent, CardHeaderComponent, CardTitleComponent, CheckboxComponent, DataTableComponent, DatePickerComponent, DateRangePickerComponent, DropdownItemComponent, DropdownLabelComponent, DropdownMenuComponent, DropdownSeparatorComponent, DropdownTriggerDirective, EmptyStateComponent, InputComponent, MaskedInputComponent, Modal, ModalComponent, ModalRef, ModalService, ModalStackService, MultiSelectComponent, OtpComponent, OtpGroupComponent, OtpSlotComponent, PaginationComponent, PopoverComponent, PopoverContentComponent, RadioGroupComponent, RadioItemComponent, RangeCalendarComponent, SegmentedComponent, SelectComponent, SelectGroupComponent, SelectItemComponent, SelectSeparatorComponent, SidebarComponent, SkeletonComponent, SwitchComponent, TOLLE_CONFIG, TextareaComponent, ThemeService, ToastContainerComponent, ToastService, TolleCellDirective, TooltipDirective, cn, provideTolleConfig };
6642
7131
  //# sourceMappingURL=tolle-ui.mjs.map