@proyecto-viviana/solid-stately 0.2.7 → 0.3.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.
- package/README.md +30 -283
- package/dist/autocomplete/createAutocompleteState.d.ts +2 -2
- package/dist/autocomplete/index.d.ts +1 -1
- package/dist/calendar/createCalendarState.d.ts +22 -7
- package/dist/calendar/createDateFieldState.d.ts +30 -7
- package/dist/calendar/createDatePickerState.d.ts +60 -0
- package/dist/calendar/createRangeCalendarState.d.ts +27 -8
- package/dist/calendar/createTimeFieldState.d.ts +34 -7
- package/dist/calendar/index.d.ts +7 -6
- package/dist/checkbox/createCheckboxGroupState.d.ts +6 -6
- package/dist/checkbox/index.d.ts +1 -1
- package/dist/collections/ListCollection.d.ts +1 -1
- package/dist/collections/createListState.d.ts +8 -8
- package/dist/collections/createMenuState.d.ts +7 -7
- package/dist/collections/createSelectionState.d.ts +5 -5
- package/dist/collections/index.d.ts +5 -5
- package/dist/collections/types.d.ts +12 -12
- package/dist/color/Color.d.ts +1 -1
- package/dist/color/createColorAreaState.d.ts +4 -2
- package/dist/color/createColorFieldState.d.ts +38 -12
- package/dist/color/createColorSliderState.d.ts +9 -2
- package/dist/color/createColorWheelState.d.ts +13 -2
- package/dist/color/getColorChannels.d.ts +1 -1
- package/dist/color/index.d.ts +6 -6
- package/dist/color/types.d.ts +4 -4
- package/dist/combobox/createComboBoxState.d.ts +27 -13
- package/dist/combobox/index.d.ts +1 -1
- package/dist/data/createAsyncList.d.ts +4 -4
- package/dist/data/createListData.d.ts +2 -2
- package/dist/data/createTreeData.d.ts +6 -2
- package/dist/data/index.d.ts +3 -3
- package/dist/datepicker/index.d.ts +2 -4
- package/dist/disclosure/createDisclosureState.d.ts +2 -2
- package/dist/disclosure/index.d.ts +1 -1
- package/dist/dnd/createDragState.d.ts +2 -2
- package/dist/dnd/createDraggableCollectionState.d.ts +2 -2
- package/dist/dnd/createDropState.d.ts +2 -2
- package/dist/dnd/createDroppableCollectionState.d.ts +3 -3
- package/dist/dnd/index.d.ts +5 -5
- package/dist/dnd/types.d.ts +16 -16
- package/dist/form/createFormValidationState.d.ts +3 -3
- package/dist/form/index.d.ts +1 -1
- package/dist/grid/createGridState.d.ts +2 -2
- package/dist/grid/index.d.ts +2 -2
- package/dist/grid/types.d.ts +13 -11
- package/dist/index.d.ts +31 -30
- package/dist/index.js +7924 -7238
- package/dist/index.js.map +1 -1
- package/dist/menu/index.d.ts +2 -2
- package/dist/numberfield/createNumberFieldState.d.ts +2 -2
- package/dist/numberfield/index.d.ts +1 -1
- package/dist/overlays/createOverlayTriggerState.d.ts +2 -2
- package/dist/overlays/index.d.ts +1 -1
- package/dist/radio/createRadioGroupState.d.ts +7 -7
- package/dist/radio/index.d.ts +1 -1
- package/dist/searchfield/createSearchFieldState.d.ts +2 -2
- package/dist/searchfield/index.d.ts +2 -2
- package/dist/select/createSelectState.d.ts +8 -8
- package/dist/select/index.d.ts +1 -1
- package/dist/selection/index.d.ts +5 -5
- package/dist/slider/createSliderState.d.ts +3 -3
- package/dist/slider/index.d.ts +2 -2
- package/dist/steplist/createStepListState.d.ts +48 -0
- package/dist/steplist/index.d.ts +1 -0
- package/dist/table/TableCollection.d.ts +3 -3
- package/dist/table/createTableColumnResizeState.d.ts +54 -0
- package/dist/table/createTableState.d.ts +2 -2
- package/dist/table/index.d.ts +5 -3
- package/dist/table/types.d.ts +10 -10
- package/dist/tabs/createTabListState.d.ts +5 -5
- package/dist/tabs/index.d.ts +1 -1
- package/dist/textfield/createTextFieldState.d.ts +2 -2
- package/dist/textfield/index.d.ts +1 -1
- package/dist/toast/createToastState.d.ts +8 -2
- package/dist/toast/index.d.ts +1 -1
- package/dist/toggle/createToggleGroupState.d.ts +4 -4
- package/dist/toggle/createToggleState.d.ts +2 -2
- package/dist/toggle/index.d.ts +2 -2
- package/dist/tooltip/createTooltipTriggerState.d.ts +2 -2
- package/dist/tooltip/index.d.ts +1 -1
- package/dist/tree/TreeCollection.d.ts +2 -2
- package/dist/tree/createTreeState.d.ts +2 -2
- package/dist/tree/index.d.ts +3 -3
- package/dist/tree/types.d.ts +19 -13
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/reactivity.d.ts +1 -1
- package/package.json +29 -30
- package/src/autocomplete/createAutocompleteState.d.ts +19 -17
- package/src/autocomplete/createAutocompleteState.ts +24 -30
- package/src/autocomplete/index.d.ts +6 -2
- package/src/autocomplete/index.ts +1 -1
- package/src/calendar/createCalendarState.d.ts +139 -117
- package/src/calendar/createCalendarState.ts +221 -74
- package/src/calendar/createDateFieldState.d.ts +130 -95
- package/src/calendar/createDateFieldState.ts +263 -151
- package/src/calendar/createDatePickerState.ts +347 -0
- package/src/calendar/createRangeCalendarState.d.ts +154 -129
- package/src/calendar/createRangeCalendarState.ts +293 -79
- package/src/calendar/createTimeFieldState.d.ts +81 -79
- package/src/calendar/createTimeFieldState.ts +187 -108
- package/src/calendar/index.d.ts +80 -7
- package/src/calendar/index.ts +21 -12
- package/src/checkbox/createCheckboxGroupState.d.ts +57 -55
- package/src/checkbox/createCheckboxGroupState.ts +20 -29
- package/src/checkbox/index.d.ts +6 -2
- package/src/checkbox/index.ts +1 -1
- package/src/collections/ListCollection.d.ts +25 -22
- package/src/collections/ListCollection.ts +6 -6
- package/src/collections/createListState.d.ts +73 -58
- package/src/collections/createListState.ts +19 -18
- package/src/collections/createMenuState.d.ts +36 -31
- package/src/collections/createMenuState.ts +18 -12
- package/src/collections/createSelectionState.d.ts +78 -65
- package/src/collections/createSelectionState.ts +36 -32
- package/src/collections/index.d.ts +38 -6
- package/src/collections/index.ts +5 -10
- package/src/collections/types.d.ts +101 -101
- package/src/collections/types.ts +12 -12
- package/src/color/Color.d.ts +20 -5
- package/src/color/Color.ts +357 -254
- package/src/color/createColorAreaState.d.ts +67 -65
- package/src/color/createColorAreaState.ts +21 -13
- package/src/color/createColorFieldState.d.ts +46 -44
- package/src/color/createColorFieldState.ts +285 -149
- package/src/color/createColorSliderState.d.ts +58 -56
- package/src/color/createColorSliderState.ts +45 -14
- package/src/color/createColorWheelState.d.ts +42 -40
- package/src/color/createColorWheelState.ts +101 -64
- package/src/color/getColorChannels.ts +7 -5
- package/src/color/index.d.ts +36 -7
- package/src/color/index.ts +6 -9
- package/src/color/types.d.ts +68 -63
- package/src/color/types.ts +11 -19
- package/src/combobox/createComboBoxState.d.ts +107 -105
- package/src/combobox/createComboBoxState.ts +176 -89
- package/src/combobox/index.d.ts +10 -2
- package/src/combobox/index.ts +1 -1
- package/src/data/createAsyncList.ts +89 -63
- package/src/data/createListData.ts +67 -67
- package/src/data/createTreeData.ts +304 -91
- package/src/data/index.ts +3 -3
- package/src/datepicker/index.ts +6 -10
- package/src/disclosure/createDisclosureState.d.ts +47 -43
- package/src/disclosure/createDisclosureState.ts +7 -21
- package/src/disclosure/index.d.ts +10 -2
- package/src/disclosure/index.ts +1 -1
- package/src/dnd/createDragState.d.ts +51 -42
- package/src/dnd/createDragState.ts +12 -14
- package/src/dnd/createDraggableCollectionState.d.ts +52 -41
- package/src/dnd/createDraggableCollectionState.ts +14 -21
- package/src/dnd/createDropState.d.ts +64 -45
- package/src/dnd/createDropState.ts +22 -32
- package/src/dnd/createDroppableCollectionState.d.ts +88 -59
- package/src/dnd/createDroppableCollectionState.ts +40 -43
- package/src/dnd/index.d.ts +54 -6
- package/src/dnd/index.ts +5 -18
- package/src/dnd/types.d.ts +167 -156
- package/src/dnd/types.ts +20 -21
- package/src/form/createFormValidationState.d.ts +47 -45
- package/src/form/createFormValidationState.ts +30 -63
- package/src/form/index.d.ts +16 -2
- package/src/form/index.ts +1 -1
- package/src/grid/createGridState.d.ts +7 -4
- package/src/grid/createGridState.ts +44 -51
- package/src/grid/index.d.ts +3 -3
- package/src/grid/index.ts +2 -8
- package/src/grid/types.d.ts +136 -129
- package/src/grid/types.ts +20 -18
- package/src/index.d.ts +310 -26
- package/src/index.ts +48 -89
- package/src/menu/index.ts +2 -4
- package/src/numberfield/createNumberFieldState.d.ts +56 -54
- package/src/numberfield/createNumberFieldState.ts +26 -31
- package/src/numberfield/index.d.ts +6 -2
- package/src/numberfield/index.ts +1 -1
- package/src/overlays/createOverlayTriggerState.d.ts +22 -20
- package/src/overlays/createOverlayTriggerState.ts +3 -4
- package/src/overlays/index.d.ts +6 -2
- package/src/overlays/index.ts +1 -1
- package/src/radio/createRadioGroupState.d.ts +54 -52
- package/src/radio/createRadioGroupState.ts +27 -29
- package/src/radio/index.d.ts +7 -2
- package/src/radio/index.ts +1 -1
- package/src/searchfield/createSearchFieldState.d.ts +16 -14
- package/src/searchfield/createSearchFieldState.ts +5 -11
- package/src/searchfield/index.d.ts +3 -3
- package/src/searchfield/index.ts +2 -5
- package/src/select/createSelectState.d.ts +63 -61
- package/src/select/createSelectState.ts +27 -27
- package/src/select/index.d.ts +2 -2
- package/src/select/index.ts +1 -5
- package/src/selection/index.ts +6 -7
- package/src/slider/createSliderState.d.ts +60 -60
- package/src/slider/createSliderState.ts +55 -33
- package/src/slider/index.d.ts +3 -3
- package/src/slider/index.ts +2 -6
- package/src/ssr/index.d.ts +1 -1
- package/src/ssr/index.ts +2 -2
- package/src/steplist/createStepListState.ts +191 -0
- package/src/steplist/index.ts +5 -0
- package/src/table/TableCollection.d.ts +42 -40
- package/src/table/TableCollection.ts +61 -64
- package/src/table/createTableColumnResizeState.ts +242 -0
- package/src/table/createTableState.d.ts +7 -4
- package/src/table/createTableState.ts +10 -7
- package/src/table/index.d.ts +14 -4
- package/src/table/index.ts +10 -3
- package/src/table/types.d.ts +98 -92
- package/src/table/types.ts +17 -13
- package/src/tabs/createTabListState.d.ts +59 -57
- package/src/tabs/createTabListState.ts +24 -31
- package/src/tabs/index.d.ts +8 -2
- package/src/tabs/index.ts +1 -1
- package/src/textfield/createTextFieldState.d.ts +16 -14
- package/src/textfield/createTextFieldState.ts +7 -18
- package/src/textfield/index.d.ts +6 -2
- package/src/textfield/index.ts +1 -1
- package/src/toast/createToastState.d.ts +92 -86
- package/src/toast/createToastState.ts +48 -43
- package/src/toast/index.d.ts +12 -2
- package/src/toast/index.ts +1 -1
- package/src/toggle/createToggleGroupState.ts +11 -21
- package/src/toggle/createToggleState.d.ts +19 -19
- package/src/toggle/createToggleState.ts +2 -16
- package/src/toggle/index.d.ts +2 -2
- package/src/toggle/index.ts +2 -6
- package/src/tooltip/createTooltipTriggerState.d.ts +17 -17
- package/src/tooltip/createTooltipTriggerState.ts +12 -11
- package/src/tooltip/index.d.ts +7 -2
- package/src/tooltip/index.ts +1 -1
- package/src/tree/TreeCollection.d.ts +26 -23
- package/src/tree/TreeCollection.ts +29 -22
- package/src/tree/createTreeState.d.ts +7 -4
- package/src/tree/createTreeState.ts +39 -41
- package/src/tree/index.d.ts +10 -4
- package/src/tree/index.ts +3 -3
- package/src/tree/types.d.ts +131 -126
- package/src/tree/types.ts +22 -13
- package/src/utils/index.d.ts +2 -2
- package/src/utils/index.ts +1 -1
- package/src/utils/reactivity.d.ts +2 -2
- package/src/utils/reactivity.ts +3 -3
|
@@ -1,144 +1,254 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ColorField state management.
|
|
3
|
-
* Based on @react-stately/color useColorFieldState.
|
|
3
|
+
* Based on @react-stately/color useColorFieldState and useColorChannelFieldState.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import type { Color, ColorChannel, ColorFormat } from
|
|
8
|
-
import { normalizeColor, parseColor } from
|
|
6
|
+
import { createEffect, createMemo, createSignal, type Accessor } from "solid-js";
|
|
7
|
+
import type { Color, ColorChannel, ColorChannelRange, ColorFormat, ColorSpace } from "./types";
|
|
8
|
+
import { createRGBColor, normalizeColor, parseColor } from "./Color";
|
|
9
|
+
|
|
10
|
+
const HEX_COLOR_MAX = 0xffffff;
|
|
11
|
+
const PARTIAL_HEX_PATTERN = /^#?[0-9a-f]{0,6}$/i;
|
|
9
12
|
|
|
10
13
|
export interface ColorFieldStateOptions {
|
|
11
14
|
/** The current color value (controlled). */
|
|
12
15
|
value?: Color | string | null;
|
|
13
16
|
/** The default color value (uncontrolled). */
|
|
14
|
-
defaultValue?: Color | string;
|
|
17
|
+
defaultValue?: Color | string | null;
|
|
15
18
|
/** Handler called when the color changes. */
|
|
16
19
|
onChange?: (color: Color | null) => void;
|
|
17
20
|
/** The color channel to edit (for single channel mode). */
|
|
18
21
|
channel?: ColorChannel;
|
|
19
|
-
/** The color
|
|
22
|
+
/** The color space to use for channel mode. */
|
|
23
|
+
colorSpace?: ColorSpace;
|
|
24
|
+
/** The color format for parsing/displaying in hex mode. */
|
|
20
25
|
colorFormat?: ColorFormat;
|
|
26
|
+
/** Locale used to format channel values. */
|
|
27
|
+
locale?: string;
|
|
21
28
|
/** Whether the field is disabled. */
|
|
22
29
|
isDisabled?: boolean;
|
|
23
30
|
/** Whether the field is read-only. */
|
|
24
31
|
isReadOnly?: boolean;
|
|
32
|
+
/** Whether the field is invalid. */
|
|
33
|
+
isInvalid?: boolean;
|
|
34
|
+
/** Whether the field is required. */
|
|
35
|
+
isRequired?: boolean;
|
|
25
36
|
}
|
|
26
37
|
|
|
27
38
|
export interface ColorFieldState {
|
|
28
|
-
/** The current color value (null if
|
|
39
|
+
/** The current color value (null if empty). */
|
|
29
40
|
readonly value: Color | null;
|
|
41
|
+
/** The color used for channel calculations. */
|
|
42
|
+
readonly colorValue: Color;
|
|
30
43
|
/** The current input text. */
|
|
31
44
|
readonly inputValue: string;
|
|
45
|
+
/** The numeric value exposed by channel fields. */
|
|
46
|
+
readonly numberValue: number;
|
|
47
|
+
/** The channel minimum value after formatting normalization. */
|
|
48
|
+
readonly minValue: number;
|
|
49
|
+
/** The channel maximum value after formatting normalization. */
|
|
50
|
+
readonly maxValue: number;
|
|
51
|
+
/** The channel keyboard step after formatting normalization. */
|
|
52
|
+
readonly step: number;
|
|
53
|
+
/** The channel page step after formatting normalization. */
|
|
54
|
+
readonly pageSize: number;
|
|
55
|
+
/** Number formatter options for channel fields. */
|
|
56
|
+
readonly formatOptions: Intl.NumberFormatOptions;
|
|
32
57
|
/** Whether the input is invalid. */
|
|
33
58
|
readonly isInvalid: boolean;
|
|
34
59
|
/** Whether the field is disabled. */
|
|
35
60
|
readonly isDisabled: boolean;
|
|
36
61
|
/** Whether the field is read-only. */
|
|
37
62
|
readonly isReadOnly: boolean;
|
|
63
|
+
/** Whether the field is required. */
|
|
64
|
+
readonly isRequired: boolean;
|
|
38
65
|
/** The color channel being edited (if single channel mode). */
|
|
39
66
|
readonly channel: ColorChannel | undefined;
|
|
40
67
|
|
|
41
68
|
/** Set the input text value. */
|
|
42
69
|
setInputValue(value: string): void;
|
|
70
|
+
/** Set the color value. */
|
|
71
|
+
setColorValue(value: Color | null): void;
|
|
43
72
|
/** Commit the current input value. */
|
|
44
73
|
commit(): void;
|
|
45
|
-
/** Increment the color
|
|
74
|
+
/** Increment the color value. */
|
|
46
75
|
increment(): void;
|
|
47
|
-
/** Decrement the color
|
|
76
|
+
/** Decrement the color value. */
|
|
48
77
|
decrement(): void;
|
|
49
|
-
/** Increment by page size. */
|
|
78
|
+
/** Increment by page size or to the maximum. */
|
|
50
79
|
incrementToMax(): void;
|
|
51
|
-
/** Decrement to minimum. */
|
|
80
|
+
/** Decrement by page size or to the minimum. */
|
|
52
81
|
decrementToMin(): void;
|
|
53
|
-
/** Validate the current input. */
|
|
54
|
-
validate(): boolean;
|
|
82
|
+
/** Validate the current input or a candidate input. */
|
|
83
|
+
validate(value?: string): boolean;
|
|
55
84
|
}
|
|
56
85
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const getOptions = () => options();
|
|
86
|
+
function normalizeNullableColor(value: Color | string | null | undefined): Color | null {
|
|
87
|
+
if (value == null) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
return normalizeColor(value);
|
|
91
|
+
}
|
|
64
92
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
93
|
+
function getDefaultValue(options: ColorFieldStateOptions): Color | null {
|
|
94
|
+
if (options.defaultValue !== undefined) {
|
|
95
|
+
return normalizeNullableColor(options.defaultValue);
|
|
96
|
+
}
|
|
69
97
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
98
|
+
if (options.value !== undefined) {
|
|
99
|
+
return normalizeNullableColor(options.value);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function convertColor(color: Color, colorSpace: ColorSpace | undefined): Color {
|
|
106
|
+
return colorSpace ? color.toFormat(colorSpace) : color;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function getDisplayColor(color: Color | null, colorSpace: ColorSpace | undefined): Color {
|
|
110
|
+
return convertColor(color ?? createRGBColor(0, 0, 0), colorSpace);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function getChannelMultiplier(color: Color, channel: ColorChannel): number {
|
|
114
|
+
const range = color.getChannelRange(channel);
|
|
115
|
+
const formatOptions = color.getChannelFormatOptions(channel);
|
|
116
|
+
return formatOptions.style === "percent" && range.maxValue === 100 ? 100 : 1;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function getNormalizedRange(color: Color, channel: ColorChannel): ColorChannelRange {
|
|
120
|
+
const multiplier = getChannelMultiplier(color, channel);
|
|
121
|
+
const range = color.getChannelRange(channel);
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
minValue: range.minValue / multiplier,
|
|
125
|
+
maxValue: range.maxValue / multiplier,
|
|
126
|
+
step: range.step / multiplier,
|
|
127
|
+
pageSize: range.pageSize / multiplier,
|
|
78
128
|
};
|
|
129
|
+
}
|
|
79
130
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
131
|
+
function formatColorValue(
|
|
132
|
+
color: Color | null,
|
|
133
|
+
channel: ColorChannel | undefined,
|
|
134
|
+
colorSpace: ColorSpace | undefined,
|
|
135
|
+
format: ColorFormat | undefined,
|
|
136
|
+
locale: string,
|
|
137
|
+
): string {
|
|
138
|
+
if (!color) {
|
|
139
|
+
return "";
|
|
87
140
|
}
|
|
88
141
|
|
|
89
|
-
|
|
142
|
+
const displayColor = getDisplayColor(color, colorSpace);
|
|
143
|
+
|
|
144
|
+
if (channel) {
|
|
145
|
+
return displayColor.formatChannelValue(channel, locale);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return displayColor.toString(format ?? "hex");
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function normalizeHexInput(value: string): string {
|
|
152
|
+
return value.startsWith("#") ? value : `#${value}`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function parseHexValue(value: string, format: ColorFormat | undefined): Color {
|
|
156
|
+
const color = parseColor(normalizeHexInput(value));
|
|
157
|
+
return format ? color.toFormat(format) : color;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function parseChannelValue(text: string, color: Color, channel: ColorChannel): number | null {
|
|
161
|
+
const trimmed = text.trim();
|
|
162
|
+
const hasPercent = trimmed.endsWith("%");
|
|
163
|
+
const parsed = Number.parseFloat(hasPercent ? trimmed.slice(0, -1) : trimmed);
|
|
164
|
+
|
|
165
|
+
if (Number.isNaN(parsed)) {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const range = color.getChannelRange(channel);
|
|
170
|
+
const formatOptions = color.getChannelFormatOptions(channel);
|
|
171
|
+
const multiplier = getChannelMultiplier(color, channel);
|
|
172
|
+
const rawValue = hasPercent && formatOptions.style === "percent" ? parsed : parsed * multiplier;
|
|
173
|
+
|
|
174
|
+
return Math.min(range.maxValue, Math.max(range.minValue, rawValue));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function hexIntToColor(value: number): Color {
|
|
178
|
+
const clamped = Math.min(HEX_COLOR_MAX, Math.max(0, Math.round(value)));
|
|
179
|
+
const red = (clamped >> 16) & 0xff;
|
|
180
|
+
const green = (clamped >> 8) & 0xff;
|
|
181
|
+
const blue = clamped & 0xff;
|
|
182
|
+
return createRGBColor(red, green, blue);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Creates state for a color field (text input for color values).
|
|
187
|
+
*/
|
|
188
|
+
export function createColorFieldState(options: Accessor<ColorFieldStateOptions>): ColorFieldState {
|
|
189
|
+
const getOptions = () => options();
|
|
190
|
+
const initialOptions = getOptions();
|
|
191
|
+
const initialValue = getDefaultValue(initialOptions);
|
|
192
|
+
|
|
193
|
+
const [internalValue, setInternalValue] = createSignal<Color | null>(initialValue);
|
|
194
|
+
const [inputValue, setInputValueInternal] = createSignal(
|
|
195
|
+
formatColorValue(
|
|
196
|
+
initialValue,
|
|
197
|
+
initialOptions.channel,
|
|
198
|
+
initialOptions.colorSpace,
|
|
199
|
+
initialOptions.colorFormat,
|
|
200
|
+
initialOptions.locale ?? "en-US",
|
|
201
|
+
),
|
|
202
|
+
);
|
|
203
|
+
const [invalidInput, setInvalidInput] = createSignal(false);
|
|
204
|
+
|
|
205
|
+
const channel = createMemo(() => getOptions().channel);
|
|
206
|
+
const colorSpace = createMemo(() => getOptions().colorSpace);
|
|
207
|
+
const locale = createMemo(() => getOptions().locale ?? "en-US");
|
|
208
|
+
|
|
90
209
|
const value = createMemo(() => {
|
|
91
210
|
const opts = getOptions();
|
|
92
211
|
if (opts.value !== undefined) {
|
|
93
|
-
|
|
94
|
-
return normalizeColor(opts.value);
|
|
212
|
+
return normalizeNullableColor(opts.value);
|
|
95
213
|
}
|
|
96
214
|
return internalValue();
|
|
97
215
|
});
|
|
98
216
|
|
|
99
|
-
const
|
|
217
|
+
const colorValue = createMemo(() => getDisplayColor(value(), colorSpace()));
|
|
100
218
|
const isDisabled = createMemo(() => getOptions().isDisabled ?? false);
|
|
101
219
|
const isReadOnly = createMemo(() => getOptions().isReadOnly ?? false);
|
|
220
|
+
const isRequired = createMemo(() => getOptions().isRequired ?? false);
|
|
221
|
+
const isInvalid = createMemo(() => (getOptions().isInvalid ?? false) || invalidInput());
|
|
102
222
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
color: Color | null,
|
|
106
|
-
chan?: ColorChannel,
|
|
107
|
-
format?: ColorFormat
|
|
108
|
-
): string {
|
|
109
|
-
if (!color) return '';
|
|
223
|
+
const formatCurrentValue = (color: Color | null = value()) =>
|
|
224
|
+
formatColorValue(color, channel(), colorSpace(), getOptions().colorFormat, locale());
|
|
110
225
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
// Full color mode
|
|
117
|
-
return color.toString(format ?? 'hex');
|
|
118
|
-
}
|
|
226
|
+
createEffect(() => {
|
|
227
|
+
setInputValueInternal(formatCurrentValue());
|
|
228
|
+
setInvalidInput(false);
|
|
229
|
+
});
|
|
119
230
|
|
|
120
|
-
// Update value
|
|
121
231
|
const updateValue = (newColor: Color | null) => {
|
|
122
232
|
const opts = getOptions();
|
|
123
233
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
opts.onChange?.(newColor);
|
|
127
|
-
return;
|
|
234
|
+
if (opts.value === undefined) {
|
|
235
|
+
setInternalValue(newColor);
|
|
128
236
|
}
|
|
129
237
|
|
|
130
|
-
// Uncontrolled mode
|
|
131
|
-
setInternalValue(newColor);
|
|
132
238
|
opts.onChange?.(newColor);
|
|
239
|
+
setInputValueInternal(formatCurrentValue(newColor));
|
|
240
|
+
setInvalidInput(false);
|
|
133
241
|
};
|
|
134
242
|
|
|
135
|
-
// Set input value
|
|
136
243
|
const setInputValue = (text: string) => {
|
|
137
244
|
setInputValueInternal(text);
|
|
138
|
-
|
|
245
|
+
setInvalidInput(false);
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
const setColorValue = (newColor: Color | null) => {
|
|
249
|
+
updateValue(newColor);
|
|
139
250
|
};
|
|
140
251
|
|
|
141
|
-
// Commit the input value
|
|
142
252
|
const commit = () => {
|
|
143
253
|
const text = inputValue().trim();
|
|
144
254
|
const opts = getOptions();
|
|
@@ -146,129 +256,151 @@ export function createColorFieldState(
|
|
|
146
256
|
|
|
147
257
|
if (!text) {
|
|
148
258
|
updateValue(null);
|
|
149
|
-
setIsInvalid(false);
|
|
150
259
|
return;
|
|
151
260
|
}
|
|
152
261
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if (
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
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
|
-
}
|
|
262
|
+
if (chan) {
|
|
263
|
+
const displayColor = colorValue();
|
|
264
|
+
const rawValue = parseChannelValue(text, displayColor, chan);
|
|
265
|
+
if (rawValue == null) {
|
|
266
|
+
setInputValueInternal(formatCurrentValue());
|
|
267
|
+
setInvalidInput(false);
|
|
268
|
+
return;
|
|
179
269
|
}
|
|
180
270
|
|
|
181
|
-
updateValue(
|
|
182
|
-
|
|
183
|
-
|
|
271
|
+
updateValue(displayColor.withChannelValue(chan, rawValue));
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
try {
|
|
276
|
+
updateValue(parseHexValue(text, opts.colorFormat));
|
|
184
277
|
} catch {
|
|
185
|
-
|
|
278
|
+
setInputValueInternal(formatCurrentValue());
|
|
279
|
+
setInvalidInput(false);
|
|
186
280
|
}
|
|
187
281
|
};
|
|
188
282
|
|
|
189
|
-
|
|
190
|
-
const increment = () => {
|
|
191
|
-
const chan = channel();
|
|
283
|
+
const incrementHex = (amount: number) => {
|
|
192
284
|
const currentColor = value();
|
|
193
|
-
if (!currentColor
|
|
285
|
+
if (!currentColor) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
194
288
|
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
const newColor = currentColor.withChannelValue(chan, newVal);
|
|
289
|
+
const newColor = hexIntToColor(currentColor.toFormat("rgb").toHexInt() + amount);
|
|
290
|
+
updateValue(getOptions().colorFormat ? newColor.toFormat(getOptions().colorFormat!) : newColor);
|
|
291
|
+
};
|
|
199
292
|
|
|
200
|
-
|
|
201
|
-
|
|
293
|
+
const updateChannel = (amount: number | "min" | "max") => {
|
|
294
|
+
const chan = channel();
|
|
295
|
+
if (!chan) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const displayColor = colorValue();
|
|
300
|
+
const range = displayColor.getChannelRange(chan);
|
|
301
|
+
const currentValue = displayColor.getChannelValue(chan);
|
|
302
|
+
const nextValue =
|
|
303
|
+
amount === "min"
|
|
304
|
+
? range.minValue
|
|
305
|
+
: amount === "max"
|
|
306
|
+
? range.maxValue
|
|
307
|
+
: Math.min(range.maxValue, Math.max(range.minValue, currentValue + amount));
|
|
308
|
+
|
|
309
|
+
updateValue(displayColor.withChannelValue(chan, nextValue));
|
|
202
310
|
};
|
|
203
311
|
|
|
204
|
-
|
|
205
|
-
const decrement = () => {
|
|
312
|
+
const increment = () => {
|
|
206
313
|
const chan = channel();
|
|
207
|
-
|
|
208
|
-
|
|
314
|
+
if (!chan) {
|
|
315
|
+
incrementHex(1);
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
209
318
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
const newVal = Math.max(range.minValue, currentVal - range.step);
|
|
213
|
-
const newColor = currentColor.withChannelValue(chan, newVal);
|
|
319
|
+
updateChannel(colorValue().getChannelRange(chan).step);
|
|
320
|
+
};
|
|
214
321
|
|
|
215
|
-
|
|
216
|
-
|
|
322
|
+
const decrement = () => {
|
|
323
|
+
const chan = channel();
|
|
324
|
+
if (!chan) {
|
|
325
|
+
incrementHex(-1);
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
updateChannel(-colorValue().getChannelRange(chan).step);
|
|
217
330
|
};
|
|
218
331
|
|
|
219
|
-
// Increment to max
|
|
220
332
|
const incrementToMax = () => {
|
|
221
333
|
const chan = channel();
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
const newColor = currentColor.withChannelValue(chan, range.maxValue);
|
|
334
|
+
if (!chan) {
|
|
335
|
+
incrementHex(HEX_COLOR_MAX);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
227
338
|
|
|
228
|
-
|
|
229
|
-
setInputValueInternal(formatColorValue(newColor, chan, getOptions().colorFormat));
|
|
339
|
+
updateChannel("max");
|
|
230
340
|
};
|
|
231
341
|
|
|
232
|
-
// Decrement to min
|
|
233
342
|
const decrementToMin = () => {
|
|
234
343
|
const chan = channel();
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
const newColor = currentColor.withChannelValue(chan, range.minValue);
|
|
344
|
+
if (!chan) {
|
|
345
|
+
incrementHex(-HEX_COLOR_MAX);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
240
348
|
|
|
241
|
-
|
|
242
|
-
setInputValueInternal(formatColorValue(newColor, chan, getOptions().colorFormat));
|
|
349
|
+
updateChannel("min");
|
|
243
350
|
};
|
|
244
351
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
const text = inputValue().trim();
|
|
352
|
+
const validate = (candidate?: string) => {
|
|
353
|
+
const text = (candidate ?? inputValue()).trim();
|
|
248
354
|
const chan = channel();
|
|
249
355
|
|
|
250
|
-
if (!text)
|
|
356
|
+
if (!text) {
|
|
357
|
+
return true;
|
|
358
|
+
}
|
|
251
359
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
const numValue = parseFloat(text);
|
|
255
|
-
return !isNaN(numValue);
|
|
256
|
-
} else {
|
|
257
|
-
parseColor(text);
|
|
258
|
-
return true;
|
|
259
|
-
}
|
|
260
|
-
} catch {
|
|
261
|
-
return false;
|
|
360
|
+
if (chan) {
|
|
361
|
+
return parseChannelValue(text, colorValue(), chan) != null;
|
|
262
362
|
}
|
|
363
|
+
|
|
364
|
+
return PARTIAL_HEX_PATTERN.test(text);
|
|
263
365
|
};
|
|
264
366
|
|
|
265
367
|
return {
|
|
266
368
|
get value() {
|
|
267
369
|
return value();
|
|
268
370
|
},
|
|
371
|
+
get colorValue() {
|
|
372
|
+
return colorValue();
|
|
373
|
+
},
|
|
269
374
|
get inputValue() {
|
|
270
375
|
return inputValue();
|
|
271
376
|
},
|
|
377
|
+
get numberValue() {
|
|
378
|
+
const chan = channel();
|
|
379
|
+
if (!chan || value() === null) {
|
|
380
|
+
return Number.NaN;
|
|
381
|
+
}
|
|
382
|
+
return colorValue().getChannelValue(chan) / getChannelMultiplier(colorValue(), chan);
|
|
383
|
+
},
|
|
384
|
+
get minValue() {
|
|
385
|
+
const chan = channel();
|
|
386
|
+
return chan ? getNormalizedRange(colorValue(), chan).minValue : 0;
|
|
387
|
+
},
|
|
388
|
+
get maxValue() {
|
|
389
|
+
const chan = channel();
|
|
390
|
+
return chan ? getNormalizedRange(colorValue(), chan).maxValue : HEX_COLOR_MAX;
|
|
391
|
+
},
|
|
392
|
+
get step() {
|
|
393
|
+
const chan = channel();
|
|
394
|
+
return chan ? getNormalizedRange(colorValue(), chan).step : 1;
|
|
395
|
+
},
|
|
396
|
+
get pageSize() {
|
|
397
|
+
const chan = channel();
|
|
398
|
+
return chan ? getNormalizedRange(colorValue(), chan).pageSize : 0x100000;
|
|
399
|
+
},
|
|
400
|
+
get formatOptions() {
|
|
401
|
+
const chan = channel();
|
|
402
|
+
return chan ? colorValue().getChannelFormatOptions(chan) : {};
|
|
403
|
+
},
|
|
272
404
|
get isInvalid() {
|
|
273
405
|
return isInvalid();
|
|
274
406
|
},
|
|
@@ -278,10 +410,14 @@ export function createColorFieldState(
|
|
|
278
410
|
get isReadOnly() {
|
|
279
411
|
return isReadOnly();
|
|
280
412
|
},
|
|
413
|
+
get isRequired() {
|
|
414
|
+
return isRequired();
|
|
415
|
+
},
|
|
281
416
|
get channel() {
|
|
282
417
|
return channel();
|
|
283
418
|
},
|
|
284
419
|
setInputValue,
|
|
420
|
+
setColorValue,
|
|
285
421
|
commit,
|
|
286
422
|
increment,
|
|
287
423
|
decrement,
|