@eocrm/design-system 0.0.0-stage → 0.6.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AI-PRIMER.md +341 -0
- package/LICENSE +202 -0
- package/README.md +266 -2
- package/docs/components/Accordion.md +114 -0
- package/docs/components/AgentAvatar.md +58 -0
- package/docs/components/Alert.md +81 -0
- package/docs/components/AppLayout.md +70 -0
- package/docs/components/Avatar.md +49 -0
- package/docs/components/AvatarGroup.md +47 -0
- package/docs/components/Badge.md +60 -0
- package/docs/components/Banner.md +89 -0
- package/docs/components/BrandIcon.md +32 -0
- package/docs/components/Breadcrumb.md +72 -0
- package/docs/components/Button.md +107 -0
- package/docs/components/ButtonGroup.md +78 -0
- package/docs/components/Calendar.md +158 -0
- package/docs/components/Card.md +165 -0
- package/docs/components/CatalogPicker.md +83 -0
- package/docs/components/Chart.md +114 -0
- package/docs/components/ChatBubble.md +55 -0
- package/docs/components/ChatLauncher.md +97 -0
- package/docs/components/Checkbox.md +55 -0
- package/docs/components/CircularProgress.md +53 -0
- package/docs/components/Cluster.md +96 -0
- package/docs/components/Code.md +35 -0
- package/docs/components/CodeBlock.md +67 -0
- package/docs/components/ColorPicker.md +115 -0
- package/docs/components/ConfirmationPopover.md +70 -0
- package/docs/components/Constrain.md +39 -0
- package/docs/components/CurrencySelect.md +57 -0
- package/docs/components/CursorPagination.md +43 -0
- package/docs/components/DashboardCanvas.md +79 -0
- package/docs/components/DashboardWidget.md +131 -0
- package/docs/components/DataTable.md +88 -0
- package/docs/components/DatePicker.md +68 -0
- package/docs/components/DateRangePicker.md +71 -0
- package/docs/components/DateStrip.md +56 -0
- package/docs/components/DefinitionList.md +82 -0
- package/docs/components/Divider.md +57 -0
- package/docs/components/Dot.md +26 -0
- package/docs/components/Drawer.md +94 -0
- package/docs/components/DropdownMenu.md +299 -0
- package/docs/components/EmojiPicker.md +84 -0
- package/docs/components/EmptyState.md +55 -0
- package/docs/components/EntityChip.md +102 -0
- package/docs/components/ErrorState.md +72 -0
- package/docs/components/Field.md +91 -0
- package/docs/components/FileUpload.md +109 -0
- package/docs/components/FilterChip.md +88 -0
- package/docs/components/FlowCanvas.md +108 -0
- package/docs/components/FormRow.md +31 -0
- package/docs/components/FormSection.md +37 -0
- package/docs/components/Grid.md +103 -0
- package/docs/components/Highlight.md +75 -0
- package/docs/components/HighlightedText.md +50 -0
- package/docs/components/IconPicker.md +134 -0
- package/docs/components/IconTile.md +30 -0
- package/docs/components/Image.md +84 -0
- package/docs/components/ImageCrop.md +163 -0
- package/docs/components/Indent.md +31 -0
- package/docs/components/InlineDatePicker.md +58 -0
- package/docs/components/InlineDateRangePicker.md +57 -0
- package/docs/components/Input.md +54 -0
- package/docs/components/Kanban.md +122 -0
- package/docs/components/Kbd.md +41 -0
- package/docs/components/Lightbox.md +105 -0
- package/docs/components/Link.md +65 -0
- package/docs/components/LinkCard.md +41 -0
- package/docs/components/LiquidEditor.md +84 -0
- package/docs/components/List.md +68 -0
- package/docs/components/LiveRegion.md +29 -0
- package/docs/components/LocaleProvider.md +25 -0
- package/docs/components/Logo.md +41 -0
- package/docs/components/Masonry.md +48 -0
- package/docs/components/MediaTile.md +95 -0
- package/docs/components/Modal.md +107 -0
- package/docs/components/MoneyInput.md +62 -0
- package/docs/components/OptionsPicker.md +123 -0
- package/docs/components/OtpInput.md +65 -0
- package/docs/components/Page.md +35 -0
- package/docs/components/PageHeader.md +133 -0
- package/docs/components/Pagination.md +44 -0
- package/docs/components/Palette.md +32 -0
- package/docs/components/PasswordInput.md +45 -0
- package/docs/components/PasswordStrengthMeter.md +27 -0
- package/docs/components/PersonDisplay.md +153 -0
- package/docs/components/PhoneInput.md +54 -0
- package/docs/components/PillMenu.md +91 -0
- package/docs/components/Popover.md +127 -0
- package/docs/components/Progress.md +56 -0
- package/docs/components/QrCode.md +34 -0
- package/docs/components/Radio.md +37 -0
- package/docs/components/RadioGroup.md +56 -0
- package/docs/components/Rail.md +191 -0
- package/docs/components/RelativeDateInput.md +114 -0
- package/docs/components/RichText.md +44 -0
- package/docs/components/RichTextEditor.md +112 -0
- package/docs/components/Screen.md +37 -0
- package/docs/components/ScrollArea.md +39 -0
- package/docs/components/Select.md +131 -0
- package/docs/components/SettingRow.md +98 -0
- package/docs/components/Skeleton.md +68 -0
- package/docs/components/Slider.md +92 -0
- package/docs/components/SlotGrid.md +55 -0
- package/docs/components/SocialButton.md +31 -0
- package/docs/components/Sortable.md +127 -0
- package/docs/components/SortableGroup.md +52 -0
- package/docs/components/Sparkline.md +51 -0
- package/docs/components/Split.md +76 -0
- package/docs/components/Stack.md +59 -0
- package/docs/components/StagePath.md +66 -0
- package/docs/components/StatGroup.md +61 -0
- package/docs/components/StatTile.md +58 -0
- package/docs/components/Sticky.md +69 -0
- package/docs/components/Switch.md +89 -0
- package/docs/components/Table.md +184 -0
- package/docs/components/TableOfContents.md +76 -0
- package/docs/components/Tabs.md +94 -0
- package/docs/components/Text.md +63 -0
- package/docs/components/Textarea.md +59 -0
- package/docs/components/Thread.md +52 -0
- package/docs/components/TimeField.md +70 -0
- package/docs/components/Timeline.md +57 -0
- package/docs/components/Title.md +54 -0
- package/docs/components/ToastViewport.md +61 -0
- package/docs/components/Tooltip.md +63 -0
- package/docs/components/TopBar.md +214 -0
- package/docs/components/Tour.md +85 -0
- package/docs/components/TreeView.md +113 -0
- package/docs/components/VisuallyHidden.md +43 -0
- package/docs/components/WidgetPreview.md +54 -0
- package/docs/components/useBelowBreakpoint.md +11 -0
- package/docs/components/useMonth.md +18 -0
- package/docs/setup.md +51 -0
- package/docs/theming.md +95 -0
- package/docs/tokens.md +123 -0
- package/docs/transient-state.md +56 -0
- package/package.json +86 -4
- package/src/_meta/manifest.ts +293 -0
- package/src/app/AppProvider.tsx +78 -0
- package/src/app/index.ts +3 -0
- package/src/app/themeTokens.ts +95 -0
- package/src/calendar/dateMath.ts +114 -0
- package/src/calendar/formatters.ts +75 -0
- package/src/calendar/index.ts +39 -0
- package/src/calendar/types.ts +36 -0
- package/src/calendar/useAgenda.ts +70 -0
- package/src/calendar/useDay.ts +51 -0
- package/src/calendar/useMonth.ts +82 -0
- package/src/calendar/useWeek.ts +62 -0
- package/src/calendar/weekInfo.ts +83 -0
- package/src/components/Accordion/Accordion.module.scss +304 -0
- package/src/components/Accordion/Accordion.tokens.scss +64 -0
- package/src/components/Accordion/Accordion.tsx +272 -0
- package/src/components/Accordion/AccordionContent.tsx +36 -0
- package/src/components/Accordion/AccordionItem.tsx +66 -0
- package/src/components/Accordion/AccordionTrigger.tsx +121 -0
- package/src/components/Accordion/context.ts +47 -0
- package/src/components/Accordion/index.ts +14 -0
- package/src/components/Alert/Alert.module.scss +92 -0
- package/src/components/Alert/Alert.tokens.scss +60 -0
- package/src/components/Alert/Alert.tsx +136 -0
- package/src/components/Alert/index.ts +2 -0
- package/src/components/AppLayout/AppLayout.module.scss +130 -0
- package/src/components/AppLayout/AppLayout.tokens.scss +27 -0
- package/src/components/AppLayout/AppLayout.tsx +211 -0
- package/src/components/AppLayout/index.ts +2 -0
- package/src/components/Avatar/AgentAvatar.module.scss +117 -0
- package/src/components/Avatar/AgentAvatar.tsx +122 -0
- package/src/components/Avatar/Avatar.module.scss +181 -0
- package/src/components/Avatar/Avatar.tokens.scss +107 -0
- package/src/components/Avatar/Avatar.tsx +210 -0
- package/src/components/Avatar/AvatarGroup.module.scss +128 -0
- package/src/components/Avatar/AvatarGroup.tsx +119 -0
- package/src/components/Avatar/AvatarGroupContext.tsx +20 -0
- package/src/components/Avatar/index.ts +6 -0
- package/src/components/Badge/Badge.module.scss +124 -0
- package/src/components/Badge/Badge.tokens.scss +74 -0
- package/src/components/Badge/Badge.tsx +164 -0
- package/src/components/Badge/index.ts +2 -0
- package/src/components/Banner/Banner.module.scss +73 -0
- package/src/components/Banner/Banner.tokens.scss +51 -0
- package/src/components/Banner/Banner.tsx +138 -0
- package/src/components/Banner/index.ts +2 -0
- package/src/components/BrandIcon/BrandIcon.module.scss +5 -0
- package/src/components/BrandIcon/BrandIcon.tsx +99 -0
- package/src/components/BrandIcon/index.ts +2 -0
- package/src/components/Breadcrumb/Breadcrumb.module.scss +32 -0
- package/src/components/Breadcrumb/Breadcrumb.tokens.scss +9 -0
- package/src/components/Breadcrumb/Breadcrumb.tsx +146 -0
- package/src/components/Breadcrumb/index.ts +2 -0
- package/src/components/Button/Button.module.scss +167 -0
- package/src/components/Button/Button.tokens.scss +90 -0
- package/src/components/Button/Button.tsx +205 -0
- package/src/components/Button/index.ts +2 -0
- package/src/components/ButtonGroup/ButtonGroup.module.scss +134 -0
- package/src/components/ButtonGroup/ButtonGroup.tokens.scss +42 -0
- package/src/components/ButtonGroup/ButtonGroup.tsx +278 -0
- package/src/components/ButtonGroup/ButtonGroupItem.tsx +74 -0
- package/src/components/ButtonGroup/context.ts +45 -0
- package/src/components/ButtonGroup/index.ts +2 -0
- package/src/components/Calendar/AgendaView.module.scss +231 -0
- package/src/components/Calendar/AgendaView.tsx +214 -0
- package/src/components/Calendar/AllDayBand.module.scss +18 -0
- package/src/components/Calendar/AllDayBand.tsx +75 -0
- package/src/components/Calendar/Calendar.module.scss +23 -0
- package/src/components/Calendar/Calendar.tokens.scss +262 -0
- package/src/components/Calendar/Calendar.tsx +370 -0
- package/src/components/Calendar/DayCell.module.scss +30 -0
- package/src/components/Calendar/DayCell.tsx +50 -0
- package/src/components/Calendar/DayView.module.scss +10 -0
- package/src/components/Calendar/DayView.tsx +163 -0
- package/src/components/Calendar/EventChip.module.scss +171 -0
- package/src/components/Calendar/EventChip.tsx +115 -0
- package/src/components/Calendar/HourGrid.module.scss +123 -0
- package/src/components/Calendar/HourGrid.tsx +381 -0
- package/src/components/Calendar/MonthView.module.scss +99 -0
- package/src/components/Calendar/MonthView.tsx +296 -0
- package/src/components/Calendar/TimedEvent.module.scss +205 -0
- package/src/components/Calendar/TimedEvent.tsx +247 -0
- package/src/components/Calendar/ViewSwitcher.module.scss +3 -0
- package/src/components/Calendar/ViewSwitcher.tsx +47 -0
- package/src/components/Calendar/WeekView.module.scss +10 -0
- package/src/components/Calendar/WeekView.tsx +137 -0
- package/src/components/Calendar/eventColor.ts +60 -0
- package/src/components/Calendar/index.ts +18 -0
- package/src/components/Calendar/types.ts +451 -0
- package/src/components/Calendar/useEventDrag.ts +783 -0
- package/src/components/Calendar/utils.ts +622 -0
- package/src/components/Card/Card.module.scss +264 -0
- package/src/components/Card/Card.tokens.scss +55 -0
- package/src/components/Card/Card.tsx +170 -0
- package/src/components/Card/CardBody.tsx +45 -0
- package/src/components/Card/CardHeader.tsx +113 -0
- package/src/components/Card/CardList.tsx +24 -0
- package/src/components/Card/CardListRow.tsx +24 -0
- package/src/components/Card/index.ts +7 -0
- package/src/components/CatalogPicker/CatalogPicker.module.scss +193 -0
- package/src/components/CatalogPicker/CatalogPicker.tokens.scss +26 -0
- package/src/components/CatalogPicker/CatalogPicker.tsx +339 -0
- package/src/components/CatalogPicker/index.ts +2 -0
- package/src/components/Chart/Chart.module.scss +287 -0
- package/src/components/Chart/Chart.tokens.scss +63 -0
- package/src/components/Chart/Chart.tsx +607 -0
- package/src/components/Chart/ChartLegend.tsx +99 -0
- package/src/components/Chart/ChartTable.tsx +62 -0
- package/src/components/Chart/ChartTooltip.tsx +91 -0
- package/src/components/Chart/chartModel.ts +689 -0
- package/src/components/Chart/index.ts +2 -0
- package/src/components/ChatBubble/ChatBubble.module.scss +93 -0
- package/src/components/ChatBubble/ChatBubble.tokens.scss +17 -0
- package/src/components/ChatBubble/ChatBubble.tsx +61 -0
- package/src/components/ChatBubble/ChatBubbleList.tsx +27 -0
- package/src/components/ChatBubble/index.ts +3 -0
- package/src/components/ChatLauncher/ChatLauncher.module.scss +68 -0
- package/src/components/ChatLauncher/ChatLauncher.tokens.scss +27 -0
- package/src/components/ChatLauncher/ChatLauncher.tsx +202 -0
- package/src/components/ChatLauncher/index.ts +2 -0
- package/src/components/ChatLauncher/isAtBottom.ts +12 -0
- package/src/components/Checkbox/Checkbox.module.scss +147 -0
- package/src/components/Checkbox/Checkbox.tokens.scss +44 -0
- package/src/components/Checkbox/Checkbox.tsx +188 -0
- package/src/components/Checkbox/index.ts +2 -0
- package/src/components/CircularProgress/CircularProgress.module.scss +133 -0
- package/src/components/CircularProgress/CircularProgress.tokens.scss +40 -0
- package/src/components/CircularProgress/CircularProgress.tsx +139 -0
- package/src/components/CircularProgress/index.ts +7 -0
- package/src/components/Cluster/Cluster.module.scss +145 -0
- package/src/components/Cluster/Cluster.tokens.scss +11 -0
- package/src/components/Cluster/Cluster.tsx +215 -0
- package/src/components/Cluster/index.ts +2 -0
- package/src/components/Code/Code.module.scss +28 -0
- package/src/components/Code/Code.tokens.scss +15 -0
- package/src/components/Code/Code.tsx +44 -0
- package/src/components/Code/index.ts +2 -0
- package/src/components/CodeBlock/CodeBlock.module.scss +146 -0
- package/src/components/CodeBlock/CodeBlock.tokens.scss +27 -0
- package/src/components/CodeBlock/CodeBlock.tsx +274 -0
- package/src/components/CodeBlock/highlight.ts +179 -0
- package/src/components/CodeBlock/index.ts +8 -0
- package/src/components/CodeBlock/sampleSync.ts +42 -0
- package/src/components/ColorPicker/ColorPicker.module.scss +286 -0
- package/src/components/ColorPicker/ColorPicker.tokens.scss +60 -0
- package/src/components/ColorPicker/ColorPicker.tsx +367 -0
- package/src/components/ColorPicker/ColorPickerPanel.tsx +265 -0
- package/src/components/ColorPicker/SVSquare.tsx +189 -0
- package/src/components/ColorPicker/colorMath.ts +141 -0
- package/src/components/ColorPicker/index.ts +6 -0
- package/src/components/ConfirmationPopover/ConfirmationPopover.module.scss +39 -0
- package/src/components/ConfirmationPopover/ConfirmationPopover.tokens.scss +13 -0
- package/src/components/ConfirmationPopover/ConfirmationPopover.tsx +395 -0
- package/src/components/ConfirmationPopover/index.ts +2 -0
- package/src/components/Constrain/Constrain.module.scss +99 -0
- package/src/components/Constrain/Constrain.tsx +79 -0
- package/src/components/Constrain/index.ts +2 -0
- package/src/components/CurrencySelect/CurrencySelect.module.scss +7 -0
- package/src/components/CurrencySelect/CurrencySelect.tsx +167 -0
- package/src/components/CurrencySelect/index.ts +3 -0
- package/src/components/CursorPagination/CursorPagination.module.scss +69 -0
- package/src/components/CursorPagination/CursorPagination.tokens.scss +32 -0
- package/src/components/CursorPagination/CursorPagination.tsx +121 -0
- package/src/components/CursorPagination/index.ts +2 -0
- package/src/components/DashboardCanvas/DashboardCanvas.module.scss +360 -0
- package/src/components/DashboardCanvas/DashboardCanvas.tokens.scss +48 -0
- package/src/components/DashboardCanvas/DashboardCanvas.tsx +1270 -0
- package/src/components/DashboardCanvas/DashboardCanvasItem.tsx +140 -0
- package/src/components/DashboardCanvas/DashboardCanvasSection.tsx +174 -0
- package/src/components/DashboardCanvas/engine.ts +363 -0
- package/src/components/DashboardCanvas/index.ts +9 -0
- package/src/components/DashboardWidget/DashboardWidget.module.scss +100 -0
- package/src/components/DashboardWidget/DashboardWidget.tokens.scss +16 -0
- package/src/components/DashboardWidget/DashboardWidget.tsx +188 -0
- package/src/components/DashboardWidget/index.ts +7 -0
- package/src/components/DataTable/BodyRow.tsx +197 -0
- package/src/components/DataTable/ColumnVisibilityTrigger.module.scss +7 -0
- package/src/components/DataTable/ColumnVisibilityTrigger.tsx +105 -0
- package/src/components/DataTable/DataTable.module.scss +511 -0
- package/src/components/DataTable/DataTable.tokens.scss +80 -0
- package/src/components/DataTable/DataTable.tsx +734 -0
- package/src/components/DataTable/HeaderCell.module.scss +215 -0
- package/src/components/DataTable/HeaderCell.tsx +440 -0
- package/src/components/DataTable/columnShift.ts +117 -0
- package/src/components/DataTable/index.ts +23 -0
- package/src/components/DataTable/pinStyle.ts +47 -0
- package/src/components/DataTable/reorderColumns.ts +66 -0
- package/src/components/DataTable/types.ts +289 -0
- package/src/components/DataTable/useColumnDragShift.ts +216 -0
- package/src/components/DataTable/useDataTable.ts +372 -0
- package/src/components/DataTable/useResizeHandle.ts +97 -0
- package/src/components/DatePicker/DatePicker.module.scss +180 -0
- package/src/components/DatePicker/DatePicker.tokens.scss +152 -0
- package/src/components/DatePicker/DatePicker.tsx +664 -0
- package/src/components/DatePicker/DatePickerGrid.module.scss +172 -0
- package/src/components/DatePicker/DatePickerGrid.tsx +341 -0
- package/src/components/DatePicker/InlineDatePicker.module.scss +32 -0
- package/src/components/DatePicker/InlineDatePicker.tsx +201 -0
- package/src/components/DatePicker/index.ts +4 -0
- package/src/components/DatePicker/utils.ts +364 -0
- package/src/components/DateRangePicker/DateRangePicker.module.scss +237 -0
- package/src/components/DateRangePicker/DateRangePicker.tokens.scss +78 -0
- package/src/components/DateRangePicker/DateRangePicker.tsx +720 -0
- package/src/components/DateRangePicker/InlineDateRangePicker.module.scss +94 -0
- package/src/components/DateRangePicker/InlineDateRangePicker.tsx +376 -0
- package/src/components/DateRangePicker/index.ts +5 -0
- package/src/components/DateRangePicker/utils.ts +151 -0
- package/src/components/DateStrip/DateStrip.module.scss +78 -0
- package/src/components/DateStrip/DateStrip.tokens.scss +23 -0
- package/src/components/DateStrip/DateStrip.tsx +205 -0
- package/src/components/DateStrip/index.ts +2 -0
- package/src/components/DefinitionList/DefinitionList.module.scss +102 -0
- package/src/components/DefinitionList/DefinitionList.tokens.scss +29 -0
- package/src/components/DefinitionList/DefinitionList.tsx +198 -0
- package/src/components/DefinitionList/index.ts +9 -0
- package/src/components/Divider/Divider.module.scss +144 -0
- package/src/components/Divider/Divider.tokens.scss +21 -0
- package/src/components/Divider/Divider.tsx +103 -0
- package/src/components/Divider/index.ts +2 -0
- package/src/components/Dot/Dot.module.scss +40 -0
- package/src/components/Dot/Dot.tokens.scss +4 -0
- package/src/components/Dot/Dot.tsx +45 -0
- package/src/components/Dot/index.ts +2 -0
- package/src/components/Drawer/Body.tsx +23 -0
- package/src/components/Drawer/Close.tsx +35 -0
- package/src/components/Drawer/Content.tsx +93 -0
- package/src/components/Drawer/Drawer.module.scss +232 -0
- package/src/components/Drawer/Drawer.tokens.scss +38 -0
- package/src/components/Drawer/Drawer.tsx +23 -0
- package/src/components/Drawer/DrawerRoot.tsx +190 -0
- package/src/components/Drawer/Footer.tsx +24 -0
- package/src/components/Drawer/Header.tsx +64 -0
- package/src/components/Drawer/Overlay.tsx +82 -0
- package/src/components/Drawer/context.ts +44 -0
- package/src/components/Drawer/index.ts +12 -0
- package/src/components/Drawer/useDragToClose.ts +149 -0
- package/src/components/DropdownMenu/CheckboxItem.tsx +144 -0
- package/src/components/DropdownMenu/Content.tsx +419 -0
- package/src/components/DropdownMenu/DropdownMenu.module.scss +280 -0
- package/src/components/DropdownMenu/DropdownMenu.tokens.scss +88 -0
- package/src/components/DropdownMenu/DropdownMenu.tsx +43 -0
- package/src/components/DropdownMenu/Group.tsx +76 -0
- package/src/components/DropdownMenu/Item.tsx +148 -0
- package/src/components/DropdownMenu/ItemIndicator.tsx +30 -0
- package/src/components/DropdownMenu/Radio.tsx +178 -0
- package/src/components/DropdownMenu/Root.tsx +100 -0
- package/src/components/DropdownMenu/Sub.tsx +332 -0
- package/src/components/DropdownMenu/Trigger.tsx +76 -0
- package/src/components/DropdownMenu/context.ts +65 -0
- package/src/components/DropdownMenu/index.ts +20 -0
- package/src/components/EmojiPicker/EmojiPicker.module.scss +84 -0
- package/src/components/EmojiPicker/EmojiPicker.tokens.scss +24 -0
- package/src/components/EmojiPicker/EmojiPicker.tsx +341 -0
- package/src/components/EmojiPicker/emojiData.ts +245 -0
- package/src/components/EmojiPicker/index.ts +3 -0
- package/src/components/EmptyState/EmptyState.module.scss +102 -0
- package/src/components/EmptyState/EmptyState.tokens.scss +28 -0
- package/src/components/EmptyState/EmptyState.tsx +112 -0
- package/src/components/EmptyState/index.ts +7 -0
- package/src/components/EntityChip/EntityChip.module.scss +275 -0
- package/src/components/EntityChip/EntityChip.tokens.scss +66 -0
- package/src/components/EntityChip/EntityChip.tsx +649 -0
- package/src/components/EntityChip/index.ts +10 -0
- package/src/components/ErrorState/ErrorState.module.scss +105 -0
- package/src/components/ErrorState/ErrorState.tokens.scss +32 -0
- package/src/components/ErrorState/ErrorState.tsx +125 -0
- package/src/components/ErrorState/index.ts +8 -0
- package/src/components/Field/Field.module.scss +46 -0
- package/src/components/Field/Field.tsx +185 -0
- package/src/components/Field/index.ts +2 -0
- package/src/components/FileUpload/FileUpload.module.scss +187 -0
- package/src/components/FileUpload/FileUpload.tokens.scss +81 -0
- package/src/components/FileUpload/FileUpload.tsx +569 -0
- package/src/components/FileUpload/formatBytes.ts +34 -0
- package/src/components/FileUpload/iconForFile.ts +40 -0
- package/src/components/FileUpload/index.ts +2 -0
- package/src/components/FilterChip/FilterChip.module.scss +61 -0
- package/src/components/FilterChip/FilterChip.tokens.scss +25 -0
- package/src/components/FilterChip/FilterChip.tsx +208 -0
- package/src/components/FilterChip/index.ts +5 -0
- package/src/components/FlowCanvas/FlowCanvas.module.scss +329 -0
- package/src/components/FlowCanvas/FlowCanvas.tokens.scss +45 -0
- package/src/components/FlowCanvas/FlowCanvas.tsx +1867 -0
- package/src/components/FlowCanvas/FlowControls.tsx +46 -0
- package/src/components/FlowCanvas/FlowEdge.tsx +58 -0
- package/src/components/FlowCanvas/FlowNode.tsx +76 -0
- package/src/components/FlowCanvas/edgePath.ts +85 -0
- package/src/components/FlowCanvas/index.ts +4 -0
- package/src/components/FlowCanvas/layout.ts +148 -0
- package/src/components/FlowCanvas/spatialNav.ts +76 -0
- package/src/components/FlowCanvas/types.ts +59 -0
- package/src/components/FlowCanvas/useViewport.ts +122 -0
- package/src/components/FormRow/FormRow.module.scss +2 -0
- package/src/components/FormRow/FormRow.tsx +37 -0
- package/src/components/FormRow/index.ts +2 -0
- package/src/components/FormSection/FormSection.module.scss +24 -0
- package/src/components/FormSection/FormSection.tsx +46 -0
- package/src/components/FormSection/index.ts +2 -0
- package/src/components/Grid/Grid.module.scss +162 -0
- package/src/components/Grid/Grid.tokens.scss +13 -0
- package/src/components/Grid/Grid.tsx +226 -0
- package/src/components/Grid/GridItem.tsx +77 -0
- package/src/components/Grid/index.ts +11 -0
- package/src/components/Highlight/Highlight.module.scss +43 -0
- package/src/components/Highlight/Highlight.tokens.scss +35 -0
- package/src/components/Highlight/Highlight.tsx +164 -0
- package/src/components/Highlight/HighlightOverlay.tsx +100 -0
- package/src/components/Highlight/index.ts +2 -0
- package/src/components/HighlightedText/HighlightedText.module.scss +11 -0
- package/src/components/HighlightedText/HighlightedText.tokens.scss +9 -0
- package/src/components/HighlightedText/HighlightedText.tsx +50 -0
- package/src/components/HighlightedText/highlightRanges.ts +44 -0
- package/src/components/HighlightedText/index.ts +2 -0
- package/src/components/IconPicker/IconPicker.module.scss +175 -0
- package/src/components/IconPicker/IconPicker.tokens.scss +40 -0
- package/src/components/IconPicker/IconPicker.tsx +629 -0
- package/src/components/IconPicker/index.ts +9 -0
- package/src/components/IconTile/IconTile.module.scss +57 -0
- package/src/components/IconTile/IconTile.tokens.scss +16 -0
- package/src/components/IconTile/IconTile.tsx +91 -0
- package/src/components/IconTile/index.ts +2 -0
- package/src/components/Image/Image.module.scss +266 -0
- package/src/components/Image/Image.tokens.scss +55 -0
- package/src/components/Image/Image.tsx +342 -0
- package/src/components/Image/index.ts +2 -0
- package/src/components/ImageCrop/ImageCrop.module.scss +274 -0
- package/src/components/ImageCrop/ImageCrop.tokens.scss +33 -0
- package/src/components/ImageCrop/ImageCrop.tsx +542 -0
- package/src/components/ImageCrop/cropGeometry.ts +120 -0
- package/src/components/ImageCrop/extractCropBlob.ts +140 -0
- package/src/components/ImageCrop/index.ts +11 -0
- package/src/components/ImageCrop/useCropPreview.ts +161 -0
- package/src/components/Indent/Indent.module.scss +33 -0
- package/src/components/Indent/Indent.tokens.scss +10 -0
- package/src/components/Indent/Indent.tsx +46 -0
- package/src/components/Indent/index.ts +2 -0
- package/src/components/Input/Input.module.scss +99 -0
- package/src/components/Input/Input.tokens.scss +54 -0
- package/src/components/Input/Input.tsx +105 -0
- package/src/components/Input/Suggestions.tsx +260 -0
- package/src/components/Input/index.ts +2 -0
- package/src/components/Kanban/Kanban.module.scss +39 -0
- package/src/components/Kanban/Kanban.tokens.scss +15 -0
- package/src/components/Kanban/Kanban.tsx +951 -0
- package/src/components/Kanban/index.ts +7 -0
- package/src/components/Kbd/Kbd.module.scss +47 -0
- package/src/components/Kbd/Kbd.tokens.scss +26 -0
- package/src/components/Kbd/Kbd.tsx +73 -0
- package/src/components/Kbd/index.ts +2 -0
- package/src/components/Lightbox/Lightbox.module.scss +283 -0
- package/src/components/Lightbox/Lightbox.tokens.scss +36 -0
- package/src/components/Lightbox/Lightbox.tsx +506 -0
- package/src/components/Lightbox/index.ts +2 -0
- package/src/components/Link/Link.module.scss +78 -0
- package/src/components/Link/Link.tokens.scss +28 -0
- package/src/components/Link/Link.tsx +114 -0
- package/src/components/Link/index.ts +2 -0
- package/src/components/LinkCard/LinkCard.module.scss +92 -0
- package/src/components/LinkCard/LinkCard.tokens.scss +9 -0
- package/src/components/LinkCard/LinkCard.tsx +72 -0
- package/src/components/LinkCard/index.ts +2 -0
- package/src/components/LiquidEditor/AutocompleteMenu.tsx +133 -0
- package/src/components/LiquidEditor/HighlightLayer.tsx +53 -0
- package/src/components/LiquidEditor/InsertVariableMenu.tsx +81 -0
- package/src/components/LiquidEditor/LiquidEditor.module.scss +339 -0
- package/src/components/LiquidEditor/LiquidEditor.tokens.scss +81 -0
- package/src/components/LiquidEditor/LiquidEditor.tsx +412 -0
- package/src/components/LiquidEditor/PreviewPane.tsx +29 -0
- package/src/components/LiquidEditor/caretCoordinates.ts +97 -0
- package/src/components/LiquidEditor/index.ts +7 -0
- package/src/components/LiquidEditor/liquidFilters.ts +23 -0
- package/src/components/LiquidEditor/liquidTokenizer.ts +235 -0
- package/src/components/LiquidEditor/types.ts +101 -0
- package/src/components/LiquidEditor/useLiquidAutocomplete.ts +178 -0
- package/src/components/List/List.module.scss +105 -0
- package/src/components/List/List.tokens.scss +42 -0
- package/src/components/List/List.tsx +124 -0
- package/src/components/List/index.ts +9 -0
- package/src/components/LiveRegion/LiveRegion.module.scss +3 -0
- package/src/components/LiveRegion/LiveRegion.tsx +116 -0
- package/src/components/LiveRegion/index.ts +2 -0
- package/src/components/Logo/Logo.module.scss +139 -0
- package/src/components/Logo/Logo.tokens.scss +14 -0
- package/src/components/Logo/Logo.tsx +126 -0
- package/src/components/Logo/index.ts +2 -0
- package/src/components/Masonry/Masonry.module.scss +42 -0
- package/src/components/Masonry/Masonry.tokens.scss +8 -0
- package/src/components/Masonry/Masonry.tsx +136 -0
- package/src/components/Masonry/index.ts +2 -0
- package/src/components/Masonry/masonryUtils.ts +42 -0
- package/src/components/MediaTile/MediaTile.module.scss +183 -0
- package/src/components/MediaTile/MediaTile.tokens.scss +60 -0
- package/src/components/MediaTile/MediaTile.tsx +208 -0
- package/src/components/MediaTile/index.ts +8 -0
- package/src/components/Modal/Body.tsx +27 -0
- package/src/components/Modal/Close.tsx +45 -0
- package/src/components/Modal/Content.tsx +98 -0
- package/src/components/Modal/Footer.tsx +28 -0
- package/src/components/Modal/Header.tsx +51 -0
- package/src/components/Modal/Modal.module.scss +262 -0
- package/src/components/Modal/Modal.tokens.scss +50 -0
- package/src/components/Modal/Modal.tsx +23 -0
- package/src/components/Modal/ModalRoot.tsx +299 -0
- package/src/components/Modal/Overlay.tsx +101 -0
- package/src/components/Modal/context.ts +60 -0
- package/src/components/Modal/index.ts +11 -0
- package/src/components/MoneyInput/MoneyInput.module.scss +57 -0
- package/src/components/MoneyInput/MoneyInput.tsx +433 -0
- package/src/components/MoneyInput/index.ts +2 -0
- package/src/components/OptionsPicker/OptionsPicker.module.scss +216 -0
- package/src/components/OptionsPicker/OptionsPicker.tokens.scss +68 -0
- package/src/components/OptionsPicker/OptionsPicker.tsx +936 -0
- package/src/components/OptionsPicker/index.ts +9 -0
- package/src/components/OtpInput/OtpInput.module.scss +69 -0
- package/src/components/OtpInput/OtpInput.tokens.scss +45 -0
- package/src/components/OtpInput/OtpInput.tsx +368 -0
- package/src/components/OtpInput/index.ts +2 -0
- package/src/components/Page/Page.module.scss +30 -0
- package/src/components/Page/Page.tokens.scss +11 -0
- package/src/components/Page/Page.tsx +53 -0
- package/src/components/Page/index.ts +2 -0
- package/src/components/PageHeader/PageHeader.module.scss +197 -0
- package/src/components/PageHeader/PageHeader.tokens.scss +41 -0
- package/src/components/PageHeader/PageHeader.tsx +310 -0
- package/src/components/PageHeader/index.ts +20 -0
- package/src/components/Pagination/Pagination.module.scss +117 -0
- package/src/components/Pagination/Pagination.tokens.scss +54 -0
- package/src/components/Pagination/Pagination.tsx +165 -0
- package/src/components/Pagination/index.ts +4 -0
- package/src/components/Pagination/paginationRange.ts +72 -0
- package/src/components/PasswordInput/PasswordInput.module.scss +120 -0
- package/src/components/PasswordInput/PasswordInput.tokens.scss +44 -0
- package/src/components/PasswordInput/PasswordInput.tsx +210 -0
- package/src/components/PasswordInput/index.ts +2 -0
- package/src/components/PasswordStrengthMeter/PasswordStrengthMeter.module.scss +80 -0
- package/src/components/PasswordStrengthMeter/PasswordStrengthMeter.tokens.scss +40 -0
- package/src/components/PasswordStrengthMeter/PasswordStrengthMeter.tsx +96 -0
- package/src/components/PasswordStrengthMeter/index.ts +2 -0
- package/src/components/PersonDisplay/PersonDisplay.module.scss +180 -0
- package/src/components/PersonDisplay/PersonDisplay.tokens.scss +35 -0
- package/src/components/PersonDisplay/PersonDisplay.tsx +420 -0
- package/src/components/PersonDisplay/index.ts +10 -0
- package/src/components/PhoneInput/PhoneInput.module.scss +23 -0
- package/src/components/PhoneInput/PhoneInput.tsx +271 -0
- package/src/components/PhoneInput/index.ts +4 -0
- package/src/components/PhoneInput/phone.ts +128 -0
- package/src/components/PillMenu/PillMenu.module.scss +117 -0
- package/src/components/PillMenu/PillMenu.tokens.scss +22 -0
- package/src/components/PillMenu/PillMenu.tsx +324 -0
- package/src/components/PillMenu/index.ts +2 -0
- package/src/components/Popover/Anchor.tsx +37 -0
- package/src/components/Popover/Close.tsx +45 -0
- package/src/components/Popover/Content.tsx +213 -0
- package/src/components/Popover/Heading.tsx +33 -0
- package/src/components/Popover/Popover.module.scss +177 -0
- package/src/components/Popover/Popover.tokens.scss +22 -0
- package/src/components/Popover/Popover.tsx +23 -0
- package/src/components/Popover/PopoverRoot.tsx +75 -0
- package/src/components/Popover/Trigger.tsx +86 -0
- package/src/components/Popover/context.ts +22 -0
- package/src/components/Popover/index.ts +7 -0
- package/src/components/Progress/Progress.module.scss +110 -0
- package/src/components/Progress/Progress.tokens.scss +44 -0
- package/src/components/Progress/Progress.tsx +129 -0
- package/src/components/Progress/index.ts +2 -0
- package/src/components/QrCode/QrCode.module.scss +97 -0
- package/src/components/QrCode/QrCode.tokens.scss +17 -0
- package/src/components/QrCode/QrCode.tsx +233 -0
- package/src/components/QrCode/index.ts +2 -0
- package/src/components/QrCode/qr.ts +161 -0
- package/src/components/Radio/Radio.module.scss +175 -0
- package/src/components/Radio/Radio.tokens.scss +59 -0
- package/src/components/Radio/Radio.tsx +127 -0
- package/src/components/Radio/RadioGroup.module.scss +42 -0
- package/src/components/Radio/RadioGroup.tsx +120 -0
- package/src/components/Radio/RadioGroupContext.tsx +30 -0
- package/src/components/Radio/index.ts +4 -0
- package/src/components/Rail/Rail.module.scss +510 -0
- package/src/components/Rail/Rail.tokens.scss +92 -0
- package/src/components/Rail/Rail.tsx +287 -0
- package/src/components/Rail/RailCollapseToggle.tsx +57 -0
- package/src/components/Rail/RailFooter.tsx +17 -0
- package/src/components/Rail/RailGroup.tsx +728 -0
- package/src/components/Rail/RailGroupContext.ts +9 -0
- package/src/components/Rail/RailHeader.tsx +23 -0
- package/src/components/Rail/RailItem.tsx +119 -0
- package/src/components/Rail/RailSection.tsx +39 -0
- package/src/components/Rail/RailSpacer.tsx +19 -0
- package/src/components/Rail/index.ts +9 -0
- package/src/components/RelativeDateInput/RelativeDateInput.module.scss +15 -0
- package/src/components/RelativeDateInput/RelativeDateInput.tokens.scss +6 -0
- package/src/components/RelativeDateInput/RelativeDateInput.tsx +474 -0
- package/src/components/RelativeDateInput/index.ts +10 -0
- package/src/components/RelativeDateInput/relativeDate.ts +127 -0
- package/src/components/RichText/RichText.module.scss +5 -0
- package/src/components/RichText/RichText.tsx +70 -0
- package/src/components/RichText/RichTextAttachment.module.scss +82 -0
- package/src/components/RichText/RichTextAttachment.tsx +122 -0
- package/src/components/RichText/_prose.scss +79 -0
- package/src/components/RichText/attachmentIcon.tsx +24 -0
- package/src/components/RichText/engine/attachment.ts +154 -0
- package/src/components/RichText/engine/autolink.ts +64 -0
- package/src/components/RichText/engine/blockUnit.ts +174 -0
- package/src/components/RichText/engine/colorMarks.ts +85 -0
- package/src/components/RichText/engine/escape.ts +11 -0
- package/src/components/RichText/engine/fromHtml.ts +353 -0
- package/src/components/RichText/engine/fromMarkdown.ts +20 -0
- package/src/components/RichText/engine/inlines.ts +82 -0
- package/src/components/RichText/engine/listDepths.ts +32 -0
- package/src/components/RichText/engine/marks.ts +80 -0
- package/src/components/RichText/engine/mdToHtml.ts +178 -0
- package/src/components/RichText/engine/model.ts +157 -0
- package/src/components/RichText/engine/position.ts +118 -0
- package/src/components/RichText/engine/renderDoc.tsx +395 -0
- package/src/components/RichText/engine/renderLink.ts +15 -0
- package/src/components/RichText/engine/renderMention.ts +16 -0
- package/src/components/RichText/engine/safeHref.ts +22 -0
- package/src/components/RichText/engine/toHtml.ts +154 -0
- package/src/components/RichText/engine/toMarkdown.ts +97 -0
- package/src/components/RichText/engine/transforms.ts +434 -0
- package/src/components/RichText/imageMaxHeightStyle.ts +10 -0
- package/src/components/RichText/index.ts +31 -0
- package/src/components/RichTextEditor/RichTextAttachmentConfig.tsx +264 -0
- package/src/components/RichTextEditor/RichTextBlockControls.tsx +365 -0
- package/src/components/RichTextEditor/RichTextBlockMenu.tsx +162 -0
- package/src/components/RichTextEditor/RichTextColorMenu.tsx +115 -0
- package/src/components/RichTextEditor/RichTextEditor.module.scss +298 -0
- package/src/components/RichTextEditor/RichTextEditor.tsx +1910 -0
- package/src/components/RichTextEditor/RichTextImageResizer.tsx +131 -0
- package/src/components/RichTextEditor/RichTextLinkEditor.tsx +151 -0
- package/src/components/RichTextEditor/RichTextMentionMenu.tsx +99 -0
- package/src/components/RichTextEditor/RichTextToolbar.tsx +379 -0
- package/src/components/RichTextEditor/autolinkInput.ts +65 -0
- package/src/components/RichTextEditor/blockDrop.ts +9 -0
- package/src/components/RichTextEditor/blockReflow.ts +62 -0
- package/src/components/RichTextEditor/commands.ts +300 -0
- package/src/components/RichTextEditor/history.ts +93 -0
- package/src/components/RichTextEditor/icons.tsx +152 -0
- package/src/components/RichTextEditor/imageSize.ts +70 -0
- package/src/components/RichTextEditor/index.ts +5 -0
- package/src/components/RichTextEditor/input.ts +209 -0
- package/src/components/RichTextEditor/inputRules.ts +71 -0
- package/src/components/RichTextEditor/links.ts +103 -0
- package/src/components/RichTextEditor/mentionContext.ts +37 -0
- package/src/components/RichTextEditor/mentions.ts +28 -0
- package/src/components/RichTextEditor/preventSelectionLoss.ts +15 -0
- package/src/components/RichTextEditor/selection.ts +415 -0
- package/src/components/RichTextEditor/shortcuts.ts +44 -0
- package/src/components/RichTextEditor/useAnchoredFloating.ts +50 -0
- package/src/components/RichTextEditor/useAttachmentConfig.ts +134 -0
- package/src/components/RichTextEditor/useDismissOnOutsidePointerDown.ts +26 -0
- package/src/components/RichTextEditor/useLinkEditor.ts +107 -0
- package/src/components/RichTextEditor/useMention.ts +226 -0
- package/src/components/RichTextEditor/useOverlayRect.ts +73 -0
- package/src/components/RichTextEditor/useUpload.ts +246 -0
- package/src/components/Screen/Screen.module.scss +46 -0
- package/src/components/Screen/Screen.tokens.scss +20 -0
- package/src/components/Screen/Screen.tsx +97 -0
- package/src/components/Screen/index.ts +2 -0
- package/src/components/ScrollArea/ScrollArea.module.scss +39 -0
- package/src/components/ScrollArea/ScrollArea.tokens.scss +15 -0
- package/src/components/ScrollArea/ScrollArea.tsx +154 -0
- package/src/components/ScrollArea/index.ts +2 -0
- package/src/components/Select/Chip.tsx +45 -0
- package/src/components/Select/Empty.tsx +39 -0
- package/src/components/Select/Error.tsx +51 -0
- package/src/components/Select/HiddenInputs.tsx +44 -0
- package/src/components/Select/Listbox.tsx +557 -0
- package/src/components/Select/Loading.tsx +30 -0
- package/src/components/Select/Select.module.scss +512 -0
- package/src/components/Select/Select.tokens.scss +140 -0
- package/src/components/Select/Select.tsx +685 -0
- package/src/components/Select/Trigger.tsx +952 -0
- package/src/components/Select/context.ts +101 -0
- package/src/components/Select/emptyStateText.ts +19 -0
- package/src/components/Select/index.ts +9 -0
- package/src/components/Select/useSelectState.ts +157 -0
- package/src/components/Select/utils-types.ts +7 -0
- package/src/components/Select/utils.ts +122 -0
- package/src/components/SettingRow/SettingRow.module.scss +166 -0
- package/src/components/SettingRow/SettingRow.tokens.scss +37 -0
- package/src/components/SettingRow/SettingRow.tsx +242 -0
- package/src/components/SettingRow/index.ts +7 -0
- package/src/components/Skeleton/Skeleton.module.scss +55 -0
- package/src/components/Skeleton/Skeleton.tokens.scss +17 -0
- package/src/components/Skeleton/Skeleton.tsx +131 -0
- package/src/components/Skeleton/index.ts +4 -0
- package/src/components/Skeleton/useSkeletonVisibility.ts +95 -0
- package/src/components/Slider/Slider.module.scss +293 -0
- package/src/components/Slider/Slider.tokens.scss +98 -0
- package/src/components/Slider/Slider.tsx +624 -0
- package/src/components/Slider/index.ts +9 -0
- package/src/components/SlotGrid/SlotGrid.module.scss +61 -0
- package/src/components/SlotGrid/SlotGrid.tokens.scss +21 -0
- package/src/components/SlotGrid/SlotGrid.tsx +111 -0
- package/src/components/SlotGrid/index.ts +2 -0
- package/src/components/SocialButton/SocialButton.module.scss +4 -0
- package/src/components/SocialButton/SocialButton.tsx +40 -0
- package/src/components/SocialButton/index.ts +2 -0
- package/src/components/Sortable/Sortable.module.scss +177 -0
- package/src/components/Sortable/Sortable.tokens.scss +27 -0
- package/src/components/Sortable/Sortable.tsx +572 -0
- package/src/components/Sortable/containerAwareCollision.ts +125 -0
- package/src/components/Sortable/index.ts +13 -0
- package/src/components/SortableGroup/SortableGroup.module.scss +23 -0
- package/src/components/SortableGroup/SortableGroup.tsx +315 -0
- package/src/components/SortableGroup/index.ts +4 -0
- package/src/components/SortableGroup/moveSortableItem.ts +39 -0
- package/src/components/Sparkline/Sparkline.module.scss +28 -0
- package/src/components/Sparkline/Sparkline.tokens.scss +7 -0
- package/src/components/Sparkline/Sparkline.tsx +133 -0
- package/src/components/Sparkline/index.ts +2 -0
- package/src/components/Sparkline/sparklineModel.ts +119 -0
- package/src/components/Split/Split.module.scss +158 -0
- package/src/components/Split/Split.tokens.scss +10 -0
- package/src/components/Split/Split.tsx +231 -0
- package/src/components/Split/index.ts +2 -0
- package/src/components/Stack/Stack.module.scss +67 -0
- package/src/components/Stack/Stack.tokens.scss +11 -0
- package/src/components/Stack/Stack.tsx +100 -0
- package/src/components/Stack/index.ts +2 -0
- package/src/components/StagePath/StagePath.module.scss +255 -0
- package/src/components/StagePath/StagePath.tokens.scss +43 -0
- package/src/components/StagePath/StagePath.tsx +176 -0
- package/src/components/StagePath/index.ts +2 -0
- package/src/components/StatGroup/StatGroup.module.scss +6 -0
- package/src/components/StatGroup/StatGroup.tsx +70 -0
- package/src/components/StatGroup/StatGroupContext.ts +10 -0
- package/src/components/StatGroup/StatTile.module.scss +99 -0
- package/src/components/StatGroup/StatTile.tokens.scss +35 -0
- package/src/components/StatGroup/StatTile.tsx +143 -0
- package/src/components/StatGroup/index.ts +4 -0
- package/src/components/Sticky/Sticky.module.scss +98 -0
- package/src/components/Sticky/Sticky.tokens.scss +17 -0
- package/src/components/Sticky/Sticky.tsx +103 -0
- package/src/components/Sticky/index.ts +2 -0
- package/src/components/Switch/Switch.module.scss +182 -0
- package/src/components/Switch/Switch.tokens.scss +59 -0
- package/src/components/Switch/Switch.tsx +214 -0
- package/src/components/Switch/index.ts +2 -0
- package/src/components/Table/Table.module.scss +308 -0
- package/src/components/Table/Table.tokens.scss +79 -0
- package/src/components/Table/Table.tsx +381 -0
- package/src/components/Table/index.ts +15 -0
- package/src/components/TableOfContents/TableOfContents.module.scss +83 -0
- package/src/components/TableOfContents/TableOfContents.tokens.scss +26 -0
- package/src/components/TableOfContents/TableOfContents.tsx +192 -0
- package/src/components/TableOfContents/activeSection.ts +18 -0
- package/src/components/TableOfContents/index.ts +2 -0
- package/src/components/Tabs/Tabs.module.scss +359 -0
- package/src/components/Tabs/Tabs.tokens.scss +50 -0
- package/src/components/Tabs/Tabs.tsx +538 -0
- package/src/components/Tabs/index.ts +2 -0
- package/src/components/Text/Text.module.scss +115 -0
- package/src/components/Text/Text.tokens.scss +55 -0
- package/src/components/Text/Text.tsx +197 -0
- package/src/components/Text/index.ts +2 -0
- package/src/components/Textarea/Textarea.module.scss +94 -0
- package/src/components/Textarea/Textarea.tokens.scss +48 -0
- package/src/components/Textarea/Textarea.tsx +246 -0
- package/src/components/Textarea/index.ts +2 -0
- package/src/components/Thread/Thread.module.scss +161 -0
- package/src/components/Thread/Thread.tokens.scss +32 -0
- package/src/components/Thread/Thread.tsx +176 -0
- package/src/components/Thread/index.ts +2 -0
- package/src/components/TimeField/TimeField.module.scss +232 -0
- package/src/components/TimeField/TimeField.tsx +793 -0
- package/src/components/TimeField/index.ts +2 -0
- package/src/components/Timeline/Timeline.module.scss +58 -0
- package/src/components/Timeline/Timeline.tokens.scss +13 -0
- package/src/components/Timeline/Timeline.tsx +53 -0
- package/src/components/Timeline/index.ts +2 -0
- package/src/components/Title/Title.module.scss +84 -0
- package/src/components/Title/Title.tokens.scss +37 -0
- package/src/components/Title/Title.tsx +150 -0
- package/src/components/Title/index.ts +2 -0
- package/src/components/Toast/Toast.module.scss +299 -0
- package/src/components/Toast/Toast.tokens.scss +65 -0
- package/src/components/Toast/Toast.tsx +122 -0
- package/src/components/Toast/ToastViewport.tsx +166 -0
- package/src/components/Toast/api.ts +175 -0
- package/src/components/Toast/index.ts +5 -0
- package/src/components/Toast/store.ts +133 -0
- package/src/components/Toast/useToastTimer.ts +94 -0
- package/src/components/Tooltip/Tooltip.module.scss +99 -0
- package/src/components/Tooltip/Tooltip.tokens.scss +23 -0
- package/src/components/Tooltip/Tooltip.tsx +341 -0
- package/src/components/Tooltip/index.ts +2 -0
- package/src/components/TopBar/TopBar.module.scss +235 -0
- package/src/components/TopBar/TopBar.tokens.scss +79 -0
- package/src/components/TopBar/TopBar.tsx +87 -0
- package/src/components/TopBar/TopBarEnd.tsx +20 -0
- package/src/components/TopBar/TopBarIconButton.tsx +86 -0
- package/src/components/TopBar/TopBarSearch.tsx +319 -0
- package/src/components/TopBar/TopBarSearchPanel.tsx +268 -0
- package/src/components/TopBar/TopBarStart.tsx +20 -0
- package/src/components/TopBar/index.ts +11 -0
- package/src/components/Tour/Spotlight.tsx +121 -0
- package/src/components/Tour/Tour.module.scss +210 -0
- package/src/components/Tour/Tour.tokens.scss +58 -0
- package/src/components/Tour/Tour.tsx +617 -0
- package/src/components/Tour/index.ts +2 -0
- package/src/components/Tour/useTourTarget.ts +115 -0
- package/src/components/TreeView/TreeView.module.scss +268 -0
- package/src/components/TreeView/TreeView.tokens.scss +36 -0
- package/src/components/TreeView/TreeView.tsx +536 -0
- package/src/components/TreeView/index.ts +3 -0
- package/src/components/TreeView/treeModel.ts +224 -0
- package/src/components/VisuallyHidden/VisuallyHidden.module.scss +7 -0
- package/src/components/VisuallyHidden/VisuallyHidden.tsx +29 -0
- package/src/components/VisuallyHidden/index.ts +2 -0
- package/src/components/WidgetPreview/WidgetPreview.module.scss +11 -0
- package/src/components/WidgetPreview/WidgetPreview.tokens.scss +26 -0
- package/src/components/WidgetPreview/WidgetPreview.tsx +36 -0
- package/src/components/WidgetPreview/WidgetShape.module.scss +140 -0
- package/src/components/WidgetPreview/WidgetShape.tsx +159 -0
- package/src/components/WidgetPreview/index.ts +2 -0
- package/src/components/_internal/Trend.tsx +27 -0
- package/src/components/_internal/choiceTile.scss +74 -0
- package/src/components/_internal/collapse.scss +10 -0
- package/src/components/_internal/collapse.ts +58 -0
- package/src/components/_internal/currency.ts +257 -0
- package/src/components/_internal/dragAnnouncements.ts +200 -0
- package/src/components/_internal/fieldWiring.ts +225 -0
- package/src/components/_internal/gridSpan.ts +54 -0
- package/src/components/_internal/isoDay.ts +35 -0
- package/src/components/_internal/letterKey.ts +26 -0
- package/src/components/_internal/overlay/focusRestore.ts +21 -0
- package/src/components/_internal/overlay/index.ts +12 -0
- package/src/components/_internal/overlay/tourHold.ts +23 -0
- package/src/components/_internal/overlay/useFocusTrap.ts +120 -0
- package/src/components/_internal/overlay/useInOverlay.ts +81 -0
- package/src/components/_internal/overlay/useOverlayStack.ts +232 -0
- package/src/components/_internal/overlay/useScrollLock.ts +112 -0
- package/src/components/_internal/refs.ts +48 -0
- package/src/components/_internal/statusColor.ts +26 -0
- package/src/components/_internal/timeUtils.ts +23 -0
- package/src/components/_internal/useAsyncOptions.ts +145 -0
- package/src/components/_internal/useClippedTooltip.ts +50 -0
- package/src/components/_internal/useControllableState.ts +50 -0
- package/src/components.manifest.json +1141 -0
- package/src/hooks/useBelowBreakpoint.ts +58 -0
- package/src/i18n/I18nProvider.tsx +52 -0
- package/src/i18n/LocaleProvider.tsx +17 -0
- package/src/i18n/en.ts +648 -0
- package/src/i18n/format.ts +71 -0
- package/src/i18n/index.ts +13 -0
- package/src/i18n/messages.ts +1275 -0
- package/src/i18n/ru.ts +677 -0
- package/src/i18n/useLocale.ts +19 -0
- package/src/i18n/useTranslation.ts +58 -0
- package/src/index.ts +913 -0
- package/src/palette/index.ts +2 -0
- package/src/palette/palette.ts +108 -0
- package/src/styles/dark.scss +4 -0
- package/src/styles/global.scss +7 -0
- package/src/styles/mixins.scss +143 -0
- package/src/styles/reset.scss +78 -0
- package/src/styles/tokens.scss +18 -0
- package/src/styles/typography.scss +44 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# `<Modal>` — focus-locked dialog
|
|
2
|
+
|
|
3
|
+
```tsx
|
|
4
|
+
const [open, setOpen] = useState(false);
|
|
5
|
+
|
|
6
|
+
<Button onClick={() => setOpen(true)}>Edit contact</Button>
|
|
7
|
+
|
|
8
|
+
<Modal open={open} onOpenChange={setOpen} size="md">
|
|
9
|
+
<Modal.Header>Edit contact</Modal.Header>
|
|
10
|
+
<Modal.Body>
|
|
11
|
+
<Stack gap="md">
|
|
12
|
+
<Input label="Name" value={name} onChange={(e) => setName(e.target.value)} />
|
|
13
|
+
</Stack>
|
|
14
|
+
</Modal.Body>
|
|
15
|
+
<Modal.Footer>
|
|
16
|
+
<Modal.Close><Button variant="secondary">Cancel</Button></Modal.Close>
|
|
17
|
+
<Button onClick={save}>Save</Button>
|
|
18
|
+
</Modal.Footer>
|
|
19
|
+
</Modal>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
<!-- props:start -->
|
|
23
|
+
|
|
24
|
+
## Props
|
|
25
|
+
|
|
26
|
+
### `ModalProps`
|
|
27
|
+
|
|
28
|
+
<!-- prettier-ignore -->
|
|
29
|
+
| Prop | Type | Required | Description |
|
|
30
|
+
|---|---|---|---|
|
|
31
|
+
| `open` | `boolean` | yes | Controlled open state. Required. |
|
|
32
|
+
| `onOpenChange` | `(open: boolean) => void` | yes | Fired when Modal wants to change open state — Esc, overlay click, Close button, programmatic. |
|
|
33
|
+
| `size` | `'sm' \| 'md' \| 'lg' \| 'full'` | no | Size preset. Defaults to `'md'`. - `'sm'` (400px) — confirms and short prompts. - `'md'` (560px) — the default; typical forms. - `'lg'` (800px) — wide forms, tables, previews. - `'full'` (95vw × 90dvh, fixed height) — long documents, e.g. an HTML email. See {@link ModalSize}. |
|
|
34
|
+
| `overlay` | `'solid' \| 'blur'` | no | Overlay variant. 'solid' (default) paints a dark dimming layer. 'blur' uses a light tinted background plus `backdrop-filter: blur(4px)` for a frosted-glass effect. 'blur' costs an extra compositor layer — fine for normal use; avoid stacking three blurred modals at once. |
|
|
35
|
+
| `stackMode` | `'replace' \| 'overlay'` | no | How this modal relates to existing modals in the stack when it opens. - `'overlay'` (default): if there's a modal below this one, it stays visible underneath. This modal's own overlay paints transparent so the parent's dim shows through. Only the bottom modal (depth 0) paints the actual dim/blur. The user sees the parent's context behind the active modal. - `'replace'`: any modals below this one are hidden via `display: none` (React state preserved). This modal paints its own overlay normally. Best for forced-step modals where the parent context is irrelevant. Has no effect when this is the only open modal. |
|
|
36
|
+
| `disableEscapeClose` | `boolean` | no | Disable Escape-to-close. Default false. Combined with `dismissOnOverlayClick: false` and omitting `<Modal.Close>` produces a fully forced step. |
|
|
37
|
+
| `dismissOnOverlayClick` | `boolean` | no | When false, clicking the overlay backdrop does NOT close the modal. Default true. |
|
|
38
|
+
| `initialFocusRef` | `RefObject<HTMLElement \| null>` | no | Initial focus target on open. Default: the dialog container itself. Pass a ref to override (e.g. focus the first input in a form). |
|
|
39
|
+
| `returnFocusRef` | `RefObject<HTMLElement \| null>` | no | Where focus goes when the modal closes. Default: back to whatever was focused when it opened. That default breaks whenever the opener is gone by close time — a row's "⋯" trigger whose row was just deleted, or a button that unmounts in the same commit that opens the modal (the capture then snapshots `<body>`). Focus lands on `<body>` and a keyboard user loses their place. The ref is read **at close time**, not at open time, so it may be pointed at whatever still exists once the work is done — the next row's trigger, the empty state's first button. If it is empty or its element has also left the document, Modal falls back to the captured opener, and then to doing nothing, exactly as before this prop existed. The target is scrolled into view (`block: 'nearest'`) after focus; the captured opener is not. |
|
|
40
|
+
| `children` | `ReactNode` | yes | Compound children: Header / Body / Footer / Close + any consumer JSX. |
|
|
41
|
+
| `className` | `string` | no | className passes through to the dialog container. |
|
|
42
|
+
| `style` | `CSSProperties` | no | style passes through to the dialog container. |
|
|
43
|
+
| `aria-label` | `string` | no | Required for a11y when no Modal.Header is rendered. When Header IS rendered, aria-labelledby auto-binds to the heading id; this prop is then ignored. |
|
|
44
|
+
| `aria-describedby` | `string` | no | Optional id of an external descriptor element; sets aria-describedby on the dialog. |
|
|
45
|
+
|
|
46
|
+
### `ModalBodyProps`
|
|
47
|
+
|
|
48
|
+
<!-- prettier-ignore -->
|
|
49
|
+
| Prop | Type | Required | Description |
|
|
50
|
+
|---|---|---|---|
|
|
51
|
+
| `padding` | `'none' \| 'default'` | no | Override body's padding. Default 'default' (--space-4). 'none' for edge-to-edge content. |
|
|
52
|
+
| …native | | | plus native `<div>` attributes |
|
|
53
|
+
|
|
54
|
+
### `ModalCloseProps`
|
|
55
|
+
|
|
56
|
+
<!-- prettier-ignore -->
|
|
57
|
+
| Prop | Type | Required | Description |
|
|
58
|
+
|---|---|---|---|
|
|
59
|
+
| `children` | `ReactElement<unknown, string \| JSXElementConstructor<any>>` | yes | Exactly one React element. Close clones this element to inject an `onClick` that closes the modal, chained with the child's existing `onClick` (consumer runs first). |
|
|
60
|
+
|
|
61
|
+
### `ModalFooterProps`
|
|
62
|
+
|
|
63
|
+
<!-- prettier-ignore -->
|
|
64
|
+
| Prop | Type | Required | Description |
|
|
65
|
+
|---|---|---|---|
|
|
66
|
+
| `align` | `'start' \| 'end' \| 'space-between'` | no | Horizontal action alignment. Default 'end'. |
|
|
67
|
+
| …native | | | plus native `<div>` attributes |
|
|
68
|
+
|
|
69
|
+
### `ModalHeaderProps`
|
|
70
|
+
|
|
71
|
+
<!-- prettier-ignore -->
|
|
72
|
+
| Prop | Type | Required | Description |
|
|
73
|
+
|---|---|---|---|
|
|
74
|
+
| `closeButton` | `boolean` | no | Show the built-in × close button on the right edge. Default true. |
|
|
75
|
+
| …native | | | plus native `<div>` attributes |
|
|
76
|
+
|
|
77
|
+
<!-- props:end -->
|
|
78
|
+
|
|
79
|
+
- **Controlled-only.** Pass `open` + `onOpenChange` always. There is no `<Modal.Trigger>` — wire your own button(s).
|
|
80
|
+
- **Four sizes:** `sm` (400px), `md` (560px, default), `lg` (800px) size to their content; `full` is a near-full-screen reader, a **fixed** 95vw × 90dvh by default (tokens `--modal-content-width-full` / `--modal-content-height-full`) with Body scrolling between a pinned Header/Footer. Use it for long documents like an HTML email, not for short forms, which float in a mostly empty box. Below 640px every size goes fullscreen.
|
|
81
|
+
- **Overlay variant:** `overlay="solid"` (default, dark dim) or `overlay="blur"` (frosted-glass effect — light tint + `backdrop-filter: blur(4px)`). Avoid stacking three blurred modals — extra compositor cost per layer.
|
|
82
|
+
- **`<Modal.Header>` auto-wires `aria-labelledby`.** Pass `closeButton={false}` to hide the built-in × button (e.g. forced-step modals). The Header's children become the dialog title.
|
|
83
|
+
- **`<Modal.Body>` scrolls** when content overflows. `padding="none"` for edge-to-edge children (e.g. a tabs strip).
|
|
84
|
+
- **Footer actions wrap, not clip.** When the actions do not fit on one row (e.g. three buttons in `size="sm"`), they wrap onto a second row.
|
|
85
|
+
- **`<Modal.Footer>` defaults to right-aligned actions.** Use `align="space-between"` to split a danger action away from save/cancel.
|
|
86
|
+
- **`<Modal.Close>`** wraps a clickable child and fires `onOpenChange(false)` on click. Chains with the child's existing `onClick`.
|
|
87
|
+
- **Forced step:** combine `disableEscapeClose`, `dismissOnOverlayClick={false}`, omit `<Modal.Close>`, and pass `<Modal.Header closeButton={false}>` to lock the user into the modal until they resolve it programmatically.
|
|
88
|
+
- **Stacked modals.** Default `stackMode="overlay"`: the parent stays visible underneath and the inner overlay paints transparent so the parent's dim shows through (one effective dim layer for the stack). Use `stackMode="replace"` to hide the parent via `display: none` (React state preserved) — best for forced steps where the parent context is irrelevant. Escape still closes only the topmost — and yields to any open floating surface first (Select/Popover/menu/date-time popover: the first press closes the surface, the next closes the modal); body scroll stays locked across the whole stack.
|
|
89
|
+
- **Initial focus:** pass `initialFocusRef` to focus a specific element (e.g. the first input). Otherwise the dialog container receives focus and the focus trap takes over.
|
|
90
|
+
- **Return focus:** on close, focus goes back to whatever was focused when the modal opened. When that element will not survive the modal — a deleted row's `⋯` trigger, or a button that unmounts in the same commit that opens the modal — pass `returnFocusRef` and point it at something that still exists. It is read at CLOSE time, so you can set `returnFocusRef.current` after the work finishes (next row's trigger, or the empty state's first button). If it is empty or detached, Modal falls back to the captured opener, then to doing nothing. A `returnFocusRef` target is scrolled into view (`{ block: 'nearest' }`) since it may be far from where the user was; the captured opener is not scrolled. A Modal unmounted while still open (e.g. `{row && <Modal open …/>}`) restores the same way, but only if focus was lost — an element your code focused after the unmount keeps it.
|
|
91
|
+
|
|
92
|
+
**Anti-patterns:**
|
|
93
|
+
|
|
94
|
+
- ❌ Rendering a Modal as a child of another component that itself uses `position: fixed` — the portal escapes that container anyway, so the fixed positioning is dead code. Just render `<Modal>` at any level; it portals to `document.body`.
|
|
95
|
+
- ❌ Calling `onOpenChange={() => {}}` AND providing `<Modal.Close>` — the Close button calls `onOpenChange(false)` which then no-ops. Use `disableEscapeClose + dismissOnOverlayClick={false}` + omit Close for forced steps.
|
|
96
|
+
- ❌ Mutating an `initialFocusRef.current` value after open — Modal reads the ref when the modal opens AND whenever it becomes the top of the stack again (e.g., after a nested modal closes). Don't rely on a specific number of reads; instead, ensure the ref points at a stable element while the modal is open.
|
|
97
|
+
- ❌ Moving focus yourself in an effect after `onOpenChange(false)` — you race Modal's own restore, which runs in a layout effect. Pass `returnFocusRef` instead.
|
|
98
|
+
- ❌ Using `<Modal>` for popovers or non-blocking notifications. Use `<Popover>`, `<DropdownMenu>`, or wait for `<Toast>`.
|
|
99
|
+
|
|
100
|
+
- **`<Modal.Footer>`** is `role="group"` so screen readers announce the action set as a unit.
|
|
101
|
+
- **`<Modal.Close>`** requires exactly one React element child (it throws otherwise).
|
|
102
|
+
- ❌ Long scrollable forms with sticky footers containing additional sticky elements inside Body — flexbox + sticky compose badly. Use `<Modal.Footer>` for the actions and let Body scroll.
|
|
103
|
+
- ❌ Opening a modal from inside another modal without using `<Modal>` itself (e.g. a custom div with `position: fixed`) — skipping the stack registry breaks Esc routing and z-index ordering.
|
|
104
|
+
- ❌ Passing neither a `<Modal.Header>` nor an `aria-label` — warns in development; screen-reader users get no announcement on open.
|
|
105
|
+
- ❌ Inline confirms attached to a button — use `<ConfirmationPopover>`.
|
|
106
|
+
|
|
107
|
+
**See also:** `<Drawer>` for edge-anchored variant.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# `<MoneyInput>`
|
|
2
|
+
|
|
3
|
+
Money field: currency picker (`CurrencySelect`) + formatted amount `Input`, controlled on one `value: MoneyValue` (`{ amount: string | null; currency: string }`), `onChange(value)`. `amount` is a **canonical decimal string** (`"1234.50"`, `"-3"`) or `null` — never a JS number. `currencies` (ISO 4217, required) lists what the picker offers; exactly one renders a static prefix instead. The amount groups as you type in the locale's separators, keeps the caret where you are editing, rejects keystrokes past the currency's precision (2 for EUR, 0 for JPY, 3 for KWD) — a digit typed inside a full fraction overwrites the next one. Backspace/Delete step over group separators, and over the decimal while a fraction follows it (select the decimal to remove it). Blur pads the fraction (`12` → `12.00`). Switching currency rounds the amount half-up to the new precision (and pads it, as blur would). An external `amount` that is not a canonical decimal string displays empty with a dev warning. In comma-decimal locales the numpad `.` key types the decimal; a paste or drop is parsed on its own, then spliced over the selection. It must be exactly one number, optionally with currency symbols, a label word before it (`Total`, `Итого:`) and a currency word after it (an ISO code or `руб.`/`р.` — never a magnitude like `K`, `млн`, `million`) and a sign (`-5`, `−5`, `+5`, `5-`, `(1,234.50)`). Grouping must be unambiguous — first group 1–3 digits, then exactly 3, one kind of group character (`,` `.` space, NBSP, apostrophe) — and with both `.` and `,` the last one is the decimal whatever the locale. Anything doubtful (`12,34` or `1.234` in en, `15.01.2024`, `$5K`, `5 10`, a line break, extra words, a second number, stray parentheses) is rejected with no change, as is a partial paste that adds a second decimal, a sign, or fraction digits past the precision. A whole-value paste truncates to the precision (`$1,234.5678` → `1,234.56`). A negative paste must replace the whole value and needs `allowNegative` (or an already-negative value). `allowNegative` permits a leading `-`; without it, adding a `-` (typed or pasted) is rejected rather than silently dropped, and an existing external negative keeps its sign while edited. `onChange` fires on every edit and currency switch — never for blur-without-edit or external value changes. `name` emits hidden `<name>` / `<name>_currency` inputs.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
const [price, setPrice] = useState<MoneyValue>({ amount: null, currency: 'EUR' });
|
|
7
|
+
<MoneyInput
|
|
8
|
+
aria-label="Price"
|
|
9
|
+
currencies={['EUR', 'USD', 'GBP']}
|
|
10
|
+
value={price}
|
|
11
|
+
onChange={setPrice}
|
|
12
|
+
/>;
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
<!-- props:start -->
|
|
16
|
+
|
|
17
|
+
## Props
|
|
18
|
+
|
|
19
|
+
<!-- prettier-ignore -->
|
|
20
|
+
| Prop | Type | Required | Description |
|
|
21
|
+
|---|---|---|---|
|
|
22
|
+
| `value` | `MoneyValue` | yes | Controlled value. `amount` is a canonical decimal string or `null`; `currency` an ISO 4217 code. |
|
|
23
|
+
| `onChange` | `(value: MoneyValue) => void` | yes | Fires on every amount edit and currency switch. Never fires for blur-without-edit or external value changes. |
|
|
24
|
+
| `currencies` | `string[]` | yes | ISO 4217 codes to offer. Exactly one renders a static prefix instead of a picker. |
|
|
25
|
+
| `currencyDisplay` | `'code' \| 'symbol' \| 'both'` | no | How the selected currency renders: `"code"` (default), `"symbol"` or `"both"`. |
|
|
26
|
+
| `currencyLabel` | `string` | no | Overrides the currency picker's accessible name (default: "<group label> Currency"). |
|
|
27
|
+
| `allowNegative` | `boolean` | no | Allow a leading minus. Default `false`. |
|
|
28
|
+
| `locale` | `string` | no | BCP-47 locale for separators and currency names. Defaults to the i18n locale. |
|
|
29
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | no | Control size, forwarded to both controls. Default `"md"`. |
|
|
30
|
+
| `invalid` | `boolean` | no | Error chrome on both controls. |
|
|
31
|
+
| `disabled` | `boolean` | no | Disable both controls. |
|
|
32
|
+
| `readOnly` | `boolean` | no | Make both controls read-only. |
|
|
33
|
+
| `required` | `boolean` | no | Mark the amount required. |
|
|
34
|
+
| `placeholder` | `string` | no | Placeholder for the amount field. |
|
|
35
|
+
| `name` | `string` | no | Emits hidden `<name>` (canonical amount) and `<name>_currency` inputs for native form posts. |
|
|
36
|
+
| `id` | `string` | no | Stable id; placed on the amount field so an external `<label htmlFor>` focuses it. |
|
|
37
|
+
| `aria-label` | `string` | no | Accessible name when used STANDALONE (outside `<Field>`). |
|
|
38
|
+
| `aria-labelledby` | `string` | no | Ids of elements labelling the group (injected by `<Field>`). |
|
|
39
|
+
| `aria-describedby` | `string` | no | Ids of description/error elements; forwarded to the amount field (injected by `<Field>`). |
|
|
40
|
+
| …native | | | plus native `<div>` attributes |
|
|
41
|
+
|
|
42
|
+
<!-- props:end -->
|
|
43
|
+
|
|
44
|
+
Inside a `<Field>` the label and error wiring are injected (the picker is named "Price Currency"):
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
<Field label="Price" error={price.amount === null ? 'Required' : undefined}>
|
|
48
|
+
<MoneyInput currencies={['EUR', 'USD']} value={price} onChange={setPrice} />
|
|
49
|
+
</Field>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**When NOT to use:** a currency without an amount — use `CurrencySelect`. Plain quantities or non-money decimals — `<Input inputMode="decimal">`.
|
|
53
|
+
|
|
54
|
+
**Anti-patterns**
|
|
55
|
+
|
|
56
|
+
- ❌ Storing the formatted display (`"1,234.50"`) — persist the emitted `amount` + `currency`; the display is reconstructed from them.
|
|
57
|
+
- ❌ `parseFloat(amount)` for maths — floats lose cents; use a decimal library or integer minor units.
|
|
58
|
+
- ❌ Comparing `amount` strings for dirtiness — `"12"` and `"12.00"` are the same money (blur pads). Compare amounts numerically (decimal-aware), not as strings.
|
|
59
|
+
|
|
60
|
+
**Known limitations**
|
|
61
|
+
|
|
62
|
+
- Latin digits and 3-digit grouping only: locales with other numerals or grouping (e.g. Indian lakh `12,34,567`) display Latin digits grouped in threes.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# `<OptionsPicker>` — filter picker (multi/single, grouped, searchable)
|
|
2
|
+
|
|
3
|
+
**Use for filter UX, not form fields.** A compound picker that opens a Popover
|
|
4
|
+
with a search input and grouped/flat checkbox (multi) or radio (single) options.
|
|
5
|
+
Multi mode buffers a draft until Apply; single mode commits per click.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
<OptionsPicker selected={events} onApply={setEvents}>
|
|
9
|
+
<OptionsPicker.Trigger>
|
|
10
|
+
<Button variant="secondary">
|
|
11
|
+
Events <ChevronDown size={14} />
|
|
12
|
+
</Button>
|
|
13
|
+
</OptionsPicker.Trigger>
|
|
14
|
+
<OptionsPicker.Content
|
|
15
|
+
label="Filter events"
|
|
16
|
+
groups={catalogGroups} // OR `options={flatOptions}` — XOR
|
|
17
|
+
/>
|
|
18
|
+
</OptionsPicker>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
<!-- props:start -->
|
|
22
|
+
|
|
23
|
+
## Props
|
|
24
|
+
|
|
25
|
+
### `OptionsPickerProps`
|
|
26
|
+
|
|
27
|
+
<!-- prettier-ignore -->
|
|
28
|
+
| Prop | Type | Required | Description |
|
|
29
|
+
|---|---|---|---|
|
|
30
|
+
| `mode` | `"multi" \| "single"` | no | |
|
|
31
|
+
| `selected` | `string[] \| string \| null` | yes | Committed selection: `string[]` (multi) or `string \| null` (single). Multi: pass a stable array (state, or memoized) — a new array identity on every render resets the open panel's draft. |
|
|
32
|
+
| `onApply` | `(next: string[], options: OptionsPickerOption[]) => void \| (next: string \| null, option: OptionsPickerOption \| null) => void` | yes | Called on Apply with the committed values and their option objects, in the same order. |
|
|
33
|
+
| `onCancel` | `(() => void)` | no | |
|
|
34
|
+
| `open` | `boolean` | no | |
|
|
35
|
+
| `onOpenChange` | `((open: boolean) => void)` | no | |
|
|
36
|
+
| `children` | `ReactNode` | yes | |
|
|
37
|
+
|
|
38
|
+
### `OptionsPickerContentProps`
|
|
39
|
+
|
|
40
|
+
<!-- prettier-ignore -->
|
|
41
|
+
| Prop | Type | Required | Description |
|
|
42
|
+
|---|---|---|---|
|
|
43
|
+
| `options` | `OptionsPickerOption[]` | no | |
|
|
44
|
+
| `groups` | `OptionsPickerGroup[]` | no | |
|
|
45
|
+
| `loadOptions` | `(query: string, signal: AbortSignal) => Promise<OptionsPickerOption[]>` | no | Server search: called with the search query (`''` on every open) after `searchDebounceMs`, and its answer replaces the list — no client-side filtering on top. Flat options only. The previous request is aborted via `signal` when a newer one starts. Takes precedence over `options` / `groups` (dev warning if both are passed). The latest function is always the one called; a new identity (e.g. an inline arrow) does not refetch. |
|
|
46
|
+
| `selectedOptions` | `OptionsPickerOption[]` | no | Option objects for currently selected values that may not be in any server answer (e.g. values restored from a URL), so they can be labelled. |
|
|
47
|
+
| `searchDebounceMs` | `number` | no | Delay between the last keystroke (or opening) and the `loadOptions` call. Default `250`. |
|
|
48
|
+
| `label` | `string` | yes | Accessible label on the panel (the dialog's `aria-label`). |
|
|
49
|
+
| `emptyState` | `ReactNode` | no | Rendered when search produces zero matches. Defaults to the `optionsPicker.noMatches` i18n string (en: `'No matches'`). With `loadOptions` the visible row is hidden from assistive tech and a live region announces it instead — this text when it is a string, otherwise the default. An EMPTY string counts as unset, matching every other label override in the library. There is no way to suppress the message entirely — a blank listbox reads as a broken filter rather than as a deliberate silence. |
|
|
50
|
+
| `footerCount` | `((selected: number, total: number) => ReactNode)` | no | Footer count formatter (multi only). Default `'${selected} of ${total}'`. Not called with `loadOptions` — there is no known total, so the footer reads `optionsPicker.selectedCount` (en: `'N selected'`). |
|
|
51
|
+
| `searchable` | `boolean` | no | Whether to render the search bar at the top of the panel. Defaults to `true`. Hide it (`false`) for small/curated option lists where typing filters would just be noise — the selection-count header is hidden alongside the search input (the footer's `N of TOTAL` text still shows in multi mode). Ignored with `loadOptions`: server search needs the input, so it always shows. |
|
|
52
|
+
| `className` | `string` | no | |
|
|
53
|
+
|
|
54
|
+
### `OptionsPickerTriggerProps`
|
|
55
|
+
|
|
56
|
+
<!-- prettier-ignore -->
|
|
57
|
+
| Prop | Type | Required | Description |
|
|
58
|
+
|---|---|---|---|
|
|
59
|
+
| `children` | `ReactElement<unknown, string \| JSXElementConstructor<any>>` | yes | Must be a single React element that accepts a ref (e.g. `<Button>`). |
|
|
60
|
+
|
|
61
|
+
<!-- props:end -->
|
|
62
|
+
|
|
63
|
+
`mode="single"` for single-select: `selected: string | null`, `onApply(value | null)`, no Apply/Cancel footer.
|
|
64
|
+
|
|
65
|
+
Don't use for form selects (use `<Select>`), action menus (use `<DropdownMenu>`),
|
|
66
|
+
or single boolean toggles (use `<Checkbox>` or `<Switch>`).
|
|
67
|
+
|
|
68
|
+
Multi mode (default) keeps a draft until Apply; Cancel, Esc and click-outside revert it. Single mode commits through `onApply` on each click and closes the panel.
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
<OptionsPicker mode="single" selected={tenantId} onApply={setTenantId}>
|
|
72
|
+
<OptionsPicker.Trigger>
|
|
73
|
+
<Button variant="secondary">Tenant</Button>
|
|
74
|
+
</OptionsPicker.Trigger>
|
|
75
|
+
<OptionsPicker.Content
|
|
76
|
+
label="Filter tenant"
|
|
77
|
+
options={tenants.map((t) => ({ value: t.id, label: t.slug }))}
|
|
78
|
+
/>
|
|
79
|
+
</OptionsPicker>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Async (server search)
|
|
83
|
+
|
|
84
|
+
For lists too large to ship to the client, pass `loadOptions` to Content instead of `options` / `groups`.
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
<OptionsPicker
|
|
88
|
+
selected={ids}
|
|
89
|
+
onApply={(next, options) => {
|
|
90
|
+
setIds(next);
|
|
91
|
+
setPicked(options); // use these to label the trigger
|
|
92
|
+
}}
|
|
93
|
+
>
|
|
94
|
+
<OptionsPicker.Trigger>
|
|
95
|
+
<Button variant="secondary">{picked.map((o) => o.label).join(', ') || 'Members'}</Button>
|
|
96
|
+
</OptionsPicker.Trigger>
|
|
97
|
+
<OptionsPicker.Content
|
|
98
|
+
label="Filter members"
|
|
99
|
+
loadOptions={(query, signal) => fetchMembers(query, signal)} // returns { value, label }[]
|
|
100
|
+
selectedOptions={picked} // labels for values restored from URL/state
|
|
101
|
+
/>
|
|
102
|
+
</OptionsPicker>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
- **Fetching.** `loadOptions(query, signal)` returns a flat `OptionsPickerOption[]`. It is called with `''` on every open and after `searchDebounceMs` (default `250`) following each keystroke; the previous request is aborted via `signal`, so pass it to `fetch`. The latest `loadOptions` is always the one called, and a new function identity does not refetch, so an inline arrow is fine. The server's answer replaces the list: there is no client-side filtering on top. The search input is always shown (`searchable` is ignored).
|
|
106
|
+
- **Rows.** Loading, error (with a Retry action) and "No matches" rows are built in. Groups are not supported in async mode (flat only). `loadOptions` wins over `options` / `groups` (dev warning if both are passed).
|
|
107
|
+
- **Selected rows are pinned** above the server results (snapshot taken on open and on each query change); ticking or unticking never moves rows under the pointer.
|
|
108
|
+
- **Labels.** Selected values are labelled from a cache of every answer seen while the picker is mounted plus `selectedOptions`; an unknown value falls back to its raw string. **Pass `selectedOptions` for values restored from URL/state** (e.g. a deep link), otherwise their rows read as raw ids.
|
|
109
|
+
- **`onApply` second argument.** Multi: `onApply(values, options)`; single: `onApply(value, option | null)`. Use it to label the trigger and keep `selectedOptions` for the next render. Also works for static `options` / `groups`.
|
|
110
|
+
- **Footer.** Multi mode reads "N selected"; `footerCount` is not called (there is no known total).
|
|
111
|
+
- **Screen readers (Hard rule 10).** One polite live region announces loading, failure and no-matches (a failure reads "Failed to load options.", no-matches uses `emptyState` when it is a string, otherwise the default). The visible loading / error / empty rows are hidden from assistive tech so nothing is announced twice. Success is silent: the list is the result.
|
|
112
|
+
|
|
113
|
+
`<OptionsPicker.Trigger>` takes a single ref-accepting element (typically a `<Button>`); open wiring and ARIA (`aria-haspopup`, `aria-expanded`, `aria-controls`) are injected, and your own ref on the child still receives the DOM node. `<OptionsPicker.Content>` hosts the search bar, the option list and (multi mode) the Apply/Cancel footer.
|
|
114
|
+
|
|
115
|
+
OptionsPicker has no `name` attribute and no form association. Action menus (Edit / Delete / Archive) are commands, not filters — use `<DropdownMenu>`.
|
|
116
|
+
|
|
117
|
+
**Anti-patterns**
|
|
118
|
+
|
|
119
|
+
- ❌ Passing BOTH `options` and `groups` to Content — TypeScript rejects it. Pick one.
|
|
120
|
+
- ❌ Calling `onApply` yourself inside Content's render — the picker owns the commit; treat `onApply(next)` as the single source of truth and update React state from it.
|
|
121
|
+
- ❌ Multi-mode `selected` as a fresh array each render (e.g. `selected={ids.filter(…)}` inline) — a new identity resets the open panel's draft. Keep it in state or `useMemo`.
|
|
122
|
+
- ❌ Async mode with `groups`, or restoring `selected` from a URL without `selectedOptions` (rows show raw ids).
|
|
123
|
+
- ❌ Controlling open state without passing both `open` and `onOpenChange` — partial control breaks invariants.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# `<OtpInput>` — one-time-code field
|
|
2
|
+
|
|
3
|
+
```tsx
|
|
4
|
+
const [code, setCode] = useState('');
|
|
5
|
+
<OtpInput value={code} onChange={setCode} onComplete={verify} />;
|
|
6
|
+
<OtpInput length={4} type="alphanumeric" aria-label="Invite code" />;
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
<!-- props:start -->
|
|
10
|
+
|
|
11
|
+
## Props
|
|
12
|
+
|
|
13
|
+
<!-- prettier-ignore -->
|
|
14
|
+
| Prop | Type | Required | Description |
|
|
15
|
+
|---|---|---|---|
|
|
16
|
+
| `length` | `number` | no | How many boxes to render. Default `6`. |
|
|
17
|
+
| `value` | `string` | no | Controlled code. Sanitized and truncated to `length` on render, so a consumer echoing back a dirty string sees it cleaned rather than mis-sliced. Pair with `onChange`. |
|
|
18
|
+
| `defaultValue` | `string` | no | Uncontrolled seed. Ignored when `value` is supplied. |
|
|
19
|
+
| `onChange` | `((value: string) => void)` | no | Fires on every edit with the whole code so far — always sanitized, never longer than `length`, and never with a gap in the middle. |
|
|
20
|
+
| `onComplete` | `((value: string) => void)` | no | Fires whenever the code becomes full with a value different from the last one — not on every keystroke while it is full. Use it to submit. Correcting a wrong character inside an already-full code re-fires with the corrected value; retyping the same character does not, since nothing changed. |
|
|
21
|
+
| `type` | `'numeric' \| 'alphanumeric'` | no | Character set. Default `'numeric'`. See `OtpInputType`. |
|
|
22
|
+
| `size` | `'sm' \| 'md' \| 'lg' \| 'xl'` | no | Cell size. Default `'md'`. - `'sm'` — 24px cells; dense admin screens. - `'md'` — 32px cells (default); most forms. - `'lg'` — 40px cells; a dedicated verification screen. - `'xl'` — 48px cells; a code field that IS the screen (IdP login challenge, touch-first). Six cells still fit a 360px phone. No `<Input>` counterpart at this height — don't line it up with fields. |
|
|
23
|
+
| `invalid` | `boolean` | no | Error visual on every cell plus `aria-invalid="true"`. Pair with a visible message and point `aria-describedby` at it (or wrap in `<Field error>`). |
|
|
24
|
+
| `disabled` | `boolean` | no | Disable every cell. |
|
|
25
|
+
| `required` | `boolean` | no | Marks the field required. There is no single native input to attach the native `required` attribute to — a per-cell `required` would let the browser block submit on an empty first cell while accepting a 1-of-`length` code as complete, which is worse than no native gate at all. Sets `aria-required="true"` on every cell instead (the group's `role="group"` does not support `aria-required` per ARIA 1.2 — only `textbox` and a handful of other roles do), the same way `aria-invalid` and `aria-describedby` are already repeated on every cell. Validating completeness is the consumer's job (pair with `invalid` + `onComplete`). |
|
|
26
|
+
| `autoFocus` | `boolean` | no | Focus the first cell on mount. |
|
|
27
|
+
| `id` | `string` | no | Id for the first cell, so an external `<label htmlFor>` focuses the field. |
|
|
28
|
+
| `aria-label` | `string` | no | Accessible name for the group. Defaults to the localized `otpInput.groupLabel` ("Verification code") when omitted OR empty — an empty string is not an explicit name — so a standalone OtpInput is never an unnamed group. |
|
|
29
|
+
| `aria-labelledby` | `string` | no | Ids of elements naming the group. Injected by `<Field>`; wins over `aria-label`. |
|
|
30
|
+
| `aria-describedby` | `string` | no | Ids of description / error elements. Set on EVERY cell — see the remarks. |
|
|
31
|
+
| …native | | | plus native `<div>` attributes |
|
|
32
|
+
|
|
33
|
+
<!-- props:end -->
|
|
34
|
+
|
|
35
|
+
- Every box carries `autocomplete="one-time-code"` + `inputMode`, so iOS and Android offer the code from a just-arrived SMS/email above the keyboard. No WebOTP — it is Android-Chrome-only and needs an origin-bound SMS body.
|
|
36
|
+
- Focus advances as characters are typed. Boxes select their content on focus, so typing over a filled box replaces it, and a delivery shorter than the full code (a keystroke, or a short paste) spreads from whichever box is focused. A delivery of EXACTLY the full length — SMS/email autofill dumping it into whichever box happens to be focused — replaces the whole value instead of splicing in at that position. A delivery LONGER than the full length (an over-long paste) still splices in at the focused box and truncates, the same as a short delivery. **Never cap a box at one character** — that truncates an autofilled code to its first digit.
|
|
37
|
+
- Roving tabindex — the whole group is one Tab stop. Arrows / Home / End move between boxes; Backspace on an empty box steps back. Deleting mid-code clears from that box onward, keeping the value contiguous.
|
|
38
|
+
- Never masks. For a secret the user must not see → `<PasswordInput>`.
|
|
39
|
+
- Not a native form control — it renders no named field, so nothing reaches `FormData`. Read the code from `onChange` / `onComplete` and submit it yourself. `required` sets `aria-required` on every box, not the group (`role="group"` doesn't support `aria-required`; a per-cell native `required` would instead pass a 1-of-`length` code as valid); validating completeness is yours.
|
|
40
|
+
|
|
41
|
+
Inside a `<Field>`, the label and error wiring are supplied:
|
|
42
|
+
|
|
43
|
+
```tsx
|
|
44
|
+
<Field label="Invite code" error={error}>
|
|
45
|
+
<OtpInput length={4} type="alphanumeric" invalid={!!error} />
|
|
46
|
+
</Field>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**When NOT to use**
|
|
50
|
+
|
|
51
|
+
- A code the user copies rather than reads — a plain `<Input>` pastes just as well and does not fight the caret.
|
|
52
|
+
- Codes longer than about eight characters — the boxes stop being scannable; use `<Input>`.
|
|
53
|
+
- A password or PIN you need masked — `<PasswordInput>`. A one-time code is read off a phone and typed once; hiding it only costs the user the ability to spot a typo.
|
|
54
|
+
|
|
55
|
+
**Anti-patterns**
|
|
56
|
+
|
|
57
|
+
- ❌ Setting `maxLength` on the cells via `className` hacks or a fork — a one-character cap truncates an autofilled code to its first character.
|
|
58
|
+
- ❌ Validating inside `onChange` and rejecting characters. The component already sanitizes; your form layer owns whether the code is correct.
|
|
59
|
+
- ❌ Reading the code out of the DOM. It arrives in `onChange` / `onComplete`.
|
|
60
|
+
|
|
61
|
+
**Accessibility**
|
|
62
|
+
|
|
63
|
+
- The wrapper is a `role="group"`, named by `aria-labelledby`, then `aria-label`, then the localized default ("Verification code"; an empty `aria-label` also falls back). Each cell is named by position ("Digit 3 of 6").
|
|
64
|
+
- There is deliberately no live region: `invalid` is a durable property already carried by `aria-invalid`, so announcing the outcome of a code check is the consumer's job.
|
|
65
|
+
- A paste or autofill that delivers the whole code fills every box in one update; a screen reader announces only the cell focus lands on ("Digit 6 of 6"). Announce the result yourself (e.g. off `onComplete`) if silent multi-box fills would confuse your users.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# `<Page>` — page-root layout primitive
|
|
2
|
+
|
|
3
|
+
```tsx
|
|
4
|
+
// Canonical CRM page shape
|
|
5
|
+
<Page>
|
|
6
|
+
<PageHeader>
|
|
7
|
+
<PageHeader.Title>Contacts</PageHeader.Title>
|
|
8
|
+
</PageHeader>
|
|
9
|
+
<Card>{filters}</Card>
|
|
10
|
+
<Table>{rows}</Table>
|
|
11
|
+
</Page>
|
|
12
|
+
|
|
13
|
+
// Per-page rhythm override (rare — only when 'lg' doesn't fit)
|
|
14
|
+
<Page gap="md">
|
|
15
|
+
{denseDashboardSections}
|
|
16
|
+
</Page>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
<!-- props:start -->
|
|
20
|
+
|
|
21
|
+
## Props
|
|
22
|
+
|
|
23
|
+
<!-- prettier-ignore -->
|
|
24
|
+
| Prop | Type | Required | Description |
|
|
25
|
+
|---|---|---|---|
|
|
26
|
+
| `gap` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| '2xl'` | no | Vertical rhythm between top-level page sections. - `xs` (4) / `sm` (8) / `md` (12) — tighter than canonical; rare. - `lg` (16, **default**) — the canonical CRM page rhythm; matches every shipped mockup. - `xl` (24) / `2xl` (32) — looser; for spacious overview / hero pages. |
|
|
27
|
+
| `children` | `ReactNode` | yes | |
|
|
28
|
+
| …native | | | plus native `<div>` attributes |
|
|
29
|
+
|
|
30
|
+
<!-- props:end -->
|
|
31
|
+
|
|
32
|
+
- Page is the OUTER wrapper at the page root. Inside it, sections compose with `<PageHeader>`, `<Card>`, `<Table>`, etc.
|
|
33
|
+
- **Use Page at the page root, not nested.** For sub-regions (inside a card, modal, drawer), use `<Stack>` instead — those contexts have their own padding contract.
|
|
34
|
+
- Page does NOT add padding. The page container (AppShell content, modal body) provides outer padding; Page just provides inner section rhythm.
|
|
35
|
+
- Page is intentionally thin — a renamed Stack with a page-level default. It exists so future page-level concerns (max-width, scroll restoration, container queries) have a natural home.
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# `<PageHeader>` — top-of-page heading area
|
|
2
|
+
|
|
3
|
+
```tsx
|
|
4
|
+
<PageHeader>
|
|
5
|
+
<PageHeader.Breadcrumb>
|
|
6
|
+
<Breadcrumb items={[...]} />
|
|
7
|
+
</PageHeader.Breadcrumb>
|
|
8
|
+
<PageHeader.BackButton href="/contacts" aria-label="Back to contacts" />
|
|
9
|
+
<PageHeader.Aside>
|
|
10
|
+
<Avatar size="lg" name="Acme Corp" />
|
|
11
|
+
</PageHeader.Aside>
|
|
12
|
+
<PageHeader.Title>Acme Corporation</PageHeader.Title>
|
|
13
|
+
<PageHeader.Subtitle>Founded 2014 · 230 employees</PageHeader.Subtitle>
|
|
14
|
+
<PageHeader.Meta>
|
|
15
|
+
<Badge tone="success">Active</Badge>
|
|
16
|
+
<Text size="sm" tone="muted">Last contacted 2 days ago</Text>
|
|
17
|
+
</PageHeader.Meta>
|
|
18
|
+
<PageHeader.Actions>
|
|
19
|
+
<Button variant="secondary">Email</Button>
|
|
20
|
+
<Button>Edit</Button>
|
|
21
|
+
</PageHeader.Actions>
|
|
22
|
+
</PageHeader>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
<!-- props:start -->
|
|
26
|
+
|
|
27
|
+
## Props
|
|
28
|
+
|
|
29
|
+
### `PageHeaderProps`
|
|
30
|
+
|
|
31
|
+
<!-- prettier-ignore -->
|
|
32
|
+
| Prop | Type | Required | Description |
|
|
33
|
+
|---|---|---|---|
|
|
34
|
+
| `borderBottom` | `boolean` | no | Render a 1px bottom border under the header. Default `true`. Set `false` when placing a `<Tabs>` component as a sibling below — Tabs has its own `border-bottom`, and you don't want the double line. |
|
|
35
|
+
| …native | | | plus native `<div>` attributes |
|
|
36
|
+
|
|
37
|
+
### `PageHeaderActionsProps`
|
|
38
|
+
|
|
39
|
+
<!-- prettier-ignore -->
|
|
40
|
+
| Prop | Type | Required | Description |
|
|
41
|
+
|---|---|---|---|
|
|
42
|
+
| `children` | `ReactNode` | yes | |
|
|
43
|
+
| …native | | | plus native `<div>` attributes |
|
|
44
|
+
|
|
45
|
+
### `PageHeaderAsideProps`
|
|
46
|
+
|
|
47
|
+
<!-- prettier-ignore -->
|
|
48
|
+
| Prop | Type | Required | Description |
|
|
49
|
+
|---|---|---|---|
|
|
50
|
+
| `children` | `ReactNode` | yes | |
|
|
51
|
+
| …native | | | plus native `<div>` attributes |
|
|
52
|
+
|
|
53
|
+
### `PageHeaderBackButtonProps`
|
|
54
|
+
|
|
55
|
+
<!-- prettier-ignore -->
|
|
56
|
+
| Prop | Type | Required | Description |
|
|
57
|
+
|---|---|---|---|
|
|
58
|
+
| `href` | `string` | no | If set, renders as `<a href={href}>`. Mutually exclusive with `onClick` — passing both renders as `<button>` (the onClick wins) and emits a dev-only warning. |
|
|
59
|
+
| `onClick` | `(() => void)` | no | If set, renders as `<button type="button" onClick={onClick}>`. Mutually exclusive with `href`. |
|
|
60
|
+
| `aria-label` | `string` | no | Accessible label. Defaults to the i18n value at `pageHeader.back` (`'Go back'` in English) when omitted OR empty — an empty string is not an explicit name, so it takes the default too. |
|
|
61
|
+
| `icon` | `ReactNode` | no | Icon to render. Default `<ChevronLeft size={16}>` from lucide-react. |
|
|
62
|
+
|
|
63
|
+
### `PageHeaderBreadcrumbProps`
|
|
64
|
+
|
|
65
|
+
<!-- prettier-ignore -->
|
|
66
|
+
| Prop | Type | Required | Description |
|
|
67
|
+
|---|---|---|---|
|
|
68
|
+
| `children` | `ReactNode` | yes | |
|
|
69
|
+
| …native | | | plus native `<div>` attributes |
|
|
70
|
+
|
|
71
|
+
### `PageHeaderMetaProps`
|
|
72
|
+
|
|
73
|
+
<!-- prettier-ignore -->
|
|
74
|
+
| Prop | Type | Required | Description |
|
|
75
|
+
|---|---|---|---|
|
|
76
|
+
| `children` | `ReactNode` | yes | |
|
|
77
|
+
| …native | | | plus native `<div>` attributes |
|
|
78
|
+
|
|
79
|
+
### `PageHeaderSubtitleProps`
|
|
80
|
+
|
|
81
|
+
<!-- prettier-ignore -->
|
|
82
|
+
| Prop | Type | Required | Description |
|
|
83
|
+
|---|---|---|---|
|
|
84
|
+
| `children` | `ReactNode` | yes | |
|
|
85
|
+
| …native | | | plus native `<p>` attributes |
|
|
86
|
+
|
|
87
|
+
### `PageHeaderTitleProps`
|
|
88
|
+
|
|
89
|
+
<!-- prettier-ignore -->
|
|
90
|
+
| Prop | Type | Required | Description |
|
|
91
|
+
|---|---|---|---|
|
|
92
|
+
| `order` | `1 \| 2 \| 3 \| 4 \| 5 \| 6` | no | Heading semantic level (1–6). Default `1`. Passes through to `<Title order={order}>` so the rendered element is `<h1>`–`<h6>`. |
|
|
93
|
+
| `size` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| '2xl' \| '3xl'` | no | Visual size override (decouples from semantic level). Pass-through to `<Title size>` — useful for "section-level page headers" where the h-level is 2 but you want it to LOOK like an h1. |
|
|
94
|
+
| `truncate` | `boolean` | no | Keep the title on one line, ending in an ellipsis where the title column ends (at the actions on wide layouts) instead of wrapping. Pass-through to `<Title truncate>`. Defaults to `false`. Anything inline AFTER the text (e.g. a status badge) is clipped with it — put badges in `PageHeader.Meta`. Screen readers still get the full text (don't add `aria-label`); sighted users don't, so show the full title somewhere else too if it matters. |
|
|
95
|
+
| `children` | `ReactNode` | yes | |
|
|
96
|
+
|
|
97
|
+
<!-- props:end -->
|
|
98
|
+
|
|
99
|
+
- **Compound API.** Seven slots: `Breadcrumb`, `BackButton`, `Aside`, `Title`, `Subtitle`, `Meta`, `Actions`. Detected via `c.type === SubComponent` after one level of Fragment unwrap. Unrecognized children are silently dropped.
|
|
100
|
+
- **All slots optional.** Missing slots collapse to zero-height rows; minimal usage is `<PageHeader><PageHeader.Title>…</PageHeader.Title></PageHeader>`.
|
|
101
|
+
- **`<PageHeader.BackButton>`** renders as `<a href>` when `href` is provided, or `<button onClick>` when `onClick` is provided. Mutually exclusive — both → button wins with a dev warn; neither → disabled button with a dev warn. Default `aria-label="Go back"`; default icon `<ChevronLeft size={16}>`. Lives in the breadcrumb row, left of the breadcrumb itself.
|
|
102
|
+
- **`<PageHeader.Aside>`** is a position-based slot (not named "Icon" / "Avatar") so it accepts whatever leading element you need. Vertically centered with the title block.
|
|
103
|
+
- **`<PageHeader.Subtitle>`** is a `<p>` with muted color.
|
|
104
|
+
- **`<PageHeader.Meta>`** is a flex row that wraps — good for badges + timestamps.
|
|
105
|
+
- **`<PageHeader.Actions>`** is already a wrapping, right-aligned flex row — put `<Button>`s directly in it. A wrapping `<Cluster>` inside it needs `justify="end"`, or its wrapped rows go ragged-left. On viewports < 640px, Actions moves below the title block and stays right-aligned (primary action at the thumb-side edge).
|
|
106
|
+
- **NOT a `<header>` landmark.** PageHeader renders a `<div>` to avoid conflicting with the AppShell's app-level `<header role="banner">`.
|
|
107
|
+
- **Shrinks to narrow containers without overflowing.** On viewports ≥ 640px the actions column is sized to fit its buttons on one line, capped so the title keeps about `--page-header-title-min` (160px, `--measure-2xs`; with an `<Aside>`, the aside and title share that room). A wide header keeps all its actions on one line to the right; once they'd leave the title less than that, the buttons wrap onto extra lines. Wrapped rows stay right-aligned when the buttons are direct children of `<PageHeader.Actions>` (or inside a `<Cluster justify="end">`). If a single action item is wider than the space left, the title shrinks below that instead of the header overflowing (as long as that item itself — plus the aside and gaps — fits in the container). `<PageHeader.Breadcrumb>` wraps too. A title word wider than its column breaks mid-word (`overflow-wrap: anywhere`) rather than overflowing — e.g. "Acme Corporation" beside an `<Aside>` in a narrow container. Below a 640px viewport the actions move below, still right-aligned: the header becomes a single column, or two (aside + title) with an `<Aside>`, and the actions span the full width.
|
|
108
|
+
- **Give it a stretched or definite width.** In a shrink-to-fit parent PageHeader is only as wide as its content, so the actions don't sit at the right edge. Fix it where the width comes from: as a non-growing flex item, give the PageHeader `flex: 1` (or `width: 100%`) via its `className`; inside an `inline-block` parent or an `auto` grid track that doesn't work (the percentage is cyclic, `flex` is ignored) — make the parent block-level, or use a `1fr` / `minmax(0, 1fr)` track.
|
|
109
|
+
|
|
110
|
+
#### Hard rule
|
|
111
|
+
|
|
112
|
+
- ❌ Nesting `<PageHeader>` inside another `<PageHeader>` — undefined behavior. Use one PageHeader per page.
|
|
113
|
+
- ❌ Putting non-PageHeader children (a `<div>`, a `<Stack>`) inside `<PageHeader>` — they're silently dropped. Use one of the seven slots.
|
|
114
|
+
- ❌ Wrapping a sub-component in an HOC or deep nesting. The `c.type ===` detection handles ONE level of Fragment unwrap only.
|
|
115
|
+
- ❌ Putting an Avatar inside `<PageHeader.Title>` — muddles the `<h1>`'s text content for screen readers. Use `<PageHeader.Aside>` instead.
|
|
116
|
+
- ❌ `position: sticky` directly on `<PageHeader>` — out of scope for v1. Wrap in your own sticky container if you need sticky behavior.
|
|
117
|
+
- ❌ Passing both `href` and `onClick` to `<PageHeader.BackButton>` — onClick wins with a dev warn. Pick one.
|
|
118
|
+
|
|
119
|
+
- **`<PageHeader.Title>` `size`** decouples visual size from semantic level: `<PageHeader.Title order={2} size="3xl">` renders an `<h2>` that looks like an h1; `order={2}` alone uses the default h2 size (`2xl`).
|
|
120
|
+
- **`<PageHeader.BackButton>` as an anchor** relies on your router to intercept the click; use `onClick={() => router.back()}` for programmatic navigation.
|
|
121
|
+
- **When NOT to use:** arbitrary section headers within a page that are not its primary heading — use `<Title order={2}>` directly; a sticky top bar — PageHeader is a regular block element.
|
|
122
|
+
- ❌ Passing multiple `<PageHeader.Title>` (or any duplicate sub-component) — only the FIRST match is rendered; duplicates are silently dropped.
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
// Next to Tabs — drop the bottom border:
|
|
126
|
+
<PageHeader borderBottom={false}>
|
|
127
|
+
<PageHeader.Title>Reports</PageHeader.Title>
|
|
128
|
+
</PageHeader>
|
|
129
|
+
<Tabs value={tab} onChange={setTab}>...</Tabs>
|
|
130
|
+
|
|
131
|
+
// Record detail — one line, ellipsis at the actions; badges go in Meta:
|
|
132
|
+
<PageHeader.Title truncate>{task.key} {task.title}</PageHeader.Title>
|
|
133
|
+
```
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# `<Pagination>` — numbered nav with windowing
|
|
2
|
+
|
|
3
|
+
```tsx
|
|
4
|
+
const [page, setPage] = useState(1);
|
|
5
|
+
<Pagination currentPage={page} pageCount={20} onPageChange={setPage} />;
|
|
6
|
+
```
|
|
7
|
+
|
|
8
|
+
<!-- props:start -->
|
|
9
|
+
|
|
10
|
+
## Props
|
|
11
|
+
|
|
12
|
+
<!-- prettier-ignore -->
|
|
13
|
+
| Prop | Type | Required | Description |
|
|
14
|
+
|---|---|---|---|
|
|
15
|
+
| `currentPage` | `number` | yes | Current 1-indexed page. Values outside `[1, pageCount]` clamp at render time (defensive — same precedent as `clampHeading` in `<EmptyState>`). |
|
|
16
|
+
| `pageCount` | `number` | yes | Total number of pages. Values `< 1` clamp to `1`. The component still renders when `pageCount === 1` (single enabled current-page button + both prev/next disabled) so consumers don't have to conditionally hide it. The `disabled` prop still disables all three controls. |
|
|
17
|
+
| `onPageChange` | `(page: number) => void` | yes | Called with the new 1-indexed page when the user clicks prev, next, or a page number. Not fired when the user clicks the current page. |
|
|
18
|
+
| `siblingCount` | `number` | no | How many page-number buttons to show on each side of `currentPage`. Default `1`. Set to `0` for the tightest possible display (sidebar / narrow column) or `2` for wider footers. Boundary is fixed at 1 — first and last pages are always shown. |
|
|
19
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | no | Visual size — `'sm'` (24px), `'md'` (32px, default), `'lg'` (40px). Tracks the Button / Input scale so Pagination sits cleanly inside a `<Cluster>` next to those components. |
|
|
20
|
+
| `disabled` | `boolean` | no | When `true`, all buttons (prev / next / numbers) are disabled. Use during page transitions (loading, saving) to prevent double-clicks. |
|
|
21
|
+
| `aria-label` | `string` | no | Accessible name for the `<nav>` wrapper. Defaults to `'Pagination'` when omitted OR empty — an empty string is not an explicit name, so it takes the default too. Override when multiple paginations appear on the same page (e.g., `'Top pagination'` / `'Bottom pagination'`). |
|
|
22
|
+
| …native | | | plus native HTML attributes |
|
|
23
|
+
|
|
24
|
+
<!-- props:end -->
|
|
25
|
+
|
|
26
|
+
- Controlled-only — consumer owns `currentPage`. No internal state.
|
|
27
|
+
- Sibling windowing — `siblingCount` (default `1`) controls how many pages on each side of current. Boundary fixed at 1 (first + last always shown). Slot count stays constant once ellipses kick in (`siblingCount * 2 + 5`) — the row's width doesn't jump as the user clicks.
|
|
28
|
+
- Current page stays focusable and carries `aria-current="page"`; activating it is a no-op. Keeping it enabled preserves keyboard focus when controlled `currentPage` updates after a page change.
|
|
29
|
+
- Sizes: `'sm'` (24px) / `'md'` (32px, default) / `'lg'` (40px) — tracks the Button / Input scale so Pagination sits cleanly next to a `<Button>` in a Cluster.
|
|
30
|
+
- Out-of-range `currentPage` / `pageCount` clamp at render time (defensive — same precedent as `<EmptyState>`'s `clampHeading`).
|
|
31
|
+
- **Not bundled**: page-size selector, count caption ("Showing 11–20 of 240"). Compose those with `<Select>` and text — keeps Pagination focused on navigation. `<DataTable>` (coming) owns its own footer.
|
|
32
|
+
- For streams without a total → use `<CursorPagination>`.
|
|
33
|
+
- For "load more" → use `<Button>` directly. **`<Button>` has no `loading` prop** — this line used to show one. Use `aria-disabled` and guard the handler, keeping the label stable: `<Button onClick={() => !isLoading && loadMore()} aria-disabled={isLoading || undefined}>Load more</Button>`. Native `disabled` would drop the button out of the tab order mid-interaction, and swapping the label renames a control the user just activated — see [Transient state and screen readers](../transient-state.md).
|
|
34
|
+
- `paginationRange(currentPage, pageCount, siblingCount)` is exported as a pure utility for advanced consumers that want to compute the same item list themselves (e.g., to render a custom layout with the same windowing).
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
// Tight display for a sidebar / narrow column
|
|
38
|
+
<Pagination currentPage={5} pageCount={100} onPageChange={setPage} siblingCount={0} size="sm" />
|
|
39
|
+
|
|
40
|
+
// Loading lock while data refetches
|
|
41
|
+
<Pagination currentPage={page} pageCount={pageCount} onPageChange={setPage} disabled={isFetching} />
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**A11y:** the wrapper is `<nav aria-label="Pagination">` (override `aria-label` when several paginations share a page). Prev/next buttons carry `aria-label="Previous page"` / `"Next page"` with the chevron icons `aria-hidden`; ellipses are decorative `<span aria-hidden>`, not focusable.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Palette — categorical color set
|
|
2
|
+
|
|
3
|
+
```tsx
|
|
4
|
+
import { paletteTokens, type PaletteColor } from '@eocrm/design-system';
|
|
5
|
+
|
|
6
|
+
// Consumer-side mapping: domain → color
|
|
7
|
+
const TEAM_COLOR: Record<string, PaletteColor> = {
|
|
8
|
+
marketing: 'violet',
|
|
9
|
+
engineering: 'teal',
|
|
10
|
+
sales: 'amber',
|
|
11
|
+
ops: 'slate',
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// Custom consumer chip using the palette tokens
|
|
15
|
+
function TeamChip({ team }: { team: string }) {
|
|
16
|
+
const { bg, fg } = paletteTokens(TEAM_COLOR[team] ?? 'stone');
|
|
17
|
+
return (
|
|
18
|
+
<span style={{ background: bg, color: fg, padding: '2px 8px', borderRadius: 4 }}>{team}</span>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Color-tagged checkbox (library-level integration)
|
|
23
|
+
<Checkbox color="violet" label="Marketing" />;
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
- 30 named colors with bg + fg pairs: `red` / `coral` / `orange` / `amber` / `gold` / `yellow` / `olive` / `lime` / `green` / `emerald` / `mint` / `teal` / `cyan` / `sky` / `blue` / `navy` / `indigo` / `violet` / `lavender` / `purple` / `plum` / `fuchsia` / `magenta` / `pink` / `rose` / `brown` / `taupe` / `slate` / `stone` / `charcoal`.
|
|
27
|
+
- `PaletteColor` is the TypeScript union; `PALETTE_COLORS` is the ordered readonly array (use for pickers / demos).
|
|
28
|
+
- `paletteTokens(color)` returns `{ bg, fg }` as `var(...)` strings — use to apply palette colors in consumer-built components.
|
|
29
|
+
- **Categorical, not semantic.** For status, use `<Badge tone="success" />` etc. Palette colors carry no built-in meaning.
|
|
30
|
+
- Consumers own the **domain → color mapping** (e.g., per-event-namespace, per-team, per-tag). The library provides only the colors and the type surface.
|
|
31
|
+
- `<Checkbox color="violet">` is the one library component that accepts palette colors out-of-the-box — tints the checked / indeterminate fill. Others (Badge, FilterChip) keep their existing semantic-tone unions; consumers build custom chips when they want palette colors.
|
|
32
|
+
- The generated token contract exposes `--color-palette-<name>-bg` and `--color-palette-<name>-fg`; contributors change their source definitions in `packages/design-tokens/src/tokens.json`.
|