panelui-web 0.5.0 → 0.6.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/dist/components/avatar/index.d.ts +66 -0
- package/dist/components/avatar/index.js +251 -0
- package/dist/components/flow/index.d.ts +26 -4
- package/dist/components/flow/index.js +81 -20
- package/dist/components/label/index.d.ts +89 -0
- package/dist/components/label/index.js +64 -0
- package/dist/components/message/index.d.ts +161 -0
- package/dist/components/message/index.js +295 -0
- package/dist/components/sheet/index.d.ts +43 -4
- package/dist/components/sheet/index.js +78 -10
- package/dist/components/switch/index.d.ts +51 -0
- package/dist/components/switch/index.js +88 -0
- package/dist/components/tabs/index.d.ts +94 -0
- package/dist/components/tabs/index.js +196 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.js +6 -1
- package/package.json +1 -1
|
@@ -17,7 +17,7 @@ export interface SheetProps {
|
|
|
17
17
|
*/
|
|
18
18
|
dismissible?: boolean;
|
|
19
19
|
}
|
|
20
|
-
declare function SheetRoot(
|
|
20
|
+
declare function SheetRoot(props: SheetProps): import("react").JSX.Element;
|
|
21
21
|
declare namespace SheetRoot {
|
|
22
22
|
var displayName: string;
|
|
23
23
|
}
|
|
@@ -28,12 +28,50 @@ export interface SheetTriggerProps extends Omit<ComponentProps<'button'>, 'child
|
|
|
28
28
|
* the click handler and the attributes that tie it to the sheet.
|
|
29
29
|
*/
|
|
30
30
|
render?: ReactElement;
|
|
31
|
+
/**
|
|
32
|
+
* The `name` of a `Sheet.Next` to open instead of the sheet this trigger is
|
|
33
|
+
* in. Any button in a sheet can open the next one this way, wherever it sits.
|
|
34
|
+
*/
|
|
35
|
+
to?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Handed to the `Sheet.Next` that `to` names, as the argument of its
|
|
38
|
+
* children function. Use it to open one sheet for every row of a list.
|
|
39
|
+
*/
|
|
40
|
+
payload?: unknown;
|
|
31
41
|
}
|
|
32
|
-
/**
|
|
33
|
-
|
|
42
|
+
/**
|
|
43
|
+
* What opens a sheet: its own, or with `to`, the `Sheet.Next` of that name.
|
|
44
|
+
* A plain button unless `render` supplies one.
|
|
45
|
+
*/
|
|
46
|
+
declare function SheetTrigger({ render, children, to, payload, ...props }: SheetTriggerProps): import("react").JSX.Element;
|
|
34
47
|
declare namespace SheetTrigger {
|
|
35
48
|
var displayName: string;
|
|
36
49
|
}
|
|
50
|
+
export interface SheetNextProps extends Omit<SheetContentProps, 'children' | 'inline'> {
|
|
51
|
+
/** What a `Sheet.Trigger` passes as `to` to open this sheet. Unique within one stack of sheets. */
|
|
52
|
+
name: string;
|
|
53
|
+
/**
|
|
54
|
+
* The sheet's parts. Pass a function to read the `payload` of the trigger
|
|
55
|
+
* that opened it; it is `undefined` when that trigger has none.
|
|
56
|
+
*/
|
|
57
|
+
children?: ReactNode | ((payload: any) => ReactNode);
|
|
58
|
+
/** The edge it rises from. Default: the edge of the sheet it is declared in. */
|
|
59
|
+
side?: SheetSide;
|
|
60
|
+
/** Whether a click outside, a swipe or Escape closes it. Default true. */
|
|
61
|
+
dismissible?: boolean;
|
|
62
|
+
/** Called when it opens or closes. */
|
|
63
|
+
onOpenChange?: (open: boolean) => void;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A sheet opened from the one it is declared in, by any `Sheet.Trigger` whose
|
|
67
|
+
* `to` is its `name`. Put it anywhere inside the first sheet's content; it
|
|
68
|
+
* takes `Sheet.Content`'s props and draws its own content, so its children are
|
|
69
|
+
* the header, body and footer.
|
|
70
|
+
*/
|
|
71
|
+
declare function SheetNext({ name, children, side, dismissible, onOpenChange, ...content }: SheetNextProps): import("react").JSX.Element;
|
|
72
|
+
declare namespace SheetNext {
|
|
73
|
+
var displayName: string;
|
|
74
|
+
}
|
|
37
75
|
export interface SheetContentProps extends Omit<ComponentProps<'div'>, 'children'> {
|
|
38
76
|
children?: ReactNode;
|
|
39
77
|
/**
|
|
@@ -136,6 +174,7 @@ declare namespace SheetStage {
|
|
|
136
174
|
}
|
|
137
175
|
export declare const Sheet: typeof SheetRoot & {
|
|
138
176
|
Trigger: typeof SheetTrigger;
|
|
177
|
+
Next: typeof SheetNext;
|
|
139
178
|
Content: typeof SheetContent;
|
|
140
179
|
Header: typeof SheetHeader;
|
|
141
180
|
Title: typeof SheetTitle;
|
|
@@ -145,4 +184,4 @@ export declare const Sheet: typeof SheetRoot & {
|
|
|
145
184
|
Close: typeof SheetClose;
|
|
146
185
|
Stage: typeof SheetStage;
|
|
147
186
|
};
|
|
148
|
-
export { SheetTrigger, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetBody, SheetFooter, SheetClose, SheetStage, };
|
|
187
|
+
export { SheetTrigger, SheetNext, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetBody, SheetFooter, SheetClose, SheetStage, };
|
|
@@ -34,6 +34,13 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
34
34
|
* still shows, and dimmed. Only the first sheet darkens the page, so two open
|
|
35
35
|
* sheets do not stack two shades of black.
|
|
36
36
|
*
|
|
37
|
+
* The next sheet is declared once, as `Sheet.Next` with a `name`, and opened
|
|
38
|
+
* by any `Sheet.Trigger to="name"` in the sheet: a row in the body, an icon in
|
|
39
|
+
* the header, a button in the footer. The trigger and the sheet it opens are
|
|
40
|
+
* apart in the markup because they are apart on the screen: a list of rows
|
|
41
|
+
* that each open a sheet should not have to wrap every row in one. A trigger
|
|
42
|
+
* can carry a `payload`, so one `Sheet.Next` serves every row of a list.
|
|
43
|
+
*
|
|
37
44
|
* ## The hand decides
|
|
38
45
|
*
|
|
39
46
|
* Every sheet follows a drag and leaves on a flick toward its own edge. The
|
|
@@ -48,7 +55,7 @@ import { tv } from 'tailwind-variants';
|
|
|
48
55
|
* back, the page receding. A fast start that eases into place reads as weight,
|
|
49
56
|
* where a symmetric ease reads as a slide show.
|
|
50
57
|
*/
|
|
51
|
-
const MOVE = '[transition:transform_450ms_cubic-bezier(0.32,0.72,0,1),border-radius_450ms_cubic-bezier(0.32,0.72,0,1),margin_450ms_cubic-bezier(0.32,0.72,0,1)]';
|
|
58
|
+
const MOVE = '[transition:transform_450ms_cubic-bezier(0.32,0.72,0,1),border-radius_450ms_cubic-bezier(0.32,0.72,0,1),margin_450ms_cubic-bezier(0.32,0.72,0,1),height_450ms_cubic-bezier(0.32,0.72,0,1)]';
|
|
52
59
|
const sheetVariants = tv({
|
|
53
60
|
slots: {
|
|
54
61
|
backdrop: [
|
|
@@ -83,6 +90,17 @@ const sheetVariants = tv({
|
|
|
83
90
|
"after:pointer-events-none after:absolute after:inset-0 after:rounded-[inherit] after:bg-popover after:opacity-0 after:transition-opacity after:duration-[450ms] after:content-['']",
|
|
84
91
|
'data-nested-drawer-open:after:opacity-50',
|
|
85
92
|
],
|
|
93
|
+
/*
|
|
94
|
+
* A top or bottom sheet behind another takes the front one's height, so
|
|
95
|
+
* the strip that shows past it is the same whichever of the two is taller.
|
|
96
|
+
* A taller sheet behind would otherwise stand above the stack, and a
|
|
97
|
+
* shorter one would vanish behind the front one entirely.
|
|
98
|
+
*/
|
|
99
|
+
stacked: [
|
|
100
|
+
'data-nested-drawer-open:h-(--drawer-frontmost-height) data-nested-drawer-open:overflow-hidden',
|
|
101
|
+
// Lets the height animate to and from `auto` where the browser can.
|
|
102
|
+
'[interpolate-size:allow-keywords]',
|
|
103
|
+
],
|
|
86
104
|
grabber: [
|
|
87
105
|
'pointer-events-none absolute inset-x-0 z-10 mx-auto flex h-6 w-24 justify-center',
|
|
88
106
|
'before:h-[5px] before:w-9 before:rounded-full before:bg-muted-foreground/35',
|
|
@@ -206,6 +224,19 @@ const SWIPE = {
|
|
|
206
224
|
left: 'left',
|
|
207
225
|
right: 'right',
|
|
208
226
|
};
|
|
227
|
+
function createStack() {
|
|
228
|
+
const handles = new Map();
|
|
229
|
+
return {
|
|
230
|
+
handle(name) {
|
|
231
|
+
let handle = handles.get(name);
|
|
232
|
+
if (!handle) {
|
|
233
|
+
handle = Drawer.createHandle();
|
|
234
|
+
handles.set(name, handle);
|
|
235
|
+
}
|
|
236
|
+
return handle;
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
}
|
|
209
240
|
const SheetContext = createContext(null);
|
|
210
241
|
function useSheet(part) {
|
|
211
242
|
const context = useContext(SheetContext);
|
|
@@ -213,10 +244,16 @@ function useSheet(part) {
|
|
|
213
244
|
throw new Error(`${part} must be used inside a <Sheet>.`);
|
|
214
245
|
return context;
|
|
215
246
|
}
|
|
216
|
-
function SheetRoot(
|
|
247
|
+
function SheetRoot(props) {
|
|
248
|
+
return _jsx(SheetFrame, { ...props });
|
|
249
|
+
}
|
|
250
|
+
SheetRoot.displayName = 'Sheet';
|
|
251
|
+
function SheetFrame({ children, open, onOpenChange, defaultOpen = false, side = 'bottom', dismissible = true, handle: drawerHandle, }) {
|
|
217
252
|
// A sheet inside another sheet's content finds that sheet's context.
|
|
218
253
|
const parent = useContext(SheetContext);
|
|
219
254
|
const nested = parent !== null && !parent.inline;
|
|
255
|
+
const [ownStack] = useState(createStack);
|
|
256
|
+
const stack = nested && parent ? parent.stack : ownStack;
|
|
220
257
|
// Held here as well as by the primitive, so a sheet opened from this one
|
|
221
258
|
// can close it.
|
|
222
259
|
const [internalOpen, setInternalOpen] = useState(defaultOpen);
|
|
@@ -243,6 +280,7 @@ function SheetRoot({ children, open, onOpenChange, defaultOpen = false, side = '
|
|
|
243
280
|
useEffect(() => (nested && parent ? parent.register(handle) : undefined), [nested, parent, handle]);
|
|
244
281
|
const context = useMemo(() => ({
|
|
245
282
|
side,
|
|
283
|
+
stack,
|
|
246
284
|
nested,
|
|
247
285
|
inline: false,
|
|
248
286
|
showClose: true,
|
|
@@ -252,8 +290,8 @@ function SheetRoot({ children, open, onOpenChange, defaultOpen = false, side = '
|
|
|
252
290
|
},
|
|
253
291
|
closeTree: handle.closeTree,
|
|
254
292
|
hasOpenChild: () => [...opened.current].some((child) => child.isOpen()),
|
|
255
|
-
}), [side, nested, handle]);
|
|
256
|
-
return (_jsx(SheetContext.Provider, { value: context, children: _jsx(Drawer.Root, { open: isOpen, swipeDirection: SWIPE[side], disablePointerDismissal: !dismissible, onOpenChange: (next, details) => {
|
|
293
|
+
}), [side, stack, nested, handle]);
|
|
294
|
+
return (_jsx(SheetContext.Provider, { value: context, children: _jsx(Drawer.Root, { open: isOpen, handle: drawerHandle, swipeDirection: SWIPE[side], disablePointerDismissal: !dismissible, onOpenChange: (next, details) => {
|
|
257
295
|
// The primitive has a switch for a press outside and none for
|
|
258
296
|
// Escape or a swipe, so those are refused here.
|
|
259
297
|
if (!next &&
|
|
@@ -263,14 +301,43 @@ function SheetRoot({ children, open, onOpenChange, defaultOpen = false, side = '
|
|
|
263
301
|
return;
|
|
264
302
|
}
|
|
265
303
|
setOpen(next);
|
|
266
|
-
}, children: children
|
|
304
|
+
}, children: typeof children === 'function'
|
|
305
|
+
? ({ payload }) => children(payload)
|
|
306
|
+
: children }) }));
|
|
267
307
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
308
|
+
/**
|
|
309
|
+
* What opens a sheet: its own, or with `to`, the `Sheet.Next` of that name.
|
|
310
|
+
* A plain button unless `render` supplies one.
|
|
311
|
+
*/
|
|
312
|
+
function SheetTrigger({ render, children, to, payload, ...props }) {
|
|
313
|
+
const sheet = useContext(SheetContext);
|
|
314
|
+
if (to !== undefined) {
|
|
315
|
+
if (!sheet)
|
|
316
|
+
throw new Error('Sheet.Trigger with `to` must be used inside a <Sheet>.');
|
|
317
|
+
return (_jsx(Drawer.Trigger, { handle: sheet.stack.handle(to), payload: payload, render: render, ...props, children: children }));
|
|
318
|
+
}
|
|
271
319
|
return (_jsx(Drawer.Trigger, { render: render, ...props, children: children }));
|
|
272
320
|
}
|
|
273
321
|
SheetTrigger.displayName = 'Sheet.Trigger';
|
|
322
|
+
/**
|
|
323
|
+
* A sheet opened from the one it is declared in, by any `Sheet.Trigger` whose
|
|
324
|
+
* `to` is its `name`. Put it anywhere inside the first sheet's content; it
|
|
325
|
+
* takes `Sheet.Content`'s props and draws its own content, so its children are
|
|
326
|
+
* the header, body and footer.
|
|
327
|
+
*/
|
|
328
|
+
function SheetNext({ name, children, side, dismissible, onOpenChange, ...content }) {
|
|
329
|
+
const parent = useSheet('Sheet.Next');
|
|
330
|
+
return (_jsx(SheetFrame, { handle: parent.stack.handle(name), side: side ?? parent.side, dismissible: dismissible, onOpenChange: onOpenChange, children: (payload) => (_jsx(SheetContent, { ...content, children: typeof children === 'function' ? _jsx(NextParts, { render: children, payload: payload }) : children })) }));
|
|
331
|
+
}
|
|
332
|
+
SheetNext.displayName = 'Sheet.Next';
|
|
333
|
+
/**
|
|
334
|
+
* Calls a `Sheet.Next` children function. As a component of its own it only
|
|
335
|
+
* runs once the sheet is open and its content mounted, which is when the
|
|
336
|
+
* trigger has handed over its payload.
|
|
337
|
+
*/
|
|
338
|
+
function NextParts({ render, payload }) {
|
|
339
|
+
return _jsx(_Fragment, { children: render(payload) });
|
|
340
|
+
}
|
|
274
341
|
/** The sheet itself, with the backdrop behind it. */
|
|
275
342
|
function SheetContent({ children, detached = true, size = 'auto', showGrabber, showClose = true, blur = false, inline = false, className, ...props }) {
|
|
276
343
|
const sheet = useSheet('Sheet.Content');
|
|
@@ -291,7 +358,7 @@ function SheetContent({ children, detached = true, size = 'auto', showGrabber, s
|
|
|
291
358
|
onClick: () => {
|
|
292
359
|
if (sheet.hasOpenChild())
|
|
293
360
|
sheet.closeTree();
|
|
294
|
-
} })), _jsx(Drawer.Viewport, { className: slots.viewport(), children: _jsx(Drawer.Popup, { className: slots.popup({ className }), ...props, children: _jsx(SheetContext.Provider, { value: context, children: inside }) }) })] }));
|
|
361
|
+
} })), _jsx(Drawer.Viewport, { className: slots.viewport(), children: _jsx(Drawer.Popup, { className: slots.popup({ className: [vertical && slots.stacked(), className] }), ...props, children: _jsx(SheetContext.Provider, { value: context, children: inside }) }) })] }));
|
|
295
362
|
}
|
|
296
363
|
SheetContent.displayName = 'Sheet.Content';
|
|
297
364
|
/**
|
|
@@ -359,6 +426,7 @@ function SheetStage({ className, children, ...props }) {
|
|
|
359
426
|
SheetStage.displayName = 'Sheet.Stage';
|
|
360
427
|
export const Sheet = Object.assign(SheetRoot, {
|
|
361
428
|
Trigger: SheetTrigger,
|
|
429
|
+
Next: SheetNext,
|
|
362
430
|
Content: SheetContent,
|
|
363
431
|
Header: SheetHeader,
|
|
364
432
|
Title: SheetTitle,
|
|
@@ -368,4 +436,4 @@ export const Sheet = Object.assign(SheetRoot, {
|
|
|
368
436
|
Close: SheetClose,
|
|
369
437
|
Stage: SheetStage,
|
|
370
438
|
});
|
|
371
|
-
export { SheetTrigger, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetBody, SheetFooter, SheetClose, SheetStage, };
|
|
439
|
+
export { SheetTrigger, SheetNext, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetBody, SheetFooter, SheetClose, SheetStage, };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Switch — on or off, for a setting that takes effect the moment it changes.
|
|
3
|
+
*
|
|
4
|
+
* ```tsx
|
|
5
|
+
* <Switch label="Dark mode" defaultChecked />
|
|
6
|
+
*
|
|
7
|
+
* <Label>
|
|
8
|
+
* <Label.Text>Email me a receipt</Label.Text>
|
|
9
|
+
* <Label.Action><Switch /></Label.Action>
|
|
10
|
+
* </Label>
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* ## It moves like a thing with weight
|
|
14
|
+
*
|
|
15
|
+
* The colour does not fade in. It sweeps along the track behind the thumb,
|
|
16
|
+
* so the track fills from the side the thumb left and reads as the thumb's
|
|
17
|
+
* doing. While it is pressed, the thumb stretches towards where it is going,
|
|
18
|
+
* so a press that has not yet let go already says which way it will flip.
|
|
19
|
+
*
|
|
20
|
+
* ## While a change is saved
|
|
21
|
+
*
|
|
22
|
+
* A setting saved to a server is not on until the server says so. `pending`
|
|
23
|
+
* puts a turning ring in the thumb and stops the switch being pressed again,
|
|
24
|
+
* so the switch can show the new state at once and still be honest that it
|
|
25
|
+
* has not landed yet.
|
|
26
|
+
*/
|
|
27
|
+
import { Switch as BaseSwitch } from '@base-ui/react/switch';
|
|
28
|
+
import type { CSSProperties } from 'react';
|
|
29
|
+
export type SwitchSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
30
|
+
export type SwitchColor = 'primary' | 'success' | 'info' | 'destructive';
|
|
31
|
+
export interface SwitchProps extends Omit<BaseSwitch.Root.Props, 'className' | 'color'> {
|
|
32
|
+
/** Default `md`. */
|
|
33
|
+
size?: SwitchSize;
|
|
34
|
+
/** The colour it fills with when on. `primary` by default; pick another for what it means. */
|
|
35
|
+
color?: SwitchColor;
|
|
36
|
+
/**
|
|
37
|
+
* What a screen reader calls it, when there is no visible label tied to it.
|
|
38
|
+
* A `Label` wrapped round it, or pointing at its `id`, is better when there is room.
|
|
39
|
+
*/
|
|
40
|
+
label?: string;
|
|
41
|
+
/** A change is being saved: a ring turns in the thumb, and it cannot be pressed again until it is done. */
|
|
42
|
+
pending?: boolean;
|
|
43
|
+
className?: string;
|
|
44
|
+
style?: CSSProperties;
|
|
45
|
+
}
|
|
46
|
+
/** A switch. Takes `checked`/`onCheckedChange` or `defaultChecked`, and `name` for a form. */
|
|
47
|
+
declare function Switch({ size, color, label, pending, className, style, readOnly, ...props }: SwitchProps): import("react").JSX.Element;
|
|
48
|
+
declare namespace Switch {
|
|
49
|
+
var displayName: string;
|
|
50
|
+
}
|
|
51
|
+
export { Switch };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Switch — on or off, for a setting that takes effect the moment it changes.
|
|
4
|
+
*
|
|
5
|
+
* ```tsx
|
|
6
|
+
* <Switch label="Dark mode" defaultChecked />
|
|
7
|
+
*
|
|
8
|
+
* <Label>
|
|
9
|
+
* <Label.Text>Email me a receipt</Label.Text>
|
|
10
|
+
* <Label.Action><Switch /></Label.Action>
|
|
11
|
+
* </Label>
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* ## It moves like a thing with weight
|
|
15
|
+
*
|
|
16
|
+
* The colour does not fade in. It sweeps along the track behind the thumb,
|
|
17
|
+
* so the track fills from the side the thumb left and reads as the thumb's
|
|
18
|
+
* doing. While it is pressed, the thumb stretches towards where it is going,
|
|
19
|
+
* so a press that has not yet let go already says which way it will flip.
|
|
20
|
+
*
|
|
21
|
+
* ## While a change is saved
|
|
22
|
+
*
|
|
23
|
+
* A setting saved to a server is not on until the server says so. `pending`
|
|
24
|
+
* puts a turning ring in the thumb and stops the switch being pressed again,
|
|
25
|
+
* so the switch can show the new state at once and still be honest that it
|
|
26
|
+
* has not landed yet.
|
|
27
|
+
*/
|
|
28
|
+
import { Switch as BaseSwitch } from '@base-ui/react/switch';
|
|
29
|
+
import { tv } from 'tailwind-variants';
|
|
30
|
+
/** Height and width of the track. The thumb is the height less 2px of padding on each side. */
|
|
31
|
+
const SIZE = {
|
|
32
|
+
xs: ['1rem', '1.75rem'],
|
|
33
|
+
sm: ['1.25rem', '2.125rem'],
|
|
34
|
+
md: ['1.5rem', '2.625rem'],
|
|
35
|
+
lg: ['1.75rem', '3.125rem'],
|
|
36
|
+
xl: ['2rem', '3.625rem'],
|
|
37
|
+
};
|
|
38
|
+
const SPRING = 'ease-[cubic-bezier(0.3,1.35,0.45,1)]';
|
|
39
|
+
const switchVariants = tv({
|
|
40
|
+
slots: {
|
|
41
|
+
root: [
|
|
42
|
+
'group/switch relative inline-flex h-(--switch-h) w-(--switch-w) shrink-0 cursor-pointer items-center rounded-full p-0.5 outline-none',
|
|
43
|
+
'bg-input transition-[background-color,box-shadow] duration-200 dark:bg-input/80',
|
|
44
|
+
'hover:not-data-disabled:bg-[color-mix(in_oklab,var(--input),var(--foreground)_8%)]',
|
|
45
|
+
'focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background',
|
|
46
|
+
'data-disabled:cursor-not-allowed data-disabled:opacity-50 aria-busy:cursor-progress',
|
|
47
|
+
// The fill: the whole track in the switch's colour, uncovered from the
|
|
48
|
+
// thumb's starting side as it travels.
|
|
49
|
+
"before:absolute before:inset-0 before:rounded-[inherit] before:bg-(--switch-color) before:content-['']",
|
|
50
|
+
'before:[clip-path:inset(0_100%_0_0_round_999px)] before:transition-[clip-path] before:duration-300 before:ease-[cubic-bezier(0.3,1.35,0.45,1)]',
|
|
51
|
+
'rtl:before:[clip-path:inset(0_0_0_100%_round_999px)]',
|
|
52
|
+
'data-checked:before:[clip-path:inset(0_0_0_0_round_999px)]',
|
|
53
|
+
'motion-reduce:before:transition-none',
|
|
54
|
+
],
|
|
55
|
+
thumb: [
|
|
56
|
+
'relative z-10 flex size-[calc(var(--switch-h)-4px)] items-center justify-center rounded-full bg-white',
|
|
57
|
+
// On, the thumb takes the colour that reads on the fill: dark on a light primary, white on the rest.
|
|
58
|
+
'transition-colors data-checked:bg-(--switch-thumb)',
|
|
59
|
+
'shadow-[0_1px_2px_rgb(0_0_0/0.18),0_2px_6px_-1px_rgb(0_0_0/0.12)]',
|
|
60
|
+
`transition-[translate,width] duration-300 ${SPRING} motion-reduce:transition-none`,
|
|
61
|
+
// On: across by the track's width less its height, which is the room left.
|
|
62
|
+
'data-checked:translate-x-[calc(var(--switch-w)-var(--switch-h))] rtl:data-checked:-translate-x-[calc(var(--switch-w)-var(--switch-h))]',
|
|
63
|
+
// Pressed: a fifth longer, towards where it is going. When on, it grows back the way it came.
|
|
64
|
+
'group-active/switch:not-data-disabled:w-[calc((var(--switch-h)-4px)*1.22)]',
|
|
65
|
+
'group-active/switch:data-checked:not-data-disabled:translate-x-[calc(var(--switch-w)-var(--switch-h)-(var(--switch-h)-4px)*0.22)]',
|
|
66
|
+
'rtl:group-active/switch:data-checked:not-data-disabled:-translate-x-[calc(var(--switch-w)-var(--switch-h)-(var(--switch-h)-4px)*0.22)]',
|
|
67
|
+
'group-aria-busy/switch:w-[calc(var(--switch-h)-4px)]',
|
|
68
|
+
],
|
|
69
|
+
spinner: 'size-[62%] animate-spin text-(--switch-color) group-not-data-checked/switch:text-muted-foreground motion-reduce:animate-none',
|
|
70
|
+
},
|
|
71
|
+
variants: {
|
|
72
|
+
color: {
|
|
73
|
+
primary: { root: '[--switch-color:var(--primary)] [--switch-thumb:var(--primary-foreground)]' },
|
|
74
|
+
success: { root: '[--switch-color:var(--success)] [--switch-thumb:white]' },
|
|
75
|
+
info: { root: '[--switch-color:var(--info)] [--switch-thumb:white]' },
|
|
76
|
+
destructive: { root: '[--switch-color:var(--destructive)] [--switch-thumb:white]' },
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
defaultVariants: { color: 'primary' },
|
|
80
|
+
});
|
|
81
|
+
/** A switch. Takes `checked`/`onCheckedChange` or `defaultChecked`, and `name` for a form. */
|
|
82
|
+
function Switch({ size = 'md', color, label, pending = false, className, style, readOnly, ...props }) {
|
|
83
|
+
const slots = switchVariants({ color });
|
|
84
|
+
const [height, width] = SIZE[size];
|
|
85
|
+
return (_jsx(BaseSwitch.Root, { "data-slot": "switch", "data-size": size, "aria-label": label, "aria-busy": pending || undefined, readOnly: pending || readOnly, className: slots.root({ className }), style: { ['--switch-h']: height, ['--switch-w']: width, ...style }, ...props, children: _jsx(BaseSwitch.Thumb, { "data-slot": "switch-thumb", className: slots.thumb(), children: pending ? (_jsxs("svg", { viewBox: "0 0 24 24", "aria-hidden": "true", className: slots.spinner(), fill: "none", children: [_jsx("circle", { cx: "12", cy: "12", r: "9", stroke: "currentColor", strokeOpacity: 0.2, strokeWidth: 3.5 }), _jsx("path", { d: "M21 12a9 9 0 0 0-9-9", stroke: "currentColor", strokeWidth: 3.5, strokeLinecap: "round" })] })) : null }) }));
|
|
86
|
+
}
|
|
87
|
+
Switch.displayName = 'Switch';
|
|
88
|
+
export { Switch };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tabs — a few views of one thing, one at a time, chosen from a row of tabs.
|
|
3
|
+
*
|
|
4
|
+
* ```tsx
|
|
5
|
+
* <Tabs defaultValue="overview">
|
|
6
|
+
* <Tabs.List>
|
|
7
|
+
* <Tabs.Trigger value="overview">Overview</Tabs.Trigger>
|
|
8
|
+
* <Tabs.Trigger value="activity">
|
|
9
|
+
* Activity <Tabs.Badge>12</Tabs.Badge>
|
|
10
|
+
* </Tabs.Trigger>
|
|
11
|
+
* </Tabs.List>
|
|
12
|
+
* <Tabs.Content value="overview">…</Tabs.Content>
|
|
13
|
+
* <Tabs.Content value="activity">…</Tabs.Content>
|
|
14
|
+
* </Tabs>
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* ## Three weights
|
|
18
|
+
*
|
|
19
|
+
* `segmented` sets the tabs in a track with the chosen one raised out of it:
|
|
20
|
+
* a control for switching what a panel shows. `underline` is lighter, a line
|
|
21
|
+
* under the chosen tab, for the sections of a page. `pills` fills the chosen
|
|
22
|
+
* tab with the primary colour, for a filter that should be seen from across
|
|
23
|
+
* the screen.
|
|
24
|
+
*
|
|
25
|
+
* ## It moves the way the hand did
|
|
26
|
+
*
|
|
27
|
+
* The marker slides to the new tab. The underline stretches as it goes, its
|
|
28
|
+
* leading end arriving first and the trailing end catching up, so a long jump
|
|
29
|
+
* reads as one movement and not as a line teleporting. The new panel slides
|
|
30
|
+
* in from the side the tab was on, so going right looks like going right.
|
|
31
|
+
*
|
|
32
|
+
* ## A row that does not fit scrolls
|
|
33
|
+
*
|
|
34
|
+
* When the tabs are wider than the space for them, the row scrolls sideways
|
|
35
|
+
* and fades at the end that has more, rather than wrapping onto a second line
|
|
36
|
+
* or shrinking the labels.
|
|
37
|
+
*/
|
|
38
|
+
import { Tabs as BaseTabs } from '@base-ui/react/tabs';
|
|
39
|
+
import { type ComponentProps } from 'react';
|
|
40
|
+
export type TabsVariant = 'segmented' | 'underline' | 'pills';
|
|
41
|
+
export interface TabsProps extends Omit<BaseTabs.Root.Props, 'className'> {
|
|
42
|
+
/** How the tabs are drawn. Default `segmented`. */
|
|
43
|
+
variant?: TabsVariant;
|
|
44
|
+
className?: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Holds which tab is chosen: `defaultValue`, or `value` with `onValueChange`.
|
|
48
|
+
* `orientation="vertical"` puts the tabs in a column beside the content.
|
|
49
|
+
*/
|
|
50
|
+
declare function TabsRoot({ variant, className, ...props }: TabsProps): import("react").JSX.Element;
|
|
51
|
+
declare namespace TabsRoot {
|
|
52
|
+
var displayName: string;
|
|
53
|
+
}
|
|
54
|
+
export interface TabsListProps extends Omit<BaseTabs.List.Props, 'className'> {
|
|
55
|
+
className?: string;
|
|
56
|
+
}
|
|
57
|
+
/** The row of tabs, with the marker under the chosen one. */
|
|
58
|
+
declare function TabsList({ className, children, ...props }: TabsListProps): import("react").JSX.Element;
|
|
59
|
+
declare namespace TabsList {
|
|
60
|
+
var displayName: string;
|
|
61
|
+
}
|
|
62
|
+
export interface TabsTriggerProps extends Omit<BaseTabs.Tab.Props, 'className'> {
|
|
63
|
+
className?: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* One tab. Its `value` names the `Tabs.Content` it shows. An icon and a
|
|
67
|
+
* `Tabs.Badge` can sit beside its label.
|
|
68
|
+
*/
|
|
69
|
+
declare function TabsTrigger({ className, ...props }: TabsTriggerProps): import("react").JSX.Element;
|
|
70
|
+
declare namespace TabsTrigger {
|
|
71
|
+
var displayName: string;
|
|
72
|
+
}
|
|
73
|
+
export interface TabsBadgeProps extends ComponentProps<'span'> {
|
|
74
|
+
}
|
|
75
|
+
/** A count beside a tab's label: unread, open, waiting. */
|
|
76
|
+
declare function TabsBadge({ className, ...props }: TabsBadgeProps): import("react").JSX.Element;
|
|
77
|
+
declare namespace TabsBadge {
|
|
78
|
+
var displayName: string;
|
|
79
|
+
}
|
|
80
|
+
export interface TabsContentProps extends Omit<BaseTabs.Panel.Props, 'className'> {
|
|
81
|
+
className?: string;
|
|
82
|
+
}
|
|
83
|
+
/** What one tab shows. Only the chosen one is in the page unless `keepMounted` is set. */
|
|
84
|
+
declare function TabsContent({ className, ...props }: TabsContentProps): import("react").JSX.Element;
|
|
85
|
+
declare namespace TabsContent {
|
|
86
|
+
var displayName: string;
|
|
87
|
+
}
|
|
88
|
+
export declare const Tabs: typeof TabsRoot & {
|
|
89
|
+
List: typeof TabsList;
|
|
90
|
+
Trigger: typeof TabsTrigger;
|
|
91
|
+
Badge: typeof TabsBadge;
|
|
92
|
+
Content: typeof TabsContent;
|
|
93
|
+
};
|
|
94
|
+
export { TabsList, TabsTrigger, TabsBadge, TabsContent };
|