@proyecto-viviana/solid-stately 0.2.4 → 0.2.7
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/LICENSE +21 -0
- package/dist/autocomplete/createAutocompleteState.d.ts +2 -1
- package/dist/checkbox/createCheckboxGroupState.d.ts +10 -1
- package/dist/collections/types.d.ts +11 -0
- package/dist/color/getColorChannels.d.ts +20 -0
- package/dist/data/createAsyncList.d.ts +111 -0
- package/dist/data/createListData.d.ts +65 -0
- package/dist/data/createTreeData.d.ts +61 -0
- package/dist/data/index.d.ts +3 -0
- package/dist/datepicker/index.d.ts +10 -0
- package/dist/grid/types.d.ts +5 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +3737 -2697
- package/dist/index.js.map +1 -7
- package/dist/menu/index.d.ts +8 -0
- package/dist/radio/createRadioGroupState.d.ts +10 -1
- package/dist/select/createSelectState.d.ts +17 -0
- package/dist/selection/index.d.ts +11 -0
- package/dist/toast/createToastState.d.ts +7 -1
- package/dist/toggle/createToggleGroupState.d.ts +45 -0
- package/dist/toggle/index.d.ts +1 -0
- package/dist/tree/TreeCollection.d.ts +3 -2
- package/package.json +6 -5
- package/src/autocomplete/createAutocompleteState.ts +10 -11
- package/src/calendar/createDateFieldState.ts +24 -1
- package/src/checkbox/createCheckboxGroupState.ts +42 -6
- package/src/collections/ListCollection.ts +152 -146
- package/src/collections/createListState.ts +266 -264
- package/src/collections/createMenuState.ts +106 -106
- package/src/collections/createSelectionState.ts +336 -336
- package/src/collections/index.ts +46 -46
- package/src/collections/types.ts +181 -169
- package/src/color/Color.ts +951 -951
- package/src/color/createColorAreaState.ts +293 -293
- package/src/color/createColorFieldState.ts +292 -292
- package/src/color/createColorSliderState.ts +241 -241
- package/src/color/createColorWheelState.ts +211 -211
- package/src/color/getColorChannels.ts +34 -0
- package/src/color/index.ts +47 -47
- package/src/color/types.ts +127 -127
- package/src/combobox/createComboBoxState.ts +703 -703
- package/src/combobox/index.ts +13 -13
- package/src/data/createAsyncList.ts +377 -0
- package/src/data/createListData.ts +298 -0
- package/src/data/createTreeData.ts +433 -0
- package/src/data/index.ts +25 -0
- package/src/datepicker/index.ts +36 -0
- package/src/disclosure/createDisclosureState.ts +4 -4
- package/src/dnd/createDragState.ts +153 -153
- package/src/dnd/createDraggableCollectionState.ts +165 -165
- package/src/dnd/createDropState.ts +212 -212
- package/src/dnd/createDroppableCollectionState.ts +357 -357
- package/src/dnd/index.ts +76 -76
- package/src/dnd/types.ts +317 -317
- package/src/form/createFormValidationState.ts +389 -389
- package/src/form/index.ts +15 -15
- package/src/grid/types.ts +5 -0
- package/src/index.ts +49 -0
- package/src/menu/index.ts +19 -0
- package/src/numberfield/createNumberFieldState.ts +427 -383
- package/src/numberfield/index.ts +5 -5
- package/src/overlays/createOverlayTriggerState.ts +67 -67
- package/src/overlays/index.ts +5 -5
- package/src/radio/createRadioGroupState.ts +44 -6
- package/src/searchfield/createSearchFieldState.ts +62 -62
- package/src/searchfield/index.ts +5 -5
- package/src/select/createSelectState.ts +290 -181
- package/src/select/index.ts +5 -5
- package/src/selection/index.ts +28 -0
- package/src/slider/createSliderState.ts +211 -211
- package/src/slider/index.ts +6 -6
- package/src/tabs/createTabListState.ts +37 -11
- package/src/toast/createToastState.d.ts +6 -1
- package/src/toast/createToastState.ts +8 -1
- package/src/toggle/createToggleGroupState.ts +127 -0
- package/src/toggle/index.ts +6 -0
- package/src/tooltip/createTooltipTriggerState.ts +183 -183
- package/src/tooltip/index.ts +6 -6
- package/src/tree/TreeCollection.ts +208 -175
- package/src/tree/createTreeState.ts +392 -392
- package/src/tree/index.ts +13 -13
- package/src/tree/types.ts +174 -174
|
@@ -1,292 +1,292 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ColorField state management.
|
|
3
|
-
* Based on @react-stately/color useColorFieldState.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { createSignal, createMemo, type Accessor } from 'solid-js';
|
|
7
|
-
import type { Color, ColorChannel, ColorFormat } from './types';
|
|
8
|
-
import { normalizeColor, parseColor } from './Color';
|
|
9
|
-
|
|
10
|
-
export interface ColorFieldStateOptions {
|
|
11
|
-
/** The current color value (controlled). */
|
|
12
|
-
value?: Color | string | null;
|
|
13
|
-
/** The default color value (uncontrolled). */
|
|
14
|
-
defaultValue?: Color | string;
|
|
15
|
-
/** Handler called when the color changes. */
|
|
16
|
-
onChange?: (color: Color | null) => void;
|
|
17
|
-
/** The color channel to edit (for single channel mode). */
|
|
18
|
-
channel?: ColorChannel;
|
|
19
|
-
/** The color format for parsing/displaying. */
|
|
20
|
-
colorFormat?: ColorFormat;
|
|
21
|
-
/** Whether the field is disabled. */
|
|
22
|
-
isDisabled?: boolean;
|
|
23
|
-
/** Whether the field is read-only. */
|
|
24
|
-
isReadOnly?: boolean;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface ColorFieldState {
|
|
28
|
-
/** The current color value (null if invalid). */
|
|
29
|
-
readonly value: Color | null;
|
|
30
|
-
/** The current input text. */
|
|
31
|
-
readonly inputValue: string;
|
|
32
|
-
/** Whether the input is invalid. */
|
|
33
|
-
readonly isInvalid: boolean;
|
|
34
|
-
/** Whether the field is disabled. */
|
|
35
|
-
readonly isDisabled: boolean;
|
|
36
|
-
/** Whether the field is read-only. */
|
|
37
|
-
readonly isReadOnly: boolean;
|
|
38
|
-
/** The color channel being edited (if single channel mode). */
|
|
39
|
-
readonly channel: ColorChannel | undefined;
|
|
40
|
-
|
|
41
|
-
/** Set the input text value. */
|
|
42
|
-
setInputValue(value: string): void;
|
|
43
|
-
/** Commit the current input value. */
|
|
44
|
-
commit(): void;
|
|
45
|
-
/** Increment the color channel value. */
|
|
46
|
-
increment(): void;
|
|
47
|
-
/** Decrement the color channel value. */
|
|
48
|
-
decrement(): void;
|
|
49
|
-
/** Increment by page size. */
|
|
50
|
-
incrementToMax(): void;
|
|
51
|
-
/** Decrement to minimum. */
|
|
52
|
-
decrementToMin(): void;
|
|
53
|
-
/** Validate the current input. */
|
|
54
|
-
validate(): boolean;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Creates state for a color field (text input for color values).
|
|
59
|
-
*/
|
|
60
|
-
export function createColorFieldState(
|
|
61
|
-
options: Accessor<ColorFieldStateOptions>
|
|
62
|
-
): ColorFieldState {
|
|
63
|
-
const getOptions = () => options();
|
|
64
|
-
|
|
65
|
-
// Internal value state
|
|
66
|
-
const [internalValue, setInternalValue] = createSignal<Color | null>(null);
|
|
67
|
-
const [inputValue, setInputValueInternal] = createSignal('');
|
|
68
|
-
const [isInvalid, setIsInvalid] = createSignal(false);
|
|
69
|
-
|
|
70
|
-
// Initialize internal value
|
|
71
|
-
const initValue = () => {
|
|
72
|
-
const opts = getOptions();
|
|
73
|
-
if (opts.defaultValue) {
|
|
74
|
-
const color = normalizeColor(opts.defaultValue);
|
|
75
|
-
return color;
|
|
76
|
-
}
|
|
77
|
-
return null;
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
// Set initial value
|
|
81
|
-
if (internalValue() === null) {
|
|
82
|
-
const init = initValue();
|
|
83
|
-
if (init) {
|
|
84
|
-
setInternalValue(init);
|
|
85
|
-
setInputValueInternal(formatColorValue(init, getOptions().channel, getOptions().colorFormat));
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Controlled vs uncontrolled value
|
|
90
|
-
const value = createMemo(() => {
|
|
91
|
-
const opts = getOptions();
|
|
92
|
-
if (opts.value !== undefined) {
|
|
93
|
-
if (opts.value === null) return null;
|
|
94
|
-
return normalizeColor(opts.value);
|
|
95
|
-
}
|
|
96
|
-
return internalValue();
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
const channel = createMemo(() => getOptions().channel);
|
|
100
|
-
const isDisabled = createMemo(() => getOptions().isDisabled ?? false);
|
|
101
|
-
const isReadOnly = createMemo(() => getOptions().isReadOnly ?? false);
|
|
102
|
-
|
|
103
|
-
// Format color value for display
|
|
104
|
-
function formatColorValue(
|
|
105
|
-
color: Color | null,
|
|
106
|
-
chan?: ColorChannel,
|
|
107
|
-
format?: ColorFormat
|
|
108
|
-
): string {
|
|
109
|
-
if (!color) return '';
|
|
110
|
-
|
|
111
|
-
// Single channel mode
|
|
112
|
-
if (chan) {
|
|
113
|
-
return String(color.getChannelValue(chan));
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Full color mode
|
|
117
|
-
return color.toString(format ?? 'hex');
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Update value
|
|
121
|
-
const updateValue = (newColor: Color | null) => {
|
|
122
|
-
const opts = getOptions();
|
|
123
|
-
|
|
124
|
-
// Controlled mode
|
|
125
|
-
if (opts.value !== undefined) {
|
|
126
|
-
opts.onChange?.(newColor);
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Uncontrolled mode
|
|
131
|
-
setInternalValue(newColor);
|
|
132
|
-
opts.onChange?.(newColor);
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
// Set input value
|
|
136
|
-
const setInputValue = (text: string) => {
|
|
137
|
-
setInputValueInternal(text);
|
|
138
|
-
setIsInvalid(false);
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
// Commit the input value
|
|
142
|
-
const commit = () => {
|
|
143
|
-
const text = inputValue().trim();
|
|
144
|
-
const opts = getOptions();
|
|
145
|
-
const chan = channel();
|
|
146
|
-
|
|
147
|
-
if (!text) {
|
|
148
|
-
updateValue(null);
|
|
149
|
-
setIsInvalid(false);
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
try {
|
|
154
|
-
let newColor: Color;
|
|
155
|
-
|
|
156
|
-
if (chan) {
|
|
157
|
-
// Single channel mode - parse as number and update channel
|
|
158
|
-
const numValue = parseFloat(text);
|
|
159
|
-
if (isNaN(numValue)) {
|
|
160
|
-
setIsInvalid(true);
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const currentColor = value();
|
|
165
|
-
if (!currentColor) {
|
|
166
|
-
setIsInvalid(true);
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const range = currentColor.getChannelRange(chan);
|
|
171
|
-
const clamped = Math.max(range.minValue, Math.min(range.maxValue, numValue));
|
|
172
|
-
newColor = currentColor.withChannelValue(chan, clamped);
|
|
173
|
-
} else {
|
|
174
|
-
// Full color mode - parse the color string
|
|
175
|
-
newColor = parseColor(text);
|
|
176
|
-
if (opts.colorFormat) {
|
|
177
|
-
newColor = newColor.toFormat(opts.colorFormat);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
updateValue(newColor);
|
|
182
|
-
setInputValueInternal(formatColorValue(newColor, chan, opts.colorFormat));
|
|
183
|
-
setIsInvalid(false);
|
|
184
|
-
} catch {
|
|
185
|
-
setIsInvalid(true);
|
|
186
|
-
}
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
// Increment channel value
|
|
190
|
-
const increment = () => {
|
|
191
|
-
const chan = channel();
|
|
192
|
-
const currentColor = value();
|
|
193
|
-
if (!currentColor || !chan) return;
|
|
194
|
-
|
|
195
|
-
const currentVal = currentColor.getChannelValue(chan);
|
|
196
|
-
const range = currentColor.getChannelRange(chan);
|
|
197
|
-
const newVal = Math.min(range.maxValue, currentVal + range.step);
|
|
198
|
-
const newColor = currentColor.withChannelValue(chan, newVal);
|
|
199
|
-
|
|
200
|
-
updateValue(newColor);
|
|
201
|
-
setInputValueInternal(formatColorValue(newColor, chan, getOptions().colorFormat));
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
// Decrement channel value
|
|
205
|
-
const decrement = () => {
|
|
206
|
-
const chan = channel();
|
|
207
|
-
const currentColor = value();
|
|
208
|
-
if (!currentColor || !chan) return;
|
|
209
|
-
|
|
210
|
-
const currentVal = currentColor.getChannelValue(chan);
|
|
211
|
-
const range = currentColor.getChannelRange(chan);
|
|
212
|
-
const newVal = Math.max(range.minValue, currentVal - range.step);
|
|
213
|
-
const newColor = currentColor.withChannelValue(chan, newVal);
|
|
214
|
-
|
|
215
|
-
updateValue(newColor);
|
|
216
|
-
setInputValueInternal(formatColorValue(newColor, chan, getOptions().colorFormat));
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
// Increment to max
|
|
220
|
-
const incrementToMax = () => {
|
|
221
|
-
const chan = channel();
|
|
222
|
-
const currentColor = value();
|
|
223
|
-
if (!currentColor || !chan) return;
|
|
224
|
-
|
|
225
|
-
const range = currentColor.getChannelRange(chan);
|
|
226
|
-
const newColor = currentColor.withChannelValue(chan, range.maxValue);
|
|
227
|
-
|
|
228
|
-
updateValue(newColor);
|
|
229
|
-
setInputValueInternal(formatColorValue(newColor, chan, getOptions().colorFormat));
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
// Decrement to min
|
|
233
|
-
const decrementToMin = () => {
|
|
234
|
-
const chan = channel();
|
|
235
|
-
const currentColor = value();
|
|
236
|
-
if (!currentColor || !chan) return;
|
|
237
|
-
|
|
238
|
-
const range = currentColor.getChannelRange(chan);
|
|
239
|
-
const newColor = currentColor.withChannelValue(chan, range.minValue);
|
|
240
|
-
|
|
241
|
-
updateValue(newColor);
|
|
242
|
-
setInputValueInternal(formatColorValue(newColor, chan, getOptions().colorFormat));
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
// Validate input
|
|
246
|
-
const validate = () => {
|
|
247
|
-
const text = inputValue().trim();
|
|
248
|
-
const chan = channel();
|
|
249
|
-
|
|
250
|
-
if (!text) return true;
|
|
251
|
-
|
|
252
|
-
try {
|
|
253
|
-
if (chan) {
|
|
254
|
-
const numValue = parseFloat(text);
|
|
255
|
-
return !isNaN(numValue);
|
|
256
|
-
} else {
|
|
257
|
-
parseColor(text);
|
|
258
|
-
return true;
|
|
259
|
-
}
|
|
260
|
-
} catch {
|
|
261
|
-
return false;
|
|
262
|
-
}
|
|
263
|
-
};
|
|
264
|
-
|
|
265
|
-
return {
|
|
266
|
-
get value() {
|
|
267
|
-
return value();
|
|
268
|
-
},
|
|
269
|
-
get inputValue() {
|
|
270
|
-
return inputValue();
|
|
271
|
-
},
|
|
272
|
-
get isInvalid() {
|
|
273
|
-
return isInvalid();
|
|
274
|
-
},
|
|
275
|
-
get isDisabled() {
|
|
276
|
-
return isDisabled();
|
|
277
|
-
},
|
|
278
|
-
get isReadOnly() {
|
|
279
|
-
return isReadOnly();
|
|
280
|
-
},
|
|
281
|
-
get channel() {
|
|
282
|
-
return channel();
|
|
283
|
-
},
|
|
284
|
-
setInputValue,
|
|
285
|
-
commit,
|
|
286
|
-
increment,
|
|
287
|
-
decrement,
|
|
288
|
-
incrementToMax,
|
|
289
|
-
decrementToMin,
|
|
290
|
-
validate,
|
|
291
|
-
};
|
|
292
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* ColorField state management.
|
|
3
|
+
* Based on @react-stately/color useColorFieldState.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { createSignal, createMemo, type Accessor } from 'solid-js';
|
|
7
|
+
import type { Color, ColorChannel, ColorFormat } from './types';
|
|
8
|
+
import { normalizeColor, parseColor } from './Color';
|
|
9
|
+
|
|
10
|
+
export interface ColorFieldStateOptions {
|
|
11
|
+
/** The current color value (controlled). */
|
|
12
|
+
value?: Color | string | null;
|
|
13
|
+
/** The default color value (uncontrolled). */
|
|
14
|
+
defaultValue?: Color | string;
|
|
15
|
+
/** Handler called when the color changes. */
|
|
16
|
+
onChange?: (color: Color | null) => void;
|
|
17
|
+
/** The color channel to edit (for single channel mode). */
|
|
18
|
+
channel?: ColorChannel;
|
|
19
|
+
/** The color format for parsing/displaying. */
|
|
20
|
+
colorFormat?: ColorFormat;
|
|
21
|
+
/** Whether the field is disabled. */
|
|
22
|
+
isDisabled?: boolean;
|
|
23
|
+
/** Whether the field is read-only. */
|
|
24
|
+
isReadOnly?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ColorFieldState {
|
|
28
|
+
/** The current color value (null if invalid). */
|
|
29
|
+
readonly value: Color | null;
|
|
30
|
+
/** The current input text. */
|
|
31
|
+
readonly inputValue: string;
|
|
32
|
+
/** Whether the input is invalid. */
|
|
33
|
+
readonly isInvalid: boolean;
|
|
34
|
+
/** Whether the field is disabled. */
|
|
35
|
+
readonly isDisabled: boolean;
|
|
36
|
+
/** Whether the field is read-only. */
|
|
37
|
+
readonly isReadOnly: boolean;
|
|
38
|
+
/** The color channel being edited (if single channel mode). */
|
|
39
|
+
readonly channel: ColorChannel | undefined;
|
|
40
|
+
|
|
41
|
+
/** Set the input text value. */
|
|
42
|
+
setInputValue(value: string): void;
|
|
43
|
+
/** Commit the current input value. */
|
|
44
|
+
commit(): void;
|
|
45
|
+
/** Increment the color channel value. */
|
|
46
|
+
increment(): void;
|
|
47
|
+
/** Decrement the color channel value. */
|
|
48
|
+
decrement(): void;
|
|
49
|
+
/** Increment by page size. */
|
|
50
|
+
incrementToMax(): void;
|
|
51
|
+
/** Decrement to minimum. */
|
|
52
|
+
decrementToMin(): void;
|
|
53
|
+
/** Validate the current input. */
|
|
54
|
+
validate(): boolean;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Creates state for a color field (text input for color values).
|
|
59
|
+
*/
|
|
60
|
+
export function createColorFieldState(
|
|
61
|
+
options: Accessor<ColorFieldStateOptions>
|
|
62
|
+
): ColorFieldState {
|
|
63
|
+
const getOptions = () => options();
|
|
64
|
+
|
|
65
|
+
// Internal value state
|
|
66
|
+
const [internalValue, setInternalValue] = createSignal<Color | null>(null);
|
|
67
|
+
const [inputValue, setInputValueInternal] = createSignal('');
|
|
68
|
+
const [isInvalid, setIsInvalid] = createSignal(false);
|
|
69
|
+
|
|
70
|
+
// Initialize internal value
|
|
71
|
+
const initValue = () => {
|
|
72
|
+
const opts = getOptions();
|
|
73
|
+
if (opts.defaultValue) {
|
|
74
|
+
const color = normalizeColor(opts.defaultValue);
|
|
75
|
+
return color;
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// Set initial value
|
|
81
|
+
if (internalValue() === null) {
|
|
82
|
+
const init = initValue();
|
|
83
|
+
if (init) {
|
|
84
|
+
setInternalValue(init);
|
|
85
|
+
setInputValueInternal(formatColorValue(init, getOptions().channel, getOptions().colorFormat));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Controlled vs uncontrolled value
|
|
90
|
+
const value = createMemo(() => {
|
|
91
|
+
const opts = getOptions();
|
|
92
|
+
if (opts.value !== undefined) {
|
|
93
|
+
if (opts.value === null) return null;
|
|
94
|
+
return normalizeColor(opts.value);
|
|
95
|
+
}
|
|
96
|
+
return internalValue();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const channel = createMemo(() => getOptions().channel);
|
|
100
|
+
const isDisabled = createMemo(() => getOptions().isDisabled ?? false);
|
|
101
|
+
const isReadOnly = createMemo(() => getOptions().isReadOnly ?? false);
|
|
102
|
+
|
|
103
|
+
// Format color value for display
|
|
104
|
+
function formatColorValue(
|
|
105
|
+
color: Color | null,
|
|
106
|
+
chan?: ColorChannel,
|
|
107
|
+
format?: ColorFormat
|
|
108
|
+
): string {
|
|
109
|
+
if (!color) return '';
|
|
110
|
+
|
|
111
|
+
// Single channel mode
|
|
112
|
+
if (chan) {
|
|
113
|
+
return String(color.getChannelValue(chan));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Full color mode
|
|
117
|
+
return color.toString(format ?? 'hex');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Update value
|
|
121
|
+
const updateValue = (newColor: Color | null) => {
|
|
122
|
+
const opts = getOptions();
|
|
123
|
+
|
|
124
|
+
// Controlled mode
|
|
125
|
+
if (opts.value !== undefined) {
|
|
126
|
+
opts.onChange?.(newColor);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Uncontrolled mode
|
|
131
|
+
setInternalValue(newColor);
|
|
132
|
+
opts.onChange?.(newColor);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// Set input value
|
|
136
|
+
const setInputValue = (text: string) => {
|
|
137
|
+
setInputValueInternal(text);
|
|
138
|
+
setIsInvalid(false);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// Commit the input value
|
|
142
|
+
const commit = () => {
|
|
143
|
+
const text = inputValue().trim();
|
|
144
|
+
const opts = getOptions();
|
|
145
|
+
const chan = channel();
|
|
146
|
+
|
|
147
|
+
if (!text) {
|
|
148
|
+
updateValue(null);
|
|
149
|
+
setIsInvalid(false);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
let newColor: Color;
|
|
155
|
+
|
|
156
|
+
if (chan) {
|
|
157
|
+
// Single channel mode - parse as number and update channel
|
|
158
|
+
const numValue = parseFloat(text);
|
|
159
|
+
if (isNaN(numValue)) {
|
|
160
|
+
setIsInvalid(true);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const currentColor = value();
|
|
165
|
+
if (!currentColor) {
|
|
166
|
+
setIsInvalid(true);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const range = currentColor.getChannelRange(chan);
|
|
171
|
+
const clamped = Math.max(range.minValue, Math.min(range.maxValue, numValue));
|
|
172
|
+
newColor = currentColor.withChannelValue(chan, clamped);
|
|
173
|
+
} else {
|
|
174
|
+
// Full color mode - parse the color string
|
|
175
|
+
newColor = parseColor(text);
|
|
176
|
+
if (opts.colorFormat) {
|
|
177
|
+
newColor = newColor.toFormat(opts.colorFormat);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
updateValue(newColor);
|
|
182
|
+
setInputValueInternal(formatColorValue(newColor, chan, opts.colorFormat));
|
|
183
|
+
setIsInvalid(false);
|
|
184
|
+
} catch {
|
|
185
|
+
setIsInvalid(true);
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// Increment channel value
|
|
190
|
+
const increment = () => {
|
|
191
|
+
const chan = channel();
|
|
192
|
+
const currentColor = value();
|
|
193
|
+
if (!currentColor || !chan) return;
|
|
194
|
+
|
|
195
|
+
const currentVal = currentColor.getChannelValue(chan);
|
|
196
|
+
const range = currentColor.getChannelRange(chan);
|
|
197
|
+
const newVal = Math.min(range.maxValue, currentVal + range.step);
|
|
198
|
+
const newColor = currentColor.withChannelValue(chan, newVal);
|
|
199
|
+
|
|
200
|
+
updateValue(newColor);
|
|
201
|
+
setInputValueInternal(formatColorValue(newColor, chan, getOptions().colorFormat));
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
// Decrement channel value
|
|
205
|
+
const decrement = () => {
|
|
206
|
+
const chan = channel();
|
|
207
|
+
const currentColor = value();
|
|
208
|
+
if (!currentColor || !chan) return;
|
|
209
|
+
|
|
210
|
+
const currentVal = currentColor.getChannelValue(chan);
|
|
211
|
+
const range = currentColor.getChannelRange(chan);
|
|
212
|
+
const newVal = Math.max(range.minValue, currentVal - range.step);
|
|
213
|
+
const newColor = currentColor.withChannelValue(chan, newVal);
|
|
214
|
+
|
|
215
|
+
updateValue(newColor);
|
|
216
|
+
setInputValueInternal(formatColorValue(newColor, chan, getOptions().colorFormat));
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
// Increment to max
|
|
220
|
+
const incrementToMax = () => {
|
|
221
|
+
const chan = channel();
|
|
222
|
+
const currentColor = value();
|
|
223
|
+
if (!currentColor || !chan) return;
|
|
224
|
+
|
|
225
|
+
const range = currentColor.getChannelRange(chan);
|
|
226
|
+
const newColor = currentColor.withChannelValue(chan, range.maxValue);
|
|
227
|
+
|
|
228
|
+
updateValue(newColor);
|
|
229
|
+
setInputValueInternal(formatColorValue(newColor, chan, getOptions().colorFormat));
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
// Decrement to min
|
|
233
|
+
const decrementToMin = () => {
|
|
234
|
+
const chan = channel();
|
|
235
|
+
const currentColor = value();
|
|
236
|
+
if (!currentColor || !chan) return;
|
|
237
|
+
|
|
238
|
+
const range = currentColor.getChannelRange(chan);
|
|
239
|
+
const newColor = currentColor.withChannelValue(chan, range.minValue);
|
|
240
|
+
|
|
241
|
+
updateValue(newColor);
|
|
242
|
+
setInputValueInternal(formatColorValue(newColor, chan, getOptions().colorFormat));
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
// Validate input
|
|
246
|
+
const validate = () => {
|
|
247
|
+
const text = inputValue().trim();
|
|
248
|
+
const chan = channel();
|
|
249
|
+
|
|
250
|
+
if (!text) return true;
|
|
251
|
+
|
|
252
|
+
try {
|
|
253
|
+
if (chan) {
|
|
254
|
+
const numValue = parseFloat(text);
|
|
255
|
+
return !isNaN(numValue);
|
|
256
|
+
} else {
|
|
257
|
+
parseColor(text);
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
} catch {
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
get value() {
|
|
267
|
+
return value();
|
|
268
|
+
},
|
|
269
|
+
get inputValue() {
|
|
270
|
+
return inputValue();
|
|
271
|
+
},
|
|
272
|
+
get isInvalid() {
|
|
273
|
+
return isInvalid();
|
|
274
|
+
},
|
|
275
|
+
get isDisabled() {
|
|
276
|
+
return isDisabled();
|
|
277
|
+
},
|
|
278
|
+
get isReadOnly() {
|
|
279
|
+
return isReadOnly();
|
|
280
|
+
},
|
|
281
|
+
get channel() {
|
|
282
|
+
return channel();
|
|
283
|
+
},
|
|
284
|
+
setInputValue,
|
|
285
|
+
commit,
|
|
286
|
+
increment,
|
|
287
|
+
decrement,
|
|
288
|
+
incrementToMax,
|
|
289
|
+
decrementToMin,
|
|
290
|
+
validate,
|
|
291
|
+
};
|
|
292
|
+
}
|