@tangle-network/ui 11.37.0 → 11.38.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/lib/surface-motion.js +11 -2
- package/dist/primitives/wizard.d.ts +30 -0
- package/dist/primitives/wizard.js +158 -0
- package/dist/primitives.d.ts +2 -1
- package/dist/primitives.js +2 -1
- package/package.json +1 -1
- package/src/lib/surface-motion.tsx +15 -3
- package/src/primitives/index.ts +3 -0
- package/src/primitives/wizard.stories.tsx +51 -0
- package/src/primitives/wizard.test.tsx +106 -0
- package/src/primitives/wizard.tsx +212 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @tangle-network/ui
|
|
2
2
|
|
|
3
|
+
## 11.38.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- c049dc4: Bound shared surface dismissal by its scoped role deadline so missing CSS animation events cannot strand Radix cleanup or focus return. Preserve retained motion values on reopen.
|
|
8
|
+
|
|
9
|
+
## 11.38.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- f115dda: Wizard: the shared multi-step flow primitive. Compound composition (`<Wizard.Step>`), uncontrolled by default with a controlled escape hatch, per-step primary actions with async gating, mobile-first progress, themed by the existing semantic tokens. The end of bespoke stepper markup across products.
|
|
14
|
+
|
|
3
15
|
## 11.37.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -96,6 +96,7 @@ import { Pagination, PaginationProps } from "./primitives/pagination.js";
|
|
|
96
96
|
import { StatusPill, StatusPillProps, StatusTone } from "./primitives/status-pill.js";
|
|
97
97
|
import { Tag, TagProps } from "./primitives/tag.js";
|
|
98
98
|
import { Chip, ChipProps } from "./primitives/chip.js";
|
|
99
|
+
import { Wizard, WizardProps, WizardStep, WizardStepProps } from "./primitives/wizard.js";
|
|
99
100
|
import { Metric, MetricProps, MetricStrip, MetricStripProps } from "./primitives/metric-strip.js";
|
|
100
101
|
import { FilterField, FilterFieldProps, Toolbar, ToolbarProps } from "./primitives/toolbar.js";
|
|
101
102
|
import { Logo, LogoProps, TangleKnot } from "./primitives.js";
|
|
@@ -134,4 +135,4 @@ import { QuestionPreview, QuestionPreviewProps } from "./tool-previews/question-
|
|
|
134
135
|
import { DiffPreview, DiffPreviewProps } from "./tool-previews/diff-preview.js";
|
|
135
136
|
import { RedactedDocSegment, RedactedDocument, RedactedDocumentData, RedactedDocumentProps, RevealResult } from "./redaction/redacted-document.js";
|
|
136
137
|
import "./redaction.js";
|
|
137
|
-
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, ApiSchema, type ApiSchemaProps, AppendUserMessageOptions, ApplySdkEventOptions, ApprovalCard, type ApprovalCardProps, type ApprovalDiffFile, ApprovalDiffSummary, type ApprovalPlanStep, 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, Calendar, type CalendarDate, type CalendarProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type CardTitleProps, type CategoryTone, ChatAuthor, 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, type CopyButtonProps, CustomToolRenderer, DatePicker, type DatePickerProps, 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, FileCard, type FileCardProps, type FileDetails, type FileFormat, FileList, type FileListItem, type FileListProps, type FileNode, FilePreview, type FilePreviewKind, type FilePreviewProps, type FileTabData, FileTabs, type FileTabsProps, FileTree, type FileTreeProps, type FileTreeVisibilityOptions, type FileUploader, FilterField, type FilterFieldProps, FinalTextPart, GitHubLoginButton, type GitHubLoginButtonProps, GlobResultsPreview, type GlobResultsPreviewProps, GrepResultsPreview, type GrepResultsPreviewProps, GroupedMessage, Heading, type HeadingProps, type HeadingVariant, HelpText, type HelpTextProps, 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 MarkdownLinkChip, type MarkdownLinkChipResolver, type MarkdownProps, MessageAuthor, type MessageAuthorProps, 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, PdfViewer, type PdfViewerConfig, type PdfViewerProps, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, QuestionPreview, type QuestionPreviewProps, RadioGroup, RadioGroupItem, 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, type RunPhase, RunPhaseList, 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, TRANSCRIPT_TEXT, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, type TableProps, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, type TagProps, TangleKnot, TaskList, type TaskListItem, type TaskListProps, type TaskStatus, TaskStreamEvent, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TerminalStreamEvent, TextPart, TextShimmer, type TextShimmerProps, Textarea, type TextareaProps, ThemeToggle, ThinkingIndicator, type ThinkingIndicatorProps, TimeSelect, type TimeSelectProps, 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, codeBlockGrammar, configurePdfViewer, copyText, createAuthFetcher, detectFileFormat, fenceInfo, fileExtension, filterFileTree, findUnsupportedOpenUINode, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatCalendarDate, formatDuration, formatUptime, getActiveSession, getAllActiveSessions, getAllProjectActivity, getCodeLanguage, getFormatLabel, getSanitizedMarkdownHeadingIdFromRawFragment, getSessionsByActivity, getSessionsForNavbar, getSessionsForProject, getSyntaxLanguage, getToolCategory, getToolDisplayMetadata, getToolErrorText, getTotalRunningSessionCount, hasBackgroundRunningSessions, initialsOf, isStreamingAtom, isViewerMessage, ixControl, ixControlAfter, ixControlSoft, ixField, ixRow, ixSelected, ixTabUnderline, localToday, messagesAtom, numericColumns, parseCalendarDate, parseCsv, parseToolEvent, partMapAtom, plainTextFromMarkdown, registerActiveSession, resetActiveSessions, resolveFilePreviewKind, setActiveSessionAttention, setActiveSessionConnection, setActiveSessionError, setActiveSessionRunning, setForegroundActiveSession, showsTaskPriority, tableCsv, taskProgress, taskStatus, 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 };
|
|
138
|
+
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, ApiSchema, type ApiSchemaProps, AppendUserMessageOptions, ApplySdkEventOptions, ApprovalCard, type ApprovalCardProps, type ApprovalDiffFile, ApprovalDiffSummary, type ApprovalPlanStep, 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, Calendar, type CalendarDate, type CalendarProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, type CardTitleProps, type CategoryTone, ChatAuthor, 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, type CopyButtonProps, CustomToolRenderer, DatePicker, type DatePickerProps, 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, FileCard, type FileCardProps, type FileDetails, type FileFormat, FileList, type FileListItem, type FileListProps, type FileNode, FilePreview, type FilePreviewKind, type FilePreviewProps, type FileTabData, FileTabs, type FileTabsProps, FileTree, type FileTreeProps, type FileTreeVisibilityOptions, type FileUploader, FilterField, type FilterFieldProps, FinalTextPart, GitHubLoginButton, type GitHubLoginButtonProps, GlobResultsPreview, type GlobResultsPreviewProps, GrepResultsPreview, type GrepResultsPreviewProps, GroupedMessage, Heading, type HeadingProps, type HeadingVariant, HelpText, type HelpTextProps, 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 MarkdownLinkChip, type MarkdownLinkChipResolver, type MarkdownProps, MessageAuthor, type MessageAuthorProps, 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, PdfViewer, type PdfViewerConfig, type PdfViewerProps, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, QuestionPreview, type QuestionPreviewProps, RadioGroup, RadioGroupItem, 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, type RunPhase, RunPhaseList, 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, TRANSCRIPT_TEXT, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, type TableProps, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, type TagProps, TangleKnot, TaskList, type TaskListItem, type TaskListProps, type TaskStatus, TaskStreamEvent, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TerminalStreamEvent, TextPart, TextShimmer, type TextShimmerProps, Textarea, type TextareaProps, ThemeToggle, ThinkingIndicator, type ThinkingIndicatorProps, TimeSelect, type TimeSelectProps, 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, Wizard, type WizardProps, WizardStep, type WizardStepProps, WriteFilePreview, type WriteFilePreviewProps, activeSessionsAtom, addMessage, addParts, badgeVariants, bumpActiveSessionActivity, buttonVariants, clearChat, cn, codeBlockGrammar, configurePdfViewer, copyText, createAuthFetcher, detectFileFormat, fenceInfo, fileExtension, filterFileTree, findUnsupportedOpenUINode, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatCalendarDate, formatDuration, formatUptime, getActiveSession, getAllActiveSessions, getAllProjectActivity, getCodeLanguage, getFormatLabel, getSanitizedMarkdownHeadingIdFromRawFragment, getSessionsByActivity, getSessionsForNavbar, getSessionsForProject, getSyntaxLanguage, getToolCategory, getToolDisplayMetadata, getToolErrorText, getTotalRunningSessionCount, hasBackgroundRunningSessions, initialsOf, isStreamingAtom, isViewerMessage, ixControl, ixControlAfter, ixControlSoft, ixField, ixRow, ixSelected, ixTabUnderline, localToday, messagesAtom, numericColumns, parseCalendarDate, parseCsv, parseToolEvent, partMapAtom, plainTextFromMarkdown, registerActiveSession, resetActiveSessions, resolveFilePreviewKind, setActiveSessionAttention, setActiveSessionConnection, setActiveSessionError, setActiveSessionRunning, setForegroundActiveSession, showsTaskPriority, tableCsv, taskProgress, taskStatus, 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
|
@@ -53,6 +53,7 @@ import { StatusPill } from "./primitives/status-pill.js";
|
|
|
53
53
|
import { CATEGORY_TONES, TONE_CLASSES, toneFor } from "./primitives/tone.js";
|
|
54
54
|
import { Tag } from "./primitives/tag.js";
|
|
55
55
|
import { Chip } from "./primitives/chip.js";
|
|
56
|
+
import { Wizard, WizardStep } from "./primitives/wizard.js";
|
|
56
57
|
import { IconTile, initialsOf } from "./primitives/icon-tile.js";
|
|
57
58
|
import { Metric, MetricStrip } from "./primitives/metric-strip.js";
|
|
58
59
|
import { FilterField, Toolbar } from "./primitives/toolbar.js";
|
|
@@ -118,4 +119,4 @@ import { addMessage, addParts, clearChat, isStreamingAtom, messagesAtom, partMap
|
|
|
118
119
|
import { copyText } from "./utils/copy-text.js";
|
|
119
120
|
import { timeAgo } from "./utils/time-ago.js";
|
|
120
121
|
import { RedactedDocument } from "./redaction/redacted-document.js";
|
|
121
|
-
export { AgentTimeline, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, ApiSchema, ApprovalCard, ApprovalDiffSummary, ArtifactPane, AuthHeader, Avatar, AvatarFallback, AvatarImage, Badge, Button, CATEGORY_TONES, CSV_PREVIEW_ROW_LIMIT, Calendar, 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, DatePicker, 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, FileCard, FileList, FilePreview, FileTabs, FileTree, FilterField, GitHubLoginButton, GlobResultsPreview, GrepResultsPreview, Heading, HelpText, IconTile, InlineCode, InlineThinkingItem, InlineToolItem, Input, Label, LiveDuration, LoginLayout, Logo, Markdown, MessageAuthor, MessageList, Metric, MetricStrip, OPENUI_NODE_JSON_SCHEMA, OPENUI_NODE_TYPES, OpenUIArtifactRenderer, PageHeader, PageShell, Pagination, PdfViewer, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, QuestionPreview, RadioGroup, RadioGroupItem, RealtimeSessionRegistry, RedactedDocument, RichFileTree, RunGroup, RunPhaseList, 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, TRANSCRIPT_TEXT, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, TangleKnot, TaskList, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TextShimmer, Textarea, ThemeToggle, ThinkingIndicator, TimeSelect, ToastContainer, ToastProvider, ToolCallFeed, Toolbar, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UploadProgress, UserMenu, UserMessage, WebSearchPreview, WriteFilePreview, activeSessionsAtom, addMessage, addParts, badgeVariants, bumpActiveSessionActivity, buttonVariants, clearChat, cn, codeBlockGrammar, configurePdfViewer, copyText, createAuthFetcher, detectFileFormat, fenceInfo, fileExtension, filterFileTree, findUnsupportedOpenUINode, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatCalendarDate, formatDuration, formatUptime, getActiveSession, getAllActiveSessions, getAllProjectActivity, getCodeLanguage, getFormatLabel, getSanitizedMarkdownHeadingIdFromRawFragment, getSessionsByActivity, getSessionsForNavbar, getSessionsForProject, getSyntaxLanguage, getToolCategory, getToolDisplayMetadata, getToolErrorText, getTotalRunningSessionCount, hasBackgroundRunningSessions, initialsOf, isStreamingAtom, isViewerMessage, ixControl, ixControlAfter, ixControlSoft, ixField, ixRow, ixSelected, ixTabUnderline, localToday, messagesAtom, numericColumns, parseCalendarDate, parseCsv, parseToolEvent, partMapAtom, plainTextFromMarkdown, registerActiveSession, resetActiveSessions, resolveFilePreviewKind, setActiveSessionAttention, setActiveSessionConnection, setActiveSessionError, setActiveSessionRunning, setForegroundActiveSession, showsTaskPriority, tableCsv, taskProgress, taskStatus, 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 };
|
|
122
|
+
export { AgentTimeline, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, ApiSchema, ApprovalCard, ApprovalDiffSummary, ArtifactPane, AuthHeader, Avatar, AvatarFallback, AvatarImage, Badge, Button, CATEGORY_TONES, CSV_PREVIEW_ROW_LIMIT, Calendar, 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, DatePicker, 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, FileCard, FileList, FilePreview, FileTabs, FileTree, FilterField, GitHubLoginButton, GlobResultsPreview, GrepResultsPreview, Heading, HelpText, IconTile, InlineCode, InlineThinkingItem, InlineToolItem, Input, Label, LiveDuration, LoginLayout, Logo, Markdown, MessageAuthor, MessageList, Metric, MetricStrip, OPENUI_NODE_JSON_SCHEMA, OPENUI_NODE_TYPES, OpenUIArtifactRenderer, PageHeader, PageShell, Pagination, PdfViewer, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverTrigger, Progress, QuestionPreview, RadioGroup, RadioGroupItem, RealtimeSessionRegistry, RedactedDocument, RichFileTree, RunGroup, RunPhaseList, 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, TRANSCRIPT_TEXT, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, TangleKnot, TaskList, TerminalCursor, TerminalDisplay, TerminalInput, TerminalLine, TextShimmer, Textarea, ThemeToggle, ThinkingIndicator, TimeSelect, ToastContainer, ToastProvider, ToolCallFeed, Toolbar, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UploadProgress, UserMenu, UserMessage, WebSearchPreview, Wizard, WizardStep, WriteFilePreview, activeSessionsAtom, addMessage, addParts, badgeVariants, bumpActiveSessionActivity, buttonVariants, clearChat, cn, codeBlockGrammar, configurePdfViewer, copyText, createAuthFetcher, detectFileFormat, fenceInfo, fileExtension, filterFileTree, findUnsupportedOpenUINode, focusField, focusFieldInvalid, focusFieldWithin, focusRing, focusRingInset, formatBytes, formatCalendarDate, formatDuration, formatUptime, getActiveSession, getAllActiveSessions, getAllProjectActivity, getCodeLanguage, getFormatLabel, getSanitizedMarkdownHeadingIdFromRawFragment, getSessionsByActivity, getSessionsForNavbar, getSessionsForProject, getSyntaxLanguage, getToolCategory, getToolDisplayMetadata, getToolErrorText, getTotalRunningSessionCount, hasBackgroundRunningSessions, initialsOf, isStreamingAtom, isViewerMessage, ixControl, ixControlAfter, ixControlSoft, ixField, ixRow, ixSelected, ixTabUnderline, localToday, messagesAtom, numericColumns, parseCalendarDate, parseCsv, parseToolEvent, partMapAtom, plainTextFromMarkdown, registerActiveSession, resetActiveSessions, resolveFilePreviewKind, setActiveSessionAttention, setActiveSessionConnection, setActiveSessionError, setActiveSessionRunning, setForegroundActiveSession, showsTaskPriority, tableCsv, taskProgress, taskStatus, 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 };
|
|
@@ -4,9 +4,10 @@ import * as React from "react";
|
|
|
4
4
|
import { jsx } from "react/jsx-runtime";
|
|
5
5
|
import { animate, motionValue, styleEffect } from "motion";
|
|
6
6
|
//#region src/lib/surface-motion.tsx
|
|
7
|
-
/** Radix owns state
|
|
7
|
+
/** Radix owns state and focus; visual retirement cannot wait for a CSS event. */
|
|
8
8
|
const SurfaceMotion = React.forwardRef(({ kind, side = "right", asChild, "data-state": state, ...props }, forwardedRef) => {
|
|
9
9
|
const element = React.useRef(null);
|
|
10
|
+
const [retired, setRetired] = React.useState(false);
|
|
10
11
|
const values = React.useRef(null);
|
|
11
12
|
const controls = React.useRef([]);
|
|
12
13
|
const ref = React.useCallback((node) => {
|
|
@@ -24,7 +25,10 @@ const SurfaceMotion = React.forwardRef(({ kind, side = "right", asChild, "data-s
|
|
|
24
25
|
if (!node) return;
|
|
25
26
|
const preference = typeof matchMedia === "function" ? matchMedia("(prefers-reduced-motion: reduce)") : void 0;
|
|
26
27
|
let cancelRender;
|
|
28
|
+
let retirement;
|
|
27
29
|
const run = () => {
|
|
30
|
+
clearTimeout(retirement);
|
|
31
|
+
setRetired(false);
|
|
28
32
|
const closed = state === "closed";
|
|
29
33
|
const bounds = node.getBoundingClientRect();
|
|
30
34
|
const hidden = {
|
|
@@ -46,6 +50,10 @@ const SurfaceMotion = React.forwardRef(({ kind, side = "right", asChild, "data-s
|
|
|
46
50
|
node.style.opacity = String(value.opacity);
|
|
47
51
|
node.style.transform = `translateX(${value.x}px) translateY(${value.y}px) scale(${value.scale})`;
|
|
48
52
|
};
|
|
53
|
+
if (closed) {
|
|
54
|
+
if (preference?.matches || seconds <= 0 || style.animationName === "none") setRetired(true);
|
|
55
|
+
else retirement = setTimeout(() => setRetired(true), seconds * 1e3);
|
|
56
|
+
}
|
|
49
57
|
if (preference?.matches || seconds <= 0 || style.animationName === "none") {
|
|
50
58
|
controls.current.forEach((control) => control.stop());
|
|
51
59
|
if (values.current) for (const key of [
|
|
@@ -84,6 +92,7 @@ const SurfaceMotion = React.forwardRef(({ kind, side = "right", asChild, "data-s
|
|
|
84
92
|
run();
|
|
85
93
|
preference?.addEventListener("change", run);
|
|
86
94
|
return () => {
|
|
95
|
+
clearTimeout(retirement);
|
|
87
96
|
preference?.removeEventListener("change", run);
|
|
88
97
|
cancelRender?.();
|
|
89
98
|
};
|
|
@@ -94,7 +103,7 @@ const SurfaceMotion = React.forwardRef(({ kind, side = "right", asChild, "data-s
|
|
|
94
103
|
props.style?.animation
|
|
95
104
|
]);
|
|
96
105
|
React.useEffect(() => () => controls.current.forEach((control) => control.stop()), []);
|
|
97
|
-
return /* @__PURE__ */ jsx(asChild ? Slot : "div", {
|
|
106
|
+
return state === "closed" && retired ? null : /* @__PURE__ */ jsx(asChild ? Slot : "div", {
|
|
98
107
|
...props,
|
|
99
108
|
"data-state": state,
|
|
100
109
|
ref
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
//#region src/primitives/wizard.d.ts
|
|
3
|
+
export interface WizardProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
4
|
+
children: React.ReactNode;
|
|
5
|
+
/** Drives the active step; omit for uncontrolled with `defaultStep`. */
|
|
6
|
+
step?: number;
|
|
7
|
+
defaultStep?: number;
|
|
8
|
+
onStepChange?: (step: number) => void;
|
|
9
|
+
/** Fires when the last step's primary action resolves. */
|
|
10
|
+
onFinished?: () => void;
|
|
11
|
+
}
|
|
12
|
+
export declare function Wizard({ children, step, defaultStep, onStepChange, onFinished, className, ...rest }: WizardProps): React.JSX.Element;
|
|
13
|
+
export interface WizardStepProps {
|
|
14
|
+
/** Shown in the rail (and the mobile position line). Steps read at a glance; keep it one or two words. */
|
|
15
|
+
title: string;
|
|
16
|
+
children: React.ReactNode;
|
|
17
|
+
/** The footer's primary action label. Omit when the step renders its own actions. */
|
|
18
|
+
nextLabel?: string;
|
|
19
|
+
/** Runs before advancing. Resolve (or return a non-false value) to advance; return false or throw to stay — the step's own content shows why. */
|
|
20
|
+
onNext?: () => Promise<unknown> | unknown;
|
|
21
|
+
/** Disables the primary action (a required choice is still missing). */
|
|
22
|
+
nextDisabled?: boolean;
|
|
23
|
+
/** Hides the Back button for steps where going back makes no sense (a completed handshake). */
|
|
24
|
+
hideBack?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export declare function WizardStep({ title, children, nextLabel, onNext, nextDisabled, hideBack }: WizardStepProps): React.JSX.Element;
|
|
27
|
+
export interface Wizard extends React.FC<WizardProps> {
|
|
28
|
+
Step: typeof WizardStep;
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { cn } from "../lib/utils.js";
|
|
2
|
+
import { Button } from "./button.js";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { Check, ChevronLeft } from "lucide-react";
|
|
6
|
+
//#region src/primitives/wizard.tsx
|
|
7
|
+
const WizardContext = React.createContext(null);
|
|
8
|
+
function useWizardContext(component) {
|
|
9
|
+
const context = React.useContext(WizardContext);
|
|
10
|
+
if (!context) throw new Error(`<${component}> must be used inside <Wizard>`);
|
|
11
|
+
return context;
|
|
12
|
+
}
|
|
13
|
+
function Wizard({ children, step, defaultStep = 0, onStepChange, onFinished, className, ...rest }) {
|
|
14
|
+
const steps = React.Children.toArray(children).filter(React.isValidElement);
|
|
15
|
+
const [internal, setInternal] = React.useState(defaultStep);
|
|
16
|
+
const current = step !== void 0 ? step : internal;
|
|
17
|
+
const total = steps.length;
|
|
18
|
+
const goTo = React.useCallback((target) => {
|
|
19
|
+
const bounded = Math.max(0, Math.min(target, total - 1));
|
|
20
|
+
if (bounded === current) return;
|
|
21
|
+
onStepChange?.(bounded);
|
|
22
|
+
if (step === void 0) setInternal(bounded);
|
|
23
|
+
}, [
|
|
24
|
+
current,
|
|
25
|
+
onStepChange,
|
|
26
|
+
step,
|
|
27
|
+
total
|
|
28
|
+
]);
|
|
29
|
+
const context = React.useMemo(() => ({
|
|
30
|
+
current,
|
|
31
|
+
total,
|
|
32
|
+
goTo,
|
|
33
|
+
next: () => goTo(current + 1),
|
|
34
|
+
back: () => goTo(current - 1),
|
|
35
|
+
finish: () => onFinished?.()
|
|
36
|
+
}), [
|
|
37
|
+
current,
|
|
38
|
+
goTo,
|
|
39
|
+
onFinished,
|
|
40
|
+
total
|
|
41
|
+
]);
|
|
42
|
+
const titles = steps.map((child) => child.props.title);
|
|
43
|
+
const activeTitle = titles[current] ?? "";
|
|
44
|
+
return /* @__PURE__ */ jsx(WizardContext.Provider, {
|
|
45
|
+
value: context,
|
|
46
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
47
|
+
className: cn("w-full", className),
|
|
48
|
+
...rest,
|
|
49
|
+
children: [
|
|
50
|
+
/* @__PURE__ */ jsx("ol", {
|
|
51
|
+
"aria-label": "Steps",
|
|
52
|
+
className: "hidden items-center gap-2 sm:flex",
|
|
53
|
+
children: titles.map((title, index) => {
|
|
54
|
+
const state = index < current ? "done" : index === current ? "active" : "future";
|
|
55
|
+
const marker = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
56
|
+
className: cn("flex size-6 shrink-0 items-center justify-center rounded-full text-xs font-medium", state === "active" && "bg-primary text-primary-foreground", state === "done" && "bg-primary/15 text-primary", state === "future" && "bg-muted text-muted-foreground"),
|
|
57
|
+
children: state === "done" ? /* @__PURE__ */ jsx(Check, {
|
|
58
|
+
className: "size-3.5",
|
|
59
|
+
"aria-hidden": true
|
|
60
|
+
}) : index + 1
|
|
61
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
62
|
+
className: cn("text-sm", state === "active" && "font-medium text-foreground", state === "done" && "text-foreground", state === "future" && "text-muted-foreground"),
|
|
63
|
+
children: title
|
|
64
|
+
})] });
|
|
65
|
+
return /* @__PURE__ */ jsxs(React.Fragment, { children: [index > 0 && /* @__PURE__ */ jsx("li", {
|
|
66
|
+
"aria-hidden": true,
|
|
67
|
+
className: "h-px flex-1 bg-border"
|
|
68
|
+
}), /* @__PURE__ */ jsx("li", { children: state === "future" ? /* @__PURE__ */ jsx("span", {
|
|
69
|
+
"aria-disabled": "true",
|
|
70
|
+
className: "flex items-center gap-2 p-1 opacity-70",
|
|
71
|
+
children: marker
|
|
72
|
+
}) : /* @__PURE__ */ jsx("button", {
|
|
73
|
+
type: "button",
|
|
74
|
+
onClick: () => goTo(index),
|
|
75
|
+
className: "-m-1 flex items-center gap-2 rounded-md p-1 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
76
|
+
children: marker
|
|
77
|
+
}) })] }, `${index}-${title}`);
|
|
78
|
+
})
|
|
79
|
+
}),
|
|
80
|
+
/* @__PURE__ */ jsxs("div", {
|
|
81
|
+
className: "sm:hidden",
|
|
82
|
+
children: [/* @__PURE__ */ jsxs("p", {
|
|
83
|
+
className: "text-xs text-muted-foreground",
|
|
84
|
+
children: [
|
|
85
|
+
"Step ",
|
|
86
|
+
current + 1,
|
|
87
|
+
" of ",
|
|
88
|
+
total,
|
|
89
|
+
" · ",
|
|
90
|
+
/* @__PURE__ */ jsx("span", {
|
|
91
|
+
className: "font-medium text-foreground",
|
|
92
|
+
children: activeTitle
|
|
93
|
+
})
|
|
94
|
+
]
|
|
95
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
96
|
+
className: "mt-2 h-1 overflow-hidden rounded-full bg-muted",
|
|
97
|
+
role: "progressbar",
|
|
98
|
+
"aria-valuemin": 1,
|
|
99
|
+
"aria-valuemax": total,
|
|
100
|
+
"aria-valuenow": current + 1,
|
|
101
|
+
"aria-label": `Step ${current + 1} of ${total}`,
|
|
102
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
103
|
+
className: "h-full rounded-full bg-primary transition-all",
|
|
104
|
+
style: { width: `${(current + 1) / total * 100}%` }
|
|
105
|
+
})
|
|
106
|
+
})]
|
|
107
|
+
}),
|
|
108
|
+
steps.map((child, index) => index === current ? /* @__PURE__ */ jsx(React.Fragment, { children: child }, index) : null)
|
|
109
|
+
]
|
|
110
|
+
})
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
function WizardStep({ title, children, nextLabel, onNext, nextDisabled, hideBack }) {
|
|
114
|
+
const { current, total, goTo, back, finish } = useWizardContext("Wizard.Step");
|
|
115
|
+
const [busy, setBusy] = React.useState(false);
|
|
116
|
+
const isLast = current === total - 1;
|
|
117
|
+
async function runNext() {
|
|
118
|
+
if (!onNext) {
|
|
119
|
+
if (isLast) finish();
|
|
120
|
+
else goTo(current + 1);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
setBusy(true);
|
|
124
|
+
try {
|
|
125
|
+
if (await onNext() === false) return;
|
|
126
|
+
if (isLast) finish();
|
|
127
|
+
else goTo(current + 1);
|
|
128
|
+
} catch {} finally {
|
|
129
|
+
setBusy(false);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
133
|
+
"aria-label": title,
|
|
134
|
+
className: "pt-6",
|
|
135
|
+
children: [children, (nextLabel || !hideBack) && /* @__PURE__ */ jsxs("div", {
|
|
136
|
+
className: "mt-6 flex items-center justify-between gap-2 border-t border-border pt-4",
|
|
137
|
+
children: [!hideBack && current > 0 ? /* @__PURE__ */ jsxs(Button, {
|
|
138
|
+
type: "button",
|
|
139
|
+
variant: "ghost",
|
|
140
|
+
onClick: back,
|
|
141
|
+
children: [/* @__PURE__ */ jsx(ChevronLeft, {
|
|
142
|
+
className: "size-4",
|
|
143
|
+
"aria-hidden": true
|
|
144
|
+
}), " Back"]
|
|
145
|
+
}) : /* @__PURE__ */ jsx("span", {}), nextLabel && /* @__PURE__ */ jsx(Button, {
|
|
146
|
+
type: "button",
|
|
147
|
+
className: "w-full sm:w-auto",
|
|
148
|
+
onClick: () => void runNext(),
|
|
149
|
+
disabled: nextDisabled,
|
|
150
|
+
loading: busy,
|
|
151
|
+
children: nextLabel
|
|
152
|
+
})]
|
|
153
|
+
})]
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
Wizard.Step = WizardStep;
|
|
157
|
+
//#endregion
|
|
158
|
+
export { Wizard, WizardStep };
|
package/dist/primitives.d.ts
CHANGED
|
@@ -50,7 +50,8 @@ import { Pagination, PaginationProps } from "./primitives/pagination.js";
|
|
|
50
50
|
import { StatusPill, StatusPillProps, StatusTone } from "./primitives/status-pill.js";
|
|
51
51
|
import { Tag, TagProps } from "./primitives/tag.js";
|
|
52
52
|
import { Chip, ChipProps } from "./primitives/chip.js";
|
|
53
|
+
import { Wizard, WizardProps, WizardStep, WizardStepProps } from "./primitives/wizard.js";
|
|
53
54
|
import { Metric, MetricProps, MetricStrip, MetricStripProps } from "./primitives/metric-strip.js";
|
|
54
55
|
import { FilterField, FilterFieldProps, Toolbar, ToolbarProps } from "./primitives/toolbar.js";
|
|
55
56
|
import { Logo, LogoProps, TangleKnot } from "@tangle-network/brand";
|
|
56
|
-
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 };
|
|
57
|
+
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, Wizard, type WizardProps, WizardStep, type WizardStepProps, badgeVariants, buttonVariants, formatCalendarDate, initialsOf, localToday, parseCalendarDate, toneFor, useTheme, useToast };
|
package/dist/primitives.js
CHANGED
|
@@ -49,8 +49,9 @@ import { StatusPill } from "./primitives/status-pill.js";
|
|
|
49
49
|
import { CATEGORY_TONES, TONE_CLASSES, toneFor } from "./primitives/tone.js";
|
|
50
50
|
import { Tag } from "./primitives/tag.js";
|
|
51
51
|
import { Chip } from "./primitives/chip.js";
|
|
52
|
+
import { Wizard, WizardStep } from "./primitives/wizard.js";
|
|
52
53
|
import { IconTile, initialsOf } from "./primitives/icon-tile.js";
|
|
53
54
|
import { Metric, MetricStrip } from "./primitives/metric-strip.js";
|
|
54
55
|
import { FilterField, Toolbar } from "./primitives/toolbar.js";
|
|
55
56
|
import { Logo, TangleKnot } from "@tangle-network/brand";
|
|
56
|
-
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 };
|
|
57
|
+
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, Wizard, WizardStep, badgeVariants, buttonVariants, formatCalendarDate, initialsOf, localToday, parseCalendarDate, toneFor, useTheme, useToast };
|
package/package.json
CHANGED
|
@@ -12,10 +12,11 @@ type SurfaceProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
|
12
12
|
"data-state"?: string;
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
-
/** Radix owns state
|
|
15
|
+
/** Radix owns state and focus; visual retirement cannot wait for a CSS event. */
|
|
16
16
|
export const SurfaceMotion = React.forwardRef<HTMLDivElement, SurfaceProps>(
|
|
17
17
|
({ kind, side = "right", asChild, "data-state": state, ...props }, forwardedRef) => {
|
|
18
18
|
const element = React.useRef<HTMLDivElement | null>(null);
|
|
19
|
+
const [retired, setRetired] = React.useState(false);
|
|
19
20
|
const values = React.useRef<Record<"opacity" | "scale" | "x" | "y", MotionValue<number>> | null>(null);
|
|
20
21
|
const controls = React.useRef<Array<ReturnType<typeof animate>>>([]);
|
|
21
22
|
const ref = React.useCallback<React.RefCallback<HTMLDivElement>>((node) => {
|
|
@@ -31,7 +32,10 @@ export const SurfaceMotion = React.forwardRef<HTMLDivElement, SurfaceProps>(
|
|
|
31
32
|
if (!node) return;
|
|
32
33
|
const preference = typeof matchMedia === "function" ? matchMedia("(prefers-reduced-motion: reduce)") : undefined;
|
|
33
34
|
let cancelRender: (() => void) | undefined;
|
|
35
|
+
let retirement: ReturnType<typeof setTimeout> | undefined;
|
|
34
36
|
const run = () => {
|
|
37
|
+
clearTimeout(retirement);
|
|
38
|
+
setRetired(false);
|
|
35
39
|
const closed = state === "closed";
|
|
36
40
|
const bounds = node.getBoundingClientRect();
|
|
37
41
|
const hidden = {
|
|
@@ -49,6 +53,14 @@ export const SurfaceMotion = React.forwardRef<HTMLDivElement, SurfaceProps>(
|
|
|
49
53
|
node.style.opacity = String(value.opacity);
|
|
50
54
|
node.style.transform = `translateX(${value.x}px) translateY(${value.y}px) scale(${value.scale})`;
|
|
51
55
|
};
|
|
56
|
+
// Radix may retain an exit until animationend. Retire this DOM node at
|
|
57
|
+
// the scoped deadline even when keyframes/events are missing or paused;
|
|
58
|
+
// its null ref lets Radix release presence, focus and modal resources.
|
|
59
|
+
// Reopen cancels retirement and keeps the same node and Motion values.
|
|
60
|
+
if (closed) {
|
|
61
|
+
if (preference?.matches || seconds <= 0 || style.animationName === "none") setRetired(true);
|
|
62
|
+
else retirement = setTimeout(() => setRetired(true), seconds * 1000);
|
|
63
|
+
}
|
|
52
64
|
// Missing styles and reduced motion settle synchronously. No semantic
|
|
53
65
|
// transition waits for a JavaScript completion callback.
|
|
54
66
|
if (preference?.matches || seconds <= 0 || style.animationName === "none") {
|
|
@@ -73,13 +85,13 @@ export const SurfaceMotion = React.forwardRef<HTMLDivElement, SurfaceProps>(
|
|
|
73
85
|
};
|
|
74
86
|
run();
|
|
75
87
|
preference?.addEventListener("change", run);
|
|
76
|
-
return () => { preference?.removeEventListener("change", run); cancelRender?.(); };
|
|
88
|
+
return () => { clearTimeout(retirement); preference?.removeEventListener("change", run); cancelRender?.(); };
|
|
77
89
|
// Radix Popper temporarily suppresses animation until placement resolves.
|
|
78
90
|
// Start the same retained values when that inline override clears.
|
|
79
91
|
}, [state, kind, side, props.style?.animation]);
|
|
80
92
|
React.useEffect(() => () => controls.current.forEach(control => control.stop()), []);
|
|
81
93
|
const Component = asChild ? Slot : "div";
|
|
82
|
-
return <Component {...props} data-state={state} ref={ref} />;
|
|
94
|
+
return state === "closed" && retired ? null : <Component {...props} data-state={state} ref={ref} />;
|
|
83
95
|
},
|
|
84
96
|
);
|
|
85
97
|
SurfaceMotion.displayName = "SurfaceMotion";
|
package/src/primitives/index.ts
CHANGED
|
@@ -217,6 +217,9 @@ export type { TagProps } from "./tag";
|
|
|
217
217
|
export { Chip } from "./chip";
|
|
218
218
|
export type { ChipProps } from "./chip";
|
|
219
219
|
|
|
220
|
+
export { Wizard, WizardStep } from "./wizard";
|
|
221
|
+
export type { WizardProps, WizardStepProps } from "./wizard";
|
|
222
|
+
|
|
220
223
|
export { IconTile, initialsOf } from "./icon-tile";
|
|
221
224
|
export type { IconTileProps } from "./icon-tile";
|
|
222
225
|
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { Button } from "./button";
|
|
4
|
+
import { Input } from "./input";
|
|
5
|
+
import { Wizard } from "./wizard";
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof Wizard> = {
|
|
8
|
+
title: "Primitives/Wizard",
|
|
9
|
+
component: Wizard,
|
|
10
|
+
};
|
|
11
|
+
export default meta;
|
|
12
|
+
|
|
13
|
+
/** The intended shape: linear steps, one primary action each, async gate in the middle. */
|
|
14
|
+
export const ConnectNumber: StoryObj<typeof Wizard> = {
|
|
15
|
+
render: () => (
|
|
16
|
+
<div className="mx-auto max-w-lg rounded-xl border border-border bg-card p-6">
|
|
17
|
+
<Wizard onFinished={() => alert("connected")}>
|
|
18
|
+
<Wizard.Step title="Number" nextLabel="Continue">
|
|
19
|
+
<label className="grid gap-2 text-sm">
|
|
20
|
+
Your business number
|
|
21
|
+
<Input placeholder="+1 618 297 8214" />
|
|
22
|
+
</label>
|
|
23
|
+
</Wizard.Step>
|
|
24
|
+
<Wizard.Step title="Verify" nextLabel="I replied" onNext={() => new Promise(resolve => setTimeout(resolve, 800))}>
|
|
25
|
+
<p className="text-sm text-muted-foreground">
|
|
26
|
+
We sent a code to your phone. Reply to the message, then continue.
|
|
27
|
+
</p>
|
|
28
|
+
</Wizard.Step>
|
|
29
|
+
<Wizard.Step title="Done" nextLabel="Finish">
|
|
30
|
+
<p className="text-sm">Your number is live. Guests can text it now.</p>
|
|
31
|
+
</Wizard.Step>
|
|
32
|
+
</Wizard>
|
|
33
|
+
</div>
|
|
34
|
+
),
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/** A step whose content owns the actions entirely: no footer, no default chrome. */
|
|
38
|
+
export const CustomActions: StoryObj<typeof Wizard> = {
|
|
39
|
+
render: () => (
|
|
40
|
+
<div className="mx-auto max-w-lg rounded-xl border border-border bg-card p-6">
|
|
41
|
+
<Wizard>
|
|
42
|
+
<Wizard.Step title="Choice">
|
|
43
|
+
<div className="grid gap-3">
|
|
44
|
+
<Button onClick={() => undefined}>Use my own number</Button>
|
|
45
|
+
<Button variant="outline" onClick={() => undefined}>Give me a Tangle number</Button>
|
|
46
|
+
</div>
|
|
47
|
+
</Wizard.Step>
|
|
48
|
+
</Wizard>
|
|
49
|
+
</div>
|
|
50
|
+
),
|
|
51
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
2
|
+
import userEvent from "@testing-library/user-event";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { describe, expect, it, vi } from "vitest";
|
|
5
|
+
import { Wizard } from "./wizard";
|
|
6
|
+
|
|
7
|
+
function Flow(props: Partial<React.ComponentProps<typeof Wizard>> = {}) {
|
|
8
|
+
return (
|
|
9
|
+
<Wizard {...props}>
|
|
10
|
+
<Wizard.Step title="Number" nextLabel="Continue">pick a number</Wizard.Step>
|
|
11
|
+
<Wizard.Step title="Verify" nextLabel="I replied">verify panel</Wizard.Step>
|
|
12
|
+
<Wizard.Step title="Done" nextLabel="Finish">done panel</Wizard.Step>
|
|
13
|
+
</Wizard>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe("Wizard", () => {
|
|
18
|
+
it("renders the first step and the rail, marks the active step, hides future panels", () => {
|
|
19
|
+
render(<Flow />);
|
|
20
|
+
expect(screen.getByText("pick a number")).toBeVisible();
|
|
21
|
+
expect(screen.queryByText("verify panel")).toBeNull();
|
|
22
|
+
expect(screen.getByRole("list", { name: "Steps" })).toBeVisible();
|
|
23
|
+
expect(screen.getAllByText("Number").length).toBeGreaterThan(0);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("advances on the primary action and mirrors onStepChange", async () => {
|
|
27
|
+
const onStepChange = vi.fn();
|
|
28
|
+
render(<Flow onStepChange={onStepChange} />);
|
|
29
|
+
await userEvent.click(screen.getByRole("button", { name: "Continue" }));
|
|
30
|
+
expect(screen.getByText("verify panel")).toBeVisible();
|
|
31
|
+
expect(onStepChange).toHaveBeenCalledWith(1);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("back returns to the previous step; the first step has no back", async () => {
|
|
35
|
+
render(<Flow />);
|
|
36
|
+
await userEvent.click(screen.getByRole("button", { name: "Continue" }));
|
|
37
|
+
await userEvent.click(screen.getByRole("button", { name: "Back" }));
|
|
38
|
+
expect(screen.getByText("pick a number")).toBeVisible();
|
|
39
|
+
expect(screen.queryByRole("button", { name: "Back" })).toBeNull();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("future steps are not clickable; past steps are", async () => {
|
|
43
|
+
render(<Flow />);
|
|
44
|
+
fireEvent.click(screen.getByText("Verify")); // future: a span, not a button
|
|
45
|
+
expect(screen.getByText("pick a number")).toBeVisible();
|
|
46
|
+
await userEvent.click(screen.getByRole("button", { name: "Continue" }));
|
|
47
|
+
await userEvent.click(screen.getAllByRole("button", { name: /Number/ })[0]!); // past: clickable
|
|
48
|
+
expect(screen.getByText("pick a number")).toBeVisible();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("an async onNext advances only on resolve; returning false keeps the step", async () => {
|
|
52
|
+
let resolveCheck: (value: unknown) => void = () => {};
|
|
53
|
+
const onNext = vi.fn(() => new Promise(resolve => { resolveCheck = resolve; }));
|
|
54
|
+
render(
|
|
55
|
+
<Wizard>
|
|
56
|
+
<Wizard.Step title="Check" nextLabel="Check" onNext={onNext}>checking</Wizard.Step>
|
|
57
|
+
<Wizard.Step title="Next">next panel</Wizard.Step>
|
|
58
|
+
</Wizard>,
|
|
59
|
+
);
|
|
60
|
+
await userEvent.click(screen.getByRole("button", { name: "Check" }));
|
|
61
|
+
expect(screen.getByText("checking")).toBeVisible(); // still here while pending
|
|
62
|
+
resolveCheck(true);
|
|
63
|
+
await waitFor(() => expect(screen.getByText("next panel")).toBeVisible());
|
|
64
|
+
expect(onNext).toHaveBeenCalledTimes(1);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("a rejected onNext stays on the step without crashing", async () => {
|
|
68
|
+
const onNext = vi.fn(() => Promise.reject(new Error("no")));
|
|
69
|
+
render(
|
|
70
|
+
<Wizard>
|
|
71
|
+
<Wizard.Step title="Check" nextLabel="Check" onNext={onNext}>checking</Wizard.Step>
|
|
72
|
+
<Wizard.Step title="Next">next panel</Wizard.Step>
|
|
73
|
+
</Wizard>,
|
|
74
|
+
);
|
|
75
|
+
await userEvent.click(screen.getByRole("button", { name: "Check" }));
|
|
76
|
+
await waitFor(() => expect(screen.getByRole("button", { name: "Check" })).toBeEnabled());
|
|
77
|
+
expect(screen.getByText("checking")).toBeVisible();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("the last step fires onFinished instead of advancing", async () => {
|
|
81
|
+
const onFinished = vi.fn();
|
|
82
|
+
render(<Flow onFinished={onFinished} defaultStep={2} />);
|
|
83
|
+
await userEvent.click(screen.getByRole("button", { name: "Finish" }));
|
|
84
|
+
expect(onFinished).toHaveBeenCalledTimes(1);
|
|
85
|
+
expect(screen.getByText("done panel")).toBeVisible();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("step can drive externally (controlled) and omit the default footer action", () => {
|
|
89
|
+
render(
|
|
90
|
+
<Wizard step={1}>
|
|
91
|
+
<Wizard.Step title="One">one</Wizard.Step>
|
|
92
|
+
<Wizard.Step title="Two"><button type="button">custom</button></Wizard.Step>
|
|
93
|
+
</Wizard>,
|
|
94
|
+
);
|
|
95
|
+
expect(screen.getAllByText("Two").length).toBeGreaterThan(0);
|
|
96
|
+
expect(screen.getByText("custom")).toBeVisible();
|
|
97
|
+
expect(screen.queryByRole("button", { name: "Continue" })).toBeNull();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("throws when a step is used outside a wizard", () => {
|
|
101
|
+
// Silence the expected console.error from React's error boundary path.
|
|
102
|
+
const spy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
103
|
+
expect(() => render(<Wizard.Step title="Alone">x</Wizard.Step>)).toThrow(/must be used inside/);
|
|
104
|
+
spy.mockRestore();
|
|
105
|
+
});
|
|
106
|
+
});
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { Check, ChevronLeft } from "lucide-react";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { cn } from "../lib/utils";
|
|
4
|
+
import { Button } from "./button";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A multi-step flow — onboarding, connection setup, anything where a person
|
|
8
|
+
* should see one decision at a time.
|
|
9
|
+
*
|
|
10
|
+
* The library owns the chrome (step rail, progress, footer); each step owns
|
|
11
|
+
* its content and its primary action. That separation is the point: products
|
|
12
|
+
* stop rebuilding stepper markup with drifting styles, and a person gets the
|
|
13
|
+
* same rhythm — one screen, one obvious action — in every Tangle app.
|
|
14
|
+
*
|
|
15
|
+
* Composition (DOM order is the step order):
|
|
16
|
+
*
|
|
17
|
+
* ```tsx
|
|
18
|
+
* <Wizard onFinished={() => navigate("/done")}>
|
|
19
|
+
* <Wizard.Step title="Number" nextLabel="Continue">…</Wizard.Step>
|
|
20
|
+
* <Wizard.Step title="Verify" nextLabel="I replied" onNext={() => check()}>…</Wizard.Step>
|
|
21
|
+
* <Wizard.Step title="Done" nextLabel="Finish">…</Wizard.Step>
|
|
22
|
+
* </Wizard>
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* - **Uncontrolled by default**; pass `step` to drive it (a background poll
|
|
26
|
+
* can jump straight to Done). `onStepChange` mirrors every move.
|
|
27
|
+
* - **The step names its action.** `nextLabel` renders the footer's primary
|
|
28
|
+
* button; an async `onNext` shows `loading` and advances only on resolve,
|
|
29
|
+
* so failures keep the person on the step with the app's inline error.
|
|
30
|
+
* Omit `nextLabel` for steps whose content renders its own actions.
|
|
31
|
+
* - **Forward is earned, backward is free.** Future steps are not clickable;
|
|
32
|
+
* past steps are (people re-check earlier choices mid-flow).
|
|
33
|
+
* - **Mobile is a first-class shape**, not a squeezed desktop: below `sm` the
|
|
34
|
+
* rail collapses to "Step 2 of 3 · Verify" and a thin progress bar.
|
|
35
|
+
*
|
|
36
|
+
* Wizard renders no card — compose it inside a `Card` when the surface needs
|
|
37
|
+
* one, so containers-within-containers stay a choice, not a default.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
interface WizardContextValue {
|
|
41
|
+
current: number;
|
|
42
|
+
total: number;
|
|
43
|
+
goTo: (step: number) => void;
|
|
44
|
+
next: () => void;
|
|
45
|
+
back: () => void;
|
|
46
|
+
/** Set by Wizard: fires onFinished when the final step's action resolves. */
|
|
47
|
+
finish: () => void;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const WizardContext = React.createContext<WizardContextValue | null>(null);
|
|
51
|
+
|
|
52
|
+
function useWizardContext(component: string): WizardContextValue {
|
|
53
|
+
const context = React.useContext(WizardContext);
|
|
54
|
+
if (!context) throw new Error(`<${component}> must be used inside <Wizard>`);
|
|
55
|
+
return context;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface WizardProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
59
|
+
children: React.ReactNode;
|
|
60
|
+
/** Drives the active step; omit for uncontrolled with `defaultStep`. */
|
|
61
|
+
step?: number;
|
|
62
|
+
defaultStep?: number;
|
|
63
|
+
onStepChange?: (step: number) => void;
|
|
64
|
+
/** Fires when the last step's primary action resolves. */
|
|
65
|
+
onFinished?: () => void;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function Wizard({ children, step, defaultStep = 0, onStepChange, onFinished, className, ...rest }: WizardProps) {
|
|
69
|
+
const steps = React.Children.toArray(children).filter(React.isValidElement) as React.ReactElement<WizardStepProps>[];
|
|
70
|
+
const [internal, setInternal] = React.useState(defaultStep);
|
|
71
|
+
const current = step !== undefined ? step : internal;
|
|
72
|
+
const total = steps.length;
|
|
73
|
+
|
|
74
|
+
const goTo = React.useCallback((target: number) => {
|
|
75
|
+
const bounded = Math.max(0, Math.min(target, total - 1));
|
|
76
|
+
if (bounded === current) return;
|
|
77
|
+
onStepChange?.(bounded);
|
|
78
|
+
if (step === undefined) setInternal(bounded);
|
|
79
|
+
}, [current, onStepChange, step, total]);
|
|
80
|
+
|
|
81
|
+
const context = React.useMemo<WizardContextValue>(() => ({
|
|
82
|
+
current,
|
|
83
|
+
total,
|
|
84
|
+
goTo,
|
|
85
|
+
next: () => goTo(current + 1),
|
|
86
|
+
back: () => goTo(current - 1),
|
|
87
|
+
finish: () => onFinished?.(),
|
|
88
|
+
}), [current, goTo, onFinished, total]);
|
|
89
|
+
|
|
90
|
+
const titles = steps.map(child => child.props.title);
|
|
91
|
+
const activeTitle = titles[current] ?? "";
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<WizardContext.Provider value={context}>
|
|
95
|
+
<div className={cn("w-full", className)} {...rest}>
|
|
96
|
+
{/* Desktop rail. */}
|
|
97
|
+
<ol aria-label="Steps" className="hidden items-center gap-2 sm:flex">
|
|
98
|
+
{titles.map((title, index) => {
|
|
99
|
+
const state = index < current ? "done" : index === current ? "active" : "future";
|
|
100
|
+
const marker = (
|
|
101
|
+
<>
|
|
102
|
+
<span className={cn(
|
|
103
|
+
"flex size-6 shrink-0 items-center justify-center rounded-full text-xs font-medium",
|
|
104
|
+
state === "active" && "bg-primary text-primary-foreground",
|
|
105
|
+
state === "done" && "bg-primary/15 text-primary",
|
|
106
|
+
state === "future" && "bg-muted text-muted-foreground",
|
|
107
|
+
)}>
|
|
108
|
+
{state === "done" ? <Check className="size-3.5" aria-hidden /> : index + 1}
|
|
109
|
+
</span>
|
|
110
|
+
<span className={cn(
|
|
111
|
+
"text-sm",
|
|
112
|
+
state === "active" && "font-medium text-foreground",
|
|
113
|
+
state === "done" && "text-foreground",
|
|
114
|
+
state === "future" && "text-muted-foreground",
|
|
115
|
+
)}>{title}</span>
|
|
116
|
+
</>
|
|
117
|
+
);
|
|
118
|
+
return (
|
|
119
|
+
<React.Fragment key={`${index}-${title}`}>
|
|
120
|
+
{index > 0 && <li aria-hidden className="h-px flex-1 bg-border" />}
|
|
121
|
+
<li>
|
|
122
|
+
{state === "future"
|
|
123
|
+
? <span aria-disabled="true" className="flex items-center gap-2 p-1 opacity-70">{marker}</span>
|
|
124
|
+
: <button type="button" onClick={() => goTo(index)}
|
|
125
|
+
className="-m-1 flex items-center gap-2 rounded-md p-1 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring">
|
|
126
|
+
{marker}
|
|
127
|
+
</button>}
|
|
128
|
+
</li>
|
|
129
|
+
</React.Fragment>
|
|
130
|
+
);
|
|
131
|
+
})}
|
|
132
|
+
</ol>
|
|
133
|
+
{/* Mobile: position + progress, not a squeezed rail. */}
|
|
134
|
+
<div className="sm:hidden">
|
|
135
|
+
<p className="text-xs text-muted-foreground">
|
|
136
|
+
Step {current + 1} of {total} · <span className="font-medium text-foreground">{activeTitle}</span>
|
|
137
|
+
</p>
|
|
138
|
+
<div className="mt-2 h-1 overflow-hidden rounded-full bg-muted" role="progressbar"
|
|
139
|
+
aria-valuemin={1} aria-valuemax={total} aria-valuenow={current + 1} aria-label={`Step ${current + 1} of ${total}`}>
|
|
140
|
+
<div className="h-full rounded-full bg-primary transition-all" style={{ width: `${((current + 1) / total) * 100}%` }} />
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
{steps.map((child, index) => index === current
|
|
144
|
+
? <React.Fragment key={index}>{child}</React.Fragment>
|
|
145
|
+
: null)}
|
|
146
|
+
</div>
|
|
147
|
+
</WizardContext.Provider>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export interface WizardStepProps {
|
|
152
|
+
/** Shown in the rail (and the mobile position line). Steps read at a glance; keep it one or two words. */
|
|
153
|
+
title: string;
|
|
154
|
+
children: React.ReactNode;
|
|
155
|
+
/** The footer's primary action label. Omit when the step renders its own actions. */
|
|
156
|
+
nextLabel?: string;
|
|
157
|
+
/** Runs before advancing. Resolve (or return a non-false value) to advance; return false or throw to stay — the step's own content shows why. */
|
|
158
|
+
onNext?: () => Promise<unknown> | unknown;
|
|
159
|
+
/** Disables the primary action (a required choice is still missing). */
|
|
160
|
+
nextDisabled?: boolean;
|
|
161
|
+
/** Hides the Back button for steps where going back makes no sense (a completed handshake). */
|
|
162
|
+
hideBack?: boolean;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function WizardStep({ title, children, nextLabel, onNext, nextDisabled, hideBack }: WizardStepProps) {
|
|
166
|
+
const { current, total, goTo, back, finish } = useWizardContext("Wizard.Step");
|
|
167
|
+
const [busy, setBusy] = React.useState(false);
|
|
168
|
+
const isLast = current === total - 1;
|
|
169
|
+
|
|
170
|
+
async function runNext() {
|
|
171
|
+
if (!onNext) {
|
|
172
|
+
if (isLast) finish(); else goTo(current + 1);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
setBusy(true);
|
|
176
|
+
try {
|
|
177
|
+
const result = await onNext();
|
|
178
|
+
if (result === false) return;
|
|
179
|
+
if (isLast) finish(); else goTo(current + 1);
|
|
180
|
+
} catch {
|
|
181
|
+
// Stay on the step; its content owns the error presentation. A rejected
|
|
182
|
+
// promise here must not surface as an unhandled flow crash.
|
|
183
|
+
} finally {
|
|
184
|
+
setBusy(false);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
<section aria-label={title} className="pt-6">
|
|
190
|
+
{children}
|
|
191
|
+
{(nextLabel || !hideBack) && (
|
|
192
|
+
<div className="mt-6 flex items-center justify-between gap-2 border-t border-border pt-4">
|
|
193
|
+
{!hideBack && current > 0
|
|
194
|
+
? <Button type="button" variant="ghost" onClick={back}><ChevronLeft className="size-4" aria-hidden /> Back</Button>
|
|
195
|
+
: <span />}
|
|
196
|
+
{nextLabel && (
|
|
197
|
+
<Button type="button" className="w-full sm:w-auto" onClick={() => void runNext()} disabled={nextDisabled} loading={busy}>
|
|
198
|
+
{nextLabel}
|
|
199
|
+
</Button>
|
|
200
|
+
)}
|
|
201
|
+
</div>
|
|
202
|
+
)}
|
|
203
|
+
</section>
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Dot-notation composition (`<Wizard.Step>`), attached after both definitions
|
|
208
|
+
// so each keeps a plain, tree-shakeable named export.
|
|
209
|
+
export interface Wizard extends React.FC<WizardProps> {
|
|
210
|
+
Step: typeof WizardStep;
|
|
211
|
+
}
|
|
212
|
+
(Wizard as Wizard).Step = WizardStep;
|