@sonny-ui/core 0.1.0-alpha.11 → 0.1.0-alpha.13

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.
@@ -3,7 +3,7 @@ import { twMerge } from 'tailwind-merge';
3
3
  import { cva } from 'class-variance-authority';
4
4
  export { cva } from 'class-variance-authority';
5
5
  import * as i0 from '@angular/core';
6
- import { InjectionToken, makeEnvironmentProviders, inject, PLATFORM_ID, signal, computed, Injectable, input, Directive, ChangeDetectionStrategy, Component, ElementRef, model, viewChild, effect, forwardRef, HostListener, Injector, afterNextRender, Renderer2, output, contentChildren } from '@angular/core';
6
+ import { inject, PLATFORM_ID, signal, computed, Injectable, InjectionToken, makeEnvironmentProviders, provideEnvironmentInitializer, input, Directive, ChangeDetectionStrategy, Component, ElementRef, model, viewChild, forwardRef, HostListener, Injector, afterNextRender, effect, Renderer2, output, contentChildren } from '@angular/core';
7
7
  import { DOCUMENT, isPlatformBrowser } from '@angular/common';
8
8
  import { Dialog, DialogRef } from '@angular/cdk/dialog';
9
9
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
@@ -13,27 +13,22 @@ function cn(...inputs) {
13
13
  return twMerge(clsx(inputs));
14
14
  }
15
15
 
16
- const DEFAULT_CONFIG = { prefix: 'sny', defaultTheme: 'light' };
17
- const SNY_CONFIG = new InjectionToken('SNY_CONFIG', {
18
- providedIn: 'root',
19
- factory: () => DEFAULT_CONFIG,
20
- });
21
- function provideSonnyUI(config = {}) {
22
- return makeEnvironmentProviders([
23
- { provide: SNY_CONFIG, useValue: { ...DEFAULT_CONFIG, ...config } },
24
- ]);
25
- }
26
-
16
+ const STORAGE_KEY = 'sny-theme';
27
17
  class ThemeService {
28
18
  document = inject(DOCUMENT);
29
19
  isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
20
+ config = inject(SNY_CONFIG);
30
21
  _theme = signal('light', ...(ngDevMode ? [{ debugName: "_theme" }] : /* istanbul ignore next */ []));
31
22
  theme = this._theme.asReadonly();
32
23
  isDark = computed(() => this._theme() === 'dark', ...(ngDevMode ? [{ debugName: "isDark" }] : /* istanbul ignore next */ []));
24
+ constructor() {
25
+ this.setTheme(this.resolveInitialTheme());
26
+ }
33
27
  setTheme(theme) {
34
28
  this._theme.set(theme);
35
29
  if (!this.isBrowser)
36
30
  return;
31
+ localStorage.setItem(STORAGE_KEY, theme);
37
32
  const root = this.document.documentElement;
38
33
  root.classList.remove('dark');
39
34
  root.removeAttribute('data-theme');
@@ -48,13 +43,40 @@ class ThemeService {
48
43
  toggleDark() {
49
44
  this.setTheme(this._theme() === 'dark' ? 'light' : 'dark');
50
45
  }
46
+ resolveInitialTheme() {
47
+ if (this.isBrowser) {
48
+ const stored = localStorage.getItem(STORAGE_KEY);
49
+ if (stored)
50
+ return stored;
51
+ }
52
+ const defaultTheme = this.config.defaultTheme ?? 'light';
53
+ if (defaultTheme === 'system') {
54
+ if (this.isBrowser && window.matchMedia('(prefers-color-scheme: dark)').matches) {
55
+ return 'dark';
56
+ }
57
+ return 'light';
58
+ }
59
+ return defaultTheme;
60
+ }
51
61
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ThemeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
52
62
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ThemeService, providedIn: 'root' });
53
63
  }
54
64
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ThemeService, decorators: [{
55
65
  type: Injectable,
56
66
  args: [{ providedIn: 'root' }]
57
- }] });
67
+ }], ctorParameters: () => [] });
68
+
69
+ const DEFAULT_CONFIG = { prefix: 'sny', defaultTheme: 'light' };
70
+ const SNY_CONFIG = new InjectionToken('SNY_CONFIG', {
71
+ providedIn: 'root',
72
+ factory: () => DEFAULT_CONFIG,
73
+ });
74
+ function provideSonnyUI(config = {}) {
75
+ return makeEnvironmentProviders([
76
+ { provide: SNY_CONFIG, useValue: { ...DEFAULT_CONFIG, ...config } },
77
+ provideEnvironmentInitializer(() => inject(ThemeService)),
78
+ ]);
79
+ }
58
80
 
59
81
  const buttonVariants = cva('inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-sm text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0', {
60
82
  variants: {
@@ -85,7 +107,7 @@ class SnyButtonDirective {
85
107
  disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
86
108
  loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
87
109
  class = input('', ...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
88
- computedClass = computed(() => cn(buttonVariants({ variant: this.variant(), size: this.size() }), this.loading() && 'relative cursor-wait', this.class()), ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
110
+ computedClass = computed(() => cn(buttonVariants({ variant: this.variant(), size: this.size() }), this.loading() && 'cursor-wait opacity-70', this.class()), ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
89
111
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyButtonDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
90
112
  static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "21.2.5", type: SnyButtonDirective, isStandalone: true, selector: "button[snyBtn], a[snyBtn]", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "computedClass()", "attr.aria-disabled": "disabled() || loading() || null", "attr.disabled": "disabled() || loading() || null", "attr.tabindex": "(disabled() || loading()) ? -1 : null" } }, ngImport: i0 });
91
113
  }
@@ -1057,19 +1079,7 @@ class SnyComboboxComponent {
1057
1079
  resizeHandler = null;
1058
1080
  _onChange = () => { };
1059
1081
  onTouched = () => { };
1060
- _writing = false;
1061
- constructor() {
1062
- effect(() => {
1063
- const val = this.value();
1064
- if (this._writing) {
1065
- this._writing = false;
1066
- return;
1067
- }
1068
- this._onChange(val);
1069
- });
1070
- }
1071
1082
  writeValue(val) {
1072
- this._writing = true;
1073
1083
  this.value.set(val ?? '');
1074
1084
  }
1075
1085
  registerOnChange(fn) {
@@ -1164,6 +1174,7 @@ class SnyComboboxComponent {
1164
1174
  }
1165
1175
  select(opt) {
1166
1176
  this.value.set(opt.value);
1177
+ this._onChange(opt.value);
1167
1178
  this.close();
1168
1179
  }
1169
1180
  onKeydown(event) {
@@ -1236,7 +1247,7 @@ class SnyComboboxComponent {
1236
1247
 
1237
1248
  <!-- Options list -->
1238
1249
  @if (filtered().length > 0) {
1239
- <ul role="listbox" class="max-h-60 overflow-auto p-1">
1250
+ <ul role="listbox" class="max-h-60 overflow-auto p-1 sny-scrollbar">
1240
1251
  @for (opt of filtered(); track opt.value; let i = $index) {
1241
1252
  <li
1242
1253
  role="option"
@@ -1314,7 +1325,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
1314
1325
 
1315
1326
  <!-- Options list -->
1316
1327
  @if (filtered().length > 0) {
1317
- <ul role="listbox" class="max-h-60 overflow-auto p-1">
1328
+ <ul role="listbox" class="max-h-60 overflow-auto p-1 sny-scrollbar">
1318
1329
  @for (opt of filtered(); track opt.value; let i = $index) {
1319
1330
  <li
1320
1331
  role="option"
@@ -1340,7 +1351,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
1340
1351
  }
1341
1352
  `,
1342
1353
  }]
1343
- }], ctorParameters: () => [], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], searchPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchPlaceholder", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], triggerRef: [{ type: i0.ViewChild, args: ['triggerEl', { isSignal: true }] }], searchRef: [{ type: i0.ViewChild, args: ['searchEl', { isSignal: true }] }], dropdownRef: [{ type: i0.ViewChild, args: ['dropdownEl', { isSignal: true }] }], onDocumentClick: [{
1354
+ }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], searchPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchPlaceholder", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], triggerRef: [{ type: i0.ViewChild, args: ['triggerEl', { isSignal: true }] }], searchRef: [{ type: i0.ViewChild, args: ['searchEl', { isSignal: true }] }], dropdownRef: [{ type: i0.ViewChild, args: ['dropdownEl', { isSignal: true }] }], onDocumentClick: [{
1344
1355
  type: HostListener,
1345
1356
  args: ['document:click', ['$event']]
1346
1357
  }] } });
@@ -1447,19 +1458,7 @@ class SnySwitchComponent {
1447
1458
  isDisabled = computed(() => this.disabled() || this._disabledByCva(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
1448
1459
  _onChange = () => { };
1449
1460
  onTouched = () => { };
1450
- _writing = false;
1451
- constructor() {
1452
- effect(() => {
1453
- const val = this.checked();
1454
- if (this._writing) {
1455
- this._writing = false;
1456
- return;
1457
- }
1458
- this._onChange(val);
1459
- });
1460
- }
1461
1461
  writeValue(val) {
1462
- this._writing = true;
1463
1462
  this.checked.set(val ?? false);
1464
1463
  }
1465
1464
  registerOnChange(fn) {
@@ -1471,6 +1470,11 @@ class SnySwitchComponent {
1471
1470
  setDisabledState(isDisabled) {
1472
1471
  this._disabledByCva.set(isDisabled);
1473
1472
  }
1473
+ toggle() {
1474
+ const newVal = !this.checked();
1475
+ this.checked.set(newVal);
1476
+ this._onChange(newVal);
1477
+ }
1474
1478
  trackClass = computed(() => cn(switchTrackVariants({ size: this.size() }), this.checked() ? 'bg-primary' : 'bg-input', this.class()), ...(ngDevMode ? [{ debugName: "trackClass" }] : /* istanbul ignore next */ []));
1475
1479
  thumbClass = computed(() => cn('pointer-events-none block rounded-full bg-background shadow-lg ring-0 transition-transform', switchThumbSize[this.size()], this.checked() ? switchThumbTranslate[this.size()] : 'translate-x-0'), ...(ngDevMode ? [{ debugName: "thumbClass" }] : /* istanbul ignore next */ []));
1476
1480
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnySwitchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
@@ -1483,7 +1487,7 @@ class SnySwitchComponent {
1483
1487
  [attr.aria-checked]="checked()"
1484
1488
  [disabled]="isDisabled()"
1485
1489
  [class]="trackClass()"
1486
- (click)="checked.set(!checked())"
1490
+ (click)="toggle()"
1487
1491
  (blur)="onTouched()"
1488
1492
  >
1489
1493
  <span [class]="thumbClass()"></span>
@@ -1507,14 +1511,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
1507
1511
  [attr.aria-checked]="checked()"
1508
1512
  [disabled]="isDisabled()"
1509
1513
  [class]="trackClass()"
1510
- (click)="checked.set(!checked())"
1514
+ (click)="toggle()"
1511
1515
  (blur)="onTouched()"
1512
1516
  >
1513
1517
  <span [class]="thumbClass()"></span>
1514
1518
  </button>
1515
1519
  `,
1516
1520
  }]
1517
- }], ctorParameters: () => [], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
1521
+ }], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
1518
1522
 
1519
1523
  const toggleVariants = cva('inline-flex items-center justify-center rounded-sm text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50', {
1520
1524
  variants: {
@@ -1543,19 +1547,7 @@ class SnyToggleDirective {
1543
1547
  isDisabled = computed(() => this._disabledByCva(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
1544
1548
  _onChange = () => { };
1545
1549
  onTouched = () => { };
1546
- _writing = false;
1547
- constructor() {
1548
- effect(() => {
1549
- const val = this.pressed();
1550
- if (this._writing) {
1551
- this._writing = false;
1552
- return;
1553
- }
1554
- this._onChange(val);
1555
- });
1556
- }
1557
1550
  writeValue(val) {
1558
- this._writing = true;
1559
1551
  this.pressed.set(val ?? false);
1560
1552
  }
1561
1553
  registerOnChange(fn) {
@@ -1570,7 +1562,9 @@ class SnyToggleDirective {
1570
1562
  toggle() {
1571
1563
  if (this.isDisabled())
1572
1564
  return;
1573
- this.pressed.set(!this.pressed());
1565
+ const newVal = !this.pressed();
1566
+ this.pressed.set(newVal);
1567
+ this._onChange(newVal);
1574
1568
  }
1575
1569
  computedClass = computed(() => cn(toggleVariants({ variant: this.variant(), size: this.size() }), this.pressed() ? 'bg-accent text-accent-foreground' : '', this.class()), ...(ngDevMode ? [{ debugName: "computedClass" }] : /* istanbul ignore next */ []));
1576
1570
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyToggleDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
@@ -1594,7 +1588,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
1594
1588
  '(blur)': 'onTouched()',
1595
1589
  },
1596
1590
  }]
1597
- }], ctorParameters: () => [], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], pressed: [{ type: i0.Input, args: [{ isSignal: true, alias: "pressed", required: false }] }, { type: i0.Output, args: ["pressedChange"] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
1591
+ }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], pressed: [{ type: i0.Input, args: [{ isSignal: true, alias: "pressed", required: false }] }, { type: i0.Output, args: ["pressedChange"] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
1598
1592
 
1599
1593
  const sliderTrackVariants = cva('relative w-full rounded-full bg-secondary cursor-pointer', {
1600
1594
  variants: {
@@ -1629,19 +1623,7 @@ class SnySliderComponent {
1629
1623
  upHandler = null;
1630
1624
  _onChange = () => { };
1631
1625
  onTouched = () => { };
1632
- _writing = false;
1633
- constructor() {
1634
- effect(() => {
1635
- const val = this.value();
1636
- if (this._writing) {
1637
- this._writing = false;
1638
- return;
1639
- }
1640
- this._onChange(val);
1641
- });
1642
- }
1643
1626
  writeValue(val) {
1644
- this._writing = true;
1645
1627
  this.value.set(val ?? 0);
1646
1628
  }
1647
1629
  registerOnChange(fn) {
@@ -1671,7 +1653,9 @@ class SnySliderComponent {
1671
1653
  const percent = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
1672
1654
  const raw = this.min() + percent * (this.max() - this.min());
1673
1655
  const stepped = Math.round(raw / this.step()) * this.step();
1674
- this.value.set(Math.max(this.min(), Math.min(this.max(), stepped)));
1656
+ const clamped = Math.max(this.min(), Math.min(this.max(), stepped));
1657
+ this.value.set(clamped);
1658
+ this._onChange(clamped);
1675
1659
  }
1676
1660
  onTrackMousedown(event) {
1677
1661
  if (this.isDisabled())
@@ -1708,26 +1692,31 @@ class SnySliderComponent {
1708
1692
  if (this.isDisabled())
1709
1693
  return;
1710
1694
  const step = this.step();
1695
+ let newVal;
1711
1696
  switch (event.key) {
1712
1697
  case 'ArrowRight':
1713
1698
  case 'ArrowUp':
1714
1699
  event.preventDefault();
1715
- this.value.set(Math.min(this.max(), this.value() + step));
1700
+ newVal = Math.min(this.max(), this.value() + step);
1716
1701
  break;
1717
1702
  case 'ArrowLeft':
1718
1703
  case 'ArrowDown':
1719
1704
  event.preventDefault();
1720
- this.value.set(Math.max(this.min(), this.value() - step));
1705
+ newVal = Math.max(this.min(), this.value() - step);
1721
1706
  break;
1722
1707
  case 'Home':
1723
1708
  event.preventDefault();
1724
- this.value.set(this.min());
1709
+ newVal = this.min();
1725
1710
  break;
1726
1711
  case 'End':
1727
1712
  event.preventDefault();
1728
- this.value.set(this.max());
1713
+ newVal = this.max();
1729
1714
  break;
1730
1715
  }
1716
+ if (newVal !== undefined) {
1717
+ this.value.set(newVal);
1718
+ this._onChange(newVal);
1719
+ }
1731
1720
  }
1732
1721
  removeListeners() {
1733
1722
  if (this.moveHandler) {
@@ -1806,7 +1795,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
1806
1795
  </div>
1807
1796
  `,
1808
1797
  }]
1809
- }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], trackRef: [{ type: i0.ViewChild, args: ['trackEl', { isSignal: true }] }] } });
1798
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], trackRef: [{ type: i0.ViewChild, args: ['trackEl', { isSignal: true }] }] } });
1810
1799
 
1811
1800
  const loaderVariants = cva('inline-flex items-center justify-center text-current', {
1812
1801
  variants: {
@@ -2052,19 +2041,7 @@ class SnySelectComponent {
2052
2041
  resizeHandler = null;
2053
2042
  _onChange = () => { };
2054
2043
  onTouched = () => { };
2055
- _writing = false;
2056
- constructor() {
2057
- effect(() => {
2058
- const val = this.value();
2059
- if (this._writing) {
2060
- this._writing = false;
2061
- return;
2062
- }
2063
- this._onChange(val);
2064
- });
2065
- }
2066
2044
  writeValue(val) {
2067
- this._writing = true;
2068
2045
  this.value.set(val ?? '');
2069
2046
  }
2070
2047
  registerOnChange(fn) {
@@ -2142,6 +2119,7 @@ class SnySelectComponent {
2142
2119
  }
2143
2120
  select(opt) {
2144
2121
  this.value.set(opt.value);
2122
+ this._onChange(opt.value);
2145
2123
  this.close();
2146
2124
  }
2147
2125
  onTriggerKeydown(event) {
@@ -2206,7 +2184,7 @@ class SnySelectComponent {
2206
2184
  #dropdownEl
2207
2185
  class="fixed z-50 rounded-sm border border-border bg-popover text-popover-foreground shadow-md"
2208
2186
  >
2209
- <ul role="listbox" class="max-h-60 overflow-auto p-1">
2187
+ <ul role="listbox" class="max-h-60 overflow-auto p-1 sny-scrollbar">
2210
2188
  @for (opt of options(); track opt.value; let i = $index) {
2211
2189
  <li
2212
2190
  role="option"
@@ -2264,7 +2242,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
2264
2242
  #dropdownEl
2265
2243
  class="fixed z-50 rounded-sm border border-border bg-popover text-popover-foreground shadow-md"
2266
2244
  >
2267
- <ul role="listbox" class="max-h-60 overflow-auto p-1">
2245
+ <ul role="listbox" class="max-h-60 overflow-auto p-1 sny-scrollbar">
2268
2246
  @for (opt of options(); track opt.value; let i = $index) {
2269
2247
  <li
2270
2248
  role="option"
@@ -2286,7 +2264,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
2286
2264
  }
2287
2265
  `,
2288
2266
  }]
2289
- }], ctorParameters: () => [], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], triggerRef: [{ type: i0.ViewChild, args: ['triggerEl', { isSignal: true }] }], dropdownRef: [{ type: i0.ViewChild, args: ['dropdownEl', { isSignal: true }] }], onDocumentClick: [{
2267
+ }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], triggerRef: [{ type: i0.ViewChild, args: ['triggerEl', { isSignal: true }] }], dropdownRef: [{ type: i0.ViewChild, args: ['dropdownEl', { isSignal: true }] }], onDocumentClick: [{
2290
2268
  type: HostListener,
2291
2269
  args: ['document:click', ['$event']]
2292
2270
  }] } });
@@ -3175,19 +3153,7 @@ class SnyFileInputComponent {
3175
3153
  isDragOver = signal(false, ...(ngDevMode ? [{ debugName: "isDragOver" }] : /* istanbul ignore next */ []));
3176
3154
  _onChange = () => { };
3177
3155
  onTouched = () => { };
3178
- _writing = false;
3179
- constructor() {
3180
- effect(() => {
3181
- const val = this.value();
3182
- if (this._writing) {
3183
- this._writing = false;
3184
- return;
3185
- }
3186
- this._onChange(val);
3187
- });
3188
- }
3189
3156
  writeValue(val) {
3190
- this._writing = true;
3191
3157
  this.value.set(val ?? null);
3192
3158
  }
3193
3159
  registerOnChange(fn) {
@@ -3233,6 +3199,7 @@ class SnyFileInputComponent {
3233
3199
  }
3234
3200
  clear() {
3235
3201
  this.value.set(null);
3202
+ this._onChange(null);
3236
3203
  const inputEl = this.fileInputRef()?.nativeElement;
3237
3204
  if (inputEl)
3238
3205
  inputEl.value = '';
@@ -3248,6 +3215,7 @@ class SnyFileInputComponent {
3248
3215
  }
3249
3216
  }
3250
3217
  this.value.set(files);
3218
+ this._onChange(files);
3251
3219
  this.fileChange.emit(files);
3252
3220
  }
3253
3221
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyFileInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
@@ -3311,7 +3279,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
3311
3279
  </label>
3312
3280
  `,
3313
3281
  }]
3314
- }], ctorParameters: () => [], propDecorators: { accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], maxSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSize", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], fileChange: [{ type: i0.Output, args: ["fileChange"] }], error: [{ type: i0.Output, args: ["error"] }], fileInputRef: [{ type: i0.ViewChild, args: ['fileInput', { isSignal: true }] }] } });
3282
+ }], propDecorators: { accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], maxSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSize", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], fileChange: [{ type: i0.Output, args: ["fileChange"] }], error: [{ type: i0.Output, args: ["error"] }], fileInputRef: [{ type: i0.ViewChild, args: ['fileInput', { isSignal: true }] }] } });
3315
3283
 
3316
3284
  const fieldsetVariants = cva('space-y-4', {
3317
3285
  variants: {
@@ -3404,19 +3372,7 @@ class SnyRatingComponent {
3404
3372
  hoverValue = signal(null, ...(ngDevMode ? [{ debugName: "hoverValue" }] : /* istanbul ignore next */ []));
3405
3373
  _onChange = () => { };
3406
3374
  onTouched = () => { };
3407
- _writing = false;
3408
- constructor() {
3409
- effect(() => {
3410
- const val = this.value();
3411
- if (this._writing) {
3412
- this._writing = false;
3413
- return;
3414
- }
3415
- this._onChange(val);
3416
- });
3417
- }
3418
3375
  writeValue(val) {
3419
- this._writing = true;
3420
3376
  this.value.set(val ?? 0);
3421
3377
  }
3422
3378
  registerOnChange(fn) {
@@ -3462,6 +3418,7 @@ class SnyRatingComponent {
3462
3418
  if (this.isDisabled())
3463
3419
  return;
3464
3420
  this.value.set(index);
3421
+ this._onChange(index);
3465
3422
  }
3466
3423
  onStarHover(index) {
3467
3424
  if (this.isDisabled())
@@ -3475,26 +3432,31 @@ class SnyRatingComponent {
3475
3432
  if (this.isDisabled())
3476
3433
  return;
3477
3434
  const step = this.half() ? 0.5 : 1;
3435
+ let newVal;
3478
3436
  switch (event.key) {
3479
3437
  case 'ArrowRight':
3480
3438
  case 'ArrowUp':
3481
3439
  event.preventDefault();
3482
- this.value.update((v) => Math.min(this.max(), v + step));
3440
+ newVal = Math.min(this.max(), this.value() + step);
3483
3441
  break;
3484
3442
  case 'ArrowLeft':
3485
3443
  case 'ArrowDown':
3486
3444
  event.preventDefault();
3487
- this.value.update((v) => Math.max(0, v - step));
3445
+ newVal = Math.max(0, this.value() - step);
3488
3446
  break;
3489
3447
  case 'Home':
3490
3448
  event.preventDefault();
3491
- this.value.set(0);
3449
+ newVal = 0;
3492
3450
  break;
3493
3451
  case 'End':
3494
3452
  event.preventDefault();
3495
- this.value.set(this.max());
3453
+ newVal = this.max();
3496
3454
  break;
3497
3455
  }
3456
+ if (newVal !== undefined) {
3457
+ this.value.set(newVal);
3458
+ this._onChange(newVal);
3459
+ }
3498
3460
  }
3499
3461
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyRatingComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
3500
3462
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: SnyRatingComponent, isStandalone: true, selector: "sny-rating", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, ratingVariant: { classPropertyName: "ratingVariant", publicName: "ratingVariant", isSignal: true, isRequired: false, transformFunction: null }, half: { classPropertyName: "half", publicName: "half", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { attributes: { "role": "slider", "tabindex": "0" }, listeners: { "keydown": "onKeydown($event)", "mouseleave": "onMouseLeave()", "blur": "onTouched()" }, properties: { "attr.aria-valuenow": "value()", "attr.aria-valuemin": "0", "attr.aria-valuemax": "max()", "attr.aria-label": "\"Rating\"", "attr.aria-readonly": "readonly() || null", "class": "computedClass()" } }, providers: [
@@ -3557,7 +3519,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
3557
3519
  }
3558
3520
  `,
3559
3521
  }]
3560
- }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], ratingVariant: [{ type: i0.Input, args: [{ isSignal: true, alias: "ratingVariant", required: false }] }], half: [{ type: i0.Input, args: [{ isSignal: true, alias: "half", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
3522
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], ratingVariant: [{ type: i0.Input, args: [{ isSignal: true, alias: "ratingVariant", required: false }] }], half: [{ type: i0.Input, args: [{ isSignal: true, alias: "half", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
3561
3523
 
3562
3524
  class SnyNavbarDirective {
3563
3525
  variant = input('default', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
@@ -5082,19 +5044,7 @@ class SnyCalendarComponent {
5082
5044
  weekDays = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
5083
5045
  _onChange = () => { };
5084
5046
  onTouched = () => { };
5085
- _writing = false;
5086
- constructor() {
5087
- effect(() => {
5088
- const val = this.value();
5089
- if (this._writing) {
5090
- this._writing = false;
5091
- return;
5092
- }
5093
- this._onChange(val);
5094
- });
5095
- }
5096
5047
  writeValue(val) {
5097
- this._writing = true;
5098
5048
  this.value.set(val ?? null);
5099
5049
  if (val) {
5100
5050
  this.viewDate.set(new Date(val.getFullYear(), val.getMonth(), 1));
@@ -5152,6 +5102,7 @@ class SnyCalendarComponent {
5152
5102
  }
5153
5103
  selectDate(date) {
5154
5104
  this.value.set(date);
5105
+ this._onChange(date);
5155
5106
  this.onTouched();
5156
5107
  }
5157
5108
  onKeydown(event) {
@@ -5183,6 +5134,7 @@ class SnyCalendarComponent {
5183
5134
  const next = new Date(current);
5184
5135
  next.setDate(next.getDate() + offset);
5185
5136
  this.value.set(next);
5137
+ this._onChange(next);
5186
5138
  this.viewDate.set(new Date(next.getFullYear(), next.getMonth(), 1));
5187
5139
  }
5188
5140
  createDay(date, isCurrentMonth, today, selected, minDate, maxDate) {
@@ -5289,7 +5241,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
5289
5241
  </div>
5290
5242
  `,
5291
5243
  }]
5292
- }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
5244
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
5293
5245
 
5294
5246
  class SnyValidatorDirective {
5295
5247
  control = input(null, ...(ngDevMode ? [{ debugName: "control" }] : /* istanbul ignore next */ []));