@sableui/react 0.2.0 → 0.3.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/AGENTS.md +3 -1
- package/CHANGELOG.md +84 -0
- package/CONTRIBUTING.md +8 -0
- package/README.md +1 -0
- package/bin/sable-lint.mjs +0 -0
- package/bin/sable.mjs +68 -26
- package/dist/blocks/index.d.ts +396 -0
- package/dist/blocks/index.js +1997 -0
- package/dist/chunk-6LCIBMMG.js +4908 -0
- package/dist/{chunk-AOWBYEQ6.js → chunk-CKNM7KSB.js} +1598 -6464
- package/dist/{chunk-PAOZBZME.js → chunk-NGBDP3BM.js} +1 -1
- package/dist/{chunk-5Z5KLOAU.js → chunk-TH563WLW.js} +14 -1
- package/dist/chunk-WCFLQVK5.js +5097 -0
- package/dist/icon/index.js +2 -2
- package/dist/icon-sets/reicon.js +1 -1
- package/dist/index.d.ts +471 -34
- package/dist/index.js +588 -2589
- package/dist/ios/index.d.ts +8 -1
- package/dist/ios/index.js +6 -5
- package/dist/ios/preview.d.ts +11 -1
- package/dist/ios/preview.js +13 -8
- package/dist/ios/styles.css +14 -0
- package/dist/{meta-6dUzgC6M.d.ts → meta-CcE_SeFu.d.ts} +37 -1
- package/dist/patterns/index.d.ts +1 -1
- package/dist/patterns/index.js +29 -30
- package/dist/presets/index.js +10 -1
- package/dist/themes/base/base.css +23 -0
- package/dist/themes/index.js +2 -2
- package/llms/blocks/api-keys-block.md +79 -0
- package/llms/blocks/chat-thread-block.md +51 -0
- package/llms/blocks/command-palette-block.md +48 -0
- package/llms/blocks/cta-band-block.md +62 -0
- package/llms/blocks/danger-zone-block.md +69 -0
- package/llms/blocks/device-comparison-block.md +91 -0
- package/llms/blocks/event-card-block.md +84 -0
- package/llms/blocks/faq-block.md +72 -0
- package/llms/blocks/feature-grid-block.md +75 -0
- package/llms/blocks/feedback-form-block.md +66 -0
- package/llms/blocks/goal-progress-block.md +54 -0
- package/llms/blocks/hero-block.md +65 -0
- package/llms/blocks/invite-team-block.md +61 -0
- package/llms/blocks/invoice-history-block.md +84 -0
- package/llms/blocks/kpi-sparkline-block.md +57 -0
- package/llms/blocks/media-player-block.md +100 -0
- package/llms/blocks/newsletter-block.md +64 -0
- package/llms/blocks/not-found-block.md +56 -0
- package/llms/blocks/notification-list-block.md +63 -0
- package/llms/blocks/notification-settings-block.md +54 -0
- package/llms/blocks/order-tracking-block.md +62 -0
- package/llms/blocks/pricing-card-block.md +82 -0
- package/llms/blocks/pricing-table-block.md +129 -0
- package/llms/blocks/product-card-block.md +69 -0
- package/llms/blocks/profile-card-block.md +103 -0
- package/llms/blocks/profile-form-block.md +114 -0
- package/llms/blocks/revenue-breakdown-block.md +70 -0
- package/llms/blocks/sign-in-block.md +64 -0
- package/llms/blocks/sign-up-block.md +78 -0
- package/llms/blocks/site-footer-block.md +96 -0
- package/llms/blocks/stat-row-block.md +59 -0
- package/llms/blocks/steps-block.md +73 -0
- package/llms/blocks/task-list-block.md +81 -0
- package/llms/blocks/team-members-block.md +91 -0
- package/llms/blocks/testimonial-block.md +78 -0
- package/llms/blocks/traffic-sources-block.md +63 -0
- package/llms/blocks/traffic-trend-block.md +95 -0
- package/llms/blocks/upload-list-block.md +66 -0
- package/llms/blocks/verify-code-block.md +59 -0
- package/llms/blocks/wallet-block.md +84 -0
- package/llms/components/area-chart.md +112 -0
- package/llms/components/bar-chart.md +131 -0
- package/llms/components/button.md +1 -1
- package/llms/components/card.md +2 -2
- package/llms/components/context-menu.md +1 -1
- package/llms/components/date-tile.md +79 -0
- package/llms/components/entity.md +4 -2
- package/llms/components/line-chart.md +101 -0
- package/llms/components/menu.md +1 -1
- package/llms/components/{dots-menu.md → overflow-menu.md} +9 -9
- package/llms/components/pie-chart.md +103 -0
- package/llms/components/radar-chart.md +106 -0
- package/llms/components/radial-chart.md +102 -0
- package/llms/components/segmented-bar.md +102 -0
- package/llms/components/settings-menu.md +2 -2
- package/llms/components/sparkline.md +80 -0
- package/llms/components/table.md +1 -1
- package/llms/patterns/auth-page.md +2 -2
- package/llms/patterns/dashboard-page.md +6 -7
- package/llms/patterns/feed-page.md +7 -7
- package/llms/patterns/list-detail-page.md +7 -15
- package/llms/patterns/settings-page.md +10 -21
- package/llms/tasks/building-an-ios-prototype.md +4 -1
- package/llms/tasks/choosing-a-component.md +2 -2
- package/llms-full.txt +4296 -277
- package/llms.txt +55 -3
- package/package.json +5 -1
- package/registry.json +6383 -3741
- package/targets/ios.json +90 -9
- package/dist/{chunk-L6Y46C62.js → chunk-MXJX3NZI.js} +1 -1
package/dist/index.js
CHANGED
|
@@ -1,31 +1,30 @@
|
|
|
1
|
+
export { DENSITY_TOKENS, MOTION_TOKENS, RADIUS_TOKENS, SEMANTIC_TOKENS, TYPE_OVERLINE_TOKENS, TYPE_REFINEMENT_TOKENS, TYPE_SCALE_TOKENS, TYPE_UI_TOKENS, WORKSPACE_SURFACE_TOKENS } from './chunk-X276MF2W.js';
|
|
1
2
|
export { Phone, phoneMeta } from './chunk-KFZ43RKU.js';
|
|
2
3
|
import './chunk-NEQNXI2G.js';
|
|
3
|
-
export {
|
|
4
|
-
import {
|
|
5
|
-
export {
|
|
4
|
+
export { AppHeader, AppShell, BottomNav, BottomNavItem, Browser, ChatComposer, DataTable, EmptyState, Marker, MessageScroller, Meter, MeterIndicator, MeterLabel, MeterTrack, MeterValue, NotificationBadge, PageHeader, Presence, RelativeTime, Reveal, SearchInput, SettingsMenu, SettingsRow, SideNav, SideNavGroup, SideNavItem, Sidebar, SidebarFooter, SidebarHeader, SidebarItem, SidebarNav, Skeleton, StreamingText, Tabs, TabsPanel as TabsContent, TabsList, TabsPanel, TabsTrigger, TextReveal, Toolbar, ToolbarButton, ToolbarGroup, ToolbarInput, ToolbarLink, ToolbarSeparator, ViewTransitionName, appHeaderMeta, appShellMeta, bottomNavMeta, browserMeta, chatComposerMeta, dataTableMeta, emptyStateMeta, markerMeta, messageScrollerMeta, meterIndicatorVariants, meterMeta, meterTrackVariants, notificationBadgeMeta, notificationCountVariants, notificationDotVariants, pageHeaderMeta, relativeTimeMeta, searchInputMeta, settingsMenuMeta, sideNavMeta, sidebarMeta, skeletonMeta, startViewTransition, streamingTextMeta, supportsViewTransitions, tabsMeta, toolbarGroupVariants, toolbarMeta, toolbarSeparatorVariants, toolbarVariants, useInView, useSidebar } from './chunk-6LCIBMMG.js';
|
|
5
|
+
import { SELECTION_CARD_CHECKED, CartesianChart, resolveSeries, chartFormatters, categoryText, numberValue, niceTicks, ChartFrame, polar, riseTransition, stagger, pathStyle, morphTransition, ChartLegend, RADIO_DOT, RADIO_RING, SELECTION_CARD_TILE_HEAD, SELECTION_CARD_GLYPH, SELECTION_CARD_SHELL, SELECTION_CARD_LAYOUT, CheckboxMark, CHECK_BOX, SEGMENTED_STEP, SEGMENTED_ITEM, SEGMENTED_TRACK, ToggleGroup, Toggle, approxTextWidth, ChartTooltipContent } from './chunk-WCFLQVK5.js';
|
|
6
|
+
export { Accordion, AccordionItem, AccordionPanel, AccordionTrigger, AreaChart, BarChart, CHART_SERIES_COUNT, Checkbox, CommandMenu, CommandMenuGroup, CommandMenuItem, CommandMenuPanel, CommandMenuTrigger, DateTile, Kbd, KbdGroup, OtpField, PieChart, RadialChart, SEGMENTED_BAR_TONES, Scroller, SegmentedBar, Slider, SliderControl, SliderIndicator, SliderRoot, SliderThumb, SliderTrack, SliderValue, Sparkline, Stepper, StepperItem, Toggle, ToggleGroup, Toggle as ToggleGroupItem, accordionMeta, areaChartMeta, barChartMeta, chartColor, toneColor as chartToneColor, checkboxMeta, commandMenuMeta, dateTileMeta, kbdMeta, otpFieldMeta, pieChartMeta, radialChartMeta, segmentedBarMeta, segmentedBarSegmentVariants, segmentedBarVariants, sliderMeta, sparklineMeta, stepperConnectorVariants, stepperIndicatorVariants, stepperMeta, toggleMeta, useCommandMenuShortcut } from './chunk-WCFLQVK5.js';
|
|
7
|
+
import { withPortalThemeScope, FIELD_SHELL_XS, useTypeTier, useDensity, FIELD_SHELL, MOBILE_FIELD_TIER, useMotion, HighlightGlide, useAnchoredDensity, anchoredScroll, useIsomorphicLayoutEffect, mergeRefs, buttonVariants, FIELD_INVALID, Select, SelectTrigger, SelectContent, SelectItem, Button, Spinner, Badge, anchoredListScroll, useResizeObserver, MENU_ITEM_DESTRUCTIVE, DensityProvider, Input, Textarea, Progress } from './chunk-CKNM7KSB.js';
|
|
8
|
+
export { AlertDialog, AlertDialogClose, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogTitle, AlertDialogTrigger, Attachment, Avatar, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarImage, AvatarRoot, BADGE_APPEARANCES, BADGE_TONES, Badge, Bubble, BubbleReaction, BubbleReactions, Button, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardList, CardTitle, Code, CodeBlock, CodeHighlightProvider, Density, DensityProvider, DestructiveActionModal, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Entity, Field, Form, Input, Label, Menu, MenuCheckboxItem, MenuContent, MenuGroup, MenuGroupLabel, MenuItem, MenuPortal, MenuRadioGroup, MenuRadioItem, MenuSeparator, MenuSubmenu, MenuSubmenuTrigger, MenuTrigger, Message, MotionProvider, OverflowMenu, Pill, Progress, ProgressIndicator, ProgressLabel, ProgressTrack, ProgressValue, Select, SelectContent, SelectGroup, SelectGroupLabel, SelectItem, SelectPortal, SelectSeparator, SelectTrigger, SelectValue, Separator, SimpleTooltip, Spinner, StatusDot, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Textarea, Tooltip, TooltipArrow, TooltipContent, TooltipPortal, TooltipProvider, TooltipTrigger, attachmentMeta, avatarGroupVariants, avatarMeta, avatarVariants, badgeMeta, badgeVariants, bubbleMeta, buttonMeta, buttonVariants, cardMeta, codeBlockMeta, codeMeta, destructiveActionModalMeta, dialogMeta, entityMeta, formMeta, inputMeta, labelMeta, menuMeta, messageMeta, overflowMenuMeta, pillVariants, progressMeta, progressTrackVariants, selectMeta, separatorMeta, spinnerMeta, statusDotMeta, statusDotVariants, switchMeta, tableMeta, textareaMeta, tooltipMeta, useAnchoredDensity, useCopy, useDensity, useMotion, useReducedMotion, useScrollActivity, useTypeTier } from './chunk-CKNM7KSB.js';
|
|
6
9
|
export { AspectBox, Bleed, Center, Col, Columns, Grid, GridOverlay, Page, Region, Row, SafeArea, Spacer, SplitPane, Stack } from './chunk-3IWZYBGH.js';
|
|
7
10
|
import { cn } from './chunk-SRQWQUTW.js';
|
|
8
11
|
export { cn } from './chunk-SRQWQUTW.js';
|
|
9
|
-
import { Icon } from './chunk-
|
|
10
|
-
export { Icon, IconProvider, composeIconSets, useIconSet } from './chunk-
|
|
11
|
-
export { reiconIconSet as fallbackIconSet, reiconIconNames, reiconIconSet } from './chunk-
|
|
12
|
-
import { featuredThemeManifests, defaultThemeId } from './chunk-
|
|
13
|
-
export { DEFAULT_THEME_ID, SABLE_COLOR_STORAGE_KEY, SABLE_THEME_ATTRIBUTE, SABLE_THEME_STORAGE_KEY, SableThemeProvider, applySableColorMode, applySableTheme, defaultThemeId, getThemeManifest, isThemeId, themeIds, themeManifestById, themeManifests, useSableTheme } from './chunk-
|
|
12
|
+
import { Icon } from './chunk-NGBDP3BM.js';
|
|
13
|
+
export { Icon, IconProvider, composeIconSets, useIconSet } from './chunk-NGBDP3BM.js';
|
|
14
|
+
export { reiconIconSet as fallbackIconSet, reiconIconNames, reiconIconSet } from './chunk-TH563WLW.js';
|
|
15
|
+
import { featuredThemeManifests, defaultThemeId } from './chunk-MXJX3NZI.js';
|
|
16
|
+
export { DEFAULT_THEME_ID, SABLE_COLOR_STORAGE_KEY, SABLE_THEME_ATTRIBUTE, SABLE_THEME_STORAGE_KEY, SableThemeProvider, applySableColorMode, applySableTheme, defaultThemeId, getThemeManifest, isThemeId, themeIds, themeManifestById, themeManifests, useSableTheme } from './chunk-MXJX3NZI.js';
|
|
17
|
+
import './chunk-7MG4SHYX.js';
|
|
14
18
|
import './chunk-KO3NAEBF.js';
|
|
15
19
|
import './chunk-L4IEIQQA.js';
|
|
16
20
|
import './chunk-EP55KSYA.js';
|
|
17
21
|
import './chunk-ADLLDWLW.js';
|
|
18
22
|
import './chunk-K7RUHUGC.js';
|
|
19
23
|
import './chunk-YXBOYWBN.js';
|
|
20
|
-
import './chunk-7MG4SHYX.js';
|
|
21
24
|
import { NumberField as NumberField$1 } from '@base-ui/react/number-field';
|
|
22
|
-
import { jsx, jsxs, Fragment
|
|
23
|
-
import
|
|
24
|
-
import { createContext, Fragment, useId, useRef, useState, useEffect, useLayoutEffect, Children, isValidElement, useContext, useMemo, useCallback, useSyncExternalStore } from 'react';
|
|
25
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
26
|
+
import { createContext, useId, useRef, useState, useEffect, useLayoutEffect, useContext, useMemo, useCallback, useSyncExternalStore, Children } from 'react';
|
|
25
27
|
import { cva } from 'class-variance-authority';
|
|
26
|
-
import { Checkbox as Checkbox$1 } from '@base-ui/react/checkbox';
|
|
27
|
-
import { Toggle as Toggle$1 } from '@base-ui/react/toggle';
|
|
28
|
-
import { ToggleGroup as ToggleGroup$1 } from '@base-ui/react/toggle-group';
|
|
29
28
|
import { Radio as Radio$1 } from '@base-ui/react/radio';
|
|
30
29
|
import { RadioGroup as RadioGroup$1 } from '@base-ui/react/radio-group';
|
|
31
30
|
import { Popover as Popover$1 } from '@base-ui/react/popover';
|
|
@@ -33,12 +32,11 @@ import { Drawer } from '@base-ui/react/drawer';
|
|
|
33
32
|
import { DayPicker } from 'react-day-picker';
|
|
34
33
|
import { format, setHours, setMinutes, setMilliseconds, setSeconds } from 'date-fns';
|
|
35
34
|
import { Toast } from '@base-ui/react/toast';
|
|
36
|
-
import { Accordion as Accordion$1 } from '@base-ui/react/accordion';
|
|
37
35
|
import { Collapsible as Collapsible$1 } from '@base-ui/react/collapsible';
|
|
38
|
-
import { Slider as Slider$1 } from '@base-ui/react/slider';
|
|
39
36
|
import { Combobox as Combobox$1 } from '@base-ui/react/combobox';
|
|
40
37
|
import { ContextMenu as ContextMenu$1 } from '@base-ui/react/context-menu';
|
|
41
38
|
import { Fieldset as Fieldset$1 } from '@base-ui/react/fieldset';
|
|
39
|
+
import { Checkbox } from '@base-ui/react/checkbox';
|
|
42
40
|
import { CheckboxGroup as CheckboxGroup$1 } from '@base-ui/react/checkbox-group';
|
|
43
41
|
import { PreviewCard } from '@base-ui/react/preview-card';
|
|
44
42
|
|
|
@@ -251,6 +249,7 @@ var componentGuidance = {
|
|
|
251
249
|
var COMPONENT_GROUPS = [
|
|
252
250
|
"Actions",
|
|
253
251
|
"Badges & pills",
|
|
252
|
+
"Charts",
|
|
254
253
|
"Chat & AI",
|
|
255
254
|
"Containers",
|
|
256
255
|
"Data display",
|
|
@@ -266,13 +265,13 @@ var COMPONENT_GROUPS = [
|
|
|
266
265
|
];
|
|
267
266
|
var COMPONENT_TAXONOMY = {
|
|
268
267
|
/* ── Actions ──────────────────────────────────────────────────────────────
|
|
269
|
-
Things whose whole purpose is to DO something on activation. `
|
|
268
|
+
Things whose whole purpose is to DO something on activation. `OverflowMenu`
|
|
270
269
|
is here, not in Overlays: it is the "more actions" button a reader reaches
|
|
271
270
|
for as an action (and the options half of Button's "main action with
|
|
272
271
|
options" pattern) — that its list opens in a layer is how it works, not
|
|
273
272
|
what it is. Copying is `useCopy` on a Button, not a component. */
|
|
274
273
|
Button: ["Actions", "either"],
|
|
275
|
-
|
|
274
|
+
OverflowMenu: ["Actions", "either"],
|
|
276
275
|
Toolbar: ["Actions", "either"],
|
|
277
276
|
/* ── Forms ────────────────────────────────────────────────────────────────
|
|
278
277
|
Typing or entering a value, and the structure a form is built from.
|
|
@@ -354,15 +353,30 @@ var COMPONENT_TAXONOMY = {
|
|
|
354
353
|
Collapsible: ["Containers", "either"],
|
|
355
354
|
Scroller: ["Containers", "either"],
|
|
356
355
|
Separator: ["Containers", "either"],
|
|
356
|
+
/* ── Charts ───────────────────────────────────────────────────────────────
|
|
357
|
+
Plotted data for dashboards, landing pages and decks: SVG on the theme's
|
|
358
|
+
tones, with arrival and data-change motion and a data table for assistive
|
|
359
|
+
tech. Data-heavy apps (streaming, zoom, thousands of points) belong on a
|
|
360
|
+
charting library. `SegmentedBar` and `Meter` stay in Data display: they
|
|
361
|
+
are readings, not plots. */
|
|
362
|
+
AreaChart: ["Charts", "page"],
|
|
363
|
+
BarChart: ["Charts", "page"],
|
|
364
|
+
LineChart: ["Charts", "page"],
|
|
365
|
+
PieChart: ["Charts", "page"],
|
|
366
|
+
RadarChart: ["Charts", "page"],
|
|
367
|
+
RadialChart: ["Charts", "page"],
|
|
368
|
+
Sparkline: ["Charts", "either"],
|
|
357
369
|
/* ── Data display ─────────────────────────────────────────────────────────
|
|
358
370
|
Showing records and readings. `Meter` (bar or ring) is here: it shows a
|
|
359
371
|
value within a known range (a reading), which is not loading or progress. */
|
|
360
372
|
Avatar: ["Data display", "either"],
|
|
361
373
|
DataTable: ["Data display", "either"],
|
|
374
|
+
DateTile: ["Data display", "page"],
|
|
362
375
|
Entity: ["Data display", "either"],
|
|
363
376
|
GlidingHighlight: ["Data display", "either"],
|
|
364
377
|
Meter: ["Data display", "either"],
|
|
365
378
|
RelativeTime: ["Data display", "either"],
|
|
379
|
+
SegmentedBar: ["Data display", "either"],
|
|
366
380
|
Table: ["Data display", "either"],
|
|
367
381
|
/* ── Badges & pills ───────────────────────────────────────────────────────
|
|
368
382
|
Small labelled shapes. `Badge` (with `dot`) / `StatusDot` belong HERE, not in
|
|
@@ -598,255 +612,6 @@ var numberFieldMeta = {
|
|
|
598
612
|
}
|
|
599
613
|
]
|
|
600
614
|
};
|
|
601
|
-
var otpExports = Object.fromEntries(Object.entries(OtpParts));
|
|
602
|
-
var Base2 = otpExports.OTPField ?? otpExports.OTPFieldPreview;
|
|
603
|
-
var defaultSlotLabel = (index, length) => `Character ${index + 1} of ${length}`;
|
|
604
|
-
function OtpField({
|
|
605
|
-
className,
|
|
606
|
-
length = 6,
|
|
607
|
-
groupSize,
|
|
608
|
-
getSlotLabel = defaultSlotLabel,
|
|
609
|
-
"aria-invalid": ariaInvalid,
|
|
610
|
-
...root
|
|
611
|
-
}) {
|
|
612
|
-
const invalid = ariaInvalid === true || ariaInvalid === "true";
|
|
613
|
-
return /* @__PURE__ */ jsx(
|
|
614
|
-
Base2.Root,
|
|
615
|
-
{
|
|
616
|
-
length,
|
|
617
|
-
"data-invalid": invalid ? "" : void 0,
|
|
618
|
-
className: cn("inline-flex max-w-full items-center gap-2", className),
|
|
619
|
-
...root,
|
|
620
|
-
children: Array.from({ length }, (_, index) => (
|
|
621
|
-
// Slots are positional and never reorder, so the index is the identity.
|
|
622
|
-
// biome-ignore lint/suspicious/noArrayIndexKey: positional slots
|
|
623
|
-
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
624
|
-
groupSize && index > 0 && index % groupSize === 0 ? /* @__PURE__ */ jsx(
|
|
625
|
-
"span",
|
|
626
|
-
{
|
|
627
|
-
"aria-hidden": "true",
|
|
628
|
-
"data-otp-gap": "",
|
|
629
|
-
className: "h-px w-2 shrink-0 bg-border-strong"
|
|
630
|
-
}
|
|
631
|
-
) : null,
|
|
632
|
-
/* @__PURE__ */ jsx(
|
|
633
|
-
Base2.Input,
|
|
634
|
-
{
|
|
635
|
-
"aria-label": index === 0 ? void 0 : getSlotLabel(index, length),
|
|
636
|
-
"aria-invalid": invalid || void 0,
|
|
637
|
-
className: cn(
|
|
638
|
-
"h-(--control-lg) w-(--control-md) min-w-0 text-center text-mono-lg text-foreground caret-primary",
|
|
639
|
-
// The shell Input wears — lib/field-shell — so a slot reads as
|
|
640
|
-
// the same field; only the slot-specific states are added here.
|
|
641
|
-
FIELD_SHELL,
|
|
642
|
-
// Focus is FIELD_SHELL's ring alone, like every other field — a ring-
|
|
643
|
-
// coloured border under it made a 3px edge only here.
|
|
644
|
-
"data-[filled]:border-border-strong",
|
|
645
|
-
FIELD_INVALID
|
|
646
|
-
)
|
|
647
|
-
}
|
|
648
|
-
)
|
|
649
|
-
] }, index)
|
|
650
|
-
))
|
|
651
|
-
}
|
|
652
|
-
);
|
|
653
|
-
}
|
|
654
|
-
OtpField.displayName = "OtpField";
|
|
655
|
-
|
|
656
|
-
// src/components/otp-field/otp-field.meta.ts
|
|
657
|
-
var otpFieldMeta = {
|
|
658
|
-
name: "OtpField",
|
|
659
|
-
summary: "One-time-code entry over Base UI's OTP Field \u2014 one slot per character, SMS autofill on the first slot, paste fills every slot, and only one tab stop for the whole code.",
|
|
660
|
-
status: "stable",
|
|
661
|
-
keywords: [
|
|
662
|
-
"otp",
|
|
663
|
-
"one-time code",
|
|
664
|
-
"one-time password",
|
|
665
|
-
"verification code",
|
|
666
|
-
"2fa",
|
|
667
|
-
"two-factor",
|
|
668
|
-
"mfa",
|
|
669
|
-
"pin",
|
|
670
|
-
"passcode",
|
|
671
|
-
"input otp"
|
|
672
|
-
],
|
|
673
|
-
usage: {
|
|
674
|
-
description: "Use OtpField for a short, fixed-length code the user copies from somewhere else \u2014 an SMS or email verification code, an authenticator app, a device pairing PIN. The slots show how long the code is and where the user is in it, which a plain Input cannot.",
|
|
675
|
-
bestPractices: [
|
|
676
|
-
{
|
|
677
|
-
guidance: "do",
|
|
678
|
-
description: "Set `length` to the exact length the server sends, and verify in `onValueComplete` (or with `autoSubmit` inside a form). Most users paste or autofill the whole code at once, so the moment of completion is the natural submit."
|
|
679
|
-
},
|
|
680
|
-
{
|
|
681
|
-
guidance: "do",
|
|
682
|
-
description: 'Label it through a `Field` ("Verification code") and say where the code was sent in the description. The first slot takes the field\'s name; the rest are announced by position.'
|
|
683
|
-
},
|
|
684
|
-
{
|
|
685
|
-
guidance: "do",
|
|
686
|
-
description: "Use `groupSize` for codes of 6 or more so the user can check the code in chunks against the message they are reading from."
|
|
687
|
-
},
|
|
688
|
-
{
|
|
689
|
-
guidance: "dont",
|
|
690
|
-
description: 'Do not use OtpField for a password, an arbitrary-length PIN, or anything the user types from memory. Slots promise a fixed length; use Input with `type="password"` and `autocomplete="current-password"` instead.'
|
|
691
|
-
},
|
|
692
|
-
{
|
|
693
|
-
guidance: "dont",
|
|
694
|
-
description: "Do not intercept paste or clear the field on a wrong code before the user has seen the error. Blocking paste breaks SMS autofill and password managers, and wiping the slots erases the evidence of which digit was wrong."
|
|
695
|
-
},
|
|
696
|
-
{
|
|
697
|
-
guidance: "dont",
|
|
698
|
-
description: "Do not swap it for NumberField because the code is digits. A code is an identifier, not a quantity: NumberField strips leading zeros and offers steppers that make no sense here."
|
|
699
|
-
}
|
|
700
|
-
],
|
|
701
|
-
anatomy: [
|
|
702
|
-
{
|
|
703
|
-
name: "Root",
|
|
704
|
-
required: true,
|
|
705
|
-
description: "A role=group that holds the value, carries the field label and description, and reports completion."
|
|
706
|
-
},
|
|
707
|
-
{
|
|
708
|
-
name: "Slot",
|
|
709
|
-
required: true,
|
|
710
|
-
description: 'One input per character. The first carries autocomplete="one-time-code" and accepts the whole code.'
|
|
711
|
-
},
|
|
712
|
-
{
|
|
713
|
-
name: "Group gap",
|
|
714
|
-
required: false,
|
|
715
|
-
description: "A decorative dash inserted every `groupSize` slots; hidden from assistive tech."
|
|
716
|
-
}
|
|
717
|
-
]
|
|
718
|
-
},
|
|
719
|
-
related: {
|
|
720
|
-
components: ["Input", "Form", "NumberField"]
|
|
721
|
-
},
|
|
722
|
-
props: [
|
|
723
|
-
{
|
|
724
|
-
name: "length",
|
|
725
|
-
type: "number",
|
|
726
|
-
default: "6",
|
|
727
|
-
description: "Number of character slots. Completion fires when every slot is filled."
|
|
728
|
-
},
|
|
729
|
-
{
|
|
730
|
-
name: "groupSize",
|
|
731
|
-
type: "number",
|
|
732
|
-
description: "Insert a visual gap every N slots (3 renders 123 \u2013 456). The value stays one contiguous string."
|
|
733
|
-
},
|
|
734
|
-
{
|
|
735
|
-
name: "value / defaultValue",
|
|
736
|
-
type: "string",
|
|
737
|
-
description: "Controlled / uncontrolled code."
|
|
738
|
-
},
|
|
739
|
-
{
|
|
740
|
-
name: "onValueChange",
|
|
741
|
-
type: "(value: string, details) => void",
|
|
742
|
-
description: "Fires on every edit. `details.reason` is input-change, input-clear, input-paste or keyboard."
|
|
743
|
-
},
|
|
744
|
-
{
|
|
745
|
-
name: "onValueComplete",
|
|
746
|
-
type: "(value: string, details) => void",
|
|
747
|
-
description: "Fires once every slot is filled \u2014 the place to verify the code. Also fires when a complete code is pasted over a complete value."
|
|
748
|
-
},
|
|
749
|
-
{
|
|
750
|
-
name: "onValueInvalid",
|
|
751
|
-
type: "(attempted: string, details) => void",
|
|
752
|
-
description: "Fires when typed or pasted text contains characters `validationType` rejects, so you can explain the rejection rather than letting it vanish."
|
|
753
|
-
},
|
|
754
|
-
{
|
|
755
|
-
name: "validationType",
|
|
756
|
-
type: '"numeric" | "alpha" | "alphanumeric" | "none"',
|
|
757
|
-
default: '"numeric"',
|
|
758
|
-
description: "Which characters a slot accepts. Also picks the virtual keyboard (numeric \u2192 number pad)."
|
|
759
|
-
},
|
|
760
|
-
{
|
|
761
|
-
name: "autoSubmit",
|
|
762
|
-
type: "boolean",
|
|
763
|
-
default: "false",
|
|
764
|
-
description: "Submit the owning form when the code becomes complete."
|
|
765
|
-
},
|
|
766
|
-
{
|
|
767
|
-
name: "mask",
|
|
768
|
-
type: "boolean",
|
|
769
|
-
default: "false",
|
|
770
|
-
description: "Render slots as password inputs. Only for codes that are secret on screen, such as a card PIN."
|
|
771
|
-
},
|
|
772
|
-
{
|
|
773
|
-
name: "getSlotLabel",
|
|
774
|
-
type: "(index: number, length: number) => string",
|
|
775
|
-
default: "Character N of L",
|
|
776
|
-
description: "Accessible name for slots after the first. Override to localize."
|
|
777
|
-
},
|
|
778
|
-
{
|
|
779
|
-
name: "aria-invalid",
|
|
780
|
-
type: "boolean",
|
|
781
|
-
description: "Marks every slot invalid (destructive border). `<Field error>` sets it for you."
|
|
782
|
-
},
|
|
783
|
-
{
|
|
784
|
-
name: "disabled / readOnly / required / name / id / form",
|
|
785
|
-
type: "\u2014",
|
|
786
|
-
description: "Standard form-control props. `id` lands on the first slot, so `<Field>` and `<label htmlFor>` name the field."
|
|
787
|
-
}
|
|
788
|
-
],
|
|
789
|
-
tokensUsed: [
|
|
790
|
-
"input",
|
|
791
|
-
"card",
|
|
792
|
-
"foreground",
|
|
793
|
-
"muted-foreground",
|
|
794
|
-
"border-strong",
|
|
795
|
-
"primary",
|
|
796
|
-
"ring",
|
|
797
|
-
"destructive"
|
|
798
|
-
],
|
|
799
|
-
motion: [
|
|
800
|
-
{
|
|
801
|
-
slot: "arrival",
|
|
802
|
-
recipe: "none",
|
|
803
|
-
part: "Root",
|
|
804
|
-
why: "A code field is part of the form it sits in and appears with it. Animating it in would delay the one thing the user arrived to do \u2014 paste the code they just copied."
|
|
805
|
-
},
|
|
806
|
-
{
|
|
807
|
-
slot: "response",
|
|
808
|
-
recipe: "motion-interactive",
|
|
809
|
-
part: "Slot",
|
|
810
|
-
why: "Focus moves between slots on every keystroke. The eased ring and border are what let the eye follow the cursor across six identical boxes instead of hunting for it after each digit."
|
|
811
|
-
},
|
|
812
|
-
{
|
|
813
|
-
slot: "transition",
|
|
814
|
-
recipe: "motion-interactive",
|
|
815
|
-
part: "Slot",
|
|
816
|
-
why: "A filled slot strengthens its border, so a pasted code visibly lands in every slot at once. Easing it reads as the code arriving rather than the field flickering."
|
|
817
|
-
},
|
|
818
|
-
{
|
|
819
|
-
slot: "activity",
|
|
820
|
-
recipe: "none",
|
|
821
|
-
part: "Root",
|
|
822
|
-
why: "Verifying the code is the form's work, not the field's. Show it on the submit Button's `loading` state, where the user expects the answer to appear."
|
|
823
|
-
}
|
|
824
|
-
],
|
|
825
|
-
a11y: [
|
|
826
|
-
'The root is a role=group named by the field label; the first slot shares the label through `id`/`htmlFor`, and later slots are announced by position ("Character 2 of 6").',
|
|
827
|
-
"Only one slot is in the tab order. Arrow keys, Home/End and Backspace move between slots; Tab leaves the whole field.",
|
|
828
|
-
'The first slot carries autocomplete="one-time-code" and the numeric virtual keyboard, so iOS and Android offer the SMS code above the keyboard.',
|
|
829
|
-
"Slots render at 20px (`text-mono-lg`), above the 16px floor that stops iOS Safari zooming on focus."
|
|
830
|
-
],
|
|
831
|
-
examples: [
|
|
832
|
-
{
|
|
833
|
-
label: "Six digits",
|
|
834
|
-
code: '<Field label="Verification code"><OtpField length={6} onValueComplete={verify} /></Field>'
|
|
835
|
-
},
|
|
836
|
-
{
|
|
837
|
-
label: "In a field, grouped",
|
|
838
|
-
code: '<Field label="Verification code" description="Enter the 6-digit code we sent to your phone."><OtpField length={6} groupSize={3} /></Field>'
|
|
839
|
-
},
|
|
840
|
-
{
|
|
841
|
-
label: "With an error",
|
|
842
|
-
code: '<Field label="Verification code" error="That code has expired. Request a new code and try again."><OtpField length={6} groupSize={3} defaultValue="482913" /></Field>'
|
|
843
|
-
},
|
|
844
|
-
{
|
|
845
|
-
label: "Alphanumeric",
|
|
846
|
-
code: '<Field label="Pairing code"><OtpField length={8} groupSize={4} validationType="alphanumeric" /></Field>'
|
|
847
|
-
}
|
|
848
|
-
]
|
|
849
|
-
};
|
|
850
615
|
var fileDropzoneVariants = cva(
|
|
851
616
|
[
|
|
852
617
|
"group flex w-full flex-col items-center justify-center gap-2 rounded-md border border-dashed border-border-strong bg-card text-center text-foreground motion-interactive",
|
|
@@ -1005,7 +770,7 @@ function FileDropzone({
|
|
|
1005
770
|
}
|
|
1006
771
|
),
|
|
1007
772
|
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-col gap-0.5", children: [
|
|
1008
|
-
/* @__PURE__ */ jsx("span", { className: tier.control, children: label ?? /* @__PURE__ */ jsxs(Fragment
|
|
773
|
+
/* @__PURE__ */ jsx("span", { className: tier.control, children: label ?? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1009
774
|
multiple ? "Drop files here or " : "Drop a file here or ",
|
|
1010
775
|
/* @__PURE__ */ jsx("span", { className: "text-primary underline underline-offset-2", children: "browse" })
|
|
1011
776
|
] }) }),
|
|
@@ -1255,200 +1020,6 @@ var fileDropzoneMeta = {
|
|
|
1255
1020
|
]
|
|
1256
1021
|
};
|
|
1257
1022
|
|
|
1258
|
-
// src/lib/selection-card.ts
|
|
1259
|
-
var SELECTION_CARD_SHELL = [
|
|
1260
|
-
"group relative flex cursor-pointer border border-input bg-card p-4 text-start motion-interactive motion-press",
|
|
1261
|
-
"hover:border-ring",
|
|
1262
|
-
// Focus is an OUTLINE, not a ring: the checked state is an inset ring in the
|
|
1263
|
-
// tone colour, and both rings write box-shadow, so a focused checked card
|
|
1264
|
-
// used to lose its tone ring to the focus ring.
|
|
1265
|
-
"focus-within:outline-2 focus-within:outline-solid focus-within:outline-offset-2 focus-within:outline-ring",
|
|
1266
|
-
"data-disabled:cursor-not-allowed data-disabled:opacity-50 data-disabled:hover:border-input"
|
|
1267
|
-
].join(" ");
|
|
1268
|
-
var SELECTION_CARD_LAYOUT = {
|
|
1269
|
-
row: "items-start gap-3 rounded-md",
|
|
1270
|
-
tile: "flex-col gap-3 rounded-lg"
|
|
1271
|
-
};
|
|
1272
|
-
var SELECTION_CARD_TILE_HEAD = "flex w-full items-start justify-between gap-3";
|
|
1273
|
-
var SELECTION_CARD_GLYPH = "shrink-0 text-2xl leading-none motion-interactive";
|
|
1274
|
-
var SELECTION_CARD_CHECKED = {
|
|
1275
|
-
neutral: "data-[checked]:border-transparent data-[checked]:ring-[1.5px] data-[checked]:ring-inset data-[checked]:ring-primary data-[checked]:bg-muted",
|
|
1276
|
-
standard: "data-[checked]:border-transparent data-[checked]:ring-[1.5px] data-[checked]:ring-inset data-[checked]:ring-tone-standard",
|
|
1277
|
-
collectible: "data-[checked]:border-transparent data-[checked]:ring-[1.5px] data-[checked]:ring-inset data-[checked]:ring-tone-collectible",
|
|
1278
|
-
edition: "data-[checked]:border-transparent data-[checked]:ring-[1.5px] data-[checked]:ring-inset data-[checked]:ring-tone-edition",
|
|
1279
|
-
info: "data-[checked]:border-transparent data-[checked]:ring-[1.5px] data-[checked]:ring-inset data-[checked]:ring-tone-info",
|
|
1280
|
-
warning: "data-[checked]:border-transparent data-[checked]:ring-[1.5px] data-[checked]:ring-inset data-[checked]:ring-tone-warning"
|
|
1281
|
-
};
|
|
1282
|
-
var CHECK_BOX = "flex size-4.5 shrink-0 items-center justify-center rounded-xs border border-control-border bg-card motion-interactive";
|
|
1283
|
-
var RADIO_RING = "relative inline-flex size-4.5 shrink-0 rounded-full border border-control-border bg-card motion-interactive";
|
|
1284
|
-
var RADIO_DOT = "absolute left-1/2 top-1/2 size-2 -translate-x-1/2 -translate-y-1/2 rounded-full motion-interactive ease-spring data-[starting-style]:scale-0 data-[ending-style]:scale-0";
|
|
1285
|
-
var CHECK_MARK_SIZE = "text-[length:--spacing(2.75)]";
|
|
1286
|
-
function CheckboxMark({ className }) {
|
|
1287
|
-
return /* @__PURE__ */ jsx(
|
|
1288
|
-
Checkbox$1.Indicator,
|
|
1289
|
-
{
|
|
1290
|
-
className: cn(
|
|
1291
|
-
"flex items-center justify-center text-current motion-interactive ease-spring data-[starting-style]:scale-0 data-[starting-style]:opacity-0 data-[ending-style]:scale-0 data-[ending-style]:opacity-0",
|
|
1292
|
-
className
|
|
1293
|
-
),
|
|
1294
|
-
render: (indicatorProps, state) => /* @__PURE__ */ jsx("span", { ...indicatorProps, children: /* @__PURE__ */ jsx(
|
|
1295
|
-
Icon,
|
|
1296
|
-
{
|
|
1297
|
-
name: state.indeterminate ? "minus" : "check",
|
|
1298
|
-
className: cn(CHECK_MARK_SIZE, "[&_path]:stroke-current [&_path]:[stroke-width:2] [&_path]:[stroke-linejoin:round] [&_path]:[stroke-linecap:round]")
|
|
1299
|
-
}
|
|
1300
|
-
) })
|
|
1301
|
-
}
|
|
1302
|
-
);
|
|
1303
|
-
}
|
|
1304
|
-
CheckboxMark.displayName = "CheckboxMark";
|
|
1305
|
-
function Checkbox({ className, children, ...props }) {
|
|
1306
|
-
return /* @__PURE__ */ jsx(
|
|
1307
|
-
Checkbox$1.Root,
|
|
1308
|
-
{
|
|
1309
|
-
className: cn(
|
|
1310
|
-
CHECK_BOX,
|
|
1311
|
-
"text-primary-foreground",
|
|
1312
|
-
"data-[checked]:border-primary data-[checked]:bg-primary",
|
|
1313
|
-
"data-[indeterminate]:border-primary data-[indeterminate]:bg-primary",
|
|
1314
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
1315
|
-
// Base UI renders the root as a <span>, which `:disabled` never matches —
|
|
1316
|
-
// the state is the data attribute, as on Switch.
|
|
1317
|
-
"data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50",
|
|
1318
|
-
className
|
|
1319
|
-
),
|
|
1320
|
-
...props,
|
|
1321
|
-
children: children ?? /* @__PURE__ */ jsx(CheckboxMark, {})
|
|
1322
|
-
}
|
|
1323
|
-
);
|
|
1324
|
-
}
|
|
1325
|
-
Checkbox.displayName = "Checkbox";
|
|
1326
|
-
|
|
1327
|
-
// src/components/checkbox/checkbox.meta.ts
|
|
1328
|
-
var checkboxMeta = {
|
|
1329
|
-
name: "Checkbox",
|
|
1330
|
-
summary: "Square toggle (18px at rest, on the spacing scale) over Base UI's Checkbox. Checked/indeterminate/disabled states; controlled or uncontrolled; form-ready via the hidden native input.",
|
|
1331
|
-
status: "stable",
|
|
1332
|
-
keywords: [
|
|
1333
|
-
"checkbox",
|
|
1334
|
-
"boolean",
|
|
1335
|
-
"multi select",
|
|
1336
|
-
"form control"
|
|
1337
|
-
],
|
|
1338
|
-
usage: {
|
|
1339
|
-
description: "Use Checkbox for independent boolean choices or for selecting multiple options in a set.",
|
|
1340
|
-
bestPractices: [
|
|
1341
|
-
{
|
|
1342
|
-
guidance: "do",
|
|
1343
|
-
description: "Use Checkbox when each option can be toggled independently."
|
|
1344
|
-
},
|
|
1345
|
-
{
|
|
1346
|
-
guidance: "do",
|
|
1347
|
-
description: "Attach a visible label that explains the consequence of checking the control."
|
|
1348
|
-
},
|
|
1349
|
-
{
|
|
1350
|
-
guidance: "dont",
|
|
1351
|
-
description: "Do not use Checkbox for mutually exclusive choices; use Radio or Select instead."
|
|
1352
|
-
},
|
|
1353
|
-
{
|
|
1354
|
-
guidance: "dont",
|
|
1355
|
-
description: 'Do not use it without a label or an aria-label. An 18px square with no accessible name is announced as just "checkbox", which tells a screen-reader user nothing about what they are agreeing to.'
|
|
1356
|
-
},
|
|
1357
|
-
{
|
|
1358
|
-
guidance: "dont",
|
|
1359
|
-
description: `Do not phrase the label as a negative, such as "Don't send read receipts". Paired with an unchecked box it becomes a double negative the reader has to solve before they can answer. Label the state being turned on.`
|
|
1360
|
-
}
|
|
1361
|
-
]
|
|
1362
|
-
},
|
|
1363
|
-
related: {
|
|
1364
|
-
components: [
|
|
1365
|
-
"CheckboxCard",
|
|
1366
|
-
"Switch",
|
|
1367
|
-
"Radio"
|
|
1368
|
-
]
|
|
1369
|
-
},
|
|
1370
|
-
props: [
|
|
1371
|
-
{
|
|
1372
|
-
name: "checked",
|
|
1373
|
-
type: "boolean",
|
|
1374
|
-
description: "Controlled checked state. Pair with onCheckedChange. Omit for uncontrolled."
|
|
1375
|
-
},
|
|
1376
|
-
{
|
|
1377
|
-
name: "defaultChecked",
|
|
1378
|
-
type: "boolean",
|
|
1379
|
-
default: "false",
|
|
1380
|
-
description: "Initial checked state for an uncontrolled checkbox."
|
|
1381
|
-
},
|
|
1382
|
-
{
|
|
1383
|
-
name: "onCheckedChange",
|
|
1384
|
-
type: "(checked: boolean, eventDetails) => void",
|
|
1385
|
-
description: "Fires when the checkbox is ticked or unticked. Note: Base UI passes a second eventDetails arg \u2014 not the common (checked) => void, so a single-arg handler must widen its signature."
|
|
1386
|
-
},
|
|
1387
|
-
{
|
|
1388
|
-
name: "indeterminate",
|
|
1389
|
-
type: "boolean",
|
|
1390
|
-
default: "false",
|
|
1391
|
-
description: 'Mixed state (neither checked nor unchecked). Renders a dash instead of the check. For a "select all" box inside a CheckboxGroup, use `parent` instead and the group sets this for you.'
|
|
1392
|
-
},
|
|
1393
|
-
{
|
|
1394
|
-
name: "parent",
|
|
1395
|
-
type: "boolean",
|
|
1396
|
-
default: "false",
|
|
1397
|
-
description: 'Makes this the "select all" box of a CheckboxGroup that has `allValues`. It checks or clears every child, and shows the dash when only some are checked. Only works inside a CheckboxGroup.'
|
|
1398
|
-
},
|
|
1399
|
-
{
|
|
1400
|
-
name: "disabled",
|
|
1401
|
-
type: "boolean",
|
|
1402
|
-
default: "false",
|
|
1403
|
-
description: "Ignores user interaction and dims the control."
|
|
1404
|
-
},
|
|
1405
|
-
{
|
|
1406
|
-
name: "name",
|
|
1407
|
-
type: "string",
|
|
1408
|
-
description: "Field name for the hidden input on form submit."
|
|
1409
|
-
},
|
|
1410
|
-
{
|
|
1411
|
-
name: "value",
|
|
1412
|
-
type: "string",
|
|
1413
|
-
description: "Value submitted with the form when checked. Inside a group-owned CheckboxGroup, it is the name the group's value array uses for this checkbox."
|
|
1414
|
-
},
|
|
1415
|
-
{
|
|
1416
|
-
name: "render",
|
|
1417
|
-
type: "ReactElement | (props, state) => ReactElement",
|
|
1418
|
-
description: "Compose the root with another element through the Base UI render prop."
|
|
1419
|
-
}
|
|
1420
|
-
],
|
|
1421
|
-
tokensUsed: [
|
|
1422
|
-
"card",
|
|
1423
|
-
"input",
|
|
1424
|
-
"primary",
|
|
1425
|
-
"primary-foreground",
|
|
1426
|
-
"ring"
|
|
1427
|
-
],
|
|
1428
|
-
a11y: [
|
|
1429
|
-
"Base UI renders a hidden native <input type=checkbox> for keyboard, focus, and form semantics.",
|
|
1430
|
-
"Space toggles; focus-visible draws a 2px ring in the --ring token.",
|
|
1431
|
-
"indeterminate sets aria-checked=mixed automatically; a `parent` checkbox in a CheckboxGroup gets aria-checked=mixed and aria-controls for its children from the group.",
|
|
1432
|
-
`Label it with Field orientation="horizontal": it wires the label's htmlFor to the checkbox, so the label text joins the click target, and adds the description and error plumbing.`
|
|
1433
|
-
],
|
|
1434
|
-
examples: [
|
|
1435
|
-
{ label: "Default", code: "<Checkbox defaultChecked />" },
|
|
1436
|
-
{
|
|
1437
|
-
label: "Controlled",
|
|
1438
|
-
code: "<Checkbox checked={value} onCheckedChange={setValue} />"
|
|
1439
|
-
},
|
|
1440
|
-
{ label: "Indeterminate", code: "<Checkbox indeterminate />" },
|
|
1441
|
-
{
|
|
1442
|
-
label: "Select all in a group",
|
|
1443
|
-
code: '<CheckboxGroup allValues={["email", "sms"]} defaultValue={["email"]} className="gap-3"><Field label="All channels" orientation="horizontal"><Checkbox parent /></Field><Field label="Email" orientation="horizontal" className="ps-7"><Checkbox value="email" /></Field><Field label="SMS" orientation="horizontal" className="ps-7"><Checkbox value="sms" /></Field></CheckboxGroup>'
|
|
1444
|
-
},
|
|
1445
|
-
{
|
|
1446
|
-
label: "With label",
|
|
1447
|
-
code: '<Field label="Remember me" orientation="horizontal"><Checkbox /></Field>'
|
|
1448
|
-
}
|
|
1449
|
-
]
|
|
1450
|
-
};
|
|
1451
|
-
|
|
1452
1023
|
// src/components/thinking-orb/thinking-orb.render.ts
|
|
1453
1024
|
var ORB_PX = { sm: 20, md: 32, lg: 64 };
|
|
1454
1025
|
var ORB_TUNING = {
|
|
@@ -2120,567 +1691,571 @@ var glidingHighlightMeta = {
|
|
|
2120
1691
|
}
|
|
2121
1692
|
]
|
|
2122
1693
|
};
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
{
|
|
2126
|
-
variants: {
|
|
2127
|
-
status: {
|
|
2128
|
-
complete: "border-primary bg-primary text-primary-foreground",
|
|
2129
|
-
current: "border-2 border-primary bg-[var(--sable-surface-bg,var(--background))] text-primary",
|
|
2130
|
-
upcoming: "border-border-strong bg-[var(--sable-surface-bg,var(--background))] text-muted-foreground",
|
|
2131
|
-
error: "border-destructive bg-destructive text-destructive-foreground"
|
|
2132
|
-
}
|
|
2133
|
-
},
|
|
2134
|
-
defaultVariants: {
|
|
2135
|
-
status: "upcoming"
|
|
2136
|
-
}
|
|
2137
|
-
}
|
|
2138
|
-
);
|
|
2139
|
-
var stepperDotVariants = cva(
|
|
2140
|
-
"size-[0.75em] shrink-0 rounded-full motion-interactive",
|
|
2141
|
-
{
|
|
2142
|
-
variants: {
|
|
2143
|
-
status: {
|
|
2144
|
-
complete: "bg-primary",
|
|
2145
|
-
current: "bg-[var(--sable-surface-bg,var(--background))] ring-[0.1875em] ring-primary ring-inset",
|
|
2146
|
-
upcoming: "bg-border-strong",
|
|
2147
|
-
error: "bg-destructive"
|
|
2148
|
-
}
|
|
2149
|
-
},
|
|
2150
|
-
defaultVariants: {
|
|
2151
|
-
status: "upcoming"
|
|
2152
|
-
}
|
|
2153
|
-
}
|
|
2154
|
-
);
|
|
2155
|
-
var stepperDotConnectorVariants = cva("shrink-0 rounded-full motion-interactive", {
|
|
2156
|
-
variants: {
|
|
2157
|
-
orientation: {
|
|
2158
|
-
horizontal: "mt-[calc(0.5lh_-_1px)] h-0.5 min-w-4 flex-1",
|
|
2159
|
-
vertical: "absolute start-[0.375em] top-[0.5lh] -bottom-[0.5lh] w-0.5 -translate-x-1/2 rtl:translate-x-1/2"
|
|
2160
|
-
},
|
|
2161
|
-
filled: {
|
|
2162
|
-
true: "bg-primary",
|
|
2163
|
-
false: "bg-border-strong"
|
|
2164
|
-
}
|
|
2165
|
-
},
|
|
2166
|
-
defaultVariants: {
|
|
2167
|
-
orientation: "horizontal",
|
|
2168
|
-
filled: false
|
|
2169
|
-
}
|
|
2170
|
-
});
|
|
2171
|
-
var stepperConnectorVariants = cva("shrink-0 rounded-full motion-interactive", {
|
|
2172
|
-
variants: {
|
|
2173
|
-
orientation: {
|
|
2174
|
-
horizontal: "mt-[calc(var(--control-xs)/2)] h-px min-w-4 flex-1",
|
|
2175
|
-
vertical: "absolute start-[calc(var(--control-xs)/2_-_0.5px)] top-[calc(var(--control-xs)_+_--spacing(1))] bottom-1 w-px"
|
|
2176
|
-
},
|
|
2177
|
-
filled: {
|
|
2178
|
-
true: "bg-primary",
|
|
2179
|
-
false: "bg-border-strong"
|
|
2180
|
-
}
|
|
2181
|
-
},
|
|
2182
|
-
defaultVariants: {
|
|
2183
|
-
orientation: "horizontal",
|
|
2184
|
-
filled: false
|
|
2185
|
-
}
|
|
2186
|
-
});
|
|
2187
|
-
var DEFAULT_STATUS_LABELS = {
|
|
2188
|
-
complete: "Completed",
|
|
2189
|
-
current: "Current step",
|
|
2190
|
-
upcoming: "Not started",
|
|
2191
|
-
error: "Needs attention"
|
|
2192
|
-
};
|
|
2193
|
-
var StepContext = createContext(null);
|
|
2194
|
-
function Stepper({
|
|
2195
|
-
current = 0,
|
|
2196
|
-
orientation = "horizontal",
|
|
2197
|
-
indicator = "number",
|
|
2198
|
-
statusLabels,
|
|
2199
|
-
className,
|
|
2200
|
-
children,
|
|
2201
|
-
...props
|
|
2202
|
-
}) {
|
|
2203
|
-
const items = Children.toArray(children).filter(isValidElement);
|
|
2204
|
-
const labels = { ...DEFAULT_STATUS_LABELS, ...statusLabels };
|
|
2205
|
-
return /* @__PURE__ */ jsx(
|
|
2206
|
-
"ol",
|
|
2207
|
-
{
|
|
2208
|
-
"data-orientation": orientation,
|
|
2209
|
-
className: cn(
|
|
2210
|
-
// isolate: the indicators' z-10 (above the connector) stays inside the
|
|
2211
|
-
// stepper instead of rising over sticky headers and nav bars.
|
|
2212
|
-
"isolate flex w-full",
|
|
2213
|
-
orientation === "horizontal" ? "flex-row items-start gap-3" : "flex-col",
|
|
2214
|
-
className
|
|
2215
|
-
),
|
|
2216
|
-
...props,
|
|
2217
|
-
children: items.map((child, index) => /* @__PURE__ */ jsx(
|
|
2218
|
-
StepContext.Provider,
|
|
2219
|
-
{
|
|
2220
|
-
value: {
|
|
2221
|
-
index,
|
|
2222
|
-
count: items.length,
|
|
2223
|
-
current,
|
|
2224
|
-
orientation,
|
|
2225
|
-
indicator,
|
|
2226
|
-
statusLabels: labels
|
|
2227
|
-
},
|
|
2228
|
-
children: child
|
|
2229
|
-
},
|
|
2230
|
-
child.key ?? index
|
|
2231
|
-
))
|
|
2232
|
-
}
|
|
2233
|
-
);
|
|
2234
|
-
}
|
|
2235
|
-
Stepper.displayName = "Stepper";
|
|
2236
|
-
function StepperItem({
|
|
2237
|
-
title,
|
|
2238
|
-
description,
|
|
2239
|
-
status: statusProp,
|
|
2240
|
-
onSelect,
|
|
2241
|
-
className,
|
|
2242
|
-
...props
|
|
2243
|
-
}) {
|
|
2244
|
-
const ctx = useContext(StepContext);
|
|
2245
|
-
const tier = useTypeTier();
|
|
2246
|
-
const index = ctx?.index ?? 0;
|
|
2247
|
-
const count2 = ctx?.count ?? 1;
|
|
2248
|
-
const orientation = ctx?.orientation ?? "horizontal";
|
|
2249
|
-
const dot = (ctx?.indicator ?? "number") === "dot";
|
|
2250
|
-
const current = ctx?.current ?? 0;
|
|
2251
|
-
const status = statusProp ?? (index < current ? "complete" : index === current ? "current" : "upcoming");
|
|
2252
|
-
const isLast = index === count2 - 1;
|
|
2253
|
-
const isCurrent = index === current;
|
|
2254
|
-
const statusLabel = (ctx?.statusLabels ?? DEFAULT_STATUS_LABELS)[status];
|
|
2255
|
-
const body = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
2256
|
-
dot ? (
|
|
2257
|
-
// One title line tall (the step carries the title's type), so the dot
|
|
2258
|
-
// centres on the title's first line at any density.
|
|
2259
|
-
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "relative z-10 flex h-[1lh] shrink-0 items-center", children: /* @__PURE__ */ jsx("span", { className: stepperDotVariants({ status }) }) })
|
|
2260
|
-
) : /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: stepperIndicatorVariants({ status }), children: status === "complete" ? /* @__PURE__ */ jsx(Icon, { name: "check", className: "text-sm" }) : status === "error" ? /* @__PURE__ */ jsx(Icon, { name: "warning", className: "text-sm" }) : index + 1 }),
|
|
2261
|
-
/* @__PURE__ */ jsxs(
|
|
2262
|
-
"span",
|
|
2263
|
-
{
|
|
2264
|
-
className: cn(
|
|
2265
|
-
"flex min-w-0 flex-col gap-0.5 text-start",
|
|
2266
|
-
!dot && "min-h-(--control-xs) justify-center"
|
|
2267
|
-
),
|
|
2268
|
-
children: [
|
|
2269
|
-
/* @__PURE__ */ jsxs(
|
|
2270
|
-
"span",
|
|
2271
|
-
{
|
|
2272
|
-
className: cn(
|
|
2273
|
-
tier.control,
|
|
2274
|
-
"underline-offset-2 motion-interactive group-hover/step:underline",
|
|
2275
|
-
status === "upcoming" && "text-muted-foreground",
|
|
2276
|
-
status === "error" && "text-destructive",
|
|
2277
|
-
(status === "complete" || status === "current") && "text-foreground"
|
|
2278
|
-
),
|
|
2279
|
-
children: [
|
|
2280
|
-
title,
|
|
2281
|
-
/* @__PURE__ */ jsx("span", { className: "sr-only", children: ` (${statusLabel})` })
|
|
2282
|
-
]
|
|
2283
|
-
}
|
|
2284
|
-
),
|
|
2285
|
-
description ? /* @__PURE__ */ jsx("span", { className: cn("text-muted-foreground", tier.secondary), children: description }) : null
|
|
2286
|
-
]
|
|
2287
|
-
}
|
|
2288
|
-
)
|
|
2289
|
-
] });
|
|
2290
|
-
return /* @__PURE__ */ jsxs(
|
|
2291
|
-
"li",
|
|
2292
|
-
{
|
|
2293
|
-
"aria-current": isCurrent ? "step" : void 0,
|
|
2294
|
-
"data-status": status,
|
|
2295
|
-
className: cn(
|
|
2296
|
-
orientation === "horizontal" ? cn("flex min-w-0 items-start gap-3", isLast ? "flex-none" : "flex-1") : cn("relative", !isLast && "pb-6"),
|
|
2297
|
-
// The dot and its line are measured in the title's `lh` and `em`.
|
|
2298
|
-
dot && tier.control,
|
|
2299
|
-
className
|
|
2300
|
-
),
|
|
2301
|
-
...props,
|
|
2302
|
-
children: [
|
|
2303
|
-
onSelect ? /* @__PURE__ */ jsx(
|
|
2304
|
-
"button",
|
|
2305
|
-
{
|
|
2306
|
-
type: "button",
|
|
2307
|
-
onClick: onSelect,
|
|
2308
|
-
className: "group/step flex min-w-0 items-start gap-3 rounded-sm motion-interactive focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
2309
|
-
children: body
|
|
2310
|
-
}
|
|
2311
|
-
) : /* @__PURE__ */ jsx("div", { className: "flex min-w-0 items-start gap-3", children: body }),
|
|
2312
|
-
isLast ? null : /* @__PURE__ */ jsx(
|
|
2313
|
-
"span",
|
|
2314
|
-
{
|
|
2315
|
-
"aria-hidden": "true",
|
|
2316
|
-
className: (dot ? stepperDotConnectorVariants : stepperConnectorVariants)({
|
|
2317
|
-
orientation,
|
|
2318
|
-
filled: status === "complete"
|
|
2319
|
-
})
|
|
2320
|
-
}
|
|
2321
|
-
)
|
|
2322
|
-
]
|
|
2323
|
-
}
|
|
2324
|
-
);
|
|
1694
|
+
function LineChart(props) {
|
|
1695
|
+
return /* @__PURE__ */ jsx(CartesianChart, { kind: "line", ...props });
|
|
2325
1696
|
}
|
|
2326
|
-
|
|
1697
|
+
LineChart.displayName = "LineChart";
|
|
2327
1698
|
|
|
2328
|
-
// src/components/
|
|
2329
|
-
var
|
|
2330
|
-
name: "
|
|
2331
|
-
summary:
|
|
2332
|
-
status: "stable",
|
|
1699
|
+
// src/components/line-chart/line-chart.meta.ts
|
|
1700
|
+
var lineChartMeta = {
|
|
1701
|
+
name: "LineChart",
|
|
1702
|
+
summary: "Lines over a sequence \u2014 for comparing how measures move. Smooth, linear or step curves, optional dots (coloured by series or by category) and value labels. Lines draw on as the chart scrolls into view.",
|
|
2333
1703
|
keywords: [
|
|
2334
|
-
"
|
|
2335
|
-
"
|
|
2336
|
-
"
|
|
2337
|
-
"
|
|
2338
|
-
"
|
|
2339
|
-
"
|
|
2340
|
-
"
|
|
2341
|
-
"
|
|
1704
|
+
"chart",
|
|
1705
|
+
"line",
|
|
1706
|
+
"trend",
|
|
1707
|
+
"time series",
|
|
1708
|
+
"graph",
|
|
1709
|
+
"compare",
|
|
1710
|
+
"analytics",
|
|
1711
|
+
"dashboard"
|
|
2342
1712
|
],
|
|
2343
1713
|
usage: {
|
|
2344
|
-
description: "Use
|
|
1714
|
+
description: "Use LineChart to compare how two or more measures change over the same sequence, or to show one trend precisely (with dots and labels). Unlike AreaChart, its value axis is fitted to the data rather than forced to zero, so small movements stay visible \u2014 say so if the axis is shown.",
|
|
2345
1715
|
bestPractices: [
|
|
2346
1716
|
{
|
|
2347
1717
|
guidance: "do",
|
|
2348
|
-
description:
|
|
2349
|
-
},
|
|
2350
|
-
{
|
|
2351
|
-
guidance: "do",
|
|
2352
|
-
description: 'Name steps with a word or two ("Shipping", "Payment"). Go vertical when steps need a description or there are more than five; horizontal titles truncate first.'
|
|
1718
|
+
description: "Turn on dots and labels for six or fewer points, when the exact values are the story."
|
|
2353
1719
|
},
|
|
2354
1720
|
{
|
|
2355
1721
|
guidance: "do",
|
|
2356
|
-
description: "
|
|
1722
|
+
description: 'Use curve="step" for values that hold until they change \u2014 prices, plan tiers, headcount.'
|
|
2357
1723
|
},
|
|
2358
1724
|
{
|
|
2359
1725
|
guidance: "dont",
|
|
2360
|
-
description: "
|
|
1726
|
+
description: "Plot unordered categories (channels, products) as a line \u2014 the line implies a path between them; use BarChart."
|
|
2361
1727
|
},
|
|
2362
1728
|
{
|
|
2363
1729
|
guidance: "dont",
|
|
2364
|
-
description: "
|
|
2365
|
-
},
|
|
2366
|
-
{
|
|
2367
|
-
guidance: "dont",
|
|
2368
|
-
description: "Do not make upcoming steps pressable to skip ahead. A step that looks reachable but is not is worse than one that is plainly locked."
|
|
2369
|
-
}
|
|
2370
|
-
],
|
|
2371
|
-
anatomy: [
|
|
2372
|
-
{
|
|
2373
|
-
name: "Stepper",
|
|
2374
|
-
required: true,
|
|
2375
|
-
description: "The ordered list. Takes `current` and `orientation`, and an aria-label naming the flow."
|
|
2376
|
-
},
|
|
2377
|
-
{
|
|
2378
|
-
name: "StepperItem",
|
|
2379
|
-
required: true,
|
|
2380
|
-
description: 'One step: a numbered disc (a check when complete, a warning mark on error) \u2014 or, with `indicator="dot"`, a dot centred on the title\'s first line \u2014 a title, and an optional description.'
|
|
2381
|
-
},
|
|
2382
|
-
{
|
|
2383
|
-
name: "Connector",
|
|
2384
|
-
required: false,
|
|
2385
|
-
description: "The line to the next step, filled once this step is complete. Drawn automatically between items."
|
|
1730
|
+
description: "Use it for data-heavy views \u2014 streaming or real-time data, thousands of points, zoom and brushing, trading charts. Those belong on a charting library (Recharts, visx, ECharts); Sable's charts are for dashboards, landing pages and decks."
|
|
2386
1731
|
}
|
|
2387
1732
|
]
|
|
2388
1733
|
},
|
|
2389
1734
|
related: {
|
|
2390
|
-
components: [
|
|
1735
|
+
components: [
|
|
1736
|
+
"AreaChart",
|
|
1737
|
+
"BarChart",
|
|
1738
|
+
"Sparkline",
|
|
1739
|
+
"Card"
|
|
1740
|
+
]
|
|
2391
1741
|
},
|
|
1742
|
+
status: "beta",
|
|
2392
1743
|
props: [
|
|
2393
1744
|
{
|
|
2394
|
-
name: "
|
|
2395
|
-
type: "
|
|
2396
|
-
|
|
2397
|
-
description: "Zero-based index of the current step. Earlier steps derive complete, later ones upcoming."
|
|
1745
|
+
name: "data",
|
|
1746
|
+
type: "Record<string, unknown>[]",
|
|
1747
|
+
description: "The rows, in order along the category axis."
|
|
2398
1748
|
},
|
|
2399
1749
|
{
|
|
2400
|
-
name: "
|
|
2401
|
-
type:
|
|
2402
|
-
|
|
2403
|
-
description: "Horizontal for a short flow across the top of a form; vertical for long flows, narrow columns or steps with descriptions."
|
|
1750
|
+
name: "index",
|
|
1751
|
+
type: "string",
|
|
1752
|
+
description: "The datum field naming each row's category (a month, a day, a channel)."
|
|
2404
1753
|
},
|
|
2405
1754
|
{
|
|
2406
|
-
name: "
|
|
2407
|
-
type:
|
|
2408
|
-
|
|
2409
|
-
description: "`number` for a flow the user moves through; `dot` for a timeline of events (delivery tracking, a document's history) where the position number adds nothing. The dot centres on the title's first line and scales with its type at every density."
|
|
1755
|
+
name: "series",
|
|
1756
|
+
type: "{ key: string; label: string; tone?: ChartTone; color?: string; icon?: string }[]",
|
|
1757
|
+
description: "The measures to plot: the datum field, its name in the legend/tooltip/table, and its colour (a tone by role, or any CSS colour). Omitted, series take the chart palette in order: --chart-1 \u2026 --chart-8, validated for colour-blind separation in light and dark; a theme may override the set."
|
|
2410
1758
|
},
|
|
2411
1759
|
{
|
|
2412
|
-
name: "
|
|
2413
|
-
type: "
|
|
2414
|
-
description: "
|
|
1760
|
+
name: "label",
|
|
1761
|
+
type: "string",
|
|
1762
|
+
description: "What the chart shows. Its accessible name and the caption of the data table assistive tech reads."
|
|
2415
1763
|
},
|
|
2416
1764
|
{
|
|
2417
|
-
name: "
|
|
1765
|
+
name: "className",
|
|
2418
1766
|
type: "string",
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
1767
|
+
default: '"h-60"',
|
|
1768
|
+
description: "Sizes the chart (plot + legend). Set a height: `h-72`, `aspect-video h-auto`."
|
|
1769
|
+
},
|
|
1770
|
+
{
|
|
1771
|
+
name: "valueFormatter",
|
|
1772
|
+
type: "(value: number) => string",
|
|
1773
|
+
description: 'Format values everywhere (axis, labels, tooltip, table) \u2014 units like "$12,480". Default: compact on the axis, full elsewhere.'
|
|
1774
|
+
},
|
|
1775
|
+
{
|
|
1776
|
+
name: "indexFormatter",
|
|
1777
|
+
type: "(value: string) => string",
|
|
1778
|
+
description: 'Shorten category labels on the axis ("January" \u2192 "Jan"); the tooltip keeps the full value.'
|
|
1779
|
+
},
|
|
1780
|
+
{
|
|
1781
|
+
name: "locale",
|
|
1782
|
+
type: "string",
|
|
1783
|
+
default: "runtime locale",
|
|
1784
|
+
description: "BCP 47 locale for default number and date formatting."
|
|
1785
|
+
},
|
|
1786
|
+
{
|
|
1787
|
+
name: "tooltip",
|
|
1788
|
+
type: 'boolean | { indicator?: "dot" | "line" | "dashed" | "none"; hideLabel?: boolean; labelFormatter?: (label, datum) => ReactNode; showTotal?: boolean }',
|
|
1789
|
+
default: "true",
|
|
1790
|
+
description: "Show a tooltip on hover and keyboard focus, or configure it: the row marker, hiding the heading, rewriting it, adding a Total row."
|
|
1791
|
+
},
|
|
1792
|
+
{
|
|
1793
|
+
name: "legend",
|
|
1794
|
+
type: "boolean",
|
|
1795
|
+
default: "true when there is more than one series",
|
|
1796
|
+
description: "Show the legend under the chart."
|
|
1797
|
+
},
|
|
1798
|
+
{
|
|
1799
|
+
name: "grid",
|
|
1800
|
+
type: "boolean",
|
|
1801
|
+
default: "true (false for horizontal bars)",
|
|
1802
|
+
description: "Hairline gridlines along the value axis."
|
|
1803
|
+
},
|
|
1804
|
+
{
|
|
1805
|
+
name: "xAxis",
|
|
1806
|
+
type: "boolean",
|
|
1807
|
+
default: "true for columns; false for horizontal bars",
|
|
1808
|
+
description: "The horizontal axis."
|
|
1809
|
+
},
|
|
1810
|
+
{
|
|
1811
|
+
name: "yAxis",
|
|
1812
|
+
type: "boolean",
|
|
1813
|
+
default: "false for columns; true for horizontal bars",
|
|
1814
|
+
description: "The vertical axis."
|
|
1815
|
+
},
|
|
1816
|
+
{
|
|
1817
|
+
name: "animate",
|
|
1818
|
+
type: "boolean",
|
|
1819
|
+
default: "true",
|
|
1820
|
+
description: "Play the arrival once the chart scrolls into view. Reduced motion shows the finished chart."
|
|
1821
|
+
},
|
|
1822
|
+
{
|
|
1823
|
+
name: "activeIndex",
|
|
1824
|
+
type: "number | null",
|
|
1825
|
+
description: "The highlighted row (controlled); `null` for none."
|
|
1826
|
+
},
|
|
1827
|
+
{
|
|
1828
|
+
name: "defaultActiveIndex",
|
|
1829
|
+
type: "number | null",
|
|
1830
|
+
description: "The row highlighted at first \u2014 a tooltip already open before anyone hovers."
|
|
1831
|
+
},
|
|
1832
|
+
{
|
|
1833
|
+
name: "onActiveIndexChange",
|
|
1834
|
+
type: "(index: number | null) => void",
|
|
1835
|
+
description: "Called as the pointer or keyboard moves between rows."
|
|
1836
|
+
},
|
|
1837
|
+
{
|
|
1838
|
+
name: "curve",
|
|
1839
|
+
type: '"smooth" | "linear" | "step"',
|
|
1840
|
+
default: '"smooth"',
|
|
1841
|
+
description: "How the line runs between points. `smooth` is monotone: it never overshoots a value the data does not contain."
|
|
1842
|
+
},
|
|
1843
|
+
{
|
|
1844
|
+
name: "dots",
|
|
1845
|
+
type: "boolean",
|
|
1846
|
+
default: "false",
|
|
1847
|
+
description: "Mark every data point with a dot."
|
|
1848
|
+
},
|
|
1849
|
+
{
|
|
1850
|
+
name: "colorBy",
|
|
1851
|
+
type: '"series" | "index"',
|
|
1852
|
+
default: '"series"',
|
|
1853
|
+
description: "Colour the dots by series, or each by its category."
|
|
1854
|
+
},
|
|
2423
1855
|
{
|
|
2424
|
-
name: "
|
|
2425
|
-
type: "
|
|
2426
|
-
|
|
1856
|
+
name: "labels",
|
|
1857
|
+
type: "boolean",
|
|
1858
|
+
default: "false",
|
|
1859
|
+
description: "Print each point's value above it."
|
|
2427
1860
|
}
|
|
2428
1861
|
],
|
|
2429
|
-
variants: {
|
|
2430
|
-
orientation: ["horizontal", "vertical"],
|
|
2431
|
-
indicator: ["number", "dot"],
|
|
2432
|
-
status: ["complete", "current", "upcoming", "error"]
|
|
2433
|
-
},
|
|
2434
1862
|
tokensUsed: [
|
|
1863
|
+
"tone-edition",
|
|
1864
|
+
"tone-collectible",
|
|
1865
|
+
"tone-standard",
|
|
1866
|
+
"tone-info",
|
|
1867
|
+
"tone-warning",
|
|
2435
1868
|
"primary",
|
|
2436
|
-
"primary-foreground",
|
|
2437
|
-
"background",
|
|
2438
|
-
"foreground",
|
|
2439
1869
|
"muted-foreground",
|
|
2440
|
-
"border
|
|
2441
|
-
"
|
|
2442
|
-
"
|
|
2443
|
-
"
|
|
1870
|
+
"border",
|
|
1871
|
+
"popover",
|
|
1872
|
+
"popover-foreground",
|
|
1873
|
+
"foreground",
|
|
1874
|
+
"background"
|
|
2444
1875
|
],
|
|
2445
1876
|
motion: [
|
|
2446
1877
|
{
|
|
2447
1878
|
slot: "arrival",
|
|
2448
|
-
recipe: "
|
|
2449
|
-
part: "
|
|
2450
|
-
why: "
|
|
1879
|
+
recipe: "custom",
|
|
1880
|
+
part: "Line",
|
|
1881
|
+
why: "Once on screen each line draws on from the first point (stroke-dashoffset), series staggered, then the dots and labels fade in. On the motion tokens; reduced motion shows the finished chart."
|
|
2451
1882
|
},
|
|
2452
1883
|
{
|
|
2453
1884
|
slot: "response",
|
|
2454
|
-
recipe: "
|
|
2455
|
-
part: "
|
|
2456
|
-
why: "
|
|
1885
|
+
recipe: "none",
|
|
1886
|
+
part: "Cursor + tooltip",
|
|
1887
|
+
why: "Hover and focus move the cursor and tooltip instantly \u2014 a lagging cursor reads as a slow chart."
|
|
2457
1888
|
},
|
|
2458
1889
|
{
|
|
2459
1890
|
slot: "transition",
|
|
2460
|
-
recipe: "
|
|
2461
|
-
part: "
|
|
2462
|
-
why: "
|
|
1891
|
+
recipe: "custom",
|
|
1892
|
+
part: "Line + dots",
|
|
1893
|
+
why: "Geometry eases from the old shape to the new one through a CSS `d` transition on the motion tokens (no recipe animates path geometry); browsers without `d` transitions update in place."
|
|
2463
1894
|
},
|
|
2464
1895
|
{
|
|
2465
1896
|
slot: "activity",
|
|
2466
1897
|
recipe: "none",
|
|
2467
|
-
part: "
|
|
2468
|
-
why: "A
|
|
1898
|
+
part: "LineChart",
|
|
1899
|
+
why: "A chart has no indeterminate state; show a Skeleton while the data loads."
|
|
2469
1900
|
}
|
|
2470
1901
|
],
|
|
2471
1902
|
a11y: [
|
|
2472
|
-
"
|
|
2473
|
-
'
|
|
2474
|
-
"
|
|
2475
|
-
"Steps with onSelect render as native buttons with a visible focus ring; steps without it are not focusable."
|
|
1903
|
+
"The drawing is aria-hidden; the same numbers are in a visually hidden data table (captioned by `label`) that the chart is aria-describedby, so assistive tech reads values, not shapes.",
|
|
1904
|
+
'With a tooltip, the chart is one tab stop (role=group, aria-roledescription="chart"): arrow keys, Home and End step through the data, Escape clears, and each step is announced in a polite live region.',
|
|
1905
|
+
"Colour is never the only key: series are named in the legend, tooltip and table."
|
|
2476
1906
|
],
|
|
2477
1907
|
examples: [
|
|
2478
1908
|
{
|
|
2479
|
-
label: "
|
|
2480
|
-
code:
|
|
2481
|
-
<StepperItem title="Cart" />
|
|
2482
|
-
<StepperItem title="Shipping" />
|
|
2483
|
-
<StepperItem title="Payment" />
|
|
2484
|
-
<StepperItem title="Review" />
|
|
2485
|
-
</Stepper>`
|
|
2486
|
-
},
|
|
2487
|
-
{
|
|
2488
|
-
label: "Vertical with descriptions",
|
|
2489
|
-
code: `<Stepper orientation="vertical" current={2} aria-label="Workspace setup">
|
|
2490
|
-
<StepperItem title="Create account" description="Email verified" />
|
|
2491
|
-
<StepperItem title="Invite team" description="3 people invited" />
|
|
2492
|
-
<StepperItem title="Connect data" description="Choose a source to import from" />
|
|
2493
|
-
<StepperItem title="Launch" />
|
|
2494
|
-
</Stepper>`
|
|
1909
|
+
label: "Compare two series",
|
|
1910
|
+
code: '<LineChart\n label="Desktop and mobile visitors"\n data={visitors}\n index="month"\n series={[\n { key: "desktop", label: "Desktop", tone: "primary" },\n { key: "mobile", label: "Mobile", tone: "info" },\n ]}\n indexFormatter={(m) => m.slice(0, 3)}\n/>'
|
|
2495
1911
|
},
|
|
2496
1912
|
{
|
|
2497
|
-
label: "
|
|
2498
|
-
code:
|
|
2499
|
-
<StepperItem title="Order placed" description="Oct 9, 10:14 AM" />
|
|
2500
|
-
<StepperItem title="Printed and packed" description="Oct 10, 4:02 PM" />
|
|
2501
|
-
<StepperItem title="Out for delivery" description="Arriving today by 6 PM" />
|
|
2502
|
-
<StepperItem title="Delivered" />
|
|
2503
|
-
</Stepper>`
|
|
2504
|
-
},
|
|
2505
|
-
{
|
|
2506
|
-
label: "A completed step with an error",
|
|
2507
|
-
code: `<Stepper current={2} aria-label="Checkout progress">
|
|
2508
|
-
<StepperItem title="Cart" onSelect={() => console.log("cart")} />
|
|
2509
|
-
<StepperItem title="Shipping" status="error" description="Address needs a postcode" onSelect={() => console.log("shipping")} />
|
|
2510
|
-
<StepperItem title="Payment" />
|
|
2511
|
-
</Stepper>`
|
|
1913
|
+
label: "Dots and labels",
|
|
1914
|
+
code: '<LineChart\n label="Desktop visitors"\n data={visitors}\n index="month"\n series={[{ key: "desktop", label: "Desktop" }]}\n curve="linear"\n dots\n labels\n grid={false}\n/>'
|
|
2512
1915
|
}
|
|
2513
1916
|
]
|
|
2514
1917
|
};
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
1918
|
+
function RadarChart({
|
|
1919
|
+
data,
|
|
1920
|
+
index,
|
|
1921
|
+
series: rawSeries,
|
|
1922
|
+
label,
|
|
1923
|
+
className,
|
|
1924
|
+
grid = "polygon",
|
|
1925
|
+
gridFill = false,
|
|
1926
|
+
filled = true,
|
|
1927
|
+
dots = false,
|
|
1928
|
+
radiusAxis = false,
|
|
1929
|
+
legend,
|
|
1930
|
+
tooltip = true,
|
|
1931
|
+
valueFormatter,
|
|
1932
|
+
locale,
|
|
1933
|
+
animate = true,
|
|
1934
|
+
activeIndex,
|
|
1935
|
+
defaultActiveIndex,
|
|
1936
|
+
onActiveIndexChange
|
|
1937
|
+
}) {
|
|
1938
|
+
const series = resolveSeries(rawSeries);
|
|
1939
|
+
const fmt = chartFormatters(locale, valueFormatter);
|
|
1940
|
+
const n = data.length;
|
|
1941
|
+
const spokes = data.map((d) => categoryText(d[index], locale));
|
|
1942
|
+
const values = series.map((s) => data.map((d) => Math.max(0, numberValue(d[s.key]))));
|
|
1943
|
+
const ticks = niceTicks(0, Math.max(0, ...values.flat()), 4);
|
|
1944
|
+
const max = ticks[ticks.length - 1] || 1;
|
|
1945
|
+
const angleAt = (i) => i / Math.max(1, n) * Math.PI * 2;
|
|
1946
|
+
const tooltipOptions = typeof tooltip === "object" ? tooltip : {};
|
|
1947
|
+
const geometry = (size) => {
|
|
1948
|
+
const labelW = Math.max(0, ...spokes.map(approxTextWidth));
|
|
1949
|
+
const outer = Math.max(8, Math.min(size.width / 2 - labelW - 10, size.height / 2 - 22));
|
|
1950
|
+
return { cx: size.width / 2, cy: size.height / 2, outer };
|
|
1951
|
+
};
|
|
1952
|
+
const ringPoints = (g, r) => spokes.map((_, i) => polar(g.cx, g.cy, r, angleAt(i)));
|
|
1953
|
+
const toPath = (pts) => pts.length ? `M${pts.map(([x, y]) => `${x.toFixed(2)},${y.toFixed(2)}`).join("L")}Z` : "";
|
|
1954
|
+
const hitTest = (x, y, size) => {
|
|
1955
|
+
const g = geometry(size);
|
|
1956
|
+
if (Math.hypot(x - g.cx, y - g.cy) > g.outer + 24 || !n) return null;
|
|
1957
|
+
const angle = (Math.atan2(x - g.cx, -(y - g.cy)) + Math.PI * 2) % (Math.PI * 2);
|
|
1958
|
+
return Math.round(angle / (Math.PI * 2 / n)) % n;
|
|
1959
|
+
};
|
|
1960
|
+
const describe = (i) => `${spokes[i]}: ${series.map((s, k) => `${s.label} ${fmt.full(values[k][i])}`).join(", ")}`;
|
|
1961
|
+
const tooltipFor = (i, size) => {
|
|
1962
|
+
const g = geometry(size);
|
|
1963
|
+
const [x, y] = polar(g.cx, g.cy, g.outer, angleAt(i));
|
|
1964
|
+
return {
|
|
1965
|
+
x,
|
|
1966
|
+
y,
|
|
1967
|
+
content: /* @__PURE__ */ jsx(
|
|
1968
|
+
ChartTooltipContent,
|
|
1969
|
+
{
|
|
1970
|
+
indicator: tooltipOptions.indicator,
|
|
1971
|
+
label: tooltipOptions.hideLabel ? void 0 : tooltipOptions.labelFormatter ? tooltipOptions.labelFormatter(spokes[i], data[i]) : spokes[i],
|
|
1972
|
+
rows: series.map((s, k) => ({ key: s.key, label: s.label, value: fmt.full(values[k][i]), color: s.colorValue, icon: s.icon }))
|
|
1973
|
+
}
|
|
1974
|
+
)
|
|
1975
|
+
};
|
|
1976
|
+
};
|
|
2523
1977
|
return /* @__PURE__ */ jsx(
|
|
2524
|
-
|
|
2525
|
-
{
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
1978
|
+
ChartFrame,
|
|
1979
|
+
{
|
|
1980
|
+
label,
|
|
1981
|
+
className: cn("h-72", className),
|
|
1982
|
+
count: n,
|
|
1983
|
+
interactive: tooltip !== false,
|
|
1984
|
+
animate,
|
|
1985
|
+
activeIndex,
|
|
1986
|
+
defaultActiveIndex,
|
|
1987
|
+
onActiveIndexChange,
|
|
1988
|
+
hitTest,
|
|
1989
|
+
tooltip: tooltip === false ? void 0 : tooltipFor,
|
|
1990
|
+
describe,
|
|
1991
|
+
table: {
|
|
1992
|
+
headers: [index.charAt(0).toUpperCase() + index.slice(1), ...series.map((s) => s.label)],
|
|
1993
|
+
rows: data.map((_, i) => [spokes[i], ...series.map((_2, k) => fmt.full(values[k][i]))])
|
|
1994
|
+
},
|
|
1995
|
+
legend: legend ?? series.length > 1 ? /* @__PURE__ */ jsx(ChartLegend, { items: series.map((s) => ({ key: s.key, label: s.label, color: s.colorValue, icon: s.icon })) }) : null,
|
|
1996
|
+
children: ({ size, activeIndex: active }) => {
|
|
1997
|
+
const g = geometry(size);
|
|
1998
|
+
const rings = ticks.slice(1).map((t) => t / max * g.outer);
|
|
1999
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2000
|
+
gridFill ? grid === "circle" ? /* @__PURE__ */ jsx("circle", { cx: g.cx, cy: g.cy, r: g.outer, className: "fill-foreground/[0.04]" }) : /* @__PURE__ */ jsx("path", { d: toPath(ringPoints(g, g.outer)), className: "fill-foreground/[0.04]" }) : null,
|
|
2001
|
+
grid !== "none" ? /* @__PURE__ */ jsxs("g", { className: "fill-none stroke-foreground/10", children: [
|
|
2002
|
+
rings.map(
|
|
2003
|
+
(r) => grid === "circle" ? /* @__PURE__ */ jsx("circle", { cx: g.cx, cy: g.cy, r }, r) : /* @__PURE__ */ jsx("path", { d: toPath(ringPoints(g, r)) }, r)
|
|
2004
|
+
),
|
|
2005
|
+
spokes.map((s, i) => {
|
|
2006
|
+
const [x, y] = polar(g.cx, g.cy, g.outer, angleAt(i));
|
|
2007
|
+
return /* @__PURE__ */ jsx("line", { x1: g.cx, y1: g.cy, x2: x, y2: y }, s);
|
|
2008
|
+
})
|
|
2009
|
+
] }) : null,
|
|
2010
|
+
series.map((s, k) => {
|
|
2011
|
+
const pts = values[k].map((v, i) => polar(g.cx, g.cy, v / max * g.outer, angleAt(i)));
|
|
2012
|
+
const d = toPath(pts);
|
|
2013
|
+
return /* @__PURE__ */ jsxs("g", { children: [
|
|
2014
|
+
/* @__PURE__ */ jsx(
|
|
2015
|
+
"path",
|
|
2016
|
+
{
|
|
2017
|
+
d,
|
|
2018
|
+
strokeWidth: 2,
|
|
2019
|
+
strokeLinejoin: "round",
|
|
2020
|
+
className: "[[data-reveal=out]_&]:scale-0 [[data-reveal=out]_&]:opacity-0",
|
|
2021
|
+
style: {
|
|
2022
|
+
...pathStyle(d),
|
|
2023
|
+
stroke: s.colorValue,
|
|
2024
|
+
fill: filled ? s.colorValue : "none",
|
|
2025
|
+
fillOpacity: filled ? 0.24 : 0,
|
|
2026
|
+
transformOrigin: `${g.cx}px ${g.cy}px`,
|
|
2027
|
+
transition: riseTransition(stagger(k, 120))
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
),
|
|
2031
|
+
dots || active != null ? pts.map(
|
|
2032
|
+
([x, y], i) => dots || active === i ? /* @__PURE__ */ jsx(
|
|
2033
|
+
"circle",
|
|
2034
|
+
{
|
|
2035
|
+
cx: x,
|
|
2036
|
+
cy: y,
|
|
2037
|
+
r: active === i ? 5 : 4,
|
|
2038
|
+
strokeWidth: 2,
|
|
2039
|
+
className: "stroke-(--sable-surface-bg,var(--background)) [[data-reveal=out]_&]:opacity-0",
|
|
2040
|
+
style: { fill: s.colorValue, transition: morphTransition() }
|
|
2041
|
+
},
|
|
2042
|
+
spokes[i]
|
|
2043
|
+
) : null
|
|
2044
|
+
) : null
|
|
2045
|
+
] }, s.key);
|
|
2046
|
+
}),
|
|
2047
|
+
spokes.map((s, i) => {
|
|
2048
|
+
const a = angleAt(i);
|
|
2049
|
+
const [x, y] = polar(g.cx, g.cy, g.outer + 12, a);
|
|
2050
|
+
const anchor = Math.sin(a) > 0.1 ? "start" : Math.sin(a) < -0.1 ? "end" : "middle";
|
|
2051
|
+
return /* @__PURE__ */ jsx("text", { x, y, dy: "0.32em", textAnchor: anchor, className: cn("text-caption", active === i ? "fill-foreground" : "fill-muted-foreground"), children: s }, s);
|
|
2052
|
+
}),
|
|
2053
|
+
radiusAxis ? ticks.slice(1, -1).map((t) => /* @__PURE__ */ jsx("text", { x: g.cx + 4, y: g.cy - t / max * g.outer, dy: "-0.2em", className: "fill-muted-foreground text-caption tabular-nums", children: fmt.axis(t) }, t)) : null
|
|
2054
|
+
] });
|
|
2055
|
+
}
|
|
2541
2056
|
}
|
|
2542
2057
|
);
|
|
2543
2058
|
}
|
|
2544
|
-
|
|
2545
|
-
function ToggleGroup({ className, appearance = "default", ...props }) {
|
|
2546
|
-
return /* @__PURE__ */ jsx(ToggleAppearanceContext.Provider, { value: appearance, children: /* @__PURE__ */ jsx(
|
|
2547
|
-
ToggleGroup$1,
|
|
2548
|
-
{
|
|
2549
|
-
"data-appearance": appearance,
|
|
2550
|
-
className: cn(
|
|
2551
|
-
appearance === "segmented" ? SEGMENTED_TRACK : "inline-flex items-center gap-1",
|
|
2552
|
-
className
|
|
2553
|
-
),
|
|
2554
|
-
...props,
|
|
2555
|
-
"aria-orientation": props.role === "toolbar" ? props.orientation ?? "horizontal" : void 0
|
|
2556
|
-
}
|
|
2557
|
-
) });
|
|
2558
|
-
}
|
|
2559
|
-
ToggleGroup.displayName = "ToggleGroup";
|
|
2059
|
+
RadarChart.displayName = "RadarChart";
|
|
2560
2060
|
|
|
2561
|
-
// src/components/
|
|
2562
|
-
var
|
|
2563
|
-
name: "
|
|
2564
|
-
summary: "
|
|
2565
|
-
status: "stable",
|
|
2061
|
+
// src/components/radar-chart/radar-chart.meta.ts
|
|
2062
|
+
var radarChartMeta = {
|
|
2063
|
+
name: "RadarChart",
|
|
2064
|
+
summary: "Several measures compared around a centre: one polygon per series over a polygon or circular web, filled or outline only, with optional dots, a radius scale and a tinted web. Polygons grow from the centre as the chart scrolls into view.",
|
|
2566
2065
|
keywords: [
|
|
2567
|
-
"
|
|
2568
|
-
"
|
|
2569
|
-
"
|
|
2570
|
-
"
|
|
2571
|
-
"
|
|
2572
|
-
"
|
|
2573
|
-
"
|
|
2574
|
-
"
|
|
2575
|
-
"view switcher"
|
|
2066
|
+
"chart",
|
|
2067
|
+
"radar",
|
|
2068
|
+
"spider",
|
|
2069
|
+
"web",
|
|
2070
|
+
"profile",
|
|
2071
|
+
"skills",
|
|
2072
|
+
"comparison",
|
|
2073
|
+
"polar"
|
|
2576
2074
|
],
|
|
2577
2075
|
usage: {
|
|
2578
|
-
description: "Use
|
|
2076
|
+
description: "Use RadarChart to compare the shape of a profile across five to eight dimensions \u2014 skills, product scores, a cycle of months \u2014 for one to three series. The shape is the message; for exact values or more than three series, BarChart is easier to read.",
|
|
2579
2077
|
bestPractices: [
|
|
2580
2078
|
{
|
|
2581
2079
|
guidance: "do",
|
|
2582
|
-
description: "
|
|
2583
|
-
},
|
|
2584
|
-
{
|
|
2585
|
-
guidance: "do",
|
|
2586
|
-
description: "Make the pressed state visually and programmatically clear."
|
|
2080
|
+
description: "Keep every spoke on the same scale and direction (more is better on all of them)."
|
|
2587
2081
|
},
|
|
2588
2082
|
{
|
|
2589
2083
|
guidance: "do",
|
|
2590
|
-
description:
|
|
2084
|
+
description: "Use filled={false} when two polygons overlap heavily, so both outlines stay visible."
|
|
2591
2085
|
},
|
|
2592
2086
|
{
|
|
2593
2087
|
guidance: "dont",
|
|
2594
|
-
description: "
|
|
2088
|
+
description: "Use fewer than five spokes \u2014 three or four make a triangle or square that says little; use BarChart."
|
|
2595
2089
|
},
|
|
2596
2090
|
{
|
|
2597
2091
|
guidance: "dont",
|
|
2598
|
-
description:
|
|
2599
|
-
}
|
|
2600
|
-
],
|
|
2601
|
-
anatomy: [
|
|
2602
|
-
{
|
|
2603
|
-
name: "Toggle",
|
|
2604
|
-
required: true,
|
|
2605
|
-
description: "A single pressed or unpressed control."
|
|
2606
|
-
},
|
|
2607
|
-
{
|
|
2608
|
-
name: "ToggleGroup",
|
|
2609
|
-
required: false,
|
|
2610
|
-
description: "A coordinated group of toggle choices."
|
|
2611
|
-
},
|
|
2612
|
-
{
|
|
2613
|
-
name: "ToggleGroupItem",
|
|
2614
|
-
required: false,
|
|
2615
|
-
description: 'One toggle inside a group \u2014 an alias of Toggle, so <Toggle value="\u2026"> inside a ToggleGroup is the same thing.'
|
|
2092
|
+
description: "Use it for data-heavy views \u2014 streaming or real-time data, thousands of points, zoom and brushing, trading charts. Those belong on a charting library (Recharts, visx, ECharts); Sable's charts are for dashboards, landing pages and decks."
|
|
2616
2093
|
}
|
|
2617
2094
|
]
|
|
2618
2095
|
},
|
|
2619
2096
|
related: {
|
|
2620
2097
|
components: [
|
|
2621
|
-
"
|
|
2622
|
-
"
|
|
2623
|
-
"
|
|
2098
|
+
"BarChart",
|
|
2099
|
+
"LineChart",
|
|
2100
|
+
"Card"
|
|
2624
2101
|
]
|
|
2625
2102
|
},
|
|
2103
|
+
status: "beta",
|
|
2626
2104
|
props: [
|
|
2627
2105
|
{
|
|
2628
|
-
name: "
|
|
2629
|
-
type:
|
|
2630
|
-
|
|
2631
|
-
description: "On ToggleGroup: `default` is a loose row of toggles; `segmented` draws the capsule segmented control (one track, the pressed option filled) shared with ThemeSwitcher. Toggles inside inherit it."
|
|
2106
|
+
name: "data",
|
|
2107
|
+
type: "Record<string, unknown>[]",
|
|
2108
|
+
description: "One row per spoke."
|
|
2632
2109
|
},
|
|
2633
2110
|
{
|
|
2634
|
-
name: "
|
|
2635
|
-
type: "
|
|
2636
|
-
|
|
2637
|
-
description: "On ToggleGroup: allow several toggles pressed at once. The default is single-select, which behaves like a radio group."
|
|
2111
|
+
name: "index",
|
|
2112
|
+
type: "string",
|
|
2113
|
+
description: "The datum field naming each spoke."
|
|
2638
2114
|
},
|
|
2639
2115
|
{
|
|
2640
|
-
name: "
|
|
2641
|
-
type: "
|
|
2642
|
-
|
|
2643
|
-
description: "A two-state button. Uncontrolled via defaultPressed; controlled via pressed + onPressedChange. Pass value to identify it inside a ToggleGroup."
|
|
2644
|
-
}
|
|
2645
|
-
],
|
|
2646
|
-
subcomponents: [
|
|
2647
|
-
{
|
|
2648
|
-
name: "ToggleGroup",
|
|
2649
|
-
type: 'Base UI ToggleGroup props (value, defaultValue, onValueChange, multiple, disabled, orientation, loopFocus) + appearance?: "default" | "segmented" + className',
|
|
2650
|
-
description: "Groups child Toggles and shares pressed state across them \u2014 each Toggle inside carries a value. Defaults to multiple: false (single-select) \u2014 the fit for view-mode switches and segmented controls; pass multiple to allow several pressed at once, e.g. a row of formatting toggles."
|
|
2116
|
+
name: "series",
|
|
2117
|
+
type: "{ key: string; label: string; tone?: ChartTone; color?: string; icon?: string }[]",
|
|
2118
|
+
description: "The measures to plot: the datum field, its name in the legend/tooltip/table, and its colour (a tone by role, or any CSS colour). Omitted, series take the chart palette in order: --chart-1 \u2026 --chart-8, validated for colour-blind separation in light and dark; a theme may override the set."
|
|
2651
2119
|
},
|
|
2652
2120
|
{
|
|
2653
|
-
name: "
|
|
2654
|
-
type: "
|
|
2655
|
-
description: "
|
|
2656
|
-
}
|
|
2121
|
+
name: "label",
|
|
2122
|
+
type: "string",
|
|
2123
|
+
description: "What the chart shows. Its accessible name and the caption of the data table assistive tech reads."
|
|
2124
|
+
},
|
|
2125
|
+
{
|
|
2126
|
+
name: "className",
|
|
2127
|
+
type: "string",
|
|
2128
|
+
default: '"h-72"',
|
|
2129
|
+
description: "Sizes the chart. Set a height."
|
|
2130
|
+
},
|
|
2131
|
+
{
|
|
2132
|
+
name: "grid",
|
|
2133
|
+
type: '"polygon" | "circle" | "none"',
|
|
2134
|
+
default: '"polygon"',
|
|
2135
|
+
description: "The web behind the polygons."
|
|
2136
|
+
},
|
|
2137
|
+
{
|
|
2138
|
+
name: "gridFill",
|
|
2139
|
+
type: "boolean",
|
|
2140
|
+
default: "false",
|
|
2141
|
+
description: "Tint the web's outer area."
|
|
2142
|
+
},
|
|
2143
|
+
{
|
|
2144
|
+
name: "filled",
|
|
2145
|
+
type: "boolean",
|
|
2146
|
+
default: "true",
|
|
2147
|
+
description: "Fill each polygon; `false` draws outlines only."
|
|
2148
|
+
},
|
|
2149
|
+
{
|
|
2150
|
+
name: "dots",
|
|
2151
|
+
type: "boolean",
|
|
2152
|
+
default: "false",
|
|
2153
|
+
description: "Mark every vertex with a dot."
|
|
2154
|
+
},
|
|
2155
|
+
{
|
|
2156
|
+
name: "radiusAxis",
|
|
2157
|
+
type: "boolean",
|
|
2158
|
+
default: "false",
|
|
2159
|
+
description: "Print the ring values along the top spoke."
|
|
2160
|
+
},
|
|
2161
|
+
{
|
|
2162
|
+
name: "legend",
|
|
2163
|
+
type: "boolean",
|
|
2164
|
+
default: "true when there is more than one series",
|
|
2165
|
+
description: "Show the legend."
|
|
2166
|
+
},
|
|
2167
|
+
{
|
|
2168
|
+
name: "tooltip",
|
|
2169
|
+
type: 'boolean | { indicator?: "dot" | "line" | "dashed" | "none"; hideLabel?: boolean; labelFormatter?: (label, datum) => ReactNode; showTotal?: boolean }',
|
|
2170
|
+
default: "true",
|
|
2171
|
+
description: "Show a tooltip on hover and keyboard focus, or configure it: the row marker, hiding the heading, rewriting it, adding a Total row."
|
|
2172
|
+
},
|
|
2173
|
+
{
|
|
2174
|
+
name: "valueFormatter",
|
|
2175
|
+
type: "(value: number) => string",
|
|
2176
|
+
description: 'Format values everywhere (axis, labels, tooltip, table) \u2014 units like "$12,480". Default: compact on the axis, full elsewhere.'
|
|
2177
|
+
},
|
|
2178
|
+
{
|
|
2179
|
+
name: "locale",
|
|
2180
|
+
type: "string",
|
|
2181
|
+
default: "runtime locale",
|
|
2182
|
+
description: "BCP 47 locale for default number and date formatting."
|
|
2183
|
+
},
|
|
2184
|
+
{
|
|
2185
|
+
name: "animate",
|
|
2186
|
+
type: "boolean",
|
|
2187
|
+
default: "true",
|
|
2188
|
+
description: "Grow the polygons from the centre when the chart scrolls into view."
|
|
2189
|
+
},
|
|
2190
|
+
{
|
|
2191
|
+
name: "activeIndex",
|
|
2192
|
+
type: "number | null",
|
|
2193
|
+
description: "The highlighted row (controlled); `null` for none."
|
|
2194
|
+
},
|
|
2195
|
+
{
|
|
2196
|
+
name: "defaultActiveIndex",
|
|
2197
|
+
type: "number | null",
|
|
2198
|
+
description: "The row highlighted at first \u2014 a tooltip already open before anyone hovers."
|
|
2199
|
+
},
|
|
2200
|
+
{
|
|
2201
|
+
name: "onActiveIndexChange",
|
|
2202
|
+
type: "(index: number | null) => void",
|
|
2203
|
+
description: "Called as the pointer or keyboard moves between rows."
|
|
2204
|
+
}
|
|
2657
2205
|
],
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
"
|
|
2664
|
-
"
|
|
2665
|
-
"
|
|
2666
|
-
"
|
|
2206
|
+
tokensUsed: [
|
|
2207
|
+
"tone-edition",
|
|
2208
|
+
"tone-collectible",
|
|
2209
|
+
"tone-standard",
|
|
2210
|
+
"tone-info",
|
|
2211
|
+
"tone-warning",
|
|
2212
|
+
"primary",
|
|
2213
|
+
"muted-foreground",
|
|
2214
|
+
"border",
|
|
2215
|
+
"popover",
|
|
2216
|
+
"popover-foreground",
|
|
2217
|
+
"foreground",
|
|
2218
|
+
"background"
|
|
2667
2219
|
],
|
|
2668
|
-
|
|
2220
|
+
motion: [
|
|
2221
|
+
{
|
|
2222
|
+
slot: "arrival",
|
|
2223
|
+
recipe: "custom",
|
|
2224
|
+
part: "Polygon",
|
|
2225
|
+
why: "Once on screen each polygon grows from the centre and fades in, series staggered, on the emphasized easing. On the motion tokens; reduced motion shows the finished chart."
|
|
2226
|
+
},
|
|
2669
2227
|
{
|
|
2670
|
-
|
|
2671
|
-
|
|
2228
|
+
slot: "response",
|
|
2229
|
+
recipe: "none",
|
|
2230
|
+
part: "Active spoke",
|
|
2231
|
+
why: "The hovered spoke's vertices and label highlight at once."
|
|
2672
2232
|
},
|
|
2673
2233
|
{
|
|
2674
|
-
|
|
2675
|
-
|
|
2234
|
+
slot: "transition",
|
|
2235
|
+
recipe: "custom",
|
|
2236
|
+
part: "Polygon",
|
|
2237
|
+
why: "Geometry eases from the old shape to the new one through a CSS `d` transition on the motion tokens (no recipe animates path geometry); browsers without `d` transitions update in place."
|
|
2676
2238
|
},
|
|
2677
2239
|
{
|
|
2678
|
-
|
|
2679
|
-
|
|
2240
|
+
slot: "activity",
|
|
2241
|
+
recipe: "none",
|
|
2242
|
+
part: "RadarChart",
|
|
2243
|
+
why: "A chart has no indeterminate state; show a Skeleton while the data loads."
|
|
2244
|
+
}
|
|
2245
|
+
],
|
|
2246
|
+
a11y: [
|
|
2247
|
+
"The drawing is aria-hidden; the same numbers are in a visually hidden data table (captioned by `label`) that the chart is aria-describedby, so assistive tech reads values, not shapes.",
|
|
2248
|
+
'With a tooltip, the chart is one tab stop (role=group, aria-roledescription="chart"): arrow keys, Home and End step through the data, Escape clears, and each step is announced in a polite live region.',
|
|
2249
|
+
"Colour is never the only key: series are named in the legend, tooltip and table."
|
|
2250
|
+
],
|
|
2251
|
+
examples: [
|
|
2252
|
+
{
|
|
2253
|
+
label: "Two profiles",
|
|
2254
|
+
code: 'const scores = [\n { skill: "Speed", a: 86, b: 65 },\n { skill: "Quality", a: 72, b: 90 },\n { skill: "Support", a: 64, b: 80 },\n { skill: "Price", a: 90, b: 55 },\n { skill: "Docs", a: 70, b: 85 },\n { skill: "Ecosystem", a: 60, b: 75 },\n]\n\n<RadarChart\n label="Product scores compared"\n data={scores}\n index="skill"\n series={[\n { key: "a", label: "Atlas" },\n { key: "b", label: "Beacon" },\n ]}\n/>'
|
|
2680
2255
|
},
|
|
2681
2256
|
{
|
|
2682
|
-
label: "
|
|
2683
|
-
code: '<
|
|
2257
|
+
label: "Circular web, outlines only",
|
|
2258
|
+
code: '<RadarChart\n label="Atlas scores"\n data={scores}\n index="skill"\n series={[{ key: "a", label: "Atlas" }]}\n grid="circle"\n gridFill\n filled={false}\n dots\n/>'
|
|
2684
2259
|
}
|
|
2685
2260
|
]
|
|
2686
2261
|
};
|
|
@@ -4333,211 +3908,6 @@ var toastMeta = {
|
|
|
4333
3908
|
}
|
|
4334
3909
|
]
|
|
4335
3910
|
};
|
|
4336
|
-
var Accordion = Accordion$1.Root;
|
|
4337
|
-
function AccordionItem({ className, ...props }) {
|
|
4338
|
-
return /* @__PURE__ */ jsx(
|
|
4339
|
-
Accordion$1.Item,
|
|
4340
|
-
{
|
|
4341
|
-
className: cn("border-b border-border", className),
|
|
4342
|
-
...props
|
|
4343
|
-
}
|
|
4344
|
-
);
|
|
4345
|
-
}
|
|
4346
|
-
function AccordionTrigger({
|
|
4347
|
-
className,
|
|
4348
|
-
children,
|
|
4349
|
-
indicator,
|
|
4350
|
-
...props
|
|
4351
|
-
}) {
|
|
4352
|
-
return /* @__PURE__ */ jsx(Accordion$1.Header, { children: /* @__PURE__ */ jsxs(
|
|
4353
|
-
Accordion$1.Trigger,
|
|
4354
|
-
{
|
|
4355
|
-
className: cn(
|
|
4356
|
-
"flex w-full items-start justify-between gap-3 py-4 text-start text-title-sm text-foreground motion-interactive hover:text-muted-foreground rounded-sm focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50",
|
|
4357
|
-
!indicator && "[&[data-panel-open]_[data-slot=accordion-chevron]]:rotate-180",
|
|
4358
|
-
className
|
|
4359
|
-
),
|
|
4360
|
-
...props,
|
|
4361
|
-
children: [
|
|
4362
|
-
children,
|
|
4363
|
-
/* @__PURE__ */ jsx(
|
|
4364
|
-
"span",
|
|
4365
|
-
{
|
|
4366
|
-
"data-slot": "accordion-chevron",
|
|
4367
|
-
className: "flex h-[1lh] shrink-0 items-center motion-interactive",
|
|
4368
|
-
children: indicator ?? /* @__PURE__ */ jsx(Icon, { name: "chevron-down", className: "text-xs text-muted-foreground" })
|
|
4369
|
-
}
|
|
4370
|
-
)
|
|
4371
|
-
]
|
|
4372
|
-
}
|
|
4373
|
-
) });
|
|
4374
|
-
}
|
|
4375
|
-
function AccordionPanel({ className, children, ...props }) {
|
|
4376
|
-
return /* @__PURE__ */ jsx(
|
|
4377
|
-
Accordion$1.Panel,
|
|
4378
|
-
{
|
|
4379
|
-
className: cn(
|
|
4380
|
-
"h-[var(--accordion-panel-height)] text-body-md text-muted-foreground motion-disclosure data-[ending-style]:h-0 data-[starting-style]:h-0",
|
|
4381
|
-
className
|
|
4382
|
-
),
|
|
4383
|
-
...props,
|
|
4384
|
-
children: /* @__PURE__ */ jsx("div", { className: "pb-4", children })
|
|
4385
|
-
}
|
|
4386
|
-
);
|
|
4387
|
-
}
|
|
4388
|
-
|
|
4389
|
-
// src/components/accordion/accordion.meta.ts
|
|
4390
|
-
var accordionMeta = {
|
|
4391
|
-
name: "Accordion",
|
|
4392
|
-
summary: "Stacked, collapsible disclosure sections over Base UI Accordion \u2014 roving focus, arrow-key navigation, ARIA region/heading/button wiring, and an animated height transition. Each item pairs a full-width trigger (with a rotating chevron) and a panel.",
|
|
4393
|
-
status: "stable",
|
|
4394
|
-
keywords: [
|
|
4395
|
-
"accordion",
|
|
4396
|
-
"disclosure",
|
|
4397
|
-
"collapse",
|
|
4398
|
-
"faq"
|
|
4399
|
-
],
|
|
4400
|
-
usage: {
|
|
4401
|
-
description: "Use Accordion to reveal a vertical set of related sections while keeping the surrounding page scannable.",
|
|
4402
|
-
bestPractices: [
|
|
4403
|
-
{
|
|
4404
|
-
guidance: "do",
|
|
4405
|
-
description: "Use concise trigger text that describes the content revealed below it."
|
|
4406
|
-
},
|
|
4407
|
-
{
|
|
4408
|
-
guidance: "do",
|
|
4409
|
-
description: "Keep section content focused so expanding one item does not feel like navigating to a new page."
|
|
4410
|
-
},
|
|
4411
|
-
{
|
|
4412
|
-
guidance: "dont",
|
|
4413
|
-
description: "Do not hide required fields, primary actions, or critical errors inside collapsed sections."
|
|
4414
|
-
},
|
|
4415
|
-
{
|
|
4416
|
-
guidance: "dont",
|
|
4417
|
-
description: "Do not nest an Accordion inside an Accordion. Two layers of roving focus fight each other, and the arrow keys become ambiguous \u2014 flatten the hierarchy or use a Tree."
|
|
4418
|
-
}
|
|
4419
|
-
],
|
|
4420
|
-
anatomy: [
|
|
4421
|
-
{
|
|
4422
|
-
name: "AccordionItem",
|
|
4423
|
-
required: true,
|
|
4424
|
-
description: "One expandable section in the group."
|
|
4425
|
-
},
|
|
4426
|
-
{
|
|
4427
|
-
name: "AccordionTrigger",
|
|
4428
|
-
required: true,
|
|
4429
|
-
description: "The button-like control that opens the section."
|
|
4430
|
-
},
|
|
4431
|
-
{
|
|
4432
|
-
name: "AccordionPanel",
|
|
4433
|
-
required: true,
|
|
4434
|
-
description: "The content revealed by the trigger."
|
|
4435
|
-
}
|
|
4436
|
-
]
|
|
4437
|
-
},
|
|
4438
|
-
related: {
|
|
4439
|
-
components: [
|
|
4440
|
-
"Collapsible",
|
|
4441
|
-
"Tabs"
|
|
4442
|
-
]
|
|
4443
|
-
},
|
|
4444
|
-
props: [
|
|
4445
|
-
{
|
|
4446
|
-
name: "Accordion (Root)",
|
|
4447
|
-
type: "Base UI Accordion.Root props (value, defaultValue, multiple, disabled, onValueChange, orientation, loopFocus, keepMounted, hiddenUntilFound)",
|
|
4448
|
-
default: "\u2014",
|
|
4449
|
-
description: "Groups all items and owns the open value(s). Uncontrolled via defaultValue, controlled via value."
|
|
4450
|
-
},
|
|
4451
|
-
{
|
|
4452
|
-
name: "defaultValue",
|
|
4453
|
-
type: "Value[]",
|
|
4454
|
-
default: "[]",
|
|
4455
|
-
description: "Uncontrolled set of item values that start expanded (e.g. ['faq-1'])."
|
|
4456
|
-
},
|
|
4457
|
-
{
|
|
4458
|
-
name: "value",
|
|
4459
|
-
type: "Value[]",
|
|
4460
|
-
default: "\u2014",
|
|
4461
|
-
description: "Controlled set of expanded item values; pair with onValueChange."
|
|
4462
|
-
},
|
|
4463
|
-
{
|
|
4464
|
-
name: "onValueChange",
|
|
4465
|
-
type: "(value: Value[]) => void",
|
|
4466
|
-
description: "Called with the new set of expanded item values when an item opens or closes."
|
|
4467
|
-
},
|
|
4468
|
-
{
|
|
4469
|
-
name: "multiple",
|
|
4470
|
-
type: "boolean",
|
|
4471
|
-
default: "false",
|
|
4472
|
-
description: "Whether more than one item can be open at the same time. When false, opening one closes the others."
|
|
4473
|
-
}
|
|
4474
|
-
],
|
|
4475
|
-
subcomponents: [
|
|
4476
|
-
{
|
|
4477
|
-
name: "AccordionItem",
|
|
4478
|
-
type: "Base UI Accordion.Item props + className (value, disabled, onOpenChange)",
|
|
4479
|
-
description: "Pairs a header with its panel. Give each a stable `value` to control or set initial open state."
|
|
4480
|
-
},
|
|
4481
|
-
{
|
|
4482
|
-
name: "AccordionTrigger",
|
|
4483
|
-
type: "Base UI Accordion.Trigger props + className + indicator?: ReactNode",
|
|
4484
|
-
description: "The header button (Header + Trigger composed). Renders children plus a chevron that rotates via data-[panel-open] by default; pass `indicator` to replace the icon and its open-state behavior entirely (e.g. a plus that becomes an \xD7 for a FAQ-style toggle)."
|
|
4485
|
-
},
|
|
4486
|
-
{
|
|
4487
|
-
name: "AccordionPanel",
|
|
4488
|
-
type: "Base UI Accordion.Panel props + className (keepMounted, hiddenUntilFound)",
|
|
4489
|
-
description: "The collapsible body. Animates height using the --accordion-panel-height CSS var; fades on data-[starting-style]/data-[ending-style]."
|
|
4490
|
-
}
|
|
4491
|
-
],
|
|
4492
|
-
tokensUsed: ["border", "foreground", "muted-foreground", "ring"],
|
|
4493
|
-
motion: [
|
|
4494
|
-
{
|
|
4495
|
-
slot: "arrival",
|
|
4496
|
-
recipe: "none",
|
|
4497
|
-
part: "Root",
|
|
4498
|
-
why: "An accordion is page furniture that is present from first paint. Animating it in would stage content the reader is already scanning past."
|
|
4499
|
-
},
|
|
4500
|
-
{
|
|
4501
|
-
slot: "response",
|
|
4502
|
-
recipe: "motion-interactive",
|
|
4503
|
-
part: "AccordionTrigger",
|
|
4504
|
-
why: "The whole header row is the hit target, and it looks like a heading rather than a control. The hover shift is what tells the reader it can be clicked before they click it."
|
|
4505
|
-
},
|
|
4506
|
-
{
|
|
4507
|
-
slot: "transition",
|
|
4508
|
-
recipe: "motion-disclosure",
|
|
4509
|
-
part: "AccordionPanel",
|
|
4510
|
-
why: "Panel height is the only cue that content came from THIS header rather than appearing at the bottom of the page. Snapping it open moves everything below by a screenful with nothing connecting cause to effect."
|
|
4511
|
-
},
|
|
4512
|
-
{
|
|
4513
|
-
slot: "activity",
|
|
4514
|
-
recipe: "none",
|
|
4515
|
-
part: "Root",
|
|
4516
|
-
why: "Nothing here is indeterminate \u2014 the panel content is already resident. A panel that loads on expand should put its own loader inside the panel."
|
|
4517
|
-
}
|
|
4518
|
-
],
|
|
4519
|
-
a11y: [
|
|
4520
|
-
"Base UI renders the trigger inside an <h3> heading and wires aria-expanded / aria-controls to the panel region.",
|
|
4521
|
-
"Arrow keys move roving focus between triggers; Home/End jump to the first/last; loopFocus wraps by default.",
|
|
4522
|
-
"Each trigger toggles its panel with Enter/Space; the panel is announced as a labelled region.",
|
|
4523
|
-
"A disabled AccordionItem dims and is skipped by keyboard focus while neighbouring items stay interactive.",
|
|
4524
|
-
"The chevron rotation is driven by data-[panel-open] and the focus ring by focus-visible \u2014 both honor reduced-motion via the consumer's transitions."
|
|
4525
|
-
],
|
|
4526
|
-
examples: [
|
|
4527
|
-
{
|
|
4528
|
-
label: "Single (default)",
|
|
4529
|
-
code: '<Accordion defaultValue={["a"]}><AccordionItem value="a"><AccordionTrigger>Is it permanent?</AccordionTrigger><AccordionPanel>Yes \u2014 stored on Arweave.</AccordionPanel></AccordionItem><AccordionItem value="b"><AccordionTrigger>How much?</AccordionTrigger><AccordionPanel>A one-time mint fee.</AccordionPanel></AccordionItem></Accordion>'
|
|
4530
|
-
},
|
|
4531
|
-
{
|
|
4532
|
-
label: "Multiple open",
|
|
4533
|
-
code: '<Accordion multiple defaultValue={["a", "b"]}><AccordionItem value="a"><AccordionTrigger>First</AccordionTrigger><AccordionPanel>One.</AccordionPanel></AccordionItem><AccordionItem value="b"><AccordionTrigger>Second</AccordionTrigger><AccordionPanel>Two.</AccordionPanel></AccordionItem></Accordion>'
|
|
4534
|
-
},
|
|
4535
|
-
{
|
|
4536
|
-
label: "Custom indicator (plus-becomes-\xD7)",
|
|
4537
|
-
code: '<Accordion defaultValue={["a"]}><AccordionItem value="a"><AccordionTrigger indicator={<Icon name="plus" className="text-xs text-muted-foreground transition-transform [[data-panel-open]_&]:rotate-45" />}>Is it permanent?</AccordionTrigger><AccordionPanel>Yes \u2014 stored on Arweave.</AccordionPanel></AccordionItem></Accordion>'
|
|
4538
|
-
}
|
|
4539
|
-
]
|
|
4540
|
-
};
|
|
4541
3911
|
var Collapsible = Collapsible$1.Root;
|
|
4542
3912
|
var CollapsibleTrigger = Collapsible$1.Trigger;
|
|
4543
3913
|
function CollapsibleIndicator({ className }) {
|
|
@@ -4680,320 +4050,45 @@ var collapsibleMeta = {
|
|
|
4680
4050
|
}
|
|
4681
4051
|
]
|
|
4682
4052
|
};
|
|
4683
|
-
var
|
|
4684
|
-
var
|
|
4685
|
-
function
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4053
|
+
var useComboboxFilteredItems = Combobox$1.useFilteredItems;
|
|
4054
|
+
var ComboboxVirtualContext = createContext(null);
|
|
4055
|
+
function Combobox({
|
|
4056
|
+
onItemHighlighted,
|
|
4057
|
+
...props
|
|
4058
|
+
}) {
|
|
4059
|
+
const revealRef = useRef(null);
|
|
4060
|
+
const highlightRef = useRef(onItemHighlighted);
|
|
4061
|
+
highlightRef.current = onItemHighlighted;
|
|
4062
|
+
const handleHighlight = useCallback(
|
|
4063
|
+
(value, details) => {
|
|
4064
|
+
highlightRef.current?.(
|
|
4065
|
+
value,
|
|
4066
|
+
details
|
|
4067
|
+
);
|
|
4068
|
+
const reveal = revealRef.current;
|
|
4069
|
+
if (reveal && details.index >= 0) {
|
|
4070
|
+
queueMicrotask(() => reveal(details.index));
|
|
4071
|
+
}
|
|
4072
|
+
},
|
|
4073
|
+
[]
|
|
4698
4074
|
);
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4075
|
+
const bridge = useMemo(
|
|
4076
|
+
() => ({
|
|
4077
|
+
register(reveal) {
|
|
4078
|
+
revealRef.current = reveal;
|
|
4079
|
+
},
|
|
4080
|
+
value: props.value,
|
|
4081
|
+
isItemEqualToValue: props.isItemEqualToValue
|
|
4082
|
+
}),
|
|
4083
|
+
[props.value, props.isItemEqualToValue]
|
|
4084
|
+
);
|
|
4085
|
+
return /* @__PURE__ */ jsx(ComboboxVirtualContext.Provider, { value: bridge, children: /* @__PURE__ */ jsx(
|
|
4086
|
+
Combobox$1.Root,
|
|
4703
4087
|
{
|
|
4704
|
-
|
|
4705
|
-
|
|
4088
|
+
...props,
|
|
4089
|
+
onItemHighlighted: handleHighlight
|
|
4706
4090
|
}
|
|
4707
|
-
);
|
|
4708
|
-
}
|
|
4709
|
-
function SliderIndicator({ className, ...props }) {
|
|
4710
|
-
return /* @__PURE__ */ jsx(
|
|
4711
|
-
Slider$1.Indicator,
|
|
4712
|
-
{
|
|
4713
|
-
className: cn("h-full rounded-full bg-primary data-[orientation=vertical]:w-full", className),
|
|
4714
|
-
...props
|
|
4715
|
-
}
|
|
4716
|
-
);
|
|
4717
|
-
}
|
|
4718
|
-
function SliderThumb({ className, children, ...props }) {
|
|
4719
|
-
return /* @__PURE__ */ jsxs(
|
|
4720
|
-
Slider$1.Thumb,
|
|
4721
|
-
{
|
|
4722
|
-
className: cn(
|
|
4723
|
-
// The hit target: fixed box, no paint, no transform. Never resize this.
|
|
4724
|
-
"group/thumb size-4 rounded-full motion-interactive",
|
|
4725
|
-
"has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-ring",
|
|
4726
|
-
"data-[disabled]:pointer-events-none",
|
|
4727
|
-
className
|
|
4728
|
-
),
|
|
4729
|
-
...props,
|
|
4730
|
-
children: [
|
|
4731
|
-
/* @__PURE__ */ jsx(
|
|
4732
|
-
"span",
|
|
4733
|
-
{
|
|
4734
|
-
"aria-hidden": true,
|
|
4735
|
-
className: cn(
|
|
4736
|
-
"pointer-events-none absolute inset-0 rounded-full border-2 border-primary bg-background shadow-sm",
|
|
4737
|
-
// The only thing that resizes. Scale (not width/height) so the box
|
|
4738
|
-
// metrics — and the row height around them — never move.
|
|
4739
|
-
"scale-75 motion-interactive ease-spring",
|
|
4740
|
-
"group-hover/thumb:scale-100 group-has-[:focus-visible]/thumb:scale-100",
|
|
4741
|
-
"group-data-[dragging]/thumb:scale-110"
|
|
4742
|
-
)
|
|
4743
|
-
}
|
|
4744
|
-
),
|
|
4745
|
-
children
|
|
4746
|
-
]
|
|
4747
|
-
}
|
|
4748
|
-
);
|
|
4749
|
-
}
|
|
4750
|
-
function Slider({
|
|
4751
|
-
className,
|
|
4752
|
-
...props
|
|
4753
|
-
}) {
|
|
4754
|
-
const current = props.value ?? props.defaultValue;
|
|
4755
|
-
const thumbCount = Array.isArray(current) ? current.length : 1;
|
|
4756
|
-
return /* @__PURE__ */ jsx(
|
|
4757
|
-
Slider$1.Root,
|
|
4758
|
-
{
|
|
4759
|
-
className: cn("w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-auto", className),
|
|
4760
|
-
...props,
|
|
4761
|
-
children: /* @__PURE__ */ jsx(SliderControl, { children: /* @__PURE__ */ jsxs(SliderTrack, { children: [
|
|
4762
|
-
/* @__PURE__ */ jsx(SliderIndicator, {}),
|
|
4763
|
-
Array.from({ length: thumbCount }, (_, i) => /* @__PURE__ */ jsx(SliderThumb, { index: thumbCount > 1 ? i : void 0 }, i))
|
|
4764
|
-
] }) })
|
|
4765
|
-
}
|
|
4766
|
-
);
|
|
4767
|
-
}
|
|
4768
|
-
Slider.displayName = "Slider";
|
|
4769
|
-
|
|
4770
|
-
// src/components/slider/slider.meta.ts
|
|
4771
|
-
var sliderMeta = {
|
|
4772
|
-
name: "Slider",
|
|
4773
|
-
summary: "Slider over Base UI Slider \u2014 pointer + keyboard control, ARIA slider wiring, and range support via multiple thumbs. The default export composes Root + Control + Track + Indicator + Thumb; pass an array value for a range.",
|
|
4774
|
-
status: "stable",
|
|
4775
|
-
keywords: [
|
|
4776
|
-
"slider",
|
|
4777
|
-
"range",
|
|
4778
|
-
"value",
|
|
4779
|
-
"input"
|
|
4780
|
-
],
|
|
4781
|
-
usage: {
|
|
4782
|
-
description: "Use Slider for bounded numeric choices where approximate adjustment is faster than typing.",
|
|
4783
|
-
bestPractices: [
|
|
4784
|
-
{
|
|
4785
|
-
guidance: "do",
|
|
4786
|
-
description: "Use visible labels, min/max context, and value output when precision matters."
|
|
4787
|
-
},
|
|
4788
|
-
{
|
|
4789
|
-
guidance: "do",
|
|
4790
|
-
description: "Choose steps that match the real granularity of the setting."
|
|
4791
|
-
},
|
|
4792
|
-
{
|
|
4793
|
-
guidance: "dont",
|
|
4794
|
-
description: "Do not use Slider when users must enter an exact arbitrary number; use NumberField."
|
|
4795
|
-
},
|
|
4796
|
-
{
|
|
4797
|
-
guidance: "dont",
|
|
4798
|
-
description: "Do not use it without showing the value. A slider with no numeric readout makes any precise choice guesswork \u2014 pair it with a ScrubField or a NumberField."
|
|
4799
|
-
},
|
|
4800
|
-
{
|
|
4801
|
-
guidance: "dont",
|
|
4802
|
-
description: "Do not resize the thumb's box on hover, focus, or drag. The thumb is a fixed-size hit target wrapping a decorative dot; only the dot scales. A target that grows under the cursor is a target nobody can learn, and WCAG 2.5.8 measures target size at rest. Restyle the dot, not the box."
|
|
4803
|
-
}
|
|
4804
|
-
],
|
|
4805
|
-
anatomy: [
|
|
4806
|
-
{
|
|
4807
|
-
name: "SliderRoot",
|
|
4808
|
-
required: true,
|
|
4809
|
-
description: "The controlled slider wrapper."
|
|
4810
|
-
},
|
|
4811
|
-
{
|
|
4812
|
-
name: "SliderControl",
|
|
4813
|
-
required: true,
|
|
4814
|
-
description: "The interactive slider control region."
|
|
4815
|
-
},
|
|
4816
|
-
{
|
|
4817
|
-
name: "SliderTrack",
|
|
4818
|
-
required: true,
|
|
4819
|
-
description: "The visible value range."
|
|
4820
|
-
},
|
|
4821
|
-
{
|
|
4822
|
-
name: "SliderIndicator",
|
|
4823
|
-
required: false,
|
|
4824
|
-
description: "The filled portion of the track."
|
|
4825
|
-
},
|
|
4826
|
-
{
|
|
4827
|
-
name: "SliderThumb",
|
|
4828
|
-
required: true,
|
|
4829
|
-
description: "The draggable handle."
|
|
4830
|
-
},
|
|
4831
|
-
{
|
|
4832
|
-
name: "SliderValue",
|
|
4833
|
-
required: false,
|
|
4834
|
-
description: "A readable value display."
|
|
4835
|
-
}
|
|
4836
|
-
]
|
|
4837
|
-
},
|
|
4838
|
-
related: {
|
|
4839
|
-
components: [
|
|
4840
|
-
"NumberField",
|
|
4841
|
-
"Progress"
|
|
4842
|
-
]
|
|
4843
|
-
},
|
|
4844
|
-
props: [
|
|
4845
|
-
{
|
|
4846
|
-
name: "value",
|
|
4847
|
-
type: "number | number[]",
|
|
4848
|
-
default: "\u2014",
|
|
4849
|
-
description: "Controlled value. An array renders a range slider with one thumb per entry."
|
|
4850
|
-
},
|
|
4851
|
-
{
|
|
4852
|
-
name: "defaultValue",
|
|
4853
|
-
type: "number | number[]",
|
|
4854
|
-
default: "\u2014",
|
|
4855
|
-
description: "Uncontrolled initial value. An array renders a range slider."
|
|
4856
|
-
},
|
|
4857
|
-
{
|
|
4858
|
-
name: "onValueChange",
|
|
4859
|
-
type: "(value, eventDetails) => void",
|
|
4860
|
-
default: "\u2014",
|
|
4861
|
-
description: "Fires on every value change. value is a number (or number[] for a range)."
|
|
4862
|
-
},
|
|
4863
|
-
{
|
|
4864
|
-
name: "min",
|
|
4865
|
-
type: "number",
|
|
4866
|
-
default: "0",
|
|
4867
|
-
description: "Minimum allowed value."
|
|
4868
|
-
},
|
|
4869
|
-
{
|
|
4870
|
-
name: "max",
|
|
4871
|
-
type: "number",
|
|
4872
|
-
default: "100",
|
|
4873
|
-
description: "Maximum allowed value."
|
|
4874
|
-
},
|
|
4875
|
-
{
|
|
4876
|
-
name: "step",
|
|
4877
|
-
type: "number",
|
|
4878
|
-
default: "1",
|
|
4879
|
-
description: "Granularity the slider steps through. Snaps to multiples."
|
|
4880
|
-
},
|
|
4881
|
-
{
|
|
4882
|
-
name: "disabled",
|
|
4883
|
-
type: "boolean",
|
|
4884
|
-
default: "false",
|
|
4885
|
-
description: "Ignore user interaction and dim the control."
|
|
4886
|
-
},
|
|
4887
|
-
{
|
|
4888
|
-
name: "orientation",
|
|
4889
|
-
type: "'horizontal' | 'vertical'",
|
|
4890
|
-
default: "horizontal",
|
|
4891
|
-
description: "Axis the slider tracks along. A vertical slider has no intrinsic length \u2014 give it a height (h-40, or flex-1 in a column)."
|
|
4892
|
-
}
|
|
4893
|
-
],
|
|
4894
|
-
subcomponents: [
|
|
4895
|
-
{
|
|
4896
|
-
name: "SliderRoot",
|
|
4897
|
-
type: "Base UI Slider.Root props",
|
|
4898
|
-
description: "The unstyled Base UI Root for custom composition \u2014 owns the value state and forwards value / defaultValue / onValueChange / min / max / step. Drag state surfaces via data-[dragging]. The default Slider export wires all the parts for you."
|
|
4899
|
-
},
|
|
4900
|
-
{
|
|
4901
|
-
name: "SliderControl",
|
|
4902
|
-
type: "Base UI Control props + className",
|
|
4903
|
-
description: "The interactive track lane \u2014 the positioning context the indicator and thumbs sit in; dims when disabled."
|
|
4904
|
-
},
|
|
4905
|
-
{
|
|
4906
|
-
name: "SliderTrack",
|
|
4907
|
-
type: "Base UI Track props + className",
|
|
4908
|
-
description: "The full-range rail beneath the indicator."
|
|
4909
|
-
},
|
|
4910
|
-
{
|
|
4911
|
-
name: "SliderIndicator",
|
|
4912
|
-
type: "Base UI Indicator props + className",
|
|
4913
|
-
description: "The filled portion from min (or the lower thumb) to the current value."
|
|
4914
|
-
},
|
|
4915
|
-
{
|
|
4916
|
-
name: "SliderThumb",
|
|
4917
|
-
type: "Base UI Thumb props + className",
|
|
4918
|
-
description: "A draggable handle: a fixed-size hit target wrapping a decorative dot. The dot rests small and scales to full size on hover, keyboard focus, and drag; the box never changes, so row metrics and the tap target hold. Range sliders render one per value; pass index for SSR. Interaction state surfaces via data-[dragging]."
|
|
4919
|
-
},
|
|
4920
|
-
{
|
|
4921
|
-
name: "SliderValue",
|
|
4922
|
-
type: "Base UI Value props",
|
|
4923
|
-
description: "Formatted value readout for custom compositions (e.g. a live number beside the control)."
|
|
4924
|
-
}
|
|
4925
|
-
],
|
|
4926
|
-
tokensUsed: ["secondary", "primary", "background", "ring"],
|
|
4927
|
-
a11y: [
|
|
4928
|
-
"Base UI provides role=slider, aria-valuemin/max/now, and arrow-key / Page Up-Down / Home-End control.",
|
|
4929
|
-
"Each thumb is a focusable input[type=range] nested in the thumb element; keyboard focus raises a 2px ring on the thumb via has-[:focus-visible] (a bare focus-visible: on the thumb never fires, since the div itself is not focusable).",
|
|
4930
|
-
"The thumb's hit area is a fixed 16px box at every state. Only the decorative inner dot resizes \u2014 rest 12px, full 16px on hover, keyboard focus, and drag \u2014 so the target a pointer or finger must land on never moves or changes size. Keyboard users, who never hover, get the enlarged dot from focus-visible.",
|
|
4931
|
-
"The rest-to-full transition uses the motion duration/easing tokens, so it collapses to an instant state change under prefers-reduced-motion or MotionProvider off. The size change itself still happens: it reports interaction state, it is not decoration.",
|
|
4932
|
-
"Range sliders get one thumb per value with minStepsBetweenValues honored.",
|
|
4933
|
-
"Pass an aria-label (or getAriaLabel on a thumb) so each handle has an accessible name."
|
|
4934
|
-
],
|
|
4935
|
-
examples: [
|
|
4936
|
-
{
|
|
4937
|
-
label: "Single value",
|
|
4938
|
-
code: '<Slider defaultValue={50} aria-label="Volume" />'
|
|
4939
|
-
},
|
|
4940
|
-
{
|
|
4941
|
-
label: "Range",
|
|
4942
|
-
code: 'const [range, setRange] = useState([20, 60])\n<Slider value={range} onValueChange={setRange} aria-label="Price range" />'
|
|
4943
|
-
},
|
|
4944
|
-
{
|
|
4945
|
-
label: "Stepped",
|
|
4946
|
-
code: "<Slider defaultValue={40} min={0} max={100} step={10} />"
|
|
4947
|
-
},
|
|
4948
|
-
{
|
|
4949
|
-
label: "Vertical",
|
|
4950
|
-
code: '<div className="h-40"><Slider orientation="vertical" defaultValue={40} aria-label="Level" /></div>'
|
|
4951
|
-
},
|
|
4952
|
-
{
|
|
4953
|
-
label: "Composed with a value readout",
|
|
4954
|
-
code: '<Field label="Volume"><SliderRoot defaultValue={40} className="flex items-center gap-3"><SliderControl><SliderTrack><SliderIndicator /><SliderThumb /></SliderTrack></SliderControl><SliderValue className="text-mono-sm" /></SliderRoot></Field>'
|
|
4955
|
-
}
|
|
4956
|
-
]
|
|
4957
|
-
};
|
|
4958
|
-
var useComboboxFilteredItems = Combobox$1.useFilteredItems;
|
|
4959
|
-
var ComboboxVirtualContext = createContext(null);
|
|
4960
|
-
function Combobox({
|
|
4961
|
-
onItemHighlighted,
|
|
4962
|
-
...props
|
|
4963
|
-
}) {
|
|
4964
|
-
const revealRef = useRef(null);
|
|
4965
|
-
const highlightRef = useRef(onItemHighlighted);
|
|
4966
|
-
highlightRef.current = onItemHighlighted;
|
|
4967
|
-
const handleHighlight = useCallback(
|
|
4968
|
-
(value, details) => {
|
|
4969
|
-
highlightRef.current?.(
|
|
4970
|
-
value,
|
|
4971
|
-
details
|
|
4972
|
-
);
|
|
4973
|
-
const reveal = revealRef.current;
|
|
4974
|
-
if (reveal && details.index >= 0) {
|
|
4975
|
-
queueMicrotask(() => reveal(details.index));
|
|
4976
|
-
}
|
|
4977
|
-
},
|
|
4978
|
-
[]
|
|
4979
|
-
);
|
|
4980
|
-
const bridge = useMemo(
|
|
4981
|
-
() => ({
|
|
4982
|
-
register(reveal) {
|
|
4983
|
-
revealRef.current = reveal;
|
|
4984
|
-
},
|
|
4985
|
-
value: props.value,
|
|
4986
|
-
isItemEqualToValue: props.isItemEqualToValue
|
|
4987
|
-
}),
|
|
4988
|
-
[props.value, props.isItemEqualToValue]
|
|
4989
|
-
);
|
|
4990
|
-
return /* @__PURE__ */ jsx(ComboboxVirtualContext.Provider, { value: bridge, children: /* @__PURE__ */ jsx(
|
|
4991
|
-
Combobox$1.Root,
|
|
4992
|
-
{
|
|
4993
|
-
...props,
|
|
4994
|
-
onItemHighlighted: handleHighlight
|
|
4995
|
-
}
|
|
4996
|
-
) });
|
|
4091
|
+
) });
|
|
4997
4092
|
}
|
|
4998
4093
|
var ComboboxValue = Combobox$1.Value;
|
|
4999
4094
|
var ComboboxClear = Combobox$1.Clear;
|
|
@@ -5755,7 +4850,7 @@ var contextMenuMeta = {
|
|
|
5755
4850
|
related: {
|
|
5756
4851
|
components: [
|
|
5757
4852
|
"Menu",
|
|
5758
|
-
"
|
|
4853
|
+
"OverflowMenu"
|
|
5759
4854
|
]
|
|
5760
4855
|
},
|
|
5761
4856
|
props: [
|
|
@@ -6244,199 +5339,62 @@ var paginationMeta = {
|
|
|
6244
5339
|
}
|
|
6245
5340
|
]
|
|
6246
5341
|
};
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
|
|
6252
|
-
}
|
|
6253
|
-
|
|
5342
|
+
|
|
5343
|
+
// src/lib/status-tone.ts
|
|
5344
|
+
var STATUS_TONES = ["neutral", "info", "success", "warning", "destructive"];
|
|
5345
|
+
var STATUS_TONE_ICON = {
|
|
5346
|
+
neutral: { name: "info", className: "text-muted-foreground" },
|
|
5347
|
+
info: { name: "info", className: "text-tone-info" },
|
|
5348
|
+
success: { name: "circle-check", className: "text-success" },
|
|
5349
|
+
warning: { name: "warning", className: "text-tone-warning" },
|
|
5350
|
+
destructive: { name: "warning", className: "text-destructive" }
|
|
6254
5351
|
};
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
5352
|
+
var noteVariants = cva(
|
|
5353
|
+
"flex gap-3 rounded-md border p-4 text-body-sm text-foreground",
|
|
5354
|
+
{
|
|
5355
|
+
variants: {
|
|
5356
|
+
tone: {
|
|
5357
|
+
info: "border-tone-info/30 bg-tone-info/10",
|
|
5358
|
+
warning: "border-tone-warning/30 bg-tone-warning/10",
|
|
5359
|
+
success: "border-success/30 bg-success/10",
|
|
5360
|
+
destructive: "border-destructive/30 bg-destructive/10",
|
|
5361
|
+
neutral: "border-border bg-muted/40"
|
|
5362
|
+
}
|
|
5363
|
+
},
|
|
5364
|
+
defaultVariants: {
|
|
5365
|
+
tone: "info"
|
|
5366
|
+
}
|
|
5367
|
+
}
|
|
5368
|
+
);
|
|
5369
|
+
var toneAction = {
|
|
5370
|
+
info: "text-tone-info",
|
|
5371
|
+
warning: "text-tone-warning",
|
|
5372
|
+
success: "text-success",
|
|
5373
|
+
destructive: "text-destructive",
|
|
5374
|
+
neutral: "text-foreground"
|
|
6261
5375
|
};
|
|
6262
|
-
function
|
|
6263
|
-
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
|
|
5376
|
+
function Note({
|
|
5377
|
+
tone = "info",
|
|
5378
|
+
title,
|
|
5379
|
+
icon,
|
|
5380
|
+
action,
|
|
5381
|
+
className,
|
|
5382
|
+
children,
|
|
5383
|
+
...props
|
|
5384
|
+
}) {
|
|
5385
|
+
const resolved = tone ?? "info";
|
|
5386
|
+
const { name, className: iconClassName } = STATUS_TONE_ICON[resolved];
|
|
5387
|
+
const glyph = icon === void 0 ? /* @__PURE__ */ jsx(Icon, { name, className: cn("text-base", iconClassName) }) : icon === null ? null : typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, className: cn("text-base", iconClassName) }) : icon;
|
|
5388
|
+
const leadingIcon = glyph ? /* @__PURE__ */ jsx(
|
|
5389
|
+
"span",
|
|
6267
5390
|
{
|
|
6268
|
-
"data-slot": "
|
|
6269
|
-
className: cn(
|
|
6270
|
-
|
|
6271
|
-
KBD_VARIANTS[variant],
|
|
6272
|
-
className
|
|
6273
|
-
),
|
|
6274
|
-
...props,
|
|
6275
|
-
children: content
|
|
5391
|
+
"data-slot": "note-icon",
|
|
5392
|
+
className: cn("flex h-[1lh] shrink-0 items-center", title ? "text-title-sm" : "text-body-sm"),
|
|
5393
|
+
children: glyph
|
|
6276
5394
|
}
|
|
6277
|
-
);
|
|
6278
|
-
|
|
6279
|
-
|
|
6280
|
-
function KbdGroup({ className, ...props }) {
|
|
6281
|
-
return /* @__PURE__ */ jsx(
|
|
6282
|
-
"kbd",
|
|
6283
|
-
{
|
|
6284
|
-
"data-slot": "kbd-group",
|
|
6285
|
-
className: cn("inline-flex items-center gap-[0.2em] align-middle", className),
|
|
6286
|
-
...props
|
|
6287
|
-
}
|
|
6288
|
-
);
|
|
6289
|
-
}
|
|
6290
|
-
KbdGroup.displayName = "KbdGroup";
|
|
6291
|
-
|
|
6292
|
-
// src/components/kbd/kbd.meta.ts
|
|
6293
|
-
var kbdMeta = {
|
|
6294
|
-
name: "Kbd",
|
|
6295
|
-
summary: 'A keyboard key indicator. Renders a raised <kbd> key cap sized from the surrounding text, so one Kbd fits body copy, menus and tooltips; KbdGroup sets one cap per key for a combo. `mod` renders the platform modifier (\u2318 or Ctrl); `variant="ghost"` is the bare label for menu rows and tooltips.',
|
|
6296
|
-
status: "stable",
|
|
6297
|
-
keywords: [
|
|
6298
|
-
"keyboard",
|
|
6299
|
-
"shortcut",
|
|
6300
|
-
"key",
|
|
6301
|
-
"kbd"
|
|
6302
|
-
],
|
|
6303
|
-
usage: {
|
|
6304
|
-
description: "Use Kbd to display keyboard keys or shortcuts in help text, menus, and docs.",
|
|
6305
|
-
bestPractices: [
|
|
6306
|
-
{
|
|
6307
|
-
guidance: "do",
|
|
6308
|
-
description: "Use it only for literal keyboard input such as Cmd, Enter, or Esc."
|
|
6309
|
-
},
|
|
6310
|
-
{
|
|
6311
|
-
guidance: "do",
|
|
6312
|
-
description: "Show a combo as one cap per key inside KbdGroup \u2014 `<KbdGroup><Kbd mod /><Kbd>K</Kbd></KbdGroup>` \u2014 not several keys typed into one cap."
|
|
6313
|
-
},
|
|
6314
|
-
{
|
|
6315
|
-
guidance: "do",
|
|
6316
|
-
description: 'Use `variant="ghost"` where the shortcut sits on a surface of its own \u2014 a menu row\'s trailing slot, a tooltip \u2014 so it reads as a hint, not a second control.'
|
|
6317
|
-
},
|
|
6318
|
-
{
|
|
6319
|
-
guidance: "dont",
|
|
6320
|
-
description: "Do not use Kbd as a decorative badge or button replacement."
|
|
6321
|
-
},
|
|
6322
|
-
{
|
|
6323
|
-
guidance: "dont",
|
|
6324
|
-
description: "Do not set a text size on a Kbd. It sizes itself from the text around it."
|
|
6325
|
-
},
|
|
6326
|
-
{
|
|
6327
|
-
guidance: "dont",
|
|
6328
|
-
description: "Do not hardcode \u2318 or Ctrl. The modifier differs by platform; use `<Kbd mod />`, which renders \u2318 on macOS/iOS and Ctrl elsewhere, or Windows users read Mac keys."
|
|
6329
|
-
}
|
|
6330
|
-
]
|
|
6331
|
-
},
|
|
6332
|
-
related: {
|
|
6333
|
-
components: [
|
|
6334
|
-
"Code",
|
|
6335
|
-
"Menu",
|
|
6336
|
-
"CommandMenu"
|
|
6337
|
-
]
|
|
6338
|
-
},
|
|
6339
|
-
props: [
|
|
6340
|
-
{
|
|
6341
|
-
name: "mod",
|
|
6342
|
-
type: "boolean",
|
|
6343
|
-
default: "false",
|
|
6344
|
-
description: "Render the platform's shortcut modifier in place of children: \u2318 on macOS and iOS, Ctrl everywhere else. Server-rendered as Ctrl and corrected on hydration."
|
|
6345
|
-
},
|
|
6346
|
-
{
|
|
6347
|
-
name: "variant",
|
|
6348
|
-
type: '"key" | "ghost"',
|
|
6349
|
-
default: '"key"',
|
|
6350
|
-
description: "`key`: a raised cap on the surface it sits on, with a heavier bottom edge as the key's lip. `ghost`: the bare label, for menu rows, tooltips and other surfaces that are already a frame."
|
|
6351
|
-
},
|
|
6352
|
-
{
|
|
6353
|
-
name: "className",
|
|
6354
|
-
type: "string",
|
|
6355
|
-
description: "Merge additional classes onto the key cap."
|
|
6356
|
-
},
|
|
6357
|
-
{
|
|
6358
|
-
name: "...props",
|
|
6359
|
-
type: 'ComponentPropsWithRef<"kbd">',
|
|
6360
|
-
description: "All native <kbd> attributes (children, ref, etc.)."
|
|
6361
|
-
}
|
|
6362
|
-
],
|
|
6363
|
-
tokensUsed: ["border", "border-strong", "background", "muted-foreground"],
|
|
6364
|
-
a11y: [
|
|
6365
|
-
"Uses the semantic <kbd> element so assistive tech announces keyboard input.",
|
|
6366
|
-
"KbdGroup is itself a <kbd> around the per-key caps \u2014 the HTML spelling of one key combination \u2014 so the combo is announced as a single piece of input.",
|
|
6367
|
-
"Sized in em from the surrounding text, so it scales with the user's text size and never shrinks below 12px."
|
|
6368
|
-
],
|
|
6369
|
-
examples: [
|
|
6370
|
-
{
|
|
6371
|
-
label: "Single keys",
|
|
6372
|
-
code: "<Kbd>Esc</Kbd>"
|
|
6373
|
-
},
|
|
6374
|
-
{
|
|
6375
|
-
label: "Combo",
|
|
6376
|
-
code: "<KbdGroup><Kbd mod /><Kbd>K</Kbd></KbdGroup>"
|
|
6377
|
-
},
|
|
6378
|
-
{
|
|
6379
|
-
label: "In a menu row",
|
|
6380
|
-
code: '<MenuItem>Search<KbdGroup className="ms-auto"><Kbd variant="ghost" mod /><Kbd variant="ghost">K</Kbd></KbdGroup></MenuItem>'
|
|
6381
|
-
}
|
|
6382
|
-
]
|
|
6383
|
-
};
|
|
6384
|
-
|
|
6385
|
-
// src/lib/status-tone.ts
|
|
6386
|
-
var STATUS_TONES = ["neutral", "info", "success", "warning", "destructive"];
|
|
6387
|
-
var STATUS_TONE_ICON = {
|
|
6388
|
-
neutral: { name: "info", className: "text-muted-foreground" },
|
|
6389
|
-
info: { name: "info", className: "text-tone-info" },
|
|
6390
|
-
success: { name: "circle-check", className: "text-success" },
|
|
6391
|
-
warning: { name: "warning", className: "text-tone-warning" },
|
|
6392
|
-
destructive: { name: "warning", className: "text-destructive" }
|
|
6393
|
-
};
|
|
6394
|
-
var noteVariants = cva(
|
|
6395
|
-
"flex gap-3 rounded-md border p-4 text-body-sm text-foreground",
|
|
6396
|
-
{
|
|
6397
|
-
variants: {
|
|
6398
|
-
tone: {
|
|
6399
|
-
info: "border-tone-info/30 bg-tone-info/10",
|
|
6400
|
-
warning: "border-tone-warning/30 bg-tone-warning/10",
|
|
6401
|
-
success: "border-success/30 bg-success/10",
|
|
6402
|
-
destructive: "border-destructive/30 bg-destructive/10",
|
|
6403
|
-
neutral: "border-border bg-muted/40"
|
|
6404
|
-
}
|
|
6405
|
-
},
|
|
6406
|
-
defaultVariants: {
|
|
6407
|
-
tone: "info"
|
|
6408
|
-
}
|
|
6409
|
-
}
|
|
6410
|
-
);
|
|
6411
|
-
var toneAction = {
|
|
6412
|
-
info: "text-tone-info",
|
|
6413
|
-
warning: "text-tone-warning",
|
|
6414
|
-
success: "text-success",
|
|
6415
|
-
destructive: "text-destructive",
|
|
6416
|
-
neutral: "text-foreground"
|
|
6417
|
-
};
|
|
6418
|
-
function Note({
|
|
6419
|
-
tone = "info",
|
|
6420
|
-
title,
|
|
6421
|
-
icon,
|
|
6422
|
-
action,
|
|
6423
|
-
className,
|
|
6424
|
-
children,
|
|
6425
|
-
...props
|
|
6426
|
-
}) {
|
|
6427
|
-
const resolved = tone ?? "info";
|
|
6428
|
-
const { name, className: iconClassName } = STATUS_TONE_ICON[resolved];
|
|
6429
|
-
const glyph = icon === void 0 ? /* @__PURE__ */ jsx(Icon, { name, className: cn("text-base", iconClassName) }) : icon === null ? null : typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, className: cn("text-base", iconClassName) }) : icon;
|
|
6430
|
-
const leadingIcon = glyph ? /* @__PURE__ */ jsx(
|
|
6431
|
-
"span",
|
|
6432
|
-
{
|
|
6433
|
-
"data-slot": "note-icon",
|
|
6434
|
-
className: cn("flex h-[1lh] shrink-0 items-center", title ? "text-title-sm" : "text-body-sm"),
|
|
6435
|
-
children: glyph
|
|
6436
|
-
}
|
|
6437
|
-
) : null;
|
|
6438
|
-
return /* @__PURE__ */ jsxs(
|
|
6439
|
-
"div",
|
|
5395
|
+
) : null;
|
|
5396
|
+
return /* @__PURE__ */ jsxs(
|
|
5397
|
+
"div",
|
|
6440
5398
|
{
|
|
6441
5399
|
role: "note",
|
|
6442
5400
|
className: cn(noteVariants({ tone: resolved }), className),
|
|
@@ -8470,7 +7428,7 @@ function TreeRow({
|
|
|
8470
7428
|
}
|
|
8471
7429
|
);
|
|
8472
7430
|
if (subtree === void 0) return row;
|
|
8473
|
-
return /* @__PURE__ */ jsxs(Fragment
|
|
7431
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
8474
7432
|
row,
|
|
8475
7433
|
expanded ? /* @__PURE__ */ jsx(TreeDepthContext.Provider, { value: depth + 1, children: subtree }) : null
|
|
8476
7434
|
] });
|
|
@@ -9688,13 +8646,13 @@ function RadioCard({
|
|
|
9688
8646
|
className
|
|
9689
8647
|
),
|
|
9690
8648
|
...props,
|
|
9691
|
-
children: icon != null ? /* @__PURE__ */ jsxs(Fragment
|
|
8649
|
+
children: icon != null ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9692
8650
|
/* @__PURE__ */ jsxs("span", { className: SELECTION_CARD_TILE_HEAD, children: [
|
|
9693
8651
|
/* @__PURE__ */ jsx("span", { className: cn(SELECTION_CARD_GLYPH, t.glyph), children: icon }),
|
|
9694
8652
|
marker
|
|
9695
8653
|
] }),
|
|
9696
8654
|
content
|
|
9697
|
-
] }) : /* @__PURE__ */ jsxs(Fragment
|
|
8655
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9698
8656
|
/* @__PURE__ */ jsx("span", { className: cn("flex h-[1lh] shrink-0 items-center text-title-sm", titleClassName), children: marker }),
|
|
9699
8657
|
content
|
|
9700
8658
|
] })
|
|
@@ -9927,7 +8885,7 @@ function CheckboxCard({
|
|
|
9927
8885
|
const t = checkboxCardToneClasses[tone];
|
|
9928
8886
|
const box = /* @__PURE__ */ jsx("span", { className: cn(CHECK_BOX, t.box), children: /* @__PURE__ */ jsx(CheckboxMark, { className: t.check }) });
|
|
9929
8887
|
return /* @__PURE__ */ jsxs(
|
|
9930
|
-
Checkbox
|
|
8888
|
+
Checkbox.Root,
|
|
9931
8889
|
{
|
|
9932
8890
|
className: cn(
|
|
9933
8891
|
SELECTION_CARD_SHELL,
|
|
@@ -10761,965 +9719,6 @@ var showMoreMeta = {
|
|
|
10761
9719
|
}
|
|
10762
9720
|
]
|
|
10763
9721
|
};
|
|
10764
|
-
var LINE_HEIGHT = 16;
|
|
10765
|
-
var PAGE_HEIGHT = 400;
|
|
10766
|
-
function toPixels(delta, mode) {
|
|
10767
|
-
if (mode === 1) return delta * LINE_HEIGHT;
|
|
10768
|
-
if (mode === 2) return delta * PAGE_HEIGHT;
|
|
10769
|
-
return delta;
|
|
10770
|
-
}
|
|
10771
|
-
function useWheelToHorizontal(enabled = true) {
|
|
10772
|
-
const nodeRef = useRef(null);
|
|
10773
|
-
const onWheel = useCallback((event) => {
|
|
10774
|
-
const el = nodeRef.current;
|
|
10775
|
-
if (!el) return;
|
|
10776
|
-
if (event.shiftKey) return;
|
|
10777
|
-
if (Math.abs(event.deltaX) > Math.abs(event.deltaY)) return;
|
|
10778
|
-
if (event.deltaY === 0) return;
|
|
10779
|
-
const delta = toPixels(event.deltaY, event.deltaMode);
|
|
10780
|
-
const max = el.scrollWidth - el.clientWidth;
|
|
10781
|
-
if (max <= 0) return;
|
|
10782
|
-
const atStart = Math.round(el.scrollLeft) <= 0;
|
|
10783
|
-
const atEnd = Math.round(el.scrollLeft) >= max;
|
|
10784
|
-
if (delta < 0 && atStart || delta > 0 && atEnd) return;
|
|
10785
|
-
event.preventDefault();
|
|
10786
|
-
el.scrollLeft += delta;
|
|
10787
|
-
}, []);
|
|
10788
|
-
return useCallback(
|
|
10789
|
-
(node) => {
|
|
10790
|
-
const previous = nodeRef.current;
|
|
10791
|
-
if (previous === node) return;
|
|
10792
|
-
previous?.removeEventListener("wheel", onWheel);
|
|
10793
|
-
nodeRef.current = node;
|
|
10794
|
-
if (node && enabled) {
|
|
10795
|
-
node.addEventListener("wheel", onWheel, { passive: false });
|
|
10796
|
-
}
|
|
10797
|
-
},
|
|
10798
|
-
[enabled, onWheel]
|
|
10799
|
-
);
|
|
10800
|
-
}
|
|
10801
|
-
function scrollRegionKeyDown(event, orientation) {
|
|
10802
|
-
const el = event.currentTarget;
|
|
10803
|
-
const horizontal = orientation === "horizontal";
|
|
10804
|
-
const page = horizontal ? el.clientWidth : el.clientHeight;
|
|
10805
|
-
const step = Math.max(40, page * 0.2);
|
|
10806
|
-
const back = horizontal ? "ArrowLeft" : "ArrowUp";
|
|
10807
|
-
const forward = horizontal ? "ArrowRight" : "ArrowDown";
|
|
10808
|
-
const axis = horizontal ? "scrollLeft" : "scrollTop";
|
|
10809
|
-
const max = horizontal ? el.scrollWidth - el.clientWidth : el.scrollHeight - el.clientHeight;
|
|
10810
|
-
let next = null;
|
|
10811
|
-
if (event.key === back) next = el[axis] - step;
|
|
10812
|
-
else if (event.key === forward) next = el[axis] + step;
|
|
10813
|
-
else if (event.key === "Home") next = 0;
|
|
10814
|
-
else if (event.key === "End") next = max;
|
|
10815
|
-
else if (event.key === "PageUp") next = el[axis] - page;
|
|
10816
|
-
else if (event.key === "PageDown") next = el[axis] + page;
|
|
10817
|
-
if (next === null) return;
|
|
10818
|
-
event.preventDefault();
|
|
10819
|
-
el[axis] = Math.max(0, Math.min(max, next));
|
|
10820
|
-
}
|
|
10821
|
-
function Scroller({
|
|
10822
|
-
orientation = "vertical",
|
|
10823
|
-
wheelScroll = true,
|
|
10824
|
-
label,
|
|
10825
|
-
className,
|
|
10826
|
-
viewportClassName,
|
|
10827
|
-
children,
|
|
10828
|
-
ref,
|
|
10829
|
-
style,
|
|
10830
|
-
onScroll,
|
|
10831
|
-
onKeyDown,
|
|
10832
|
-
...props
|
|
10833
|
-
}) {
|
|
10834
|
-
const regionRef = useRef(null);
|
|
10835
|
-
const isHorizontal = orientation === "horizontal";
|
|
10836
|
-
const trackActivity = useScrollActivity();
|
|
10837
|
-
const trackWheel = useWheelToHorizontal(isHorizontal && wheelScroll);
|
|
10838
|
-
const setRegion = useCallback(
|
|
10839
|
-
(node) => {
|
|
10840
|
-
setRef(ref, node);
|
|
10841
|
-
trackActivity(node);
|
|
10842
|
-
trackWheel(node);
|
|
10843
|
-
regionRef.current = node;
|
|
10844
|
-
},
|
|
10845
|
-
[ref, trackActivity, trackWheel]
|
|
10846
|
-
);
|
|
10847
|
-
const handleKeyDown = useCallback(
|
|
10848
|
-
(event) => {
|
|
10849
|
-
onKeyDown?.(event);
|
|
10850
|
-
if (event.defaultPrevented) return;
|
|
10851
|
-
scrollRegionKeyDown(event, orientation);
|
|
10852
|
-
},
|
|
10853
|
-
[onKeyDown, orientation]
|
|
10854
|
-
);
|
|
10855
|
-
const [showStart, setShowStart] = useState(false);
|
|
10856
|
-
const [showEnd, setShowEnd] = useState(false);
|
|
10857
|
-
const sync = useCallback(
|
|
10858
|
-
(el) => {
|
|
10859
|
-
if (orientation === "horizontal") {
|
|
10860
|
-
const { scrollLeft, scrollWidth, clientWidth } = el;
|
|
10861
|
-
setShowStart(scrollLeft > 0);
|
|
10862
|
-
setShowEnd(scrollLeft < scrollWidth - clientWidth - 1);
|
|
10863
|
-
} else {
|
|
10864
|
-
const { scrollTop, scrollHeight, clientHeight } = el;
|
|
10865
|
-
setShowStart(scrollTop > 0);
|
|
10866
|
-
setShowEnd(scrollTop < scrollHeight - clientHeight - 1);
|
|
10867
|
-
}
|
|
10868
|
-
},
|
|
10869
|
-
[orientation]
|
|
10870
|
-
);
|
|
10871
|
-
useLayoutEffect(() => {
|
|
10872
|
-
if (regionRef.current) sync(regionRef.current);
|
|
10873
|
-
}, [sync]);
|
|
10874
|
-
const handleScroll = useCallback(
|
|
10875
|
-
(event) => {
|
|
10876
|
-
sync(event.currentTarget);
|
|
10877
|
-
onScroll?.(event);
|
|
10878
|
-
},
|
|
10879
|
-
[sync, onScroll]
|
|
10880
|
-
);
|
|
10881
|
-
const isVertical = orientation === "vertical";
|
|
10882
|
-
const fade = "calc(var(--spacing) * 7)";
|
|
10883
|
-
const mask = showStart || showEnd ? `linear-gradient(to ${isVertical ? "bottom" : "right"}, ${showStart ? "transparent" : "#000"}, #000 ${showStart ? fade : "0"}, #000 ${showEnd ? `calc(100% - ${fade})` : "100%"}, ${showEnd ? "transparent" : "#000"})` : void 0;
|
|
10884
|
-
return (
|
|
10885
|
-
// The chrome box. Carries the consumer's border/background/radius and the
|
|
10886
|
-
// sizing, and is NOT masked — which is the entire point of the split. A
|
|
10887
|
-
// flex column so the scroller below can be `flex-1 min-h-0`, which bounds
|
|
10888
|
-
// off `h-*`, `max-h-*` and `flex-1` alike with no percentage to resolve.
|
|
10889
|
-
/* @__PURE__ */ jsx("div", { style, className: cn("flex flex-col", className), children: /* @__PURE__ */ jsx(
|
|
10890
|
-
"div",
|
|
10891
|
-
{
|
|
10892
|
-
ref: setRegion,
|
|
10893
|
-
onScroll: handleScroll,
|
|
10894
|
-
onKeyDown: handleKeyDown,
|
|
10895
|
-
tabIndex: 0,
|
|
10896
|
-
role: "group",
|
|
10897
|
-
"aria-label": label,
|
|
10898
|
-
"data-orientation": orientation,
|
|
10899
|
-
style: mask ? { maskImage: mask, WebkitMaskImage: mask } : void 0,
|
|
10900
|
-
className: cn(
|
|
10901
|
-
// Fill the chrome box without a percentage height. `min-h-0` /
|
|
10902
|
-
// `min-w-0` because a flex item's default `min-size: auto` refuses to
|
|
10903
|
-
// shrink below its content — which is exactly how a scroll region
|
|
10904
|
-
// silently stops scrolling.
|
|
10905
|
-
"min-h-0 min-w-0 flex-1",
|
|
10906
|
-
// Inherit the wrapper's radius so content clips to the same curve the
|
|
10907
|
-
// border draws; a scroll container already clips to its own padding
|
|
10908
|
-
// box, so this needs no `overflow-hidden` fighting the scroll axis.
|
|
10909
|
-
"rounded-[inherit]",
|
|
10910
|
-
"motion-interactive focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
10911
|
-
// Contained overscroll: a Scroller is almost always a bounded region
|
|
10912
|
-
// inside a page or a popup, and a wheel gesture that reaches its end
|
|
10913
|
-
// should stop rather than start scrolling whatever is behind it.
|
|
10914
|
-
"overscroll-contain",
|
|
10915
|
-
isVertical ? (
|
|
10916
|
-
// Vertical: a thin bar, revealed while scrolling. The fade says
|
|
10917
|
-
// "more below"; the bar says how much and where you are — a long
|
|
10918
|
-
// list needs both.
|
|
10919
|
-
"scrollbar-quiet overflow-y-auto overflow-x-hidden"
|
|
10920
|
-
) : (
|
|
10921
|
-
// Horizontal: no bar at all. The edge fades this component already
|
|
10922
|
-
// draws ARE the affordance, and they say the same thing better —
|
|
10923
|
-
// which is the carousel read a media strip or chip row wants. A
|
|
10924
|
-
// 4px rail under a row of tiles adds chrome and no information.
|
|
10925
|
-
"scrollbar-hidden overflow-x-auto overflow-y-hidden"
|
|
10926
|
-
),
|
|
10927
|
-
viewportClassName
|
|
10928
|
-
),
|
|
10929
|
-
...props,
|
|
10930
|
-
children
|
|
10931
|
-
}
|
|
10932
|
-
) })
|
|
10933
|
-
);
|
|
10934
|
-
}
|
|
10935
|
-
Scroller.displayName = "Scroller";
|
|
10936
|
-
function defaultFilter(items, query) {
|
|
10937
|
-
const q = query.trim().toLowerCase();
|
|
10938
|
-
if (!q) return items;
|
|
10939
|
-
return items.filter((item) => item.label.toLowerCase().includes(q));
|
|
10940
|
-
}
|
|
10941
|
-
function useCommandQuery(queryProp, onQueryChange) {
|
|
10942
|
-
const [ownQuery, setOwnQuery] = useState("");
|
|
10943
|
-
const query = queryProp ?? ownQuery;
|
|
10944
|
-
const setQuery = (next) => {
|
|
10945
|
-
if (queryProp === void 0) setOwnQuery(next);
|
|
10946
|
-
onQueryChange?.(next);
|
|
10947
|
-
};
|
|
10948
|
-
return [query, setQuery];
|
|
10949
|
-
}
|
|
10950
|
-
var COMMAND_GEOMETRY = "[--cm-inset:max(--spacing(1),calc(var(--radius-lg)/3))] [--cm-pad:max(--spacing(2),calc(--spacing(4)-var(--cm-inset)))]";
|
|
10951
|
-
function CommandMenuBody({
|
|
10952
|
-
query,
|
|
10953
|
-
setQuery,
|
|
10954
|
-
items,
|
|
10955
|
-
filter,
|
|
10956
|
-
listClassName,
|
|
10957
|
-
placeholder,
|
|
10958
|
-
emptyMessage,
|
|
10959
|
-
title,
|
|
10960
|
-
scope,
|
|
10961
|
-
inputProps,
|
|
10962
|
-
children,
|
|
10963
|
-
close,
|
|
10964
|
-
open,
|
|
10965
|
-
inline
|
|
10966
|
-
}) {
|
|
10967
|
-
const [activeId, setActiveId] = useState(null);
|
|
10968
|
-
const listRef = useRef(null);
|
|
10969
|
-
const listboxId = useId();
|
|
10970
|
-
const tier = useTypeTier();
|
|
10971
|
-
const grouped = useMemo(() => {
|
|
10972
|
-
if (!items) return [];
|
|
10973
|
-
const visible = filter === null ? items : filter ? filter(items, query) : defaultFilter(items, query);
|
|
10974
|
-
const map = /* @__PURE__ */ new Map();
|
|
10975
|
-
for (const item of visible) {
|
|
10976
|
-
const bucket = map.get(item.group);
|
|
10977
|
-
if (bucket) bucket.push(item);
|
|
10978
|
-
else map.set(item.group, [item]);
|
|
10979
|
-
}
|
|
10980
|
-
return Array.from(map, ([group, groupItems]) => ({ group, groupItems }));
|
|
10981
|
-
}, [items, query, filter]);
|
|
10982
|
-
const q = filter === void 0 ? query.trim().toLowerCase() : "";
|
|
10983
|
-
const options = () => Array.from(
|
|
10984
|
-
listRef.current?.querySelectorAll('[role="option"]') ?? []
|
|
10985
|
-
);
|
|
10986
|
-
const seedActive = useCallback(() => {
|
|
10987
|
-
const all = options();
|
|
10988
|
-
const runnable = all.find(
|
|
10989
|
-
(option) => option.getAttribute("aria-disabled") !== "true"
|
|
10990
|
-
);
|
|
10991
|
-
setActiveId((runnable ?? all[0])?.id ?? null);
|
|
10992
|
-
}, []);
|
|
10993
|
-
const [resultCount, setResultCount] = useState(0);
|
|
10994
|
-
useEffect(() => {
|
|
10995
|
-
const all = options();
|
|
10996
|
-
const next = all.length;
|
|
10997
|
-
setResultCount((current) => current === next ? current : next);
|
|
10998
|
-
if (inline && (activeId ? !all.some((option) => option.id === activeId) : next > 0)) {
|
|
10999
|
-
seedActive();
|
|
11000
|
-
}
|
|
11001
|
-
});
|
|
11002
|
-
const attachList = useCallback(
|
|
11003
|
-
(node) => {
|
|
11004
|
-
listRef.current = node;
|
|
11005
|
-
if (node) seedActive();
|
|
11006
|
-
},
|
|
11007
|
-
[seedActive]
|
|
11008
|
-
);
|
|
11009
|
-
useEffect(() => {
|
|
11010
|
-
if (open) seedActive();
|
|
11011
|
-
}, [open, query]);
|
|
11012
|
-
const onInputKeyDown = (event) => {
|
|
11013
|
-
inputProps?.onKeyDown?.(event);
|
|
11014
|
-
if (event.defaultPrevented) return;
|
|
11015
|
-
if (inline && event.key === "Escape") {
|
|
11016
|
-
if (query) {
|
|
11017
|
-
event.preventDefault();
|
|
11018
|
-
event.stopPropagation();
|
|
11019
|
-
setQuery("");
|
|
11020
|
-
}
|
|
11021
|
-
return;
|
|
11022
|
-
}
|
|
11023
|
-
const opts = options();
|
|
11024
|
-
if (opts.length === 0) return;
|
|
11025
|
-
const index = opts.findIndex((o) => o.id === activeId);
|
|
11026
|
-
const move = (next) => {
|
|
11027
|
-
if (!next) return;
|
|
11028
|
-
event.preventDefault();
|
|
11029
|
-
setActiveId(next.id);
|
|
11030
|
-
next.scrollIntoView({ block: "nearest" });
|
|
11031
|
-
};
|
|
11032
|
-
switch (event.key) {
|
|
11033
|
-
case "ArrowDown":
|
|
11034
|
-
move(index < 0 ? opts[0] : opts[(index + 1) % opts.length]);
|
|
11035
|
-
break;
|
|
11036
|
-
case "ArrowUp":
|
|
11037
|
-
move(
|
|
11038
|
-
index < 0 ? opts[opts.length - 1] : opts[(index - 1 + opts.length) % opts.length]
|
|
11039
|
-
);
|
|
11040
|
-
break;
|
|
11041
|
-
case "Home":
|
|
11042
|
-
move(opts[0]);
|
|
11043
|
-
break;
|
|
11044
|
-
case "End":
|
|
11045
|
-
move(opts[opts.length - 1]);
|
|
11046
|
-
break;
|
|
11047
|
-
// A screenful, near enough. Rows are a fixed height, so "ten" is the
|
|
11048
|
-
// same distance every time — measuring the listbox would be more exact
|
|
11049
|
-
// and less predictable. Clamped rather than wrapped: a page jump that
|
|
11050
|
-
// wraps to the far end is a jump nobody asked for.
|
|
11051
|
-
case "PageDown":
|
|
11052
|
-
move(opts[Math.min(opts.length - 1, (index < 0 ? 0 : index) + 10)]);
|
|
11053
|
-
break;
|
|
11054
|
-
case "PageUp":
|
|
11055
|
-
move(opts[Math.max(0, (index < 0 ? 0 : index) - 10)]);
|
|
11056
|
-
break;
|
|
11057
|
-
case "Enter": {
|
|
11058
|
-
const active = opts.find((o) => o.id === activeId) ?? opts[0];
|
|
11059
|
-
if (active) {
|
|
11060
|
-
event.preventDefault();
|
|
11061
|
-
if (active.getAttribute("aria-disabled") !== "true") active.click();
|
|
11062
|
-
}
|
|
11063
|
-
break;
|
|
11064
|
-
}
|
|
11065
|
-
}
|
|
11066
|
-
};
|
|
11067
|
-
return /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
11068
|
-
/* @__PURE__ */ jsxs("div", { className: cn("flex shrink-0 items-center gap-2 border-b border-border px-[calc(var(--cm-inset)+var(--cm-pad))] py-3", COMMAND_GEOMETRY), children: [
|
|
11069
|
-
/* @__PURE__ */ jsx(Icon, { name: "search", className: "text-base text-muted-foreground" }),
|
|
11070
|
-
scope ? /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center", children: scope }) : null,
|
|
11071
|
-
/* @__PURE__ */ jsx(
|
|
11072
|
-
"input",
|
|
11073
|
-
{
|
|
11074
|
-
autoFocus: !inline,
|
|
11075
|
-
type: "text",
|
|
11076
|
-
...inputProps,
|
|
11077
|
-
"data-command-menu-input": inline ? void 0 : "",
|
|
11078
|
-
role: "combobox",
|
|
11079
|
-
"aria-expanded": true,
|
|
11080
|
-
"aria-controls": listboxId,
|
|
11081
|
-
"aria-activedescendant": activeId ?? void 0,
|
|
11082
|
-
"aria-autocomplete": "list",
|
|
11083
|
-
value: query,
|
|
11084
|
-
onChange: (event) => setQuery(event.target.value),
|
|
11085
|
-
onKeyDown: onInputKeyDown,
|
|
11086
|
-
placeholder,
|
|
11087
|
-
"aria-label": inputProps?.["aria-label"] ?? "Command search",
|
|
11088
|
-
className: cn(
|
|
11089
|
-
`w-full border-0 bg-transparent ${tier.body} text-foreground outline-none placeholder:text-muted-foreground`,
|
|
11090
|
-
inputProps?.className
|
|
11091
|
-
)
|
|
11092
|
-
}
|
|
11093
|
-
)
|
|
11094
|
-
] }),
|
|
11095
|
-
/* @__PURE__ */ jsx("div", { role: "status", "aria-live": "polite", className: "sr-only", children: resultCount === 0 ? "No results" : `${resultCount} result${resultCount === 1 ? "" : "s"}` }),
|
|
11096
|
-
/* @__PURE__ */ jsxs(
|
|
11097
|
-
Scroller,
|
|
11098
|
-
{
|
|
11099
|
-
ref: attachList,
|
|
11100
|
-
role: "listbox",
|
|
11101
|
-
id: listboxId,
|
|
11102
|
-
"aria-label": title,
|
|
11103
|
-
className: cn("min-h-0 max-h-80 p-(--cm-inset)", COMMAND_GEOMETRY, listClassName),
|
|
11104
|
-
children: [
|
|
11105
|
-
/* @__PURE__ */ jsx(
|
|
11106
|
-
CommandQueryContext.Provider,
|
|
11107
|
-
{
|
|
11108
|
-
value: { query: children ? q : "", close, activeId, setActiveId },
|
|
11109
|
-
children: children ?? grouped.map(({ group, groupItems }) => /* @__PURE__ */ jsx(CommandMenuGroup, { label: group, children: groupItems.map((item) => /* @__PURE__ */ jsx(
|
|
11110
|
-
CommandMenuItem,
|
|
11111
|
-
{
|
|
11112
|
-
id: item.id ? `${listboxId}-${item.id}` : void 0,
|
|
11113
|
-
icon: item.icon,
|
|
11114
|
-
detail: item.detail,
|
|
11115
|
-
hint: item.hint,
|
|
11116
|
-
disabled: item.disabled,
|
|
11117
|
-
onSelect: () => {
|
|
11118
|
-
item.onSelect();
|
|
11119
|
-
close();
|
|
11120
|
-
},
|
|
11121
|
-
children: item.label
|
|
11122
|
-
},
|
|
11123
|
-
item.id ?? `${group}:${item.label}`
|
|
11124
|
-
)) }, group))
|
|
11125
|
-
}
|
|
11126
|
-
),
|
|
11127
|
-
(children ? resultCount === 0 : grouped.length === 0) && /* @__PURE__ */ jsx("div", { className: "px-(--cm-pad) py-6 text-center text-body-sm text-muted-foreground", children: emptyMessage })
|
|
11128
|
-
]
|
|
11129
|
-
}
|
|
11130
|
-
)
|
|
11131
|
-
] });
|
|
11132
|
-
}
|
|
11133
|
-
function CommandMenu({
|
|
11134
|
-
open,
|
|
11135
|
-
onOpenChange,
|
|
11136
|
-
items,
|
|
11137
|
-
query: queryProp,
|
|
11138
|
-
onQueryChange,
|
|
11139
|
-
filter,
|
|
11140
|
-
listClassName,
|
|
11141
|
-
placeholder = "Type a command or search\u2026",
|
|
11142
|
-
emptyMessage = "No results found.",
|
|
11143
|
-
title = "Command menu",
|
|
11144
|
-
description = "Search for a command and press Enter to run it.",
|
|
11145
|
-
finalFocus,
|
|
11146
|
-
scope,
|
|
11147
|
-
inputProps,
|
|
11148
|
-
children,
|
|
11149
|
-
className,
|
|
11150
|
-
...props
|
|
11151
|
-
}) {
|
|
11152
|
-
const [query, setQuery] = useCommandQuery(queryProp, onQueryChange);
|
|
11153
|
-
const anchoredDensity = useAnchoredDensity();
|
|
11154
|
-
const handleOpenChange = (next) => {
|
|
11155
|
-
if (!next) setQuery("");
|
|
11156
|
-
onOpenChange(next);
|
|
11157
|
-
};
|
|
11158
|
-
const close = () => handleOpenChange(false);
|
|
11159
|
-
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: handleOpenChange, children: /* @__PURE__ */ jsxs(
|
|
11160
|
-
DialogContent,
|
|
11161
|
-
{
|
|
11162
|
-
scroll: false,
|
|
11163
|
-
...anchoredDensity,
|
|
11164
|
-
finalFocus,
|
|
11165
|
-
className: cn(
|
|
11166
|
-
// Top-anchored rather than centred, so the viewport cap DialogContent
|
|
11167
|
-
// applies (which assumes a 1rem margin top and bottom) has to be
|
|
11168
|
-
// restated for the 12vh offset: everything below the offset, less the
|
|
11169
|
-
// bottom margin.
|
|
11170
|
-
"top-[12vh] max-h-[calc(88dvh-1rem)] max-w-140 -translate-y-0 overflow-hidden p-0",
|
|
11171
|
-
className
|
|
11172
|
-
),
|
|
11173
|
-
...props,
|
|
11174
|
-
children: [
|
|
11175
|
-
/* @__PURE__ */ jsx(DialogTitle, { className: "sr-only", children: title }),
|
|
11176
|
-
/* @__PURE__ */ jsx(DialogDescription, { className: "sr-only", children: description }),
|
|
11177
|
-
/* @__PURE__ */ jsx(
|
|
11178
|
-
CommandMenuBody,
|
|
11179
|
-
{
|
|
11180
|
-
query,
|
|
11181
|
-
setQuery,
|
|
11182
|
-
items,
|
|
11183
|
-
filter,
|
|
11184
|
-
listClassName,
|
|
11185
|
-
placeholder,
|
|
11186
|
-
emptyMessage,
|
|
11187
|
-
title,
|
|
11188
|
-
scope,
|
|
11189
|
-
inputProps,
|
|
11190
|
-
close,
|
|
11191
|
-
open,
|
|
11192
|
-
inline: false,
|
|
11193
|
-
children
|
|
11194
|
-
}
|
|
11195
|
-
)
|
|
11196
|
-
]
|
|
11197
|
-
}
|
|
11198
|
-
) });
|
|
11199
|
-
}
|
|
11200
|
-
var noop = () => {
|
|
11201
|
-
};
|
|
11202
|
-
function CommandMenuPanel({
|
|
11203
|
-
items,
|
|
11204
|
-
query: queryProp,
|
|
11205
|
-
onQueryChange,
|
|
11206
|
-
filter,
|
|
11207
|
-
listClassName,
|
|
11208
|
-
placeholder = "Type a command or search\u2026",
|
|
11209
|
-
emptyMessage = "No results found.",
|
|
11210
|
-
title = "Command menu",
|
|
11211
|
-
scope,
|
|
11212
|
-
inputProps,
|
|
11213
|
-
children,
|
|
11214
|
-
className,
|
|
11215
|
-
...props
|
|
11216
|
-
}) {
|
|
11217
|
-
const [query, setQuery] = useCommandQuery(queryProp, onQueryChange);
|
|
11218
|
-
return /* @__PURE__ */ jsx(
|
|
11219
|
-
"div",
|
|
11220
|
-
{
|
|
11221
|
-
"data-slot": "command-menu-panel",
|
|
11222
|
-
className: cn(
|
|
11223
|
-
"flex min-h-0 flex-col overflow-hidden rounded-lg border border-border bg-card text-card-foreground",
|
|
11224
|
-
className
|
|
11225
|
-
),
|
|
11226
|
-
...props,
|
|
11227
|
-
children: /* @__PURE__ */ jsx(
|
|
11228
|
-
CommandMenuBody,
|
|
11229
|
-
{
|
|
11230
|
-
query,
|
|
11231
|
-
setQuery,
|
|
11232
|
-
items,
|
|
11233
|
-
filter,
|
|
11234
|
-
listClassName,
|
|
11235
|
-
placeholder,
|
|
11236
|
-
emptyMessage,
|
|
11237
|
-
title,
|
|
11238
|
-
scope,
|
|
11239
|
-
inputProps,
|
|
11240
|
-
close: noop,
|
|
11241
|
-
open: true,
|
|
11242
|
-
inline: true,
|
|
11243
|
-
children
|
|
11244
|
-
}
|
|
11245
|
-
)
|
|
11246
|
-
}
|
|
11247
|
-
);
|
|
11248
|
-
}
|
|
11249
|
-
var CommandQueryContext = createContext(
|
|
11250
|
-
null
|
|
11251
|
-
);
|
|
11252
|
-
function CommandMenuGroup({
|
|
11253
|
-
label,
|
|
11254
|
-
className,
|
|
11255
|
-
children,
|
|
11256
|
-
...props
|
|
11257
|
-
}) {
|
|
11258
|
-
const labelId = useId();
|
|
11259
|
-
const tier = useTypeTier();
|
|
11260
|
-
return /* @__PURE__ */ jsxs(
|
|
11261
|
-
"div",
|
|
11262
|
-
{
|
|
11263
|
-
role: "group",
|
|
11264
|
-
"aria-labelledby": labelId,
|
|
11265
|
-
className: cn("py-1", className),
|
|
11266
|
-
...props,
|
|
11267
|
-
children: [
|
|
11268
|
-
/* @__PURE__ */ jsx(
|
|
11269
|
-
"div",
|
|
11270
|
-
{
|
|
11271
|
-
id: labelId,
|
|
11272
|
-
className: `px-(--cm-pad,--spacing(2)) py-1 ${tier.label} text-muted-foreground`,
|
|
11273
|
-
children: label
|
|
11274
|
-
}
|
|
11275
|
-
),
|
|
11276
|
-
children
|
|
11277
|
-
]
|
|
11278
|
-
}
|
|
11279
|
-
);
|
|
11280
|
-
}
|
|
11281
|
-
function CommandMenuItem({
|
|
11282
|
-
onSelect,
|
|
11283
|
-
icon,
|
|
11284
|
-
detail,
|
|
11285
|
-
hint,
|
|
11286
|
-
id: idProp,
|
|
11287
|
-
className,
|
|
11288
|
-
children,
|
|
11289
|
-
disabled,
|
|
11290
|
-
...props
|
|
11291
|
-
}) {
|
|
11292
|
-
const ctx = useContext(CommandQueryContext);
|
|
11293
|
-
const tier = useTypeTier();
|
|
11294
|
-
const autoId = useId();
|
|
11295
|
-
const id = idProp ?? autoId;
|
|
11296
|
-
if (ctx?.query) {
|
|
11297
|
-
const text = textOf(children).toLowerCase();
|
|
11298
|
-
if (!text.includes(ctx.query)) return null;
|
|
11299
|
-
}
|
|
11300
|
-
const handleSelect = () => {
|
|
11301
|
-
if (disabled) return;
|
|
11302
|
-
onSelect?.();
|
|
11303
|
-
ctx?.close();
|
|
11304
|
-
};
|
|
11305
|
-
return /* @__PURE__ */ jsxs(
|
|
11306
|
-
"div",
|
|
11307
|
-
{
|
|
11308
|
-
id,
|
|
11309
|
-
role: "option",
|
|
11310
|
-
"aria-selected": ctx?.activeId === id,
|
|
11311
|
-
"aria-disabled": disabled || void 0,
|
|
11312
|
-
onClick: handleSelect,
|
|
11313
|
-
onMouseMove: () => ctx?.setActiveId(id),
|
|
11314
|
-
className: cn(
|
|
11315
|
-
`flex items-center gap-2 rounded-[max(0px,calc(var(--radius-lg)-var(--cm-inset,--spacing(1))-1px))] px-(--cm-pad,--spacing(2)) py-2 ${tier.body} outline-none motion-interactive aria-selected:bg-accent aria-selected:text-accent-foreground [&_svg]:shrink-0`,
|
|
11316
|
-
disabled ? "cursor-default text-muted-foreground" : "cursor-pointer text-foreground",
|
|
11317
|
-
className
|
|
11318
|
-
),
|
|
11319
|
-
...props,
|
|
11320
|
-
children: [
|
|
11321
|
-
icon && /* @__PURE__ */ jsx(Icon, { name: icon, className: "text-base text-muted-foreground" }),
|
|
11322
|
-
/* @__PURE__ */ jsx("span", { className: "truncate", children }),
|
|
11323
|
-
detail && /* @__PURE__ */ jsx("span", { className: "truncate text-body-sm text-muted-foreground", children: detail }),
|
|
11324
|
-
hint && /* @__PURE__ */ jsx("span", { className: "ms-auto flex min-w-0 max-w-1/2 items-center ps-4 text-body-sm text-muted-foreground", children: /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: hint }) })
|
|
11325
|
-
]
|
|
11326
|
-
}
|
|
11327
|
-
);
|
|
11328
|
-
}
|
|
11329
|
-
function textOf(node) {
|
|
11330
|
-
if (node == null || typeof node === "boolean") return "";
|
|
11331
|
-
if (typeof node === "string" || typeof node === "number") return String(node);
|
|
11332
|
-
if (Array.isArray(node)) return node.map(textOf).join("");
|
|
11333
|
-
if (typeof node === "object" && "props" in node) {
|
|
11334
|
-
const props = node.props;
|
|
11335
|
-
return textOf(props?.children);
|
|
11336
|
-
}
|
|
11337
|
-
return "";
|
|
11338
|
-
}
|
|
11339
|
-
var COMMAND_MENU_INPUT_ATTR = "data-command-menu-input";
|
|
11340
|
-
var NON_TEXT_INPUTS = /* @__PURE__ */ new Set([
|
|
11341
|
-
"button",
|
|
11342
|
-
"checkbox",
|
|
11343
|
-
"color",
|
|
11344
|
-
"file",
|
|
11345
|
-
"image",
|
|
11346
|
-
"radio",
|
|
11347
|
-
"range",
|
|
11348
|
-
"reset",
|
|
11349
|
-
"submit"
|
|
11350
|
-
]);
|
|
11351
|
-
function isEditable(target) {
|
|
11352
|
-
if (!(target instanceof Element)) return false;
|
|
11353
|
-
if (target.closest(`[${COMMAND_MENU_INPUT_ATTR}]`)) return false;
|
|
11354
|
-
if (target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement)
|
|
11355
|
-
return true;
|
|
11356
|
-
if (target instanceof HTMLInputElement)
|
|
11357
|
-
return !NON_TEXT_INPUTS.has(target.type);
|
|
11358
|
-
if (target instanceof HTMLElement && target.isContentEditable) return true;
|
|
11359
|
-
return target.closest('[contenteditable]:not([contenteditable="false"])') !== null;
|
|
11360
|
-
}
|
|
11361
|
-
function detectApple2() {
|
|
11362
|
-
if (typeof navigator === "undefined") return false;
|
|
11363
|
-
const nav = navigator;
|
|
11364
|
-
const platform = nav.userAgentData?.platform || nav.platform || nav.userAgent || "";
|
|
11365
|
-
return /mac|iphone|ipad|ipod/i.test(platform);
|
|
11366
|
-
}
|
|
11367
|
-
var noopSubscribe2 = () => () => {
|
|
11368
|
-
};
|
|
11369
|
-
function useIsApple2() {
|
|
11370
|
-
return useSyncExternalStore(noopSubscribe2, detectApple2, () => false);
|
|
11371
|
-
}
|
|
11372
|
-
function useCommandMenuShortcut(onShortcut, key = "k") {
|
|
11373
|
-
const handler = useRef(onShortcut);
|
|
11374
|
-
useIsomorphicLayoutEffect(() => {
|
|
11375
|
-
handler.current = onShortcut;
|
|
11376
|
-
});
|
|
11377
|
-
useEffect(() => {
|
|
11378
|
-
if (key === false || typeof window === "undefined") return;
|
|
11379
|
-
const wanted = key.toLowerCase();
|
|
11380
|
-
const apple = detectApple2();
|
|
11381
|
-
function onKeyDown(event) {
|
|
11382
|
-
if (event.defaultPrevented || event.isComposing) return;
|
|
11383
|
-
if (typeof event.key !== "string" || event.key.toLowerCase() !== wanted) return;
|
|
11384
|
-
const mod = apple ? event.metaKey : event.ctrlKey;
|
|
11385
|
-
const other = apple ? event.ctrlKey : event.metaKey;
|
|
11386
|
-
if (!mod || other || event.altKey || event.shiftKey) return;
|
|
11387
|
-
if (isEditable(event.target)) return;
|
|
11388
|
-
event.preventDefault();
|
|
11389
|
-
handler.current(event);
|
|
11390
|
-
}
|
|
11391
|
-
window.addEventListener("keydown", onKeyDown);
|
|
11392
|
-
return () => window.removeEventListener("keydown", onKeyDown);
|
|
11393
|
-
}, [key]);
|
|
11394
|
-
}
|
|
11395
|
-
function CommandMenuTrigger({
|
|
11396
|
-
onOpen,
|
|
11397
|
-
onClick,
|
|
11398
|
-
placeholder = "Search",
|
|
11399
|
-
shortcut = "k",
|
|
11400
|
-
size = "md",
|
|
11401
|
-
className,
|
|
11402
|
-
type,
|
|
11403
|
-
...props
|
|
11404
|
-
}) {
|
|
11405
|
-
const tier = useTypeTier();
|
|
11406
|
-
const apple = useIsApple2();
|
|
11407
|
-
useCommandMenuShortcut(() => onOpen?.(), props.disabled ? false : shortcut);
|
|
11408
|
-
const keyLabel = shortcut === false ? null : shortcut.toUpperCase();
|
|
11409
|
-
return /* @__PURE__ */ jsxs(
|
|
11410
|
-
"button",
|
|
11411
|
-
{
|
|
11412
|
-
type: type ?? "button",
|
|
11413
|
-
"aria-haspopup": "dialog",
|
|
11414
|
-
"aria-keyshortcuts": keyLabel ? `${apple ? "Meta" : "Control"}+${keyLabel}` : void 0,
|
|
11415
|
-
onClick: (event) => {
|
|
11416
|
-
onClick?.(event);
|
|
11417
|
-
if (!event.defaultPrevented) onOpen?.();
|
|
11418
|
-
},
|
|
11419
|
-
className: cn(
|
|
11420
|
-
// FIELD_SHELL carries motion-interactive too; it is restated here so
|
|
11421
|
-
// the hover it eases is visibly paired with its recipe.
|
|
11422
|
-
"flex w-full items-center gap-2 text-start text-muted-foreground motion-interactive hover:text-foreground",
|
|
11423
|
-
FIELD_SHELL,
|
|
11424
|
-
size === "sm" ? `h-(--control-sm) ps-2.5 pe-1.5 ${tier.supporting}` : `h-(--control-md) ps-3 pe-2 ${tier.body}`,
|
|
11425
|
-
className
|
|
11426
|
-
),
|
|
11427
|
-
...props,
|
|
11428
|
-
children: [
|
|
11429
|
-
/* @__PURE__ */ jsx(Icon, { name: "search", className: "shrink-0 text-sm" }),
|
|
11430
|
-
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: placeholder }),
|
|
11431
|
-
keyLabel ? (
|
|
11432
|
-
// aria-hidden: the shortcut is announced once, through
|
|
11433
|
-
// aria-keyshortcuts, rather than read out as part of the name.
|
|
11434
|
-
// One cap per key (Kbd's own guidance) — "Ctrl K" in a single cap
|
|
11435
|
-
// read as one key.
|
|
11436
|
-
/* @__PURE__ */ jsxs(KbdGroup, { "aria-hidden": true, className: "shrink-0", children: [
|
|
11437
|
-
/* @__PURE__ */ jsx(Kbd, { children: apple ? "\u2318" : "Ctrl" }),
|
|
11438
|
-
/* @__PURE__ */ jsx(Kbd, { children: keyLabel })
|
|
11439
|
-
] })
|
|
11440
|
-
) : null
|
|
11441
|
-
]
|
|
11442
|
-
}
|
|
11443
|
-
);
|
|
11444
|
-
}
|
|
11445
|
-
CommandMenuTrigger.displayName = "CommandMenuTrigger";
|
|
11446
|
-
|
|
11447
|
-
// src/components/command-menu/command-menu.meta.ts
|
|
11448
|
-
var commandMenuMeta = {
|
|
11449
|
-
name: "CommandMenu",
|
|
11450
|
-
summary: "A command palette (Cmd+K) built on our Dialog: a top-aligned wide surface with a search input and a scrollable, JS-filtered list of grouped commands. CommandMenuPanel renders the same palette in place, on a card in the page.",
|
|
11451
|
-
keywords: [
|
|
11452
|
-
"command",
|
|
11453
|
-
"command menu",
|
|
11454
|
-
"palette",
|
|
11455
|
-
"launcher",
|
|
11456
|
-
"cmd k",
|
|
11457
|
-
"search",
|
|
11458
|
-
"quick action",
|
|
11459
|
-
"spotlight",
|
|
11460
|
-
"navigation",
|
|
11461
|
-
"search trigger",
|
|
11462
|
-
"header search",
|
|
11463
|
-
"keyboard shortcut",
|
|
11464
|
-
"inline command menu",
|
|
11465
|
-
"embedded palette",
|
|
11466
|
-
"settings search"
|
|
11467
|
-
],
|
|
11468
|
-
usage: {
|
|
11469
|
-
description: "Use CommandMenu for global or contextual keyboard-driven actions: navigation, creation, quick settings, or searchable commands. It should feel curated and fast; use Select, Menu, or Combobox for ordinary inline choices.",
|
|
11470
|
-
bestPractices: [
|
|
11471
|
-
{
|
|
11472
|
-
guidance: "do",
|
|
11473
|
-
description: "Open the palette from a header search with CommandMenuTrigger: a button wearing SearchInput's field shell, with the \u2318K / Ctrl K hint and the page-wide shortcut built in. A palette with no visible trigger still takes useCommandMenuShortcut rather than a hand-written keydown listener."
|
|
11474
|
-
},
|
|
11475
|
-
{
|
|
11476
|
-
guidance: "dont",
|
|
11477
|
-
description: "Hand-style a <button> to look like a search field, or put a SearchInput in the header that opens a dialog when focused. The first drifts from the field it imitates; the second is a text box you cannot type in. A header search that opens the palette is CommandMenuTrigger."
|
|
11478
|
-
},
|
|
11479
|
-
{
|
|
11480
|
-
guidance: "do",
|
|
11481
|
-
description: "Group items by task area, such as Navigation, Create, or Settings, so users can scan before typing."
|
|
11482
|
-
},
|
|
11483
|
-
{
|
|
11484
|
-
guidance: "do",
|
|
11485
|
-
description: "Keep command labels action-oriented and unique enough to search."
|
|
11486
|
-
},
|
|
11487
|
-
{
|
|
11488
|
-
guidance: "do",
|
|
11489
|
-
description: "Provide useful bootstrap items so the menu is valuable before the user types."
|
|
11490
|
-
},
|
|
11491
|
-
{
|
|
11492
|
-
guidance: "dont",
|
|
11493
|
-
description: "Use CommandMenu as a replacement for every dropdown. Simple choices belong in Select, Menu, or Tabs."
|
|
11494
|
-
},
|
|
11495
|
-
{
|
|
11496
|
-
guidance: "do",
|
|
11497
|
-
description: "Pass a filter when the palette searches more than one source. Ranking (exact prefix over substring over subsequence) is the caller's to own, and the returned order is kept verbatim \u2014 including group order, which follows each group's first surviving row."
|
|
11498
|
-
},
|
|
11499
|
-
{
|
|
11500
|
-
guidance: "do",
|
|
11501
|
-
description: "List a command the user cannot currently run as disabled with the reason in hint, rather than filtering it away. A palette that hides what you cannot do teaches you nothing about why."
|
|
11502
|
-
},
|
|
11503
|
-
{
|
|
11504
|
-
guidance: "do",
|
|
11505
|
-
description: "Resize the list with listClassName rather than reaching past className into the palette's DOM. className is the dialog surface; listClassName is the Scroller that actually scrolls."
|
|
11506
|
-
},
|
|
11507
|
-
{
|
|
11508
|
-
guidance: "do",
|
|
11509
|
-
description: "Use CommandMenuPanel when the palette lives in the page: a sidebar launcher, a settings search, a demo. It takes the same items, filter, query and children, and keeps the same keyboard model; it is a static card, so it does not trap focus or take it on mount, and Esc clears the query instead of closing anything."
|
|
11510
|
-
},
|
|
11511
|
-
{
|
|
11512
|
-
guidance: "dont",
|
|
11513
|
-
description: "Hand-build a palette mock from a search glyph, a row of buttons and a highlight. That is CommandMenuPanel, and the hand-built one has no combobox, no filtering and no keyboard model."
|
|
11514
|
-
},
|
|
11515
|
-
{
|
|
11516
|
-
guidance: "dont",
|
|
11517
|
-
description: "Dump every app route and action into one uncurated list. Keep the palette opinionated."
|
|
11518
|
-
},
|
|
11519
|
-
{
|
|
11520
|
-
guidance: "dont",
|
|
11521
|
-
description: "Rebuild the combobox over Dialog to get consumer-owned ranking. query/onQueryChange and filter are that seam; the keyboard model, activedescendant wiring, and result-count live region come with them."
|
|
11522
|
-
}
|
|
11523
|
-
],
|
|
11524
|
-
anatomy: [
|
|
11525
|
-
{
|
|
11526
|
-
name: "Trigger",
|
|
11527
|
-
required: false,
|
|
11528
|
-
description: "CommandMenuTrigger \u2014 the search-field-shaped button in a header that opens the palette on click or on \u2318K / Ctrl+K. It sits outside CommandMenu; wire its onOpen to the palette's open state."
|
|
11529
|
-
},
|
|
11530
|
-
{
|
|
11531
|
-
name: "Dialog surface",
|
|
11532
|
-
required: true,
|
|
11533
|
-
description: "The modal shell that traps focus and positions the launcher. CommandMenuPanel replaces it with a static card in the page (border, card fill, no shadow)."
|
|
11534
|
-
},
|
|
11535
|
-
{
|
|
11536
|
-
name: "Search input",
|
|
11537
|
-
required: true,
|
|
11538
|
-
description: "The combobox input that filters commands while focus remains in the field."
|
|
11539
|
-
},
|
|
11540
|
-
{
|
|
11541
|
-
name: "Groups",
|
|
11542
|
-
required: false,
|
|
11543
|
-
description: "Labelled command sections that keep results scannable."
|
|
11544
|
-
},
|
|
11545
|
-
{
|
|
11546
|
-
name: "Items",
|
|
11547
|
-
required: true,
|
|
11548
|
-
description: "Keyboard-selectable command rows that run onSelect and close the palette."
|
|
11549
|
-
}
|
|
11550
|
-
]
|
|
11551
|
-
},
|
|
11552
|
-
related: {
|
|
11553
|
-
components: ["Dialog", "SearchInput", "Kbd", "Menu", "Select", "Combobox"],
|
|
11554
|
-
patterns: ["DashboardPage", "ListDetailPage"]
|
|
11555
|
-
},
|
|
11556
|
-
status: "stable",
|
|
11557
|
-
props: [
|
|
11558
|
-
{
|
|
11559
|
-
name: "scope",
|
|
11560
|
-
type: "ReactNode",
|
|
11561
|
-
description: 'A scope chip in the header, between the search glyph and the input \u2014 "In: Backdrop". A scope belongs beside the field rather than in its value: put it in the value and the user deletes prose to widen their search, and your filter parses it back out. Pair with inputProps.onKeyDown handling Backspace on an empty query, or the only way out of a scope is the mouse.'
|
|
11562
|
-
},
|
|
11563
|
-
{
|
|
11564
|
-
name: "inputProps",
|
|
11565
|
-
type: "Omit<ComponentProps<'input'>, 'role' | 'value' | 'onChange' | aria-*>",
|
|
11566
|
-
description: "Extra props for the search input. `onKeyDown` runs BEFORE the palette's own handling and opts out of a key by calling preventDefault() \u2014 which is what makes a scope chip Backspace-dismissable. Running first is deliberate: Backspace happens not to be a key the palette claims today, so a plain passthrough would work until the palette grows a 'Backspace clears the query' affordance, at which point the consumer's behaviour breaks silently. The combobox wiring (role, aria relationships, controlled value) is not overridable; aria-label and className compose."
|
|
11567
|
-
},
|
|
11568
|
-
{
|
|
11569
|
-
name: "open",
|
|
11570
|
-
type: "boolean",
|
|
11571
|
-
description: "Controlled open state of the palette."
|
|
11572
|
-
},
|
|
11573
|
-
{
|
|
11574
|
-
name: "onOpenChange",
|
|
11575
|
-
type: "(open: boolean) => void",
|
|
11576
|
-
description: "Called on open/close (select, Esc, outside click)."
|
|
11577
|
-
},
|
|
11578
|
-
{
|
|
11579
|
-
name: "items",
|
|
11580
|
-
type: "CommandItem[]",
|
|
11581
|
-
description: "Data-driven commands ({ group, label, onSelect, id?, icon?, detail?, hint?, disabled? }). Ignored when children are provided. id is optional identity: filter is handed these same objects, so it is how a caller that ranks over its own richer model maps the array back onto it \u2014 and it becomes the row's DOM id (namespaced to the palette) and React key, so two rows may share a label."
|
|
11582
|
-
},
|
|
11583
|
-
{
|
|
11584
|
-
name: "query",
|
|
11585
|
-
type: "string",
|
|
11586
|
-
description: "Controlled query. Leave undefined to let the palette own it; either way it resets on close (a controlled caller gets onQueryChange(''))."
|
|
11587
|
-
},
|
|
11588
|
-
{
|
|
11589
|
-
name: "onQueryChange",
|
|
11590
|
-
type: "(query: string) => void",
|
|
11591
|
-
description: "Called on every query change. Required to control query, optional otherwise."
|
|
11592
|
-
},
|
|
11593
|
-
{
|
|
11594
|
-
name: "filter",
|
|
11595
|
-
type: "((items: CommandItem[], query: string) => CommandItem[]) | null",
|
|
11596
|
-
default: "case-insensitive substring match on label",
|
|
11597
|
-
description: "Replaces or disables the built-in match. A function ranks and filters items itself and its order is kept verbatim; null renders items exactly as given. Either also stops declarative CommandMenuItem children self-filtering. The query arrives raw \u2014 trimming and case are policy, and this is the prop that hands policy over."
|
|
11598
|
-
},
|
|
11599
|
-
{
|
|
11600
|
-
name: "listClassName",
|
|
11601
|
-
type: "string",
|
|
11602
|
-
description: `Classes for the scrolling list. className lands on the dialog surface, so this is the seam for the list's own height cap \u2014 listClassName="max-h-[60vh]". The list is a Scroller, so the cap lands on the element that scrolls and the edge fades come free.`
|
|
11603
|
-
},
|
|
11604
|
-
{
|
|
11605
|
-
name: "placeholder",
|
|
11606
|
-
type: "string",
|
|
11607
|
-
default: '"Type a command or search\u2026"',
|
|
11608
|
-
description: "Placeholder for the search input."
|
|
11609
|
-
},
|
|
11610
|
-
{
|
|
11611
|
-
name: "emptyMessage",
|
|
11612
|
-
type: "string",
|
|
11613
|
-
default: '"No results found."',
|
|
11614
|
-
description: "Copy shown when the query matches nothing."
|
|
11615
|
-
},
|
|
11616
|
-
{
|
|
11617
|
-
name: "title",
|
|
11618
|
-
type: "string",
|
|
11619
|
-
default: '"Command menu"',
|
|
11620
|
-
description: "Accessible name for the palette dialog (visually hidden)."
|
|
11621
|
-
},
|
|
11622
|
-
{
|
|
11623
|
-
name: "description",
|
|
11624
|
-
type: "string",
|
|
11625
|
-
default: '"Search for a command and press Enter to run it."',
|
|
11626
|
-
description: "Accessible description for the palette dialog (visually hidden)."
|
|
11627
|
-
},
|
|
11628
|
-
{
|
|
11629
|
-
name: "finalFocus",
|
|
11630
|
-
type: "RefObject<HTMLElement> | ((closeType) => HTMLElement | false) | false",
|
|
11631
|
-
description: "Where focus goes when the palette closes. The Dialog default returns it to the trigger \u2014 the CommandMenuTrigger, when there is one. A palette opened by the keystroke alone has none, so pass a resolver to restore whatever had focus, or false to restore nothing."
|
|
11632
|
-
},
|
|
11633
|
-
{
|
|
11634
|
-
name: "children",
|
|
11635
|
-
type: "ReactNode",
|
|
11636
|
-
description: "Declarative commands via CommandMenuGroup / CommandMenuItem. Takes precedence over items."
|
|
11637
|
-
}
|
|
11638
|
-
],
|
|
11639
|
-
subcomponents: [
|
|
11640
|
-
{
|
|
11641
|
-
name: "CommandMenuTrigger",
|
|
11642
|
-
type: '{ onOpen?: () => void; placeholder?: string; shortcut?: string | false; size?: "sm" | "md"; disabled?: boolean }',
|
|
11643
|
-
description: `The search field in a header that is really a button. Wears SearchInput's shell (border-input, card surface, leading magnifier, muted placeholder) on the control ladder, with a trailing Kbd: \u2318K on macOS, Ctrl K elsewhere. onOpen runs on click and on the shortcut, which it listens for page-wide (useCommandMenuShortcut). shortcut is the key (default "k"); false drops both the hint and the listener. size is sm in a header beside small buttons, md (default) beside Input and SearchInput. It sits outside CommandMenu, so the Dialog returns focus to it on close.`
|
|
11644
|
-
},
|
|
11645
|
-
{
|
|
11646
|
-
name: "useCommandMenuShortcut",
|
|
11647
|
-
type: "(onShortcut: (event: KeyboardEvent) => void, key?: string | false) => void",
|
|
11648
|
-
description: `The page-wide \u2318K / Ctrl+K listener CommandMenuTrigger installs, for a palette with no visible trigger. Ignored while focus is in a text field, textarea, select or contenteditable (Ctrl+K belongs to the field there) except the palette's own input, so the callback may toggle. It calls preventDefault() on a match and skips an event already prevented, so two listeners on one page fire once. key defaults to "k"; false turns it off. Removed on unmount.`
|
|
11649
|
-
},
|
|
11650
|
-
{
|
|
11651
|
-
name: "CommandMenuPanel",
|
|
11652
|
-
type: "{ items?: CommandItem[]; query?: string; onQueryChange?: (query: string) => void; filter?: CommandFilter; listClassName?: string; placeholder?: string; emptyMessage?: string; title?: string; scope?: ReactNode; inputProps?: ComponentProps<'input'>; className?: string; children?: ReactNode }",
|
|
11653
|
-
description: "The palette rendered in place: the same search header, filtered and grouped listbox, keyboard model and result-count live region, on a static card (border-border, bg-card, no shadow) instead of in a Dialog. Takes every CommandMenu prop except open, onOpenChange, finalFocus and description; className lands on the card, title names the listbox. Focus is not trapped and the input does not take focus on mount (inputProps.autoFocus opts in). Esc clears the query, and passes through once it is empty, so a Popover around the panel still closes. A chosen row runs its onSelect and the panel stays as it was. The page-wide shortcut does not fire from its input."
|
|
11654
|
-
},
|
|
11655
|
-
{
|
|
11656
|
-
name: "CommandMenuGroup",
|
|
11657
|
-
type: "{ label: string; children }",
|
|
11658
|
-
description: "A labelled group of command rows."
|
|
11659
|
-
},
|
|
11660
|
-
{
|
|
11661
|
-
name: "CommandMenuItem",
|
|
11662
|
-
type: "{ onSelect?: () => void; icon?: string; detail?: ReactNode; hint?: ReactNode; disabled?: boolean; id?: string; children }",
|
|
11663
|
-
description: "A selectable row. Self-filters by its text (unless the root was given a filter), runs onSelect, then closes the palette. detail is secondary text after the label; hint is the end-aligned trailing slot \u2014 a shortcut, a count, or a disabled row's reason. A disabled row stays listed, stays announced, and stays reachable by arrow key so its reason can be read, but is inert to Enter and to a click."
|
|
11664
|
-
}
|
|
11665
|
-
],
|
|
11666
|
-
tokensUsed: [
|
|
11667
|
-
"card",
|
|
11668
|
-
"card-foreground",
|
|
11669
|
-
"border",
|
|
11670
|
-
"foreground",
|
|
11671
|
-
"muted-foreground",
|
|
11672
|
-
"accent",
|
|
11673
|
-
"accent-foreground"
|
|
11674
|
-
],
|
|
11675
|
-
a11y: [
|
|
11676
|
-
"CommandMenuTrigger is a real <button> named by its visible placeholder, with aria-haspopup=dialog and aria-keyshortcuts (Meta+K on macOS, Control+K elsewhere). Its Kbd hint is aria-hidden, so the shortcut is announced once rather than read as part of the name.",
|
|
11677
|
-
"The shortcut never fires from inside a text field, textarea, select or contenteditable (Ctrl+K is delete-to-end-of-line in macOS text views), except the palette's own input.",
|
|
11678
|
-
"CommandMenuPanel is the same combobox and listbox in the page, with no dialog: it does not trap or steal focus, and Esc clears a non-empty query (stopping there) instead of dismissing. With an empty query Esc is left alone for whatever contains the panel.",
|
|
11679
|
-
"Built on Base UI Dialog, so focus trap, scroll lock, and Esc/outside dismissal come for free; the dialog carries a visually-hidden title + description so it is announced.",
|
|
11680
|
-
"Follows the ARIA combobox pattern: the search input is role=combobox (aria-autocomplete=list) that owns the role=listbox popup via aria-controls.",
|
|
11681
|
-
"ArrowDown/ArrowUp/Home/End and PageUp/PageDown move a virtual aria-activedescendant highlight while focus stays in the input; Enter runs the highlighted command. Arrow keys wrap; page jumps clamp.",
|
|
11682
|
-
"Groups expose role=group labelled by their heading; items are role=option and never enter the tab order.",
|
|
11683
|
-
"A polite role=status live region announces the result count as the query narrows, so the size of the list is available without reading it.",
|
|
11684
|
-
"A disabled row carries aria-disabled and is skipped when seeding the highlight, but stays in the arrow-key walk so its reason can be reached and read.",
|
|
11685
|
-
"The component keeps ownership of aria-activedescendant even when a caller claims a key through inputProps.onKeyDown. Skipping a move is coherent (they took responsibility for that key), but nothing may leave the active id naming a row that is not painted as active \u2014 so the opt-out returns before any highlight change rather than after it.",
|
|
11686
|
-
"Exactly one row is ever painted as active: the pointer takes the highlight over (moving onto a row makes it the aria-activedescendant) rather than adding a hover fill of its own, so what looks highlighted is always what Enter will run."
|
|
11687
|
-
],
|
|
11688
|
-
examples: [
|
|
11689
|
-
{
|
|
11690
|
-
label: "Data-driven",
|
|
11691
|
-
code: "<CommandMenu open={open} onOpenChange={setOpen} items={[{ group: 'Navigation', label: 'Go to feed', icon: 'home', onSelect: () => {} }, { group: 'Actions', label: 'New post', onSelect: () => {} }]} />"
|
|
11692
|
-
},
|
|
11693
|
-
{
|
|
11694
|
-
label: "Declarative",
|
|
11695
|
-
code: "<CommandMenu open={open} onOpenChange={setOpen}><CommandMenuGroup label='Navigation'><CommandMenuItem icon='home' onSelect={() => {}}>Go to feed</CommandMenuItem></CommandMenuGroup></CommandMenu>"
|
|
11696
|
-
},
|
|
11697
|
-
{
|
|
11698
|
-
label: "Grouped launcher",
|
|
11699
|
-
code: '<CommandMenu open={open} onOpenChange={setOpen}><CommandMenuGroup label="Create"><CommandMenuItem icon="plus" onSelect={() => createPost()}>New post</CommandMenuItem><CommandMenuItem icon="sparkles" onSelect={() => createEdition()}>New edition</CommandMenuItem></CommandMenuGroup><CommandMenuGroup label="Navigation"><CommandMenuItem icon="home" onSelect={() => navigate("/")}>Home</CommandMenuItem><CommandMenuItem icon="gear" onSelect={() => navigate("/settings")}>Settings</CommandMenuItem></CommandMenuGroup></CommandMenu>'
|
|
11700
|
-
},
|
|
11701
|
-
{
|
|
11702
|
-
label: "Consumer-owned ranking",
|
|
11703
|
-
code: "<CommandMenu open={open} onOpenChange={setOpen} query={query} onQueryChange={setQuery} filter={(items, q) => rank(items, q)} items={items} />"
|
|
11704
|
-
},
|
|
11705
|
-
{
|
|
11706
|
-
label: "Listed but unavailable",
|
|
11707
|
-
code: "<CommandMenu open={open} onOpenChange={setOpen} items={[{ group: 'Layers', label: 'Align left', disabled: true, hint: 'Select another layer to align to', onSelect: () => {} }]} />"
|
|
11708
|
-
},
|
|
11709
|
-
{
|
|
11710
|
-
label: "Inline panel",
|
|
11711
|
-
code: `<CommandMenuPanel className="w-80" items={[{ group: 'Create', label: 'New project', icon: 'plus', hint: <Kbd>N</Kbd>, onSelect: () => {} }, { group: 'Settings', label: 'Open settings', icon: 'gear', onSelect: () => {} }]} />`
|
|
11712
|
-
},
|
|
11713
|
-
{
|
|
11714
|
-
label: "Header search trigger",
|
|
11715
|
-
code: '<CommandMenuTrigger size="sm" placeholder="Search docs" onOpen={() => setOpen(true)} className="w-56" />\n<CommandMenu open={open} onOpenChange={setOpen} items={items} />'
|
|
11716
|
-
},
|
|
11717
|
-
{
|
|
11718
|
-
label: "Keyboard shortcut with no visible trigger",
|
|
11719
|
-
code: "useCommandMenuShortcut(() => setOpen((value) => !value))\n<CommandMenu open={open} onOpenChange={setOpen} items={items} />"
|
|
11720
|
-
}
|
|
11721
|
-
]
|
|
11722
|
-
};
|
|
11723
9722
|
var ContextCard = PreviewCard.Root;
|
|
11724
9723
|
var ContextCardTrigger = PreviewCard.Trigger;
|
|
11725
9724
|
var ContextCardPortal = withPortalThemeScope(PreviewCard.Portal);
|
|
@@ -12073,7 +10072,7 @@ function MiddleTruncate({
|
|
|
12073
10072
|
title: fits ? void 0 : text,
|
|
12074
10073
|
className: cn("text-mono-sm tabular-nums", className),
|
|
12075
10074
|
...props,
|
|
12076
|
-
children: fits ? text : /* @__PURE__ */ jsxs(Fragment
|
|
10075
|
+
children: fits ? text : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
12077
10076
|
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: `${text.slice(0, startChars)}\u2026${text.slice(-endChars)}` }),
|
|
12078
10077
|
/* @__PURE__ */ jsx("span", { className: "sr-only", children: text })
|
|
12079
10078
|
] })
|
|
@@ -13074,7 +11073,7 @@ function Carousel({
|
|
|
13074
11073
|
)
|
|
13075
11074
|
}
|
|
13076
11075
|
),
|
|
13077
|
-
showArrows && /* @__PURE__ */ jsxs(Fragment
|
|
11076
|
+
showArrows && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
13078
11077
|
/* @__PURE__ */ jsx(
|
|
13079
11078
|
CarouselArrowButton,
|
|
13080
11079
|
{
|
|
@@ -13261,6 +11260,6 @@ var carouselMeta = {
|
|
|
13261
11260
|
};
|
|
13262
11261
|
|
|
13263
11262
|
// src/index.ts
|
|
13264
|
-
var SABLE_UI_VERSION = "0.
|
|
11263
|
+
var SABLE_UI_VERSION = "0.3.1";
|
|
13265
11264
|
|
|
13266
|
-
export {
|
|
11265
|
+
export { Banner, BreadcrumbItem, BreadcrumbLink, BreadcrumbPage, BreadcrumbSeparator, Breadcrumbs, COMPONENT_GROUPS, COMPONENT_TAXONOMY, Calendar, Carousel, CheckboxCard, CheckboxGroup, Collapsible, CollapsibleIndicator, CollapsiblePanel, CollapsibleTrigger, ColorField, Combobox, ComboboxChips, ComboboxClear, ComboboxContent, ComboboxEmpty, ComboboxGroup, ComboboxGroupLabel, ComboboxInput, ComboboxItem, ComboboxItemIndicator, ComboboxList, ComboboxPortal, ComboboxValue, ComboboxVirtualList, ContextCard, ContextCardArrow, ContextCardContent, ContextCardPortal, ContextCardTrigger, ContextMenu, ContextMenuContent, ContextMenuGroup, ContextMenuGroupLabel, ContextMenuItem, ContextMenuPortal, ContextMenuSeparator, ContextMenuTrigger, DatePicker, Feedback, Fieldset, FieldsetContent, FieldsetDescription, FieldsetFooter, FieldsetLegend, FileDropzone, GlidingHighlight, InlineStatus, LineChart, Marquee, MiddleTruncate, Note, NumberField, PageLoader, Pagination, Panel, PanelGroup, PanelResizer, PanelSection, Popover, PopoverArrow, PopoverClose, PopoverContent, PopoverDescription, PopoverPortal, PopoverTitle, PopoverTrigger, PropertyPair, PropertyRow, RadarChart, Radio, RadioCard, RadioGroup, SABLE_UI_VERSION, STATUS_TONES, STATUS_TONE_ICON, ScrubField, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetPortal, SheetTitle, SheetTrigger, ShowMore, StatusBar, StatusBarGroup, StatusBarSeparator, TRANSPARENT, ThemeSelector, ThemeSwitcher, ThinkingOrb, ToastAction, ToastClose, ToastDescription, ToastProvider, ToastRoot, ToastTitle, ToastViewport, Tree, TreeAddRow, TreeEmpty, TreeRow, TreeSection, Video, Workspace, WorkspaceBar, WorkspaceCanvas, bannerMeta, bannerVariants, breadcrumbsMeta, calendarMeta, carouselMeta, checkboxCardMeta, collapsibleMeta, colorFieldMeta, comboboxMeta, componentGuidance, componentLabel, componentSlug, componentsInGroup, contextCardMeta, contextMenuMeta, datePickerMeta, feedbackMeta, fieldsetMeta, fileDropzoneMeta, fileDropzoneVariants, formatFileSize, formatHex, glidingHighlightMeta, hsvToRgb, hueColor, inlineStatusMeta, isTransparent, lineChartMeta, marqueeMeta, middleTruncateMeta, noteMeta, noteVariants, numberFieldMeta, pageLoaderMeta, paginationMeta, panelGroupMeta, panelSectionMeta, parseHex, pillMeta, popoverMeta, radarChartMeta, radioCardMeta, radioMeta, rgbToHsv, scrollerMeta, scrubFieldMeta, sheetMeta, sheetVariants, showMoreMeta, statusBarMeta, themeSelectorMeta, themeSwitcherMeta, thinkingOrbMeta, toast, toastMeta, treeIndent, treeMeta, useComboboxFilteredItems, usePanelWidth, useToast, useToastManager, videoMeta, workspaceMeta };
|