@tangle-network/ui 11.28.2 → 11.30.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 +12 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +6 -2
- package/dist/lib/control-presentation.d.ts +10 -0
- package/dist/lib/control-presentation.js +1 -1
- package/dist/primitives/button.d.ts +1 -1
- package/dist/primitives/button.js +3 -2
- package/dist/primitives/calendar.d.ts +38 -0
- package/dist/primitives/calendar.js +186 -0
- package/dist/primitives/date-picker.d.ts +32 -0
- package/dist/primitives/date-picker.js +90 -0
- package/dist/primitives/radio-group.d.ts +13 -0
- package/dist/primitives/radio-group.js +28 -0
- package/dist/primitives/time-select.d.ts +27 -0
- package/dist/primitives/time-select.js +51 -0
- package/dist/primitives/toolbar.js +1 -1
- package/dist/primitives.d.ts +5 -1
- package/dist/primitives.js +6 -2
- package/package.json +3 -2
- package/src/lib/control-presentation.ts +3 -1
- package/src/primitives/button.tsx +9 -3
- package/src/primitives/calendar.tsx +242 -0
- package/src/primitives/control-presentation.test.tsx +2 -2
- package/src/primitives/date-picker.stories.tsx +55 -0
- package/src/primitives/date-picker.test.tsx +146 -0
- package/src/primitives/date-picker.tsx +126 -0
- package/src/primitives/index.ts +9 -0
- package/src/primitives/radio-group.test.tsx +26 -0
- package/src/primitives/radio-group.tsx +43 -0
- package/src/primitives/time-select.tsx +66 -0
- package/src/primitives/toolbar.tsx +3 -1
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./select.js";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
//#region src/primitives/time-select.tsx
|
|
6
|
+
const TIME_PATTERN = /^([01]\d|2[0-3]):([0-5]\d)$/;
|
|
7
|
+
/**
|
|
8
|
+
* A time of day as a Select on the control scale: the day in `step`-minute
|
|
9
|
+
* slots, labelled in the viewer's locale (9:30 AM, 21:30). A value between
|
|
10
|
+
* slots stays listed so the current time is never lost.
|
|
11
|
+
*/
|
|
12
|
+
const TimeSelect = React.forwardRef(({ value, onChange, step = 15, placeholder = "Pick a time", size = "md", disabled, locale, id, name, className, ...aria }, ref) => {
|
|
13
|
+
const format = React.useMemo(() => new Intl.DateTimeFormat(locale, {
|
|
14
|
+
hour: "numeric",
|
|
15
|
+
minute: "2-digit",
|
|
16
|
+
timeZone: "UTC"
|
|
17
|
+
}), [locale]);
|
|
18
|
+
const options = React.useMemo(() => {
|
|
19
|
+
const values = [];
|
|
20
|
+
for (let minutes = 0; minutes < 1440; minutes += Math.max(1, step)) values.push(`${String(Math.floor(minutes / 60)).padStart(2, "0")}:${String(minutes % 60).padStart(2, "0")}`);
|
|
21
|
+
if (value && TIME_PATTERN.test(value) && !values.includes(value)) values.push(value);
|
|
22
|
+
return values.sort();
|
|
23
|
+
}, [step, value]);
|
|
24
|
+
const label = (time) => {
|
|
25
|
+
const [hours, minutes] = time.split(":").map(Number);
|
|
26
|
+
return format.format(Date.UTC(1970, 0, 1, hours, minutes));
|
|
27
|
+
};
|
|
28
|
+
return /* @__PURE__ */ jsxs(Select, {
|
|
29
|
+
value: value && TIME_PATTERN.test(value) ? value : void 0,
|
|
30
|
+
onValueChange: onChange,
|
|
31
|
+
disabled,
|
|
32
|
+
name,
|
|
33
|
+
children: [/* @__PURE__ */ jsx(SelectTrigger, {
|
|
34
|
+
ref,
|
|
35
|
+
id,
|
|
36
|
+
size,
|
|
37
|
+
className,
|
|
38
|
+
...aria,
|
|
39
|
+
children: /* @__PURE__ */ jsx(SelectValue, { placeholder })
|
|
40
|
+
}), /* @__PURE__ */ jsx(SelectContent, {
|
|
41
|
+
className: "max-h-72",
|
|
42
|
+
children: options.map((time) => /* @__PURE__ */ jsx(SelectItem, {
|
|
43
|
+
value: time,
|
|
44
|
+
children: label(time)
|
|
45
|
+
}, time))
|
|
46
|
+
})]
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
TimeSelect.displayName = "TimeSelect";
|
|
50
|
+
//#endregion
|
|
51
|
+
export { TimeSelect };
|
|
@@ -4,7 +4,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
4
4
|
//#region src/primitives/toolbar.tsx
|
|
5
5
|
const Toolbar = React.forwardRef(({ className, search, filters, actions, ...props }, ref) => /* @__PURE__ */ jsxs("div", {
|
|
6
6
|
ref,
|
|
7
|
-
className: cn("mb-4 flex flex-col gap-3 lg:flex-row lg:items-center", className),
|
|
7
|
+
className: cn("mb-4 flex flex-col gap-3 lg:flex-row lg:items-center [--field-surface:hsl(var(--card))]", className),
|
|
8
8
|
...props,
|
|
9
9
|
children: [
|
|
10
10
|
search && /* @__PURE__ */ jsx("div", {
|
package/dist/primitives.d.ts
CHANGED
|
@@ -11,6 +11,9 @@ import { Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger }
|
|
|
11
11
|
import { Slider } from "./primitives/slider.js";
|
|
12
12
|
import { Input, InputProps, Textarea, TextareaProps } from "./primitives/input.js";
|
|
13
13
|
import { HelpText, HelpTextProps } from "./primitives/help-text.js";
|
|
14
|
+
import { Calendar, CalendarDate, CalendarProps, formatCalendarDate, localToday, parseCalendarDate } from "./primitives/calendar.js";
|
|
15
|
+
import { DatePicker, DatePickerProps } from "./primitives/date-picker.js";
|
|
16
|
+
import { TimeSelect, TimeSelectProps } from "./primitives/time-select.js";
|
|
14
17
|
import { Badge, BadgeProps, badgeVariants } from "./primitives/badge.js";
|
|
15
18
|
import { Avatar, AvatarFallback, AvatarImage } from "./primitives/avatar.js";
|
|
16
19
|
import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from "./primitives/dropdown-menu.js";
|
|
@@ -21,6 +24,7 @@ import { SegmentedControl, SegmentedControlOption, SegmentedControlProps } from
|
|
|
21
24
|
import { Progress } from "./primitives/progress.js";
|
|
22
25
|
import { Switch } from "./primitives/switch.js";
|
|
23
26
|
import { Checkbox } from "./primitives/checkbox.js";
|
|
27
|
+
import { RadioGroup, RadioGroupItem } from "./primitives/radio-group.js";
|
|
24
28
|
import { Separator } from "./primitives/separator.js";
|
|
25
29
|
import { Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger } from "./primitives/sheet.js";
|
|
26
30
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./primitives/tooltip.js";
|
|
@@ -48,4 +52,4 @@ import { Chip, ChipProps } from "./primitives/chip.js";
|
|
|
48
52
|
import { Metric, MetricProps, MetricStrip, MetricStripProps } from "./primitives/metric-strip.js";
|
|
49
53
|
import { FilterField, FilterFieldProps, Toolbar, ToolbarProps } from "./primitives/toolbar.js";
|
|
50
54
|
import { Logo, LogoProps, TangleKnot } from "@tangle-network/brand";
|
|
51
|
-
export { Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, type AlertProps, AlertTitle, type AlertTone, ArtifactPane, type ArtifactPaneProps, Avatar, AvatarFallback, AvatarImage, Badge, type BadgeProps, Button, type ButtonProps, CATEGORY_TONES, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type CardTitleProps, type CategoryTone, Checkbox, Chip, type ChipProps, CodeBlock, type CodeBlockProps, Collapsible, CollapsibleContent, CollapsibleTrigger, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropZone, type DropZoneProps, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, type EmptyStateProps, FilterField, type FilterFieldProps, Heading, type HeadingProps, type HeadingVariant, HelpText, type HelpTextProps, IconTile, type IconTileProps, InlineCode, type InlineCodeProps, Input, type InputProps, Label, Logo, type LogoProps, Metric, type MetricProps, MetricStrip, type MetricStripProps, PageHeader, type PageHeaderProps, PageShell, type PageShellProps, Pagination, type PaginationProps, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, SegmentedControl, type SegmentedControlOption, type SegmentedControlProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarDropZone, type SidebarDropZoneProps, Skeleton, SkeletonCard, SkeletonTable, Slider, StatCard, type StatCardProps, StatusPill, type StatusPillProps, type StatusTone, Switch, TONE_CLASSES, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, type TableProps, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, type TagProps, TangleKnot, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TextShimmer, type TextShimmerProps, Textarea, type TextareaProps, ThemeToggle, type Toast, ToastContainer, ToastProvider, type Tone, type ToneClasses, Toolbar, type ToolbarProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type UploadFile, UploadProgress, type UploadProgressProps, badgeVariants, buttonVariants, initialsOf, toneFor, useTheme, useToast };
|
|
55
|
+
export { Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, type AlertProps, AlertTitle, type AlertTone, ArtifactPane, type ArtifactPaneProps, Avatar, AvatarFallback, AvatarImage, Badge, type BadgeProps, Button, type ButtonProps, CATEGORY_TONES, Calendar, type CalendarDate, type CalendarProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type CardTitleProps, type CategoryTone, Checkbox, Chip, type ChipProps, CodeBlock, type CodeBlockProps, Collapsible, CollapsibleContent, CollapsibleTrigger, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, DatePicker, type DatePickerProps, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropZone, type DropZoneProps, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, type EmptyStateProps, FilterField, type FilterFieldProps, Heading, type HeadingProps, type HeadingVariant, HelpText, type HelpTextProps, IconTile, type IconTileProps, InlineCode, type InlineCodeProps, Input, type InputProps, Label, Logo, type LogoProps, Metric, type MetricProps, MetricStrip, type MetricStripProps, PageHeader, type PageHeaderProps, PageShell, type PageShellProps, Pagination, type PaginationProps, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, SegmentedControl, type SegmentedControlOption, type SegmentedControlProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarDropZone, type SidebarDropZoneProps, Skeleton, SkeletonCard, SkeletonTable, Slider, StatCard, type StatCardProps, StatusPill, type StatusPillProps, type StatusTone, Switch, TONE_CLASSES, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, type TableProps, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, type TagProps, TangleKnot, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TextShimmer, type TextShimmerProps, Textarea, type TextareaProps, ThemeToggle, TimeSelect, type TimeSelectProps, type Toast, ToastContainer, ToastProvider, type Tone, type ToneClasses, Toolbar, type ToolbarProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type UploadFile, UploadProgress, type UploadProgressProps, badgeVariants, buttonVariants, formatCalendarDate, initialsOf, localToday, parseCalendarDate, toneFor, useTheme, useToast };
|
package/dist/primitives.js
CHANGED
|
@@ -10,16 +10,20 @@ import { Slider } from "./primitives/slider.js";
|
|
|
10
10
|
import { HelpText } from "./primitives/help-text.js";
|
|
11
11
|
import { Label } from "./primitives/label.js";
|
|
12
12
|
import { Input, Textarea } from "./primitives/input.js";
|
|
13
|
+
import { Calendar, formatCalendarDate, localToday, parseCalendarDate } from "./primitives/calendar.js";
|
|
14
|
+
import { DatePicker } from "./primitives/date-picker.js";
|
|
15
|
+
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue } from "./primitives/select.js";
|
|
16
|
+
import { TimeSelect } from "./primitives/time-select.js";
|
|
13
17
|
import { Badge, badgeVariants } from "./primitives/badge.js";
|
|
14
18
|
import { Avatar, AvatarFallback, AvatarImage } from "./primitives/avatar.js";
|
|
15
19
|
import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from "./primitives/dropdown-menu.js";
|
|
16
|
-
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue } from "./primitives/select.js";
|
|
17
20
|
import { Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow } from "./primitives/table.js";
|
|
18
21
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "./primitives/tabs.js";
|
|
19
22
|
import { SegmentedControl } from "./primitives/segmented-control.js";
|
|
20
23
|
import { Progress } from "./primitives/progress.js";
|
|
21
24
|
import { Switch } from "./primitives/switch.js";
|
|
22
25
|
import { Checkbox } from "./primitives/checkbox.js";
|
|
26
|
+
import { RadioGroup, RadioGroupItem } from "./primitives/radio-group.js";
|
|
23
27
|
import { Separator } from "./primitives/separator.js";
|
|
24
28
|
import { Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger } from "./primitives/sheet.js";
|
|
25
29
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./primitives/tooltip.js";
|
|
@@ -48,4 +52,4 @@ import { IconTile, initialsOf } from "./primitives/icon-tile.js";
|
|
|
48
52
|
import { Metric, MetricStrip } from "./primitives/metric-strip.js";
|
|
49
53
|
import { FilterField, Toolbar } from "./primitives/toolbar.js";
|
|
50
54
|
import { Logo, TangleKnot } from "@tangle-network/brand";
|
|
51
|
-
export { Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, ArtifactPane, Avatar, AvatarFallback, AvatarImage, Badge, Button, CATEGORY_TONES, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Chip, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropZone, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, FilterField, Heading, HelpText, IconTile, InlineCode, Input, Label, Logo, Metric, MetricStrip, PageHeader, PageShell, Pagination, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, SegmentedControl, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarDropZone, Skeleton, SkeletonCard, SkeletonTable, Slider, StatCard, StatusPill, Switch, TONE_CLASSES, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, TangleKnot, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TextShimmer, Textarea, ThemeToggle, ToastContainer, ToastProvider, Toolbar, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UploadProgress, badgeVariants, buttonVariants, initialsOf, toneFor, useTheme, useToast };
|
|
55
|
+
export { Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, ArtifactPane, Avatar, AvatarFallback, AvatarImage, Badge, Button, CATEGORY_TONES, Calendar, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Chip, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, DatePicker, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropZone, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, FilterField, Heading, HelpText, IconTile, InlineCode, Input, Label, Logo, Metric, MetricStrip, PageHeader, PageShell, Pagination, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, SegmentedControl, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarDropZone, Skeleton, SkeletonCard, SkeletonTable, Slider, StatCard, StatusPill, Switch, TONE_CLASSES, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, TangleKnot, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TextShimmer, Textarea, ThemeToggle, TimeSelect, ToastContainer, ToastProvider, Toolbar, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UploadProgress, badgeVariants, buttonVariants, formatCalendarDate, initialsOf, localToday, parseCalendarDate, toneFor, useTheme, useToast };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/ui",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.30.0",
|
|
4
4
|
"description": "Generic React UI components for Tangle products — primitives, chat, run, files, editor, markdown.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -126,6 +126,7 @@
|
|
|
126
126
|
"@radix-ui/react-label": "^2.1.15",
|
|
127
127
|
"@radix-ui/react-popover": "^1.1.23",
|
|
128
128
|
"@radix-ui/react-progress": "^1.1.16",
|
|
129
|
+
"@radix-ui/react-radio-group": "^1.4.8",
|
|
129
130
|
"@radix-ui/react-select": "^2.3.7",
|
|
130
131
|
"@radix-ui/react-separator": "^1.1.15",
|
|
131
132
|
"@radix-ui/react-slider": "^1.4.7",
|
|
@@ -149,7 +150,7 @@
|
|
|
149
150
|
"peerDependencies": {
|
|
150
151
|
"@hocuspocus/provider": ">=2.15.0 <5.0.0",
|
|
151
152
|
"@nanostores/react": ">=0.8.4 <3.0.0",
|
|
152
|
-
"@tangle-network/brand": "^1.
|
|
153
|
+
"@tangle-network/brand": "^1.19.2",
|
|
153
154
|
"@tiptap/core": ">=3.20.4 <4.0.0",
|
|
154
155
|
"@tiptap/extension-collaboration": ">=3.20.4 <4.0.0",
|
|
155
156
|
"@tiptap/extension-collaboration-caret": ">=3.7.0 <4.0.0",
|
|
@@ -64,10 +64,12 @@ export const controlSizes = {
|
|
|
64
64
|
// resolves the recessed well inside every canonical/named light/dark scope.
|
|
65
65
|
// An autofill inset shadow coexists with the Tailwind focus ring; never hide
|
|
66
66
|
// autofill with a seconds-long background transition or clear the focus halo.
|
|
67
|
+
// --field-surface lets a context raise its fields: Toolbar sets it to the card
|
|
68
|
+
// surface so filters on the page canvas read as controls, not as holes in it.
|
|
67
69
|
export const fieldPresentation = cn(
|
|
68
70
|
focusField,
|
|
69
71
|
controlMotion,
|
|
70
|
-
"min-w-0 bg-[var(--bg-input)] text-foreground",
|
|
72
|
+
"min-w-0 bg-[var(--field-surface,var(--bg-input))] text-foreground",
|
|
71
73
|
"aria-invalid:border-[var(--surface-danger-border)] aria-invalid:hover:border-[var(--surface-danger-border)] aria-invalid:focus:border-[var(--focus-border-danger)] aria-invalid:focus:ring-[var(--focus-halo-danger)]",
|
|
72
74
|
"autofill:shadow-[inset_0_0_0_1000px_var(--bg-input)] autofill:[-webkit-text-fill-color:var(--text-primary)] autofill:caret-[var(--text-primary)]",
|
|
73
75
|
);
|
|
@@ -46,9 +46,15 @@ const buttonVariants = cva(
|
|
|
46
46
|
},
|
|
47
47
|
);
|
|
48
48
|
|
|
49
|
+
// `bare` keeps the shared focus ring, disabled and loading behavior and drops
|
|
50
|
+
// every surface, size and type class. It is for pressable rows, tiles and pills
|
|
51
|
+
// that draw their own layout; anything that reads as a button uses a styled
|
|
52
|
+
// variant and a size.
|
|
53
|
+
const bareButton = `${focusRing} disabled:pointer-events-none disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:opacity-50`;
|
|
54
|
+
|
|
49
55
|
export interface ButtonProps
|
|
50
56
|
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
51
|
-
variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link" | "sandbox" | null;
|
|
57
|
+
variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link" | "sandbox" | "bare" | null;
|
|
52
58
|
/** sm 32px, default/md 36px (`--control-height`), lg 44px; icon sizes are squares on the same heights. */
|
|
53
59
|
size?: "default" | "md" | "sm" | "lg" | "xl" | "icon" | "icon-sm" | "icon-lg" | "compact" | "touch" | null;
|
|
54
60
|
asChild?: boolean;
|
|
@@ -109,7 +115,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
109
115
|
// Keep slotted content intact: its action name must survive loading too.
|
|
110
116
|
return (
|
|
111
117
|
<Slot
|
|
112
|
-
className={cn(buttonVariants({ variant, size, className }))}
|
|
118
|
+
className={variant === "bare" ? cn(bareButton, className) : cn(buttonVariants({ variant, size, className }))}
|
|
113
119
|
ref={ref}
|
|
114
120
|
{...props}
|
|
115
121
|
{...(isDisabled ? disabledHandlers(props) : {})}
|
|
@@ -134,7 +140,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
134
140
|
|
|
135
141
|
return (
|
|
136
142
|
<button
|
|
137
|
-
className={cn(buttonVariants({ variant, size, className }))}
|
|
143
|
+
className={variant === "bare" ? cn(bareButton, className) : cn(buttonVariants({ variant, size, className }))}
|
|
138
144
|
ref={ref}
|
|
139
145
|
{...props}
|
|
140
146
|
disabled={disabled || loading}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
import { focusRing } from "../lib/focus";
|
|
6
|
+
import { cn } from "../lib/utils";
|
|
7
|
+
import { Button } from "./button";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Calendar dates are plain `YYYY-MM-DD` strings, the same value a native date
|
|
11
|
+
* input reports. All arithmetic runs in UTC so a date never shifts with the
|
|
12
|
+
* viewer's time zone or a daylight-saving boundary.
|
|
13
|
+
*/
|
|
14
|
+
export type CalendarDate = string;
|
|
15
|
+
|
|
16
|
+
const DATE_PATTERN = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
17
|
+
const DAY_MS = 86_400_000;
|
|
18
|
+
|
|
19
|
+
export function parseCalendarDate(value: string | null | undefined): number | null {
|
|
20
|
+
const match = value ? DATE_PATTERN.exec(value) : null;
|
|
21
|
+
if (!match) return null;
|
|
22
|
+
const time = Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3]));
|
|
23
|
+
const date = new Date(time);
|
|
24
|
+
// Reject impossible dates such as 2026-02-31 rather than rolling them over.
|
|
25
|
+
return date.getUTCMonth() === Number(match[2]) - 1 ? time : null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function formatCalendarDate(time: number): CalendarDate {
|
|
29
|
+
const date = new Date(time);
|
|
30
|
+
const month = String(date.getUTCMonth() + 1).padStart(2, "0");
|
|
31
|
+
const day = String(date.getUTCDate()).padStart(2, "0");
|
|
32
|
+
return `${date.getUTCFullYear()}-${month}-${day}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Today in the viewer's local calendar, as a `YYYY-MM-DD` string. */
|
|
36
|
+
export function localToday(now = new Date()): CalendarDate {
|
|
37
|
+
return formatCalendarDate(Date.UTC(now.getFullYear(), now.getMonth(), now.getDate()));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function firstOfMonth(time: number): number {
|
|
41
|
+
const date = new Date(time);
|
|
42
|
+
return Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function addMonths(time: number, delta: number): number {
|
|
46
|
+
const date = new Date(time);
|
|
47
|
+
const target = Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + delta, 1);
|
|
48
|
+
const lastDay = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + delta + 1, 0)).getUTCDate();
|
|
49
|
+
const day = Math.min(date.getUTCDate(), lastDay);
|
|
50
|
+
return target + (day - 1) * DAY_MS;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface CalendarProps {
|
|
54
|
+
/** The selected date, `YYYY-MM-DD`. */
|
|
55
|
+
value?: CalendarDate | null;
|
|
56
|
+
onChange?: (value: CalendarDate) => void;
|
|
57
|
+
/** The month shown first (any date inside it). Defaults to the value, else today. */
|
|
58
|
+
defaultMonth?: CalendarDate;
|
|
59
|
+
/** Earliest and latest selectable dates, inclusive. */
|
|
60
|
+
min?: CalendarDate;
|
|
61
|
+
max?: CalendarDate;
|
|
62
|
+
isDateDisabled?: (value: CalendarDate) => boolean;
|
|
63
|
+
/** The date marked as today. Pass it to use a workspace time zone instead of the viewer's. */
|
|
64
|
+
today?: CalendarDate;
|
|
65
|
+
/** 0 starts weeks on Sunday, 1 on Monday. */
|
|
66
|
+
weekStartsOn?: 0 | 1;
|
|
67
|
+
locale?: string;
|
|
68
|
+
className?: string;
|
|
69
|
+
"aria-label"?: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* A month grid that picks one date. Arrow keys move by day and week, Page Up
|
|
74
|
+
* and Page Down by month, Home and End to the week's ends; Enter or Space picks
|
|
75
|
+
* the focused date. Six weeks always render so the grid never changes height.
|
|
76
|
+
*/
|
|
77
|
+
const Calendar = React.forwardRef<HTMLDivElement, CalendarProps>(
|
|
78
|
+
(
|
|
79
|
+
{
|
|
80
|
+
value,
|
|
81
|
+
onChange,
|
|
82
|
+
defaultMonth,
|
|
83
|
+
min,
|
|
84
|
+
max,
|
|
85
|
+
isDateDisabled,
|
|
86
|
+
today: todayProp,
|
|
87
|
+
weekStartsOn = 0,
|
|
88
|
+
locale,
|
|
89
|
+
className,
|
|
90
|
+
"aria-label": ariaLabel,
|
|
91
|
+
},
|
|
92
|
+
ref,
|
|
93
|
+
) => {
|
|
94
|
+
const today = parseCalendarDate(todayProp) ?? (parseCalendarDate(localToday()) as number);
|
|
95
|
+
const selected = parseCalendarDate(value);
|
|
96
|
+
const minTime = parseCalendarDate(min);
|
|
97
|
+
const maxTime = parseCalendarDate(max);
|
|
98
|
+
const [focused, setFocused] = React.useState<number>(
|
|
99
|
+
() => selected ?? parseCalendarDate(defaultMonth) ?? today,
|
|
100
|
+
);
|
|
101
|
+
const viewMonth = firstOfMonth(focused);
|
|
102
|
+
const gridRef = React.useRef<HTMLDivElement>(null);
|
|
103
|
+
const moveFocusRef = React.useRef(false);
|
|
104
|
+
|
|
105
|
+
React.useEffect(() => {
|
|
106
|
+
if (selected !== null) setFocused(selected);
|
|
107
|
+
}, [selected]);
|
|
108
|
+
|
|
109
|
+
React.useEffect(() => {
|
|
110
|
+
if (!moveFocusRef.current) return;
|
|
111
|
+
moveFocusRef.current = false;
|
|
112
|
+
gridRef.current?.querySelector<HTMLButtonElement>('[data-focused="true"]')?.focus();
|
|
113
|
+
}, [focused]);
|
|
114
|
+
|
|
115
|
+
const disabled = (time: number) =>
|
|
116
|
+
(minTime !== null && time < minTime) ||
|
|
117
|
+
(maxTime !== null && time > maxTime) ||
|
|
118
|
+
Boolean(isDateDisabled?.(formatCalendarDate(time)));
|
|
119
|
+
|
|
120
|
+
const leading = (new Date(viewMonth).getUTCDay() - weekStartsOn + 7) % 7;
|
|
121
|
+
const start = viewMonth - leading * DAY_MS;
|
|
122
|
+
const days = Array.from({ length: 42 }, (_, index) => start + index * DAY_MS);
|
|
123
|
+
const weeks = Array.from({ length: 6 }, (_, row) => days.slice(row * 7, row * 7 + 7));
|
|
124
|
+
|
|
125
|
+
const monthLabel = new Intl.DateTimeFormat(locale, { month: "long", year: "numeric", timeZone: "UTC" }).format(viewMonth);
|
|
126
|
+
const dayLabel = new Intl.DateTimeFormat(locale, { weekday: "long", month: "long", day: "numeric", year: "numeric", timeZone: "UTC" });
|
|
127
|
+
const weekdayShort = new Intl.DateTimeFormat(locale, { weekday: "short", timeZone: "UTC" });
|
|
128
|
+
const weekdayLong = new Intl.DateTimeFormat(locale, { weekday: "long", timeZone: "UTC" });
|
|
129
|
+
const weekdays = days.slice(0, 7);
|
|
130
|
+
|
|
131
|
+
const move = (next: number) => {
|
|
132
|
+
moveFocusRef.current = true;
|
|
133
|
+
setFocused(next);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const onKeyDown = (event: React.KeyboardEvent) => {
|
|
137
|
+
const weekday = (new Date(focused).getUTCDay() - weekStartsOn + 7) % 7;
|
|
138
|
+
const steps: Record<string, () => number> = {
|
|
139
|
+
ArrowLeft: () => focused - DAY_MS,
|
|
140
|
+
ArrowRight: () => focused + DAY_MS,
|
|
141
|
+
ArrowUp: () => focused - 7 * DAY_MS,
|
|
142
|
+
ArrowDown: () => focused + 7 * DAY_MS,
|
|
143
|
+
PageUp: () => addMonths(focused, event.shiftKey ? -12 : -1),
|
|
144
|
+
PageDown: () => addMonths(focused, event.shiftKey ? 12 : 1),
|
|
145
|
+
Home: () => focused - weekday * DAY_MS,
|
|
146
|
+
End: () => focused + (6 - weekday) * DAY_MS,
|
|
147
|
+
};
|
|
148
|
+
const step = steps[event.key];
|
|
149
|
+
if (!step) return;
|
|
150
|
+
event.preventDefault();
|
|
151
|
+
move(step());
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const pick = (time: number) => {
|
|
155
|
+
if (disabled(time)) return;
|
|
156
|
+
setFocused(time);
|
|
157
|
+
onChange?.(formatCalendarDate(time));
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<div ref={ref} className={cn("w-fit select-none", className)}>
|
|
162
|
+
<div className="mb-2 flex items-center justify-between gap-2">
|
|
163
|
+
<Button
|
|
164
|
+
type="button"
|
|
165
|
+
variant="ghost"
|
|
166
|
+
size="icon-sm"
|
|
167
|
+
aria-label="Previous month"
|
|
168
|
+
onClick={() => setFocused(addMonths(focused, -1))}
|
|
169
|
+
>
|
|
170
|
+
<ChevronLeft />
|
|
171
|
+
</Button>
|
|
172
|
+
<div aria-live="polite" className="font-medium text-[length:var(--control-text-md,0.875rem)] text-foreground">
|
|
173
|
+
{monthLabel}
|
|
174
|
+
</div>
|
|
175
|
+
<Button
|
|
176
|
+
type="button"
|
|
177
|
+
variant="ghost"
|
|
178
|
+
size="icon-sm"
|
|
179
|
+
aria-label="Next month"
|
|
180
|
+
onClick={() => setFocused(addMonths(focused, 1))}
|
|
181
|
+
>
|
|
182
|
+
<ChevronRight />
|
|
183
|
+
</Button>
|
|
184
|
+
</div>
|
|
185
|
+
<div ref={gridRef} role="grid" aria-label={ariaLabel ?? monthLabel} onKeyDown={onKeyDown}>
|
|
186
|
+
<div role="row" className="grid grid-cols-7">
|
|
187
|
+
{weekdays.map((time) => (
|
|
188
|
+
<div
|
|
189
|
+
key={time}
|
|
190
|
+
role="columnheader"
|
|
191
|
+
aria-label={weekdayLong.format(time)}
|
|
192
|
+
className="flex h-8 items-center justify-center text-[length:var(--font-size-help,0.75rem)] text-muted-foreground"
|
|
193
|
+
>
|
|
194
|
+
{weekdayShort.format(time).slice(0, 2)}
|
|
195
|
+
</div>
|
|
196
|
+
))}
|
|
197
|
+
</div>
|
|
198
|
+
{weeks.map((week) => (
|
|
199
|
+
<div key={week[0]} role="row" className="grid grid-cols-7">
|
|
200
|
+
{week.map((time) => {
|
|
201
|
+
const outside = firstOfMonth(time) !== viewMonth;
|
|
202
|
+
const isSelected = selected === time;
|
|
203
|
+
const isToday = today === time;
|
|
204
|
+
const isDisabled = disabled(time);
|
|
205
|
+
return (
|
|
206
|
+
<div key={time} role="gridcell" aria-selected={isSelected}>
|
|
207
|
+
<button
|
|
208
|
+
type="button"
|
|
209
|
+
tabIndex={time === focused ? 0 : -1}
|
|
210
|
+
data-focused={time === focused}
|
|
211
|
+
data-outside={outside || undefined}
|
|
212
|
+
data-today={isToday || undefined}
|
|
213
|
+
aria-label={dayLabel.format(time)}
|
|
214
|
+
aria-current={isToday ? "date" : undefined}
|
|
215
|
+
aria-disabled={isDisabled || undefined}
|
|
216
|
+
onClick={() => pick(time)}
|
|
217
|
+
onFocus={() => { if (time !== focused) setFocused(time); }}
|
|
218
|
+
className={cn(
|
|
219
|
+
"flex size-[var(--control-height,2.25rem)] items-center justify-center rounded-lg text-[length:var(--control-text-md,0.875rem)] tabular-nums transition-colors",
|
|
220
|
+
focusRing,
|
|
221
|
+
"hover:bg-muted",
|
|
222
|
+
outside && "text-muted-foreground/60",
|
|
223
|
+
isToday && !isSelected && "font-semibold text-[var(--accent-text)]",
|
|
224
|
+
isSelected && "bg-primary font-medium text-primary-foreground hover:bg-primary/90",
|
|
225
|
+
isDisabled && "cursor-not-allowed opacity-40 hover:bg-transparent",
|
|
226
|
+
)}
|
|
227
|
+
>
|
|
228
|
+
{new Date(time).getUTCDate()}
|
|
229
|
+
</button>
|
|
230
|
+
</div>
|
|
231
|
+
);
|
|
232
|
+
})}
|
|
233
|
+
</div>
|
|
234
|
+
))}
|
|
235
|
+
</div>
|
|
236
|
+
</div>
|
|
237
|
+
);
|
|
238
|
+
},
|
|
239
|
+
);
|
|
240
|
+
Calendar.displayName = "Calendar";
|
|
241
|
+
|
|
242
|
+
export { Calendar };
|
|
@@ -12,7 +12,7 @@ afterEach(cleanup);
|
|
|
12
12
|
|
|
13
13
|
describe("shared control presentation", () => {
|
|
14
14
|
it("uses the field well, not the border/track alias, with scoped foreground and time-only motion", () => {
|
|
15
|
-
expect(fieldPresentation).toContain("bg-[var(--bg-input)]");
|
|
15
|
+
expect(fieldPresentation).toContain("bg-[var(--field-surface,var(--bg-input))]");
|
|
16
16
|
expect(fieldPresentation.split(" ")).not.toContain("bg-input");
|
|
17
17
|
expect(fieldPresentation.split(" ")).not.toContain("bg-card");
|
|
18
18
|
expect(fieldPresentation).toContain("border-border");
|
|
@@ -100,7 +100,7 @@ describe("shared control presentation", () => {
|
|
|
100
100
|
expect(input.current).toHaveAttribute("aria-invalid", "true");
|
|
101
101
|
expect(input.current!.className).toContain("autofill:shadow-");
|
|
102
102
|
expect(input.current!.className.split(" ")).toContain("bg-card");
|
|
103
|
-
expect(input.current!.className).not.toContain("bg-[var(--bg-input)]");
|
|
103
|
+
expect(input.current!.className).not.toContain("bg-[var(--field-surface,var(--bg-input))]");
|
|
104
104
|
expect(textarea.current).toBeDisabled();
|
|
105
105
|
expect(trigger.current).toBe(screen.getByLabelText("Choice"));
|
|
106
106
|
expect(trigger.current).toBeDisabled();
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
|
+
import { Button } from "./button";
|
|
4
|
+
import { Calendar } from "./calendar";
|
|
5
|
+
import { DatePicker } from "./date-picker";
|
|
6
|
+
import { Input } from "./input";
|
|
7
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./select";
|
|
8
|
+
import { TimeSelect } from "./time-select";
|
|
9
|
+
import { FilterField, Toolbar } from "./toolbar";
|
|
10
|
+
|
|
11
|
+
function FilterBar() {
|
|
12
|
+
const [date, setDate] = React.useState("");
|
|
13
|
+
const [format, setFormat] = React.useState("all");
|
|
14
|
+
const [time, setTime] = React.useState("09:30");
|
|
15
|
+
return (
|
|
16
|
+
<main className="space-y-8 bg-background p-6 text-foreground" data-date-picker-story>
|
|
17
|
+
<section>
|
|
18
|
+
<h2 className="mb-3 font-semibold text-section">On the page canvas, in a Toolbar</h2>
|
|
19
|
+
<Toolbar
|
|
20
|
+
search={<Input aria-label="Filter assets" placeholder="Filter assets…" />}
|
|
21
|
+
filters={<>
|
|
22
|
+
<FilterField label="Format" htmlFor="format">
|
|
23
|
+
<Select value={format} onValueChange={setFormat}>
|
|
24
|
+
<SelectTrigger id="format" className="w-40"><SelectValue /></SelectTrigger>
|
|
25
|
+
<SelectContent>
|
|
26
|
+
<SelectItem value="all">All formats</SelectItem>
|
|
27
|
+
<SelectItem value="email">Email</SelectItem>
|
|
28
|
+
<SelectItem value="story">Story</SelectItem>
|
|
29
|
+
</SelectContent>
|
|
30
|
+
</Select>
|
|
31
|
+
</FilterField>
|
|
32
|
+
<FilterField label="Schedule for" htmlFor="schedule">
|
|
33
|
+
<DatePicker id="schedule" className="w-40" placeholder="Any date" value={date} onChange={setDate} />
|
|
34
|
+
</FilterField>
|
|
35
|
+
<FilterField label="At" htmlFor="at">
|
|
36
|
+
<TimeSelect id="at" className="w-32" value={time} onChange={setTime} />
|
|
37
|
+
</FilterField>
|
|
38
|
+
</>}
|
|
39
|
+
actions={<Button variant="outline">Approve all (4)</Button>}
|
|
40
|
+
/>
|
|
41
|
+
</section>
|
|
42
|
+
<section className="max-w-sm space-y-2 rounded-xl border border-border bg-card p-4">
|
|
43
|
+
<h2 className="font-semibold text-section">In a card, in the field well</h2>
|
|
44
|
+
<DatePicker aria-label="Due" value="2026-10-08" />
|
|
45
|
+
</section>
|
|
46
|
+
<section className="w-fit rounded-xl border border-border bg-card p-3">
|
|
47
|
+
<Calendar value="2026-10-08" today="2026-10-08" weekStartsOn={1} />
|
|
48
|
+
</section>
|
|
49
|
+
</main>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const meta = { title: "Primitives/Date Picker", component: FilterBar, parameters: { layout: "fullscreen" } } satisfies Meta<typeof FilterBar>;
|
|
54
|
+
export default meta;
|
|
55
|
+
export const InAToolbar: StoryObj<typeof meta> = {};
|