@sonny-ui/core 0.1.0-alpha.15 → 0.1.0-alpha.16
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.
- package/fesm2022/sonny-ui-core.mjs +1389 -2
- package/fesm2022/sonny-ui-core.mjs.map +1 -1
- package/package.json +1 -1
- package/src/lib/color-picker/color-picker.component.spec.ts +328 -0
- package/src/lib/color-picker/color-picker.component.ts +537 -0
- package/src/lib/color-picker/color-picker.types.ts +24 -0
- package/src/lib/color-picker/color-picker.utils.ts +183 -0
- package/src/lib/color-picker/color-picker.variants.ts +17 -0
- package/src/lib/color-picker/index.ts +20 -0
- package/src/lib/command-palette/command-palette.component.spec.ts +178 -0
- package/src/lib/command-palette/command-palette.component.ts +195 -0
- package/src/lib/command-palette/command-palette.service.ts +36 -0
- package/src/lib/command-palette/command-palette.types.ts +23 -0
- package/src/lib/command-palette/index.ts +7 -0
- package/src/lib/otp-input/index.ts +2 -0
- package/src/lib/otp-input/otp-input.component.spec.ts +252 -0
- package/src/lib/otp-input/otp-input.component.ts +275 -0
- package/src/lib/otp-input/otp-input.variants.ts +18 -0
- package/types/sonny-ui-core.d.ts +206 -3
|
@@ -3,7 +3,7 @@ import { twMerge } from 'tailwind-merge';
|
|
|
3
3
|
import { cva } from 'class-variance-authority';
|
|
4
4
|
export { cva } from 'class-variance-authority';
|
|
5
5
|
import * as i0 from '@angular/core';
|
|
6
|
-
import { inject, PLATFORM_ID, signal, computed, Injectable, InjectionToken, makeEnvironmentProviders, provideEnvironmentInitializer, input, Directive, ChangeDetectionStrategy, Component, ElementRef, model, viewChild, forwardRef, HostListener, TemplateRef, output, contentChildren, contentChild, effect, untracked, Injector, afterNextRender, Renderer2, linkedSignal } from '@angular/core';
|
|
6
|
+
import { inject, PLATFORM_ID, signal, computed, Injectable, InjectionToken, makeEnvironmentProviders, provideEnvironmentInitializer, input, Directive, ChangeDetectionStrategy, Component, ElementRef, model, viewChild, forwardRef, HostListener, TemplateRef, output, contentChildren, contentChild, effect, untracked, Injector, afterNextRender, Renderer2, linkedSignal, viewChildren } from '@angular/core';
|
|
7
7
|
import { DOCUMENT, isPlatformBrowser, NgTemplateOutlet } from '@angular/common';
|
|
8
8
|
import { Dialog, DialogRef } from '@angular/cdk/dialog';
|
|
9
9
|
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
@@ -6850,6 +6850,1393 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
6850
6850
|
args: ['keydown.escape']
|
|
6851
6851
|
}] } });
|
|
6852
6852
|
|
|
6853
|
+
function hexToRgb(hex) {
|
|
6854
|
+
const clean = hex.replace(/^#/, '');
|
|
6855
|
+
if (!/^[0-9a-fA-F]+$/.test(clean))
|
|
6856
|
+
return null;
|
|
6857
|
+
if (clean.length === 3) {
|
|
6858
|
+
const r = parseInt(clean[0] + clean[0], 16);
|
|
6859
|
+
const g = parseInt(clean[1] + clean[1], 16);
|
|
6860
|
+
const b = parseInt(clean[2] + clean[2], 16);
|
|
6861
|
+
return { r, g, b };
|
|
6862
|
+
}
|
|
6863
|
+
if (clean.length === 6) {
|
|
6864
|
+
const r = parseInt(clean.slice(0, 2), 16);
|
|
6865
|
+
const g = parseInt(clean.slice(2, 4), 16);
|
|
6866
|
+
const b = parseInt(clean.slice(4, 6), 16);
|
|
6867
|
+
return { r, g, b };
|
|
6868
|
+
}
|
|
6869
|
+
return null;
|
|
6870
|
+
}
|
|
6871
|
+
function rgbToHex(rgb) {
|
|
6872
|
+
const toHex = (n) => Math.round(Math.max(0, Math.min(255, n))).toString(16).padStart(2, '0');
|
|
6873
|
+
return `#${toHex(rgb.r)}${toHex(rgb.g)}${toHex(rgb.b)}`;
|
|
6874
|
+
}
|
|
6875
|
+
function rgbToHsl(rgb) {
|
|
6876
|
+
const r = rgb.r / 255;
|
|
6877
|
+
const g = rgb.g / 255;
|
|
6878
|
+
const b = rgb.b / 255;
|
|
6879
|
+
const max = Math.max(r, g, b);
|
|
6880
|
+
const min = Math.min(r, g, b);
|
|
6881
|
+
const l = (max + min) / 2;
|
|
6882
|
+
let h = 0;
|
|
6883
|
+
let s = 0;
|
|
6884
|
+
if (max !== min) {
|
|
6885
|
+
const d = max - min;
|
|
6886
|
+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
6887
|
+
switch (max) {
|
|
6888
|
+
case r:
|
|
6889
|
+
h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
|
|
6890
|
+
break;
|
|
6891
|
+
case g:
|
|
6892
|
+
h = ((b - r) / d + 2) / 6;
|
|
6893
|
+
break;
|
|
6894
|
+
case b:
|
|
6895
|
+
h = ((r - g) / d + 4) / 6;
|
|
6896
|
+
break;
|
|
6897
|
+
}
|
|
6898
|
+
}
|
|
6899
|
+
return {
|
|
6900
|
+
h: Math.round(h * 360),
|
|
6901
|
+
s: Math.round(s * 100),
|
|
6902
|
+
l: Math.round(l * 100),
|
|
6903
|
+
};
|
|
6904
|
+
}
|
|
6905
|
+
function hslToRgb(hsl) {
|
|
6906
|
+
const h = hsl.h / 360;
|
|
6907
|
+
const s = hsl.s / 100;
|
|
6908
|
+
const l = hsl.l / 100;
|
|
6909
|
+
if (s === 0) {
|
|
6910
|
+
const v = Math.round(l * 255);
|
|
6911
|
+
return { r: v, g: v, b: v };
|
|
6912
|
+
}
|
|
6913
|
+
const hue2rgb = (p, q, t) => {
|
|
6914
|
+
if (t < 0)
|
|
6915
|
+
t += 1;
|
|
6916
|
+
if (t > 1)
|
|
6917
|
+
t -= 1;
|
|
6918
|
+
if (t < 1 / 6)
|
|
6919
|
+
return p + (q - p) * 6 * t;
|
|
6920
|
+
if (t < 1 / 2)
|
|
6921
|
+
return q;
|
|
6922
|
+
if (t < 2 / 3)
|
|
6923
|
+
return p + (q - p) * (2 / 3 - t) * 6;
|
|
6924
|
+
return p;
|
|
6925
|
+
};
|
|
6926
|
+
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
6927
|
+
const p = 2 * l - q;
|
|
6928
|
+
return {
|
|
6929
|
+
r: Math.round(hue2rgb(p, q, h + 1 / 3) * 255),
|
|
6930
|
+
g: Math.round(hue2rgb(p, q, h) * 255),
|
|
6931
|
+
b: Math.round(hue2rgb(p, q, h - 1 / 3) * 255),
|
|
6932
|
+
};
|
|
6933
|
+
}
|
|
6934
|
+
function rgbToHsv(rgb) {
|
|
6935
|
+
const r = rgb.r / 255;
|
|
6936
|
+
const g = rgb.g / 255;
|
|
6937
|
+
const b = rgb.b / 255;
|
|
6938
|
+
const max = Math.max(r, g, b);
|
|
6939
|
+
const min = Math.min(r, g, b);
|
|
6940
|
+
const d = max - min;
|
|
6941
|
+
let h = 0;
|
|
6942
|
+
const s = max === 0 ? 0 : d / max;
|
|
6943
|
+
const v = max;
|
|
6944
|
+
if (max !== min) {
|
|
6945
|
+
switch (max) {
|
|
6946
|
+
case r:
|
|
6947
|
+
h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
|
|
6948
|
+
break;
|
|
6949
|
+
case g:
|
|
6950
|
+
h = ((b - r) / d + 2) / 6;
|
|
6951
|
+
break;
|
|
6952
|
+
case b:
|
|
6953
|
+
h = ((r - g) / d + 4) / 6;
|
|
6954
|
+
break;
|
|
6955
|
+
}
|
|
6956
|
+
}
|
|
6957
|
+
return { h: Math.round(h * 360), s, v };
|
|
6958
|
+
}
|
|
6959
|
+
function hsvToRgb(hsv) {
|
|
6960
|
+
const h = hsv.h / 360;
|
|
6961
|
+
const s = hsv.s;
|
|
6962
|
+
const v = hsv.v;
|
|
6963
|
+
const i = Math.floor(h * 6);
|
|
6964
|
+
const f = h * 6 - i;
|
|
6965
|
+
const p = v * (1 - s);
|
|
6966
|
+
const q = v * (1 - f * s);
|
|
6967
|
+
const t = v * (1 - (1 - f) * s);
|
|
6968
|
+
let r, g, b;
|
|
6969
|
+
switch (i % 6) {
|
|
6970
|
+
case 0:
|
|
6971
|
+
r = v;
|
|
6972
|
+
g = t;
|
|
6973
|
+
b = p;
|
|
6974
|
+
break;
|
|
6975
|
+
case 1:
|
|
6976
|
+
r = q;
|
|
6977
|
+
g = v;
|
|
6978
|
+
b = p;
|
|
6979
|
+
break;
|
|
6980
|
+
case 2:
|
|
6981
|
+
r = p;
|
|
6982
|
+
g = v;
|
|
6983
|
+
b = t;
|
|
6984
|
+
break;
|
|
6985
|
+
case 3:
|
|
6986
|
+
r = p;
|
|
6987
|
+
g = q;
|
|
6988
|
+
b = v;
|
|
6989
|
+
break;
|
|
6990
|
+
case 4:
|
|
6991
|
+
r = t;
|
|
6992
|
+
g = p;
|
|
6993
|
+
b = v;
|
|
6994
|
+
break;
|
|
6995
|
+
default:
|
|
6996
|
+
r = v;
|
|
6997
|
+
g = p;
|
|
6998
|
+
b = q;
|
|
6999
|
+
break;
|
|
7000
|
+
}
|
|
7001
|
+
return {
|
|
7002
|
+
r: Math.round(r * 255),
|
|
7003
|
+
g: Math.round(g * 255),
|
|
7004
|
+
b: Math.round(b * 255),
|
|
7005
|
+
};
|
|
7006
|
+
}
|
|
7007
|
+
function parseColor(input) {
|
|
7008
|
+
const trimmed = input.trim().toLowerCase();
|
|
7009
|
+
// HEX
|
|
7010
|
+
if (trimmed.startsWith('#')) {
|
|
7011
|
+
return hexToRgb(trimmed);
|
|
7012
|
+
}
|
|
7013
|
+
// rgb(r, g, b)
|
|
7014
|
+
const rgbMatch = trimmed.match(/^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/);
|
|
7015
|
+
if (rgbMatch) {
|
|
7016
|
+
return {
|
|
7017
|
+
r: Math.min(255, parseInt(rgbMatch[1])),
|
|
7018
|
+
g: Math.min(255, parseInt(rgbMatch[2])),
|
|
7019
|
+
b: Math.min(255, parseInt(rgbMatch[3])),
|
|
7020
|
+
};
|
|
7021
|
+
}
|
|
7022
|
+
// hsl(h, s%, l%)
|
|
7023
|
+
const hslMatch = trimmed.match(/^hsl\(\s*(\d{1,3})\s*,\s*(\d{1,3})%\s*,\s*(\d{1,3})%\s*\)$/);
|
|
7024
|
+
if (hslMatch) {
|
|
7025
|
+
return hslToRgb({
|
|
7026
|
+
h: Math.min(360, parseInt(hslMatch[1])),
|
|
7027
|
+
s: Math.min(100, parseInt(hslMatch[2])),
|
|
7028
|
+
l: Math.min(100, parseInt(hslMatch[3])),
|
|
7029
|
+
});
|
|
7030
|
+
}
|
|
7031
|
+
return null;
|
|
7032
|
+
}
|
|
7033
|
+
function formatColor(rgb, format, alpha) {
|
|
7034
|
+
switch (format) {
|
|
7035
|
+
case 'hex':
|
|
7036
|
+
return rgbToHex(rgb);
|
|
7037
|
+
case 'rgb':
|
|
7038
|
+
if (alpha !== undefined && alpha < 1) {
|
|
7039
|
+
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha.toFixed(2)})`;
|
|
7040
|
+
}
|
|
7041
|
+
return `rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`;
|
|
7042
|
+
case 'hsl': {
|
|
7043
|
+
const hsl = rgbToHsl(rgb);
|
|
7044
|
+
if (alpha !== undefined && alpha < 1) {
|
|
7045
|
+
return `hsla(${hsl.h}, ${hsl.s}%, ${hsl.l}%, ${alpha.toFixed(2)})`;
|
|
7046
|
+
}
|
|
7047
|
+
return `hsl(${hsl.h}, ${hsl.s}%, ${hsl.l}%)`;
|
|
7048
|
+
}
|
|
7049
|
+
}
|
|
7050
|
+
}
|
|
7051
|
+
function isValidColor(input) {
|
|
7052
|
+
return parseColor(input) !== null;
|
|
7053
|
+
}
|
|
7054
|
+
|
|
7055
|
+
const colorPickerTriggerVariants = cva('inline-flex w-full items-center gap-2 whitespace-nowrap rounded-sm border border-border bg-background px-3 py-2 text-sm ring-offset-background transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50', {
|
|
7056
|
+
variants: {
|
|
7057
|
+
size: {
|
|
7058
|
+
sm: 'h-9 text-xs',
|
|
7059
|
+
md: 'h-10 text-sm',
|
|
7060
|
+
lg: 'h-11 text-base',
|
|
7061
|
+
},
|
|
7062
|
+
},
|
|
7063
|
+
defaultVariants: { size: 'md' },
|
|
7064
|
+
});
|
|
7065
|
+
|
|
7066
|
+
class SnyColorPickerComponent {
|
|
7067
|
+
// Public API
|
|
7068
|
+
value = model('#000000', ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
7069
|
+
format = input('hex', ...(ngDevMode ? [{ debugName: "format" }] : /* istanbul ignore next */ []));
|
|
7070
|
+
presets = input([], ...(ngDevMode ? [{ debugName: "presets" }] : /* istanbul ignore next */ []));
|
|
7071
|
+
showInput = input(true, ...(ngDevMode ? [{ debugName: "showInput" }] : /* istanbul ignore next */ []));
|
|
7072
|
+
showEyeDropper = input(true, ...(ngDevMode ? [{ debugName: "showEyeDropper" }] : /* istanbul ignore next */ []));
|
|
7073
|
+
showFavorites = input(false, ...(ngDevMode ? [{ debugName: "showFavorites" }] : /* istanbul ignore next */ []));
|
|
7074
|
+
inline = input(false, ...(ngDevMode ? [{ debugName: "inline" }] : /* istanbul ignore next */ []));
|
|
7075
|
+
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
7076
|
+
placeholder = input('Pick a color...', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
7077
|
+
size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
7078
|
+
class = input('', ...(ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []));
|
|
7079
|
+
colorChange = output();
|
|
7080
|
+
formatChange = output();
|
|
7081
|
+
// Internal state
|
|
7082
|
+
hsv = signal({ h: 0, s: 0, v: 0 }, ...(ngDevMode ? [{ debugName: "hsv" }] : /* istanbul ignore next */ []));
|
|
7083
|
+
currentFormat = signal('hex', ...(ngDevMode ? [{ debugName: "currentFormat" }] : /* istanbul ignore next */ []));
|
|
7084
|
+
inputValue = signal('', ...(ngDevMode ? [{ debugName: "inputValue" }] : /* istanbul ignore next */ []));
|
|
7085
|
+
open = signal(false, ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
|
|
7086
|
+
favorites = signal([], ...(ngDevMode ? [{ debugName: "favorites" }] : /* istanbul ignore next */ []));
|
|
7087
|
+
copied = signal(false, ...(ngDevMode ? [{ debugName: "copied" }] : /* istanbul ignore next */ []));
|
|
7088
|
+
_disabledByCva = signal(false, ...(ngDevMode ? [{ debugName: "_disabledByCva" }] : /* istanbul ignore next */ []));
|
|
7089
|
+
isDisabled = computed(() => this.disabled() || this._disabledByCva(), ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
|
|
7090
|
+
triggerRef = viewChild('triggerEl', ...(ngDevMode ? [{ debugName: "triggerRef" }] : /* istanbul ignore next */ []));
|
|
7091
|
+
panelRef = viewChild('panelEl', ...(ngDevMode ? [{ debugName: "panelRef" }] : /* istanbul ignore next */ []));
|
|
7092
|
+
satPanelRef = viewChild('satPanel', ...(ngDevMode ? [{ debugName: "satPanelRef" }] : /* istanbul ignore next */ []));
|
|
7093
|
+
hueTrackRef = viewChild('hueTrack', ...(ngDevMode ? [{ debugName: "hueTrackRef" }] : /* istanbul ignore next */ []));
|
|
7094
|
+
elRef = inject(ElementRef);
|
|
7095
|
+
moveHandler = null;
|
|
7096
|
+
upHandler = null;
|
|
7097
|
+
scrollHandler = null;
|
|
7098
|
+
resizeHandler = null;
|
|
7099
|
+
_onChange = () => { };
|
|
7100
|
+
onTouched = () => { };
|
|
7101
|
+
hasEyeDropper = typeof window !== 'undefined' && 'EyeDropper' in window;
|
|
7102
|
+
// Computed
|
|
7103
|
+
rgb = computed(() => hsvToRgb(this.hsv()), ...(ngDevMode ? [{ debugName: "rgb" }] : /* istanbul ignore next */ []));
|
|
7104
|
+
displayValue = computed(() => formatColor(this.rgb(), this.currentFormat()), ...(ngDevMode ? [{ debugName: "displayValue" }] : /* istanbul ignore next */ []));
|
|
7105
|
+
saturationBg = computed(() => `linear-gradient(to right, #fff, hsl(${this.hsv().h}, 100%, 50%))`, ...(ngDevMode ? [{ debugName: "saturationBg" }] : /* istanbul ignore next */ []));
|
|
7106
|
+
triggerClass = computed(() => cn(colorPickerTriggerVariants({ size: this.size() }), this.class()), ...(ngDevMode ? [{ debugName: "triggerClass" }] : /* istanbul ignore next */ []));
|
|
7107
|
+
panelClass = computed(() => this.inline()
|
|
7108
|
+
? 'inline-block p-3 rounded-md border border-border bg-popover text-popover-foreground w-60'
|
|
7109
|
+
: 'fixed z-50 p-3 rounded-md border border-border bg-popover text-popover-foreground shadow-lg animate-in fade-in-0 zoom-in-95 w-60', ...(ngDevMode ? [{ debugName: "panelClass" }] : /* istanbul ignore next */ []));
|
|
7110
|
+
constructor() {
|
|
7111
|
+
// Sync format input
|
|
7112
|
+
effect(() => {
|
|
7113
|
+
const fmt = this.format();
|
|
7114
|
+
untracked(() => this.currentFormat.set(fmt));
|
|
7115
|
+
});
|
|
7116
|
+
// Sync value → HSV when value changes externally
|
|
7117
|
+
effect(() => {
|
|
7118
|
+
const val = this.value();
|
|
7119
|
+
untracked(() => {
|
|
7120
|
+
const rgb = parseColor(val);
|
|
7121
|
+
if (rgb) {
|
|
7122
|
+
this.hsv.set(rgbToHsv(rgb));
|
|
7123
|
+
this.inputValue.set(this.displayValue());
|
|
7124
|
+
}
|
|
7125
|
+
});
|
|
7126
|
+
});
|
|
7127
|
+
}
|
|
7128
|
+
// CVA
|
|
7129
|
+
writeValue(val) {
|
|
7130
|
+
this.value.set(val ?? '#000000');
|
|
7131
|
+
}
|
|
7132
|
+
registerOnChange(fn) {
|
|
7133
|
+
this._onChange = fn;
|
|
7134
|
+
}
|
|
7135
|
+
registerOnTouched(fn) {
|
|
7136
|
+
this.onTouched = fn;
|
|
7137
|
+
}
|
|
7138
|
+
setDisabledState(isDisabled) {
|
|
7139
|
+
this._disabledByCva.set(isDisabled);
|
|
7140
|
+
}
|
|
7141
|
+
// Emit helper
|
|
7142
|
+
emitColor() {
|
|
7143
|
+
const formatted = this.displayValue();
|
|
7144
|
+
this.value.set(formatted);
|
|
7145
|
+
this.inputValue.set(formatted);
|
|
7146
|
+
this._onChange(formatted);
|
|
7147
|
+
this.colorChange.emit(formatted);
|
|
7148
|
+
}
|
|
7149
|
+
// Saturation panel
|
|
7150
|
+
onSatPanelDown(event) {
|
|
7151
|
+
event.preventDefault();
|
|
7152
|
+
this.updateSatFromPosition(event.clientX, event.clientY);
|
|
7153
|
+
this.startDrag((e) => {
|
|
7154
|
+
const x = e instanceof MouseEvent ? e.clientX : e.touches[0].clientX;
|
|
7155
|
+
const y = e instanceof MouseEvent ? e.clientY : e.touches[0].clientY;
|
|
7156
|
+
this.updateSatFromPosition(x, y);
|
|
7157
|
+
});
|
|
7158
|
+
}
|
|
7159
|
+
onSatPanelTouch(event) {
|
|
7160
|
+
this.updateSatFromPosition(event.touches[0].clientX, event.touches[0].clientY);
|
|
7161
|
+
this.startDrag((e) => {
|
|
7162
|
+
const x = e instanceof MouseEvent ? e.clientX : e.touches[0].clientX;
|
|
7163
|
+
const y = e instanceof MouseEvent ? e.clientY : e.touches[0].clientY;
|
|
7164
|
+
this.updateSatFromPosition(x, y);
|
|
7165
|
+
}, true);
|
|
7166
|
+
}
|
|
7167
|
+
updateSatFromPosition(clientX, clientY) {
|
|
7168
|
+
const panel = this.satPanelRef()?.nativeElement;
|
|
7169
|
+
if (!panel)
|
|
7170
|
+
return;
|
|
7171
|
+
const rect = panel.getBoundingClientRect();
|
|
7172
|
+
const s = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
|
|
7173
|
+
const v = Math.max(0, Math.min(1, 1 - (clientY - rect.top) / rect.height));
|
|
7174
|
+
this.hsv.update((prev) => ({ ...prev, s, v }));
|
|
7175
|
+
this.emitColor();
|
|
7176
|
+
}
|
|
7177
|
+
// Hue slider
|
|
7178
|
+
onHueDown(event) {
|
|
7179
|
+
event.preventDefault();
|
|
7180
|
+
this.updateHueFromPosition(event.clientX);
|
|
7181
|
+
this.startDrag((e) => {
|
|
7182
|
+
const x = e instanceof MouseEvent ? e.clientX : e.touches[0].clientX;
|
|
7183
|
+
this.updateHueFromPosition(x);
|
|
7184
|
+
});
|
|
7185
|
+
}
|
|
7186
|
+
onHueTouch(event) {
|
|
7187
|
+
this.updateHueFromPosition(event.touches[0].clientX);
|
|
7188
|
+
this.startDrag((e) => {
|
|
7189
|
+
const x = e instanceof MouseEvent ? e.clientX : e.touches[0].clientX;
|
|
7190
|
+
this.updateHueFromPosition(x);
|
|
7191
|
+
}, true);
|
|
7192
|
+
}
|
|
7193
|
+
updateHueFromPosition(clientX) {
|
|
7194
|
+
const track = this.hueTrackRef()?.nativeElement;
|
|
7195
|
+
if (!track)
|
|
7196
|
+
return;
|
|
7197
|
+
const rect = track.getBoundingClientRect();
|
|
7198
|
+
const percent = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
|
|
7199
|
+
const h = Math.round(percent * 360);
|
|
7200
|
+
this.hsv.update((prev) => ({ ...prev, h }));
|
|
7201
|
+
this.emitColor();
|
|
7202
|
+
}
|
|
7203
|
+
// Drag helpers (same pattern as slider component)
|
|
7204
|
+
startDrag(handler, touch = false) {
|
|
7205
|
+
this.removeDragListeners();
|
|
7206
|
+
this.moveHandler = handler;
|
|
7207
|
+
this.upHandler = () => {
|
|
7208
|
+
this.onTouched();
|
|
7209
|
+
this.removeDragListeners();
|
|
7210
|
+
};
|
|
7211
|
+
if (touch) {
|
|
7212
|
+
document.addEventListener('touchmove', this.moveHandler, { passive: true });
|
|
7213
|
+
document.addEventListener('touchend', this.upHandler);
|
|
7214
|
+
}
|
|
7215
|
+
else {
|
|
7216
|
+
document.addEventListener('mousemove', this.moveHandler);
|
|
7217
|
+
document.addEventListener('mouseup', this.upHandler);
|
|
7218
|
+
}
|
|
7219
|
+
}
|
|
7220
|
+
removeDragListeners() {
|
|
7221
|
+
if (this.moveHandler) {
|
|
7222
|
+
document.removeEventListener('mousemove', this.moveHandler);
|
|
7223
|
+
document.removeEventListener('touchmove', this.moveHandler);
|
|
7224
|
+
this.moveHandler = null;
|
|
7225
|
+
}
|
|
7226
|
+
if (this.upHandler) {
|
|
7227
|
+
document.removeEventListener('mouseup', this.upHandler);
|
|
7228
|
+
document.removeEventListener('touchend', this.upHandler);
|
|
7229
|
+
this.upHandler = null;
|
|
7230
|
+
}
|
|
7231
|
+
}
|
|
7232
|
+
// Input
|
|
7233
|
+
onInputChange(event) {
|
|
7234
|
+
this.inputValue.set(event.target.value);
|
|
7235
|
+
}
|
|
7236
|
+
commitInput() {
|
|
7237
|
+
const val = this.inputValue().trim();
|
|
7238
|
+
if (isValidColor(val)) {
|
|
7239
|
+
const rgb = parseColor(val);
|
|
7240
|
+
this.hsv.set(rgbToHsv(rgb));
|
|
7241
|
+
this.emitColor();
|
|
7242
|
+
}
|
|
7243
|
+
else {
|
|
7244
|
+
this.inputValue.set(this.displayValue());
|
|
7245
|
+
}
|
|
7246
|
+
}
|
|
7247
|
+
// Format
|
|
7248
|
+
cycleFormat() {
|
|
7249
|
+
const formats = ['hex', 'rgb', 'hsl'];
|
|
7250
|
+
const idx = formats.indexOf(this.currentFormat());
|
|
7251
|
+
const next = formats[(idx + 1) % formats.length];
|
|
7252
|
+
this.currentFormat.set(next);
|
|
7253
|
+
this.inputValue.set(this.displayValue());
|
|
7254
|
+
this.formatChange.emit(next);
|
|
7255
|
+
}
|
|
7256
|
+
// Copy
|
|
7257
|
+
copyColor() {
|
|
7258
|
+
navigator.clipboard.writeText(this.displayValue());
|
|
7259
|
+
this.copied.set(true);
|
|
7260
|
+
setTimeout(() => this.copied.set(false), 2000);
|
|
7261
|
+
}
|
|
7262
|
+
// Presets & favorites
|
|
7263
|
+
selectColor(color) {
|
|
7264
|
+
const rgb = parseColor(color);
|
|
7265
|
+
if (rgb) {
|
|
7266
|
+
this.hsv.set(rgbToHsv(rgb));
|
|
7267
|
+
this.emitColor();
|
|
7268
|
+
}
|
|
7269
|
+
}
|
|
7270
|
+
addFavorite() {
|
|
7271
|
+
const hex = rgbToHex(this.rgb());
|
|
7272
|
+
this.favorites.update((favs) => favs.includes(hex) ? favs : [...favs, hex]);
|
|
7273
|
+
}
|
|
7274
|
+
removeFavorite(color) {
|
|
7275
|
+
this.favorites.update((favs) => favs.filter((f) => f !== color));
|
|
7276
|
+
}
|
|
7277
|
+
// EyeDropper
|
|
7278
|
+
async pickFromScreen() {
|
|
7279
|
+
if (!this.hasEyeDropper)
|
|
7280
|
+
return;
|
|
7281
|
+
try {
|
|
7282
|
+
const dropper = new window.EyeDropper();
|
|
7283
|
+
const result = await dropper.open();
|
|
7284
|
+
this.selectColor(result.sRGBHex);
|
|
7285
|
+
}
|
|
7286
|
+
catch {
|
|
7287
|
+
// User cancelled
|
|
7288
|
+
}
|
|
7289
|
+
}
|
|
7290
|
+
// Popover
|
|
7291
|
+
toggle() {
|
|
7292
|
+
if (this.open()) {
|
|
7293
|
+
this.close();
|
|
7294
|
+
}
|
|
7295
|
+
else {
|
|
7296
|
+
this.open.set(true);
|
|
7297
|
+
this.addPositionListeners();
|
|
7298
|
+
setTimeout(() => this.updatePanelPosition());
|
|
7299
|
+
}
|
|
7300
|
+
}
|
|
7301
|
+
close() {
|
|
7302
|
+
this.open.set(false);
|
|
7303
|
+
this.removePositionListeners();
|
|
7304
|
+
}
|
|
7305
|
+
updatePanelPosition() {
|
|
7306
|
+
if (this.inline())
|
|
7307
|
+
return;
|
|
7308
|
+
const trigger = this.triggerRef()?.nativeElement;
|
|
7309
|
+
if (!trigger)
|
|
7310
|
+
return;
|
|
7311
|
+
const rect = trigger.getBoundingClientRect();
|
|
7312
|
+
const panel = this.panelRef()?.nativeElement;
|
|
7313
|
+
if (panel) {
|
|
7314
|
+
panel.style.top = `${rect.bottom + 4}px`;
|
|
7315
|
+
panel.style.left = `${rect.left}px`;
|
|
7316
|
+
}
|
|
7317
|
+
}
|
|
7318
|
+
addPositionListeners() {
|
|
7319
|
+
this.removePositionListeners();
|
|
7320
|
+
this.scrollHandler = () => requestAnimationFrame(() => this.updatePanelPosition());
|
|
7321
|
+
this.resizeHandler = () => requestAnimationFrame(() => this.updatePanelPosition());
|
|
7322
|
+
document.addEventListener('scroll', this.scrollHandler, { capture: true, passive: true });
|
|
7323
|
+
window.addEventListener('resize', this.resizeHandler, { passive: true });
|
|
7324
|
+
}
|
|
7325
|
+
removePositionListeners() {
|
|
7326
|
+
if (this.scrollHandler) {
|
|
7327
|
+
document.removeEventListener('scroll', this.scrollHandler, { capture: true });
|
|
7328
|
+
this.scrollHandler = null;
|
|
7329
|
+
}
|
|
7330
|
+
if (this.resizeHandler) {
|
|
7331
|
+
window.removeEventListener('resize', this.resizeHandler);
|
|
7332
|
+
this.resizeHandler = null;
|
|
7333
|
+
}
|
|
7334
|
+
}
|
|
7335
|
+
onDocumentClick(event) {
|
|
7336
|
+
if (!this.elRef.nativeElement.contains(event.target)) {
|
|
7337
|
+
this.close();
|
|
7338
|
+
}
|
|
7339
|
+
}
|
|
7340
|
+
onEscape() {
|
|
7341
|
+
this.close();
|
|
7342
|
+
}
|
|
7343
|
+
ngOnDestroy() {
|
|
7344
|
+
this.removeDragListeners();
|
|
7345
|
+
this.removePositionListeners();
|
|
7346
|
+
}
|
|
7347
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyColorPickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7348
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: SnyColorPickerComponent, isStandalone: true, selector: "sny-color-picker", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, presets: { classPropertyName: "presets", publicName: "presets", isSignal: true, isRequired: false, transformFunction: null }, showInput: { classPropertyName: "showInput", publicName: "showInput", isSignal: true, isRequired: false, transformFunction: null }, showEyeDropper: { classPropertyName: "showEyeDropper", publicName: "showEyeDropper", isSignal: true, isRequired: false, transformFunction: null }, showFavorites: { classPropertyName: "showFavorites", publicName: "showFavorites", isSignal: true, isRequired: false, transformFunction: null }, inline: { classPropertyName: "inline", publicName: "inline", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", colorChange: "colorChange", formatChange: "formatChange" }, host: { listeners: { "document:click": "onDocumentClick($event)", "keydown.escape": "onEscape()" }, classAttribute: "relative inline-block" }, providers: [
|
|
7349
|
+
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SnyColorPickerComponent), multi: true },
|
|
7350
|
+
], viewQueries: [{ propertyName: "triggerRef", first: true, predicate: ["triggerEl"], descendants: true, isSignal: true }, { propertyName: "panelRef", first: true, predicate: ["panelEl"], descendants: true, isSignal: true }, { propertyName: "satPanelRef", first: true, predicate: ["satPanel"], descendants: true, isSignal: true }, { propertyName: "hueTrackRef", first: true, predicate: ["hueTrack"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
7351
|
+
<!-- Trigger -->
|
|
7352
|
+
@if (!inline()) {
|
|
7353
|
+
<button
|
|
7354
|
+
#triggerEl
|
|
7355
|
+
type="button"
|
|
7356
|
+
role="combobox"
|
|
7357
|
+
[attr.aria-expanded]="open()"
|
|
7358
|
+
aria-haspopup="dialog"
|
|
7359
|
+
[disabled]="isDisabled()"
|
|
7360
|
+
[class]="triggerClass()"
|
|
7361
|
+
(click)="toggle()"
|
|
7362
|
+
(blur)="onTouched()"
|
|
7363
|
+
>
|
|
7364
|
+
<div
|
|
7365
|
+
class="h-5 w-5 rounded-sm border border-border shrink-0"
|
|
7366
|
+
[style.backgroundColor]="displayValue()"
|
|
7367
|
+
></div>
|
|
7368
|
+
<span class="truncate">{{ displayValue() || placeholder() }}</span>
|
|
7369
|
+
</button>
|
|
7370
|
+
}
|
|
7371
|
+
|
|
7372
|
+
<!-- Panel -->
|
|
7373
|
+
@if (open() || inline()) {
|
|
7374
|
+
<div
|
|
7375
|
+
#panelEl
|
|
7376
|
+
[class]="panelClass()"
|
|
7377
|
+
role="dialog"
|
|
7378
|
+
aria-modal="true"
|
|
7379
|
+
aria-label="Color picker"
|
|
7380
|
+
>
|
|
7381
|
+
<!-- Saturation/Brightness Panel -->
|
|
7382
|
+
<div
|
|
7383
|
+
#satPanel
|
|
7384
|
+
class="relative h-36 w-full rounded-md cursor-crosshair overflow-hidden"
|
|
7385
|
+
[style.background]="saturationBg()"
|
|
7386
|
+
(mousedown)="onSatPanelDown($event)"
|
|
7387
|
+
(touchstart)="onSatPanelTouch($event)"
|
|
7388
|
+
>
|
|
7389
|
+
<div class="absolute inset-0 bg-gradient-to-t from-black to-transparent"></div>
|
|
7390
|
+
<div
|
|
7391
|
+
class="absolute h-4 w-4 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white shadow-md pointer-events-none"
|
|
7392
|
+
[style.left.%]="hsv().s * 100"
|
|
7393
|
+
[style.top.%]="(1 - hsv().v) * 100"
|
|
7394
|
+
></div>
|
|
7395
|
+
</div>
|
|
7396
|
+
|
|
7397
|
+
<!-- Hue Slider -->
|
|
7398
|
+
<div
|
|
7399
|
+
#hueTrack
|
|
7400
|
+
class="relative h-3 w-full rounded-full cursor-pointer mt-3"
|
|
7401
|
+
style="background: linear-gradient(to right, hsl(0,100%,50%), hsl(60,100%,50%), hsl(120,100%,50%), hsl(180,100%,50%), hsl(240,100%,50%), hsl(300,100%,50%), hsl(360,100%,50%))"
|
|
7402
|
+
(mousedown)="onHueDown($event)"
|
|
7403
|
+
(touchstart)="onHueTouch($event)"
|
|
7404
|
+
>
|
|
7405
|
+
<div
|
|
7406
|
+
class="absolute top-1/2 h-4 w-4 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white shadow-md pointer-events-none"
|
|
7407
|
+
[style.left.%]="hsv().h / 360 * 100"
|
|
7408
|
+
[style.backgroundColor]="'hsl(' + hsv().h + ', 100%, 50%)'"
|
|
7409
|
+
></div>
|
|
7410
|
+
</div>
|
|
7411
|
+
|
|
7412
|
+
<!-- Input + Format + Copy + Actions -->
|
|
7413
|
+
@if (showInput()) {
|
|
7414
|
+
<div class="mt-3 flex items-center gap-1.5">
|
|
7415
|
+
<div
|
|
7416
|
+
class="h-8 w-8 rounded-sm border border-border shrink-0"
|
|
7417
|
+
[style.backgroundColor]="displayValue()"
|
|
7418
|
+
></div>
|
|
7419
|
+
<input
|
|
7420
|
+
class="flex-1 min-w-0 h-8 rounded-sm border border-border bg-background px-2 text-xs font-mono focus:outline-none focus:ring-1 focus:ring-ring"
|
|
7421
|
+
[value]="inputValue()"
|
|
7422
|
+
(input)="onInputChange($event)"
|
|
7423
|
+
(blur)="commitInput()"
|
|
7424
|
+
(keydown.enter)="commitInput()"
|
|
7425
|
+
/>
|
|
7426
|
+
<button
|
|
7427
|
+
type="button"
|
|
7428
|
+
class="h-8 px-1.5 rounded-sm border border-border text-[10px] font-semibold uppercase hover:bg-accent transition-colors shrink-0"
|
|
7429
|
+
(click)="cycleFormat()"
|
|
7430
|
+
title="Switch format"
|
|
7431
|
+
>
|
|
7432
|
+
{{ currentFormat() }}
|
|
7433
|
+
</button>
|
|
7434
|
+
<button
|
|
7435
|
+
type="button"
|
|
7436
|
+
class="h-8 w-8 inline-flex items-center justify-center rounded-sm border border-border hover:bg-accent transition-colors shrink-0"
|
|
7437
|
+
(click)="copyColor()"
|
|
7438
|
+
[title]="copied() ? 'Copied!' : 'Copy color'"
|
|
7439
|
+
>
|
|
7440
|
+
@if (copied()) {
|
|
7441
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>
|
|
7442
|
+
} @else {
|
|
7443
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="14" x="8" y="8" rx="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>
|
|
7444
|
+
}
|
|
7445
|
+
</button>
|
|
7446
|
+
</div>
|
|
7447
|
+
<!-- Secondary actions row -->
|
|
7448
|
+
<div class="mt-2 flex items-center gap-1.5">
|
|
7449
|
+
@if (showEyeDropper() && hasEyeDropper) {
|
|
7450
|
+
<button
|
|
7451
|
+
type="button"
|
|
7452
|
+
class="h-7 px-2 inline-flex items-center gap-1.5 rounded-sm border border-border text-xs text-muted-foreground hover:bg-accent hover:text-foreground transition-colors"
|
|
7453
|
+
(click)="pickFromScreen()"
|
|
7454
|
+
title="Pick from screen"
|
|
7455
|
+
>
|
|
7456
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m2 22 1-1h3l9-9"/><path d="M3 21v-3l9-9"/><path d="m15 6 3.4-3.4a2.1 2.1 0 1 1 3 3L18 9l.4.4a2.1 2.1 0 1 1-3 3l-3.8-3.8a2.1 2.1 0 1 1 3-3l.4.4Z"/></svg>
|
|
7457
|
+
Pick
|
|
7458
|
+
</button>
|
|
7459
|
+
}
|
|
7460
|
+
@if (showFavorites()) {
|
|
7461
|
+
<button
|
|
7462
|
+
type="button"
|
|
7463
|
+
class="h-7 px-2 inline-flex items-center gap-1.5 rounded-sm border border-border text-xs text-muted-foreground hover:bg-accent hover:text-foreground transition-colors"
|
|
7464
|
+
(click)="addFavorite()"
|
|
7465
|
+
title="Save to favorites"
|
|
7466
|
+
>
|
|
7467
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"/></svg>
|
|
7468
|
+
Save
|
|
7469
|
+
</button>
|
|
7470
|
+
}
|
|
7471
|
+
</div>
|
|
7472
|
+
}
|
|
7473
|
+
|
|
7474
|
+
<!-- Presets -->
|
|
7475
|
+
@for (preset of presets(); track $index) {
|
|
7476
|
+
<div class="mt-3">
|
|
7477
|
+
@if (preset.label) {
|
|
7478
|
+
<p class="text-xs font-medium text-muted-foreground mb-1.5">{{ preset.label }}</p>
|
|
7479
|
+
}
|
|
7480
|
+
<div class="flex flex-wrap gap-1.5">
|
|
7481
|
+
@for (color of preset.colors; track color) {
|
|
7482
|
+
<button
|
|
7483
|
+
type="button"
|
|
7484
|
+
class="h-6 w-6 rounded-sm border border-border hover:scale-110 transition-transform cursor-pointer"
|
|
7485
|
+
[style.backgroundColor]="color"
|
|
7486
|
+
[title]="color"
|
|
7487
|
+
(click)="selectColor(color)"
|
|
7488
|
+
></button>
|
|
7489
|
+
}
|
|
7490
|
+
</div>
|
|
7491
|
+
</div>
|
|
7492
|
+
}
|
|
7493
|
+
|
|
7494
|
+
<!-- Favorites -->
|
|
7495
|
+
@if (showFavorites() && favorites().length > 0) {
|
|
7496
|
+
<div class="mt-3">
|
|
7497
|
+
<p class="text-xs font-medium text-muted-foreground mb-1.5">Favorites</p>
|
|
7498
|
+
<div class="flex flex-wrap gap-1.5">
|
|
7499
|
+
@for (fav of favorites(); track fav) {
|
|
7500
|
+
<div class="relative group">
|
|
7501
|
+
<button
|
|
7502
|
+
type="button"
|
|
7503
|
+
class="h-6 w-6 rounded-sm border border-border hover:scale-110 transition-transform cursor-pointer"
|
|
7504
|
+
[style.backgroundColor]="fav"
|
|
7505
|
+
[title]="fav"
|
|
7506
|
+
(click)="selectColor(fav)"
|
|
7507
|
+
></button>
|
|
7508
|
+
<button
|
|
7509
|
+
type="button"
|
|
7510
|
+
class="absolute -top-1 -right-1 h-3.5 w-3.5 rounded-full bg-destructive text-destructive-foreground text-[8px] leading-none items-center justify-center hidden group-hover:inline-flex"
|
|
7511
|
+
(click)="removeFavorite(fav); $event.stopPropagation()"
|
|
7512
|
+
>×</button>
|
|
7513
|
+
</div>
|
|
7514
|
+
}
|
|
7515
|
+
</div>
|
|
7516
|
+
</div>
|
|
7517
|
+
}
|
|
7518
|
+
</div>
|
|
7519
|
+
}
|
|
7520
|
+
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
7521
|
+
}
|
|
7522
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyColorPickerComponent, decorators: [{
|
|
7523
|
+
type: Component,
|
|
7524
|
+
args: [{
|
|
7525
|
+
selector: 'sny-color-picker',
|
|
7526
|
+
standalone: true,
|
|
7527
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
7528
|
+
host: { class: 'relative inline-block' },
|
|
7529
|
+
providers: [
|
|
7530
|
+
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SnyColorPickerComponent), multi: true },
|
|
7531
|
+
],
|
|
7532
|
+
template: `
|
|
7533
|
+
<!-- Trigger -->
|
|
7534
|
+
@if (!inline()) {
|
|
7535
|
+
<button
|
|
7536
|
+
#triggerEl
|
|
7537
|
+
type="button"
|
|
7538
|
+
role="combobox"
|
|
7539
|
+
[attr.aria-expanded]="open()"
|
|
7540
|
+
aria-haspopup="dialog"
|
|
7541
|
+
[disabled]="isDisabled()"
|
|
7542
|
+
[class]="triggerClass()"
|
|
7543
|
+
(click)="toggle()"
|
|
7544
|
+
(blur)="onTouched()"
|
|
7545
|
+
>
|
|
7546
|
+
<div
|
|
7547
|
+
class="h-5 w-5 rounded-sm border border-border shrink-0"
|
|
7548
|
+
[style.backgroundColor]="displayValue()"
|
|
7549
|
+
></div>
|
|
7550
|
+
<span class="truncate">{{ displayValue() || placeholder() }}</span>
|
|
7551
|
+
</button>
|
|
7552
|
+
}
|
|
7553
|
+
|
|
7554
|
+
<!-- Panel -->
|
|
7555
|
+
@if (open() || inline()) {
|
|
7556
|
+
<div
|
|
7557
|
+
#panelEl
|
|
7558
|
+
[class]="panelClass()"
|
|
7559
|
+
role="dialog"
|
|
7560
|
+
aria-modal="true"
|
|
7561
|
+
aria-label="Color picker"
|
|
7562
|
+
>
|
|
7563
|
+
<!-- Saturation/Brightness Panel -->
|
|
7564
|
+
<div
|
|
7565
|
+
#satPanel
|
|
7566
|
+
class="relative h-36 w-full rounded-md cursor-crosshair overflow-hidden"
|
|
7567
|
+
[style.background]="saturationBg()"
|
|
7568
|
+
(mousedown)="onSatPanelDown($event)"
|
|
7569
|
+
(touchstart)="onSatPanelTouch($event)"
|
|
7570
|
+
>
|
|
7571
|
+
<div class="absolute inset-0 bg-gradient-to-t from-black to-transparent"></div>
|
|
7572
|
+
<div
|
|
7573
|
+
class="absolute h-4 w-4 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white shadow-md pointer-events-none"
|
|
7574
|
+
[style.left.%]="hsv().s * 100"
|
|
7575
|
+
[style.top.%]="(1 - hsv().v) * 100"
|
|
7576
|
+
></div>
|
|
7577
|
+
</div>
|
|
7578
|
+
|
|
7579
|
+
<!-- Hue Slider -->
|
|
7580
|
+
<div
|
|
7581
|
+
#hueTrack
|
|
7582
|
+
class="relative h-3 w-full rounded-full cursor-pointer mt-3"
|
|
7583
|
+
style="background: linear-gradient(to right, hsl(0,100%,50%), hsl(60,100%,50%), hsl(120,100%,50%), hsl(180,100%,50%), hsl(240,100%,50%), hsl(300,100%,50%), hsl(360,100%,50%))"
|
|
7584
|
+
(mousedown)="onHueDown($event)"
|
|
7585
|
+
(touchstart)="onHueTouch($event)"
|
|
7586
|
+
>
|
|
7587
|
+
<div
|
|
7588
|
+
class="absolute top-1/2 h-4 w-4 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white shadow-md pointer-events-none"
|
|
7589
|
+
[style.left.%]="hsv().h / 360 * 100"
|
|
7590
|
+
[style.backgroundColor]="'hsl(' + hsv().h + ', 100%, 50%)'"
|
|
7591
|
+
></div>
|
|
7592
|
+
</div>
|
|
7593
|
+
|
|
7594
|
+
<!-- Input + Format + Copy + Actions -->
|
|
7595
|
+
@if (showInput()) {
|
|
7596
|
+
<div class="mt-3 flex items-center gap-1.5">
|
|
7597
|
+
<div
|
|
7598
|
+
class="h-8 w-8 rounded-sm border border-border shrink-0"
|
|
7599
|
+
[style.backgroundColor]="displayValue()"
|
|
7600
|
+
></div>
|
|
7601
|
+
<input
|
|
7602
|
+
class="flex-1 min-w-0 h-8 rounded-sm border border-border bg-background px-2 text-xs font-mono focus:outline-none focus:ring-1 focus:ring-ring"
|
|
7603
|
+
[value]="inputValue()"
|
|
7604
|
+
(input)="onInputChange($event)"
|
|
7605
|
+
(blur)="commitInput()"
|
|
7606
|
+
(keydown.enter)="commitInput()"
|
|
7607
|
+
/>
|
|
7608
|
+
<button
|
|
7609
|
+
type="button"
|
|
7610
|
+
class="h-8 px-1.5 rounded-sm border border-border text-[10px] font-semibold uppercase hover:bg-accent transition-colors shrink-0"
|
|
7611
|
+
(click)="cycleFormat()"
|
|
7612
|
+
title="Switch format"
|
|
7613
|
+
>
|
|
7614
|
+
{{ currentFormat() }}
|
|
7615
|
+
</button>
|
|
7616
|
+
<button
|
|
7617
|
+
type="button"
|
|
7618
|
+
class="h-8 w-8 inline-flex items-center justify-center rounded-sm border border-border hover:bg-accent transition-colors shrink-0"
|
|
7619
|
+
(click)="copyColor()"
|
|
7620
|
+
[title]="copied() ? 'Copied!' : 'Copy color'"
|
|
7621
|
+
>
|
|
7622
|
+
@if (copied()) {
|
|
7623
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>
|
|
7624
|
+
} @else {
|
|
7625
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="14" x="8" y="8" rx="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>
|
|
7626
|
+
}
|
|
7627
|
+
</button>
|
|
7628
|
+
</div>
|
|
7629
|
+
<!-- Secondary actions row -->
|
|
7630
|
+
<div class="mt-2 flex items-center gap-1.5">
|
|
7631
|
+
@if (showEyeDropper() && hasEyeDropper) {
|
|
7632
|
+
<button
|
|
7633
|
+
type="button"
|
|
7634
|
+
class="h-7 px-2 inline-flex items-center gap-1.5 rounded-sm border border-border text-xs text-muted-foreground hover:bg-accent hover:text-foreground transition-colors"
|
|
7635
|
+
(click)="pickFromScreen()"
|
|
7636
|
+
title="Pick from screen"
|
|
7637
|
+
>
|
|
7638
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m2 22 1-1h3l9-9"/><path d="M3 21v-3l9-9"/><path d="m15 6 3.4-3.4a2.1 2.1 0 1 1 3 3L18 9l.4.4a2.1 2.1 0 1 1-3 3l-3.8-3.8a2.1 2.1 0 1 1 3-3l.4.4Z"/></svg>
|
|
7639
|
+
Pick
|
|
7640
|
+
</button>
|
|
7641
|
+
}
|
|
7642
|
+
@if (showFavorites()) {
|
|
7643
|
+
<button
|
|
7644
|
+
type="button"
|
|
7645
|
+
class="h-7 px-2 inline-flex items-center gap-1.5 rounded-sm border border-border text-xs text-muted-foreground hover:bg-accent hover:text-foreground transition-colors"
|
|
7646
|
+
(click)="addFavorite()"
|
|
7647
|
+
title="Save to favorites"
|
|
7648
|
+
>
|
|
7649
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"/></svg>
|
|
7650
|
+
Save
|
|
7651
|
+
</button>
|
|
7652
|
+
}
|
|
7653
|
+
</div>
|
|
7654
|
+
}
|
|
7655
|
+
|
|
7656
|
+
<!-- Presets -->
|
|
7657
|
+
@for (preset of presets(); track $index) {
|
|
7658
|
+
<div class="mt-3">
|
|
7659
|
+
@if (preset.label) {
|
|
7660
|
+
<p class="text-xs font-medium text-muted-foreground mb-1.5">{{ preset.label }}</p>
|
|
7661
|
+
}
|
|
7662
|
+
<div class="flex flex-wrap gap-1.5">
|
|
7663
|
+
@for (color of preset.colors; track color) {
|
|
7664
|
+
<button
|
|
7665
|
+
type="button"
|
|
7666
|
+
class="h-6 w-6 rounded-sm border border-border hover:scale-110 transition-transform cursor-pointer"
|
|
7667
|
+
[style.backgroundColor]="color"
|
|
7668
|
+
[title]="color"
|
|
7669
|
+
(click)="selectColor(color)"
|
|
7670
|
+
></button>
|
|
7671
|
+
}
|
|
7672
|
+
</div>
|
|
7673
|
+
</div>
|
|
7674
|
+
}
|
|
7675
|
+
|
|
7676
|
+
<!-- Favorites -->
|
|
7677
|
+
@if (showFavorites() && favorites().length > 0) {
|
|
7678
|
+
<div class="mt-3">
|
|
7679
|
+
<p class="text-xs font-medium text-muted-foreground mb-1.5">Favorites</p>
|
|
7680
|
+
<div class="flex flex-wrap gap-1.5">
|
|
7681
|
+
@for (fav of favorites(); track fav) {
|
|
7682
|
+
<div class="relative group">
|
|
7683
|
+
<button
|
|
7684
|
+
type="button"
|
|
7685
|
+
class="h-6 w-6 rounded-sm border border-border hover:scale-110 transition-transform cursor-pointer"
|
|
7686
|
+
[style.backgroundColor]="fav"
|
|
7687
|
+
[title]="fav"
|
|
7688
|
+
(click)="selectColor(fav)"
|
|
7689
|
+
></button>
|
|
7690
|
+
<button
|
|
7691
|
+
type="button"
|
|
7692
|
+
class="absolute -top-1 -right-1 h-3.5 w-3.5 rounded-full bg-destructive text-destructive-foreground text-[8px] leading-none items-center justify-center hidden group-hover:inline-flex"
|
|
7693
|
+
(click)="removeFavorite(fav); $event.stopPropagation()"
|
|
7694
|
+
>×</button>
|
|
7695
|
+
</div>
|
|
7696
|
+
}
|
|
7697
|
+
</div>
|
|
7698
|
+
</div>
|
|
7699
|
+
}
|
|
7700
|
+
</div>
|
|
7701
|
+
}
|
|
7702
|
+
`,
|
|
7703
|
+
}]
|
|
7704
|
+
}], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], presets: [{ type: i0.Input, args: [{ isSignal: true, alias: "presets", required: false }] }], showInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "showInput", required: false }] }], showEyeDropper: [{ type: i0.Input, args: [{ isSignal: true, alias: "showEyeDropper", required: false }] }], showFavorites: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFavorites", required: false }] }], inline: [{ type: i0.Input, args: [{ isSignal: true, alias: "inline", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], colorChange: [{ type: i0.Output, args: ["colorChange"] }], formatChange: [{ type: i0.Output, args: ["formatChange"] }], triggerRef: [{ type: i0.ViewChild, args: ['triggerEl', { isSignal: true }] }], panelRef: [{ type: i0.ViewChild, args: ['panelEl', { isSignal: true }] }], satPanelRef: [{ type: i0.ViewChild, args: ['satPanel', { isSignal: true }] }], hueTrackRef: [{ type: i0.ViewChild, args: ['hueTrack', { isSignal: true }] }], onDocumentClick: [{
|
|
7705
|
+
type: HostListener,
|
|
7706
|
+
args: ['document:click', ['$event']]
|
|
7707
|
+
}], onEscape: [{
|
|
7708
|
+
type: HostListener,
|
|
7709
|
+
args: ['keydown.escape']
|
|
7710
|
+
}] } });
|
|
7711
|
+
|
|
7712
|
+
const otpCellVariants = cva('text-center font-mono font-semibold border border-border bg-background rounded-md transition-all focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed', {
|
|
7713
|
+
variants: {
|
|
7714
|
+
size: {
|
|
7715
|
+
sm: 'h-9 w-9 text-sm',
|
|
7716
|
+
md: 'h-11 w-11 text-lg',
|
|
7717
|
+
lg: 'h-14 w-14 text-2xl',
|
|
7718
|
+
},
|
|
7719
|
+
},
|
|
7720
|
+
defaultVariants: { size: 'md' },
|
|
7721
|
+
});
|
|
7722
|
+
|
|
7723
|
+
class SnyOtpInputComponent {
|
|
7724
|
+
// Public API
|
|
7725
|
+
length = input(6, ...(ngDevMode ? [{ debugName: "length" }] : /* istanbul ignore next */ []));
|
|
7726
|
+
type = input('number', ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
|
|
7727
|
+
size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
7728
|
+
disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
7729
|
+
mask = input(false, ...(ngDevMode ? [{ debugName: "mask" }] : /* istanbul ignore next */ []));
|
|
7730
|
+
autoFocus = input(true, ...(ngDevMode ? [{ debugName: "autoFocus" }] : /* istanbul ignore next */ []));
|
|
7731
|
+
placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
7732
|
+
separator = input(null, ...(ngDevMode ? [{ debugName: "separator" }] : /* istanbul ignore next */ []));
|
|
7733
|
+
status = input('idle', ...(ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []));
|
|
7734
|
+
value = model('', ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
7735
|
+
completed = output();
|
|
7736
|
+
// Internal state
|
|
7737
|
+
digits = linkedSignal(() => Array(this.length()).fill(''), ...(ngDevMode ? [{ debugName: "digits" }] : /* istanbul ignore next */ []));
|
|
7738
|
+
focusedIndex = signal(-1, ...(ngDevMode ? [{ debugName: "focusedIndex" }] : /* istanbul ignore next */ []));
|
|
7739
|
+
inputRefs = viewChildren('inputEl', ...(ngDevMode ? [{ debugName: "inputRefs" }] : /* istanbul ignore next */ []));
|
|
7740
|
+
_disabledByCva = signal(false, ...(ngDevMode ? [{ debugName: "_disabledByCva" }] : /* istanbul ignore next */ []));
|
|
7741
|
+
isDisabled = computed(() => this.disabled() || this._disabledByCva() || this.status() === 'loading', ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
|
|
7742
|
+
_onChange = () => { };
|
|
7743
|
+
_onTouched = () => { };
|
|
7744
|
+
// Computed
|
|
7745
|
+
fullValue = computed(() => this.digits().join(''), ...(ngDevMode ? [{ debugName: "fullValue" }] : /* istanbul ignore next */ []));
|
|
7746
|
+
isComplete = computed(() => {
|
|
7747
|
+
const d = this.digits();
|
|
7748
|
+
return d.length === this.length() && d.every((c) => c !== '');
|
|
7749
|
+
}, ...(ngDevMode ? [{ debugName: "isComplete" }] : /* istanbul ignore next */ []));
|
|
7750
|
+
constructor() {
|
|
7751
|
+
// Sync value → digits when value changes externally (e.g. reset)
|
|
7752
|
+
effect(() => {
|
|
7753
|
+
const val = this.value();
|
|
7754
|
+
untracked(() => {
|
|
7755
|
+
const chars = val.split('').slice(0, this.length());
|
|
7756
|
+
const padded = [...chars, ...Array(this.length() - chars.length).fill('')];
|
|
7757
|
+
const current = this.digits();
|
|
7758
|
+
if (padded.join('') !== current.join('')) {
|
|
7759
|
+
this.digits.set(padded);
|
|
7760
|
+
}
|
|
7761
|
+
});
|
|
7762
|
+
});
|
|
7763
|
+
afterNextRender(() => {
|
|
7764
|
+
if (this.autoFocus()) {
|
|
7765
|
+
this.focusInput(0);
|
|
7766
|
+
}
|
|
7767
|
+
});
|
|
7768
|
+
}
|
|
7769
|
+
// CVA
|
|
7770
|
+
writeValue(val) {
|
|
7771
|
+
const str = val ?? '';
|
|
7772
|
+
this.value.set(str);
|
|
7773
|
+
const chars = str.split('').slice(0, this.length());
|
|
7774
|
+
const padded = [...chars, ...Array(this.length() - chars.length).fill('')];
|
|
7775
|
+
this.digits.set(padded);
|
|
7776
|
+
}
|
|
7777
|
+
registerOnChange(fn) {
|
|
7778
|
+
this._onChange = fn;
|
|
7779
|
+
}
|
|
7780
|
+
registerOnTouched(fn) {
|
|
7781
|
+
this._onTouched = fn;
|
|
7782
|
+
}
|
|
7783
|
+
setDisabledState(isDisabled) {
|
|
7784
|
+
this._disabledByCva.set(isDisabled);
|
|
7785
|
+
}
|
|
7786
|
+
// Cell class
|
|
7787
|
+
cellClass(index) {
|
|
7788
|
+
const isFocused = this.focusedIndex() === index;
|
|
7789
|
+
const hasValue = this.digits()[index] !== '';
|
|
7790
|
+
const st = this.status();
|
|
7791
|
+
return cn(otpCellVariants({ size: this.size() }), st === 'idle' && isFocused && 'border-primary ring-2 ring-ring', st === 'idle' && hasValue && !isFocused && 'border-primary/50', st === 'loading' && 'border-muted-foreground/30 opacity-70', st === 'success' && 'border-green-500 bg-green-500/5', st === 'error' && 'border-destructive bg-destructive/5 animate-shake');
|
|
7792
|
+
}
|
|
7793
|
+
// Input handler
|
|
7794
|
+
onInput(event, index) {
|
|
7795
|
+
const input = event.target;
|
|
7796
|
+
const char = input.value.slice(-1);
|
|
7797
|
+
if (!this.isValidChar(char)) {
|
|
7798
|
+
input.value = this.digits()[index];
|
|
7799
|
+
return;
|
|
7800
|
+
}
|
|
7801
|
+
this.setDigit(index, char);
|
|
7802
|
+
if (index < this.length() - 1) {
|
|
7803
|
+
this.focusInput(index + 1);
|
|
7804
|
+
}
|
|
7805
|
+
this.emitValue();
|
|
7806
|
+
}
|
|
7807
|
+
// Keyboard handler
|
|
7808
|
+
onKeydown(event, index) {
|
|
7809
|
+
switch (event.key) {
|
|
7810
|
+
case 'Backspace':
|
|
7811
|
+
event.preventDefault();
|
|
7812
|
+
if (this.digits()[index] !== '') {
|
|
7813
|
+
this.setDigit(index, '');
|
|
7814
|
+
this.emitValue();
|
|
7815
|
+
}
|
|
7816
|
+
else if (index > 0) {
|
|
7817
|
+
this.setDigit(index - 1, '');
|
|
7818
|
+
this.focusInput(index - 1);
|
|
7819
|
+
this.emitValue();
|
|
7820
|
+
}
|
|
7821
|
+
break;
|
|
7822
|
+
case 'Delete':
|
|
7823
|
+
event.preventDefault();
|
|
7824
|
+
this.setDigit(index, '');
|
|
7825
|
+
this.emitValue();
|
|
7826
|
+
break;
|
|
7827
|
+
case 'ArrowLeft':
|
|
7828
|
+
event.preventDefault();
|
|
7829
|
+
if (index > 0)
|
|
7830
|
+
this.focusInput(index - 1);
|
|
7831
|
+
break;
|
|
7832
|
+
case 'ArrowRight':
|
|
7833
|
+
event.preventDefault();
|
|
7834
|
+
if (index < this.length() - 1)
|
|
7835
|
+
this.focusInput(index + 1);
|
|
7836
|
+
break;
|
|
7837
|
+
case 'Home':
|
|
7838
|
+
event.preventDefault();
|
|
7839
|
+
this.focusInput(0);
|
|
7840
|
+
break;
|
|
7841
|
+
case 'End':
|
|
7842
|
+
event.preventDefault();
|
|
7843
|
+
this.focusInput(this.length() - 1);
|
|
7844
|
+
break;
|
|
7845
|
+
}
|
|
7846
|
+
}
|
|
7847
|
+
// Paste handler
|
|
7848
|
+
onPaste(event, index) {
|
|
7849
|
+
event.preventDefault();
|
|
7850
|
+
const text = event.clipboardData?.getData('text') ?? '';
|
|
7851
|
+
const chars = text.split('').filter((c) => this.isValidChar(c));
|
|
7852
|
+
if (chars.length === 0)
|
|
7853
|
+
return;
|
|
7854
|
+
const newDigits = [...this.digits()];
|
|
7855
|
+
let lastFilledIndex = index;
|
|
7856
|
+
for (let i = 0; i < chars.length && index + i < this.length(); i++) {
|
|
7857
|
+
newDigits[index + i] = chars[i];
|
|
7858
|
+
lastFilledIndex = index + i;
|
|
7859
|
+
}
|
|
7860
|
+
this.digits.set(newDigits);
|
|
7861
|
+
const nextIndex = Math.min(lastFilledIndex + 1, this.length() - 1);
|
|
7862
|
+
this.focusInput(nextIndex);
|
|
7863
|
+
this.emitValue();
|
|
7864
|
+
}
|
|
7865
|
+
// Blur
|
|
7866
|
+
onBlur() {
|
|
7867
|
+
this.focusedIndex.set(-1);
|
|
7868
|
+
this._onTouched();
|
|
7869
|
+
}
|
|
7870
|
+
// Helpers
|
|
7871
|
+
setDigit(index, char) {
|
|
7872
|
+
this.digits.update((d) => {
|
|
7873
|
+
const next = [...d];
|
|
7874
|
+
next[index] = char;
|
|
7875
|
+
return next;
|
|
7876
|
+
});
|
|
7877
|
+
}
|
|
7878
|
+
emitValue() {
|
|
7879
|
+
const val = this.fullValue();
|
|
7880
|
+
this.value.set(val);
|
|
7881
|
+
this._onChange(val);
|
|
7882
|
+
if (this.isComplete()) {
|
|
7883
|
+
this.completed.emit(val);
|
|
7884
|
+
}
|
|
7885
|
+
}
|
|
7886
|
+
focusInput(index) {
|
|
7887
|
+
const refs = this.inputRefs();
|
|
7888
|
+
if (refs[index]) {
|
|
7889
|
+
const el = refs[index].nativeElement;
|
|
7890
|
+
el.focus();
|
|
7891
|
+
el.select();
|
|
7892
|
+
}
|
|
7893
|
+
}
|
|
7894
|
+
isValidChar(char) {
|
|
7895
|
+
if (!char || char.length !== 1)
|
|
7896
|
+
return false;
|
|
7897
|
+
if (this.type() === 'number')
|
|
7898
|
+
return /^[0-9]$/.test(char);
|
|
7899
|
+
return /^[a-zA-Z0-9]$/.test(char);
|
|
7900
|
+
}
|
|
7901
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyOtpInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7902
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: SnyOtpInputComponent, isStandalone: true, selector: "sny-otp-input", inputs: { length: { classPropertyName: "length", publicName: "length", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, mask: { classPropertyName: "mask", publicName: "mask", isSignal: true, isRequired: false, transformFunction: null }, autoFocus: { classPropertyName: "autoFocus", publicName: "autoFocus", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, separator: { classPropertyName: "separator", publicName: "separator", isSignal: true, isRequired: false, transformFunction: null }, status: { classPropertyName: "status", publicName: "status", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", completed: "completed" }, providers: [
|
|
7903
|
+
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SnyOtpInputComponent), multi: true },
|
|
7904
|
+
], viewQueries: [{ propertyName: "inputRefs", predicate: ["inputEl"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
7905
|
+
<div
|
|
7906
|
+
role="group"
|
|
7907
|
+
[attr.aria-label]="'OTP input, ' + length() + ' digits'"
|
|
7908
|
+
class="flex items-center gap-2"
|
|
7909
|
+
>
|
|
7910
|
+
@for (digit of digits(); track $index; let i = $index) {
|
|
7911
|
+
@if (separator() !== null && i === separator() && i > 0) {
|
|
7912
|
+
<span class="text-muted-foreground text-lg select-none" aria-hidden="true">—</span>
|
|
7913
|
+
}
|
|
7914
|
+
<input
|
|
7915
|
+
#inputEl
|
|
7916
|
+
[type]="mask() ? 'password' : 'text'"
|
|
7917
|
+
[inputMode]="type() === 'number' ? 'numeric' : 'text'"
|
|
7918
|
+
[attr.pattern]="type() === 'number' ? '[0-9]' : '[a-zA-Z0-9]'"
|
|
7919
|
+
maxlength="1"
|
|
7920
|
+
autocomplete="one-time-code"
|
|
7921
|
+
[value]="digit"
|
|
7922
|
+
[placeholder]="placeholder()"
|
|
7923
|
+
[disabled]="isDisabled()"
|
|
7924
|
+
[class]="cellClass(i)"
|
|
7925
|
+
[attr.aria-label]="'Digit ' + (i + 1) + ' of ' + length()"
|
|
7926
|
+
(input)="onInput($event, i)"
|
|
7927
|
+
(keydown)="onKeydown($event, i)"
|
|
7928
|
+
(paste)="onPaste($event, i)"
|
|
7929
|
+
(focus)="focusedIndex.set(i)"
|
|
7930
|
+
(blur)="onBlur()"
|
|
7931
|
+
/>
|
|
7932
|
+
}
|
|
7933
|
+
</div>
|
|
7934
|
+
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
7935
|
+
}
|
|
7936
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyOtpInputComponent, decorators: [{
|
|
7937
|
+
type: Component,
|
|
7938
|
+
args: [{
|
|
7939
|
+
selector: 'sny-otp-input',
|
|
7940
|
+
standalone: true,
|
|
7941
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
7942
|
+
providers: [
|
|
7943
|
+
{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => SnyOtpInputComponent), multi: true },
|
|
7944
|
+
],
|
|
7945
|
+
template: `
|
|
7946
|
+
<div
|
|
7947
|
+
role="group"
|
|
7948
|
+
[attr.aria-label]="'OTP input, ' + length() + ' digits'"
|
|
7949
|
+
class="flex items-center gap-2"
|
|
7950
|
+
>
|
|
7951
|
+
@for (digit of digits(); track $index; let i = $index) {
|
|
7952
|
+
@if (separator() !== null && i === separator() && i > 0) {
|
|
7953
|
+
<span class="text-muted-foreground text-lg select-none" aria-hidden="true">—</span>
|
|
7954
|
+
}
|
|
7955
|
+
<input
|
|
7956
|
+
#inputEl
|
|
7957
|
+
[type]="mask() ? 'password' : 'text'"
|
|
7958
|
+
[inputMode]="type() === 'number' ? 'numeric' : 'text'"
|
|
7959
|
+
[attr.pattern]="type() === 'number' ? '[0-9]' : '[a-zA-Z0-9]'"
|
|
7960
|
+
maxlength="1"
|
|
7961
|
+
autocomplete="one-time-code"
|
|
7962
|
+
[value]="digit"
|
|
7963
|
+
[placeholder]="placeholder()"
|
|
7964
|
+
[disabled]="isDisabled()"
|
|
7965
|
+
[class]="cellClass(i)"
|
|
7966
|
+
[attr.aria-label]="'Digit ' + (i + 1) + ' of ' + length()"
|
|
7967
|
+
(input)="onInput($event, i)"
|
|
7968
|
+
(keydown)="onKeydown($event, i)"
|
|
7969
|
+
(paste)="onPaste($event, i)"
|
|
7970
|
+
(focus)="focusedIndex.set(i)"
|
|
7971
|
+
(blur)="onBlur()"
|
|
7972
|
+
/>
|
|
7973
|
+
}
|
|
7974
|
+
</div>
|
|
7975
|
+
`,
|
|
7976
|
+
}]
|
|
7977
|
+
}], ctorParameters: () => [], propDecorators: { length: [{ type: i0.Input, args: [{ isSignal: true, alias: "length", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], mask: [{ type: i0.Input, args: [{ isSignal: true, alias: "mask", required: false }] }], autoFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoFocus", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], separator: [{ type: i0.Input, args: [{ isSignal: true, alias: "separator", required: false }] }], status: [{ type: i0.Input, args: [{ isSignal: true, alias: "status", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], completed: [{ type: i0.Output, args: ["completed"] }], inputRefs: [{ type: i0.ViewChildren, args: ['inputEl', { isSignal: true }] }] } });
|
|
7978
|
+
|
|
7979
|
+
class SnyCommandPaletteComponent {
|
|
7980
|
+
config = inject(SNY_DIALOG_DATA);
|
|
7981
|
+
dialogRef = inject(DialogRef);
|
|
7982
|
+
searchInput = viewChild('searchInput', ...(ngDevMode ? [{ debugName: "searchInput" }] : /* istanbul ignore next */ []));
|
|
7983
|
+
query = signal('', ...(ngDevMode ? [{ debugName: "query" }] : /* istanbul ignore next */ []));
|
|
7984
|
+
activeIndex = signal(0, ...(ngDevMode ? [{ debugName: "activeIndex" }] : /* istanbul ignore next */ []));
|
|
7985
|
+
filteredGroups = computed(() => {
|
|
7986
|
+
const q = this.query().toLowerCase().trim();
|
|
7987
|
+
const commands = this.config.commands.filter((cmd) => {
|
|
7988
|
+
if (cmd.disabled)
|
|
7989
|
+
return false;
|
|
7990
|
+
if (!q)
|
|
7991
|
+
return true;
|
|
7992
|
+
return (cmd.label.toLowerCase().includes(q) ||
|
|
7993
|
+
cmd.description?.toLowerCase().includes(q) ||
|
|
7994
|
+
cmd.keywords?.some((k) => k.toLowerCase().includes(q)));
|
|
7995
|
+
});
|
|
7996
|
+
const groups = new Map();
|
|
7997
|
+
for (const cmd of commands) {
|
|
7998
|
+
const group = cmd.group ?? '';
|
|
7999
|
+
if (!groups.has(group))
|
|
8000
|
+
groups.set(group, []);
|
|
8001
|
+
groups.get(group).push(cmd);
|
|
8002
|
+
}
|
|
8003
|
+
return [...groups.entries()].map(([name, cmds]) => ({ name, commands: cmds }));
|
|
8004
|
+
}, ...(ngDevMode ? [{ debugName: "filteredGroups" }] : /* istanbul ignore next */ []));
|
|
8005
|
+
flatResults = computed(() => this.filteredGroups().flatMap((g) => g.commands), ...(ngDevMode ? [{ debugName: "flatResults" }] : /* istanbul ignore next */ []));
|
|
8006
|
+
constructor() {
|
|
8007
|
+
afterNextRender(() => {
|
|
8008
|
+
this.searchInput()?.nativeElement.focus();
|
|
8009
|
+
});
|
|
8010
|
+
}
|
|
8011
|
+
onQueryChange(value) {
|
|
8012
|
+
this.query.set(value);
|
|
8013
|
+
this.activeIndex.set(0);
|
|
8014
|
+
}
|
|
8015
|
+
flatIndex(cmd) {
|
|
8016
|
+
return this.flatResults().indexOf(cmd);
|
|
8017
|
+
}
|
|
8018
|
+
execute(cmd) {
|
|
8019
|
+
this.dialogRef.close();
|
|
8020
|
+
cmd.action();
|
|
8021
|
+
}
|
|
8022
|
+
onKeydown(event) {
|
|
8023
|
+
const results = this.flatResults();
|
|
8024
|
+
const len = results.length;
|
|
8025
|
+
if (len === 0 && event.key !== 'Escape')
|
|
8026
|
+
return;
|
|
8027
|
+
switch (event.key) {
|
|
8028
|
+
case 'ArrowDown':
|
|
8029
|
+
event.preventDefault();
|
|
8030
|
+
this.activeIndex.update((i) => (i + 1) % len);
|
|
8031
|
+
this.scrollActiveIntoView();
|
|
8032
|
+
break;
|
|
8033
|
+
case 'ArrowUp':
|
|
8034
|
+
event.preventDefault();
|
|
8035
|
+
this.activeIndex.update((i) => (i - 1 + len) % len);
|
|
8036
|
+
this.scrollActiveIntoView();
|
|
8037
|
+
break;
|
|
8038
|
+
case 'Enter':
|
|
8039
|
+
event.preventDefault();
|
|
8040
|
+
const active = results[this.activeIndex()];
|
|
8041
|
+
if (active)
|
|
8042
|
+
this.execute(active);
|
|
8043
|
+
break;
|
|
8044
|
+
}
|
|
8045
|
+
}
|
|
8046
|
+
scrollActiveIntoView() {
|
|
8047
|
+
requestAnimationFrame(() => {
|
|
8048
|
+
const el = document.querySelector(`[data-cmd-idx="${this.activeIndex()}"]`);
|
|
8049
|
+
el?.scrollIntoView({ block: 'nearest' });
|
|
8050
|
+
});
|
|
8051
|
+
}
|
|
8052
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyCommandPaletteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
8053
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: SnyCommandPaletteComponent, isStandalone: true, selector: "sny-command-palette", host: { listeners: { "keydown": "onKeydown($event)" }, classAttribute: "block" }, viewQueries: [{ propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
8054
|
+
<!-- Search -->
|
|
8055
|
+
<div class="flex items-center gap-2 border-b border-border px-4 py-3">
|
|
8056
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-muted-foreground shrink-0"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
|
|
8057
|
+
<input
|
|
8058
|
+
#searchInput
|
|
8059
|
+
snyInput
|
|
8060
|
+
class="border-none shadow-none h-8 px-0 focus-visible:ring-0 focus-visible:ring-offset-0"
|
|
8061
|
+
[placeholder]="config.placeholder ?? 'Type a command...'"
|
|
8062
|
+
[value]="query()"
|
|
8063
|
+
(input)="onQueryChange(searchInput.value)"
|
|
8064
|
+
/>
|
|
8065
|
+
<kbd class="rounded border border-border bg-muted px-1.5 py-0.5 font-mono text-[10px] text-muted-foreground shrink-0">Esc</kbd>
|
|
8066
|
+
</div>
|
|
8067
|
+
|
|
8068
|
+
<!-- Results -->
|
|
8069
|
+
<div class="max-h-[300px] overflow-y-auto p-2 sny-scrollbar">
|
|
8070
|
+
@if (flatResults().length === 0) {
|
|
8071
|
+
<p class="py-6 text-center text-sm text-muted-foreground">
|
|
8072
|
+
{{ config.emptyText ?? 'No results found.' }}
|
|
8073
|
+
</p>
|
|
8074
|
+
} @else {
|
|
8075
|
+
@for (group of filteredGroups(); track group.name) {
|
|
8076
|
+
@if (group.name) {
|
|
8077
|
+
<p class="px-2 py-1.5 text-xs font-semibold text-muted-foreground uppercase tracking-wider">{{ group.name }}</p>
|
|
8078
|
+
}
|
|
8079
|
+
@for (cmd of group.commands; track cmd.id) {
|
|
8080
|
+
@let idx = flatIndex(cmd);
|
|
8081
|
+
<button
|
|
8082
|
+
[attr.data-cmd-idx]="idx"
|
|
8083
|
+
[class]="
|
|
8084
|
+
'w-full text-left rounded-sm px-3 py-2 text-sm transition-colors flex items-center gap-3 cursor-pointer ' +
|
|
8085
|
+
(idx === activeIndex()
|
|
8086
|
+
? 'bg-accent text-accent-foreground'
|
|
8087
|
+
: 'text-foreground hover:bg-accent/50')
|
|
8088
|
+
"
|
|
8089
|
+
(click)="execute(cmd)"
|
|
8090
|
+
(mouseenter)="activeIndex.set(idx)"
|
|
8091
|
+
>
|
|
8092
|
+
@if (cmd.icon) {
|
|
8093
|
+
<span class="shrink-0 w-5 text-center text-muted-foreground" [innerHTML]="cmd.icon"></span>
|
|
8094
|
+
}
|
|
8095
|
+
<div class="flex-1 min-w-0">
|
|
8096
|
+
<span class="block truncate font-medium">{{ cmd.label }}</span>
|
|
8097
|
+
@if (cmd.description) {
|
|
8098
|
+
<span class="block text-xs text-muted-foreground truncate">{{ cmd.description }}</span>
|
|
8099
|
+
}
|
|
8100
|
+
</div>
|
|
8101
|
+
@if (cmd.shortcut) {
|
|
8102
|
+
<kbd class="rounded border border-border bg-muted px-1.5 py-0.5 font-mono text-[10px] text-muted-foreground shrink-0">{{ cmd.shortcut }}</kbd>
|
|
8103
|
+
}
|
|
8104
|
+
</button>
|
|
8105
|
+
}
|
|
8106
|
+
}
|
|
8107
|
+
}
|
|
8108
|
+
</div>
|
|
8109
|
+
|
|
8110
|
+
<!-- Footer -->
|
|
8111
|
+
<div class="border-t border-border px-4 py-2">
|
|
8112
|
+
<div class="flex items-center gap-3 text-xs text-muted-foreground">
|
|
8113
|
+
<span class="flex items-center gap-1">
|
|
8114
|
+
<kbd class="rounded border border-border bg-muted px-1 py-0.5 font-mono text-[10px]">↑↓</kbd>
|
|
8115
|
+
Navigate
|
|
8116
|
+
</span>
|
|
8117
|
+
<span class="flex items-center gap-1">
|
|
8118
|
+
<kbd class="rounded border border-border bg-muted px-1 py-0.5 font-mono text-[10px]">↵</kbd>
|
|
8119
|
+
Execute
|
|
8120
|
+
</span>
|
|
8121
|
+
<span class="flex items-center gap-1">
|
|
8122
|
+
<kbd class="rounded border border-border bg-muted px-1 py-0.5 font-mono text-[10px]">Esc</kbd>
|
|
8123
|
+
Close
|
|
8124
|
+
</span>
|
|
8125
|
+
</div>
|
|
8126
|
+
</div>
|
|
8127
|
+
`, isInline: true, styles: [":host{display:block;background-color:var(--sny-background);border:1px solid var(--sny-border);border-radius:.5rem;box-shadow:0 25px 50px -12px #00000040;overflow:hidden}\n"], dependencies: [{ kind: "directive", type: SnyInputDirective, selector: "input[snyInput], textarea[snyInput]", inputs: ["variant", "inputSize", "class", "ariaDescribedBy"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
8128
|
+
}
|
|
8129
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyCommandPaletteComponent, decorators: [{
|
|
8130
|
+
type: Component,
|
|
8131
|
+
args: [{ selector: 'sny-command-palette', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [SnyInputDirective], host: {
|
|
8132
|
+
'(keydown)': 'onKeydown($event)',
|
|
8133
|
+
'class': 'block',
|
|
8134
|
+
}, template: `
|
|
8135
|
+
<!-- Search -->
|
|
8136
|
+
<div class="flex items-center gap-2 border-b border-border px-4 py-3">
|
|
8137
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-muted-foreground shrink-0"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>
|
|
8138
|
+
<input
|
|
8139
|
+
#searchInput
|
|
8140
|
+
snyInput
|
|
8141
|
+
class="border-none shadow-none h-8 px-0 focus-visible:ring-0 focus-visible:ring-offset-0"
|
|
8142
|
+
[placeholder]="config.placeholder ?? 'Type a command...'"
|
|
8143
|
+
[value]="query()"
|
|
8144
|
+
(input)="onQueryChange(searchInput.value)"
|
|
8145
|
+
/>
|
|
8146
|
+
<kbd class="rounded border border-border bg-muted px-1.5 py-0.5 font-mono text-[10px] text-muted-foreground shrink-0">Esc</kbd>
|
|
8147
|
+
</div>
|
|
8148
|
+
|
|
8149
|
+
<!-- Results -->
|
|
8150
|
+
<div class="max-h-[300px] overflow-y-auto p-2 sny-scrollbar">
|
|
8151
|
+
@if (flatResults().length === 0) {
|
|
8152
|
+
<p class="py-6 text-center text-sm text-muted-foreground">
|
|
8153
|
+
{{ config.emptyText ?? 'No results found.' }}
|
|
8154
|
+
</p>
|
|
8155
|
+
} @else {
|
|
8156
|
+
@for (group of filteredGroups(); track group.name) {
|
|
8157
|
+
@if (group.name) {
|
|
8158
|
+
<p class="px-2 py-1.5 text-xs font-semibold text-muted-foreground uppercase tracking-wider">{{ group.name }}</p>
|
|
8159
|
+
}
|
|
8160
|
+
@for (cmd of group.commands; track cmd.id) {
|
|
8161
|
+
@let idx = flatIndex(cmd);
|
|
8162
|
+
<button
|
|
8163
|
+
[attr.data-cmd-idx]="idx"
|
|
8164
|
+
[class]="
|
|
8165
|
+
'w-full text-left rounded-sm px-3 py-2 text-sm transition-colors flex items-center gap-3 cursor-pointer ' +
|
|
8166
|
+
(idx === activeIndex()
|
|
8167
|
+
? 'bg-accent text-accent-foreground'
|
|
8168
|
+
: 'text-foreground hover:bg-accent/50')
|
|
8169
|
+
"
|
|
8170
|
+
(click)="execute(cmd)"
|
|
8171
|
+
(mouseenter)="activeIndex.set(idx)"
|
|
8172
|
+
>
|
|
8173
|
+
@if (cmd.icon) {
|
|
8174
|
+
<span class="shrink-0 w-5 text-center text-muted-foreground" [innerHTML]="cmd.icon"></span>
|
|
8175
|
+
}
|
|
8176
|
+
<div class="flex-1 min-w-0">
|
|
8177
|
+
<span class="block truncate font-medium">{{ cmd.label }}</span>
|
|
8178
|
+
@if (cmd.description) {
|
|
8179
|
+
<span class="block text-xs text-muted-foreground truncate">{{ cmd.description }}</span>
|
|
8180
|
+
}
|
|
8181
|
+
</div>
|
|
8182
|
+
@if (cmd.shortcut) {
|
|
8183
|
+
<kbd class="rounded border border-border bg-muted px-1.5 py-0.5 font-mono text-[10px] text-muted-foreground shrink-0">{{ cmd.shortcut }}</kbd>
|
|
8184
|
+
}
|
|
8185
|
+
</button>
|
|
8186
|
+
}
|
|
8187
|
+
}
|
|
8188
|
+
}
|
|
8189
|
+
</div>
|
|
8190
|
+
|
|
8191
|
+
<!-- Footer -->
|
|
8192
|
+
<div class="border-t border-border px-4 py-2">
|
|
8193
|
+
<div class="flex items-center gap-3 text-xs text-muted-foreground">
|
|
8194
|
+
<span class="flex items-center gap-1">
|
|
8195
|
+
<kbd class="rounded border border-border bg-muted px-1 py-0.5 font-mono text-[10px]">↑↓</kbd>
|
|
8196
|
+
Navigate
|
|
8197
|
+
</span>
|
|
8198
|
+
<span class="flex items-center gap-1">
|
|
8199
|
+
<kbd class="rounded border border-border bg-muted px-1 py-0.5 font-mono text-[10px]">↵</kbd>
|
|
8200
|
+
Execute
|
|
8201
|
+
</span>
|
|
8202
|
+
<span class="flex items-center gap-1">
|
|
8203
|
+
<kbd class="rounded border border-border bg-muted px-1 py-0.5 font-mono text-[10px]">Esc</kbd>
|
|
8204
|
+
Close
|
|
8205
|
+
</span>
|
|
8206
|
+
</div>
|
|
8207
|
+
</div>
|
|
8208
|
+
`, styles: [":host{display:block;background-color:var(--sny-background);border:1px solid var(--sny-border);border-radius:.5rem;box-shadow:0 25px 50px -12px #00000040;overflow:hidden}\n"] }]
|
|
8209
|
+
}], ctorParameters: () => [], propDecorators: { searchInput: [{ type: i0.ViewChild, args: ['searchInput', { isSignal: true }] }] } });
|
|
8210
|
+
|
|
8211
|
+
class SnyCommandPaletteService {
|
|
8212
|
+
dialogService = inject(SnyDialogService);
|
|
8213
|
+
isOpen = false;
|
|
8214
|
+
open(config) {
|
|
8215
|
+
if (this.isOpen)
|
|
8216
|
+
return null;
|
|
8217
|
+
this.isOpen = true;
|
|
8218
|
+
const ref = this.dialogService.open(SnyCommandPaletteComponent, {
|
|
8219
|
+
width: config.width ?? '32rem',
|
|
8220
|
+
data: config,
|
|
8221
|
+
});
|
|
8222
|
+
ref.closed.subscribe(() => {
|
|
8223
|
+
this.isOpen = false;
|
|
8224
|
+
});
|
|
8225
|
+
return ref;
|
|
8226
|
+
}
|
|
8227
|
+
close() {
|
|
8228
|
+
if (this.isOpen) {
|
|
8229
|
+
this.dialogService.closeAll();
|
|
8230
|
+
}
|
|
8231
|
+
}
|
|
8232
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyCommandPaletteService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
8233
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyCommandPaletteService, providedIn: 'root' });
|
|
8234
|
+
}
|
|
8235
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SnyCommandPaletteService, decorators: [{
|
|
8236
|
+
type: Injectable,
|
|
8237
|
+
args: [{ providedIn: 'root' }]
|
|
8238
|
+
}] });
|
|
8239
|
+
|
|
6853
8240
|
class SnyValidatorDirective {
|
|
6854
8241
|
control = input(null, ...(ngDevMode ? [{ debugName: "control" }] : /* istanbul ignore next */ []));
|
|
6855
8242
|
state = input('default', ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
|
|
@@ -6909,5 +8296,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
6909
8296
|
* Generated bundle index. Do not edit.
|
|
6910
8297
|
*/
|
|
6911
8298
|
|
|
6912
|
-
export { SNY_ACCORDION, SNY_ACCORDION_ITEM, SNY_CAROUSEL, SNY_CHAT_BUBBLE, SNY_CONFIG, SNY_DIALOG_DATA, SNY_DRAWER, SNY_DROPDOWN, SNY_FAB, SNY_SHEET_DATA, SNY_STEPS, SNY_TABLE, SNY_TABS, SNY_TIMELINE, SnyAccordionContentDirective, SnyAccordionDirective, SnyAccordionItemDirective, SnyAccordionTriggerDirective, SnyAlertDescriptionDirective, SnyAlertDirective, SnyAlertTitleDirective, SnyAvatarComponent, SnyBadgeDirective, SnyBreadcrumbDirective, SnyBreadcrumbItemDirective, SnyBreadcrumbLinkDirective, SnyBreadcrumbListDirective, SnyBreadcrumbPageDirective, SnyBreadcrumbSeparatorDirective, SnyBulkActionsDefDirective, SnyButtonDirective, SnyButtonGroupDirective, SnyCalendarComponent, SnyCardContentDirective, SnyCardDescriptionDirective, SnyCardDirective, SnyCardFooterDirective, SnyCardHeaderDirective, SnyCardTitleDirective, SnyCarouselContentDirective, SnyCarouselDirective, SnyCarouselItemDirective, SnyCarouselNextDirective, SnyCarouselPrevDirective, SnyCellDefDirective, SnyChatBubbleAvatarDirective, SnyChatBubbleBodyDirective, SnyChatBubbleContentDirective, SnyChatBubbleDirective, SnyChatBubbleFooterDirective, SnyChatBubbleHeaderDirective, SnyCheckboxDirective, SnyComboboxComponent, SnyDataTableComponent, SnyDatePickerComponent, SnyDateRangePickerComponent, SnyDialogCloseDirective, SnyDialogContentDirective, SnyDialogDescriptionDirective, SnyDialogFooterDirective, SnyDialogHeaderDirective, SnyDialogRef, SnyDialogService, SnyDialogTitleDirective, SnyDiffComponent, SnyDividerComponent, SnyDockDirective, SnyDockItemDirective, SnyDrawerContentDirective, SnyDrawerLayoutComponent, SnyDrawerLayoutDirective, SnyDrawerSideDirective, SnyDropdownContentDirective, SnyDropdownDirective, SnyDropdownTriggerDirective, SnyFabActionDirective, SnyFabDirective, SnyFabTriggerDirective, SnyFieldsetContentDirective, SnyFieldsetDirective, SnyFieldsetLegendDirective, SnyFileInputComponent, SnyHeaderCellDefDirective, SnyIndicatorBadgeDirective, SnyIndicatorDirective, SnyInputDirective, SnyKbdDirective, SnyLabelDirective, SnyLinkDirective, SnyListDirective, SnyListItemActionDirective, SnyListItemContentDirective, SnyListItemDirective, SnyListItemIconDirective, SnyLoaderComponent, SnyMenuContentDirective, SnyMenuItemDirective, SnyMenuLabelDirective, SnyMenuSeparatorDirective, SnyNavbarBrandDirective, SnyNavbarContentDirective, SnyNavbarDirective, SnyNavbarEndDirective, SnyPaginationComponent, SnyProgressComponent, SnyRadialProgressComponent, SnyRadioDirective, SnyRatingComponent, SnyRowExpandDefDirective, SnySelectComponent, SnySheetCloseDirective, SnySheetContentDirective, SnySheetDescriptionDirective, SnySheetHeaderDirective, SnySheetRef, SnySheetService, SnySheetTitleDirective, SnySkeletonDirective, SnySliderComponent, SnyStatDescriptionDirective, SnyStatDirective, SnyStatFigureDirective, SnyStatTitleDirective, SnyStatValueDirective, SnyStatusDirective, SnyStepDirective, SnyStepsDirective, SnySwitchComponent, SnyTableBodyDirective, SnyTableCaptionDirective, SnyTableCellDirective, SnyTableDirective, SnyTableFooterDirective, SnyTableHeadDirective, SnyTableHeaderDirective, SnyTableRowDirective, SnyTabsContentDirective, SnyTabsDirective, SnyTabsListDirective, SnyTabsTriggerDirective, SnyTextareaDirective, SnyTimelineDirective, SnyTimelineEndDirective, SnyTimelineItemDirective, SnyTimelineMiddleDirective, SnyTimelineStartDirective, SnyToastService, SnyToasterComponent, SnyToggleDirective, SnyTooltipDirective, SnyValidatorDirective, SnyValidatorHintDirective, ThemeService, alertVariants, avatarVariants, badgeVariants, buttonGroupVariants, buttonVariants, cardVariants, checkboxVariants, cn, comboboxTriggerVariants, datePickerTriggerVariants, dividerVariants, dropdownContentVariants, dropdownItemVariants, fieldsetVariants, fileInputVariants, inputVariants, kbdVariants, labelVariants, linkVariants, loaderVariants, paginationItemVariants, progressBarVariants, progressTrackVariants, provideSonnyUI, radioVariants, ratingVariants, selectTriggerVariants, skeletonVariants, sliderTrackVariants, statusVariants, switchTrackVariants, tableCellVariants, tableVariants, tabsListVariants, tabsTriggerVariants, textareaVariants, toastVariants, toggleVariants, tooltipVariants };
|
|
8299
|
+
export { SNY_ACCORDION, SNY_ACCORDION_ITEM, SNY_CAROUSEL, SNY_CHAT_BUBBLE, SNY_CONFIG, SNY_DIALOG_DATA, SNY_DRAWER, SNY_DROPDOWN, SNY_FAB, SNY_SHEET_DATA, SNY_STEPS, SNY_TABLE, SNY_TABS, SNY_TIMELINE, SnyAccordionContentDirective, SnyAccordionDirective, SnyAccordionItemDirective, SnyAccordionTriggerDirective, SnyAlertDescriptionDirective, SnyAlertDirective, SnyAlertTitleDirective, SnyAvatarComponent, SnyBadgeDirective, SnyBreadcrumbDirective, SnyBreadcrumbItemDirective, SnyBreadcrumbLinkDirective, SnyBreadcrumbListDirective, SnyBreadcrumbPageDirective, SnyBreadcrumbSeparatorDirective, SnyBulkActionsDefDirective, SnyButtonDirective, SnyButtonGroupDirective, SnyCalendarComponent, SnyCardContentDirective, SnyCardDescriptionDirective, SnyCardDirective, SnyCardFooterDirective, SnyCardHeaderDirective, SnyCardTitleDirective, SnyCarouselContentDirective, SnyCarouselDirective, SnyCarouselItemDirective, SnyCarouselNextDirective, SnyCarouselPrevDirective, SnyCellDefDirective, SnyChatBubbleAvatarDirective, SnyChatBubbleBodyDirective, SnyChatBubbleContentDirective, SnyChatBubbleDirective, SnyChatBubbleFooterDirective, SnyChatBubbleHeaderDirective, SnyCheckboxDirective, SnyColorPickerComponent, SnyComboboxComponent, SnyCommandPaletteComponent, SnyCommandPaletteService, SnyDataTableComponent, SnyDatePickerComponent, SnyDateRangePickerComponent, SnyDialogCloseDirective, SnyDialogContentDirective, SnyDialogDescriptionDirective, SnyDialogFooterDirective, SnyDialogHeaderDirective, SnyDialogRef, SnyDialogService, SnyDialogTitleDirective, SnyDiffComponent, SnyDividerComponent, SnyDockDirective, SnyDockItemDirective, SnyDrawerContentDirective, SnyDrawerLayoutComponent, SnyDrawerLayoutDirective, SnyDrawerSideDirective, SnyDropdownContentDirective, SnyDropdownDirective, SnyDropdownTriggerDirective, SnyFabActionDirective, SnyFabDirective, SnyFabTriggerDirective, SnyFieldsetContentDirective, SnyFieldsetDirective, SnyFieldsetLegendDirective, SnyFileInputComponent, SnyHeaderCellDefDirective, SnyIndicatorBadgeDirective, SnyIndicatorDirective, SnyInputDirective, SnyKbdDirective, SnyLabelDirective, SnyLinkDirective, SnyListDirective, SnyListItemActionDirective, SnyListItemContentDirective, SnyListItemDirective, SnyListItemIconDirective, SnyLoaderComponent, SnyMenuContentDirective, SnyMenuItemDirective, SnyMenuLabelDirective, SnyMenuSeparatorDirective, SnyNavbarBrandDirective, SnyNavbarContentDirective, SnyNavbarDirective, SnyNavbarEndDirective, SnyOtpInputComponent, SnyPaginationComponent, SnyProgressComponent, SnyRadialProgressComponent, SnyRadioDirective, SnyRatingComponent, SnyRowExpandDefDirective, SnySelectComponent, SnySheetCloseDirective, SnySheetContentDirective, SnySheetDescriptionDirective, SnySheetHeaderDirective, SnySheetRef, SnySheetService, SnySheetTitleDirective, SnySkeletonDirective, SnySliderComponent, SnyStatDescriptionDirective, SnyStatDirective, SnyStatFigureDirective, SnyStatTitleDirective, SnyStatValueDirective, SnyStatusDirective, SnyStepDirective, SnyStepsDirective, SnySwitchComponent, SnyTableBodyDirective, SnyTableCaptionDirective, SnyTableCellDirective, SnyTableDirective, SnyTableFooterDirective, SnyTableHeadDirective, SnyTableHeaderDirective, SnyTableRowDirective, SnyTabsContentDirective, SnyTabsDirective, SnyTabsListDirective, SnyTabsTriggerDirective, SnyTextareaDirective, SnyTimelineDirective, SnyTimelineEndDirective, SnyTimelineItemDirective, SnyTimelineMiddleDirective, SnyTimelineStartDirective, SnyToastService, SnyToasterComponent, SnyToggleDirective, SnyTooltipDirective, SnyValidatorDirective, SnyValidatorHintDirective, ThemeService, alertVariants, avatarVariants, badgeVariants, buttonGroupVariants, buttonVariants, cardVariants, checkboxVariants, cn, colorPickerTriggerVariants, comboboxTriggerVariants, datePickerTriggerVariants, dividerVariants, dropdownContentVariants, dropdownItemVariants, fieldsetVariants, fileInputVariants, formatColor, hexToRgb, hslToRgb, hsvToRgb, inputVariants, isValidColor, kbdVariants, labelVariants, linkVariants, loaderVariants, otpCellVariants, paginationItemVariants, parseColor, progressBarVariants, progressTrackVariants, provideSonnyUI, radioVariants, ratingVariants, rgbToHex, rgbToHsl, rgbToHsv, selectTriggerVariants, skeletonVariants, sliderTrackVariants, statusVariants, switchTrackVariants, tableCellVariants, tableVariants, tabsListVariants, tabsTriggerVariants, textareaVariants, toastVariants, toggleVariants, tooltipVariants };
|
|
6913
8300
|
//# sourceMappingURL=sonny-ui-core.mjs.map
|