@charcuterie/ui 1.0.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ColorSchemeSwitcher/ColorSchemeSwitcher.d.ts +35 -0
- package/dist/ColorSchemeSwitcher/ColorSchemeSwitcher.d.ts.map +1 -0
- package/dist/ColorSchemeSwitcher/ColorSchemeSwitcher.js +33 -0
- package/dist/ColorSchemeSwitcher/ColorSchemeSwitcher.js.map +1 -0
- package/dist/ColorSchemeToggle/ColorSchemeToggle.d.ts +54 -0
- package/dist/ColorSchemeToggle/ColorSchemeToggle.d.ts.map +1 -0
- package/dist/ColorSchemeToggle/ColorSchemeToggle.js +30 -0
- package/dist/ColorSchemeToggle/ColorSchemeToggle.js.map +1 -0
- package/dist/Combobox/Combobox.d.ts +54 -0
- package/dist/Combobox/Combobox.d.ts.map +1 -0
- package/dist/Combobox/Combobox.js +216 -0
- package/dist/Combobox/Combobox.js.map +1 -0
- package/dist/Combobox/ComboboxOption.d.ts +36 -0
- package/dist/Combobox/ComboboxOption.d.ts.map +1 -0
- package/dist/Combobox/ComboboxOption.js +32 -0
- package/dist/Combobox/ComboboxOption.js.map +1 -0
- package/dist/Dialog/Dialog.d.ts +47 -0
- package/dist/Dialog/Dialog.d.ts.map +1 -0
- package/dist/Dialog/Dialog.js +52 -0
- package/dist/Dialog/Dialog.js.map +1 -0
- package/dist/Lightbox/Lightbox.d.ts +57 -0
- package/dist/Lightbox/Lightbox.d.ts.map +1 -0
- package/dist/Lightbox/Lightbox.js +50 -0
- package/dist/Lightbox/Lightbox.js.map +1 -0
- package/dist/Listbox/Listbox.d.ts +56 -0
- package/dist/Listbox/Listbox.d.ts.map +1 -0
- package/dist/Listbox/Listbox.js +151 -0
- package/dist/Listbox/Listbox.js.map +1 -0
- package/dist/Listbox/ListboxOption.d.ts +36 -0
- package/dist/Listbox/ListboxOption.d.ts.map +1 -0
- package/dist/Listbox/ListboxOption.js +42 -0
- package/dist/Listbox/ListboxOption.js.map +1 -0
- package/dist/Menu/Menu.d.ts.map +1 -1
- package/dist/Menu/Menu.js +47 -97
- package/dist/Menu/Menu.js.map +1 -1
- package/dist/Modal/Modal.d.ts +11 -59
- package/dist/Modal/Modal.d.ts.map +1 -1
- package/dist/Modal/Modal.js +28 -101
- package/dist/Modal/Modal.js.map +1 -1
- package/dist/Overlay/OverlayPanel.d.ts +50 -0
- package/dist/Overlay/OverlayPanel.d.ts.map +1 -0
- package/dist/Overlay/OverlayPanel.js +72 -0
- package/dist/Overlay/OverlayPanel.js.map +1 -0
- package/dist/Overlay/OverlayStack.d.ts +47 -0
- package/dist/Overlay/OverlayStack.d.ts.map +1 -0
- package/dist/Overlay/OverlayStack.js +46 -0
- package/dist/Overlay/OverlayStack.js.map +1 -0
- package/dist/Overlay/SharedBackdrop.d.ts +24 -0
- package/dist/Overlay/SharedBackdrop.d.ts.map +1 -0
- package/dist/Overlay/SharedBackdrop.js +28 -0
- package/dist/Overlay/SharedBackdrop.js.map +1 -0
- package/dist/Overlay/lockScrollBehind.d.ts.map +1 -0
- package/dist/Overlay/lockScrollBehind.js.map +1 -0
- package/dist/Overlay/overlayPanelClass.d.ts +17 -0
- package/dist/Overlay/overlayPanelClass.d.ts.map +1 -0
- package/dist/Overlay/overlayPanelClass.js +17 -0
- package/dist/Overlay/overlayPanelClass.js.map +1 -0
- package/dist/Overlay/useAnchoredOverlay.d.ts +97 -0
- package/dist/Overlay/useAnchoredOverlay.d.ts.map +1 -0
- package/dist/Overlay/useAnchoredOverlay.js +81 -0
- package/dist/Overlay/useAnchoredOverlay.js.map +1 -0
- package/dist/Popover/Popover.d.ts +16 -24
- package/dist/Popover/Popover.d.ts.map +1 -1
- package/dist/Popover/Popover.js +32 -68
- package/dist/Popover/Popover.js.map +1 -1
- package/dist/Tooltip/Tooltip.d.ts.map +1 -1
- package/dist/Tooltip/Tooltip.js +25 -25
- package/dist/Tooltip/Tooltip.js.map +1 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/mountStory.testHelpers.d.ts +1 -0
- package/dist/mountStory.testHelpers.d.ts.map +1 -1
- package/dist/mountStory.testHelpers.js +21 -1
- package/dist/mountStory.testHelpers.js.map +1 -1
- package/package.json +5 -4
- package/src/ColorSchemeSwitcher/ColorSchemeSwitcher.mdx +70 -0
- package/src/ColorSchemeSwitcher/ColorSchemeSwitcher.stories.tsx +118 -0
- package/src/ColorSchemeSwitcher/ColorSchemeSwitcher.test.tsx +53 -0
- package/src/ColorSchemeSwitcher/ColorSchemeSwitcher.tsx +90 -0
- package/src/ColorSchemeToggle/ColorSchemeToggle.mdx +48 -0
- package/src/ColorSchemeToggle/ColorSchemeToggle.stories.tsx +161 -0
- package/src/ColorSchemeToggle/ColorSchemeToggle.test.tsx +80 -0
- package/src/ColorSchemeToggle/ColorSchemeToggle.tsx +107 -0
- package/src/Combobox/Combobox.mdx +71 -0
- package/src/Combobox/Combobox.stories.tsx +245 -0
- package/src/Combobox/Combobox.test.tsx +289 -0
- package/src/Combobox/Combobox.tsx +513 -0
- package/src/Combobox/ComboboxOption.tsx +88 -0
- package/src/Dialog/Dialog.mdx +87 -0
- package/src/Dialog/Dialog.stories.tsx +347 -0
- package/src/Dialog/Dialog.test.tsx +312 -0
- package/src/Dialog/Dialog.tsx +127 -0
- package/src/Field/Field.test.tsx +6 -4
- package/src/Lightbox/Lightbox.mdx +63 -0
- package/src/Lightbox/Lightbox.stories.tsx +133 -0
- package/src/Lightbox/Lightbox.test.tsx +124 -0
- package/src/Lightbox/Lightbox.tsx +143 -0
- package/src/Listbox/Listbox.mdx +68 -0
- package/src/Listbox/Listbox.stories.tsx +205 -0
- package/src/Listbox/Listbox.test.tsx +198 -0
- package/src/Listbox/Listbox.tsx +295 -0
- package/src/Listbox/ListboxOption.tsx +104 -0
- package/src/Menu/Menu.mdx +11 -13
- package/src/Menu/Menu.test.tsx +47 -38
- package/src/Menu/Menu.tsx +81 -142
- package/src/Modal/Modal.mdx +29 -82
- package/src/Modal/Modal.stories.tsx +55 -188
- package/src/Modal/Modal.test.tsx +44 -177
- package/src/Modal/Modal.tsx +54 -204
- package/src/Overlay/OverlayPanel.tsx +195 -0
- package/src/Overlay/OverlayStack.test.tsx +235 -0
- package/src/Overlay/OverlayStack.tsx +132 -0
- package/src/Overlay/SharedBackdrop.tsx +40 -0
- package/src/Overlay/overlayPanelClass.ts +17 -0
- package/src/Overlay/useAnchoredOverlay.ts +197 -0
- package/src/Popover/Popover.mdx +17 -20
- package/src/Popover/Popover.stories.tsx +33 -0
- package/src/Popover/Popover.test.tsx +68 -32
- package/src/Popover/Popover.tsx +56 -95
- package/src/Tooltip/Tooltip.mdx +12 -9
- package/src/Tooltip/Tooltip.test.tsx +17 -19
- package/src/Tooltip/Tooltip.tsx +35 -32
- package/src/icons.storyHelpers.tsx +22 -0
- package/src/index.ts +22 -4
- package/src/mountStory.testHelpers.ts +25 -1
- package/src/sourceRules.test.ts +26 -1
- package/src/storyControls.test.ts +25 -12
- package/dist/Modal/lockScrollBehind.d.ts.map +0 -1
- package/dist/Modal/lockScrollBehind.js.map +0 -1
- /package/dist/{Modal → Overlay}/lockScrollBehind.d.ts +0 -0
- /package/dist/{Modal → Overlay}/lockScrollBehind.js +0 -0
- /package/src/{Modal → Overlay}/lockScrollBehind.ts +0 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ColorSchemeApplier, ColorSchemePersistence, ColorSchemeResolver } from "@charcuterie/logic";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
import type { ColorSchemeIcons, ColorSchemeToggleProps } from "../ColorSchemeToggle/ColorSchemeToggle.tsx";
|
|
4
|
+
export type ColorSchemeSwitcherProps = Pick<ColorSchemeToggleProps, "appearance" | "className" | "isDisabled" | "label" | "order" | "size"> & {
|
|
5
|
+
/** Override where the resolved scheme is written. Default `<html>`. */
|
|
6
|
+
apply?: ColorSchemeApplier;
|
|
7
|
+
icons: ColorSchemeIcons;
|
|
8
|
+
/** Override persistence. Default `localStorage` under `storageKey`. */
|
|
9
|
+
persistence?: ColorSchemePersistence;
|
|
10
|
+
/**
|
|
11
|
+
* Override the OS resolver. Default `matchMedia`. This is the seam
|
|
12
|
+
* the image-viewer passes Electron `nativeTheme` into, and a
|
|
13
|
+
* future React-Native app passes `Appearance` — the whole reason
|
|
14
|
+
* the browser dependency is isolated to this one layer.
|
|
15
|
+
*/
|
|
16
|
+
resolver?: ColorSchemeResolver;
|
|
17
|
+
storageKey?: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* The connected colour-scheme switcher — Layer 3, and the only
|
|
21
|
+
* layer that touches the browser.
|
|
22
|
+
*
|
|
23
|
+
* It is a thin wrapper: it wires `useColorScheme` to the three
|
|
24
|
+
* browser defaults from `@charcuterie/logic/browser`
|
|
25
|
+
* (`matchMedia` + `localStorage` + `data-scheme` on `<html>`), and
|
|
26
|
+
* renders the presentational `ColorSchemeToggle`. Every seam is
|
|
27
|
+
* overridable, so a non-browser host swaps `resolver`/`persistence`/
|
|
28
|
+
* `apply` and the same component drives Electron or React-Native.
|
|
29
|
+
*
|
|
30
|
+
* The seams are built once (lazy `useState`) so the `matchMedia`
|
|
31
|
+
* query and the core survive re-renders and StrictMode's double
|
|
32
|
+
* mount.
|
|
33
|
+
*/
|
|
34
|
+
export declare const ColorSchemeSwitcher: ({ apply, icons, order, persistence, resolver, storageKey, ...toggleProps }: ColorSchemeSwitcherProps) => ReactNode;
|
|
35
|
+
//# sourceMappingURL=ColorSchemeSwitcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ColorSchemeSwitcher.d.ts","sourceRoot":"","sources":["../../src/ColorSchemeSwitcher/ColorSchemeSwitcher.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,sBAAsB,EACtB,mBAAmB,EACpB,MAAM,oBAAoB,CAAA;AAO3B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC,OAAO,KAAK,EACV,gBAAgB,EAChB,sBAAsB,EACvB,MAAM,4CAA4C,CAAA;AAGnD,MAAM,MAAM,wBAAwB,GAAG,IAAI,CACzC,sBAAsB,EACpB,YAAY,GACZ,WAAW,GACX,YAAY,GACZ,OAAO,GACP,OAAO,GACP,MAAM,CACT,GAAG;IACF,uEAAuE;IACvE,KAAK,CAAC,EAAE,kBAAkB,CAAA;IAC1B,KAAK,EAAE,gBAAgB,CAAA;IACvB,uEAAuE;IACvE,WAAW,CAAC,EAAE,sBAAsB,CAAA;IACpC;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,mBAAmB,CAAA;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,mBAAmB,GAAI,4EAQjC,wBAAwB,KAAG,SAsB7B,CAAA"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useColorScheme } from "@charcuterie/logic";
|
|
3
|
+
import { dataSchemeApplier, localStoragePersistence, matchMediaResolver, } from "@charcuterie/logic/browser";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import { ColorSchemeToggle } from "../ColorSchemeToggle/ColorSchemeToggle.js";
|
|
6
|
+
/**
|
|
7
|
+
* The connected colour-scheme switcher — Layer 3, and the only
|
|
8
|
+
* layer that touches the browser.
|
|
9
|
+
*
|
|
10
|
+
* It is a thin wrapper: it wires `useColorScheme` to the three
|
|
11
|
+
* browser defaults from `@charcuterie/logic/browser`
|
|
12
|
+
* (`matchMedia` + `localStorage` + `data-scheme` on `<html>`), and
|
|
13
|
+
* renders the presentational `ColorSchemeToggle`. Every seam is
|
|
14
|
+
* overridable, so a non-browser host swaps `resolver`/`persistence`/
|
|
15
|
+
* `apply` and the same component drives Electron or React-Native.
|
|
16
|
+
*
|
|
17
|
+
* The seams are built once (lazy `useState`) so the `matchMedia`
|
|
18
|
+
* query and the core survive re-renders and StrictMode's double
|
|
19
|
+
* mount.
|
|
20
|
+
*/
|
|
21
|
+
export const ColorSchemeSwitcher = ({ apply, icons, order, persistence, resolver, storageKey, ...toggleProps }) => {
|
|
22
|
+
const [seams] = useState(() => ({
|
|
23
|
+
apply: apply ?? dataSchemeApplier(),
|
|
24
|
+
persistence: persistence ?? localStoragePersistence(storageKey),
|
|
25
|
+
resolver: resolver ?? matchMediaResolver(),
|
|
26
|
+
}));
|
|
27
|
+
const { cycle, mode } = useColorScheme({
|
|
28
|
+
...seams,
|
|
29
|
+
order,
|
|
30
|
+
});
|
|
31
|
+
return (_jsx(ColorSchemeToggle, { ...toggleProps, icons: icons, mode: mode, onCycle: cycle, order: order }));
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=ColorSchemeSwitcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ColorSchemeSwitcher.js","sourceRoot":"","sources":["../../src/ColorSchemeSwitcher/ColorSchemeSwitcher.tsx"],"names":[],"mappings":";AAKA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,4BAA4B,CAAA;AAEnC,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAMhC,OAAO,EAAE,iBAAiB,EAAE,MAAM,2CAA4C,CAAA;AA0B9E;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,EAClC,KAAK,EACL,KAAK,EACL,KAAK,EACL,WAAW,EACX,QAAQ,EACR,UAAU,EACV,GAAG,WAAW,EACW,EAAa,EAAE;IACxC,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC;QAC9B,KAAK,EAAE,KAAK,IAAI,iBAAiB,EAAE;QACnC,WAAW,EACT,WAAW,IAAI,uBAAuB,CAAC,UAAU,CAAC;QACpD,QAAQ,EAAE,QAAQ,IAAI,kBAAkB,EAAE;KAC3C,CAAC,CAAC,CAAA;IAEH,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,cAAc,CAAC;QACrC,GAAG,KAAK;QACR,KAAK;KACN,CAAC,CAAA;IAEF,OAAO,CACL,KAAC,iBAAiB,OACZ,WAAW,EACf,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,KAAK,EACd,KAAK,EAAE,KAAK,GACZ,CACH,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ColorSchemeMode } from "@charcuterie/logic";
|
|
2
|
+
import type { ControlSize } from "@charcuterie/tokens";
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
import type { IntentAppearance } from "../intentStyles.ts";
|
|
5
|
+
/**
|
|
6
|
+
* One `ReactNode` per mode. The library ships **no icons** — lucide
|
|
7
|
+
* (ISC) is the fleet recommendation — so an app passes its own
|
|
8
|
+
* `Sun` / `Moon` / `Monitor`, exactly as `IconButton` takes its
|
|
9
|
+
* glyph as `children`
|
|
10
|
+
* ([decision](../../../../docs/decisions/2026-07-29-ship-no-icons-and-no-symbol-glyphs.md)).
|
|
11
|
+
*/
|
|
12
|
+
export type ColorSchemeIcons = Record<ColorSchemeMode, ReactNode>;
|
|
13
|
+
export type ColorSchemeToggleProps = {
|
|
14
|
+
appearance?: IntentAppearance;
|
|
15
|
+
className?: string;
|
|
16
|
+
icons: ColorSchemeIcons;
|
|
17
|
+
isDisabled?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Builds the accessible name from the current and next mode.
|
|
20
|
+
* Default announces the current mode and where a press goes —
|
|
21
|
+
* "Colour scheme: dark. Activate to switch to system." — so the
|
|
22
|
+
* name updates as the control cycles and
|
|
23
|
+
* `getByRole("button", { name: /colour scheme/i })` resolves.
|
|
24
|
+
*/
|
|
25
|
+
label?: (mode: ColorSchemeMode, nextMode: ColorSchemeMode) => string;
|
|
26
|
+
mode: ColorSchemeMode;
|
|
27
|
+
/**
|
|
28
|
+
* Called with the next mode on activation. **Controlled**: this
|
|
29
|
+
* component stores nothing — it renders `mode` and reports intent,
|
|
30
|
+
* which is what keeps it free of `matchMedia`/`document` and
|
|
31
|
+
* usable in any runtime. `<ColorSchemeSwitcher>` wires this to the
|
|
32
|
+
* `useColorScheme` hook; a plain consumer wires it to its own
|
|
33
|
+
* state.
|
|
34
|
+
*/
|
|
35
|
+
onCycle: (nextMode: ColorSchemeMode) => void;
|
|
36
|
+
order?: readonly ColorSchemeMode[];
|
|
37
|
+
size?: ControlSize;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* The cycling colour-scheme button — light → dark → system → light.
|
|
41
|
+
*
|
|
42
|
+
* Layer 2 of the three: presentational and controlled, with no
|
|
43
|
+
* browser coupling. It is an `IconButton`, so it inherits the one
|
|
44
|
+
* rule that makes the fleet agent-drivable — a glyph is not a name,
|
|
45
|
+
* and this control's name states the current mode rather than
|
|
46
|
+
* leaving a screen reader to read a sun path as nothing.
|
|
47
|
+
*
|
|
48
|
+
* A *segmented* all-three-visible form was considered and deferred
|
|
49
|
+
* (owner chose cycling-only, 2026-08-03); `icons` and `order` are
|
|
50
|
+
* shaped so a `ColorSchemePicker` could join later without changing
|
|
51
|
+
* this API.
|
|
52
|
+
*/
|
|
53
|
+
export declare const ColorSchemeToggle: ({ appearance, className, icons, isDisabled, label, mode, onCycle, order, size, }: ColorSchemeToggleProps) => ReactNode;
|
|
54
|
+
//# sourceMappingURL=ColorSchemeToggle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ColorSchemeToggle.d.ts","sourceRoot":"","sources":["../../src/ColorSchemeToggle/ColorSchemeToggle.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAKzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAE1D;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,MAAM,CACnC,eAAe,EACf,SAAS,CACV,CAAA;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,UAAU,CAAC,EAAE,gBAAgB,CAAA;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,gBAAgB,CAAA;IACvB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,CACN,IAAI,EAAE,eAAe,EACrB,QAAQ,EAAE,eAAe,KACtB,MAAM,CAAA;IACX,IAAI,EAAE,eAAe,CAAA;IACrB;;;;;;;OAOG;IACH,OAAO,EAAE,CAAC,QAAQ,EAAE,eAAe,KAAK,IAAI,CAAA;IAC5C,KAAK,CAAC,EAAE,SAAS,eAAe,EAAE,CAAA;IAClC,IAAI,CAAC,EAAE,WAAW,CAAA;CACnB,CAAA;AAcD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB,GAAI,kFAU/B,sBAAsB,KAAG,SAiB3B,CAAA"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { DEFAULT_COLOR_SCHEME_ORDER, nextColorSchemeMode, } from "@charcuterie/logic";
|
|
3
|
+
import { IconButton } from "../IconButton/IconButton.js";
|
|
4
|
+
const MODE_LABEL = {
|
|
5
|
+
dark: "Dark",
|
|
6
|
+
light: "Light",
|
|
7
|
+
system: "System",
|
|
8
|
+
};
|
|
9
|
+
const defaultLabel = (mode, nextMode) => `Colour scheme: ${MODE_LABEL[mode]}. Activate to switch to ${MODE_LABEL[nextMode]}.`;
|
|
10
|
+
/**
|
|
11
|
+
* The cycling colour-scheme button — light → dark → system → light.
|
|
12
|
+
*
|
|
13
|
+
* Layer 2 of the three: presentational and controlled, with no
|
|
14
|
+
* browser coupling. It is an `IconButton`, so it inherits the one
|
|
15
|
+
* rule that makes the fleet agent-drivable — a glyph is not a name,
|
|
16
|
+
* and this control's name states the current mode rather than
|
|
17
|
+
* leaving a screen reader to read a sun path as nothing.
|
|
18
|
+
*
|
|
19
|
+
* A *segmented* all-three-visible form was considered and deferred
|
|
20
|
+
* (owner chose cycling-only, 2026-08-03); `icons` and `order` are
|
|
21
|
+
* shaped so a `ColorSchemePicker` could join later without changing
|
|
22
|
+
* this API.
|
|
23
|
+
*/
|
|
24
|
+
export const ColorSchemeToggle = ({ appearance = "ghost", className, icons, isDisabled = false, label = defaultLabel, mode, onCycle, order = DEFAULT_COLOR_SCHEME_ORDER, size = "md", }) => {
|
|
25
|
+
const nextMode = nextColorSchemeMode(mode, order);
|
|
26
|
+
return (_jsx(IconButton, { appearance: appearance, className: className, isDisabled: isDisabled, label: label(mode, nextMode), onClick: () => {
|
|
27
|
+
onCycle(nextMode);
|
|
28
|
+
}, size: size, children: icons[mode] }));
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=ColorSchemeToggle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ColorSchemeToggle.js","sourceRoot":"","sources":["../../src/ColorSchemeToggle/ColorSchemeToggle.tsx"],"names":[],"mappings":";AACA,OAAO,EACL,0BAA0B,EAC1B,mBAAmB,GACpB,MAAM,oBAAoB,CAAA;AAI3B,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA8B,CAAA;AA6CzD,MAAM,UAAU,GAAoC;IAClD,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,OAAO;IACd,MAAM,EAAE,QAAQ;CACjB,CAAA;AAED,MAAM,YAAY,GAAG,CACnB,IAAqB,EACrB,QAAyB,EACzB,EAAE,CACF,kBAAkB,UAAU,CAAC,IAAI,CAAC,2BAA2B,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAA;AAEtF;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,EAChC,UAAU,GAAG,OAAO,EACpB,SAAS,EACT,KAAK,EACL,UAAU,GAAG,KAAK,EAClB,KAAK,GAAG,YAAY,EACpB,IAAI,EACJ,OAAO,EACP,KAAK,GAAG,0BAA0B,EAClC,IAAI,GAAG,IAAI,GACY,EAAa,EAAE;IACtC,MAAM,QAAQ,GAAG,mBAAmB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAEjD,OAAO,CACL,KAAC,UAAU,IACT,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,SAAS,EACpB,UAAU,EAAE,UAAU,EACtB,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,EAC5B,OAAO,EAAE,GAAG,EAAE;YACZ,OAAO,CAAC,QAAQ,CAAC,CAAA;QACnB,CAAC,EACD,IAAI,EAAE,IAAI,YAET,KAAK,CAAC,IAAI,CAAC,GACD,CACd,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ReactElement, ReactNode } from "react";
|
|
2
|
+
import type { ListboxItem } from "../Listbox/Listbox.tsx";
|
|
3
|
+
export type ComboboxProps = {
|
|
4
|
+
className?: string;
|
|
5
|
+
/** Shown when nothing matches. Default "No matches". */
|
|
6
|
+
emptyLabel?: string;
|
|
7
|
+
/** The error panel state (`PathPicker`). */
|
|
8
|
+
error?: ReactNode;
|
|
9
|
+
/** A sticky footer hint (`LinkPicker`'s "type a path directly"). */
|
|
10
|
+
footer?: ReactNode;
|
|
11
|
+
/** Enter commits the raw query as a value (`AssFieldPicker`). */
|
|
12
|
+
isCreatable?: boolean;
|
|
13
|
+
/** The loading panel state. */
|
|
14
|
+
isLoading?: boolean;
|
|
15
|
+
/** Multi-select, rendered as removable chips (`LanguageCodeField`). */
|
|
16
|
+
isMultiple?: boolean;
|
|
17
|
+
/** Windowing. Auto-on above ~100 options when left undefined. */
|
|
18
|
+
isVirtualized?: boolean;
|
|
19
|
+
isVisible: boolean;
|
|
20
|
+
onDismiss: () => void;
|
|
21
|
+
onSelect: (value: string) => void;
|
|
22
|
+
/**
|
|
23
|
+
* Omit to filter internally by `textValue`; supply to own the query
|
|
24
|
+
* (a react-query search, say), in which case `options` arrives
|
|
25
|
+
* pre-filtered.
|
|
26
|
+
*/
|
|
27
|
+
onQueryChange?: (query: string) => void;
|
|
28
|
+
options: readonly ListboxItem[];
|
|
29
|
+
placeholder?: string;
|
|
30
|
+
/** Controlled query; uncontrolled when absent. */
|
|
31
|
+
query?: string;
|
|
32
|
+
selectedValue?: readonly string[] | string;
|
|
33
|
+
/** The control the popup hangs off. **Cloned, not wrapped.** */
|
|
34
|
+
trigger: ReactElement;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* The searchable, filtering, virtualized picker — the other half of
|
|
38
|
+
* the picker family, and the one that consolidates the fleet's
|
|
39
|
+
* hand-rolled dropdowns.
|
|
40
|
+
*
|
|
41
|
+
* The ARIA contract differs from `Listbox` and the difference is
|
|
42
|
+
* forced, not stylistic: **focus stays in the text input**, so the
|
|
43
|
+
* active option is tracked with `aria-activedescendant`, not roving
|
|
44
|
+
* tabindex. The input is the `role="combobox"`; the popup is a
|
|
45
|
+
* `role="listbox"`. Arrows move the active descendant while the caret
|
|
46
|
+
* stays put; Enter selects; Escape clears the query before it closes.
|
|
47
|
+
*
|
|
48
|
+
* Filtering is the consumer's when `onQueryChange` is supplied (the
|
|
49
|
+
* `options` then arrive pre-filtered — a react-query search owns the
|
|
50
|
+
* debounce, the request token, the cache), and internal on `textValue`
|
|
51
|
+
* otherwise.
|
|
52
|
+
*/
|
|
53
|
+
export declare const Combobox: ({ className, emptyLabel, error, footer, isCreatable, isLoading, isMultiple, isVirtualized, isVisible, onDismiss, onQueryChange, onSelect, options, placeholder, query, selectedValue, trigger, }: ComboboxProps) => ReactNode;
|
|
54
|
+
//# sourceMappingURL=Combobox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Combobox.d.ts","sourceRoot":"","sources":["../../src/Combobox/Combobox.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAMzD,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,4CAA4C;IAC5C,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,oEAAoE;IACpE,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,iEAAiE;IACjE,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,+BAA+B;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,uEAAuE;IACvE,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,iEAAiE;IACjE,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,SAAS,EAAE,OAAO,CAAA;IAClB,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACvC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAA;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAAA;IAC1C,gEAAgE;IAChE,OAAO,EAAE,YAAY,CAAA;CACtB,CAAA;AA4BD;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,QAAQ,GAAI,kMAkBtB,aAAa,KAAG,SAmZlB,CAAA"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { FloatingFocusManager, FloatingPortal, } from "@floating-ui/react";
|
|
3
|
+
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
4
|
+
import { useEffect, useRef, useState } from "react";
|
|
5
|
+
import { PANEL_SURFACE_CLASS } from "../Overlay/overlayPanelClass.js";
|
|
6
|
+
import { useAnchoredOverlay } from "../Overlay/useAnchoredOverlay.js";
|
|
7
|
+
import { toClassName } from "../toClassName.js";
|
|
8
|
+
import { ComboboxOption } from "./ComboboxOption.js";
|
|
9
|
+
const AUTO_VIRTUALIZE_THRESHOLD = 100;
|
|
10
|
+
const getOptionText = (item) => (item.textValue ??
|
|
11
|
+
(typeof item.label === "string" ? item.label : "")).toLowerCase();
|
|
12
|
+
const nextEnabledIndex = (options, from, step) => {
|
|
13
|
+
for (let index = from + step; index >= 0 && index < options.length; index += step) {
|
|
14
|
+
if (!options[index]?.isDisabled) {
|
|
15
|
+
return index;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return from;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* The searchable, filtering, virtualized picker — the other half of
|
|
22
|
+
* the picker family, and the one that consolidates the fleet's
|
|
23
|
+
* hand-rolled dropdowns.
|
|
24
|
+
*
|
|
25
|
+
* The ARIA contract differs from `Listbox` and the difference is
|
|
26
|
+
* forced, not stylistic: **focus stays in the text input**, so the
|
|
27
|
+
* active option is tracked with `aria-activedescendant`, not roving
|
|
28
|
+
* tabindex. The input is the `role="combobox"`; the popup is a
|
|
29
|
+
* `role="listbox"`. Arrows move the active descendant while the caret
|
|
30
|
+
* stays put; Enter selects; Escape clears the query before it closes.
|
|
31
|
+
*
|
|
32
|
+
* Filtering is the consumer's when `onQueryChange` is supplied (the
|
|
33
|
+
* `options` then arrive pre-filtered — a react-query search owns the
|
|
34
|
+
* debounce, the request token, the cache), and internal on `textValue`
|
|
35
|
+
* otherwise.
|
|
36
|
+
*/
|
|
37
|
+
export const Combobox = ({ className, emptyLabel = "No matches", error, footer, isCreatable = false, isLoading = false, isMultiple = false, isVirtualized, isVisible, onDismiss, onQueryChange, onSelect, options, placeholder = "Search…", query, selectedValue, trigger, }) => {
|
|
38
|
+
const inputElement = useRef(null);
|
|
39
|
+
const listElement = useRef(null);
|
|
40
|
+
const isQueryControlled = query !== undefined;
|
|
41
|
+
const [internalQuery, setInternalQuery] = useState("");
|
|
42
|
+
const currentQuery = query ?? internalQuery;
|
|
43
|
+
const [activeIndex, setActiveIndex] = useState(0);
|
|
44
|
+
const [selected, setSelected] = useState(selectedValue === undefined
|
|
45
|
+
? []
|
|
46
|
+
: typeof selectedValue === "string"
|
|
47
|
+
? [selectedValue]
|
|
48
|
+
: [...selectedValue]);
|
|
49
|
+
// The consumer owns filtering when it owns the query; otherwise it is
|
|
50
|
+
// a plain `textValue` contains-match.
|
|
51
|
+
const filtered = onQueryChange === undefined
|
|
52
|
+
? options.filter((option) => getOptionText(option).includes(currentQuery.toLowerCase()))
|
|
53
|
+
: options;
|
|
54
|
+
const isWindowed = isVirtualized ??
|
|
55
|
+
filtered.length > AUTO_VIRTUALIZE_THRESHOLD;
|
|
56
|
+
const { clonedTrigger, context, floatingStyles, getFloatingProps, setFloating, triggerId, } = useAnchoredOverlay({
|
|
57
|
+
// Escape is handled here so it can clear the query before closing.
|
|
58
|
+
isEscapeDismissable: false,
|
|
59
|
+
isVisible,
|
|
60
|
+
maxHeightPx: 384,
|
|
61
|
+
offsetValue: 4,
|
|
62
|
+
onDismiss,
|
|
63
|
+
role: "listbox",
|
|
64
|
+
trigger,
|
|
65
|
+
});
|
|
66
|
+
// The panel wraps an input *and* a listbox, so the wrapper is a
|
|
67
|
+
// plain div: the `role="listbox"`/`id` `useRole` produced go on the
|
|
68
|
+
// inner list, which is what the trigger's and input's `aria-controls`
|
|
69
|
+
// then resolve to.
|
|
70
|
+
const { id: listboxId, role: _discardedPanelRole, ...floatingProps } = getFloatingProps();
|
|
71
|
+
const setQuery = (nextQuery) => {
|
|
72
|
+
if (!isQueryControlled) {
|
|
73
|
+
setInternalQuery(nextQuery);
|
|
74
|
+
}
|
|
75
|
+
setActiveIndex(0);
|
|
76
|
+
onQueryChange?.(nextQuery);
|
|
77
|
+
};
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (isVisible) {
|
|
80
|
+
inputElement.current?.focus();
|
|
81
|
+
}
|
|
82
|
+
}, [isVisible]);
|
|
83
|
+
const rowVirtualizer = useVirtualizer({
|
|
84
|
+
count: filtered.length,
|
|
85
|
+
estimateSize: () => 36,
|
|
86
|
+
getScrollElement: () => listElement.current,
|
|
87
|
+
overscan: 8,
|
|
88
|
+
});
|
|
89
|
+
const optionDomId = (value) => `${listboxId}-opt-${value}`;
|
|
90
|
+
// `activeIndex` is seeded at 0 on open and on every query reset, which
|
|
91
|
+
// can land on a disabled option; arrowing skips disabled but the seed
|
|
92
|
+
// does not. Resolve to the nearest enabled row and drive the highlight,
|
|
93
|
+
// `aria-activedescendant`, arrow moves and commit off *this* — never the
|
|
94
|
+
// raw index — so a disabled option is neither announced nor committable.
|
|
95
|
+
const resolvedActiveIndex = filtered.length === 0
|
|
96
|
+
? -1
|
|
97
|
+
: filtered[activeIndex] !== undefined &&
|
|
98
|
+
!filtered[activeIndex]?.isDisabled
|
|
99
|
+
? activeIndex
|
|
100
|
+
: nextEnabledIndex(filtered, -1, 1);
|
|
101
|
+
const activeValue = resolvedActiveIndex < 0
|
|
102
|
+
? undefined
|
|
103
|
+
: filtered[resolvedActiveIndex]?.value;
|
|
104
|
+
const commitValue = (value) => {
|
|
105
|
+
if (isMultiple) {
|
|
106
|
+
setSelected((previous) => previous.includes(value)
|
|
107
|
+
? previous.filter((one) => one !== value)
|
|
108
|
+
: [...previous, value]);
|
|
109
|
+
onSelect(value);
|
|
110
|
+
// Multi-select stays open and clears the query for the next one.
|
|
111
|
+
setQuery("");
|
|
112
|
+
inputElement.current?.focus();
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
setSelected([value]);
|
|
116
|
+
onSelect(value);
|
|
117
|
+
onDismiss();
|
|
118
|
+
};
|
|
119
|
+
const handleKeyDown = (keyEvent) => {
|
|
120
|
+
if (keyEvent.key === "ArrowDown") {
|
|
121
|
+
keyEvent.preventDefault();
|
|
122
|
+
setActiveIndex(nextEnabledIndex(filtered, resolvedActiveIndex, 1));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (keyEvent.key === "ArrowUp") {
|
|
126
|
+
keyEvent.preventDefault();
|
|
127
|
+
setActiveIndex(nextEnabledIndex(filtered, resolvedActiveIndex, -1));
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if (keyEvent.key === "Enter" ||
|
|
131
|
+
// Tab-to-accept (`PathPicker`): the active suggestion is taken.
|
|
132
|
+
// Single-select only — in multi-select `commitValue` refocuses the
|
|
133
|
+
// input, so accepting on Tab would trap forward focus in the field.
|
|
134
|
+
(keyEvent.key === "Tab" &&
|
|
135
|
+
!keyEvent.shiftKey &&
|
|
136
|
+
!isMultiple &&
|
|
137
|
+
activeValue !== undefined)) {
|
|
138
|
+
if (activeValue !== undefined) {
|
|
139
|
+
keyEvent.preventDefault();
|
|
140
|
+
commitValue(activeValue);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
// Creatable: Enter on a query with no match commits it raw.
|
|
144
|
+
if (isCreatable &&
|
|
145
|
+
keyEvent.key === "Enter" &&
|
|
146
|
+
currentQuery.trim().length > 0) {
|
|
147
|
+
keyEvent.preventDefault();
|
|
148
|
+
commitValue(currentQuery.trim());
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (keyEvent.key === "Escape") {
|
|
153
|
+
// Clears the query first, closes only when it is already empty.
|
|
154
|
+
if (currentQuery.length > 0) {
|
|
155
|
+
keyEvent.preventDefault();
|
|
156
|
+
setQuery("");
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
onDismiss();
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
// Keep the active row in view — a real bug in every fleet picker:
|
|
163
|
+
// arrowing past the panel edge did not scroll.
|
|
164
|
+
useEffect(() => {
|
|
165
|
+
if (!isVisible) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (isWindowed) {
|
|
169
|
+
rowVirtualizer.scrollToIndex(resolvedActiveIndex);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (activeValue !== undefined) {
|
|
173
|
+
listElement.current
|
|
174
|
+
?.querySelector(`#${CSS.escape(`${listboxId}-opt-${activeValue}`)}`)
|
|
175
|
+
?.scrollIntoView({ block: "nearest" });
|
|
176
|
+
}
|
|
177
|
+
}, [
|
|
178
|
+
resolvedActiveIndex,
|
|
179
|
+
activeValue,
|
|
180
|
+
isVisible,
|
|
181
|
+
isWindowed,
|
|
182
|
+
listboxId,
|
|
183
|
+
rowVirtualizer.scrollToIndex,
|
|
184
|
+
]);
|
|
185
|
+
const renderOption = (option, index) => (_jsx(ComboboxOption, { id: optionDomId(option.value), isActive: index === resolvedActiveIndex, isSelected: selected.includes(option.value), item: option, onSelect: commitValue, posInSet: isWindowed ? index + 1 : undefined, setSize: isWindowed ? filtered.length : undefined }, option.value));
|
|
186
|
+
return (_jsxs(_Fragment, { children: [clonedTrigger, isVisible ? (_jsx(FloatingPortal, { children: _jsx(FloatingFocusManager, { context: context, initialFocus: inputElement, modal: false, children: _jsxs("div", { ...floatingProps, className: toClassName(PANEL_SURFACE_CLASS, "z-[var(--layer-modal)] flex min-w-64 flex-col overflow-hidden", className), ref: setFloating, style: floatingStyles, children: [_jsxs("div", { className: "flex flex-wrap items-center gap-1 border-border-subtle border-b p-2", children: [isMultiple
|
|
187
|
+
? selected.map((value) => {
|
|
188
|
+
const chip = options.find((option) => option.value === value);
|
|
189
|
+
return (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-sm bg-intent-neutral-surface px-1.5 py-0.5 text-content-primary text-xs", children: [chip?.textValue ?? value, _jsx("button", { "aria-label": `Remove ${chip?.textValue ?? value}`, className: "cursor-pointer text-content-secondary hover:text-content-primary", onClick: () => {
|
|
190
|
+
setSelected((previous) => previous.filter((one) => one !== value));
|
|
191
|
+
// Mirror the list toggle-off: the parent
|
|
192
|
+
// tracks selection through `onSelect`, so a
|
|
193
|
+
// chip removal must report the same value.
|
|
194
|
+
onSelect(value);
|
|
195
|
+
}, type: "button", children: "\u2715" })] }, value));
|
|
196
|
+
})
|
|
197
|
+
: null, _jsx("input", { "aria-activedescendant": activeValue === undefined
|
|
198
|
+
? undefined
|
|
199
|
+
: optionDomId(activeValue), "aria-autocomplete": "list", "aria-controls": listboxId, "aria-expanded": "true", "aria-labelledby": triggerId, className: "min-w-24 flex-1 bg-transparent text-content-primary text-sm outline-none placeholder:text-content-muted", onChange: (changeEvent) => {
|
|
200
|
+
setQuery(changeEvent.target.value);
|
|
201
|
+
}, onKeyDown: handleKeyDown, placeholder: placeholder, ref: inputElement, role: "combobox", type: "text", value: currentQuery })] }), _jsx("div", { "aria-labelledby": triggerId, className: "min-h-0 flex-1 overflow-auto p-1", id: listboxId, ref: listElement, role: "listbox", children: isLoading ? (_jsx("p", { className: "px-2 py-3 text-center text-content-secondary text-sm", children: "Loading\u2026" })) : error ? (_jsx("div", { className: "px-2 py-3 text-center text-intent-danger-content text-sm", children: error })) : filtered.length === 0 ? (_jsx("p", { className: "px-2 py-3 text-center text-content-secondary text-sm", children: emptyLabel })) : isWindowed ? (_jsx("div", { className: "relative w-full", style: {
|
|
202
|
+
height: `${rowVirtualizer.getTotalSize()}px`,
|
|
203
|
+
}, children: rowVirtualizer
|
|
204
|
+
.getVirtualItems()
|
|
205
|
+
.map((virtualRow) => {
|
|
206
|
+
const option = filtered[virtualRow.index];
|
|
207
|
+
if (!option) {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
return (_jsx("div", { className: "absolute start-0 top-0 w-full", style: {
|
|
211
|
+
height: `${virtualRow.size}px`,
|
|
212
|
+
transform: `translateY(${virtualRow.start}px)`,
|
|
213
|
+
}, children: renderOption(option, virtualRow.index) }, option.value));
|
|
214
|
+
}) })) : (filtered.map((option, index) => renderOption(option, index))) }), footer ? (_jsx("div", { className: "shrink-0 border-border-subtle border-t p-2 text-content-secondary text-xs", children: footer })) : null] }) }) })) : null] }));
|
|
215
|
+
};
|
|
216
|
+
//# sourceMappingURL=Combobox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Combobox.js","sourceRoot":"","sources":["../../src/Combobox/Combobox.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { ListboxItem } from "../Listbox/Listbox.tsx";
|
|
3
|
+
export type ComboboxOptionProps = {
|
|
4
|
+
/** The stable DOM id `aria-activedescendant` points at. */
|
|
5
|
+
id: string;
|
|
6
|
+
isActive: boolean;
|
|
7
|
+
isSelected: boolean;
|
|
8
|
+
item: ListboxItem;
|
|
9
|
+
onSelect: (value: string) => void;
|
|
10
|
+
/**
|
|
11
|
+
* `aria-posinset`/`aria-setsize`, required when the list is
|
|
12
|
+
* virtualized: most of it is not in the DOM, so a screen reader
|
|
13
|
+
* would otherwise announce "2 of 12" for a 4,000-item list.
|
|
14
|
+
*/
|
|
15
|
+
posInSet?: number;
|
|
16
|
+
setSize?: number;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* `role="option"` with **`tabIndex={-1}`**, and both halves matter.
|
|
20
|
+
* The APG combobox keeps focus in the text input and tracks the
|
|
21
|
+
* active option with `aria-activedescendant`, so no option is ever a
|
|
22
|
+
* tab stop — `tabIndex={-1}` is that. It is a `<button>` (which
|
|
23
|
+
* ARIA-in-HTML allows `role="option"` on, and `Listbox` uses too)
|
|
24
|
+
* rather than a `<div>`, so `onClick` carries its own keyboard route
|
|
25
|
+
* and the package keeps its no-Biome-suppression record.
|
|
26
|
+
*
|
|
27
|
+
* The one caveat is not to run `expectAgentDrivable` on an individual
|
|
28
|
+
* option: its roving-tabindex rule would (correctly) reject a native
|
|
29
|
+
* control with `tabIndex={-1}`, and a combobox legitimately has zero
|
|
30
|
+
* tab stops in its popup. Tests query the options with `getByRole`.
|
|
31
|
+
*
|
|
32
|
+
* "Active" is a visual state only — the option the arrow keys have
|
|
33
|
+
* moved to, highlighted because nothing here holds real focus.
|
|
34
|
+
*/
|
|
35
|
+
export declare const ComboboxOption: ({ id, isActive, isSelected, item, onSelect, posInSet, setSize, }: ComboboxOptionProps) => ReactNode;
|
|
36
|
+
//# sourceMappingURL=ComboboxOption.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ComboboxOption.d.ts","sourceRoot":"","sources":["../../src/Combobox/ComboboxOption.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAGzD,MAAM,MAAM,mBAAmB,GAAG;IAChC,2DAA2D;IAC3D,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,OAAO,CAAA;IACjB,UAAU,EAAE,OAAO,CAAA;IACnB,IAAI,EAAE,WAAW,CAAA;IACjB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,cAAc,GAAI,kEAQ5B,mBAAmB,KAAG,SAyCxB,CAAA"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { toClassName } from "../toClassName.js";
|
|
3
|
+
/**
|
|
4
|
+
* `role="option"` with **`tabIndex={-1}`**, and both halves matter.
|
|
5
|
+
* The APG combobox keeps focus in the text input and tracks the
|
|
6
|
+
* active option with `aria-activedescendant`, so no option is ever a
|
|
7
|
+
* tab stop — `tabIndex={-1}` is that. It is a `<button>` (which
|
|
8
|
+
* ARIA-in-HTML allows `role="option"` on, and `Listbox` uses too)
|
|
9
|
+
* rather than a `<div>`, so `onClick` carries its own keyboard route
|
|
10
|
+
* and the package keeps its no-Biome-suppression record.
|
|
11
|
+
*
|
|
12
|
+
* The one caveat is not to run `expectAgentDrivable` on an individual
|
|
13
|
+
* option: its roving-tabindex rule would (correctly) reject a native
|
|
14
|
+
* control with `tabIndex={-1}`, and a combobox legitimately has zero
|
|
15
|
+
* tab stops in its popup. Tests query the options with `getByRole`.
|
|
16
|
+
*
|
|
17
|
+
* "Active" is a visual state only — the option the arrow keys have
|
|
18
|
+
* moved to, highlighted because nothing here holds real focus.
|
|
19
|
+
*/
|
|
20
|
+
export const ComboboxOption = ({ id, isActive, isSelected, item, onSelect, posInSet, setSize, }) => {
|
|
21
|
+
const { isDisabled = false, label, value } = item;
|
|
22
|
+
return (_jsxs("button", { "aria-posinset": posInSet, "aria-selected": isSelected, "aria-setsize": setSize, className: toClassName("flex w-full cursor-pointer items-center justify-between gap-2 rounded-sm bg-transparent px-2 py-1.5 text-start text-content-primary text-sm", isActive && "bg-intent-neutral-surface", isSelected && "bg-intent-accent-surface", isDisabled &&
|
|
23
|
+
"cursor-not-allowed text-content-disabled"), disabled: isDisabled, id: id,
|
|
24
|
+
// `onMouseDown` rather than `onClick`: the input holds focus, and
|
|
25
|
+
// a click would blur it first. Prevented default keeps the caret
|
|
26
|
+
// in the input while the choice is made.
|
|
27
|
+
onMouseDown: (mouseEvent) => {
|
|
28
|
+
mouseEvent.preventDefault();
|
|
29
|
+
onSelect(value);
|
|
30
|
+
}, role: "option", tabIndex: -1, type: "button", children: [label, isSelected ? (_jsx("span", { "aria-hidden": "true", className: "shrink-0 text-intent-accent-content", children: "\u2713" })) : null] }));
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=ComboboxOption.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ComboboxOption.js","sourceRoot":"","sources":["../../src/Combobox/ComboboxOption.tsx"],"names":[],"mappings":";AAGA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAkB/C;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,EAC7B,EAAE,EACF,QAAQ,EACR,UAAU,EACV,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,OAAO,GACa,EAAa,EAAE;IACnC,MAAM,EAAE,UAAU,GAAG,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,IAAI,CAAA;IAEjD,OAAO,CACL,mCACiB,QAAQ,mBACR,UAAU,kBACX,OAAO,EACrB,SAAS,EAAE,WAAW,CACpB,6IAA6I,EAC7I,QAAQ,IAAI,2BAA2B,EACvC,UAAU,IAAI,0BAA0B,EACxC,UAAU;YACR,0CAA0C,CAC7C,EACD,QAAQ,EAAE,UAAU,EACpB,EAAE,EAAE,EAAE;QACN,kEAAkE;QAClE,iEAAiE;QACjE,yCAAyC;QACzC,WAAW,EAAE,CAAC,UAAU,EAAE,EAAE;YAC1B,UAAU,CAAC,cAAc,EAAE,CAAA;YAE3B,QAAQ,CAAC,KAAK,CAAC,CAAA;QACjB,CAAC,EACD,IAAI,EAAC,QAAQ,EACb,QAAQ,EAAE,CAAC,CAAC,EACZ,IAAI,EAAC,QAAQ,aAEZ,KAAK,EAEL,UAAU,CAAC,CAAC,CAAC,CACZ,8BACc,MAAM,EAClB,SAAS,EAAC,qCAAqC,uBAG1C,CACR,CAAC,CAAC,CAAC,IAAI,IACD,CACV,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export type DialogSize = "full" | "lg" | "md" | "sm" | "xl";
|
|
3
|
+
export type DialogProps = {
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
className?: string;
|
|
6
|
+
/** The footer row. Buttons, usually. */
|
|
7
|
+
footer?: ReactNode;
|
|
8
|
+
heading: string;
|
|
9
|
+
headingLevel?: 2 | 3 | 4;
|
|
10
|
+
/**
|
|
11
|
+
* Escape and a backdrop press both close. Off for the one honest
|
|
12
|
+
* case — a destructive confirm the user has to answer.
|
|
13
|
+
*/
|
|
14
|
+
isDismissable?: boolean;
|
|
15
|
+
isVisible: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Every route out lands here: the close button, Escape, a backdrop
|
|
18
|
+
* press. Wire it to a `useVisibility()`'s `hide`.
|
|
19
|
+
*/
|
|
20
|
+
onClose: () => void;
|
|
21
|
+
size?: DialogSize;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* `Modal` plus the chrome — heading, a Close button, a scrolling body,
|
|
25
|
+
* an optional footer, and five sizes. This is what `Modal` *was*
|
|
26
|
+
* before the M8 rebuild renamed it: a dialog is one kind of modal, so
|
|
27
|
+
* it takes the specific name and hands the generic one back to the
|
|
28
|
+
* base layer.
|
|
29
|
+
*
|
|
30
|
+
* Everything modal — the portal, the scrim, Escape and outside-press
|
|
31
|
+
* dismiss, the focus trap, the scroll lock, the stacking — is `Modal`'s
|
|
32
|
+
* (via `OverlayPanel`), so this component is just the box's contents
|
|
33
|
+
* and the size class it wants that box to be. `SIZE_CLASS` is composed
|
|
34
|
+
* over `Modal`'s default surface, which is why `full` carries
|
|
35
|
+
* `rounded-none` to square off the base's `rounded-lg` at the viewport
|
|
36
|
+
* edge.
|
|
37
|
+
*
|
|
38
|
+
* ### The accessible name
|
|
39
|
+
*
|
|
40
|
+
* `heading` is required and is a string, not a `ReactNode`. It names
|
|
41
|
+
* both the visible header and — through `aria-labelledby` on the
|
|
42
|
+
* modal — the dialog itself. A dialog with no name is announced as
|
|
43
|
+
* "dialog" and nothing else, which is what every hand-rolled modal in
|
|
44
|
+
* the inventory does.
|
|
45
|
+
*/
|
|
46
|
+
export declare const Dialog: ({ children, className, footer, heading, headingLevel, isDismissable, isVisible, onClose, size, }: DialogProps) => ReactNode;
|
|
47
|
+
//# sourceMappingURL=Dialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Dialog.d.ts","sourceRoot":"","sources":["../../src/Dialog/Dialog.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAMtC,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAE3D,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,EAAE,SAAS,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,wCAAwC;IACxC,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACxB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,SAAS,EAAE,OAAO,CAAA;IAClB;;;OAGG;IACH,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,IAAI,CAAC,EAAE,UAAU,CAAA;CAClB,CAAA;AAqBD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,MAAM,GAAI,kGAUpB,WAAW,KAAG,SA4ChB,CAAA"}
|