@yarcl/react 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -2
- package/{src/apply.ts → dist/apply.d.ts} +4 -23
- package/dist/apply.js +37 -0
- package/dist/classes.d.ts +13 -0
- package/dist/classes.js +22 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +58 -0
- package/dist/color.js +49 -32
- package/dist/components/Accordion.d.ts +96 -0
- package/dist/components/Accordion.js +148 -0
- package/dist/components/Alert.d.ts +60 -0
- package/dist/components/Alert.js +61 -0
- package/dist/components/Avatar.d.ts +100 -0
- package/dist/components/Avatar.js +89 -0
- package/dist/components/Badge.d.ts +44 -0
- package/dist/components/Badge.js +43 -0
- package/dist/components/Breadcrumb.d.ts +77 -0
- package/dist/components/Breadcrumb.js +126 -0
- package/dist/components/Button.d.ts +18 -0
- package/dist/components/Button.js +33 -0
- package/dist/components/ButtonGroup.d.ts +29 -0
- package/dist/components/ButtonGroup.js +37 -0
- package/dist/components/Card.d.ts +36 -0
- package/dist/components/Card.js +26 -0
- package/dist/components/Checkbox.d.ts +28 -0
- package/dist/components/Checkbox.js +38 -0
- package/dist/components/Combobox.d.ts +75 -0
- package/dist/components/Combobox.js +362 -0
- package/dist/components/CommandPalette.d.ts +92 -0
- package/dist/components/CommandPalette.js +293 -0
- package/dist/components/DatePicker.d.ts +157 -0
- package/dist/components/DatePicker.js +230 -0
- package/dist/components/Dialog.d.ts +25 -0
- package/dist/components/Dialog.js +29 -0
- package/dist/components/Divider.d.ts +23 -0
- package/dist/components/Divider.js +25 -0
- package/dist/components/Drawer.d.ts +21 -0
- package/dist/components/Drawer.js +25 -0
- package/dist/components/EmptyState.d.ts +51 -0
- package/dist/components/EmptyState.js +47 -0
- package/dist/components/Field.d.ts +27 -0
- package/dist/components/Field.js +59 -0
- package/dist/components/Heading.d.ts +26 -0
- package/dist/components/Heading.js +25 -0
- package/dist/components/HoverCard.d.ts +47 -0
- package/dist/components/HoverCard.js +53 -0
- package/dist/components/IconButton.d.ts +19 -0
- package/dist/components/IconButton.js +32 -0
- package/dist/components/Inline.d.ts +21 -0
- package/dist/components/Inline.js +24 -0
- package/dist/components/Input.d.ts +19 -0
- package/dist/components/Input.js +25 -0
- package/dist/components/Label.d.ts +46 -0
- package/dist/components/Label.js +33 -0
- package/dist/components/Link.d.ts +27 -0
- package/dist/components/Link.js +26 -0
- package/dist/components/Menu.d.ts +73 -0
- package/dist/components/Menu.js +143 -0
- package/dist/components/Modal.d.ts +45 -0
- package/dist/components/Modal.js +110 -0
- package/dist/components/NumberInput.d.ts +49 -0
- package/dist/components/NumberInput.js +130 -0
- package/dist/components/Pagination.d.ts +86 -0
- package/dist/components/Pagination.js +119 -0
- package/dist/components/Popover.d.ts +72 -0
- package/dist/components/Popover.js +87 -0
- package/dist/components/Progress.d.ts +60 -0
- package/dist/components/Progress.js +59 -0
- package/dist/components/Radio.d.ts +32 -0
- package/dist/components/Radio.js +47 -0
- package/dist/components/RadioGroup.d.ts +57 -0
- package/dist/components/RadioGroup.js +68 -0
- package/dist/components/Select.d.ts +47 -0
- package/dist/components/Select.js +138 -0
- package/dist/components/Skeleton.d.ts +46 -0
- package/dist/components/Skeleton.js +40 -0
- package/dist/components/Slider.d.ts +126 -0
- package/dist/components/Slider.js +166 -0
- package/dist/components/Spinner.d.ts +23 -0
- package/dist/components/Spinner.js +23 -0
- package/dist/components/Stack.d.ts +35 -0
- package/dist/components/Stack.js +24 -0
- package/dist/components/Switch.d.ts +27 -0
- package/dist/components/Switch.js +33 -0
- package/dist/components/Table.d.ts +71 -0
- package/dist/components/Table.js +61 -0
- package/dist/components/Tabs.d.ts +66 -0
- package/dist/components/Tabs.js +109 -0
- package/dist/components/Text.d.ts +32 -0
- package/dist/components/Text.js +28 -0
- package/dist/components/Textarea.d.ts +15 -0
- package/dist/components/Textarea.js +24 -0
- package/dist/components/Toast.d.ts +90 -0
- package/dist/components/Toast.js +156 -0
- package/dist/components/ToggleGroup.d.ts +79 -0
- package/dist/components/ToggleGroup.js +101 -0
- package/dist/components/Tooltip.d.ts +46 -0
- package/dist/components/Tooltip.js +62 -0
- package/dist/components/VisuallyHidden.d.ts +18 -0
- package/dist/components/VisuallyHidden.js +22 -0
- package/dist/contrast.d.ts +32 -0
- package/dist/css.d.ts +2 -2
- package/dist/css.js +193 -157
- package/dist/define.d.ts +40 -15
- package/dist/define.js +30 -27
- package/dist/demo.d.ts +2 -0
- package/dist/demo.js +2 -0
- package/dist/esbuild.cjs +5 -0
- package/dist/esbuild.d.ts +4 -0
- package/dist/esbuild.js +6 -0
- package/dist/field-context.d.ts +16 -0
- package/dist/field-context.js +17 -0
- package/dist/floating.d.ts +12 -0
- package/dist/floating.js +52 -0
- package/dist/hooks.d.ts +1 -0
- package/dist/hooks.js +12 -0
- package/{src/index.ts → dist/index.d.ts} +11 -33
- package/dist/index.js +50 -0
- package/dist/init.d.ts +18 -0
- package/dist/init.js +224 -0
- package/dist/plugin.d.ts +10 -26
- package/dist/plugin.js +152 -78
- package/dist/reference/DemoYarcl.d.ts +12 -0
- package/dist/reference/DemoYarcl.js +312 -0
- package/dist/reference/DesignReference.d.ts +24 -0
- package/dist/reference/DesignReference.js +403 -0
- package/dist/reference/index.js +2 -0
- package/dist/rollup.cjs +5 -0
- package/dist/rollup.d.ts +4 -0
- package/dist/rollup.js +6 -0
- package/dist/rspack.cjs +5 -0
- package/dist/rspack.d.ts +4 -0
- package/dist/rspack.js +6 -0
- package/dist/runtime.d.ts +13 -0
- package/dist/runtime.js +30 -0
- package/dist/setup.d.ts +6 -0
- package/dist/setup.js +33 -0
- package/{src → dist}/styles.css +224 -106
- package/dist/themes/bloom.d.ts +295 -0
- package/dist/themes/bloom.js +202 -0
- package/dist/themes/brutalist.d.ts +277 -0
- package/dist/themes/brutalist.js +199 -0
- package/dist/themes/compact.d.ts +274 -0
- package/dist/themes/compact.js +200 -0
- package/dist/themes/contract.d.ts +23 -0
- package/dist/themes/editorial.d.ts +277 -0
- package/dist/themes/editorial.js +183 -0
- package/dist/themes/index.d.ts +1425 -0
- package/dist/themes/index.js +45 -0
- package/{src/types.ts → dist/types.d.ts} +40 -37
- package/dist/vite.cjs +5 -0
- package/dist/vite.d.ts +4 -0
- package/dist/vite.js +6 -0
- package/dist/webpack.cjs +5 -0
- package/dist/webpack.d.ts +4 -0
- package/dist/webpack.js +6 -0
- package/dist/yarcl.config.d.ts +276 -0
- package/dist/yarcl.config.js +273 -0
- package/package.json +69 -17
- package/src/classes.ts +0 -23
- package/src/color.ts +0 -48
- package/src/components/Accordion.tsx +0 -274
- package/src/components/Alert.tsx +0 -85
- package/src/components/Badge.tsx +0 -79
- package/src/components/Breadcrumb.tsx +0 -239
- package/src/components/Button.tsx +0 -58
- package/src/components/ButtonGroup.tsx +0 -61
- package/src/components/Card.tsx +0 -50
- package/src/components/Checkbox.tsx +0 -49
- package/src/components/Combobox.tsx +0 -522
- package/src/components/CommandPalette.tsx +0 -452
- package/src/components/DatePicker.tsx +0 -432
- package/src/components/Dialog.tsx +0 -39
- package/src/components/Divider.tsx +0 -34
- package/src/components/Drawer.tsx +0 -31
- package/src/components/Field.tsx +0 -61
- package/src/components/Heading.tsx +0 -40
- package/src/components/HoverCard.tsx +0 -108
- package/src/components/IconButton.tsx +0 -59
- package/src/components/Inline.tsx +0 -42
- package/src/components/Input.tsx +0 -28
- package/src/components/Label.tsx +0 -58
- package/src/components/Link.tsx +0 -41
- package/src/components/Menu.tsx +0 -207
- package/src/components/Modal.tsx +0 -164
- package/src/components/NumberInput.tsx +0 -208
- package/src/components/Pagination.tsx +0 -227
- package/src/components/Popover.tsx +0 -140
- package/src/components/Progress.tsx +0 -122
- package/src/components/Radio.tsx +0 -59
- package/src/components/RadioGroup.tsx +0 -114
- package/src/components/Select.tsx +0 -197
- package/src/components/Skeleton.tsx +0 -95
- package/src/components/Slider.tsx +0 -315
- package/src/components/Spinner.tsx +0 -38
- package/src/components/Stack.tsx +0 -57
- package/src/components/Switch.tsx +0 -42
- package/src/components/Table.tsx +0 -99
- package/src/components/Tabs.tsx +0 -159
- package/src/components/Text.tsx +0 -67
- package/src/components/Textarea.tsx +0 -26
- package/src/components/Toast.tsx +0 -190
- package/src/components/ToggleGroup.tsx +0 -227
- package/src/components/Tooltip.tsx +0 -78
- package/src/contrast.ts +0 -82
- package/src/css.ts +0 -228
- package/src/define.check.ts +0 -259
- package/src/define.ts +0 -379
- package/src/field-context.ts +0 -31
- package/src/floating.tsx +0 -61
- package/src/hooks.ts +0 -19
- package/src/plugin.ts +0 -94
- package/src/reference/DesignReference.tsx +0 -374
- package/src/runtime.ts +0 -38
- package/src/themes/bloom.ts +0 -72
- package/src/themes/brutalist.ts +0 -63
- package/src/themes/compact.ts +0 -63
- package/src/themes/contract.ts +0 -24
- package/src/themes/editorial.ts +0 -55
- package/src/themes/index.ts +0 -47
- package/src/yarcl.config.ts +0 -104
- /package/{src/reference/index.ts → dist/reference/index.d.ts} +0 -0
- /package/{src → dist}/reference/reference.css +0 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ComponentProps } from 'react';
|
|
2
|
+
import type { Radius, Shadow, Spacing } from '../types';
|
|
3
|
+
/** Props for {@link Card}. */
|
|
4
|
+
export interface CardProps extends ComponentProps<'div'> {
|
|
5
|
+
/**
|
|
6
|
+
* Element to render.
|
|
7
|
+
* @default 'div'
|
|
8
|
+
*/
|
|
9
|
+
as?: 'div' | 'section' | 'article' | 'aside';
|
|
10
|
+
/**
|
|
11
|
+
* Inner padding, from the `spacing` config.
|
|
12
|
+
* @default config.defaults.padding
|
|
13
|
+
*/
|
|
14
|
+
padding?: Spacing;
|
|
15
|
+
/**
|
|
16
|
+
* Corner radius, from the `radii` config.
|
|
17
|
+
* @default config.defaults.radius
|
|
18
|
+
*/
|
|
19
|
+
radius?: Radius;
|
|
20
|
+
/** Elevation, from the `shadows` config. No shadow when omitted. */
|
|
21
|
+
shadow?: Shadow;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A bordered surface for grouping content.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```tsx
|
|
28
|
+
* <Card shadow="md">
|
|
29
|
+
* <Stack>
|
|
30
|
+
* <Heading level={3}>Plan</Heading>
|
|
31
|
+
* <Text muted>Pro, billed yearly</Text>
|
|
32
|
+
* </Stack>
|
|
33
|
+
* </Card>
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare function Card({ as, padding, radius, shadow, className, ...props }: CardProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { useDefaults } from "../runtime.js";
|
|
2
|
+
import { cx, paddingClass, radiusClass, shadowClass } from "../classes.js";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
//#region src/components/Card.tsx
|
|
5
|
+
/**
|
|
6
|
+
* A bordered surface for grouping content.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```tsx
|
|
10
|
+
* <Card shadow="md">
|
|
11
|
+
* <Stack>
|
|
12
|
+
* <Heading level={3}>Plan</Heading>
|
|
13
|
+
* <Text muted>Pro, billed yearly</Text>
|
|
14
|
+
* </Stack>
|
|
15
|
+
* </Card>
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
function Card({ as = "div", padding, radius, shadow, className, ...props }) {
|
|
19
|
+
const own = useDefaults("Card");
|
|
20
|
+
return /* @__PURE__ */ jsx(as, {
|
|
21
|
+
className: cx("yarcl-card", paddingClass(padding ?? own.padding), radiusClass(radius ?? own.radius), shadowClass(shadow ?? own.shadow), className),
|
|
22
|
+
...props
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
export { Card };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type ComponentProps, type ReactNode } from 'react';
|
|
2
|
+
import type { Color, ComponentSize } from '../types';
|
|
3
|
+
/** Props for {@link Checkbox}. Accepts all native `<input>` attributes except `type`, `color` and `size`. */
|
|
4
|
+
export interface CheckboxProps extends Omit<ComponentProps<'input'>, 'type' | 'color' | 'size' | 'children'> {
|
|
5
|
+
/** Label shown next to the box. */
|
|
6
|
+
children?: ReactNode;
|
|
7
|
+
/**
|
|
8
|
+
* Box size, from the `sizes` config (uses the size's `iconSize`).
|
|
9
|
+
* @default config.defaults.size
|
|
10
|
+
*/
|
|
11
|
+
size?: ComponentSize<'Checkbox'>;
|
|
12
|
+
/**
|
|
13
|
+
* Color of the checked box, from the `colors` config.
|
|
14
|
+
* @default config.defaults.color
|
|
15
|
+
*/
|
|
16
|
+
color?: Color;
|
|
17
|
+
/** Shows a mixed state. Cleared by the browser when the user toggles the box. */
|
|
18
|
+
indeterminate?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* A checkbox with an optional label.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```tsx
|
|
25
|
+
* <Checkbox defaultChecked>Remember me</Checkbox>
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare function Checkbox(props: CheckboxProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useDefaults } from "../runtime.js";
|
|
2
|
+
import { colorClass, cx, sizeClass } from "../classes.js";
|
|
3
|
+
import { useFieldProps } from "../field-context.js";
|
|
4
|
+
import { useEffect, useImperativeHandle, useRef } from "react";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
//#region src/components/Checkbox.tsx
|
|
7
|
+
/**
|
|
8
|
+
* A checkbox with an optional label.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```tsx
|
|
12
|
+
* <Checkbox defaultChecked>Remember me</Checkbox>
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
function Checkbox(props) {
|
|
16
|
+
const own = useDefaults("Checkbox");
|
|
17
|
+
const { children, size, color, indeterminate = false, className, style, ref, ...rest } = useFieldProps(props);
|
|
18
|
+
const inner = useRef(null);
|
|
19
|
+
useImperativeHandle(ref, () => inner.current, []);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (inner.current) inner.current.indeterminate = indeterminate;
|
|
22
|
+
}, [indeterminate]);
|
|
23
|
+
return /* @__PURE__ */ jsxs("label", {
|
|
24
|
+
className: cx("yarcl-checkbox", sizeClass(size ?? own.size, "Checkbox"), colorClass(color ?? own.color), className),
|
|
25
|
+
style,
|
|
26
|
+
children: [/* @__PURE__ */ jsx("input", {
|
|
27
|
+
ref: inner,
|
|
28
|
+
type: "checkbox",
|
|
29
|
+
className: "yarcl-checkbox-input",
|
|
30
|
+
...rest
|
|
31
|
+
}), children != null && /* @__PURE__ */ jsx("span", {
|
|
32
|
+
className: "yarcl-checkbox-label",
|
|
33
|
+
children
|
|
34
|
+
})]
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { Checkbox };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { type ComponentProps, type ReactNode } from 'react';
|
|
2
|
+
import type { TokenProps } from '../types';
|
|
3
|
+
import type { SelectOption } from './Select';
|
|
4
|
+
/** Props shared by both forms of {@link ComboboxProps}. */
|
|
5
|
+
export interface ComboboxBaseProps<V extends string = string> extends TokenProps<'Combobox'>, Omit<ComponentProps<'input'>, 'color' | 'size' | 'value' | 'defaultValue' | 'onChange' | 'children' | 'multiple'> {
|
|
6
|
+
/** The options to suggest. For async search, update this as results arrive and set `filter={false}`. */
|
|
7
|
+
options: readonly SelectOption<V>[];
|
|
8
|
+
/** Controlled text in the input. */
|
|
9
|
+
inputValue?: string;
|
|
10
|
+
/** Called on every keystroke with the input text. Use it to fetch options for async search. */
|
|
11
|
+
onInputValueChange?: (text: string) => void;
|
|
12
|
+
/**
|
|
13
|
+
* How options are matched against the typed text.
|
|
14
|
+
* `true`: case-insensitive "contains" on the label; a function for custom matching;
|
|
15
|
+
* `false` to show `options` as given (e.g. already filtered by a server).
|
|
16
|
+
* @default true
|
|
17
|
+
*/
|
|
18
|
+
filter?: boolean | ((option: SelectOption<V>, text: string) => boolean);
|
|
19
|
+
/**
|
|
20
|
+
* Accepts typed text that doesn't match an option (typeahead / autocomplete).
|
|
21
|
+
* When `false`, unchosen text is discarded on blur: the input resets to the selected option's label,
|
|
22
|
+
* or clears with `multiple`. With `multiple`, Enter adds the typed text as a chip.
|
|
23
|
+
* @default false
|
|
24
|
+
*/
|
|
25
|
+
allowCustomValue?: boolean;
|
|
26
|
+
/** Shows a loading row instead of options. */
|
|
27
|
+
loading?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Shown when no options match.
|
|
30
|
+
* @default 'No results'
|
|
31
|
+
*/
|
|
32
|
+
emptyMessage?: ReactNode;
|
|
33
|
+
/**
|
|
34
|
+
* Shown while `loading` is set.
|
|
35
|
+
* @default 'Loading…'
|
|
36
|
+
*/
|
|
37
|
+
loadingMessage?: ReactNode;
|
|
38
|
+
}
|
|
39
|
+
/** Props for {@link Combobox}. `multiple` switches the value from one option to an array of them. */
|
|
40
|
+
export type ComboboxProps<V extends string = string> = ComboboxBaseProps<V> & ({
|
|
41
|
+
/** One value at a time. The input shows the selected option's label. */
|
|
42
|
+
multiple?: false;
|
|
43
|
+
/** Controlled selected value. `null` means nothing is selected. */
|
|
44
|
+
value?: V | null;
|
|
45
|
+
/** Initial selected value when uncontrolled. */
|
|
46
|
+
defaultValue?: V | null;
|
|
47
|
+
/** Called when an option is chosen, or with the typed text when `allowCustomValue` is set. */
|
|
48
|
+
onValueChange?: (value: V | null) => void;
|
|
49
|
+
maxSelected?: never;
|
|
50
|
+
} | {
|
|
51
|
+
/** Any number of values, shown as removable chips before the input. */
|
|
52
|
+
multiple: true;
|
|
53
|
+
/** Controlled selected values. */
|
|
54
|
+
value?: readonly V[];
|
|
55
|
+
/** Initial selected values when uncontrolled. */
|
|
56
|
+
defaultValue?: readonly V[];
|
|
57
|
+
/** Called with all selected values. */
|
|
58
|
+
onValueChange?: (value: V[]) => void;
|
|
59
|
+
/** The most values that can be selected. Other options are disabled once it is reached. */
|
|
60
|
+
maxSelected?: number;
|
|
61
|
+
});
|
|
62
|
+
/**
|
|
63
|
+
* A text input with a filtered list of suggestions.
|
|
64
|
+
* Covers searchable select (default), typeahead / autocomplete (`allowCustomValue`),
|
|
65
|
+
* async search (`filter={false}` + `onInputValueChange` + `loading`)
|
|
66
|
+
* and multi-select with removable chips (`multiple`).
|
|
67
|
+
* Shares the size scale with {@link Input}. Works inside a {@link Field}.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```tsx
|
|
71
|
+
* <Combobox placeholder="Country" options={countries} onValueChange={setCountry} />
|
|
72
|
+
* <Combobox multiple placeholder="Countries" options={countries} onValueChange={setCountries} />
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export declare function Combobox<V extends string = string>(props: ComboboxProps<V>): import("react").JSX.Element;
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import { useDefaults } from "../runtime.js";
|
|
2
|
+
import { colorClass, cx, radiusClass, sizeClass } from "../classes.js";
|
|
3
|
+
import { useControllable } from "../hooks.js";
|
|
4
|
+
import { useFieldProps } from "../field-context.js";
|
|
5
|
+
import { CheckIcon, floatingMiddleware } from "../floating.js";
|
|
6
|
+
import { Badge, BadgeRemove } from "./Badge.js";
|
|
7
|
+
import { useId, useRef, useState } from "react";
|
|
8
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
import { FloatingPortal, autoUpdate, useDismiss, useFloating, useInteractions, useListNavigation, useMergeRefs, useRole } from "@floating-ui/react";
|
|
10
|
+
//#region src/components/Combobox.tsx
|
|
11
|
+
var contains = (option, text) => option.label.toLowerCase().includes(text.trim().toLowerCase());
|
|
12
|
+
/**
|
|
13
|
+
* A text input with a filtered list of suggestions.
|
|
14
|
+
* Covers searchable select (default), typeahead / autocomplete (`allowCustomValue`),
|
|
15
|
+
* async search (`filter={false}` + `onInputValueChange` + `loading`)
|
|
16
|
+
* and multi-select with removable chips (`multiple`).
|
|
17
|
+
* Shares the size scale with {@link Input}. Works inside a {@link Field}.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```tsx
|
|
21
|
+
* <Combobox placeholder="Country" options={countries} onValueChange={setCountry} />
|
|
22
|
+
* <Combobox multiple placeholder="Countries" options={countries} onValueChange={setCountries} />
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
function Combobox(props) {
|
|
26
|
+
return props.multiple ? /* @__PURE__ */ jsx(MultipleCombobox, { ...props }) : /* @__PURE__ */ jsx(SingleCombobox, { ...props });
|
|
27
|
+
}
|
|
28
|
+
function useComboboxList(opts) {
|
|
29
|
+
const { options, text, filter, loading, isSelected, isDisabled, onChoose, containerRef } = opts;
|
|
30
|
+
const [open, setOpen] = useState(false);
|
|
31
|
+
const [activeIndex, setActiveIndex] = useState(null);
|
|
32
|
+
const listRef = useRef([]);
|
|
33
|
+
const idPrefix = useId();
|
|
34
|
+
const match = filter === true || filter === void 0 ? contains : filter === false ? () => true : filter;
|
|
35
|
+
const visible = options.filter((option) => match(option, text));
|
|
36
|
+
function close() {
|
|
37
|
+
setOpen(false);
|
|
38
|
+
setActiveIndex(null);
|
|
39
|
+
}
|
|
40
|
+
const { refs, floatingStyles, context } = useFloating({
|
|
41
|
+
open,
|
|
42
|
+
onOpenChange(next) {
|
|
43
|
+
setOpen(next);
|
|
44
|
+
if (!next) opts.onDismiss();
|
|
45
|
+
},
|
|
46
|
+
placement: "bottom-start",
|
|
47
|
+
transform: false,
|
|
48
|
+
whileElementsMounted: autoUpdate,
|
|
49
|
+
middleware: floatingMiddleware({
|
|
50
|
+
gap: 4,
|
|
51
|
+
matchWidth: true
|
|
52
|
+
})
|
|
53
|
+
});
|
|
54
|
+
const { getReferenceProps, getFloatingProps, getItemProps } = useInteractions([
|
|
55
|
+
useRole(context, { role: "combobox" }),
|
|
56
|
+
useDismiss(context, { outsidePress: (event) => !containerRef?.current?.contains(event.target) }),
|
|
57
|
+
useListNavigation(context, {
|
|
58
|
+
listRef,
|
|
59
|
+
activeIndex,
|
|
60
|
+
onNavigate: setActiveIndex,
|
|
61
|
+
virtual: true,
|
|
62
|
+
loop: true,
|
|
63
|
+
disabledIndices: visible.flatMap((option, i) => isDisabled(option) ? [i] : [])
|
|
64
|
+
})
|
|
65
|
+
]);
|
|
66
|
+
return {
|
|
67
|
+
open,
|
|
68
|
+
setOpen,
|
|
69
|
+
activeIndex,
|
|
70
|
+
setActiveIndex,
|
|
71
|
+
close,
|
|
72
|
+
visible,
|
|
73
|
+
match,
|
|
74
|
+
refs,
|
|
75
|
+
getReferenceProps,
|
|
76
|
+
list: open && (loading || visible.length > 0 || text.trim() !== "") && /* @__PURE__ */ jsx(FloatingPortal, { children: /* @__PURE__ */ jsx("div", {
|
|
77
|
+
ref: refs.setFloating,
|
|
78
|
+
style: floatingStyles,
|
|
79
|
+
className: cx("yarcl-floating yarcl-panel yarcl-listbox", sizeClass(opts.size, "Combobox"), colorClass(opts.color)),
|
|
80
|
+
...getFloatingProps({
|
|
81
|
+
"aria-multiselectable": opts.multiple || void 0,
|
|
82
|
+
onMouseDown: (event) => event.preventDefault()
|
|
83
|
+
}),
|
|
84
|
+
children: loading ? /* @__PURE__ */ jsx("div", {
|
|
85
|
+
className: "yarcl-listbox-message",
|
|
86
|
+
children: opts.loadingMessage
|
|
87
|
+
}) : visible.length === 0 ? /* @__PURE__ */ jsx("div", {
|
|
88
|
+
className: "yarcl-listbox-message",
|
|
89
|
+
children: opts.emptyMessage
|
|
90
|
+
}) : visible.map((option, i) => {
|
|
91
|
+
const isActive = i === activeIndex;
|
|
92
|
+
const selected = isSelected(option);
|
|
93
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
94
|
+
id: `${idPrefix}-${i}`,
|
|
95
|
+
ref: (node) => {
|
|
96
|
+
listRef.current[i] = node;
|
|
97
|
+
},
|
|
98
|
+
"aria-disabled": isDisabled(option) || void 0,
|
|
99
|
+
"data-active": isActive || void 0,
|
|
100
|
+
className: "yarcl-option",
|
|
101
|
+
...getItemProps({
|
|
102
|
+
active: isActive,
|
|
103
|
+
selected,
|
|
104
|
+
onClick: () => onChoose(option)
|
|
105
|
+
}),
|
|
106
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
107
|
+
className: "yarcl-option-label",
|
|
108
|
+
children: option.label
|
|
109
|
+
}), selected && /* @__PURE__ */ jsx(CheckIcon, {})]
|
|
110
|
+
}, option.value);
|
|
111
|
+
})
|
|
112
|
+
}) })
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function SingleCombobox(props) {
|
|
116
|
+
const own = useDefaults("Combobox");
|
|
117
|
+
const { options, value: valueProp, defaultValue = null, onValueChange, inputValue: inputValueProp, onInputValueChange, filter = true, allowCustomValue = false, loading = false, emptyMessage = "No results", loadingMessage = "Loading…", size, radius, color, className, onBlur, name, multiple: _multiple, maxSelected: _maxSelected, ...rest } = useFieldProps(props);
|
|
118
|
+
const [value, setValue] = useControllable(valueProp, defaultValue, onValueChange);
|
|
119
|
+
const labelOf = (v) => options.find((option) => option.value === v)?.label ?? (allowCustomValue ? v ?? "" : "");
|
|
120
|
+
const [text, setText] = useControllable(inputValueProp, labelOf(defaultValue), onInputValueChange);
|
|
121
|
+
function choose(option) {
|
|
122
|
+
if (option.disabled) return;
|
|
123
|
+
setValue(option.value);
|
|
124
|
+
setText(option.label);
|
|
125
|
+
close();
|
|
126
|
+
}
|
|
127
|
+
function restore() {
|
|
128
|
+
if (allowCustomValue) return;
|
|
129
|
+
const label = labelOf(value);
|
|
130
|
+
if (text !== label) setText(label);
|
|
131
|
+
}
|
|
132
|
+
const { open, setOpen, activeIndex, setActiveIndex, close, visible, refs, getReferenceProps, list } = useComboboxList({
|
|
133
|
+
options,
|
|
134
|
+
text,
|
|
135
|
+
filter,
|
|
136
|
+
loading,
|
|
137
|
+
emptyMessage,
|
|
138
|
+
loadingMessage,
|
|
139
|
+
size: size ?? own.size,
|
|
140
|
+
color: color ?? own.color,
|
|
141
|
+
multiple: false,
|
|
142
|
+
isSelected: (option) => option.value === value,
|
|
143
|
+
isDisabled: (option) => !!option.disabled,
|
|
144
|
+
onChoose: choose,
|
|
145
|
+
onDismiss: restore
|
|
146
|
+
});
|
|
147
|
+
function onChange(event) {
|
|
148
|
+
const next = event.target.value;
|
|
149
|
+
setText(next);
|
|
150
|
+
setOpen(true);
|
|
151
|
+
setActiveIndex(next ? 0 : null);
|
|
152
|
+
if (allowCustomValue) setValue(next ? next : null);
|
|
153
|
+
else if (!next) setValue(null);
|
|
154
|
+
}
|
|
155
|
+
function onKeyDown(event) {
|
|
156
|
+
if (event.key !== "Enter" || !open) return;
|
|
157
|
+
event.preventDefault();
|
|
158
|
+
const option = activeIndex != null ? visible[activeIndex] : void 0;
|
|
159
|
+
if (option) choose(option);
|
|
160
|
+
else close();
|
|
161
|
+
}
|
|
162
|
+
const referenceRef = useMergeRefs([refs.setReference, rest.ref]);
|
|
163
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
164
|
+
/* @__PURE__ */ jsx("input", {
|
|
165
|
+
ref: referenceRef,
|
|
166
|
+
className: cx("yarcl-input yarcl-combobox", sizeClass(size ?? own.size, "Combobox"), radiusClass(radius ?? own.radius, size ?? own.size), colorClass(color ?? own.color), className),
|
|
167
|
+
autoComplete: "off",
|
|
168
|
+
"aria-autocomplete": "list",
|
|
169
|
+
...getReferenceProps({
|
|
170
|
+
...rest,
|
|
171
|
+
value: text,
|
|
172
|
+
onChange,
|
|
173
|
+
onKeyDown,
|
|
174
|
+
onFocus: () => setOpen(true),
|
|
175
|
+
onClick: () => setOpen(true),
|
|
176
|
+
onBlur(event) {
|
|
177
|
+
restore();
|
|
178
|
+
onBlur?.(event);
|
|
179
|
+
}
|
|
180
|
+
})
|
|
181
|
+
}),
|
|
182
|
+
name && /* @__PURE__ */ jsx("input", {
|
|
183
|
+
type: "hidden",
|
|
184
|
+
name,
|
|
185
|
+
value: value ?? ""
|
|
186
|
+
}),
|
|
187
|
+
list
|
|
188
|
+
] });
|
|
189
|
+
}
|
|
190
|
+
function MultipleCombobox(props) {
|
|
191
|
+
const own = useDefaults("Combobox");
|
|
192
|
+
const { options, value: valueProp, defaultValue = [], onValueChange, maxSelected, inputValue: inputValueProp, onInputValueChange, filter = true, allowCustomValue = false, loading = false, emptyMessage = "No results", loadingMessage = "Loading…", size, radius, color, className, style, name, required, disabled, placeholder, onBlur, multiple: _multiple, ...rest } = useFieldProps(props);
|
|
193
|
+
const [values, setValues] = useControllable(valueProp, defaultValue, onValueChange);
|
|
194
|
+
const [text, setText] = useControllable(inputValueProp, "", onInputValueChange);
|
|
195
|
+
const controlRef = useRef(null);
|
|
196
|
+
const inputRef = useRef(null);
|
|
197
|
+
const chipsRef = useRef(null);
|
|
198
|
+
const knownLabels = useRef(/* @__PURE__ */ new Map());
|
|
199
|
+
const quietFocus = useRef(false);
|
|
200
|
+
const full = maxSelected != null && values.length >= maxSelected;
|
|
201
|
+
const labelOf = (v) => options.find((option) => option.value === v)?.label ?? knownLabels.current.get(v) ?? v;
|
|
202
|
+
function toggle(option) {
|
|
203
|
+
if (option.disabled) return;
|
|
204
|
+
const selected = values.includes(option.value);
|
|
205
|
+
if (!selected && full) return;
|
|
206
|
+
knownLabels.current.set(option.value, option.label);
|
|
207
|
+
setValues(selected ? values.filter((v) => v !== option.value) : [...values, option.value]);
|
|
208
|
+
if (text) {
|
|
209
|
+
setText("");
|
|
210
|
+
const index = options.filter((o) => match(o, "")).indexOf(option);
|
|
211
|
+
setActiveIndex(index >= 0 ? index : null);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function addCustom() {
|
|
215
|
+
const typed = text.trim();
|
|
216
|
+
if (!values.includes(typed) && !full) setValues([...values, typed]);
|
|
217
|
+
setText("");
|
|
218
|
+
setActiveIndex(null);
|
|
219
|
+
}
|
|
220
|
+
function focusInput() {
|
|
221
|
+
const input = inputRef.current;
|
|
222
|
+
if (!input) return;
|
|
223
|
+
if (document.activeElement !== input) quietFocus.current = true;
|
|
224
|
+
input.focus();
|
|
225
|
+
}
|
|
226
|
+
function remove(v) {
|
|
227
|
+
setValues(values.filter((x) => x !== v));
|
|
228
|
+
focusInput();
|
|
229
|
+
}
|
|
230
|
+
function restore() {
|
|
231
|
+
if (!allowCustomValue && text) setText("");
|
|
232
|
+
}
|
|
233
|
+
const { open, setOpen, activeIndex, setActiveIndex, close, visible, match, refs, getReferenceProps, list } = useComboboxList({
|
|
234
|
+
options,
|
|
235
|
+
text,
|
|
236
|
+
filter,
|
|
237
|
+
loading,
|
|
238
|
+
emptyMessage,
|
|
239
|
+
loadingMessage,
|
|
240
|
+
size: size ?? own.size,
|
|
241
|
+
color: color ?? own.color,
|
|
242
|
+
multiple: true,
|
|
243
|
+
isSelected: (option) => values.includes(option.value),
|
|
244
|
+
isDisabled: (option) => !!option.disabled || full && !values.includes(option.value),
|
|
245
|
+
onChoose: toggle,
|
|
246
|
+
onDismiss: () => {},
|
|
247
|
+
containerRef: controlRef
|
|
248
|
+
});
|
|
249
|
+
const controlRefs = useMergeRefs([controlRef, refs.setPositionReference]);
|
|
250
|
+
const inputRefs = useMergeRefs([
|
|
251
|
+
inputRef,
|
|
252
|
+
refs.setReference,
|
|
253
|
+
rest.ref
|
|
254
|
+
]);
|
|
255
|
+
const chipButtons = () => [...chipsRef.current?.querySelectorAll(".yarcl-badge-remove") ?? []];
|
|
256
|
+
function onChange(event) {
|
|
257
|
+
const next = event.target.value;
|
|
258
|
+
setText(next);
|
|
259
|
+
setOpen(true);
|
|
260
|
+
setActiveIndex(next ? 0 : null);
|
|
261
|
+
}
|
|
262
|
+
function onKeyDown(event) {
|
|
263
|
+
const input = event.currentTarget;
|
|
264
|
+
if (event.key === "Enter") {
|
|
265
|
+
const option = open && activeIndex != null ? visible[activeIndex] : void 0;
|
|
266
|
+
if (option) toggle(option);
|
|
267
|
+
else if (allowCustomValue && text.trim()) addCustom();
|
|
268
|
+
else if (open) close();
|
|
269
|
+
else return;
|
|
270
|
+
event.preventDefault();
|
|
271
|
+
} else if (event.key === "Backspace" && text === "" && values.length > 0) {
|
|
272
|
+
event.preventDefault();
|
|
273
|
+
setValues(values.slice(0, -1));
|
|
274
|
+
} else if (event.key === "ArrowLeft" && input.selectionStart === 0 && input.selectionEnd === 0 && values.length > 0) {
|
|
275
|
+
event.preventDefault();
|
|
276
|
+
close();
|
|
277
|
+
chipButtons().at(-1)?.focus();
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
function onChipKeyDown(event) {
|
|
281
|
+
const buttons = chipButtons();
|
|
282
|
+
const i = buttons.indexOf(event.target);
|
|
283
|
+
if (i < 0) return;
|
|
284
|
+
if (event.key === "ArrowLeft") buttons[Math.max(i - 1, 0)].focus();
|
|
285
|
+
else if (event.key === "ArrowRight") {
|
|
286
|
+
if (i === buttons.length - 1) focusInput();
|
|
287
|
+
else buttons[i + 1].focus();
|
|
288
|
+
} else if (event.key === "Backspace" || event.key === "Delete") remove(values[i]);
|
|
289
|
+
else if (event.key === "Escape") focusInput();
|
|
290
|
+
else return;
|
|
291
|
+
event.preventDefault();
|
|
292
|
+
}
|
|
293
|
+
function onControlMouseDown(event) {
|
|
294
|
+
if (disabled || event.target.closest("button, input")) return;
|
|
295
|
+
event.preventDefault();
|
|
296
|
+
inputRef.current?.focus();
|
|
297
|
+
setOpen(true);
|
|
298
|
+
}
|
|
299
|
+
const s = size ?? own.size;
|
|
300
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
|
|
301
|
+
ref: controlRefs,
|
|
302
|
+
className: cx("yarcl-input yarcl-combobox-control", sizeClass(s, "Combobox"), radiusClass(radius ?? own.radius, s), colorClass(color ?? own.color), className),
|
|
303
|
+
style,
|
|
304
|
+
onMouseDown: onControlMouseDown,
|
|
305
|
+
children: [
|
|
306
|
+
values.length > 0 && /* @__PURE__ */ jsx("ul", {
|
|
307
|
+
ref: chipsRef,
|
|
308
|
+
role: "list",
|
|
309
|
+
"aria-label": "Selected",
|
|
310
|
+
className: "yarcl-combobox-chips",
|
|
311
|
+
onKeyDown: onChipKeyDown,
|
|
312
|
+
children: values.map((v) => {
|
|
313
|
+
const label = labelOf(v);
|
|
314
|
+
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(Badge, {
|
|
315
|
+
size: s,
|
|
316
|
+
color: color ?? own.color,
|
|
317
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
318
|
+
className: "yarcl-combobox-chip-label",
|
|
319
|
+
children: label
|
|
320
|
+
}), /* @__PURE__ */ jsx(BadgeRemove, {
|
|
321
|
+
"aria-label": `Remove ${label}`,
|
|
322
|
+
tabIndex: -1,
|
|
323
|
+
disabled,
|
|
324
|
+
onClick: () => remove(v)
|
|
325
|
+
})]
|
|
326
|
+
}) }, v);
|
|
327
|
+
})
|
|
328
|
+
}),
|
|
329
|
+
/* @__PURE__ */ jsx("input", {
|
|
330
|
+
ref: inputRefs,
|
|
331
|
+
className: "yarcl-combobox-input",
|
|
332
|
+
autoComplete: "off",
|
|
333
|
+
"aria-autocomplete": "list",
|
|
334
|
+
...getReferenceProps({
|
|
335
|
+
...rest,
|
|
336
|
+
disabled,
|
|
337
|
+
required: required && values.length === 0,
|
|
338
|
+
placeholder: values.length > 0 ? void 0 : placeholder,
|
|
339
|
+
value: text,
|
|
340
|
+
onChange,
|
|
341
|
+
onKeyDown,
|
|
342
|
+
onFocus() {
|
|
343
|
+
if (quietFocus.current) quietFocus.current = false;
|
|
344
|
+
else setOpen(true);
|
|
345
|
+
},
|
|
346
|
+
onClick: () => setOpen(true),
|
|
347
|
+
onBlur(event) {
|
|
348
|
+
if (!controlRef.current?.contains(event.relatedTarget)) restore();
|
|
349
|
+
onBlur?.(event);
|
|
350
|
+
}
|
|
351
|
+
})
|
|
352
|
+
}),
|
|
353
|
+
name && values.map((v) => /* @__PURE__ */ jsx("input", {
|
|
354
|
+
type: "hidden",
|
|
355
|
+
name,
|
|
356
|
+
value: v
|
|
357
|
+
}, v))
|
|
358
|
+
]
|
|
359
|
+
}), list] });
|
|
360
|
+
}
|
|
361
|
+
//#endregion
|
|
362
|
+
export { Combobox };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { type ComponentProps, type MouseEvent, type ReactElement, type ReactNode } from 'react';
|
|
2
|
+
import type { TokenProps } from '../types';
|
|
3
|
+
/** A command shown in a {@link CommandPalette}. */
|
|
4
|
+
export interface CommandPaletteCommand {
|
|
5
|
+
/** Unique id of the command. */
|
|
6
|
+
id: string;
|
|
7
|
+
/** Text shown in the list and matched by the search. */
|
|
8
|
+
label: string;
|
|
9
|
+
/** Heading of the group the command is listed under. Groups appear in the order they're first used. */
|
|
10
|
+
group?: string;
|
|
11
|
+
/** Extra words the search matches, e.g. synonyms. */
|
|
12
|
+
keywords?: readonly string[];
|
|
13
|
+
/**
|
|
14
|
+
* Keyboard shortcut hint shown next to the label, e.g. `'Mod+S'`, or `'G D'` for a sequence.
|
|
15
|
+
* `Mod` is ⌘ on Apple devices and Ctrl elsewhere. Only a hint: bind the shortcut yourself.
|
|
16
|
+
*/
|
|
17
|
+
shortcut?: string;
|
|
18
|
+
/** An icon shown before the label. */
|
|
19
|
+
icon?: ReactNode;
|
|
20
|
+
/** Shows the command but prevents choosing it. */
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
/** Called when the command is chosen. The palette closes first. */
|
|
23
|
+
onSelect?: () => void;
|
|
24
|
+
}
|
|
25
|
+
/** Props for {@link CommandPalette}. */
|
|
26
|
+
export interface CommandPaletteProps extends TokenProps<'CommandPalette'>, Omit<ComponentProps<'dialog'>, 'color' | 'open' | 'onSelect' | 'children' | 'title'> {
|
|
27
|
+
/** The commands to search. Commands with the same `group` are listed together under a heading. */
|
|
28
|
+
commands: readonly CommandPaletteCommand[];
|
|
29
|
+
/** Controlled open state. */
|
|
30
|
+
open?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Initial open state when uncontrolled.
|
|
33
|
+
* @default false
|
|
34
|
+
*/
|
|
35
|
+
defaultOpen?: boolean;
|
|
36
|
+
/** Called when the palette opens or closes, including by the shortcut, Esc or a backdrop click. */
|
|
37
|
+
onOpenChange?: (open: boolean) => void;
|
|
38
|
+
/**
|
|
39
|
+
* Keyboard shortcut that toggles the palette from anywhere on the page, e.g. `'Mod+K'` or `'Shift+Mod+P'`.
|
|
40
|
+
* `Mod` is ⌘ on Apple devices and Ctrl elsewhere. `false` disables it.
|
|
41
|
+
* @default 'Mod+K'
|
|
42
|
+
*/
|
|
43
|
+
shortcut?: string | false;
|
|
44
|
+
/** An element that opens the palette when clicked, e.g. a {@link Button}. */
|
|
45
|
+
trigger?: ReactElement<{
|
|
46
|
+
onClick?: (event: MouseEvent) => void;
|
|
47
|
+
}>;
|
|
48
|
+
/** Called with the chosen command, after its own `onSelect`. */
|
|
49
|
+
onSelect?: (command: CommandPaletteCommand) => void;
|
|
50
|
+
/** Controlled search text. */
|
|
51
|
+
inputValue?: string;
|
|
52
|
+
/** Called on every keystroke with the search text. */
|
|
53
|
+
onInputValueChange?: (text: string) => void;
|
|
54
|
+
/**
|
|
55
|
+
* How commands are matched against the search text.
|
|
56
|
+
* `true`: every typed word appears in the label, group or keywords (case-insensitive);
|
|
57
|
+
* a function for custom matching; `false` to show `commands` as given.
|
|
58
|
+
* @default true
|
|
59
|
+
*/
|
|
60
|
+
filter?: boolean | ((command: CommandPaletteCommand, text: string) => boolean);
|
|
61
|
+
/**
|
|
62
|
+
* Placeholder of the search input.
|
|
63
|
+
* @default 'Search commands…'
|
|
64
|
+
*/
|
|
65
|
+
placeholder?: string;
|
|
66
|
+
/**
|
|
67
|
+
* Accessible name of the palette and its search input.
|
|
68
|
+
* @default 'Command palette'
|
|
69
|
+
*/
|
|
70
|
+
label?: string;
|
|
71
|
+
/**
|
|
72
|
+
* Shown when no commands match.
|
|
73
|
+
* @default 'No results'
|
|
74
|
+
*/
|
|
75
|
+
emptyMessage?: ReactNode;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* A searchable command menu in a modal dialog, opened with a keyboard shortcut (⌘K or Ctrl+K by default).
|
|
79
|
+
* The search input is a combobox: arrow keys, Home and End move through the matching commands,
|
|
80
|
+
* Enter runs the highlighted one, Esc closes the palette and returns focus.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```tsx
|
|
84
|
+
* <CommandPalette
|
|
85
|
+
* commands={[
|
|
86
|
+
* { id: 'new', label: 'New file', group: 'File', shortcut: 'Mod+N', onSelect: createFile },
|
|
87
|
+
* { id: 'theme', label: 'Toggle dark mode', group: 'View', keywords: ['theme'], onSelect: toggleTheme },
|
|
88
|
+
* ]}
|
|
89
|
+
* />
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export declare function CommandPalette({ commands, open: openProp, defaultOpen, onOpenChange, shortcut, trigger, onSelect, inputValue, onInputValueChange, filter, placeholder, label, emptyMessage, size, radius, color, className, onClick, onClose, ...props }: CommandPaletteProps): import("react").JSX.Element;
|