@shadow-js/ui 0.0.0-stage → 1.0.1
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/CHANGELOG.md +30 -0
- package/LICENSE +21 -0
- package/README.md +598 -2
- package/dist/cli.mjs +2281 -0
- package/dist/dev/cli/schema.d.ts +71 -0
- package/dist/dev/index.d.ts +6 -0
- package/dist/dev/index.js +5 -0
- package/dist/dev/locales/ar.d.ts +139 -0
- package/dist/dev/locales/ar.js +127 -0
- package/dist/dev/locales/ar.js.map +1 -0
- package/dist/dev/locales/en.d.ts +139 -0
- package/dist/dev/locales/en.js +127 -0
- package/dist/dev/locales/en.js.map +1 -0
- package/dist/dev/registry/utils/utils.d.ts +27 -0
- package/dist/dev/registry/utils/utils.js +26 -0
- package/dist/dev/registry/utils/utils.js.map +1 -0
- package/dist/dev/theme/theme-logic.d.ts +185 -0
- package/dist/dev/theme/theme-logic.js +183 -0
- package/dist/dev/theme/theme-logic.js.map +1 -0
- package/dist/dev/theme/theme-provider.d.ts +88 -0
- package/dist/dev/theme/theme-provider.js +149 -0
- package/dist/dev/theme/theme-provider.js.map +1 -0
- package/dist/prod/index.js +1 -0
- package/dist/prod/locales/ar.js +2 -0
- package/dist/prod/locales/ar.js.map +1 -0
- package/dist/prod/locales/en.js +2 -0
- package/dist/prod/locales/en.js.map +1 -0
- package/dist/prod/registry/utils/utils.js +2 -0
- package/dist/prod/registry/utils/utils.js.map +1 -0
- package/dist/prod/theme/theme-logic.js +2 -0
- package/dist/prod/theme/theme-logic.js.map +1 -0
- package/dist/prod/theme/theme-provider.js +2 -0
- package/dist/prod/theme/theme-provider.js.map +1 -0
- package/package.json +102 -4
- package/registry/accordion/accordion.tsx +210 -0
- package/registry/accordion/item.json +4 -0
- package/registry/alert/alert.tsx +200 -0
- package/registry/alert/item.json +4 -0
- package/registry/alert-dialog/alert-dialog.tsx +530 -0
- package/registry/alert-dialog/item.json +4 -0
- package/registry/aspect-ratio/aspect-ratio.tsx +71 -0
- package/registry/aspect-ratio/item.json +4 -0
- package/registry/attachment/attachment.tsx +459 -0
- package/registry/attachment/item.json +4 -0
- package/registry/avatar/avatar.tsx +385 -0
- package/registry/avatar/item.json +4 -0
- package/registry/badge/badge.tsx +108 -0
- package/registry/badge/item.json +4 -0
- package/registry/breadcrumb/breadcrumb.tsx +354 -0
- package/registry/breadcrumb/item.json +4 -0
- package/registry/bubble/bubble.tsx +260 -0
- package/registry/bubble/item.json +4 -0
- package/registry/button/button.tsx +125 -0
- package/registry/button/item.json +4 -0
- package/registry/button-group/button-group.tsx +189 -0
- package/registry/button-group/item.json +4 -0
- package/registry/calendar/calendar.tsx +348 -0
- package/registry/calendar/item.json +4 -0
- package/registry/card/card.tsx +311 -0
- package/registry/card/item.json +4 -0
- package/registry/carousel/carousel.tsx +851 -0
- package/registry/carousel/item.json +4 -0
- package/registry/chart/chart.tsx +1956 -0
- package/registry/chart/item.json +4 -0
- package/registry/chart-scale/chart-scale.ts +670 -0
- package/registry/chart-scale/item.json +5 -0
- package/registry/checkbox/checkbox.tsx +79 -0
- package/registry/checkbox/item.json +4 -0
- package/registry/code-block/code-block.tsx +470 -0
- package/registry/code-block/item.json +4 -0
- package/registry/collapsible/collapsible.tsx +117 -0
- package/registry/collapsible/item.json +4 -0
- package/registry/combobox/combobox.tsx +977 -0
- package/registry/combobox/item.json +4 -0
- package/registry/command/command.tsx +757 -0
- package/registry/command/item.json +4 -0
- package/registry/context-menu/context-menu.tsx +697 -0
- package/registry/context-menu/item.json +4 -0
- package/registry/data-table/data-table.tsx +437 -0
- package/registry/data-table/item.json +4 -0
- package/registry/date-picker/date-picker.tsx +349 -0
- package/registry/date-picker/item.json +4 -0
- package/registry/dialog/dialog.tsx +412 -0
- package/registry/dialog/item.json +4 -0
- package/registry/direction/direction.tsx +88 -0
- package/registry/direction/item.json +4 -0
- package/registry/drawer/drawer.tsx +787 -0
- package/registry/drawer/item.json +4 -0
- package/registry/dropdown-menu/dropdown-menu.tsx +692 -0
- package/registry/dropdown-menu/item.json +4 -0
- package/registry/empty/empty.tsx +240 -0
- package/registry/empty/item.json +4 -0
- package/registry/field/field.tsx +624 -0
- package/registry/field/item.json +4 -0
- package/registry/form/form.tsx +1112 -0
- package/registry/form/item.json +4 -0
- package/registry/hover-card/hover-card.tsx +144 -0
- package/registry/hover-card/item.json +4 -0
- package/registry/icons/icons.tsx +1691 -0
- package/registry/icons/item.json +4 -0
- package/registry/input/input.tsx +43 -0
- package/registry/input/item.json +4 -0
- package/registry/input-group/input-group.tsx +315 -0
- package/registry/input-group/item.json +4 -0
- package/registry/input-otp/input-otp.tsx +329 -0
- package/registry/input-otp/item.json +4 -0
- package/registry/item/item.json +4 -0
- package/registry/item/item.tsx +439 -0
- package/registry/kbd/item.json +4 -0
- package/registry/kbd/kbd.tsx +65 -0
- package/registry/label/item.json +4 -0
- package/registry/label/label.tsx +60 -0
- package/registry/marker/item.json +4 -0
- package/registry/marker/marker.tsx +175 -0
- package/registry/menubar/item.json +4 -0
- package/registry/menubar/menubar.tsx +694 -0
- package/registry/message/item.json +4 -0
- package/registry/message/message.tsx +280 -0
- package/registry/message-scroller/item.json +4 -0
- package/registry/message-scroller/message-scroller.tsx +262 -0
- package/registry/native-select/item.json +4 -0
- package/registry/native-select/native-select.tsx +113 -0
- package/registry/navigation-menu/item.json +4 -0
- package/registry/navigation-menu/navigation-menu.tsx +362 -0
- package/registry/pagination/item.json +4 -0
- package/registry/pagination/pagination.tsx +460 -0
- package/registry/password-input/item.json +5 -0
- package/registry/password-input/password-input.tsx +494 -0
- package/registry/popover/item.json +4 -0
- package/registry/popover/popover.tsx +284 -0
- package/registry/progress/item.json +4 -0
- package/registry/progress/progress.tsx +190 -0
- package/registry/questionnaire/item.json +4 -0
- package/registry/questionnaire/questionnaire.tsx +569 -0
- package/registry/radio-group/item.json +4 -0
- package/registry/radio-group/radio-group.tsx +101 -0
- package/registry/resizable/item.json +4 -0
- package/registry/resizable/resizable.tsx +455 -0
- package/registry/scroll-area/item.json +4 -0
- package/registry/scroll-area/scroll-area.tsx +149 -0
- package/registry/select/item.json +4 -0
- package/registry/select/select.tsx +571 -0
- package/registry/separator/item.json +4 -0
- package/registry/separator/separator.tsx +52 -0
- package/registry/sheet/item.json +4 -0
- package/registry/sheet/sheet.tsx +476 -0
- package/registry/sidebar/item.json +4 -0
- package/registry/sidebar/sidebar.tsx +1870 -0
- package/registry/skeleton/item.json +4 -0
- package/registry/skeleton/skeleton.tsx +47 -0
- package/registry/slider/item.json +4 -0
- package/registry/slider/slider.tsx +149 -0
- package/registry/spinner/item.json +4 -0
- package/registry/spinner/spinner.tsx +51 -0
- package/registry/switch/item.json +4 -0
- package/registry/switch/switch.tsx +93 -0
- package/registry/table/item.json +4 -0
- package/registry/table/table.tsx +373 -0
- package/registry/tabs/item.json +4 -0
- package/registry/tabs/tabs.tsx +209 -0
- package/registry/textarea/item.json +4 -0
- package/registry/textarea/textarea.tsx +51 -0
- package/registry/toast/item.json +4 -0
- package/registry/toast/toast.tsx +949 -0
- package/registry/toggle/item.json +4 -0
- package/registry/toggle/toggle.tsx +110 -0
- package/registry/toggle-group/item.json +4 -0
- package/registry/toggle-group/toggle-group.tsx +189 -0
- package/registry/tooltip/item.json +4 -0
- package/registry/tooltip/tooltip.tsx +169 -0
- package/registry/trace/item.json +4 -0
- package/registry/trace/trace.tsx +356 -0
- package/registry/ui-strings/item.json +5 -0
- package/registry/ui-strings/ui-strings.tsx +454 -0
- package/registry/use-highlighted-code/item.json +5 -0
- package/registry/use-highlighted-code/use-highlighted-code.tsx +145 -0
- package/registry/use-mobile/item.json +5 -0
- package/registry/use-mobile/use-mobile.ts +46 -0
- package/registry/utils/item.json +5 -0
- package/registry/utils/utils.ts +38 -0
- package/registry.json +3274 -0
- package/styles/animations.css +321 -0
- package/styles/theme-core.css +55 -0
- package/styles/theme.css +12 -0
- package/styles/themes/gray.css +69 -0
- package/styles/themes/neutral.css +69 -0
- package/styles/themes/slate.css +69 -0
- package/styles/themes/stone.css +69 -0
- package/styles/themes/zinc.css +69 -0
- package/styles/typeset.css +251 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { splitProps } from "@shadow-js/core";
|
|
2
|
+
import type { ComponentProps, JSX } from "@shadow-js/web";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A placeholder shaped like the content that is loading: a pulsing block that you size with
|
|
7
|
+
* `class`. The placeholder is decorative, so it is hidden from assistive technology
|
|
8
|
+
* (`aria-hidden="true"`). Mark the region that loads with `aria-busy="true"`, or announce the
|
|
9
|
+
* loading state with a status (see {@link Spinner}). Under reduced motion the pulse stops.
|
|
10
|
+
*
|
|
11
|
+
* Keyboard and ARIA: no keys and no role. The element is `aria-hidden="true"`, so the loading
|
|
12
|
+
* state must be announced by the region around it, not by the placeholder.
|
|
13
|
+
*
|
|
14
|
+
* shadcn/ui differences: `class` replaces `className`. Unlike shadcn/ui, the placeholder is
|
|
15
|
+
* `aria-hidden="true"` by default.
|
|
16
|
+
*
|
|
17
|
+
* @param props - The props: the attributes of the `div`. `class` is merged with the base classes.
|
|
18
|
+
* @returns The placeholder `div`.
|
|
19
|
+
* @example
|
|
20
|
+
* ```tsx
|
|
21
|
+
* import { Skeleton } from "@/components/ui/skeleton";
|
|
22
|
+
*
|
|
23
|
+
* export function CardSkeleton() {
|
|
24
|
+
* return (
|
|
25
|
+
* <div aria-busy="true" class="flex items-center gap-4">
|
|
26
|
+
* <Skeleton class="size-12 rounded-full" />
|
|
27
|
+
* <Skeleton class="h-4 w-[250px]" />
|
|
28
|
+
* </div>
|
|
29
|
+
* );
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
* @since 1.0.0
|
|
33
|
+
* @see {@link Spinner}
|
|
34
|
+
*/
|
|
35
|
+
function Skeleton(props: ComponentProps<"div">): JSX.Element {
|
|
36
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
37
|
+
return (
|
|
38
|
+
<div
|
|
39
|
+
data-slot="skeleton"
|
|
40
|
+
aria-hidden="true"
|
|
41
|
+
class={cn("animate-pulse rounded-md bg-accent motion-reduce:animate-none", local.class)}
|
|
42
|
+
{...rest}
|
|
43
|
+
/>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export { Skeleton };
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { splitProps } from "@shadow-js/core";
|
|
2
|
+
import {
|
|
3
|
+
type PolymorphicTarget,
|
|
4
|
+
Slider as SliderPrimitive,
|
|
5
|
+
type SliderProps as SliderPrimitiveProps,
|
|
6
|
+
} from "@shadow-js/primitives";
|
|
7
|
+
import { For, type JSX } from "@shadow-js/web";
|
|
8
|
+
import { type ClassValue, cn } from "@/lib/utils";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Props that {@link Slider} adds to the primitive's props. Use them to name the thumbs and to
|
|
12
|
+
* give a value text.
|
|
13
|
+
* @since 1.0.0
|
|
14
|
+
* @see {@link Slider}
|
|
15
|
+
*/
|
|
16
|
+
export interface SliderExtraProps {
|
|
17
|
+
/**
|
|
18
|
+
* The accessible name of each thumb of a range slider, by thumb index (for example
|
|
19
|
+
* `["Minimum price", "Maximum price"]`). Without it a range of two thumbs is named "Minimum" and
|
|
20
|
+
* "Maximum". A single-thumb slider is named by `aria-label` or `aria-labelledby` instead.
|
|
21
|
+
* @default none (a range of two thumbs is named "Minimum" and "Maximum")
|
|
22
|
+
*/
|
|
23
|
+
readonly thumbLabels?: readonly string[] | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* The `aria-valuetext` of each thumb of a range, by thumb index (for example
|
|
26
|
+
* `["40 events per second"]`). It is set on the thumb, the element with `role="slider"`, and
|
|
27
|
+
* never on the group that wraps a range.
|
|
28
|
+
* @default none
|
|
29
|
+
*/
|
|
30
|
+
readonly thumbValueTexts?: readonly string[] | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* The value text of a single-thumb slider, for example `"30 percent"`. It is set on the thumb.
|
|
33
|
+
* @default none
|
|
34
|
+
*/
|
|
35
|
+
readonly "aria-valuetext"?: string | undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** The props as the implementation sees them. */
|
|
39
|
+
type Loose = {
|
|
40
|
+
readonly class?: ClassValue;
|
|
41
|
+
readonly value?: readonly number[] | undefined;
|
|
42
|
+
readonly defaultValue?: readonly number[] | undefined;
|
|
43
|
+
readonly min?: number | undefined;
|
|
44
|
+
readonly thumbLabels?: readonly string[] | undefined;
|
|
45
|
+
readonly thumbValueTexts?: readonly string[] | undefined;
|
|
46
|
+
readonly "aria-label"?: string | undefined;
|
|
47
|
+
readonly "aria-labelledby"?: string | undefined;
|
|
48
|
+
readonly "aria-valuetext"?: string | undefined;
|
|
49
|
+
readonly [prop: string]: unknown;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Props of {@link Slider}: the primitive's props (`value`, `defaultValue`, `onValueChange`,
|
|
54
|
+
* `onValueCommit`, `min`, `max`, `step`, `orientation`, `dir`, `inverted`, `disabled`, `name` and
|
|
55
|
+
* others), plus the {@link SliderExtraProps}. The remaining props are the attributes of the root
|
|
56
|
+
* element.
|
|
57
|
+
* @since 1.0.0
|
|
58
|
+
* @see {@link Slider}
|
|
59
|
+
*/
|
|
60
|
+
export type SliderProps<T extends PolymorphicTarget = "span"> = SliderPrimitiveProps<T> &
|
|
61
|
+
SliderExtraProps;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Selects a number, or a range of several numbers, by dragging a thumb along a track or with the
|
|
65
|
+
* keyboard. The number of thumbs is the length of `value` or `defaultValue` (one thumb at `min` when
|
|
66
|
+
* neither is given). Use `value` and `onValueChange` to control it, or `defaultValue` to start it.
|
|
67
|
+
*
|
|
68
|
+
* Keyboard and ARIA: each thumb is a focusable element with `role="slider"`. Arrow keys move one
|
|
69
|
+
* step (the arrow that raises the value follows the direction, `inverted` and the orientation).
|
|
70
|
+
* Shift+Arrow and PageUp or PageDown move ten steps, and Home and End jump to the ends. The
|
|
71
|
+
* thumb sets `aria-valuemin`, `aria-valuenow`, `aria-valuemax` and `aria-orientation`. While
|
|
72
|
+
* disabled it leaves the tab order and has `aria-disabled="true"`. A single slider is named by
|
|
73
|
+
* `aria-label` or `aria-labelledby`, which the component puts on its thumb. A range is a
|
|
74
|
+
* `role="group"` named by `aria-label` or `aria-labelledby`, and each of its thumbs is named by
|
|
75
|
+
* `thumbLabels` (default "Minimum" and "Maximum"). A press on the track moves the nearest thumb to
|
|
76
|
+
* the pointer, and the drag continues outside the slider.
|
|
77
|
+
*
|
|
78
|
+
* shadcn/ui differences: `class` replaces `className`. Each thumb gets its own accessible name
|
|
79
|
+
* here (`thumbLabels`, or `aria-label` for a single thumb). shadcn/ui does not name the thumbs.
|
|
80
|
+
*
|
|
81
|
+
* @param props - The props, see {@link SliderProps}.
|
|
82
|
+
* @returns The slider root, with the track, the range and one thumb per value.
|
|
83
|
+
* @example
|
|
84
|
+
* ```tsx
|
|
85
|
+
* import { Slider } from "@/components/ui/slider";
|
|
86
|
+
*
|
|
87
|
+
* export function VolumeControl() {
|
|
88
|
+
* return <Slider defaultValue={[50]} max={100} step={1} aria-label="Volume" class="w-64" />;
|
|
89
|
+
* }
|
|
90
|
+
* ```
|
|
91
|
+
* @since 1.0.0
|
|
92
|
+
* @see {@link SliderProps}
|
|
93
|
+
*/
|
|
94
|
+
function Slider<T extends PolymorphicTarget = "span">(props: SliderProps<T>): JSX.Element {
|
|
95
|
+
const [local, rest] = splitProps(props as unknown as Loose, [
|
|
96
|
+
"class",
|
|
97
|
+
"value",
|
|
98
|
+
"defaultValue",
|
|
99
|
+
"min",
|
|
100
|
+
"thumbLabels",
|
|
101
|
+
"thumbValueTexts",
|
|
102
|
+
"aria-label",
|
|
103
|
+
"aria-labelledby",
|
|
104
|
+
"aria-valuetext",
|
|
105
|
+
]);
|
|
106
|
+
const values = (): readonly number[] => local.value ?? local.defaultValue ?? [local.min ?? 0];
|
|
107
|
+
const single = (): boolean => values().length === 1;
|
|
108
|
+
return (
|
|
109
|
+
<SliderPrimitive
|
|
110
|
+
data-slot="slider"
|
|
111
|
+
value={local.value}
|
|
112
|
+
defaultValue={local.defaultValue}
|
|
113
|
+
min={local.min}
|
|
114
|
+
role={single() ? undefined : "group"}
|
|
115
|
+
aria-label={single() ? undefined : local["aria-label"]}
|
|
116
|
+
aria-labelledby={single() ? undefined : local["aria-labelledby"]}
|
|
117
|
+
class={cn(
|
|
118
|
+
"relative flex w-full touch-none items-center select-none data-[disabled]:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col",
|
|
119
|
+
local.class,
|
|
120
|
+
)}
|
|
121
|
+
{...rest}
|
|
122
|
+
>
|
|
123
|
+
<SliderPrimitive.Track
|
|
124
|
+
data-slot="slider-track"
|
|
125
|
+
class="relative grow overflow-hidden rounded-full bg-muted data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5 forced-colors:border forced-colors:border-[ButtonText] forced-colors:bg-[Canvas]"
|
|
126
|
+
>
|
|
127
|
+
<SliderPrimitive.Range
|
|
128
|
+
data-slot="slider-range"
|
|
129
|
+
class="absolute bg-primary data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full forced-colors:bg-[Highlight]"
|
|
130
|
+
/>
|
|
131
|
+
</SliderPrimitive.Track>
|
|
132
|
+
<For each={values().map((_, index) => index)}>
|
|
133
|
+
{(index: number) => (
|
|
134
|
+
<SliderPrimitive.Thumb
|
|
135
|
+
data-slot="slider-thumb"
|
|
136
|
+
aria-label={single() ? local["aria-label"] : local.thumbLabels?.[index]}
|
|
137
|
+
aria-labelledby={single() ? local["aria-labelledby"] : undefined}
|
|
138
|
+
aria-valuetext={
|
|
139
|
+
local.thumbValueTexts?.[index] ?? (single() ? local["aria-valuetext"] : undefined)
|
|
140
|
+
}
|
|
141
|
+
class="block size-4 shrink-0 rounded-full border border-primary bg-white shadow-sm ring-ring/50 transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden motion-reduce:transition-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 forced-colors:border-[ButtonText] forced-colors:bg-[ButtonFace]"
|
|
142
|
+
/>
|
|
143
|
+
)}
|
|
144
|
+
</For>
|
|
145
|
+
</SliderPrimitive>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export { Slider };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { splitProps } from "@shadow-js/core";
|
|
2
|
+
import type { JSX } from "@shadow-js/web";
|
|
3
|
+
import { type IconProps, LoaderIcon } from "@/components/ui/icons";
|
|
4
|
+
import { useUiStrings } from "@/lib/ui-strings";
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A loading indicator: a spinning arc that assistive technology announces as "Loading". It is an
|
|
9
|
+
* `svg` with `role="status"`. It is sized with `class` (the default is `size-4`, the icon size).
|
|
10
|
+
* Under reduced motion it turns slowly instead of stopping, so it still reads as busy.
|
|
11
|
+
*
|
|
12
|
+
* Keyboard and ARIA: no keys. The element is `role="status"` with `aria-label="Loading"` by
|
|
13
|
+
* default; pass your own `aria-label` (for example "Saving") to name what is loading.
|
|
14
|
+
*
|
|
15
|
+
* shadcn/ui differences: `class` replaces `className`. The spinner also keeps turning slowly under
|
|
16
|
+
* reduced motion, which shadcn/ui does not do.
|
|
17
|
+
*
|
|
18
|
+
* @param props - The props: the attributes of the `svg`, such as `class` and `aria-label`.
|
|
19
|
+
* @returns The spinner `svg`.
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* import { Button } from "@/components/ui/button";
|
|
23
|
+
* import { Spinner } from "@/components/ui/spinner";
|
|
24
|
+
*
|
|
25
|
+
* export function SaveButton() {
|
|
26
|
+
* return (
|
|
27
|
+
* <Button disabled>
|
|
28
|
+
* <Spinner />
|
|
29
|
+
* Saving
|
|
30
|
+
* </Button>
|
|
31
|
+
* );
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
* @since 1.0.0
|
|
35
|
+
*/
|
|
36
|
+
function Spinner(props: IconProps): JSX.Element {
|
|
37
|
+
const [local, rest] = splitProps(props, ["class"]);
|
|
38
|
+
const t = useUiStrings();
|
|
39
|
+
return (
|
|
40
|
+
<LoaderIcon
|
|
41
|
+
role="status"
|
|
42
|
+
aria-label={t("spinner.loading", "Loading")}
|
|
43
|
+
aria-hidden="false"
|
|
44
|
+
data-slot="spinner"
|
|
45
|
+
class={cn("size-4 animate-spin motion-reduce:animate-[spin_3s_linear_infinite]", local.class)}
|
|
46
|
+
{...rest}
|
|
47
|
+
/>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { Spinner };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { splitProps } from "@shadow-js/core";
|
|
2
|
+
import {
|
|
3
|
+
type PolymorphicTarget,
|
|
4
|
+
Switch as SwitchPrimitive,
|
|
5
|
+
type SwitchProps as SwitchPrimitiveProps,
|
|
6
|
+
} from "@shadow-js/primitives";
|
|
7
|
+
import type { JSX } from "@shadow-js/web";
|
|
8
|
+
import { type ClassValue, cn } from "@/lib/utils";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Props of {@link Switch}: the primitive's props (`checked`, `defaultChecked`, `onCheckedChange`,
|
|
12
|
+
* `name`, `value`, `required`, `disabled`, `form`), `size` (`"default"` or `"sm"`), `as` to render
|
|
13
|
+
* another element, and the attributes of the rendered element.
|
|
14
|
+
* @since 1.0.0
|
|
15
|
+
* @see {@link Switch}
|
|
16
|
+
*/
|
|
17
|
+
export type SwitchProps<T extends PolymorphicTarget = "button"> = SwitchPrimitiveProps<T> &
|
|
18
|
+
SwitchExtraProps;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The switch's own props: its size.
|
|
22
|
+
* @since 1.0.0
|
|
23
|
+
* @see {@link Switch}
|
|
24
|
+
*/
|
|
25
|
+
export interface SwitchExtraProps {
|
|
26
|
+
/**
|
|
27
|
+
* The size of the switch: `default` is 20px high and 32px wide, `sm` is 14px high and 24px wide.
|
|
28
|
+
* @default "default"
|
|
29
|
+
*/
|
|
30
|
+
readonly size?: "sm" | "default" | undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The props as the implementation sees them. */
|
|
34
|
+
type Loose = {
|
|
35
|
+
readonly class?: ClassValue;
|
|
36
|
+
readonly size?: "sm" | "default" | undefined;
|
|
37
|
+
readonly [prop: string]: unknown;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A control that switches one setting on or off. Controlled with `checked` and `onCheckedChange`,
|
|
42
|
+
* or uncontrolled with `defaultChecked`. With a `name` it takes part in native forms. The thumb
|
|
43
|
+
* moves the right way in right-to-left layouts.
|
|
44
|
+
*
|
|
45
|
+
* Keyboard and ARIA: Tab moves focus to the switch. Space or Enter toggles it. It has
|
|
46
|
+
* `role="switch"` and `aria-checked` (`"true"` or `"false"`), and `data-state` (`"checked"` or
|
|
47
|
+
* `"unchecked"`). A disabled switch is a disabled button. `required` sets `aria-required`. Name it
|
|
48
|
+
* with a {@link Label} (`id` on the switch, `htmlFor` on the label) or with `aria-label`. Clicking
|
|
49
|
+
* the label toggles the switch.
|
|
50
|
+
*
|
|
51
|
+
* shadcn/ui differences: `as` replaces `asChild` for choosing the rendered element, and `class`
|
|
52
|
+
* replaces `className`. Each part has its `data-slot` (`switch` and `switch-thumb`).
|
|
53
|
+
*
|
|
54
|
+
* @param props - The props, see {@link SwitchProps}.
|
|
55
|
+
* @returns The switch element with its thumb inside.
|
|
56
|
+
* @example
|
|
57
|
+
* ```tsx
|
|
58
|
+
* import { Label } from "@/components/ui/label";
|
|
59
|
+
* import { Switch } from "@/components/ui/switch";
|
|
60
|
+
*
|
|
61
|
+
* export function AirplaneMode() {
|
|
62
|
+
* return (
|
|
63
|
+
* <div class="flex items-center gap-2">
|
|
64
|
+
* <Switch id="airplane" onCheckedChange={(on: boolean) => console.log(on)} />
|
|
65
|
+
* <Label htmlFor="airplane">Airplane mode</Label>
|
|
66
|
+
* </div>
|
|
67
|
+
* );
|
|
68
|
+
* }
|
|
69
|
+
* ```
|
|
70
|
+
* @since 1.0.0
|
|
71
|
+
* @see {@link SwitchProps}
|
|
72
|
+
*/
|
|
73
|
+
function Switch<T extends PolymorphicTarget = "button">(props: SwitchProps<T>): JSX.Element {
|
|
74
|
+
const [local, rest] = splitProps(props as unknown as Loose, ["class", "size"]);
|
|
75
|
+
return (
|
|
76
|
+
<SwitchPrimitive
|
|
77
|
+
data-slot="switch"
|
|
78
|
+
data-size={local.size ?? "default"}
|
|
79
|
+
class={cn(
|
|
80
|
+
"peer group/switch inline-flex h-[1.15rem] w-8 data-[size=sm]:h-3.5 data-[size=sm]:w-6 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 motion-reduce:transition-none data-[state=checked]:bg-primary data-[state=unchecked]:bg-input dark:data-[state=unchecked]:bg-input/80 forced-colors:border-[ButtonText] forced-colors:data-[state=checked]:bg-[Highlight] forced-colors:data-[state=unchecked]:bg-[Canvas] focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-transparent",
|
|
81
|
+
local.class,
|
|
82
|
+
)}
|
|
83
|
+
{...rest}
|
|
84
|
+
>
|
|
85
|
+
<SwitchPrimitive.Thumb
|
|
86
|
+
data-slot="switch-thumb"
|
|
87
|
+
class="pointer-events-none block size-4 group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 rounded-full bg-background ring-0 transition-transform motion-reduce:transition-none data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0 rtl:data-[state=checked]:-translate-x-[calc(100%-2px)] dark:data-[state=checked]:bg-primary-foreground dark:data-[state=unchecked]:bg-foreground forced-colors:bg-[ButtonText] forced-colors:data-[state=checked]:bg-[HighlightText]"
|
|
88
|
+
/>
|
|
89
|
+
</SwitchPrimitive>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export { Switch };
|