@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,70 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { dataAttr } from "../internal/data-attr.mjs";
|
|
3
|
+
import { resolveStateValue, useRender } from "../internal/use-render.mjs";
|
|
4
|
+
import { useDevWarning } from "../internal/use-dev-warning.mjs";
|
|
5
|
+
import { ComboboxListContext, ComboboxPopupContext, useComboboxContext } from "./combobox-context.mjs";
|
|
6
|
+
import { use, useCallback, useRef } from "react";
|
|
7
|
+
import { jsx } from "react/jsx-runtime";
|
|
8
|
+
//#region src/combobox/combobox-list.tsx
|
|
9
|
+
const isDev = process.env.NODE_ENV !== "production";
|
|
10
|
+
/**
|
|
11
|
+
* The listbox: `<div role="listbox" aria-labelledby={label}>` (only while a `Combobox.Label` is
|
|
12
|
+
* mounted), the element the input's `aria-controls` names. Its children are the results for the
|
|
13
|
+
* deferred `query` (the library filters nothing, design 6.1); while they are for an older text it
|
|
14
|
+
* carries `aria-busy="true"` and `data-stale`. Contains only Options and Groups; a `Status`
|
|
15
|
+
* rendered as a Suspense fallback inside it marks the results as loading (`aria-busy="true"`).
|
|
16
|
+
*
|
|
17
|
+
* Owned keys (conventions 1.2): `id`, `role`, `aria-labelledby` (token-merged), `aria-busy`,
|
|
18
|
+
* `data-stale`, `data-empty`.
|
|
19
|
+
*/
|
|
20
|
+
function ComboboxList({ className, style, children, render, ...rest }) {
|
|
21
|
+
const ctx = useComboboxContext("Combobox.List");
|
|
22
|
+
const inPopup = use(ComboboxPopupContext);
|
|
23
|
+
useDevWarning(!inPopup, "Combobox.List: rendered outside a Combobox.Popup. The listbox must be inside the Popup, which is the element the library shows and hides.");
|
|
24
|
+
const { listRef } = ctx;
|
|
25
|
+
const warnedName = useRef(false);
|
|
26
|
+
const ref = useCallback((element) => {
|
|
27
|
+
listRef.current = element;
|
|
28
|
+
if (isDev && !warnedName.current) queueMicrotask(() => {
|
|
29
|
+
if (warnedName.current || !element.isConnected) return;
|
|
30
|
+
if (element.hasAttribute("aria-labelledby") || element.hasAttribute("aria-label")) return;
|
|
31
|
+
warnedName.current = true;
|
|
32
|
+
console.error("Combobox.List: the listbox has no accessible name. Render a Combobox.Label, or give the List an `aria-label` / `aria-labelledby`.");
|
|
33
|
+
});
|
|
34
|
+
return () => {
|
|
35
|
+
if (listRef.current === element) listRef.current = null;
|
|
36
|
+
};
|
|
37
|
+
}, [listRef]);
|
|
38
|
+
const state = {
|
|
39
|
+
query: ctx.query,
|
|
40
|
+
stale: ctx.stale,
|
|
41
|
+
empty: ctx.empty
|
|
42
|
+
};
|
|
43
|
+
const libraryProps = {
|
|
44
|
+
id: ctx.ids.list,
|
|
45
|
+
role: "listbox",
|
|
46
|
+
...ctx.hasLabel && { "aria-labelledby": ctx.ids.label },
|
|
47
|
+
"aria-busy": ctx.stale || ctx.loading ? true : void 0,
|
|
48
|
+
"data-stale": dataAttr(ctx.stale),
|
|
49
|
+
"data-empty": dataAttr(ctx.empty)
|
|
50
|
+
};
|
|
51
|
+
return useRender({
|
|
52
|
+
defaultElement: "div",
|
|
53
|
+
render,
|
|
54
|
+
state,
|
|
55
|
+
className,
|
|
56
|
+
style,
|
|
57
|
+
children: (s) => /* @__PURE__ */ jsx(ComboboxListContext, {
|
|
58
|
+
value: true,
|
|
59
|
+
children: resolveStateValue(children, s)
|
|
60
|
+
}),
|
|
61
|
+
props: rest,
|
|
62
|
+
libraryProps,
|
|
63
|
+
ref
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
ComboboxList.displayName = "ComboboxList";
|
|
67
|
+
//#endregion
|
|
68
|
+
export { ComboboxList };
|
|
69
|
+
|
|
70
|
+
//# sourceMappingURL=combobox-list.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"combobox-list.mjs","names":[],"sources":["../../src/combobox/combobox-list.tsx"],"sourcesContent":["\"use client\";\nimport { use, useCallback, useRef } from \"react\";\nimport type { ComponentPropsWithRef, CSSProperties, ReactNode } from \"react\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { useDevWarning } from \"../internal/use-dev-warning.js\";\nimport { resolveStateValue, useRender } from \"../internal/use-render.js\";\nimport type { DataAttributes } from \"../internal/use-render.js\";\nimport {\n ComboboxListContext,\n ComboboxPopupContext,\n useComboboxContext,\n} from \"./combobox-context.js\";\n\nconst isDev = process.env.NODE_ENV !== \"production\";\n\nexport interface ComboboxListState {\n /** The deferred text the results are for: render the Options for it. */\n readonly query: string;\n /** The results shown are for an older text (`aria-busy`). */\n readonly stale: boolean;\n /** No option is mounted and the results are settled. */\n readonly empty: boolean;\n}\n\nexport interface ComboboxListProps extends Omit<\n ComponentPropsWithRef<\"div\">,\n \"id\" | \"role\" | \"aria-busy\" | \"className\" | \"style\" | \"children\"\n> {\n className?: StateValue<string | undefined, ComboboxListState>;\n style?: StateValue<CSSProperties | undefined, ComboboxListState>;\n /**\n * Usually a function of `{ query }` rendering the Options for it, under your own `<Suspense>`\n * for an async search. The previous results stay visible while the next ones load.\n */\n children?: StateValue<ReactNode, ComboboxListState>;\n render?: RenderProp<ComponentPropsWithRef<\"div\">, ComboboxListState>;\n}\n\n/**\n * The listbox: `<div role=\"listbox\" aria-labelledby={label}>` (only while a `Combobox.Label` is\n * mounted), the element the input's `aria-controls` names. Its children are the results for the\n * deferred `query` (the library filters nothing, design 6.1); while they are for an older text it\n * carries `aria-busy=\"true\"` and `data-stale`. Contains only Options and Groups; a `Status`\n * rendered as a Suspense fallback inside it marks the results as loading (`aria-busy=\"true\"`).\n *\n * Owned keys (conventions 1.2): `id`, `role`, `aria-labelledby` (token-merged), `aria-busy`,\n * `data-stale`, `data-empty`.\n */\nexport function ComboboxList({ className, style, children, render, ...rest }: ComboboxListProps) {\n const ctx = useComboboxContext(\"Combobox.List\");\n const inPopup = use(ComboboxPopupContext);\n useDevWarning(\n !inPopup,\n \"Combobox.List: rendered outside a Combobox.Popup. The listbox must be inside the Popup, \" +\n \"which is the element the library shows and hides.\",\n );\n const { listRef } = ctx;\n const warnedName = useRef(false);\n\n const ref = useCallback(\n (element: HTMLElement) => {\n listRef.current = element;\n if (isDev && !warnedName.current) {\n // A mounted Label registers in a layout effect whose re-render is flushed before this\n // microtask, so `aria-labelledby` is in the DOM by then if it ever will be.\n queueMicrotask(() => {\n if (warnedName.current || !element.isConnected) return;\n if (element.hasAttribute(\"aria-labelledby\") || element.hasAttribute(\"aria-label\")) return;\n warnedName.current = true;\n console.error(\n \"Combobox.List: the listbox has no accessible name. Render a Combobox.Label, or give \" +\n \"the List an `aria-label` / `aria-labelledby`.\",\n );\n });\n }\n return () => {\n if (listRef.current === element) listRef.current = null;\n };\n },\n [listRef],\n );\n\n const state: ComboboxListState = { query: ctx.query, stale: ctx.stale, empty: ctx.empty };\n\n const libraryProps: ComponentPropsWithRef<\"div\"> & DataAttributes = {\n id: ctx.ids.list,\n role: \"listbox\",\n ...(ctx.hasLabel && { \"aria-labelledby\": ctx.ids.label }),\n \"aria-busy\": ctx.stale || ctx.loading ? true : undefined,\n \"data-stale\": dataAttr(ctx.stale),\n \"data-empty\": dataAttr(ctx.empty),\n };\n\n return useRender<ComponentPropsWithRef<\"div\">, ComboboxListState>({\n defaultElement: \"div\",\n render,\n state,\n className,\n style,\n children: (s) => (\n <ComboboxListContext value={true}>{resolveStateValue(children, s)}</ComboboxListContext>\n ),\n props: rest,\n libraryProps,\n ref,\n });\n}\n\nComboboxList.displayName = \"ComboboxList\";\n"],"mappings":";;;;;;;;AAcA,MAAM,QAAQ,QAAQ,IAAI,aAAa;;;;;;;;;;;AAmCvC,SAAgB,aAAa,EAAE,WAAW,OAAO,UAAU,QAAQ,GAAG,QAA2B;CAC/F,MAAM,MAAM,mBAAmB,eAAe;CAC9C,MAAM,UAAU,IAAI,oBAAoB;CACxC,cACE,CAAC,SACD,2IAEF;CACA,MAAM,EAAE,YAAY;CACpB,MAAM,aAAa,OAAO,KAAK;CAE/B,MAAM,MAAM,aACT,YAAyB;EACxB,QAAQ,UAAU;EAClB,IAAI,SAAS,CAAC,WAAW,SAGvB,qBAAqB;GACnB,IAAI,WAAW,WAAW,CAAC,QAAQ,aAAa;GAChD,IAAI,QAAQ,aAAa,iBAAiB,KAAK,QAAQ,aAAa,YAAY,GAAG;GACnF,WAAW,UAAU;GACrB,QAAQ,MACN,mIAEF;EACF,CAAC;EAEH,aAAa;GACX,IAAI,QAAQ,YAAY,SAAS,QAAQ,UAAU;EACrD;CACF,GACA,CAAC,OAAO,CACV;CAEA,MAAM,QAA2B;EAAE,OAAO,IAAI;EAAO,OAAO,IAAI;EAAO,OAAO,IAAI;CAAM;CAExF,MAAM,eAA8D;EAClE,IAAI,IAAI,IAAI;EACZ,MAAM;EACN,GAAI,IAAI,YAAY,EAAE,mBAAmB,IAAI,IAAI,MAAM;EACvD,aAAa,IAAI,SAAS,IAAI,UAAU,OAAO,KAAA;EAC/C,cAAc,SAAS,IAAI,KAAK;EAChC,cAAc,SAAS,IAAI,KAAK;CAClC;CAEA,OAAO,UAA2D;EAChE,gBAAgB;EAChB;EACA;EACA;EACA;EACA,WAAW,MACT,oBAAC,qBAAD;GAAqB,OAAO;GAAO,UAAA,kBAAkB,UAAU,CAAC;EAAuB,CAAA;EAEzF,OAAO;EACP;EACA;CACF,CAAC;AACH;AAEA,aAAa,cAAc"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { RenderProp, StateValue } from "../internal/types.mjs";
|
|
2
|
+
import { CSSProperties, ComponentPropsWithRef, ReactNode } from "react";
|
|
3
|
+
//#region src/combobox/combobox-option.d.ts
|
|
4
|
+
export interface ComboboxOptionState {
|
|
5
|
+
readonly value: string;
|
|
6
|
+
/** Optimistic: this option's value is the (optimistic) selection. */
|
|
7
|
+
readonly selected: boolean;
|
|
8
|
+
/** `aria-activedescendant` points here (urgent). */
|
|
9
|
+
readonly active: boolean;
|
|
10
|
+
/** The selection is pending and this option is its optimistic target. */
|
|
11
|
+
readonly pending: boolean;
|
|
12
|
+
readonly disabled: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface ComboboxOptionProps extends Omit<ComponentPropsWithRef<"div">, "id" | "role" | "aria-selected" | "aria-disabled" | "onClick" | "onPointerDown" | "onMouseDown" | "onPointerMove" | "className" | "style" | "children"> {
|
|
15
|
+
/** The selection this option stands for. Unique within the Combobox; also its id fragment. */
|
|
16
|
+
value: string;
|
|
17
|
+
/**
|
|
18
|
+
* The text written into the input when this option is accepted. Default: the element's
|
|
19
|
+
* trimmed `textContent` at that moment. Not used for matching: the library filters nothing.
|
|
20
|
+
*/
|
|
21
|
+
label?: string;
|
|
22
|
+
/** `aria-disabled`: shown, skipped by the arrow keys, never accepted. */
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
className?: StateValue<string | undefined, ComboboxOptionState>;
|
|
25
|
+
style?: StateValue<CSSProperties | undefined, ComboboxOptionState>;
|
|
26
|
+
/** Rendered from optimistic state; must not suspend. */
|
|
27
|
+
children?: StateValue<ReactNode, ComboboxOptionState>;
|
|
28
|
+
render?: RenderProp<ComponentPropsWithRef<"div">, ComboboxOptionState>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* One result: `<div role="option" id aria-selected>`, never focused (virtual focus through the
|
|
32
|
+
* input's `aria-activedescendant`). A click (or Enter while it is active) accepts it: the input
|
|
33
|
+
* text becomes its label at once, it shows `data-selected` and `data-pending`, and the listbox
|
|
34
|
+
* closes when `valueChangeAction` settles. Hovering makes it active.
|
|
35
|
+
*
|
|
36
|
+
* Owned keys (conventions 1.2): `id` (derived from `value`), `role`, `aria-selected`,
|
|
37
|
+
* `aria-disabled`, `onClick`, `onPointerDown`, `onMouseDown`, `onPointerMove`, `data-selected`,
|
|
38
|
+
* `data-active`, `data-pending`, `data-disabled`.
|
|
39
|
+
*/
|
|
40
|
+
export declare function ComboboxOption({ value, label, disabled, className, style, children, render, ...rest }: ComboboxOptionProps): ReactNode;
|
|
41
|
+
export declare namespace ComboboxOption {
|
|
42
|
+
var displayName: string;
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
//# sourceMappingURL=combobox-option.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"combobox-option.d.mts","names":[],"sources":["../../src/combobox/combobox-option.tsx"],"mappings":";;;iBAgBiB;WACN;;WAEA;;WAEA;;WAEA;WACA;;iBAGM,4BAA4B,KAC3C;;EAcA;;;;;EAKA;;EAEA;EACA,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;;EAE9C,WAAW,WAAW,WAAW;EACjC,SAAS,WAAW,8BAA8B;;;;;;;;;;;;wBAepC,iBACd,OACA,OACA,UACA,WACA,OACA,UACA,WACG,QACF,sBAAmB;yBATN"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { dataAttr } from "../internal/data-attr.mjs";
|
|
3
|
+
import { useRender } from "../internal/use-render.mjs";
|
|
4
|
+
import { useDevWarning } from "../internal/use-dev-warning.mjs";
|
|
5
|
+
import { ComboboxListContext, optionId, useComboboxContext } from "./combobox-context.mjs";
|
|
6
|
+
import { use, useCallback, useLayoutEffect, useRef } from "react";
|
|
7
|
+
//#region src/combobox/combobox-option.tsx
|
|
8
|
+
const preventDefault = (event) => event.preventDefault();
|
|
9
|
+
/**
|
|
10
|
+
* One result: `<div role="option" id aria-selected>`, never focused (virtual focus through the
|
|
11
|
+
* input's `aria-activedescendant`). A click (or Enter while it is active) accepts it: the input
|
|
12
|
+
* text becomes its label at once, it shows `data-selected` and `data-pending`, and the listbox
|
|
13
|
+
* closes when `valueChangeAction` settles. Hovering makes it active.
|
|
14
|
+
*
|
|
15
|
+
* Owned keys (conventions 1.2): `id` (derived from `value`), `role`, `aria-selected`,
|
|
16
|
+
* `aria-disabled`, `onClick`, `onPointerDown`, `onMouseDown`, `onPointerMove`, `data-selected`,
|
|
17
|
+
* `data-active`, `data-pending`, `data-disabled`.
|
|
18
|
+
*/
|
|
19
|
+
function ComboboxOption({ value, label, disabled = false, className, style, children, render, ...rest }) {
|
|
20
|
+
const ctx = useComboboxContext("Combobox.Option");
|
|
21
|
+
const inList = use(ComboboxListContext);
|
|
22
|
+
useDevWarning(!inList, "Combobox.Option: rendered outside a Combobox.List. Options belong in the listbox.");
|
|
23
|
+
const { registerOption } = ctx;
|
|
24
|
+
const elementRef = useRef(null);
|
|
25
|
+
const ref = useCallback((element) => {
|
|
26
|
+
elementRef.current = element;
|
|
27
|
+
return () => {
|
|
28
|
+
if (elementRef.current === element) elementRef.current = null;
|
|
29
|
+
};
|
|
30
|
+
}, []);
|
|
31
|
+
useLayoutEffect(() => {
|
|
32
|
+
const element = elementRef.current;
|
|
33
|
+
if (element === null) return;
|
|
34
|
+
return registerOption(value, {
|
|
35
|
+
element,
|
|
36
|
+
label,
|
|
37
|
+
disabled
|
|
38
|
+
});
|
|
39
|
+
}, [
|
|
40
|
+
registerOption,
|
|
41
|
+
value,
|
|
42
|
+
label,
|
|
43
|
+
disabled
|
|
44
|
+
]);
|
|
45
|
+
const selected = ctx.value === value;
|
|
46
|
+
const state = {
|
|
47
|
+
value,
|
|
48
|
+
selected,
|
|
49
|
+
active: ctx.activeValue === value,
|
|
50
|
+
pending: ctx.valuePending && selected,
|
|
51
|
+
disabled
|
|
52
|
+
};
|
|
53
|
+
const libraryProps = {
|
|
54
|
+
id: optionId(ctx.ids.optionPrefix, value),
|
|
55
|
+
role: "option",
|
|
56
|
+
"aria-selected": selected,
|
|
57
|
+
"aria-disabled": disabled ? true : void 0,
|
|
58
|
+
onClick: (event) => {
|
|
59
|
+
if (disabled) return;
|
|
60
|
+
ctx.accept(value, event.nativeEvent);
|
|
61
|
+
},
|
|
62
|
+
onPointerDown: (event) => preventDefault(event),
|
|
63
|
+
onMouseDown: (event) => preventDefault(event),
|
|
64
|
+
onPointerMove: () => {
|
|
65
|
+
if (!disabled && ctx.activeValue !== value) ctx.setActive(value);
|
|
66
|
+
},
|
|
67
|
+
"data-selected": dataAttr(selected),
|
|
68
|
+
"data-active": dataAttr(state.active),
|
|
69
|
+
"data-pending": dataAttr(state.pending),
|
|
70
|
+
"data-disabled": dataAttr(disabled)
|
|
71
|
+
};
|
|
72
|
+
return useRender({
|
|
73
|
+
defaultElement: "div",
|
|
74
|
+
render,
|
|
75
|
+
state,
|
|
76
|
+
className,
|
|
77
|
+
style,
|
|
78
|
+
children,
|
|
79
|
+
props: rest,
|
|
80
|
+
libraryProps,
|
|
81
|
+
ref
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
ComboboxOption.displayName = "ComboboxOption";
|
|
85
|
+
//#endregion
|
|
86
|
+
export { ComboboxOption };
|
|
87
|
+
|
|
88
|
+
//# sourceMappingURL=combobox-option.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"combobox-option.mjs","names":[],"sources":["../../src/combobox/combobox-option.tsx"],"sourcesContent":["\"use client\";\nimport { use, useCallback, useLayoutEffect, useRef } from \"react\";\nimport type {\n ComponentPropsWithRef,\n CSSProperties,\n MouseEvent,\n PointerEvent,\n ReactNode,\n} from \"react\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { useDevWarning } from \"../internal/use-dev-warning.js\";\nimport { useRender } from \"../internal/use-render.js\";\nimport type { DataAttributes } from \"../internal/use-render.js\";\nimport { ComboboxListContext, optionId, useComboboxContext } from \"./combobox-context.js\";\n\nexport interface ComboboxOptionState {\n readonly value: string;\n /** Optimistic: this option's value is the (optimistic) selection. */\n readonly selected: boolean;\n /** `aria-activedescendant` points here (urgent). */\n readonly active: boolean;\n /** The selection is pending and this option is its optimistic target. */\n readonly pending: boolean;\n readonly disabled: boolean;\n}\n\nexport interface ComboboxOptionProps extends Omit<\n ComponentPropsWithRef<\"div\">,\n | \"id\"\n | \"role\"\n | \"aria-selected\"\n | \"aria-disabled\"\n | \"onClick\"\n | \"onPointerDown\"\n | \"onMouseDown\"\n | \"onPointerMove\"\n | \"className\"\n | \"style\"\n | \"children\"\n> {\n /** The selection this option stands for. Unique within the Combobox; also its id fragment. */\n value: string;\n /**\n * The text written into the input when this option is accepted. Default: the element's\n * trimmed `textContent` at that moment. Not used for matching: the library filters nothing.\n */\n label?: string;\n /** `aria-disabled`: shown, skipped by the arrow keys, never accepted. */\n disabled?: boolean;\n className?: StateValue<string | undefined, ComboboxOptionState>;\n style?: StateValue<CSSProperties | undefined, ComboboxOptionState>;\n /** Rendered from optimistic state; must not suspend. */\n children?: StateValue<ReactNode, ComboboxOptionState>;\n render?: RenderProp<ComponentPropsWithRef<\"div\">, ComboboxOptionState>;\n}\n\nconst preventDefault = (event: { preventDefault(): void }) => event.preventDefault();\n\n/**\n * One result: `<div role=\"option\" id aria-selected>`, never focused (virtual focus through the\n * input's `aria-activedescendant`). A click (or Enter while it is active) accepts it: the input\n * text becomes its label at once, it shows `data-selected` and `data-pending`, and the listbox\n * closes when `valueChangeAction` settles. Hovering makes it active.\n *\n * Owned keys (conventions 1.2): `id` (derived from `value`), `role`, `aria-selected`,\n * `aria-disabled`, `onClick`, `onPointerDown`, `onMouseDown`, `onPointerMove`, `data-selected`,\n * `data-active`, `data-pending`, `data-disabled`.\n */\nexport function ComboboxOption({\n value,\n label,\n disabled = false,\n className,\n style,\n children,\n render,\n ...rest\n}: ComboboxOptionProps) {\n const ctx = useComboboxContext(\"Combobox.Option\");\n const inList = use(ComboboxListContext);\n useDevWarning(\n !inList,\n \"Combobox.Option: rendered outside a Combobox.List. Options belong in the listbox.\",\n );\n const { registerOption } = ctx;\n\n const elementRef = useRef<HTMLElement | null>(null);\n const ref = useCallback((element: HTMLElement) => {\n elementRef.current = element;\n return () => {\n if (elementRef.current === element) elementRef.current = null;\n };\n }, []);\n\n // Registration (design 14): the Root's registry drives the arrow keys, `Empty`, the active\n // option's revalidation, the announcements and the label cache.\n useLayoutEffect(() => {\n const element = elementRef.current;\n if (element === null) return;\n return registerOption(value, { element, label, disabled });\n }, [registerOption, value, label, disabled]);\n\n const selected = ctx.value === value;\n const state: ComboboxOptionState = {\n value,\n selected,\n active: ctx.activeValue === value,\n pending: ctx.valuePending && selected,\n disabled,\n };\n\n const libraryProps: ComponentPropsWithRef<\"div\"> & DataAttributes = {\n id: optionId(ctx.ids.optionPrefix, value),\n role: \"option\",\n \"aria-selected\": selected,\n \"aria-disabled\": disabled ? true : undefined,\n onClick: (event: MouseEvent<HTMLDivElement>) => {\n if (disabled) return;\n ctx.accept(value, event.nativeEvent);\n },\n // The input keeps focus (design 7.1), also for an Option rendered outside a Popup.\n onPointerDown: (event: PointerEvent<HTMLDivElement>) => preventDefault(event),\n onMouseDown: (event: MouseEvent<HTMLDivElement>) => preventDefault(event),\n onPointerMove: () => {\n if (!disabled && ctx.activeValue !== value) ctx.setActive(value);\n },\n \"data-selected\": dataAttr(selected),\n \"data-active\": dataAttr(state.active),\n \"data-pending\": dataAttr(state.pending),\n \"data-disabled\": dataAttr(disabled),\n };\n\n return useRender<ComponentPropsWithRef<\"div\">, ComboboxOptionState>({\n defaultElement: \"div\",\n render,\n state,\n className,\n style,\n children,\n props: rest,\n libraryProps,\n ref,\n });\n}\n\nComboboxOption.displayName = \"ComboboxOption\";\n"],"mappings":";;;;;;;AAyDA,MAAM,kBAAkB,UAAsC,MAAM,eAAe;;;;;;;;;;;AAYnF,SAAgB,eAAe,EAC7B,OACA,OACA,WAAW,OACX,WACA,OACA,UACA,QACA,GAAG,QACmB;CACtB,MAAM,MAAM,mBAAmB,iBAAiB;CAChD,MAAM,SAAS,IAAI,mBAAmB;CACtC,cACE,CAAC,QACD,mFACF;CACA,MAAM,EAAE,mBAAmB;CAE3B,MAAM,aAAa,OAA2B,IAAI;CAClD,MAAM,MAAM,aAAa,YAAyB;EAChD,WAAW,UAAU;EACrB,aAAa;GACX,IAAI,WAAW,YAAY,SAAS,WAAW,UAAU;EAC3D;CACF,GAAG,CAAC,CAAC;CAIL,sBAAsB;EACpB,MAAM,UAAU,WAAW;EAC3B,IAAI,YAAY,MAAM;EACtB,OAAO,eAAe,OAAO;GAAE;GAAS;GAAO;EAAS,CAAC;CAC3D,GAAG;EAAC;EAAgB;EAAO;EAAO;CAAQ,CAAC;CAE3C,MAAM,WAAW,IAAI,UAAU;CAC/B,MAAM,QAA6B;EACjC;EACA;EACA,QAAQ,IAAI,gBAAgB;EAC5B,SAAS,IAAI,gBAAgB;EAC7B;CACF;CAEA,MAAM,eAA8D;EAClE,IAAI,SAAS,IAAI,IAAI,cAAc,KAAK;EACxC,MAAM;EACN,iBAAiB;EACjB,iBAAiB,WAAW,OAAO,KAAA;EACnC,UAAU,UAAsC;GAC9C,IAAI,UAAU;GACd,IAAI,OAAO,OAAO,MAAM,WAAW;EACrC;EAEA,gBAAgB,UAAwC,eAAe,KAAK;EAC5E,cAAc,UAAsC,eAAe,KAAK;EACxE,qBAAqB;GACnB,IAAI,CAAC,YAAY,IAAI,gBAAgB,OAAO,IAAI,UAAU,KAAK;EACjE;EACA,iBAAiB,SAAS,QAAQ;EAClC,eAAe,SAAS,MAAM,MAAM;EACpC,gBAAgB,SAAS,MAAM,OAAO;EACtC,iBAAiB,SAAS,QAAQ;CACpC;CAEA,OAAO,UAA6D;EAClE,gBAAgB;EAChB;EACA;EACA;EACA;EACA;EACA,OAAO;EACP;EACA;CACF,CAAC;AACH;AAEA,eAAe,cAAc"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { RenderProp, StateValue } from "../internal/types.mjs";
|
|
2
|
+
import { CSSProperties, ComponentPropsWithRef, ReactNode, ViewTransitionClass } from "react";
|
|
3
|
+
//#region src/combobox/combobox-popup.d.ts
|
|
4
|
+
export interface ComboboxPopupState {
|
|
5
|
+
/** Committed open state. Read `:popover-open` for the DOM truth. */
|
|
6
|
+
readonly open: boolean;
|
|
7
|
+
/** The results shown are for an older text. */
|
|
8
|
+
readonly stale: boolean;
|
|
9
|
+
/** The deferred text the results are for. */
|
|
10
|
+
readonly query: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ComboboxPopupProps extends Omit<ComponentPropsWithRef<"div">, "id" | "popover" | "onToggle" | "onBeforeToggle" | "onPointerDown" | "onMouseDown" | "className" | "style" | "children"> {
|
|
13
|
+
/**
|
|
14
|
+
* `false` (default): the children are not rendered while the listbox is closed. `true`: they
|
|
15
|
+
* stay mounted inside `<Activity mode="hidden">`.
|
|
16
|
+
*/
|
|
17
|
+
keepMounted?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Forwarded to the `<ViewTransition default>` around the popover element itself. Animates
|
|
20
|
+
* library-driven opens and closes (typing, the Trigger, an accepted option) in every engine;
|
|
21
|
+
* dismissals are the browser's (CSS only). **A deferred results render also activates the
|
|
22
|
+
* boundary** (React: updates from `useDeferredValue` animate), so use a type map with
|
|
23
|
+
* `default: "none"` unless every result change should cross-fade. Static.
|
|
24
|
+
*/
|
|
25
|
+
transition?: ViewTransitionClass;
|
|
26
|
+
className?: StateValue<string | undefined, ComboboxPopupState>;
|
|
27
|
+
style?: StateValue<CSSProperties | undefined, ComboboxPopupState>;
|
|
28
|
+
children?: StateValue<ReactNode, ComboboxPopupState>;
|
|
29
|
+
/** Must render a `<div>`: a `<dialog>` would pull focus out of the input. */
|
|
30
|
+
render?: RenderProp<ComponentPropsWithRef<"div">, ComboboxPopupState>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The listbox's container: `<div popover="auto">`, opened by the library with
|
|
34
|
+
* `showPopover({ source: input })` and placed below (or, flipped, above) the input with CSS
|
|
35
|
+
* Anchor Positioning, at least as wide as the input. Focus never enters it: a press anywhere in
|
|
36
|
+
* it is `preventDefault()`ed so the input keeps focus (design 7.1). Holds the `List` and, outside
|
|
37
|
+
* it, `Empty` and `Status`. Style it with `:popover-open`; it emits `data-stale` but no
|
|
38
|
+
* `data-open`.
|
|
39
|
+
*
|
|
40
|
+
* Owned keys (conventions 1.2): `id`, `popover`, `onPointerDown`, `onMouseDown`, `data-stale`.
|
|
41
|
+
* Inline `position-area`, `position-try-fallbacks` and `min-width` sit under the consumer's
|
|
42
|
+
* `style` (`style={{ positionTryFallbacks: "none" }}` removes the flip).
|
|
43
|
+
*/
|
|
44
|
+
export declare function ComboboxPopup({ keepMounted, transition, className, style, children, render, ...rest }: ComboboxPopupProps): ReactNode;
|
|
45
|
+
export declare namespace ComboboxPopup {
|
|
46
|
+
var displayName: string;
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
//# sourceMappingURL=combobox-popup.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"combobox-popup.d.mts","names":[],"sources":["../../src/combobox/combobox-popup.tsx"],"mappings":";;;iBAmBiB;;WAEN;;WAEA;;WAEA;;iBAGM,2BAA2B,KAC1C;;;;;EAeA;;;;;;;;EAQA,aAAa;EACb,YAAY,+BAA+B;EAC3C,QAAQ,WAAW,2BAA2B;EAC9C,WAAW,WAAW,WAAW;;EAEjC,SAAS,WAAW,8BAA8B;;;;;;;;;;;;;;wBAmBpC,gBACd,aACA,YACA,WACA,OACA,UACA,WACG,QACF,qBAAkB;yBARL"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useTransitionBoundary } from "../internal/transition-boundary.mjs";
|
|
3
|
+
import { dataAttr } from "../internal/data-attr.mjs";
|
|
4
|
+
import { mergeRefs } from "../internal/merge-props.mjs";
|
|
5
|
+
import { resolveStateValue, useRender } from "../internal/use-render.mjs";
|
|
6
|
+
import { usePopoverPopup } from "../internal/use-popover-popup.mjs";
|
|
7
|
+
import { ComboboxPopupContext, useComboboxContext } from "./combobox-context.mjs";
|
|
8
|
+
import { Activity, useCallback, useMemo } from "react";
|
|
9
|
+
import { jsx } from "react/jsx-runtime";
|
|
10
|
+
//#region src/combobox/combobox-popup.tsx
|
|
11
|
+
const isDev = process.env.NODE_ENV !== "production";
|
|
12
|
+
const preventDefault = (event) => event.preventDefault();
|
|
13
|
+
/**
|
|
14
|
+
* The listbox's container: `<div popover="auto">`, opened by the library with
|
|
15
|
+
* `showPopover({ source: input })` and placed below (or, flipped, above) the input with CSS
|
|
16
|
+
* Anchor Positioning, at least as wide as the input. Focus never enters it: a press anywhere in
|
|
17
|
+
* it is `preventDefault()`ed so the input keeps focus (design 7.1). Holds the `List` and, outside
|
|
18
|
+
* it, `Empty` and `Status`. Style it with `:popover-open`; it emits `data-stale` but no
|
|
19
|
+
* `data-open`.
|
|
20
|
+
*
|
|
21
|
+
* Owned keys (conventions 1.2): `id`, `popover`, `onPointerDown`, `onMouseDown`, `data-stale`.
|
|
22
|
+
* Inline `position-area`, `position-try-fallbacks` and `min-width` sit under the consumer's
|
|
23
|
+
* `style` (`style={{ positionTryFallbacks: "none" }}` removes the flip).
|
|
24
|
+
*/
|
|
25
|
+
function ComboboxPopup({ keepMounted, transition, className, style, children, render, ...rest }) {
|
|
26
|
+
const ctx = useComboboxContext("Combobox.Popup");
|
|
27
|
+
const { popover } = ctx;
|
|
28
|
+
const { setElement, visible, wrapContent } = usePopoverPopup(popover, {
|
|
29
|
+
part: "Combobox.Popup",
|
|
30
|
+
initialFocus: void 0,
|
|
31
|
+
resolveInitialFocus: () => null,
|
|
32
|
+
keepMounted,
|
|
33
|
+
transition
|
|
34
|
+
});
|
|
35
|
+
const devRef = useCallback((element) => {
|
|
36
|
+
if (isDev && element.tagName !== "DIV") console.error(`Combobox.Popup: rendered a <${element.tagName.toLowerCase()}>. Render a <div>: a <dialog popover> runs the dialog focusing steps and takes focus out of the input.`);
|
|
37
|
+
return () => {};
|
|
38
|
+
}, []);
|
|
39
|
+
const ref = useMemo(() => mergeRefs(setElement, devRef), [setElement, devRef]);
|
|
40
|
+
const state = {
|
|
41
|
+
open: popover.committed,
|
|
42
|
+
stale: ctx.stale,
|
|
43
|
+
query: ctx.query
|
|
44
|
+
};
|
|
45
|
+
const libraryProps = {
|
|
46
|
+
id: popover.ids.popup,
|
|
47
|
+
popover: "auto",
|
|
48
|
+
onPointerDown: (event) => preventDefault(event),
|
|
49
|
+
onMouseDown: (event) => preventDefault(event),
|
|
50
|
+
"data-stale": dataAttr(ctx.stale)
|
|
51
|
+
};
|
|
52
|
+
const content = (s) => {
|
|
53
|
+
let node;
|
|
54
|
+
if (keepMounted === true) node = /* @__PURE__ */ jsx(Activity, {
|
|
55
|
+
mode: visible ? "visible" : "hidden",
|
|
56
|
+
children: wrapContent(resolveStateValue(children, s))
|
|
57
|
+
});
|
|
58
|
+
else node = visible ? wrapContent(resolveStateValue(children, s)) : null;
|
|
59
|
+
return /* @__PURE__ */ jsx(ComboboxPopupContext, {
|
|
60
|
+
value: true,
|
|
61
|
+
children: node
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
const element = useRender({
|
|
65
|
+
defaultElement: "div",
|
|
66
|
+
render,
|
|
67
|
+
state,
|
|
68
|
+
className,
|
|
69
|
+
style: (s) => ({
|
|
70
|
+
positionArea: "block-end span-inline-end",
|
|
71
|
+
positionTryFallbacks: "flip-block",
|
|
72
|
+
minWidth: "anchor-size(width)",
|
|
73
|
+
...resolveStateValue(style, s)
|
|
74
|
+
}),
|
|
75
|
+
children: content,
|
|
76
|
+
props: rest,
|
|
77
|
+
libraryProps,
|
|
78
|
+
ref
|
|
79
|
+
});
|
|
80
|
+
return useTransitionBoundary("Combobox.Popup", transition, element);
|
|
81
|
+
}
|
|
82
|
+
ComboboxPopup.displayName = "ComboboxPopup";
|
|
83
|
+
//#endregion
|
|
84
|
+
export { ComboboxPopup };
|
|
85
|
+
|
|
86
|
+
//# sourceMappingURL=combobox-popup.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"combobox-popup.mjs","names":[],"sources":["../../src/combobox/combobox-popup.tsx"],"sourcesContent":["\"use client\";\nimport { Activity, useCallback, useMemo } from \"react\";\nimport type {\n ComponentPropsWithRef,\n CSSProperties,\n MouseEvent,\n PointerEvent,\n ReactNode,\n ViewTransitionClass,\n} from \"react\";\nimport { dataAttr } from \"../internal/data-attr.js\";\nimport { mergeRefs } from \"../internal/merge-props.js\";\nimport { useTransitionBoundary } from \"../internal/transition-boundary.js\";\nimport type { RenderProp, StateValue } from \"../internal/types.js\";\nimport { usePopoverPopup } from \"../internal/use-popover-popup.js\";\nimport { resolveStateValue, useRender } from \"../internal/use-render.js\";\nimport type { DataAttributes } from \"../internal/use-render.js\";\nimport { ComboboxPopupContext, useComboboxContext } from \"./combobox-context.js\";\n\nexport interface ComboboxPopupState {\n /** Committed open state. Read `:popover-open` for the DOM truth. */\n readonly open: boolean;\n /** The results shown are for an older text. */\n readonly stale: boolean;\n /** The deferred text the results are for. */\n readonly query: string;\n}\n\nexport interface ComboboxPopupProps extends Omit<\n ComponentPropsWithRef<\"div\">,\n | \"id\"\n | \"popover\"\n | \"onToggle\"\n | \"onBeforeToggle\"\n | \"onPointerDown\"\n | \"onMouseDown\"\n | \"className\"\n | \"style\"\n | \"children\"\n> {\n /**\n * `false` (default): the children are not rendered while the listbox is closed. `true`: they\n * stay mounted inside `<Activity mode=\"hidden\">`.\n */\n keepMounted?: boolean;\n /**\n * Forwarded to the `<ViewTransition default>` around the popover element itself. Animates\n * library-driven opens and closes (typing, the Trigger, an accepted option) in every engine;\n * dismissals are the browser's (CSS only). **A deferred results render also activates the\n * boundary** (React: updates from `useDeferredValue` animate), so use a type map with\n * `default: \"none\"` unless every result change should cross-fade. Static.\n */\n transition?: ViewTransitionClass;\n className?: StateValue<string | undefined, ComboboxPopupState>;\n style?: StateValue<CSSProperties | undefined, ComboboxPopupState>;\n children?: StateValue<ReactNode, ComboboxPopupState>;\n /** Must render a `<div>`: a `<dialog>` would pull focus out of the input. */\n render?: RenderProp<ComponentPropsWithRef<\"div\">, ComboboxPopupState>;\n}\n\nconst isDev = process.env.NODE_ENV !== \"production\";\n\nconst preventDefault = (event: { preventDefault(): void }) => event.preventDefault();\n\n/**\n * The listbox's container: `<div popover=\"auto\">`, opened by the library with\n * `showPopover({ source: input })` and placed below (or, flipped, above) the input with CSS\n * Anchor Positioning, at least as wide as the input. Focus never enters it: a press anywhere in\n * it is `preventDefault()`ed so the input keeps focus (design 7.1). Holds the `List` and, outside\n * it, `Empty` and `Status`. Style it with `:popover-open`; it emits `data-stale` but no\n * `data-open`.\n *\n * Owned keys (conventions 1.2): `id`, `popover`, `onPointerDown`, `onMouseDown`, `data-stale`.\n * Inline `position-area`, `position-try-fallbacks` and `min-width` sit under the consumer's\n * `style` (`style={{ positionTryFallbacks: \"none\" }}` removes the flip).\n */\nexport function ComboboxPopup({\n keepMounted,\n transition,\n className,\n style,\n children,\n render,\n ...rest\n}: ComboboxPopupProps) {\n const ctx = useComboboxContext(\"Combobox.Popup\");\n const { popover } = ctx;\n\n const { setElement, visible, wrapContent } = usePopoverPopup(popover, {\n part: \"Combobox.Popup\",\n initialFocus: undefined,\n // Focus stays in the input: nothing inside the popup is ever focused.\n resolveInitialFocus: () => null,\n keepMounted,\n transition,\n });\n\n const devRef = useCallback((element: HTMLElement) => {\n if (isDev && element.tagName !== \"DIV\") {\n console.error(\n `Combobox.Popup: rendered a <${element.tagName.toLowerCase()}>. Render a <div>: a ` +\n \"<dialog popover> runs the dialog focusing steps and takes focus out of the input.\",\n );\n }\n return () => {};\n }, []);\n const ref = useMemo(() => mergeRefs(setElement, devRef), [setElement, devRef]);\n\n const state: ComboboxPopupState = {\n open: popover.committed,\n stale: ctx.stale,\n query: ctx.query,\n };\n\n const libraryProps: ComponentPropsWithRef<\"div\"> & DataAttributes = {\n id: popover.ids.popup,\n popover: \"auto\",\n onPointerDown: (event: PointerEvent<HTMLDivElement>) => preventDefault(event),\n onMouseDown: (event: MouseEvent<HTMLDivElement>) => preventDefault(event),\n \"data-stale\": dataAttr(ctx.stale),\n };\n\n const content = (s: ComboboxPopupState): ReactNode => {\n let node: ReactNode;\n if (keepMounted === true) {\n node = (\n <Activity mode={visible ? \"visible\" : \"hidden\"}>\n {wrapContent(resolveStateValue(children, s))}\n </Activity>\n );\n } else {\n node = visible ? wrapContent(resolveStateValue(children, s)) : null;\n }\n return <ComboboxPopupContext value={true}>{node}</ComboboxPopupContext>;\n };\n\n const element = useRender<ComponentPropsWithRef<\"div\">, ComboboxPopupState>({\n defaultElement: \"div\",\n render,\n state,\n className,\n // Logical keywords: right under `direction: rtl` with no JS (design 7.2). The consumer wins.\n style: (s) => ({\n positionArea: \"block-end span-inline-end\",\n positionTryFallbacks: \"flip-block\",\n minWidth: \"anchor-size(width)\",\n ...resolveStateValue(style, s),\n }),\n children: content,\n props: rest,\n libraryProps,\n ref,\n });\n\n return useTransitionBoundary(\"Combobox.Popup\", transition, element);\n}\n\nComboboxPopup.displayName = \"ComboboxPopup\";\n"],"mappings":";;;;;;;;;;AA4DA,MAAM,QAAQ,QAAQ,IAAI,aAAa;AAEvC,MAAM,kBAAkB,UAAsC,MAAM,eAAe;;;;;;;;;;;;;AAcnF,SAAgB,cAAc,EAC5B,aACA,YACA,WACA,OACA,UACA,QACA,GAAG,QACkB;CACrB,MAAM,MAAM,mBAAmB,gBAAgB;CAC/C,MAAM,EAAE,YAAY;CAEpB,MAAM,EAAE,YAAY,SAAS,gBAAgB,gBAAgB,SAAS;EACpE,MAAM;EACN,cAAc,KAAA;EAEd,2BAA2B;EAC3B;EACA;CACF,CAAC;CAED,MAAM,SAAS,aAAa,YAAyB;EACnD,IAAI,SAAS,QAAQ,YAAY,OAC/B,QAAQ,MACN,+BAA+B,QAAQ,QAAQ,YAAY,EAAE,uGAE/D;EAEF,aAAa,CAAC;CAChB,GAAG,CAAC,CAAC;CACL,MAAM,MAAM,cAAc,UAAU,YAAY,MAAM,GAAG,CAAC,YAAY,MAAM,CAAC;CAE7E,MAAM,QAA4B;EAChC,MAAM,QAAQ;EACd,OAAO,IAAI;EACX,OAAO,IAAI;CACb;CAEA,MAAM,eAA8D;EAClE,IAAI,QAAQ,IAAI;EAChB,SAAS;EACT,gBAAgB,UAAwC,eAAe,KAAK;EAC5E,cAAc,UAAsC,eAAe,KAAK;EACxE,cAAc,SAAS,IAAI,KAAK;CAClC;CAEA,MAAM,WAAW,MAAqC;EACpD,IAAI;EACJ,IAAI,gBAAgB,MAClB,OACE,oBAAC,UAAD;GAAU,MAAM,UAAU,YAAY;GACnC,UAAA,YAAY,kBAAkB,UAAU,CAAC,CAAC;EACnC,CAAA;OAGZ,OAAO,UAAU,YAAY,kBAAkB,UAAU,CAAC,CAAC,IAAI;EAEjE,OAAO,oBAAC,sBAAD;GAAsB,OAAO;GAAO,UAAA;EAA2B,CAAA;CACxE;CAEA,MAAM,UAAU,UAA4D;EAC1E,gBAAgB;EAChB;EACA;EACA;EAEA,QAAQ,OAAO;GACb,cAAc;GACd,sBAAsB;GACtB,UAAU;GACV,GAAG,kBAAkB,OAAO,CAAC;EAC/B;EACA,UAAU;EACV,OAAO;EACP;EACA;CACF,CAAC;CAED,OAAO,sBAAsB,kBAAkB,YAAY,OAAO;AACpE;AAEA,cAAc,cAAc"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { ActionDetails, ChangeAction, ChangeReason, StateValue, UrgentChangeAction } from "../internal/types.mjs";
|
|
2
|
+
import { ReactNode } from "react";
|
|
3
|
+
//#region src/combobox/combobox-root.d.ts
|
|
4
|
+
/** Details passed as the second argument of `Combobox.Root`'s `valueChangeAction`. */
|
|
5
|
+
export interface ComboboxValueChangeDetails extends ActionDetails {
|
|
6
|
+
/**
|
|
7
|
+
* `"activate"`: the user accepted an option (Enter, click; `details.event` is the `keydown` or
|
|
8
|
+
* `click`), committed free text with `allowCustomValue` (Enter: the `keydown`; focus leaving:
|
|
9
|
+
* the `focusout`), or pressed Clear (`click`).
|
|
10
|
+
* `"platform"`: a form reset (`details.event` is the `reset` event).
|
|
11
|
+
*/
|
|
12
|
+
readonly reason: ChangeReason;
|
|
13
|
+
/** `true` when `next` is free text committed under `allowCustomValue`, not an option's value. */
|
|
14
|
+
readonly custom: boolean;
|
|
15
|
+
}
|
|
16
|
+
/** Details passed as the second argument of `Combobox.Root`'s `inputValueChangeAction`. */
|
|
17
|
+
export interface ComboboxInputValueChangeDetails extends ActionDetails {
|
|
18
|
+
/**
|
|
19
|
+
* `"activate"`: typing (the `input` event), an accepted option's label, Clear.
|
|
20
|
+
* `"dismiss"`: a revert: focus left with unmatched text, or the selection rolled back.
|
|
21
|
+
* `"platform"`: form reset (the `reset` event), or text found in the input at mount
|
|
22
|
+
* (`undefined`).
|
|
23
|
+
*/
|
|
24
|
+
readonly reason: ChangeReason;
|
|
25
|
+
}
|
|
26
|
+
/** Details passed as the second argument of `Combobox.Root`'s `openChangeAction`. */
|
|
27
|
+
export interface ComboboxOpenChangeDetails extends ActionDetails {
|
|
28
|
+
/**
|
|
29
|
+
* `"activate"`: typing, ArrowDown / ArrowUp / Alt+ArrowDown, the Trigger, a click on the input
|
|
30
|
+
* with `openOnClick`, an accepted option (the close after it), a foreign `showPopover()`.
|
|
31
|
+
* `"dismiss"`: Escape, light dismiss, focus leaving the combobox, another top-layer element.
|
|
32
|
+
* `"platform"`: the popup was found open on mount (only script can cause it).
|
|
33
|
+
*/
|
|
34
|
+
readonly reason: ChangeReason;
|
|
35
|
+
}
|
|
36
|
+
export interface ComboboxRootState {
|
|
37
|
+
/** Optimistic selection. Content rendered from it must not suspend. */
|
|
38
|
+
readonly value: string | null;
|
|
39
|
+
/** The text in the input (urgent). */
|
|
40
|
+
readonly inputValue: string;
|
|
41
|
+
/** The deferred text the mounted results are for. */
|
|
42
|
+
readonly query: string;
|
|
43
|
+
/** `inputValue !== query`: the results shown are for an older text. */
|
|
44
|
+
readonly stale: boolean;
|
|
45
|
+
/** The value of the option `aria-activedescendant` points at, or `null`. */
|
|
46
|
+
readonly activeValue: string | null;
|
|
47
|
+
/** Optimistic open state. */
|
|
48
|
+
readonly open: boolean;
|
|
49
|
+
/** A `valueChangeAction` or `openChangeAction` dispatched through this Combobox is in flight. */
|
|
50
|
+
readonly pending: boolean;
|
|
51
|
+
readonly disabled: boolean;
|
|
52
|
+
}
|
|
53
|
+
export interface ComboboxRootProps {
|
|
54
|
+
/** Controlled selection. `null`: nothing selected. `undefined`: uncontrolled. */
|
|
55
|
+
value?: string | null;
|
|
56
|
+
/** Initial selection in uncontrolled mode. Default `null`. */
|
|
57
|
+
defaultValue?: string | null;
|
|
58
|
+
/**
|
|
59
|
+
* Runs inside the Root's transition when the user accepts an option, commits free text
|
|
60
|
+
* (`allowCustomValue`), presses Clear, or the form resets. Awaited; the listbox closes in the
|
|
61
|
+
* commit that ends it; a rejection rolls the input text and `data-selected` back and leaves
|
|
62
|
+
* the listbox open. May be a Server Function. A `setState` after your own `await` needs
|
|
63
|
+
* `startTransition`.
|
|
64
|
+
*/
|
|
65
|
+
valueChangeAction?: ChangeAction<string | null, ComboboxValueChangeDetails>;
|
|
66
|
+
/** Controlled input text. `undefined`: uncontrolled. */
|
|
67
|
+
inputValue?: string;
|
|
68
|
+
/**
|
|
69
|
+
* Initial input text. Default `""`. Pass the selected value's label when `defaultValue` is
|
|
70
|
+
* set: the library never derives the text from the value.
|
|
71
|
+
*/
|
|
72
|
+
defaultInputValue?: string;
|
|
73
|
+
/**
|
|
74
|
+
* The urgent lane: called synchronously, outside any Transition, on every change of the text
|
|
75
|
+
* (typing, and the library's own writes: an accepted option's label, a revert, Clear, a form
|
|
76
|
+
* reset, text typed before hydration). Not awaited; nothing is pending for it. A controlled
|
|
77
|
+
* parent updates its state in the body (an urgent update, which is what a controlled text
|
|
78
|
+
* input needs).
|
|
79
|
+
*/
|
|
80
|
+
inputValueChangeAction?: UrgentChangeAction<string, ComboboxInputValueChangeDetails>;
|
|
81
|
+
/** Controlled open state. `undefined`: uncontrolled. */
|
|
82
|
+
open?: boolean;
|
|
83
|
+
/** Initial open state when uncontrolled. Opens after hydration. */
|
|
84
|
+
defaultOpen?: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Awaited for opens and for the close after an accepted option (or the Trigger); a
|
|
87
|
+
* notification for dismissals (Escape, a click outside, focus leaving).
|
|
88
|
+
*/
|
|
89
|
+
openChangeAction?: ChangeAction<boolean, ComboboxOpenChangeDetails>;
|
|
90
|
+
/**
|
|
91
|
+
* Free text is a value: Enter with no active option, or focus leaving with text that is not
|
|
92
|
+
* the selected option's label, dispatches the text with `details.custom: true` (empty text
|
|
93
|
+
* dispatches `null`). Default `false`: unmatched text reverts to the selection's label when
|
|
94
|
+
* focus leaves.
|
|
95
|
+
*/
|
|
96
|
+
allowCustomValue?: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Make the first enabled option active when the results for an open or an edit settle (APG
|
|
99
|
+
* "automatic selection"). Stale results are never highlighted. Default `false`.
|
|
100
|
+
*/
|
|
101
|
+
autoHighlight?: boolean;
|
|
102
|
+
/** Open the listbox on a pointer click in the input (not on focus). Default `false`. */
|
|
103
|
+
openOnClick?: boolean;
|
|
104
|
+
/** Wrap from the last option to the first and back with the arrow keys. Default `false`. */
|
|
105
|
+
loop?: boolean;
|
|
106
|
+
/**
|
|
107
|
+
* The message `ariaNotify()` announces on the List when the results settle, from the number
|
|
108
|
+
* of mounted options. Default (English): `"No results"`, `"1 result"`, `"N results"`. Override
|
|
109
|
+
* for localisation or a richer message; return `null` to announce nothing. Ignored where
|
|
110
|
+
* `ariaNotify` is missing.
|
|
111
|
+
*/
|
|
112
|
+
announce?: (count: number, state: ComboboxRootState) => string | null;
|
|
113
|
+
/** Form participation through a hidden input with this name (value `""` for `null`). */
|
|
114
|
+
name?: string;
|
|
115
|
+
/** The `form` attribute of the hidden input. */
|
|
116
|
+
form?: string;
|
|
117
|
+
/** Disables the Input, Trigger and Clear; nothing dispatches for user interaction. */
|
|
118
|
+
disabled?: boolean;
|
|
119
|
+
children?: StateValue<ReactNode, ComboboxRootState>;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Owns the two states of a combobox (design/components/combobox.md 2): the selection (`value`,
|
|
123
|
+
* a transition with `useOptimistic`, like every other value) and the text (`inputValue`, the
|
|
124
|
+
* library's **urgent** lane: `inputValueChangeAction` is called synchronously, outside any
|
|
125
|
+
* transition). The results render from `useDeferredValue(inputValue)`; the active option is
|
|
126
|
+
* urgent virtual focus (`aria-activedescendant`); the listbox is a `popover="auto"` opened by the
|
|
127
|
+
* library with the input as `source`. Renders no DOM, except one `<input type="hidden">` when
|
|
128
|
+
* `name` is given.
|
|
129
|
+
*/
|
|
130
|
+
export declare function ComboboxRoot(props: ComboboxRootProps): import("react").JSX.Element;
|
|
131
|
+
export declare namespace ComboboxRoot {
|
|
132
|
+
var displayName: string;
|
|
133
|
+
}
|
|
134
|
+
//#endregion
|
|
135
|
+
//# sourceMappingURL=combobox-root.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"combobox-root.d.mts","names":[],"sources":["../../src/combobox/combobox-root.tsx"],"mappings":";;;;iBAgCiB,mCAAmC;;;;;;;WAOzC,QAAQ;;WAER;;;iBAIM,wCAAwC;;;;;;;WAO9C,QAAQ;;;iBAIF,kCAAkC;;;;;;;WAOxC,QAAQ;;iBAGF;;WAEN;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA;WACA;;iBAGM;;EAEf;;EAEA;;;;;;;;EAQA,oBAAoB,4BAA4B;;EAGhD;;;;;EAKA;;;;;;;;EAQA,yBAAyB,2BAA2B;;EAGpD;;EAEA;;;;;EAKA,mBAAmB,sBAAsB;;;;;;;EAQzC;;;;;EAKA;;EAEA;;EAEA;;;;;;;EAOA,YAAY,eAAe,OAAO;;EAGlC;;EAEA;;EAEA;EACA,WAAW,WAAW,WAAW;;;;;;;;;;;wBA+DnB,aAAa,OAAO,oCAAiB,IAAA;yBAArC"}
|