@tekus/design-system 5.31.0 → 5.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Pure utilities implementing the official WCAG 2.x contrast formula
3
+ * (relative luminance + contrast ratio).
4
+ *
5
+ * Thresholds follow the normal-text criteria (conservative):
6
+ * `>= 7` AAA, `>= 4.5` AA, otherwise `fail`.
7
+ */
8
+ /**
9
+ * Parses a hex color (`#abc`, `abc`, `#aabbcc` or `aabbcc`) into RGB channels (0–255).
10
+ * Returns `null` when the value is not a valid hex color.
11
+ */
12
+ function hexToRgb(hex) {
13
+ let clean = hex.replace('#', '');
14
+ if (/^[0-9a-fA-F]{3}$/.test(clean)) {
15
+ clean = clean[0] + clean[0] + clean[1] + clean[1] + clean[2] + clean[2];
16
+ }
17
+ if (!/^[0-9a-fA-F]{6}$/.test(clean)) {
18
+ return null;
19
+ }
20
+ return {
21
+ r: Number.parseInt(clean.slice(0, 2), 16),
22
+ g: Number.parseInt(clean.slice(2, 4), 16),
23
+ b: Number.parseInt(clean.slice(4, 6), 16),
24
+ };
25
+ }
26
+ /**
27
+ * WCAG relative luminance of a hex color (0 = black, 1 = white).
28
+ * Returns `NaN` for invalid hex values.
29
+ */
30
+ function relativeLuminance(hex) {
31
+ const rgb = hexToRgb(hex);
32
+ if (!rgb) {
33
+ return Number.NaN;
34
+ }
35
+ const [r, g, b] = [rgb.r, rgb.g, rgb.b].map((c) => {
36
+ const s = c / 255;
37
+ return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
38
+ });
39
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b;
40
+ }
41
+ /**
42
+ * Contrast ratio between two hex colors, rounded to 2 decimals.
43
+ * Range: 1 (identical) to 21 (black on white).
44
+ */
45
+ function calcContrastRatio(bg, fg) {
46
+ const l1 = relativeLuminance(bg);
47
+ const l2 = relativeLuminance(fg);
48
+ const lighter = Math.max(l1, l2);
49
+ const darker = Math.min(l1, l2);
50
+ return Number.parseFloat(((lighter + 0.05) / (darker + 0.05)).toFixed(2));
51
+ }
52
+ /**
53
+ * Maps a contrast ratio to its WCAG level for normal text:
54
+ * `>= 7` → AAA, `>= 4.5` → AA, otherwise `fail`.
55
+ */
56
+ function resolveWcagLevel(ratio) {
57
+ if (ratio >= 7) {
58
+ return 'AAA';
59
+ }
60
+ if (ratio >= 4.5) {
61
+ return 'AA';
62
+ }
63
+ return 'fail';
64
+ }
65
+ /**
66
+ * Convenience helper combining {@link calcContrastRatio} and {@link resolveWcagLevel}.
67
+ */
68
+ function getContrastResult(bg, fg) {
69
+ const ratio = calcContrastRatio(bg, fg);
70
+ return { ratio, level: resolveWcagLevel(ratio) };
71
+ }
72
+ /**
73
+ * Whether a hex color is perceived as light, using the ITU-R BT.601
74
+ * perceived-luminance formula normalized to [0, 1] with a 0.5 threshold.
75
+ * Invalid hex values are treated as dark (`false`).
76
+ */
77
+ function isLightColor(hex) {
78
+ const rgb = hexToRgb(hex);
79
+ if (!rgb) {
80
+ return false;
81
+ }
82
+ return (0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b) / 255 > 0.5;
83
+ }
84
+ /**
85
+ * Ideal text color over the given background:
86
+ * black (`#000000`) on light backgrounds, white (`#ffffff`) on dark ones.
87
+ */
88
+ function getContrastTextColor(background) {
89
+ return isLightColor(background) ? '#000000' : '#ffffff';
90
+ }
91
+
92
+ /**
93
+ * Generated bundle index. Do not edit.
94
+ */
95
+
96
+ export { calcContrastRatio, getContrastResult, getContrastTextColor, hexToRgb, isLightColor, relativeLuminance, resolveWcagLevel };
97
+ //# sourceMappingURL=tekus-design-system-utils-wcag-contrast.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tekus-design-system-utils-wcag-contrast.mjs","sources":["../../../projects/design-system/utils/wcag-contrast/src/wcag-contrast.ts","../../../projects/design-system/utils/wcag-contrast/tekus-design-system-utils-wcag-contrast.ts"],"sourcesContent":["/**\n * Pure utilities implementing the official WCAG 2.x contrast formula\n * (relative luminance + contrast ratio).\n *\n * Thresholds follow the normal-text criteria (conservative):\n * `>= 7` AAA, `>= 4.5` AA, otherwise `fail`.\n */\n\nexport type WcagLevel = 'AAA' | 'AA' | 'fail';\n\nexport interface RgbColor {\n r: number;\n g: number;\n b: number;\n}\n\nexport interface ContrastResult {\n ratio: number;\n level: WcagLevel;\n}\n\n/**\n * Parses a hex color (`#abc`, `abc`, `#aabbcc` or `aabbcc`) into RGB channels (0–255).\n * Returns `null` when the value is not a valid hex color.\n */\nexport function hexToRgb(hex: string): RgbColor | null {\n let clean = hex.replace('#', '');\n if (/^[0-9a-fA-F]{3}$/.test(clean)) {\n clean = clean[0] + clean[0] + clean[1] + clean[1] + clean[2] + clean[2];\n }\n if (!/^[0-9a-fA-F]{6}$/.test(clean)) {\n return null;\n }\n return {\n r: Number.parseInt(clean.slice(0, 2), 16),\n g: Number.parseInt(clean.slice(2, 4), 16),\n b: Number.parseInt(clean.slice(4, 6), 16),\n };\n}\n\n/**\n * WCAG relative luminance of a hex color (0 = black, 1 = white).\n * Returns `NaN` for invalid hex values.\n */\nexport function relativeLuminance(hex: string): number {\n const rgb = hexToRgb(hex);\n if (!rgb) {\n return Number.NaN;\n }\n const [r, g, b] = [rgb.r, rgb.g, rgb.b].map((c) => {\n const s = c / 255;\n return s <= 0.03928 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);\n });\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n}\n\n/**\n * Contrast ratio between two hex colors, rounded to 2 decimals.\n * Range: 1 (identical) to 21 (black on white).\n */\nexport function calcContrastRatio(bg: string, fg: string): number {\n const l1 = relativeLuminance(bg);\n const l2 = relativeLuminance(fg);\n const lighter = Math.max(l1, l2);\n const darker = Math.min(l1, l2);\n return Number.parseFloat(((lighter + 0.05) / (darker + 0.05)).toFixed(2));\n}\n\n/**\n * Maps a contrast ratio to its WCAG level for normal text:\n * `>= 7` → AAA, `>= 4.5` → AA, otherwise `fail`.\n */\nexport function resolveWcagLevel(ratio: number): WcagLevel {\n if (ratio >= 7) {\n return 'AAA';\n }\n if (ratio >= 4.5) {\n return 'AA';\n }\n return 'fail';\n}\n\n/**\n * Convenience helper combining {@link calcContrastRatio} and {@link resolveWcagLevel}.\n */\nexport function getContrastResult(bg: string, fg: string): ContrastResult {\n const ratio = calcContrastRatio(bg, fg);\n return { ratio, level: resolveWcagLevel(ratio) };\n}\n\n/**\n * Whether a hex color is perceived as light, using the ITU-R BT.601\n * perceived-luminance formula normalized to [0, 1] with a 0.5 threshold.\n * Invalid hex values are treated as dark (`false`).\n */\nexport function isLightColor(hex: string): boolean {\n const rgb = hexToRgb(hex);\n if (!rgb) {\n return false;\n }\n return (0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b) / 255 > 0.5;\n}\n\n/**\n * Ideal text color over the given background:\n * black (`#000000`) on light backgrounds, white (`#ffffff`) on dark ones.\n */\nexport function getContrastTextColor(background: string): string {\n return isLightColor(background) ? '#000000' : '#ffffff';\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tekus/design-system",
3
3
  "description": "Tekus design system library",
4
- "version": "5.31.0",
4
+ "version": "5.32.0",
5
5
  "license": "UNLICENSED",
6
6
  "peerDependencies": {
7
7
  "@angular/core": "^21.0.0",
@@ -72,6 +72,10 @@
72
72
  "types": "./types/tekus-design-system-components-checkbox.d.ts",
73
73
  "default": "./fesm2022/tekus-design-system-components-checkbox.mjs"
74
74
  },
75
+ "./components/color-picker": {
76
+ "types": "./types/tekus-design-system-components-color-picker.d.ts",
77
+ "default": "./fesm2022/tekus-design-system-components-color-picker.mjs"
78
+ },
75
79
  "./components/date-picker": {
76
80
  "types": "./types/tekus-design-system-components-date-picker.d.ts",
77
81
  "default": "./fesm2022/tekus-design-system-components-date-picker.mjs"
@@ -172,6 +176,10 @@
172
176
  "types": "./types/tekus-design-system-components-time-ago.d.ts",
173
177
  "default": "./fesm2022/tekus-design-system-components-time-ago.mjs"
174
178
  },
179
+ "./components/time-picker": {
180
+ "types": "./types/tekus-design-system-components-time-picker.d.ts",
181
+ "default": "./fesm2022/tekus-design-system-components-time-picker.mjs"
182
+ },
175
183
  "./components/toast": {
176
184
  "types": "./types/tekus-design-system-components-toast.d.ts",
177
185
  "default": "./fesm2022/tekus-design-system-components-toast.mjs"
@@ -207,6 +215,10 @@
207
215
  "./utils/sanitizer-utils": {
208
216
  "types": "./types/tekus-design-system-utils-sanitizer-utils.d.ts",
209
217
  "default": "./fesm2022/tekus-design-system-utils-sanitizer-utils.mjs"
218
+ },
219
+ "./utils/wcag-contrast": {
220
+ "types": "./types/tekus-design-system-utils-wcag-contrast.d.ts",
221
+ "default": "./fesm2022/tekus-design-system-utils-wcag-contrast.mjs"
210
222
  }
211
223
  },
212
224
  "module": "fesm2022/tekus-design-system.mjs",
@@ -0,0 +1,356 @@
1
+ import * as _angular_core from '@angular/core';
2
+ import { ControlValueAccessor, NgControl, FormControl } from '@angular/forms';
3
+ import { Popover } from 'primeng/popover';
4
+ import { ContrastResult } from '@tekus/design-system/utils/wcag-contrast';
5
+
6
+ /**
7
+ * Trigger appearance of `tk-color-picker`.
8
+ * - `input`: swatch + editable HEX value + chevron.
9
+ * - `swatch`: compact swatch + chevron (no HEX shown).
10
+ */
11
+ type ColorPickerVariant = 'input' | 'swatch';
12
+ /**
13
+ * Reason the picker popover was closed.
14
+ * - `accept`: the selection was confirmed.
15
+ */
16
+ type ColorPickerCloseReason = 'accept';
17
+ /**
18
+ * Enables or disables each section of the picker popover.
19
+ * Every flag defaults to `true`; disabled sections are not rendered.
20
+ * The contrast section additionally requires a `contrastColor` to be provided.
21
+ */
22
+ interface ColorPickerSections {
23
+ /** Predefined color palette grid. */
24
+ swatches?: boolean;
25
+ /** User-saved colors grid with the "+" add button. */
26
+ customColors?: boolean;
27
+ /** Saturation/brightness selection canvas. */
28
+ spectrum?: boolean;
29
+ /** Hue slider bar. */
30
+ hue?: boolean;
31
+ /** Color preview + editable HEX field. */
32
+ hex?: boolean;
33
+ /** EyeDropper button (only shown when the browser supports the EyeDropper API). */
34
+ eyedropper?: boolean;
35
+ /** WCAG contrast preview (requires `contrastColor`). */
36
+ contrast?: boolean;
37
+ }
38
+ /**
39
+ * UI texts of the picker. All values have English defaults so consumers
40
+ * can localize them with their own i18n solution.
41
+ */
42
+ interface ColorPickerTexts {
43
+ swatchesLabel?: string;
44
+ customColorsLabel?: string;
45
+ hexLabel?: string;
46
+ contrastLabel?: string;
47
+ contrastSampleText?: string;
48
+ contrastSampleLabel?: string;
49
+ contrastLowLabel?: string;
50
+ invalidHexError?: string;
51
+ requiredError?: string;
52
+ eyedropperLabel?: string;
53
+ addCustomColorLabel?: string;
54
+ removeCustomColorLabel?: string;
55
+ openPickerLabel?: string;
56
+ spectrumLabel?: string;
57
+ hueLabel?: string;
58
+ dialogLabel?: string;
59
+ }
60
+
61
+ /**
62
+ * @component ColorPickerComponent
63
+ * @description
64
+ * Configurable color picker of the Tekus Design System. Renders a trigger
65
+ * (`input` variant with editable HEX, or compact `swatch` variant) that opens
66
+ * a PrimeNG `p-popover` composed of independently toggleable sections:
67
+ * predefined swatches, custom colors, saturation/brightness spectrum, hue bar,
68
+ * HEX field with EyeDropper support, WCAG contrast preview and a
69
+ * Cancel/Accept action bar with temporary selection state.
70
+ * Implements `ControlValueAccessor` for Reactive Forms integration.
71
+ *
72
+ * @usage
73
+ * ```html
74
+ * <tk-color-picker
75
+ * label="Color"
76
+ * [(value)]="color"
77
+ * [contrastColor]="'#ffffff'"
78
+ * (colorChange)="onColor($event)">
79
+ * </tk-color-picker>
80
+ * ```
81
+ */
82
+ declare class ColorPickerComponent implements ControlValueAccessor {
83
+ readonly ngControl: NgControl | null;
84
+ private readonly destroyRef;
85
+ private readonly el;
86
+ private readonly init;
87
+ /**
88
+ * @property {ColorPickerVariant} variant
89
+ * @description
90
+ * Trigger appearance: `input` shows swatch + editable HEX + chevron,
91
+ * `swatch` shows only swatch + chevron.
92
+ * @default `'input'`
93
+ */
94
+ variant: _angular_core.InputSignal<ColorPickerVariant>;
95
+ /**
96
+ * @property {string} label
97
+ * @description
98
+ * Label displayed above the trigger.
99
+ * @default `''`
100
+ */
101
+ label: _angular_core.InputSignal<string>;
102
+ /**
103
+ * @property {string} hint
104
+ * @description
105
+ * Hint text displayed below the trigger (hidden while an error is shown).
106
+ * @default `''`
107
+ */
108
+ hint: _angular_core.InputSignal<string>;
109
+ /**
110
+ * @property {boolean} disabled
111
+ * @description
112
+ * Disables the trigger and the popover. Also controlled by Reactive Forms
113
+ * via `setDisabledState`.
114
+ * @default `false`
115
+ */
116
+ disabled: _angular_core.InputSignal<boolean>;
117
+ /**
118
+ * @property {boolean} required
119
+ * @description
120
+ * When `true`, an empty HEX field shows the required error on blur.
121
+ * @default `false`
122
+ */
123
+ required: _angular_core.InputSignal<boolean>;
124
+ /**
125
+ * @property {ColorPickerSections} sections
126
+ * @description
127
+ * Enables/disables each popover section. Missing flags default to `true`.
128
+ * Disabling `actionBar` switches the picker to live mode (every valid
129
+ * change is emitted immediately).
130
+ * @default `{}` (all sections enabled)
131
+ */
132
+ sections: _angular_core.InputSignal<ColorPickerSections>;
133
+ /**
134
+ * @property {string[]} presetColors
135
+ * @description
136
+ * Colors of the predefined palette section.
137
+ * @default Design System base palette
138
+ */
139
+ presetColors: _angular_core.InputSignal<string[]>;
140
+ /**
141
+ * @property {string | null} contrastColor
142
+ * @description
143
+ * Optional override of the text color used for the WCAG contrast pair.
144
+ * When `null` (default) the text color is resolved automatically from the
145
+ * picked background luminance (ITU-R BT.601): black over light colors,
146
+ * white over dark ones.
147
+ * @default `null`
148
+ */
149
+ contrastColor: _angular_core.InputSignal<string | null>;
150
+ /**
151
+ * @property {number} maxCustomColors
152
+ * @description
153
+ * Maximum number of custom color slots. Once the list is at capacity, the
154
+ * oldest slot is replaced using a circular buffer — the "+" button stays
155
+ * visible at all times.
156
+ * @default `18`
157
+ */
158
+ maxCustomColors: _angular_core.InputSignal<number>;
159
+ /**
160
+ * @property {'top' | 'bottom'} placement
161
+ * @description
162
+ * Preferred position of the popover relative to the trigger.
163
+ * @default `'bottom'`
164
+ */
165
+ placement: _angular_core.InputSignal<"top" | "bottom">;
166
+ /**
167
+ * @property {string} errorMessage
168
+ * @description
169
+ * External error message that overrides the internal validation messages.
170
+ * @default `''`
171
+ */
172
+ errorMessage: _angular_core.InputSignal<string>;
173
+ /**
174
+ * @property {ColorPickerTexts} texts
175
+ * @description
176
+ * UI texts (section labels, buttons, errors). Defaults are in English so
177
+ * consumers can localize with their own i18n solution.
178
+ * @default `{}` (English defaults)
179
+ */
180
+ texts: _angular_core.InputSignal<ColorPickerTexts>;
181
+ /**
182
+ * @property {ModelSignal<string>} value
183
+ * @description
184
+ * Confirmed color as a normalized lowercase 6-digit hex with `#`.
185
+ * Two-way bindable.
186
+ * @default `''`
187
+ */
188
+ value: _angular_core.ModelSignal<string>;
189
+ /**
190
+ * @property {ModelSignal<string[]>} customColors
191
+ * @description
192
+ * User-saved custom colors. Two-way bindable so the consumer decides
193
+ * where to persist them.
194
+ * @default `[]`
195
+ */
196
+ customColors: _angular_core.ModelSignal<string[]>;
197
+ /**
198
+ * @event colorChange
199
+ * @description
200
+ * Emits the confirmed background hex color: on Accept (action bar mode) or
201
+ * on every valid change (live mode / trigger HEX edit while closed). Never
202
+ * emits invalid values, on hydration, or while disabled.
203
+ */
204
+ colorChange: _angular_core.OutputEmitterRef<string>;
205
+ /**
206
+ * @event textColorChange
207
+ * @description
208
+ * Emits together with `colorChange`: the ideal text color over the
209
+ * confirmed background (`#000000` on light colors, `#ffffff` on dark ones,
210
+ * or the `contrastColor` override when provided).
211
+ */
212
+ textColorChange: _angular_core.OutputEmitterRef<string>;
213
+ /**
214
+ * @event validChange
215
+ * @description
216
+ * Emits only when the validity of the HEX value changes.
217
+ */
218
+ validChange: _angular_core.OutputEmitterRef<boolean>;
219
+ /**
220
+ * @event opened
221
+ * @description
222
+ * Emits when the picker popover opens.
223
+ */
224
+ opened: _angular_core.OutputEmitterRef<void>;
225
+ /**
226
+ * @event closed
227
+ * @description
228
+ * Emits when the popover closes, with the close reason
229
+ * (`accept` confirmed, `cancel` discarded/restored).
230
+ */
231
+ closed: _angular_core.OutputEmitterRef<"accept">;
232
+ popover: _angular_core.Signal<Popover | undefined>;
233
+ private swatchBtnRef;
234
+ private inputTriggerRef;
235
+ private panelHexInput;
236
+ /**
237
+ * Internal FormControl handed to the `tk-input-text` trigger so the
238
+ * disabled state propagates through the Design System input.
239
+ */
240
+ protected readonly triggerControl: FormControl<string | null>;
241
+ protected readonly isOpen: _angular_core.WritableSignal<boolean>;
242
+ protected readonly hue: _angular_core.WritableSignal<number>;
243
+ protected readonly saturation: _angular_core.WritableSignal<number>;
244
+ protected readonly brightness: _angular_core.WritableSignal<number>;
245
+ protected readonly draftHex: _angular_core.WritableSignal<string>;
246
+ protected readonly displayHexNoHash: _angular_core.WritableSignal<string>;
247
+ protected readonly isValid: _angular_core.WritableSignal<boolean>;
248
+ protected readonly errorType: _angular_core.WritableSignal<"invalid" | "required">;
249
+ private readonly cvaDisabled;
250
+ protected readonly isDisabled: _angular_core.Signal<boolean>;
251
+ protected readonly resolvedSections: _angular_core.Signal<Required<ColorPickerSections>>;
252
+ protected readonly t: _angular_core.Signal<Required<ColorPickerTexts>>;
253
+ /** Valid draft hex to paint swatches, or null → neutral fallback via CSS. */
254
+ protected readonly swatchColor: _angular_core.Signal<string | null>;
255
+ /**
256
+ * Text color of the contrast pair: the `contrastColor` override when valid,
257
+ * otherwise resolved automatically from the draft background luminance.
258
+ */
259
+ protected readonly contrastTextColor: _angular_core.Signal<string>;
260
+ protected readonly contrastResult: _angular_core.Signal<ContrastResult | null>;
261
+ protected readonly showContrast: _angular_core.Signal<boolean>;
262
+ protected readonly eyeDropperSupported: boolean;
263
+ protected readonly showEyedropper: _angular_core.Signal<boolean>;
264
+ protected readonly canAddCustom: _angular_core.Signal<boolean>;
265
+ protected readonly errorText: _angular_core.Signal<string>;
266
+ protected readonly fieldId: string;
267
+ protected readonly panelFieldId: string;
268
+ protected readonly errorId: string;
269
+ private static instanceCount;
270
+ private originalValue;
271
+ private readonly customColorWritePtr;
272
+ private readonly hexInput$;
273
+ onChange: (value: string) => void;
274
+ onTouched: () => void;
275
+ constructor();
276
+ /**
277
+ * @method writeValue
278
+ * @description Hydrates the picker from the form model without emitting.
279
+ */
280
+ writeValue(value: string): void;
281
+ /**
282
+ * @method registerOnChange
283
+ * @description Registers the Reactive Forms change callback.
284
+ */
285
+ registerOnChange(fn: (value: string) => void): void;
286
+ /**
287
+ * @method registerOnTouched
288
+ * @description Registers the Reactive Forms touched callback.
289
+ */
290
+ registerOnTouched(fn: () => void): void;
291
+ /**
292
+ * @method setDisabledState
293
+ * @description Syncs the disabled state from Reactive Forms.
294
+ */
295
+ setDisabledState(isDisabled: boolean): void;
296
+ protected onTriggerClick(event: Event): void;
297
+ /** Returns the precise anchor element for popover positioning.
298
+ * For the input variant we target the `p-floatlabel` child instead of the
299
+ * full `tk-input-text` host. The host includes a bottom section with
300
+ * `min-height: 1.25rem + margin-top: 0.25rem` that is invisible when empty
301
+ * but would push the panel ~1.5rem below the visible input field.
302
+ *
303
+ * Coupling note: `p-floatlabel` is PrimeNG's float-label host element selector.
304
+ * If PrimeNG renames it in a future major, the `?? host` fallback keeps
305
+ * positioning functional (panel opens ~1.5rem lower than ideal). */
306
+ private getAnchorEl;
307
+ protected onPopoverShow(): void;
308
+ private fixPanelPosition;
309
+ protected onPopoverHide(): void;
310
+ protected onPanelEnter(event: Event): void;
311
+ private confirmAndClose;
312
+ protected onSpectrumChange(change: {
313
+ saturation: number;
314
+ brightness: number;
315
+ }): void;
316
+ protected onHueChange(event: Event): void;
317
+ protected onSwatchPick(color: string): void;
318
+ protected addCustomColor(): void;
319
+ protected isCustomColorSelected(): boolean;
320
+ protected removeCustomColor(): void;
321
+ protected openEyeDropper(): void;
322
+ protected onHexInput(clean: string): void;
323
+ /**
324
+ * Sanitizes typing inside a `tk-input-text` hex field (trigger or popover):
325
+ * strips non-hex characters at the DOM level (keeping the caret behavior of
326
+ * the native input) and feeds the shared validation pipeline.
327
+ */
328
+ protected onHexNativeInput(event: Event): void;
329
+ /** Blocks non-hex keypresses and enforces 6-char max before char enters the DOM. Enter confirms selection. */
330
+ protected onHexKeydown(event: KeyboardEvent): void;
331
+ /** Strips non-hex chars from pasted text before it reaches the input model. */
332
+ protected onHexPaste(event: ClipboardEvent): void;
333
+ protected onHexBlur(): void;
334
+ private processHexInput;
335
+ private parseHex;
336
+ private applyValidHex;
337
+ private applyInvalidHex;
338
+ private setValid;
339
+ private syncFromValue;
340
+ private setFromHex;
341
+ private applyHsv;
342
+ /**
343
+ * Commits the draft color immediately (live mode).
344
+ */
345
+ private maybeCommit;
346
+ private commitDraft;
347
+ private normalizeHex;
348
+ private isValidHex;
349
+ private hexToHsv;
350
+ private hsvToHex;
351
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ColorPickerComponent, never>;
352
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<ColorPickerComponent, "tk-color-picker", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "hint": { "alias": "hint"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "sections": { "alias": "sections"; "required": false; "isSignal": true; }; "presetColors": { "alias": "presetColors"; "required": false; "isSignal": true; }; "contrastColor": { "alias": "contrastColor"; "required": false; "isSignal": true; }; "maxCustomColors": { "alias": "maxCustomColors"; "required": false; "isSignal": true; }; "placement": { "alias": "placement"; "required": false; "isSignal": true; }; "errorMessage": { "alias": "errorMessage"; "required": false; "isSignal": true; }; "texts": { "alias": "texts"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "customColors": { "alias": "customColors"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; "customColors": "customColorsChange"; "colorChange": "colorChange"; "textColorChange": "textColorChange"; "validChange": "validChange"; "opened": "opened"; "closed": "closed"; }, never, never, true, never>;
353
+ }
354
+
355
+ export { ColorPickerComponent };
356
+ export type { ColorPickerCloseReason, ColorPickerSections, ColorPickerTexts, ColorPickerVariant };