@eocrm/design-system 0.0.0-stage → 0.6.18
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/AI-PRIMER.md +341 -0
- package/LICENSE +202 -0
- package/README.md +266 -2
- package/docs/components/Accordion.md +114 -0
- package/docs/components/AgentAvatar.md +58 -0
- package/docs/components/Alert.md +81 -0
- package/docs/components/AppLayout.md +70 -0
- package/docs/components/Avatar.md +49 -0
- package/docs/components/AvatarGroup.md +47 -0
- package/docs/components/Badge.md +60 -0
- package/docs/components/Banner.md +89 -0
- package/docs/components/BrandIcon.md +32 -0
- package/docs/components/Breadcrumb.md +72 -0
- package/docs/components/Button.md +107 -0
- package/docs/components/ButtonGroup.md +78 -0
- package/docs/components/Calendar.md +158 -0
- package/docs/components/Card.md +165 -0
- package/docs/components/CatalogPicker.md +83 -0
- package/docs/components/Chart.md +114 -0
- package/docs/components/ChatBubble.md +55 -0
- package/docs/components/ChatLauncher.md +97 -0
- package/docs/components/Checkbox.md +55 -0
- package/docs/components/CircularProgress.md +53 -0
- package/docs/components/Cluster.md +96 -0
- package/docs/components/Code.md +35 -0
- package/docs/components/CodeBlock.md +67 -0
- package/docs/components/ColorPicker.md +115 -0
- package/docs/components/ConfirmationPopover.md +70 -0
- package/docs/components/Constrain.md +39 -0
- package/docs/components/CurrencySelect.md +57 -0
- package/docs/components/CursorPagination.md +43 -0
- package/docs/components/DashboardCanvas.md +79 -0
- package/docs/components/DashboardWidget.md +131 -0
- package/docs/components/DataTable.md +88 -0
- package/docs/components/DatePicker.md +68 -0
- package/docs/components/DateRangePicker.md +71 -0
- package/docs/components/DateStrip.md +56 -0
- package/docs/components/DefinitionList.md +82 -0
- package/docs/components/Divider.md +57 -0
- package/docs/components/Dot.md +26 -0
- package/docs/components/Drawer.md +94 -0
- package/docs/components/DropdownMenu.md +299 -0
- package/docs/components/EmojiPicker.md +84 -0
- package/docs/components/EmptyState.md +55 -0
- package/docs/components/EntityChip.md +102 -0
- package/docs/components/ErrorState.md +72 -0
- package/docs/components/Field.md +91 -0
- package/docs/components/FileUpload.md +109 -0
- package/docs/components/FilterChip.md +88 -0
- package/docs/components/FlowCanvas.md +108 -0
- package/docs/components/FormRow.md +31 -0
- package/docs/components/FormSection.md +37 -0
- package/docs/components/Grid.md +103 -0
- package/docs/components/Highlight.md +75 -0
- package/docs/components/HighlightedText.md +50 -0
- package/docs/components/IconPicker.md +134 -0
- package/docs/components/IconTile.md +30 -0
- package/docs/components/Image.md +84 -0
- package/docs/components/ImageCrop.md +163 -0
- package/docs/components/Indent.md +31 -0
- package/docs/components/InlineDatePicker.md +58 -0
- package/docs/components/InlineDateRangePicker.md +57 -0
- package/docs/components/Input.md +54 -0
- package/docs/components/Kanban.md +122 -0
- package/docs/components/Kbd.md +41 -0
- package/docs/components/Lightbox.md +105 -0
- package/docs/components/Link.md +65 -0
- package/docs/components/LinkCard.md +41 -0
- package/docs/components/LiquidEditor.md +84 -0
- package/docs/components/List.md +68 -0
- package/docs/components/LiveRegion.md +29 -0
- package/docs/components/LocaleProvider.md +25 -0
- package/docs/components/Logo.md +41 -0
- package/docs/components/Masonry.md +48 -0
- package/docs/components/MediaTile.md +95 -0
- package/docs/components/Modal.md +107 -0
- package/docs/components/MoneyInput.md +62 -0
- package/docs/components/OptionsPicker.md +123 -0
- package/docs/components/OtpInput.md +65 -0
- package/docs/components/Page.md +35 -0
- package/docs/components/PageHeader.md +133 -0
- package/docs/components/Pagination.md +44 -0
- package/docs/components/Palette.md +32 -0
- package/docs/components/PasswordInput.md +45 -0
- package/docs/components/PasswordStrengthMeter.md +27 -0
- package/docs/components/PersonDisplay.md +153 -0
- package/docs/components/PhoneInput.md +54 -0
- package/docs/components/PillMenu.md +91 -0
- package/docs/components/Popover.md +127 -0
- package/docs/components/Progress.md +56 -0
- package/docs/components/QrCode.md +34 -0
- package/docs/components/Radio.md +37 -0
- package/docs/components/RadioGroup.md +56 -0
- package/docs/components/Rail.md +191 -0
- package/docs/components/RelativeDateInput.md +114 -0
- package/docs/components/RichText.md +44 -0
- package/docs/components/RichTextEditor.md +112 -0
- package/docs/components/Screen.md +37 -0
- package/docs/components/ScrollArea.md +39 -0
- package/docs/components/Select.md +131 -0
- package/docs/components/SettingRow.md +98 -0
- package/docs/components/Skeleton.md +68 -0
- package/docs/components/Slider.md +92 -0
- package/docs/components/SlotGrid.md +55 -0
- package/docs/components/SocialButton.md +31 -0
- package/docs/components/Sortable.md +127 -0
- package/docs/components/SortableGroup.md +52 -0
- package/docs/components/Sparkline.md +51 -0
- package/docs/components/Split.md +76 -0
- package/docs/components/Stack.md +59 -0
- package/docs/components/StagePath.md +66 -0
- package/docs/components/StatGroup.md +61 -0
- package/docs/components/StatTile.md +58 -0
- package/docs/components/Sticky.md +69 -0
- package/docs/components/Switch.md +89 -0
- package/docs/components/Table.md +184 -0
- package/docs/components/TableOfContents.md +76 -0
- package/docs/components/Tabs.md +94 -0
- package/docs/components/Text.md +63 -0
- package/docs/components/Textarea.md +59 -0
- package/docs/components/Thread.md +52 -0
- package/docs/components/TimeField.md +70 -0
- package/docs/components/Timeline.md +57 -0
- package/docs/components/Title.md +54 -0
- package/docs/components/ToastViewport.md +61 -0
- package/docs/components/Tooltip.md +63 -0
- package/docs/components/TopBar.md +214 -0
- package/docs/components/Tour.md +85 -0
- package/docs/components/TreeView.md +113 -0
- package/docs/components/VisuallyHidden.md +43 -0
- package/docs/components/WidgetPreview.md +54 -0
- package/docs/components/useBelowBreakpoint.md +11 -0
- package/docs/components/useMonth.md +18 -0
- package/docs/setup.md +51 -0
- package/docs/theming.md +95 -0
- package/docs/tokens.md +123 -0
- package/docs/transient-state.md +56 -0
- package/package.json +86 -4
- package/src/_meta/manifest.ts +293 -0
- package/src/app/AppProvider.tsx +78 -0
- package/src/app/index.ts +3 -0
- package/src/app/themeTokens.ts +95 -0
- package/src/calendar/dateMath.ts +114 -0
- package/src/calendar/formatters.ts +75 -0
- package/src/calendar/index.ts +39 -0
- package/src/calendar/types.ts +36 -0
- package/src/calendar/useAgenda.ts +70 -0
- package/src/calendar/useDay.ts +51 -0
- package/src/calendar/useMonth.ts +82 -0
- package/src/calendar/useWeek.ts +62 -0
- package/src/calendar/weekInfo.ts +83 -0
- package/src/components/Accordion/Accordion.module.scss +304 -0
- package/src/components/Accordion/Accordion.tokens.scss +64 -0
- package/src/components/Accordion/Accordion.tsx +272 -0
- package/src/components/Accordion/AccordionContent.tsx +36 -0
- package/src/components/Accordion/AccordionItem.tsx +66 -0
- package/src/components/Accordion/AccordionTrigger.tsx +121 -0
- package/src/components/Accordion/context.ts +47 -0
- package/src/components/Accordion/index.ts +14 -0
- package/src/components/Alert/Alert.module.scss +92 -0
- package/src/components/Alert/Alert.tokens.scss +60 -0
- package/src/components/Alert/Alert.tsx +136 -0
- package/src/components/Alert/index.ts +2 -0
- package/src/components/AppLayout/AppLayout.module.scss +130 -0
- package/src/components/AppLayout/AppLayout.tokens.scss +27 -0
- package/src/components/AppLayout/AppLayout.tsx +211 -0
- package/src/components/AppLayout/index.ts +2 -0
- package/src/components/Avatar/AgentAvatar.module.scss +117 -0
- package/src/components/Avatar/AgentAvatar.tsx +122 -0
- package/src/components/Avatar/Avatar.module.scss +181 -0
- package/src/components/Avatar/Avatar.tokens.scss +107 -0
- package/src/components/Avatar/Avatar.tsx +210 -0
- package/src/components/Avatar/AvatarGroup.module.scss +128 -0
- package/src/components/Avatar/AvatarGroup.tsx +119 -0
- package/src/components/Avatar/AvatarGroupContext.tsx +20 -0
- package/src/components/Avatar/index.ts +6 -0
- package/src/components/Badge/Badge.module.scss +124 -0
- package/src/components/Badge/Badge.tokens.scss +74 -0
- package/src/components/Badge/Badge.tsx +164 -0
- package/src/components/Badge/index.ts +2 -0
- package/src/components/Banner/Banner.module.scss +73 -0
- package/src/components/Banner/Banner.tokens.scss +51 -0
- package/src/components/Banner/Banner.tsx +138 -0
- package/src/components/Banner/index.ts +2 -0
- package/src/components/BrandIcon/BrandIcon.module.scss +5 -0
- package/src/components/BrandIcon/BrandIcon.tsx +99 -0
- package/src/components/BrandIcon/index.ts +2 -0
- package/src/components/Breadcrumb/Breadcrumb.module.scss +32 -0
- package/src/components/Breadcrumb/Breadcrumb.tokens.scss +9 -0
- package/src/components/Breadcrumb/Breadcrumb.tsx +146 -0
- package/src/components/Breadcrumb/index.ts +2 -0
- package/src/components/Button/Button.module.scss +167 -0
- package/src/components/Button/Button.tokens.scss +90 -0
- package/src/components/Button/Button.tsx +205 -0
- package/src/components/Button/index.ts +2 -0
- package/src/components/ButtonGroup/ButtonGroup.module.scss +134 -0
- package/src/components/ButtonGroup/ButtonGroup.tokens.scss +42 -0
- package/src/components/ButtonGroup/ButtonGroup.tsx +278 -0
- package/src/components/ButtonGroup/ButtonGroupItem.tsx +74 -0
- package/src/components/ButtonGroup/context.ts +45 -0
- package/src/components/ButtonGroup/index.ts +2 -0
- package/src/components/Calendar/AgendaView.module.scss +231 -0
- package/src/components/Calendar/AgendaView.tsx +214 -0
- package/src/components/Calendar/AllDayBand.module.scss +18 -0
- package/src/components/Calendar/AllDayBand.tsx +75 -0
- package/src/components/Calendar/Calendar.module.scss +23 -0
- package/src/components/Calendar/Calendar.tokens.scss +262 -0
- package/src/components/Calendar/Calendar.tsx +370 -0
- package/src/components/Calendar/DayCell.module.scss +30 -0
- package/src/components/Calendar/DayCell.tsx +50 -0
- package/src/components/Calendar/DayView.module.scss +10 -0
- package/src/components/Calendar/DayView.tsx +163 -0
- package/src/components/Calendar/EventChip.module.scss +171 -0
- package/src/components/Calendar/EventChip.tsx +115 -0
- package/src/components/Calendar/HourGrid.module.scss +123 -0
- package/src/components/Calendar/HourGrid.tsx +381 -0
- package/src/components/Calendar/MonthView.module.scss +99 -0
- package/src/components/Calendar/MonthView.tsx +296 -0
- package/src/components/Calendar/TimedEvent.module.scss +205 -0
- package/src/components/Calendar/TimedEvent.tsx +247 -0
- package/src/components/Calendar/ViewSwitcher.module.scss +3 -0
- package/src/components/Calendar/ViewSwitcher.tsx +47 -0
- package/src/components/Calendar/WeekView.module.scss +10 -0
- package/src/components/Calendar/WeekView.tsx +137 -0
- package/src/components/Calendar/eventColor.ts +60 -0
- package/src/components/Calendar/index.ts +18 -0
- package/src/components/Calendar/types.ts +451 -0
- package/src/components/Calendar/useEventDrag.ts +783 -0
- package/src/components/Calendar/utils.ts +622 -0
- package/src/components/Card/Card.module.scss +264 -0
- package/src/components/Card/Card.tokens.scss +55 -0
- package/src/components/Card/Card.tsx +170 -0
- package/src/components/Card/CardBody.tsx +45 -0
- package/src/components/Card/CardHeader.tsx +113 -0
- package/src/components/Card/CardList.tsx +24 -0
- package/src/components/Card/CardListRow.tsx +24 -0
- package/src/components/Card/index.ts +7 -0
- package/src/components/CatalogPicker/CatalogPicker.module.scss +193 -0
- package/src/components/CatalogPicker/CatalogPicker.tokens.scss +26 -0
- package/src/components/CatalogPicker/CatalogPicker.tsx +339 -0
- package/src/components/CatalogPicker/index.ts +2 -0
- package/src/components/Chart/Chart.module.scss +287 -0
- package/src/components/Chart/Chart.tokens.scss +63 -0
- package/src/components/Chart/Chart.tsx +607 -0
- package/src/components/Chart/ChartLegend.tsx +99 -0
- package/src/components/Chart/ChartTable.tsx +62 -0
- package/src/components/Chart/ChartTooltip.tsx +91 -0
- package/src/components/Chart/chartModel.ts +689 -0
- package/src/components/Chart/index.ts +2 -0
- package/src/components/ChatBubble/ChatBubble.module.scss +93 -0
- package/src/components/ChatBubble/ChatBubble.tokens.scss +17 -0
- package/src/components/ChatBubble/ChatBubble.tsx +61 -0
- package/src/components/ChatBubble/ChatBubbleList.tsx +27 -0
- package/src/components/ChatBubble/index.ts +3 -0
- package/src/components/ChatLauncher/ChatLauncher.module.scss +68 -0
- package/src/components/ChatLauncher/ChatLauncher.tokens.scss +27 -0
- package/src/components/ChatLauncher/ChatLauncher.tsx +202 -0
- package/src/components/ChatLauncher/index.ts +2 -0
- package/src/components/ChatLauncher/isAtBottom.ts +12 -0
- package/src/components/Checkbox/Checkbox.module.scss +147 -0
- package/src/components/Checkbox/Checkbox.tokens.scss +44 -0
- package/src/components/Checkbox/Checkbox.tsx +188 -0
- package/src/components/Checkbox/index.ts +2 -0
- package/src/components/CircularProgress/CircularProgress.module.scss +133 -0
- package/src/components/CircularProgress/CircularProgress.tokens.scss +40 -0
- package/src/components/CircularProgress/CircularProgress.tsx +139 -0
- package/src/components/CircularProgress/index.ts +7 -0
- package/src/components/Cluster/Cluster.module.scss +145 -0
- package/src/components/Cluster/Cluster.tokens.scss +11 -0
- package/src/components/Cluster/Cluster.tsx +215 -0
- package/src/components/Cluster/index.ts +2 -0
- package/src/components/Code/Code.module.scss +28 -0
- package/src/components/Code/Code.tokens.scss +15 -0
- package/src/components/Code/Code.tsx +44 -0
- package/src/components/Code/index.ts +2 -0
- package/src/components/CodeBlock/CodeBlock.module.scss +146 -0
- package/src/components/CodeBlock/CodeBlock.tokens.scss +27 -0
- package/src/components/CodeBlock/CodeBlock.tsx +274 -0
- package/src/components/CodeBlock/highlight.ts +179 -0
- package/src/components/CodeBlock/index.ts +8 -0
- package/src/components/CodeBlock/sampleSync.ts +42 -0
- package/src/components/ColorPicker/ColorPicker.module.scss +286 -0
- package/src/components/ColorPicker/ColorPicker.tokens.scss +60 -0
- package/src/components/ColorPicker/ColorPicker.tsx +367 -0
- package/src/components/ColorPicker/ColorPickerPanel.tsx +265 -0
- package/src/components/ColorPicker/SVSquare.tsx +189 -0
- package/src/components/ColorPicker/colorMath.ts +141 -0
- package/src/components/ColorPicker/index.ts +6 -0
- package/src/components/ConfirmationPopover/ConfirmationPopover.module.scss +39 -0
- package/src/components/ConfirmationPopover/ConfirmationPopover.tokens.scss +13 -0
- package/src/components/ConfirmationPopover/ConfirmationPopover.tsx +395 -0
- package/src/components/ConfirmationPopover/index.ts +2 -0
- package/src/components/Constrain/Constrain.module.scss +99 -0
- package/src/components/Constrain/Constrain.tsx +79 -0
- package/src/components/Constrain/index.ts +2 -0
- package/src/components/CurrencySelect/CurrencySelect.module.scss +7 -0
- package/src/components/CurrencySelect/CurrencySelect.tsx +167 -0
- package/src/components/CurrencySelect/index.ts +3 -0
- package/src/components/CursorPagination/CursorPagination.module.scss +69 -0
- package/src/components/CursorPagination/CursorPagination.tokens.scss +32 -0
- package/src/components/CursorPagination/CursorPagination.tsx +121 -0
- package/src/components/CursorPagination/index.ts +2 -0
- package/src/components/DashboardCanvas/DashboardCanvas.module.scss +360 -0
- package/src/components/DashboardCanvas/DashboardCanvas.tokens.scss +48 -0
- package/src/components/DashboardCanvas/DashboardCanvas.tsx +1270 -0
- package/src/components/DashboardCanvas/DashboardCanvasItem.tsx +140 -0
- package/src/components/DashboardCanvas/DashboardCanvasSection.tsx +174 -0
- package/src/components/DashboardCanvas/engine.ts +363 -0
- package/src/components/DashboardCanvas/index.ts +9 -0
- package/src/components/DashboardWidget/DashboardWidget.module.scss +100 -0
- package/src/components/DashboardWidget/DashboardWidget.tokens.scss +16 -0
- package/src/components/DashboardWidget/DashboardWidget.tsx +188 -0
- package/src/components/DashboardWidget/index.ts +7 -0
- package/src/components/DataTable/BodyRow.tsx +197 -0
- package/src/components/DataTable/ColumnVisibilityTrigger.module.scss +7 -0
- package/src/components/DataTable/ColumnVisibilityTrigger.tsx +105 -0
- package/src/components/DataTable/DataTable.module.scss +511 -0
- package/src/components/DataTable/DataTable.tokens.scss +80 -0
- package/src/components/DataTable/DataTable.tsx +734 -0
- package/src/components/DataTable/HeaderCell.module.scss +215 -0
- package/src/components/DataTable/HeaderCell.tsx +440 -0
- package/src/components/DataTable/columnShift.ts +117 -0
- package/src/components/DataTable/index.ts +23 -0
- package/src/components/DataTable/pinStyle.ts +47 -0
- package/src/components/DataTable/reorderColumns.ts +66 -0
- package/src/components/DataTable/types.ts +289 -0
- package/src/components/DataTable/useColumnDragShift.ts +216 -0
- package/src/components/DataTable/useDataTable.ts +372 -0
- package/src/components/DataTable/useResizeHandle.ts +97 -0
- package/src/components/DatePicker/DatePicker.module.scss +180 -0
- package/src/components/DatePicker/DatePicker.tokens.scss +152 -0
- package/src/components/DatePicker/DatePicker.tsx +664 -0
- package/src/components/DatePicker/DatePickerGrid.module.scss +172 -0
- package/src/components/DatePicker/DatePickerGrid.tsx +341 -0
- package/src/components/DatePicker/InlineDatePicker.module.scss +32 -0
- package/src/components/DatePicker/InlineDatePicker.tsx +201 -0
- package/src/components/DatePicker/index.ts +4 -0
- package/src/components/DatePicker/utils.ts +364 -0
- package/src/components/DateRangePicker/DateRangePicker.module.scss +237 -0
- package/src/components/DateRangePicker/DateRangePicker.tokens.scss +78 -0
- package/src/components/DateRangePicker/DateRangePicker.tsx +720 -0
- package/src/components/DateRangePicker/InlineDateRangePicker.module.scss +94 -0
- package/src/components/DateRangePicker/InlineDateRangePicker.tsx +376 -0
- package/src/components/DateRangePicker/index.ts +5 -0
- package/src/components/DateRangePicker/utils.ts +151 -0
- package/src/components/DateStrip/DateStrip.module.scss +78 -0
- package/src/components/DateStrip/DateStrip.tokens.scss +23 -0
- package/src/components/DateStrip/DateStrip.tsx +205 -0
- package/src/components/DateStrip/index.ts +2 -0
- package/src/components/DefinitionList/DefinitionList.module.scss +102 -0
- package/src/components/DefinitionList/DefinitionList.tokens.scss +29 -0
- package/src/components/DefinitionList/DefinitionList.tsx +198 -0
- package/src/components/DefinitionList/index.ts +9 -0
- package/src/components/Divider/Divider.module.scss +144 -0
- package/src/components/Divider/Divider.tokens.scss +21 -0
- package/src/components/Divider/Divider.tsx +103 -0
- package/src/components/Divider/index.ts +2 -0
- package/src/components/Dot/Dot.module.scss +40 -0
- package/src/components/Dot/Dot.tokens.scss +4 -0
- package/src/components/Dot/Dot.tsx +45 -0
- package/src/components/Dot/index.ts +2 -0
- package/src/components/Drawer/Body.tsx +23 -0
- package/src/components/Drawer/Close.tsx +35 -0
- package/src/components/Drawer/Content.tsx +93 -0
- package/src/components/Drawer/Drawer.module.scss +232 -0
- package/src/components/Drawer/Drawer.tokens.scss +38 -0
- package/src/components/Drawer/Drawer.tsx +23 -0
- package/src/components/Drawer/DrawerRoot.tsx +190 -0
- package/src/components/Drawer/Footer.tsx +24 -0
- package/src/components/Drawer/Header.tsx +64 -0
- package/src/components/Drawer/Overlay.tsx +82 -0
- package/src/components/Drawer/context.ts +44 -0
- package/src/components/Drawer/index.ts +12 -0
- package/src/components/Drawer/useDragToClose.ts +149 -0
- package/src/components/DropdownMenu/CheckboxItem.tsx +144 -0
- package/src/components/DropdownMenu/Content.tsx +419 -0
- package/src/components/DropdownMenu/DropdownMenu.module.scss +280 -0
- package/src/components/DropdownMenu/DropdownMenu.tokens.scss +88 -0
- package/src/components/DropdownMenu/DropdownMenu.tsx +43 -0
- package/src/components/DropdownMenu/Group.tsx +76 -0
- package/src/components/DropdownMenu/Item.tsx +148 -0
- package/src/components/DropdownMenu/ItemIndicator.tsx +30 -0
- package/src/components/DropdownMenu/Radio.tsx +178 -0
- package/src/components/DropdownMenu/Root.tsx +100 -0
- package/src/components/DropdownMenu/Sub.tsx +332 -0
- package/src/components/DropdownMenu/Trigger.tsx +76 -0
- package/src/components/DropdownMenu/context.ts +65 -0
- package/src/components/DropdownMenu/index.ts +20 -0
- package/src/components/EmojiPicker/EmojiPicker.module.scss +84 -0
- package/src/components/EmojiPicker/EmojiPicker.tokens.scss +24 -0
- package/src/components/EmojiPicker/EmojiPicker.tsx +341 -0
- package/src/components/EmojiPicker/emojiData.ts +245 -0
- package/src/components/EmojiPicker/index.ts +3 -0
- package/src/components/EmptyState/EmptyState.module.scss +102 -0
- package/src/components/EmptyState/EmptyState.tokens.scss +28 -0
- package/src/components/EmptyState/EmptyState.tsx +112 -0
- package/src/components/EmptyState/index.ts +7 -0
- package/src/components/EntityChip/EntityChip.module.scss +275 -0
- package/src/components/EntityChip/EntityChip.tokens.scss +66 -0
- package/src/components/EntityChip/EntityChip.tsx +649 -0
- package/src/components/EntityChip/index.ts +10 -0
- package/src/components/ErrorState/ErrorState.module.scss +105 -0
- package/src/components/ErrorState/ErrorState.tokens.scss +32 -0
- package/src/components/ErrorState/ErrorState.tsx +125 -0
- package/src/components/ErrorState/index.ts +8 -0
- package/src/components/Field/Field.module.scss +46 -0
- package/src/components/Field/Field.tsx +185 -0
- package/src/components/Field/index.ts +2 -0
- package/src/components/FileUpload/FileUpload.module.scss +187 -0
- package/src/components/FileUpload/FileUpload.tokens.scss +81 -0
- package/src/components/FileUpload/FileUpload.tsx +569 -0
- package/src/components/FileUpload/formatBytes.ts +34 -0
- package/src/components/FileUpload/iconForFile.ts +40 -0
- package/src/components/FileUpload/index.ts +2 -0
- package/src/components/FilterChip/FilterChip.module.scss +61 -0
- package/src/components/FilterChip/FilterChip.tokens.scss +25 -0
- package/src/components/FilterChip/FilterChip.tsx +208 -0
- package/src/components/FilterChip/index.ts +5 -0
- package/src/components/FlowCanvas/FlowCanvas.module.scss +329 -0
- package/src/components/FlowCanvas/FlowCanvas.tokens.scss +45 -0
- package/src/components/FlowCanvas/FlowCanvas.tsx +1867 -0
- package/src/components/FlowCanvas/FlowControls.tsx +46 -0
- package/src/components/FlowCanvas/FlowEdge.tsx +58 -0
- package/src/components/FlowCanvas/FlowNode.tsx +76 -0
- package/src/components/FlowCanvas/edgePath.ts +85 -0
- package/src/components/FlowCanvas/index.ts +4 -0
- package/src/components/FlowCanvas/layout.ts +148 -0
- package/src/components/FlowCanvas/spatialNav.ts +76 -0
- package/src/components/FlowCanvas/types.ts +59 -0
- package/src/components/FlowCanvas/useViewport.ts +122 -0
- package/src/components/FormRow/FormRow.module.scss +2 -0
- package/src/components/FormRow/FormRow.tsx +37 -0
- package/src/components/FormRow/index.ts +2 -0
- package/src/components/FormSection/FormSection.module.scss +24 -0
- package/src/components/FormSection/FormSection.tsx +46 -0
- package/src/components/FormSection/index.ts +2 -0
- package/src/components/Grid/Grid.module.scss +162 -0
- package/src/components/Grid/Grid.tokens.scss +13 -0
- package/src/components/Grid/Grid.tsx +226 -0
- package/src/components/Grid/GridItem.tsx +77 -0
- package/src/components/Grid/index.ts +11 -0
- package/src/components/Highlight/Highlight.module.scss +43 -0
- package/src/components/Highlight/Highlight.tokens.scss +35 -0
- package/src/components/Highlight/Highlight.tsx +164 -0
- package/src/components/Highlight/HighlightOverlay.tsx +100 -0
- package/src/components/Highlight/index.ts +2 -0
- package/src/components/HighlightedText/HighlightedText.module.scss +11 -0
- package/src/components/HighlightedText/HighlightedText.tokens.scss +9 -0
- package/src/components/HighlightedText/HighlightedText.tsx +50 -0
- package/src/components/HighlightedText/highlightRanges.ts +44 -0
- package/src/components/HighlightedText/index.ts +2 -0
- package/src/components/IconPicker/IconPicker.module.scss +175 -0
- package/src/components/IconPicker/IconPicker.tokens.scss +40 -0
- package/src/components/IconPicker/IconPicker.tsx +629 -0
- package/src/components/IconPicker/index.ts +9 -0
- package/src/components/IconTile/IconTile.module.scss +57 -0
- package/src/components/IconTile/IconTile.tokens.scss +16 -0
- package/src/components/IconTile/IconTile.tsx +91 -0
- package/src/components/IconTile/index.ts +2 -0
- package/src/components/Image/Image.module.scss +266 -0
- package/src/components/Image/Image.tokens.scss +55 -0
- package/src/components/Image/Image.tsx +342 -0
- package/src/components/Image/index.ts +2 -0
- package/src/components/ImageCrop/ImageCrop.module.scss +274 -0
- package/src/components/ImageCrop/ImageCrop.tokens.scss +33 -0
- package/src/components/ImageCrop/ImageCrop.tsx +542 -0
- package/src/components/ImageCrop/cropGeometry.ts +120 -0
- package/src/components/ImageCrop/extractCropBlob.ts +140 -0
- package/src/components/ImageCrop/index.ts +11 -0
- package/src/components/ImageCrop/useCropPreview.ts +161 -0
- package/src/components/Indent/Indent.module.scss +33 -0
- package/src/components/Indent/Indent.tokens.scss +10 -0
- package/src/components/Indent/Indent.tsx +46 -0
- package/src/components/Indent/index.ts +2 -0
- package/src/components/Input/Input.module.scss +99 -0
- package/src/components/Input/Input.tokens.scss +54 -0
- package/src/components/Input/Input.tsx +105 -0
- package/src/components/Input/Suggestions.tsx +260 -0
- package/src/components/Input/index.ts +2 -0
- package/src/components/Kanban/Kanban.module.scss +39 -0
- package/src/components/Kanban/Kanban.tokens.scss +15 -0
- package/src/components/Kanban/Kanban.tsx +951 -0
- package/src/components/Kanban/index.ts +7 -0
- package/src/components/Kbd/Kbd.module.scss +47 -0
- package/src/components/Kbd/Kbd.tokens.scss +26 -0
- package/src/components/Kbd/Kbd.tsx +73 -0
- package/src/components/Kbd/index.ts +2 -0
- package/src/components/Lightbox/Lightbox.module.scss +283 -0
- package/src/components/Lightbox/Lightbox.tokens.scss +36 -0
- package/src/components/Lightbox/Lightbox.tsx +506 -0
- package/src/components/Lightbox/index.ts +2 -0
- package/src/components/Link/Link.module.scss +78 -0
- package/src/components/Link/Link.tokens.scss +28 -0
- package/src/components/Link/Link.tsx +114 -0
- package/src/components/Link/index.ts +2 -0
- package/src/components/LinkCard/LinkCard.module.scss +92 -0
- package/src/components/LinkCard/LinkCard.tokens.scss +9 -0
- package/src/components/LinkCard/LinkCard.tsx +72 -0
- package/src/components/LinkCard/index.ts +2 -0
- package/src/components/LiquidEditor/AutocompleteMenu.tsx +133 -0
- package/src/components/LiquidEditor/HighlightLayer.tsx +53 -0
- package/src/components/LiquidEditor/InsertVariableMenu.tsx +81 -0
- package/src/components/LiquidEditor/LiquidEditor.module.scss +339 -0
- package/src/components/LiquidEditor/LiquidEditor.tokens.scss +81 -0
- package/src/components/LiquidEditor/LiquidEditor.tsx +412 -0
- package/src/components/LiquidEditor/PreviewPane.tsx +29 -0
- package/src/components/LiquidEditor/caretCoordinates.ts +97 -0
- package/src/components/LiquidEditor/index.ts +7 -0
- package/src/components/LiquidEditor/liquidFilters.ts +23 -0
- package/src/components/LiquidEditor/liquidTokenizer.ts +235 -0
- package/src/components/LiquidEditor/types.ts +101 -0
- package/src/components/LiquidEditor/useLiquidAutocomplete.ts +178 -0
- package/src/components/List/List.module.scss +105 -0
- package/src/components/List/List.tokens.scss +42 -0
- package/src/components/List/List.tsx +124 -0
- package/src/components/List/index.ts +9 -0
- package/src/components/LiveRegion/LiveRegion.module.scss +3 -0
- package/src/components/LiveRegion/LiveRegion.tsx +116 -0
- package/src/components/LiveRegion/index.ts +2 -0
- package/src/components/Logo/Logo.module.scss +139 -0
- package/src/components/Logo/Logo.tokens.scss +14 -0
- package/src/components/Logo/Logo.tsx +126 -0
- package/src/components/Logo/index.ts +2 -0
- package/src/components/Masonry/Masonry.module.scss +42 -0
- package/src/components/Masonry/Masonry.tokens.scss +8 -0
- package/src/components/Masonry/Masonry.tsx +136 -0
- package/src/components/Masonry/index.ts +2 -0
- package/src/components/Masonry/masonryUtils.ts +42 -0
- package/src/components/MediaTile/MediaTile.module.scss +183 -0
- package/src/components/MediaTile/MediaTile.tokens.scss +60 -0
- package/src/components/MediaTile/MediaTile.tsx +208 -0
- package/src/components/MediaTile/index.ts +8 -0
- package/src/components/Modal/Body.tsx +27 -0
- package/src/components/Modal/Close.tsx +45 -0
- package/src/components/Modal/Content.tsx +98 -0
- package/src/components/Modal/Footer.tsx +28 -0
- package/src/components/Modal/Header.tsx +51 -0
- package/src/components/Modal/Modal.module.scss +262 -0
- package/src/components/Modal/Modal.tokens.scss +50 -0
- package/src/components/Modal/Modal.tsx +23 -0
- package/src/components/Modal/ModalRoot.tsx +299 -0
- package/src/components/Modal/Overlay.tsx +101 -0
- package/src/components/Modal/context.ts +60 -0
- package/src/components/Modal/index.ts +11 -0
- package/src/components/MoneyInput/MoneyInput.module.scss +57 -0
- package/src/components/MoneyInput/MoneyInput.tsx +433 -0
- package/src/components/MoneyInput/index.ts +2 -0
- package/src/components/OptionsPicker/OptionsPicker.module.scss +216 -0
- package/src/components/OptionsPicker/OptionsPicker.tokens.scss +68 -0
- package/src/components/OptionsPicker/OptionsPicker.tsx +936 -0
- package/src/components/OptionsPicker/index.ts +9 -0
- package/src/components/OtpInput/OtpInput.module.scss +69 -0
- package/src/components/OtpInput/OtpInput.tokens.scss +45 -0
- package/src/components/OtpInput/OtpInput.tsx +368 -0
- package/src/components/OtpInput/index.ts +2 -0
- package/src/components/Page/Page.module.scss +30 -0
- package/src/components/Page/Page.tokens.scss +11 -0
- package/src/components/Page/Page.tsx +53 -0
- package/src/components/Page/index.ts +2 -0
- package/src/components/PageHeader/PageHeader.module.scss +197 -0
- package/src/components/PageHeader/PageHeader.tokens.scss +41 -0
- package/src/components/PageHeader/PageHeader.tsx +310 -0
- package/src/components/PageHeader/index.ts +20 -0
- package/src/components/Pagination/Pagination.module.scss +117 -0
- package/src/components/Pagination/Pagination.tokens.scss +54 -0
- package/src/components/Pagination/Pagination.tsx +165 -0
- package/src/components/Pagination/index.ts +4 -0
- package/src/components/Pagination/paginationRange.ts +72 -0
- package/src/components/PasswordInput/PasswordInput.module.scss +120 -0
- package/src/components/PasswordInput/PasswordInput.tokens.scss +44 -0
- package/src/components/PasswordInput/PasswordInput.tsx +210 -0
- package/src/components/PasswordInput/index.ts +2 -0
- package/src/components/PasswordStrengthMeter/PasswordStrengthMeter.module.scss +80 -0
- package/src/components/PasswordStrengthMeter/PasswordStrengthMeter.tokens.scss +40 -0
- package/src/components/PasswordStrengthMeter/PasswordStrengthMeter.tsx +96 -0
- package/src/components/PasswordStrengthMeter/index.ts +2 -0
- package/src/components/PersonDisplay/PersonDisplay.module.scss +180 -0
- package/src/components/PersonDisplay/PersonDisplay.tokens.scss +35 -0
- package/src/components/PersonDisplay/PersonDisplay.tsx +420 -0
- package/src/components/PersonDisplay/index.ts +10 -0
- package/src/components/PhoneInput/PhoneInput.module.scss +23 -0
- package/src/components/PhoneInput/PhoneInput.tsx +271 -0
- package/src/components/PhoneInput/index.ts +4 -0
- package/src/components/PhoneInput/phone.ts +128 -0
- package/src/components/PillMenu/PillMenu.module.scss +117 -0
- package/src/components/PillMenu/PillMenu.tokens.scss +22 -0
- package/src/components/PillMenu/PillMenu.tsx +324 -0
- package/src/components/PillMenu/index.ts +2 -0
- package/src/components/Popover/Anchor.tsx +37 -0
- package/src/components/Popover/Close.tsx +45 -0
- package/src/components/Popover/Content.tsx +213 -0
- package/src/components/Popover/Heading.tsx +33 -0
- package/src/components/Popover/Popover.module.scss +177 -0
- package/src/components/Popover/Popover.tokens.scss +22 -0
- package/src/components/Popover/Popover.tsx +23 -0
- package/src/components/Popover/PopoverRoot.tsx +75 -0
- package/src/components/Popover/Trigger.tsx +86 -0
- package/src/components/Popover/context.ts +22 -0
- package/src/components/Popover/index.ts +7 -0
- package/src/components/Progress/Progress.module.scss +110 -0
- package/src/components/Progress/Progress.tokens.scss +44 -0
- package/src/components/Progress/Progress.tsx +129 -0
- package/src/components/Progress/index.ts +2 -0
- package/src/components/QrCode/QrCode.module.scss +97 -0
- package/src/components/QrCode/QrCode.tokens.scss +17 -0
- package/src/components/QrCode/QrCode.tsx +233 -0
- package/src/components/QrCode/index.ts +2 -0
- package/src/components/QrCode/qr.ts +161 -0
- package/src/components/Radio/Radio.module.scss +175 -0
- package/src/components/Radio/Radio.tokens.scss +59 -0
- package/src/components/Radio/Radio.tsx +127 -0
- package/src/components/Radio/RadioGroup.module.scss +42 -0
- package/src/components/Radio/RadioGroup.tsx +120 -0
- package/src/components/Radio/RadioGroupContext.tsx +30 -0
- package/src/components/Radio/index.ts +4 -0
- package/src/components/Rail/Rail.module.scss +510 -0
- package/src/components/Rail/Rail.tokens.scss +92 -0
- package/src/components/Rail/Rail.tsx +287 -0
- package/src/components/Rail/RailCollapseToggle.tsx +57 -0
- package/src/components/Rail/RailFooter.tsx +17 -0
- package/src/components/Rail/RailGroup.tsx +728 -0
- package/src/components/Rail/RailGroupContext.ts +9 -0
- package/src/components/Rail/RailHeader.tsx +23 -0
- package/src/components/Rail/RailItem.tsx +119 -0
- package/src/components/Rail/RailSection.tsx +39 -0
- package/src/components/Rail/RailSpacer.tsx +19 -0
- package/src/components/Rail/index.ts +9 -0
- package/src/components/RelativeDateInput/RelativeDateInput.module.scss +15 -0
- package/src/components/RelativeDateInput/RelativeDateInput.tokens.scss +6 -0
- package/src/components/RelativeDateInput/RelativeDateInput.tsx +474 -0
- package/src/components/RelativeDateInput/index.ts +10 -0
- package/src/components/RelativeDateInput/relativeDate.ts +127 -0
- package/src/components/RichText/RichText.module.scss +5 -0
- package/src/components/RichText/RichText.tsx +70 -0
- package/src/components/RichText/RichTextAttachment.module.scss +82 -0
- package/src/components/RichText/RichTextAttachment.tsx +122 -0
- package/src/components/RichText/_prose.scss +79 -0
- package/src/components/RichText/attachmentIcon.tsx +24 -0
- package/src/components/RichText/engine/attachment.ts +154 -0
- package/src/components/RichText/engine/autolink.ts +64 -0
- package/src/components/RichText/engine/blockUnit.ts +174 -0
- package/src/components/RichText/engine/colorMarks.ts +85 -0
- package/src/components/RichText/engine/escape.ts +11 -0
- package/src/components/RichText/engine/fromHtml.ts +353 -0
- package/src/components/RichText/engine/fromMarkdown.ts +20 -0
- package/src/components/RichText/engine/inlines.ts +82 -0
- package/src/components/RichText/engine/listDepths.ts +32 -0
- package/src/components/RichText/engine/marks.ts +80 -0
- package/src/components/RichText/engine/mdToHtml.ts +178 -0
- package/src/components/RichText/engine/model.ts +157 -0
- package/src/components/RichText/engine/position.ts +118 -0
- package/src/components/RichText/engine/renderDoc.tsx +395 -0
- package/src/components/RichText/engine/renderLink.ts +15 -0
- package/src/components/RichText/engine/renderMention.ts +16 -0
- package/src/components/RichText/engine/safeHref.ts +22 -0
- package/src/components/RichText/engine/toHtml.ts +154 -0
- package/src/components/RichText/engine/toMarkdown.ts +97 -0
- package/src/components/RichText/engine/transforms.ts +434 -0
- package/src/components/RichText/imageMaxHeightStyle.ts +10 -0
- package/src/components/RichText/index.ts +31 -0
- package/src/components/RichTextEditor/RichTextAttachmentConfig.tsx +264 -0
- package/src/components/RichTextEditor/RichTextBlockControls.tsx +365 -0
- package/src/components/RichTextEditor/RichTextBlockMenu.tsx +162 -0
- package/src/components/RichTextEditor/RichTextColorMenu.tsx +115 -0
- package/src/components/RichTextEditor/RichTextEditor.module.scss +298 -0
- package/src/components/RichTextEditor/RichTextEditor.tsx +1910 -0
- package/src/components/RichTextEditor/RichTextImageResizer.tsx +131 -0
- package/src/components/RichTextEditor/RichTextLinkEditor.tsx +151 -0
- package/src/components/RichTextEditor/RichTextMentionMenu.tsx +99 -0
- package/src/components/RichTextEditor/RichTextToolbar.tsx +379 -0
- package/src/components/RichTextEditor/autolinkInput.ts +65 -0
- package/src/components/RichTextEditor/blockDrop.ts +9 -0
- package/src/components/RichTextEditor/blockReflow.ts +62 -0
- package/src/components/RichTextEditor/commands.ts +300 -0
- package/src/components/RichTextEditor/history.ts +93 -0
- package/src/components/RichTextEditor/icons.tsx +152 -0
- package/src/components/RichTextEditor/imageSize.ts +70 -0
- package/src/components/RichTextEditor/index.ts +5 -0
- package/src/components/RichTextEditor/input.ts +209 -0
- package/src/components/RichTextEditor/inputRules.ts +71 -0
- package/src/components/RichTextEditor/links.ts +103 -0
- package/src/components/RichTextEditor/mentionContext.ts +37 -0
- package/src/components/RichTextEditor/mentions.ts +28 -0
- package/src/components/RichTextEditor/preventSelectionLoss.ts +15 -0
- package/src/components/RichTextEditor/selection.ts +415 -0
- package/src/components/RichTextEditor/shortcuts.ts +44 -0
- package/src/components/RichTextEditor/useAnchoredFloating.ts +50 -0
- package/src/components/RichTextEditor/useAttachmentConfig.ts +134 -0
- package/src/components/RichTextEditor/useDismissOnOutsidePointerDown.ts +26 -0
- package/src/components/RichTextEditor/useLinkEditor.ts +107 -0
- package/src/components/RichTextEditor/useMention.ts +226 -0
- package/src/components/RichTextEditor/useOverlayRect.ts +73 -0
- package/src/components/RichTextEditor/useUpload.ts +246 -0
- package/src/components/Screen/Screen.module.scss +46 -0
- package/src/components/Screen/Screen.tokens.scss +20 -0
- package/src/components/Screen/Screen.tsx +97 -0
- package/src/components/Screen/index.ts +2 -0
- package/src/components/ScrollArea/ScrollArea.module.scss +39 -0
- package/src/components/ScrollArea/ScrollArea.tokens.scss +15 -0
- package/src/components/ScrollArea/ScrollArea.tsx +154 -0
- package/src/components/ScrollArea/index.ts +2 -0
- package/src/components/Select/Chip.tsx +45 -0
- package/src/components/Select/Empty.tsx +39 -0
- package/src/components/Select/Error.tsx +51 -0
- package/src/components/Select/HiddenInputs.tsx +44 -0
- package/src/components/Select/Listbox.tsx +557 -0
- package/src/components/Select/Loading.tsx +30 -0
- package/src/components/Select/Select.module.scss +512 -0
- package/src/components/Select/Select.tokens.scss +140 -0
- package/src/components/Select/Select.tsx +685 -0
- package/src/components/Select/Trigger.tsx +952 -0
- package/src/components/Select/context.ts +101 -0
- package/src/components/Select/emptyStateText.ts +19 -0
- package/src/components/Select/index.ts +9 -0
- package/src/components/Select/useSelectState.ts +157 -0
- package/src/components/Select/utils-types.ts +7 -0
- package/src/components/Select/utils.ts +122 -0
- package/src/components/SettingRow/SettingRow.module.scss +166 -0
- package/src/components/SettingRow/SettingRow.tokens.scss +37 -0
- package/src/components/SettingRow/SettingRow.tsx +242 -0
- package/src/components/SettingRow/index.ts +7 -0
- package/src/components/Skeleton/Skeleton.module.scss +55 -0
- package/src/components/Skeleton/Skeleton.tokens.scss +17 -0
- package/src/components/Skeleton/Skeleton.tsx +131 -0
- package/src/components/Skeleton/index.ts +4 -0
- package/src/components/Skeleton/useSkeletonVisibility.ts +95 -0
- package/src/components/Slider/Slider.module.scss +293 -0
- package/src/components/Slider/Slider.tokens.scss +98 -0
- package/src/components/Slider/Slider.tsx +624 -0
- package/src/components/Slider/index.ts +9 -0
- package/src/components/SlotGrid/SlotGrid.module.scss +61 -0
- package/src/components/SlotGrid/SlotGrid.tokens.scss +21 -0
- package/src/components/SlotGrid/SlotGrid.tsx +111 -0
- package/src/components/SlotGrid/index.ts +2 -0
- package/src/components/SocialButton/SocialButton.module.scss +4 -0
- package/src/components/SocialButton/SocialButton.tsx +40 -0
- package/src/components/SocialButton/index.ts +2 -0
- package/src/components/Sortable/Sortable.module.scss +177 -0
- package/src/components/Sortable/Sortable.tokens.scss +27 -0
- package/src/components/Sortable/Sortable.tsx +572 -0
- package/src/components/Sortable/containerAwareCollision.ts +125 -0
- package/src/components/Sortable/index.ts +13 -0
- package/src/components/SortableGroup/SortableGroup.module.scss +23 -0
- package/src/components/SortableGroup/SortableGroup.tsx +315 -0
- package/src/components/SortableGroup/index.ts +4 -0
- package/src/components/SortableGroup/moveSortableItem.ts +39 -0
- package/src/components/Sparkline/Sparkline.module.scss +28 -0
- package/src/components/Sparkline/Sparkline.tokens.scss +7 -0
- package/src/components/Sparkline/Sparkline.tsx +133 -0
- package/src/components/Sparkline/index.ts +2 -0
- package/src/components/Sparkline/sparklineModel.ts +119 -0
- package/src/components/Split/Split.module.scss +158 -0
- package/src/components/Split/Split.tokens.scss +10 -0
- package/src/components/Split/Split.tsx +231 -0
- package/src/components/Split/index.ts +2 -0
- package/src/components/Stack/Stack.module.scss +67 -0
- package/src/components/Stack/Stack.tokens.scss +11 -0
- package/src/components/Stack/Stack.tsx +100 -0
- package/src/components/Stack/index.ts +2 -0
- package/src/components/StagePath/StagePath.module.scss +255 -0
- package/src/components/StagePath/StagePath.tokens.scss +43 -0
- package/src/components/StagePath/StagePath.tsx +176 -0
- package/src/components/StagePath/index.ts +2 -0
- package/src/components/StatGroup/StatGroup.module.scss +6 -0
- package/src/components/StatGroup/StatGroup.tsx +70 -0
- package/src/components/StatGroup/StatGroupContext.ts +10 -0
- package/src/components/StatGroup/StatTile.module.scss +99 -0
- package/src/components/StatGroup/StatTile.tokens.scss +35 -0
- package/src/components/StatGroup/StatTile.tsx +143 -0
- package/src/components/StatGroup/index.ts +4 -0
- package/src/components/Sticky/Sticky.module.scss +98 -0
- package/src/components/Sticky/Sticky.tokens.scss +17 -0
- package/src/components/Sticky/Sticky.tsx +103 -0
- package/src/components/Sticky/index.ts +2 -0
- package/src/components/Switch/Switch.module.scss +182 -0
- package/src/components/Switch/Switch.tokens.scss +59 -0
- package/src/components/Switch/Switch.tsx +214 -0
- package/src/components/Switch/index.ts +2 -0
- package/src/components/Table/Table.module.scss +308 -0
- package/src/components/Table/Table.tokens.scss +79 -0
- package/src/components/Table/Table.tsx +381 -0
- package/src/components/Table/index.ts +15 -0
- package/src/components/TableOfContents/TableOfContents.module.scss +83 -0
- package/src/components/TableOfContents/TableOfContents.tokens.scss +26 -0
- package/src/components/TableOfContents/TableOfContents.tsx +192 -0
- package/src/components/TableOfContents/activeSection.ts +18 -0
- package/src/components/TableOfContents/index.ts +2 -0
- package/src/components/Tabs/Tabs.module.scss +359 -0
- package/src/components/Tabs/Tabs.tokens.scss +50 -0
- package/src/components/Tabs/Tabs.tsx +538 -0
- package/src/components/Tabs/index.ts +2 -0
- package/src/components/Text/Text.module.scss +115 -0
- package/src/components/Text/Text.tokens.scss +55 -0
- package/src/components/Text/Text.tsx +197 -0
- package/src/components/Text/index.ts +2 -0
- package/src/components/Textarea/Textarea.module.scss +94 -0
- package/src/components/Textarea/Textarea.tokens.scss +48 -0
- package/src/components/Textarea/Textarea.tsx +246 -0
- package/src/components/Textarea/index.ts +2 -0
- package/src/components/Thread/Thread.module.scss +161 -0
- package/src/components/Thread/Thread.tokens.scss +32 -0
- package/src/components/Thread/Thread.tsx +176 -0
- package/src/components/Thread/index.ts +2 -0
- package/src/components/TimeField/TimeField.module.scss +232 -0
- package/src/components/TimeField/TimeField.tsx +793 -0
- package/src/components/TimeField/index.ts +2 -0
- package/src/components/Timeline/Timeline.module.scss +58 -0
- package/src/components/Timeline/Timeline.tokens.scss +13 -0
- package/src/components/Timeline/Timeline.tsx +53 -0
- package/src/components/Timeline/index.ts +2 -0
- package/src/components/Title/Title.module.scss +84 -0
- package/src/components/Title/Title.tokens.scss +37 -0
- package/src/components/Title/Title.tsx +150 -0
- package/src/components/Title/index.ts +2 -0
- package/src/components/Toast/Toast.module.scss +299 -0
- package/src/components/Toast/Toast.tokens.scss +65 -0
- package/src/components/Toast/Toast.tsx +122 -0
- package/src/components/Toast/ToastViewport.tsx +166 -0
- package/src/components/Toast/api.ts +175 -0
- package/src/components/Toast/index.ts +5 -0
- package/src/components/Toast/store.ts +133 -0
- package/src/components/Toast/useToastTimer.ts +94 -0
- package/src/components/Tooltip/Tooltip.module.scss +99 -0
- package/src/components/Tooltip/Tooltip.tokens.scss +23 -0
- package/src/components/Tooltip/Tooltip.tsx +341 -0
- package/src/components/Tooltip/index.ts +2 -0
- package/src/components/TopBar/TopBar.module.scss +235 -0
- package/src/components/TopBar/TopBar.tokens.scss +79 -0
- package/src/components/TopBar/TopBar.tsx +87 -0
- package/src/components/TopBar/TopBarEnd.tsx +20 -0
- package/src/components/TopBar/TopBarIconButton.tsx +86 -0
- package/src/components/TopBar/TopBarSearch.tsx +319 -0
- package/src/components/TopBar/TopBarSearchPanel.tsx +268 -0
- package/src/components/TopBar/TopBarStart.tsx +20 -0
- package/src/components/TopBar/index.ts +11 -0
- package/src/components/Tour/Spotlight.tsx +121 -0
- package/src/components/Tour/Tour.module.scss +210 -0
- package/src/components/Tour/Tour.tokens.scss +58 -0
- package/src/components/Tour/Tour.tsx +617 -0
- package/src/components/Tour/index.ts +2 -0
- package/src/components/Tour/useTourTarget.ts +115 -0
- package/src/components/TreeView/TreeView.module.scss +268 -0
- package/src/components/TreeView/TreeView.tokens.scss +36 -0
- package/src/components/TreeView/TreeView.tsx +536 -0
- package/src/components/TreeView/index.ts +3 -0
- package/src/components/TreeView/treeModel.ts +224 -0
- package/src/components/VisuallyHidden/VisuallyHidden.module.scss +7 -0
- package/src/components/VisuallyHidden/VisuallyHidden.tsx +29 -0
- package/src/components/VisuallyHidden/index.ts +2 -0
- package/src/components/WidgetPreview/WidgetPreview.module.scss +11 -0
- package/src/components/WidgetPreview/WidgetPreview.tokens.scss +26 -0
- package/src/components/WidgetPreview/WidgetPreview.tsx +36 -0
- package/src/components/WidgetPreview/WidgetShape.module.scss +140 -0
- package/src/components/WidgetPreview/WidgetShape.tsx +159 -0
- package/src/components/WidgetPreview/index.ts +2 -0
- package/src/components/_internal/Trend.tsx +27 -0
- package/src/components/_internal/choiceTile.scss +74 -0
- package/src/components/_internal/collapse.scss +10 -0
- package/src/components/_internal/collapse.ts +58 -0
- package/src/components/_internal/currency.ts +257 -0
- package/src/components/_internal/dragAnnouncements.ts +200 -0
- package/src/components/_internal/fieldWiring.ts +225 -0
- package/src/components/_internal/gridSpan.ts +54 -0
- package/src/components/_internal/isoDay.ts +35 -0
- package/src/components/_internal/letterKey.ts +26 -0
- package/src/components/_internal/overlay/focusRestore.ts +21 -0
- package/src/components/_internal/overlay/index.ts +12 -0
- package/src/components/_internal/overlay/tourHold.ts +23 -0
- package/src/components/_internal/overlay/useFocusTrap.ts +120 -0
- package/src/components/_internal/overlay/useInOverlay.ts +81 -0
- package/src/components/_internal/overlay/useOverlayStack.ts +232 -0
- package/src/components/_internal/overlay/useScrollLock.ts +112 -0
- package/src/components/_internal/refs.ts +48 -0
- package/src/components/_internal/statusColor.ts +26 -0
- package/src/components/_internal/timeUtils.ts +23 -0
- package/src/components/_internal/useAsyncOptions.ts +145 -0
- package/src/components/_internal/useClippedTooltip.ts +50 -0
- package/src/components/_internal/useControllableState.ts +50 -0
- package/src/components.manifest.json +1141 -0
- package/src/hooks/useBelowBreakpoint.ts +58 -0
- package/src/i18n/I18nProvider.tsx +52 -0
- package/src/i18n/LocaleProvider.tsx +17 -0
- package/src/i18n/en.ts +648 -0
- package/src/i18n/format.ts +71 -0
- package/src/i18n/index.ts +13 -0
- package/src/i18n/messages.ts +1275 -0
- package/src/i18n/ru.ts +677 -0
- package/src/i18n/useLocale.ts +19 -0
- package/src/i18n/useTranslation.ts +58 -0
- package/src/index.ts +913 -0
- package/src/palette/index.ts +2 -0
- package/src/palette/palette.ts +108 -0
- package/src/styles/dark.scss +4 -0
- package/src/styles/global.scss +7 -0
- package/src/styles/mixins.scss +143 -0
- package/src/styles/reset.scss +78 -0
- package/src/styles/tokens.scss +18 -0
- package/src/styles/typography.scss +44 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# `<Grid>` — 2D layout primitive
|
|
2
|
+
|
|
3
|
+
```tsx
|
|
4
|
+
// Auto-fit responsive (default) — columns reflow by container width.
|
|
5
|
+
<Grid gap="md">
|
|
6
|
+
{cards.map(c => <Card key={c.id}>...</Card>)}
|
|
7
|
+
</Grid>
|
|
8
|
+
|
|
9
|
+
// Fixed N equal columns.
|
|
10
|
+
<Grid columns={2} gap="lg">
|
|
11
|
+
<Input label="First name" />
|
|
12
|
+
<Input label="Last name" />
|
|
13
|
+
</Grid>
|
|
14
|
+
|
|
15
|
+
// Dashboard widgets — 12-column base, fraction spans, collapses under 640px.
|
|
16
|
+
<Grid columns={12} gap="md" collapseBelow="md">
|
|
17
|
+
<Grid.Item span="25%"><Card>KPI</Card></Grid.Item>
|
|
18
|
+
<Grid.Item span="75%"><Card>Chart</Card></Grid.Item>
|
|
19
|
+
<Grid.Item span="33%"><Card>List</Card></Grid.Item>
|
|
20
|
+
<Grid.Item span="67%"><Card>Table</Card></Grid.Item>
|
|
21
|
+
<Grid.Item span="100%"><Card>Footer row</Card></Grid.Item>
|
|
22
|
+
</Grid>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
<!-- props:start -->
|
|
26
|
+
|
|
27
|
+
## Props
|
|
28
|
+
|
|
29
|
+
### `GridProps`
|
|
30
|
+
|
|
31
|
+
<!-- prettier-ignore -->
|
|
32
|
+
| Prop | Type | Required | Description |
|
|
33
|
+
|---|---|---|---|
|
|
34
|
+
| `gap` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| '2xl'` | no | Gap between cells. `xs` (4) / `sm` (8) / `md` (12, default) / `lg` (16) / `xl` (24) / `2xl` (32). Same scale as Stack and Cluster. |
|
|
35
|
+
| `alignItems` | `'start' \| 'center' \| 'end' \| 'stretch'` | no | Cross-axis (vertical within row) alignment of each cell. Default browser behavior is `stretch`; omit to use the default. Useful for cards of varying intrinsic height. |
|
|
36
|
+
| `justifyItems` | `'start' \| 'center' \| 'end' \| 'stretch'` | no | Main-axis (horizontal within track) alignment of each cell. Default browser behavior is `stretch`; omit to use the default. |
|
|
37
|
+
| `as` | `'div' \| 'section' \| 'ul' \| 'ol' \| 'nav' \| 'main' \| 'aside' \| 'article' \| 'header' \| 'footer'` | no | Element to render. Default `'div'`. Limited to `div`, `section`, `ul`, `ol`, `nav`, `main`, `aside`, `article`, `header`, `footer` rather than fully polymorphic. |
|
|
38
|
+
| `columns` | `number` | no | Fixed number of equal-width columns. Mutually exclusive with `minColumnWidth`. |
|
|
39
|
+
| `minColumnWidth` | `string` | no | Minimum column width for auto-fit responsive layout. Columns reflow based on container width — no breakpoints needed. CSS length string like `'240px'` or `'15rem'`. Defaults to `'240px'` when neither `columns` nor `minColumnWidth` is provided. |
|
|
40
|
+
| `collapseBelow` | `CollapseBreakpoint \| Partial<Record<CollapseBreakpoint, number>>` | no | Collapse to a single visual column when the GRID'S OWN width (container query, not viewport) drops below the preset: `sm` 480px / `md` 640px / `lg` 768px. Every child spans the full row below the threshold — `Grid.Item` spans included. Only valid with `columns` (auto-fit grids already reflow). Consumer inline `style={{ gridColumn }}` on a child still wins below the threshold (inline beats any stylesheet rule) — don't do that; use `Grid.Item span` instead. ❌ Anti-pattern: a `collapseBelow` grid must get its width from its parent. `container-type: inline-size` zeroes the grid's contribution to intrinsic sizing, so in an intrinsic-width context (`Split`'s default `auto` aside track, a `Cluster` item, `width: max-content`) it renders at width 0 — give the parent a concrete width instead. Whichever element carries the containment also becomes the containing block for absolutely-positioned descendants (layout containment) — the grid itself for the string form, the wrapper below for the map form. Also accepts a graduated breakpoint→columns map, e.g. `collapseBelow={{ md: 6, sm: 1 }}`: below 640px the grid re-templates to 6 columns (item spans clamp to fit — a span wider than the step becomes a full row), and below 480px to a single column. Use when jumping straight from N columns to 1 wastes tablet widths. When several breakpoints match, the smallest wins. Only `Grid.Item` children get span clamping; plain children auto-place into the step's tracks. ⚠️ The map form (and ONLY the map form) renders an extra wrapper `<div>` around the grid element — it carries `container-type: inline-size`, because re-templating the grid requires querying an ancestor, not the grid itself. Consequences: a `> child` CSS selector aimed at the grid from its parent now hits the wrapper instead, and layout the parent applies to "the Grid" (`flex: 1`, `grid-column`, `align-self` via `className`) lands on the grid *inside* the wrapper, where the parent's layout can't see it — put that layout on an element you control around the Grid. `ref`, `className`, `style`, `as` and all spread props stay on the grid element. |
|
|
41
|
+
| …native | | | plus native HTML attributes |
|
|
42
|
+
|
|
43
|
+
### `GridItemProps`
|
|
44
|
+
|
|
45
|
+
<!-- prettier-ignore -->
|
|
46
|
+
| Prop | Type | Required | Description |
|
|
47
|
+
|---|---|---|---|
|
|
48
|
+
| `span` | `GridItemSpan` | no | Column span. Omit for a single track (auto placement). - number — `span N` of the parent's `columns`. - `'25%' \| '33%' \| '50%' \| '67%' \| '75%'` — fractions of a 12-column grid; use with `columns={12}` (other counts won't produce the named fraction — documented, not validated). - `'100%'` / `'full'` — the entire row; safe with any Grid variant. |
|
|
49
|
+
| `as` | `'div' \| 'li' \| 'section' \| 'article' \| 'aside'` | no | Element to render. Default `'div'`. Use `'li'` inside `<Grid as="ul">`. |
|
|
50
|
+
| …native | | | plus native HTML attributes |
|
|
51
|
+
|
|
52
|
+
<!-- props:end -->
|
|
53
|
+
|
|
54
|
+
- **One of `columns` or `minColumnWidth`, not both.** TypeScript enforces it.
|
|
55
|
+
|
|
56
|
+
**`<Grid.Item span>` — per-cell column span:**
|
|
57
|
+
|
|
58
|
+
| `span` | Tracks (12-col grid) |
|
|
59
|
+
| ------------------- | --------------------------------------------------------- |
|
|
60
|
+
| `'25%'` | 3/12 |
|
|
61
|
+
| `'33%'` | 4/12 |
|
|
62
|
+
| `'50%'` | 6/12 |
|
|
63
|
+
| `'67%'` | 8/12 |
|
|
64
|
+
| `'75%'` | 9/12 |
|
|
65
|
+
| `'100%'` / `'full'` | full row (`1 / -1`), safe in ANY grid |
|
|
66
|
+
| number | `span N` tracks of whatever `columns` the parent declares |
|
|
67
|
+
|
|
68
|
+
Fractions assume a 12-column grid — pair them with `columns={12}` on the parent `<Grid>`. A numeric `span` is relative to the parent's actual `columns` count, no 12-col assumption. `Grid.Item` is opt-in; plain children remain valid Grid cells.
|
|
69
|
+
|
|
70
|
+
**`collapseBelow` — collapse to one column below a width preset:**
|
|
71
|
+
|
|
72
|
+
Only valid on a fixed-`columns` Grid (auto-fit grids already reflow). Presets: `'sm'` 480px / `'md'` 640px / `'lg'` 768px. This is a **container query on the Grid's own width, not the viewport** — it fires based on the space the Grid itself has, not the browser window, so it collapses correctly inside a narrow sidebar or split pane even on a wide screen — provided the pane gives the grid a definite width. Below the threshold every child spans the full row, `Grid.Item` spans included.
|
|
73
|
+
|
|
74
|
+
For "one row while the content fits, stacked when it doesn't", use `<Cluster stretchWrapped>` instead of guessing a threshold.
|
|
75
|
+
|
|
76
|
+
Also takes a graduated breakpoint→columns map instead of a single string, for a step-down rather than straight-to-1-column collapse — `Grid.Item` spans clamp to fit each step:
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
<Grid columns={12} gap="md" collapseBelow={{ md: 6, sm: 1 }}>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The map form — and only the map form — renders an extra wrapper `<div>` around the grid element, because re-templating the grid needs the size container on an ancestor (a container query never restyles its own container). `ref`, `className`, `style`, `as` and spread props all stay on the grid element, so the only thing that changes for a consumer is the DOM: a `> child` selector aimed at the Grid from its parent now hits the wrapper, and layout meant for "the Grid" (`flex: 1`, `grid-column`, `align-self` via `className`) lands inside the wrapper where the parent can't see it — put that layout on your own element around the Grid. The string form's DOM is unchanged.
|
|
83
|
+
|
|
84
|
+
**Anti-patterns:**
|
|
85
|
+
|
|
86
|
+
- ❌ Grid for a single column of vertical flow — use Stack.
|
|
87
|
+
- ❌ Grid for unaligned wrapping rows (toolbars, tag lists) — use Cluster.
|
|
88
|
+
- ❌ `<Grid columns="auto 1fr">` strings — not supported in v1. For asymmetric / named tracks, use raw CSS Grid via className.
|
|
89
|
+
- ❌ `<Grid as="ul">` with non-`<li>` children. The component doesn't enforce list semantics; consumers must.
|
|
90
|
+
- ❌ Fraction spans (other than `'100%'`) on a Grid whose `columns` isn't 12 — the span is a fixed track count, so it overflows into implicit tracks on a non-12 grid.
|
|
91
|
+
- ❌ A `collapseBelow` grid in an intrinsic-width context (`Split`'s default `auto` aside track, a `Cluster` item, `width: max-content`). `container-type: inline-size` makes the grid contribute zero intrinsic width, so it renders at width 0 — the grid must get its width from its parent; give the aside a concrete width instead. The element carrying the containment also becomes the containing block for absolutely-positioned descendants (layout containment) — the grid itself for the string form, the wrapper for the map form; same box geometry either way.
|
|
92
|
+
- ❌ `<Grid>` for a list of clickable items — semantics matter; use `<ul><li>` or `<Grid as="ul">` with `<li>` children.
|
|
93
|
+
- ❌ Inline `gridTemplateColumns` in `style` instead of `columns` / `minColumnWidth` — it bypasses tokens and the responsive default.
|
|
94
|
+
- ❌ A numeric `Grid.Item` `span` larger than `columns` — it overflows into implicit tracks, like fraction spans on a non-12 grid.
|
|
95
|
+
- Under a map-form `collapseBelow`, a `Grid.Item` span wider than a step's column count becomes a full row.
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
// Photo gallery — auto-fit with a smaller minimum
|
|
99
|
+
<Grid minColumnWidth="160px" gap="sm">{photos.map((p) => <img key={p.id} src={p.src} />)}</Grid>
|
|
100
|
+
|
|
101
|
+
// Semantic element via `as`
|
|
102
|
+
<Grid as="section" columns={3} gap="md" aria-labelledby="dashboard-title">...</Grid>
|
|
103
|
+
```
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# `<Highlight>` — temporary attention ring on any block
|
|
2
|
+
|
|
3
|
+
```tsx
|
|
4
|
+
const [justAdded, setJustAdded] = useState<string | null>(null);
|
|
5
|
+
|
|
6
|
+
<Highlight active={row.id === justAdded} onDone={() => setJustAdded(null)}>
|
|
7
|
+
<Table.Row>…</Table.Row>
|
|
8
|
+
</Highlight>;
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Draws a steady 1px ring just outside its single child, with two waves growing outward 20px from the edge, for `duration` ms (default 3000). The waves run for as long as it's on. After `duration` it fades out and calls `onDone`. It renders **no wrapper**: it clones the child to attach a ref and `data-highlight="on" | "fading"` (a hook for tests and consumers). Nothing else on the child changes, so a `Card` keeps its elevation shadow and tone stripe. It works on `<tr>`, `<li>`, grid cells and any DS component that forwards `ref` to a DOM element. It's decorative only: no role, `aria-*` or `tabIndex` changes.
|
|
12
|
+
|
|
13
|
+
**How it's drawn:** the ring and waves are on a separate overlay (`aria-hidden`, `pointer-events: none`), portaled to `<body>` and `position: fixed` over the child. It tracks scroll, resize and layout shift. So the waves pass over neighbouring elements and are never clipped by a scroll container, a table's scroll wrapper or a `Card`'s `overflow`. The ring covers the child's **visible** part: the child's rect clipped by every ancestor with `overflow` ≠ `visible` and by the viewport. If the child is scrolled fully out of view, the overlay is hidden. The overlay sits above page content but below fixed app chrome (`--z-app-chrome`), so the top bar covers it when the child scrolls underneath, and dropdowns open above it. Inside a `Modal`, `Drawer`, `Popover` or `Tour`, it rises with its host.
|
|
14
|
+
|
|
15
|
+
**Known limit:** the ring can paint over sticky elements (e.g. a sticky table header) that the highlighted element scrolls under during a run, unless that sticky element's container clips it.
|
|
16
|
+
|
|
17
|
+
**Announcing it:** with `focus`, the focus move IS the announcement (the screen reader reads the focused block), so don't also fire a `LiveRegion`. Without `focus`, announce the event with `<LiveRegion>` (for example "Contact created").
|
|
18
|
+
|
|
19
|
+
**Renamed / removed tokens:** `--highlight-ring` is now `--highlight-color` (override that to recolour the ring and waves). `--highlight-ring-peak`, `--highlight-glow`, `--highlight-glow-blur`, `--highlight-wave`, `--highlight-wave-blur` and `--highlight-wave-count` no longer exist. Use `--highlight-color` and the `--highlight-wave-*` tokens (`-duration`, `-delay`, `-reach`, `-edge`, `-glow`).
|
|
20
|
+
|
|
21
|
+
<!-- props:start -->
|
|
22
|
+
|
|
23
|
+
## Props
|
|
24
|
+
|
|
25
|
+
<!-- prettier-ignore -->
|
|
26
|
+
| Prop | Type | Required | Description |
|
|
27
|
+
|---|---|---|---|
|
|
28
|
+
| `active` | `boolean` | yes | Turns the highlight on. Each false → true change starts it once: the ring and waves show, the optional scroll and focus run, and after `duration` it fades out and `onDone` fires. Setting it back to false early removes the ring at once WITHOUT calling `onDone`. Keeping it `true` doesn't restart anything; toggle false → true to highlight again. |
|
|
29
|
+
| `duration` | `number` | no | How long the ring holds before fading, in ms. Read when the highlight starts; changing it mid-run has no effect. A non-finite value (`Infinity`, `NaN`) keeps it on until `active` goes false. Default: `3000`. |
|
|
30
|
+
| `onDone` | `(() => void)` | no | Called once, after the fade ends (straight after `duration` under `prefers-reduced-motion`). Typically clears the consumer's `active` state. Not called when `active` is cleared early or on unmount. |
|
|
31
|
+
| `scrollIntoView` | `boolean` | no | On activation, scroll the child to the vertical centre of its scroll container. Smooth, or instant under `prefers-reduced-motion`. Default: `false`. |
|
|
32
|
+
| `focus` | `boolean` | no | On activation, move focus to the child (`preventScroll`, so it never fights `scrollIntoView`). The child must be focusable: give a non-interactive block (`Card`, `<section>`, `<tr>`) `tabIndex={-1}`. Highlight never adds it for you. Default: `false`. |
|
|
33
|
+
| `color` | `PaletteColor` | no | Ring and wave colour, from the shared palette (its saturated `fg` half, readable in both themes). Unset = the accent ring colour. Use a palette colour when the highlight should match the thing it points to (e.g. a category colour); leave it unset otherwise. |
|
|
34
|
+
| `children` | `ReactElement<unknown, string \| JSXElementConstructor<any>>` | yes | Exactly one element that forwards `ref` to a DOM element (nothing else is needed): any DS component, or a native element such as `<tr>` or `<li>`. Highlight renders no wrapper and changes nothing on the child but a `data-highlight` attribute; the ring is drawn on a separate overlay. |
|
|
35
|
+
|
|
36
|
+
<!-- props:end -->
|
|
37
|
+
|
|
38
|
+
- **Bring it to the user's attention in one call:** `<Highlight active scrollIntoView focus>`. On activation it scrolls the child to the centre (instantly under `prefers-reduced-motion`) and focuses it with `preventScroll`. A non-interactive child needs `tabIndex={-1}` to take focus. The focus move announces it; no `LiveRegion` needed.
|
|
39
|
+
- **Inside a `DashboardCanvas`:** wrap what `renderItem` returns:
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
renderItem={(id) => (
|
|
43
|
+
<Highlight active={id === addedId} scrollIntoView focus onDone={() => setAddedId(null)}>
|
|
44
|
+
<DashboardWidget tabIndex={-1} title={widgets[id].title}>…</DashboardWidget>
|
|
45
|
+
</Highlight>
|
|
46
|
+
)}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
- **Colour:** `color` takes a palette colour (the saturated `fg` half, readable in both themes); unset is the accent. Use it to match what the highlight points to:
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
<Highlight active={id === movedId} color="green" onDone={() => setMovedId(null)}>
|
|
53
|
+
<Card>…</Card>
|
|
54
|
+
</Highlight>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
- Under `prefers-reduced-motion` the ring is static (no waves, no fade). `onDone` fires straight after `duration`.
|
|
58
|
+
- `duration={Infinity}` keeps it on until `active` goes false. `duration` is read when the highlight starts; changing it mid-run has no effect.
|
|
59
|
+
- No `forwardRef` / prop spread: Highlight renders no DOM of its own (same as `Tooltip`); props go on the child.
|
|
60
|
+
|
|
61
|
+
**When NOT to use:**
|
|
62
|
+
|
|
63
|
+
- To mark a persistent selected or current state, use the component's own selected styling (`aria-selected`, `aria-current`).
|
|
64
|
+
- To show focus, use `:focus-visible`.
|
|
65
|
+
- To report an error, use `Field`'s invalid state or an `Alert`.
|
|
66
|
+
|
|
67
|
+
#### Anti-patterns
|
|
68
|
+
|
|
69
|
+
- ❌ `<Highlight active>{'text'}</Highlight>` or multiple children. It needs exactly one element child that forwards `ref`.
|
|
70
|
+
- ❌ Expecting `onDone` after clearing `active` yourself. It only fires when the highlight runs its course.
|
|
71
|
+
- ❌ Leaving `active` true forever with the default duration and expecting it to re-flash. Toggle it false → true to re-trigger.
|
|
72
|
+
- ❌ Relying on the ring alone to tell screen-reader users something changed. Use `focus` (the focus move announces it) or, without `focus`, a `LiveRegion` announcement.
|
|
73
|
+
- ❌ `focus` AND a `LiveRegion` for the same event. The screen reader announces it twice.
|
|
74
|
+
- ❌ A child component that drops `ref` (doesn't forward it to a DOM element). No ring, scroll or focus happens, and Highlight warns in development.
|
|
75
|
+
- ❌ `focus` on a child that can't take focus. Add `tabIndex={-1}`.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# `<HighlightedText>` — `Text` with matched runs in `<mark>`
|
|
2
|
+
|
|
3
|
+
**TL;DR:** render a plain-text excerpt (e.g. a search result) and highlight the matched substrings. Takes the same props as `Text`, plus `text` and `ranges` (UTF-16 offsets, as most search APIs return them).
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
<HighlightedText
|
|
7
|
+
text={result.excerpt}
|
|
8
|
+
ranges={result.highlights} // [{ start: 12, length: 5 }]
|
|
9
|
+
size="sm"
|
|
10
|
+
tone="muted"
|
|
11
|
+
lineClamp={2}
|
|
12
|
+
/>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
<!-- props:start -->
|
|
16
|
+
|
|
17
|
+
## Props
|
|
18
|
+
|
|
19
|
+
<!-- prettier-ignore -->
|
|
20
|
+
| Prop | Type | Required | Description |
|
|
21
|
+
|---|---|---|---|
|
|
22
|
+
| `text` | `string` | yes | The plain text to render. Always rendered as text, never parsed as HTML — safe for untrusted search excerpts. |
|
|
23
|
+
| `ranges` | `readonly HighlightRange[]` | no | Runs of `text` to highlight, as `{ start, length }` in UTF-16 code units (JavaScript string offsets — what most search APIs return). Out-of-range runs are clamped, empty ones dropped, overlapping or touching ones merged. Order doesn't matter. Defaults to `[]` (plain text). |
|
|
24
|
+
| `htmlFor` | `string` | no | Associates a `<label>` with its form control. Only meaningful when `as="label"` — passes through as the native `for` attribute. |
|
|
25
|
+
| `as` | `'p' \| 'span' \| 'div' \| 'label'` | no | Rendered element. Defaults to `'p'` (block, default body text). Use `'span'` for inline runs, `'div'` for block containers that can't be a `<p>` (e.g. when the body needs nested block-level elements that React would warn about inside `<p>`), `'label'` for form labels (pair with `htmlFor`). |
|
|
26
|
+
| `size` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'inherit'` | no | Visual size. Defaults to `'md'` (body text). - `xs` — 11px, dense metadata / captions - `sm` — 12px, small body / labels - `md` — 14px, body text (default) - `lg` — 16px, large body / lead text - `xl` — 20px, very large body (rare) - `inherit` — no fixed size; font-size AND line-height inherit from the parent. For inline runs inside a heading (`as="span"` inside a `<Title>` / `<PageHeader.Title>`) that must keep the heading's size — e.g. a muted task-key prefix. Tone / weight still apply — font-weight stays Text's own (default `regular`), it does NOT inherit; pass `weight` to match the heading if needed. |
|
|
27
|
+
| `tone` | `'default' \| 'muted' \| 'subtle' \| 'accent' \| 'danger' \| 'success' \| 'warning'` | no | Color tone. Defaults to `'default'`. - `default` — `--color-fg` - `muted` — `--color-fg-muted` (for secondary copy) - `subtle` — **@deprecated: resolves to `muted`. Use `muted`.** In LIGHT theme the two neutrals were indistinguishable: OKLab ΔE 0.0261 when the deprecation was filed, 0.0365 after `--color-fg-muted` was retuned, against the 0.065 floor this library's perceptual gates use. In DARK they were 0.0707 apart — a real step — so **this deprecation changes dark-theme appearance**: `subtle` text in dark gets lighter, moving from `--color-fg-subtle` to `--color-fg-muted`. That is the accepted trade (a tier that exists in one theme only is not a tier), but it is a visual change, not the removal of a duplicate. `--text-fg-subtle` now aliases `--text-fg-muted`, so nothing breaks at the type or build level; if you need the old dark value back, override `--text-fg-subtle`. - `accent` — `--color-accent` - `danger` / `success` / `warning` — state-coded text |
|
|
28
|
+
| `weight` | `'regular' \| 'medium' \| 'semibold' \| 'bold'` | no | Font weight. Defaults to `'regular'`. |
|
|
29
|
+
| `align` | `'left' \| 'center' \| 'right'` | no | Text alignment. Defaults to `'left'`. |
|
|
30
|
+
| `truncate` | `boolean` | no | Truncate to a single line with ellipsis. Defaults to `false`. Use inside narrow containers (table cells, card list rows). Mutually exclusive with `lineClamp` — if both are set, `lineClamp` wins. |
|
|
31
|
+
| `lineClamp` | `number` | no | Clamp to N lines with ellipsis (uses `-webkit-line-clamp`). Defaults to `undefined`. Overrides `truncate` when set. Example: `lineClamp={2}` for a 2-line description that ellipses on the third. If you also pass `style.WebkitLineClamp`, the `lineClamp` prop takes precedence — the component merges the dynamic line-clamp value into `style` AFTER spreading your `style`, so the prop wins. |
|
|
32
|
+
| …native | | | plus native HTML attributes |
|
|
33
|
+
|
|
34
|
+
<!-- props:end -->
|
|
35
|
+
|
|
36
|
+
- Matches render as native `<mark>` (the HTML highlight role), so screen readers that report highlights see them — not just a colour change. Background is `--highlighted-text-mark-bg` (defaults to `--color-palette-yellow-bg`); matched text is always full-strength `--highlighted-text-mark-fg` (`--color-fg`) regardless of `tone` — muted and danger text fail AA contrast on the dark-theme yellow, and the stronger match text reads as emphasis anyway.
|
|
37
|
+
- `text` is always rendered as text, never parsed as HTML — safe for untrusted excerpts.
|
|
38
|
+
- Ranges are forgiving: unsorted, overlapping, touching, out-of-range and empty ranges are all fine (merged / clamped / dropped).
|
|
39
|
+
|
|
40
|
+
#### When NOT to use
|
|
41
|
+
|
|
42
|
+
- ❌ Drawing attention to a whole block, card or field — that's `Highlight` (an attention ring), not a text match.
|
|
43
|
+
- ❌ Rich formatted content — `HighlightedText` takes a plain string. For formatted docs use `RichText`.
|
|
44
|
+
- ❌ Emphasis that isn't a match (`<strong>`-style importance) — use `Text weight="semibold"`.
|
|
45
|
+
|
|
46
|
+
#### Anti-patterns
|
|
47
|
+
|
|
48
|
+
- ❌ Building the `<mark>` spans yourself with `dangerouslySetInnerHTML` from the API's highlighted HTML — XSS risk and no tokens. Pass the plain excerpt + `ranges`.
|
|
49
|
+
- ❌ Computing ranges with code-point indices (`[...text]`) — offsets are UTF-16 (`text.slice` / `indexOf` units). Emoji and other astral characters shift otherwise.
|
|
50
|
+
- ❌ Restyling the mark with an inline `style` — override `--highlighted-text-mark-bg` instead.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# `<IconPicker>` — choose one glyph from a consumer catalog
|
|
2
|
+
|
|
3
|
+
**TL;DR:** controlled popover grid for choosing one icon from a consumer-curated catalog. Add `color` + `onColorChange` to also pick a palette colour, and `size` to match `Input` / `Select` heights.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
const options = [
|
|
7
|
+
{ value: 'flame', label: 'Flame', icon: <Flame /> },
|
|
8
|
+
{ value: 'zap', label: 'Lightning', icon: <Zap /> },
|
|
9
|
+
];
|
|
10
|
+
<Field label="Priority icon">
|
|
11
|
+
<IconPicker value={icon} options={options} onChange={setIcon} />
|
|
12
|
+
</Field>;
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
<!-- props:start -->
|
|
16
|
+
|
|
17
|
+
## Props
|
|
18
|
+
|
|
19
|
+
### `IconPickerProps`
|
|
20
|
+
|
|
21
|
+
<!-- prettier-ignore -->
|
|
22
|
+
| Prop | Type | Required | Description |
|
|
23
|
+
|---|---|---|---|
|
|
24
|
+
| `value` | `string` | yes | Currently selected option value. Controlled — required. |
|
|
25
|
+
| `options` | `IconPickerOption[]` | yes | Consumer-curated icons rendered in the supplied order. Values must be stable and unique. An empty list disables the trigger. |
|
|
26
|
+
| `onChange` | `(value: string) => void` | yes | Fires exactly once with the chosen option value, including when it equals the controlled `value`. Closes the popover in icon-only mode; colour mode keeps it open. |
|
|
27
|
+
| `disabled` | `boolean` | no | Disables the trigger, prevents opening and selection, and closes an open picker. Default: `false`. |
|
|
28
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | no | Trigger size, matching the sm/md/lg field heights. Default: `'md'`. |
|
|
29
|
+
| `invalid` | `boolean` | no | Marks the focusable trigger invalid for Field composition. Default: `false`. |
|
|
30
|
+
| `required` | `boolean` | no | Consumed for Field composition so Field can render its visible required marker. Native buttons do not support `aria-required`, so this does not add required semantics to the trigger. Default: `false`. |
|
|
31
|
+
| `popoverPlacement` | `'top' \| 'top-start' \| 'top-end' \| 'bottom' \| 'bottom-start' \| 'bottom-end'` | no | Preferred popover placement. Floating UI may flip it to remain visible. - `'top'` — above, centered. - `'top-start'` — above, start-aligned. - `'top-end'` — above, end-aligned. - `'bottom'` — below, centered. - `'bottom-start'` — below, start-aligned. - `'bottom-end'` — below, end-aligned. Default: `'bottom-start'`. |
|
|
32
|
+
| `aria-label` | `string` | no | Accessible picker purpose. It names the trigger with the selected icon appended and names the dialog without that suffix (in icon-only mode it names the radiogroup too; in colour mode the radiogroups are named by their visible "Colour"/"Icon" headings). Defaults to the localized `iconPicker.triggerLabel` value when omitted OR empty — an empty string is not an explicit name, so it takes the default too. In colour mode the default is `iconPicker.triggerLabelWithColor` and the trigger appends the colour name as well. |
|
|
33
|
+
| `aria-labelledby` | `string` | no | Id(s) of external elements that provide the picker purpose. They name the dialog directly (and the radiogroup in icon-only mode; colour-mode radiogroups use their visible headings). The trigger also references hidden selected-option text, plus hidden colour-name text in colour mode, so its name remains the visible purpose plus the current selection. |
|
|
34
|
+
| `aria-describedby` | `string` | no | Id(s) of element(s) that describe the trigger button. |
|
|
35
|
+
| `color` | `PaletteColor` | no | Controlled palette colour. Passing it turns on colour mode. |
|
|
36
|
+
| `onColorChange` | `(color: PaletteColor) => void` | no | Fires with the chosen palette colour; the popover stays open. |
|
|
37
|
+
| `colors` | `readonly PaletteColor[]` | no | Offered colours, in order. Default: `PALETTE_COLORS`. |
|
|
38
|
+
| …native | | | plus native HTML attributes |
|
|
39
|
+
|
|
40
|
+
### `IconPickerBaseProps`
|
|
41
|
+
|
|
42
|
+
<!-- prettier-ignore -->
|
|
43
|
+
| Prop | Type | Required | Description |
|
|
44
|
+
|---|---|---|---|
|
|
45
|
+
| `value` | `string` | yes | Currently selected option value. Controlled — required. |
|
|
46
|
+
| `options` | `IconPickerOption[]` | yes | Consumer-curated icons rendered in the supplied order. Values must be stable and unique. An empty list disables the trigger. |
|
|
47
|
+
| `onChange` | `(value: string) => void` | yes | Fires exactly once with the chosen option value, including when it equals the controlled `value`. Closes the popover in icon-only mode; colour mode keeps it open. |
|
|
48
|
+
| `disabled` | `boolean` | no | Disables the trigger, prevents opening and selection, and closes an open picker. Default: `false`. |
|
|
49
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | no | Trigger size, matching the sm/md/lg field heights. Default: `'md'`. |
|
|
50
|
+
| `invalid` | `boolean` | no | Marks the focusable trigger invalid for Field composition. Default: `false`. |
|
|
51
|
+
| `required` | `boolean` | no | Consumed for Field composition so Field can render its visible required marker. Native buttons do not support `aria-required`, so this does not add required semantics to the trigger. Default: `false`. |
|
|
52
|
+
| `popoverPlacement` | `'top' \| 'top-start' \| 'top-end' \| 'bottom' \| 'bottom-start' \| 'bottom-end'` | no | Preferred popover placement. Floating UI may flip it to remain visible. - `'top'` — above, centered. - `'top-start'` — above, start-aligned. - `'top-end'` — above, end-aligned. - `'bottom'` — below, centered. - `'bottom-start'` — below, start-aligned. - `'bottom-end'` — below, end-aligned. Default: `'bottom-start'`. |
|
|
53
|
+
| `aria-label` | `string` | no | Accessible picker purpose. It names the trigger with the selected icon appended and names the dialog without that suffix (in icon-only mode it names the radiogroup too; in colour mode the radiogroups are named by their visible "Colour"/"Icon" headings). Defaults to the localized `iconPicker.triggerLabel` value when omitted OR empty — an empty string is not an explicit name, so it takes the default too. In colour mode the default is `iconPicker.triggerLabelWithColor` and the trigger appends the colour name as well. |
|
|
54
|
+
| `aria-labelledby` | `string` | no | Id(s) of external elements that provide the picker purpose. They name the dialog directly (and the radiogroup in icon-only mode; colour-mode radiogroups use their visible headings). The trigger also references hidden selected-option text, plus hidden colour-name text in colour mode, so its name remains the visible purpose plus the current selection. |
|
|
55
|
+
| `aria-describedby` | `string` | no | Id(s) of element(s) that describe the trigger button. |
|
|
56
|
+
| …native | | | plus native `<div>` attributes |
|
|
57
|
+
|
|
58
|
+
### `IconPickerColorProps`
|
|
59
|
+
|
|
60
|
+
<!-- prettier-ignore -->
|
|
61
|
+
| Prop | Type | Required | Description |
|
|
62
|
+
|---|---|---|---|
|
|
63
|
+
| `color` | `PaletteColor` | no | Controlled palette colour. Passing it turns on colour mode. |
|
|
64
|
+
| `onColorChange` | `(color: PaletteColor) => void` | no | Fires with the chosen palette colour; the popover stays open. |
|
|
65
|
+
| `colors` | `readonly PaletteColor[]` | no | Offered colours, in order. Default: `PALETTE_COLORS`. |
|
|
66
|
+
|
|
67
|
+
<!-- props:end -->
|
|
68
|
+
|
|
69
|
+
`color` and `onColorChange` are required together (both or neither), even though the table above shows each as optional.
|
|
70
|
+
|
|
71
|
+
- The consumer owns icon values, labels, glyphs, ordering, and controlled state.
|
|
72
|
+
- `<Field label>` names the trigger with the visible label plus the selected option, and forwards description and invalid state to that button; the dialog and radiogroup use only the visible Field label. These attributes do not sit on the role-less wrapper. `required` remains a visible Field marker rather than unsupported `aria-required` on the native button.
|
|
73
|
+
- Use it for compact visual choices; use `Select` when visible option text matters.
|
|
74
|
+
- Labels must be human-readable and values unique. Do not pass icon codes as labels.
|
|
75
|
+
- Controlled-only. Not for a small always-visible set of text choices (use `<RadioGroup>`) or for actions rather than a persistent value (use `<DropdownMenu>`).
|
|
76
|
+
- Do not place buttons, links or other focusable content inside an option glyph.
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
const [icon, setIcon] = useState('flame');
|
|
80
|
+
<Cluster gap="sm" align="center">
|
|
81
|
+
<IconPicker value={icon} options={iconOptions} onChange={setIcon} />
|
|
82
|
+
<Text>{icon}</Text>
|
|
83
|
+
</Cluster>;
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Colour mode
|
|
87
|
+
|
|
88
|
+
Pass `color` + `onColorChange` (both or neither) to also pick a palette colour. The trigger is tinted, the popover shows "Colour" and "Icon" groups, and picking does not close it. `colors` defaults to every palette colour; narrow it to a subset. A disabled picker keeps its tint, dimmed.
|
|
89
|
+
|
|
90
|
+
Wrapping: `IconPickerColorProps` is a union, so intersect with a type alias, not `interface extends`: `type MyProps = IconPickerBaseProps & IconPickerColorProps & { ... }`. To narrow safely, use `Omit<IconPickerBaseProps, ...> & IconPickerColorProps` (never `Omit` the union itself). For a record whose colour may be unset, pass a fallback: `color={record.color ?? 'slate'}`.
|
|
91
|
+
|
|
92
|
+
Keyboard in colour mode: Tab moves between the Colour and Icon groups, arrows move and select, picking keeps the popover open, Esc closes and returns focus to the trigger.
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
<Field label="Appearance">
|
|
96
|
+
<IconPicker
|
|
97
|
+
value={icon}
|
|
98
|
+
options={iconOptions}
|
|
99
|
+
onChange={setIcon}
|
|
100
|
+
color={color}
|
|
101
|
+
onColorChange={setColor}
|
|
102
|
+
/>
|
|
103
|
+
</Field>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Beside an Input
|
|
107
|
+
|
|
108
|
+
`size` (`sm` / `md` / `lg`, 24 / 32 / 40px) matches `Input` / `Select` heights. Without a label it is still named "Pick icon and colour"; when that is too generic, give it a specific purpose via `aria-label` or `<Field label>`.
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
<Split
|
|
112
|
+
gap="sm"
|
|
113
|
+
align="center"
|
|
114
|
+
aside={
|
|
115
|
+
<IconPicker
|
|
116
|
+
size="md"
|
|
117
|
+
aria-label="Icon and colour" // localize this
|
|
118
|
+
value={icon}
|
|
119
|
+
options={iconOptions}
|
|
120
|
+
onChange={setIcon}
|
|
121
|
+
color={color}
|
|
122
|
+
onColorChange={setColor}
|
|
123
|
+
/>
|
|
124
|
+
}
|
|
125
|
+
>
|
|
126
|
+
<Input size="md" aria-label="Name" value={name} onChange={(e) => setName(e.target.value)} />
|
|
127
|
+
</Split>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## When NOT to use
|
|
131
|
+
|
|
132
|
+
- ❌ Pairing IconPicker with a separate colour `PillMenu` / `Select` — use colour mode.
|
|
133
|
+
- ❌ Using it for arbitrary hex values — use `ColorPicker`.
|
|
134
|
+
- ❌ A picker beside a Name input left with the generic default name when a specific purpose (`aria-label` or `<Field label>`) would help.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# `<IconTile>` — palette-colored icon frame
|
|
2
|
+
|
|
3
|
+
```tsx
|
|
4
|
+
<IconTile color="blue" icon={<Zap size={16} />} />
|
|
5
|
+
<IconTile color="amber" shape="circle" icon={<MailPlus size={14} />} />
|
|
6
|
+
<IconTile color="green" label="Verified" icon={<Check size={16} />} />
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
<!-- props:start -->
|
|
10
|
+
|
|
11
|
+
## Props
|
|
12
|
+
|
|
13
|
+
<!-- prettier-ignore -->
|
|
14
|
+
| Prop | Type | Required | Description |
|
|
15
|
+
|---|---|---|---|
|
|
16
|
+
| `icon` | `ReactNode` | yes | The icon to frame — a lucide icon (sized by you, ~14–20px), custom SVG, or any ReactNode. Required; IconTile is purely an icon frame. |
|
|
17
|
+
| `color` | `PaletteColor` | no | Palette color for the tint — one of the 30 categorical colors. Defaults to `'slate'`. Categorical (visual identity), NOT semantic; for status use a `<Badge tone>` instead. |
|
|
18
|
+
| `size` | `'inline' \| 'xs' \| 'sm' \| 'md' \| 'lg'` | no | Tile box size. `'xs'` 20 / `'sm'` 24 / `'md'` 32 (**default**) / `'lg'` 40 px. Sizes the tile box — size your icon child separately. `'xs'` brackets a 14px glyph with ~3px of padding so the tile sits inside a text / Badge / EntityChip row without growing it. `'inline'` is font-relative: a `1em` tile (the surrounding text's font-size) centred on the text's capitals, whose glyph the tile sizes to `0.75em` — a direct `<svg>` child's (a lucide icon's) own `size` is overridden; wrap nothing around the icon. Use it in text contexts (`EntityChip` `icon` / `trailing`, `Badge` rows, dense lists, running text) where even `xs` towers over the capitals. It follows `font-size`, so there is no size decision per call site. Not for standalone tiles — it is only as big as the text. |
|
|
19
|
+
| `shape` | `'square' \| 'circle'` | no | `'square'` (radius-md, **default**) or `'circle'` (radius-full). |
|
|
20
|
+
| `label` | `string` | no | Accessible name. Omit (**default**) → the tile is decorative (`aria-hidden`), for use beside text that carries the meaning. Set it → `role="img"` + `aria-label`, for a standalone tile whose icon is the only indicator. |
|
|
21
|
+
| …native | | | plus native `<span>` attributes |
|
|
22
|
+
|
|
23
|
+
<!-- props:end -->
|
|
24
|
+
|
|
25
|
+
- A small decorative tile framing one icon, tinted by a **Palette** `color` (one of the 30 categorical colors; default `'slate'`). For a person use `<Avatar>`; for text/status use `<Badge>`.
|
|
26
|
+
- Color is categorical (visual identity), **not** semantic — use `<Badge tone>` for status.
|
|
27
|
+
- A11y: decorative by default (`aria-hidden`); pass `label` to make it `role="img"` + `aria-label` when the icon is the only indicator.
|
|
28
|
+
- Not for a plain icon with no tinted container — render the lucide icon directly.
|
|
29
|
+
- ❌ A decorative IconTile as the ONLY indicator of meaning with no nearby text — pass a `label`.
|
|
30
|
+
- ❌ `size="xs"` in a chip or text row where it towers over the text (it is a fixed 20px) — use `size="inline"`.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# `<Image>` — image with loading + error states
|
|
2
|
+
|
|
3
|
+
```tsx
|
|
4
|
+
<Image src={url} alt="Quarterly revenue chart" aspectRatio="16 / 9" />
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
<!-- props:start -->
|
|
8
|
+
|
|
9
|
+
## Props
|
|
10
|
+
|
|
11
|
+
<!-- prettier-ignore -->
|
|
12
|
+
| Prop | Type | Required | Description |
|
|
13
|
+
|---|---|---|---|
|
|
14
|
+
| `src` | `string` | yes | Image URL. Changing it resets the component to the loading state. |
|
|
15
|
+
| `alt` | `string` | yes | Alternative text (required). Describe the image's content or function. Pass an empty string (`alt=""`) for purely decorative images. |
|
|
16
|
+
| `objectFit` | `'cover' \| 'contain' \| 'fill' \| 'none' \| 'scale-down'` | no | How the image fills its box. Defaults to `'cover'`. - `'cover'` — fill + crop (no distortion). Best for thumbnails / heroes. - `'contain'` — whole image, letterboxed; the bars show the surface, or the muted box with `background="muted"`. - `'fill'` — stretch to the box (may distort). - `'none'` / `'scale-down'` — native size / the smaller of none\|contain. |
|
|
17
|
+
| `objectPosition` | `string` | no | Which part of the image stays in view when `objectFit` crops or letterboxes it. Any CSS `object-position` value. Defaults to `'center'`. Use `'top'` for document / screenshot thumbnails cropped square, so the header stays visible. |
|
|
18
|
+
| `aspectRatio` | `string \| number` | no | Reserve the box at a fixed ratio to prevent layout shift while loading. Number (`1.5`) or CSS string (`'16 / 9'`). |
|
|
19
|
+
| `size` | `'xs' \| 'sm' \| 'md' \| 'lg'` | no | Render a fixed **square** box of this size (from the shared `--size-*` scale: `'xs'` 20 / `'sm'` 24 / `'md'` 32 / `'lg'` 40 px) instead of filling the container's width. Use for dense thumbnails (e.g. a 40px image cell in a table row) so you don't need a consumer-owned fixed-width wrapper. Omit for the default responsive behavior. When set, `aspectRatio` is ignored (the box is already square); pair with `objectFit="cover"` to crop. Note that a sized image's error tile is icon-only and **not retryable** — see the component description. |
|
|
20
|
+
| `radius` | `'none' \| 'sm' \| 'md' \| 'lg' \| 'full'` | no | Corner rounding. Defaults to `'md'`. Pass `'none'` for square corners; for circular profile images use `<Avatar>`. |
|
|
21
|
+
| `background` | `'muted' \| 'none'` | no | Backdrop behind the loaded image. Defaults to `'none'`, so transparent assets (logos, PNG/SVG) show their surface. Pass `'muted'` for the muted fill (letterboxed `contain` images). The loading skeleton and the error tile keep their fill either way. |
|
|
22
|
+
| `fallback` | `ReactNode` | no | Custom node rendered in place of the default broken-image placeholder when the image fails to load. Overrides the icon + message + retry entirely. This is also the only way to put a control on a failed fixed-`size` image, whose built-in error tile is icon-only. At a fixed `size` the node is rendered in a slot that fills the box (so it cannot flow outside the wrapper's clip); fitting your content into those 20-40px is still yours, and a labelled button does not fit any of them. |
|
|
23
|
+
| `loading` | `'eager' \| 'lazy'` | no | Native lazy-loading hint. Defaults to `'lazy'`. Pass `'eager'` for above-the-fold / LCP images. |
|
|
24
|
+
| `interactive` | `boolean` | no | Make the image a flush, keyboard-accessible click target — renders the image inside a chromeless `<button>` (no padding/border/background; DS focus ring on `:focus-visible`). Implied when `onClick` is set. Use for a thumbnail that opens a preview/lightbox. The broken-image error state is non-interactive (the fluid tile's Retry takes over when its box has room; otherwise there is no control). |
|
|
25
|
+
| `onClick` | `MouseEventHandler<HTMLButtonElement>` | no | Click handler for the interactive trigger. Setting it implies `interactive`. Fires on click and on Enter/Space (native `<button>` keyboard behavior). |
|
|
26
|
+
| `ariaLabel` | `string` | no | Accessible label for the interactive trigger — action-oriented, e.g. `"Preview report.png"`. Defaults to `alt` when omitted OR empty — an empty string is not an explicit name, so it takes the default too. Only used when interactive. Note: a native `aria-label` passed via `{...rest}` lands on the `<img>`, not the trigger button — use `ariaLabel` to name the trigger. |
|
|
27
|
+
| …native | | | plus native `<img>` attributes |
|
|
28
|
+
|
|
29
|
+
<!-- props:end -->
|
|
30
|
+
|
|
31
|
+
Robust `<img>`: `Skeleton` while loading, fade-in on load, compact `ImageOff` error
|
|
32
|
+
placeholder on failure — with a retry button when the image is fluid, icon-only when
|
|
33
|
+
it has a fixed `size`.
|
|
34
|
+
|
|
35
|
+
- `ref` → the `<img>`, which is re-created when `src` changes or on Retry (re-attach observers in an effect keyed on `src`); `className`/`style` → the wrapper box.
|
|
36
|
+
|
|
37
|
+
**The error tile depends on `size`.** Without `size` it is icon + message + a **Retry** button when the box can hold them (about 96×104px or more at the default tokens; a longer custom `image.retry` label needs a wider box; the message is kept to two lines, then truncated with an ellipsis). A smaller box gets the icon alone: Retry is removed and the message stays for screen readers, so nothing is left clipped but still focusable. A fluid `Image` with no `aspectRatio` gets a 40px height on error rather than collapsing to 0, so it shows that icon-only tile. A height you set yourself (className/style) still wins. Reserve the box if you want the retry. With `size` (all four of `xs`/`sm`/`md`/`lg` — 20/24/32/40px) it is the **icon alone**, scaled to the box, and the failure is **not retryable**: none of those squares can hold the message _and_ an `sm` Button, and the one that used to render there sat outside the wrapper's `overflow: hidden`, painted nowhere yet still focusable. A screen reader still gets the failure — the sized icon is named `"{alt}: Image failed to load"` (just the phrase when `alt=""`), where the fluid tile leaves the phrase to its visible text. Need a control on a failed thumbnail at those sizes? Pass `fallback`: at a fixed `size` it renders in a slot filling the box, so it can't escape the wrapper, but sizing it to fit 20-40px is on you.
|
|
38
|
+
|
|
39
|
+
**Backdrop.** `background` defaults to `'none'`: once loaded, the box has no fill, so transparent logos/PNGs show the surface behind them. The skeleton and error tile keep the muted fill. Pass `background="muted"` for the old backdrop (e.g. letterboxed `objectFit="contain"`).
|
|
40
|
+
|
|
41
|
+
Changed: the loaded state used to paint the muted fill by default; pass `background="muted"` to keep that look.
|
|
42
|
+
|
|
43
|
+
**Transparent logos**
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
<Image src={logoSvg} alt="Acme" objectFit="contain" aspectRatio="16 / 9" />
|
|
47
|
+
<Image src={logoSvg} alt="Acme" objectFit="contain" aspectRatio="16 / 9" background="muted" />
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**When NOT to use:** circular avatars → `<Avatar>`; crop/zoom UI → `<ImageCrop>`; CSS
|
|
51
|
+
backgrounds → `background-image`; icons → lucide / inline SVG.
|
|
52
|
+
|
|
53
|
+
**Interactive (flush click target):** set `interactive` (or just `onClick`) to render the image inside a chromeless `<button>` (no padding/border/background; DS focus ring on `:focus-visible`) — a thumbnail that opens a preview/lightbox on click/Enter/Space. `ariaLabel` names the trigger (defaults to `alt`). The broken-image error state is non-interactive (the fluid tile's retry control takes over; a sized tile has no control at all); `ref` still forwards to the `<img>`.
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
<Image
|
|
57
|
+
src={att.url}
|
|
58
|
+
alt={att.filename}
|
|
59
|
+
size="lg"
|
|
60
|
+
objectFit="cover"
|
|
61
|
+
onClick={() => openPreview(att)}
|
|
62
|
+
ariaLabel={`Preview ${att.filename}`}
|
|
63
|
+
/>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
// Fixed 40px thumbnail (dense table cell) — no width:100% stretch
|
|
68
|
+
<Image src={url} alt="report.pdf preview" size="lg" objectFit="cover" />
|
|
69
|
+
|
|
70
|
+
// Document / screenshot thumbnail cropped square from the TOP (keeps the header in view)
|
|
71
|
+
<Image src={pageUrl} alt="invoice.pdf" aspectRatio={1} objectFit="cover" objectPosition="top" />
|
|
72
|
+
|
|
73
|
+
// Logo contained on its muted box, square corners
|
|
74
|
+
<Image src={logo} alt="Acme Corp" objectFit="contain" background="muted" radius="none" />
|
|
75
|
+
|
|
76
|
+
// Eager above-the-fold hero with a custom error fallback
|
|
77
|
+
<Image src={hero} alt="Welcome aboard" loading="eager" aspectRatio={2} fallback={<EmptyState title="Couldn't load the hero image" />} />
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The wrapper fills its container's width: give it an `aspectRatio` (or a height) so the box is reserved before the image arrives, unless you pass `size`. Native `width` / `height` attributes on the `<img>` are intrinsic-ratio hints only, not the rendered size.
|
|
81
|
+
|
|
82
|
+
- ❌ Empty `alt` for a meaningful image — pass a real description.
|
|
83
|
+
- ❌ No `aspectRatio` / height when you care about layout shift.
|
|
84
|
+
- ❌ Wrapping `<Image>` in a `<Button>` / `<Link>` for a clickable thumbnail — it paints button/link chrome over it; use `interactive` / `onClick` for a flush trigger.
|