@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
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
autoUpdate,
|
|
3
|
-
FloatingFocusManager,
|
|
4
|
-
FloatingPortal,
|
|
5
|
-
useClick,
|
|
6
|
-
useDismiss,
|
|
7
|
-
useFloating,
|
|
8
|
-
useInteractions,
|
|
9
|
-
useRole,
|
|
10
|
-
type Placement,
|
|
11
|
-
} from '@floating-ui/react';
|
|
12
|
-
import { createContext, useContext, type ComponentProps, type ReactElement, type ReactNode, type RefObject } from 'react';
|
|
13
|
-
import { cx, paddingClass, radiusClass } from '../classes';
|
|
14
|
-
import { floatingMiddleware, useTrigger } from '../floating';
|
|
15
|
-
import { useControllable } from '../hooks';
|
|
16
|
-
import type { Radius, Spacing } from '../types';
|
|
17
|
-
import { useDefaults } from '../runtime';
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
type PopoverContextValue = ReturnType<typeof usePopoverState>;
|
|
21
|
-
|
|
22
|
-
const PopoverContext = createContext<PopoverContextValue | null>(null);
|
|
23
|
-
|
|
24
|
-
function usePopoverContext(component: string) {
|
|
25
|
-
const context = useContext(PopoverContext);
|
|
26
|
-
if (!context) throw new Error(`yarcl: <${component}> must be inside <Popover>`);
|
|
27
|
-
return context;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function usePopoverState({ open: openProp, defaultOpen = false, onOpenChange, placement = 'bottom', modal = false }: PopoverProps) {
|
|
31
|
-
const [open, setOpen] = useControllable(openProp, defaultOpen, onOpenChange);
|
|
32
|
-
const floating = useFloating({
|
|
33
|
-
open,
|
|
34
|
-
onOpenChange: setOpen,
|
|
35
|
-
placement,
|
|
36
|
-
transform: false,
|
|
37
|
-
whileElementsMounted: autoUpdate,
|
|
38
|
-
middleware: floatingMiddleware(),
|
|
39
|
-
});
|
|
40
|
-
const interactions = useInteractions([
|
|
41
|
-
useClick(floating.context),
|
|
42
|
-
useDismiss(floating.context),
|
|
43
|
-
useRole(floating.context, { role: 'dialog' }),
|
|
44
|
-
]);
|
|
45
|
-
return { open, setOpen, modal, ...floating, ...interactions };
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** Props for {@link Popover}. */
|
|
49
|
-
export interface PopoverProps {
|
|
50
|
-
/** `Popover.Trigger` and `Popover.Content`. */
|
|
51
|
-
children?: ReactNode;
|
|
52
|
-
/** Controlled open state. */
|
|
53
|
-
open?: boolean;
|
|
54
|
-
/**
|
|
55
|
-
* Initial open state when uncontrolled.
|
|
56
|
-
* @default false
|
|
57
|
-
*/
|
|
58
|
-
defaultOpen?: boolean;
|
|
59
|
-
/** Called when the popover opens or closes. */
|
|
60
|
-
onOpenChange?: (open: boolean) => void;
|
|
61
|
-
/**
|
|
62
|
-
* Preferred side. Flips when there isn't room.
|
|
63
|
-
* @default 'bottom'
|
|
64
|
-
*/
|
|
65
|
-
placement?: Placement;
|
|
66
|
-
/**
|
|
67
|
-
* Traps focus inside the content while open.
|
|
68
|
-
* @default false
|
|
69
|
-
*/
|
|
70
|
-
modal?: boolean;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function PopoverRoot(props: PopoverProps) {
|
|
74
|
-
const state = usePopoverState(props);
|
|
75
|
-
return <PopoverContext.Provider value={state}>{props.children}</PopoverContext.Provider>;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/** Props for `Popover.Trigger`. */
|
|
79
|
-
export interface PopoverTriggerProps {
|
|
80
|
-
/** A single element, usually a {@link Button}. It receives the click handler and ARIA attributes. */
|
|
81
|
-
children: ReactElement;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function PopoverTrigger({ children }: PopoverTriggerProps) {
|
|
85
|
-
const { refs, getReferenceProps } = usePopoverContext('Popover.Trigger');
|
|
86
|
-
return useTrigger(children, refs.setReference, getReferenceProps);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/** Props for `Popover.Content`. */
|
|
90
|
-
export interface PopoverContentProps extends ComponentProps<'div'> {
|
|
91
|
-
/**
|
|
92
|
-
* Inner padding, from the `spacing` config.
|
|
93
|
-
* @default config.defaults.padding
|
|
94
|
-
*/
|
|
95
|
-
padding?: Spacing;
|
|
96
|
-
/**
|
|
97
|
-
* Corner radius, from the `radii` config.
|
|
98
|
-
* @default config.defaults.radius
|
|
99
|
-
*/
|
|
100
|
-
radius?: Radius;
|
|
101
|
-
/**
|
|
102
|
-
* Element focused when the content opens: an index into its tabbable elements, or a ref.
|
|
103
|
-
* @default 0
|
|
104
|
-
*/
|
|
105
|
-
initialFocus?: number | RefObject<HTMLElement | null>;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function PopoverContent({ padding, radius, initialFocus, className, style, ...props }: PopoverContentProps) {
|
|
109
|
-
const own = useDefaults('Popover');
|
|
110
|
-
const { open, modal, refs, floatingStyles, context, getFloatingProps } = usePopoverContext('Popover.Content');
|
|
111
|
-
if (!open) return null;
|
|
112
|
-
return (
|
|
113
|
-
<FloatingPortal>
|
|
114
|
-
<FloatingFocusManager context={context} modal={modal} initialFocus={initialFocus}>
|
|
115
|
-
<div
|
|
116
|
-
ref={refs.setFloating}
|
|
117
|
-
style={{ ...floatingStyles, ...style }}
|
|
118
|
-
className={cx('yarcl-floating yarcl-panel yarcl-popover', paddingClass(padding ?? own.padding), radiusClass(radius ?? own.radius), className)}
|
|
119
|
-
{...getFloatingProps(props)}
|
|
120
|
-
/>
|
|
121
|
-
</FloatingFocusManager>
|
|
122
|
-
</FloatingPortal>
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* A floating panel anchored to a trigger, opened by click. Closes on Esc or outside click.
|
|
128
|
-
* The base for custom floating UI; use {@link Menu}, {@link Select} or {@link Combobox} for lists.
|
|
129
|
-
*
|
|
130
|
-
* @example
|
|
131
|
-
* ```tsx
|
|
132
|
-
* <Popover>
|
|
133
|
-
* <Popover.Trigger><Button variant="outline">Filters</Button></Popover.Trigger>
|
|
134
|
-
* <Popover.Content>
|
|
135
|
-
* <Stack><Checkbox>Archived</Checkbox></Stack>
|
|
136
|
-
* </Popover.Content>
|
|
137
|
-
* </Popover>
|
|
138
|
-
* ```
|
|
139
|
-
*/
|
|
140
|
-
export const Popover = Object.assign(PopoverRoot, { Trigger: PopoverTrigger, Content: PopoverContent });
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import { useId, type ComponentProps, type CSSProperties, type ReactNode } from 'react';
|
|
2
|
-
import { colorClass, cx, radiusClass, sizeClass } from '../classes';
|
|
3
|
-
import type { Color, Radius, Size } from '../types';
|
|
4
|
-
import { useDefaults } from '../runtime';
|
|
5
|
-
|
|
6
|
-
/** Props for {@link Progress}. */
|
|
7
|
-
export interface ProgressProps extends Omit<ComponentProps<'div'>, 'color' | 'children'> {
|
|
8
|
-
/**
|
|
9
|
-
* Current value, from 0 to `max`. Leave undefined for an indeterminate bar that animates until the work is measurable.
|
|
10
|
-
* @example
|
|
11
|
-
* ```tsx
|
|
12
|
-
* <Progress value={40} label="Uploading" />
|
|
13
|
-
* ```
|
|
14
|
-
*/
|
|
15
|
-
value?: number;
|
|
16
|
-
/**
|
|
17
|
-
* The value that means complete.
|
|
18
|
-
* @default 100
|
|
19
|
-
*/
|
|
20
|
-
max?: number;
|
|
21
|
-
/**
|
|
22
|
-
* Visible label above the bar. It also names the progress bar for screen readers;
|
|
23
|
-
* without it, pass `aria-label` or `aria-labelledby`.
|
|
24
|
-
*/
|
|
25
|
-
label?: ReactNode;
|
|
26
|
-
/** Shows the value next to the label, as a percentage unless `formatValue` is set. Ignored when indeterminate. */
|
|
27
|
-
showValue?: boolean;
|
|
28
|
-
/**
|
|
29
|
-
* Formats the value for the visible text and `aria-valuetext`.
|
|
30
|
-
* @example
|
|
31
|
-
* ```tsx
|
|
32
|
-
* <Progress value={3} max={8} showValue formatValue={(v, max) => `${v} of ${max} files`} />
|
|
33
|
-
* ```
|
|
34
|
-
*/
|
|
35
|
-
formatValue?: (value: number, max: number) => string;
|
|
36
|
-
/**
|
|
37
|
-
* Track height, scaled from the size's `iconSize` in the `sizes` config. Also sets the label's font size.
|
|
38
|
-
* @default config.defaults.size
|
|
39
|
-
*/
|
|
40
|
-
size?: Size;
|
|
41
|
-
/**
|
|
42
|
-
* Bar color, from the `colors` config.
|
|
43
|
-
* @default config.defaults.color
|
|
44
|
-
*/
|
|
45
|
-
color?: Color;
|
|
46
|
-
/**
|
|
47
|
-
* Corner radius of the track and bar, from the `radii` config.
|
|
48
|
-
* @default config.defaults.radius
|
|
49
|
-
*/
|
|
50
|
-
radius?: Radius | 'size';
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* A progress bar. Determinate when `value` is set, indeterminate otherwise.
|
|
55
|
-
* Renders `role="progressbar"` with `aria-valuenow`, `aria-valuemin` and `aria-valuemax`
|
|
56
|
-
* (omitted when indeterminate), named by `label` or `aria-label`.
|
|
57
|
-
*
|
|
58
|
-
* @example
|
|
59
|
-
* ```tsx
|
|
60
|
-
* <Progress value={64} label="Storage used" showValue color="warning" />
|
|
61
|
-
* <Progress aria-label="Loading results" />
|
|
62
|
-
* ```
|
|
63
|
-
*/
|
|
64
|
-
export function Progress({
|
|
65
|
-
value,
|
|
66
|
-
max = 100,
|
|
67
|
-
label,
|
|
68
|
-
showValue,
|
|
69
|
-
formatValue,
|
|
70
|
-
size,
|
|
71
|
-
color,
|
|
72
|
-
radius,
|
|
73
|
-
className,
|
|
74
|
-
style,
|
|
75
|
-
...props
|
|
76
|
-
}: ProgressProps) {
|
|
77
|
-
const own = useDefaults('Progress');
|
|
78
|
-
const labelId = useId();
|
|
79
|
-
const indeterminate = value == null;
|
|
80
|
-
const limit = max > 0 ? max : 100;
|
|
81
|
-
const current = indeterminate ? 0 : Math.min(Math.max(value, 0), limit);
|
|
82
|
-
const text = formatValue ? formatValue(current, limit) : `${Math.round((current / limit) * 100)}%`;
|
|
83
|
-
const hasLabel = label != null;
|
|
84
|
-
const named = hasLabel || props['aria-label'] != null || props['aria-labelledby'] != null;
|
|
85
|
-
const valueVisible = showValue && !indeterminate;
|
|
86
|
-
|
|
87
|
-
return (
|
|
88
|
-
<div
|
|
89
|
-
role="progressbar"
|
|
90
|
-
aria-labelledby={hasLabel ? labelId : undefined}
|
|
91
|
-
aria-label={named ? undefined : 'Progress'}
|
|
92
|
-
aria-valuemin={indeterminate ? undefined : 0}
|
|
93
|
-
aria-valuemax={indeterminate ? undefined : limit}
|
|
94
|
-
aria-valuenow={indeterminate ? undefined : current}
|
|
95
|
-
aria-valuetext={indeterminate || !formatValue ? undefined : text}
|
|
96
|
-
data-indeterminate={indeterminate || undefined}
|
|
97
|
-
className={cx(
|
|
98
|
-
'yarcl-progress',
|
|
99
|
-
sizeClass(size ?? own.size),
|
|
100
|
-
colorClass(color ?? own.color),
|
|
101
|
-
radiusClass(radius ?? own.radius, size ?? own.size),
|
|
102
|
-
className,
|
|
103
|
-
)}
|
|
104
|
-
style={{ ...(indeterminate ? null : { '--yarcl-progress': current / limit }), ...style } as CSSProperties}
|
|
105
|
-
{...props}
|
|
106
|
-
>
|
|
107
|
-
{(hasLabel || valueVisible) && (
|
|
108
|
-
<div className="yarcl-progress-header">
|
|
109
|
-
{hasLabel && (
|
|
110
|
-
<span id={labelId} className="yarcl-progress-label">
|
|
111
|
-
{label}
|
|
112
|
-
</span>
|
|
113
|
-
)}
|
|
114
|
-
{valueVisible && <span className="yarcl-progress-value">{text}</span>}
|
|
115
|
-
</div>
|
|
116
|
-
)}
|
|
117
|
-
<div className="yarcl-progress-track">
|
|
118
|
-
<div className="yarcl-progress-bar" />
|
|
119
|
-
</div>
|
|
120
|
-
</div>
|
|
121
|
-
);
|
|
122
|
-
}
|
package/src/components/Radio.tsx
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import type { ChangeEvent, ComponentProps, ReactNode } from 'react';
|
|
2
|
-
import { colorClass, cx, sizeClass } from '../classes';
|
|
3
|
-
import type { Color, Size } from '../types';
|
|
4
|
-
import { useRadioGroup } from './RadioGroup';
|
|
5
|
-
import { useDefaults } from '../runtime';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
/** Props for {@link Radio}. Accepts all native `<input>` attributes except `type`, `color` and `size`. */
|
|
9
|
-
export interface RadioProps extends Omit<ComponentProps<'input'>, 'type' | 'color' | 'size' | 'children' | 'value'> {
|
|
10
|
-
/** Label shown next to the radio. */
|
|
11
|
-
children?: ReactNode;
|
|
12
|
-
/** Value reported to the {@link RadioGroup} and submitted with forms. */
|
|
13
|
-
value?: string;
|
|
14
|
-
/**
|
|
15
|
-
* Radio size, from the `sizes` config (uses the size's `iconSize`). Inherited from a {@link RadioGroup}.
|
|
16
|
-
* @default config.defaults.size
|
|
17
|
-
*/
|
|
18
|
-
size?: Size;
|
|
19
|
-
/**
|
|
20
|
-
* Color of the selected radio, from the `colors` config. Inherited from a {@link RadioGroup}.
|
|
21
|
-
* @default config.defaults.color
|
|
22
|
-
*/
|
|
23
|
-
color?: Color;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* A radio button with an optional label. Place inside a {@link RadioGroup}, or group standalone
|
|
28
|
-
* radios with the same `name`.
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* ```tsx
|
|
32
|
-
* <RadioGroup label="Plan" defaultValue="pro">
|
|
33
|
-
* <Radio value="free">Free</Radio>
|
|
34
|
-
* <Radio value="pro">Pro</Radio>
|
|
35
|
-
* </RadioGroup>
|
|
36
|
-
* ```
|
|
37
|
-
*/
|
|
38
|
-
export function Radio({ children, size, color, value, className, style, onChange, ...props }: RadioProps) {
|
|
39
|
-
const own = useDefaults('Radio');
|
|
40
|
-
const group = useRadioGroup();
|
|
41
|
-
const groupProps = group && {
|
|
42
|
-
name: group.name,
|
|
43
|
-
checked: value !== undefined && group.value === value,
|
|
44
|
-
'aria-invalid': group.invalid || undefined,
|
|
45
|
-
onChange: (event: ChangeEvent<HTMLInputElement>) => {
|
|
46
|
-
onChange?.(event);
|
|
47
|
-
if (event.target.checked && value !== undefined) group.select(value);
|
|
48
|
-
},
|
|
49
|
-
};
|
|
50
|
-
return (
|
|
51
|
-
<label
|
|
52
|
-
className={cx('yarcl-radio', sizeClass(size ?? group?.size ?? own.size), colorClass(color ?? group?.color ?? own.color), className)}
|
|
53
|
-
style={style}
|
|
54
|
-
>
|
|
55
|
-
<input type="radio" className="yarcl-radio-input" value={value} onChange={onChange} {...props} {...groupProps} />
|
|
56
|
-
{children != null && <span className="yarcl-radio-label">{children}</span>}
|
|
57
|
-
</label>
|
|
58
|
-
);
|
|
59
|
-
}
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { createContext, useContext, useId, type ComponentProps, type ReactNode } from 'react';
|
|
2
|
-
import { cx, typeClass } from '../classes';
|
|
3
|
-
import { useControllable } from '../hooks';
|
|
4
|
-
import type { Color, Size } from '../types';
|
|
5
|
-
import { useConfig } from '../runtime';
|
|
6
|
-
|
|
7
|
-
interface RadioGroupContextValue {
|
|
8
|
-
name: string;
|
|
9
|
-
value: string | null;
|
|
10
|
-
select: (value: string) => void;
|
|
11
|
-
size?: Size;
|
|
12
|
-
color?: Color;
|
|
13
|
-
invalid: boolean;
|
|
14
|
-
disabled?: boolean;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const RadioGroupContext = createContext<RadioGroupContextValue | null>(null);
|
|
18
|
-
|
|
19
|
-
export function useRadioGroup() {
|
|
20
|
-
return useContext(RadioGroupContext);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/** Props for {@link RadioGroup}. */
|
|
24
|
-
export interface RadioGroupProps extends Omit<ComponentProps<'fieldset'>, 'defaultValue' | 'onChange' | 'color'> {
|
|
25
|
-
/** Visible group label, rendered as the `<legend>`. */
|
|
26
|
-
label: ReactNode;
|
|
27
|
-
/** Helper text shown below the options. */
|
|
28
|
-
description?: ReactNode;
|
|
29
|
-
/** Error message. Marks the options invalid and styles them with `defaults.errorColor`. */
|
|
30
|
-
error?: ReactNode;
|
|
31
|
-
/** Form field name shared by the radios. Generated when omitted. */
|
|
32
|
-
name?: string;
|
|
33
|
-
/** Controlled selected value. */
|
|
34
|
-
value?: string | null;
|
|
35
|
-
/**
|
|
36
|
-
* Initially selected value when uncontrolled.
|
|
37
|
-
* @default null
|
|
38
|
-
*/
|
|
39
|
-
defaultValue?: string | null;
|
|
40
|
-
/** Called with the selected radio's `value`. */
|
|
41
|
-
onValueChange?: (value: string) => void;
|
|
42
|
-
/** Size of every radio, from the `sizes` config. */
|
|
43
|
-
size?: Size;
|
|
44
|
-
/** Color of every radio, from the `colors` config. */
|
|
45
|
-
color?: Color;
|
|
46
|
-
/**
|
|
47
|
-
* Lays the radios out in a row or a column.
|
|
48
|
-
* @default 'vertical'
|
|
49
|
-
*/
|
|
50
|
-
orientation?: 'horizontal' | 'vertical';
|
|
51
|
-
/** {@link Radio}s, each with a `value`. */
|
|
52
|
-
children: ReactNode;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* A labelled group of {@link Radio}s. Renders a `<fieldset>` and `<legend>`, shares one `name`,
|
|
57
|
-
* and tracks the selected value. Radios inside don't need `name` or `checked`.
|
|
58
|
-
*
|
|
59
|
-
* @example
|
|
60
|
-
* ```tsx
|
|
61
|
-
* <RadioGroup label="Plan" defaultValue="pro" orientation="horizontal">
|
|
62
|
-
* <Radio value="free">Free</Radio>
|
|
63
|
-
* <Radio value="pro">Pro</Radio>
|
|
64
|
-
* </RadioGroup>
|
|
65
|
-
* ```
|
|
66
|
-
*/
|
|
67
|
-
export function RadioGroup({
|
|
68
|
-
label,
|
|
69
|
-
description,
|
|
70
|
-
error,
|
|
71
|
-
name,
|
|
72
|
-
value: valueProp,
|
|
73
|
-
defaultValue = null,
|
|
74
|
-
onValueChange,
|
|
75
|
-
size,
|
|
76
|
-
color,
|
|
77
|
-
orientation = 'vertical',
|
|
78
|
-
disabled,
|
|
79
|
-
className,
|
|
80
|
-
children,
|
|
81
|
-
...props
|
|
82
|
-
}: RadioGroupProps) {
|
|
83
|
-
const config = useConfig();
|
|
84
|
-
const id = useId();
|
|
85
|
-
const [value, setValue] = useControllable<string | null>(valueProp, defaultValue, (v) => v != null && onValueChange?.(v));
|
|
86
|
-
const descriptionId = description != null ? `${id}-description` : undefined;
|
|
87
|
-
const errorId = error != null && error !== false ? `${id}-error` : undefined;
|
|
88
|
-
|
|
89
|
-
return (
|
|
90
|
-
<RadioGroupContext.Provider
|
|
91
|
-
value={{ name: name ?? id, value, select: setValue, size, color, invalid: errorId != null, disabled }}
|
|
92
|
-
>
|
|
93
|
-
<fieldset
|
|
94
|
-
className={cx('yarcl-field yarcl-radio-group', className)}
|
|
95
|
-
aria-describedby={cx(descriptionId, errorId) || undefined}
|
|
96
|
-
disabled={disabled}
|
|
97
|
-
{...props}
|
|
98
|
-
>
|
|
99
|
-
<legend className={cx('yarcl-field-label', typeClass(config.defaults.labelStyle))}>{label}</legend>
|
|
100
|
-
<div className={cx('yarcl-radio-group-options', `yarcl-radio-group-${orientation}`)}>{children}</div>
|
|
101
|
-
{descriptionId && (
|
|
102
|
-
<p className={cx('yarcl-field-description', typeClass(config.defaults.helperStyle))} id={descriptionId}>
|
|
103
|
-
{description}
|
|
104
|
-
</p>
|
|
105
|
-
)}
|
|
106
|
-
{errorId && (
|
|
107
|
-
<p className={cx('yarcl-field-error', typeClass(config.defaults.helperStyle))} id={errorId}>
|
|
108
|
-
{error}
|
|
109
|
-
</p>
|
|
110
|
-
)}
|
|
111
|
-
</fieldset>
|
|
112
|
-
</RadioGroupContext.Provider>
|
|
113
|
-
);
|
|
114
|
-
}
|
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
autoUpdate,
|
|
3
|
-
FloatingFocusManager,
|
|
4
|
-
FloatingPortal,
|
|
5
|
-
useClick,
|
|
6
|
-
useDismiss,
|
|
7
|
-
useFloating,
|
|
8
|
-
useInteractions,
|
|
9
|
-
useListNavigation,
|
|
10
|
-
useRole,
|
|
11
|
-
useTypeahead,
|
|
12
|
-
} from '@floating-ui/react';
|
|
13
|
-
import { useRef, useState, type ComponentProps, type ReactNode } from 'react';
|
|
14
|
-
import { colorClass, cx, radiusClass, sizeClass } from '../classes';
|
|
15
|
-
import { useFieldProps } from '../field-context';
|
|
16
|
-
import { CheckIcon, ChevronIcon, floatingMiddleware } from '../floating';
|
|
17
|
-
import { useControllable } from '../hooks';
|
|
18
|
-
import type { TokenProps } from '../types';
|
|
19
|
-
import { useDefaults } from '../runtime';
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
/** An option of a {@link Select} or {@link Combobox}. */
|
|
23
|
-
export interface SelectOption<V extends string = string> {
|
|
24
|
-
/** Value reported by `onValueChange` and submitted with forms. */
|
|
25
|
-
value: V;
|
|
26
|
-
/** Text shown to the user. Also used for type-to-select and filtering. */
|
|
27
|
-
label: string;
|
|
28
|
-
/** Prevents choosing this option. */
|
|
29
|
-
disabled?: boolean;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** Props for {@link Select}. */
|
|
33
|
-
export interface SelectProps<V extends string = string>
|
|
34
|
-
extends TokenProps,
|
|
35
|
-
Omit<ComponentProps<'button'>, 'color' | 'value' | 'defaultValue' | 'onChange' | 'children'> {
|
|
36
|
-
/** The options to choose from. */
|
|
37
|
-
options: readonly SelectOption<V>[];
|
|
38
|
-
/** Controlled value. `null` means nothing is selected. */
|
|
39
|
-
value?: V | null;
|
|
40
|
-
/**
|
|
41
|
-
* Initial value when uncontrolled.
|
|
42
|
-
* @default null
|
|
43
|
-
*/
|
|
44
|
-
defaultValue?: V | null;
|
|
45
|
-
/** Called with the chosen option's value. */
|
|
46
|
-
onValueChange?: (value: V | null) => void;
|
|
47
|
-
/** Text shown when nothing is selected. */
|
|
48
|
-
placeholder?: ReactNode;
|
|
49
|
-
/** Form field name. Renders a hidden input with the value. */
|
|
50
|
-
name?: string;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Picks one value from a list, like a native `<select>` but styled from the design tokens.
|
|
55
|
-
* Arrow keys and type-to-select work both open and closed.
|
|
56
|
-
* Shares the size scale with {@link Input} and {@link Button}. Works inside a {@link Field}.
|
|
57
|
-
*
|
|
58
|
-
* @example
|
|
59
|
-
* ```tsx
|
|
60
|
-
* <Select
|
|
61
|
-
* placeholder="Choose a plan"
|
|
62
|
-
* options={[
|
|
63
|
-
* { value: 'free', label: 'Free' },
|
|
64
|
-
* { value: 'pro', label: 'Pro' },
|
|
65
|
-
* ]}
|
|
66
|
-
* onValueChange={setPlan}
|
|
67
|
-
* />
|
|
68
|
-
* ```
|
|
69
|
-
*/
|
|
70
|
-
export function Select<V extends string = string>(props: SelectProps<V>) {
|
|
71
|
-
const own = useDefaults('Select');
|
|
72
|
-
const {
|
|
73
|
-
options,
|
|
74
|
-
value: valueProp,
|
|
75
|
-
defaultValue = null,
|
|
76
|
-
onValueChange,
|
|
77
|
-
placeholder,
|
|
78
|
-
name,
|
|
79
|
-
size,
|
|
80
|
-
radius,
|
|
81
|
-
color,
|
|
82
|
-
className,
|
|
83
|
-
disabled,
|
|
84
|
-
...rest
|
|
85
|
-
} = useFieldProps(props);
|
|
86
|
-
|
|
87
|
-
const [value, setValue] = useControllable<V | null>(valueProp, defaultValue, onValueChange);
|
|
88
|
-
const [open, setOpen] = useState(false);
|
|
89
|
-
const [activeIndex, setActiveIndex] = useState<number | null>(null);
|
|
90
|
-
const listRef = useRef<(HTMLElement | null)[]>([]);
|
|
91
|
-
const labelsRef = useRef<(string | null)[]>([]);
|
|
92
|
-
const typingRef = useRef(false);
|
|
93
|
-
|
|
94
|
-
labelsRef.current = options.map((option) => (option.disabled ? null : option.label));
|
|
95
|
-
const selectedIndex = options.findIndex((option) => option.value === value);
|
|
96
|
-
const selected = selectedIndex >= 0 ? options[selectedIndex] : undefined;
|
|
97
|
-
const disabledIndices = options.flatMap((option, i) => (option.disabled ? [i] : []));
|
|
98
|
-
|
|
99
|
-
function choose(index: number) {
|
|
100
|
-
const option = options[index];
|
|
101
|
-
if (!option || option.disabled) return;
|
|
102
|
-
setValue(option.value);
|
|
103
|
-
setOpen(false);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const { refs, floatingStyles, context } = useFloating({
|
|
107
|
-
open,
|
|
108
|
-
onOpenChange: setOpen,
|
|
109
|
-
placement: 'bottom-start',
|
|
110
|
-
transform: false,
|
|
111
|
-
whileElementsMounted: autoUpdate,
|
|
112
|
-
middleware: floatingMiddleware({ gap: 4, matchWidth: true }),
|
|
113
|
-
});
|
|
114
|
-
const { getReferenceProps, getFloatingProps, getItemProps } = useInteractions([
|
|
115
|
-
useClick(context, { enabled: !disabled }),
|
|
116
|
-
useDismiss(context),
|
|
117
|
-
useRole(context, { role: 'select' }),
|
|
118
|
-
useListNavigation(context, {
|
|
119
|
-
listRef,
|
|
120
|
-
activeIndex,
|
|
121
|
-
selectedIndex: selectedIndex >= 0 ? selectedIndex : null,
|
|
122
|
-
onNavigate: setActiveIndex,
|
|
123
|
-
disabledIndices,
|
|
124
|
-
loop: true,
|
|
125
|
-
}),
|
|
126
|
-
useTypeahead(context, {
|
|
127
|
-
listRef: labelsRef,
|
|
128
|
-
activeIndex,
|
|
129
|
-
selectedIndex: selectedIndex >= 0 ? selectedIndex : null,
|
|
130
|
-
onMatch: open ? setActiveIndex : choose,
|
|
131
|
-
onTypingChange: (typing) => {
|
|
132
|
-
typingRef.current = typing;
|
|
133
|
-
},
|
|
134
|
-
}),
|
|
135
|
-
]);
|
|
136
|
-
|
|
137
|
-
return (
|
|
138
|
-
<>
|
|
139
|
-
<button
|
|
140
|
-
ref={refs.setReference}
|
|
141
|
-
type="button"
|
|
142
|
-
disabled={disabled}
|
|
143
|
-
className={cx('yarcl-input yarcl-select', sizeClass(size ?? own.size), radiusClass(radius ?? own.radius, size ?? own.size), colorClass(color ?? own.color), className)}
|
|
144
|
-
{...getReferenceProps(rest)}
|
|
145
|
-
>
|
|
146
|
-
<span className={cx('yarcl-select-value', !selected && 'yarcl-select-placeholder')}>
|
|
147
|
-
{selected ? selected.label : placeholder}
|
|
148
|
-
</span>
|
|
149
|
-
<ChevronIcon />
|
|
150
|
-
</button>
|
|
151
|
-
{name && <input type="hidden" name={name} value={value ?? ''} />}
|
|
152
|
-
{open && (
|
|
153
|
-
<FloatingPortal>
|
|
154
|
-
<FloatingFocusManager context={context} modal={false}>
|
|
155
|
-
<div
|
|
156
|
-
ref={refs.setFloating}
|
|
157
|
-
style={floatingStyles}
|
|
158
|
-
className={cx('yarcl-floating yarcl-panel yarcl-listbox', sizeClass(size ?? own.size), colorClass(color ?? own.color))}
|
|
159
|
-
{...getFloatingProps()}
|
|
160
|
-
>
|
|
161
|
-
{options.map((option, i) => {
|
|
162
|
-
const isSelected = i === selectedIndex;
|
|
163
|
-
const isActive = i === activeIndex;
|
|
164
|
-
return (
|
|
165
|
-
<div
|
|
166
|
-
key={option.value}
|
|
167
|
-
ref={(node) => {
|
|
168
|
-
listRef.current[i] = node;
|
|
169
|
-
}}
|
|
170
|
-
tabIndex={isActive ? 0 : -1}
|
|
171
|
-
aria-disabled={option.disabled || undefined}
|
|
172
|
-
data-active={isActive || undefined}
|
|
173
|
-
className="yarcl-option"
|
|
174
|
-
{...getItemProps({
|
|
175
|
-
active: isActive,
|
|
176
|
-
selected: isSelected,
|
|
177
|
-
onClick: () => choose(i),
|
|
178
|
-
onKeyDown(event) {
|
|
179
|
-
if (event.key === 'Enter' || (event.key === ' ' && !typingRef.current)) {
|
|
180
|
-
event.preventDefault();
|
|
181
|
-
choose(i);
|
|
182
|
-
}
|
|
183
|
-
},
|
|
184
|
-
})}
|
|
185
|
-
>
|
|
186
|
-
<span className="yarcl-option-label">{option.label}</span>
|
|
187
|
-
{isSelected && <CheckIcon />}
|
|
188
|
-
</div>
|
|
189
|
-
);
|
|
190
|
-
})}
|
|
191
|
-
</div>
|
|
192
|
-
</FloatingFocusManager>
|
|
193
|
-
</FloatingPortal>
|
|
194
|
-
)}
|
|
195
|
-
</>
|
|
196
|
-
);
|
|
197
|
-
}
|