@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,83 @@
|
|
|
1
|
+
# `<CatalogPicker>` — searchable card picker for a catalog
|
|
2
|
+
|
|
3
|
+
A search box, optional category pills, a live result count and an auto-fill grid of cards, for choosing one item from a catalog (e.g. "Add widget"). It is a surface, not an overlay: drop it into `Drawer.Body` or `Modal.Body`. Selecting is an action (`onSelect(id)`), not a value; the app decides whether to add immediately or open a configure step.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { Badge, CatalogPicker, Drawer, WidgetPreview } from '@eocrm/design-system';
|
|
7
|
+
|
|
8
|
+
<Drawer open={open} onOpenChange={setOpen}>
|
|
9
|
+
<Drawer.Header>Add widget</Drawer.Header>
|
|
10
|
+
<Drawer.Body>
|
|
11
|
+
<CatalogPicker
|
|
12
|
+
label="Widget catalog"
|
|
13
|
+
categories={[
|
|
14
|
+
{ id: 'sales', label: 'Sales' },
|
|
15
|
+
{ id: 'tasks', label: 'Tasks' },
|
|
16
|
+
]}
|
|
17
|
+
items={[
|
|
18
|
+
{
|
|
19
|
+
id: 'open-deals',
|
|
20
|
+
title: 'Open deals',
|
|
21
|
+
description: 'Count of deals in open stages.',
|
|
22
|
+
category: 'sales',
|
|
23
|
+
tags: ['pipeline', 'kpi'],
|
|
24
|
+
badge: <Badge>New</Badge>,
|
|
25
|
+
preview: <WidgetPreview variant="kpi" />,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: 'my-tasks',
|
|
29
|
+
title: 'My tasks',
|
|
30
|
+
category: 'tasks',
|
|
31
|
+
preview: <WidgetPreview variant="list" />,
|
|
32
|
+
disabledReason: 'Already on dashboard',
|
|
33
|
+
},
|
|
34
|
+
]}
|
|
35
|
+
onSelect={(id) => addOrConfigure(id)}
|
|
36
|
+
/>
|
|
37
|
+
</Drawer.Body>
|
|
38
|
+
</Drawer>;
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
<!-- props:start -->
|
|
42
|
+
|
|
43
|
+
## Props
|
|
44
|
+
|
|
45
|
+
<!-- prettier-ignore -->
|
|
46
|
+
| Prop | Type | Required | Description |
|
|
47
|
+
|---|---|---|---|
|
|
48
|
+
| `items` | `CatalogPickerItem[]` | yes | The catalog. Rendered in the given order. |
|
|
49
|
+
| `categories` | `CatalogPickerCategory[]` | no | Category pills (after a leading localized "All"). Omit or pass `[]` for no pills. |
|
|
50
|
+
| `onSelect` | `(id: string) => void` | yes | Called with the item id when an AVAILABLE item is clicked or activated with Enter/Space. |
|
|
51
|
+
| `label` | `string` | yes | Accessible name of the results listbox, e.g. "Widget catalog". Required. |
|
|
52
|
+
| …native | | | plus native `<div>` attributes |
|
|
53
|
+
|
|
54
|
+
<!-- props:end -->
|
|
55
|
+
|
|
56
|
+
- **`onSelect` is an action.** It fires on click, Enter or Space of an available item. Options are `aria-selected="false"` always; nothing stays selected. Close the overlay or open the next step in the handler.
|
|
57
|
+
- **Search** matches a case-insensitive substring of `title`, `description` or any of `tags` (tags are not rendered). Combined with the category pill (AND). Items keep your order.
|
|
58
|
+
- **Categories.** Omit or pass `[]` for no pills; otherwise a localised "All" pill is added first and is the default. Items without a `category` only appear under "All".
|
|
59
|
+
- **Result count** is a `role="status"` element ("N results") that updates as the filter changes. When nothing matches, an `EmptyState` replaces the grid.
|
|
60
|
+
- **Unavailable items** (`disabledReason`): stay visible and focusable, dimmed, `aria-disabled`, reason shown as text and added to the option's description. `onSelect` is not called. Never hide them.
|
|
61
|
+
- **Keyboard** (one tab stop; roving focus inside the grid):
|
|
62
|
+
- `ArrowDown` in the search field focuses the first result.
|
|
63
|
+
- `←` / `→` move by one item (flipped in RTL); `↑` / `↓` move by one row (column count read from the rendered grid); `Home` / `End` jump to first / last.
|
|
64
|
+
- `↑` from the first row returns focus to the search field.
|
|
65
|
+
- `Enter` / `Space` activate (no-op on an unavailable item).
|
|
66
|
+
- Changing the query or category resets the focus target to the first result.
|
|
67
|
+
- **The toolbar is sticky** so search stays reachable while the drawer/modal body scrolls.
|
|
68
|
+
- **Options must not contain interactive content.** Each card is a single `role="option"`; `preview` and `badge` are display-only.
|
|
69
|
+
- Fills its container width; the grid fills the row with as many columns as fit (about 2 in a Drawer, 3 to 4 in a Modal).
|
|
70
|
+
|
|
71
|
+
#### When NOT to use
|
|
72
|
+
|
|
73
|
+
- ❌ A persistent form value (a field that stores a choice) → `<Select>` / `<IconPicker>`.
|
|
74
|
+
- ❌ Filtering a list or table → `<OptionsPicker>` / `<FilterChip>`.
|
|
75
|
+
- ❌ Picking emoji → `<EmojiPicker>`.
|
|
76
|
+
- ❌ Multi-select. It selects one item per activation.
|
|
77
|
+
|
|
78
|
+
#### Anti-patterns
|
|
79
|
+
|
|
80
|
+
- ❌ Hiding unavailable items instead of passing `disabledReason`. Users then cannot tell why a widget is missing.
|
|
81
|
+
- ❌ Putting buttons or links inside `preview` / `badge`. Options must not contain interactive content; nested controls are unreachable and invalid ARIA.
|
|
82
|
+
- ❌ Building overlay behaviour into the picker — put it in `Drawer.Body` / `Modal.Body`.
|
|
83
|
+
- ❌ Passing `label` as a placeholder-style hint. It is the listbox's accessible name ("Widget catalog").
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# `<Chart>` — time-series chart for a dashboard widget
|
|
2
|
+
|
|
3
|
+
Line, bar, stacked bar or area chart over app-bucketed categories. Fills a `DashboardWidget variant="chart"` body, reflows to any cell size, and ships its own legend, tooltip, keyboard inspection, data table and empty state. The app owns data, formatting, loading and errors.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { Chart, DashboardWidget, ErrorState } from '@eocrm/design-system';
|
|
7
|
+
|
|
8
|
+
<DashboardWidget variant="chart" title="Deals won" loading={isLoading}>
|
|
9
|
+
{error ? (
|
|
10
|
+
<ErrorState title="Could not load" headingLevel={4} />
|
|
11
|
+
) : (
|
|
12
|
+
<Chart
|
|
13
|
+
type="line"
|
|
14
|
+
label="Deals won per week"
|
|
15
|
+
categories={['W1', 'W2', 'W3', 'W4']}
|
|
16
|
+
series={[
|
|
17
|
+
{ key: 'won', label: 'Deals won', values: [3, 5, null, 8] },
|
|
18
|
+
{ key: 'won-prev', label: 'Previous period', values: [2, 4, 6, 5], comparisonOf: 'won' },
|
|
19
|
+
]}
|
|
20
|
+
formatValue={(n) => numberFormat.format(n)}
|
|
21
|
+
/>
|
|
22
|
+
)}
|
|
23
|
+
</DashboardWidget>;
|
|
24
|
+
|
|
25
|
+
// Stacked, with the previous period as a total line
|
|
26
|
+
<Chart
|
|
27
|
+
type="stacked-bar"
|
|
28
|
+
label="Deals won by owner"
|
|
29
|
+
categories={weeks}
|
|
30
|
+
series={[
|
|
31
|
+
...owners.map((o) => ({ key: o.id, label: o.name, values: o.values })),
|
|
32
|
+
{ key: 'prev', label: 'Previous period', values: prevTotals, comparisonOf: 'total' },
|
|
33
|
+
]}
|
|
34
|
+
formatValue={formatCurrency}
|
|
35
|
+
/>;
|
|
36
|
+
|
|
37
|
+
// Markers: an event line, a campaign band, weekends as subtle decorative bands
|
|
38
|
+
<Chart
|
|
39
|
+
type="line"
|
|
40
|
+
label="Calls per day"
|
|
41
|
+
categories={days}
|
|
42
|
+
series={calls}
|
|
43
|
+
formatValue={formatCount}
|
|
44
|
+
markers={[
|
|
45
|
+
{ key: 'launch', label: 'Launch', at: '10 Jul' },
|
|
46
|
+
{ key: 'promo', label: 'Summer promo', from: '21 Jul', to: '30 Jul' },
|
|
47
|
+
...weekends.map(([sat, sun]) => ({ key: sat, from: sat, to: sun, tone: 'subtle' as const })),
|
|
48
|
+
]}
|
|
49
|
+
/>;
|
|
50
|
+
|
|
51
|
+
// Footnote: a short note under the plot; the plot fills the rest of the cell
|
|
52
|
+
<DashboardWidget variant="chart" title="Revenue">
|
|
53
|
+
<Chart
|
|
54
|
+
type="line"
|
|
55
|
+
label="Revenue per month"
|
|
56
|
+
categories={months}
|
|
57
|
+
series={revenue}
|
|
58
|
+
formatValue={formatCurrency}
|
|
59
|
+
footnote={`${excluded} deals in other currencies not included`}
|
|
60
|
+
/>
|
|
61
|
+
</DashboardWidget>;
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
<!-- props:start -->
|
|
65
|
+
|
|
66
|
+
## Props
|
|
67
|
+
|
|
68
|
+
<!-- prettier-ignore -->
|
|
69
|
+
| Prop | Type | Required | Description |
|
|
70
|
+
|---|---|---|---|
|
|
71
|
+
| `type` | `'line' \| 'bar' \| 'stacked-bar' \| 'area'` | yes | Chart form. Required. - `'line'` — one smoothed line per series; trends over time. Comparison series dashed. - `'bar'` — grouped bars per category; compare a few series per bucket. Comparison bars outlined + light fill. - `'stacked-bar'` — bars stacked per category; parts of a total. Comparison only as `comparisonOf: 'total'` (dashed total line). - `'area'` — smoothed stacked areas; parts of a total over time. Same comparison rule as `stacked-bar`. |
|
|
72
|
+
| `label` | `string` | yes | Accessible name of the figure and caption of its data table, e.g. "Deals won per week". Required. |
|
|
73
|
+
| `categories` | `string[]` | yes | X-axis buckets, already formatted by the app ("12 Jan", "W3", "Mar"). `series[].values[i]` belongs to `categories[i]`. |
|
|
74
|
+
| `series` | `ChartSeries[]` | yes | Series to plot, in a stable order: colour follows position (non-comparison series 1–8 get palette slots 1–8), so keep the order fixed when filtering. At most 8 primary series — fold the rest into an "Other" series in the app (series 9+ still render, dashed/outlined, with a dev warning). `values` shorter than `categories` are padded with gaps; `null` is a gap (lines break, bars are absent). `comparisonOf` marks a de-emphasised comparison (e.g. previous period): a series key (line / bar) or `'total'` (stacked-bar / area). |
|
|
75
|
+
| `formatValue` | `(n: number) => string` | yes | Formats a number for axis ticks, tooltip and data table (the app owns i18n and currency). Required. |
|
|
76
|
+
| `emptyMessage` | `ReactNode` | no | EmptyState title when there are no categories or every value is null/zero. Default: translated "No data for this period". |
|
|
77
|
+
| `hiddenSeries` | `string[]` | no | Controlled list of hidden series keys (legend toggles). Omit for uncontrolled. Unknown keys are ignored. If it would hide every primary series, it is ignored for primaries (the last primary can't be hidden). |
|
|
78
|
+
| `onHiddenSeriesChange` | `((keys: string[]) => void)` | no | Called with the next hidden keys when the user toggles a legend item. |
|
|
79
|
+
| `markers` | `ChartMarker[]` | no | Static annotations pinned to `categories`: `{ key, label?, at }` draws a vertical line at one category, `{ key, label?, from, to }` shades a category range (inclusive), e.g. a campaign or weekends. `tone`: `'neutral'` for events (the tone default) or `'subtle'` for recurring context. Labelled markers are also listed in the tooltip, the keyboard announcement and the data table; unlabelled ones are decorative. A marker naming an unknown category is ignored with a dev warning. |
|
|
80
|
+
| `footnote` | `ReactNode` | no | Short note under the plot, e.g. "3 deals in other currencies not included" or why a comparison was omitted. Takes its own row (wraps, never truncates) and the plot fills the rest; also describes the plot for screen readers. Shown in the empty state too, where it often explains the emptiness. |
|
|
81
|
+
| …native | | | plus native HTML attributes |
|
|
82
|
+
|
|
83
|
+
<!-- props:end -->
|
|
84
|
+
|
|
85
|
+
- **Pre-bucketed data.** `categories` are x labels the app already formatted; `series[].values[i]` belongs to `categories[i]`. Shorter `values` are padded with gaps, extras ignored. `null` and non-finite values (NaN, Infinity) are gaps: lines and areas break, bars are absent.
|
|
86
|
+
- **Types.** `line` (trend), `bar` (grouped per bucket), `stacked-bar` (parts of a total), `area` (always stacked; overlapping areas hide each other).
|
|
87
|
+
- **Smoothing.** Every line (comparisons and the `stacked-bar` total included) and `area` is drawn with a monotone curve: it passes through every value and never leaves the range of the two values it joins, so there is no dip before a step, no bump after it, no peak above a real maximum and no negative between counts. Peaks, troughs and flat runs ease in flat. Two-point runs are straight. Stacked areas are the exception: each layer stays within its own values, but where layers trade places the stacked top can rise slightly above the largest real total; the y axis grows to keep it inside the plot. Stacked areas smooth each series on its own and stack the curves, so a steady layer stays steady whatever its neighbours do, a series that is 0 has no thickness, and with non-negative data no layer turns inside out or crosses its neighbour. A `'total'` comparison equal to the stack stays close to its top but is smoothed on its own, so next to a spike or a series' zero it can drift up to about a tenth of the plot height (typically 2–4%). Markers and the tooltip sit on the real points; the data table has the exact values. Not configurable.
|
|
88
|
+
- **Y axis** always includes 0, with rounded ticks formatted by `formatValue`. In `bar` and `line`, negative values grow down from 0; `stacked-bar` diverges around 0 (positives up, negatives down); `area` stacks without a diverging offset, so negatives accumulate.
|
|
89
|
+
- **Colour** follows a series' position among non-comparison series: 1–8 get the 8 validated palette colours (light + dark). Keep the order stable so filtering never repaints survivors.
|
|
90
|
+
- **Comparison series** (`comparisonOf`): in `line` / `bar`, name the parent's key — it inherits the parent's colour, dashed (line) or outlined with a light fill (bar), sits after its parent in legend and tooltip, and hides with it. In `stacked-bar` / `area`, use `comparisonOf: 'total'` — a dashed neutral line of the previous period's total. Mismatches are ignored with a dev warning.
|
|
91
|
+
- **Markers** (`markers`) are static annotations pinned to `categories`: `{ key, label?, at }` draws a dashed vertical line at the centre of one category; `{ key, label?, from, to }` shades whole buckets from `from` to `to` (inclusive, either order). Positions snap to buckets: there is no sub-bucket time. `tone: 'neutral'` (default) for events, `'subtle'` for recurring context such as weekends (one marker per weekend). Bands sit behind the grid and data, lines behind the data. Labels are drawn at the top of the plot when they fit (earlier markers win a collision; hidden when the chart is compact) and are always listed in the tooltip, the keyboard announcement and a "Markers" column of the data table. Unlabelled markers are decorative. A marker naming an unknown category is ignored with a dev warning. Building markers with `.map` inside an array literal widens `tone` to `string`: write `'subtle' as const`. Don't encode status with markers (no red "incident" tone): put the meaning in the label.
|
|
92
|
+
- **Footnote** (`footnote`) is a short muted note in its own row under the plot, e.g. excluded currencies or why a comparison was dropped. It wraps (never truncates) and the plot shrinks to make room, so keep it to a line or two. It is also the plot's accessible description (after the key hint), and stays visible in the empty state, where it often explains why there is no data. Don't use it for errors (use `ErrorState`) or for legends.
|
|
93
|
+
- **Legend** appears with more than one series. Hover or focus an item to highlight its series; click to hide/show it (`aria-pressed`). The last visible primary series can't be hidden: its button is `aria-disabled` but stays focusable. A comparison's button is disabled while its parent is hidden. Control with `hiddenSeries` / `onHiddenSeriesChange` to persist per widget. Below 120px height the legend shrinks to one row with a "+N" counter; overflow items are not rendered, so they are not keyboard-reachable (the data table still lists every series) — except hidden series, which always render so they can be re-shown (the "+N" counts only the remaining visible ones).
|
|
94
|
+
- **Tooltip** on hover or tap shows every visible series at that bucket. On touch, tapping the plot shows it and it stays until you tap outside the plot. **Keyboard:** the plot is one tab stop (named by `label`, with the key hint as its description); ←/→ move, Home/End jump, Esc closes. Screen-reader users in browse mode never receive the arrow keys on the plot — they should read the data table instead. Values are announced politely only while a keyboard user inspects — never on load, so a dashboard of charts stays quiet.
|
|
95
|
+
- **Screen readers** (browse mode: use the table, not the plot's arrow keys) get a visually hidden data table (caption = `label`) with exactly the plotted values (a null cell reads "No data"); the SVG is hidden from them. Identity never relies on colour alone: dashes for comparisons, marker shapes per series, labels everywhere.
|
|
96
|
+
- **Empty** (no categories, or every value null/0) renders `EmptyState` with `emptyMessage` (default "No data for this period"; an empty string falls back to the default).
|
|
97
|
+
- **Sizing.** Fills its parent (`width/height: 100%`) and redraws on resize. The parent must have a definite height (e.g. a `DashboardWidget` chart body); in an auto-height parent the plot measures 0 and only the legend renders. X labels never overlap: end labels are clamped inside the chart and the last label is dropped if it would collide. It never scrolls horizontally.
|
|
98
|
+
- **Performance.** Layout is recomputed when the `formatValue`, `categories` or `series` identities change. Memoise them (`useCallback` / `useMemo`) when the parent re-renders often.
|
|
99
|
+
|
|
100
|
+
#### When NOT to use
|
|
101
|
+
|
|
102
|
+
- ❌ One headline number → `DashboardWidget variant="kpi"` or `StatGroup`.
|
|
103
|
+
- ❌ More than 8 groups at once → fold the tail into an "Other" series in the app (the DS can't know whether your metric is additive).
|
|
104
|
+
- ❌ Two measures with different units → two charts. There is no dual axis.
|
|
105
|
+
- ❌ Non-time categorical comparisons that need sorting, pies, scatter or zoom — not supported.
|
|
106
|
+
|
|
107
|
+
#### Anti-patterns
|
|
108
|
+
|
|
109
|
+
- ❌ Wrapping `Chart` in a fixed-height or scrolling div inside the widget body. The cell is the size.
|
|
110
|
+
- ❌ Rendering a note next to `Chart` in the widget body (it steals the plot's height unpredictably). Use `footnote`.
|
|
111
|
+
- ❌ Passing `loading` / error UI into `Chart`. Use `DashboardWidget loading` and an `ErrorState` child.
|
|
112
|
+
- ❌ Reordering `series` by value each refresh — colours jump between entities.
|
|
113
|
+
- ❌ Formatting inside `categories` with the value (`"W1: 3"`); values go in `series`, labels in `categories`.
|
|
114
|
+
- ❌ Comparison series on 8 groups (16 marks): compare only ungrouped or with ≤ 4 groups.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# `<ChatBubble>` — one chat message, plus `ChatBubble.List` (the `role="log"` container)
|
|
2
|
+
|
|
3
|
+
A message bubble aligned by speaker: `from="member"` (the signed-in user, end side, accent tint) or `from="assistant"` (start side, neutral). Always put bubbles inside `<ChatBubble.List aria-label="…">`: it is a `role="log"` + `aria-live="polite"` region, so a screen reader announces each new message, with the speaker, as it arrives.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { Button, ChatBubble } from '@eocrm/design-system';
|
|
7
|
+
|
|
8
|
+
<ChatBubble.List aria-label="Conversation with Scout">
|
|
9
|
+
<ChatBubble from="member">Which deals are at risk?</ChatBubble>
|
|
10
|
+
<ChatBubble from="assistant" footer={<Button size="xs">Helpful</Button>}>
|
|
11
|
+
Three deals have had no activity for 14 days.
|
|
12
|
+
</ChatBubble>
|
|
13
|
+
{waiting && <ChatBubble from="assistant" pending />}
|
|
14
|
+
</ChatBubble.List>;
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
<!-- props:start -->
|
|
18
|
+
|
|
19
|
+
## Props
|
|
20
|
+
|
|
21
|
+
### `ChatBubbleProps`
|
|
22
|
+
|
|
23
|
+
<!-- prettier-ignore -->
|
|
24
|
+
| Prop | Type | Required | Description |
|
|
25
|
+
|---|---|---|---|
|
|
26
|
+
| `from` | `'member' \| 'assistant'` | yes | Speaker. `member` = the user (accent tint, end side); `assistant` = the AI (neutral, start side). |
|
|
27
|
+
| `children` | `ReactNode` | no | Message content. Ignored while `pending`. |
|
|
28
|
+
| `footer` | `ReactNode` | no | Slot below the coloured body, same side (feedback buttons, sources). |
|
|
29
|
+
| `pending` | `boolean` | no | Show a "typing" indicator instead of `children`; announced once when inserted into a `ChatBubble.List`. Intended for `from="assistant"`. Default: `false`. |
|
|
30
|
+
| …native | | | plus native `<div>` attributes |
|
|
31
|
+
|
|
32
|
+
### `ChatBubbleListProps`
|
|
33
|
+
|
|
34
|
+
<!-- prettier-ignore -->
|
|
35
|
+
| Prop | Type | Required | Description |
|
|
36
|
+
|---|---|---|---|
|
|
37
|
+
| `children` | `ReactNode` | yes | The `ChatBubble`s of the conversation, oldest first. Pass an `aria-label` naming the conversation. |
|
|
38
|
+
| …native | | | plus native `<div>` attributes |
|
|
39
|
+
|
|
40
|
+
<!-- props:end -->
|
|
41
|
+
|
|
42
|
+
- **The speaker is spoken.** Every bubble starts with a visually hidden, localised "You said: " / "Assistant said: " (`chatBubble.*` i18n keys, no label props).
|
|
43
|
+
- **`pending`** shows animated dots (static under `prefers-reduced-motion`) and a hidden "Assistant is typing…"; `children` are ignored. Inserted into the `role="log"` list it is announced once, and the real reply is announced when it replaces it. `aria-busy` is not used. It carries no "said" prefix; the typing text names the speaker.
|
|
44
|
+
- **`footer`** renders below the coloured body, same side, outside the tint.
|
|
45
|
+
- `ChatBubble.List` spreads props first: `role` and `aria-live` always win. Gap between bubbles is `--chat-bubble-list-gap`.
|
|
46
|
+
- Bubble width is capped at `--chat-bubble-max-width` (80%). The root is a full-width row; alignment is `justify-content`, so the parent never needs to align it.
|
|
47
|
+
|
|
48
|
+
**When NOT to use:** threaded/commented records (use `Thread`), toasts or banners (use `toast` / `Banner`), a bubble outside a `ChatBubble.List` for a live conversation (nothing will be announced), rich per-message layout (the body is plain content; compose it inside `children`).
|
|
49
|
+
|
|
50
|
+
**Anti-patterns**
|
|
51
|
+
|
|
52
|
+
- A `ChatBubble.List` without `aria-label`.
|
|
53
|
+
- Overriding `role` on `ChatBubble.List`: ignored — it is always `role="log"`. Don't wrap the bubbles in your own `role="list"`/`listitem` markup either: a list inside the log adds noise ("list, 3 items") to every announcement.
|
|
54
|
+
- Setting `aria-busy` or building your own typing indicator: use `pending`.
|
|
55
|
+
- Re-adding a "You:" / "Assistant:" prefix in `children`: it would be read twice.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# `<ChatLauncher>` — floating launcher + non-modal chat panel
|
|
2
|
+
|
|
3
|
+
A fixed bottom-end icon button that toggles a chat panel above it. The panel is a `role="dialog"` **without** `aria-modal`: no focus trap, backdrop or scroll lock, so the page stays usable while chatting. It is portaled to `document.body` and layered on `--z-chat-launcher` (above app chrome, below dropdowns, popovers and modals). Controlled only: `open` + `onOpenChange`.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { useRef, useState } from 'react';
|
|
7
|
+
import { Sparkles, Send } from 'lucide-react';
|
|
8
|
+
import {
|
|
9
|
+
Button,
|
|
10
|
+
ChatBubble,
|
|
11
|
+
ChatLauncher,
|
|
12
|
+
Cluster,
|
|
13
|
+
Constrain,
|
|
14
|
+
Textarea,
|
|
15
|
+
} from '@eocrm/design-system';
|
|
16
|
+
|
|
17
|
+
const [open, setOpen] = useState(false);
|
|
18
|
+
const composerRef = useRef<HTMLTextAreaElement>(null);
|
|
19
|
+
|
|
20
|
+
<ChatLauncher
|
|
21
|
+
label="Ask Scout"
|
|
22
|
+
title="Scout"
|
|
23
|
+
icon={<Sparkles size={20} aria-hidden="true" />}
|
|
24
|
+
open={open}
|
|
25
|
+
onOpenChange={setOpen}
|
|
26
|
+
initialFocusRef={composerRef}
|
|
27
|
+
footer={
|
|
28
|
+
// One row: the Textarea grows (autoGrow, up to 4 rows), the send button keeps its size.
|
|
29
|
+
<Cluster wrap={false} gap="sm" align="end">
|
|
30
|
+
<Constrain flex="grow">
|
|
31
|
+
<Textarea
|
|
32
|
+
ref={composerRef}
|
|
33
|
+
aria-label="Message"
|
|
34
|
+
minRows={1}
|
|
35
|
+
maxRows={4}
|
|
36
|
+
onKeyDown={(e) => {
|
|
37
|
+
// Enter sends, Shift+Enter is a newline; never send mid-IME-composition.
|
|
38
|
+
if (e.key === 'Enter' && !e.shiftKey && !e.nativeEvent.isComposing) {
|
|
39
|
+
e.preventDefault(); // no stray newline after the draft is cleared
|
|
40
|
+
send(e);
|
|
41
|
+
}
|
|
42
|
+
}}
|
|
43
|
+
/>
|
|
44
|
+
</Constrain>
|
|
45
|
+
<Button variant="primary" iconOnly aria-label="Send" onClick={send}>
|
|
46
|
+
<Send size={16} aria-hidden="true" />
|
|
47
|
+
</Button>
|
|
48
|
+
</Cluster>
|
|
49
|
+
}
|
|
50
|
+
>
|
|
51
|
+
<ChatBubble.List aria-label="Conversation with Scout">
|
|
52
|
+
{messages.map((m) => (
|
|
53
|
+
<ChatBubble key={m.id} from={m.from}>
|
|
54
|
+
{m.text}
|
|
55
|
+
</ChatBubble>
|
|
56
|
+
))}
|
|
57
|
+
{waiting && <ChatBubble from="assistant" pending />}
|
|
58
|
+
</ChatBubble.List>
|
|
59
|
+
</ChatLauncher>;
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
<!-- props:start -->
|
|
63
|
+
|
|
64
|
+
## Props
|
|
65
|
+
|
|
66
|
+
<!-- prettier-ignore -->
|
|
67
|
+
| Prop | Type | Required | Description |
|
|
68
|
+
|---|---|---|---|
|
|
69
|
+
| `label` | `string` | yes | Accessible name of the launcher button — names the assistant ("Ask Scout"). |
|
|
70
|
+
| `open` | `boolean` | yes | Whether the panel is open. Controlled only: pair with `onOpenChange`. |
|
|
71
|
+
| `onOpenChange` | `(open: boolean) => void` | yes | Called with the requested state: launcher toggle, close button, Escape. |
|
|
72
|
+
| `icon` | `ReactNode` | yes | Launcher glyph (e.g. a lucide icon with `aria-hidden`). |
|
|
73
|
+
| `title` | `ReactNode` | yes | Panel header text; also the dialog's accessible name. |
|
|
74
|
+
| `children` | `ReactNode` | yes | Scrolling body — usually a `<ChatBubble.List>`. Stays mounted while closed. |
|
|
75
|
+
| `footer` | `ReactNode` | no | Pinned area below the body — the composer (`Textarea` + send `Button`). |
|
|
76
|
+
| `initialFocusRef` | `RefObject<HTMLElement \| null>` | no | Element focused when the panel opens (point it at the composer). Default: the panel itself. Focus already inside the panel is not moved. |
|
|
77
|
+
| …native | | | plus native `<div>` attributes |
|
|
78
|
+
|
|
79
|
+
<!-- props:end -->
|
|
80
|
+
|
|
81
|
+
- **Stays mounted when closed.** The panel gets `hidden`; children are not unmounted, so an uncontrolled draft and the scroll position survive close/reopen.
|
|
82
|
+
- **Sticks to the bottom.** While open, the body follows new content if the user was already at (within 24px of) the bottom; a user scrolled up to read is not moved. Reopening scrolls to the bottom if it was at the bottom when closed (replies that arrived meanwhile are in view), otherwise restores the saved position.
|
|
83
|
+
- **Focus.** Opening moves focus to `initialFocusRef` (point it at the composer), else to the panel; focus already inside is left alone. Escape (inside the panel or on the launcher) and the close button close it and return focus to the launcher. A programmatic close returns focus only if it was inside the chat. An Escape a child already handled (`event.preventDefault()`, e.g. a menu closing itself) is ignored.
|
|
84
|
+
- The close button's name is the `chatLauncher.close` i18n key (no label prop). `label` names your assistant and is the launcher's accessible name.
|
|
85
|
+
- `ref`, `className`, `id` and other native attributes go to the panel; `role`, `hidden`, `aria-labelledby` and `tabIndex` always win.
|
|
86
|
+
- **Closed means silent.** While closed, the `role="log"` sits in a `hidden` subtree, so replies that arrive then are not announced (and there is no unread badge). On reopen they are in view at the bottom if the chat was left scrolled to the bottom; otherwise the saved scroll position is restored. If a reply must reach the user while the chat is closed, announce it yourself (e.g. a `toast` — with the viewport at a top position, see below).
|
|
87
|
+
- **Toasts share the corner.** `ToastViewport` defaults to `bottom-right`, above the launcher's layer, so toasts would cover the launcher and the open panel's composer. On a page with a ChatLauncher, use `<ToastViewport position="top-right" />` (or another top position).
|
|
88
|
+
- Size: `--chat-launcher-panel-width` (380px) × `--chat-launcher-panel-height` (560px), capped to the viewport minus `--chat-launcher-inset`.
|
|
89
|
+
|
|
90
|
+
**When NOT to use:** page-level dialogs or forms that must block the page (use `Modal` / `Drawer`); content anchored to a trigger in the page (use `Popover`); notifications (use `toast`); more than one launcher per page.
|
|
91
|
+
|
|
92
|
+
**Anti-patterns**
|
|
93
|
+
|
|
94
|
+
- Unmounting `<ChatLauncher>` when closed: you lose the draft and scroll position the component keeps for you.
|
|
95
|
+
- Positioning it yourself (wrapping it in a fixed container, adding `className` with `position`/`bottom`): it already owns the viewport corner.
|
|
96
|
+
- Putting the composer in `children`: it would scroll away. Use `footer`.
|
|
97
|
+
- Bubbles without `ChatBubble.List`: new replies won't be announced.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# `<Checkbox>` — checkbox with native input + custom paint
|
|
2
|
+
|
|
3
|
+
```tsx
|
|
4
|
+
<Checkbox label="I agree" />
|
|
5
|
+
<Checkbox checked={agreed} onChange={setAgreed} label="Subscribe" />
|
|
6
|
+
<Checkbox indeterminate checked={allSelected} onChange={selectAll} aria-label="Select all" />
|
|
7
|
+
|
|
8
|
+
// "Select all" pattern
|
|
9
|
+
<Checkbox
|
|
10
|
+
checked={allSelected}
|
|
11
|
+
indeterminate={someSelected && !allSelected}
|
|
12
|
+
onChange={(next) => (next ? selectAll() : selectNone())}
|
|
13
|
+
aria-label="Select all rows"
|
|
14
|
+
/>
|
|
15
|
+
|
|
16
|
+
// Icon-only (no visible label)
|
|
17
|
+
<Checkbox aria-label="Select row" checked={isSelected} onChange={setIsSelected} />
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
<!-- props:start -->
|
|
21
|
+
|
|
22
|
+
## Props
|
|
23
|
+
|
|
24
|
+
<!-- prettier-ignore -->
|
|
25
|
+
| Prop | Type | Required | Description |
|
|
26
|
+
|---|---|---|---|
|
|
27
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | no | Box diameter + label type scale. Defaults to `'md'`. - `'sm'` — 14px box, font-size-sm label. Dense tables, inline filters. - `'md'` — 16px box, font-size-md label. Default. - `'lg'` — 20px box, font-size-lg label. Hero forms, mobile-friendly. Note: shadows the native HTML `<input size>` attribute (which on checkboxes is meaningless anyway). |
|
|
28
|
+
| `checked` | `boolean` | no | Controlled checked state. Pair with `onChange`. Omit (with optional `defaultChecked`) for uncontrolled use. |
|
|
29
|
+
| `defaultChecked` | `boolean` | no | Initial checked state for uncontrolled use. Defaults to `false`. |
|
|
30
|
+
| `indeterminate` | `boolean` | no | Indeterminate (mixed) visual + a11y state. Independent of `checked` — the box paints with a dash icon and `input.indeterminate = true` so AT announces "mixed". Consumer drives this based on partial selection (e.g., a "select all" header where some-but-not-all rows are selected). When the user clicks an indeterminate checkbox, the native change event fires with the next `checked` value (`true` if it was `false`). The consumer typically responds by clearing `indeterminate`. |
|
|
31
|
+
| `label` | `ReactNode` | no | Optional label rendered next to the box. The whole `<label>` is the click target. Omit for icon-only checkboxes (e.g., a DataTable row selector) — pass `aria-label` instead. |
|
|
32
|
+
| `invalid` | `boolean` | no | Toggles the error visual + sets `aria-invalid="true"`. Pair with a visible error message and `aria-describedby`. |
|
|
33
|
+
| `color` | `PaletteColor` | no | Optional palette color for the checked / indeterminate fill. When set, the filled state uses the palette color's fg token instead of `--color-accent`. Use to color-tag checkbox groups (per-team, per-status, per-category). Default unchanged (accent blue). The focus ring, hover border, and unchecked state remain accent-colored regardless of `color` — only the checked / indeterminate fill is affected. |
|
|
34
|
+
| `onChange` | `((checked: boolean, event: ChangeEvent<HTMLInputElement, Element>) => void)` | no | Fires on every change. Receives the next checked state AND the native event so consumers can do `event.preventDefault()`, read modifier keys, etc. |
|
|
35
|
+
| …native | | | plus native `<input>` attributes |
|
|
36
|
+
|
|
37
|
+
<!-- props:end -->
|
|
38
|
+
|
|
39
|
+
- Native `<input type='checkbox'>` is visually hidden but stays in tab order + AT tree — keyboard, screen reader, form submission, RHF/Zod, autofill all work for free.
|
|
40
|
+
- Native HTML attrs flow through (`name`, `value`, `required`, `form`, `autoFocus`, etc.). `FormData.getAll(name)` returns the array of checked values for same-`name` checkboxes.
|
|
41
|
+
- forwardRef points at the native `<input>` so consumers can `.focus()` or programmatically set `.indeterminate`.
|
|
42
|
+
|
|
43
|
+
- The native input owns all a11y (keyboard, screen reader, form submission); the custom paint owns the look. States: checked / unchecked / indeterminate / disabled / invalid. `aria-disabled="true"` gets the disabled look and ignores clicks/Space but stays focusable — use it for a busy state (a write in flight) where real `disabled` would drop keyboard focus.
|
|
44
|
+
|
|
45
|
+
#### When NOT to use
|
|
46
|
+
|
|
47
|
+
- ❌ A single binary on/off setting that applies immediately → `Switch`.
|
|
48
|
+
- ❌ One-of-many choice from a fixed set → `Radio`.
|
|
49
|
+
- ❌ Multi-select from a long list → `<Select multi>`.
|
|
50
|
+
|
|
51
|
+
#### Anti-patterns
|
|
52
|
+
|
|
53
|
+
- ❌ Treating `indeterminate` as a third value. It is a display flag; `checked` is still the underlying boolean.
|
|
54
|
+
- ❌ Wrapping the checkbox in your own `<label>`. It is already wrapped; an outer `<label>` nests two and breaks the click contract.
|
|
55
|
+
- ❌ Omitting `label` AND `aria-label`. Screen readers announce just "checkbox" with no context.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# `<CircularProgress>` — circular progress / spinner
|
|
2
|
+
|
|
3
|
+
```tsx
|
|
4
|
+
<CircularProgress value={45} /> // donut, 32px default
|
|
5
|
+
<CircularProgress value={75} label /> // centered "75%"
|
|
6
|
+
<CircularProgress /> // indeterminate spinner (the "Loader" use case)
|
|
7
|
+
<CircularProgress size="sm" /> // 16px inline spinner next to a button
|
|
8
|
+
<CircularProgress tone="success" value={100} /> // green full circle
|
|
9
|
+
<CircularProgress size="lg" value={80} tone="success" label /> // stat-card-style donut
|
|
10
|
+
<CircularProgress value={3} max={10} label={`3 / 10`} /> // custom label ("n of N", a status word)
|
|
11
|
+
|
|
12
|
+
// Canonical inline loader next to a button
|
|
13
|
+
<Cluster gap="sm">
|
|
14
|
+
<Button>Save</Button>
|
|
15
|
+
<CircularProgress size="sm" aria-label="Saving" />
|
|
16
|
+
</Cluster>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
<!-- props:start -->
|
|
20
|
+
|
|
21
|
+
## Props
|
|
22
|
+
|
|
23
|
+
<!-- prettier-ignore -->
|
|
24
|
+
| Prop | Type | Required | Description |
|
|
25
|
+
|---|---|---|---|
|
|
26
|
+
| `value` | `number` | no | Current progress value. Omit (or pass `undefined`) to render the indeterminate spinning animation. Two-channel behavior for out-of-range and degenerate values: - **Visual arc** is clamped to [0%, 100%]. Values outside [0, max] render at the nearest valid bound. - **ARIA `aria-valuenow`** reports the raw number so consumer bugs (a value drifting past max) surface in audits and SR announcements. Degenerate inputs fall back to the indeterminate spinner: `NaN`, `Infinity`, `-Infinity`, and the case where `max <= 0`. This is the common file-upload race condition (`bytes_uploaded / total_bytes` before `total_bytes` is known produces NaN). |
|
|
27
|
+
| `max` | `number` | no | Upper bound. Defaults to `100`. Consumers using fraction values (0.0–1.0) pass `max={1}`. Consumers tracking a count ("3 of 10") pass `max={10}`. |
|
|
28
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | no | Diameter + stroke pairing. - `sm` — 16px diameter, 2px stroke (inline next to a button — also the shape to use for "Saving…" loading affordances; pass no `value` for the indeterminate spinner.) - `md` — 32px diameter, 3px stroke (default — near a heading) - `lg` — 56px diameter, 4px stroke (page-level loader) |
|
|
29
|
+
| `tone` | `'default' \| 'success' \| 'warning' \| 'danger'` | no | Stroke color tone. Defaults to `'default'` (accent blue). Same vocab as `<Progress>`. Indeterminate ignores tone — always accent. |
|
|
30
|
+
| `label` | `CircularProgressLabel` | no | Optional centered label. - `false` (default) — no label - `true` — render `{Math.round(percent)}%` centered. Auto-suppressed at `size='sm'` (16px circle has no room for text) AND when indeterminate. - `ReactNode` — render the node centered in BOTH modes. Still auto-suppressed at `size='sm'` regardless — the geometry doesn't change. |
|
|
31
|
+
| …native | | | plus native `<div>` attributes |
|
|
32
|
+
|
|
33
|
+
<!-- props:end -->
|
|
34
|
+
|
|
35
|
+
- Same prop vocabulary as `<Progress>` — `value?`, `max?`, `size`, `tone`, `label`.
|
|
36
|
+
- Same NaN/Infinity/max<=0 indeterminate fallback as `<Progress>`.
|
|
37
|
+
- Built as inline `<svg viewBox="0 0 36 36">` with two `<circle>` elements (track + fill). Determinate arc is driven by `stroke-dashoffset`; indeterminate is a CSS `rotate` animation on a partial arc.
|
|
38
|
+
- Centered `label` auto-suppressed at `size="sm"` (no room for text) AND when `label=true` on indeterminate. ReactNode labels still suppress at `size="sm"` regardless.
|
|
39
|
+
- `prefers-reduced-motion` disables the spin animation and shows a static accent ring.
|
|
40
|
+
|
|
41
|
+
- `role="progressbar"` is locked (can't be overridden via the `role` prop) and is announced to screen readers. Tracks known progress like `<Progress>` in a circular geometry, better for inline loading indicators and tight spaces; indeterminate mode is the canonical spinner.
|
|
42
|
+
|
|
43
|
+
#### When NOT to use
|
|
44
|
+
|
|
45
|
+
- ❌ Horizontal progress next to row content → `<Progress>` linear.
|
|
46
|
+
- ❌ Replacing `<Skeleton>` for loading placeholders. Skeleton implies "structure on its way"; CircularProgress implies "I'm working on it."
|
|
47
|
+
- ❌ A decorative icon: the `progressbar` role is announced.
|
|
48
|
+
|
|
49
|
+
#### Anti-patterns
|
|
50
|
+
|
|
51
|
+
- ❌ Hand-rolled spinning `<svg>` per page. `<CircularProgress />` indeterminate is the same visual, accessible and reduced-motion-aware.
|
|
52
|
+
- ❌ `<CircularProgress value={0}>` to render an empty circle. `value={0}` is determinate (0% done); the intent is usually indeterminate, so omit `value`.
|
|
53
|
+
- ❌ `<CircularProgress size="sm" label>` expecting centered text in a 16px circle. The label is auto-suppressed at `sm` by design; use `md` or `lg`.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# `<Cluster>` — horizontal layout that wraps
|
|
2
|
+
|
|
3
|
+
```tsx
|
|
4
|
+
// Form footer:
|
|
5
|
+
<Cluster justify="end" gap="sm">
|
|
6
|
+
<Button variant="secondary">Cancel</Button>
|
|
7
|
+
<Button type="submit">Save</Button>
|
|
8
|
+
</Cluster>
|
|
9
|
+
|
|
10
|
+
// Toolbar (title left, actions right):
|
|
11
|
+
<Cluster justify="between" gap="md">
|
|
12
|
+
<h1>Users</h1>
|
|
13
|
+
<Cluster gap="sm">
|
|
14
|
+
<Button variant="secondary">Filter</Button>
|
|
15
|
+
<Button>Add user</Button>
|
|
16
|
+
</Cluster>
|
|
17
|
+
</Cluster>
|
|
18
|
+
|
|
19
|
+
// Tag list: wraps to multiple lines when narrow
|
|
20
|
+
<Cluster gap="xs">
|
|
21
|
+
{tags.map((t) => <Badge key={t.id} tone={t.tone}>{t.label}</Badge>)}
|
|
22
|
+
</Cluster>
|
|
23
|
+
|
|
24
|
+
// Pill left + two buttons right while they fit; buttons drop under the pill
|
|
25
|
+
// at full width (equal columns) when they don't — no breakpoint to guess:
|
|
26
|
+
<Cluster stretchWrapped gap="sm">
|
|
27
|
+
<Badge tone="info">In review</Badge>
|
|
28
|
+
<Grid columns={2} gap="sm">
|
|
29
|
+
<Button variant="secondary">Reject</Button>
|
|
30
|
+
<Button>Approve</Button>
|
|
31
|
+
</Grid>
|
|
32
|
+
</Cluster>
|
|
33
|
+
|
|
34
|
+
// Inline, inside a <button>/<a>/<label> where a <div> is invalid HTML —
|
|
35
|
+
// as="span" renders inline-flex (e.g. icon + label in a ButtonGroup.Item):
|
|
36
|
+
<ButtonGroup.Item value="list">
|
|
37
|
+
<Cluster as="span" gap="xs" align="center" wrap={false}>
|
|
38
|
+
<List size={14} aria-hidden />
|
|
39
|
+
List
|
|
40
|
+
</Cluster>
|
|
41
|
+
</ButtonGroup.Item>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
<!-- props:start -->
|
|
45
|
+
|
|
46
|
+
## Props
|
|
47
|
+
|
|
48
|
+
<!-- prettier-ignore -->
|
|
49
|
+
| Prop | Type | Required | Description |
|
|
50
|
+
|---|---|---|---|
|
|
51
|
+
| `as` | `'div' \| 'span' \| 'section' \| 'aside'` | no | Element to render. Defaults to `'div'`. - `div` (default) — block-level flex container; right for nearly all uses. - `span` — renders `display: inline-flex`, for phrasing-content contexts where a block element is invalid HTML: inside `<button>` (e.g. a `ButtonGroup.Item` icon + label), `<a>`, or `<label>`. - `section` — only for a genuinely standalone, nameable region; pair with `aria-label`/`aria-labelledby` (an unnamed section is just a div to AT). - `aside` — exposes a `complementary` landmark to screen readers; label it, and never use it for mere visual grouping. `span` is the ONLY value valid inside `<button>`/`<a>`/`<label>` — `section` and `aside` are flow content and remain invalid HTML there. |
|
|
52
|
+
| `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). |
|
|
53
|
+
| `justify` | `'start' \| 'center' \| 'end' \| 'between'` | no | Horizontal distribution. - `start` (default) — items at the start. - `center` — items centered. - `end` — items at the end. Canonical form-footer pattern. - `between` — first item at start, last at end, gap between. Canonical toolbar pattern (title left, actions right). |
|
|
54
|
+
| `align` | `'start' \| 'center' \| 'end' \| 'baseline'` | no | Vertical alignment. - `start` / `center` (default) / `end` / `baseline`. |
|
|
55
|
+
| `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. Reach for it when this Cluster is an item of a **row** flex container (or a grid item) that clips — a `Calendar` `renderEvent` chip, a `Card.Header` row. Without it the flex default (`min-width: auto`) floors the container at its content's min-content width and the ellipsis never appears. That floor applies because a Cluster 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`. That is why a `<Text truncate>` (overflow hidden) never needs this and a Cluster around it does. It is a **no-op** inside a column `Stack` (the automatic minimum size applies only on the flex MAIN axis, so there is no horizontal floor), and inside a plain block or a table cell (the automatic minimum size 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`. |
|
|
56
|
+
| `maxWidthFull` | `boolean` | no | Caps the cluster at its container's width (`max-width: 100%`). Meant for `as="span"` inside running text: an inline cluster otherwise sizes to its content, so a `<Text as="span" truncate>` child with a long title makes the cluster overflow the paragraph instead of ellipsizing at the line edge. Combine with `wrap={false}` + a truncating child. The chip is one inline box: if the text before it leaves too little room on the line, the whole chip wraps to the next line, then ellipsizes at the full paragraph width. Phrasing-safe (still a span) — unlike `<Constrain maxWidth>`, which renders a `<div>` that is invalid inside `<p>`. For an entity link row (icon, KEY, title, status, adornments) prefer `<EntityChip truncate trailing={…}>`, which does exactly this. Default: `false`. |
|
|
57
|
+
| `stretchWrapped` | `boolean` | no | Content-driven switch between one row and stacked. While the children fit on one line they stay content-sized and the free space collects between the first child and the rest (the rest sit at the end). When they no longer fit, every child that wraps onto its own line stretches to fill that line. Canonical case: a status pill plus a `<Grid columns={2}>` of two buttons — pill left and buttons right while there is room, buttons dropping under the pill at full width (equal columns) when there is not. No breakpoint to guess, unlike `Grid collapseBelow`. How: an invisible `::before` spacer soaks up a shared line's slack, so the children there get a negligible share (at most 1px per 1000px of slack); a non-first child alone on a wrapped line takes the whole line. Children wrapped together share it. The spacer cancels its own gap, so it adds no width: the switch happens exactly when the content stops fitting, also with a single child or `as="span"`. Limits: `justify` is inert while this is on (the spacer owns the slack); `wrap={false}` makes it moot (nothing wraps); a consumer `order` on children breaks it — don't. A child's own `flex` (via its `className`) overrides the stretch for that child. "First child" means the first ELEMENT: a leading bare text node is not it — wrap text in an element (e.g. `<Text>`). Default: `false`. |
|
|
58
|
+
| `wrap` | `boolean` | no | Whether children wrap to additional lines when the container is narrow. - `true` (default) — natural for toolbars and tag lists. - `false` — use sparingly, when overflow is preferable to wrapping (e.g. inside a narrow table cell with fixed-width action buttons). |
|
|
59
|
+
| …native | | | plus native HTML attributes |
|
|
60
|
+
|
|
61
|
+
<!-- props:end -->
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
// Custom Calendar chip content that ellipsizes instead of hard-clipping.
|
|
65
|
+
// The chip is a <button> with overflow: hidden, so the Cluster is a flex item:
|
|
66
|
+
// as="span" for valid HTML, minWidth0 so it can actually shrink.
|
|
67
|
+
<Calendar
|
|
68
|
+
events={events}
|
|
69
|
+
renderEvent={(event) => (
|
|
70
|
+
<Cluster as="span" gap="xs" align="center" wrap={false} minWidth0>
|
|
71
|
+
<Dot color="violet" />
|
|
72
|
+
<Text as="span" size="inherit" truncate>
|
|
73
|
+
{event.title}
|
|
74
|
+
</Text>
|
|
75
|
+
</Cluster>
|
|
76
|
+
)}
|
|
77
|
+
/>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Use it for button rows, toolbars, tag lists and breadcrumbs: anywhere you would otherwise write `display: flex; gap: ...; flex-wrap: wrap`.
|
|
81
|
+
|
|
82
|
+
#### When NOT to use
|
|
83
|
+
|
|
84
|
+
- ❌ Aligned columns of equal width → `<Grid>`. Cluster wraps unpredictably at narrow widths and isn't a column system.
|
|
85
|
+
- ❌ Content that must never wrap: `wrap={false}` if you must, but reconsider whether that's truly required on narrow viewports.
|
|
86
|
+
|
|
87
|
+
#### Anti-patterns
|
|
88
|
+
|
|
89
|
+
- ❌ Cluster as a 2-column layout. Use `<Grid columns={2}>` for "two columns, always".
|
|
90
|
+
- ❌ `style={{ marginLeft: 'auto' }}` on a child to push it right. Use `justify="between"` (with a sibling on the left) or split into two Clusters in the parent.
|
|
91
|
+
- ❌ `minWidth0` on a Cluster holding buttons or badges: it lets the container shrink, so non-truncatable content gets clipped. Use it on the container whose TEXT should give way.
|
|
92
|
+
- ❌ `Grid collapseBelow` with a guessed pixel threshold to stack a pill + buttons row. The right width depends on content and locale; use `stretchWrapped` and let the content decide.
|
|
93
|
+
- ❌ `stretchWrapped` with `justify`, `wrap={false}` or a child `order`: `justify` is inert, `wrap={false}` never wraps so nothing switches, `order` breaks the spacer.
|
|
94
|
+
- ❌ `stretchWrapped` with a leading bare text node: the first child is the first ELEMENT, so the text is not treated as it. Wrap the text in an element (e.g. `<Text>`).
|
|
95
|
+
- A child's own `flex` (via its `className`) overrides `stretchWrapped` for that child; that child no longer stretches when it wraps.
|
|
96
|
+
- ❌ `as="span"` as a general styling hook. Reach for it only when block content is invalid HTML (inside `<button>`, `<a>`, `<label>`); in normal flow the default block `div` is what you want.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# `<Code>` — inline `<code>` chip
|
|
2
|
+
|
|
3
|
+
```tsx
|
|
4
|
+
<Text>Use <Code>npm install</Code> to add deps.</Text>
|
|
5
|
+
<Code tone="danger">--no-verify</Code>
|
|
6
|
+
```
|
|
7
|
+
|
|
8
|
+
<!-- props:start -->
|
|
9
|
+
|
|
10
|
+
## Props
|
|
11
|
+
|
|
12
|
+
<!-- prettier-ignore -->
|
|
13
|
+
| Prop | Type | Required | Description |
|
|
14
|
+
|---|---|---|---|
|
|
15
|
+
| `tone` | `'default' \| 'muted' \| 'accent' \| 'danger'` | no | Color tone for the code text. The chip background stays the same; only the text color changes. - `default` — `--color-fg` - `muted` — `--color-fg-muted` - `accent` — `--color-accent` - `danger` — `--color-danger` |
|
|
16
|
+
| `children` | `ReactNode` | yes | Code content. |
|
|
17
|
+
| …native | | | plus native HTML attributes |
|
|
18
|
+
|
|
19
|
+
<!-- props:end -->
|
|
20
|
+
|
|
21
|
+
- **Inline only.** Multi-line or syntax-highlighted code belongs in [`CodeBlock`](CodeBlock.md).
|
|
22
|
+
|
|
23
|
+
- The chip background is intentionally `--color-bg-muted` so it subordinates to body text.
|
|
24
|
+
|
|
25
|
+
#### When NOT to use
|
|
26
|
+
|
|
27
|
+
- ❌ Block-level code with multiple lines or syntax highlighting.
|
|
28
|
+
- ❌ Action triggers that LOOK like code (`<Button variant="ghost">`).
|
|
29
|
+
- ❌ A substitute for `<kbd>` (keyboard input rendering; not yet shipped).
|
|
30
|
+
|
|
31
|
+
#### Anti-patterns
|
|
32
|
+
|
|
33
|
+
- ❌ `<Code>multi-line\nblock</Code>`: Code is inline only; the chip background doesn't extend across newlines. Use `CodeBlock`, or `<pre><Code>...</Code></pre>` for a static block inside the library.
|
|
34
|
+
- ❌ `<Code style={{ background: '#xxx' }}>`: a different background is a token-vocabulary conversation.
|
|
35
|
+
- ❌ Wrapping a `<Button>` or `<Link>` in `<Code>` to style it code-like. Code is for content semantics (it IS code), not visual styling.
|