@eocrm/design-system 0.0.0-stage → 0.6.17
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/README.md +262 -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 +82 -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,127 @@
|
|
|
1
|
+
# `<Sortable>` — drag-to-reorder list (single column)
|
|
2
|
+
|
|
3
|
+
For reorderable lists — todo priority, image gallery, settings ordering, queue management. Renders `<ol>`/`<li>`. Compound: `Sortable`, `Sortable.Item`, `Sortable.Handle`. Built on `@dnd-kit/sortable`; mouse / touch / pen via PointerSensor + keyboard via KeyboardSensor.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { arrayMove } from '@dnd-kit/sortable';
|
|
7
|
+
|
|
8
|
+
const [items, setItems] = useState([
|
|
9
|
+
{ id: 1, title: 'Onboarding email' },
|
|
10
|
+
{ id: 2, title: 'Renewal reminder' },
|
|
11
|
+
{ id: 3, title: 'Quarterly report' },
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
<Sortable onReorder={({ from, to }) => setItems((curr) => arrayMove(curr, from, to))}>
|
|
15
|
+
{items.map((item) => (
|
|
16
|
+
<Sortable.Item key={item.id} id={item.id}>
|
|
17
|
+
<Card>
|
|
18
|
+
<Cluster gap="sm" align="center">
|
|
19
|
+
<Sortable.Handle aria-label={`Reorder ${item.title}`}>
|
|
20
|
+
<GripVertical size={14} />
|
|
21
|
+
</Sortable.Handle>
|
|
22
|
+
<Title order={3}>{item.title}</Title>
|
|
23
|
+
</Cluster>
|
|
24
|
+
</Card>
|
|
25
|
+
</Sortable.Item>
|
|
26
|
+
))}
|
|
27
|
+
</Sortable>;
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
<!-- props:start -->
|
|
31
|
+
|
|
32
|
+
## Props
|
|
33
|
+
|
|
34
|
+
### `SortableProps`
|
|
35
|
+
|
|
36
|
+
<!-- prettier-ignore -->
|
|
37
|
+
| Prop | Type | Required | Description |
|
|
38
|
+
|---|---|---|---|
|
|
39
|
+
| `onReorder` | `((event: SortableReorderEvent) => void)` | no | Fires after a drag or keyboard move with the new position. Consumer holds the items array and re-renders with the new order. Not fired if the drop position equals the source position (no-op). |
|
|
40
|
+
| `restrictToContainer` | `boolean` | no | When `true` (default), the dragged item is clamped to the list's bounding box — it can't be dragged outside the `<ol>`. Set `false` for free-drag (the item can be dragged anywhere on the page). For a vertical list this constrains the drag vertically to the list's extent. |
|
|
41
|
+
| `arrangement` | `'list' \| 'grid'` | no | Layout arrangement of the items. - `'list'` (default) — single vertical column; existing behavior unchanged. - `'grid'` — the `<ol>` becomes a `columns`-track CSS grid. Items carry column spans (`Sortable.Item span`), rows are equal-height, and siblings reflow in 2D during a drag. Reorder semantics stay order-based (a drop reorders the flow; nothing here persists an x/y position). |
|
|
42
|
+
| `columns` | `number` | no | Number of equal-width grid tracks. Only applies when `arrangement="grid"` (ignored for a list). Default `12`, matching `Grid.Item`'s 12-column fraction spans (`'25%'`→3 … `'75%'`→9 tracks). Set another count for a simpler grid, but the named fraction spans then won't map to their fraction (documented, not validated — same as `Grid`). |
|
|
43
|
+
| `collapseBelow` | `CollapseBreakpoint \| Partial<Record<CollapseBreakpoint, number>>` | no | Responsive collapse for the grid arrangement — mirrors `Grid`'s `collapseBelow` exactly (same breakpoints, same container-query mechanism). Ignored unless `arrangement="grid"`. - `'sm' \| 'md' \| 'lg'` — below the container-width threshold (480 / 640 / 768px of the LIST'S OWN width) every item spans the full row: a single visual column. - `{ lg?: n, md?: n, sm?: n }` — graduated: below each breakpoint the grid re-templates to that many columns and item spans clamp to fit (a span wider than the step becomes a full row). E.g. `{ md: 6, sm: 1 }`. ❌ Anti-pattern: a collapsible Sortable must get its width from its parent — `container-type: inline-size` zeroes the list's contribution to intrinsic sizing (same caveat as `Grid`'s `collapseBelow`). ⚠️ The map form (and ONLY the map form) wraps the `<ol>` in an extra `<div>` carrying `container-type: inline-size` — re-templating the `<ol>` requires querying an ancestor, not the `<ol>` itself. A `> child` selector aimed at the list from its parent hits that wrapper instead, and layout the parent applies via `className` lands on the `<ol>` inside it. `ref`, `className`, `style` and spread props all stay on the `<ol>`. |
|
|
44
|
+
| …native | | | plus native `<ol>` attributes |
|
|
45
|
+
|
|
46
|
+
### `SortableHandleProps`
|
|
47
|
+
|
|
48
|
+
<!-- prettier-ignore -->
|
|
49
|
+
| Prop | Type | Required | Description |
|
|
50
|
+
|---|---|---|---|
|
|
51
|
+
| `children` | `ReactNode` | no | Decorative content — typically a grip icon (e.g. `<GripVertical size={14} />`). Rendered inside the Handle's `<button>` wrapped with `aria-hidden`. |
|
|
52
|
+
| …native | | | plus native `<button>` attributes |
|
|
53
|
+
|
|
54
|
+
### `SortableItemProps`
|
|
55
|
+
|
|
56
|
+
<!-- prettier-ignore -->
|
|
57
|
+
| Prop | Type | Required | Description |
|
|
58
|
+
|---|---|---|---|
|
|
59
|
+
| `id` | `string \| number` | yes | Stable identifier for this item. Used by dnd-kit for tracking the item across reorders. String or number — typically the consumer's database id. |
|
|
60
|
+
| `children` | `ReactNode` | yes | Item content — typically a `<Card>` or a row of text + icons. |
|
|
61
|
+
| `span` | `GridItemSpan` | no | Column span, only meaningful under `<Sortable arrangement="grid">` (ignored in a list). Mirrors `Grid.Item`'s `span` exactly: - number — `span N` of the grid's `columns`. - `'25%' \| '33%' \| '50%' \| '67%' \| '75%'` — fractions of a 12-column grid. - `'100%'` / `'full'` — the entire row (`1 / -1`); safe with any `columns`. Omit for a single track (auto placement). |
|
|
62
|
+
| …native | | | plus native `<li>` attributes |
|
|
63
|
+
|
|
64
|
+
<!-- props:end -->
|
|
65
|
+
|
|
66
|
+
Reorder with `arrayMove` from `@dnd-kit/sortable` (already a dependency) — import it from there; `Sortable.Item` ids must be stable (`string | number`). For `arrangement` / `columns` see **Grid arrangement** below.
|
|
67
|
+
|
|
68
|
+
**Grid arrangement** (`arrangement="grid"`): re-lays the `<ol>` as a `columns`-track CSS grid (`columns?: number`, default 12) driven by dnd-kit's `rectSortingStrategy`, so siblings reflow in **2D** during a drag instead of shifting only vertically. Give each item a span via `<Sortable.Item span="50%">` (or `span={6}` / `span="100%"`) — **same values as `Grid.Item`** (`25%`→3 … `75%`→9 tracks of 12; `100%`/`full`→whole row). Rows are equal-height (`align-items: stretch`). Semantics stay order-based: a drop reorders the flow — nothing persists a grid x/y position. `restrictToContainer` still clamps the drag to the grid box. Default `arrangement="list"` is unchanged (single vertical column). Canonical use: a WYSIWYG dashboard customize view where edit mode mirrors view mode's 12-col widget grid.
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
<Sortable arrangement="grid" columns={12} onReorder={handle}>
|
|
72
|
+
{widgets.map((w) => (
|
|
73
|
+
<Sortable.Item key={w.id} id={w.id} span={w.span}>
|
|
74
|
+
<Card style={{ height: '100%' }}>{w.title}</Card>
|
|
75
|
+
</Sortable.Item>
|
|
76
|
+
))}
|
|
77
|
+
</Sortable>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`collapseBelow` (grid arrangement only) mirrors Grid's `collapseBelow` exactly — `'sm' | 'md' | 'lg'` for a binary collapse to one column, or `{ md: 6, sm: 1 }` for a graduated step-down with item spans clamped per step. The map form wraps the `<ol>` in an extra size-container `<div>`, for the same reason and with the same consequences as Grid's map form (see `<Grid>` below); `ref`, `className`, `style` and spread props stay on the `<ol>`.
|
|
81
|
+
|
|
82
|
+
**Drag origin** (hybrid): if `<Sortable.Handle>` is present in the Item subtree, only the Handle initiates drag. If no Handle is present, the entire Item is draggable + focusable. A 5px activation distance means short clicks-without-movement on internal buttons / links pass through.
|
|
83
|
+
|
|
84
|
+
**Keyboard reorder**: Tab to focus the Handle (or the Item if no Handle), press **Space** to pick up, **ArrowUp** / **ArrowDown** to move, **Space** to drop, **Escape** to cancel. dnd-kit's KeyboardSensor ships built-in `aria-live` announcements describing each move. Inside a `Modal`/`Drawer`, an in-progress drag is an Escape-consuming mode: Escape cancels the drag and the host survives that press; the next Escape closes the host. Same for `DataTable` column reorder.
|
|
85
|
+
|
|
86
|
+
**Drop visual / async-safe**: the dragged item renders in a dnd-kit `<DragOverlay>` (a portaled, fixed-position clone); the active list `<li>` is an invisible placeholder during drag. Because the overlay owns the drop animation, the in-list row never carries a stale drag transform — so `onReorder` may commit the new order **asynchronously / optimistically** (e.g. an optimistic TanStack mutation's `onMutate` that lands a tick late) without the dropped row glitching ("fly up then settle"). You no longer need to reorder synchronously. The drop animation respects `prefers-reduced-motion` (it's disabled under reduced motion).
|
|
87
|
+
|
|
88
|
+
**Anti-patterns**
|
|
89
|
+
|
|
90
|
+
- ❌ Mutating items in place inside `onReorder`. Always return a new array (`arrayMove(items, from, to)` from `@dnd-kit/sortable`) — React needs a fresh reference.
|
|
91
|
+
- ❌ Using a non-stable `id` (e.g. array index). The id must persist across reorders for React reconciliation and for `onReorder`'s `id` field to be meaningful.
|
|
92
|
+
- ❌ Wrapping non-`Sortable.Item` content inside `<Sortable>`. dnd-kit's `SortableContext` only tracks the ids you pass it; arbitrary children render but won't be reorderable.
|
|
93
|
+
- ❌ Relying on whole-item drag (no Handle) for screen-reader-accessible lists. Without a Handle, dnd-kit puts `role="button"` on the `<li>` and the listitem semantics are lost — screen readers stop announcing "item N of M." For accessible lists, always include a `<Sortable.Handle>`.
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
// Plain text list — no handle needed; the whole item is draggable.
|
|
97
|
+
<Sortable onReorder={({ from, to }) => setItems((curr) => arrayMove(curr, from, to))}>
|
|
98
|
+
{items.map((item) => (
|
|
99
|
+
<Sortable.Item key={item.id} id={item.id}>{item.label}</Sortable.Item>
|
|
100
|
+
))}
|
|
101
|
+
</Sortable>
|
|
102
|
+
|
|
103
|
+
// Card with an internal DropdownMenu — an explicit Handle keeps the menu clickable.
|
|
104
|
+
<Sortable onReorder={handle}>
|
|
105
|
+
{cards.map((c) => (
|
|
106
|
+
<Sortable.Item key={c.id} id={c.id}>
|
|
107
|
+
<Card>
|
|
108
|
+
<Cluster justify="between">
|
|
109
|
+
<Sortable.Handle aria-label={`Reorder ${c.title}`}><GripVertical size={14} /></Sortable.Handle>
|
|
110
|
+
<Title order={3}>{c.title}</Title>
|
|
111
|
+
<DropdownMenu>...</DropdownMenu>
|
|
112
|
+
</Cluster>
|
|
113
|
+
</Card>
|
|
114
|
+
</Sortable.Item>
|
|
115
|
+
))}
|
|
116
|
+
</Sortable>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
- `Sortable.Item` renders an `<li>`; `span` applies only under `arrangement="grid"` and is ignored in a list. Drag announcements name the item by its rendered text; pass `aria-label` to override it when the item renders a lot of chrome (badges, timestamps, avatars) and only part of it is the name.
|
|
120
|
+
- `Sortable.Handle` is a `<button>` (keyboard-focusable); its children are decorative (`aria-hidden`), so give it an `aria-label`.
|
|
121
|
+
- Place `<Sortable.Handle>` at the same JSX nesting level as the Item's other content. A Handle hidden inside your own wrapper component (e.g. a `<TaskRow>` that renders it internally) is not detected, and the Item falls back to whole-item drag.
|
|
122
|
+
|
|
123
|
+
**When NOT to use**
|
|
124
|
+
|
|
125
|
+
- Tabular data: use `<Table>` / `<DataTable>` (DataTable has its own column reorder).
|
|
126
|
+
- Static lists that never reorder: use `<Stack>` or `<DefinitionList>`.
|
|
127
|
+
- Cross-list drag: use `<SortableGroup>`.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# `<SortableGroup>` — multi-container sortable (drag between lists)
|
|
2
|
+
|
|
3
|
+
`<SortableGroup onMove>` + `<SortableGroup.Container id items>` under one shared `DndContext` — drag `<Sortable.Item>`s within a list AND between lists. Controlled + live: `onMove({ id, from:{container,index}, to:{container,index} })` fires on each cross-container handoff during the drag AND on drop; apply it with the exported pure `moveSortableItem(containers, event)` (immutable, generic over item type). For a single list, use `<Sortable>`.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
const [groups, setGroups] = useState<Record<string, Field[]>>(initial);
|
|
7
|
+
<SortableGroup onMove={(e) => setGroups((g) => moveSortableItem(g, e))}>
|
|
8
|
+
{Object.entries(groups).map(([gid, fields]) => (
|
|
9
|
+
<SortableGroup.Container key={gid} id={gid} items={fields.map((f) => f.id)}>
|
|
10
|
+
{fields.map((f) => (
|
|
11
|
+
<Sortable.Item key={f.id} id={f.id}>
|
|
12
|
+
<Sortable.Handle>⋮⋮</Sortable.Handle>
|
|
13
|
+
{f.label}
|
|
14
|
+
</Sortable.Item>
|
|
15
|
+
))}
|
|
16
|
+
</SortableGroup.Container>
|
|
17
|
+
))}
|
|
18
|
+
</SortableGroup>;
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
<!-- props:start -->
|
|
22
|
+
|
|
23
|
+
## Props
|
|
24
|
+
|
|
25
|
+
### `SortableGroupProps`
|
|
26
|
+
|
|
27
|
+
<!-- prettier-ignore -->
|
|
28
|
+
| Prop | Type | Required | Description |
|
|
29
|
+
|---|---|---|---|
|
|
30
|
+
| `onMove` | `((event: SortableMoveEvent) => void)` | no | Fires on every cross-container handoff (during the drag) AND on the final drop. Apply it to your controlled per-container state — see `moveSortableItem`. |
|
|
31
|
+
| `children` | `ReactNode` | yes | The `<SortableGroup.Container>` lists. |
|
|
32
|
+
|
|
33
|
+
### `SortableGroupContainerProps`
|
|
34
|
+
|
|
35
|
+
<!-- prettier-ignore -->
|
|
36
|
+
| Prop | Type | Required | Description |
|
|
37
|
+
|---|---|---|---|
|
|
38
|
+
| `id` | `Id` | yes | Stable container id (the `container` reported in `SortableMoveEvent`). |
|
|
39
|
+
| `items` | `Id[]` | yes | Ordered item ids in THIS container — the controlled source of truth. |
|
|
40
|
+
| `children` | `ReactNode` | yes | `<Sortable.Item>`s for the ids in `items`. |
|
|
41
|
+
| …native | | | plus native `<ol>` attributes |
|
|
42
|
+
|
|
43
|
+
<!-- props:end -->
|
|
44
|
+
|
|
45
|
+
- Give each `Container` an `aria-label` (or `aria-labelledby`) — it names the `<ol>` for screen readers AND names the list in drag announcements ("…position 2 of 4 in In review").
|
|
46
|
+
- Container ids and item ids share dnd-kit's one id namespace — keep them all unique.
|
|
47
|
+
- Each `Container.items` must match its `<Sortable.Item>` child ids (it's the ordering source of truth).
|
|
48
|
+
- Esc-cancel doesn't revert (moves are applied to your state optimistically) — snapshot before drag to undo.
|
|
49
|
+
|
|
50
|
+
- The root renders no host DOM node, so it forwards no `ref`; attach refs to `<SortableGroup.Container>`, which forwards to its `<ol>`.
|
|
51
|
+
- `Container` renders an `<ol>` registered as a droppable, so an empty list still accepts cross-container drops.
|
|
52
|
+
- Mutating state inside `onMove` is wrong: return a NEW object (use `moveSortableItem`).
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# `<Sparkline>` — compact trend graphic for KPI cards
|
|
2
|
+
|
|
3
|
+
Static, axis-free line / area / bar trend with an intrinsic height (`--size-sparkline`, 40px) and full width. `role="img"` named "{label}: from {first} to {last}, low {min}, high {max}" (numbers through `formatValue`). `null` and non-finite values are gaps; area and bar anchor at zero for non-negative data. With negative data a line spans min..max, an area fills from the bottom (min), and bars hang from a zero line (all-negative series hang from the top). No hover, no tab stop, no animation.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { DashboardWidget, Sparkline, StatGroup, StatTile } from '@eocrm/design-system';
|
|
7
|
+
|
|
8
|
+
// Standalone
|
|
9
|
+
<Sparkline label="Contacts per day" values={dailyCounts} />
|
|
10
|
+
<Sparkline label="Deals won per week" type="bar" values={weekly} formatValue={fmt} />
|
|
11
|
+
|
|
12
|
+
// StatTile slot
|
|
13
|
+
<StatGroup aria-label="Usage">
|
|
14
|
+
<StatTile
|
|
15
|
+
label="Contacts"
|
|
16
|
+
value="1,284"
|
|
17
|
+
sparkline={<Sparkline label="Contacts per day" type="area" values={dailyCounts} />}
|
|
18
|
+
/>
|
|
19
|
+
</StatGroup>
|
|
20
|
+
|
|
21
|
+
// kpi DashboardWidget: pass it as children
|
|
22
|
+
<DashboardWidget variant="kpi" title="Open deals" value="128">
|
|
23
|
+
<Sparkline label="Open deals per day" values={dailyCounts} />
|
|
24
|
+
</DashboardWidget>;
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
<!-- props:start -->
|
|
28
|
+
|
|
29
|
+
## Props
|
|
30
|
+
|
|
31
|
+
<!-- prettier-ignore -->
|
|
32
|
+
| Prop | Type | Required | Description |
|
|
33
|
+
|---|---|---|---|
|
|
34
|
+
| `label` | `string` | yes | What the series is, e.g. "Contacts per day". Starts the accessible name. |
|
|
35
|
+
| `values` | `readonly (number \| null)[]` | yes | Values oldest → newest. `null` (or a non-finite number) = no data for that slot: a gap in a line/area, no bar. |
|
|
36
|
+
| `type` | `'line' \| 'area' \| 'bar'` | no | Mark type. Default `'line'`. - `'line'` — smoothed line with a dot on the latest value. Trends of a rate or level. - `'area'` — the line over a filled area anchored at zero. Volumes / counts over time. - `'bar'` — one bar per value. Discrete per-period counts (daily signups). |
|
|
37
|
+
| `formatValue` | `((n: number) => string)` | no | Formats numbers in the accessible summary. Default: `Intl.NumberFormat` for the current locale. |
|
|
38
|
+
| `endDot` | `boolean` | no | Dot on the latest value (`line` / `area`; `bar` never has one). Default `true`. Turn off where the dot reads as a stray marker rather than emphasis, e.g. a dense row of `StatTile` sparklines. |
|
|
39
|
+
| …native | | | plus native `<div>` attributes |
|
|
40
|
+
|
|
41
|
+
<!-- props:end -->
|
|
42
|
+
|
|
43
|
+
- **Name, not a table.** The summary (first, last, low, high) is folded into the accessible name; there is no data table. Empty or all-null `values` render nothing and are named "{label}: no data".
|
|
44
|
+
- **Ignored attributes.** `role`, `aria-label` and `aria-labelledby` are ignored; the name is always derived from `label` and `values`.
|
|
45
|
+
- **Marks.** `line` and `area` are smoothed and end in a dot on the last value (`endDot={false}` removes it, e.g. in a dense row of tiles); `bar` draws one bar per non-null value. A flat `line` is drawn mid-height (a flat positive area/bar fills from zero).
|
|
46
|
+
|
|
47
|
+
#### When NOT to use
|
|
48
|
+
|
|
49
|
+
- ❌ Values someone must read or compare exactly, axes or dates, several series, hover / keyboard inspection → `Chart`.
|
|
50
|
+
- ❌ Wrapping it in `Constrain` or giving it a height: it brings its own.
|
|
51
|
+
- ❌ Passing `role` / `aria-label` / `aria-labelledby` to rename it: they are ignored.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# `<Split>` — master–detail two-pane layout
|
|
2
|
+
|
|
3
|
+
Intrinsic-width `aside` pane beside a filling `main` pane (`children`), via CSS
|
|
4
|
+
grid `auto 1fr`. Never wraps. Sibling to Stack/Cluster/Grid; for in-page
|
|
5
|
+
master–detail (a vertical `Tabs` rail beside its detail panel, a filter column
|
|
6
|
+
beside results).
|
|
7
|
+
|
|
8
|
+
```tsx
|
|
9
|
+
<Split
|
|
10
|
+
asideWidth="220px"
|
|
11
|
+
collapseBelow="sm"
|
|
12
|
+
aside={<Tabs orientation="auto" items={items} activeId={id} onChange={setId} />}
|
|
13
|
+
gap="lg"
|
|
14
|
+
>
|
|
15
|
+
<SectionPanel id={id} />
|
|
16
|
+
</Split>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
<!-- props:start -->
|
|
20
|
+
|
|
21
|
+
## Props
|
|
22
|
+
|
|
23
|
+
<!-- prettier-ignore -->
|
|
24
|
+
| Prop | Type | Required | Description |
|
|
25
|
+
|---|---|---|---|
|
|
26
|
+
| `aside` | `ReactNode` | no | The narrow, intrinsic-width pane — a vertical `Tabs` rail, filter list, or nav column. `null` / `false` / `undefined` renders no aside cell at all (no empty track or gap), so content can be conditionally moved out of the aside; `main` then fills the split. |
|
|
27
|
+
| `side` | `'start' \| 'end'` | no | Which side `aside` sits on. - `'start'` (default) — leading edge (left in LTR). - `'end'` — trailing edge (right in LTR). |
|
|
28
|
+
| `asideWidth` | `string` | no | `aside` column width. - `'auto'` (default) — intrinsic; sizes to the pane's content. - a CSS length (e.g. `'240px'`) — pins the column so `main` doesn't reflow when `aside` content changes width. |
|
|
29
|
+
| `gap` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| '2xl'` | no | Gap between the two panes, in pixels: `xs` (4) / `sm` (8) / `md` (12, default) / `lg` (16) / `xl` (24) / `2xl` (32). Same scale as Stack, Cluster, and Grid. |
|
|
30
|
+
| `align` | `'start' \| 'stretch' \| 'center'` | no | Cross-axis (vertical) alignment of the panes. - `'start'` (default) — panes hug the top. - `'stretch'` — `aside` matches `main`'s height (full-height bordered rail). - `'center'` — panes vertically centered. |
|
|
31
|
+
| `collapseBelow` | `'sm' \| 'md' \| 'lg'` | no | Stack the two panes vertically when the SPLIT'S OWN width (container query, not viewport) drops below the preset: `sm` 480px / `md` 640px / `lg` 768px. Same scale as `Grid`'s `collapseBelow`. Without it a pinned `asideWidth` rail squeezes `main` to nothing on narrow screens. A pinned `asideWidth` is preserved while the panes are side-by-side and space is available, but can shrink as the container narrows so the stacked panes stay within the split. Collapsed panes stack in **DOM order**, not always aside-first: with the default `side="start"` that's aside → main; with `side="end"` it's main → aside, because that's the order the panes are in the DOM. Forcing aside-first when collapsed would need CSS `order`, which desynchronizes visual order from tab order — an a11y defect. If you need the aside on top when stacked, use `side="start"`. A `<Sticky>` passed as the `aside` stops pinning (and drops its `scroll` height cap) while stacked, so it can't trap the page scroll on a phone. ❌ Anti-pattern: a `collapseBelow` split must get its width from its parent. `container-type: inline-size` zeroes the split's contribution to intrinsic sizing, so in an intrinsic-width context (another `Split`'s default `auto` aside track, a `Cluster` item, `width: max-content`) it renders at width 0 — give the parent a concrete width instead. The split also becomes the containing block for absolutely-positioned descendants (layout containment). Splits without the prop pay none of this. |
|
|
32
|
+
| `onCollapsedChange` | `((collapsed: boolean) => void)` | no | Called with `true` when a `collapseBelow` split stacks and `false` when it goes back side by side — on mount with the initial state (twice under React StrictMode in development), then on every change. Ignored without `collapseBelow`. Use it to move content you own between placements so DOM order matches visual order in both states, instead of CSS `order` — e.g. render a comment thread at the end of `main` while side by side and after the `<Split>` once stacked, or a status switcher at the top of the aside vs. above the split. It measures the SPLIT'S OWN content width against the same inclusive threshold its container query uses (a `ResizeObserver`, read synchronously on mount so the first paint is already right), so it agrees with the CSS where `useBelowBreakpoint` — which measures the viewport — would not beside an app sidebar. The same state is on the root as `data-collapsed="true" \| "false"`. |
|
|
33
|
+
| …native | | | plus native `<div>` attributes |
|
|
34
|
+
|
|
35
|
+
<!-- props:end -->
|
|
36
|
+
|
|
37
|
+
- `children` is the filling main pane (`min-width: 0` — long content shrinks/scrolls instead of overflowing).
|
|
38
|
+
- An empty `aside` (`null` / `false` / `undefined`) renders no aside track even with `asideWidth`. To keep the track reserved (e.g. while data loads), pass an empty fragment (`<></>`) or a placeholder.
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
// Settings screen: a 220px rail that becomes a horizontal strip when it stacks.
|
|
42
|
+
<Split aside={<Tabs orientation="auto" ... />} asideWidth="220px" collapseBelow="sm">
|
|
43
|
+
<SettingsPanel />
|
|
44
|
+
</Split>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
- Collapsed panes stack in **DOM order**: aside → main for `side="start"` (default), main → aside for `side="end"`. No CSS `order` flip — visual order stays in sync with tab order. Need the aside on top when stacked? Use `side="start"`.
|
|
48
|
+
- A pinned `<Sticky>` aside needs `align="stretch"`: the default `align="start"` makes the aside cell only as tall as its content, leaving nothing to pin within.
|
|
49
|
+
- A `<Sticky>` passed as `aside` becomes a plain block while collapsed (no pin, no `scroll` cap) — don't shim it.
|
|
50
|
+
- `onCollapsedChange(collapsed)` — fires on mount with the initial state (twice under StrictMode in dev), then on every change; the root also carries `data-collapsed`. It measures the split's own content width against the same inclusive threshold as the container query, so it agrees with the CSS (`useBelowBreakpoint` measures the viewport and does not, beside an app sidebar). Use it to move content YOU own so DOM order = visual order in both states — never CSS `order`:
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
// Comments last on the page, side by side or stacked:
|
|
54
|
+
const [stacked, setStacked] = useState(false);
|
|
55
|
+
<>
|
|
56
|
+
<Split aside={<Sidebar />} side="end" collapseBelow="lg" onCollapsedChange={setStacked}>
|
|
57
|
+
<RecordData />
|
|
58
|
+
{!stacked && <Comments />}
|
|
59
|
+
</Split>
|
|
60
|
+
{stacked && <Comments />}
|
|
61
|
+
</>;
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
- ❌ A `collapseBelow` split in an intrinsic-width context (another `Split`'s default `auto` aside track, a `Cluster` item, `width: max-content`). `container-type: inline-size` makes it contribute zero intrinsic width, so it renders at width 0 — give the parent a concrete width instead. It also becomes the containing block for absolutely-positioned descendants (layout containment). Splits without the prop pay neither cost.
|
|
65
|
+
|
|
66
|
+
When NOT to use: equal columns → `<Grid columns={2}>`; wrapping peer row → `<Cluster>`; app shell sidebar → `<AppLayout>`/`<Rail>`.
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
// Pinned rail width, aside on the right:
|
|
70
|
+
<Split aside={<Filters />} side="end" asideWidth="260px" gap="lg">
|
|
71
|
+
<Results />
|
|
72
|
+
</Split>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
- Split owns only its internal grid (like `AppLayout` / `Page` / `Screen`, a documented exception to "components don't own layout"). Unlike `Cluster` it never wraps the panel below the rail; unlike `Grid columns={2}` the rail keeps its natural width instead of taking half.
|
|
76
|
+
- ❌ Primary page navigation in `aside`: that belongs in the app shell (`<Rail>` / `<AppLayout sidebar>`); Split's aside is intra-page.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# `<Stack>` — vertical layout
|
|
2
|
+
|
|
3
|
+
```tsx
|
|
4
|
+
<Stack gap="md">
|
|
5
|
+
<Input ... />
|
|
6
|
+
<Input ... />
|
|
7
|
+
<Cluster justify="end" gap="sm">
|
|
8
|
+
<Button variant="secondary">Cancel</Button>
|
|
9
|
+
<Button type="submit">Save</Button>
|
|
10
|
+
</Cluster>
|
|
11
|
+
</Stack>
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
<!-- props:start -->
|
|
15
|
+
|
|
16
|
+
## Props
|
|
17
|
+
|
|
18
|
+
<!-- prettier-ignore -->
|
|
19
|
+
| Prop | Type | Required | Description |
|
|
20
|
+
|---|---|---|---|
|
|
21
|
+
| `gap` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| '2xl'` | no | Gap between children, in pixels: `xs` (4) / `sm` (8) / `md` (12, default) / `lg` (16) / `xl` (24) / `2xl` (32). |
|
|
22
|
+
| `align` | `'start' \| 'center' \| 'end' \| 'stretch'` | no | Cross-axis alignment. - `stretch` (default) — children fill the stack's horizontal width. - `start` / `center` / `end` — left, center, right alignment. |
|
|
23
|
+
| `minWidth0` | `boolean` | no | Lets the container shrink below its content's intrinsic width (`min-width: 0`), so a `<Text truncate>` inside can ellipsize instead of being hard-cut by a clipping ancestor. Only bites when the Stack is itself an item of a **row** flex container (or a grid item) that clips — what decides is the PARENT's main axis, not the Stack's own direction. That is the two-line label beside a fixed badge, or a detail column in a squeezed toolbar. Without it the flex default (`min-width: auto`) floors the Stack at its widest line and the ellipsis never appears. That floor applies because a Stack sets no `overflow` — per CSS Flexbox §4.5 a flex item keeps its automatic minimum size while its computed `overflow` is non-scrollable (`visible` or `clip`); the scrollable values (`hidden`/`auto`/`scroll`) drop it to `0`. It is a **no-op** when the Stack sits in another `Stack` (the automatic minimum size applies only on the flex MAIN axis, so there is no horizontal floor), or in a plain block or table cell (that minimum applies to flex and grid ITEMS only, so `min-width: auto` is just `0` there). A table cell is a no-op for a different reason than it looks: auto table layout floors the cell at its content's min-content width regardless, so what makes text truncate there is `table-layout: fixed` or a `max-width` on the cell — not this prop. Opt-in rather than the default on purpose: a container that CAN shrink also VOLUNTEERS for shrink, so turning it on where the content is NOT truncatable (buttons, badges, icons) lets that content be clipped instead. Set it on the container whose text should give way, not on one holding controls. Related: `<Constrain flex="grow">` applies the same `min-width: 0` but also forces `flex: 1 1 0`, and renders a `<div>` — reach for this prop when you want only the shrink permission, or when you are inside a `<button>`/`<a>`/`<label>` where a `<div>` is invalid HTML. Default: `false`. |
|
|
24
|
+
| …native | | | plus native `<div>` attributes |
|
|
25
|
+
|
|
26
|
+
<!-- props:end -->
|
|
27
|
+
|
|
28
|
+
- `minWidth0`: `false` (default). Sets `min-width: 0` so the Stack can shrink below its content's intrinsic width, letting a `<Text truncate>` inside ellipsize instead of being hard-cut. Only bites when the Stack is itself an item of a **row** flex container (or a grid item) that clips — what decides is the PARENT's main axis, not the Stack's own direction: a two-line label beside a fixed badge, a detail column in a squeezed toolbar. **No-op** inside another `Stack`, a plain block, or a table cell — and note a table cell is a no-op for a different reason than it looks: auto table layout floors the cell at its content's min-content width regardless, so what makes text truncate there is `table-layout: fixed` or a `max-width` on the cell (that is what `<Table.Cell truncate>` does), not this prop. **Opt-in on purpose:** a container that can shrink also _volunteers_ for shrink, so setting it where the content is NOT truncatable (buttons, badges, icons) lets that content be clipped instead.
|
|
29
|
+
|
|
30
|
+
Pairs with `<Cluster>` (horizontal); together they cover most CRM layout. Use it instead of ad-hoc `display: flex; flex-direction: column` divs.
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
// Page sections with a larger gap:
|
|
34
|
+
<Stack gap="xl">
|
|
35
|
+
<header>...</header>
|
|
36
|
+
<section>...</section>
|
|
37
|
+
<section>...</section>
|
|
38
|
+
</Stack>
|
|
39
|
+
|
|
40
|
+
// minWidth0 — a Stack inside a squeezing flex row, so truncating children can ellipsize:
|
|
41
|
+
<Cluster wrap={false} gap="sm">
|
|
42
|
+
<Stack gap="xs" minWidth0>
|
|
43
|
+
<Text weight="medium" truncate>{deal.name}</Text>
|
|
44
|
+
<Text size="xs" tone="muted" truncate>{deal.accountPath}</Text>
|
|
45
|
+
</Stack>
|
|
46
|
+
<Badge tone="success">{deal.stage}</Badge>
|
|
47
|
+
</Cluster>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**When NOT to use**
|
|
51
|
+
|
|
52
|
+
- Tabular data: use a real `<table>` or `<Grid>`.
|
|
53
|
+
- A list of clickable items: semantics matter, use `<ul><li>` with appropriate styling.
|
|
54
|
+
|
|
55
|
+
**Anti-patterns**
|
|
56
|
+
|
|
57
|
+
- `minWidth0` on a Stack holding buttons or badges: the container shrinks and non-truncatable content is clipped. Use it on the container whose TEXT should give way.
|
|
58
|
+
- Nested Stacks with different gaps just to bend spacing locally. Sometimes legitimate (page sections at `xl` containing field stacks at `md`), but it usually signals unclear hierarchy.
|
|
59
|
+
- A Stack with one or two children (`<Stack><Button /></Stack>`) is noise; inline the child.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# `<StagePath>` — chevron row of record stages
|
|
2
|
+
|
|
3
|
+
A record's ordered stages as interlocking chevrons: done (tinted), current (solid), upcoming (neutral). Read-only by default; pass `onValueChange` to make every non-current stage a button. `tone` recolours done + current once the record reaches an outcome.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { Button, Cluster, StagePath } from '@eocrm/design-system';
|
|
7
|
+
|
|
8
|
+
<Cluster wrap={false}>
|
|
9
|
+
<StagePath
|
|
10
|
+
aria-label="Deal stage"
|
|
11
|
+
stages={[
|
|
12
|
+
{ id: 'lead', label: 'Lead' },
|
|
13
|
+
{ id: 'qualified', label: 'Qualified' },
|
|
14
|
+
{ id: 'proposal', label: 'Proposal' },
|
|
15
|
+
{ id: 'negotiation', label: 'Negotiation' },
|
|
16
|
+
]}
|
|
17
|
+
value={deal.stage}
|
|
18
|
+
tone={deal.outcome === 'won' ? 'success' : deal.outcome === 'lost' ? 'danger' : 'default'}
|
|
19
|
+
onValueChange={(id) => moveDeal(deal.id, id)}
|
|
20
|
+
/>
|
|
21
|
+
<Button variant="success">Won</Button>
|
|
22
|
+
<Button variant="danger-outline">Lost</Button>
|
|
23
|
+
</Cluster>;
|
|
24
|
+
|
|
25
|
+
// Read-only (list rows, previews, no permission)
|
|
26
|
+
<StagePath aria-label="Project stage" stages={projectStages} value={project.stage} />;
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
<!-- props:start -->
|
|
30
|
+
|
|
31
|
+
## Props
|
|
32
|
+
|
|
33
|
+
<!-- prettier-ignore -->
|
|
34
|
+
| Prop | Type | Required | Description |
|
|
35
|
+
|---|---|---|---|
|
|
36
|
+
| `stages` | `StagePathStage[]` | yes | Ordered stages, first to last. |
|
|
37
|
+
| `value` | `string` | yes | Id of the current stage. Stages before it render as done, after it as upcoming. An id not in `stages` renders every stage as upcoming (and warns in development) rather than throwing. |
|
|
38
|
+
| `tone` | `'default' \| 'success' \| 'danger'` | no | Outcome colour for done + current stages. See `StagePathTone`. Default: `'default'`. |
|
|
39
|
+
| `onValueChange` | `((id: string) => void)` | no | Makes the path interactive: every non-current stage renders as a button that calls this with its id. The current stage is never a button. Omit for a read-only path. Whether a move is allowed (e.g. backwards) is the consumer's rule — confirm or ignore inside the handler. |
|
|
40
|
+
| …native | | | plus native `<ol>` attributes |
|
|
41
|
+
|
|
42
|
+
<!-- props:end -->
|
|
43
|
+
|
|
44
|
+
- **Always controlled.** `value` is required; there is no internal state. `onValueChange` is a request — update `value` when the move succeeds (after a confirmation or server call if needed).
|
|
45
|
+
- **Stage semantics.** `<ol>` with `aria-current="step"` on the current stage's chevron (the element focus returns to after a keyboard move); done/upcoming stages carry visually hidden, localised "completed" / "upcoming" text, so state is never colour alone.
|
|
46
|
+
- **Fills its container.** `width: 100%`, equal-width stages, labels ellipsize with a tooltip only when clipped. Beside other controls, use `<Cluster wrap={false}>` so the path takes the remaining width instead of wrapping onto its own line.
|
|
47
|
+
- **Every non-current stage is clickable** when `onValueChange` is passed, including going backwards. Enforce business rules in the handler.
|
|
48
|
+
- **Stage ids must be unique** (they are React keys). An empty `stages` array renders an empty list (and warns, since no stage matches `value`).
|
|
49
|
+
- One size (32px, matches `Button` `md`).
|
|
50
|
+
- **RTL follows the nearest `dir` attribute** (an ancestor's or the path's own). `dir="auto"` is ignored (the nearest explicit `dir` wins), and RTL re-nested inside an LTR island inside an RTL page renders LTR chevrons.
|
|
51
|
+
- **No pending state.** Nothing changes while `onValueChange`'s work resolves, and a double click calls it twice. Guard in the handler, or render the path read-only (omit `onValueChange`) while saving.
|
|
52
|
+
- **Geometry tokens are tuned together.** The chevron-shaped focus ring is computed for the default `--stage-path-arrow` / `--stage-path-height`; overriding either skews the ring's slanted edges.
|
|
53
|
+
|
|
54
|
+
#### When NOT to use
|
|
55
|
+
|
|
56
|
+
- ❌ Navigation between pages → `<Breadcrumb>` / `<Tabs>`.
|
|
57
|
+
- ❌ A multi-step form wizard. StagePath reflects a record's state; it doesn't drive a form flow.
|
|
58
|
+
- ❌ A history of what happened and when → `<Timeline>`.
|
|
59
|
+
- ❌ Percentage progress → `<Progress>`.
|
|
60
|
+
|
|
61
|
+
#### Anti-patterns
|
|
62
|
+
|
|
63
|
+
- ❌ Encoding the outcome as an extra stage ("Closed Lost") instead of `tone="danger"`.
|
|
64
|
+
- ❌ `tone="success"` to celebrate reaching the last stage. Tone is for an outcome (won/lost), not for being on the final step.
|
|
65
|
+
- ❌ More than ~7 stages: labels truncate to uselessness. Group stages or show a summary.
|
|
66
|
+
- ❌ Wrapping StagePath in a wrapping `Cluster`: at `width: 100%` it drops onto its own line.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# `<StatGroup>` — responsive grid of KPI tiles
|
|
2
|
+
|
|
3
|
+
```tsx
|
|
4
|
+
<DashboardWidget title="Key metrics">
|
|
5
|
+
<StatGroup aria-label="Key metrics" loading={isLoading}>
|
|
6
|
+
<StatTile
|
|
7
|
+
label="Pipeline value"
|
|
8
|
+
value={formatCurrency(pipeline)}
|
|
9
|
+
trend={{ label: '12.4% vs previous period', direction: 'up' }}
|
|
10
|
+
footnote="2 deals in other currencies not included"
|
|
11
|
+
icon={<IconTile icon={<Euro size={14} />} color="blue" size="sm" />}
|
|
12
|
+
/>
|
|
13
|
+
<StatTile label="Win rate" value={winRate ?? null} />
|
|
14
|
+
<StatTile
|
|
15
|
+
label="Overdue tasks"
|
|
16
|
+
value="17"
|
|
17
|
+
trend={{ label: '4 vs previous period', direction: 'up', sentiment: 'negative' }}
|
|
18
|
+
/>
|
|
19
|
+
</StatGroup>
|
|
20
|
+
</DashboardWidget>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
TL;DR: a `<ul>` grid of 1–8 [`StatTile`](StatTile.md)s for a dashboard widget that shows several numbers at once. Tiles reflow by available width (`minColumnWidth`, auto-fit). In a cell narrower than one column a single tile shrinks instead of overflowing, and very narrow tiles drop the icon and shrink the value. Put it in `DashboardWidget` `variant="stats"` (compact padding, leaves slack in a 3-row cell; `standard` also works): its body scrolls if the tiles overflow the cell.
|
|
24
|
+
|
|
25
|
+
**Surface:** `variant="filled"` (default) is a muted tile with no border — right inside a `Card` / `DashboardWidget`. `variant="card"` gives every tile the card background, border and shadow, for a group sitting directly on the page background next to other `Card`s. Don't use `card` inside a Card (card-in-card). The `Progress` track and skeletons stay visible on both surfaces.
|
|
26
|
+
|
|
27
|
+
**Loading:** `loading` keeps labels and icons and replaces each value and trend with a skeleton, so the placeholder has the real layout. Each tile's value slot carries visually hidden "Loading…" (read when browsing the tiles); no `aria-busy`: the per-tile hidden text already carries the state. There is deliberately no live region: a dashboard loads many widgets at once, and per-widget announcements would flood screen readers. Announce "dashboard loaded" once at page level if needed. Footnotes hide while loading (they describe data that is not there yet).
|
|
28
|
+
|
|
29
|
+
**Accessibility:** give the group a name (`aria-label`, or `aria-labelledby` pointing at the widget title). Screen readers read each tile in DOM order: label, value, trend ("Increase 12.4% vs previous period"), footnote.
|
|
30
|
+
|
|
31
|
+
<!-- props:start -->
|
|
32
|
+
|
|
33
|
+
## Props
|
|
34
|
+
|
|
35
|
+
<!-- prettier-ignore -->
|
|
36
|
+
| Prop | Type | Required | Description |
|
|
37
|
+
|---|---|---|---|
|
|
38
|
+
| `minColumnWidth` | `string` | no | Minimum tile width before the grid drops a column (auto-fit). Any CSS length. Clamped to the group's width, so in a cell narrower than one column a single tile shrinks instead of overflowing. Default `'11rem'`. |
|
|
39
|
+
| `gap` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| '2xl'` | no | Gap between tiles. `xs` (4) / `sm` (8) / `md` (12, default) / `lg` (16) / `xl` (24) / `2xl` (32). |
|
|
40
|
+
| `loading` | `boolean` | no | Loading state for every tile: labels and icons stay; each value and trend becomes a skeleton with visually hidden "Loading…" in the value slot, and footnotes hide. Deliberately NO live region: a dashboard loads many widgets at once, so announcements would flood screen readers. No `aria-busy`: the per-tile hidden text already carries the state. Default `false`. |
|
|
41
|
+
| `variant` | `'filled' \| 'card'` | no | Surface of every tile, so a group stays consistent. Default `'filled'`. - `'filled'` — muted fill, no border. Use inside a `Card` / `DashboardWidget`. - `'card'` — card background, border and shadow, matching `Card`s on a page background; avoid inside a Card (card-in-card). |
|
|
42
|
+
| `children` | `ReactNode` | no | `StatTile` elements. |
|
|
43
|
+
| …native | | | plus native `<ul>` attributes |
|
|
44
|
+
|
|
45
|
+
<!-- props:end -->
|
|
46
|
+
|
|
47
|
+
**When NOT to use:**
|
|
48
|
+
|
|
49
|
+
- One number per card: use `DashboardWidget variant="kpi"`.
|
|
50
|
+
- Key/value facts that are not metrics (owner, stage, created date): use `DefinitionList`.
|
|
51
|
+
- A chart or a list of records: use `DashboardWidget variant="chart"` / `"list"`.
|
|
52
|
+
|
|
53
|
+
#### Anti-patterns
|
|
54
|
+
|
|
55
|
+
- ❌ Hand-composing `Grid` + `IconTile` + `Text` tiles. You lose the trend semantics, the "no data" text and the loading state.
|
|
56
|
+
- ❌ `<StatTile>` outside a `StatGroup`. It renders an `<li>` and reads `loading` from the group.
|
|
57
|
+
- ❌ `value="—"` for missing data. Pass `value={null}` so screen readers hear "No data" instead of silence or "em dash".
|
|
58
|
+
- ❌ Unformatted values (`value={1240000}`). Format before passing, using compact notation for large amounts: `Intl.NumberFormat(locale, { style: 'currency', currency, notation: 'compact', maximumSignificantDigits: 3 })` gives `€1.24M`, not `€1,240,000`.
|
|
59
|
+
- ❌ Colouring a "bad" increase with `direction: 'down'`. Keep `direction` truthful and set `sentiment: 'negative'`.
|
|
60
|
+
- ❌ More than about 8 tiles in one widget. Split into two widgets.
|
|
61
|
+
- ❌ Putting a `StatGroup` in a parent that sizes to its content (a `Cluster` item, `width: max-content`, an `auto` `Split` aside). Each tile is a size container, so it contributes no intrinsic width and the tiles collapse. Give the parent a concrete width; a `DashboardWidget` cell already has one.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# `<StatTile>` — one KPI tile inside a StatGroup
|
|
2
|
+
|
|
3
|
+
```tsx
|
|
4
|
+
<StatGroup aria-label="Sales performance">
|
|
5
|
+
<StatTile
|
|
6
|
+
label="Revenue"
|
|
7
|
+
value="€86,400"
|
|
8
|
+
trend={{ label: '3.1% vs previous period', direction: 'down' }}
|
|
9
|
+
/>
|
|
10
|
+
</StatGroup>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
TL;DR: label, large value, optional trend, footnote and icon, on a filled tile (or a card surface, via `StatGroup`'s `variant`). Only valid as a child of [`StatGroup`](StatGroup.md), which owns the grid, the `<ul>` and the loading state. See `StatGroup.md` for loading and accessibility.
|
|
14
|
+
|
|
15
|
+
- `value={null}` / `undefined` shows "—" with visually hidden "No data". `0` is data and renders as `0`.
|
|
16
|
+
- `trend` is the same shape as `DashboardWidgetTrend`. `sentiment` defaults from `direction` (up → positive, down → negative, flat → neutral); override it when up is bad.
|
|
17
|
+
- `icon` is decorative and rendered as-is before the label; pass an `IconTile` (`size="sm"`). It hides in very narrow tiles.
|
|
18
|
+
- `sparkline` takes a [`Sparkline`](Sparkline.md) (or any node) and renders it under the trend, above the footnote. While the group is loading it becomes a sparkline-height skeleton. In `DashboardWidget variant="kpi"`, pass the `Sparkline` as `children` instead.
|
|
19
|
+
- `progress={{ value, max, tone? }}` adds a quota meter under the value/trend (before the sparkline): a small `Progress` named by the tile `label` (no `aria-label` needed), with a progress-height skeleton while loading. Put the readable "used / limit" in `value`; use `tone` for near/over-limit:
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
<StatTile
|
|
23
|
+
label="Storage"
|
|
24
|
+
value="1.2 GB / 5 GB"
|
|
25
|
+
progress={{ value: 1.2, max: 5, tone: 1.2 / 5 > 0.9 ? 'warning' : 'default' }}
|
|
26
|
+
/>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Over the limit the bar shows full (the `value` text carries the overage); no meter renders when `max` isn't positive or a number isn't finite (unlimited plan, limit still loading). This is the supported quota meter — don't put a `Progress` in the `sparkline` slot.
|
|
30
|
+
|
|
31
|
+
- The tile surface (`filled` or `card`) is set once for the whole group by `StatGroup`'s `variant`.
|
|
32
|
+
- Format large values compactly (`€1.24M`, not `€1,240,000`): the value is `3xl` in a tile at least 11rem wide by default (`minColumnWidth`), and long numbers wrap. Use `Intl.NumberFormat(locale, { style: 'currency', currency, notation: 'compact', maximumSignificantDigits: 3 })`.
|
|
33
|
+
- The hidden trend words ("Increase / Decrease / No change") come from the `dashboardWidget.trendUp` / `trendDown` / `trendFlat` i18n keys, shared with `DashboardWidget`; override with `<I18nProvider overrides={{ dashboardWidget: { trendUp: '…' } }}>`, which also changes `DashboardWidget`'s KPI trend.
|
|
34
|
+
|
|
35
|
+
<!-- props:start -->
|
|
36
|
+
|
|
37
|
+
## Props
|
|
38
|
+
|
|
39
|
+
<!-- prettier-ignore -->
|
|
40
|
+
| Prop | Type | Required | Description |
|
|
41
|
+
|---|---|---|---|
|
|
42
|
+
| `label` | `ReactNode` | yes | What the number is, e.g. "Pipeline value". Read first by screen readers. |
|
|
43
|
+
| `value` | `ReactNode` | yes | The pre-formatted value: an integer, currency or percentage (`"€1.24M"`, `"38%"`). Required so "no data" is explicit: pass `null` (or `undefined`) and the tile shows "—" with visually hidden "No data". `0` is data and renders as `0`. |
|
|
44
|
+
| `trend` | `DashboardWidgetTrend` | no | Delta under the value, e.g. `{ label: '12.4% vs previous period', direction: 'up' }`. Arrow + hidden "Increase / Decrease / No change"; colour from `sentiment`, which defaults from `direction` (up → positive). Override when up is bad (overdue tasks). |
|
|
45
|
+
| `footnote` | `ReactNode` | no | Small muted note under the trend, e.g. "2 deals in other currencies not included". Hidden while loading. |
|
|
46
|
+
| `icon` | `ReactNode` | no | Decorative icon before the label, typically `<IconTile size="sm" … />`. Hidden in very narrow tiles. |
|
|
47
|
+
| `sparkline` | `ReactNode` | no | Trend graphic under the value/trend, typically `<Sparkline label=… values=… />`. Replaced by a sparkline-height skeleton while the group is loading. |
|
|
48
|
+
| `progress` | `StatProgress` | no | Quota meter under the value/trend: "used / limit" tiles, e.g. `value="1.2 GB / 5 GB" progress={{ value: 1.2, max: 5 }}`. Renders a small `Progress` named by the tile `label` (no `aria-label` needed); progress-height skeleton while the group is loading. Put the readable "used / limit" text in `value`. Over the limit, the bar shows full (ARIA value clamped to `max`); the text in `value` carries the overage. No meter renders when `max` is not positive or a number is not finite (an unlimited plan, a limit still loading). |
|
|
49
|
+
| …native | | | plus native `<li>` attributes |
|
|
50
|
+
|
|
51
|
+
<!-- props:end -->
|
|
52
|
+
|
|
53
|
+
**When NOT to use:** a single KPI card (`DashboardWidget variant="kpi"`), or a standalone tile outside a group.
|
|
54
|
+
|
|
55
|
+
#### Anti-patterns
|
|
56
|
+
|
|
57
|
+
- ❌ Wrapping `StatTile` in another element inside `StatGroup`: the `<li>` must be a direct child of the `<ul>`.
|
|
58
|
+
- ❌ Putting interactive controls in a tile. Tiles are read-only; link out from the widget's actions instead.
|