@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.
Files changed (94) hide show
  1. package/dist/combobox/combobox-clear.d.mts +34 -0
  2. package/dist/combobox/combobox-clear.d.mts.map +1 -0
  3. package/dist/combobox/combobox-clear.mjs +58 -0
  4. package/dist/combobox/combobox-clear.mjs.map +1 -0
  5. package/dist/combobox/combobox-context.mjs +23 -0
  6. package/dist/combobox/combobox-context.mjs.map +1 -0
  7. package/dist/combobox/combobox-group.d.mts +72 -0
  8. package/dist/combobox/combobox-group.d.mts.map +1 -0
  9. package/dist/combobox/combobox-group.mjs +104 -0
  10. package/dist/combobox/combobox-group.mjs.map +1 -0
  11. package/dist/combobox/combobox-input.d.mts +40 -0
  12. package/dist/combobox/combobox-input.d.mts.map +1 -0
  13. package/dist/combobox/combobox-input.mjs +88 -0
  14. package/dist/combobox/combobox-input.mjs.map +1 -0
  15. package/dist/combobox/combobox-label.d.mts +22 -0
  16. package/dist/combobox/combobox-label.d.mts.map +1 -0
  17. package/dist/combobox/combobox-label.mjs +32 -0
  18. package/dist/combobox/combobox-label.mjs.map +1 -0
  19. package/dist/combobox/combobox-list.d.mts +37 -0
  20. package/dist/combobox/combobox-list.d.mts.map +1 -0
  21. package/dist/combobox/combobox-list.mjs +70 -0
  22. package/dist/combobox/combobox-list.mjs.map +1 -0
  23. package/dist/combobox/combobox-option.d.mts +45 -0
  24. package/dist/combobox/combobox-option.d.mts.map +1 -0
  25. package/dist/combobox/combobox-option.mjs +88 -0
  26. package/dist/combobox/combobox-option.mjs.map +1 -0
  27. package/dist/combobox/combobox-popup.d.mts +49 -0
  28. package/dist/combobox/combobox-popup.d.mts.map +1 -0
  29. package/dist/combobox/combobox-popup.mjs +86 -0
  30. package/dist/combobox/combobox-popup.mjs.map +1 -0
  31. package/dist/combobox/combobox-root.d.mts +135 -0
  32. package/dist/combobox/combobox-root.d.mts.map +1 -0
  33. package/dist/combobox/combobox-root.mjs +508 -0
  34. package/dist/combobox/combobox-root.mjs.map +1 -0
  35. package/dist/combobox/combobox-trigger.d.mts +36 -0
  36. package/dist/combobox/combobox-trigger.d.mts.map +1 -0
  37. package/dist/combobox/combobox-trigger.mjs +92 -0
  38. package/dist/combobox/combobox-trigger.mjs.map +1 -0
  39. package/dist/combobox/index.d.mts +36 -0
  40. package/dist/combobox/index.d.mts.map +1 -0
  41. package/dist/combobox/index.mjs +36 -0
  42. package/dist/combobox/index.mjs.map +1 -0
  43. package/dist/index.d.mts +20 -2
  44. package/dist/index.mjs +3 -1
  45. package/dist/internal/platform-attributes.d.mts +12 -1
  46. package/dist/internal/platform-attributes.d.mts.map +1 -1
  47. package/dist/internal/types.d.mts +8 -0
  48. package/dist/internal/types.d.mts.map +1 -1
  49. package/dist/internal/use-controllable-action.mjs +5 -1
  50. package/dist/internal/use-controllable-action.mjs.map +1 -1
  51. package/dist/internal/use-form-reset.mjs +5 -1
  52. package/dist/internal/use-form-reset.mjs.map +1 -1
  53. package/dist/internal/use-hydrated-mount.mjs +21 -0
  54. package/dist/internal/use-hydrated-mount.mjs.map +1 -0
  55. package/dist/internal/use-native-group-reconciliation.mjs.map +1 -1
  56. package/dist/internal/use-native-select-value.mjs +182 -0
  57. package/dist/internal/use-native-select-value.mjs.map +1 -0
  58. package/dist/select/index.d.mts +30 -0
  59. package/dist/select/index.d.mts.map +1 -0
  60. package/dist/select/index.mjs +30 -0
  61. package/dist/select/index.mjs.map +1 -0
  62. package/dist/select/select-context.mjs +13 -0
  63. package/dist/select/select-context.mjs.map +1 -0
  64. package/dist/select/select-dom.mjs +44 -0
  65. package/dist/select/select-dom.mjs.map +1 -0
  66. package/dist/select/select-group-label.d.mts +22 -0
  67. package/dist/select/select-group-label.d.mts.map +1 -0
  68. package/dist/select/select-group-label.mjs +39 -0
  69. package/dist/select/select-group-label.mjs.map +1 -0
  70. package/dist/select/select-group.d.mts +28 -0
  71. package/dist/select/select-group.d.mts.map +1 -0
  72. package/dist/select/select-group.mjs +45 -0
  73. package/dist/select/select-group.mjs.map +1 -0
  74. package/dist/select/select-option.d.mts +42 -0
  75. package/dist/select/select-option.d.mts.map +1 -0
  76. package/dist/select/select-option.mjs +65 -0
  77. package/dist/select/select-option.mjs.map +1 -0
  78. package/dist/select/select-root.d.mts +66 -0
  79. package/dist/select/select-root.d.mts.map +1 -0
  80. package/dist/select/select-root.mjs +94 -0
  81. package/dist/select/select-root.mjs.map +1 -0
  82. package/dist/select/select-separator.d.mts +20 -0
  83. package/dist/select/select-separator.d.mts.map +1 -0
  84. package/dist/select/select-separator.mjs +30 -0
  85. package/dist/select/select-separator.mjs.map +1 -0
  86. package/dist/select/select-trigger.d.mts +29 -0
  87. package/dist/select/select-trigger.d.mts.map +1 -0
  88. package/dist/select/select-trigger.mjs +48 -0
  89. package/dist/select/select-trigger.mjs.map +1 -0
  90. package/dist/select/select-value.d.mts +31 -0
  91. package/dist/select/select-value.d.mts.map +1 -0
  92. package/dist/select/select-value.mjs +101 -0
  93. package/dist/select/select-value.mjs.map +1 -0
  94. package/package.json +3 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-option.mjs","names":[],"sources":["../../src/select/select-option.tsx"],"sourcesContent":["\"use client\";\nimport { useEffectEvent, useLayoutEffect } from \"react\";\nimport type { ComponentPropsWithRef, CSSProperties, ReactNode } from \"react\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport { syncPresence } from \"../internal/sync-presence.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { useHydratedMount } from \"../internal/use-hydrated-mount.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport { useSelectContext } from \"./select-context.js\";\nimport { useSelectPartRef } from \"./select-dom.js\";\n\nexport interface SelectOptionState {\n readonly value: string;\n /** Optimistic: true as soon as the user picks this option, before the action settles. */\n readonly selected: boolean;\n /** The Root is pending and this option is the optimistic target. */\n readonly pending: boolean;\n /** Disabled by its own prop, a disabled `Select.Group`, or `Root disabled` (as `:disabled`). */\n readonly disabled: boolean;\n}\n\nexport interface SelectOptionProps extends Omit<\n ComponentPropsWithRef<\"option\">,\n \"value\" | \"selected\" | \"className\" | \"style\" | \"children\"\n> {\n /** The option's value. Required: React cannot infer a value from rich children. */\n value: string;\n /** Native `disabled`: not pickable, skipped by keyboard navigation. */\n disabled?: boolean;\n className?: StateValue<string | undefined, SelectOptionState>;\n style?: StateValue<CSSProperties | undefined, SelectOptionState>;\n /**\n * The option's content. Keep it readable as plain text: engines without customizable select\n * (Firefox today) show only the text. Give icons `aria-hidden` and no text. Avoid the `label`\n * attribute (Chromium names the option from it but shows the content).\n */\n children?: StateValue<ReactNode, SelectOptionState>;\n /** Replace the rendered element. Must keep an `<option>`. */\n render?: RenderProp<ComponentPropsWithRef<\"option\">, SelectOptionState>;\n}\n\n/**\n * One `<option>`. It never renders `selected`: the selection is the Root's `value={optimistic}`\n * plus the Root's re-apply for options that mount after the select's own commit (a Suspense\n * boundary or `use()` inside the select), which the Option reports from its mount layout effect.\n *\n * Owned keys: `value`, `selected` (never rendered), `disabled`, `data-selected`, `data-pending`,\n * `data-disabled`.\n */\nexport function SelectOption({\n value,\n disabled: disabledProp = false,\n className,\n style,\n children,\n render,\n ...rest\n}: SelectOptionProps) {\n const ctx = useSelectContext(\"Select.Option\");\n const selected = ctx.optimistic === value;\n const pending = ctx.pending && selected;\n const disabled = ctx.disabled || ctx.groupDisabled || disabledProp;\n const hydrated = useHydratedMount();\n const { ref, node } = useSelectPartRef<HTMLOptionElement>(\"Select.Option\", \"option\");\n\n // Reads the mount render's values: for a hydrated option that is the hydration render, which\n // used the server snapshot (`hydrated === true`).\n const reportMount = useEffectEvent(() => {\n const el = node.current;\n if (el !== null) {\n // Hydration keeps server attributes that differ from the client render (React only warns);\n // an option hydrating after its Root can render a different state than the server did.\n syncPresence(el, \"data-selected\", selected);\n syncPresence(el, \"data-pending\", pending);\n syncPresence(el, \"data-disabled\", disabled);\n }\n ctx.optionMounted(el, hydrated);\n });\n useLayoutEffect(() => {\n reportMount();\n }, []);\n\n const state: SelectOptionState = { value, selected, pending, disabled };\n\n return useRender<ComponentPropsWithRef<\"option\">, SelectOptionState>({\n defaultElement: \"option\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps: {\n value,\n selected: undefined,\n // Only the Option's own prop: `Root disabled` and the Group disable natively themselves.\n disabled: disabledProp,\n \"data-selected\": dataAttr(selected),\n \"data-pending\": dataAttr(pending),\n \"data-disabled\": dataAttr(disabled),\n },\n ref,\n });\n}\n\nSelectOption.displayName = \"SelectOption\";\n"],"mappings":";;;;;;;;;;;;;;;;;AAiDA,SAAgB,aAAa,EAC3B,OACA,UAAU,eAAe,OACzB,WACA,OACA,UACA,QACA,GAAG,QACiB;CACpB,MAAM,MAAM,iBAAiB,eAAe;CAC5C,MAAM,WAAW,IAAI,eAAe;CACpC,MAAM,UAAU,IAAI,WAAW;CAC/B,MAAM,WAAW,IAAI,YAAY,IAAI,iBAAiB;CACtD,MAAM,WAAW,iBAAiB;CAClC,MAAM,EAAE,KAAK,SAAS,iBAAoC,iBAAiB,QAAQ;CAInF,MAAM,cAAc,qBAAqB;EACvC,MAAM,KAAK,KAAK;EAChB,IAAI,OAAO,MAAM;GAGf,aAAa,IAAI,iBAAiB,QAAQ;GAC1C,aAAa,IAAI,gBAAgB,OAAO;GACxC,aAAa,IAAI,iBAAiB,QAAQ;EAC5C;EACA,IAAI,cAAc,IAAI,QAAQ;CAChC,CAAC;CACD,sBAAsB;EACpB,YAAY;CACd,GAAG,CAAC,CAAC;CAIL,OAAO,UAA8D;EACnE,gBAAgB;EAChB;EACA,OAAA;GALiC;GAAO;GAAU;GAAS;EAKvD;EACJ;EACA;EACA;EACA,OAAO;EACP,cAAc;GACZ;GACA,UAAU,KAAA;GAEV,UAAU;GACV,iBAAiB,SAAS,QAAQ;GAClC,gBAAgB,SAAS,OAAO;GAChC,iBAAiB,SAAS,QAAQ;EACpC;EACA;CACF,CAAC;AACH;AAEA,aAAa,cAAc"}
@@ -0,0 +1,66 @@
1
+ import { ActionDetails, ChangeAction, ChangeReason, RenderProp, StateValue } from "../internal/types.mjs";
2
+ import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
3
+ //#region src/select/select-root.d.ts
4
+ /** Details passed as the second argument of `valueChangeAction`. */
5
+ export interface SelectValueChangeDetails extends ActionDetails {
6
+ /**
7
+ * `"activate"`: the user picked an option (pointer, keyboard, typeahead, or browser autofill,
8
+ * which the library cannot tell from a pick); `details.event` is the native `change` event.
9
+ * `"platform"`: the browser changed the value without a pick the library saw: a form reset
10
+ * (`details.event` is the `reset` event), or a pick before hydration / Firefox form-state
11
+ * restoration reconciled at mount (`details.event` is `undefined`).
12
+ */
13
+ readonly reason: ChangeReason;
14
+ }
15
+ export interface SelectRootState {
16
+ /** Optimistic value. Content rendered from it must not suspend. */
17
+ readonly value: string;
18
+ /** An action dispatched through this Select is in flight. */
19
+ readonly pending: boolean;
20
+ /** The `disabled` prop. */
21
+ readonly disabled: boolean;
22
+ }
23
+ export interface SelectRootProps extends Omit<ComponentPropsWithRef<"select">, "value" | "defaultValue" | "onChange" | "multiple" | "size" | "disabled" | "className" | "style" | "children"> {
24
+ /** Controlled value. `undefined` means uncontrolled. Use `""` for the placeholder option. */
25
+ value?: string;
26
+ /**
27
+ * Initial value in uncontrolled mode, and the value a form reset returns to (the latest prop is
28
+ * mirrored into the options' `defaultSelected`). When omitted, the browser's choice (an option
29
+ * whose value is `""`, else the first enabled option) is adopted at mount, without a dispatch,
30
+ * and is what a form reset returns to. Until a pick (or a platform change) sets the value,
31
+ * options that mount later without the adopted one (a Suspense fallback) adopt again.
32
+ */
33
+ defaultValue?: string;
34
+ /**
35
+ * Runs inside the Root's transition whenever the selected option changes. May be sync or async
36
+ * (also a Server Function); rejections reach the nearest Error Boundary and roll the selection
37
+ * back.
38
+ *
39
+ * In engines whose closed select changes the value on every arrow press (Firefox, Safari with
40
+ * native appearance), every press dispatches; the latest dispatch wins. A `setState` placed
41
+ * after your own `await` is not a Transition update; wrap it in `startTransition` (React
42
+ * limitation). Controlled mode is the way to veto a change.
43
+ */
44
+ valueChangeAction?: ChangeAction<string, SelectValueChangeDetails>;
45
+ /** Native `disabled`. Never set by the library while pending. */
46
+ disabled?: boolean;
47
+ className?: StateValue<string | undefined, SelectRootState>;
48
+ style?: StateValue<CSSProperties | undefined, SelectRootState>;
49
+ children?: StateValue<ReactNode, SelectRootState>;
50
+ /** Replace the rendered element. Must keep a `<select>`. */
51
+ render?: RenderProp<ComponentPropsWithRef<"select">, SelectRootState>;
52
+ }
53
+ /**
54
+ * The `<select>` itself: owns the value, the transition, the form-reset listener, the mount
55
+ * reconciliation and the re-apply of late options (design/components/select.md 15.2). Renders
56
+ * `value={optimistic}`: react-dom restores a controlled select to its props after every event,
57
+ * and only the sync-lane optimistic value survives that restore.
58
+ *
59
+ * Owned keys (conventions 1.2): `value`, `disabled`, `data-pending`, `data-disabled`, `onChange`.
60
+ */
61
+ export declare function SelectRoot({ value, defaultValue, valueChangeAction, disabled, className, style, children, render, ...rest }: SelectRootProps): import("react").JSX.Element;
62
+ export declare namespace SelectRoot {
63
+ var displayName: string;
64
+ }
65
+ //#endregion
66
+ //# sourceMappingURL=select-root.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-root.d.mts","names":[],"sources":["../../src/select/select-root.tsx"],"mappings":";;;;iBAqBiB,iCAAiC;;;;;;;;WAQvC,QAAQ;;iBAGF;;WAEN;;WAEA;;WAEA;;iBAGM,wBAAwB,KACvC;;EAYA;;;;;;;;EAQA;;;;;;;;;;;EAWA,oBAAoB,qBAAqB;;EAEzC;EACA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;;EAEjC,SAAS,WAAW,iCAAiC;;;;;;;;;;wBAWvC,aACd,OACA,cACA,mBACA,UACA,WACA,OACA,UACA,WACG,QACF,kCAAe,IAAA;yBAVF"}
@@ -0,0 +1,94 @@
1
+ "use client";
2
+ import { createActionDetails } from "../internal/action-details.mjs";
3
+ import { dataAttr } from "../internal/data-attr.mjs";
4
+ import { transitionTypes } from "../internal/transition-types.mjs";
5
+ import { useControllableAction } from "../internal/use-controllable-action.mjs";
6
+ import { useRender } from "../internal/use-render.mjs";
7
+ import { SelectContext } from "./select-context.mjs";
8
+ import { useSelectPartRef } from "./select-dom.mjs";
9
+ import { useNativeSelectValue } from "../internal/use-native-select-value.mjs";
10
+ import { useRef, useState } from "react";
11
+ import { jsx } from "react/jsx-runtime";
12
+ //#region src/select/select-root.tsx
13
+ /**
14
+ * The `<select>` itself: owns the value, the transition, the form-reset listener, the mount
15
+ * reconciliation and the re-apply of late options (design/components/select.md 15.2). Renders
16
+ * `value={optimistic}`: react-dom restores a controlled select to its props after every event,
17
+ * and only the sync-lane optimistic value survives that restore.
18
+ *
19
+ * Owned keys (conventions 1.2): `value`, `disabled`, `data-pending`, `data-disabled`, `onChange`.
20
+ */
21
+ function SelectRoot({ value, defaultValue, valueChangeAction, disabled = false, className, style, children, render, ...rest }) {
22
+ const { ref: libraryRef, node: selectRef } = useSelectPartRef("Select.Root", "select");
23
+ const { optimistic, pending, dispatch, latestInFlight, adopt } = useControllableAction({
24
+ controlled: value,
25
+ defaultValue,
26
+ initialValue: "",
27
+ action: valueChangeAction,
28
+ transitionTypes: () => transitionTypes("select"),
29
+ warn: {
30
+ component: "Select.Root",
31
+ prop: "value"
32
+ }
33
+ });
34
+ const [mountValue] = useState(value);
35
+ const resetTarget = value === void 0 ? defaultValue : mountValue ?? value;
36
+ const established = useRef(false);
37
+ const dispatchChange = (next, details) => {
38
+ established.current = true;
39
+ dispatch(next, details);
40
+ };
41
+ const { optionMounted } = useNativeSelectValue({
42
+ ref: selectRef,
43
+ optimistic,
44
+ latestInFlight,
45
+ resetTarget,
46
+ adoptDefault: value === void 0 && defaultValue === void 0 ? {
47
+ adopt,
48
+ established: () => established.current
49
+ } : void 0,
50
+ onPlatformChange: (next, event) => dispatchChange(next, createActionDetails(event, { reason: "platform" }))
51
+ });
52
+ const context = {
53
+ optimistic,
54
+ pending,
55
+ disabled,
56
+ groupDisabled: false,
57
+ optionMounted
58
+ };
59
+ const element = useRender({
60
+ defaultElement: "select",
61
+ render,
62
+ state: {
63
+ value: optimistic,
64
+ pending,
65
+ disabled
66
+ },
67
+ className,
68
+ style,
69
+ children,
70
+ props: rest,
71
+ libraryProps: {
72
+ value: optimistic,
73
+ disabled,
74
+ "data-pending": dataAttr(pending),
75
+ "data-disabled": dataAttr(disabled),
76
+ onChange: (event) => {
77
+ if (disabled) return;
78
+ const next = event.currentTarget.value;
79
+ if (next === optimistic) return;
80
+ dispatchChange(next, createActionDetails(event.nativeEvent, { reason: "activate" }));
81
+ }
82
+ },
83
+ ref: libraryRef
84
+ });
85
+ return /* @__PURE__ */ jsx(SelectContext, {
86
+ value: context,
87
+ children: element
88
+ });
89
+ }
90
+ SelectRoot.displayName = "SelectRoot";
91
+ //#endregion
92
+ export { SelectRoot };
93
+
94
+ //# sourceMappingURL=select-root.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-root.mjs","names":[],"sources":["../../src/select/select-root.tsx"],"sourcesContent":["\"use client\";\nimport { useRef, useState } from \"react\";\nimport type { ChangeEvent, ComponentPropsWithRef, CSSProperties, ReactNode } from \"react\";\nimport { createActionDetails } from \"../internal/action-details.js\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport { transitionTypes } from \"../internal/transition-types.js\";\nimport type {\n ActionDetails,\n ChangeAction,\n ChangeReason,\n RenderProp,\n StateValue,\n} from \"../internal/types.js\";\nimport { useControllableAction } from \"../internal/use-controllable-action.js\";\nimport { useNativeSelectValue } from \"../internal/use-native-select-value.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport { SelectContext } from \"./select-context.js\";\nimport type { SelectContextValue } from \"./select-context.js\";\nimport { useSelectPartRef } from \"./select-dom.js\";\n\n/** Details passed as the second argument of `valueChangeAction`. */\nexport interface SelectValueChangeDetails extends ActionDetails {\n /**\n * `\"activate\"`: the user picked an option (pointer, keyboard, typeahead, or browser autofill,\n * which the library cannot tell from a pick); `details.event` is the native `change` event.\n * `\"platform\"`: the browser changed the value without a pick the library saw: a form reset\n * (`details.event` is the `reset` event), or a pick before hydration / Firefox form-state\n * restoration reconciled at mount (`details.event` is `undefined`).\n */\n readonly reason: ChangeReason;\n}\n\nexport interface SelectRootState {\n /** Optimistic value. Content rendered from it must not suspend. */\n readonly value: string;\n /** An action dispatched through this Select is in flight. */\n readonly pending: boolean;\n /** The `disabled` prop. */\n readonly disabled: boolean;\n}\n\nexport interface SelectRootProps extends Omit<\n ComponentPropsWithRef<\"select\">,\n | \"value\"\n | \"defaultValue\"\n | \"onChange\"\n | \"multiple\"\n | \"size\"\n | \"disabled\"\n | \"className\"\n | \"style\"\n | \"children\"\n> {\n /** Controlled value. `undefined` means uncontrolled. Use `\"\"` for the placeholder option. */\n value?: string;\n /**\n * Initial value in uncontrolled mode, and the value a form reset returns to (the latest prop is\n * mirrored into the options' `defaultSelected`). When omitted, the browser's choice (an option\n * whose value is `\"\"`, else the first enabled option) is adopted at mount, without a dispatch,\n * and is what a form reset returns to. Until a pick (or a platform change) sets the value,\n * options that mount later without the adopted one (a Suspense fallback) adopt again.\n */\n defaultValue?: string;\n /**\n * Runs inside the Root's transition whenever the selected option changes. May be sync or async\n * (also a Server Function); rejections reach the nearest Error Boundary and roll the selection\n * back.\n *\n * In engines whose closed select changes the value on every arrow press (Firefox, Safari with\n * native appearance), every press dispatches; the latest dispatch wins. A `setState` placed\n * after your own `await` is not a Transition update; wrap it in `startTransition` (React\n * limitation). Controlled mode is the way to veto a change.\n */\n valueChangeAction?: ChangeAction<string, SelectValueChangeDetails>;\n /** Native `disabled`. Never set by the library while pending. */\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 a `<select>`. */\n render?: RenderProp<ComponentPropsWithRef<\"select\">, SelectRootState>;\n}\n\n/**\n * The `<select>` itself: owns the value, the transition, the form-reset listener, the mount\n * reconciliation and the re-apply of late options (design/components/select.md 15.2). Renders\n * `value={optimistic}`: react-dom restores a controlled select to its props after every event,\n * and only the sync-lane optimistic value survives that restore.\n *\n * Owned keys (conventions 1.2): `value`, `disabled`, `data-pending`, `data-disabled`, `onChange`.\n */\nexport function SelectRoot({\n value,\n defaultValue,\n valueChangeAction,\n disabled = false,\n className,\n style,\n children,\n render,\n ...rest\n}: SelectRootProps) {\n const { ref: libraryRef, node: selectRef } = useSelectPartRef<HTMLSelectElement>(\n \"Select.Root\",\n \"select\",\n );\n\n const { optimistic, pending, dispatch, latestInFlight, adopt } = useControllableAction<\n string,\n SelectValueChangeDetails\n >({\n controlled: value,\n defaultValue,\n initialValue: \"\",\n action: valueChangeAction,\n transitionTypes: () => transitionTypes(\"select\"),\n warn: { component: \"Select.Root\", prop: \"value\" },\n });\n // Deliberately no warning when neither `value` nor `defaultValue` is given: the browser's\n // first-option default is adopted (design decision 3, product-owner decision 4).\n\n // Controlled: the mount-time value is the reset target (conventions 3.8 rule 6). Neither\n // `value` nor `defaultValue`: the value adopted at mount, chosen inside the hook.\n const [mountValue] = useState(value);\n const resetTarget = value === undefined ? defaultValue : (mountValue ?? value);\n\n // Any dispatch, a pick or a platform change, ends the adoption of the browser's default.\n const established = useRef(false);\n const dispatchChange = (next: string, details: SelectValueChangeDetails) => {\n established.current = true;\n dispatch(next, details);\n };\n\n const { optionMounted } = useNativeSelectValue({\n ref: selectRef,\n optimistic,\n latestInFlight,\n resetTarget,\n adoptDefault:\n value === undefined && defaultValue === undefined\n ? { adopt, established: () => established.current }\n : undefined,\n onPlatformChange: (next, event) =>\n dispatchChange(next, createActionDetails(event, { reason: \"platform\" })),\n });\n\n const context: SelectContextValue = {\n optimistic,\n pending,\n disabled,\n groupDisabled: false,\n optionMounted,\n };\n const state: SelectRootState = { value: optimistic, pending, disabled };\n\n const element = useRender<ComponentPropsWithRef<\"select\">, SelectRootState>({\n defaultElement: \"select\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps: {\n value: optimistic,\n disabled,\n \"data-pending\": dataAttr(pending),\n \"data-disabled\": dataAttr(disabled),\n onChange: (event: ChangeEvent<HTMLSelectElement>) => {\n // A natively disabled select fires no change; this covers `render` elements.\n if (disabled) return;\n const next = event.currentTarget.value;\n // No-op rule against the optimistic value (conventions 2.3, design 4.2).\n if (next === optimistic) return;\n dispatchChange(next, createActionDetails(event.nativeEvent, { reason: \"activate\" }));\n },\n },\n ref: libraryRef,\n });\n\n return <SelectContext value={context}>{element}</SelectContext>;\n}\n\nSelectRoot.displayName = \"SelectRoot\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA2FA,SAAgB,WAAW,EACzB,OACA,cACA,mBACA,WAAW,OACX,WACA,OACA,UACA,QACA,GAAG,QACe;CAClB,MAAM,EAAE,KAAK,YAAY,MAAM,cAAc,iBAC3C,eACA,QACF;CAEA,MAAM,EAAE,YAAY,SAAS,UAAU,gBAAgB,UAAU,sBAG/D;EACA,YAAY;EACZ;EACA,cAAc;EACd,QAAQ;EACR,uBAAuB,gBAAgB,QAAQ;EAC/C,MAAM;GAAE,WAAW;GAAe,MAAM;EAAQ;CAClD,CAAC;CAMD,MAAM,CAAC,cAAc,SAAS,KAAK;CACnC,MAAM,cAAc,UAAU,KAAA,IAAY,eAAgB,cAAc;CAGxE,MAAM,cAAc,OAAO,KAAK;CAChC,MAAM,kBAAkB,MAAc,YAAsC;EAC1E,YAAY,UAAU;EACtB,SAAS,MAAM,OAAO;CACxB;CAEA,MAAM,EAAE,kBAAkB,qBAAqB;EAC7C,KAAK;EACL;EACA;EACA;EACA,cACE,UAAU,KAAA,KAAa,iBAAiB,KAAA,IACpC;GAAE;GAAO,mBAAmB,YAAY;EAAQ,IAChD,KAAA;EACN,mBAAmB,MAAM,UACvB,eAAe,MAAM,oBAAoB,OAAO,EAAE,QAAQ,WAAW,CAAC,CAAC;CAC3E,CAAC;CAED,MAAM,UAA8B;EAClC;EACA;EACA;EACA,eAAe;EACf;CACF;CAGA,MAAM,UAAU,UAA4D;EAC1E,gBAAgB;EAChB;EACA,OAAA;GAL+B,OAAO;GAAY;GAAS;EAKvD;EACJ;EACA;EACA;EACA,OAAO;EACP,cAAc;GACZ,OAAO;GACP;GACA,gBAAgB,SAAS,OAAO;GAChC,iBAAiB,SAAS,QAAQ;GAClC,WAAW,UAA0C;IAEnD,IAAI,UAAU;IACd,MAAM,OAAO,MAAM,cAAc;IAEjC,IAAI,SAAS,YAAY;IACzB,eAAe,MAAM,oBAAoB,MAAM,aAAa,EAAE,QAAQ,WAAW,CAAC,CAAC;GACrF;EACF;EACA,KAAK;CACP,CAAC;CAED,OAAO,oBAAC,eAAD;EAAe,OAAO;EAAU,UAAA;CAAuB,CAAA;AAChE;AAEA,WAAW,cAAc"}
@@ -0,0 +1,20 @@
1
+ import { RenderProp, StateValue } from "../internal/types.mjs";
2
+ import { SelectRootState } from "./select-root.mjs";
3
+ import { CSSProperties, ComponentPropsWithRef } from "react";
4
+ //#region src/select/select-separator.d.ts
5
+ export interface SelectSeparatorProps extends Omit<ComponentPropsWithRef<"hr">, "className" | "style" | "children"> {
6
+ className?: StateValue<string | undefined, SelectRootState>;
7
+ style?: StateValue<CSSProperties | undefined, SelectRootState>;
8
+ /** Replace the rendered element. */
9
+ render?: RenderProp<ComponentPropsWithRef<"hr">, SelectRootState>;
10
+ }
11
+ /**
12
+ * A decorative `<hr>` between options or groups (exposed as `separator` by Chromium's
13
+ * customizable select; a line in native pickers). Owns no keys.
14
+ */
15
+ export declare function SelectSeparator({ className, style, render, ...rest }: SelectSeparatorProps): import("react").ReactNode;
16
+ export declare namespace SelectSeparator {
17
+ var displayName: string;
18
+ }
19
+ //#endregion
20
+ //# sourceMappingURL=select-separator.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-separator.d.mts","names":[],"sources":["../../src/select/select-separator.tsx"],"mappings":";;;;iBAOiB,6BAA6B,KAC5C;EAGA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;;EAE9C,SAAS,WAAW,6BAA6B;;;;;;wBAOnC,kBAAkB,WAAW,OAAO,WAAW,QAAQ,uCAAoB;yBAA3E"}
@@ -0,0 +1,30 @@
1
+ "use client";
2
+ import { useRender } from "../internal/use-render.mjs";
3
+ import { useSelectContext } from "./select-context.mjs";
4
+ //#region src/select/select-separator.tsx
5
+ /**
6
+ * A decorative `<hr>` between options or groups (exposed as `separator` by Chromium's
7
+ * customizable select; a line in native pickers). Owns no keys.
8
+ */
9
+ function SelectSeparator({ className, style, render, ...rest }) {
10
+ const ctx = useSelectContext("Select.Separator");
11
+ const state = {
12
+ value: ctx.optimistic,
13
+ pending: ctx.pending,
14
+ disabled: ctx.disabled
15
+ };
16
+ return useRender({
17
+ defaultElement: "hr",
18
+ render,
19
+ state,
20
+ className,
21
+ style,
22
+ children: void 0,
23
+ props: rest
24
+ });
25
+ }
26
+ SelectSeparator.displayName = "SelectSeparator";
27
+ //#endregion
28
+ export { SelectSeparator };
29
+
30
+ //# sourceMappingURL=select-separator.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-separator.mjs","names":[],"sources":["../../src/select/select-separator.tsx"],"sourcesContent":["\"use client\";\nimport type { ComponentPropsWithRef, CSSProperties } from \"react\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport { useSelectContext } from \"./select-context.js\";\nimport type { SelectRootState } from \"./select-root.js\";\n\nexport interface SelectSeparatorProps extends Omit<\n ComponentPropsWithRef<\"hr\">,\n \"className\" | \"style\" | \"children\"\n> {\n className?: StateValue<string | undefined, SelectRootState>;\n style?: StateValue<CSSProperties | undefined, SelectRootState>;\n /** Replace the rendered element. */\n render?: RenderProp<ComponentPropsWithRef<\"hr\">, SelectRootState>;\n}\n\n/**\n * A decorative `<hr>` between options or groups (exposed as `separator` by Chromium's\n * customizable select; a line in native pickers). Owns no keys.\n */\nexport function SelectSeparator({ className, style, render, ...rest }: SelectSeparatorProps) {\n const ctx = useSelectContext(\"Select.Separator\");\n const state: SelectRootState = {\n value: ctx.optimistic,\n pending: ctx.pending,\n disabled: ctx.disabled,\n };\n return useRender<ComponentPropsWithRef<\"hr\">, SelectRootState>({\n defaultElement: \"hr\",\n render,\n state,\n className,\n style,\n children: undefined,\n props: rest,\n });\n}\n\nSelectSeparator.displayName = \"SelectSeparator\";\n"],"mappings":";;;;;;;;AAqBA,SAAgB,gBAAgB,EAAE,WAAW,OAAO,QAAQ,GAAG,QAA8B;CAC3F,MAAM,MAAM,iBAAiB,kBAAkB;CAC/C,MAAM,QAAyB;EAC7B,OAAO,IAAI;EACX,SAAS,IAAI;EACb,UAAU,IAAI;CAChB;CACA,OAAO,UAAwD;EAC7D,gBAAgB;EAChB;EACA;EACA;EACA;EACA,UAAU,KAAA;EACV,OAAO;CACT,CAAC;AACH;AAEA,gBAAgB,cAAc"}
@@ -0,0 +1,29 @@
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-trigger.d.ts
5
+ export interface SelectTriggerProps extends Omit<ComponentPropsWithRef<"button">, "type" | "className" | "style" | "children"> {
6
+ className?: StateValue<string | undefined, SelectRootState>;
7
+ style?: StateValue<CSSProperties | undefined, SelectRootState>;
8
+ /**
9
+ * Non-interactive content only (HTML content model of a select's button), usually
10
+ * `<Select.Value />`. Not rendered by engines without customizable select (Firefox today).
11
+ */
12
+ children?: StateValue<ReactNode, SelectRootState>;
13
+ /** Replace the rendered element. Must keep a `<button>`. */
14
+ render?: RenderProp<ComponentPropsWithRef<"button">, SelectRootState>;
15
+ }
16
+ /**
17
+ * The select's own `<button>` (customizable select): replaces the UA button in engines with
18
+ * `appearance: base-select`. Optional; **must be the first child** of `Select.Root`. Its state is
19
+ * the Root's state, so a pending indicator next to `<Select.Value />` goes in its function
20
+ * `children`.
21
+ *
22
+ * Owned keys: `type` (always `"button"`), `data-pending`, `data-disabled`.
23
+ */
24
+ export declare function SelectTrigger({ className, style, children, render, ...rest }: SelectTriggerProps): ReactNode;
25
+ export declare namespace SelectTrigger {
26
+ var displayName: string;
27
+ }
28
+ //#endregion
29
+ //# sourceMappingURL=select-trigger.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-trigger.d.mts","names":[],"sources":["../../src/select/select-trigger.tsx"],"mappings":";;;;iBASiB,2BAA2B,KAC1C;EAGA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;;;;;EAK9C,WAAW,WAAW,WAAW;;EAEjC,SAAS,WAAW,iCAAiC;;;;;;;;;;wBAoBvC,gBAAgB,WAAW,OAAO,UAAU,WAAW,QAAQ,qBAAkB;yBAAjF"}
@@ -0,0 +1,48 @@
1
+ "use client";
2
+ import { dataAttr } from "../internal/data-attr.mjs";
3
+ import { useRender } from "../internal/use-render.mjs";
4
+ import { useSelectContext } from "./select-context.mjs";
5
+ import { useSelectPartRef } from "./select-dom.mjs";
6
+ //#region src/select/select-trigger.tsx
7
+ function firstChildOfSelect(el) {
8
+ const parent = el.parentElement;
9
+ if (parent?.tagName === "SELECT" && parent.firstElementChild === el) return null;
10
+ return "must be the first child of Select.Root's <select> (HTML content model); elsewhere the browser does not use it as the select's button.";
11
+ }
12
+ /**
13
+ * The select's own `<button>` (customizable select): replaces the UA button in engines with
14
+ * `appearance: base-select`. Optional; **must be the first child** of `Select.Root`. Its state is
15
+ * the Root's state, so a pending indicator next to `<Select.Value />` goes in its function
16
+ * `children`.
17
+ *
18
+ * Owned keys: `type` (always `"button"`), `data-pending`, `data-disabled`.
19
+ */
20
+ function SelectTrigger({ className, style, children, render, ...rest }) {
21
+ const ctx = useSelectContext("Select.Trigger");
22
+ const state = {
23
+ value: ctx.optimistic,
24
+ pending: ctx.pending,
25
+ disabled: ctx.disabled
26
+ };
27
+ const { ref } = useSelectPartRef("Select.Trigger", "button", firstChildOfSelect);
28
+ return useRender({
29
+ defaultElement: "button",
30
+ render,
31
+ state,
32
+ className,
33
+ style,
34
+ children,
35
+ props: rest,
36
+ libraryProps: {
37
+ type: "button",
38
+ "data-pending": dataAttr(ctx.pending),
39
+ "data-disabled": dataAttr(ctx.disabled)
40
+ },
41
+ ref
42
+ });
43
+ }
44
+ SelectTrigger.displayName = "SelectTrigger";
45
+ //#endregion
46
+ export { SelectTrigger };
47
+
48
+ //# sourceMappingURL=select-trigger.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-trigger.mjs","names":[],"sources":["../../src/select/select-trigger.tsx"],"sourcesContent":["\"use client\";\nimport type { ComponentPropsWithRef, CSSProperties, ReactNode } from \"react\";\nimport { dataAttr } from \"../internal/data-attr.js\";\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 SelectTriggerProps extends Omit<\n ComponentPropsWithRef<\"button\">,\n \"type\" | \"className\" | \"style\" | \"children\"\n> {\n className?: StateValue<string | undefined, SelectRootState>;\n style?: StateValue<CSSProperties | undefined, SelectRootState>;\n /**\n * Non-interactive content only (HTML content model of a select's button), usually\n * `<Select.Value />`. Not rendered by engines without customizable select (Firefox today).\n */\n children?: StateValue<ReactNode, SelectRootState>;\n /** Replace the rendered element. Must keep a `<button>`. */\n render?: RenderProp<ComponentPropsWithRef<\"button\">, SelectRootState>;\n}\n\nfunction firstChildOfSelect(el: HTMLButtonElement): string | null {\n const parent = el.parentElement;\n if (parent?.tagName === \"SELECT\" && parent.firstElementChild === el) return null;\n return (\n \"must be the first child of Select.Root's <select> (HTML content model); elsewhere the \" +\n \"browser does not use it as the select's button.\"\n );\n}\n\n/**\n * The select's own `<button>` (customizable select): replaces the UA button in engines with\n * `appearance: base-select`. Optional; **must be the first child** of `Select.Root`. Its state is\n * the Root's state, so a pending indicator next to `<Select.Value />` goes in its function\n * `children`.\n *\n * Owned keys: `type` (always `\"button\"`), `data-pending`, `data-disabled`.\n */\nexport function SelectTrigger({ className, style, children, render, ...rest }: SelectTriggerProps) {\n const ctx = useSelectContext(\"Select.Trigger\");\n const state: SelectRootState = {\n value: ctx.optimistic,\n pending: ctx.pending,\n disabled: ctx.disabled,\n };\n const { ref } = useSelectPartRef<HTMLButtonElement>(\n \"Select.Trigger\",\n \"button\",\n firstChildOfSelect,\n );\n return useRender<ComponentPropsWithRef<\"button\">, SelectRootState>({\n defaultElement: \"button\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps: {\n // Explicit: Chromium 156 reports `.type === \"submit\"` for a select's button.\n type: \"button\",\n \"data-pending\": dataAttr(ctx.pending),\n \"data-disabled\": dataAttr(ctx.disabled),\n },\n ref,\n });\n}\n\nSelectTrigger.displayName = \"SelectTrigger\";\n"],"mappings":";;;;;;AAwBA,SAAS,mBAAmB,IAAsC;CAChE,MAAM,SAAS,GAAG;CAClB,IAAI,QAAQ,YAAY,YAAY,OAAO,sBAAsB,IAAI,OAAO;CAC5E,OACE;AAGJ;;;;;;;;;AAUA,SAAgB,cAAc,EAAE,WAAW,OAAO,UAAU,QAAQ,GAAG,QAA4B;CACjG,MAAM,MAAM,iBAAiB,gBAAgB;CAC7C,MAAM,QAAyB;EAC7B,OAAO,IAAI;EACX,SAAS,IAAI;EACb,UAAU,IAAI;CAChB;CACA,MAAM,EAAE,QAAQ,iBACd,kBACA,UACA,kBACF;CACA,OAAO,UAA4D;EACjE,gBAAgB;EAChB;EACA;EACA;EACA;EACA;EACA,OAAO;EACP,cAAc;GAEZ,MAAM;GACN,gBAAgB,SAAS,IAAI,OAAO;GACpC,iBAAiB,SAAS,IAAI,QAAQ;EACxC;EACA;CACF,CAAC;AACH;AAEA,cAAc,cAAc"}
@@ -0,0 +1,31 @@
1
+ import { RenderProp, StateValue } from "../internal/types.mjs";
2
+ import { SelectRootState } from "./select-root.mjs";
3
+ import { CSSProperties, ComponentPropsWithRef } from "react";
4
+ //#region src/select/select-value.d.ts
5
+ export interface SelectValueProps extends Omit<ComponentPropsWithRef<"selectedcontent">, "children" | "dangerouslySetInnerHTML" | "className" | "style"> {
6
+ className?: StateValue<string | undefined, SelectRootState>;
7
+ style?: StateValue<CSSProperties | undefined, SelectRootState>;
8
+ /** Replace the rendered element. Must keep a `<selectedcontent>`. */
9
+ render?: RenderProp<ComponentPropsWithRef<"selectedcontent">, SelectRootState>;
10
+ }
11
+ /**
12
+ * `<selectedcontent>`: the browser clones the selected option's content into it (inside
13
+ * `Select.Trigger`). It has no `children`. Rendered with `suppressHydrationWarning` and an empty
14
+ * `dangerouslySetInnerHTML` so that hydration accepts the content the parser cloned before React
15
+ * ran (design/components/select.md 3.3).
16
+ *
17
+ * Also keeps it fresh: the platform re-clones only on selection changes, so when React changes
18
+ * the selected option's content (a label, a state function, a Suspense boundary resolving inside
19
+ * it), a `MutationObserver` re-assigns `select.value = select.value`, which re-clones without
20
+ * events and without touching the selection or an open picker (6.3). The same refresh follows a
21
+ * form reset, which Chromium does not re-clone for. Not installed where
22
+ * `HTMLSelectedContentElement` is undefined (Firefox today, jsdom): nothing is cloned there.
23
+ *
24
+ * Owned keys: `dangerouslySetInnerHTML`, `suppressHydrationWarning`, `children`.
25
+ */
26
+ export declare function SelectValue({ className, style, render, ...rest }: SelectValueProps): import("react").ReactNode;
27
+ export declare namespace SelectValue {
28
+ var displayName: string;
29
+ }
30
+ //#endregion
31
+ //# sourceMappingURL=select-value.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-value.d.mts","names":[],"sources":["../../src/select/select-value.tsx"],"mappings":";;;;iBAYiB,yBAAyB,KACxC;EAGA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;;EAE9C,SAAS,WAAW,0CAA0C;;;;;;;;;;;;;;;;;wBA2ChD,cAAc,WAAW,OAAO,WAAW,QAAQ,mCAAgB;yBAAnE"}
@@ -0,0 +1,101 @@
1
+ "use client";
2
+ import { useRender } from "../internal/use-render.mjs";
3
+ import { useFormReset } from "../internal/use-form-reset.mjs";
4
+ import { useSelectContext } from "./select-context.mjs";
5
+ import { useSelectPartRef } from "./select-dom.mjs";
6
+ import { useLayoutEffect, useMemo } from "react";
7
+ //#region src/select/select-value.tsx
8
+ const isDev = process.env.NODE_ENV !== "production";
9
+ /** Where the browser clones options into `<selectedcontent>` (Chromium 135+, Safari 27). */
10
+ function supportsSelectedContent() {
11
+ return typeof globalThis !== "undefined" && "HTMLSelectedContentElement" in globalThis;
12
+ }
13
+ /**
14
+ * Whether a mutation record changes what `<selectedcontent>` shows: a record inside the selected
15
+ * option. Attribute records on the option element itself are ignored (option attributes are not
16
+ * cloned, and the library writes `data-*` there); records inside `<selectedcontent>` are never
17
+ * inside an option, so the refresh's own clone does not loop.
18
+ */
19
+ function touchesSelected(record, selected) {
20
+ if (record.type === "attributes" && record.target === selected) return false;
21
+ return selected.contains(record.target);
22
+ }
23
+ /** Re-clones the selected option into `<selectedcontent>`. */
24
+ function refresh(select) {
25
+ if (select.selectedIndex === -1) return;
26
+ select.value = select.value;
27
+ }
28
+ /**
29
+ * `<selectedcontent>`: the browser clones the selected option's content into it (inside
30
+ * `Select.Trigger`). It has no `children`. Rendered with `suppressHydrationWarning` and an empty
31
+ * `dangerouslySetInnerHTML` so that hydration accepts the content the parser cloned before React
32
+ * ran (design/components/select.md 3.3).
33
+ *
34
+ * Also keeps it fresh: the platform re-clones only on selection changes, so when React changes
35
+ * the selected option's content (a label, a state function, a Suspense boundary resolving inside
36
+ * it), a `MutationObserver` re-assigns `select.value = select.value`, which re-clones without
37
+ * events and without touching the selection or an open picker (6.3). The same refresh follows a
38
+ * form reset, which Chromium does not re-clone for. Not installed where
39
+ * `HTMLSelectedContentElement` is undefined (Firefox today, jsdom): nothing is cloned there.
40
+ *
41
+ * Owned keys: `dangerouslySetInnerHTML`, `suppressHydrationWarning`, `children`.
42
+ */
43
+ function SelectValue({ className, style, render, ...rest }) {
44
+ const ctx = useSelectContext("Select.Value");
45
+ const { ref, node } = useSelectPartRef("Select.Value", "selectedcontent");
46
+ useLayoutEffect(() => {
47
+ const el = node.current;
48
+ if (el === null) return;
49
+ const select = el.closest("select");
50
+ if (select === null) {
51
+ if (isDev) console.error("Select.Value: rendered outside the Select.Root's <select>; put it inside Select.Trigger, the select's first child.");
52
+ return;
53
+ }
54
+ if (!supportsSelectedContent()) return;
55
+ const observer = new MutationObserver((records) => {
56
+ const selected = select.options[select.selectedIndex];
57
+ if (selected === void 0) return;
58
+ if (records.some((record) => touchesSelected(record, selected))) refresh(select);
59
+ });
60
+ observer.observe(select, {
61
+ childList: true,
62
+ characterData: true,
63
+ attributes: true,
64
+ subtree: true
65
+ });
66
+ return () => {
67
+ observer.disconnect();
68
+ };
69
+ }, [node]);
70
+ const selectRef = useMemo(() => ({ get current() {
71
+ return node.current?.closest("select") ?? null;
72
+ } }), [node]);
73
+ useFormReset(selectRef, () => {
74
+ const select = selectRef.current;
75
+ if (select !== null && supportsSelectedContent()) refresh(select);
76
+ });
77
+ const state = {
78
+ value: ctx.optimistic,
79
+ pending: ctx.pending,
80
+ disabled: ctx.disabled
81
+ };
82
+ return useRender({
83
+ defaultElement: "selectedcontent",
84
+ render,
85
+ state,
86
+ className,
87
+ style,
88
+ children: void 0,
89
+ props: rest,
90
+ libraryProps: {
91
+ suppressHydrationWarning: true,
92
+ dangerouslySetInnerHTML: { __html: "" }
93
+ },
94
+ ref
95
+ });
96
+ }
97
+ SelectValue.displayName = "SelectValue";
98
+ //#endregion
99
+ export { SelectValue };
100
+
101
+ //# sourceMappingURL=select-value.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-value.mjs","names":[],"sources":["../../src/select/select-value.tsx"],"sourcesContent":["\"use client\";\nimport { useLayoutEffect, useMemo } from \"react\";\nimport type { ComponentPropsWithRef, CSSProperties } from \"react\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { useFormReset } from \"../internal/use-form-reset.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\nconst isDev = process.env.NODE_ENV !== \"production\";\n\nexport interface SelectValueProps extends Omit<\n ComponentPropsWithRef<\"selectedcontent\">,\n \"children\" | \"dangerouslySetInnerHTML\" | \"className\" | \"style\"\n> {\n className?: StateValue<string | undefined, SelectRootState>;\n style?: StateValue<CSSProperties | undefined, SelectRootState>;\n /** Replace the rendered element. Must keep a `<selectedcontent>`. */\n render?: RenderProp<ComponentPropsWithRef<\"selectedcontent\">, SelectRootState>;\n}\n\n/** Where the browser clones options into `<selectedcontent>` (Chromium 135+, Safari 27). */\nfunction supportsSelectedContent(): boolean {\n return typeof globalThis !== \"undefined\" && \"HTMLSelectedContentElement\" in globalThis;\n}\n\n/**\n * Whether a mutation record changes what `<selectedcontent>` shows: a record inside the selected\n * option. Attribute records on the option element itself are ignored (option attributes are not\n * cloned, and the library writes `data-*` there); records inside `<selectedcontent>` are never\n * inside an option, so the refresh's own clone does not loop.\n */\nfunction touchesSelected(record: MutationRecord, selected: HTMLOptionElement): boolean {\n if (record.type === \"attributes\" && record.target === selected) return false;\n return selected.contains(record.target);\n}\n\n/** Re-clones the selected option into `<selectedcontent>`. */\nfunction refresh(select: HTMLSelectElement): void {\n // Nothing selected: assigning the value would select the placeholder (probe case 7).\n if (select.selectedIndex === -1) return;\n // The assignment is the re-clone trigger; it fires no event and keeps the selection.\n const current = select.value;\n select.value = current;\n}\n\n/**\n * `<selectedcontent>`: the browser clones the selected option's content into it (inside\n * `Select.Trigger`). It has no `children`. Rendered with `suppressHydrationWarning` and an empty\n * `dangerouslySetInnerHTML` so that hydration accepts the content the parser cloned before React\n * ran (design/components/select.md 3.3).\n *\n * Also keeps it fresh: the platform re-clones only on selection changes, so when React changes\n * the selected option's content (a label, a state function, a Suspense boundary resolving inside\n * it), a `MutationObserver` re-assigns `select.value = select.value`, which re-clones without\n * events and without touching the selection or an open picker (6.3). The same refresh follows a\n * form reset, which Chromium does not re-clone for. Not installed where\n * `HTMLSelectedContentElement` is undefined (Firefox today, jsdom): nothing is cloned there.\n *\n * Owned keys: `dangerouslySetInnerHTML`, `suppressHydrationWarning`, `children`.\n */\nexport function SelectValue({ className, style, render, ...rest }: SelectValueProps) {\n const ctx = useSelectContext(\"Select.Value\");\n const { ref, node } = useSelectPartRef<HTMLElement>(\"Select.Value\", \"selectedcontent\");\n\n useLayoutEffect(() => {\n const el = node.current;\n if (el === null) return;\n const select = el.closest(\"select\");\n if (select === null) {\n if (isDev) {\n console.error(\n \"Select.Value: rendered outside the Select.Root's <select>; put it inside \" +\n \"Select.Trigger, the select's first child.\",\n );\n }\n return;\n }\n if (!supportsSelectedContent()) return;\n const observer = new MutationObserver((records) => {\n const selected = select.options[select.selectedIndex];\n if (selected === undefined) return;\n if (records.some((record) => touchesSelected(record, selected))) refresh(select);\n });\n observer.observe(select, {\n childList: true,\n characterData: true,\n attributes: true,\n subtree: true,\n });\n return () => {\n observer.disconnect();\n };\n }, [node]);\n\n // A form reset changes the selection without re-cloning (Chromium 156: `form.reset()` leaves\n // the old option's content in <selectedcontent>; memo/developer/select.md). `useFormReset`\n // listens on the select's form owner itself (so neither `stopPropagation()` on the form nor a\n // shadow root hides the event), follows a changed owner or `form` attribute, and calls back in\n // a microtask after an uncancelled reset. React's own reset after a form action is a\n // `form.reset()` too.\n const selectRef = useMemo(\n () => ({\n get current(): HTMLSelectElement | null {\n return node.current?.closest(\"select\") ?? null;\n },\n }),\n [node],\n );\n useFormReset(selectRef, () => {\n const select = selectRef.current;\n if (select !== null && supportsSelectedContent()) refresh(select);\n });\n\n const state: SelectRootState = {\n value: ctx.optimistic,\n pending: ctx.pending,\n disabled: ctx.disabled,\n };\n return useRender<ComponentPropsWithRef<\"selectedcontent\">, SelectRootState>({\n defaultElement: \"selectedcontent\",\n render,\n state,\n className,\n style,\n children: undefined,\n props: rest,\n libraryProps: {\n suppressHydrationWarning: true,\n dangerouslySetInnerHTML: { __html: \"\" },\n },\n ref,\n });\n}\n\nSelectValue.displayName = \"SelectValue\";\n"],"mappings":";;;;;;;AAUA,MAAM,QAAQ,QAAQ,IAAI,aAAa;;AAavC,SAAS,0BAAmC;CAC1C,OAAO,OAAO,eAAe,eAAe,gCAAgC;AAC9E;;;;;;;AAQA,SAAS,gBAAgB,QAAwB,UAAsC;CACrF,IAAI,OAAO,SAAS,gBAAgB,OAAO,WAAW,UAAU,OAAO;CACvE,OAAO,SAAS,SAAS,OAAO,MAAM;AACxC;;AAGA,SAAS,QAAQ,QAAiC;CAEhD,IAAI,OAAO,kBAAkB,IAAI;CAGjC,OAAO,QADS,OAAO;AAEzB;;;;;;;;;;;;;;;;AAiBA,SAAgB,YAAY,EAAE,WAAW,OAAO,QAAQ,GAAG,QAA0B;CACnF,MAAM,MAAM,iBAAiB,cAAc;CAC3C,MAAM,EAAE,KAAK,SAAS,iBAA8B,gBAAgB,iBAAiB;CAErF,sBAAsB;EACpB,MAAM,KAAK,KAAK;EAChB,IAAI,OAAO,MAAM;EACjB,MAAM,SAAS,GAAG,QAAQ,QAAQ;EAClC,IAAI,WAAW,MAAM;GACnB,IAAI,OACF,QAAQ,MACN,oHAEF;GAEF;EACF;EACA,IAAI,CAAC,wBAAwB,GAAG;EAChC,MAAM,WAAW,IAAI,kBAAkB,YAAY;GACjD,MAAM,WAAW,OAAO,QAAQ,OAAO;GACvC,IAAI,aAAa,KAAA,GAAW;GAC5B,IAAI,QAAQ,MAAM,WAAW,gBAAgB,QAAQ,QAAQ,CAAC,GAAG,QAAQ,MAAM;EACjF,CAAC;EACD,SAAS,QAAQ,QAAQ;GACvB,WAAW;GACX,eAAe;GACf,YAAY;GACZ,SAAS;EACX,CAAC;EACD,aAAa;GACX,SAAS,WAAW;EACtB;CACF,GAAG,CAAC,IAAI,CAAC;CAQT,MAAM,YAAY,eACT,EACL,IAAI,UAAoC;EACtC,OAAO,KAAK,SAAS,QAAQ,QAAQ,KAAK;CAC5C,EACF,IACA,CAAC,IAAI,CACP;CACA,aAAa,iBAAiB;EAC5B,MAAM,SAAS,UAAU;EACzB,IAAI,WAAW,QAAQ,wBAAwB,GAAG,QAAQ,MAAM;CAClE,CAAC;CAED,MAAM,QAAyB;EAC7B,OAAO,IAAI;EACX,SAAS,IAAI;EACb,UAAU,IAAI;CAChB;CACA,OAAO,UAAqE;EAC1E,gBAAgB;EAChB;EACA;EACA;EACA;EACA,UAAU,KAAA;EACV,OAAO;EACP,cAAc;GACZ,0BAA0B;GAC1B,yBAAyB,EAAE,QAAQ,GAAG;EACxC;EACA;CACF,CAAC;AACH;AAEA,YAAY,cAAc"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@funstack/ui",
3
- "version": "0.1.0-alpha.1",
3
+ "version": "0.1.0-alpha.2",
4
4
  "description": "Headless UI library for React — the first Async React-native UI library",
5
5
  "keywords": [
6
6
  "components",
@@ -32,10 +32,12 @@
32
32
  "./accordion": "./dist/accordion/index.mjs",
33
33
  "./checkbox": "./dist/checkbox/index.mjs",
34
34
  "./checkbox-group": "./dist/checkbox-group/index.mjs",
35
+ "./combobox": "./dist/combobox/index.mjs",
35
36
  "./dialog": "./dist/dialog/index.mjs",
36
37
  "./menu": "./dist/menu/index.mjs",
37
38
  "./popover": "./dist/popover/index.mjs",
38
39
  "./radio-group": "./dist/radio-group/index.mjs",
40
+ "./select": "./dist/select/index.mjs",
39
41
  "./switch": "./dist/switch/index.mjs",
40
42
  "./tabs": "./dist/tabs/index.mjs",
41
43
  "./toggle": "./dist/toggle/index.mjs",