@funstack/ui 0.1.0-alpha.1 → 0.1.0-alpha.2
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/dist/combobox/combobox-clear.d.mts +34 -0
- package/dist/combobox/combobox-clear.d.mts.map +1 -0
- package/dist/combobox/combobox-clear.mjs +58 -0
- package/dist/combobox/combobox-clear.mjs.map +1 -0
- package/dist/combobox/combobox-context.mjs +23 -0
- package/dist/combobox/combobox-context.mjs.map +1 -0
- package/dist/combobox/combobox-group.d.mts +72 -0
- package/dist/combobox/combobox-group.d.mts.map +1 -0
- package/dist/combobox/combobox-group.mjs +104 -0
- package/dist/combobox/combobox-group.mjs.map +1 -0
- package/dist/combobox/combobox-input.d.mts +40 -0
- package/dist/combobox/combobox-input.d.mts.map +1 -0
- package/dist/combobox/combobox-input.mjs +88 -0
- package/dist/combobox/combobox-input.mjs.map +1 -0
- package/dist/combobox/combobox-label.d.mts +22 -0
- package/dist/combobox/combobox-label.d.mts.map +1 -0
- package/dist/combobox/combobox-label.mjs +32 -0
- package/dist/combobox/combobox-label.mjs.map +1 -0
- package/dist/combobox/combobox-list.d.mts +37 -0
- package/dist/combobox/combobox-list.d.mts.map +1 -0
- package/dist/combobox/combobox-list.mjs +70 -0
- package/dist/combobox/combobox-list.mjs.map +1 -0
- package/dist/combobox/combobox-option.d.mts +45 -0
- package/dist/combobox/combobox-option.d.mts.map +1 -0
- package/dist/combobox/combobox-option.mjs +88 -0
- package/dist/combobox/combobox-option.mjs.map +1 -0
- package/dist/combobox/combobox-popup.d.mts +49 -0
- package/dist/combobox/combobox-popup.d.mts.map +1 -0
- package/dist/combobox/combobox-popup.mjs +86 -0
- package/dist/combobox/combobox-popup.mjs.map +1 -0
- package/dist/combobox/combobox-root.d.mts +135 -0
- package/dist/combobox/combobox-root.d.mts.map +1 -0
- package/dist/combobox/combobox-root.mjs +508 -0
- package/dist/combobox/combobox-root.mjs.map +1 -0
- package/dist/combobox/combobox-trigger.d.mts +36 -0
- package/dist/combobox/combobox-trigger.d.mts.map +1 -0
- package/dist/combobox/combobox-trigger.mjs +92 -0
- package/dist/combobox/combobox-trigger.mjs.map +1 -0
- package/dist/combobox/index.d.mts +36 -0
- package/dist/combobox/index.d.mts.map +1 -0
- package/dist/combobox/index.mjs +36 -0
- package/dist/combobox/index.mjs.map +1 -0
- package/dist/index.d.mts +20 -2
- package/dist/index.mjs +3 -1
- package/dist/internal/platform-attributes.d.mts +12 -1
- package/dist/internal/platform-attributes.d.mts.map +1 -1
- package/dist/internal/types.d.mts +8 -0
- package/dist/internal/types.d.mts.map +1 -1
- package/dist/internal/use-controllable-action.mjs +5 -1
- package/dist/internal/use-controllable-action.mjs.map +1 -1
- package/dist/internal/use-form-reset.mjs +5 -1
- package/dist/internal/use-form-reset.mjs.map +1 -1
- package/dist/internal/use-hydrated-mount.mjs +21 -0
- package/dist/internal/use-hydrated-mount.mjs.map +1 -0
- package/dist/internal/use-native-group-reconciliation.mjs.map +1 -1
- package/dist/internal/use-native-select-value.mjs +182 -0
- package/dist/internal/use-native-select-value.mjs.map +1 -0
- package/dist/select/index.d.mts +30 -0
- package/dist/select/index.d.mts.map +1 -0
- package/dist/select/index.mjs +30 -0
- package/dist/select/index.mjs.map +1 -0
- package/dist/select/select-context.mjs +13 -0
- package/dist/select/select-context.mjs.map +1 -0
- package/dist/select/select-dom.mjs +44 -0
- package/dist/select/select-dom.mjs.map +1 -0
- package/dist/select/select-group-label.d.mts +22 -0
- package/dist/select/select-group-label.d.mts.map +1 -0
- package/dist/select/select-group-label.mjs +39 -0
- package/dist/select/select-group-label.mjs.map +1 -0
- package/dist/select/select-group.d.mts +28 -0
- package/dist/select/select-group.d.mts.map +1 -0
- package/dist/select/select-group.mjs +45 -0
- package/dist/select/select-group.mjs.map +1 -0
- package/dist/select/select-option.d.mts +42 -0
- package/dist/select/select-option.d.mts.map +1 -0
- package/dist/select/select-option.mjs +65 -0
- package/dist/select/select-option.mjs.map +1 -0
- package/dist/select/select-root.d.mts +66 -0
- package/dist/select/select-root.d.mts.map +1 -0
- package/dist/select/select-root.mjs +94 -0
- package/dist/select/select-root.mjs.map +1 -0
- package/dist/select/select-separator.d.mts +20 -0
- package/dist/select/select-separator.d.mts.map +1 -0
- package/dist/select/select-separator.mjs +30 -0
- package/dist/select/select-separator.mjs.map +1 -0
- package/dist/select/select-trigger.d.mts +29 -0
- package/dist/select/select-trigger.d.mts.map +1 -0
- package/dist/select/select-trigger.mjs +48 -0
- package/dist/select/select-trigger.mjs.map +1 -0
- package/dist/select/select-value.d.mts +31 -0
- package/dist/select/select-value.d.mts.map +1 -0
- package/dist/select/select-value.mjs +101 -0
- package/dist/select/select-value.mjs.map +1 -0
- package/package.json +3 -1
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useNativeGroupReconciliation } from "./use-native-group-reconciliation.mjs";
|
|
3
|
+
import { useCallback, useEffectEvent, useLayoutEffect, useMemo, useRef } from "react";
|
|
4
|
+
//#region src/internal/use-native-select-value.ts
|
|
5
|
+
/** The first option whose value is `value` (react-dom's `updateOptions` selects the first). */
|
|
6
|
+
function findOption(select, value) {
|
|
7
|
+
for (const option of select.options) if (option.value === value) return option;
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
function isEnabled(option) {
|
|
11
|
+
if (option.disabled) return false;
|
|
12
|
+
const parent = option.parentElement;
|
|
13
|
+
return !(parent?.tagName === "OPTGROUP" && parent.disabled);
|
|
14
|
+
}
|
|
15
|
+
function firstEnabled(select) {
|
|
16
|
+
for (const option of select.options) if (isEnabled(option)) return option.value;
|
|
17
|
+
return "";
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The spec's reset result for a single select: the last option whose `defaultSelected` is true,
|
|
21
|
+
* else the first enabled option, else `""` (nothing selected).
|
|
22
|
+
*/
|
|
23
|
+
function readResetValue(select) {
|
|
24
|
+
let last = null;
|
|
25
|
+
for (const option of select.options) if (option.defaultSelected) last = option;
|
|
26
|
+
return last !== null ? last.value : firstEnabled(select);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The option a `<select>` rendered with the value `""` selects: an option whose value is `""`
|
|
30
|
+
* (react-dom's `updateOptions("")` and Fizz's `selected` attribute both pick it, even when it is
|
|
31
|
+
* disabled), else the first enabled option (the selectedness setting algorithm), else `""`.
|
|
32
|
+
*/
|
|
33
|
+
function implicitDefault(select) {
|
|
34
|
+
return findOption(select, "") !== null ? "" : firstEnabled(select);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The reset-target mirror followed by the re-apply, in one synchronous step
|
|
38
|
+
* (design/components/select.md 6.1). Writing `defaultSelected = true` on an option whose
|
|
39
|
+
* dirtiness is false selects it, so the intended option is re-selected right after. Writes only
|
|
40
|
+
* what differs: the common commit writes nothing.
|
|
41
|
+
*/
|
|
42
|
+
function applySelection(select, target, resetTarget) {
|
|
43
|
+
if (resetTarget !== void 0) {
|
|
44
|
+
const def = findOption(select, resetTarget);
|
|
45
|
+
for (const option of select.options) {
|
|
46
|
+
const want = option === def;
|
|
47
|
+
if (option.defaultSelected !== want) option.defaultSelected = want;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const option = findOption(select, target);
|
|
51
|
+
if (option !== null && !option.selected) option.selected = true;
|
|
52
|
+
}
|
|
53
|
+
const read = (select) => ({
|
|
54
|
+
value: select.value,
|
|
55
|
+
index: select.selectedIndex
|
|
56
|
+
});
|
|
57
|
+
/**
|
|
58
|
+
* Native-`<select>` machinery for `Select.Root` (conventions 3.8, the select analogue of
|
|
59
|
+
* `useNativeChecked`), on top of `useNativeGroupReconciliation`:
|
|
60
|
+
*
|
|
61
|
+
* 1. Adoption (uncontrolled without `defaultValue`): at mount the browser's default becomes the
|
|
62
|
+
* committed value with no dispatch, and the mount reconciliation compares against it, so a
|
|
63
|
+
* pre-hydration pick of another option still dispatches. It is also the reset target (the
|
|
64
|
+
* value Fizz marks `selected` when it comes from a `""` option). While no dispatch has set
|
|
65
|
+
* the value, late options mounting without the state's option adopt again (loading).
|
|
66
|
+
* 2. Mount reconciliation and form reset through the group adapter: one dispatch per Select, and
|
|
67
|
+
* only when the intended option exists in the DOM (a value whose option is still loading is
|
|
68
|
+
* never "reconciled" to the fallback).
|
|
69
|
+
* 3. After every Root commit: the reset-target mirror, then the re-apply of the intended option.
|
|
70
|
+
* 4. Late options: the same, once per commit; a hydrated late option is also reconciled, from a
|
|
71
|
+
* snapshot of the selection taken before the re-apply.
|
|
72
|
+
*/
|
|
73
|
+
function useNativeSelectValue(options) {
|
|
74
|
+
const { ref: elementRef } = options;
|
|
75
|
+
const ref = useMemo(() => ({ get current() {
|
|
76
|
+
const el = elementRef.current;
|
|
77
|
+
return el?.tagName === "SELECT" ? el : null;
|
|
78
|
+
} }), [elementRef]);
|
|
79
|
+
const latest = useRef(options);
|
|
80
|
+
useLayoutEffect(() => {
|
|
81
|
+
latest.current = options;
|
|
82
|
+
});
|
|
83
|
+
/**
|
|
84
|
+
* A default adopted in this layout phase. Until the adoption's render (a sync update, flushed
|
|
85
|
+
* right after this commit) the state still says the old value, so this is what the user
|
|
86
|
+
* intends unless a dispatch is in flight. Cleared in a microtask, after that render.
|
|
87
|
+
*/
|
|
88
|
+
const adopted = useRef(null);
|
|
89
|
+
const intended = (o) => {
|
|
90
|
+
const f = o.latestInFlight();
|
|
91
|
+
if (f !== null) return f.next;
|
|
92
|
+
return adopted.current ?? o.optimistic;
|
|
93
|
+
};
|
|
94
|
+
const adoptNow = (value, adopt) => {
|
|
95
|
+
adopted.current = value;
|
|
96
|
+
adopt(value);
|
|
97
|
+
queueMicrotask(() => {
|
|
98
|
+
adopted.current = null;
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
/** Adopt mode: the value adopted at mount, the reset target (as the mount value is, controlled). */
|
|
102
|
+
const mountAdopted = useRef(void 0);
|
|
103
|
+
const resetTargetOf = (o) => o.adoptDefault !== void 0 ? mountAdopted.current : o.resetTarget;
|
|
104
|
+
/** The selection read in the layout phase of a late-option commit (conventions 3.8 rule 3). */
|
|
105
|
+
const lateSnapshot = useRef(null);
|
|
106
|
+
const adoptAtMount = useEffectEvent(() => {
|
|
107
|
+
const select = ref.current;
|
|
108
|
+
const adoptDefault = options.adoptDefault;
|
|
109
|
+
if (select === null || adoptDefault === void 0 || adoptDefault.established()) return;
|
|
110
|
+
const value = implicitDefault(select);
|
|
111
|
+
mountAdopted.current = value === "" && findOption(select, "") === null ? void 0 : value;
|
|
112
|
+
adoptNow(value, adoptDefault.adopt);
|
|
113
|
+
});
|
|
114
|
+
useLayoutEffect(() => {
|
|
115
|
+
adoptAtMount();
|
|
116
|
+
}, []);
|
|
117
|
+
const { onItemMounted } = useNativeGroupReconciliation({
|
|
118
|
+
element: ref,
|
|
119
|
+
readCurrent: () => {
|
|
120
|
+
const select = ref.current;
|
|
121
|
+
const snapshot = lateSnapshot.current;
|
|
122
|
+
lateSnapshot.current = null;
|
|
123
|
+
const base = intended(latest.current);
|
|
124
|
+
if (select === null) return base;
|
|
125
|
+
const current = snapshot ?? read(select);
|
|
126
|
+
if (current.index === -1 || findOption(select, base) === null) return base;
|
|
127
|
+
return current.value;
|
|
128
|
+
},
|
|
129
|
+
readDefault: () => {
|
|
130
|
+
const select = ref.current;
|
|
131
|
+
return select === null ? intended(latest.current) : readResetValue(select);
|
|
132
|
+
},
|
|
133
|
+
base: () => intended(latest.current),
|
|
134
|
+
onPlatformChange: (next, event) => latest.current.onPlatformChange(next, event)
|
|
135
|
+
});
|
|
136
|
+
useLayoutEffect(() => {
|
|
137
|
+
const select = ref.current;
|
|
138
|
+
if (select === null) return;
|
|
139
|
+
applySelection(select, intended(options), resetTargetOf(options));
|
|
140
|
+
});
|
|
141
|
+
/** The Root's own mount effects have run: later Option mounts are late. */
|
|
142
|
+
const rootMounted = useRef(false);
|
|
143
|
+
useLayoutEffect(() => {
|
|
144
|
+
rootMounted.current = true;
|
|
145
|
+
return () => {
|
|
146
|
+
rootMounted.current = false;
|
|
147
|
+
};
|
|
148
|
+
}, []);
|
|
149
|
+
/**
|
|
150
|
+
* This commit's late options were handled (snapshot, adoption, re-apply). A microtask window,
|
|
151
|
+
* not a per-commit flag: an Option's layout effect cannot tell which commit it belongs to (the
|
|
152
|
+
* Root does not commit with a boundary that resolves inside it). A second commit in the same
|
|
153
|
+
* task is covered by the per-report re-apply below; its hydrated options are reconciled against
|
|
154
|
+
* the first commit's snapshot (theoretical: no part triggers such a commit).
|
|
155
|
+
*/
|
|
156
|
+
const lateHandled = useRef(false);
|
|
157
|
+
return { optionMounted: useCallback((el, hydrated) => {
|
|
158
|
+
if (!rootMounted.current) return;
|
|
159
|
+
const select = ref.current;
|
|
160
|
+
if (select === null) return;
|
|
161
|
+
if (!lateHandled.current) {
|
|
162
|
+
lateHandled.current = true;
|
|
163
|
+
lateSnapshot.current = read(select);
|
|
164
|
+
const current = latest.current;
|
|
165
|
+
const adoptDefault = current.adoptDefault;
|
|
166
|
+
if (adoptDefault !== void 0 && !adoptDefault.established() && current.latestInFlight() === null && findOption(select, intended(current)) === null) adoptNow(implicitDefault(select), adoptDefault.adopt);
|
|
167
|
+
applySelection(select, intended(current), resetTargetOf(current));
|
|
168
|
+
queueMicrotask(() => queueMicrotask(() => {
|
|
169
|
+
lateHandled.current = false;
|
|
170
|
+
lateSnapshot.current = null;
|
|
171
|
+
}));
|
|
172
|
+
} else if (el !== null) {
|
|
173
|
+
const current = latest.current;
|
|
174
|
+
if (el.value === intended(current) || el.value === resetTargetOf(current)) applySelection(select, intended(current), resetTargetOf(current));
|
|
175
|
+
}
|
|
176
|
+
if (hydrated) onItemMounted(el);
|
|
177
|
+
}, [ref, onItemMounted]) };
|
|
178
|
+
}
|
|
179
|
+
//#endregion
|
|
180
|
+
export { applySelection, findOption, implicitDefault, readResetValue, useNativeSelectValue };
|
|
181
|
+
|
|
182
|
+
//# sourceMappingURL=use-native-select-value.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-native-select-value.mjs","names":[],"sources":["../../src/internal/use-native-select-value.ts"],"sourcesContent":["\"use client\";\nimport { useCallback, useEffectEvent, useLayoutEffect, useMemo, useRef } from \"react\";\nimport type { RefObject } from \"react\";\nimport { useNativeGroupReconciliation } from \"./use-native-group-reconciliation.js\";\n\nexport interface UseNativeSelectValueOptions {\n /** The `<select>`. */\n readonly ref: RefObject<HTMLSelectElement | null>;\n /** The optimistic value (what the `<select>` renders). */\n readonly optimistic: string;\n /** From `useControllableAction`: the latest in-flight dispatch, if any. */\n readonly latestInFlight: () => { readonly next: string } | null;\n /**\n * The value a form reset must return to, mirrored into `option.defaultSelected` after every\n * commit: uncontrolled, the latest `defaultValue`; controlled, the Root's mount-time value.\n * Ignored in adopt mode (`adoptDefault` set), where the target is the value adopted at mount.\n */\n readonly resetTarget: string | undefined;\n /**\n * Uncontrolled without `defaultValue` (design/components/select.md 5): adopts the browser's\n * choice as the committed value, without a dispatch. `undefined` in every other mode.\n */\n readonly adoptDefault: AdoptDefault | undefined;\n /**\n * The browser changed the value on a residual path (pre-hydration pick, Firefox form-state\n * restoration, a late hydrated option, a form reset) and it differs from what the user intends.\n * `event` is the `reset` event or `undefined`. The Root dispatches once with\n * `reason: \"platform\"`.\n */\n readonly onPlatformChange: (next: string, event: Event | undefined) => void;\n}\n\nexport interface AdoptDefault {\n /** `adopt` of `useControllableAction`: writes the committed value, no action. */\n readonly adopt: (next: string) => void;\n /**\n * A dispatch (a pick or a platform change) has set the value. From then on the browser's\n * default is never adopted again: late options only get the re-apply, so options that suspend\n * again (a keyed boundary, a refetch) and come back keep the user's value.\n */\n readonly established: () => boolean;\n}\n\nexport interface UseNativeSelectValueResult {\n /**\n * Stable. Each `Select.Option` calls it from its mount layout effect with its element and\n * whether that render hydrated server HTML (`useHydratedMount`). Reports from the Root's own\n * commit are ignored (the Root's effects cover them). A later report re-applies the intended\n * option (React never selects options mounted after the select's own commit); a hydrated one\n * is also reconciled, from a DOM snapshot taken before the re-apply.\n */\n readonly optionMounted: (el: HTMLOptionElement | null, hydrated: boolean) => void;\n}\n\n/** The first option whose value is `value` (react-dom's `updateOptions` selects the first). */\nexport function findOption(select: HTMLSelectElement, value: string): HTMLOptionElement | null {\n for (const option of select.options) {\n if (option.value === value) return option;\n }\n return null;\n}\n\nfunction isEnabled(option: HTMLOptionElement): boolean {\n if (option.disabled) return false;\n const parent = option.parentElement;\n return !(parent?.tagName === \"OPTGROUP\" && (parent as HTMLOptGroupElement).disabled);\n}\n\nfunction firstEnabled(select: HTMLSelectElement): string {\n for (const option of select.options) {\n if (isEnabled(option)) return option.value;\n }\n return \"\";\n}\n\n/**\n * The spec's reset result for a single select: the last option whose `defaultSelected` is true,\n * else the first enabled option, else `\"\"` (nothing selected).\n */\nexport function readResetValue(select: HTMLSelectElement): string {\n let last: HTMLOptionElement | null = null;\n for (const option of select.options) {\n if (option.defaultSelected) last = option;\n }\n return last !== null ? last.value : firstEnabled(select);\n}\n\n/**\n * The option a `<select>` rendered with the value `\"\"` selects: an option whose value is `\"\"`\n * (react-dom's `updateOptions(\"\")` and Fizz's `selected` attribute both pick it, even when it is\n * disabled), else the first enabled option (the selectedness setting algorithm), else `\"\"`.\n */\nexport function implicitDefault(select: HTMLSelectElement): string {\n return findOption(select, \"\") !== null ? \"\" : firstEnabled(select);\n}\n\n/**\n * The reset-target mirror followed by the re-apply, in one synchronous step\n * (design/components/select.md 6.1). Writing `defaultSelected = true` on an option whose\n * dirtiness is false selects it, so the intended option is re-selected right after. Writes only\n * what differs: the common commit writes nothing.\n */\nexport function applySelection(\n select: HTMLSelectElement,\n target: string,\n resetTarget: string | undefined,\n): void {\n if (resetTarget !== undefined) {\n // Only the first match, the option react-dom would select for that value.\n const def = findOption(select, resetTarget);\n for (const option of select.options) {\n const want = option === def;\n if (option.defaultSelected !== want) option.defaultSelected = want;\n }\n }\n const option = findOption(select, target);\n if (option !== null && !option.selected) option.selected = true;\n}\n\ninterface Snapshot {\n readonly value: string;\n readonly index: number;\n}\n\nconst read = (select: HTMLSelectElement): Snapshot => ({\n value: select.value,\n index: select.selectedIndex,\n});\n\n/**\n * Native-`<select>` machinery for `Select.Root` (conventions 3.8, the select analogue of\n * `useNativeChecked`), on top of `useNativeGroupReconciliation`:\n *\n * 1. Adoption (uncontrolled without `defaultValue`): at mount the browser's default becomes the\n * committed value with no dispatch, and the mount reconciliation compares against it, so a\n * pre-hydration pick of another option still dispatches. It is also the reset target (the\n * value Fizz marks `selected` when it comes from a `\"\"` option). While no dispatch has set\n * the value, late options mounting without the state's option adopt again (loading).\n * 2. Mount reconciliation and form reset through the group adapter: one dispatch per Select, and\n * only when the intended option exists in the DOM (a value whose option is still loading is\n * never \"reconciled\" to the fallback).\n * 3. After every Root commit: the reset-target mirror, then the re-apply of the intended option.\n * 4. Late options: the same, once per commit; a hydrated late option is also reconciled, from a\n * snapshot of the selection taken before the re-apply.\n */\nexport function useNativeSelectValue(\n options: UseNativeSelectValueOptions,\n): UseNativeSelectValueResult {\n const { ref: elementRef } = options;\n // A `render` element that is not a <select> (dev warning in the Root) gets no DOM work.\n const ref = useMemo(\n () => ({\n get current(): HTMLSelectElement | null {\n const el = elementRef.current;\n // Tag, not `instanceof`: the element may come from another realm (an iframe).\n return el?.tagName === \"SELECT\" ? (el as HTMLSelectElement) : null;\n },\n }),\n [elementRef],\n );\n\n const latest = useRef(options);\n useLayoutEffect(() => {\n latest.current = options;\n });\n\n /**\n * A default adopted in this layout phase. Until the adoption's render (a sync update, flushed\n * right after this commit) the state still says the old value, so this is what the user\n * intends unless a dispatch is in flight. Cleared in a microtask, after that render.\n */\n const adopted = useRef<string | null>(null);\n const intended = (o: UseNativeSelectValueOptions): string => {\n const f = o.latestInFlight();\n if (f !== null) return f.next;\n return adopted.current ?? o.optimistic;\n };\n\n const adoptNow = (value: string, adopt: (next: string) => void) => {\n adopted.current = value;\n adopt(value);\n queueMicrotask(() => {\n adopted.current = null;\n });\n };\n\n /** Adopt mode: the value adopted at mount, the reset target (as the mount value is, controlled). */\n const mountAdopted = useRef<string | undefined>(undefined);\n const resetTargetOf = (o: UseNativeSelectValueOptions): string | undefined =>\n o.adoptDefault !== undefined ? mountAdopted.current : o.resetTarget;\n\n /** The selection read in the layout phase of a late-option commit (conventions 3.8 rule 3). */\n const lateSnapshot = useRef<Snapshot | null>(null);\n\n // 1. Adoption at mount. Declared before the adapter so that its mount reconciliation compares\n // the DOM against the adopted default.\n const adoptAtMount = useEffectEvent(() => {\n const select = ref.current;\n const adoptDefault = options.adoptDefault;\n // `established`: StrictMode's re-run after a mount reconciliation already dispatched.\n if (select === null || adoptDefault === undefined || adoptDefault.established()) return;\n const value = implicitDefault(select);\n // `\"\"` from a select with nothing selectable is no reset target (design 6.1): only a `\"\"`\n // option, the placeholder react-dom and Fizz select, is mirrored.\n mountAdopted.current = value === \"\" && findOption(select, \"\") === null ? undefined : value;\n adoptNow(value, adoptDefault.adopt);\n });\n useLayoutEffect(() => {\n adoptAtMount();\n }, []);\n\n // 2. Residual paths: one dispatch per Select.\n const { onItemMounted } = useNativeGroupReconciliation<string>({\n element: ref,\n readCurrent: () => {\n const select = ref.current;\n const snapshot = lateSnapshot.current;\n lateSnapshot.current = null;\n const base = intended(latest.current);\n if (select === null) return base;\n const current = snapshot ?? read(select);\n // The dispatch gate: nothing selected, or the intended option absent, is not a pick.\n if (current.index === -1 || findOption(select, base) === null) return base;\n return current.value;\n },\n readDefault: () => {\n const select = ref.current;\n return select === null ? intended(latest.current) : readResetValue(select);\n },\n base: () => intended(latest.current),\n onPlatformChange: (next, event) => latest.current.onPlatformChange(next, event),\n });\n\n // 3. After every commit. Declared after the adapter, so the mount commit's DOM is read by the\n // mount reconciliation before anything is re-applied.\n useLayoutEffect(() => {\n const select = ref.current;\n if (select === null) return;\n applySelection(select, intended(options), resetTargetOf(options));\n });\n\n /** The Root's own mount effects have run: later Option mounts are late. */\n const rootMounted = useRef(false);\n useLayoutEffect(() => {\n rootMounted.current = true;\n return () => {\n rootMounted.current = false;\n };\n }, []);\n /**\n * This commit's late options were handled (snapshot, adoption, re-apply). A microtask window,\n * not a per-commit flag: an Option's layout effect cannot tell which commit it belongs to (the\n * Root does not commit with a boundary that resolves inside it). A second commit in the same\n * task is covered by the per-report re-apply below; its hydrated options are reconciled against\n * the first commit's snapshot (theoretical: no part triggers such a commit).\n */\n const lateHandled = useRef(false);\n\n // 4. Late options.\n const optionMounted = useCallback(\n (el: HTMLOptionElement | null, hydrated: boolean) => {\n if (!rootMounted.current) return;\n const select = ref.current;\n if (select === null) return;\n if (!lateHandled.current) {\n lateHandled.current = true;\n // Every option of this commit is in the DOM already (the mutation phase precedes layout).\n lateSnapshot.current = read(select);\n const current = latest.current;\n const adoptDefault = current.adoptDefault;\n if (\n adoptDefault !== undefined &&\n !adoptDefault.established() &&\n current.latestInFlight() === null &&\n findOption(select, intended(current)) === null\n ) {\n // Still loading and nothing chosen yet (the fallback-only case): adopt the browser's\n // default. Never after a dispatch: that would overwrite the user's value silently.\n adoptNow(implicitDefault(select), adoptDefault.adopt);\n }\n applySelection(select, intended(current), resetTargetOf(current));\n // Cleared after the adapter's microtask, which a later report of this same commit may\n // queue after this point: two hops keep the snapshot for it.\n queueMicrotask(() =>\n queueMicrotask(() => {\n lateHandled.current = false;\n lateSnapshot.current = null;\n }),\n );\n } else if (el !== null) {\n // Per report, for an option that matters (cheap: at most the intended and the reset\n // target's options): a second commit within the window gets its re-apply too.\n const current = latest.current;\n if (el.value === intended(current) || el.value === resetTargetOf(current)) {\n applySelection(select, intended(current), resetTargetOf(current));\n }\n }\n if (hydrated) onItemMounted(el);\n },\n [ref, onItemMounted],\n );\n\n return { optionMounted };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import "../internal/platform-attributes.mjs";
|
|
2
|
+
import { SelectRoot, SelectRootProps, SelectRootState, SelectValueChangeDetails } from "./select-root.mjs";
|
|
3
|
+
import { SelectGroup, SelectGroupProps } from "./select-group.mjs";
|
|
4
|
+
import { SelectGroupLabel, SelectGroupLabelProps } from "./select-group-label.mjs";
|
|
5
|
+
import { SelectOption, SelectOptionProps, SelectOptionState } from "./select-option.mjs";
|
|
6
|
+
import { SelectSeparator, SelectSeparatorProps } from "./select-separator.mjs";
|
|
7
|
+
import { SelectTrigger, SelectTriggerProps } from "./select-trigger.mjs";
|
|
8
|
+
import { SelectValue, SelectValueProps } from "./select-value.mjs";
|
|
9
|
+
//#region src/select/index.d.ts
|
|
10
|
+
/**
|
|
11
|
+
* Select: a real `<select>` (`Root`), progressively enhanced with customizable select. Optional
|
|
12
|
+
* `Trigger` (`<button>`, first child) and `Value` (`<selectedcontent>`) for rich button content,
|
|
13
|
+
* `Option` (`<option>`), `Group` (`<optgroup>`) with an optional `GroupLabel` (`<legend>`), and
|
|
14
|
+
* `Separator` (`<hr>`). The picker, its keyboard and its placement are the browser's; opt into
|
|
15
|
+
* the styled picker with `appearance: base-select` in your CSS. The selected option is the
|
|
16
|
+
* optimistic value; the Root, the Trigger and the picked Option carry `data-pending` while
|
|
17
|
+
* `valueChangeAction` is in flight.
|
|
18
|
+
*/
|
|
19
|
+
export declare const Select: {
|
|
20
|
+
readonly Root: typeof SelectRoot;
|
|
21
|
+
readonly Trigger: typeof SelectTrigger;
|
|
22
|
+
readonly Value: typeof SelectValue;
|
|
23
|
+
readonly Option: typeof SelectOption;
|
|
24
|
+
readonly Group: typeof SelectGroup;
|
|
25
|
+
readonly GroupLabel: typeof SelectGroupLabel;
|
|
26
|
+
readonly Separator: typeof SelectSeparator;
|
|
27
|
+
};
|
|
28
|
+
//#endregion
|
|
29
|
+
export type { SelectGroupLabelProps, SelectGroupProps, SelectOptionProps, SelectOptionState, SelectRootProps, SelectRootState, SelectSeparatorProps, SelectTriggerProps, SelectValueChangeDetails, SelectValueProps };
|
|
30
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/select/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;qBAoBa;WACX,aAAI;WACJ,gBAAO;WACP,cAAK;WACL,eAAM;WACN,cAAK;WACL,mBAAU;WACV,kBAAS"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { SelectGroup } from "./select-group.mjs";
|
|
2
|
+
import { SelectGroupLabel } from "./select-group-label.mjs";
|
|
3
|
+
import { SelectOption } from "./select-option.mjs";
|
|
4
|
+
import { SelectRoot } from "./select-root.mjs";
|
|
5
|
+
import { SelectSeparator } from "./select-separator.mjs";
|
|
6
|
+
import { SelectTrigger } from "./select-trigger.mjs";
|
|
7
|
+
import { SelectValue } from "./select-value.mjs";
|
|
8
|
+
//#region src/select/index.ts
|
|
9
|
+
/**
|
|
10
|
+
* Select: a real `<select>` (`Root`), progressively enhanced with customizable select. Optional
|
|
11
|
+
* `Trigger` (`<button>`, first child) and `Value` (`<selectedcontent>`) for rich button content,
|
|
12
|
+
* `Option` (`<option>`), `Group` (`<optgroup>`) with an optional `GroupLabel` (`<legend>`), and
|
|
13
|
+
* `Separator` (`<hr>`). The picker, its keyboard and its placement are the browser's; opt into
|
|
14
|
+
* the styled picker with `appearance: base-select` in your CSS. The selected option is the
|
|
15
|
+
* optimistic value; the Root, the Trigger and the picked Option carry `data-pending` while
|
|
16
|
+
* `valueChangeAction` is in flight.
|
|
17
|
+
*/
|
|
18
|
+
const Select = {
|
|
19
|
+
Root: SelectRoot,
|
|
20
|
+
Trigger: SelectTrigger,
|
|
21
|
+
Value: SelectValue,
|
|
22
|
+
Option: SelectOption,
|
|
23
|
+
Group: SelectGroup,
|
|
24
|
+
GroupLabel: SelectGroupLabel,
|
|
25
|
+
Separator: SelectSeparator
|
|
26
|
+
};
|
|
27
|
+
//#endregion
|
|
28
|
+
export { Select };
|
|
29
|
+
|
|
30
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/select/index.ts"],"sourcesContent":["// Ships the platform typings (the `selectedcontent` element) with this subpath (conventions 7.4).\n// Type-only: the built JS has no import of it.\nimport \"../internal/platform-attributes.js\";\nimport { SelectGroup } from \"./select-group.js\";\nimport { SelectGroupLabel } from \"./select-group-label.js\";\nimport { SelectOption } from \"./select-option.js\";\nimport { SelectRoot } from \"./select-root.js\";\nimport { SelectSeparator } from \"./select-separator.js\";\nimport { SelectTrigger } from \"./select-trigger.js\";\nimport { SelectValue } from \"./select-value.js\";\n\n/**\n * Select: a real `<select>` (`Root`), progressively enhanced with customizable select. Optional\n * `Trigger` (`<button>`, first child) and `Value` (`<selectedcontent>`) for rich button content,\n * `Option` (`<option>`), `Group` (`<optgroup>`) with an optional `GroupLabel` (`<legend>`), and\n * `Separator` (`<hr>`). The picker, its keyboard and its placement are the browser's; opt into\n * the styled picker with `appearance: base-select` in your CSS. The selected option is the\n * optimistic value; the Root, the Trigger and the picked Option carry `data-pending` while\n * `valueChangeAction` is in flight.\n */\nexport const Select = {\n Root: SelectRoot,\n Trigger: SelectTrigger,\n Value: SelectValue,\n Option: SelectOption,\n Group: SelectGroup,\n GroupLabel: SelectGroupLabel,\n Separator: SelectSeparator,\n} as const;\n\nexport type { SelectRootProps, SelectRootState, SelectValueChangeDetails } from \"./select-root.js\";\nexport type { SelectTriggerProps } from \"./select-trigger.js\";\nexport type { SelectValueProps } from \"./select-value.js\";\nexport type { SelectOptionProps, SelectOptionState } from \"./select-option.js\";\nexport type { SelectGroupProps } from \"./select-group.js\";\nexport type { SelectGroupLabelProps } from \"./select-group-label.js\";\nexport type { SelectSeparatorProps } from \"./select-separator.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;AAoBA,MAAa,SAAS;CACpB,MAAM;CACN,SAAS;CACT,OAAO;CACP,QAAQ;CACR,OAAO;CACP,YAAY;CACZ,WAAW;AACb"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { createContext, use } from "react";
|
|
3
|
+
//#region src/select/select-context.ts
|
|
4
|
+
const SelectContext = createContext(null);
|
|
5
|
+
function useSelectContext(part) {
|
|
6
|
+
const context = use(SelectContext);
|
|
7
|
+
if (context === null) throw new Error(`${part} must be rendered inside a Select.Root.`);
|
|
8
|
+
return context;
|
|
9
|
+
}
|
|
10
|
+
//#endregion
|
|
11
|
+
export { SelectContext, useSelectContext };
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=select-context.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"select-context.mjs","names":[],"sources":["../../src/select/select-context.ts"],"sourcesContent":["\"use client\";\nimport { createContext, use } from \"react\";\n\nexport interface SelectContextValue {\n /** Optimistic value: Options render `data-selected` from it. */\n readonly optimistic: string;\n readonly pending: boolean;\n /** `Root disabled` (natively disables the whole `<select>`). */\n readonly disabled: boolean;\n /** Inside a `Select.Group disabled` (`false` outside groups): Options match `:disabled`. */\n readonly groupDisabled: boolean;\n /**\n * Called by each Option from its mount layout effect with its element and whether that render\n * hydrated server HTML. Late options are re-applied, and hydrated late options reconciled\n * (design/components/select.md 6.2).\n */\n readonly optionMounted: (el: HTMLOptionElement | null, hydrated: boolean) => void;\n}\n\nexport const SelectContext = createContext<SelectContextValue | null>(null);\n\nexport function useSelectContext(part: string): SelectContextValue {\n const context = use(SelectContext);\n if (context === null) {\n throw new Error(`${part} must be rendered inside a Select.Root.`);\n }\n return context;\n}\n"],"mappings":";;;AAmBA,MAAa,gBAAgB,cAAyC,IAAI;AAE1E,SAAgB,iBAAiB,MAAkC;CACjE,MAAM,UAAU,IAAI,aAAa;CACjC,IAAI,YAAY,MACd,MAAM,IAAI,MAAM,GAAG,KAAK,wCAAwC;CAElE,OAAO;AACT"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useCallback, useRef } from "react";
|
|
3
|
+
//#region src/select/select-dom.ts
|
|
4
|
+
const isDev = process.env.NODE_ENV !== "production";
|
|
5
|
+
/**
|
|
6
|
+
* The library ref of a Select part: stores the node in `node` and, in development,
|
|
7
|
+
* warns once per instance when the mounted element is not `<tag>` (conventions 1.2: Select's parts
|
|
8
|
+
* keep their native elements) or when `position` reports a misplaced element (a message, or
|
|
9
|
+
* `null` when the element is where HTML wants it). Ref callbacks run in the layout phase, when
|
|
10
|
+
* the element's siblings are in place.
|
|
11
|
+
*/
|
|
12
|
+
function useSelectPartRef(component, tag, position) {
|
|
13
|
+
const node = useRef(null);
|
|
14
|
+
const warnedTag = useRef(false);
|
|
15
|
+
const warnedPosition = useRef(false);
|
|
16
|
+
return {
|
|
17
|
+
ref: useCallback((el) => {
|
|
18
|
+
node.current = el;
|
|
19
|
+
if (isDev && !warnedTag.current && el.tagName !== tag.toUpperCase()) {
|
|
20
|
+
warnedTag.current = true;
|
|
21
|
+
console.error(`${component}: expected to render a <${tag}> element but got <${el.tagName.toLowerCase()}>. The parser, the picker and <selectedcontent> cloning key on the native element; a \`render\` element must keep it.`);
|
|
22
|
+
}
|
|
23
|
+
if (isDev && position && !warnedPosition.current) {
|
|
24
|
+
const message = position(el);
|
|
25
|
+
if (message !== null) {
|
|
26
|
+
warnedPosition.current = true;
|
|
27
|
+
console.error(`${component}: ${message}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return () => {
|
|
31
|
+
node.current = null;
|
|
32
|
+
};
|
|
33
|
+
}, [
|
|
34
|
+
component,
|
|
35
|
+
tag,
|
|
36
|
+
position
|
|
37
|
+
]),
|
|
38
|
+
node
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
export { useSelectPartRef };
|
|
43
|
+
|
|
44
|
+
//# sourceMappingURL=select-dom.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"select-dom.mjs","names":[],"sources":["../../src/select/select-dom.ts"],"sourcesContent":["\"use client\";\nimport { useCallback, useRef } from \"react\";\nimport type { RefCallback, RefObject } from \"react\";\n\nconst isDev = process.env.NODE_ENV !== \"production\";\n\nexport interface SelectPartRef<E extends Element> {\n /** The library ref to pass to `useRender`. */\n readonly ref: RefCallback<E>;\n /** The mounted element. Read it from effects and handlers only. */\n readonly node: RefObject<E | null>;\n}\n\n/**\n * The library ref of a Select part: stores the node in `node` and, in development,\n * warns once per instance when the mounted element is not `<tag>` (conventions 1.2: Select's parts\n * keep their native elements) or when `position` reports a misplaced element (a message, or\n * `null` when the element is where HTML wants it). Ref callbacks run in the layout phase, when\n * the element's siblings are in place.\n */\nexport function useSelectPartRef<E extends Element>(\n component: string,\n tag: string,\n position?: (el: E) => string | null,\n): SelectPartRef<E> {\n const node = useRef<E | null>(null);\n const warnedTag = useRef(false);\n const warnedPosition = useRef(false);\n const ref = useCallback(\n (el: E) => {\n node.current = el;\n if (isDev && !warnedTag.current && el.tagName !== tag.toUpperCase()) {\n warnedTag.current = true;\n console.error(\n `${component}: expected to render a <${tag}> element but got ` +\n `<${el.tagName.toLowerCase()}>. The parser, the picker and <selectedcontent> cloning ` +\n `key on the native element; a \\`render\\` element must keep it.`,\n );\n }\n if (isDev && position && !warnedPosition.current) {\n const message = position(el);\n if (message !== null) {\n warnedPosition.current = true;\n console.error(`${component}: ${message}`);\n }\n }\n return () => {\n node.current = null;\n };\n },\n // `position` is a module-level function at every call site, so the callback is stable.\n [component, tag, position],\n );\n return { ref, node };\n}\n"],"mappings":";;;AAIA,MAAM,QAAQ,QAAQ,IAAI,aAAa;;;;;;;;AAgBvC,SAAgB,iBACd,WACA,KACA,UACkB;CAClB,MAAM,OAAO,OAAiB,IAAI;CAClC,MAAM,YAAY,OAAO,KAAK;CAC9B,MAAM,iBAAiB,OAAO,KAAK;CA0BnC,OAAO;EAAE,KAzBG,aACT,OAAU;GACT,KAAK,UAAU;GACf,IAAI,SAAS,CAAC,UAAU,WAAW,GAAG,YAAY,IAAI,YAAY,GAAG;IACnE,UAAU,UAAU;IACpB,QAAQ,MACN,GAAG,UAAU,0BAA0B,IAAI,qBACrC,GAAG,QAAQ,YAAY,EAAE,sHAEjC;GACF;GACA,IAAI,SAAS,YAAY,CAAC,eAAe,SAAS;IAChD,MAAM,UAAU,SAAS,EAAE;IAC3B,IAAI,YAAY,MAAM;KACpB,eAAe,UAAU;KACzB,QAAQ,MAAM,GAAG,UAAU,IAAI,SAAS;IAC1C;GACF;GACA,aAAa;IACX,KAAK,UAAU;GACjB;EACF,GAEA;GAAC;GAAW;GAAK;EAAQ,CAEhB;EAAG;CAAK;AACrB"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { RenderProp, StateValue } from "../internal/types.mjs";
|
|
2
|
+
import { SelectRootState } from "./select-root.mjs";
|
|
3
|
+
import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
|
|
4
|
+
//#region src/select/select-group-label.d.ts
|
|
5
|
+
export interface SelectGroupLabelProps extends Omit<ComponentPropsWithRef<"legend">, "className" | "style" | "children"> {
|
|
6
|
+
className?: StateValue<string | undefined, SelectRootState>;
|
|
7
|
+
style?: StateValue<CSSProperties | undefined, SelectRootState>;
|
|
8
|
+
children?: StateValue<ReactNode, SelectRootState>;
|
|
9
|
+
/** Replace the rendered element. Must keep a `<legend>`. */
|
|
10
|
+
render?: RenderProp<ComponentPropsWithRef<"legend">, SelectRootState>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The rich label of a `Select.Group`: a `<legend>`, rendered by engines with customizable select.
|
|
14
|
+
* Optional; must be the **first child** of its Group. Keep its text equal to the Group's `label`,
|
|
15
|
+
* which native pickers and assistive technology use. Owns no keys.
|
|
16
|
+
*/
|
|
17
|
+
export declare function SelectGroupLabel({ className, style, children, render, ...rest }: SelectGroupLabelProps): ReactNode;
|
|
18
|
+
export declare namespace SelectGroupLabel {
|
|
19
|
+
var displayName: string;
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
//# sourceMappingURL=select-group-label.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"select-group-label.d.mts","names":[],"sources":["../../src/select/select-group-label.tsx"],"mappings":";;;;iBAQiB,8BAA8B,KAC7C;EAGA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;;EAEjC,SAAS,WAAW,iCAAiC;;;;;;;wBAcvC,mBACd,WACA,OACA,UACA,WACG,QACF,wBAAqB;yBANR"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useRender } from "../internal/use-render.mjs";
|
|
3
|
+
import { useSelectContext } from "./select-context.mjs";
|
|
4
|
+
import { useSelectPartRef } from "./select-dom.mjs";
|
|
5
|
+
//#region src/select/select-group-label.tsx
|
|
6
|
+
function firstChildOfGroup(el) {
|
|
7
|
+
const parent = el.parentElement;
|
|
8
|
+
if (parent?.tagName === "OPTGROUP" && parent.firstElementChild === el) return null;
|
|
9
|
+
return "must be the first child of a Select.Group's <optgroup> (HTML content model).";
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The rich label of a `Select.Group`: a `<legend>`, rendered by engines with customizable select.
|
|
13
|
+
* Optional; must be the **first child** of its Group. Keep its text equal to the Group's `label`,
|
|
14
|
+
* which native pickers and assistive technology use. Owns no keys.
|
|
15
|
+
*/
|
|
16
|
+
function SelectGroupLabel({ className, style, children, render, ...rest }) {
|
|
17
|
+
const ctx = useSelectContext("Select.GroupLabel");
|
|
18
|
+
const state = {
|
|
19
|
+
value: ctx.optimistic,
|
|
20
|
+
pending: ctx.pending,
|
|
21
|
+
disabled: ctx.disabled
|
|
22
|
+
};
|
|
23
|
+
const { ref } = useSelectPartRef("Select.GroupLabel", "legend", firstChildOfGroup);
|
|
24
|
+
return useRender({
|
|
25
|
+
defaultElement: "legend",
|
|
26
|
+
render,
|
|
27
|
+
state,
|
|
28
|
+
className,
|
|
29
|
+
style,
|
|
30
|
+
children,
|
|
31
|
+
props: rest,
|
|
32
|
+
ref
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
SelectGroupLabel.displayName = "SelectGroupLabel";
|
|
36
|
+
//#endregion
|
|
37
|
+
export { SelectGroupLabel };
|
|
38
|
+
|
|
39
|
+
//# sourceMappingURL=select-group-label.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"select-group-label.mjs","names":[],"sources":["../../src/select/select-group-label.tsx"],"sourcesContent":["\"use client\";\nimport type { ComponentPropsWithRef, CSSProperties, ReactNode } from \"react\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport { useSelectContext } from \"./select-context.js\";\nimport { useSelectPartRef } from \"./select-dom.js\";\nimport type { SelectRootState } from \"./select-root.js\";\n\nexport interface SelectGroupLabelProps extends Omit<\n ComponentPropsWithRef<\"legend\">,\n \"className\" | \"style\" | \"children\"\n> {\n className?: StateValue<string | undefined, SelectRootState>;\n style?: StateValue<CSSProperties | undefined, SelectRootState>;\n children?: StateValue<ReactNode, SelectRootState>;\n /** Replace the rendered element. Must keep a `<legend>`. */\n render?: RenderProp<ComponentPropsWithRef<\"legend\">, SelectRootState>;\n}\n\nfunction firstChildOfGroup(el: HTMLLegendElement): string | null {\n const parent = el.parentElement;\n if (parent?.tagName === \"OPTGROUP\" && parent.firstElementChild === el) return null;\n return \"must be the first child of a Select.Group's <optgroup> (HTML content model).\";\n}\n\n/**\n * The rich label of a `Select.Group`: a `<legend>`, rendered by engines with customizable select.\n * Optional; must be the **first child** of its Group. Keep its text equal to the Group's `label`,\n * which native pickers and assistive technology use. Owns no keys.\n */\nexport function SelectGroupLabel({\n className,\n style,\n children,\n render,\n ...rest\n}: SelectGroupLabelProps) {\n const ctx = useSelectContext(\"Select.GroupLabel\");\n const state: SelectRootState = {\n value: ctx.optimistic,\n pending: ctx.pending,\n disabled: ctx.disabled,\n };\n const { ref } = useSelectPartRef<HTMLLegendElement>(\n \"Select.GroupLabel\",\n \"legend\",\n firstChildOfGroup,\n );\n return useRender<ComponentPropsWithRef<\"legend\">, SelectRootState>({\n defaultElement: \"legend\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n ref,\n });\n}\n\nSelectGroupLabel.displayName = \"SelectGroupLabel\";\n"],"mappings":";;;;;AAmBA,SAAS,kBAAkB,IAAsC;CAC/D,MAAM,SAAS,GAAG;CAClB,IAAI,QAAQ,YAAY,cAAc,OAAO,sBAAsB,IAAI,OAAO;CAC9E,OAAO;AACT;;;;;;AAOA,SAAgB,iBAAiB,EAC/B,WACA,OACA,UACA,QACA,GAAG,QACqB;CACxB,MAAM,MAAM,iBAAiB,mBAAmB;CAChD,MAAM,QAAyB;EAC7B,OAAO,IAAI;EACX,SAAS,IAAI;EACb,UAAU,IAAI;CAChB;CACA,MAAM,EAAE,QAAQ,iBACd,qBACA,UACA,iBACF;CACA,OAAO,UAA4D;EACjE,gBAAgB;EAChB;EACA;EACA;EACA;EACA;EACA,OAAO;EACP;CACF,CAAC;AACH;AAEA,iBAAiB,cAAc"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { RenderProp, StateValue } from "../internal/types.mjs";
|
|
2
|
+
import { SelectRootState } from "./select-root.mjs";
|
|
3
|
+
import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
|
|
4
|
+
//#region src/select/select-group.d.ts
|
|
5
|
+
export interface SelectGroupProps extends Omit<ComponentPropsWithRef<"optgroup">, "label" | "className" | "style" | "children"> {
|
|
6
|
+
/**
|
|
7
|
+
* The `<optgroup label>`: shown by native pickers (Firefox today) and read by assistive
|
|
8
|
+
* technology. Required. Keep it equal to the text of a `Select.GroupLabel`, if any.
|
|
9
|
+
*/
|
|
10
|
+
label: string;
|
|
11
|
+
/** Native `disabled`: every option in the group is disabled. */
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
className?: StateValue<string | undefined, SelectRootState>;
|
|
14
|
+
style?: StateValue<CSSProperties | undefined, SelectRootState>;
|
|
15
|
+
children?: StateValue<ReactNode, SelectRootState>;
|
|
16
|
+
/** Replace the rendered element. Must keep an `<optgroup>`. */
|
|
17
|
+
render?: RenderProp<ComponentPropsWithRef<"optgroup">, SelectRootState>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* An `<optgroup>` of options. Its state is the Root's state. Owned key: `label`. Its `disabled`
|
|
21
|
+
* reaches the Options' `state.disabled` / `data-disabled` through context, as `:disabled` does.
|
|
22
|
+
*/
|
|
23
|
+
export declare function SelectGroup({ label, disabled, className, style, children, render, ...rest }: SelectGroupProps): import("react").JSX.Element;
|
|
24
|
+
export declare namespace SelectGroup {
|
|
25
|
+
var displayName: string;
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
//# sourceMappingURL=select-group.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"select-group.d.mts","names":[],"sources":["../../src/select/select-group.tsx"],"mappings":";;;;iBAQiB,yBAAyB,KACxC;;;;;EAOA;;EAEA;EACA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;;EAEjC,SAAS,WAAW,mCAAmC;;;;;;wBAOzC,cACd,OACA,UACA,WACA,OACA,UACA,WACG,QACF,mCAAgB,IAAA;yBARH"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useRender } from "../internal/use-render.mjs";
|
|
3
|
+
import { SelectContext, useSelectContext } from "./select-context.mjs";
|
|
4
|
+
import { useSelectPartRef } from "./select-dom.mjs";
|
|
5
|
+
import { jsx } from "react/jsx-runtime";
|
|
6
|
+
//#region src/select/select-group.tsx
|
|
7
|
+
/**
|
|
8
|
+
* An `<optgroup>` of options. Its state is the Root's state. Owned key: `label`. Its `disabled`
|
|
9
|
+
* reaches the Options' `state.disabled` / `data-disabled` through context, as `:disabled` does.
|
|
10
|
+
*/
|
|
11
|
+
function SelectGroup({ label, disabled = false, className, style, children, render, ...rest }) {
|
|
12
|
+
const ctx = useSelectContext("Select.Group");
|
|
13
|
+
const state = {
|
|
14
|
+
value: ctx.optimistic,
|
|
15
|
+
pending: ctx.pending,
|
|
16
|
+
disabled: ctx.disabled
|
|
17
|
+
};
|
|
18
|
+
const { ref } = useSelectPartRef("Select.Group", "optgroup");
|
|
19
|
+
const element = useRender({
|
|
20
|
+
defaultElement: "optgroup",
|
|
21
|
+
render,
|
|
22
|
+
state,
|
|
23
|
+
className,
|
|
24
|
+
style,
|
|
25
|
+
children,
|
|
26
|
+
props: {
|
|
27
|
+
...rest,
|
|
28
|
+
disabled
|
|
29
|
+
},
|
|
30
|
+
libraryProps: { label },
|
|
31
|
+
ref
|
|
32
|
+
});
|
|
33
|
+
return /* @__PURE__ */ jsx(SelectContext, {
|
|
34
|
+
value: {
|
|
35
|
+
...ctx,
|
|
36
|
+
groupDisabled: disabled
|
|
37
|
+
},
|
|
38
|
+
children: element
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
SelectGroup.displayName = "SelectGroup";
|
|
42
|
+
//#endregion
|
|
43
|
+
export { SelectGroup };
|
|
44
|
+
|
|
45
|
+
//# sourceMappingURL=select-group.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"select-group.mjs","names":[],"sources":["../../src/select/select-group.tsx"],"sourcesContent":["\"use client\";\nimport type { ComponentPropsWithRef, CSSProperties, ReactNode } from \"react\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport { SelectContext, useSelectContext } from \"./select-context.js\";\nimport { useSelectPartRef } from \"./select-dom.js\";\nimport type { SelectRootState } from \"./select-root.js\";\n\nexport interface SelectGroupProps extends Omit<\n ComponentPropsWithRef<\"optgroup\">,\n \"label\" | \"className\" | \"style\" | \"children\"\n> {\n /**\n * The `<optgroup label>`: shown by native pickers (Firefox today) and read by assistive\n * technology. Required. Keep it equal to the text of a `Select.GroupLabel`, if any.\n */\n label: string;\n /** Native `disabled`: every option in the group is disabled. */\n disabled?: boolean;\n className?: StateValue<string | undefined, SelectRootState>;\n style?: StateValue<CSSProperties | undefined, SelectRootState>;\n children?: StateValue<ReactNode, SelectRootState>;\n /** Replace the rendered element. Must keep an `<optgroup>`. */\n render?: RenderProp<ComponentPropsWithRef<\"optgroup\">, SelectRootState>;\n}\n\n/**\n * An `<optgroup>` of options. Its state is the Root's state. Owned key: `label`. Its `disabled`\n * reaches the Options' `state.disabled` / `data-disabled` through context, as `:disabled` does.\n */\nexport function SelectGroup({\n label,\n disabled = false,\n className,\n style,\n children,\n render,\n ...rest\n}: SelectGroupProps) {\n const ctx = useSelectContext(\"Select.Group\");\n const state: SelectRootState = {\n value: ctx.optimistic,\n pending: ctx.pending,\n disabled: ctx.disabled,\n };\n const { ref } = useSelectPartRef<HTMLOptGroupElement>(\"Select.Group\", \"optgroup\");\n const element = useRender<ComponentPropsWithRef<\"optgroup\">, SelectRootState>({\n defaultElement: \"optgroup\",\n render,\n state,\n className,\n style,\n children,\n props: { ...rest, disabled },\n libraryProps: { label },\n ref,\n });\n // Nested groups are not valid HTML, so this only ever turns `false` into the group's value.\n return <SelectContext value={{ ...ctx, groupDisabled: disabled }}>{element}</SelectContext>;\n}\n\nSelectGroup.displayName = \"SelectGroup\";\n"],"mappings":";;;;;;;;;;AA8BA,SAAgB,YAAY,EAC1B,OACA,WAAW,OACX,WACA,OACA,UACA,QACA,GAAG,QACgB;CACnB,MAAM,MAAM,iBAAiB,cAAc;CAC3C,MAAM,QAAyB;EAC7B,OAAO,IAAI;EACX,SAAS,IAAI;EACb,UAAU,IAAI;CAChB;CACA,MAAM,EAAE,QAAQ,iBAAsC,gBAAgB,UAAU;CAChF,MAAM,UAAU,UAA8D;EAC5E,gBAAgB;EAChB;EACA;EACA;EACA;EACA;EACA,OAAO;GAAE,GAAG;GAAM;EAAS;EAC3B,cAAc,EAAE,MAAM;EACtB;CACF,CAAC;CAED,OAAO,oBAAC,eAAD;EAAe,OAAO;GAAE,GAAG;GAAK,eAAe;EAAS;EAAI,UAAA;CAAuB,CAAA;AAC5F;AAEA,YAAY,cAAc"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { RenderProp, StateValue } from "../internal/types.mjs";
|
|
2
|
+
import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
|
|
3
|
+
//#region src/select/select-option.d.ts
|
|
4
|
+
export interface SelectOptionState {
|
|
5
|
+
readonly value: string;
|
|
6
|
+
/** Optimistic: true as soon as the user picks this option, before the action settles. */
|
|
7
|
+
readonly selected: boolean;
|
|
8
|
+
/** The Root is pending and this option is the optimistic target. */
|
|
9
|
+
readonly pending: boolean;
|
|
10
|
+
/** Disabled by its own prop, a disabled `Select.Group`, or `Root disabled` (as `:disabled`). */
|
|
11
|
+
readonly disabled: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface SelectOptionProps extends Omit<ComponentPropsWithRef<"option">, "value" | "selected" | "className" | "style" | "children"> {
|
|
14
|
+
/** The option's value. Required: React cannot infer a value from rich children. */
|
|
15
|
+
value: string;
|
|
16
|
+
/** Native `disabled`: not pickable, skipped by keyboard navigation. */
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
className?: StateValue<string | undefined, SelectOptionState>;
|
|
19
|
+
style?: StateValue<CSSProperties | undefined, SelectOptionState>;
|
|
20
|
+
/**
|
|
21
|
+
* The option's content. Keep it readable as plain text: engines without customizable select
|
|
22
|
+
* (Firefox today) show only the text. Give icons `aria-hidden` and no text. Avoid the `label`
|
|
23
|
+
* attribute (Chromium names the option from it but shows the content).
|
|
24
|
+
*/
|
|
25
|
+
children?: StateValue<ReactNode, SelectOptionState>;
|
|
26
|
+
/** Replace the rendered element. Must keep an `<option>`. */
|
|
27
|
+
render?: RenderProp<ComponentPropsWithRef<"option">, SelectOptionState>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* One `<option>`. It never renders `selected`: the selection is the Root's `value={optimistic}`
|
|
31
|
+
* plus the Root's re-apply for options that mount after the select's own commit (a Suspense
|
|
32
|
+
* boundary or `use()` inside the select), which the Option reports from its mount layout effect.
|
|
33
|
+
*
|
|
34
|
+
* Owned keys: `value`, `selected` (never rendered), `disabled`, `data-selected`, `data-pending`,
|
|
35
|
+
* `data-disabled`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function SelectOption({ value, disabled: disabledProp, className, style, children, render, ...rest }: SelectOptionProps): ReactNode;
|
|
38
|
+
export declare namespace SelectOption {
|
|
39
|
+
var displayName: string;
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
//# sourceMappingURL=select-option.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"select-option.d.mts","names":[],"sources":["../../src/select/select-option.tsx"],"mappings":";;;iBAWiB;WACN;;WAEA;;WAEA;;WAEA;;iBAGM,0BAA0B,KACzC;;EAIA;;EAEA;EACA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;;;;;;EAM9C,WAAW,WAAW,WAAW;;EAEjC,SAAS,WAAW,iCAAiC;;;;;;;;;;wBAWvC,eACd,OACA,UAAU,cACV,WACA,OACA,UACA,WACG,QACF,oBAAiB;yBARJ"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { dataAttr } from "../internal/data-attr.mjs";
|
|
3
|
+
import { useRender } from "../internal/use-render.mjs";
|
|
4
|
+
import { syncPresence } from "../internal/sync-presence.mjs";
|
|
5
|
+
import { useSelectContext } from "./select-context.mjs";
|
|
6
|
+
import { useSelectPartRef } from "./select-dom.mjs";
|
|
7
|
+
import { useHydratedMount } from "../internal/use-hydrated-mount.mjs";
|
|
8
|
+
import { useEffectEvent, useLayoutEffect } from "react";
|
|
9
|
+
//#region src/select/select-option.tsx
|
|
10
|
+
/**
|
|
11
|
+
* One `<option>`. It never renders `selected`: the selection is the Root's `value={optimistic}`
|
|
12
|
+
* plus the Root's re-apply for options that mount after the select's own commit (a Suspense
|
|
13
|
+
* boundary or `use()` inside the select), which the Option reports from its mount layout effect.
|
|
14
|
+
*
|
|
15
|
+
* Owned keys: `value`, `selected` (never rendered), `disabled`, `data-selected`, `data-pending`,
|
|
16
|
+
* `data-disabled`.
|
|
17
|
+
*/
|
|
18
|
+
function SelectOption({ value, disabled: disabledProp = false, className, style, children, render, ...rest }) {
|
|
19
|
+
const ctx = useSelectContext("Select.Option");
|
|
20
|
+
const selected = ctx.optimistic === value;
|
|
21
|
+
const pending = ctx.pending && selected;
|
|
22
|
+
const disabled = ctx.disabled || ctx.groupDisabled || disabledProp;
|
|
23
|
+
const hydrated = useHydratedMount();
|
|
24
|
+
const { ref, node } = useSelectPartRef("Select.Option", "option");
|
|
25
|
+
const reportMount = useEffectEvent(() => {
|
|
26
|
+
const el = node.current;
|
|
27
|
+
if (el !== null) {
|
|
28
|
+
syncPresence(el, "data-selected", selected);
|
|
29
|
+
syncPresence(el, "data-pending", pending);
|
|
30
|
+
syncPresence(el, "data-disabled", disabled);
|
|
31
|
+
}
|
|
32
|
+
ctx.optionMounted(el, hydrated);
|
|
33
|
+
});
|
|
34
|
+
useLayoutEffect(() => {
|
|
35
|
+
reportMount();
|
|
36
|
+
}, []);
|
|
37
|
+
return useRender({
|
|
38
|
+
defaultElement: "option",
|
|
39
|
+
render,
|
|
40
|
+
state: {
|
|
41
|
+
value,
|
|
42
|
+
selected,
|
|
43
|
+
pending,
|
|
44
|
+
disabled
|
|
45
|
+
},
|
|
46
|
+
className,
|
|
47
|
+
style,
|
|
48
|
+
children,
|
|
49
|
+
props: rest,
|
|
50
|
+
libraryProps: {
|
|
51
|
+
value,
|
|
52
|
+
selected: void 0,
|
|
53
|
+
disabled: disabledProp,
|
|
54
|
+
"data-selected": dataAttr(selected),
|
|
55
|
+
"data-pending": dataAttr(pending),
|
|
56
|
+
"data-disabled": dataAttr(disabled)
|
|
57
|
+
},
|
|
58
|
+
ref
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
SelectOption.displayName = "SelectOption";
|
|
62
|
+
//#endregion
|
|
63
|
+
export { SelectOption };
|
|
64
|
+
|
|
65
|
+
//# sourceMappingURL=select-option.mjs.map
|