@tangle-network/ui 11.19.0 → 11.21.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 +3 -1
- package/dist/index.js +3 -1
- package/dist/primitives/alert.d.ts +35 -0
- package/dist/primitives/alert.js +47 -0
- package/dist/primitives/icon-tile.d.ts +6 -1
- package/dist/primitives/icon-tile.js +3 -1
- package/dist/primitives/pagination.d.ts +19 -0
- package/dist/primitives/pagination.js +81 -0
- package/dist/primitives/segmented-control.d.ts +4 -0
- package/dist/primitives/segmented-control.js +4 -1
- package/dist/primitives.d.ts +3 -1
- package/dist/primitives.js +3 -1
- package/package.json +2 -2
- package/src/primitives/alert.tsx +80 -0
- package/src/primitives/icon-tile.stories.tsx +2 -2
- package/src/primitives/icon-tile.test.tsx +39 -0
- package/src/primitives/icon-tile.tsx +8 -1
- package/src/primitives/index.ts +6 -0
- package/src/primitives/pagination-alert.stories.tsx +51 -0
- package/src/primitives/pagination-alert.test.tsx +90 -0
- package/src/primitives/pagination.tsx +117 -0
- package/src/primitives/segmented-control.test.tsx +20 -0
- package/src/primitives/segmented-control.tsx +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @tangle-network/ui
|
|
2
2
|
|
|
3
|
+
## 11.21.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- a69c72f: Add `Pagination` (zero-based `page`, `pageCount`, `onPageChange`; a constant number of page slots with first and last always reachable) and `Alert` with `AlertTitle` and `AlertDescription` (Brand status tones plus `neutral`). `SegmentedControl` options take `testId`, and the group takes `data-testid`.
|
|
8
|
+
|
|
9
|
+
## 11.20.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 24ea445: IconTile gains `xl` (64px) and `2xl` (80px) sizes for page and profile headers, so an app can show an entity's face at header scale without a local avatar component.
|
|
14
|
+
|
|
3
15
|
## 11.19.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -76,6 +76,8 @@ import { Heading, HeadingProps, HeadingVariant } from "./primitives/heading.js";
|
|
|
76
76
|
import { PageShell, PageShellProps } from "./primitives/page-shell.js";
|
|
77
77
|
import { PageHeader, PageHeaderProps } from "./primitives/page-header.js";
|
|
78
78
|
import { StatusPill, StatusPillProps, StatusTone } from "./primitives/status-pill.js";
|
|
79
|
+
import { Alert, AlertDescription, AlertProps, AlertTitle, AlertTone } from "./primitives/alert.js";
|
|
80
|
+
import { Pagination, PaginationProps } from "./primitives/pagination.js";
|
|
79
81
|
import { CATEGORY_TONES, CategoryTone, TONE_CLASSES, Tone, ToneClasses, toneFor } from "./primitives/tone.js";
|
|
80
82
|
import { Tag, TagProps } from "./primitives/tag.js";
|
|
81
83
|
import { Chip, ChipProps } from "./primitives/chip.js";
|
|
@@ -112,4 +114,4 @@ import { QuestionPreview, QuestionPreviewProps } from "./tool-previews/question-
|
|
|
112
114
|
import { DiffPreview, DiffPreviewProps } from "./tool-previews/diff-preview.js";
|
|
113
115
|
import { RedactedDocSegment, RedactedDocument, RedactedDocumentData, RedactedDocumentProps, RevealResult } from "./redaction/redacted-document.js";
|
|
114
116
|
import "./redaction.js";
|
|
115
|
-
export { ActiveProjectActivity, ActiveSessionActivityOptions, ActiveSessionConnectionOptions, ActiveSessionConnectionState, ActiveSessionReconnectState, ActiveSessionRecord, ActiveSessionStatus, ActiveSessionTransportMode, ActiveSessionsState, AgentBranding, AgentStreamEvent, AgentTimeline, type AgentTimelineArtifactItem, type AgentTimelineCustomItem, type AgentTimelineItem, type AgentTimelineMessageItem, type AgentTimelineProps, type AgentTimelineStatusItem, type AgentTimelineTone, type AgentTimelineToolGroupItem, type AgentTimelineToolItem, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AppendUserMessageOptions, ApplySdkEventOptions, ArtifactPane, type ArtifactPaneProps, AuthHeader, type AuthHeaderProps, AuthUser, AutomationStreamEvent, Avatar, AvatarFallback, AvatarImage, Badge, type BadgeProps, BeginAssistantMessageOptions, BotStreamEvent, Button, type ButtonProps, CATEGORY_TONES, CSV_PREVIEW_ROW_LIMIT, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type CardTitleProps, type CategoryTone, ChatContainer, type ChatContainerProps, ChatMessage, type ChatMessageProps, Checkbox, Chip, type ChipProps, CodeBlock, type CodeBlockProps, Collapsible, CollapsibleContent, CollapsibleTrigger, CommandPreview, type CommandPreviewProps, CompleteAssistantMessageOptions, ConnectionState, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, CustomToolRenderer, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DiffPreview, type DiffPreviewProps, DisplayVariant, DropZone, type DropZoneProps, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, type EmptyStateProps, ExpandedToolDetail, type ExpandedToolDetailProps, type FeedSegment, FileArtifactPane, type FileArtifactPaneProps, type FileFormat, type FileNode, FilePreview, type FilePreviewKind, type FilePreviewProps, type FileTabData, FileTabs, type FileTabsProps, FileTree, type FileTreeProps, type FileTreeVisibilityOptions, FilterField, type FilterFieldProps, FinalTextPart, GitHubLoginButton, type GitHubLoginButtonProps, GlobResultsPreview, type GlobResultsPreviewProps, GrepResultsPreview, type GrepResultsPreviewProps, GroupedMessage, Heading, type HeadingProps, type HeadingVariant, IconTile, type IconTileProps, InlineCode, type InlineCodeProps, InlineThinkingItem, type InlineThinkingItemProps, InlineToolItem, type InlineToolItemProps, Input, type InputProps, Label, LiveDuration, LoginLayout, type LoginLayoutProps, Logo, type LogoProps, Markdown, type MarkdownProps, MessageList, type MessageListProps, type MessageRole, MessageRun, MessageUser, Metric, type MetricProps, MetricStrip, type MetricStripProps, OPENUI_NODE_JSON_SCHEMA, OPENUI_NODE_TYPES, type OpenUIAction, type OpenUIActionsNode, OpenUIArtifactRenderer, type OpenUIArtifactRendererProps, type OpenUIBadgeNode, type OpenUICardNode, type OpenUICodeNode, type OpenUIComponentNode, type OpenUIGridNode, type OpenUIHeadingNode, type OpenUIKeyValueNode, type OpenUIMarkdownNode, type OpenUIPrimitive, type OpenUISchemaIssue, type OpenUISchemaValidation, type OpenUISeparatorNode, type OpenUIStackNode, type OpenUIStatNode, type OpenUITableNode, type OpenUITextNode, PageHeader, type PageHeaderProps, PageShell, type PageShellProps, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, QuestionPreview, type QuestionPreviewProps, RealtimeSessionOptions, RealtimeSessionRegistry, RealtimeSessionRegistryProps, RealtimeSessionState, RealtimeSessionTarget, ReasoningPart, type RedactedDocSegment, RedactedDocument, type RedactedDocumentData, type RedactedDocumentProps, RegisterActiveSessionOptions, type RevealResult, RichFileTree, type RichFileTreeGitEntry, type RichFileTreeGitStatus, type RichFileTreeProps, type RichFileTreeThemeVars, Run, RunGroup, type RunGroupProps, RunRowShell, type RunRowShellProps, type RunRowStatus, RunRowStatusDot, RunStats, SSEEvent, SdkSessionAttachment, SdkSessionEvent, SdkSessionSeed, SegmentedControl, type SegmentedControlOption, type SegmentedControlProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, SessionMessage, SessionPart, SessionProjectKey, type SessionUser, 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, TOOL_CATEGORY_ICONS, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, type TableProps, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, type TagProps, TangleKnot, TaskStreamEvent, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TerminalStreamEvent, TextPart, TextShimmer, type TextShimmerProps, Textarea, type TextareaProps, ThemeToggle, ThinkingIndicator, type ThinkingIndicatorProps, type Toast, ToastContainer, ToastProvider, type Tone, type ToneClasses, type ToolCallData, ToolCallFeed, type ToolCallFeedProps, type ToolCallStatus, type ToolCallType, ToolCategory, ToolDisplayMetadata, ToolPart, ToolState, ToolStatus, ToolTime, Toolbar, type ToolbarProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type UploadFile, UploadProgress, type UploadProgressProps, UseAuthOptions, UseAuthResult, UseRunGroupsOptions, UseSSEStreamOptions, UseSSEStreamResult, UseSdkSessionOptions, UseSdkSessionReturn, UseToolCallStreamReturn, UserMenu, type UserMenuProps, UserMessage, type UserMessageProps, WebSearchPreview, type WebSearchPreviewProps, WriteFilePreview, type WriteFilePreviewProps, activeSessionsAtom, addMessage, addParts, badgeVariants, bumpActiveSessionActivity, buttonVariants, clearChat, cn, copyText, createAuthFetcher, detectFileFormat, fileExtension, filterFileTree, findUnsupportedOpenUINode, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatDuration, formatUptime, getActiveSession, getAllActiveSessions, getAllProjectActivity, getCodeLanguage, getFormatLabel, getSanitizedMarkdownHeadingIdFromRawFragment, getSessionsByActivity, getSessionsForNavbar, getSessionsForProject, getSyntaxLanguage, getToolCategory, getToolDisplayMetadata, getToolErrorText, getTotalRunningSessionCount, hasBackgroundRunningSessions, initialsOf, isStreamingAtom, messagesAtom, parseCsv, parseToolEvent, partMapAtom, registerActiveSession, resetActiveSessions, resolveFilePreviewKind, setActiveSessionAttention, setActiveSessionConnection, setActiveSessionError, setActiveSessionRunning, setForegroundActiveSession, timeAgo, toneFor, truncateText, unregisterActiveSession, updateActiveSessionMeta, updatePart, useActiveSession, useActiveSessions, useActiveSessionsState, useApiKey, useAuth, useAutoScroll, useDropdownMenu, useHasBackgroundRunningSessions, useLiveTime, useNavbarSessions, useProjectActivity, useProjectSessions, useRealtimeSession, useRunCollapseState, useRunGroups, useSSEStream, useSdkSession, useSessionsByActivity, useTheme, useToast, useToolCallStream, useTotalRunningSessions, validateOpenUIJsonArtifact, validateOpenUIJsonNode };
|
|
117
|
+
export { ActiveProjectActivity, ActiveSessionActivityOptions, ActiveSessionConnectionOptions, ActiveSessionConnectionState, ActiveSessionReconnectState, ActiveSessionRecord, ActiveSessionStatus, ActiveSessionTransportMode, ActiveSessionsState, AgentBranding, AgentStreamEvent, AgentTimeline, type AgentTimelineArtifactItem, type AgentTimelineCustomItem, type AgentTimelineItem, type AgentTimelineMessageItem, type AgentTimelineProps, type AgentTimelineStatusItem, type AgentTimelineTone, type AgentTimelineToolGroupItem, type AgentTimelineToolItem, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, type AlertProps, AlertTitle, type AlertTone, AppendUserMessageOptions, ApplySdkEventOptions, ArtifactPane, type ArtifactPaneProps, AuthHeader, type AuthHeaderProps, AuthUser, AutomationStreamEvent, Avatar, AvatarFallback, AvatarImage, Badge, type BadgeProps, BeginAssistantMessageOptions, BotStreamEvent, Button, type ButtonProps, CATEGORY_TONES, CSV_PREVIEW_ROW_LIMIT, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type CardTitleProps, type CategoryTone, ChatContainer, type ChatContainerProps, ChatMessage, type ChatMessageProps, Checkbox, Chip, type ChipProps, CodeBlock, type CodeBlockProps, Collapsible, CollapsibleContent, CollapsibleTrigger, CommandPreview, type CommandPreviewProps, CompleteAssistantMessageOptions, ConnectionState, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, CustomToolRenderer, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DiffPreview, type DiffPreviewProps, DisplayVariant, DropZone, type DropZoneProps, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, type EmptyStateProps, ExpandedToolDetail, type ExpandedToolDetailProps, type FeedSegment, FileArtifactPane, type FileArtifactPaneProps, type FileFormat, type FileNode, FilePreview, type FilePreviewKind, type FilePreviewProps, type FileTabData, FileTabs, type FileTabsProps, FileTree, type FileTreeProps, type FileTreeVisibilityOptions, FilterField, type FilterFieldProps, FinalTextPart, GitHubLoginButton, type GitHubLoginButtonProps, GlobResultsPreview, type GlobResultsPreviewProps, GrepResultsPreview, type GrepResultsPreviewProps, GroupedMessage, Heading, type HeadingProps, type HeadingVariant, IconTile, type IconTileProps, InlineCode, type InlineCodeProps, InlineThinkingItem, type InlineThinkingItemProps, InlineToolItem, type InlineToolItemProps, Input, type InputProps, Label, LiveDuration, LoginLayout, type LoginLayoutProps, Logo, type LogoProps, Markdown, type MarkdownProps, MessageList, type MessageListProps, type MessageRole, MessageRun, MessageUser, Metric, type MetricProps, MetricStrip, type MetricStripProps, OPENUI_NODE_JSON_SCHEMA, OPENUI_NODE_TYPES, type OpenUIAction, type OpenUIActionsNode, OpenUIArtifactRenderer, type OpenUIArtifactRendererProps, type OpenUIBadgeNode, type OpenUICardNode, type OpenUICodeNode, type OpenUIComponentNode, type OpenUIGridNode, type OpenUIHeadingNode, type OpenUIKeyValueNode, type OpenUIMarkdownNode, type OpenUIPrimitive, type OpenUISchemaIssue, type OpenUISchemaValidation, type OpenUISeparatorNode, type OpenUIStackNode, type OpenUIStatNode, type OpenUITableNode, type OpenUITextNode, PageHeader, type PageHeaderProps, PageShell, type PageShellProps, Pagination, type PaginationProps, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, QuestionPreview, type QuestionPreviewProps, RealtimeSessionOptions, RealtimeSessionRegistry, RealtimeSessionRegistryProps, RealtimeSessionState, RealtimeSessionTarget, ReasoningPart, type RedactedDocSegment, RedactedDocument, type RedactedDocumentData, type RedactedDocumentProps, RegisterActiveSessionOptions, type RevealResult, RichFileTree, type RichFileTreeGitEntry, type RichFileTreeGitStatus, type RichFileTreeProps, type RichFileTreeThemeVars, Run, RunGroup, type RunGroupProps, RunRowShell, type RunRowShellProps, type RunRowStatus, RunRowStatusDot, RunStats, SSEEvent, SdkSessionAttachment, SdkSessionEvent, SdkSessionSeed, SegmentedControl, type SegmentedControlOption, type SegmentedControlProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, SessionMessage, SessionPart, SessionProjectKey, type SessionUser, 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, TOOL_CATEGORY_ICONS, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, type TableProps, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, type TagProps, TangleKnot, TaskStreamEvent, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TerminalStreamEvent, TextPart, TextShimmer, type TextShimmerProps, Textarea, type TextareaProps, ThemeToggle, ThinkingIndicator, type ThinkingIndicatorProps, type Toast, ToastContainer, ToastProvider, type Tone, type ToneClasses, type ToolCallData, ToolCallFeed, type ToolCallFeedProps, type ToolCallStatus, type ToolCallType, ToolCategory, ToolDisplayMetadata, ToolPart, ToolState, ToolStatus, ToolTime, Toolbar, type ToolbarProps, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type UploadFile, UploadProgress, type UploadProgressProps, UseAuthOptions, UseAuthResult, UseRunGroupsOptions, UseSSEStreamOptions, UseSSEStreamResult, UseSdkSessionOptions, UseSdkSessionReturn, UseToolCallStreamReturn, UserMenu, type UserMenuProps, UserMessage, type UserMessageProps, WebSearchPreview, type WebSearchPreviewProps, WriteFilePreview, type WriteFilePreviewProps, activeSessionsAtom, addMessage, addParts, badgeVariants, bumpActiveSessionActivity, buttonVariants, clearChat, cn, copyText, createAuthFetcher, detectFileFormat, fileExtension, filterFileTree, findUnsupportedOpenUINode, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatDuration, formatUptime, getActiveSession, getAllActiveSessions, getAllProjectActivity, getCodeLanguage, getFormatLabel, getSanitizedMarkdownHeadingIdFromRawFragment, getSessionsByActivity, getSessionsForNavbar, getSessionsForProject, getSyntaxLanguage, getToolCategory, getToolDisplayMetadata, getToolErrorText, getTotalRunningSessionCount, hasBackgroundRunningSessions, initialsOf, isStreamingAtom, messagesAtom, parseCsv, parseToolEvent, partMapAtom, registerActiveSession, resetActiveSessions, resolveFilePreviewKind, setActiveSessionAttention, setActiveSessionConnection, setActiveSessionError, setActiveSessionRunning, setForegroundActiveSession, timeAgo, toneFor, truncateText, unregisterActiveSession, updateActiveSessionMeta, updatePart, useActiveSession, useActiveSessions, useActiveSessionsState, useApiKey, useAuth, useAutoScroll, useDropdownMenu, useHasBackgroundRunningSessions, useLiveTime, useNavbarSessions, useProjectActivity, useProjectSessions, useRealtimeSession, useRunCollapseState, useRunGroups, useSSEStream, useSdkSession, useSessionsByActivity, useTheme, useToast, useToolCallStream, useTotalRunningSessions, validateOpenUIJsonArtifact, validateOpenUIJsonNode };
|
package/dist/index.js
CHANGED
|
@@ -39,6 +39,8 @@ import { InlineCode } from "./primitives/code-block.js";
|
|
|
39
39
|
import { ThemeToggle, useTheme } from "./primitives/theme-toggle.js";
|
|
40
40
|
import { PageShell } from "./primitives/page-shell.js";
|
|
41
41
|
import { PageHeader } from "./primitives/page-header.js";
|
|
42
|
+
import { Alert, AlertDescription, AlertTitle } from "./primitives/alert.js";
|
|
43
|
+
import { Pagination } from "./primitives/pagination.js";
|
|
42
44
|
import { StatusPill } from "./primitives/status-pill.js";
|
|
43
45
|
import { CATEGORY_TONES, TONE_CLASSES, toneFor } from "./primitives/tone.js";
|
|
44
46
|
import { Tag } from "./primitives/tag.js";
|
|
@@ -96,4 +98,4 @@ import { addMessage, addParts, clearChat, isStreamingAtom, messagesAtom, partMap
|
|
|
96
98
|
import { copyText } from "./utils/copy-text.js";
|
|
97
99
|
import { timeAgo } from "./utils/time-ago.js";
|
|
98
100
|
import { RedactedDocument } from "./redaction/redacted-document.js";
|
|
99
|
-
export { AgentTimeline, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, ArtifactPane, AuthHeader, Avatar, AvatarFallback, AvatarImage, Badge, Button, CATEGORY_TONES, CSV_PREVIEW_ROW_LIMIT, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, ChatContainer, ChatMessage, Checkbox, Chip, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, CommandPreview, 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, DiffPreview, DropZone, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, ExpandedToolDetail, FileArtifactPane, FilePreview, FileTabs, FileTree, FilterField, GitHubLoginButton, GlobResultsPreview, GrepResultsPreview, Heading, IconTile, InlineCode, InlineThinkingItem, InlineToolItem, Input, Label, LiveDuration, LoginLayout, Logo, Markdown, MessageList, Metric, MetricStrip, OPENUI_NODE_JSON_SCHEMA, OPENUI_NODE_TYPES, OpenUIArtifactRenderer, PageHeader, PageShell, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, QuestionPreview, RealtimeSessionRegistry, RedactedDocument, RichFileTree, RunGroup, RunRowShell, RunRowStatusDot, 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, TOOL_CATEGORY_ICONS, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, TangleKnot, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TextShimmer, Textarea, ThemeToggle, ThinkingIndicator, ToastContainer, ToastProvider, ToolCallFeed, Toolbar, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UploadProgress, UserMenu, UserMessage, WebSearchPreview, WriteFilePreview, activeSessionsAtom, addMessage, addParts, badgeVariants, bumpActiveSessionActivity, buttonVariants, clearChat, cn, copyText, createAuthFetcher, detectFileFormat, fileExtension, filterFileTree, findUnsupportedOpenUINode, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatDuration, formatUptime, getActiveSession, getAllActiveSessions, getAllProjectActivity, getCodeLanguage, getFormatLabel, getSanitizedMarkdownHeadingIdFromRawFragment, getSessionsByActivity, getSessionsForNavbar, getSessionsForProject, getSyntaxLanguage, getToolCategory, getToolDisplayMetadata, getToolErrorText, getTotalRunningSessionCount, hasBackgroundRunningSessions, initialsOf, isStreamingAtom, messagesAtom, parseCsv, parseToolEvent, partMapAtom, registerActiveSession, resetActiveSessions, resolveFilePreviewKind, setActiveSessionAttention, setActiveSessionConnection, setActiveSessionError, setActiveSessionRunning, setForegroundActiveSession, timeAgo, toneFor, truncateText, unregisterActiveSession, updateActiveSessionMeta, updatePart, useActiveSession, useActiveSessions, useActiveSessionsState, useApiKey, useAuth, useAutoScroll, useDropdownMenu, useHasBackgroundRunningSessions, useLiveTime, useNavbarSessions, useProjectActivity, useProjectSessions, useRealtimeSession, useRunCollapseState, useRunGroups, useSSEStream, useSdkSession, useSessionsByActivity, useTheme, useToast, useToolCallStream, useTotalRunningSessions, validateOpenUIJsonArtifact, validateOpenUIJsonNode };
|
|
101
|
+
export { AgentTimeline, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, ArtifactPane, AuthHeader, Avatar, AvatarFallback, AvatarImage, Badge, Button, CATEGORY_TONES, CSV_PREVIEW_ROW_LIMIT, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, ChatContainer, ChatMessage, Checkbox, Chip, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, CommandPreview, 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, DiffPreview, DropZone, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, ExpandedToolDetail, FileArtifactPane, FilePreview, FileTabs, FileTree, FilterField, GitHubLoginButton, GlobResultsPreview, GrepResultsPreview, Heading, IconTile, InlineCode, InlineThinkingItem, InlineToolItem, Input, Label, LiveDuration, LoginLayout, Logo, Markdown, MessageList, Metric, MetricStrip, OPENUI_NODE_JSON_SCHEMA, OPENUI_NODE_TYPES, OpenUIArtifactRenderer, PageHeader, PageShell, Pagination, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, QuestionPreview, RealtimeSessionRegistry, RedactedDocument, RichFileTree, RunGroup, RunRowShell, RunRowStatusDot, 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, TOOL_CATEGORY_ICONS, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, TangleKnot, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TextShimmer, Textarea, ThemeToggle, ThinkingIndicator, ToastContainer, ToastProvider, ToolCallFeed, Toolbar, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UploadProgress, UserMenu, UserMessage, WebSearchPreview, WriteFilePreview, activeSessionsAtom, addMessage, addParts, badgeVariants, bumpActiveSessionActivity, buttonVariants, clearChat, cn, copyText, createAuthFetcher, detectFileFormat, fileExtension, filterFileTree, findUnsupportedOpenUINode, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatDuration, formatUptime, getActiveSession, getAllActiveSessions, getAllProjectActivity, getCodeLanguage, getFormatLabel, getSanitizedMarkdownHeadingIdFromRawFragment, getSessionsByActivity, getSessionsForNavbar, getSessionsForProject, getSyntaxLanguage, getToolCategory, getToolDisplayMetadata, getToolErrorText, getTotalRunningSessionCount, hasBackgroundRunningSessions, initialsOf, isStreamingAtom, messagesAtom, parseCsv, parseToolEvent, partMapAtom, registerActiveSession, resetActiveSessions, resolveFilePreviewKind, setActiveSessionAttention, setActiveSessionConnection, setActiveSessionError, setActiveSessionRunning, setForegroundActiveSession, timeAgo, toneFor, truncateText, unregisterActiveSession, updateActiveSessionMeta, updatePart, useActiveSession, useActiveSessions, useActiveSessionsState, useApiKey, useAuth, useAutoScroll, useDropdownMenu, useHasBackgroundRunningSessions, useLiveTime, useNavbarSessions, useProjectActivity, useProjectSessions, useRealtimeSession, useRunCollapseState, useRunGroups, useSSEStream, useSdkSession, useSessionsByActivity, useTheme, useToast, useToolCallStream, useTotalRunningSessions, validateOpenUIJsonArtifact, validateOpenUIJsonNode };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { StatusTone } from "./status-pill.js";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
//#region src/primitives/alert.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* An inline message that stays on the page: a policy note above a form, a
|
|
6
|
+
* failed save beside the field it concerns. For a message that interrupts, use
|
|
7
|
+
* `AlertDialog`; for one that goes away by itself, use a toast.
|
|
8
|
+
*
|
|
9
|
+
* Each tone paints its fill, border and text from Brand's matched status
|
|
10
|
+
* triple, the same one `StatusPill` uses, so the text is solved against its
|
|
11
|
+
* own background on every plane. `neutral` is a card with body text.
|
|
12
|
+
*
|
|
13
|
+
* `danger` and `warning` announce themselves (`role="alert"`); the quieter
|
|
14
|
+
* tones are `role="status"` so a screen reader is not interrupted by a note.
|
|
15
|
+
*/
|
|
16
|
+
export type AlertTone = Exclude<StatusTone, "running">;
|
|
17
|
+
export interface AlertProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
18
|
+
tone?: AlertTone;
|
|
19
|
+
/** A leading icon. It takes the tone's colour and sits beside the title. */
|
|
20
|
+
icon?: React.ReactNode;
|
|
21
|
+
}
|
|
22
|
+
export declare const Alert: React.ForwardRefExoticComponent<AlertProps & React.RefAttributes<HTMLDivElement>>;
|
|
23
|
+
export declare function AlertTitle({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>): React.JSX.Element;
|
|
24
|
+
export declare namespace AlertTitle {
|
|
25
|
+
var displayName: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Body text. Under a toned alert it keeps the tone's ink, which is the colour
|
|
29
|
+
* tested against the tone's fill; under `neutral` it is the muted tier.
|
|
30
|
+
*/
|
|
31
|
+
export declare function AlertDescription({ className, ...props }: React.HTMLAttributes<HTMLDivElement>): React.JSX.Element;
|
|
32
|
+
export declare namespace AlertDescription {
|
|
33
|
+
var displayName: string;
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { cn } from "../lib/utils.js";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
//#region src/primitives/alert.tsx
|
|
5
|
+
const TONE = {
|
|
6
|
+
neutral: "border-border bg-card text-card-foreground",
|
|
7
|
+
info: "border-[var(--surface-info-border)] bg-[var(--surface-info-bg)] text-[var(--surface-info-text)]",
|
|
8
|
+
success: "border-[var(--surface-success-border)] bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]",
|
|
9
|
+
warning: "border-[var(--surface-warning-border)] bg-[var(--surface-warning-bg)] text-[var(--surface-warning-text)]",
|
|
10
|
+
danger: "border-[var(--surface-danger-border)] bg-[var(--surface-danger-bg)] text-[var(--surface-danger-text)]"
|
|
11
|
+
};
|
|
12
|
+
const Alert = React.forwardRef(({ tone = "neutral", icon, className, children, role, ...props }, ref) => /* @__PURE__ */ jsxs("div", {
|
|
13
|
+
ref,
|
|
14
|
+
role: role ?? (tone === "danger" || tone === "warning" ? "alert" : "status"),
|
|
15
|
+
"data-tone": tone,
|
|
16
|
+
className: cn("flex w-full min-w-0 gap-3 rounded-lg border px-4 py-3 text-sm", TONE[tone], className),
|
|
17
|
+
...props,
|
|
18
|
+
children: [icon ? /* @__PURE__ */ jsx("span", {
|
|
19
|
+
"aria-hidden": "true",
|
|
20
|
+
className: "mt-0.5 flex shrink-0 [&_svg]:size-4",
|
|
21
|
+
children: icon
|
|
22
|
+
}) : null, /* @__PURE__ */ jsx("div", {
|
|
23
|
+
className: "grid min-w-0 flex-1 gap-1",
|
|
24
|
+
children
|
|
25
|
+
})]
|
|
26
|
+
}));
|
|
27
|
+
Alert.displayName = "Alert";
|
|
28
|
+
function AlertTitle({ className, ...props }) {
|
|
29
|
+
return /* @__PURE__ */ jsx("p", {
|
|
30
|
+
className: cn("font-medium leading-snug [overflow-wrap:anywhere]", className),
|
|
31
|
+
...props
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
AlertTitle.displayName = "AlertTitle";
|
|
35
|
+
/**
|
|
36
|
+
* Body text. Under a toned alert it keeps the tone's ink, which is the colour
|
|
37
|
+
* tested against the tone's fill; under `neutral` it is the muted tier.
|
|
38
|
+
*/
|
|
39
|
+
function AlertDescription({ className, ...props }) {
|
|
40
|
+
return /* @__PURE__ */ jsx("div", {
|
|
41
|
+
className: cn("leading-relaxed [overflow-wrap:anywhere] [[data-tone=neutral]_&]:text-muted-foreground", className),
|
|
42
|
+
...props
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
AlertDescription.displayName = "AlertDescription";
|
|
46
|
+
//#endregion
|
|
47
|
+
export { Alert, AlertDescription, AlertTitle };
|
|
@@ -17,7 +17,12 @@ import * as React from "react";
|
|
|
17
17
|
export interface IconTileProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, "children"> {
|
|
18
18
|
/** Defaults to a tone derived from `name`, or violet when there is no name. */
|
|
19
19
|
tone?: CategoryTone;
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
|
+
* `xs` to `lg` (20 to 40px) sit in rows and cards. `xl` (64px) and `2xl`
|
|
22
|
+
* (80px) are for a page or profile header, where the tile is the thing's
|
|
23
|
+
* face rather than a list marker.
|
|
24
|
+
*/
|
|
25
|
+
size?: "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
|
|
21
26
|
src?: string;
|
|
22
27
|
icon?: React.ReactNode;
|
|
23
28
|
/** The thing's name. Supplies the initials and the derived tone. */
|
|
@@ -7,7 +7,9 @@ const SIZE = {
|
|
|
7
7
|
xs: "size-5 rounded-[5px] text-[10px] [&_svg]:size-3",
|
|
8
8
|
sm: "size-6 rounded-md text-[11px] [&_svg]:size-3.5",
|
|
9
9
|
md: "size-8 rounded-md text-xs [&_svg]:size-4",
|
|
10
|
-
lg: "size-10 rounded-lg text-sm [&_svg]:size-5"
|
|
10
|
+
lg: "size-10 rounded-lg text-sm [&_svg]:size-5",
|
|
11
|
+
xl: "size-16 rounded-2xl text-xl [&_svg]:size-8",
|
|
12
|
+
"2xl": "size-20 rounded-2xl text-2xl [&_svg]:size-10"
|
|
11
13
|
};
|
|
12
14
|
/** Up to two initials: the first letters of the first two words, or of the one word. */
|
|
13
15
|
function initialsOf(name) {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
//#region src/primitives/pagination.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Page controls for a paged list or table: previous, the page numbers around
|
|
5
|
+
* the current one with the first and last always reachable, and next.
|
|
6
|
+
*
|
|
7
|
+
* Pages are zero-based in the API and one-based on screen. With one page or
|
|
8
|
+
* none there is nothing to choose, so it renders nothing.
|
|
9
|
+
*/
|
|
10
|
+
export interface PaginationProps extends Omit<React.HTMLAttributes<HTMLElement>, "onChange" | "children"> {
|
|
11
|
+
/** The current page, zero-based. */
|
|
12
|
+
page: number;
|
|
13
|
+
pageCount: number;
|
|
14
|
+
onPageChange: (page: number) => void;
|
|
15
|
+
/** Pages shown on each side of the current one before an ellipsis. */
|
|
16
|
+
siblings?: number;
|
|
17
|
+
}
|
|
18
|
+
export declare function Pagination({ page, pageCount, onPageChange, siblings, className, "aria-label": ariaLabel, ...rest }: PaginationProps): React.JSX.Element | null;
|
|
19
|
+
//#endregion
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { cn } from "../lib/utils.js";
|
|
2
|
+
import { buttonVariants } from "./button.js";
|
|
3
|
+
import "react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
6
|
+
//#region src/primitives/pagination.tsx
|
|
7
|
+
/**
|
|
8
|
+
* The visible slots for `page` of `pageCount`. The number of slots stays the
|
|
9
|
+
* same as the current page moves, so the controls do not shift under the
|
|
10
|
+
* pointer while a person clicks through.
|
|
11
|
+
*/
|
|
12
|
+
function pageSlots(page, pageCount, siblings = 1) {
|
|
13
|
+
const last = pageCount - 1;
|
|
14
|
+
if (pageCount <= siblings * 2 + 5) return Array.from({ length: pageCount }, (_, i) => i);
|
|
15
|
+
const current = Math.min(Math.max(page, 0), last);
|
|
16
|
+
const start = Math.max(Math.min(current - siblings, last - siblings * 2 - 2), 2);
|
|
17
|
+
const end = Math.min(Math.max(current + siblings, siblings * 2 + 2), last - 2);
|
|
18
|
+
const slots = [0, start > 2 ? "gap-start" : 1];
|
|
19
|
+
for (let i = start; i <= end; i++) slots.push(i);
|
|
20
|
+
slots.push(end < last - 2 ? "gap-end" : last - 1, last);
|
|
21
|
+
return slots;
|
|
22
|
+
}
|
|
23
|
+
const pageButton = cn(buttonVariants({
|
|
24
|
+
variant: "ghost",
|
|
25
|
+
size: "sm"
|
|
26
|
+
}), "min-w-8 px-2 tabular-nums");
|
|
27
|
+
function Pagination({ page, pageCount, onPageChange, siblings = 1, className, "aria-label": ariaLabel = "Pagination", ...rest }) {
|
|
28
|
+
if (pageCount <= 1) return null;
|
|
29
|
+
const last = pageCount - 1;
|
|
30
|
+
const current = Math.min(Math.max(page, 0), last);
|
|
31
|
+
const go = (next) => {
|
|
32
|
+
const target = Math.min(Math.max(next, 0), last);
|
|
33
|
+
if (target !== current) onPageChange(target);
|
|
34
|
+
};
|
|
35
|
+
return /* @__PURE__ */ jsx("nav", {
|
|
36
|
+
"aria-label": ariaLabel,
|
|
37
|
+
className: cn("flex items-center", className),
|
|
38
|
+
...rest,
|
|
39
|
+
children: /* @__PURE__ */ jsxs("ul", {
|
|
40
|
+
className: "flex flex-wrap items-center gap-1",
|
|
41
|
+
children: [
|
|
42
|
+
/* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("button", {
|
|
43
|
+
type: "button",
|
|
44
|
+
"aria-label": "Previous page",
|
|
45
|
+
disabled: current === 0,
|
|
46
|
+
onClick: () => go(current - 1),
|
|
47
|
+
className: cn(pageButton, "gap-1 pl-1.5"),
|
|
48
|
+
children: [/* @__PURE__ */ jsx(ChevronLeft, { "aria-hidden": "true" }), /* @__PURE__ */ jsx("span", {
|
|
49
|
+
className: "hidden sm:inline",
|
|
50
|
+
children: "Previous"
|
|
51
|
+
})]
|
|
52
|
+
}) }),
|
|
53
|
+
pageSlots(current, pageCount, siblings).map((slot) => typeof slot === "number" ? /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("button", {
|
|
54
|
+
type: "button",
|
|
55
|
+
"aria-label": `Page ${slot + 1}`,
|
|
56
|
+
"aria-current": slot === current ? "page" : void 0,
|
|
57
|
+
onClick: () => go(slot),
|
|
58
|
+
className: cn(pageButton, slot === current && "border-border bg-card font-semibold text-foreground hover:bg-card"),
|
|
59
|
+
children: slot + 1
|
|
60
|
+
}) }, slot) : /* @__PURE__ */ jsx("li", {
|
|
61
|
+
"aria-hidden": "true",
|
|
62
|
+
className: "flex min-w-8 justify-center text-sm text-muted-foreground",
|
|
63
|
+
children: "…"
|
|
64
|
+
}, slot)),
|
|
65
|
+
/* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("button", {
|
|
66
|
+
type: "button",
|
|
67
|
+
"aria-label": "Next page",
|
|
68
|
+
disabled: current === last,
|
|
69
|
+
onClick: () => go(current + 1),
|
|
70
|
+
className: cn(pageButton, "gap-1 pr-1.5"),
|
|
71
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
72
|
+
className: "hidden sm:inline",
|
|
73
|
+
children: "Next"
|
|
74
|
+
}), /* @__PURE__ */ jsx(ChevronRight, { "aria-hidden": "true" })]
|
|
75
|
+
}) })
|
|
76
|
+
]
|
|
77
|
+
})
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
export { Pagination, pageSlots };
|
|
@@ -23,6 +23,8 @@ export interface SegmentedControlOption<T extends string = string> {
|
|
|
23
23
|
label: React.ReactNode;
|
|
24
24
|
/** Rendered right of the label, typically a count or status pill. */
|
|
25
25
|
adornment?: React.ReactNode;
|
|
26
|
+
/** Rendered as the option button's `data-testid`, for end-to-end selectors. */
|
|
27
|
+
testId?: string;
|
|
26
28
|
}
|
|
27
29
|
export interface SegmentedControlProps<T extends string = string> extends Pick<React.HTMLAttributes<HTMLDivElement>, "id" | "className" | "aria-label" | "aria-labelledby"> {
|
|
28
30
|
value: T;
|
|
@@ -35,6 +37,8 @@ export interface SegmentedControlProps<T extends string = string> extends Pick<R
|
|
|
35
37
|
* reads as a classic tab (used on the Team page)
|
|
36
38
|
*/
|
|
37
39
|
variant?: "row" | "tabs";
|
|
40
|
+
/** Rendered as the group's `data-testid`. */
|
|
41
|
+
"data-testid"?: string;
|
|
38
42
|
}
|
|
39
43
|
export declare function SegmentedControl<T extends string = string>({ value, onValueChange, options, variant, className, ...rest }: SegmentedControlProps<T>): React.JSX.Element;
|
|
40
44
|
//#endregion
|
|
@@ -28,10 +28,12 @@ function SegmentedControl({ value, onValueChange, options, variant = "row", clas
|
|
|
28
28
|
id: rest.id,
|
|
29
29
|
"aria-label": rest["aria-label"],
|
|
30
30
|
"aria-labelledby": rest["aria-labelledby"],
|
|
31
|
+
"data-testid": rest["data-testid"],
|
|
31
32
|
onKeyDown: handleKeyDown,
|
|
32
33
|
className: cn("flex gap-1", variant === "row" && "flex-wrap items-center rounded-lg border border-border bg-card p-1", variant === "tabs" && "flex-nowrap items-end border-b border-border pb-0 overflow-x-auto", className),
|
|
33
34
|
children: options.map((option, i) => {
|
|
34
35
|
const active = option.value === value;
|
|
36
|
+
const focusable = active || !hasMatch && i === 0;
|
|
35
37
|
return /* @__PURE__ */ jsxs("button", {
|
|
36
38
|
ref: (el) => {
|
|
37
39
|
if (el) optionRefs.current.set(option.value, el);
|
|
@@ -40,7 +42,8 @@ function SegmentedControl({ value, onValueChange, options, variant = "row", clas
|
|
|
40
42
|
type: "button",
|
|
41
43
|
role: "radio",
|
|
42
44
|
"aria-checked": active,
|
|
43
|
-
|
|
45
|
+
"data-testid": option.testId,
|
|
46
|
+
tabIndex: focusable ? 0 : -1,
|
|
44
47
|
onClick: () => {
|
|
45
48
|
if (!active) onValueChange(option.value);
|
|
46
49
|
},
|
package/dist/primitives.d.ts
CHANGED
|
@@ -38,6 +38,8 @@ import { Heading, HeadingProps, HeadingVariant } from "./primitives/heading.js";
|
|
|
38
38
|
import { PageShell, PageShellProps } from "./primitives/page-shell.js";
|
|
39
39
|
import { PageHeader, PageHeaderProps } from "./primitives/page-header.js";
|
|
40
40
|
import { StatusPill, StatusPillProps, StatusTone } from "./primitives/status-pill.js";
|
|
41
|
+
import { Alert, AlertDescription, AlertProps, AlertTitle, AlertTone } from "./primitives/alert.js";
|
|
42
|
+
import { Pagination, PaginationProps } from "./primitives/pagination.js";
|
|
41
43
|
import { CATEGORY_TONES, CategoryTone, TONE_CLASSES, Tone, ToneClasses, toneFor } from "./primitives/tone.js";
|
|
42
44
|
import { Tag, TagProps } from "./primitives/tag.js";
|
|
43
45
|
import { Chip, ChipProps } from "./primitives/chip.js";
|
|
@@ -45,4 +47,4 @@ import { IconTile, IconTileProps, initialsOf } from "./primitives/icon-tile.js";
|
|
|
45
47
|
import { Metric, MetricProps, MetricStrip, MetricStripProps } from "./primitives/metric-strip.js";
|
|
46
48
|
import { FilterField, FilterFieldProps, Toolbar, ToolbarProps } from "./primitives/toolbar.js";
|
|
47
49
|
import { Logo, LogoProps, TangleKnot } from "@tangle-network/brand";
|
|
48
|
-
export { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, 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, 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, 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 };
|
|
50
|
+
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, 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 };
|
package/dist/primitives.js
CHANGED
|
@@ -37,6 +37,8 @@ import { InlineCode } from "./primitives/code-block.js";
|
|
|
37
37
|
import { ThemeToggle, useTheme } from "./primitives/theme-toggle.js";
|
|
38
38
|
import { PageShell } from "./primitives/page-shell.js";
|
|
39
39
|
import { PageHeader } from "./primitives/page-header.js";
|
|
40
|
+
import { Alert, AlertDescription, AlertTitle } from "./primitives/alert.js";
|
|
41
|
+
import { Pagination } from "./primitives/pagination.js";
|
|
40
42
|
import { StatusPill } from "./primitives/status-pill.js";
|
|
41
43
|
import { CATEGORY_TONES, TONE_CLASSES, toneFor } from "./primitives/tone.js";
|
|
42
44
|
import { Tag } from "./primitives/tag.js";
|
|
@@ -45,4 +47,4 @@ import { IconTile, initialsOf } from "./primitives/icon-tile.js";
|
|
|
45
47
|
import { Metric, MetricStrip } from "./primitives/metric-strip.js";
|
|
46
48
|
import { FilterField, Toolbar } from "./primitives/toolbar.js";
|
|
47
49
|
import { Logo, TangleKnot } from "@tangle-network/brand";
|
|
48
|
-
export { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, 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, IconTile, InlineCode, Input, Label, Logo, Metric, MetricStrip, PageHeader, PageShell, 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 };
|
|
50
|
+
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, 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 };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/ui",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.21.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",
|
|
@@ -149,7 +149,7 @@
|
|
|
149
149
|
"peerDependencies": {
|
|
150
150
|
"@hocuspocus/provider": ">=2.15.0 <5.0.0",
|
|
151
151
|
"@nanostores/react": ">=0.8.4 <3.0.0",
|
|
152
|
-
"@tangle-network/brand": "^1.
|
|
152
|
+
"@tangle-network/brand": "^1.15.2",
|
|
153
153
|
"@tiptap/core": ">=3.20.4 <4.0.0",
|
|
154
154
|
"@tiptap/extension-collaboration": ">=3.20.4 <4.0.0",
|
|
155
155
|
"@tiptap/extension-collaboration-caret": ">=3.7.0 <4.0.0",
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "../lib/utils";
|
|
3
|
+
import type { StatusTone } from "./status-pill";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* An inline message that stays on the page: a policy note above a form, a
|
|
7
|
+
* failed save beside the field it concerns. For a message that interrupts, use
|
|
8
|
+
* `AlertDialog`; for one that goes away by itself, use a toast.
|
|
9
|
+
*
|
|
10
|
+
* Each tone paints its fill, border and text from Brand's matched status
|
|
11
|
+
* triple, the same one `StatusPill` uses, so the text is solved against its
|
|
12
|
+
* own background on every plane. `neutral` is a card with body text.
|
|
13
|
+
*
|
|
14
|
+
* `danger` and `warning` announce themselves (`role="alert"`); the quieter
|
|
15
|
+
* tones are `role="status"` so a screen reader is not interrupted by a note.
|
|
16
|
+
*/
|
|
17
|
+
export type AlertTone = Exclude<StatusTone, "running">;
|
|
18
|
+
|
|
19
|
+
export interface AlertProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
20
|
+
tone?: AlertTone;
|
|
21
|
+
/** A leading icon. It takes the tone's colour and sits beside the title. */
|
|
22
|
+
icon?: React.ReactNode;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const TONE: Record<AlertTone, string> = {
|
|
26
|
+
neutral: "border-border bg-card text-card-foreground",
|
|
27
|
+
info: "border-[var(--surface-info-border)] bg-[var(--surface-info-bg)] text-[var(--surface-info-text)]",
|
|
28
|
+
success:
|
|
29
|
+
"border-[var(--surface-success-border)] bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]",
|
|
30
|
+
warning:
|
|
31
|
+
"border-[var(--surface-warning-border)] bg-[var(--surface-warning-bg)] text-[var(--surface-warning-text)]",
|
|
32
|
+
danger:
|
|
33
|
+
"border-[var(--surface-danger-border)] bg-[var(--surface-danger-bg)] text-[var(--surface-danger-text)]",
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
|
|
37
|
+
({ tone = "neutral", icon, className, children, role, ...props }, ref) => (
|
|
38
|
+
<div
|
|
39
|
+
ref={ref}
|
|
40
|
+
role={role ?? (tone === "danger" || tone === "warning" ? "alert" : "status")}
|
|
41
|
+
data-tone={tone}
|
|
42
|
+
className={cn(
|
|
43
|
+
"flex w-full min-w-0 gap-3 rounded-lg border px-4 py-3 text-sm",
|
|
44
|
+
TONE[tone],
|
|
45
|
+
className,
|
|
46
|
+
)}
|
|
47
|
+
{...props}
|
|
48
|
+
>
|
|
49
|
+
{icon ? (
|
|
50
|
+
<span aria-hidden="true" className="mt-0.5 flex shrink-0 [&_svg]:size-4">
|
|
51
|
+
{icon}
|
|
52
|
+
</span>
|
|
53
|
+
) : null}
|
|
54
|
+
<div className="grid min-w-0 flex-1 gap-1">{children}</div>
|
|
55
|
+
</div>
|
|
56
|
+
),
|
|
57
|
+
);
|
|
58
|
+
Alert.displayName = "Alert";
|
|
59
|
+
|
|
60
|
+
export function AlertTitle({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) {
|
|
61
|
+
return <p className={cn("font-medium leading-snug [overflow-wrap:anywhere]", className)} {...props} />;
|
|
62
|
+
}
|
|
63
|
+
AlertTitle.displayName = "AlertTitle";
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Body text. Under a toned alert it keeps the tone's ink, which is the colour
|
|
67
|
+
* tested against the tone's fill; under `neutral` it is the muted tier.
|
|
68
|
+
*/
|
|
69
|
+
export function AlertDescription({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
70
|
+
return (
|
|
71
|
+
<div
|
|
72
|
+
className={cn(
|
|
73
|
+
"leading-relaxed [overflow-wrap:anywhere] [[data-tone=neutral]_&]:text-muted-foreground",
|
|
74
|
+
className,
|
|
75
|
+
)}
|
|
76
|
+
{...props}
|
|
77
|
+
/>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
AlertDescription.displayName = "AlertDescription";
|
|
@@ -62,10 +62,10 @@ export const Sizes: Story = {
|
|
|
62
62
|
render: () => (
|
|
63
63
|
<BothThemes>
|
|
64
64
|
<div className="flex items-end gap-3">
|
|
65
|
-
{(["xs", "sm", "md", "lg"] as const).map((size) => (
|
|
65
|
+
{(["xs", "sm", "md", "lg", "xl", "2xl"] as const).map((size) => (
|
|
66
66
|
<IconTile key={size} size={size} name="Tangle Network" />
|
|
67
67
|
))}
|
|
68
|
-
{(["xs", "sm", "md", "lg"] as const).map((size) => (
|
|
68
|
+
{(["xs", "sm", "md", "lg", "xl", "2xl"] as const).map((size) => (
|
|
69
69
|
<IconTile key={`g-${size}`} size={size} tone="cyan" icon={<Globe />} />
|
|
70
70
|
))}
|
|
71
71
|
</div>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { render } from "@testing-library/react";
|
|
2
|
+
import { Globe } from "lucide-react";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import { IconTile, initialsOf } from "./icon-tile";
|
|
5
|
+
|
|
6
|
+
describe("IconTile", () => {
|
|
7
|
+
it.each([
|
|
8
|
+
["xs", "size-5"],
|
|
9
|
+
["sm", "size-6"],
|
|
10
|
+
["md", "size-8"],
|
|
11
|
+
["lg", "size-10"],
|
|
12
|
+
["xl", "size-16"],
|
|
13
|
+
["2xl", "size-20"],
|
|
14
|
+
] as const)("renders size %s as %s", (size, cls) => {
|
|
15
|
+
const { container } = render(<IconTile size={size} name="Tangle Network" />);
|
|
16
|
+
expect(container.firstElementChild).toHaveClass(cls);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("scales the glyph with the large sizes", () => {
|
|
20
|
+
const { container } = render(<IconTile size="2xl" tone="cyan" icon={<Globe />} />);
|
|
21
|
+
expect(container.firstElementChild?.className).toContain("[&_svg]:size-10");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("shows initials and is decorative without a label", () => {
|
|
25
|
+
const { container } = render(<IconTile size="xl" name="Acme Robotics" />);
|
|
26
|
+
const tile = container.firstElementChild;
|
|
27
|
+
expect(tile).toHaveTextContent("AR");
|
|
28
|
+
expect(tile).toHaveAttribute("aria-hidden", "true");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("is an image with an accessible name when labelled", () => {
|
|
32
|
+
const { getByRole } = render(<IconTile size="xl" name="Acme" label="Acme" />);
|
|
33
|
+
expect(getByRole("img", { name: "Acme" })).toBeInTheDocument();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("keeps an emoji whole in the initials", () => {
|
|
37
|
+
expect(initialsOf("🦊")).toBe("🦊");
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -18,7 +18,12 @@ import { type CategoryTone, TONE_CLASSES, toneFor } from "./tone";
|
|
|
18
18
|
export interface IconTileProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, "children"> {
|
|
19
19
|
/** Defaults to a tone derived from `name`, or violet when there is no name. */
|
|
20
20
|
tone?: CategoryTone;
|
|
21
|
-
|
|
21
|
+
/**
|
|
22
|
+
* `xs` to `lg` (20 to 40px) sit in rows and cards. `xl` (64px) and `2xl`
|
|
23
|
+
* (80px) are for a page or profile header, where the tile is the thing's
|
|
24
|
+
* face rather than a list marker.
|
|
25
|
+
*/
|
|
26
|
+
size?: "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
|
|
22
27
|
src?: string;
|
|
23
28
|
icon?: React.ReactNode;
|
|
24
29
|
/** The thing's name. Supplies the initials and the derived tone. */
|
|
@@ -32,6 +37,8 @@ const SIZE = {
|
|
|
32
37
|
sm: "size-6 rounded-md text-[11px] [&_svg]:size-3.5",
|
|
33
38
|
md: "size-8 rounded-md text-xs [&_svg]:size-4",
|
|
34
39
|
lg: "size-10 rounded-lg text-sm [&_svg]:size-5",
|
|
40
|
+
xl: "size-16 rounded-2xl text-xl [&_svg]:size-8",
|
|
41
|
+
"2xl": "size-20 rounded-2xl text-2xl [&_svg]:size-10",
|
|
35
42
|
} as const;
|
|
36
43
|
|
|
37
44
|
/** Up to two initials: the first letters of the first two words, or of the one word. */
|
package/src/primitives/index.ts
CHANGED
|
@@ -190,6 +190,12 @@ export type { PageShellProps } from "./page-shell";
|
|
|
190
190
|
export { PageHeader } from "./page-header";
|
|
191
191
|
export type { PageHeaderProps } from "./page-header";
|
|
192
192
|
|
|
193
|
+
export { Alert, AlertDescription, AlertTitle } from "./alert";
|
|
194
|
+
export type { AlertProps, AlertTone } from "./alert";
|
|
195
|
+
|
|
196
|
+
export { Pagination } from "./pagination";
|
|
197
|
+
export type { PaginationProps } from "./pagination";
|
|
198
|
+
|
|
193
199
|
export { StatusPill } from "./status-pill";
|
|
194
200
|
export type { StatusPillProps, StatusTone } from "./status-pill";
|
|
195
201
|
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { Info, TriangleAlert } from 'lucide-react'
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import { Alert, AlertDescription, AlertTitle } from './alert'
|
|
5
|
+
import { Pagination } from './pagination'
|
|
6
|
+
|
|
7
|
+
const meta: Meta = {
|
|
8
|
+
title: 'Primitives/Pagination and Alert',
|
|
9
|
+
}
|
|
10
|
+
export default meta
|
|
11
|
+
|
|
12
|
+
function PagedTable({ pageCount }: { pageCount: number }) {
|
|
13
|
+
const [page, setPage] = React.useState(0)
|
|
14
|
+
return <Pagination page={page} pageCount={pageCount} onPageChange={setPage} />
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const PaginationLengths: StoryObj = {
|
|
18
|
+
render: () => (
|
|
19
|
+
<div className="flex flex-col gap-6 bg-background p-6 text-foreground">
|
|
20
|
+
<PagedTable pageCount={1} />
|
|
21
|
+
<PagedTable pageCount={4} />
|
|
22
|
+
<PagedTable pageCount={48} />
|
|
23
|
+
<PagedTable pageCount={1200} />
|
|
24
|
+
</div>
|
|
25
|
+
),
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const AlertTones: StoryObj = {
|
|
29
|
+
render: () => (
|
|
30
|
+
<div className="flex max-w-xl flex-col gap-3 bg-background p-6 text-foreground">
|
|
31
|
+
<Alert icon={<Info />}>
|
|
32
|
+
<AlertTitle>Public events</AlertTitle>
|
|
33
|
+
<AlertDescription>Anyone with the link can join a public event.</AlertDescription>
|
|
34
|
+
</Alert>
|
|
35
|
+
<Alert tone="info" icon={<Info />}>
|
|
36
|
+
<AlertTitle>Applies from the next billing cycle</AlertTitle>
|
|
37
|
+
</Alert>
|
|
38
|
+
<Alert tone="success">
|
|
39
|
+
<AlertDescription>Changes saved.</AlertDescription>
|
|
40
|
+
</Alert>
|
|
41
|
+
<Alert tone="warning" icon={<TriangleAlert />}>
|
|
42
|
+
<AlertTitle>A_very_long_policy_identifier_without_any_spaces_that_has_to_wrap_inside_the_alert</AlertTitle>
|
|
43
|
+
<AlertDescription>Seats above the limit are removed when the cycle closes.</AlertDescription>
|
|
44
|
+
</Alert>
|
|
45
|
+
<Alert tone="danger" icon={<TriangleAlert />}>
|
|
46
|
+
<AlertTitle>Could not save the policy</AlertTitle>
|
|
47
|
+
<AlertDescription>The server refused the change. Try again.</AlertDescription>
|
|
48
|
+
</Alert>
|
|
49
|
+
</div>
|
|
50
|
+
),
|
|
51
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { render, screen } from "@testing-library/react";
|
|
2
|
+
import userEvent from "@testing-library/user-event";
|
|
3
|
+
import { describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { Alert, AlertDescription, AlertTitle } from "./alert";
|
|
5
|
+
import { Pagination, pageSlots } from "./pagination";
|
|
6
|
+
|
|
7
|
+
const PALETTE = /\b(?:bg|text|border)-(?:slate|zinc|gray|neutral|blue|green|red|amber|emerald|black|white)(?:-\d|\b)/;
|
|
8
|
+
|
|
9
|
+
describe("pageSlots", () => {
|
|
10
|
+
it("lists every page when they fit", () => {
|
|
11
|
+
expect(pageSlots(0, 1)).toEqual([0]);
|
|
12
|
+
expect(pageSlots(3, 7)).toEqual([0, 1, 2, 3, 4, 5, 6]);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("keeps the same number of slots as the current page moves", () => {
|
|
16
|
+
expect(pageSlots(0, 10)).toEqual([0, 1, 2, 3, 4, "gap-end", 9]);
|
|
17
|
+
expect(pageSlots(5, 10)).toEqual([0, "gap-start", 4, 5, 6, "gap-end", 9]);
|
|
18
|
+
expect(pageSlots(9, 10)).toEqual([0, "gap-start", 5, 6, 7, 8, 9]);
|
|
19
|
+
for (let page = 0; page < 40; page++) expect(pageSlots(page, 40)).toHaveLength(7);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("clamps a page outside the range", () => {
|
|
23
|
+
expect(pageSlots(-3, 10)).toEqual(pageSlots(0, 10));
|
|
24
|
+
expect(pageSlots(99, 10)).toEqual(pageSlots(9, 10));
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe("Pagination", () => {
|
|
29
|
+
it("renders nothing for a single page", () => {
|
|
30
|
+
const { container } = render(<Pagination page={0} pageCount={1} onPageChange={vi.fn()} />);
|
|
31
|
+
expect(container).toBeEmptyDOMElement();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("marks the current page and moves with previous, next and a number", async () => {
|
|
35
|
+
const user = userEvent.setup();
|
|
36
|
+
const onPageChange = vi.fn();
|
|
37
|
+
render(<Pagination page={4} pageCount={12} onPageChange={onPageChange} />);
|
|
38
|
+
|
|
39
|
+
expect(screen.getByRole("navigation", { name: "Pagination" })).toBeInTheDocument();
|
|
40
|
+
const current = screen.getByRole("button", { name: "Page 5" });
|
|
41
|
+
expect(current).toHaveAttribute("aria-current", "page");
|
|
42
|
+
expect(current.className).not.toMatch(PALETTE);
|
|
43
|
+
|
|
44
|
+
await user.click(screen.getByRole("button", { name: "Previous page" }));
|
|
45
|
+
await user.click(screen.getByRole("button", { name: "Next page" }));
|
|
46
|
+
await user.click(screen.getByRole("button", { name: "Page 12" }));
|
|
47
|
+
expect(onPageChange.mock.calls).toEqual([[3], [5], [11]]);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("disables previous on the first page and next on the last", () => {
|
|
51
|
+
const { rerender } = render(<Pagination page={0} pageCount={3} onPageChange={vi.fn()} />);
|
|
52
|
+
expect(screen.getByRole("button", { name: "Previous page" })).toBeDisabled();
|
|
53
|
+
rerender(<Pagination page={2} pageCount={3} onPageChange={vi.fn()} />);
|
|
54
|
+
expect(screen.getByRole("button", { name: "Next page" })).toBeDisabled();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("does not report the page that is already current", async () => {
|
|
58
|
+
const user = userEvent.setup();
|
|
59
|
+
const onPageChange = vi.fn();
|
|
60
|
+
render(<Pagination page={1} pageCount={3} onPageChange={onPageChange} />);
|
|
61
|
+
await user.click(screen.getByRole("button", { name: "Page 2" }));
|
|
62
|
+
expect(onPageChange).not.toHaveBeenCalled();
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe("Alert", () => {
|
|
67
|
+
it("paints a tone from the matched Brand status triple", () => {
|
|
68
|
+
render(
|
|
69
|
+
<Alert tone="warning">
|
|
70
|
+
<AlertTitle>Policy is changing</AlertTitle>
|
|
71
|
+
<AlertDescription>Seats above the limit are removed next cycle.</AlertDescription>
|
|
72
|
+
</Alert>,
|
|
73
|
+
);
|
|
74
|
+
const alert = screen.getByRole("alert");
|
|
75
|
+
expect(alert.className).toContain("bg-[var(--surface-warning-bg)]");
|
|
76
|
+
expect(alert.className).toContain("text-[var(--surface-warning-text)]");
|
|
77
|
+
expect(alert.className).not.toMatch(PALETTE);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("uses role status for quiet tones and keeps an explicit role", () => {
|
|
81
|
+
const { rerender } = render(<Alert tone="info">Saved</Alert>);
|
|
82
|
+
expect(screen.getByRole("status")).toHaveTextContent("Saved");
|
|
83
|
+
rerender(
|
|
84
|
+
<Alert tone="neutral" role="note">
|
|
85
|
+
Note
|
|
86
|
+
</Alert>,
|
|
87
|
+
);
|
|
88
|
+
expect(screen.getByRole("note")).toHaveAttribute("data-tone", "neutral");
|
|
89
|
+
});
|
|
90
|
+
});
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { cn } from "../lib/utils";
|
|
4
|
+
import { buttonVariants } from "./button";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Page controls for a paged list or table: previous, the page numbers around
|
|
8
|
+
* the current one with the first and last always reachable, and next.
|
|
9
|
+
*
|
|
10
|
+
* Pages are zero-based in the API and one-based on screen. With one page or
|
|
11
|
+
* none there is nothing to choose, so it renders nothing.
|
|
12
|
+
*/
|
|
13
|
+
export interface PaginationProps
|
|
14
|
+
extends Omit<React.HTMLAttributes<HTMLElement>, "onChange" | "children"> {
|
|
15
|
+
/** The current page, zero-based. */
|
|
16
|
+
page: number;
|
|
17
|
+
pageCount: number;
|
|
18
|
+
onPageChange: (page: number) => void;
|
|
19
|
+
/** Pages shown on each side of the current one before an ellipsis. */
|
|
20
|
+
siblings?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type PageSlot = number | "gap-start" | "gap-end";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The visible slots for `page` of `pageCount`. The number of slots stays the
|
|
27
|
+
* same as the current page moves, so the controls do not shift under the
|
|
28
|
+
* pointer while a person clicks through.
|
|
29
|
+
*/
|
|
30
|
+
export function pageSlots(page: number, pageCount: number, siblings = 1): PageSlot[] {
|
|
31
|
+
const last = pageCount - 1;
|
|
32
|
+
if (pageCount <= siblings * 2 + 5) return Array.from({ length: pageCount }, (_, i) => i);
|
|
33
|
+
|
|
34
|
+
const current = Math.min(Math.max(page, 0), last);
|
|
35
|
+
const start = Math.max(Math.min(current - siblings, last - siblings * 2 - 2), 2);
|
|
36
|
+
const end = Math.min(Math.max(current + siblings, siblings * 2 + 2), last - 2);
|
|
37
|
+
|
|
38
|
+
const slots: PageSlot[] = [0, start > 2 ? "gap-start" : 1];
|
|
39
|
+
for (let i = start; i <= end; i++) slots.push(i);
|
|
40
|
+
slots.push(end < last - 2 ? "gap-end" : last - 1, last);
|
|
41
|
+
return slots;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const pageButton = cn(buttonVariants({ variant: "ghost", size: "sm" }), "min-w-8 px-2 tabular-nums");
|
|
45
|
+
|
|
46
|
+
export function Pagination({
|
|
47
|
+
page,
|
|
48
|
+
pageCount,
|
|
49
|
+
onPageChange,
|
|
50
|
+
siblings = 1,
|
|
51
|
+
className,
|
|
52
|
+
"aria-label": ariaLabel = "Pagination",
|
|
53
|
+
...rest
|
|
54
|
+
}: PaginationProps) {
|
|
55
|
+
if (pageCount <= 1) return null;
|
|
56
|
+
|
|
57
|
+
const last = pageCount - 1;
|
|
58
|
+
const current = Math.min(Math.max(page, 0), last);
|
|
59
|
+
const go = (next: number) => {
|
|
60
|
+
const target = Math.min(Math.max(next, 0), last);
|
|
61
|
+
if (target !== current) onPageChange(target);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<nav aria-label={ariaLabel} className={cn("flex items-center", className)} {...rest}>
|
|
66
|
+
<ul className="flex flex-wrap items-center gap-1">
|
|
67
|
+
<li>
|
|
68
|
+
<button
|
|
69
|
+
type="button"
|
|
70
|
+
aria-label="Previous page"
|
|
71
|
+
disabled={current === 0}
|
|
72
|
+
onClick={() => go(current - 1)}
|
|
73
|
+
className={cn(pageButton, "gap-1 pl-1.5")}
|
|
74
|
+
>
|
|
75
|
+
<ChevronLeft aria-hidden="true" />
|
|
76
|
+
<span className="hidden sm:inline">Previous</span>
|
|
77
|
+
</button>
|
|
78
|
+
</li>
|
|
79
|
+
{pageSlots(current, pageCount, siblings).map((slot) =>
|
|
80
|
+
typeof slot === "number" ? (
|
|
81
|
+
<li key={slot}>
|
|
82
|
+
<button
|
|
83
|
+
type="button"
|
|
84
|
+
aria-label={`Page ${slot + 1}`}
|
|
85
|
+
aria-current={slot === current ? "page" : undefined}
|
|
86
|
+
onClick={() => go(slot)}
|
|
87
|
+
className={cn(
|
|
88
|
+
pageButton,
|
|
89
|
+
slot === current &&
|
|
90
|
+
"border-border bg-card font-semibold text-foreground hover:bg-card",
|
|
91
|
+
)}
|
|
92
|
+
>
|
|
93
|
+
{slot + 1}
|
|
94
|
+
</button>
|
|
95
|
+
</li>
|
|
96
|
+
) : (
|
|
97
|
+
<li key={slot} aria-hidden="true" className="flex min-w-8 justify-center text-sm text-muted-foreground">
|
|
98
|
+
…
|
|
99
|
+
</li>
|
|
100
|
+
),
|
|
101
|
+
)}
|
|
102
|
+
<li>
|
|
103
|
+
<button
|
|
104
|
+
type="button"
|
|
105
|
+
aria-label="Next page"
|
|
106
|
+
disabled={current === last}
|
|
107
|
+
onClick={() => go(current + 1)}
|
|
108
|
+
className={cn(pageButton, "gap-1 pr-1.5")}
|
|
109
|
+
>
|
|
110
|
+
<span className="hidden sm:inline">Next</span>
|
|
111
|
+
<ChevronRight aria-hidden="true" />
|
|
112
|
+
</button>
|
|
113
|
+
</li>
|
|
114
|
+
</ul>
|
|
115
|
+
</nav>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
@@ -326,3 +326,23 @@ describe("SegmentedControl", () => {
|
|
|
326
326
|
expect(screen.getByRole("radiogroup")).toHaveAttribute("id", "scope-control")
|
|
327
327
|
})
|
|
328
328
|
})
|
|
329
|
+
|
|
330
|
+
describe("SegmentedControl test ids", () => {
|
|
331
|
+
it("renders option and group test ids for end-to-end selectors", () => {
|
|
332
|
+
render(
|
|
333
|
+
<SegmentedControl
|
|
334
|
+
aria-label="Workbench view"
|
|
335
|
+
data-testid="workbench-tabs"
|
|
336
|
+
value="code"
|
|
337
|
+
onValueChange={vi.fn()}
|
|
338
|
+
options={[
|
|
339
|
+
{ value: "code", label: "Code", testId: "workbench-tab-code" },
|
|
340
|
+
{ value: "preview", label: "Preview", testId: "workbench-tab-preview" },
|
|
341
|
+
]}
|
|
342
|
+
/>,
|
|
343
|
+
)
|
|
344
|
+
expect(screen.getByTestId("workbench-tabs")).toHaveAttribute("role", "radiogroup")
|
|
345
|
+
expect(screen.getByTestId("workbench-tab-preview")).toHaveAttribute("aria-checked", "false")
|
|
346
|
+
expect(screen.getByTestId("workbench-tab-code")).toHaveAttribute("aria-checked", "true")
|
|
347
|
+
})
|
|
348
|
+
})
|
|
@@ -29,6 +29,8 @@ export interface SegmentedControlOption<T extends string = string> {
|
|
|
29
29
|
label: React.ReactNode
|
|
30
30
|
/** Rendered right of the label, typically a count or status pill. */
|
|
31
31
|
adornment?: React.ReactNode
|
|
32
|
+
/** Rendered as the option button's `data-testid`, for end-to-end selectors. */
|
|
33
|
+
testId?: string
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
export interface SegmentedControlProps<T extends string = string>
|
|
@@ -43,6 +45,8 @@ export interface SegmentedControlProps<T extends string = string>
|
|
|
43
45
|
* reads as a classic tab (used on the Team page)
|
|
44
46
|
*/
|
|
45
47
|
variant?: "row" | "tabs"
|
|
48
|
+
/** Rendered as the group's `data-testid`. */
|
|
49
|
+
"data-testid"?: string
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
export function SegmentedControl<T extends string = string>({
|
|
@@ -93,6 +97,7 @@ export function SegmentedControl<T extends string = string>({
|
|
|
93
97
|
id={rest.id}
|
|
94
98
|
aria-label={rest["aria-label"]}
|
|
95
99
|
aria-labelledby={rest["aria-labelledby"]}
|
|
100
|
+
data-testid={rest["data-testid"]}
|
|
96
101
|
onKeyDown={handleKeyDown}
|
|
97
102
|
className={cn(
|
|
98
103
|
"flex gap-1",
|
|
@@ -116,6 +121,7 @@ export function SegmentedControl<T extends string = string>({
|
|
|
116
121
|
type="button"
|
|
117
122
|
role="radio"
|
|
118
123
|
aria-checked={active}
|
|
124
|
+
data-testid={option.testId}
|
|
119
125
|
tabIndex={focusable ? 0 : -1}
|
|
120
126
|
onClick={() => {
|
|
121
127
|
if (!active) onValueChange(option.value)
|