@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,46 @@
|
|
|
1
|
+
import type { ComponentProps } from 'react';
|
|
2
|
+
import type { Color, ComponentSize, Radius, TextStyle, Variant } from '../types';
|
|
3
|
+
/** Props for {@link Label}. */
|
|
4
|
+
export interface LabelProps extends Omit<ComponentProps<'label'>, 'color'> {
|
|
5
|
+
/**
|
|
6
|
+
* Text style, from the `typography.styles` config.
|
|
7
|
+
* @default config.defaults.labelStyle
|
|
8
|
+
*/
|
|
9
|
+
textStyle?: TextStyle;
|
|
10
|
+
/**
|
|
11
|
+
* Semantic color, from the `colors` config. Colors the label text; neutral when omitted.
|
|
12
|
+
*/
|
|
13
|
+
color?: Color;
|
|
14
|
+
/**
|
|
15
|
+
* Style recipe, from the `variants` config, for a filled label. Plain when omitted.
|
|
16
|
+
*/
|
|
17
|
+
variant?: Variant;
|
|
18
|
+
/**
|
|
19
|
+
* Font size scale, from the `sizes` config. Uses the text style's size when omitted.
|
|
20
|
+
*/
|
|
21
|
+
size?: ComponentSize<'Label'>;
|
|
22
|
+
/**
|
|
23
|
+
* Corner radius, from the `radii` config.
|
|
24
|
+
* @default config.defaults.radius
|
|
25
|
+
*/
|
|
26
|
+
radius?: Radius | 'size';
|
|
27
|
+
/**
|
|
28
|
+
* Shows a required marker in `defaults.errorColor`. The marker is hidden from screen readers,
|
|
29
|
+
* so also set `required` (or `aria-required`) on the control.
|
|
30
|
+
*/
|
|
31
|
+
required?: boolean;
|
|
32
|
+
/** Uses the muted color, to match a disabled control. */
|
|
33
|
+
disabled?: boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* A form label in the consumer's `defaults.labelStyle`, or a filled label with a variant. Connect
|
|
37
|
+
* it with `htmlFor`, or give it an `id` and point the control's `aria-labelledby` at it.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```tsx
|
|
41
|
+
* <Label htmlFor="email" required>Email</Label>
|
|
42
|
+
* <Input id="email" type="email" />
|
|
43
|
+
* <Label variant="soft" color="success">Active</Label>
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export declare function Label({ textStyle, color, variant, size, radius, required, disabled, className, children, ...props }: LabelProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useConfig, useDefaults } from "../runtime.js";
|
|
2
|
+
import { colorClass, cx, radiusClass, sizeClass, softVariantClass, typeClass } from "../classes.js";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
//#region src/components/Label.tsx
|
|
5
|
+
/**
|
|
6
|
+
* A form label in the consumer's `defaults.labelStyle`, or a filled label with a variant. Connect
|
|
7
|
+
* it with `htmlFor`, or give it an `id` and point the control's `aria-labelledby` at it.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* <Label htmlFor="email" required>Email</Label>
|
|
12
|
+
* <Input id="email" type="email" />
|
|
13
|
+
* <Label variant="soft" color="success">Active</Label>
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
function Label({ textStyle, color, variant, size, radius, required, disabled, className, children, ...props }) {
|
|
17
|
+
const config = useConfig();
|
|
18
|
+
const own = useDefaults("Label");
|
|
19
|
+
const resolvedSize = size ?? own.size;
|
|
20
|
+
const resolvedColor = color ?? own.color;
|
|
21
|
+
const resolvedVariant = variant ?? own.variant;
|
|
22
|
+
return /* @__PURE__ */ jsxs("label", {
|
|
23
|
+
className: cx("yarcl-label", typeClass(textStyle ?? own.textStyle ?? config.defaults.labelStyle), resolvedColor && cx("yarcl-label-colored", colorClass(resolvedColor)), resolvedVariant && cx("yarcl-label-variant", softVariantClass(resolvedVariant)), resolvedSize && cx("yarcl-label-sized", sizeClass(resolvedSize, "Label")), radiusClass(radius ?? own.radius, resolvedSize), disabled && "yarcl-label-disabled", className),
|
|
24
|
+
...props,
|
|
25
|
+
children: [children, required && /* @__PURE__ */ jsx("span", {
|
|
26
|
+
className: "yarcl-label-required",
|
|
27
|
+
"aria-hidden": "true",
|
|
28
|
+
children: " *"
|
|
29
|
+
})]
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { Label };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ComponentProps } from 'react';
|
|
2
|
+
import type { Color } from '../types';
|
|
3
|
+
/** Props for {@link Link}. Accepts all native `<a>` attributes except `color`. */
|
|
4
|
+
export interface LinkProps extends Omit<ComponentProps<'a'>, 'color'> {
|
|
5
|
+
/**
|
|
6
|
+
* Link color, from the `colors` config.
|
|
7
|
+
* @default config.defaults.color
|
|
8
|
+
*/
|
|
9
|
+
color?: Color;
|
|
10
|
+
/**
|
|
11
|
+
* When to underline. Keep `'always'` for links inside running text, so they don't rely on color alone.
|
|
12
|
+
* @default 'always'
|
|
13
|
+
*/
|
|
14
|
+
underline?: 'always' | 'hover' | 'none';
|
|
15
|
+
/** Opens in a new tab with `rel="noopener noreferrer"`. */
|
|
16
|
+
external?: boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A text link that inherits the surrounding text style.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```tsx
|
|
23
|
+
* <Link href="/docs">Read the docs</Link>
|
|
24
|
+
* <Link href="https://example.com" external>Example</Link>
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function Link({ color, underline, external, className, ...props }: LinkProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { useDefaults } from "../runtime.js";
|
|
2
|
+
import { colorClass, cx } from "../classes.js";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
//#region src/components/Link.tsx
|
|
5
|
+
/**
|
|
6
|
+
* A text link that inherits the surrounding text style.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```tsx
|
|
10
|
+
* <Link href="/docs">Read the docs</Link>
|
|
11
|
+
* <Link href="https://example.com" external>Example</Link>
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
function Link({ color, underline = "always", external, className, ...props }) {
|
|
15
|
+
const own = useDefaults("Link");
|
|
16
|
+
return /* @__PURE__ */ jsx("a", {
|
|
17
|
+
className: cx("yarcl-link", `yarcl-link-underline-${underline}`, colorClass(color ?? own.color), className),
|
|
18
|
+
...external && {
|
|
19
|
+
target: "_blank",
|
|
20
|
+
rel: "noopener noreferrer"
|
|
21
|
+
},
|
|
22
|
+
...props
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
export { Link };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { type Placement } from '@floating-ui/react';
|
|
2
|
+
import { type ComponentProps, type ReactElement, type ReactNode } from 'react';
|
|
3
|
+
import type { Color, ComponentSize } from '../types';
|
|
4
|
+
/** Props for {@link Menu}. */
|
|
5
|
+
export interface MenuProps {
|
|
6
|
+
/** `Menu.Trigger` and `Menu.Content`. */
|
|
7
|
+
children?: ReactNode;
|
|
8
|
+
/** Controlled open state. */
|
|
9
|
+
open?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Initial open state when uncontrolled.
|
|
12
|
+
* @default false
|
|
13
|
+
*/
|
|
14
|
+
defaultOpen?: boolean;
|
|
15
|
+
/** Called when the menu opens or closes. */
|
|
16
|
+
onOpenChange?: (open: boolean) => void;
|
|
17
|
+
/**
|
|
18
|
+
* Preferred side. Flips when there isn't room.
|
|
19
|
+
* @default 'bottom-start'
|
|
20
|
+
*/
|
|
21
|
+
placement?: Placement;
|
|
22
|
+
/**
|
|
23
|
+
* Item size, from the `sizes` config.
|
|
24
|
+
* @default config.defaults.size
|
|
25
|
+
*/
|
|
26
|
+
size?: ComponentSize<'Menu'>;
|
|
27
|
+
}
|
|
28
|
+
declare function MenuRoot(props: MenuProps): import("react").JSX.Element;
|
|
29
|
+
/** Props for `Menu.Trigger`. */
|
|
30
|
+
export interface MenuTriggerProps {
|
|
31
|
+
/** A single element, usually a {@link Button}. */
|
|
32
|
+
children: ReactElement;
|
|
33
|
+
}
|
|
34
|
+
declare function MenuTrigger({ children }: MenuTriggerProps): ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
|
|
35
|
+
/** Props for `Menu.Content`. */
|
|
36
|
+
export interface MenuContentProps extends ComponentProps<'div'> {
|
|
37
|
+
}
|
|
38
|
+
declare function MenuContent({ className, style, children, ...props }: MenuContentProps): import("react").JSX.Element | null;
|
|
39
|
+
/** Props for `Menu.Item`. */
|
|
40
|
+
export interface MenuItemProps extends Omit<ComponentProps<'button'>, 'color' | 'onSelect'> {
|
|
41
|
+
/** Called when the item is chosen by click, Enter or Space. The menu closes afterwards. */
|
|
42
|
+
onSelect?: () => void;
|
|
43
|
+
/** Semantic color for the item, e.g. a destructive action. From the `colors` config. */
|
|
44
|
+
color?: Color;
|
|
45
|
+
/** Text used for type-to-select. Defaults to `children` when it is a string. */
|
|
46
|
+
textValue?: string;
|
|
47
|
+
}
|
|
48
|
+
declare function MenuItem({ onSelect, color, textValue, disabled, className, children, ...props }: MenuItemProps): import("react").JSX.Element;
|
|
49
|
+
/** A line between groups of `Menu.Item`s. */
|
|
50
|
+
declare function MenuSeparator(): import("react").JSX.Element;
|
|
51
|
+
/**
|
|
52
|
+
* A list of actions opened from a trigger. Arrow keys move between items,
|
|
53
|
+
* typing jumps to a matching item, Esc closes.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```tsx
|
|
57
|
+
* <Menu>
|
|
58
|
+
* <Menu.Trigger><Button variant="outline">Actions</Button></Menu.Trigger>
|
|
59
|
+
* <Menu.Content>
|
|
60
|
+
* <Menu.Item onSelect={rename}>Rename</Menu.Item>
|
|
61
|
+
* <Menu.Separator />
|
|
62
|
+
* <Menu.Item color="danger" onSelect={remove}>Delete</Menu.Item>
|
|
63
|
+
* </Menu.Content>
|
|
64
|
+
* </Menu>
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export declare const Menu: typeof MenuRoot & {
|
|
68
|
+
Trigger: typeof MenuTrigger;
|
|
69
|
+
Content: typeof MenuContent;
|
|
70
|
+
Item: typeof MenuItem;
|
|
71
|
+
Separator: typeof MenuSeparator;
|
|
72
|
+
};
|
|
73
|
+
export {};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { useDefaults } from "../runtime.js";
|
|
2
|
+
import { colorClass, cx, sizeClass } from "../classes.js";
|
|
3
|
+
import { useControllable } from "../hooks.js";
|
|
4
|
+
import { floatingMiddleware, useTrigger } from "../floating.js";
|
|
5
|
+
import { createContext, useContext, useRef, useState } from "react";
|
|
6
|
+
import { jsx } from "react/jsx-runtime";
|
|
7
|
+
import { FloatingFocusManager, FloatingList, FloatingPortal, autoUpdate, useClick, useDismiss, useFloating, useInteractions, useListItem, useListNavigation, useRole, useTypeahead } from "@floating-ui/react";
|
|
8
|
+
//#region src/components/Menu.tsx
|
|
9
|
+
var MenuContext = createContext(null);
|
|
10
|
+
function useMenuContext(component) {
|
|
11
|
+
const context = useContext(MenuContext);
|
|
12
|
+
if (!context) throw new Error(`yarcl: <${component}> must be inside <Menu>`);
|
|
13
|
+
return context;
|
|
14
|
+
}
|
|
15
|
+
function useMenuState({ open: openProp, defaultOpen = false, onOpenChange, placement = "bottom-start", size }) {
|
|
16
|
+
const [open, setOpen] = useControllable(openProp, defaultOpen, onOpenChange);
|
|
17
|
+
const [activeIndex, setActiveIndex] = useState(null);
|
|
18
|
+
const elementsRef = useRef([]);
|
|
19
|
+
const labelsRef = useRef([]);
|
|
20
|
+
const floating = useFloating({
|
|
21
|
+
open,
|
|
22
|
+
onOpenChange: setOpen,
|
|
23
|
+
placement,
|
|
24
|
+
transform: false,
|
|
25
|
+
whileElementsMounted: autoUpdate,
|
|
26
|
+
middleware: floatingMiddleware()
|
|
27
|
+
});
|
|
28
|
+
const interactions = useInteractions([
|
|
29
|
+
useClick(floating.context),
|
|
30
|
+
useDismiss(floating.context),
|
|
31
|
+
useRole(floating.context, { role: "menu" }),
|
|
32
|
+
useListNavigation(floating.context, {
|
|
33
|
+
listRef: elementsRef,
|
|
34
|
+
activeIndex,
|
|
35
|
+
onNavigate: setActiveIndex,
|
|
36
|
+
loop: true
|
|
37
|
+
}),
|
|
38
|
+
useTypeahead(floating.context, {
|
|
39
|
+
listRef: labelsRef,
|
|
40
|
+
activeIndex,
|
|
41
|
+
onMatch: open ? setActiveIndex : void 0
|
|
42
|
+
})
|
|
43
|
+
]);
|
|
44
|
+
return {
|
|
45
|
+
open,
|
|
46
|
+
setOpen,
|
|
47
|
+
activeIndex,
|
|
48
|
+
setActiveIndex,
|
|
49
|
+
elementsRef,
|
|
50
|
+
labelsRef,
|
|
51
|
+
size,
|
|
52
|
+
...floating,
|
|
53
|
+
...interactions
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function MenuRoot(props) {
|
|
57
|
+
const state = useMenuState(props);
|
|
58
|
+
return /* @__PURE__ */ jsx(MenuContext.Provider, {
|
|
59
|
+
value: state,
|
|
60
|
+
children: props.children
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function MenuTrigger({ children }) {
|
|
64
|
+
const { refs, getReferenceProps } = useMenuContext("Menu.Trigger");
|
|
65
|
+
return useTrigger(children, refs.setReference, getReferenceProps);
|
|
66
|
+
}
|
|
67
|
+
function MenuContent({ className, style, children, ...props }) {
|
|
68
|
+
const own = useDefaults("Menu");
|
|
69
|
+
const { open, size, refs, floatingStyles, context, getFloatingProps, elementsRef, labelsRef } = useMenuContext("Menu.Content");
|
|
70
|
+
if (!open) return null;
|
|
71
|
+
return /* @__PURE__ */ jsx(FloatingPortal, { children: /* @__PURE__ */ jsx(FloatingFocusManager, {
|
|
72
|
+
context,
|
|
73
|
+
modal: false,
|
|
74
|
+
initialFocus: 0,
|
|
75
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
76
|
+
ref: refs.setFloating,
|
|
77
|
+
style: {
|
|
78
|
+
...floatingStyles,
|
|
79
|
+
...style
|
|
80
|
+
},
|
|
81
|
+
className: cx("yarcl-floating yarcl-panel yarcl-listbox", sizeClass(size ?? own.size, "Menu"), colorClass(), className),
|
|
82
|
+
...getFloatingProps(props),
|
|
83
|
+
children: /* @__PURE__ */ jsx(FloatingList, {
|
|
84
|
+
elementsRef,
|
|
85
|
+
labelsRef,
|
|
86
|
+
children
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
}) });
|
|
90
|
+
}
|
|
91
|
+
function MenuItem({ onSelect, color, textValue, disabled, className, children, ...props }) {
|
|
92
|
+
const { activeIndex, setOpen, getItemProps } = useMenuContext("Menu.Item");
|
|
93
|
+
const { ref, index } = useListItem({ label: disabled ? null : textValue ?? (typeof children === "string" ? children : null) });
|
|
94
|
+
const active = activeIndex === index;
|
|
95
|
+
return /* @__PURE__ */ jsx("button", {
|
|
96
|
+
ref,
|
|
97
|
+
type: "button",
|
|
98
|
+
role: "menuitem",
|
|
99
|
+
tabIndex: active ? 0 : -1,
|
|
100
|
+
disabled,
|
|
101
|
+
"data-active": active || void 0,
|
|
102
|
+
className: cx("yarcl-option", color && cx("yarcl-option-colored", colorClass(color)), className),
|
|
103
|
+
...getItemProps({
|
|
104
|
+
...props,
|
|
105
|
+
onClick() {
|
|
106
|
+
onSelect?.();
|
|
107
|
+
setOpen(false);
|
|
108
|
+
}
|
|
109
|
+
}),
|
|
110
|
+
children
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/** A line between groups of `Menu.Item`s. */
|
|
114
|
+
function MenuSeparator() {
|
|
115
|
+
return /* @__PURE__ */ jsx("div", {
|
|
116
|
+
role: "separator",
|
|
117
|
+
className: "yarcl-listbox-separator"
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* A list of actions opened from a trigger. Arrow keys move between items,
|
|
122
|
+
* typing jumps to a matching item, Esc closes.
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```tsx
|
|
126
|
+
* <Menu>
|
|
127
|
+
* <Menu.Trigger><Button variant="outline">Actions</Button></Menu.Trigger>
|
|
128
|
+
* <Menu.Content>
|
|
129
|
+
* <Menu.Item onSelect={rename}>Rename</Menu.Item>
|
|
130
|
+
* <Menu.Separator />
|
|
131
|
+
* <Menu.Item color="danger" onSelect={remove}>Delete</Menu.Item>
|
|
132
|
+
* </Menu.Content>
|
|
133
|
+
* </Menu>
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
var Menu = Object.assign(MenuRoot, {
|
|
137
|
+
Trigger: MenuTrigger,
|
|
138
|
+
Content: MenuContent,
|
|
139
|
+
Item: MenuItem,
|
|
140
|
+
Separator: MenuSeparator
|
|
141
|
+
});
|
|
142
|
+
//#endregion
|
|
143
|
+
export { Menu };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type MouseEvent, type ReactElement, type ReactNode, type SyntheticEvent } from 'react';
|
|
2
|
+
import type { ModalSize, Radius } from '../types';
|
|
3
|
+
/** Props shared by {@link Dialog} and {@link Drawer}. */
|
|
4
|
+
export interface ModalProps {
|
|
5
|
+
/** Heading of the dialog. Also its accessible name. */
|
|
6
|
+
title: ReactNode;
|
|
7
|
+
/** Supporting text below the title. Also its accessible description. */
|
|
8
|
+
description?: ReactNode;
|
|
9
|
+
/** Body content. Scrolls when it doesn't fit. */
|
|
10
|
+
children?: ReactNode;
|
|
11
|
+
/** Actions shown at the bottom, e.g. Cancel and Confirm buttons. */
|
|
12
|
+
footer?: ReactNode;
|
|
13
|
+
/** An element that opens the dialog when clicked, e.g. a {@link Button}. Optional when controlled. */
|
|
14
|
+
trigger?: ReactElement<{
|
|
15
|
+
onClick?: (event: MouseEvent) => void;
|
|
16
|
+
}>;
|
|
17
|
+
/** Controlled open state. */
|
|
18
|
+
open?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Initial open state when uncontrolled.
|
|
21
|
+
* @default false
|
|
22
|
+
*/
|
|
23
|
+
defaultOpen?: boolean;
|
|
24
|
+
/** Called when the dialog opens or closes, including by Esc, the close button or a backdrop click. */
|
|
25
|
+
onOpenChange?: (open: boolean) => void;
|
|
26
|
+
/**
|
|
27
|
+
* Closes when the backdrop is clicked.
|
|
28
|
+
* @default true
|
|
29
|
+
*/
|
|
30
|
+
closeOnBackdrop?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Width, from the `modalSizes` config. Height follows the content, up to the viewport.
|
|
33
|
+
* @default config.defaults.modalSize (Drawer: its component default, `sm` in the library defaults)
|
|
34
|
+
*/
|
|
35
|
+
size?: ModalSize;
|
|
36
|
+
}
|
|
37
|
+
export declare function useModalDialog(open: boolean, setOpen: (open: boolean) => void, closeOnBackdrop?: boolean): {
|
|
38
|
+
ref: import("react").RefObject<HTMLDialogElement | null>;
|
|
39
|
+
onClose(event: SyntheticEvent<HTMLDialogElement>): void;
|
|
40
|
+
onClick(event: MouseEvent<HTMLDialogElement>): void;
|
|
41
|
+
};
|
|
42
|
+
export declare function Modal({ title, description, children, footer, trigger, open: openProp, defaultOpen, onOpenChange, closeOnBackdrop, size, className, radius, }: ModalProps & {
|
|
43
|
+
className: string;
|
|
44
|
+
radius?: Radius;
|
|
45
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { useConfig } from "../runtime.js";
|
|
2
|
+
import { cx, radiusClass, typeClass } from "../classes.js";
|
|
3
|
+
import { useControllable } from "../hooks.js";
|
|
4
|
+
import { cloneElement, useEffect, useId, useRef } from "react";
|
|
5
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
//#region src/components/Modal.tsx
|
|
7
|
+
var openModals = [];
|
|
8
|
+
function syncBackdrops() {
|
|
9
|
+
openModals.forEach((dialog, i) => dialog.toggleAttribute("data-yarcl-covered", i < openModals.length - 1));
|
|
10
|
+
}
|
|
11
|
+
function removeOpenModal(dialog) {
|
|
12
|
+
const index = openModals.indexOf(dialog);
|
|
13
|
+
if (index === -1) return;
|
|
14
|
+
openModals.splice(index, 1);
|
|
15
|
+
dialog.removeAttribute("data-yarcl-covered");
|
|
16
|
+
syncBackdrops();
|
|
17
|
+
}
|
|
18
|
+
function CloseIcon() {
|
|
19
|
+
return /* @__PURE__ */ jsx("svg", {
|
|
20
|
+
viewBox: "0 0 24 24",
|
|
21
|
+
fill: "none",
|
|
22
|
+
stroke: "currentColor",
|
|
23
|
+
strokeWidth: "2",
|
|
24
|
+
strokeLinecap: "round",
|
|
25
|
+
"aria-hidden": "true",
|
|
26
|
+
children: /* @__PURE__ */ jsx("path", { d: "M6 6l12 12M18 6 6 18" })
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function useModalDialog(open, setOpen, closeOnBackdrop = true) {
|
|
30
|
+
const ref = useRef(null);
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
const dialog = ref.current;
|
|
33
|
+
if (!dialog) return;
|
|
34
|
+
if (open && !dialog.open) {
|
|
35
|
+
dialog.showModal();
|
|
36
|
+
openModals.push(dialog);
|
|
37
|
+
syncBackdrops();
|
|
38
|
+
} else if (!open) {
|
|
39
|
+
if (dialog.open) dialog.close();
|
|
40
|
+
removeOpenModal(dialog);
|
|
41
|
+
}
|
|
42
|
+
}, [open]);
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
const dialog = ref.current;
|
|
45
|
+
return () => {
|
|
46
|
+
if (dialog) removeOpenModal(dialog);
|
|
47
|
+
};
|
|
48
|
+
}, []);
|
|
49
|
+
return {
|
|
50
|
+
ref,
|
|
51
|
+
onClose(event) {
|
|
52
|
+
if (event.target === event.currentTarget && open) setOpen(false);
|
|
53
|
+
},
|
|
54
|
+
onClick(event) {
|
|
55
|
+
if (closeOnBackdrop && event.target === event.currentTarget) setOpen(false);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function Modal({ title, description, children, footer, trigger, open: openProp, defaultOpen = false, onOpenChange, closeOnBackdrop = true, size, className, radius }) {
|
|
60
|
+
const config = useConfig();
|
|
61
|
+
const [open, setOpen] = useControllable(openProp, defaultOpen, onOpenChange);
|
|
62
|
+
const dialogProps = useModalDialog(open, setOpen, closeOnBackdrop);
|
|
63
|
+
const titleId = useId();
|
|
64
|
+
const descriptionId = useId();
|
|
65
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [trigger && cloneElement(trigger, { onClick(event) {
|
|
66
|
+
trigger.props.onClick?.(event);
|
|
67
|
+
setOpen(true);
|
|
68
|
+
} }), /* @__PURE__ */ jsx("dialog", {
|
|
69
|
+
...dialogProps,
|
|
70
|
+
className: cx("yarcl-modal", `yarcl-modal-size-${size ?? config.defaults.modalSize}`, radiusClass(radius), className),
|
|
71
|
+
"aria-labelledby": titleId,
|
|
72
|
+
"aria-describedby": description != null ? descriptionId : void 0,
|
|
73
|
+
children: open && /* @__PURE__ */ jsxs("div", {
|
|
74
|
+
className: "yarcl-modal-content",
|
|
75
|
+
children: [
|
|
76
|
+
/* @__PURE__ */ jsxs("div", {
|
|
77
|
+
className: "yarcl-modal-header",
|
|
78
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
79
|
+
className: "yarcl-modal-heading",
|
|
80
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
81
|
+
id: titleId,
|
|
82
|
+
className: cx("yarcl-modal-title", typeClass(config.typography.headings.h2)),
|
|
83
|
+
children: title
|
|
84
|
+
}), description != null && /* @__PURE__ */ jsx("p", {
|
|
85
|
+
id: descriptionId,
|
|
86
|
+
className: "yarcl-modal-description",
|
|
87
|
+
children: description
|
|
88
|
+
})]
|
|
89
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
90
|
+
type: "button",
|
|
91
|
+
className: "yarcl-modal-close",
|
|
92
|
+
"aria-label": "Close",
|
|
93
|
+
onClick: () => setOpen(false),
|
|
94
|
+
children: /* @__PURE__ */ jsx(CloseIcon, {})
|
|
95
|
+
})]
|
|
96
|
+
}),
|
|
97
|
+
children != null && /* @__PURE__ */ jsx("div", {
|
|
98
|
+
className: "yarcl-modal-body",
|
|
99
|
+
children
|
|
100
|
+
}),
|
|
101
|
+
footer != null && /* @__PURE__ */ jsx("div", {
|
|
102
|
+
className: "yarcl-modal-footer",
|
|
103
|
+
children: footer
|
|
104
|
+
})
|
|
105
|
+
]
|
|
106
|
+
})
|
|
107
|
+
})] });
|
|
108
|
+
}
|
|
109
|
+
//#endregion
|
|
110
|
+
export { Modal, useModalDialog };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type ComponentProps } from 'react';
|
|
2
|
+
import type { TokenProps } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Props for {@link NumberInput}. Native `<input>` attributes go to the inner input;
|
|
5
|
+
* `className` and `style` go to the outer box.
|
|
6
|
+
*/
|
|
7
|
+
export interface NumberInputProps extends TokenProps<'NumberInput'>, Omit<ComponentProps<'input'>, 'color' | 'size' | 'type' | 'value' | 'defaultValue' | 'onChange' | 'min' | 'max' | 'step'> {
|
|
8
|
+
/** Controlled value. `null` means the input is empty. */
|
|
9
|
+
value?: number | null;
|
|
10
|
+
/**
|
|
11
|
+
* Initial value when uncontrolled.
|
|
12
|
+
* @default null
|
|
13
|
+
*/
|
|
14
|
+
defaultValue?: number | null;
|
|
15
|
+
/** Called with the new value after a step, or when typed text is committed on blur or Enter. */
|
|
16
|
+
onValueChange?: (value: number | null) => void;
|
|
17
|
+
/** Lowest allowed value. Typed values are clamped to it, and `Home` jumps to it. */
|
|
18
|
+
min?: number;
|
|
19
|
+
/** Highest allowed value. Typed values are clamped to it, and `End` jumps to it. */
|
|
20
|
+
max?: number;
|
|
21
|
+
/**
|
|
22
|
+
* Amount added or removed by the stepper buttons and the arrow keys. `PageUp` and `PageDown` step ten times as far.
|
|
23
|
+
* @default 1
|
|
24
|
+
*/
|
|
25
|
+
step?: number;
|
|
26
|
+
/**
|
|
27
|
+
* Accessible label of the increment button.
|
|
28
|
+
* @default 'Increase'
|
|
29
|
+
*/
|
|
30
|
+
incrementLabel?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Accessible label of the decrement button.
|
|
33
|
+
* @default 'Decrease'
|
|
34
|
+
*/
|
|
35
|
+
decrementLabel?: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* A numeric input with increment and decrement buttons, exposed as a `spinbutton`.
|
|
39
|
+
* Arrow keys step by `step`, `PageUp` and `PageDown` by ten steps, `Home` and `End` jump to `min` and `max`.
|
|
40
|
+
* Shares the base size scale with {@link Input}; component size overrides can adjust either one. Works inside a {@link Field}.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```tsx
|
|
44
|
+
* <Field label="Guests">
|
|
45
|
+
* <NumberInput min={1} max={10} defaultValue={2} onValueChange={setGuests} />
|
|
46
|
+
* </Field>
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export declare function NumberInput(props: NumberInputProps): import("react").JSX.Element;
|