@shadow-js/ui 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/LICENSE +21 -0
- package/README.md +598 -2
- package/dist/cli.mjs +2293 -0
- package/dist/dev/cli/schema.d.ts +73 -0
- package/dist/dev/index.d.ts +6 -0
- package/dist/dev/index.js +5 -0
- package/dist/dev/locales/ar.d.ts +141 -0
- package/dist/dev/locales/ar.js +128 -0
- package/dist/dev/locales/ar.js.map +1 -0
- package/dist/dev/locales/en.d.ts +141 -0
- package/dist/dev/locales/en.js +128 -0
- package/dist/dev/locales/en.js.map +1 -0
- package/dist/dev/registry/utils/utils.d.ts +29 -0
- package/dist/dev/registry/utils/utils.js +27 -0
- package/dist/dev/registry/utils/utils.js.map +1 -0
- package/dist/dev/theme/theme-logic.d.ts +187 -0
- package/dist/dev/theme/theme-logic.js +184 -0
- package/dist/dev/theme/theme-logic.js.map +1 -0
- package/dist/dev/theme/theme-provider.d.ts +90 -0
- package/dist/dev/theme/theme-provider.js +150 -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,110 @@
|
|
|
1
|
+
import { splitProps } from "@shadow-js/core";
|
|
2
|
+
import {
|
|
3
|
+
type PolymorphicTarget,
|
|
4
|
+
Toggle as TogglePrimitive,
|
|
5
|
+
type ToggleProps as TogglePrimitiveProps,
|
|
6
|
+
} from "@shadow-js/primitives";
|
|
7
|
+
import type { JSX } from "@shadow-js/web";
|
|
8
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
9
|
+
import { type ClassValue, cn } from "@/lib/utils";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The class list of a toggle, built from `variant` and `size`. Use it to style another element
|
|
13
|
+
* (for example a link) like a toggle. The pressed look comes from `data-state="on"`, which
|
|
14
|
+
* {@link Toggle} sets.
|
|
15
|
+
*
|
|
16
|
+
* @param props - Optional `variant` (`"default"` or `"outline"`) and `size` (`"default"`, `"sm"` or
|
|
17
|
+
* `"lg"`), and an optional `class` to add.
|
|
18
|
+
* @returns The class string.
|
|
19
|
+
* @example
|
|
20
|
+
* ```tsx
|
|
21
|
+
* import { toggleVariants } from "@/components/ui/toggle";
|
|
22
|
+
*
|
|
23
|
+
* export function BoldLink() {
|
|
24
|
+
* return (
|
|
25
|
+
* <a href="/bold" class={toggleVariants({ variant: "outline", size: "sm" })}>
|
|
26
|
+
* Bold
|
|
27
|
+
* </a>
|
|
28
|
+
* );
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
* @since 1.0.0
|
|
32
|
+
* @see {@link Toggle}
|
|
33
|
+
*/
|
|
34
|
+
const toggleVariants = cva(
|
|
35
|
+
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none hover:bg-muted hover:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 motion-reduce:transition-none data-[state=on]:bg-accent data-[state=on]:text-accent-foreground dark:aria-invalid:ring-destructive/40 forced-colors:data-[state=on]:bg-[Highlight] forced-colors:data-[state=on]:text-[HighlightText] [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-transparent",
|
|
36
|
+
{
|
|
37
|
+
variants: {
|
|
38
|
+
variant: {
|
|
39
|
+
default: "bg-transparent",
|
|
40
|
+
outline:
|
|
41
|
+
"border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground",
|
|
42
|
+
},
|
|
43
|
+
size: {
|
|
44
|
+
default: "h-9 min-w-9 px-2",
|
|
45
|
+
sm: "h-8 min-w-8 px-1.5",
|
|
46
|
+
lg: "h-10 min-w-10 px-2.5",
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
defaultVariants: {
|
|
50
|
+
variant: "default",
|
|
51
|
+
size: "default",
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
/** The props as the implementation sees them. */
|
|
57
|
+
type Loose = VariantProps<typeof toggleVariants> & {
|
|
58
|
+
readonly class?: ClassValue;
|
|
59
|
+
readonly [prop: string]: unknown;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Props of {@link Toggle}: the primitive's props (`pressed`, `defaultPressed`, `onPressedChange`,
|
|
64
|
+
* `disabled`), `variant` (`"default"` or `"outline"`) and `size` (`"default"`, `"sm"` or `"lg"`),
|
|
65
|
+
* and the attributes of the rendered `button`. `as` renders another element instead.
|
|
66
|
+
* @since 1.0.0
|
|
67
|
+
* @see {@link Toggle}
|
|
68
|
+
*/
|
|
69
|
+
export type ToggleProps<T extends PolymorphicTarget = "button"> = TogglePrimitiveProps<T> &
|
|
70
|
+
VariantProps<typeof toggleVariants>;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* A two-state button that is either pressed (on) or not. Controlled with `pressed` and
|
|
74
|
+
* `onPressedChange`, or uncontrolled with `defaultPressed`. Pick a `variant` and a `size`.
|
|
75
|
+
*
|
|
76
|
+
* Keyboard and ARIA: Space, Enter and a click toggle it. It is a `button` with `aria-pressed`
|
|
77
|
+
* (`"true"` or `"false"`) and `data-state` (`"on"` or `"off"`). Disabled, it is a disabled button.
|
|
78
|
+
* A toggle that shows only an icon needs an `aria-label`, because the icon has no text to name it.
|
|
79
|
+
*
|
|
80
|
+
* shadcn/ui differences: `class` replaces `className`, and `as` replaces `asChild`.
|
|
81
|
+
*
|
|
82
|
+
* @param props - The props, see {@link ToggleProps}.
|
|
83
|
+
* @returns The toggle `button`.
|
|
84
|
+
* @example
|
|
85
|
+
* ```tsx
|
|
86
|
+
* import { Toggle } from "@/components/ui/toggle";
|
|
87
|
+
*
|
|
88
|
+
* export function BoldToggle() {
|
|
89
|
+
* return (
|
|
90
|
+
* <Toggle aria-label="Toggle bold" variant="outline" onPressedChange={(on: boolean) => console.log(on)}>
|
|
91
|
+
* B
|
|
92
|
+
* </Toggle>
|
|
93
|
+
* );
|
|
94
|
+
* }
|
|
95
|
+
* ```
|
|
96
|
+
* @since 1.0.0
|
|
97
|
+
* @see {@link toggleVariants}
|
|
98
|
+
*/
|
|
99
|
+
function Toggle<T extends PolymorphicTarget = "button">(props: ToggleProps<T>): JSX.Element {
|
|
100
|
+
const [local, rest] = splitProps(props as unknown as Loose, ["class", "variant", "size"]);
|
|
101
|
+
return (
|
|
102
|
+
<TogglePrimitive
|
|
103
|
+
data-slot="toggle"
|
|
104
|
+
class={cn(toggleVariants({ variant: local.variant, size: local.size }), local.class)}
|
|
105
|
+
{...rest}
|
|
106
|
+
/>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export { Toggle, toggleVariants };
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { createContext, splitProps, useContext } from "@shadow-js/core";
|
|
2
|
+
import {
|
|
3
|
+
type PolymorphicTarget,
|
|
4
|
+
type ToggleGroupItemProps as ToggleGroupItemPrimitiveProps,
|
|
5
|
+
ToggleGroup as ToggleGroupPrimitive,
|
|
6
|
+
type ToggleGroupProps as ToggleGroupPrimitiveProps,
|
|
7
|
+
} from "@shadow-js/primitives";
|
|
8
|
+
import type { JSX } from "@shadow-js/web";
|
|
9
|
+
import type { VariantProps } from "class-variance-authority";
|
|
10
|
+
import { toggleVariants } from "@/components/ui/toggle";
|
|
11
|
+
import { type ClassValue, cn } from "@/lib/utils";
|
|
12
|
+
|
|
13
|
+
type ToggleVariants = VariantProps<typeof toggleVariants>;
|
|
14
|
+
|
|
15
|
+
/** What the items read from their group. */
|
|
16
|
+
interface ToggleGroupContextValue {
|
|
17
|
+
readonly variant: () => ToggleVariants["variant"];
|
|
18
|
+
readonly size: () => ToggleVariants["size"];
|
|
19
|
+
readonly spacing: () => number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const ToggleGroupContext = createContext<ToggleGroupContextValue>({
|
|
23
|
+
variant: () => "default",
|
|
24
|
+
size: () => "default",
|
|
25
|
+
spacing: () => 0,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
/** The props as the implementation sees them. */
|
|
29
|
+
type Loose = ToggleVariants & {
|
|
30
|
+
readonly class?: ClassValue;
|
|
31
|
+
readonly children?: JSX.Element;
|
|
32
|
+
readonly [prop: string]: unknown;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Props of {@link ToggleGroup}: the primitive's props (`type="single" | "multiple"`, `value`,
|
|
37
|
+
* `defaultValue`, `onValueChange`, `disabled`, `orientation`, `dir`, `loop`, `rovingFocus`),
|
|
38
|
+
* `variant` and `size` (inherited by the items) and the attributes of the rendered `div`.
|
|
39
|
+
* @since 1.0.0
|
|
40
|
+
* @see {@link ToggleGroup}
|
|
41
|
+
*/
|
|
42
|
+
export type ToggleGroupProps<T extends PolymorphicTarget = "div"> = ToggleGroupPrimitiveProps<T> &
|
|
43
|
+
ToggleVariants & {
|
|
44
|
+
/**
|
|
45
|
+
* The gap between the items, in units of the spacing scale (`spacing={2}` is 0.5 rem). `0` keeps the
|
|
46
|
+
* items connected: they share their borders and only the two ends are rounded. Any other value puts
|
|
47
|
+
* a gap between them and every item keeps its own corners and borders.
|
|
48
|
+
* @default 0
|
|
49
|
+
* @see {@link ToggleGroup}
|
|
50
|
+
*/
|
|
51
|
+
readonly spacing?: number | undefined;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Props of {@link ToggleGroupItem}: the primitive's props (`value`, `disabled`), `variant` and
|
|
56
|
+
* `size` (they win over the group's) and the attributes of the rendered `button`.
|
|
57
|
+
* @since 1.0.0
|
|
58
|
+
* @see {@link ToggleGroupItem}
|
|
59
|
+
*/
|
|
60
|
+
export type ToggleGroupItemProps<T extends PolymorphicTarget = "button"> =
|
|
61
|
+
ToggleGroupItemPrimitiveProps<T> & ToggleVariants;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* A row of toggles that share one look. With `type="single"` at most one item is pressed, and the
|
|
65
|
+
* items are radios. With `type="multiple"` any number can be pressed. Control it with `value` and
|
|
66
|
+
* `onValueChange`, or start it with `defaultValue`. A single group reports one string (`""` when
|
|
67
|
+
* the pressed item is pressed again), and a multiple group reports an array. `variant` and `size`
|
|
68
|
+
* reach every item, unless an item sets its own.
|
|
69
|
+
*
|
|
70
|
+
* Keyboard and ARIA: the root is a `role="group"` and needs a name from you (`aria-label` or
|
|
71
|
+
* `aria-labelledby`). The group is one tab stop: the item that had focus last, else the pressed
|
|
72
|
+
* item, else the first enabled item. The arrow keys move between items (all four arrows unless
|
|
73
|
+
* `orientation` is set; Left and Right swap in a right-to-left group), Home and End go to the ends,
|
|
74
|
+
* and the group wraps and skips disabled items. Space and Enter press the focused item. In a single
|
|
75
|
+
* group an item is a `role="radio"` with `aria-checked`; in a multiple group it has `aria-pressed`.
|
|
76
|
+
*
|
|
77
|
+
* shadcn/ui differences: `class` replaces `className`.
|
|
78
|
+
*
|
|
79
|
+
* @param props - The props, see {@link ToggleGroupProps}.
|
|
80
|
+
* @returns The group `div` with its items inside.
|
|
81
|
+
* @example
|
|
82
|
+
* ```tsx
|
|
83
|
+
* import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
|
|
84
|
+
*
|
|
85
|
+
* export function TextStyle() {
|
|
86
|
+
* return (
|
|
87
|
+
* <ToggleGroup type="multiple" variant="outline" aria-label="Text style">
|
|
88
|
+
* <ToggleGroupItem value="bold">Bold</ToggleGroupItem>
|
|
89
|
+
* <ToggleGroupItem value="italic">Italic</ToggleGroupItem>
|
|
90
|
+
* </ToggleGroup>
|
|
91
|
+
* );
|
|
92
|
+
* }
|
|
93
|
+
* ```
|
|
94
|
+
* @since 1.0.0
|
|
95
|
+
* @see {@link ToggleGroupItem}
|
|
96
|
+
*/
|
|
97
|
+
function ToggleGroup<T extends PolymorphicTarget = "div">(props: ToggleGroupProps<T>): JSX.Element {
|
|
98
|
+
const [local, rest] = splitProps(props as unknown as Loose, [
|
|
99
|
+
"class",
|
|
100
|
+
"variant",
|
|
101
|
+
"size",
|
|
102
|
+
"spacing",
|
|
103
|
+
"children",
|
|
104
|
+
]);
|
|
105
|
+
const spacing = (): number => (local.spacing as number | undefined) ?? 0;
|
|
106
|
+
return (
|
|
107
|
+
<ToggleGroupPrimitive
|
|
108
|
+
data-slot="toggle-group"
|
|
109
|
+
data-variant={local.variant}
|
|
110
|
+
data-size={local.size}
|
|
111
|
+
data-spacing={spacing()}
|
|
112
|
+
style={{ "--gap": String(spacing()) }}
|
|
113
|
+
class={cn(
|
|
114
|
+
"group/toggle-group flex w-fit items-center gap-[--spacing(var(--gap))] rounded-md data-[orientation=vertical]:flex-col data-[orientation=vertical]:items-stretch data-[variant=outline]:shadow-xs",
|
|
115
|
+
local.class,
|
|
116
|
+
)}
|
|
117
|
+
{...(rest as unknown as ToggleGroupPrimitiveProps)}
|
|
118
|
+
>
|
|
119
|
+
<ToggleGroupContext.Provider
|
|
120
|
+
value={{ variant: () => local.variant, size: () => local.size, spacing }}
|
|
121
|
+
>
|
|
122
|
+
{local.children}
|
|
123
|
+
</ToggleGroupContext.Provider>
|
|
124
|
+
</ToggleGroupPrimitive>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* One toggle of a {@link ToggleGroup}, identified by its `value`. Items sit flush against each
|
|
130
|
+
* other, so only the first and last item have rounded outer corners. A `variant` or `size` set on
|
|
131
|
+
* the item wins over the group's.
|
|
132
|
+
*
|
|
133
|
+
* Keyboard and ARIA: as for the group (see {@link ToggleGroup}). Space and Enter press the item. It
|
|
134
|
+
* is a `role="radio"` with `aria-checked` in a single group, and a `button` with `aria-pressed` in a
|
|
135
|
+
* multiple group. An item that shows only an icon needs an `aria-label`.
|
|
136
|
+
*
|
|
137
|
+
* @param props - The props: `value` (required), `disabled`, `variant`, `size`, and the attributes of
|
|
138
|
+
* the `button`.
|
|
139
|
+
* @returns The item `button`.
|
|
140
|
+
* @example
|
|
141
|
+
* ```tsx
|
|
142
|
+
* import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
|
|
143
|
+
*
|
|
144
|
+
* export function Alignment() {
|
|
145
|
+
* return (
|
|
146
|
+
* <ToggleGroup type="single" aria-label="Alignment">
|
|
147
|
+
* <ToggleGroupItem value="left" aria-label="Align left">
|
|
148
|
+
* Left
|
|
149
|
+
* </ToggleGroupItem>
|
|
150
|
+
* <ToggleGroupItem value="right" aria-label="Align right">
|
|
151
|
+
* Right
|
|
152
|
+
* </ToggleGroupItem>
|
|
153
|
+
* </ToggleGroup>
|
|
154
|
+
* );
|
|
155
|
+
* }
|
|
156
|
+
* ```
|
|
157
|
+
* @since 1.0.0
|
|
158
|
+
* @see {@link ToggleGroup}
|
|
159
|
+
*/
|
|
160
|
+
function ToggleGroupItem<T extends PolymorphicTarget = "button">(
|
|
161
|
+
props: ToggleGroupItemProps<T>,
|
|
162
|
+
): JSX.Element {
|
|
163
|
+
const group = useContext(ToggleGroupContext);
|
|
164
|
+
const [local, rest] = splitProps(props as unknown as Loose & { readonly value: string }, [
|
|
165
|
+
"class",
|
|
166
|
+
"variant",
|
|
167
|
+
"size",
|
|
168
|
+
"value",
|
|
169
|
+
]);
|
|
170
|
+
const variant = (): ToggleVariants["variant"] => local.variant ?? group.variant();
|
|
171
|
+
const size = (): ToggleVariants["size"] => local.size ?? group.size();
|
|
172
|
+
return (
|
|
173
|
+
<ToggleGroupPrimitive.Item
|
|
174
|
+
data-slot="toggle-group-item"
|
|
175
|
+
data-variant={variant()}
|
|
176
|
+
data-size={size()}
|
|
177
|
+
data-spacing={group.spacing()}
|
|
178
|
+
value={local.value}
|
|
179
|
+
class={cn(
|
|
180
|
+
toggleVariants({ variant: variant(), size: size() }),
|
|
181
|
+
"min-w-0 flex-1 shrink-0 shadow-none focus:z-10 focus-visible:z-10 data-[spacing=0]:rounded-none data-[spacing=0]:first:rounded-s-md data-[spacing=0]:last:rounded-e-md data-[spacing=0]:data-[variant=outline]:border-s-0 data-[spacing=0]:data-[variant=outline]:first:border-s group-data-[orientation=vertical]/toggle-group:data-[spacing=0]:first:rounded-t-md group-data-[orientation=vertical]/toggle-group:data-[spacing=0]:first:rounded-b-none group-data-[orientation=vertical]/toggle-group:data-[spacing=0]:last:rounded-b-md group-data-[orientation=vertical]/toggle-group:data-[spacing=0]:last:rounded-t-none group-data-[orientation=vertical]/toggle-group:data-[spacing=0]:data-[variant=outline]:border-s group-data-[orientation=vertical]/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-[orientation=vertical]/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t",
|
|
182
|
+
local.class,
|
|
183
|
+
)}
|
|
184
|
+
{...rest}
|
|
185
|
+
/>
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export { ToggleGroup, ToggleGroupItem };
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { splitProps } from "@shadow-js/core";
|
|
2
|
+
import {
|
|
3
|
+
type PolymorphicTarget,
|
|
4
|
+
type TooltipContentProps,
|
|
5
|
+
Tooltip as TooltipPrimitive,
|
|
6
|
+
type TooltipProps,
|
|
7
|
+
type TooltipProviderProps,
|
|
8
|
+
type TooltipTriggerProps,
|
|
9
|
+
tooltipGroupContext,
|
|
10
|
+
} from "@shadow-js/primitives";
|
|
11
|
+
import type { JSX } from "@shadow-js/web";
|
|
12
|
+
import { cn } from "@/lib/utils";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Shares the open delays between the tooltips inside it. `delayDuration` is how long the pointer
|
|
16
|
+
* must rest on a trigger before its tooltip opens, and it defaults to `0` here. `skipDelayDuration`
|
|
17
|
+
* is the window after one tooltip closes during which the next one opens without a delay. Renders
|
|
18
|
+
* no element; put one near the root of the application.
|
|
19
|
+
*
|
|
20
|
+
* @param props - The props: `delayDuration`, `skipDelayDuration`, and the other provider props.
|
|
21
|
+
* @returns The provider, wrapping its children.
|
|
22
|
+
* @example
|
|
23
|
+
* ```tsx
|
|
24
|
+
* import { TooltipProvider } from "@/components/ui/tooltip";
|
|
25
|
+
*
|
|
26
|
+
* export function App() {
|
|
27
|
+
* return (
|
|
28
|
+
* <TooltipProvider delayDuration={400}>
|
|
29
|
+
* <p>Hover a control to see its tooltip.</p>
|
|
30
|
+
* </TooltipProvider>
|
|
31
|
+
* );
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
* @since 1.0.0
|
|
35
|
+
* @see {@link Tooltip}
|
|
36
|
+
*/
|
|
37
|
+
function TooltipProvider(props: TooltipProviderProps): JSX.Element {
|
|
38
|
+
return <TooltipPrimitive.Provider {...props} delayDuration={props.delayDuration ?? 0} />;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* A short label that appears when its trigger is hovered or focused. Put a {@link TooltipTrigger}
|
|
43
|
+
* and a {@link TooltipContent} inside it. Control it with `open` and `onOpenChange`, or start it
|
|
44
|
+
* with `defaultOpen`. Inside a {@link TooltipProvider} it uses the provider's delays. On its own it
|
|
45
|
+
* opens without a delay.
|
|
46
|
+
*
|
|
47
|
+
* Keyboard and ARIA: keyboard focus on the trigger opens the tooltip at once, and blurring the
|
|
48
|
+
* trigger closes it. Escape closes it too. A mouse that rests on the trigger opens it after the
|
|
49
|
+
* delay, and touch never opens it. The trigger is a `button` with `aria-describedby` pointing at the
|
|
50
|
+
* content while it is open, and the content is a `role="tooltip"`. A tooltip describes its trigger
|
|
51
|
+
* and does not name it, so give an icon-only trigger an `aria-label`.
|
|
52
|
+
*
|
|
53
|
+
* shadcn/ui differences: `as` replaces `asChild` on the trigger, and `class` replaces `className`.
|
|
54
|
+
*
|
|
55
|
+
* @param props - The props, see {@link TooltipProps}.
|
|
56
|
+
* @returns The tooltip, with its trigger and content inside.
|
|
57
|
+
* @example
|
|
58
|
+
* ```tsx
|
|
59
|
+
* import { Button } from "@/components/ui/button";
|
|
60
|
+
* import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
|
61
|
+
*
|
|
62
|
+
* export function SaveHint() {
|
|
63
|
+
* return (
|
|
64
|
+
* <Tooltip>
|
|
65
|
+
* <TooltipTrigger as={Button} variant="outline">
|
|
66
|
+
* Save
|
|
67
|
+
* </TooltipTrigger>
|
|
68
|
+
* <TooltipContent>Save the document</TooltipContent>
|
|
69
|
+
* </Tooltip>
|
|
70
|
+
* );
|
|
71
|
+
* }
|
|
72
|
+
* ```
|
|
73
|
+
* @since 1.0.0
|
|
74
|
+
* @see {@link TooltipProvider}
|
|
75
|
+
*/
|
|
76
|
+
function Tooltip(props: TooltipProps): JSX.Element {
|
|
77
|
+
// A tooltip with a provider above it keeps that provider's delay and skip window.
|
|
78
|
+
const grouped = tooltipGroupContext.useOptional() !== undefined;
|
|
79
|
+
return grouped ? (
|
|
80
|
+
<TooltipPrimitive {...props} />
|
|
81
|
+
) : (
|
|
82
|
+
<TooltipProvider>
|
|
83
|
+
<TooltipPrimitive {...props} />
|
|
84
|
+
</TooltipProvider>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* The element the tooltip describes, and its anchor. It renders a `button`; `as` renders another
|
|
90
|
+
* element instead, such as a shadcn/ui `Button`.
|
|
91
|
+
*
|
|
92
|
+
* Keyboard and ARIA: it takes focus, which opens the tooltip; blurring, pressing, clicking or
|
|
93
|
+
* pressing Escape closes it. While the tooltip is open it has `aria-describedby` pointing at the
|
|
94
|
+
* content, and its `data-state` is `"closed"`, `"delayed-open"` or `"instant-open"`.
|
|
95
|
+
*
|
|
96
|
+
* shadcn/ui differences: `as` replaces `asChild`, and `class` replaces `className`.
|
|
97
|
+
*
|
|
98
|
+
* @param props - The props: `as` and the attributes of the trigger element.
|
|
99
|
+
* @returns The trigger element.
|
|
100
|
+
* @example
|
|
101
|
+
* ```tsx
|
|
102
|
+
* import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
|
103
|
+
*
|
|
104
|
+
* export function BoldHint() {
|
|
105
|
+
* return (
|
|
106
|
+
* <Tooltip>
|
|
107
|
+
* <TooltipTrigger aria-label="Bold">B</TooltipTrigger>
|
|
108
|
+
* <TooltipContent>Bold</TooltipContent>
|
|
109
|
+
* </Tooltip>
|
|
110
|
+
* );
|
|
111
|
+
* }
|
|
112
|
+
* ```
|
|
113
|
+
* @since 1.0.0
|
|
114
|
+
*/
|
|
115
|
+
function TooltipTrigger<T extends PolymorphicTarget = "button">(
|
|
116
|
+
props: TooltipTriggerProps<T>,
|
|
117
|
+
): JSX.Element {
|
|
118
|
+
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* The tooltip bubble with its arrow. It is rendered in `document.body` and placed above the trigger
|
|
123
|
+
* by default; `side` and `sideOffset` change that. Assistive technology reads a visually hidden copy
|
|
124
|
+
* of the content with `role="tooltip"`, so the visible content may hold markup and styling.
|
|
125
|
+
*
|
|
126
|
+
* Keyboard and ARIA: the content takes no focus and has no keys of its own; Escape closes the
|
|
127
|
+
* tooltip (see {@link Tooltip}). The trigger's `aria-describedby` points at the hidden copy.
|
|
128
|
+
*
|
|
129
|
+
* shadcn/ui differences: `class` replaces `className`.
|
|
130
|
+
*
|
|
131
|
+
* @param props - The props: `side`, `sideOffset`, `ariaLabel`, and the attributes of the bubble.
|
|
132
|
+
* @returns The bubble, rendered in a portal.
|
|
133
|
+
* @example
|
|
134
|
+
* ```tsx
|
|
135
|
+
* import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
|
136
|
+
*
|
|
137
|
+
* export function AddHint() {
|
|
138
|
+
* return (
|
|
139
|
+
* <Tooltip>
|
|
140
|
+
* <TooltipTrigger aria-label="Add">+</TooltipTrigger>
|
|
141
|
+
* <TooltipContent side="bottom">Add to library</TooltipContent>
|
|
142
|
+
* </Tooltip>
|
|
143
|
+
* );
|
|
144
|
+
* }
|
|
145
|
+
* ```
|
|
146
|
+
* @since 1.0.0
|
|
147
|
+
* @see {@link Tooltip}
|
|
148
|
+
*/
|
|
149
|
+
function TooltipContent(props: TooltipContentProps): JSX.Element {
|
|
150
|
+
const [local, rest] = splitProps(props, ["class", "sideOffset", "children"]);
|
|
151
|
+
return (
|
|
152
|
+
<TooltipPrimitive.Portal>
|
|
153
|
+
<TooltipPrimitive.Content
|
|
154
|
+
data-slot="tooltip-content"
|
|
155
|
+
sideOffset={local.sideOffset ?? 0}
|
|
156
|
+
class={cn(
|
|
157
|
+
"z-50 w-fit origin-(--primitives-transform-origin) animate-in rounded-md bg-foreground px-3 py-1.5 text-xs text-balance text-background fade-in-0 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 forced-colors:border",
|
|
158
|
+
local.class,
|
|
159
|
+
)}
|
|
160
|
+
{...rest}
|
|
161
|
+
>
|
|
162
|
+
{local.children}
|
|
163
|
+
<TooltipPrimitive.Arrow class="fill-foreground" width={10} height={5} />
|
|
164
|
+
</TooltipPrimitive.Content>
|
|
165
|
+
</TooltipPrimitive.Portal>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };
|