@ship-ui/core 0.18.5 → 0.18.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,18 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, inject, computed, ElementRef, Renderer2, input, ChangeDetectionStrategy, Component, viewChild, effect, HostListener, NgModule, signal, Injectable, DOCUMENT, model, output, ApplicationRef, OutputEmitterRef, TemplateRef, createComponent, isSignal, DestroyRef, PLATFORM_ID, ViewChild, Directive, contentChild, contentChildren, afterNextRender, Injector, HostBinding, untracked, runInInjectionContext, ChangeDetectorRef, viewChildren, ViewContainerRef, EnvironmentInjector } from '@angular/core';
2
+ import { InjectionToken, inject, computed, ElementRef, Renderer2, input, ChangeDetectionStrategy, Component, viewChild, effect, HostListener, NgModule, signal, Injectable, DOCUMENT, model, output, ApplicationRef, OutputEmitterRef, TemplateRef, createComponent, isSignal, DestroyRef, PLATFORM_ID, ViewChild, Directive, untracked, contentChild, contentChildren, afterNextRender, Injector, HostBinding, runInInjectionContext, ChangeDetectorRef, viewChildren, ViewContainerRef, EnvironmentInjector } from '@angular/core';
3
3
  import { isPlatformBrowser, JsonPipe, DatePipe, isPlatformServer, NgTemplateOutlet } from '@angular/common';
4
+ import { ShipButton as ShipButton$1 } from 'ship-ui';
4
5
  import { NgModel } from '@angular/forms';
5
6
  import { SIGNAL } from '@angular/core/primitives/signals';
6
7
 
8
+ const defaultThemeColors = {
9
+ primary: 'hsl(217, 91%, 60%)',
10
+ accent: 'hsl(258, 90%, 66%)',
11
+ warn: 'hsl(37, 92%, 50%)',
12
+ error: 'hsl(0, 84%, 60%)',
13
+ success: 'hsl(159, 84%, 39%)',
14
+ base: 'hsl(0, 0%, 46%)'
15
+ };
7
16
  const SHIP_CONFIG = new InjectionToken('Ship UI Config');
8
17
 
9
18
  function shipComponentClasses(componentName, inputs) {
@@ -40,6 +49,8 @@ function shipComponentClasses(componentName, inputs) {
40
49
  const dynamic = (inputs.dynamic?.() ?? componentConfig?.dynamic) || false;
41
50
  // Resolve readonly (Input > Component Config > false)
42
51
  const readonly = (inputs.readonly?.() ?? componentConfig?.readonly) || false;
52
+ // Resolve alwaysShow (Input > Component Config > false)
53
+ const alwaysShow = (inputs.alwaysShow?.() ?? componentConfig?.alwaysShow) || false;
43
54
  const classList = [];
44
55
  if (color)
45
56
  classList.push(color);
@@ -60,6 +71,8 @@ function shipComponentClasses(componentName, inputs) {
60
71
  classList.push('dynamic');
61
72
  if (readonly)
62
73
  classList.push('readonly');
74
+ if (alwaysShow)
75
+ classList.push('always-show');
63
76
  return classList.join(' ');
64
77
  });
65
78
  }
@@ -654,6 +667,145 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
654
667
  }]
655
668
  }] });
656
669
 
670
+ function contentProjectionSignal(querySelector, options = { childList: true }) {
671
+ const hostElement = inject((ElementRef)).nativeElement;
672
+ const destroyRef = inject(DestroyRef);
673
+ const projectedElementsSignal = signal([], ...(ngDevMode ? [{ debugName: "projectedElementsSignal" }] : /* istanbul ignore next */ []));
674
+ const updateElements = () => {
675
+ projectedElementsSignal.set(Array.from(hostElement.querySelectorAll(querySelector)));
676
+ };
677
+ updateElements();
678
+ if (typeof MutationObserver === 'undefined')
679
+ return projectedElementsSignal.asReadonly();
680
+ const observer = new MutationObserver((mutations) => {
681
+ const hasChildListChanges = mutations.some((mutation) => mutation.type === 'childList');
682
+ if (hasChildListChanges) {
683
+ updateElements();
684
+ }
685
+ });
686
+ observer.observe(hostElement, options);
687
+ destroyRef.onDestroy(() => observer.disconnect());
688
+ return projectedElementsSignal.asReadonly();
689
+ }
690
+
691
+ class ShipAccordion {
692
+ constructor() {
693
+ this.selfElement = inject((ElementRef)).nativeElement;
694
+ this.name = input(`sh-accordion-${Math.random().toString(36).substring(2, 9)}`, ...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
695
+ this.value = model(null, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
696
+ this.allowMultiple = input(false, ...(ngDevMode ? [{ debugName: "allowMultiple" }] : /* istanbul ignore next */ []));
697
+ this.variant = input(null, ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
698
+ this.size = input(null, ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
699
+ this.hostClasses = shipComponentClasses('accordion', {
700
+ variant: this.variant,
701
+ size: this.size,
702
+ });
703
+ this.items = contentProjectionSignal('details', {
704
+ childList: true,
705
+ subtree: true,
706
+ attributes: true,
707
+ attributeFilter: ['open', 'value'],
708
+ });
709
+ this.selfElement.addEventListener('toggle', this.onToggle.bind(this), true);
710
+ effect(() => {
711
+ const isMultiple = this.allowMultiple();
712
+ const groupName = this.name();
713
+ const valStr = this.value();
714
+ const vals = valStr ? valStr.split(',').filter((v) => v !== '') : [];
715
+ this.items().forEach((details) => {
716
+ if (!isMultiple) {
717
+ details.setAttribute('name', groupName);
718
+ }
719
+ else {
720
+ details.removeAttribute('name');
721
+ }
722
+ const summary = details.querySelector('summary');
723
+ if (summary && !summary.querySelector('sh-icon')) {
724
+ const icon = document.createElement('sh-icon');
725
+ icon.textContent = 'caret-down';
726
+ summary.appendChild(icon);
727
+ }
728
+ let contentWrapper = details.querySelector(':scope > .content');
729
+ if (!contentWrapper) {
730
+ const newWrapper = document.createElement('div');
731
+ newWrapper.className = 'content';
732
+ const childrenToMove = Array.from(details.childNodes).filter((node) => {
733
+ if (node.nodeType === Node.ELEMENT_NODE &&
734
+ node.tagName.toLowerCase() === 'summary') {
735
+ return false;
736
+ }
737
+ return true;
738
+ });
739
+ childrenToMove.forEach((child) => newWrapper.appendChild(child));
740
+ details.appendChild(newWrapper);
741
+ contentWrapper = newWrapper;
742
+ }
743
+ const itemVal = details.getAttribute('value');
744
+ if (itemVal && vals.includes(itemVal)) {
745
+ // Avoid triggering endless loops by only setting open if it actually changed
746
+ if (!details.open)
747
+ details.open = true;
748
+ }
749
+ else if (itemVal) {
750
+ if (details.open)
751
+ details.open = false;
752
+ }
753
+ });
754
+ });
755
+ }
756
+ onToggle(event) {
757
+ const target = event.target;
758
+ if (target.tagName !== 'DETAILS')
759
+ return;
760
+ if (!this.selfElement.contains(target))
761
+ return;
762
+ const itemVal = target.getAttribute('value');
763
+ if (!itemVal)
764
+ return; // Uncontrolled details element
765
+ const isOpen = target.open;
766
+ if (this.allowMultiple()) {
767
+ let vals = this.value()
768
+ ? this.value()
769
+ .split(',')
770
+ .filter((v) => v !== '')
771
+ : [];
772
+ if (isOpen && !vals.includes(itemVal))
773
+ vals.push(itemVal);
774
+ if (!isOpen)
775
+ vals = vals.filter((v) => v !== itemVal);
776
+ this.value.set(vals.join(','));
777
+ }
778
+ else {
779
+ if (isOpen) {
780
+ this.value.set(itemVal);
781
+ }
782
+ else {
783
+ if (this.value() === itemVal) {
784
+ this.value.set(null);
785
+ }
786
+ }
787
+ }
788
+ }
789
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ShipAccordion, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
790
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.5", type: ShipAccordion, isStandalone: true, selector: "sh-accordion", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, allowMultiple: { classPropertyName: "allowMultiple", publicName: "allowMultiple", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { properties: { "class.sh-accordion": "true", "class": "hostClasses()" } }, ngImport: i0, template: `
791
+ <ng-content></ng-content>
792
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
793
+ }
794
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ShipAccordion, decorators: [{
795
+ type: Component,
796
+ args: [{
797
+ selector: 'sh-accordion',
798
+ template: `
799
+ <ng-content></ng-content>
800
+ `,
801
+ changeDetection: ChangeDetectionStrategy.OnPush,
802
+ host: {
803
+ '[class.sh-accordion]': 'true',
804
+ '[class]': 'hostClasses()',
805
+ },
806
+ }]
807
+ }], ctorParameters: () => [], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], allowMultiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowMultiple", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }] } });
808
+
657
809
  function classMutationSignal(_element = null) {
658
810
  const element = _element ?? inject(ElementRef).nativeElement;
659
811
  if (!element)
@@ -1788,27 +1940,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
1788
1940
  args: ['document:touchend', ['$event']]
1789
1941
  }] } });
1790
1942
 
1791
- function contentProjectionSignal(querySelector, options = { childList: true }) {
1792
- const hostElement = inject((ElementRef)).nativeElement;
1793
- const destroyRef = inject(DestroyRef);
1794
- const projectedElementsSignal = signal([], ...(ngDevMode ? [{ debugName: "projectedElementsSignal" }] : /* istanbul ignore next */ []));
1795
- const updateElements = () => {
1796
- projectedElementsSignal.set(Array.from(hostElement.querySelectorAll(querySelector)));
1797
- };
1798
- updateElements();
1799
- if (typeof MutationObserver === 'undefined')
1800
- return projectedElementsSignal.asReadonly();
1801
- const observer = new MutationObserver((mutations) => {
1802
- const hasChildListChanges = mutations.some((mutation) => mutation.type === 'childList');
1803
- if (hasChildListChanges) {
1804
- updateElements();
1805
- }
1806
- });
1807
- observer.observe(hostElement, options);
1808
- destroyRef.onDestroy(() => observer.disconnect());
1809
- return projectedElementsSignal.asReadonly();
1810
- }
1811
-
1812
1943
  class ShipSelectionGroup {
1813
1944
  constructor(itemSelector, activeClass) {
1814
1945
  this.itemSelector = itemSelector;
@@ -2112,69 +2243,313 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
2112
2243
  }]
2113
2244
  }], propDecorators: { color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], sharp: [{ type: i0.Input, args: [{ isSignal: true, alias: "sharp", required: false }] }], dynamic: [{ type: i0.Input, args: [{ isSignal: true, alias: "dynamic", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }] } });
2114
2245
 
2115
- // TODOS
2116
- // - Add a color picker input
2117
- // - Add alpha support
2246
+ function hslToRgbExact(h, s, l) {
2247
+ s /= 100;
2248
+ l /= 100;
2249
+ const k = (n) => (n + h / 30) % 12;
2250
+ const a = s * Math.min(l, 1 - l);
2251
+ const f = (n) => l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)));
2252
+ return [Math.round(255 * f(0)), Math.round(255 * f(8)), Math.round(255 * f(4))];
2253
+ }
2254
+ function hsvToRgbExact(h, s, v) {
2255
+ const sNorm = s / 100;
2256
+ const vNorm = v / 100;
2257
+ const c = vNorm * sNorm;
2258
+ const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
2259
+ const m = vNorm - c;
2260
+ let r = 0, g = 0, b = 0;
2261
+ if (h >= 0 && h < 60) {
2262
+ r = c;
2263
+ g = x;
2264
+ b = 0;
2265
+ }
2266
+ else if (h >= 60 && h < 120) {
2267
+ r = x;
2268
+ g = c;
2269
+ b = 0;
2270
+ }
2271
+ else if (h >= 120 && h < 180) {
2272
+ r = 0;
2273
+ g = c;
2274
+ b = x;
2275
+ }
2276
+ else if (h >= 180 && h < 240) {
2277
+ r = 0;
2278
+ g = x;
2279
+ b = c;
2280
+ }
2281
+ else if (h >= 240 && h < 300) {
2282
+ r = x;
2283
+ g = 0;
2284
+ b = c;
2285
+ }
2286
+ else if (h >= 300 && h < 360) {
2287
+ r = c;
2288
+ g = 0;
2289
+ b = x;
2290
+ }
2291
+ return [Math.round((r + m) * 255), Math.round((g + m) * 255), Math.round((b + m) * 255)];
2292
+ }
2293
+ function rgbToHsv(r, g, b) {
2294
+ r /= 255;
2295
+ g /= 255;
2296
+ b /= 255;
2297
+ const max = Math.max(r, g, b), min = Math.min(r, g, b);
2298
+ const d = max - min;
2299
+ const v = max;
2300
+ const s = max === 0 ? 0 : d / max;
2301
+ let h = 0;
2302
+ if (max !== min) {
2303
+ switch (max) {
2304
+ case r:
2305
+ h = (g - b) / d + (g < b ? 6 : 0);
2306
+ break;
2307
+ case g:
2308
+ h = (b - r) / d + 2;
2309
+ break;
2310
+ case b:
2311
+ h = (r - g) / d + 4;
2312
+ break;
2313
+ }
2314
+ h /= 6;
2315
+ }
2316
+ return { h: h * 360, s: s * 100, v: v * 100 };
2317
+ }
2318
+ function rgbToHex(r, g, b) {
2319
+ return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
2320
+ }
2321
+ function rgbaToHex8(r, g, b, a) {
2322
+ const alphaHex = Math.round(a * 255)
2323
+ .toString(16)
2324
+ .padStart(2, '0');
2325
+ return rgbToHex(r, g, b) + alphaHex;
2326
+ }
2327
+ function rgbToHsl(r, g, b) {
2328
+ r /= 255;
2329
+ g /= 255;
2330
+ b /= 255;
2331
+ const max = Math.max(r, g, b), min = Math.min(r, g, b);
2332
+ let h = 0, s = 0, l = (max + min) / 2;
2333
+ if (max === min) {
2334
+ h = s = 0;
2335
+ }
2336
+ else {
2337
+ const d = max - min;
2338
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
2339
+ switch (max) {
2340
+ case r:
2341
+ h = (g - b) / d + (g < b ? 6 : 0);
2342
+ break;
2343
+ case g:
2344
+ h = (b - r) / d + 2;
2345
+ break;
2346
+ case b:
2347
+ h = (r - g) / d + 4;
2348
+ break;
2349
+ }
2350
+ h /= 6;
2351
+ }
2352
+ const hDeg = Math.floor(h * 360);
2353
+ const sPct = Math.round(s * 100);
2354
+ const lPct = Math.round(l * 100);
2355
+ return {
2356
+ h: hDeg,
2357
+ s: sPct,
2358
+ l: lPct,
2359
+ string: `hsl(${hDeg}, ${sPct}%, ${lPct}%)`,
2360
+ };
2361
+ }
2362
+ function rgbToOklch(r, g, b) {
2363
+ // Convert sRGB to Linear sRGB
2364
+ const toLinear = (c) => {
2365
+ const v = c / 255;
2366
+ return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
2367
+ };
2368
+ const lr = toLinear(r);
2369
+ const lg = toLinear(g);
2370
+ const lb = toLinear(b);
2371
+ // Convert Linear sRGB to LMS (Oklab intermediate)
2372
+ const l = 0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb;
2373
+ const m = 0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb;
2374
+ const s = 0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb;
2375
+ // Cube root of LMS
2376
+ const l_ = Math.cbrt(l);
2377
+ const m_ = Math.cbrt(m);
2378
+ const s_ = Math.cbrt(s);
2379
+ // Convert LMS to OKLAB
2380
+ const oklabL = 0.2104542553 * l_ + 0.7936177850 * m_ - 0.0040720468 * s_;
2381
+ const oklabA = 1.9779984951 * l_ - 2.4285922050 * m_ + 0.4505937099 * s_;
2382
+ const oklabB = 0.0259040371 * l_ + 0.7827717662 * m_ - 0.8086757660 * s_;
2383
+ // Convert OKLAB to OKLCH
2384
+ const chroma = Math.sqrt(oklabA * oklabA + oklabB * oklabB);
2385
+ let hue = Math.atan2(oklabB, oklabA) * (180 / Math.PI);
2386
+ if (hue < 0)
2387
+ hue += 360;
2388
+ return { l: oklabL, c: chroma, h: hue };
2389
+ }
2390
+ function hslToOklch(h, s, l) {
2391
+ const [r, g, b] = hslToRgbExact(h, s, l);
2392
+ return rgbToOklch(r, g, b);
2393
+ }
2394
+
2118
2395
  class ShipColorPicker {
2119
2396
  constructor() {
2120
2397
  this.#document = inject(DOCUMENT);
2398
+ this.#platformId = inject(PLATFORM_ID);
2121
2399
  this.canvasRef = viewChild.required('colorCanvas');
2122
- this.canvasData = signal(null, ...(ngDevMode ? [{ debugName: "canvasData" }] : /* istanbul ignore next */ []));
2400
+ this.#canvasData = signal(null, ...(ngDevMode ? [{ debugName: "#canvasData" }] : /* istanbul ignore next */ []));
2123
2401
  this.showDarkColors = input(false, ...(ngDevMode ? [{ debugName: "showDarkColors" }] : /* istanbul ignore next */ []));
2124
2402
  this.renderingType = input('hsl', ...(ngDevMode ? [{ debugName: "renderingType" }] : /* istanbul ignore next */ []));
2125
2403
  this.gridSize = input(20, ...(ngDevMode ? [{ debugName: "gridSize" }] : /* istanbul ignore next */ []));
2126
- this.hue = input(0, ...(ngDevMode ? [{ debugName: "hue" }] : /* istanbul ignore next */ []));
2404
+ this.hue = model(0, ...(ngDevMode ? [{ debugName: "hue" }] : /* istanbul ignore next */ []));
2127
2405
  this.direction = input('horizontal', ...(ngDevMode ? [{ debugName: "direction" }] : /* istanbul ignore next */ []));
2128
- this.selectedColor = model([255, 255, 255], ...(ngDevMode ? [{ debugName: "selectedColor" }] : /* istanbul ignore next */ []));
2406
+ this.selectedColor = model([255, 255, 255, 1], ...(ngDevMode ? [{ debugName: "selectedColor" }] : /* istanbul ignore next */ []));
2407
+ this.alpha = model(1, ...(ngDevMode ? [{ debugName: "alpha" }] : /* istanbul ignore next */ []));
2129
2408
  this.currentColor = output();
2130
2409
  this.centerLightness = computed(() => (this.showDarkColors() ? 200 : 100), ...(ngDevMode ? [{ debugName: "centerLightness" }] : /* istanbul ignore next */ []));
2131
2410
  this.isDragging = signal(false, ...(ngDevMode ? [{ debugName: "isDragging" }] : /* istanbul ignore next */ []));
2132
2411
  this.markerPosition = signal({ x: '50%', y: '50%' }, ...(ngDevMode ? [{ debugName: "markerPosition" }] : /* istanbul ignore next */ []));
2133
2412
  this._pos = { x: '0', y: '0' };
2134
2413
  this._markerPosition = effect(() => (this._pos = this.markerPosition()), ...(ngDevMode ? [{ debugName: "_markerPosition" }] : /* istanbul ignore next */ []));
2135
- this.selectedColorRgb = computed(() => `rgb(${this.selectedColor().join(',')})`, ...(ngDevMode ? [{ debugName: "selectedColorRgb" }] : /* istanbul ignore next */ []));
2136
- this.selectedColorHex = computed(() => this.rgbToHex(...this.selectedColor()), ...(ngDevMode ? [{ debugName: "selectedColorHex" }] : /* istanbul ignore next */ []));
2137
- this.selectedColorHsl = computed(() => this.rgbToHsl(...this.selectedColor()), ...(ngDevMode ? [{ debugName: "selectedColorHsl" }] : /* istanbul ignore next */ []));
2414
+ this.#skipMarkerUpdate = false;
2415
+ this.selectedColorRgb = computed(() => {
2416
+ const c = this.selectedColor();
2417
+ return c[3] !== undefined ? `rgba(${c[0]},${c[1]},${c[2]},${c[3]})` : `rgb(${c[0]},${c[1]},${c[2]})`;
2418
+ }, ...(ngDevMode ? [{ debugName: "selectedColorRgb" }] : /* istanbul ignore next */ []));
2419
+ this.selectedColorHex = computed(() => rgbToHex(...this.selectedColor()), ...(ngDevMode ? [{ debugName: "selectedColorHex" }] : /* istanbul ignore next */ []));
2420
+ this.selectedColorHsl = computed(() => rgbToHsl(...this.selectedColor()).string, ...(ngDevMode ? [{ debugName: "selectedColorHsl" }] : /* istanbul ignore next */ []));
2421
+ this.alphaEffect = effect(() => {
2422
+ const a = this.alpha();
2423
+ const current = untracked(() => this.selectedColor());
2424
+ if (current[3] !== a) {
2425
+ this.#skipMarkerUpdate = false;
2426
+ this.selectedColor.set([current[0], current[1], current[2], a]);
2427
+ }
2428
+ }, ...(ngDevMode ? [{ debugName: "alphaEffect" }] : /* istanbul ignore next */ []));
2429
+ this._prevColorStr = '';
2138
2430
  this.selectedColorEffect = effect(() => {
2139
2431
  const selectedColor = this.selectedColor();
2140
- const hsl = this.rgbToHsl(...selectedColor);
2141
- const hex = this.rgbToHex(...selectedColor);
2142
- this.updateMarkerFromColor(selectedColor);
2432
+ const r = selectedColor[0];
2433
+ const g = selectedColor[1];
2434
+ const b = selectedColor[2];
2435
+ const a = selectedColor[3] ?? 1;
2436
+ untracked(() => {
2437
+ if (this.alpha() !== a) {
2438
+ this.alpha.set(a);
2439
+ }
2440
+ });
2441
+ const str = `${r},${g},${b},${a}`;
2442
+ if (this._prevColorStr === str && !this.#skipMarkerUpdate) {
2443
+ // We still want to clear skipMarkerUpdate if it was set
2444
+ // wait, actually if skipMarkerUpdate is true, it means we JUST dragged.
2445
+ // In that case we do want to emit currentColor.
2446
+ }
2447
+ const hsl = rgbToHsl(r, g, b);
2448
+ const hex = rgbToHex(r, g, b);
2449
+ if (this.#skipMarkerUpdate) {
2450
+ this.#skipMarkerUpdate = false;
2451
+ this._prevColorStr = str;
2452
+ }
2453
+ else {
2454
+ if (this._prevColorStr !== str) {
2455
+ this.updateMarkerFromColor(selectedColor);
2456
+ this._prevColorStr = str;
2457
+ }
2458
+ else {
2459
+ // Color is structurally identical and not from a drag event, skip marker jump
2460
+ }
2461
+ }
2143
2462
  this.currentColor.emit({
2144
- rgb: `rgb(${selectedColor.join(',')})`,
2463
+ rgb: `rgb(${r}, ${g}, ${b})`,
2464
+ rgba: `rgba(${r}, ${g}, ${b}, ${a})`,
2145
2465
  hex: hex,
2146
- hsl: hsl,
2147
- hue: hsl.match(/\d+/g).map(Number)[0],
2148
- saturation: hsl.match(/\d+/g).map(Number)[1],
2466
+ hex8: rgbaToHex8(r, g, b, a),
2467
+ hsl: hsl.string,
2468
+ hsla: `hsla(${hsl.h}, ${hsl.s}%, ${hsl.l}%, ${a})`,
2469
+ hue: hsl.h,
2470
+ saturation: hsl.s,
2471
+ alpha: a,
2149
2472
  });
2150
2473
  }, ...(ngDevMode ? [{ debugName: "selectedColorEffect" }] : /* istanbul ignore next */ []));
2474
+ this.alphaColorRedrawEffect = effect(() => {
2475
+ const color = this.selectedColor();
2476
+ if (this.renderingType() === 'alpha' && this.#canvasData()) {
2477
+ untracked(() => this.drawAlpha());
2478
+ }
2479
+ }, ...(ngDevMode ? [{ debugName: "alphaColorRedrawEffect" }] : /* istanbul ignore next */ []));
2480
+ this.previousLayoutHash = '';
2481
+ this.previousHue = null;
2151
2482
  this.renderingTypeEffect = effect(() => {
2152
- const currentRenderingType = this.renderingType();
2153
- if (this.canvasData()) {
2154
- this.drawColorPicker();
2155
- if (currentRenderingType === 'hsl') {
2156
- this.adjustMarkerPosition();
2157
- queueMicrotask(() => this.updateMarkerFromColor(this.selectedColor()));
2483
+ const currentHue = this.hue();
2484
+ const layoutHash = this.getLayoutHash();
2485
+ const canvasData = untracked(() => this.#canvasData());
2486
+ if (canvasData) {
2487
+ untracked(() => this.drawColorPicker());
2488
+ if (this.previousLayoutHash !== layoutHash) {
2489
+ if (this.renderingType() === 'hsl') {
2490
+ this.adjustMarkerPosition();
2491
+ queueMicrotask(() => this.updateMarkerFromColor(untracked(() => this.selectedColor())));
2492
+ }
2493
+ else {
2494
+ this.updateMarkerFromColor(untracked(() => this.selectedColor()));
2495
+ }
2496
+ this.previousLayoutHash = layoutHash;
2497
+ this.previousHue = currentHue;
2158
2498
  }
2159
- else {
2160
- this.updateMarkerFromColor(this.selectedColor());
2499
+ else if (this.previousHue !== currentHue) {
2500
+ const pos = untracked(() => this.markerPosition());
2501
+ const { canvas, ctx } = canvasData;
2502
+ let x = (parseFloat(pos.x.replace('%', '')) / 100) * Math.max(1, canvas.width - 1);
2503
+ let y = (parseFloat(pos.y.replace('%', '')) / 100) * Math.max(1, canvas.height - 1);
2504
+ x = Math.max(0, Math.min(canvas.width - 1, Math.round(x)));
2505
+ y = Math.max(0, Math.min(canvas.height - 1, Math.round(y)));
2506
+ const color = untracked(() => this.getColorAtPosition(x, y));
2507
+ if (this.renderingType() !== 'alpha') {
2508
+ color[3] = untracked(() => this.selectedColor()[3] ?? 1);
2509
+ }
2510
+ this.#skipMarkerUpdate = true;
2511
+ this.selectedColor.set(color);
2512
+ this.previousHue = currentHue;
2161
2513
  }
2162
2514
  }
2163
2515
  }, ...(ngDevMode ? [{ debugName: "renderingTypeEffect" }] : /* istanbul ignore next */ []));
2516
+ this.#resizeObserver = typeof ResizeObserver !== 'undefined'
2517
+ ? new ResizeObserver((entries) => {
2518
+ for (const entry of entries) {
2519
+ if (entry.contentRect.width > 0 && entry.contentRect.height > 0) {
2520
+ this.setCanvasSize();
2521
+ }
2522
+ }
2523
+ })
2524
+ : null;
2164
2525
  this.initColor = null;
2165
2526
  }
2166
2527
  #document;
2528
+ #platformId;
2529
+ #canvasData;
2530
+ #skipMarkerUpdate;
2167
2531
  onResize() {
2168
2532
  this.setCanvasSize();
2169
2533
  }
2534
+ getLayoutHash() {
2535
+ return `${this.renderingType()}-${this.direction()}-${this.gridSize()}-${this.showDarkColors()}`;
2536
+ }
2537
+ #resizeObserver;
2170
2538
  ngAfterViewInit() {
2171
2539
  this.initColor = this.selectedColor();
2540
+ const canvas = this.canvasRef()?.nativeElement;
2541
+ if (canvas?.parentElement) {
2542
+ this.#resizeObserver?.observe(canvas.parentElement);
2543
+ }
2172
2544
  this.setCanvasSize();
2173
2545
  this.initCanvasEvents();
2174
2546
  }
2175
- updateMarkerFromColor(rgb) {
2176
- const [r, g, b] = rgb;
2177
- const coords = this.findPositionByColor(r, g, b);
2547
+ ngOnDestroy() {
2548
+ this.#resizeObserver?.disconnect();
2549
+ }
2550
+ updateMarkerFromColor(rgba) {
2551
+ const [r, g, b, a] = rgba;
2552
+ const coords = this.findPositionByColor(r, g, b, a);
2178
2553
  if (coords === null)
2179
2554
  return;
2180
2555
  const { x, y } = coords;
@@ -2187,11 +2562,63 @@ class ShipColorPicker {
2187
2562
  };
2188
2563
  this.updateColorAndMarker(mockEvent, false, true);
2189
2564
  }
2190
- findPositionByColor(r, g, b) {
2191
- const canvasData = this.canvasData();
2565
+ findPositionByColor(r, g, b, a) {
2566
+ const canvasData = this.#canvasData();
2192
2567
  if (!canvasData || !canvasData.canvas)
2193
2568
  return null;
2194
2569
  const { canvas, ctx } = canvasData;
2570
+ if (canvas.width === 0 || canvas.height === 0)
2571
+ return null;
2572
+ if (this.renderingType() === 'alpha') {
2573
+ const aVal = a ?? 1;
2574
+ if (this.direction() === 'horizontal') {
2575
+ return { x: Math.round(aVal * (canvas.width - 1)), y: Math.round((canvas.height - 1) / 2) };
2576
+ }
2577
+ else {
2578
+ return { x: Math.round((canvas.width - 1) / 2), y: Math.round(aVal * (canvas.height - 1)) };
2579
+ }
2580
+ }
2581
+ if (this.renderingType() === 'hue') {
2582
+ const hsl = rgbToHsl(r, g, b);
2583
+ const ratio = hsl.h / 360;
2584
+ if (this.direction() === 'horizontal') {
2585
+ return { x: Math.round(ratio * (canvas.width - 1)), y: Math.round((canvas.height - 1) / 2) };
2586
+ }
2587
+ else {
2588
+ return { x: Math.round((canvas.width - 1) / 2), y: Math.round(ratio * (canvas.height - 1)) };
2589
+ }
2590
+ }
2591
+ if (this.renderingType() === 'rgb') {
2592
+ const hsv = rgbToHsv(r, g, b);
2593
+ return {
2594
+ x: Math.round((hsv.s / 100) * (canvas.width - 1)),
2595
+ y: Math.round((1 - hsv.v / 100) * (canvas.height - 1)),
2596
+ };
2597
+ }
2598
+ if (this.renderingType() === 'saturation') {
2599
+ const hsl = rgbToHsl(r, g, b);
2600
+ const ratio = hsl.s / 100;
2601
+ if (this.direction() === 'horizontal') {
2602
+ return { x: Math.round(ratio * (canvas.width - 1)), y: Math.round((canvas.height - 1) / 2) };
2603
+ }
2604
+ else {
2605
+ return { x: Math.round((canvas.width - 1) / 2), y: Math.round(ratio * (canvas.height - 1)) };
2606
+ }
2607
+ }
2608
+ if (this.renderingType() === 'hsl') {
2609
+ const { centerX, centerY, radius } = canvasData;
2610
+ const hsl = rgbToHsl(r, g, b);
2611
+ const centerL = this.centerLightness();
2612
+ let distance = 0;
2613
+ if (centerL > 0) {
2614
+ distance = ((100 - hsl.l) * radius) / ((50 * centerL) / 100);
2615
+ }
2616
+ const angle = (hsl.h / 360) * 2 * Math.PI - Math.PI;
2617
+ return {
2618
+ x: Math.round(centerX + distance * Math.cos(angle)),
2619
+ y: Math.round(centerY + distance * Math.sin(angle)),
2620
+ };
2621
+ }
2195
2622
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
2196
2623
  let bestMatch = { x: 0, y: 0, distance: Infinity };
2197
2624
  for (let y = 0; y < canvas.height; y++) {
@@ -2209,7 +2636,7 @@ class ShipColorPicker {
2209
2636
  return { x: bestMatch.x, y: bestMatch.y };
2210
2637
  }
2211
2638
  adjustMarkerPosition() {
2212
- const { canvas, centerX, centerY, radius } = this.canvasData();
2639
+ const { canvas, centerX, centerY, radius } = this.#canvasData();
2213
2640
  let { x, y } = this._pos;
2214
2641
  let markerX = (parseFloat(x.replace('%', '')) / 100) * canvas.width;
2215
2642
  let markerY = (parseFloat(y.replace('%', '')) / 100) * canvas.height;
@@ -2224,7 +2651,10 @@ class ShipColorPicker {
2224
2651
  }
2225
2652
  }
2226
2653
  initCanvasEvents() {
2227
- const { canvas } = this.canvasData();
2654
+ const data = this.#canvasData();
2655
+ if (!data)
2656
+ return;
2657
+ const { canvas } = data;
2228
2658
  canvas.addEventListener('mousedown', (event) => {
2229
2659
  this.isDragging.set(true);
2230
2660
  this.updateColorAndMarker(event);
@@ -2247,6 +2677,8 @@ class ShipColorPicker {
2247
2677
  this.#document.addEventListener('touchcancel', () => this.isDragging.set(false));
2248
2678
  }
2249
2679
  setCanvasSize() {
2680
+ if (!isPlatformBrowser(this.#platformId))
2681
+ return;
2250
2682
  const canvas = this.canvasRef()?.nativeElement;
2251
2683
  if (canvas) {
2252
2684
  const ctx = canvas.getContext('2d', {
@@ -2257,7 +2689,7 @@ class ShipColorPicker {
2257
2689
  const parentWidth = canvas.parentElement?.offsetWidth || canvas.offsetWidth;
2258
2690
  canvas.width = parentWidth;
2259
2691
  canvas.height = parentWidth;
2260
- this.canvasData.set({
2692
+ this.#canvasData.set({
2261
2693
  canvas,
2262
2694
  ctx,
2263
2695
  centerX: canvas.width / 2,
@@ -2265,10 +2697,42 @@ class ShipColorPicker {
2265
2697
  radius: Math.min(canvas.width, canvas.height) / 2,
2266
2698
  });
2267
2699
  this.drawColorPicker();
2700
+ setTimeout(() => {
2701
+ if (!this.isDragging()) {
2702
+ this.updateMarkerFromColor(untracked(() => this.selectedColor()));
2703
+ }
2704
+ });
2705
+ }
2706
+ }
2707
+ getColorAtPosition(mouseX, mouseY) {
2708
+ const { canvas, ctx } = this.#canvasData();
2709
+ const w = Math.max(1, canvas.width - 1);
2710
+ const h = Math.max(1, canvas.height - 1);
2711
+ const xRatio = mouseX / w;
2712
+ const yRatio = mouseY / h;
2713
+ if (this.renderingType() === 'rgb') {
2714
+ return hsvToRgbExact(this.hue(), xRatio * 100, (1 - yRatio) * 100);
2715
+ }
2716
+ else if (this.renderingType() === 'saturation') {
2717
+ const ratio = this.direction() === 'horizontal' ? xRatio : yRatio;
2718
+ return hslToRgbExact(this.hue(), ratio * 100, 50);
2719
+ }
2720
+ else if (this.renderingType() === 'hue') {
2721
+ const ratio = this.direction() === 'horizontal' ? xRatio : yRatio;
2722
+ return hslToRgbExact(ratio * 360, 100, 50);
2723
+ }
2724
+ else if (this.renderingType() === 'alpha') {
2725
+ const ratio = this.direction() === 'horizontal' ? xRatio : yRatio;
2726
+ const current = this.selectedColor();
2727
+ return [current[0], current[1], current[2], parseFloat(ratio.toFixed(2))];
2728
+ }
2729
+ else {
2730
+ const pixelData = ctx.getImageData(mouseX, mouseY, 1, 1).data;
2731
+ return [pixelData[0], pixelData[1], pixelData[2]];
2268
2732
  }
2269
2733
  }
2270
2734
  updateColorAndMarker(event, outsideCanvas = false, onlyMarker = false) {
2271
- const { canvas, ctx } = this.canvasData();
2735
+ const { canvas, ctx } = this.#canvasData();
2272
2736
  let mouseX = event instanceof MouseEvent ? event.offsetX : event.clientX;
2273
2737
  let mouseY = event instanceof MouseEvent ? event.offsetY : event.clientY;
2274
2738
  if (outsideCanvas) {
@@ -2289,13 +2753,16 @@ class ShipColorPicker {
2289
2753
  }
2290
2754
  mouseX = Math.max(0, Math.min(canvas.width - 1, Math.round(mouseX)));
2291
2755
  mouseY = Math.max(0, Math.min(canvas.height - 1, Math.round(mouseY)));
2292
- const pixelData = ctx.getImageData(mouseX, mouseY, 1, 1).data;
2293
- const [r, g, b] = pixelData;
2294
2756
  if (!onlyMarker) {
2295
- this.selectedColor.set([r, g, b]);
2757
+ const newColor = this.getColorAtPosition(mouseX, mouseY);
2758
+ if (this.renderingType() !== 'alpha') {
2759
+ newColor[3] = this.selectedColor()[3] ?? 1;
2760
+ }
2761
+ this.#skipMarkerUpdate = true;
2762
+ this.selectedColor.set(newColor);
2296
2763
  }
2297
- const xPercent = ((mouseX / canvas.width) * 100).toFixed(2) + '%';
2298
- const yPercent = ((mouseY / canvas.height) * 100).toFixed(2) + '%';
2764
+ const xPercent = ((mouseX / Math.max(1, canvas.width - 1)) * 100).toFixed(2) + '%';
2765
+ const yPercent = ((mouseY / Math.max(1, canvas.height - 1)) * 100).toFixed(2) + '%';
2299
2766
  this.markerPosition.set({ x: xPercent, y: yPercent });
2300
2767
  }
2301
2768
  drawColorPicker() {
@@ -2309,6 +2776,9 @@ class ShipColorPicker {
2309
2776
  case 'hue':
2310
2777
  this.drawHue();
2311
2778
  break;
2779
+ case 'alpha':
2780
+ this.drawAlpha();
2781
+ break;
2312
2782
  case 'rgb':
2313
2783
  this.drawRgb();
2314
2784
  break;
@@ -2317,32 +2787,48 @@ class ShipColorPicker {
2317
2787
  break;
2318
2788
  }
2319
2789
  }
2790
+ drawAlpha() {
2791
+ const { canvas, ctx } = this.#canvasData();
2792
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
2793
+ const w = Math.max(1, canvas.width - 1);
2794
+ const h = Math.max(1, canvas.height - 1);
2795
+ const gradient = ctx.createLinearGradient(0, 0, this.direction() === 'horizontal' ? w : 0, this.direction() === 'horizontal' ? 0 : h);
2796
+ const [r, g, b] = this.selectedColor();
2797
+ gradient.addColorStop(0, `rgba(${r}, ${g}, ${b}, 0)`);
2798
+ gradient.addColorStop(1, `rgba(${r}, ${g}, ${b}, 1)`);
2799
+ ctx.fillStyle = gradient;
2800
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
2801
+ }
2320
2802
  drawRgb() {
2321
- const { canvas, ctx } = this.canvasData();
2803
+ const { canvas, ctx } = this.#canvasData();
2322
2804
  ctx.clearRect(0, 0, canvas.width, canvas.height);
2323
- const gradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
2324
- gradient.addColorStop(0, 'white');
2805
+ const w = Math.max(1, canvas.width - 1);
2806
+ const h = Math.max(1, canvas.height - 1);
2807
+ const gradient = ctx.createLinearGradient(0, 0, w, 0);
2808
+ gradient.addColorStop(0, '#ffffff');
2325
2809
  gradient.addColorStop(1, `hsl(${this.hue()}, 100%, 50%)`);
2326
2810
  ctx.fillStyle = gradient;
2327
2811
  ctx.fillRect(0, 0, canvas.width, canvas.height);
2328
- const gradient2 = ctx.createLinearGradient(0, 0, 0, canvas.height);
2812
+ const gradient2 = ctx.createLinearGradient(0, 0, 0, h);
2329
2813
  gradient2.addColorStop(0, 'rgba(0, 0, 0, 0)');
2330
- gradient2.addColorStop(1, 'black');
2814
+ gradient2.addColorStop(1, '#000000');
2331
2815
  ctx.fillStyle = gradient2;
2332
2816
  ctx.fillRect(0, 0, canvas.width, canvas.height);
2333
2817
  }
2334
2818
  drawSaturation() {
2335
- const { canvas, ctx } = this.canvasData();
2819
+ const { canvas, ctx } = this.#canvasData();
2336
2820
  ctx.clearRect(0, 0, canvas.width, canvas.height);
2821
+ const w = Math.max(1, canvas.width - 1);
2822
+ const h = Math.max(1, canvas.height - 1);
2337
2823
  if (this.direction() === 'horizontal') {
2338
- const gradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
2824
+ const gradient = ctx.createLinearGradient(0, 0, w, 0);
2339
2825
  gradient.addColorStop(0, `hsl(${this.hue()}, 0%, 50%)`);
2340
2826
  gradient.addColorStop(1, `hsl(${this.hue()}, 100%, 50%)`);
2341
2827
  ctx.fillStyle = gradient;
2342
2828
  ctx.fillRect(0, 0, canvas.width, canvas.height);
2343
2829
  }
2344
2830
  else {
2345
- const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
2831
+ const gradient = ctx.createLinearGradient(0, 0, 0, h);
2346
2832
  gradient.addColorStop(0, `hsl(${this.hue()}, 0%, 50%)`);
2347
2833
  gradient.addColorStop(1, `hsl(${this.hue()}, 100%, 50%)`);
2348
2834
  ctx.fillStyle = gradient;
@@ -2350,9 +2836,11 @@ class ShipColorPicker {
2350
2836
  }
2351
2837
  }
2352
2838
  drawHue() {
2353
- const { canvas, ctx } = this.canvasData();
2839
+ const { canvas, ctx } = this.#canvasData();
2354
2840
  ctx.clearRect(0, 0, canvas.width, canvas.height);
2355
- const gradient = ctx.createLinearGradient(0, 0, this.direction() === 'horizontal' ? canvas.width : 0, this.direction() === 'horizontal' ? 0 : canvas.height);
2841
+ const w = Math.max(1, canvas.width - 1);
2842
+ const h = Math.max(1, canvas.height - 1);
2843
+ const gradient = ctx.createLinearGradient(0, 0, this.direction() === 'horizontal' ? w : 0, this.direction() === 'horizontal' ? 0 : h);
2356
2844
  for (let i = 0; i <= 360; i += 10) {
2357
2845
  gradient.addColorStop(i / 360, `hsl(${i}, 100%, 50%)`);
2358
2846
  }
@@ -2360,7 +2848,7 @@ class ShipColorPicker {
2360
2848
  ctx.fillRect(0, 0, canvas.width, canvas.height);
2361
2849
  }
2362
2850
  drawColorWheel() {
2363
- const { canvas, ctx, centerX, centerY, radius } = this.canvasData();
2851
+ const { canvas, ctx, centerX, centerY, radius } = this.#canvasData();
2364
2852
  ctx.clearRect(0, 0, canvas.width, canvas.height);
2365
2853
  for (let y = 0; y < canvas.height; y++) {
2366
2854
  for (let x = 0; x < canvas.width; x++) {
@@ -2375,7 +2863,7 @@ class ShipColorPicker {
2375
2863
  }
2376
2864
  }
2377
2865
  drawGrid() {
2378
- const { canvas, ctx } = this.canvasData();
2866
+ const { canvas, ctx } = this.#canvasData();
2379
2867
  ctx.clearRect(0, 0, canvas.width, canvas.height);
2380
2868
  const gridSize = this.gridSize();
2381
2869
  const cellSize = canvas.width / gridSize;
@@ -2396,38 +2884,8 @@ class ShipColorPicker {
2396
2884
  }
2397
2885
  }
2398
2886
  }
2399
- rgbToHex(r, g, b) {
2400
- return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
2401
- }
2402
- rgbToHsl(r, g, b) {
2403
- r /= 255;
2404
- g /= 255;
2405
- b /= 255;
2406
- const max = Math.max(r, g, b), min = Math.min(r, g, b);
2407
- let h = 0, s = 0, l = (max + min) / 2;
2408
- if (max === min) {
2409
- h = s = 0;
2410
- }
2411
- else {
2412
- const d = max - min;
2413
- s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
2414
- switch (max) {
2415
- case r:
2416
- h = (g - b) / d + (g < b ? 6 : 0);
2417
- break;
2418
- case g:
2419
- h = (b - r) / d + 2;
2420
- break;
2421
- case b:
2422
- h = (r - g) / d + 4;
2423
- break;
2424
- }
2425
- h /= 6;
2426
- }
2427
- return `hsl(${Math.floor(h * 360)}, ${Math.round(s * 100)}%, ${Math.round(l * 100)}%)`;
2428
- }
2429
2887
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ShipColorPicker, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2430
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.5", type: ShipColorPicker, isStandalone: true, selector: "sh-color-picker", inputs: { showDarkColors: { classPropertyName: "showDarkColors", publicName: "showDarkColors", isSignal: true, isRequired: false, transformFunction: null }, renderingType: { classPropertyName: "renderingType", publicName: "renderingType", isSignal: true, isRequired: false, transformFunction: null }, gridSize: { classPropertyName: "gridSize", publicName: "gridSize", isSignal: true, isRequired: false, transformFunction: null }, hue: { classPropertyName: "hue", publicName: "hue", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, selectedColor: { classPropertyName: "selectedColor", publicName: "selectedColor", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedColor: "selectedColorChange", currentColor: "currentColor" }, host: { listeners: { "window:resize": "onResize()" }, properties: { "class": "renderingType()", "class.vertical": "(renderingType() === \"hue\" || renderingType() === \"saturation\") && direction() === \"vertical\"" } }, viewQueries: [{ propertyName: "canvasRef", first: true, predicate: ["colorCanvas"], descendants: true, isSignal: true }], ngImport: i0, template: `
2888
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.5", type: ShipColorPicker, isStandalone: true, selector: "sh-color-picker", inputs: { showDarkColors: { classPropertyName: "showDarkColors", publicName: "showDarkColors", isSignal: true, isRequired: false, transformFunction: null }, renderingType: { classPropertyName: "renderingType", publicName: "renderingType", isSignal: true, isRequired: false, transformFunction: null }, gridSize: { classPropertyName: "gridSize", publicName: "gridSize", isSignal: true, isRequired: false, transformFunction: null }, hue: { classPropertyName: "hue", publicName: "hue", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, selectedColor: { classPropertyName: "selectedColor", publicName: "selectedColor", isSignal: true, isRequired: false, transformFunction: null }, alpha: { classPropertyName: "alpha", publicName: "alpha", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { hue: "hueChange", selectedColor: "selectedColorChange", alpha: "alphaChange", currentColor: "currentColor" }, host: { listeners: { "window:resize": "onResize()" }, properties: { "class": "renderingType()", "class.vertical": "(renderingType() === \"hue\" || renderingType() === \"saturation\") && direction() === \"vertical\"" } }, viewQueries: [{ propertyName: "canvasRef", first: true, predicate: ["colorCanvas"], descendants: true, isSignal: true }], ngImport: i0, template: `
2431
2889
  <canvas #colorCanvas></canvas>
2432
2890
  <div
2433
2891
  class="marker"
@@ -2455,641 +2913,389 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
2455
2913
  '[class.vertical]': '(renderingType() === "hue" || renderingType() === "saturation") && direction() === "vertical"',
2456
2914
  },
2457
2915
  }]
2458
- }], propDecorators: { canvasRef: [{ type: i0.ViewChild, args: ['colorCanvas', { isSignal: true }] }], showDarkColors: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDarkColors", required: false }] }], renderingType: [{ type: i0.Input, args: [{ isSignal: true, alias: "renderingType", required: false }] }], gridSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "gridSize", required: false }] }], hue: [{ type: i0.Input, args: [{ isSignal: true, alias: "hue", required: false }] }], direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }], selectedColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedColor", required: false }] }, { type: i0.Output, args: ["selectedColorChange"] }], currentColor: [{ type: i0.Output, args: ["currentColor"] }], onResize: [{
2916
+ }], propDecorators: { canvasRef: [{ type: i0.ViewChild, args: ['colorCanvas', { isSignal: true }] }], showDarkColors: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDarkColors", required: false }] }], renderingType: [{ type: i0.Input, args: [{ isSignal: true, alias: "renderingType", required: false }] }], gridSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "gridSize", required: false }] }], hue: [{ type: i0.Input, args: [{ isSignal: true, alias: "hue", required: false }] }, { type: i0.Output, args: ["hueChange"] }], direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }], selectedColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedColor", required: false }] }, { type: i0.Output, args: ["selectedColorChange"] }], alpha: [{ type: i0.Input, args: [{ isSignal: true, alias: "alpha", required: false }] }, { type: i0.Output, args: ["alphaChange"] }], currentColor: [{ type: i0.Output, args: ["currentColor"] }], onResize: [{
2459
2917
  type: HostListener,
2460
2918
  args: ['window:resize', []]
2461
2919
  }] } });
2462
2920
 
2463
- class ShipDatepicker {
2921
+ const BASE_SPACE = 4;
2922
+ const SCROLLABLE_STYLES = ['scroll', 'auto'];
2923
+ const DEFAULT_OPTIONS = {
2924
+ width: undefined,
2925
+ height: undefined,
2926
+ closeOnButton: true,
2927
+ closeOnEsc: true,
2928
+ };
2929
+ class ShipPopover {
2464
2930
  constructor() {
2465
- this.#INIT_DATE = new Date(new Date().setHours(0, 0, 0, 0));
2466
- this.date = model(null, ...(ngDevMode ? [{ debugName: "date" }] : /* istanbul ignore next */ []));
2467
- this.endDate = model(null, ...(ngDevMode ? [{ debugName: "endDate" }] : /* istanbul ignore next */ []));
2468
- this.asRange = input(false, ...(ngDevMode ? [{ debugName: "asRange" }] : /* istanbul ignore next */ []));
2469
- this.monthsToShow = input(1, ...(ngDevMode ? [{ debugName: "monthsToShow" }] : /* istanbul ignore next */ []));
2470
- this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
2471
- this.startOfWeek = input(1, ...(ngDevMode ? [{ debugName: "startOfWeek" }] : /* istanbul ignore next */ []));
2472
- this.weekdayLabels = input(['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], ...(ngDevMode ? [{ debugName: "weekdayLabels" }] : /* istanbul ignore next */ []));
2473
- this.daysRef = viewChild('daysRef', ...(ngDevMode ? [{ debugName: "daysRef" }] : /* istanbul ignore next */ []));
2474
- this.currentDate = signal(this.date() ?? this.#INIT_DATE, ...(ngDevMode ? [{ debugName: "currentDate" }] : /* istanbul ignore next */ []));
2475
- this.monthOffsets = computed(() => {
2476
- return Array.from({ length: this.monthsToShow() }, (_, i) => i);
2477
- }, ...(ngDevMode ? [{ debugName: "monthOffsets" }] : /* istanbul ignore next */ []));
2478
- this.selectedDateStylePosition = signal(null, ...(ngDevMode ? [{ debugName: "selectedDateStylePosition" }] : /* istanbul ignore next */ []));
2479
- this.weekdays = computed(() => {
2480
- const startOfWeek = this.startOfWeek();
2481
- const weekdayLabels = this.weekdayLabels();
2482
- return weekdayLabels.slice(startOfWeek).concat(weekdayLabels.slice(0, startOfWeek));
2483
- }, ...(ngDevMode ? [{ debugName: "weekdays" }] : /* istanbul ignore next */ []));
2484
- this.currentClasses = classMutationSignal();
2485
- this.someEffect = effect(() => {
2486
- const _ = this.currentClasses();
2487
- this.#findSelectedAndCalc();
2488
- }, ...(ngDevMode ? [{ debugName: "someEffect" }] : /* istanbul ignore next */ []));
2489
- this.#newDateEffect = effect(() => {
2490
- if (this.monthsToShow() > 1)
2491
- return;
2492
- this.#setDateAsCurrent();
2493
- }, ...(ngDevMode ? [{ debugName: "#newDateEffect" }] : /* istanbul ignore next */ []));
2494
- }
2495
- #INIT_DATE;
2496
- getLastVisibleMonth() {
2497
- const lastMonthOffset = this.monthsToShow() - 1;
2498
- return this.getOffsetDate(lastMonthOffset);
2499
- }
2500
- getOffsetDate(monthOffset) {
2501
- const date = new Date(this.currentDate());
2502
- date.setMonth(date.getMonth() + monthOffset);
2503
- return date;
2931
+ this.#document = inject(DOCUMENT);
2932
+ this.SUPPORTS_ANCHOR = typeof CSS !== 'undefined' && CSS.supports('position-anchor', '--abc') && CSS.supports('anchor-name', '--abc');
2933
+ this.asMultiLayer = input(false, ...(ngDevMode ? [{ debugName: "asMultiLayer" }] : /* istanbul ignore next */ []));
2934
+ this.asSheetOnMobile = input(false, ...(ngDevMode ? [{ debugName: "asSheetOnMobile" }] : /* istanbul ignore next */ []));
2935
+ this.disableOpenByClick = input(false, ...(ngDevMode ? [{ debugName: "disableOpenByClick" }] : /* istanbul ignore next */ []));
2936
+ this.isOpen = model(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
2937
+ this.options = input(...(ngDevMode ? [undefined, { debugName: "options" }] : /* istanbul ignore next */ []));
2938
+ this.closed = output();
2939
+ this.defaultOptionMerge = computed(() => ({
2940
+ ...DEFAULT_OPTIONS,
2941
+ ...this.options(),
2942
+ }), ...(ngDevMode ? [{ debugName: "defaultOptionMerge" }] : /* istanbul ignore next */ []));
2943
+ this.triggerRef = viewChild.required('triggerRef');
2944
+ this.popoverRef = viewChild('popoverRef', ...(ngDevMode ? [{ debugName: "popoverRef" }] : /* istanbul ignore next */ []));
2945
+ this.popoverContentRef = viewChild('popoverContentRef', ...(ngDevMode ? [{ debugName: "popoverContentRef" }] : /* istanbul ignore next */ []));
2946
+ this.id = signal('--' + generateUniqueId(), ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
2947
+ this.menuStyle = signal(null, ...(ngDevMode ? [{ debugName: "menuStyle" }] : /* istanbul ignore next */ []));
2948
+ this.openAbort = null;
2949
+ this.#originalOverflow = null;
2950
+ this.openEffect = effect(() => {
2951
+ const open = this.isOpen();
2952
+ queueMicrotask(() => {
2953
+ const popoverEl = this.popoverRef()?.nativeElement;
2954
+ if (!popoverEl) {
2955
+ this.openAbort?.abort();
2956
+ this.openAbort = null;
2957
+ return;
2958
+ }
2959
+ if (open) {
2960
+ if (this.openAbort) {
2961
+ this.openAbort.abort();
2962
+ }
2963
+ this.openAbort = new AbortController();
2964
+ this.#document.addEventListener('keydown', (e) => {
2965
+ if (e.key === 'Escape' && !this.defaultOptionMerge().closeOnEsc) {
2966
+ e.preventDefault();
2967
+ }
2968
+ if (e.key === 'Escape' && this.defaultOptionMerge().closeOnEsc) {
2969
+ this.isOpen.set(false);
2970
+ }
2971
+ }, {
2972
+ signal: this.openAbort?.signal,
2973
+ });
2974
+ if (!popoverEl.isConnected)
2975
+ return;
2976
+ popoverEl.showPopover();
2977
+ const isMobileSheet = this.asSheetOnMobile() && window.innerWidth <= 768;
2978
+ if (isMobileSheet) {
2979
+ this.#originalOverflow = this.#document.body.style.overflow;
2980
+ this.#document.body.style.overflow = 'hidden';
2981
+ this.menuStyle.set(null); // Clear any leftover inline styles
2982
+ }
2983
+ if (!this.SUPPORTS_ANCHOR && !isMobileSheet) {
2984
+ setTimeout(() => {
2985
+ const scrollableParent = this.#findScrollableParent(popoverEl);
2986
+ scrollableParent.addEventListener('scroll', () => this.#calculateMenuPosition(), {
2987
+ signal: this.openAbort?.signal,
2988
+ });
2989
+ window?.addEventListener('resize', () => this.#calculateMenuPosition(), {
2990
+ signal: this.openAbort?.signal,
2991
+ });
2992
+ this.#calculateMenuPosition();
2993
+ });
2994
+ }
2995
+ }
2996
+ else {
2997
+ this.closed.emit();
2998
+ popoverEl.hidePopover();
2999
+ this.openAbort?.abort();
3000
+ this.openAbort = null;
3001
+ if (this.asSheetOnMobile() && this.#originalOverflow !== null) {
3002
+ this.#document.body.style.overflow = this.#originalOverflow;
3003
+ this.#originalOverflow = null;
3004
+ }
3005
+ }
3006
+ });
3007
+ }, ...(ngDevMode ? [{ debugName: "openEffect" }] : /* istanbul ignore next */ []));
2504
3008
  }
2505
- getMonthDates(monthOffset) {
2506
- const offsetDate = this.getOffsetDate(monthOffset);
2507
- return this.#generateMonthDates(offsetDate, this.startOfWeek());
3009
+ #document;
3010
+ #originalOverflow;
3011
+ toggleIsOpen(event) {
3012
+ if (!this.disableOpenByClick()) {
3013
+ event.preventDefault();
3014
+ event.stopPropagation();
3015
+ this.isOpen.set(!this.isOpen());
3016
+ }
2508
3017
  }
2509
- #newDateEffect;
2510
- ngOnInit() {
2511
- if (this.monthsToShow() === 1)
3018
+ eventClose($event) {
3019
+ if (!this.isOpen())
2512
3020
  return;
2513
- this.#setDateAsCurrent();
2514
- }
2515
- #setDateAsCurrent() {
2516
- const newDate = this.date();
2517
- if (newDate)
2518
- this.currentDate.set(newDate);
2519
- this.#findSelectedAndCalc();
3021
+ this.isOpen.set(false);
2520
3022
  }
2521
- #findSelectedAndCalc() {
2522
- setTimeout(() => {
2523
- const selectedElement = this.daysRef()?.nativeElement.querySelector('.sel');
2524
- if (!selectedElement) {
2525
- return this.selectedDateStylePosition.update((x) => (x ? { ...x, opacity: '0' } : null));
3023
+ #findScrollableParent(element) {
3024
+ let parent = element.parentElement;
3025
+ while (parent) {
3026
+ if (SCROLLABLE_STYLES.indexOf(window?.getComputedStyle(parent).overflowY) > -1 &&
3027
+ parent.scrollHeight > parent.clientHeight) {
3028
+ return parent;
2526
3029
  }
2527
- this.setSelectedDateStylePosition(selectedElement);
2528
- });
2529
- }
2530
- #generateMonthDates(date, startOfWeek) {
2531
- const year = date.getFullYear();
2532
- const month = date.getMonth();
2533
- const firstDay = new Date(year, month, 1).getDay();
2534
- const daysInMonth = new Date(year, month + 1, 0).getDate();
2535
- const dates = [];
2536
- let offset = firstDay - startOfWeek;
2537
- if (offset < 0) {
2538
- offset += 7;
2539
- }
2540
- const lastDayOfPrevMonth = new Date(year, month, 0).getDate();
2541
- for (let i = offset - 1; i >= 0; i--) {
2542
- dates.push(new Date(year, month - 1, lastDayOfPrevMonth - i, 0, 0, 0, 0));
2543
- }
2544
- for (let i = 1; i <= daysInMonth; i++) {
2545
- dates.push(new Date(year, month, i, 0, 0, 0, 0));
2546
- }
2547
- let nextMonthDay = 1;
2548
- while (dates.length % 7 !== 0) {
2549
- dates.push(new Date(year, month + 1, nextMonthDay++, 0, 0, 0, 0));
3030
+ parent = parent.parentElement;
2550
3031
  }
2551
- return dates;
3032
+ return this.#document.documentElement;
2552
3033
  }
2553
- nextMonth() {
2554
- this.currentDate.update((currentDate) => {
2555
- const newDate = new Date(currentDate);
2556
- newDate.setMonth(currentDate.getMonth() + 1);
2557
- return newDate;
2558
- });
2559
- this.#findSelectedAndCalc();
3034
+ /**
3035
+ * Position generators that mirror the CSS position-try-fallbacks.
3036
+ * Each returns { left, top } for the popover-content in fixed coordinates.
3037
+ */
3038
+ // bottom span-right: below trigger, left edge aligned with trigger left
3039
+ #bottomSpanRight(t, _m) {
3040
+ return { left: t.left, top: t.bottom + BASE_SPACE };
2560
3041
  }
2561
- previousMonth() {
2562
- this.currentDate.update((currentDate) => {
2563
- const newDate = new Date(currentDate);
2564
- newDate.setMonth(currentDate.getMonth() - 1);
2565
- return newDate;
2566
- });
2567
- this.#findSelectedAndCalc();
3042
+ // top span-right: above trigger, left edge aligned with trigger left
3043
+ #topSpanRight(t, m) {
3044
+ return { left: t.left, top: t.top - m.height - BASE_SPACE };
2568
3045
  }
2569
- setDate(newDate, selectedElement) {
2570
- const createDateWithExistingTime = (newDate, existingDate) => {
2571
- const hours = existingDate?.getHours() ?? 0;
2572
- const minutes = existingDate?.getMinutes() ?? 0;
2573
- const seconds = existingDate?.getSeconds() ?? 0;
2574
- const milliseconds = existingDate?.getMilliseconds() ?? 0;
2575
- return new Date(newDate.getFullYear(), newDate.getMonth(), newDate.getDate(), hours, minutes, seconds, milliseconds);
2576
- };
2577
- if (!this.asRange()) {
2578
- this.date.set(createDateWithExistingTime(newDate, this.date()));
2579
- this.endDate.set(null);
2580
- }
2581
- else {
2582
- const startDate = this.date();
2583
- const endDate = this.endDate();
2584
- const utcDate = createDateWithExistingTime(newDate, null);
2585
- if (!startDate) {
2586
- this.date.set(utcDate);
2587
- }
2588
- else if (!endDate) {
2589
- if (utcDate < startDate) {
2590
- this.date.set(utcDate);
2591
- this.endDate.set(null);
2592
- }
2593
- else {
2594
- this.endDate.set(utcDate);
2595
- }
2596
- }
2597
- else {
2598
- this.date.set(utcDate);
2599
- this.endDate.set(null);
2600
- }
2601
- }
2602
- if (this.asRange())
2603
- return;
2604
- this.setSelectedDateStylePosition(selectedElement);
3046
+ // bottom span-left: below trigger, right edge aligned with trigger right
3047
+ #bottomSpanLeft(t, m) {
3048
+ return { left: t.right - m.width, top: t.bottom + BASE_SPACE };
2605
3049
  }
2606
- isDateSelected(date) {
2607
- const startDate = this.date();
2608
- const endDate = this.endDate();
2609
- if (startDate === null)
2610
- return null;
2611
- const startOfDay = (date) => {
2612
- return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0);
2613
- };
2614
- const endOfDay = (date) => {
2615
- return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59, 999);
2616
- };
2617
- const currentDate = startOfDay(date);
2618
- const rangeStart = startOfDay(startDate);
2619
- const rangeEnd = endDate ? endOfDay(endDate) : null;
2620
- let classes = [];
2621
- if (this.asRange()) {
2622
- if (rangeEnd === null) {
2623
- if (currentDate.getTime() === rangeStart.getTime()) {
2624
- classes.push('sel first last');
2625
- }
2626
- }
2627
- else {
2628
- if (currentDate.getTime() === rangeStart.getTime()) {
2629
- classes.push('first');
2630
- }
2631
- if (currentDate.getTime() === startOfDay(rangeEnd).getTime()) {
2632
- classes.push('last');
2633
- }
2634
- if (currentDate >= rangeStart && currentDate <= rangeEnd) {
2635
- classes.push('sel');
2636
- const dayOfWeek = currentDate.getDay();
2637
- const startOfWeek = this.startOfWeek();
2638
- if (dayOfWeek === startOfWeek) {
2639
- classes.push('week-start');
2640
- }
2641
- const endOfWeek = (startOfWeek + 6) % 7;
2642
- if (dayOfWeek === endOfWeek) {
2643
- classes.push('week-end');
2644
- }
2645
- }
2646
- const nextDate = new Date(currentDate);
2647
- nextDate.setDate(currentDate.getDate() + 1);
2648
- const prevDate = new Date(currentDate);
2649
- prevDate.setDate(currentDate.getDate() - 1);
2650
- const isFirstOfMonth = currentDate.getDate() === 1;
2651
- const isLastOfMonth = nextDate.getMonth() !== currentDate.getMonth();
2652
- if (isFirstOfMonth) {
2653
- classes.push('month-start');
2654
- }
2655
- if (isLastOfMonth) {
2656
- classes.push('month-end');
2657
- }
2658
- }
2659
- }
2660
- else {
2661
- if (currentDate.getTime() === rangeStart.getTime()) {
2662
- classes.push('sel');
2663
- }
2664
- }
2665
- return classes.join(' ') || null;
3050
+ // top span-left: above trigger, right edge aligned with trigger right
3051
+ #topSpanLeft(t, m) {
3052
+ return { left: t.right - m.width, top: t.top - m.height - BASE_SPACE };
2666
3053
  }
2667
- setSelectedDateStylePosition(selectedElement) {
2668
- this.selectedDateStylePosition.set({
2669
- transform: `translate(${selectedElement.offsetLeft}px, ${selectedElement.offsetTop}px)`,
2670
- opacity: '1',
2671
- });
3054
+ // right span-bottom: to the right of trigger, top edge aligned with trigger top
3055
+ #rightSpanBottom(t, _m) {
3056
+ return { left: t.right + BASE_SPACE, top: t.top };
2672
3057
  }
2673
- getMonthName(date) {
2674
- return date.toLocaleString('default', { month: 'long' });
3058
+ // left span-bottom: to the left of trigger, top edge aligned with trigger top
3059
+ #leftSpanBottom(t, m) {
3060
+ return { left: t.left - m.width - BASE_SPACE, top: t.top };
2675
3061
  }
2676
- getFullYear(date) {
2677
- return date.getFullYear();
3062
+ // right center: to the right of trigger, vertically centered
3063
+ #rightCenter(t, m) {
3064
+ return { left: t.right + BASE_SPACE, top: t.top + t.height / 2 - m.height / 2 };
2678
3065
  }
2679
- // Rest of the component methods remain the same, but update isCurrentMonth:
2680
- isCurrentMonth(date, monthOffset) {
2681
- const offsetDate = this.getOffsetDate(monthOffset);
2682
- return date.getMonth() === offsetDate.getMonth();
3066
+ // left center: to the left of trigger, vertically centered
3067
+ #leftCenter(t, m) {
3068
+ return { left: t.left - m.width - BASE_SPACE, top: t.top + t.height / 2 - m.height / 2 };
2683
3069
  }
2684
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ShipDatepicker, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2685
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: ShipDatepicker, isStandalone: true, selector: "sh-datepicker", inputs: { date: { classPropertyName: "date", publicName: "date", isSignal: true, isRequired: false, transformFunction: null }, endDate: { classPropertyName: "endDate", publicName: "endDate", isSignal: true, isRequired: false, transformFunction: null }, asRange: { classPropertyName: "asRange", publicName: "asRange", isSignal: true, isRequired: false, transformFunction: null }, monthsToShow: { classPropertyName: "monthsToShow", publicName: "monthsToShow", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, startOfWeek: { classPropertyName: "startOfWeek", publicName: "startOfWeek", isSignal: true, isRequired: false, transformFunction: null }, weekdayLabels: { classPropertyName: "weekdayLabels", publicName: "weekdayLabels", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { date: "dateChange", endDate: "endDateChange" }, host: { properties: { "class.as-range": "asRange()", "class": "\"columns-\" + monthsToShow()", "class.disabled": "disabled()" } }, viewQueries: [{ propertyName: "daysRef", first: true, predicate: ["daysRef"], descendants: true, isSignal: true }], ngImport: i0, template: `
2686
- <header>
2687
- <button (click)="previousMonth()"><sh-icon>caret-left</sh-icon></button>
2688
- <div class="title">
2689
- {{ getMonthName(currentDate()!) }}
2690
- @if (monthsToShow() > 1) {
2691
- - {{ getMonthName(getLastVisibleMonth()) }}
3070
+ // right span-top: to the right of trigger, bottom edge aligned with trigger bottom
3071
+ #rightSpanTop(t, m) {
3072
+ return { left: t.right + BASE_SPACE, top: t.bottom - m.height };
3073
+ }
3074
+ // left span-top: to the left of trigger, bottom edge aligned with trigger bottom
3075
+ #leftSpanTop(t, m) {
3076
+ return { left: t.left - m.width - BASE_SPACE, top: t.bottom - m.height };
3077
+ }
3078
+ /** Check if a position fits entirely within the viewport */
3079
+ #fitsInViewport(pos, m) {
3080
+ return (pos.left >= 0 &&
3081
+ pos.top >= 0 &&
3082
+ pos.left + m.width <= window.innerWidth &&
3083
+ pos.top + m.height <= window.innerHeight);
3084
+ }
3085
+ /** Clamp a position so the popover stays within the viewport */
3086
+ #clampToViewport(pos, m) {
3087
+ return {
3088
+ left: Math.max(0, Math.min(pos.left, window.innerWidth - m.width)),
3089
+ top: Math.max(0, Math.min(pos.top, window.innerHeight - m.height)),
3090
+ };
3091
+ }
3092
+ #calculateMenuPosition() {
3093
+ const triggerRect = this.triggerRef()?.nativeElement.getBoundingClientRect();
3094
+ const menuRect = this.popoverContentRef()?.nativeElement.getBoundingClientRect();
3095
+ if (!triggerRect || !menuRect)
3096
+ return;
3097
+ // Mirror the CSS position-try-fallbacks order
3098
+ const tryOrderDefault = [
3099
+ this.#bottomSpanRight,
3100
+ this.#topSpanRight,
3101
+ this.#bottomSpanLeft,
3102
+ this.#topSpanLeft,
3103
+ this.#rightSpanBottom,
3104
+ this.#leftSpanBottom,
3105
+ this.#rightCenter,
3106
+ this.#leftCenter,
3107
+ this.#rightSpanTop,
3108
+ this.#leftSpanTop,
3109
+ ];
3110
+ const tryOrderMultiLayer = [
3111
+ this.#rightSpanBottom,
3112
+ this.#rightSpanTop,
3113
+ this.#leftSpanBottom,
3114
+ this.#leftSpanTop,
3115
+ this.#rightCenter,
3116
+ this.#leftCenter,
3117
+ this.#bottomSpanRight,
3118
+ this.#topSpanRight,
3119
+ this.#bottomSpanLeft,
3120
+ this.#topSpanLeft,
3121
+ ];
3122
+ const tryOrder = this.asMultiLayer() ? tryOrderMultiLayer : tryOrderDefault;
3123
+ // Try each position, use the first one that fits
3124
+ for (const positionFn of tryOrder) {
3125
+ const pos = positionFn.call(this, triggerRect, menuRect);
3126
+ if (this.#fitsInViewport(pos, menuRect)) {
3127
+ this.menuStyle.set({ left: pos.left + 'px', top: pos.top + 'px' });
3128
+ return;
3129
+ }
2692
3130
  }
2693
- {{ getFullYear(currentDate()!) }}
3131
+ // If nothing fits perfectly, use the first position clamped to viewport
3132
+ const fallback = this.#clampToViewport(tryOrder[0].call(this, triggerRect, menuRect), menuRect);
3133
+ this.menuStyle.set({ left: fallback.left + 'px', top: fallback.top + 'px' });
3134
+ }
3135
+ ngOnDestroy() {
3136
+ this.openAbort?.abort();
3137
+ this.openAbort = null;
3138
+ }
3139
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ShipPopover, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3140
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: ShipPopover, isStandalone: true, selector: "sh-popover", inputs: { asMultiLayer: { classPropertyName: "asMultiLayer", publicName: "asMultiLayer", isSignal: true, isRequired: false, transformFunction: null }, asSheetOnMobile: { classPropertyName: "asSheetOnMobile", publicName: "asSheetOnMobile", isSignal: true, isRequired: false, transformFunction: null }, disableOpenByClick: { classPropertyName: "disableOpenByClick", publicName: "disableOpenByClick", isSignal: true, isRequired: false, transformFunction: null }, isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { isOpen: "isOpenChange", closed: "closed" }, host: { properties: { "class.multi-layer": "asMultiLayer()", "class.as-sheet": "asSheetOnMobile()" } }, viewQueries: [{ propertyName: "triggerRef", first: true, predicate: ["triggerRef"], descendants: true, isSignal: true }, { propertyName: "popoverRef", first: true, predicate: ["popoverRef"], descendants: true, isSignal: true }, { propertyName: "popoverContentRef", first: true, predicate: ["popoverContentRef"], descendants: true, isSignal: true }], ngImport: i0, template: `
3141
+ <div class="trigger" #triggerRef [attr.popovertarget]="id() + 'hello'" (click)="toggleIsOpen($event)">
3142
+ <div class="trigger-wrapper">
3143
+ <ng-content select="[trigger]" />
3144
+ <ng-content select="button" />
3145
+ <ng-content select="[shButton]" />
2694
3146
  </div>
2695
- <button (click)="nextMonth()"><sh-icon>caret-right</sh-icon></button>
2696
- </header>
2697
-
2698
- <section class="months-container">
2699
- @for (monthOffset of monthOffsets(); track monthOffset) {
2700
- <div class="month">
2701
- <nav class="weekdays">
2702
- @for (day of weekdays(); track $index) {
2703
- <div>{{ day }}</div>
2704
- }
2705
- </nav>
2706
-
2707
- <div class="days" #daysRef>
2708
- @for (calDate of getMonthDates(monthOffset); track $index) {
2709
- <div
2710
- #elementRef
2711
- [class.out-of-scope]="!isCurrentMonth(calDate, monthOffset)"
2712
- [class]="isDateSelected(calDate)"
2713
- (click)="setDate(calDate, elementRef)">
2714
- {{ calDate.getDate() }}
2715
- </div>
2716
- }
3147
+ <div class="trigger-anchor" [style.anchor-name]="id()"></div>
3148
+ </div>
2717
3149
 
2718
- @if (!asRange()) {
2719
- <article class="days">
2720
- <div class="sel-el" [style]="selectedDateStylePosition()"></div>
2721
- </article>
2722
- }
2723
- </div>
3150
+ @if (isOpen()) {
3151
+ <div [attr.id]="id() + 'hello'" popover="manual" #popoverRef class="popover">
3152
+ <div class="overlay" (click)="eventClose($event)"></div>
3153
+ <div class="popover-content" #popoverContentRef [style.position-anchor]="id()" [style]="menuStyle()">
3154
+ <ng-content />
2724
3155
  </div>
2725
- }
2726
- </section>
2727
- `, isInline: true, dependencies: [{ kind: "component", type: ShipIcon, selector: "sh-icon", inputs: ["color", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3156
+ </div>
3157
+ }
3158
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2728
3159
  }
2729
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ShipDatepicker, decorators: [{
3160
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ShipPopover, decorators: [{
2730
3161
  type: Component,
2731
3162
  args: [{
2732
- selector: 'sh-datepicker',
2733
- imports: [ShipIcon],
3163
+ selector: 'sh-popover',
3164
+ imports: [],
2734
3165
  template: `
2735
- <header>
2736
- <button (click)="previousMonth()"><sh-icon>caret-left</sh-icon></button>
2737
- <div class="title">
2738
- {{ getMonthName(currentDate()!) }}
2739
- @if (monthsToShow() > 1) {
2740
- - {{ getMonthName(getLastVisibleMonth()) }}
2741
- }
2742
- {{ getFullYear(currentDate()!) }}
3166
+ <div class="trigger" #triggerRef [attr.popovertarget]="id() + 'hello'" (click)="toggleIsOpen($event)">
3167
+ <div class="trigger-wrapper">
3168
+ <ng-content select="[trigger]" />
3169
+ <ng-content select="button" />
3170
+ <ng-content select="[shButton]" />
2743
3171
  </div>
2744
- <button (click)="nextMonth()"><sh-icon>caret-right</sh-icon></button>
2745
- </header>
2746
-
2747
- <section class="months-container">
2748
- @for (monthOffset of monthOffsets(); track monthOffset) {
2749
- <div class="month">
2750
- <nav class="weekdays">
2751
- @for (day of weekdays(); track $index) {
2752
- <div>{{ day }}</div>
2753
- }
2754
- </nav>
2755
-
2756
- <div class="days" #daysRef>
2757
- @for (calDate of getMonthDates(monthOffset); track $index) {
2758
- <div
2759
- #elementRef
2760
- [class.out-of-scope]="!isCurrentMonth(calDate, monthOffset)"
2761
- [class]="isDateSelected(calDate)"
2762
- (click)="setDate(calDate, elementRef)">
2763
- {{ calDate.getDate() }}
2764
- </div>
2765
- }
3172
+ <div class="trigger-anchor" [style.anchor-name]="id()"></div>
3173
+ </div>
2766
3174
 
2767
- @if (!asRange()) {
2768
- <article class="days">
2769
- <div class="sel-el" [style]="selectedDateStylePosition()"></div>
2770
- </article>
2771
- }
2772
- </div>
3175
+ @if (isOpen()) {
3176
+ <div [attr.id]="id() + 'hello'" popover="manual" #popoverRef class="popover">
3177
+ <div class="overlay" (click)="eventClose($event)"></div>
3178
+ <div class="popover-content" #popoverContentRef [style.position-anchor]="id()" [style]="menuStyle()">
3179
+ <ng-content />
2773
3180
  </div>
2774
- }
2775
- </section>
3181
+ </div>
3182
+ }
2776
3183
  `,
2777
3184
  changeDetection: ChangeDetectionStrategy.OnPush,
2778
3185
  host: {
2779
- '[class.as-range]': 'asRange()',
2780
- '[class]': '"columns-" + monthsToShow()',
2781
- '[class.disabled]': 'disabled()',
3186
+ '[class.multi-layer]': 'asMultiLayer()',
3187
+ '[class.as-sheet]': 'asSheetOnMobile()',
2782
3188
  },
2783
3189
  }]
2784
- }], propDecorators: { date: [{ type: i0.Input, args: [{ isSignal: true, alias: "date", required: false }] }, { type: i0.Output, args: ["dateChange"] }], endDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "endDate", required: false }] }, { type: i0.Output, args: ["endDateChange"] }], asRange: [{ type: i0.Input, args: [{ isSignal: true, alias: "asRange", required: false }] }], monthsToShow: [{ type: i0.Input, args: [{ isSignal: true, alias: "monthsToShow", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], startOfWeek: [{ type: i0.Input, args: [{ isSignal: true, alias: "startOfWeek", required: false }] }], weekdayLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "weekdayLabels", required: false }] }], daysRef: [{ type: i0.ViewChild, args: ['daysRef', { isSignal: true }] }] } });
3190
+ }], propDecorators: { asMultiLayer: [{ type: i0.Input, args: [{ isSignal: true, alias: "asMultiLayer", required: false }] }], asSheetOnMobile: [{ type: i0.Input, args: [{ isSignal: true, alias: "asSheetOnMobile", required: false }] }], disableOpenByClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "disableOpenByClick", required: false }] }], isOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "isOpen", required: false }] }, { type: i0.Output, args: ["isOpenChange"] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], triggerRef: [{ type: i0.ViewChild, args: ['triggerRef', { isSignal: true }] }], popoverRef: [{ type: i0.ViewChild, args: ['popoverRef', { isSignal: true }] }], popoverContentRef: [{ type: i0.ViewChild, args: ['popoverContentRef', { isSignal: true }] }] } });
2785
3191
 
2786
- const BASE_SPACE = 4;
2787
- const SCROLLABLE_STYLES = ['scroll', 'auto'];
2788
- const DEFAULT_OPTIONS = {
2789
- width: undefined,
2790
- height: undefined,
2791
- closeOnButton: true,
2792
- closeOnEsc: true,
2793
- };
2794
- class ShipPopover {
3192
+ class ShipFormFieldPopover {
2795
3193
  constructor() {
2796
- this.#document = inject(DOCUMENT);
2797
- this.SUPPORTS_ANCHOR = typeof CSS !== 'undefined' && CSS.supports('position-anchor', '--abc') && CSS.supports('anchor-name', '--abc');
2798
- this.asMultiLayer = input(false, ...(ngDevMode ? [{ debugName: "asMultiLayer" }] : /* istanbul ignore next */ []));
2799
- this.disableOpenByClick = input(false, ...(ngDevMode ? [{ debugName: "disableOpenByClick" }] : /* istanbul ignore next */ []));
3194
+ this.#selfRef = inject(ElementRef);
2800
3195
  this.isOpen = model(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
2801
- this.options = input(...(ngDevMode ? [undefined, { debugName: "options" }] : /* istanbul ignore next */ []));
2802
3196
  this.closed = output();
2803
- this.defaultOptionMerge = computed(() => ({
2804
- ...DEFAULT_OPTIONS,
2805
- ...this.options(),
2806
- }), ...(ngDevMode ? [{ debugName: "defaultOptionMerge" }] : /* istanbul ignore next */ []));
2807
- this.triggerRef = viewChild.required('triggerRef');
2808
- this.popoverRef = viewChild('popoverRef', ...(ngDevMode ? [{ debugName: "popoverRef" }] : /* istanbul ignore next */ []));
2809
- this.popoverContentRef = viewChild('popoverContentRef', ...(ngDevMode ? [{ debugName: "popoverContentRef" }] : /* istanbul ignore next */ []));
2810
- this.id = signal('--' + generateUniqueId(), ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
2811
- this.menuStyle = signal(null, ...(ngDevMode ? [{ debugName: "menuStyle" }] : /* istanbul ignore next */ []));
2812
- this.openAbort = null;
2813
- this.openEffect = effect(() => {
2814
- const open = this.isOpen();
2815
- queueMicrotask(() => {
2816
- const popoverEl = this.popoverRef()?.nativeElement;
2817
- if (!popoverEl) {
2818
- this.openAbort?.abort();
2819
- this.openAbort = null;
2820
- return;
2821
- }
2822
- if (open) {
2823
- if (this.openAbort) {
2824
- this.openAbort.abort();
2825
- }
2826
- this.openAbort = new AbortController();
2827
- this.#document.addEventListener('keydown', (e) => {
2828
- if (e.key === 'Escape' && !this.defaultOptionMerge().closeOnEsc) {
2829
- e.preventDefault();
2830
- }
2831
- if (e.key === 'Escape' && this.defaultOptionMerge().closeOnEsc) {
2832
- this.isOpen.set(false);
2833
- }
2834
- }, {
2835
- signal: this.openAbort?.signal,
2836
- });
2837
- if (!popoverEl.isConnected)
2838
- return;
2839
- popoverEl.showPopover();
2840
- if (!this.SUPPORTS_ANCHOR) {
2841
- setTimeout(() => {
2842
- const scrollableParent = this.#findScrollableParent(popoverEl);
2843
- scrollableParent.addEventListener('scroll', () => this.#calculateMenuPosition(), {
2844
- signal: this.openAbort?.signal,
2845
- });
2846
- window?.addEventListener('resize', () => this.#calculateMenuPosition(), {
2847
- signal: this.openAbort?.signal,
2848
- });
2849
- this.#calculateMenuPosition();
2850
- });
2851
- }
2852
- }
2853
- else {
2854
- this.closed.emit();
2855
- popoverEl.hidePopover();
2856
- this.openAbort?.abort();
2857
- this.openAbort = null;
2858
- }
2859
- });
2860
- }, ...(ngDevMode ? [{ debugName: "openEffect" }] : /* istanbul ignore next */ []));
3197
+ this.color = input(null, ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
3198
+ this.variant = input(null, ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
3199
+ this.size = input(null, ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
3200
+ this.readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
3201
+ this.hostClasses = shipComponentClasses('formField', {
3202
+ color: this.color,
3203
+ variant: this.variant,
3204
+ size: this.size,
3205
+ readonly: this.readonly,
3206
+ });
2861
3207
  }
2862
- #document;
2863
- toggleIsOpen(event) {
2864
- if (!this.disableOpenByClick()) {
2865
- event.preventDefault();
2866
- event.stopPropagation();
2867
- this.isOpen.set(!this.isOpen());
3208
+ #selfRef;
3209
+ onClick() {
3210
+ if (this.#selfRef.nativeElement.querySelector('input')) {
3211
+ this.#selfRef.nativeElement.querySelector('input').focus();
2868
3212
  }
2869
- }
2870
- eventClose($event) {
2871
- if (!this.isOpen())
2872
- return;
2873
- this.isOpen.set(false);
2874
- }
2875
- #findScrollableParent(element) {
2876
- let parent = element.parentElement;
2877
- while (parent) {
2878
- if (SCROLLABLE_STYLES.indexOf(window?.getComputedStyle(parent).overflowY) > -1 &&
2879
- parent.scrollHeight > parent.clientHeight) {
2880
- return parent;
2881
- }
2882
- parent = parent.parentElement;
3213
+ if (this.#selfRef.nativeElement.querySelector('textarea')) {
3214
+ this.#selfRef.nativeElement.querySelector('textarea').focus();
2883
3215
  }
2884
- return this.#document.documentElement;
2885
- }
2886
- /**
2887
- * Position generators that mirror the CSS position-try-fallbacks.
2888
- * Each returns { left, top } for the popover-content in fixed coordinates.
2889
- */
2890
- // bottom span-right: below trigger, left edge aligned with trigger left
2891
- #bottomSpanRight(t, _m) {
2892
- return { left: t.left, top: t.bottom + BASE_SPACE };
2893
- }
2894
- // top span-right: above trigger, left edge aligned with trigger left
2895
- #topSpanRight(t, m) {
2896
- return { left: t.left, top: t.top - m.height - BASE_SPACE };
2897
- }
2898
- // bottom span-left: below trigger, right edge aligned with trigger right
2899
- #bottomSpanLeft(t, m) {
2900
- return { left: t.right - m.width, top: t.bottom + BASE_SPACE };
2901
- }
2902
- // top span-left: above trigger, right edge aligned with trigger right
2903
- #topSpanLeft(t, m) {
2904
- return { left: t.right - m.width, top: t.top - m.height - BASE_SPACE };
2905
- }
2906
- // right span-bottom: to the right of trigger, top edge aligned with trigger top
2907
- #rightSpanBottom(t, _m) {
2908
- return { left: t.right + BASE_SPACE, top: t.top };
2909
- }
2910
- // left span-bottom: to the left of trigger, top edge aligned with trigger top
2911
- #leftSpanBottom(t, m) {
2912
- return { left: t.left - m.width - BASE_SPACE, top: t.top };
2913
- }
2914
- // right center: to the right of trigger, vertically centered
2915
- #rightCenter(t, m) {
2916
- return { left: t.right + BASE_SPACE, top: t.top + t.height / 2 - m.height / 2 };
2917
- }
2918
- // left center: to the left of trigger, vertically centered
2919
- #leftCenter(t, m) {
2920
- return { left: t.left - m.width - BASE_SPACE, top: t.top + t.height / 2 - m.height / 2 };
2921
- }
2922
- // right span-top: to the right of trigger, bottom edge aligned with trigger bottom
2923
- #rightSpanTop(t, m) {
2924
- return { left: t.right + BASE_SPACE, top: t.bottom - m.height };
2925
- }
2926
- // left span-top: to the left of trigger, bottom edge aligned with trigger bottom
2927
- #leftSpanTop(t, m) {
2928
- return { left: t.left - m.width - BASE_SPACE, top: t.bottom - m.height };
2929
- }
2930
- /** Check if a position fits entirely within the viewport */
2931
- #fitsInViewport(pos, m) {
2932
- return (pos.left >= 0 &&
2933
- pos.top >= 0 &&
2934
- pos.left + m.width <= window.innerWidth &&
2935
- pos.top + m.height <= window.innerHeight);
2936
3216
  }
2937
- /** Clamp a position so the popover stays within the viewport */
2938
- #clampToViewport(pos, m) {
2939
- return {
2940
- left: Math.max(0, Math.min(pos.left, window.innerWidth - m.width)),
2941
- top: Math.max(0, Math.min(pos.top, window.innerHeight - m.height)),
2942
- };
3217
+ close() {
3218
+ this.closed.emit();
2943
3219
  }
2944
- #calculateMenuPosition() {
2945
- const triggerRect = this.triggerRef()?.nativeElement.getBoundingClientRect();
2946
- const menuRect = this.popoverContentRef()?.nativeElement.getBoundingClientRect();
2947
- if (!triggerRect || !menuRect)
2948
- return;
2949
- // Mirror the CSS position-try-fallbacks order
2950
- const tryOrderDefault = [
2951
- this.#bottomSpanRight,
2952
- this.#topSpanRight,
2953
- this.#bottomSpanLeft,
2954
- this.#topSpanLeft,
2955
- this.#rightSpanBottom,
2956
- this.#leftSpanBottom,
2957
- this.#rightCenter,
2958
- this.#leftCenter,
2959
- this.#rightSpanTop,
2960
- this.#leftSpanTop,
2961
- ];
2962
- const tryOrderMultiLayer = [
2963
- this.#rightSpanBottom,
2964
- this.#rightSpanTop,
2965
- this.#leftSpanBottom,
2966
- this.#leftSpanTop,
2967
- this.#rightCenter,
2968
- this.#leftCenter,
2969
- this.#bottomSpanRight,
2970
- this.#topSpanRight,
2971
- this.#bottomSpanLeft,
2972
- this.#topSpanLeft,
2973
- ];
2974
- const tryOrder = this.asMultiLayer() ? tryOrderMultiLayer : tryOrderDefault;
2975
- // Try each position, use the first one that fits
2976
- for (const positionFn of tryOrder) {
2977
- const pos = positionFn.call(this, triggerRect, menuRect);
2978
- if (this.#fitsInViewport(pos, menuRect)) {
2979
- this.menuStyle.set({ left: pos.left + 'px', top: pos.top + 'px' });
2980
- return;
3220
+ ngOnInit() {
3221
+ const supportFieldSizing = typeof CSS !== 'undefined' && CSS.supports('field-sizing', 'content');
3222
+ const text = this.#selfRef.nativeElement.querySelector('textarea');
3223
+ if (!supportFieldSizing && text !== null) {
3224
+ const text = this.#selfRef.nativeElement.querySelector('textarea');
3225
+ function resize() {
3226
+ text.style.height = 'auto';
3227
+ text.style.height = text.scrollHeight + 'px';
3228
+ }
3229
+ /* 0-timeout to get the already changed text */
3230
+ function delayedResize() {
3231
+ setTimeout(resize, 0);
3232
+ }
3233
+ if (text) {
3234
+ text.addEventListener('change', resize);
3235
+ text.addEventListener('cut', delayedResize);
3236
+ text.addEventListener('paste', delayedResize);
3237
+ text.addEventListener('drop', delayedResize);
3238
+ text.addEventListener('keydown', delayedResize);
3239
+ text.focus();
3240
+ text.select();
3241
+ resize();
2981
3242
  }
2982
3243
  }
2983
- // If nothing fits perfectly, use the first position clamped to viewport
2984
- const fallback = this.#clampToViewport(tryOrder[0].call(this, triggerRect, menuRect), menuRect);
2985
- this.menuStyle.set({ left: fallback.left + 'px', top: fallback.top + 'px' });
2986
- }
2987
- ngOnDestroy() {
2988
- this.openAbort?.abort();
2989
- this.openAbort = null;
2990
3244
  }
2991
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ShipPopover, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
2992
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: ShipPopover, isStandalone: true, selector: "sh-popover", inputs: { asMultiLayer: { classPropertyName: "asMultiLayer", publicName: "asMultiLayer", isSignal: true, isRequired: false, transformFunction: null }, disableOpenByClick: { classPropertyName: "disableOpenByClick", publicName: "disableOpenByClick", isSignal: true, isRequired: false, transformFunction: null }, isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { isOpen: "isOpenChange", closed: "closed" }, host: { properties: { "class.multi-layer": "asMultiLayer()" } }, viewQueries: [{ propertyName: "triggerRef", first: true, predicate: ["triggerRef"], descendants: true, isSignal: true }, { propertyName: "popoverRef", first: true, predicate: ["popoverRef"], descendants: true, isSignal: true }, { propertyName: "popoverContentRef", first: true, predicate: ["popoverContentRef"], descendants: true, isSignal: true }], ngImport: i0, template: `
2993
- <div class="trigger" #triggerRef [attr.popovertarget]="id() + 'hello'" (click)="toggleIsOpen($event)">
2994
- <div class="trigger-wrapper">
2995
- <ng-content select="[trigger]" />
2996
- <ng-content select="button" />
2997
- <ng-content select="[shButton]" />
2998
- </div>
2999
- <div class="trigger-anchor" [style.anchor-name]="id()"></div>
3000
- </div>
3245
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ShipFormFieldPopover, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3246
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.5", type: ShipFormFieldPopover, isStandalone: true, selector: "sh-form-field-popover", inputs: { isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { isOpen: "isOpenChange", closed: "closed" }, host: { listeners: { "click": "onClick()" }, properties: { "class": "hostClasses()" } }, ngImport: i0, template: `
3247
+ <ng-content select="label"></ng-content>
3001
3248
 
3002
- @if (isOpen()) {
3003
- <div [attr.id]="id() + 'hello'" popover="manual" #popoverRef class="popover">
3004
- <div class="overlay" (click)="eventClose($event)"></div>
3005
- <div class="popover-content" #popoverContentRef [style.position-anchor]="id()" [style]="menuStyle()">
3006
- <ng-content />
3249
+ <sh-popover
3250
+ [(isOpen)]="isOpen"
3251
+ (closed)="close()"
3252
+ [asSheetOnMobile]="true"
3253
+ [options]="{
3254
+ closeOnButton: false,
3255
+ closeOnEsc: true,
3256
+ }">
3257
+ <div trigger class="input-wrap">
3258
+ <div class="prefix">
3259
+ <ng-content select="[prefix]"></ng-content>
3260
+ <ng-content select="[textPrefix]"></ng-content>
3007
3261
  </div>
3262
+
3263
+ <div class="prefix-space"></div>
3264
+
3265
+ <ng-content select="input"></ng-content>
3266
+
3267
+ <ng-content select="textarea"></ng-content>
3268
+
3269
+ <ng-content select="[textSuffix]"></ng-content>
3270
+ <div class="suffix-space"></div>
3271
+ <ng-content select="[suffix]"></ng-content>
3008
3272
  </div>
3009
- }
3010
- `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3273
+ <ng-content select="[popoverContent]"></ng-content>
3274
+ </sh-popover>
3275
+
3276
+ <div class="helpers">
3277
+ <div class="error-wrap">
3278
+ <ng-content select="[error]"></ng-content>
3279
+ </div>
3280
+
3281
+ <div class="hint">
3282
+ <ng-content select="[hint]"></ng-content>
3283
+ </div>
3284
+ </div>
3285
+ `, isInline: true, dependencies: [{ kind: "component", type: ShipPopover, selector: "sh-popover", inputs: ["asMultiLayer", "asSheetOnMobile", "disableOpenByClick", "isOpen", "options"], outputs: ["isOpenChange", "closed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3011
3286
  }
3012
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ShipPopover, decorators: [{
3287
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ShipFormFieldPopover, decorators: [{
3013
3288
  type: Component,
3014
3289
  args: [{
3015
- selector: 'sh-popover',
3016
- imports: [],
3290
+ selector: 'sh-form-field-popover',
3291
+ imports: [ShipPopover],
3017
3292
  template: `
3018
- <div class="trigger" #triggerRef [attr.popovertarget]="id() + 'hello'" (click)="toggleIsOpen($event)">
3019
- <div class="trigger-wrapper">
3020
- <ng-content select="[trigger]" />
3021
- <ng-content select="button" />
3022
- <ng-content select="[shButton]" />
3023
- </div>
3024
- <div class="trigger-anchor" [style.anchor-name]="id()"></div>
3025
- </div>
3026
-
3027
- @if (isOpen()) {
3028
- <div [attr.id]="id() + 'hello'" popover="manual" #popoverRef class="popover">
3029
- <div class="overlay" (click)="eventClose($event)"></div>
3030
- <div class="popover-content" #popoverContentRef [style.position-anchor]="id()" [style]="menuStyle()">
3031
- <ng-content />
3032
- </div>
3033
- </div>
3034
- }
3035
- `,
3036
- changeDetection: ChangeDetectionStrategy.OnPush,
3037
- host: {
3038
- '[class.multi-layer]': 'asMultiLayer()',
3039
- },
3040
- }]
3041
- }], propDecorators: { asMultiLayer: [{ type: i0.Input, args: [{ isSignal: true, alias: "asMultiLayer", required: false }] }], disableOpenByClick: [{ type: i0.Input, args: [{ isSignal: true, alias: "disableOpenByClick", required: false }] }], isOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "isOpen", required: false }] }, { type: i0.Output, args: ["isOpenChange"] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], triggerRef: [{ type: i0.ViewChild, args: ['triggerRef', { isSignal: true }] }], popoverRef: [{ type: i0.ViewChild, args: ['popoverRef', { isSignal: true }] }], popoverContentRef: [{ type: i0.ViewChild, args: ['popoverContentRef', { isSignal: true }] }] } });
3042
-
3043
- class ShipFormFieldPopover {
3044
- constructor() {
3045
- this.#selfRef = inject(ElementRef);
3046
- this.isOpen = model(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
3047
- this.closed = output();
3048
- }
3049
- #selfRef;
3050
- onClick() {
3051
- if (this.#selfRef.nativeElement.querySelector('input')) {
3052
- this.#selfRef.nativeElement.querySelector('input').focus();
3053
- }
3054
- if (this.#selfRef.nativeElement.querySelector('textarea')) {
3055
- this.#selfRef.nativeElement.querySelector('textarea').focus();
3056
- }
3057
- }
3058
- close() {
3059
- this.closed.emit();
3060
- }
3061
- ngOnInit() {
3062
- const supportFieldSizing = typeof CSS !== 'undefined' && CSS.supports('field-sizing', 'content');
3063
- const text = this.#selfRef.nativeElement.querySelector('textarea');
3064
- if (!supportFieldSizing && text !== null) {
3065
- const text = this.#selfRef.nativeElement.querySelector('textarea');
3066
- function resize() {
3067
- text.style.height = 'auto';
3068
- text.style.height = text.scrollHeight + 'px';
3069
- }
3070
- /* 0-timeout to get the already changed text */
3071
- function delayedResize() {
3072
- setTimeout(resize, 0);
3073
- }
3074
- if (text) {
3075
- text.addEventListener('change', resize);
3076
- text.addEventListener('cut', delayedResize);
3077
- text.addEventListener('paste', delayedResize);
3078
- text.addEventListener('drop', delayedResize);
3079
- text.addEventListener('keydown', delayedResize);
3080
- text.focus();
3081
- text.select();
3082
- resize();
3083
- }
3084
- }
3085
- }
3086
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ShipFormFieldPopover, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3087
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.5", type: ShipFormFieldPopover, isStandalone: true, selector: "sh-form-field-popover", inputs: { isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { isOpen: "isOpenChange", closed: "closed" }, host: { listeners: { "click": "onClick()" } }, ngImport: i0, template: `
3088
3293
  <ng-content select="label"></ng-content>
3089
3294
 
3090
3295
  <sh-popover
3091
3296
  [(isOpen)]="isOpen"
3092
3297
  (closed)="close()"
3298
+ [asSheetOnMobile]="true"
3093
3299
  [options]="{
3094
3300
  closeOnButton: false,
3095
3301
  closeOnEsc: true,
@@ -3118,62 +3324,727 @@ class ShipFormFieldPopover {
3118
3324
  <ng-content select="[error]"></ng-content>
3119
3325
  </div>
3120
3326
 
3121
- <div class="hint">
3122
- <ng-content select="[hint]"></ng-content>
3123
- </div>
3124
- </div>
3125
- `, isInline: true, dependencies: [{ kind: "component", type: ShipPopover, selector: "sh-popover", inputs: ["asMultiLayer", "disableOpenByClick", "isOpen", "options"], outputs: ["isOpenChange", "closed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3327
+ <div class="hint">
3328
+ <ng-content select="[hint]"></ng-content>
3329
+ </div>
3330
+ </div>
3331
+ `,
3332
+ changeDetection: ChangeDetectionStrategy.OnPush,
3333
+ host: {
3334
+ '[class]': 'hostClasses()',
3335
+ },
3336
+ }]
3337
+ }], propDecorators: { isOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "isOpen", required: false }] }, { type: i0.Output, args: ["isOpenChange"] }], closed: [{ type: i0.Output, args: ["closed"] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], onClick: [{
3338
+ type: HostListener,
3339
+ args: ['click']
3340
+ }] } });
3341
+
3342
+ class ShipColorPickerInput {
3343
+ constructor() {
3344
+ this.#document = inject(DOCUMENT);
3345
+ this.renderingType = input('hsl', ...(ngDevMode ? [{ debugName: "renderingType" }] : /* istanbul ignore next */ []));
3346
+ this.format = input('rgb', ...(ngDevMode ? [{ debugName: "format" }] : /* istanbul ignore next */ []));
3347
+ this.color = input(null, ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
3348
+ this.variant = input(null, ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
3349
+ this.size = input(null, ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
3350
+ this.readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : /* istanbul ignore next */ []));
3351
+ this.closed = output();
3352
+ this.isOpen = model(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
3353
+ this.currentClass = classMutationSignal();
3354
+ this.isEyeDropperSupported = typeof window !== 'undefined' && 'EyeDropper' in window;
3355
+ this.showEyeDropper = input(true, ...(ngDevMode ? [{ debugName: "showEyeDropper" }] : /* istanbul ignore next */ []));
3356
+ this.internalHue = signal(0, ...(ngDevMode ? [{ debugName: "internalHue" }] : /* istanbul ignore next */ []));
3357
+ this.internalAlpha = signal(1, ...(ngDevMode ? [{ debugName: "internalAlpha" }] : /* istanbul ignore next */ []));
3358
+ this.internalColorTuple = signal([255, 255, 255, 1], ...(ngDevMode ? [{ debugName: "internalColorTuple" }] : /* istanbul ignore next */ []));
3359
+ this.hasAlpha = computed(() => ['rgba', 'hex8', 'hsla'].includes(this.format()), ...(ngDevMode ? [{ debugName: "hasAlpha" }] : /* istanbul ignore next */ []));
3360
+ this.formattedColorString = computed(() => {
3361
+ const format = this.format();
3362
+ const tuple = this.internalColorTuple();
3363
+ const [r, g, b, aRaw] = tuple;
3364
+ const a = aRaw ?? 1;
3365
+ switch (format) {
3366
+ case 'rgb':
3367
+ return `rgb(${r}, ${g}, ${b})`;
3368
+ case 'rgba':
3369
+ return `rgba(${r}, ${g}, ${b}, ${a})`;
3370
+ case 'hex':
3371
+ return rgbToHex(r, g, b);
3372
+ case 'hex8':
3373
+ return rgbaToHex8(r, g, b, a);
3374
+ case 'hsl':
3375
+ return rgbToHsl(r, g, b).string;
3376
+ case 'hsla': {
3377
+ const hsl = rgbToHsl(r, g, b);
3378
+ return `hsla(${hsl.h}, ${hsl.s}%, ${hsl.l}%, ${a})`;
3379
+ }
3380
+ default:
3381
+ return `rgb(${r}, ${g}, ${b})`;
3382
+ }
3383
+ }, ...(ngDevMode ? [{ debugName: "formattedColorString" }] : /* istanbul ignore next */ []));
3384
+ this.#formatSyncEffect = effect(() => {
3385
+ const str = this.formattedColorString();
3386
+ const input = untracked(() => this.#inputRef());
3387
+ if (input && input.value !== str) {
3388
+ if (this.#document.activeElement !== input) {
3389
+ input.value = str;
3390
+ input.dispatchEvent(new Event('input'));
3391
+ }
3392
+ }
3393
+ }, ...(ngDevMode ? [{ debugName: "#formatSyncEffect" }] : /* istanbul ignore next */ []));
3394
+ this.pureHueColor = computed(() => {
3395
+ return hslToRgbExact(this.internalHue(), 100, 50);
3396
+ }, ...(ngDevMode ? [{ debugName: "pureHueColor" }] : /* istanbul ignore next */ []));
3397
+ this.#inputRef = signal(null, ...(ngDevMode ? [{ debugName: "#inputRef" }] : /* istanbul ignore next */ []));
3398
+ this.#inputObserver = contentProjectionSignal('#input-wrap input');
3399
+ this.#inputRefEffect = effect(() => {
3400
+ const inputs = this.#inputObserver();
3401
+ if (!inputs.length)
3402
+ return;
3403
+ const input = inputs[0];
3404
+ if (!input)
3405
+ return;
3406
+ this.#createCustomInputEventListener(input);
3407
+ input.addEventListener('inputValueChanged', (event) => {
3408
+ this.parseAndSetColor(event.detail.value);
3409
+ });
3410
+ input.addEventListener('input', (event) => {
3411
+ const target = event.target;
3412
+ this.parseAndSetColor(target.value);
3413
+ });
3414
+ input.addEventListener('blur', () => {
3415
+ const str = untracked(() => this.formattedColorString());
3416
+ if (input.value !== str) {
3417
+ input.value = str;
3418
+ input.dispatchEvent(new Event('input'));
3419
+ }
3420
+ });
3421
+ input.addEventListener('focus', () => {
3422
+ this.isOpen.set(true);
3423
+ });
3424
+ this.#inputRef.set(input);
3425
+ input.autocomplete = 'off';
3426
+ if (typeof input.value === 'string' && input.value) {
3427
+ this.parseAndSetColor(input.value);
3428
+ }
3429
+ }, ...(ngDevMode ? [{ debugName: "#inputRefEffect" }] : /* istanbul ignore next */ []));
3430
+ }
3431
+ #document;
3432
+ #formatSyncEffect;
3433
+ #inputRef;
3434
+ #inputObserver;
3435
+ onMainColorChange(colorObj) {
3436
+ if (colorObj.hue !== undefined) {
3437
+ if (this.renderingType() === 'hsl' && colorObj.saturation > 0) {
3438
+ this.internalHue.set(colorObj.hue);
3439
+ }
3440
+ }
3441
+ }
3442
+ onHuePickerChange(colorObj) {
3443
+ if (colorObj.hue !== undefined && colorObj.hue !== this.internalHue()) {
3444
+ this.internalHue.set(colorObj.hue);
3445
+ }
3446
+ }
3447
+ async openEyeDropper(event) {
3448
+ event.preventDefault();
3449
+ event.stopPropagation();
3450
+ try {
3451
+ // @ts-ignore
3452
+ const eyeDropper = new window.EyeDropper();
3453
+ const result = await eyeDropper.open();
3454
+ if (result && result.sRGBHex) {
3455
+ this.parseAndSetColor(result.sRGBHex);
3456
+ // Force the text field to immediately display the new output string according to the active format
3457
+ const input = untracked(() => this.#inputRef());
3458
+ if (input) {
3459
+ const str = this.formattedColorString();
3460
+ if (input.value !== str) {
3461
+ input.value = str;
3462
+ input.dispatchEvent(new Event('input'));
3463
+ }
3464
+ }
3465
+ }
3466
+ }
3467
+ catch (e) {
3468
+ // User cancelled
3469
+ }
3470
+ }
3471
+ close() {
3472
+ this.closed.emit(this.formattedColorString());
3473
+ }
3474
+ #inputRefEffect;
3475
+ parseAndSetColor(colorStr) {
3476
+ if (!colorStr)
3477
+ return;
3478
+ if (colorStr === untracked(() => this.formattedColorString()))
3479
+ return;
3480
+ const div = this.#document.createElement('div');
3481
+ div.style.color = colorStr;
3482
+ if (div.style.color === '')
3483
+ return; // Not a valid color format
3484
+ this.#document.body.appendChild(div);
3485
+ const computedColor = getComputedStyle(div).color;
3486
+ this.#document.body.removeChild(div);
3487
+ const rgbaMatch = computedColor.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
3488
+ if (rgbaMatch) {
3489
+ const r = parseInt(rgbaMatch[1], 10);
3490
+ const g = parseInt(rgbaMatch[2], 10);
3491
+ const b = parseInt(rgbaMatch[3], 10);
3492
+ const a = rgbaMatch[4] ? parseFloat(rgbaMatch[4]) : 1;
3493
+ const current = untracked(() => this.internalColorTuple());
3494
+ if (current[0] !== r || current[1] !== g || current[2] !== b || (current[3] ?? 1) !== a) {
3495
+ this.internalColorTuple.set([r, g, b, a]);
3496
+ this.internalAlpha.set(a);
3497
+ // Also update hue so the hue slider matches the typed color!
3498
+ const max = Math.max(r, g, b) / 255;
3499
+ const min = Math.min(r, g, b) / 255;
3500
+ if (max !== min) {
3501
+ const d = max - min;
3502
+ let h = 0;
3503
+ switch (max) {
3504
+ case r / 255:
3505
+ h = (g / 255 - b / 255) / d + (g / 255 < b / 255 ? 6 : 0);
3506
+ break;
3507
+ case g / 255:
3508
+ h = (b / 255 - r / 255) / d + 2;
3509
+ break;
3510
+ case b / 255:
3511
+ h = (r / 255 - g / 255) / d + 4;
3512
+ break;
3513
+ }
3514
+ h /= 6;
3515
+ this.internalHue.set(Math.floor(h * 360));
3516
+ }
3517
+ }
3518
+ }
3519
+ }
3520
+ hslToRgbExact(h, s, l) {
3521
+ s /= 100;
3522
+ l /= 100;
3523
+ const k = (n) => (n + h / 30) % 12;
3524
+ const a = s * Math.min(l, 1 - l);
3525
+ const f = (n) => l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)));
3526
+ return [Math.round(255 * f(0)), Math.round(255 * f(8)), Math.round(255 * f(4))];
3527
+ }
3528
+ rgbToHex(r, g, b) {
3529
+ return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
3530
+ }
3531
+ rgbaToHex8(r, g, b, a) {
3532
+ const alphaHex = Math.round(a * 255)
3533
+ .toString(16)
3534
+ .padStart(2, '0');
3535
+ return this.rgbToHex(r, g, b) + alphaHex;
3536
+ }
3537
+ rgbToHsl(r, g, b) {
3538
+ r /= 255;
3539
+ g /= 255;
3540
+ b /= 255;
3541
+ const max = Math.max(r, g, b), min = Math.min(r, g, b);
3542
+ let h = 0, s = 0, l = (max + min) / 2;
3543
+ if (max === min) {
3544
+ h = s = 0;
3545
+ }
3546
+ else {
3547
+ const d = max - min;
3548
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
3549
+ switch (max) {
3550
+ case r:
3551
+ h = (g - b) / d + (g < b ? 6 : 0);
3552
+ break;
3553
+ case g:
3554
+ h = (b - r) / d + 2;
3555
+ break;
3556
+ case b:
3557
+ h = (r - g) / d + 4;
3558
+ break;
3559
+ }
3560
+ h /= 6;
3561
+ }
3562
+ const hDeg = Math.floor(h * 360);
3563
+ const sPct = Math.round(s * 100);
3564
+ const lPct = Math.round(l * 100);
3565
+ return {
3566
+ h: hDeg,
3567
+ s: sPct,
3568
+ l: lPct,
3569
+ string: `hsl(${hDeg}, ${sPct}%, ${lPct}%)`,
3570
+ };
3571
+ }
3572
+ #createCustomInputEventListener(input) {
3573
+ Object.defineProperty(input, 'value', {
3574
+ configurable: true,
3575
+ get() {
3576
+ const descriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');
3577
+ return descriptor.get.call(this);
3578
+ },
3579
+ set(newVal) {
3580
+ const descriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');
3581
+ descriptor.set.call(this, newVal);
3582
+ const inputEvent = new CustomEvent('inputValueChanged', {
3583
+ bubbles: true,
3584
+ cancelable: true,
3585
+ detail: {
3586
+ value: newVal,
3587
+ },
3588
+ });
3589
+ this.dispatchEvent(inputEvent);
3590
+ return newVal;
3591
+ },
3592
+ });
3593
+ return input;
3594
+ }
3595
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ShipColorPickerInput, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3596
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: ShipColorPickerInput, isStandalone: true, selector: "sh-color-picker-input", inputs: { renderingType: { classPropertyName: "renderingType", publicName: "renderingType", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, isOpen: { classPropertyName: "isOpen", publicName: "isOpen", isSignal: true, isRequired: false, transformFunction: null }, showEyeDropper: { classPropertyName: "showEyeDropper", publicName: "showEyeDropper", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed", isOpen: "isOpenChange" }, ngImport: i0, template: `
3597
+ <sh-form-field-popover
3598
+ (closed)="close()"
3599
+ [(isOpen)]="isOpen"
3600
+ [class]="currentClass()"
3601
+ [variant]="variant()"
3602
+ [size]="size()"
3603
+ [color]="color()"
3604
+ [readonly]="readonly()"
3605
+ >
3606
+ <ng-content select="label" ngProjectAs="label" />
3607
+
3608
+ <ng-content select="[prefix]" ngProjectAs="[prefix]" />
3609
+ <ng-content select="[textPrefix]" ngProjectAs="[textPrefix]" />
3610
+
3611
+ <div id="input-wrap" class="input-container" ngProjectAs="input">
3612
+ <ng-content select="input" />
3613
+
3614
+ <div class="color-indicator" [style.--indicator-color]="formattedColorString()"></div>
3615
+
3616
+ @if (isEyeDropperSupported && showEyeDropper()) {
3617
+ <button size="xsmall" tabindex="-1" variant="outlined" shButton (click)="openEyeDropper($event)">
3618
+ <sh-icon>eyedropper</sh-icon>
3619
+ </button>
3620
+ }
3621
+ </div>
3622
+
3623
+ <ng-content select="[textSuffix]" ngProjectAs="[textSuffix]" />
3624
+ <ng-content select="[suffix]" ngProjectAs="[suffix]" />
3625
+
3626
+ <div popoverContent>
3627
+ @if (this.isOpen()) {
3628
+ <sh-color-picker
3629
+ [renderingType]="renderingType()"
3630
+ [direction]="'horizontal'"
3631
+ [(selectedColor)]="internalColorTuple"
3632
+ [hue]="internalHue()"
3633
+ [(alpha)]="internalAlpha"
3634
+ (currentColor)="onMainColorChange($event)" />
3635
+
3636
+ @if (renderingType() !== 'hsl') {
3637
+ <sh-color-picker
3638
+ renderingType="hue"
3639
+ [direction]="'horizontal'"
3640
+ [hue]="internalHue()"
3641
+ [selectedColor]="pureHueColor()"
3642
+ (currentColor)="onHuePickerChange($event)" />
3643
+ }
3644
+
3645
+ @if (hasAlpha()) {
3646
+ <sh-color-picker
3647
+ renderingType="alpha"
3648
+ [direction]="'horizontal'"
3649
+ [(alpha)]="internalAlpha"
3650
+ [(selectedColor)]="internalColorTuple" />
3651
+ }
3652
+ }
3653
+ </div>
3654
+ </sh-form-field-popover>
3655
+ `, isInline: true, dependencies: [{ kind: "component", type: ShipFormFieldPopover, selector: "sh-form-field-popover", inputs: ["isOpen", "color", "variant", "size", "readonly"], outputs: ["isOpenChange", "closed"] }, { kind: "component", type: ShipColorPicker, selector: "sh-color-picker", inputs: ["showDarkColors", "renderingType", "gridSize", "hue", "direction", "selectedColor", "alpha"], outputs: ["hueChange", "selectedColorChange", "alphaChange", "currentColor"] }, { kind: "component", type: ShipIcon, selector: "sh-icon", inputs: ["color", "size"] }, { kind: "component", type: ShipButton$1, selector: "[shButton]", inputs: ["color", "variant", "size", "readonly"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3656
+ }
3657
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ShipColorPickerInput, decorators: [{
3658
+ type: Component,
3659
+ args: [{
3660
+ selector: 'sh-color-picker-input',
3661
+ imports: [ShipFormFieldPopover, ShipColorPicker, ShipIcon, ShipButton$1],
3662
+ template: `
3663
+ <sh-form-field-popover
3664
+ (closed)="close()"
3665
+ [(isOpen)]="isOpen"
3666
+ [class]="currentClass()"
3667
+ [variant]="variant()"
3668
+ [size]="size()"
3669
+ [color]="color()"
3670
+ [readonly]="readonly()"
3671
+ >
3672
+ <ng-content select="label" ngProjectAs="label" />
3673
+
3674
+ <ng-content select="[prefix]" ngProjectAs="[prefix]" />
3675
+ <ng-content select="[textPrefix]" ngProjectAs="[textPrefix]" />
3676
+
3677
+ <div id="input-wrap" class="input-container" ngProjectAs="input">
3678
+ <ng-content select="input" />
3679
+
3680
+ <div class="color-indicator" [style.--indicator-color]="formattedColorString()"></div>
3681
+
3682
+ @if (isEyeDropperSupported && showEyeDropper()) {
3683
+ <button size="xsmall" tabindex="-1" variant="outlined" shButton (click)="openEyeDropper($event)">
3684
+ <sh-icon>eyedropper</sh-icon>
3685
+ </button>
3686
+ }
3687
+ </div>
3688
+
3689
+ <ng-content select="[textSuffix]" ngProjectAs="[textSuffix]" />
3690
+ <ng-content select="[suffix]" ngProjectAs="[suffix]" />
3691
+
3692
+ <div popoverContent>
3693
+ @if (this.isOpen()) {
3694
+ <sh-color-picker
3695
+ [renderingType]="renderingType()"
3696
+ [direction]="'horizontal'"
3697
+ [(selectedColor)]="internalColorTuple"
3698
+ [hue]="internalHue()"
3699
+ [(alpha)]="internalAlpha"
3700
+ (currentColor)="onMainColorChange($event)" />
3701
+
3702
+ @if (renderingType() !== 'hsl') {
3703
+ <sh-color-picker
3704
+ renderingType="hue"
3705
+ [direction]="'horizontal'"
3706
+ [hue]="internalHue()"
3707
+ [selectedColor]="pureHueColor()"
3708
+ (currentColor)="onHuePickerChange($event)" />
3709
+ }
3710
+
3711
+ @if (hasAlpha()) {
3712
+ <sh-color-picker
3713
+ renderingType="alpha"
3714
+ [direction]="'horizontal'"
3715
+ [(alpha)]="internalAlpha"
3716
+ [(selectedColor)]="internalColorTuple" />
3717
+ }
3718
+ }
3719
+ </div>
3720
+ </sh-form-field-popover>
3721
+ `,
3722
+ changeDetection: ChangeDetectionStrategy.OnPush,
3723
+ }]
3724
+ }], propDecorators: { renderingType: [{ type: i0.Input, args: [{ isSignal: true, alias: "renderingType", required: false }] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], closed: [{ type: i0.Output, args: ["closed"] }], isOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "isOpen", required: false }] }, { type: i0.Output, args: ["isOpenChange"] }], showEyeDropper: [{ type: i0.Input, args: [{ isSignal: true, alias: "showEyeDropper", required: false }] }] } });
3725
+
3726
+ class ShipDatepicker {
3727
+ constructor() {
3728
+ this.#INIT_DATE = new Date(new Date().setHours(0, 0, 0, 0));
3729
+ this.date = model(null, ...(ngDevMode ? [{ debugName: "date" }] : /* istanbul ignore next */ []));
3730
+ this.endDate = model(null, ...(ngDevMode ? [{ debugName: "endDate" }] : /* istanbul ignore next */ []));
3731
+ this.asRange = input(false, ...(ngDevMode ? [{ debugName: "asRange" }] : /* istanbul ignore next */ []));
3732
+ this.monthsToShow = input(1, ...(ngDevMode ? [{ debugName: "monthsToShow" }] : /* istanbul ignore next */ []));
3733
+ this.disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
3734
+ this.startOfWeek = input(1, ...(ngDevMode ? [{ debugName: "startOfWeek" }] : /* istanbul ignore next */ []));
3735
+ this.weekdayLabels = input(['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'], ...(ngDevMode ? [{ debugName: "weekdayLabels" }] : /* istanbul ignore next */ []));
3736
+ this.daysRef = viewChild('daysRef', ...(ngDevMode ? [{ debugName: "daysRef" }] : /* istanbul ignore next */ []));
3737
+ this.currentDate = signal(this.date() ?? this.#INIT_DATE, ...(ngDevMode ? [{ debugName: "currentDate" }] : /* istanbul ignore next */ []));
3738
+ this.monthOffsets = computed(() => {
3739
+ return Array.from({ length: this.monthsToShow() }, (_, i) => i);
3740
+ }, ...(ngDevMode ? [{ debugName: "monthOffsets" }] : /* istanbul ignore next */ []));
3741
+ this.selectedDateStylePosition = signal(null, ...(ngDevMode ? [{ debugName: "selectedDateStylePosition" }] : /* istanbul ignore next */ []));
3742
+ this.weekdays = computed(() => {
3743
+ const startOfWeek = this.startOfWeek();
3744
+ const weekdayLabels = this.weekdayLabels();
3745
+ return weekdayLabels.slice(startOfWeek).concat(weekdayLabels.slice(0, startOfWeek));
3746
+ }, ...(ngDevMode ? [{ debugName: "weekdays" }] : /* istanbul ignore next */ []));
3747
+ this.currentClasses = classMutationSignal();
3748
+ this.someEffect = effect(() => {
3749
+ const _ = this.currentClasses();
3750
+ this.#findSelectedAndCalc();
3751
+ }, ...(ngDevMode ? [{ debugName: "someEffect" }] : /* istanbul ignore next */ []));
3752
+ this.#newDateEffect = effect(() => {
3753
+ if (this.monthsToShow() > 1)
3754
+ return;
3755
+ this.#setDateAsCurrent();
3756
+ }, ...(ngDevMode ? [{ debugName: "#newDateEffect" }] : /* istanbul ignore next */ []));
3757
+ }
3758
+ #INIT_DATE;
3759
+ getLastVisibleMonth() {
3760
+ const lastMonthOffset = this.monthsToShow() - 1;
3761
+ return this.getOffsetDate(lastMonthOffset);
3762
+ }
3763
+ getOffsetDate(monthOffset) {
3764
+ const date = new Date(this.currentDate());
3765
+ date.setMonth(date.getMonth() + monthOffset);
3766
+ return date;
3767
+ }
3768
+ getMonthDates(monthOffset) {
3769
+ const offsetDate = this.getOffsetDate(monthOffset);
3770
+ return this.#generateMonthDates(offsetDate, this.startOfWeek());
3771
+ }
3772
+ #newDateEffect;
3773
+ ngOnInit() {
3774
+ if (this.monthsToShow() === 1)
3775
+ return;
3776
+ this.#setDateAsCurrent();
3777
+ }
3778
+ #setDateAsCurrent() {
3779
+ const newDate = this.date();
3780
+ if (newDate)
3781
+ this.currentDate.set(newDate);
3782
+ this.#findSelectedAndCalc();
3783
+ }
3784
+ #findSelectedAndCalc() {
3785
+ setTimeout(() => {
3786
+ const selectedElement = this.daysRef()?.nativeElement.querySelector('.sel');
3787
+ if (!selectedElement) {
3788
+ return this.selectedDateStylePosition.update((x) => (x ? { ...x, opacity: '0' } : null));
3789
+ }
3790
+ this.setSelectedDateStylePosition(selectedElement);
3791
+ });
3792
+ }
3793
+ #generateMonthDates(date, startOfWeek) {
3794
+ const year = date.getFullYear();
3795
+ const month = date.getMonth();
3796
+ const firstDay = new Date(year, month, 1).getDay();
3797
+ const daysInMonth = new Date(year, month + 1, 0).getDate();
3798
+ const dates = [];
3799
+ let offset = firstDay - startOfWeek;
3800
+ if (offset < 0) {
3801
+ offset += 7;
3802
+ }
3803
+ const lastDayOfPrevMonth = new Date(year, month, 0).getDate();
3804
+ for (let i = offset - 1; i >= 0; i--) {
3805
+ dates.push(new Date(year, month - 1, lastDayOfPrevMonth - i, 0, 0, 0, 0));
3806
+ }
3807
+ for (let i = 1; i <= daysInMonth; i++) {
3808
+ dates.push(new Date(year, month, i, 0, 0, 0, 0));
3809
+ }
3810
+ let nextMonthDay = 1;
3811
+ while (dates.length % 7 !== 0) {
3812
+ dates.push(new Date(year, month + 1, nextMonthDay++, 0, 0, 0, 0));
3813
+ }
3814
+ return dates;
3815
+ }
3816
+ nextMonth() {
3817
+ this.currentDate.update((currentDate) => {
3818
+ const newDate = new Date(currentDate);
3819
+ newDate.setMonth(currentDate.getMonth() + 1);
3820
+ return newDate;
3821
+ });
3822
+ this.#findSelectedAndCalc();
3823
+ }
3824
+ previousMonth() {
3825
+ this.currentDate.update((currentDate) => {
3826
+ const newDate = new Date(currentDate);
3827
+ newDate.setMonth(currentDate.getMonth() - 1);
3828
+ return newDate;
3829
+ });
3830
+ this.#findSelectedAndCalc();
3831
+ }
3832
+ setDate(newDate, selectedElement) {
3833
+ const createDateWithExistingTime = (newDate, existingDate) => {
3834
+ const hours = existingDate?.getHours() ?? 0;
3835
+ const minutes = existingDate?.getMinutes() ?? 0;
3836
+ const seconds = existingDate?.getSeconds() ?? 0;
3837
+ const milliseconds = existingDate?.getMilliseconds() ?? 0;
3838
+ return new Date(newDate.getFullYear(), newDate.getMonth(), newDate.getDate(), hours, minutes, seconds, milliseconds);
3839
+ };
3840
+ if (!this.asRange()) {
3841
+ this.date.set(createDateWithExistingTime(newDate, this.date()));
3842
+ this.endDate.set(null);
3843
+ }
3844
+ else {
3845
+ const startDate = this.date();
3846
+ const endDate = this.endDate();
3847
+ const utcDate = createDateWithExistingTime(newDate, null);
3848
+ if (!startDate) {
3849
+ this.date.set(utcDate);
3850
+ }
3851
+ else if (!endDate) {
3852
+ if (utcDate < startDate) {
3853
+ this.date.set(utcDate);
3854
+ this.endDate.set(null);
3855
+ }
3856
+ else {
3857
+ this.endDate.set(utcDate);
3858
+ }
3859
+ }
3860
+ else {
3861
+ this.date.set(utcDate);
3862
+ this.endDate.set(null);
3863
+ }
3864
+ }
3865
+ if (this.asRange())
3866
+ return;
3867
+ this.setSelectedDateStylePosition(selectedElement);
3868
+ }
3869
+ isDateSelected(date) {
3870
+ const startDate = this.date();
3871
+ const endDate = this.endDate();
3872
+ if (startDate === null)
3873
+ return null;
3874
+ const startOfDay = (date) => {
3875
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0);
3876
+ };
3877
+ const endOfDay = (date) => {
3878
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59, 999);
3879
+ };
3880
+ const currentDate = startOfDay(date);
3881
+ const rangeStart = startOfDay(startDate);
3882
+ const rangeEnd = endDate ? endOfDay(endDate) : null;
3883
+ let classes = [];
3884
+ if (this.asRange()) {
3885
+ if (rangeEnd === null) {
3886
+ if (currentDate.getTime() === rangeStart.getTime()) {
3887
+ classes.push('sel first last');
3888
+ }
3889
+ }
3890
+ else {
3891
+ if (currentDate.getTime() === rangeStart.getTime()) {
3892
+ classes.push('first');
3893
+ }
3894
+ if (currentDate.getTime() === startOfDay(rangeEnd).getTime()) {
3895
+ classes.push('last');
3896
+ }
3897
+ if (currentDate >= rangeStart && currentDate <= rangeEnd) {
3898
+ classes.push('sel');
3899
+ const dayOfWeek = currentDate.getDay();
3900
+ const startOfWeek = this.startOfWeek();
3901
+ if (dayOfWeek === startOfWeek) {
3902
+ classes.push('week-start');
3903
+ }
3904
+ const endOfWeek = (startOfWeek + 6) % 7;
3905
+ if (dayOfWeek === endOfWeek) {
3906
+ classes.push('week-end');
3907
+ }
3908
+ }
3909
+ const nextDate = new Date(currentDate);
3910
+ nextDate.setDate(currentDate.getDate() + 1);
3911
+ const prevDate = new Date(currentDate);
3912
+ prevDate.setDate(currentDate.getDate() - 1);
3913
+ const isFirstOfMonth = currentDate.getDate() === 1;
3914
+ const isLastOfMonth = nextDate.getMonth() !== currentDate.getMonth();
3915
+ if (isFirstOfMonth) {
3916
+ classes.push('month-start');
3917
+ }
3918
+ if (isLastOfMonth) {
3919
+ classes.push('month-end');
3920
+ }
3921
+ }
3922
+ }
3923
+ else {
3924
+ if (currentDate.getTime() === rangeStart.getTime()) {
3925
+ classes.push('sel');
3926
+ }
3927
+ }
3928
+ return classes.join(' ') || null;
3929
+ }
3930
+ setSelectedDateStylePosition(selectedElement) {
3931
+ this.selectedDateStylePosition.set({
3932
+ transform: `translate(${selectedElement.offsetLeft}px, ${selectedElement.offsetTop}px)`,
3933
+ opacity: '1',
3934
+ });
3935
+ }
3936
+ getMonthName(date) {
3937
+ return date.toLocaleString('default', { month: 'long' });
3938
+ }
3939
+ getFullYear(date) {
3940
+ return date.getFullYear();
3941
+ }
3942
+ // Rest of the component methods remain the same, but update isCurrentMonth:
3943
+ isCurrentMonth(date, monthOffset) {
3944
+ const offsetDate = this.getOffsetDate(monthOffset);
3945
+ return date.getMonth() === offsetDate.getMonth();
3946
+ }
3947
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ShipDatepicker, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3948
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: ShipDatepicker, isStandalone: true, selector: "sh-datepicker", inputs: { date: { classPropertyName: "date", publicName: "date", isSignal: true, isRequired: false, transformFunction: null }, endDate: { classPropertyName: "endDate", publicName: "endDate", isSignal: true, isRequired: false, transformFunction: null }, asRange: { classPropertyName: "asRange", publicName: "asRange", isSignal: true, isRequired: false, transformFunction: null }, monthsToShow: { classPropertyName: "monthsToShow", publicName: "monthsToShow", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, startOfWeek: { classPropertyName: "startOfWeek", publicName: "startOfWeek", isSignal: true, isRequired: false, transformFunction: null }, weekdayLabels: { classPropertyName: "weekdayLabels", publicName: "weekdayLabels", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { date: "dateChange", endDate: "endDateChange" }, host: { properties: { "class.as-range": "asRange()", "class": "\"columns-\" + monthsToShow()", "class.disabled": "disabled()" } }, viewQueries: [{ propertyName: "daysRef", first: true, predicate: ["daysRef"], descendants: true, isSignal: true }], ngImport: i0, template: `
3949
+ <header>
3950
+ <button (click)="previousMonth()"><sh-icon>caret-left</sh-icon></button>
3951
+ <div class="title">
3952
+ {{ getMonthName(currentDate()!) }}
3953
+ @if (monthsToShow() > 1) {
3954
+ - {{ getMonthName(getLastVisibleMonth()) }}
3955
+ }
3956
+ {{ getFullYear(currentDate()!) }}
3957
+ </div>
3958
+ <button (click)="nextMonth()"><sh-icon>caret-right</sh-icon></button>
3959
+ </header>
3960
+
3961
+ <section class="months-container">
3962
+ @for (monthOffset of monthOffsets(); track monthOffset) {
3963
+ <div class="month">
3964
+ <nav class="weekdays">
3965
+ @for (day of weekdays(); track $index) {
3966
+ <div>{{ day }}</div>
3967
+ }
3968
+ </nav>
3969
+
3970
+ <div class="days" #daysRef>
3971
+ @for (calDate of getMonthDates(monthOffset); track $index) {
3972
+ <div
3973
+ #elementRef
3974
+ [class.out-of-scope]="!isCurrentMonth(calDate, monthOffset)"
3975
+ [class]="isDateSelected(calDate)"
3976
+ (click)="setDate(calDate, elementRef)">
3977
+ {{ calDate.getDate() }}
3978
+ </div>
3979
+ }
3980
+
3981
+ @if (!asRange()) {
3982
+ <article class="days">
3983
+ <div class="sel-el" [style]="selectedDateStylePosition()"></div>
3984
+ </article>
3985
+ }
3986
+ </div>
3987
+ </div>
3988
+ }
3989
+ </section>
3990
+ `, isInline: true, dependencies: [{ kind: "component", type: ShipIcon, selector: "sh-icon", inputs: ["color", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3126
3991
  }
3127
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ShipFormFieldPopover, decorators: [{
3992
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ShipDatepicker, decorators: [{
3128
3993
  type: Component,
3129
3994
  args: [{
3130
- selector: 'sh-form-field-popover',
3131
- imports: [ShipPopover],
3995
+ selector: 'sh-datepicker',
3996
+ imports: [ShipIcon],
3132
3997
  template: `
3133
- <ng-content select="label"></ng-content>
3134
-
3135
- <sh-popover
3136
- [(isOpen)]="isOpen"
3137
- (closed)="close()"
3138
- [options]="{
3139
- closeOnButton: false,
3140
- closeOnEsc: true,
3141
- }">
3142
- <div trigger class="input-wrap">
3143
- <div class="prefix">
3144
- <ng-content select="[prefix]"></ng-content>
3145
- <ng-content select="[textPrefix]"></ng-content>
3146
- </div>
3147
-
3148
- <div class="prefix-space"></div>
3149
-
3150
- <ng-content select="input"></ng-content>
3151
-
3152
- <ng-content select="textarea"></ng-content>
3153
-
3154
- <ng-content select="[textSuffix]"></ng-content>
3155
- <div class="suffix-space"></div>
3156
- <ng-content select="[suffix]"></ng-content>
3998
+ <header>
3999
+ <button (click)="previousMonth()"><sh-icon>caret-left</sh-icon></button>
4000
+ <div class="title">
4001
+ {{ getMonthName(currentDate()!) }}
4002
+ @if (monthsToShow() > 1) {
4003
+ - {{ getMonthName(getLastVisibleMonth()) }}
4004
+ }
4005
+ {{ getFullYear(currentDate()!) }}
3157
4006
  </div>
3158
- <ng-content select="[popoverContent]"></ng-content>
3159
- </sh-popover>
4007
+ <button (click)="nextMonth()"><sh-icon>caret-right</sh-icon></button>
4008
+ </header>
3160
4009
 
3161
- <div class="helpers">
3162
- <div class="error-wrap">
3163
- <ng-content select="[error]"></ng-content>
3164
- </div>
4010
+ <section class="months-container">
4011
+ @for (monthOffset of monthOffsets(); track monthOffset) {
4012
+ <div class="month">
4013
+ <nav class="weekdays">
4014
+ @for (day of weekdays(); track $index) {
4015
+ <div>{{ day }}</div>
4016
+ }
4017
+ </nav>
3165
4018
 
3166
- <div class="hint">
3167
- <ng-content select="[hint]"></ng-content>
3168
- </div>
3169
- </div>
4019
+ <div class="days" #daysRef>
4020
+ @for (calDate of getMonthDates(monthOffset); track $index) {
4021
+ <div
4022
+ #elementRef
4023
+ [class.out-of-scope]="!isCurrentMonth(calDate, monthOffset)"
4024
+ [class]="isDateSelected(calDate)"
4025
+ (click)="setDate(calDate, elementRef)">
4026
+ {{ calDate.getDate() }}
4027
+ </div>
4028
+ }
4029
+
4030
+ @if (!asRange()) {
4031
+ <article class="days">
4032
+ <div class="sel-el" [style]="selectedDateStylePosition()"></div>
4033
+ </article>
4034
+ }
4035
+ </div>
4036
+ </div>
4037
+ }
4038
+ </section>
3170
4039
  `,
3171
4040
  changeDetection: ChangeDetectionStrategy.OnPush,
4041
+ host: {
4042
+ '[class.as-range]': 'asRange()',
4043
+ '[class]': '"columns-" + monthsToShow()',
4044
+ '[class.disabled]': 'disabled()',
4045
+ },
3172
4046
  }]
3173
- }], propDecorators: { isOpen: [{ type: i0.Input, args: [{ isSignal: true, alias: "isOpen", required: false }] }, { type: i0.Output, args: ["isOpenChange"] }], closed: [{ type: i0.Output, args: ["closed"] }], onClick: [{
3174
- type: HostListener,
3175
- args: ['click']
3176
- }] } });
4047
+ }], propDecorators: { date: [{ type: i0.Input, args: [{ isSignal: true, alias: "date", required: false }] }, { type: i0.Output, args: ["dateChange"] }], endDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "endDate", required: false }] }, { type: i0.Output, args: ["endDateChange"] }], asRange: [{ type: i0.Input, args: [{ isSignal: true, alias: "asRange", required: false }] }], monthsToShow: [{ type: i0.Input, args: [{ isSignal: true, alias: "monthsToShow", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], startOfWeek: [{ type: i0.Input, args: [{ isSignal: true, alias: "startOfWeek", required: false }] }], weekdayLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "weekdayLabels", required: false }] }], daysRef: [{ type: i0.ViewChild, args: ['daysRef', { isSignal: true }] }] } });
3177
4048
 
3178
4049
  class ShipDatepickerInput {
3179
4050
  constructor() {
@@ -3285,7 +4156,7 @@ class ShipDatepickerInput {
3285
4156
  </sh-form-field-popover>
3286
4157
 
3287
4158
  <ng-template #defaultIndicator></ng-template>
3288
- `, isInline: true, dependencies: [{ kind: "component", type: ShipDatepicker, selector: "sh-datepicker", inputs: ["date", "endDate", "asRange", "monthsToShow", "disabled", "startOfWeek", "weekdayLabels"], outputs: ["dateChange", "endDateChange"] }, { kind: "component", type: ShipFormFieldPopover, selector: "sh-form-field-popover", inputs: ["isOpen"], outputs: ["isOpenChange", "closed"] }, { kind: "component", type: ShipIcon, selector: "sh-icon", inputs: ["color", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4159
+ `, isInline: true, dependencies: [{ kind: "component", type: ShipDatepicker, selector: "sh-datepicker", inputs: ["date", "endDate", "asRange", "monthsToShow", "disabled", "startOfWeek", "weekdayLabels"], outputs: ["dateChange", "endDateChange"] }, { kind: "component", type: ShipFormFieldPopover, selector: "sh-form-field-popover", inputs: ["isOpen", "color", "variant", "size", "readonly"], outputs: ["isOpenChange", "closed"] }, { kind: "component", type: ShipIcon, selector: "sh-icon", inputs: ["color", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3289
4160
  }
3290
4161
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ShipDatepickerInput, decorators: [{
3291
4162
  type: Component,
@@ -3457,7 +4328,7 @@ class ShipDaterangeInput {
3457
4328
  }
3458
4329
  </div>
3459
4330
  </sh-form-field-popover>
3460
- `, isInline: true, dependencies: [{ kind: "component", type: ShipDatepicker, selector: "sh-datepicker", inputs: ["date", "endDate", "asRange", "monthsToShow", "disabled", "startOfWeek", "weekdayLabels"], outputs: ["dateChange", "endDateChange"] }, { kind: "component", type: ShipFormFieldPopover, selector: "sh-form-field-popover", inputs: ["isOpen"], outputs: ["isOpenChange", "closed"] }, { kind: "component", type: ShipIcon, selector: "sh-icon", inputs: ["color", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4331
+ `, isInline: true, dependencies: [{ kind: "component", type: ShipDatepicker, selector: "sh-datepicker", inputs: ["date", "endDate", "asRange", "monthsToShow", "disabled", "startOfWeek", "weekdayLabels"], outputs: ["dateChange", "endDateChange"] }, { kind: "component", type: ShipFormFieldPopover, selector: "sh-form-field-popover", inputs: ["isOpen", "color", "variant", "size", "readonly"], outputs: ["isOpenChange", "closed"] }, { kind: "component", type: ShipIcon, selector: "sh-icon", inputs: ["color", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3461
4332
  }
3462
4333
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ShipDaterangeInput, decorators: [{
3463
4334
  type: Component,
@@ -3783,124 +4654,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
3783
4654
  }]
3784
4655
  }], propDecorators: { inputRef: [{ type: i0.ViewChild, args: ['input', { isSignal: true }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }, { type: i0.Output, args: ["placeholderChange"] }], overlayText: [{ type: i0.Input, args: [{ isSignal: true, alias: "overlayText", required: false }] }], files: [{ type: i0.Input, args: [{ isSignal: true, alias: "files", required: false }] }, { type: i0.Output, args: ["filesChange"] }] } });
3785
4656
 
3786
- class ShipAccordion {
3787
- constructor() {
3788
- this.selfElement = inject((ElementRef)).nativeElement;
3789
- this.name = input(`sh-accordion-${Math.random().toString(36).substring(2, 9)}`, ...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
3790
- this.value = model(null, ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
3791
- this.allowMultiple = input(false, ...(ngDevMode ? [{ debugName: "allowMultiple" }] : /* istanbul ignore next */ []));
3792
- this.variant = input(null, ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
3793
- this.size = input(null, ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
3794
- this.hostClasses = shipComponentClasses('accordion', {
3795
- variant: this.variant,
3796
- size: this.size,
3797
- });
3798
- this.items = contentProjectionSignal('details', {
3799
- childList: true,
3800
- subtree: true,
3801
- attributes: true,
3802
- attributeFilter: ['open', 'value'],
3803
- });
3804
- this.selfElement.addEventListener('toggle', this.onToggle.bind(this), true);
3805
- effect(() => {
3806
- const isMultiple = this.allowMultiple();
3807
- const groupName = this.name();
3808
- const valStr = this.value();
3809
- const vals = valStr ? valStr.split(',').filter((v) => v !== '') : [];
3810
- this.items().forEach((details) => {
3811
- if (!isMultiple) {
3812
- details.setAttribute('name', groupName);
3813
- }
3814
- else {
3815
- details.removeAttribute('name');
3816
- }
3817
- const summary = details.querySelector('summary');
3818
- if (summary && !summary.querySelector('sh-icon')) {
3819
- const icon = document.createElement('sh-icon');
3820
- icon.textContent = 'caret-down';
3821
- summary.appendChild(icon);
3822
- }
3823
- let contentWrapper = details.querySelector(':scope > .content');
3824
- if (!contentWrapper) {
3825
- const newWrapper = document.createElement('div');
3826
- newWrapper.className = 'content';
3827
- const childrenToMove = Array.from(details.childNodes).filter((node) => {
3828
- if (node.nodeType === Node.ELEMENT_NODE &&
3829
- node.tagName.toLowerCase() === 'summary') {
3830
- return false;
3831
- }
3832
- return true;
3833
- });
3834
- childrenToMove.forEach((child) => newWrapper.appendChild(child));
3835
- details.appendChild(newWrapper);
3836
- contentWrapper = newWrapper;
3837
- }
3838
- const itemVal = details.getAttribute('value');
3839
- if (itemVal && vals.includes(itemVal)) {
3840
- // Avoid triggering endless loops by only setting open if it actually changed
3841
- if (!details.open)
3842
- details.open = true;
3843
- }
3844
- else if (itemVal) {
3845
- if (details.open)
3846
- details.open = false;
3847
- }
3848
- });
3849
- });
3850
- }
3851
- onToggle(event) {
3852
- const target = event.target;
3853
- if (target.tagName !== 'DETAILS')
3854
- return;
3855
- if (!this.selfElement.contains(target))
3856
- return;
3857
- const itemVal = target.getAttribute('value');
3858
- if (!itemVal)
3859
- return; // Uncontrolled details element
3860
- const isOpen = target.open;
3861
- if (this.allowMultiple()) {
3862
- let vals = this.value()
3863
- ? this.value()
3864
- .split(',')
3865
- .filter((v) => v !== '')
3866
- : [];
3867
- if (isOpen && !vals.includes(itemVal))
3868
- vals.push(itemVal);
3869
- if (!isOpen)
3870
- vals = vals.filter((v) => v !== itemVal);
3871
- this.value.set(vals.join(','));
3872
- }
3873
- else {
3874
- if (isOpen) {
3875
- this.value.set(itemVal);
3876
- }
3877
- else {
3878
- if (this.value() === itemVal) {
3879
- this.value.set(null);
3880
- }
3881
- }
3882
- }
3883
- }
3884
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ShipAccordion, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3885
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.5", type: ShipAccordion, isStandalone: true, selector: "sh-accordion", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, allowMultiple: { classPropertyName: "allowMultiple", publicName: "allowMultiple", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { properties: { "class.sh-accordion": "true", "class": "hostClasses()" } }, ngImport: i0, template: `
3886
- <ng-content></ng-content>
3887
- `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3888
- }
3889
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ShipAccordion, decorators: [{
3890
- type: Component,
3891
- args: [{
3892
- selector: 'sh-accordion',
3893
- template: `
3894
- <ng-content></ng-content>
3895
- `,
3896
- changeDetection: ChangeDetectionStrategy.OnPush,
3897
- host: {
3898
- '[class.sh-accordion]': 'true',
3899
- '[class]': 'hostClasses()',
3900
- },
3901
- }]
3902
- }], ctorParameters: () => [], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], allowMultiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowMultiple", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }] } });
3903
-
3904
4657
  class ShipList {
3905
4658
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ShipList, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3906
4659
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.5", type: ShipList, isStandalone: true, selector: "sh-list", ngImport: i0, template: `
@@ -4474,7 +5227,7 @@ class ShipMenu {
4474
5227
  <ng-content select="[menu]" />
4475
5228
  </div>
4476
5229
  </sh-popover>
4477
- `, isInline: true, dependencies: [{ kind: "component", type: ShipPopover, selector: "sh-popover", inputs: ["asMultiLayer", "disableOpenByClick", "isOpen", "options"], outputs: ["isOpenChange", "closed"] }, { kind: "component", type: ShipFormField, selector: "sh-form-field", inputs: ["color", "variant", "size", "readonly"] }, { kind: "component", type: ShipIcon, selector: "sh-icon", inputs: ["color", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
5230
+ `, isInline: true, dependencies: [{ kind: "component", type: ShipPopover, selector: "sh-popover", inputs: ["asMultiLayer", "asSheetOnMobile", "disableOpenByClick", "isOpen", "options"], outputs: ["isOpenChange", "closed"] }, { kind: "component", type: ShipFormField, selector: "sh-form-field", inputs: ["color", "variant", "size", "readonly"] }, { kind: "component", type: ShipIcon, selector: "sh-icon", inputs: ["color", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4478
5231
  }
4479
5232
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ShipMenu, decorators: [{
4480
5233
  type: Component,
@@ -4674,6 +5427,18 @@ class ShipRangeSlider {
4674
5427
  max: 100,
4675
5428
  step: 1,
4676
5429
  }, ...(ngDevMode ? [{ debugName: "inputState" }] : /* istanbul ignore next */ []));
5430
+ this.color = input(null, ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
5431
+ this.variant = input(null, ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
5432
+ this.size = input(null, ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
5433
+ this.sharp = input(undefined, ...(ngDevMode ? [{ debugName: "sharp" }] : /* istanbul ignore next */ []));
5434
+ this.alwaysShow = input(undefined, ...(ngDevMode ? [{ debugName: "alwaysShow" }] : /* istanbul ignore next */ []));
5435
+ this.hostClasses = shipComponentClasses('rangeSlider', {
5436
+ color: this.color,
5437
+ variant: this.variant,
5438
+ size: this.size,
5439
+ sharp: this.sharp,
5440
+ alwaysShow: this.alwaysShow,
5441
+ });
4677
5442
  this.valuePercentage = computed(() => {
4678
5443
  const { min, max } = this.inputState();
4679
5444
  const currentValue = this.value() ?? min;
@@ -4843,7 +5608,7 @@ class ShipRangeSlider {
4843
5608
  }
4844
5609
  }
4845
5610
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ShipRangeSlider, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4846
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.5", type: ShipRangeSlider, isStandalone: true, selector: "sh-range-slider", inputs: { unit: { classPropertyName: "unit", publicName: "unit", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { properties: { "class.has-input": "this.hasInputElement" } }, ngImport: i0, template: `
5611
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.5", type: ShipRangeSlider, isStandalone: true, selector: "sh-range-slider", inputs: { unit: { classPropertyName: "unit", publicName: "unit", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, sharp: { classPropertyName: "sharp", publicName: "sharp", isSignal: true, isRequired: false, transformFunction: null }, alwaysShow: { classPropertyName: "alwaysShow", publicName: "alwaysShow", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, host: { properties: { "class.sh-range-slider": "true", "class": "hostClasses()", "class.has-input": "this.hasInputElement" } }, ngImport: i0, template: `
4847
5612
  <div class="label">
4848
5613
  <ng-content select="label"></ng-content>
4849
5614
  </div>
@@ -4900,8 +5665,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
4900
5665
  </div>
4901
5666
  `,
4902
5667
  changeDetection: ChangeDetectionStrategy.OnPush,
5668
+ host: {
5669
+ '[class.sh-range-slider]': 'true',
5670
+ '[class]': 'hostClasses()',
5671
+ },
4903
5672
  }]
4904
- }], propDecorators: { unit: [{ type: i0.Input, args: [{ isSignal: true, alias: "unit", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], hasInputElement: [{
5673
+ }], propDecorators: { unit: [{ type: i0.Input, args: [{ isSignal: true, alias: "unit", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], sharp: [{ type: i0.Input, args: [{ isSignal: true, alias: "sharp", required: false }] }], alwaysShow: [{ type: i0.Input, args: [{ isSignal: true, alias: "alwaysShow", required: false }] }], hasInputElement: [{
4905
5674
  type: HostBinding,
4906
5675
  args: ['class.has-input']
4907
5676
  }] } });
@@ -5659,7 +6428,7 @@ class ShipSelect {
5659
6428
  }
5660
6429
  </div>
5661
6430
  </sh-popover>
5662
- `, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ShipPopover, selector: "sh-popover", inputs: ["asMultiLayer", "disableOpenByClick", "isOpen", "options"], outputs: ["isOpenChange", "closed"] }, { kind: "component", type: ShipFormField, selector: "sh-form-field", inputs: ["color", "variant", "size", "readonly"] }, { kind: "component", type: ShipIcon, selector: "sh-icon", inputs: ["color", "size"] }, { kind: "component", type: ShipCheckbox, selector: "sh-checkbox", inputs: ["checked", "color", "variant", "readonly", "disabled", "noInternalInput"], outputs: ["checkedChange"] }, { kind: "component", type: ShipSpinner, selector: "sh-spinner", inputs: ["color"] }, { kind: "component", type: ShipChip, selector: "sh-chip", inputs: ["color", "variant", "size", "sharp", "dynamic", "readonly"] }, { kind: "component", type: ShipDivider, selector: "sh-divider" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
6431
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: ShipPopover, selector: "sh-popover", inputs: ["asMultiLayer", "asSheetOnMobile", "disableOpenByClick", "isOpen", "options"], outputs: ["isOpenChange", "closed"] }, { kind: "component", type: ShipFormField, selector: "sh-form-field", inputs: ["color", "variant", "size", "readonly"] }, { kind: "component", type: ShipIcon, selector: "sh-icon", inputs: ["color", "size"] }, { kind: "component", type: ShipCheckbox, selector: "sh-checkbox", inputs: ["checked", "color", "variant", "readonly", "disabled", "noInternalInput"], outputs: ["checkedChange"] }, { kind: "component", type: ShipSpinner, selector: "sh-spinner", inputs: ["color"] }, { kind: "component", type: ShipChip, selector: "sh-chip", inputs: ["color", "variant", "size", "sharp", "dynamic", "readonly"] }, { kind: "component", type: ShipDivider, selector: "sh-divider" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
5663
6432
  }
5664
6433
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ShipSelect, decorators: [{
5665
6434
  type: Component,
@@ -7727,5 +8496,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
7727
8496
  * Generated bundle index. Do not edit.
7728
8497
  */
7729
8498
 
7730
- export { GridSortable, SHIP_CONFIG, ShipAccordion, ShipAlert, ShipAlertContainer, ShipAlertModule, ShipAlertService, ShipBlueprint, ShipButton, ShipButtonGroup, ShipCard, ShipCheckbox, ShipChip, ShipColorPicker, ShipDatepicker, ShipDatepickerInput, ShipDaterangeInput, ShipDialog, ShipDialogService, ShipDivider, ShipEventCard, ShipFileDragDrop, ShipFileUpload, ShipFormField, ShipFormFieldExperimental, ShipIcon, ShipInputMask, ShipList, ShipMenu, ShipPopover, ShipPreventWheel, ShipProgressBar, ShipRadio, ShipRangeSlider, ShipResize, ShipSelect, ShipSidenav, ShipSort, ShipSortable, ShipSpinner, ShipStepper, ShipStickyColumns, ShipTable, ShipTabs, ShipThemeToggle, ShipToggle, ShipToggleCard, ShipTooltip, ShipTooltipWrapper, ShipVirtualScroll, TEST_NODES, moveIndex, watchHostClass };
8499
+ export { GridSortable, SHIP_CONFIG, ShipAccordion, ShipAlert, ShipAlertContainer, ShipAlertModule, ShipAlertService, ShipBlueprint, ShipButton, ShipButtonGroup, ShipCard, ShipCheckbox, ShipChip, ShipColorPicker, ShipColorPickerInput, ShipDatepicker, ShipDatepickerInput, ShipDaterangeInput, ShipDialog, ShipDialogService, ShipDivider, ShipEventCard, ShipFileDragDrop, ShipFileUpload, ShipFormField, ShipFormFieldExperimental, ShipIcon, ShipInputMask, ShipList, ShipMenu, ShipPopover, ShipPreventWheel, ShipProgressBar, ShipRadio, ShipRangeSlider, ShipResize, ShipSelect, ShipSidenav, ShipSort, ShipSortable, ShipSpinner, ShipStepper, ShipStickyColumns, ShipTable, ShipTabs, ShipThemeToggle, ShipToggle, ShipToggleCard, ShipTooltip, ShipTooltipWrapper, ShipVirtualScroll, TEST_NODES, defaultThemeColors, hslToOklch, hslToRgbExact, hsvToRgbExact, moveIndex, rgbToHex, rgbToHsl, rgbToHsv, rgbToOklch, rgbaToHex8, watchHostClass };
7731
8500
  //# sourceMappingURL=ship-ui-core.mjs.map