@eocrm/design-system 0.0.0-stage → 0.6.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AI-PRIMER.md +341 -0
- package/README.md +262 -2
- package/docs/components/Accordion.md +114 -0
- package/docs/components/AgentAvatar.md +58 -0
- package/docs/components/Alert.md +81 -0
- package/docs/components/AppLayout.md +70 -0
- package/docs/components/Avatar.md +49 -0
- package/docs/components/AvatarGroup.md +47 -0
- package/docs/components/Badge.md +60 -0
- package/docs/components/Banner.md +89 -0
- package/docs/components/BrandIcon.md +32 -0
- package/docs/components/Breadcrumb.md +72 -0
- package/docs/components/Button.md +107 -0
- package/docs/components/ButtonGroup.md +78 -0
- package/docs/components/Calendar.md +158 -0
- package/docs/components/Card.md +165 -0
- package/docs/components/CatalogPicker.md +83 -0
- package/docs/components/Chart.md +114 -0
- package/docs/components/ChatBubble.md +55 -0
- package/docs/components/ChatLauncher.md +97 -0
- package/docs/components/Checkbox.md +55 -0
- package/docs/components/CircularProgress.md +53 -0
- package/docs/components/Cluster.md +96 -0
- package/docs/components/Code.md +35 -0
- package/docs/components/CodeBlock.md +67 -0
- package/docs/components/ColorPicker.md +115 -0
- package/docs/components/ConfirmationPopover.md +70 -0
- package/docs/components/Constrain.md +39 -0
- package/docs/components/CurrencySelect.md +57 -0
- package/docs/components/CursorPagination.md +43 -0
- package/docs/components/DashboardCanvas.md +79 -0
- package/docs/components/DashboardWidget.md +131 -0
- package/docs/components/DataTable.md +88 -0
- package/docs/components/DatePicker.md +68 -0
- package/docs/components/DateRangePicker.md +71 -0
- package/docs/components/DateStrip.md +56 -0
- package/docs/components/DefinitionList.md +82 -0
- package/docs/components/Divider.md +57 -0
- package/docs/components/Dot.md +26 -0
- package/docs/components/Drawer.md +94 -0
- package/docs/components/DropdownMenu.md +299 -0
- package/docs/components/EmojiPicker.md +84 -0
- package/docs/components/EmptyState.md +55 -0
- package/docs/components/EntityChip.md +102 -0
- package/docs/components/ErrorState.md +72 -0
- package/docs/components/Field.md +91 -0
- package/docs/components/FileUpload.md +109 -0
- package/docs/components/FilterChip.md +88 -0
- package/docs/components/FlowCanvas.md +108 -0
- package/docs/components/FormRow.md +31 -0
- package/docs/components/FormSection.md +37 -0
- package/docs/components/Grid.md +103 -0
- package/docs/components/Highlight.md +75 -0
- package/docs/components/HighlightedText.md +50 -0
- package/docs/components/IconPicker.md +134 -0
- package/docs/components/IconTile.md +30 -0
- package/docs/components/Image.md +84 -0
- package/docs/components/ImageCrop.md +163 -0
- package/docs/components/Indent.md +31 -0
- package/docs/components/InlineDatePicker.md +58 -0
- package/docs/components/InlineDateRangePicker.md +57 -0
- package/docs/components/Input.md +54 -0
- package/docs/components/Kanban.md +122 -0
- package/docs/components/Kbd.md +41 -0
- package/docs/components/Lightbox.md +105 -0
- package/docs/components/Link.md +65 -0
- package/docs/components/LinkCard.md +41 -0
- package/docs/components/LiquidEditor.md +84 -0
- package/docs/components/List.md +68 -0
- package/docs/components/LiveRegion.md +29 -0
- package/docs/components/LocaleProvider.md +25 -0
- package/docs/components/Logo.md +41 -0
- package/docs/components/Masonry.md +48 -0
- package/docs/components/MediaTile.md +95 -0
- package/docs/components/Modal.md +107 -0
- package/docs/components/MoneyInput.md +62 -0
- package/docs/components/OptionsPicker.md +123 -0
- package/docs/components/OtpInput.md +65 -0
- package/docs/components/Page.md +35 -0
- package/docs/components/PageHeader.md +133 -0
- package/docs/components/Pagination.md +44 -0
- package/docs/components/Palette.md +32 -0
- package/docs/components/PasswordInput.md +45 -0
- package/docs/components/PasswordStrengthMeter.md +27 -0
- package/docs/components/PersonDisplay.md +153 -0
- package/docs/components/PhoneInput.md +54 -0
- package/docs/components/PillMenu.md +91 -0
- package/docs/components/Popover.md +127 -0
- package/docs/components/Progress.md +56 -0
- package/docs/components/QrCode.md +34 -0
- package/docs/components/Radio.md +37 -0
- package/docs/components/RadioGroup.md +56 -0
- package/docs/components/Rail.md +191 -0
- package/docs/components/RelativeDateInput.md +114 -0
- package/docs/components/RichText.md +44 -0
- package/docs/components/RichTextEditor.md +112 -0
- package/docs/components/Screen.md +37 -0
- package/docs/components/ScrollArea.md +39 -0
- package/docs/components/Select.md +131 -0
- package/docs/components/SettingRow.md +98 -0
- package/docs/components/Skeleton.md +68 -0
- package/docs/components/Slider.md +92 -0
- package/docs/components/SlotGrid.md +55 -0
- package/docs/components/SocialButton.md +31 -0
- package/docs/components/Sortable.md +127 -0
- package/docs/components/SortableGroup.md +52 -0
- package/docs/components/Sparkline.md +51 -0
- package/docs/components/Split.md +76 -0
- package/docs/components/Stack.md +59 -0
- package/docs/components/StagePath.md +66 -0
- package/docs/components/StatGroup.md +61 -0
- package/docs/components/StatTile.md +58 -0
- package/docs/components/Sticky.md +69 -0
- package/docs/components/Switch.md +89 -0
- package/docs/components/Table.md +184 -0
- package/docs/components/TableOfContents.md +76 -0
- package/docs/components/Tabs.md +94 -0
- package/docs/components/Text.md +63 -0
- package/docs/components/Textarea.md +59 -0
- package/docs/components/Thread.md +52 -0
- package/docs/components/TimeField.md +70 -0
- package/docs/components/Timeline.md +57 -0
- package/docs/components/Title.md +54 -0
- package/docs/components/ToastViewport.md +61 -0
- package/docs/components/Tooltip.md +63 -0
- package/docs/components/TopBar.md +214 -0
- package/docs/components/Tour.md +85 -0
- package/docs/components/TreeView.md +113 -0
- package/docs/components/VisuallyHidden.md +43 -0
- package/docs/components/WidgetPreview.md +54 -0
- package/docs/components/useBelowBreakpoint.md +11 -0
- package/docs/components/useMonth.md +18 -0
- package/docs/setup.md +51 -0
- package/docs/theming.md +95 -0
- package/docs/tokens.md +123 -0
- package/docs/transient-state.md +56 -0
- package/package.json +82 -4
- package/src/_meta/manifest.ts +293 -0
- package/src/app/AppProvider.tsx +78 -0
- package/src/app/index.ts +3 -0
- package/src/app/themeTokens.ts +95 -0
- package/src/calendar/dateMath.ts +114 -0
- package/src/calendar/formatters.ts +75 -0
- package/src/calendar/index.ts +39 -0
- package/src/calendar/types.ts +36 -0
- package/src/calendar/useAgenda.ts +70 -0
- package/src/calendar/useDay.ts +51 -0
- package/src/calendar/useMonth.ts +82 -0
- package/src/calendar/useWeek.ts +62 -0
- package/src/calendar/weekInfo.ts +83 -0
- package/src/components/Accordion/Accordion.module.scss +304 -0
- package/src/components/Accordion/Accordion.tokens.scss +64 -0
- package/src/components/Accordion/Accordion.tsx +272 -0
- package/src/components/Accordion/AccordionContent.tsx +36 -0
- package/src/components/Accordion/AccordionItem.tsx +66 -0
- package/src/components/Accordion/AccordionTrigger.tsx +121 -0
- package/src/components/Accordion/context.ts +47 -0
- package/src/components/Accordion/index.ts +14 -0
- package/src/components/Alert/Alert.module.scss +92 -0
- package/src/components/Alert/Alert.tokens.scss +60 -0
- package/src/components/Alert/Alert.tsx +136 -0
- package/src/components/Alert/index.ts +2 -0
- package/src/components/AppLayout/AppLayout.module.scss +130 -0
- package/src/components/AppLayout/AppLayout.tokens.scss +27 -0
- package/src/components/AppLayout/AppLayout.tsx +211 -0
- package/src/components/AppLayout/index.ts +2 -0
- package/src/components/Avatar/AgentAvatar.module.scss +117 -0
- package/src/components/Avatar/AgentAvatar.tsx +122 -0
- package/src/components/Avatar/Avatar.module.scss +181 -0
- package/src/components/Avatar/Avatar.tokens.scss +107 -0
- package/src/components/Avatar/Avatar.tsx +210 -0
- package/src/components/Avatar/AvatarGroup.module.scss +128 -0
- package/src/components/Avatar/AvatarGroup.tsx +119 -0
- package/src/components/Avatar/AvatarGroupContext.tsx +20 -0
- package/src/components/Avatar/index.ts +6 -0
- package/src/components/Badge/Badge.module.scss +124 -0
- package/src/components/Badge/Badge.tokens.scss +74 -0
- package/src/components/Badge/Badge.tsx +164 -0
- package/src/components/Badge/index.ts +2 -0
- package/src/components/Banner/Banner.module.scss +73 -0
- package/src/components/Banner/Banner.tokens.scss +51 -0
- package/src/components/Banner/Banner.tsx +138 -0
- package/src/components/Banner/index.ts +2 -0
- package/src/components/BrandIcon/BrandIcon.module.scss +5 -0
- package/src/components/BrandIcon/BrandIcon.tsx +99 -0
- package/src/components/BrandIcon/index.ts +2 -0
- package/src/components/Breadcrumb/Breadcrumb.module.scss +32 -0
- package/src/components/Breadcrumb/Breadcrumb.tokens.scss +9 -0
- package/src/components/Breadcrumb/Breadcrumb.tsx +146 -0
- package/src/components/Breadcrumb/index.ts +2 -0
- package/src/components/Button/Button.module.scss +167 -0
- package/src/components/Button/Button.tokens.scss +90 -0
- package/src/components/Button/Button.tsx +205 -0
- package/src/components/Button/index.ts +2 -0
- package/src/components/ButtonGroup/ButtonGroup.module.scss +134 -0
- package/src/components/ButtonGroup/ButtonGroup.tokens.scss +42 -0
- package/src/components/ButtonGroup/ButtonGroup.tsx +278 -0
- package/src/components/ButtonGroup/ButtonGroupItem.tsx +74 -0
- package/src/components/ButtonGroup/context.ts +45 -0
- package/src/components/ButtonGroup/index.ts +2 -0
- package/src/components/Calendar/AgendaView.module.scss +231 -0
- package/src/components/Calendar/AgendaView.tsx +214 -0
- package/src/components/Calendar/AllDayBand.module.scss +18 -0
- package/src/components/Calendar/AllDayBand.tsx +75 -0
- package/src/components/Calendar/Calendar.module.scss +23 -0
- package/src/components/Calendar/Calendar.tokens.scss +262 -0
- package/src/components/Calendar/Calendar.tsx +370 -0
- package/src/components/Calendar/DayCell.module.scss +30 -0
- package/src/components/Calendar/DayCell.tsx +50 -0
- package/src/components/Calendar/DayView.module.scss +10 -0
- package/src/components/Calendar/DayView.tsx +163 -0
- package/src/components/Calendar/EventChip.module.scss +171 -0
- package/src/components/Calendar/EventChip.tsx +115 -0
- package/src/components/Calendar/HourGrid.module.scss +123 -0
- package/src/components/Calendar/HourGrid.tsx +381 -0
- package/src/components/Calendar/MonthView.module.scss +99 -0
- package/src/components/Calendar/MonthView.tsx +296 -0
- package/src/components/Calendar/TimedEvent.module.scss +205 -0
- package/src/components/Calendar/TimedEvent.tsx +247 -0
- package/src/components/Calendar/ViewSwitcher.module.scss +3 -0
- package/src/components/Calendar/ViewSwitcher.tsx +47 -0
- package/src/components/Calendar/WeekView.module.scss +10 -0
- package/src/components/Calendar/WeekView.tsx +137 -0
- package/src/components/Calendar/eventColor.ts +60 -0
- package/src/components/Calendar/index.ts +18 -0
- package/src/components/Calendar/types.ts +451 -0
- package/src/components/Calendar/useEventDrag.ts +783 -0
- package/src/components/Calendar/utils.ts +622 -0
- package/src/components/Card/Card.module.scss +264 -0
- package/src/components/Card/Card.tokens.scss +55 -0
- package/src/components/Card/Card.tsx +170 -0
- package/src/components/Card/CardBody.tsx +45 -0
- package/src/components/Card/CardHeader.tsx +113 -0
- package/src/components/Card/CardList.tsx +24 -0
- package/src/components/Card/CardListRow.tsx +24 -0
- package/src/components/Card/index.ts +7 -0
- package/src/components/CatalogPicker/CatalogPicker.module.scss +193 -0
- package/src/components/CatalogPicker/CatalogPicker.tokens.scss +26 -0
- package/src/components/CatalogPicker/CatalogPicker.tsx +339 -0
- package/src/components/CatalogPicker/index.ts +2 -0
- package/src/components/Chart/Chart.module.scss +287 -0
- package/src/components/Chart/Chart.tokens.scss +63 -0
- package/src/components/Chart/Chart.tsx +607 -0
- package/src/components/Chart/ChartLegend.tsx +99 -0
- package/src/components/Chart/ChartTable.tsx +62 -0
- package/src/components/Chart/ChartTooltip.tsx +91 -0
- package/src/components/Chart/chartModel.ts +689 -0
- package/src/components/Chart/index.ts +2 -0
- package/src/components/ChatBubble/ChatBubble.module.scss +93 -0
- package/src/components/ChatBubble/ChatBubble.tokens.scss +17 -0
- package/src/components/ChatBubble/ChatBubble.tsx +61 -0
- package/src/components/ChatBubble/ChatBubbleList.tsx +27 -0
- package/src/components/ChatBubble/index.ts +3 -0
- package/src/components/ChatLauncher/ChatLauncher.module.scss +68 -0
- package/src/components/ChatLauncher/ChatLauncher.tokens.scss +27 -0
- package/src/components/ChatLauncher/ChatLauncher.tsx +202 -0
- package/src/components/ChatLauncher/index.ts +2 -0
- package/src/components/ChatLauncher/isAtBottom.ts +12 -0
- package/src/components/Checkbox/Checkbox.module.scss +147 -0
- package/src/components/Checkbox/Checkbox.tokens.scss +44 -0
- package/src/components/Checkbox/Checkbox.tsx +188 -0
- package/src/components/Checkbox/index.ts +2 -0
- package/src/components/CircularProgress/CircularProgress.module.scss +133 -0
- package/src/components/CircularProgress/CircularProgress.tokens.scss +40 -0
- package/src/components/CircularProgress/CircularProgress.tsx +139 -0
- package/src/components/CircularProgress/index.ts +7 -0
- package/src/components/Cluster/Cluster.module.scss +145 -0
- package/src/components/Cluster/Cluster.tokens.scss +11 -0
- package/src/components/Cluster/Cluster.tsx +215 -0
- package/src/components/Cluster/index.ts +2 -0
- package/src/components/Code/Code.module.scss +28 -0
- package/src/components/Code/Code.tokens.scss +15 -0
- package/src/components/Code/Code.tsx +44 -0
- package/src/components/Code/index.ts +2 -0
- package/src/components/CodeBlock/CodeBlock.module.scss +146 -0
- package/src/components/CodeBlock/CodeBlock.tokens.scss +27 -0
- package/src/components/CodeBlock/CodeBlock.tsx +274 -0
- package/src/components/CodeBlock/highlight.ts +179 -0
- package/src/components/CodeBlock/index.ts +8 -0
- package/src/components/CodeBlock/sampleSync.ts +42 -0
- package/src/components/ColorPicker/ColorPicker.module.scss +286 -0
- package/src/components/ColorPicker/ColorPicker.tokens.scss +60 -0
- package/src/components/ColorPicker/ColorPicker.tsx +367 -0
- package/src/components/ColorPicker/ColorPickerPanel.tsx +265 -0
- package/src/components/ColorPicker/SVSquare.tsx +189 -0
- package/src/components/ColorPicker/colorMath.ts +141 -0
- package/src/components/ColorPicker/index.ts +6 -0
- package/src/components/ConfirmationPopover/ConfirmationPopover.module.scss +39 -0
- package/src/components/ConfirmationPopover/ConfirmationPopover.tokens.scss +13 -0
- package/src/components/ConfirmationPopover/ConfirmationPopover.tsx +395 -0
- package/src/components/ConfirmationPopover/index.ts +2 -0
- package/src/components/Constrain/Constrain.module.scss +99 -0
- package/src/components/Constrain/Constrain.tsx +79 -0
- package/src/components/Constrain/index.ts +2 -0
- package/src/components/CurrencySelect/CurrencySelect.module.scss +7 -0
- package/src/components/CurrencySelect/CurrencySelect.tsx +167 -0
- package/src/components/CurrencySelect/index.ts +3 -0
- package/src/components/CursorPagination/CursorPagination.module.scss +69 -0
- package/src/components/CursorPagination/CursorPagination.tokens.scss +32 -0
- package/src/components/CursorPagination/CursorPagination.tsx +121 -0
- package/src/components/CursorPagination/index.ts +2 -0
- package/src/components/DashboardCanvas/DashboardCanvas.module.scss +360 -0
- package/src/components/DashboardCanvas/DashboardCanvas.tokens.scss +48 -0
- package/src/components/DashboardCanvas/DashboardCanvas.tsx +1270 -0
- package/src/components/DashboardCanvas/DashboardCanvasItem.tsx +140 -0
- package/src/components/DashboardCanvas/DashboardCanvasSection.tsx +174 -0
- package/src/components/DashboardCanvas/engine.ts +363 -0
- package/src/components/DashboardCanvas/index.ts +9 -0
- package/src/components/DashboardWidget/DashboardWidget.module.scss +100 -0
- package/src/components/DashboardWidget/DashboardWidget.tokens.scss +16 -0
- package/src/components/DashboardWidget/DashboardWidget.tsx +188 -0
- package/src/components/DashboardWidget/index.ts +7 -0
- package/src/components/DataTable/BodyRow.tsx +197 -0
- package/src/components/DataTable/ColumnVisibilityTrigger.module.scss +7 -0
- package/src/components/DataTable/ColumnVisibilityTrigger.tsx +105 -0
- package/src/components/DataTable/DataTable.module.scss +511 -0
- package/src/components/DataTable/DataTable.tokens.scss +80 -0
- package/src/components/DataTable/DataTable.tsx +734 -0
- package/src/components/DataTable/HeaderCell.module.scss +215 -0
- package/src/components/DataTable/HeaderCell.tsx +440 -0
- package/src/components/DataTable/columnShift.ts +117 -0
- package/src/components/DataTable/index.ts +23 -0
- package/src/components/DataTable/pinStyle.ts +47 -0
- package/src/components/DataTable/reorderColumns.ts +66 -0
- package/src/components/DataTable/types.ts +289 -0
- package/src/components/DataTable/useColumnDragShift.ts +216 -0
- package/src/components/DataTable/useDataTable.ts +372 -0
- package/src/components/DataTable/useResizeHandle.ts +97 -0
- package/src/components/DatePicker/DatePicker.module.scss +180 -0
- package/src/components/DatePicker/DatePicker.tokens.scss +152 -0
- package/src/components/DatePicker/DatePicker.tsx +664 -0
- package/src/components/DatePicker/DatePickerGrid.module.scss +172 -0
- package/src/components/DatePicker/DatePickerGrid.tsx +341 -0
- package/src/components/DatePicker/InlineDatePicker.module.scss +32 -0
- package/src/components/DatePicker/InlineDatePicker.tsx +201 -0
- package/src/components/DatePicker/index.ts +4 -0
- package/src/components/DatePicker/utils.ts +364 -0
- package/src/components/DateRangePicker/DateRangePicker.module.scss +237 -0
- package/src/components/DateRangePicker/DateRangePicker.tokens.scss +78 -0
- package/src/components/DateRangePicker/DateRangePicker.tsx +720 -0
- package/src/components/DateRangePicker/InlineDateRangePicker.module.scss +94 -0
- package/src/components/DateRangePicker/InlineDateRangePicker.tsx +376 -0
- package/src/components/DateRangePicker/index.ts +5 -0
- package/src/components/DateRangePicker/utils.ts +151 -0
- package/src/components/DateStrip/DateStrip.module.scss +78 -0
- package/src/components/DateStrip/DateStrip.tokens.scss +23 -0
- package/src/components/DateStrip/DateStrip.tsx +205 -0
- package/src/components/DateStrip/index.ts +2 -0
- package/src/components/DefinitionList/DefinitionList.module.scss +102 -0
- package/src/components/DefinitionList/DefinitionList.tokens.scss +29 -0
- package/src/components/DefinitionList/DefinitionList.tsx +198 -0
- package/src/components/DefinitionList/index.ts +9 -0
- package/src/components/Divider/Divider.module.scss +144 -0
- package/src/components/Divider/Divider.tokens.scss +21 -0
- package/src/components/Divider/Divider.tsx +103 -0
- package/src/components/Divider/index.ts +2 -0
- package/src/components/Dot/Dot.module.scss +40 -0
- package/src/components/Dot/Dot.tokens.scss +4 -0
- package/src/components/Dot/Dot.tsx +45 -0
- package/src/components/Dot/index.ts +2 -0
- package/src/components/Drawer/Body.tsx +23 -0
- package/src/components/Drawer/Close.tsx +35 -0
- package/src/components/Drawer/Content.tsx +93 -0
- package/src/components/Drawer/Drawer.module.scss +232 -0
- package/src/components/Drawer/Drawer.tokens.scss +38 -0
- package/src/components/Drawer/Drawer.tsx +23 -0
- package/src/components/Drawer/DrawerRoot.tsx +190 -0
- package/src/components/Drawer/Footer.tsx +24 -0
- package/src/components/Drawer/Header.tsx +64 -0
- package/src/components/Drawer/Overlay.tsx +82 -0
- package/src/components/Drawer/context.ts +44 -0
- package/src/components/Drawer/index.ts +12 -0
- package/src/components/Drawer/useDragToClose.ts +149 -0
- package/src/components/DropdownMenu/CheckboxItem.tsx +144 -0
- package/src/components/DropdownMenu/Content.tsx +419 -0
- package/src/components/DropdownMenu/DropdownMenu.module.scss +280 -0
- package/src/components/DropdownMenu/DropdownMenu.tokens.scss +88 -0
- package/src/components/DropdownMenu/DropdownMenu.tsx +43 -0
- package/src/components/DropdownMenu/Group.tsx +76 -0
- package/src/components/DropdownMenu/Item.tsx +148 -0
- package/src/components/DropdownMenu/ItemIndicator.tsx +30 -0
- package/src/components/DropdownMenu/Radio.tsx +178 -0
- package/src/components/DropdownMenu/Root.tsx +100 -0
- package/src/components/DropdownMenu/Sub.tsx +332 -0
- package/src/components/DropdownMenu/Trigger.tsx +76 -0
- package/src/components/DropdownMenu/context.ts +65 -0
- package/src/components/DropdownMenu/index.ts +20 -0
- package/src/components/EmojiPicker/EmojiPicker.module.scss +84 -0
- package/src/components/EmojiPicker/EmojiPicker.tokens.scss +24 -0
- package/src/components/EmojiPicker/EmojiPicker.tsx +341 -0
- package/src/components/EmojiPicker/emojiData.ts +245 -0
- package/src/components/EmojiPicker/index.ts +3 -0
- package/src/components/EmptyState/EmptyState.module.scss +102 -0
- package/src/components/EmptyState/EmptyState.tokens.scss +28 -0
- package/src/components/EmptyState/EmptyState.tsx +112 -0
- package/src/components/EmptyState/index.ts +7 -0
- package/src/components/EntityChip/EntityChip.module.scss +275 -0
- package/src/components/EntityChip/EntityChip.tokens.scss +66 -0
- package/src/components/EntityChip/EntityChip.tsx +649 -0
- package/src/components/EntityChip/index.ts +10 -0
- package/src/components/ErrorState/ErrorState.module.scss +105 -0
- package/src/components/ErrorState/ErrorState.tokens.scss +32 -0
- package/src/components/ErrorState/ErrorState.tsx +125 -0
- package/src/components/ErrorState/index.ts +8 -0
- package/src/components/Field/Field.module.scss +46 -0
- package/src/components/Field/Field.tsx +185 -0
- package/src/components/Field/index.ts +2 -0
- package/src/components/FileUpload/FileUpload.module.scss +187 -0
- package/src/components/FileUpload/FileUpload.tokens.scss +81 -0
- package/src/components/FileUpload/FileUpload.tsx +569 -0
- package/src/components/FileUpload/formatBytes.ts +34 -0
- package/src/components/FileUpload/iconForFile.ts +40 -0
- package/src/components/FileUpload/index.ts +2 -0
- package/src/components/FilterChip/FilterChip.module.scss +61 -0
- package/src/components/FilterChip/FilterChip.tokens.scss +25 -0
- package/src/components/FilterChip/FilterChip.tsx +208 -0
- package/src/components/FilterChip/index.ts +5 -0
- package/src/components/FlowCanvas/FlowCanvas.module.scss +329 -0
- package/src/components/FlowCanvas/FlowCanvas.tokens.scss +45 -0
- package/src/components/FlowCanvas/FlowCanvas.tsx +1867 -0
- package/src/components/FlowCanvas/FlowControls.tsx +46 -0
- package/src/components/FlowCanvas/FlowEdge.tsx +58 -0
- package/src/components/FlowCanvas/FlowNode.tsx +76 -0
- package/src/components/FlowCanvas/edgePath.ts +85 -0
- package/src/components/FlowCanvas/index.ts +4 -0
- package/src/components/FlowCanvas/layout.ts +148 -0
- package/src/components/FlowCanvas/spatialNav.ts +76 -0
- package/src/components/FlowCanvas/types.ts +59 -0
- package/src/components/FlowCanvas/useViewport.ts +122 -0
- package/src/components/FormRow/FormRow.module.scss +2 -0
- package/src/components/FormRow/FormRow.tsx +37 -0
- package/src/components/FormRow/index.ts +2 -0
- package/src/components/FormSection/FormSection.module.scss +24 -0
- package/src/components/FormSection/FormSection.tsx +46 -0
- package/src/components/FormSection/index.ts +2 -0
- package/src/components/Grid/Grid.module.scss +162 -0
- package/src/components/Grid/Grid.tokens.scss +13 -0
- package/src/components/Grid/Grid.tsx +226 -0
- package/src/components/Grid/GridItem.tsx +77 -0
- package/src/components/Grid/index.ts +11 -0
- package/src/components/Highlight/Highlight.module.scss +43 -0
- package/src/components/Highlight/Highlight.tokens.scss +35 -0
- package/src/components/Highlight/Highlight.tsx +164 -0
- package/src/components/Highlight/HighlightOverlay.tsx +100 -0
- package/src/components/Highlight/index.ts +2 -0
- package/src/components/HighlightedText/HighlightedText.module.scss +11 -0
- package/src/components/HighlightedText/HighlightedText.tokens.scss +9 -0
- package/src/components/HighlightedText/HighlightedText.tsx +50 -0
- package/src/components/HighlightedText/highlightRanges.ts +44 -0
- package/src/components/HighlightedText/index.ts +2 -0
- package/src/components/IconPicker/IconPicker.module.scss +175 -0
- package/src/components/IconPicker/IconPicker.tokens.scss +40 -0
- package/src/components/IconPicker/IconPicker.tsx +629 -0
- package/src/components/IconPicker/index.ts +9 -0
- package/src/components/IconTile/IconTile.module.scss +57 -0
- package/src/components/IconTile/IconTile.tokens.scss +16 -0
- package/src/components/IconTile/IconTile.tsx +91 -0
- package/src/components/IconTile/index.ts +2 -0
- package/src/components/Image/Image.module.scss +266 -0
- package/src/components/Image/Image.tokens.scss +55 -0
- package/src/components/Image/Image.tsx +342 -0
- package/src/components/Image/index.ts +2 -0
- package/src/components/ImageCrop/ImageCrop.module.scss +274 -0
- package/src/components/ImageCrop/ImageCrop.tokens.scss +33 -0
- package/src/components/ImageCrop/ImageCrop.tsx +542 -0
- package/src/components/ImageCrop/cropGeometry.ts +120 -0
- package/src/components/ImageCrop/extractCropBlob.ts +140 -0
- package/src/components/ImageCrop/index.ts +11 -0
- package/src/components/ImageCrop/useCropPreview.ts +161 -0
- package/src/components/Indent/Indent.module.scss +33 -0
- package/src/components/Indent/Indent.tokens.scss +10 -0
- package/src/components/Indent/Indent.tsx +46 -0
- package/src/components/Indent/index.ts +2 -0
- package/src/components/Input/Input.module.scss +99 -0
- package/src/components/Input/Input.tokens.scss +54 -0
- package/src/components/Input/Input.tsx +105 -0
- package/src/components/Input/Suggestions.tsx +260 -0
- package/src/components/Input/index.ts +2 -0
- package/src/components/Kanban/Kanban.module.scss +39 -0
- package/src/components/Kanban/Kanban.tokens.scss +15 -0
- package/src/components/Kanban/Kanban.tsx +951 -0
- package/src/components/Kanban/index.ts +7 -0
- package/src/components/Kbd/Kbd.module.scss +47 -0
- package/src/components/Kbd/Kbd.tokens.scss +26 -0
- package/src/components/Kbd/Kbd.tsx +73 -0
- package/src/components/Kbd/index.ts +2 -0
- package/src/components/Lightbox/Lightbox.module.scss +283 -0
- package/src/components/Lightbox/Lightbox.tokens.scss +36 -0
- package/src/components/Lightbox/Lightbox.tsx +506 -0
- package/src/components/Lightbox/index.ts +2 -0
- package/src/components/Link/Link.module.scss +78 -0
- package/src/components/Link/Link.tokens.scss +28 -0
- package/src/components/Link/Link.tsx +114 -0
- package/src/components/Link/index.ts +2 -0
- package/src/components/LinkCard/LinkCard.module.scss +92 -0
- package/src/components/LinkCard/LinkCard.tokens.scss +9 -0
- package/src/components/LinkCard/LinkCard.tsx +72 -0
- package/src/components/LinkCard/index.ts +2 -0
- package/src/components/LiquidEditor/AutocompleteMenu.tsx +133 -0
- package/src/components/LiquidEditor/HighlightLayer.tsx +53 -0
- package/src/components/LiquidEditor/InsertVariableMenu.tsx +81 -0
- package/src/components/LiquidEditor/LiquidEditor.module.scss +339 -0
- package/src/components/LiquidEditor/LiquidEditor.tokens.scss +81 -0
- package/src/components/LiquidEditor/LiquidEditor.tsx +412 -0
- package/src/components/LiquidEditor/PreviewPane.tsx +29 -0
- package/src/components/LiquidEditor/caretCoordinates.ts +97 -0
- package/src/components/LiquidEditor/index.ts +7 -0
- package/src/components/LiquidEditor/liquidFilters.ts +23 -0
- package/src/components/LiquidEditor/liquidTokenizer.ts +235 -0
- package/src/components/LiquidEditor/types.ts +101 -0
- package/src/components/LiquidEditor/useLiquidAutocomplete.ts +178 -0
- package/src/components/List/List.module.scss +105 -0
- package/src/components/List/List.tokens.scss +42 -0
- package/src/components/List/List.tsx +124 -0
- package/src/components/List/index.ts +9 -0
- package/src/components/LiveRegion/LiveRegion.module.scss +3 -0
- package/src/components/LiveRegion/LiveRegion.tsx +116 -0
- package/src/components/LiveRegion/index.ts +2 -0
- package/src/components/Logo/Logo.module.scss +139 -0
- package/src/components/Logo/Logo.tokens.scss +14 -0
- package/src/components/Logo/Logo.tsx +126 -0
- package/src/components/Logo/index.ts +2 -0
- package/src/components/Masonry/Masonry.module.scss +42 -0
- package/src/components/Masonry/Masonry.tokens.scss +8 -0
- package/src/components/Masonry/Masonry.tsx +136 -0
- package/src/components/Masonry/index.ts +2 -0
- package/src/components/Masonry/masonryUtils.ts +42 -0
- package/src/components/MediaTile/MediaTile.module.scss +183 -0
- package/src/components/MediaTile/MediaTile.tokens.scss +60 -0
- package/src/components/MediaTile/MediaTile.tsx +208 -0
- package/src/components/MediaTile/index.ts +8 -0
- package/src/components/Modal/Body.tsx +27 -0
- package/src/components/Modal/Close.tsx +45 -0
- package/src/components/Modal/Content.tsx +98 -0
- package/src/components/Modal/Footer.tsx +28 -0
- package/src/components/Modal/Header.tsx +51 -0
- package/src/components/Modal/Modal.module.scss +262 -0
- package/src/components/Modal/Modal.tokens.scss +50 -0
- package/src/components/Modal/Modal.tsx +23 -0
- package/src/components/Modal/ModalRoot.tsx +299 -0
- package/src/components/Modal/Overlay.tsx +101 -0
- package/src/components/Modal/context.ts +60 -0
- package/src/components/Modal/index.ts +11 -0
- package/src/components/MoneyInput/MoneyInput.module.scss +57 -0
- package/src/components/MoneyInput/MoneyInput.tsx +433 -0
- package/src/components/MoneyInput/index.ts +2 -0
- package/src/components/OptionsPicker/OptionsPicker.module.scss +216 -0
- package/src/components/OptionsPicker/OptionsPicker.tokens.scss +68 -0
- package/src/components/OptionsPicker/OptionsPicker.tsx +936 -0
- package/src/components/OptionsPicker/index.ts +9 -0
- package/src/components/OtpInput/OtpInput.module.scss +69 -0
- package/src/components/OtpInput/OtpInput.tokens.scss +45 -0
- package/src/components/OtpInput/OtpInput.tsx +368 -0
- package/src/components/OtpInput/index.ts +2 -0
- package/src/components/Page/Page.module.scss +30 -0
- package/src/components/Page/Page.tokens.scss +11 -0
- package/src/components/Page/Page.tsx +53 -0
- package/src/components/Page/index.ts +2 -0
- package/src/components/PageHeader/PageHeader.module.scss +197 -0
- package/src/components/PageHeader/PageHeader.tokens.scss +41 -0
- package/src/components/PageHeader/PageHeader.tsx +310 -0
- package/src/components/PageHeader/index.ts +20 -0
- package/src/components/Pagination/Pagination.module.scss +117 -0
- package/src/components/Pagination/Pagination.tokens.scss +54 -0
- package/src/components/Pagination/Pagination.tsx +165 -0
- package/src/components/Pagination/index.ts +4 -0
- package/src/components/Pagination/paginationRange.ts +72 -0
- package/src/components/PasswordInput/PasswordInput.module.scss +120 -0
- package/src/components/PasswordInput/PasswordInput.tokens.scss +44 -0
- package/src/components/PasswordInput/PasswordInput.tsx +210 -0
- package/src/components/PasswordInput/index.ts +2 -0
- package/src/components/PasswordStrengthMeter/PasswordStrengthMeter.module.scss +80 -0
- package/src/components/PasswordStrengthMeter/PasswordStrengthMeter.tokens.scss +40 -0
- package/src/components/PasswordStrengthMeter/PasswordStrengthMeter.tsx +96 -0
- package/src/components/PasswordStrengthMeter/index.ts +2 -0
- package/src/components/PersonDisplay/PersonDisplay.module.scss +180 -0
- package/src/components/PersonDisplay/PersonDisplay.tokens.scss +35 -0
- package/src/components/PersonDisplay/PersonDisplay.tsx +420 -0
- package/src/components/PersonDisplay/index.ts +10 -0
- package/src/components/PhoneInput/PhoneInput.module.scss +23 -0
- package/src/components/PhoneInput/PhoneInput.tsx +271 -0
- package/src/components/PhoneInput/index.ts +4 -0
- package/src/components/PhoneInput/phone.ts +128 -0
- package/src/components/PillMenu/PillMenu.module.scss +117 -0
- package/src/components/PillMenu/PillMenu.tokens.scss +22 -0
- package/src/components/PillMenu/PillMenu.tsx +324 -0
- package/src/components/PillMenu/index.ts +2 -0
- package/src/components/Popover/Anchor.tsx +37 -0
- package/src/components/Popover/Close.tsx +45 -0
- package/src/components/Popover/Content.tsx +213 -0
- package/src/components/Popover/Heading.tsx +33 -0
- package/src/components/Popover/Popover.module.scss +177 -0
- package/src/components/Popover/Popover.tokens.scss +22 -0
- package/src/components/Popover/Popover.tsx +23 -0
- package/src/components/Popover/PopoverRoot.tsx +75 -0
- package/src/components/Popover/Trigger.tsx +86 -0
- package/src/components/Popover/context.ts +22 -0
- package/src/components/Popover/index.ts +7 -0
- package/src/components/Progress/Progress.module.scss +110 -0
- package/src/components/Progress/Progress.tokens.scss +44 -0
- package/src/components/Progress/Progress.tsx +129 -0
- package/src/components/Progress/index.ts +2 -0
- package/src/components/QrCode/QrCode.module.scss +97 -0
- package/src/components/QrCode/QrCode.tokens.scss +17 -0
- package/src/components/QrCode/QrCode.tsx +233 -0
- package/src/components/QrCode/index.ts +2 -0
- package/src/components/QrCode/qr.ts +161 -0
- package/src/components/Radio/Radio.module.scss +175 -0
- package/src/components/Radio/Radio.tokens.scss +59 -0
- package/src/components/Radio/Radio.tsx +127 -0
- package/src/components/Radio/RadioGroup.module.scss +42 -0
- package/src/components/Radio/RadioGroup.tsx +120 -0
- package/src/components/Radio/RadioGroupContext.tsx +30 -0
- package/src/components/Radio/index.ts +4 -0
- package/src/components/Rail/Rail.module.scss +510 -0
- package/src/components/Rail/Rail.tokens.scss +92 -0
- package/src/components/Rail/Rail.tsx +287 -0
- package/src/components/Rail/RailCollapseToggle.tsx +57 -0
- package/src/components/Rail/RailFooter.tsx +17 -0
- package/src/components/Rail/RailGroup.tsx +728 -0
- package/src/components/Rail/RailGroupContext.ts +9 -0
- package/src/components/Rail/RailHeader.tsx +23 -0
- package/src/components/Rail/RailItem.tsx +119 -0
- package/src/components/Rail/RailSection.tsx +39 -0
- package/src/components/Rail/RailSpacer.tsx +19 -0
- package/src/components/Rail/index.ts +9 -0
- package/src/components/RelativeDateInput/RelativeDateInput.module.scss +15 -0
- package/src/components/RelativeDateInput/RelativeDateInput.tokens.scss +6 -0
- package/src/components/RelativeDateInput/RelativeDateInput.tsx +474 -0
- package/src/components/RelativeDateInput/index.ts +10 -0
- package/src/components/RelativeDateInput/relativeDate.ts +127 -0
- package/src/components/RichText/RichText.module.scss +5 -0
- package/src/components/RichText/RichText.tsx +70 -0
- package/src/components/RichText/RichTextAttachment.module.scss +82 -0
- package/src/components/RichText/RichTextAttachment.tsx +122 -0
- package/src/components/RichText/_prose.scss +79 -0
- package/src/components/RichText/attachmentIcon.tsx +24 -0
- package/src/components/RichText/engine/attachment.ts +154 -0
- package/src/components/RichText/engine/autolink.ts +64 -0
- package/src/components/RichText/engine/blockUnit.ts +174 -0
- package/src/components/RichText/engine/colorMarks.ts +85 -0
- package/src/components/RichText/engine/escape.ts +11 -0
- package/src/components/RichText/engine/fromHtml.ts +353 -0
- package/src/components/RichText/engine/fromMarkdown.ts +20 -0
- package/src/components/RichText/engine/inlines.ts +82 -0
- package/src/components/RichText/engine/listDepths.ts +32 -0
- package/src/components/RichText/engine/marks.ts +80 -0
- package/src/components/RichText/engine/mdToHtml.ts +178 -0
- package/src/components/RichText/engine/model.ts +157 -0
- package/src/components/RichText/engine/position.ts +118 -0
- package/src/components/RichText/engine/renderDoc.tsx +395 -0
- package/src/components/RichText/engine/renderLink.ts +15 -0
- package/src/components/RichText/engine/renderMention.ts +16 -0
- package/src/components/RichText/engine/safeHref.ts +22 -0
- package/src/components/RichText/engine/toHtml.ts +154 -0
- package/src/components/RichText/engine/toMarkdown.ts +97 -0
- package/src/components/RichText/engine/transforms.ts +434 -0
- package/src/components/RichText/imageMaxHeightStyle.ts +10 -0
- package/src/components/RichText/index.ts +31 -0
- package/src/components/RichTextEditor/RichTextAttachmentConfig.tsx +264 -0
- package/src/components/RichTextEditor/RichTextBlockControls.tsx +365 -0
- package/src/components/RichTextEditor/RichTextBlockMenu.tsx +162 -0
- package/src/components/RichTextEditor/RichTextColorMenu.tsx +115 -0
- package/src/components/RichTextEditor/RichTextEditor.module.scss +298 -0
- package/src/components/RichTextEditor/RichTextEditor.tsx +1910 -0
- package/src/components/RichTextEditor/RichTextImageResizer.tsx +131 -0
- package/src/components/RichTextEditor/RichTextLinkEditor.tsx +151 -0
- package/src/components/RichTextEditor/RichTextMentionMenu.tsx +99 -0
- package/src/components/RichTextEditor/RichTextToolbar.tsx +379 -0
- package/src/components/RichTextEditor/autolinkInput.ts +65 -0
- package/src/components/RichTextEditor/blockDrop.ts +9 -0
- package/src/components/RichTextEditor/blockReflow.ts +62 -0
- package/src/components/RichTextEditor/commands.ts +300 -0
- package/src/components/RichTextEditor/history.ts +93 -0
- package/src/components/RichTextEditor/icons.tsx +152 -0
- package/src/components/RichTextEditor/imageSize.ts +70 -0
- package/src/components/RichTextEditor/index.ts +5 -0
- package/src/components/RichTextEditor/input.ts +209 -0
- package/src/components/RichTextEditor/inputRules.ts +71 -0
- package/src/components/RichTextEditor/links.ts +103 -0
- package/src/components/RichTextEditor/mentionContext.ts +37 -0
- package/src/components/RichTextEditor/mentions.ts +28 -0
- package/src/components/RichTextEditor/preventSelectionLoss.ts +15 -0
- package/src/components/RichTextEditor/selection.ts +415 -0
- package/src/components/RichTextEditor/shortcuts.ts +44 -0
- package/src/components/RichTextEditor/useAnchoredFloating.ts +50 -0
- package/src/components/RichTextEditor/useAttachmentConfig.ts +134 -0
- package/src/components/RichTextEditor/useDismissOnOutsidePointerDown.ts +26 -0
- package/src/components/RichTextEditor/useLinkEditor.ts +107 -0
- package/src/components/RichTextEditor/useMention.ts +226 -0
- package/src/components/RichTextEditor/useOverlayRect.ts +73 -0
- package/src/components/RichTextEditor/useUpload.ts +246 -0
- package/src/components/Screen/Screen.module.scss +46 -0
- package/src/components/Screen/Screen.tokens.scss +20 -0
- package/src/components/Screen/Screen.tsx +97 -0
- package/src/components/Screen/index.ts +2 -0
- package/src/components/ScrollArea/ScrollArea.module.scss +39 -0
- package/src/components/ScrollArea/ScrollArea.tokens.scss +15 -0
- package/src/components/ScrollArea/ScrollArea.tsx +154 -0
- package/src/components/ScrollArea/index.ts +2 -0
- package/src/components/Select/Chip.tsx +45 -0
- package/src/components/Select/Empty.tsx +39 -0
- package/src/components/Select/Error.tsx +51 -0
- package/src/components/Select/HiddenInputs.tsx +44 -0
- package/src/components/Select/Listbox.tsx +557 -0
- package/src/components/Select/Loading.tsx +30 -0
- package/src/components/Select/Select.module.scss +512 -0
- package/src/components/Select/Select.tokens.scss +140 -0
- package/src/components/Select/Select.tsx +685 -0
- package/src/components/Select/Trigger.tsx +952 -0
- package/src/components/Select/context.ts +101 -0
- package/src/components/Select/emptyStateText.ts +19 -0
- package/src/components/Select/index.ts +9 -0
- package/src/components/Select/useSelectState.ts +157 -0
- package/src/components/Select/utils-types.ts +7 -0
- package/src/components/Select/utils.ts +122 -0
- package/src/components/SettingRow/SettingRow.module.scss +166 -0
- package/src/components/SettingRow/SettingRow.tokens.scss +37 -0
- package/src/components/SettingRow/SettingRow.tsx +242 -0
- package/src/components/SettingRow/index.ts +7 -0
- package/src/components/Skeleton/Skeleton.module.scss +55 -0
- package/src/components/Skeleton/Skeleton.tokens.scss +17 -0
- package/src/components/Skeleton/Skeleton.tsx +131 -0
- package/src/components/Skeleton/index.ts +4 -0
- package/src/components/Skeleton/useSkeletonVisibility.ts +95 -0
- package/src/components/Slider/Slider.module.scss +293 -0
- package/src/components/Slider/Slider.tokens.scss +98 -0
- package/src/components/Slider/Slider.tsx +624 -0
- package/src/components/Slider/index.ts +9 -0
- package/src/components/SlotGrid/SlotGrid.module.scss +61 -0
- package/src/components/SlotGrid/SlotGrid.tokens.scss +21 -0
- package/src/components/SlotGrid/SlotGrid.tsx +111 -0
- package/src/components/SlotGrid/index.ts +2 -0
- package/src/components/SocialButton/SocialButton.module.scss +4 -0
- package/src/components/SocialButton/SocialButton.tsx +40 -0
- package/src/components/SocialButton/index.ts +2 -0
- package/src/components/Sortable/Sortable.module.scss +177 -0
- package/src/components/Sortable/Sortable.tokens.scss +27 -0
- package/src/components/Sortable/Sortable.tsx +572 -0
- package/src/components/Sortable/containerAwareCollision.ts +125 -0
- package/src/components/Sortable/index.ts +13 -0
- package/src/components/SortableGroup/SortableGroup.module.scss +23 -0
- package/src/components/SortableGroup/SortableGroup.tsx +315 -0
- package/src/components/SortableGroup/index.ts +4 -0
- package/src/components/SortableGroup/moveSortableItem.ts +39 -0
- package/src/components/Sparkline/Sparkline.module.scss +28 -0
- package/src/components/Sparkline/Sparkline.tokens.scss +7 -0
- package/src/components/Sparkline/Sparkline.tsx +133 -0
- package/src/components/Sparkline/index.ts +2 -0
- package/src/components/Sparkline/sparklineModel.ts +119 -0
- package/src/components/Split/Split.module.scss +158 -0
- package/src/components/Split/Split.tokens.scss +10 -0
- package/src/components/Split/Split.tsx +231 -0
- package/src/components/Split/index.ts +2 -0
- package/src/components/Stack/Stack.module.scss +67 -0
- package/src/components/Stack/Stack.tokens.scss +11 -0
- package/src/components/Stack/Stack.tsx +100 -0
- package/src/components/Stack/index.ts +2 -0
- package/src/components/StagePath/StagePath.module.scss +255 -0
- package/src/components/StagePath/StagePath.tokens.scss +43 -0
- package/src/components/StagePath/StagePath.tsx +176 -0
- package/src/components/StagePath/index.ts +2 -0
- package/src/components/StatGroup/StatGroup.module.scss +6 -0
- package/src/components/StatGroup/StatGroup.tsx +70 -0
- package/src/components/StatGroup/StatGroupContext.ts +10 -0
- package/src/components/StatGroup/StatTile.module.scss +99 -0
- package/src/components/StatGroup/StatTile.tokens.scss +35 -0
- package/src/components/StatGroup/StatTile.tsx +143 -0
- package/src/components/StatGroup/index.ts +4 -0
- package/src/components/Sticky/Sticky.module.scss +98 -0
- package/src/components/Sticky/Sticky.tokens.scss +17 -0
- package/src/components/Sticky/Sticky.tsx +103 -0
- package/src/components/Sticky/index.ts +2 -0
- package/src/components/Switch/Switch.module.scss +182 -0
- package/src/components/Switch/Switch.tokens.scss +59 -0
- package/src/components/Switch/Switch.tsx +214 -0
- package/src/components/Switch/index.ts +2 -0
- package/src/components/Table/Table.module.scss +308 -0
- package/src/components/Table/Table.tokens.scss +79 -0
- package/src/components/Table/Table.tsx +381 -0
- package/src/components/Table/index.ts +15 -0
- package/src/components/TableOfContents/TableOfContents.module.scss +83 -0
- package/src/components/TableOfContents/TableOfContents.tokens.scss +26 -0
- package/src/components/TableOfContents/TableOfContents.tsx +192 -0
- package/src/components/TableOfContents/activeSection.ts +18 -0
- package/src/components/TableOfContents/index.ts +2 -0
- package/src/components/Tabs/Tabs.module.scss +359 -0
- package/src/components/Tabs/Tabs.tokens.scss +50 -0
- package/src/components/Tabs/Tabs.tsx +538 -0
- package/src/components/Tabs/index.ts +2 -0
- package/src/components/Text/Text.module.scss +115 -0
- package/src/components/Text/Text.tokens.scss +55 -0
- package/src/components/Text/Text.tsx +197 -0
- package/src/components/Text/index.ts +2 -0
- package/src/components/Textarea/Textarea.module.scss +94 -0
- package/src/components/Textarea/Textarea.tokens.scss +48 -0
- package/src/components/Textarea/Textarea.tsx +246 -0
- package/src/components/Textarea/index.ts +2 -0
- package/src/components/Thread/Thread.module.scss +161 -0
- package/src/components/Thread/Thread.tokens.scss +32 -0
- package/src/components/Thread/Thread.tsx +176 -0
- package/src/components/Thread/index.ts +2 -0
- package/src/components/TimeField/TimeField.module.scss +232 -0
- package/src/components/TimeField/TimeField.tsx +793 -0
- package/src/components/TimeField/index.ts +2 -0
- package/src/components/Timeline/Timeline.module.scss +58 -0
- package/src/components/Timeline/Timeline.tokens.scss +13 -0
- package/src/components/Timeline/Timeline.tsx +53 -0
- package/src/components/Timeline/index.ts +2 -0
- package/src/components/Title/Title.module.scss +84 -0
- package/src/components/Title/Title.tokens.scss +37 -0
- package/src/components/Title/Title.tsx +150 -0
- package/src/components/Title/index.ts +2 -0
- package/src/components/Toast/Toast.module.scss +299 -0
- package/src/components/Toast/Toast.tokens.scss +65 -0
- package/src/components/Toast/Toast.tsx +122 -0
- package/src/components/Toast/ToastViewport.tsx +166 -0
- package/src/components/Toast/api.ts +175 -0
- package/src/components/Toast/index.ts +5 -0
- package/src/components/Toast/store.ts +133 -0
- package/src/components/Toast/useToastTimer.ts +94 -0
- package/src/components/Tooltip/Tooltip.module.scss +99 -0
- package/src/components/Tooltip/Tooltip.tokens.scss +23 -0
- package/src/components/Tooltip/Tooltip.tsx +341 -0
- package/src/components/Tooltip/index.ts +2 -0
- package/src/components/TopBar/TopBar.module.scss +235 -0
- package/src/components/TopBar/TopBar.tokens.scss +79 -0
- package/src/components/TopBar/TopBar.tsx +87 -0
- package/src/components/TopBar/TopBarEnd.tsx +20 -0
- package/src/components/TopBar/TopBarIconButton.tsx +86 -0
- package/src/components/TopBar/TopBarSearch.tsx +319 -0
- package/src/components/TopBar/TopBarSearchPanel.tsx +268 -0
- package/src/components/TopBar/TopBarStart.tsx +20 -0
- package/src/components/TopBar/index.ts +11 -0
- package/src/components/Tour/Spotlight.tsx +121 -0
- package/src/components/Tour/Tour.module.scss +210 -0
- package/src/components/Tour/Tour.tokens.scss +58 -0
- package/src/components/Tour/Tour.tsx +617 -0
- package/src/components/Tour/index.ts +2 -0
- package/src/components/Tour/useTourTarget.ts +115 -0
- package/src/components/TreeView/TreeView.module.scss +268 -0
- package/src/components/TreeView/TreeView.tokens.scss +36 -0
- package/src/components/TreeView/TreeView.tsx +536 -0
- package/src/components/TreeView/index.ts +3 -0
- package/src/components/TreeView/treeModel.ts +224 -0
- package/src/components/VisuallyHidden/VisuallyHidden.module.scss +7 -0
- package/src/components/VisuallyHidden/VisuallyHidden.tsx +29 -0
- package/src/components/VisuallyHidden/index.ts +2 -0
- package/src/components/WidgetPreview/WidgetPreview.module.scss +11 -0
- package/src/components/WidgetPreview/WidgetPreview.tokens.scss +26 -0
- package/src/components/WidgetPreview/WidgetPreview.tsx +36 -0
- package/src/components/WidgetPreview/WidgetShape.module.scss +140 -0
- package/src/components/WidgetPreview/WidgetShape.tsx +159 -0
- package/src/components/WidgetPreview/index.ts +2 -0
- package/src/components/_internal/Trend.tsx +27 -0
- package/src/components/_internal/choiceTile.scss +74 -0
- package/src/components/_internal/collapse.scss +10 -0
- package/src/components/_internal/collapse.ts +58 -0
- package/src/components/_internal/currency.ts +257 -0
- package/src/components/_internal/dragAnnouncements.ts +200 -0
- package/src/components/_internal/fieldWiring.ts +225 -0
- package/src/components/_internal/gridSpan.ts +54 -0
- package/src/components/_internal/isoDay.ts +35 -0
- package/src/components/_internal/letterKey.ts +26 -0
- package/src/components/_internal/overlay/focusRestore.ts +21 -0
- package/src/components/_internal/overlay/index.ts +12 -0
- package/src/components/_internal/overlay/tourHold.ts +23 -0
- package/src/components/_internal/overlay/useFocusTrap.ts +120 -0
- package/src/components/_internal/overlay/useInOverlay.ts +81 -0
- package/src/components/_internal/overlay/useOverlayStack.ts +232 -0
- package/src/components/_internal/overlay/useScrollLock.ts +112 -0
- package/src/components/_internal/refs.ts +48 -0
- package/src/components/_internal/statusColor.ts +26 -0
- package/src/components/_internal/timeUtils.ts +23 -0
- package/src/components/_internal/useAsyncOptions.ts +145 -0
- package/src/components/_internal/useClippedTooltip.ts +50 -0
- package/src/components/_internal/useControllableState.ts +50 -0
- package/src/components.manifest.json +1141 -0
- package/src/hooks/useBelowBreakpoint.ts +58 -0
- package/src/i18n/I18nProvider.tsx +52 -0
- package/src/i18n/LocaleProvider.tsx +17 -0
- package/src/i18n/en.ts +648 -0
- package/src/i18n/format.ts +71 -0
- package/src/i18n/index.ts +13 -0
- package/src/i18n/messages.ts +1275 -0
- package/src/i18n/ru.ts +677 -0
- package/src/i18n/useLocale.ts +19 -0
- package/src/i18n/useTranslation.ts +58 -0
- package/src/index.ts +913 -0
- package/src/palette/index.ts +2 -0
- package/src/palette/palette.ts +108 -0
- package/src/styles/dark.scss +4 -0
- package/src/styles/global.scss +7 -0
- package/src/styles/mixins.scss +143 -0
- package/src/styles/reset.scss +78 -0
- package/src/styles/tokens.scss +18 -0
- package/src/styles/typography.scss +44 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# `<Accordion>` — vertically-stacked collapsible panels
|
|
2
|
+
|
|
3
|
+
Compound component for FAQ-style content, settings sections, and any case where you have a list of headings with optional drill-down detail. Two modes: `single` (one open at a time) or `multiple` (any combination).
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { Accordion } from '@eocrm/design-system';
|
|
7
|
+
|
|
8
|
+
// Single-open with collapsible (FAQ-style)
|
|
9
|
+
<Accordion type="single" collapsible defaultValue="faq-2">
|
|
10
|
+
<Accordion.Item value="faq-1">
|
|
11
|
+
<Accordion.Trigger>How do I reset my password?</Accordion.Trigger>
|
|
12
|
+
<Accordion.Content>Visit Settings → Security → Reset.</Accordion.Content>
|
|
13
|
+
</Accordion.Item>
|
|
14
|
+
<Accordion.Item value="faq-2">
|
|
15
|
+
<Accordion.Trigger>How do I export my data?</Accordion.Trigger>
|
|
16
|
+
<Accordion.Content>Use the gear icon → Export → CSV.</Accordion.Content>
|
|
17
|
+
</Accordion.Item>
|
|
18
|
+
</Accordion>
|
|
19
|
+
|
|
20
|
+
// Multiple — independent sections
|
|
21
|
+
<Accordion type="multiple" defaultValue={['account', 'notifications']}>
|
|
22
|
+
<Accordion.Item value="account">...</Accordion.Item>
|
|
23
|
+
<Accordion.Item value="notifications">...</Accordion.Item>
|
|
24
|
+
</Accordion>
|
|
25
|
+
|
|
26
|
+
// Disabled item
|
|
27
|
+
<Accordion.Item value="advanced" disabled>...</Accordion.Item>
|
|
28
|
+
|
|
29
|
+
// Controlled
|
|
30
|
+
<Accordion type="single" value={open} onValueChange={setOpen}>...</Accordion>
|
|
31
|
+
|
|
32
|
+
// Borderless + large, e.g. hero FAQ sections
|
|
33
|
+
<Accordion type="single" collapsible variant="borderless" size="lg">...</Accordion>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
<!-- props:start -->
|
|
37
|
+
|
|
38
|
+
## Props
|
|
39
|
+
|
|
40
|
+
### `AccordionProps`
|
|
41
|
+
|
|
42
|
+
<!-- prettier-ignore -->
|
|
43
|
+
| Prop | Type | Required | Description |
|
|
44
|
+
|---|---|---|---|
|
|
45
|
+
| `variant` | `'bordered' \| 'borderless'` | no | Visual variant. Defaults to `'bordered'`. |
|
|
46
|
+
| `size` | `'sm' \| 'md' \| 'lg'` | no | Trigger size (font + padding). Defaults to `'md'`. |
|
|
47
|
+
| `gap` | `'sm' \| 'md' \| 'lg'` | no | Gap between items → separated "card" look (each item gets its own border + radius; the outer container chrome is dropped). Omit for the joined default. |
|
|
48
|
+
| `indicatorSide` | `'left' \| 'right'` | no | Which side the chevron indicator sits on. Defaults to `'right'`. |
|
|
49
|
+
| `actionsWhenClosed` | `'show' \| 'hide'` | no | Whether `Accordion.Trigger` `actions` stay visible when the item is collapsed. Defaults to `'show'`; `'hide'` fades them out (and drops them from focus order) while closed. |
|
|
50
|
+
| `children` | `ReactNode` | yes | |
|
|
51
|
+
| `type` | `"single" \| "multiple"` | yes | |
|
|
52
|
+
| `value` | `string \| string[]` | no | Controlled open item value. `''` = nothing open (only meaningful when `collapsible`). |
|
|
53
|
+
| `defaultValue` | `string \| string[]` | no | Initial open item for uncontrolled use. |
|
|
54
|
+
| `onValueChange` | `((next: string) => void) \| ((next: string[]) => void)` | no | Fires when the open item changes. |
|
|
55
|
+
| `collapsible` | `boolean` | no | When true, clicking the currently-open item closes it. Default: `false` (matches Radix; prevents accidentally closing the only available content). |
|
|
56
|
+
| …native | | | plus native HTML attributes |
|
|
57
|
+
|
|
58
|
+
### `AccordionContentProps`
|
|
59
|
+
|
|
60
|
+
<!-- prettier-ignore -->
|
|
61
|
+
| Prop | Type | Required | Description |
|
|
62
|
+
|---|---|---|---|
|
|
63
|
+
| `children` | `ReactNode` | yes | |
|
|
64
|
+
| …native | | | plus native `<div>` attributes |
|
|
65
|
+
|
|
66
|
+
### `AccordionItemProps`
|
|
67
|
+
|
|
68
|
+
<!-- prettier-ignore -->
|
|
69
|
+
| Prop | Type | Required | Description |
|
|
70
|
+
|---|---|---|---|
|
|
71
|
+
| `value` | `string` | yes | Unique value used to identify the item in the root's `value`/`onValueChange`. |
|
|
72
|
+
| `disabled` | `boolean` | no | When true, the trigger is non-interactive and keyboard nav skips this item. |
|
|
73
|
+
| `headerLevel` | `'h2' \| 'h3' \| 'h4' \| 'h5' \| 'h6'` | no | Heading level wrapping the trigger. WAI-ARIA APG requires triggers to live inside a heading element. Defaults to `'h3'`. |
|
|
74
|
+
| `children` | `ReactNode` | yes | |
|
|
75
|
+
| …native | | | plus native `<div>` attributes |
|
|
76
|
+
|
|
77
|
+
### `AccordionTriggerProps`
|
|
78
|
+
|
|
79
|
+
<!-- prettier-ignore -->
|
|
80
|
+
| Prop | Type | Required | Description |
|
|
81
|
+
|---|---|---|---|
|
|
82
|
+
| `icon` | `ReactNode` | no | Override the default trigger indicator icon (rotates 180° when open). Pass `null` to suppress the icon entirely. Default: `<ChevronDown />`. |
|
|
83
|
+
| `actions` | `ReactNode` | no | Controls rendered at the **right of the header**, OUTSIDE the toggle button — so their buttons/menus are clickable without toggling the section, and the heading's accessible name stays just the title. Keep it to a few small controls (`<Button iconOnly>`, a `<DropdownMenu>` trigger, a `<Switch>`). |
|
|
84
|
+
| `children` | `ReactNode` | yes | |
|
|
85
|
+
| …native | | | plus native `<button>` attributes |
|
|
86
|
+
|
|
87
|
+
<!-- props:end -->
|
|
88
|
+
|
|
89
|
+
- **`type="single"`** + `collapsible={true}` — one item open at a time, click to close.
|
|
90
|
+
- **`type="multiple"`** — any combination.
|
|
91
|
+
- **`variant`** — `"bordered"` (default; outer border + radius + item dividers) or `"borderless"` (no chrome; for nesting inside Cards or as a quiet section divider).
|
|
92
|
+
- **`size`** — `"sm"` / `"md"` (default) / `"lg"` controls trigger font-size + padding (and content padding).
|
|
93
|
+
- **`gap`** — `"sm"` / `"md"` / `"lg"` separates items into **collapsible cards** (each gets its own border + radius; the joined container chrome is dropped). Omit for the default joined look.
|
|
94
|
+
- **`indicatorSide`** — `"right"` (default) or `"left"` to put the chevron before the title.
|
|
95
|
+
- **`<Accordion.Trigger actions={…}>`** — a controls slot at the **right of the header**, rendered OUTSIDE the toggle button so its buttons/menus are clickable without toggling the section (and don't pollute the heading's accessible name). Keep it to a few small controls.
|
|
96
|
+
- **`actionsWhenClosed`** — `"show"` (default) keeps `actions` always visible; `"hide"` fades them out (and drops them from focus order) while the item is collapsed — for dense sidebars of collapsed cards.
|
|
97
|
+
- **Smooth animation** via CSS `grid-template-rows: 0fr → 1fr`. No JS measurement.
|
|
98
|
+
- **Heading wrapping** — Trigger is wrapped in `<h3>` by default per WAI-ARIA APG. Override via `headerLevel` on Item.
|
|
99
|
+
- **Keyboard**: ArrowDown/Up cycles between triggers, Home/End jumps to ends, Space/Enter toggles. Disabled items are skipped.
|
|
100
|
+
- **Anatomy**: Content is `role="region"` with `aria-labelledby` pointing at its trigger. The Trigger's default indicator is a `<ChevronDown>` that rotates 180° when open.
|
|
101
|
+
|
|
102
|
+
#### When NOT to use
|
|
103
|
+
|
|
104
|
+
- ❌ Mutually-exclusive view switchers → `<Tabs>` (tabs imply parallel content; accordions imply hierarchy).
|
|
105
|
+
- ❌ A simple show/hide toggle for a single section → use a `<Button>` + conditional render.
|
|
106
|
+
- ❌ Step-by-step wizard flows → a dedicated Stepper (not shipped).
|
|
107
|
+
|
|
108
|
+
#### Anti-patterns
|
|
109
|
+
|
|
110
|
+
- ❌ Nesting `<Accordion.Trigger>` inside a heading the consumer also renders manually. Trigger ALREADY wraps itself in a heading.
|
|
111
|
+
- ❌ Setting `aria-expanded` manually on the Trigger via `{...props}`. The component owns the ARIA contract.
|
|
112
|
+
- ❌ Using `headerLevel="h1"`. There should only be one `<h1>` per page; Accordion lives below it.
|
|
113
|
+
|
|
114
|
+
- ❌ Putting bulky or primary content in the Trigger `actions` slot. It is for a few small header controls (an icon `<Button>`, a `<DropdownMenu>` trigger, a `<Switch>`); the section's real content belongs in `<Accordion.Content>`.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# `<AgentAvatar>` — AI agent tile with the acting member
|
|
2
|
+
|
|
3
|
+
**TL;DR:** a neutral tile with the agent's mark, plus a badge of the member it acts for. Use it where the agent is the actor and stands alone, such as a Thread or activity node. In a row with a name, use `PersonDisplay.AgentAvatar` instead (see [PersonDisplay](PersonDisplay.md)).
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
// Agent acting for a member: neutral tile + the member's avatar as a badge
|
|
7
|
+
<AgentAvatar name="Scout" member={{ name: 'Sarah Chen', src: '/avatars/sarah.png' }} />
|
|
8
|
+
|
|
9
|
+
// Agent alone (no `member`): no badge
|
|
10
|
+
<AgentAvatar name="Scout" />
|
|
11
|
+
|
|
12
|
+
// Erased member (`null` or an empty name): placeholder badge, "former member" label
|
|
13
|
+
<AgentAvatar name="Scout" member={null} />
|
|
14
|
+
|
|
15
|
+
// Custom mark; inherits currentColor in the tile
|
|
16
|
+
<AgentAvatar name="Scout" mark={<ScoutLogo />} member={{ name: 'Sarah Chen' }} size="lg" />
|
|
17
|
+
|
|
18
|
+
// Thread node beside a visible byline that already names both: decorative
|
|
19
|
+
<Thread.Item node={<AgentAvatar name="Scout" member={{ name: 'Sarah Chen' }} size="sm" decorative />}>
|
|
20
|
+
<PersonDisplay size="inline">
|
|
21
|
+
<PersonDisplay.Name>Scout</PersonDisplay.Name>
|
|
22
|
+
<PersonDisplay.OnBehalfOf>Sarah Chen</PersonDisplay.OnBehalfOf>
|
|
23
|
+
</PersonDisplay>
|
|
24
|
+
…
|
|
25
|
+
</Thread.Item>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
<!-- props:start -->
|
|
29
|
+
|
|
30
|
+
## Props
|
|
31
|
+
|
|
32
|
+
<!-- prettier-ignore -->
|
|
33
|
+
| Prop | Type | Required | Description |
|
|
34
|
+
|---|---|---|---|
|
|
35
|
+
| `name` | `string` | yes | Agent label; drives the accessible name (an empty label reads as "?", like `Avatar`). Unused when `decorative`. |
|
|
36
|
+
| `mark` | `ReactNode` | no | Agent mark, inherits `currentColor` (a neutral foreground), so pass a glyph drawn with `fill`/`stroke="currentColor"`; marks with hard-coded fills keep their colours (the app's responsibility). Defaults to a lucide `Bot`. |
|
|
37
|
+
| `member` | `AgentAvatarMember \| null` | no | The member the agent acts for. Omit for an agent alone (no badge); `null` or an empty name for an erased member (placeholder badge). |
|
|
38
|
+
| `size` | `'inline' \| 'sm' \| 'md' \| 'lg' \| 'xl'` | no | Diameter, same scale as `Avatar`. The member badge is hidden at `inline`. |
|
|
39
|
+
| `decorative` | `boolean` | no | Hide the avatar from assistive tech (`aria-hidden`, no role or label). Use when visible text next to it already names the agent and the member, e.g. a Thread node beside a "Scout for Sarah Chen" byline; otherwise both are announced. Without such text keep the default: a `role="img"` named "{agent}, on behalf of {member}". `PersonDisplay.AgentAvatar` always sets it. Defaults to `false`. |
|
|
40
|
+
| …native | | | plus native `<span>` attributes |
|
|
41
|
+
|
|
42
|
+
<!-- props:end -->
|
|
43
|
+
|
|
44
|
+
- Same `size` scale as [`Avatar`](Avatar.md). At `inline` the member badge is hidden (too small to read); the label still names the member.
|
|
45
|
+
- Accessible name: `role="img"` with `"{agent}, on behalf of {member}"` (localized), `"{agent}, on behalf of a former member"` when erased, or just the agent name when `member` is omitted. An empty `name` reads as `"?"`, as with `Avatar`.
|
|
46
|
+
- `decorative`: set it when visible text next to the avatar already names the agent and the member (a Thread node beside a byline). It renders `aria-hidden` with no role or label, so nothing is announced twice. With no such text, keep the default `role="img"`. `PersonDisplay.AgentAvatar` always sets it.
|
|
47
|
+
|
|
48
|
+
#### When NOT to use
|
|
49
|
+
|
|
50
|
+
- For a human. A person is an [`Avatar`](Avatar.md) (or `PersonDisplay.Avatar`).
|
|
51
|
+
- Next to an agent name in a row. Use `PersonDisplay.AgentAvatar` + `PersonDisplay.OnBehalfOf`, which localize "for {member}" and truncate it.
|
|
52
|
+
|
|
53
|
+
#### Anti-patterns
|
|
54
|
+
|
|
55
|
+
- ❌ Leaving the default `role="img"` beside a visible "Scout for Sarah Chen" byline. Screen readers announce the agent and member twice; pass `decorative`.
|
|
56
|
+
- ❌ `decorative` with no visible text naming the agent. The avatar then says nothing at all.
|
|
57
|
+
- ❌ Passing a multi-colour brand `mark` and expecting it to come out neutral. The tile only sets `color` (a neutral foreground); draw the glyph with `fill`/`stroke="currentColor"`. Hard-coded fills keep their colours, which is the app's responsibility.
|
|
58
|
+
- ❌ Deriving the brand `mark` from a client-chosen name. Pick the mark from a trusted, server-known agent identity, or an agent can impersonate another product.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# `<Alert>` — persistent in-flow notification
|
|
2
|
+
|
|
3
|
+
Tone-driven banner for messages that need to stay visible while the user reads the page (subscription warnings, save failures, "update available" notices). Complements `<Toast>` (transient).
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { Alert } from '@eocrm/design-system';
|
|
7
|
+
|
|
8
|
+
// Basic
|
|
9
|
+
<Alert tone="info" title="Synced 5 minutes ago" />
|
|
10
|
+
<Alert tone="warning">Your storage is at 85% capacity.</Alert>
|
|
11
|
+
|
|
12
|
+
// With actions
|
|
13
|
+
<Alert tone="warning" title="Update available" actions={<Button size="sm">Reload</Button>}>
|
|
14
|
+
A new version is ready. Reload to apply.
|
|
15
|
+
</Alert>
|
|
16
|
+
|
|
17
|
+
// Dismissible (controlled by consumer)
|
|
18
|
+
const [show, setShow] = useState(true);
|
|
19
|
+
{show && (
|
|
20
|
+
<Alert tone="success" onDismiss={() => setShow(false)}>
|
|
21
|
+
Changes saved.
|
|
22
|
+
</Alert>
|
|
23
|
+
)}
|
|
24
|
+
|
|
25
|
+
// Static callout that is part of the page, not a status change (no live region)
|
|
26
|
+
<Alert tone="warning" live={false} title="Needs action">
|
|
27
|
+
The client asked for a revised quote by Friday.
|
|
28
|
+
</Alert>
|
|
29
|
+
|
|
30
|
+
// Custom icon / suppressed icon
|
|
31
|
+
<Alert tone="info" icon={<Bell size={16} />} title="New mention" />
|
|
32
|
+
<Alert tone="info" icon={null}>Quietly informative.</Alert>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
<!-- props:start -->
|
|
36
|
+
|
|
37
|
+
## Props
|
|
38
|
+
|
|
39
|
+
<!-- prettier-ignore -->
|
|
40
|
+
| Prop | Type | Required | Description |
|
|
41
|
+
|---|---|---|---|
|
|
42
|
+
| `tone` | `'info' \| 'success' \| 'warning' \| 'error'` | no | Tone. Defaults to `'info'`. See `AlertTone` for full descriptions. |
|
|
43
|
+
| `title` | `ReactNode` | no | Optional bold heading. Renders above the description. ReactNode so you can embed inline emphasis (`<>Update <strong>1.2.3</strong> available</>`). The native HTML `title` attribute (tooltip-on-hover) is collapsed by this prop. Use `aria-label` if you need that. |
|
|
44
|
+
| `children` | `ReactNode` | no | Description body. Rendered below the title in muted color. |
|
|
45
|
+
| `icon` | `ReactNode` | no | Override the tone's default icon. Pass any ReactNode (typically a lucide-react icon). Pass `null` to hide the icon entirely. Defaults: `info` → `Info`, `success` → `CheckCircle2`, `warning` → `AlertTriangle`, `error` → `XCircle`. |
|
|
46
|
+
| `actions` | `ReactNode` | no | Optional action row rendered below the description. Typically a single Button or a Cluster of two ("Retry", "Dismiss"). The component doesn't manage the action row's layout — pass a pre-laid-out node. |
|
|
47
|
+
| `live` | `boolean` | no | Whether the Alert is a live region. Defaults to `true`: `role="status"` (polite), or `role="alert"` (assertive) for `tone="error"` — right for a message that APPEARS in response to something (a save failed, an update arrived). Pass `false` for a callout that is simply part of the page when it opens — a "Needs action" note in each card of a list, a standing warning in a form. It renders `role="note"` with the same visuals and no live semantics, so screen readers read it in place instead of announcing it. N live Alerts mounting together can queue N announcements. The tone is shown by the icon's shape and the colour only; it is not exposed to assistive tech in either mode (the icon is decorative). With `live={false}`, `tone="error"` also loses the "alert" role that browse-mode readers speak. So put the urgency in `title` ("Needs action"). Decide it at mount: flipping `false` → `true` on a mounted Alert turns content that is already there into a live region, which announces nothing. |
|
|
48
|
+
| `onDismiss` | `(() => void)` | no | Called when the user clicks the close (×) button. When set, the close button renders; when omitted, no close button. Alert is **controlled** — the component does NOT manage internal hidden state. Hide via conditional render in the consumer: (example below) |
|
|
49
|
+
| …native | | | plus native HTML attributes |
|
|
50
|
+
|
|
51
|
+
**`onDismiss`** example:
|
|
52
|
+
|
|
53
|
+
<!-- prettier-ignore -->
|
|
54
|
+
```tsx
|
|
55
|
+
const [show, setShow] = useState(true);
|
|
56
|
+
{show && <Alert tone="success" onDismiss={() => setShow(false)}>Saved</Alert>}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
<!-- props:end -->
|
|
60
|
+
|
|
61
|
+
- **Four tones** (`info` / `success` / `warning` / `error`). Default icon + accent stripe per tone.
|
|
62
|
+
- **`role="alert"`** only for `error` (assertive, interrupts SR). Others use `role="status"` (polite). Both only while `live` (the default).
|
|
63
|
+
- **`live={false}`** for a static callout that is part of the page when it opens (a "Needs action" note in each card of a list): renders `role="note"`, the same visuals, and no live region, so N of them don't queue N announcements. Keep the default for messages that appear in response to something. The tone (icon + colour) is never exposed to assistive tech, and `tone="error" live={false}` also loses the spoken "alert" role, so put the urgency in `title`. Decide `live` at mount: flipping it later announces nothing.
|
|
64
|
+
- **Persistent** — no auto-dismiss. Use Toast for transient messages.
|
|
65
|
+
- **Controlled dismiss** — `onDismiss` callback fires on × click; consumer hides via conditional render.
|
|
66
|
+
- **`icon={null}`** suppresses the icon entirely; any ReactNode overrides the default.
|
|
67
|
+
|
|
68
|
+
#### When NOT to use
|
|
69
|
+
|
|
70
|
+
- ❌ Transient confirmations → `<Toast>` / `toast.success(...)`.
|
|
71
|
+
- ❌ Empty-state placeholders ("No deals yet") → `<EmptyState>`.
|
|
72
|
+
- ❌ Form-field validation messages → inline error text + `aria-describedby`.
|
|
73
|
+
- ❌ Destructive confirmations needing yes/no → `<ConfirmationPopover>` or `<Modal>`.
|
|
74
|
+
|
|
75
|
+
#### Anti-patterns
|
|
76
|
+
|
|
77
|
+
- ❌ Auto-dismissing the Alert with a `setTimeout` — that's what Toast is for.
|
|
78
|
+
- ❌ Using `tone="error"` for non-critical warnings. Reserve `error` for genuine failures; `role="alert"` interrupts screen readers.
|
|
79
|
+
- ❌ Multiple stacked Alerts above a page — pick one (most urgent tone) or compose into the page layout with explicit hierarchy.
|
|
80
|
+
- ❌ A live Alert (the default) for content that exists when the page opens, especially one per list item — each is a live region and some screen readers queue an announcement per item. Pass `live={false}`.
|
|
81
|
+
- ❌ `live={false}` for a message that appears in response to an action (a failed save) — it is never announced. For a must-hear reactive message use `tone="error"` (`role="alert"` is announced on insertion) or a Toast. A polite live Alert that mounts together with its text is not reliably announced either.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# `<AppLayout>` — viewport-filling app shell
|
|
2
|
+
|
|
3
|
+
```tsx
|
|
4
|
+
<AppLayout topBar={<TopBar />} sidebar={<Rail>{nav}</Rail>}>
|
|
5
|
+
<Page>{content}</Page>
|
|
6
|
+
</AppLayout>
|
|
7
|
+
|
|
8
|
+
// No sidebar — top bar + content only
|
|
9
|
+
<AppLayout topBar={<TopBar />}>
|
|
10
|
+
<Page>{content}</Page>
|
|
11
|
+
</AppLayout>
|
|
12
|
+
|
|
13
|
+
// Tall pages: pin the sidebar so a Rail's footer/CollapseToggle stays at the viewport bottom
|
|
14
|
+
<AppLayout sidebar={<Rail>{nav}<Rail.Footer>{footer}</Rail.Footer></Rail>} sidebarPinned>
|
|
15
|
+
<Page>{content}</Page>
|
|
16
|
+
</AppLayout>
|
|
17
|
+
|
|
18
|
+
// System banner above everything + a module-scoped one under the top bar
|
|
19
|
+
<AppLayout
|
|
20
|
+
banner={<Banner tone="info" title="Maintenance Saturday 22:00.">CRM will be read-only.</Banner>}
|
|
21
|
+
contextBanner={suspended && <Banner tone="danger" title="Email sending suspended." />}
|
|
22
|
+
topBar={<TopBar />}
|
|
23
|
+
sidebar={<Rail>{nav}</Rail>}
|
|
24
|
+
>
|
|
25
|
+
<Page>{content}</Page>
|
|
26
|
+
</AppLayout>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
<!-- props:start -->
|
|
30
|
+
|
|
31
|
+
## Props
|
|
32
|
+
|
|
33
|
+
<!-- prettier-ignore -->
|
|
34
|
+
| Prop | Type | Required | Description |
|
|
35
|
+
|---|---|---|---|
|
|
36
|
+
| `banner` | `ReactNode` | no | System-wide banner slot — full WINDOW width, above the sidebar + content row. For messages that apply wherever the user is: maintenance, billing overdue, impersonation. Pass a `<Banner>` (or several, most severe first). Not sticky: it scrolls away with the page while the `topBar` stays pinned. Omit (or pass `null` / `false`) for no wrapper. With `sidebarPinned`, the pinned sidebar starts below this banner, so at scroll position 0 its bottom (a `Rail.Footer`) sits up to one banner height below the fold until the page scrolls past the banner. This is accepted — banners are temporary. |
|
|
37
|
+
| `contextBanner` | `ReactNode` | no | Context banner slot — inside the content column, between the `topBar` and the padded content region, full-bleed within the column. For messages scoped to a module or route ("Email sending suspended" on the Email pages); the app decides which routes render it. Pass a `<Banner>`. Not sticky. Page-level messages belong in the content as `<Alert>` instead. |
|
|
38
|
+
| `topBar` | `ReactNode` | no | Top bar slot — sits above the main content, spanning the content column to the right of the sidebar (not the full window width). Omit for no top bar. |
|
|
39
|
+
| `sidebar` | `ReactNode` | no | Sidebar slot — runs the full height down the left, alongside both the top bar and the content. Sets its own width (intrinsic). Omit for no sidebar. |
|
|
40
|
+
| `sidebarPinned` | `boolean` | no | Pin the sidebar to the viewport: `position: sticky; top: 0; height: 100dvh` with internal overflow scrolling. On pages taller than the viewport the sidebar (and a `Rail` inside it — including its `Rail.Footer` / CollapseToggle) spans exactly the SCREEN, keeping the footer glued to the viewport bottom instead of the page bottom. Default `false` (sidebar stretches to the full row/page height — the original behavior). Prefer this over wrapping the sidebar slot in `Sticky` — the rail pins its footer by filling its own `height: 100%` box, which needs a DEFINITE height to resolve against. `Sticky` sets `align-self: start`, which drops the row stretch that made it definite, so the footer stops pinning. `100dvh` is always relative to the real browser viewport, never to a nested scroll container — so this only pins correctly when AppLayout is the actual outermost, page-scroll shell (its documented top-level use). Nest it inside another scrollable region and the sidebar will size to the whole window, not that region, and overflow it. |
|
|
41
|
+
| `sidebarOverlayBelow` | `'sm' \| 'md' \| 'lg'` | no | Move the sidebar out of the flow and into a left-anchored `<Drawer>` while the **viewport** is at or below a width threshold: `'sm'` 480px / `'md'` 640px / `'lg'` 768px. Omit for no responsive behavior (the default) — the sidebar always renders in the flow. Below the threshold the content column claims the full viewport width, and the sidebar is reachable only by opening the drawer. Render your own trigger (a hamburger in the `topBar`) and drive it with `sidebarOpen` + `onSidebarOpenChange` — AppLayout deliberately renders no trigger of its own, since where it belongs in the bar is the consumer's call, which means both props are effectively required together (see `sidebarOpen`'s doc). Use the exported `useBelowBreakpoint` hook to show that trigger only while the overlay mode is active. `sidebarPinned` is ignored below the threshold: the drawer owns the sidebar's box there, and a `sticky; height: 100dvh` wrapper inside it would size the rail to the window instead of the drawer. Measures the viewport (`matchMedia`), not a container — the sidebar's presence in the row is exactly what the threshold changes, so a container query would be circular. Same scale and same basis as `<Rail collapseBelow>`. The `sidebar` node moves between the in-flow slot and the `Drawer`, remounting it, so a `Rail`'s internal state (expanded `Rail.Group`s, scroll position) resets each time, by design. A `<Rail>` in the drawer scrolls its own body while its footer stays visible; a custom non-`Rail` sidebar needs its own height-filling scroll container, especially on short viewports (phone landscape, ~380px). |
|
|
42
|
+
| `sidebarOpen` | `boolean` | no | Open state of the overlay sidebar. Technically optional, but effectively required together with `onSidebarOpenChange` whenever `sidebarOverlayBelow` is set — AppLayout renders no trigger of its own (see `sidebarOverlayBelow`), so with both omitted nothing can ever open the drawer; Esc/backdrop close it, but there's no way in. Has no effect unless `sidebarOverlayBelow` is set and the viewport is below it. |
|
|
43
|
+
| `onSidebarOpenChange` | `((open: boolean) => void)` | no | Fires whenever the overlay sidebar opens or closes — close button, Esc, backdrop click, swipe, or programmatic. Pair with `sidebarOpen` — see its doc. |
|
|
44
|
+
| `mainLandmark` | `boolean` | no | Render the content region as a `<main>` landmark instead of a plain `<div>`. Pass it on the outermost app shell so the page gets its single `main` landmark without a wrapper element. Leave it off (default) for nested use — demos, documentation previews — so the page never gets two `main`s. |
|
|
45
|
+
| `children` | `ReactNode` | yes | Main content slot — fills the remaining space below the top bar. |
|
|
46
|
+
| …native | | | plus native `<div>` attributes |
|
|
47
|
+
|
|
48
|
+
<!-- props:end -->
|
|
49
|
+
|
|
50
|
+
- Top-level shell layout, mounted **once** at the app root. Matches the CRM shell topology: a **full-height `sidebar`** down the left, an optional `topBar` **over the content column** (not full window width), and the main `children` below it. Root is `min-height: 100vh`.
|
|
51
|
+
- Layout-owning primitive (the `<Page>` / `<Screen>` / `<Rail>` exception to "no layout properties"). Its only own-styling is the main region's gutter + subtle canvas (both token-driven, below); the slots bring their own surfaces. Don't nest inside `<Page>` / `<Screen>`, and don't nest inside another `AppLayout` **in product code** — the only sanctioned AppLayout-in-AppLayout nesting is a demo/documentation preview (see the landmark note below); for a chromeless page use `<Screen>`, for in-page layout use `<Stack>` / `<Cluster>`.
|
|
52
|
+
- **Content padding (default):** the main region ships the canonical shell gutter (`--app-layout-content-padding`, default `var(--space-6)`, stepping down to `var(--space-4)` at ≤640px viewport width) — routed content is padded with **no prop and no raw CSS**. For a full-bleed main region, override the token (`--app-layout-content-padding: 0`) in your scope; there is no prop. Scope the override to a class or `body`, not another `:root` rule — the narrow-viewport step-down is itself a `:root` rule at the same specificity, so two `:root` rules fight on load order below 640px. _Migration:_ if you previously shimmed this gutter with your own `padding: var(--space-6)` wrapper, remove that shim now or the padding doubles.
|
|
53
|
+
- **Content canvas (default):** the main region paints a subtle canvas (`--app-layout-content-background`, default `var(--color-bg-subtle)`) so white `<Card>`s lift off it — matches every mockup, **no prop, no raw CSS**. For a flat content area, override the token (`--app-layout-content-background: transparent`); there is no prop. _Migration:_ if you shimmed your own `background: var(--color-bg-subtle)` on the content region, remove it.
|
|
54
|
+
- **Page-scroll shell:** `min-height: 100vh` means tall content scrolls the whole window, but the chrome doesn't scroll away with it — `topBar` is always pinned (`position: sticky`), and `sidebar` pins too when `sidebarPinned` is set. Only the main content region scrolls. For fixed chrome + independently-scrolling content, override the root to a fixed `height: 100vh` / `100dvh` via `className`.
|
|
55
|
+
- **`sidebarPinned`** (default `false`): on pages taller than the viewport, pins the sidebar wrapper (`position: sticky; top: 0; height: 100dvh; overflow-y: auto`) so a `Rail`'s footer/`CollapseToggle` stays glued to the viewport bottom instead of scrolling away with the page. Don't reach for wrapping the sidebar slot in `<Sticky>` instead — the Rail pins its footer by filling its own `height: 100%` box, and `Sticky`'s `align-self: start` drops the row stretch that made that height definite, so the footer-pinning doesn't work. `100dvh` is always relative to the real browser viewport, not a nested scroll container — only use it when AppLayout is the outermost, page-scroll shell (its documented top-level use); nested inside another scrollable region, the sidebar sizes to the whole window and overflows it.
|
|
56
|
+
- **`sidebarOverlayBelow`** (default: none): below a **viewport** threshold (`'sm'` 480 / `'md'` 640 / `'lg'` 768) the sidebar leaves the flow and renders in a left `<Drawer>`, so the content column gets the full viewport width — the fix for a 240px rail eating a phone screen. **AppLayout renders no trigger** — put a hamburger in your `topBar` and gate it with the `useBelowBreakpoint` hook (see below) so it only shows while the overlay is active. Because there's no built-in trigger, `sidebarOpen` + `onSidebarOpenChange` are technically optional props but **effectively required together**: with both omitted nothing can ever open the drawer. Once open, the drawer has a visible close button and a header that can be swiped left to dismiss, in addition to Esc/backdrop dismissal. `sidebarPinned` is ignored below the threshold (the drawer owns the sidebar's box there).
|
|
57
|
+
- The `children` slot renders inside a plain `<div>` by default, not a `<main>` — `AppLayout` can be nested in a demo or documentation preview (never in product code, see above), so it must not unilaterally claim the page's `main` landmark. The outermost app shell passes **`mainLandmark`** so the content region itself becomes the page's single `<main>` (no wrapper element, same styling); nested previews keep the default. Never set it on more than one `AppLayout` per page.
|
|
58
|
+
|
|
59
|
+
- A `<Sticky>` inside the main region must clear the pinned `topBar`: use `<Sticky top="topbar">`, which resolves to the standard top-bar height plus the normal content gap. Override `--sticky-top-topbar` when your chrome uses a custom height or clearance.
|
|
60
|
+
|
|
61
|
+
#### When NOT to use
|
|
62
|
+
|
|
63
|
+
- ❌ In-page content layout: use `<Stack>` / `<Cluster>` / `<Grid>`.
|
|
64
|
+
- ❌ A chromeless full-bleed page (sign-in / 404 / error): use `<Screen>`.
|
|
65
|
+
|
|
66
|
+
#### Anti-patterns
|
|
67
|
+
|
|
68
|
+
- ❌ Setting `sidebarOverlayBelow` without rendering a trigger: the sidebar becomes unreachable below the threshold.
|
|
69
|
+
- ❌ Duplicating the threshold as a raw media query in consumer CSS to hide the trigger. Use `useBelowBreakpoint(bp)` with the same token so the two can't drift.
|
|
70
|
+
- ❌ `<Rail collapseBelow>` and `sidebarOverlayBelow` at the same breakpoint: the rail would render icon-only inside a drawer that already has room for labels. Pick one behavior per width.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# `<Avatar>` — profile circle
|
|
2
|
+
|
|
3
|
+
```tsx
|
|
4
|
+
<Avatar name="Alex Rivera" />
|
|
5
|
+
<Avatar name="Alex Rivera" src="https://example.com/alex.jpg" size="lg" status="online" />
|
|
6
|
+
|
|
7
|
+
// Hover-discoverable name (off by default; opt in)
|
|
8
|
+
<Avatar name="Alex Rivera" tooltip />
|
|
9
|
+
|
|
10
|
+
// In a table row
|
|
11
|
+
<Cluster gap="sm" align="center">
|
|
12
|
+
<Avatar name={contact.name} size="sm" />
|
|
13
|
+
<span>{contact.name}</span>
|
|
14
|
+
</Cluster>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
<!-- props:start -->
|
|
18
|
+
|
|
19
|
+
## Props
|
|
20
|
+
|
|
21
|
+
<!-- prettier-ignore -->
|
|
22
|
+
| Prop | Type | Required | Description |
|
|
23
|
+
|---|---|---|---|
|
|
24
|
+
| `name` | `string` | yes | The person's name. Required — used as the `alt`/`aria-label`, as the source of the initials, and as the seed for the deterministic fallback color. The same name always renders the same color. When `tooltip` is true, also used as the tooltip body. |
|
|
25
|
+
| `src` | `string` | no | Image URL. When provided, the `<img>` is rendered with `alt={name}`. Empty/whitespace strings are treated as missing. If the image fails to load (404, network), the component automatically falls back to initials. |
|
|
26
|
+
| `size` | `'inline' \| 'sm' \| 'md' \| 'lg' \| 'xl'` | no | Diameter. - `sm` (24px) — table rows, dense lists. - `md` (32px, default) — most uses. - `lg` (40px) — detail-page headers. - `xl` (80px) — member-card popovers / profile headers. - `inline` — one line of the surrounding text tall (`1lh`), initials scaled to match, so an avatar in a text row (a `DefinitionList` value, a table cell, a line of text) doesn't make it taller. Follows the text size; no size decision per call site. Not available on `<AvatarGroup>` — and don't set it per child inside one: the group's overlaps are sized for its fixed steps. Inside `<AvatarGroup>`, defaults to the group's `size`. |
|
|
27
|
+
| `status` | `'online' \| 'busy' \| 'away' \| 'offline'` | no | Presence dot in the bottom-right. - `'online'` — green. - `'busy'` — red. - `'away'` — amber (the categorical amber, dark enough to read at dot size). - `'offline'` — gray. Omit to render no dot at all. Setting `status` changes the accessible name to `"{name}, {status}"` (localized), because colour alone cannot carry the status (WCAG 1.4.1); query with `getByRole('img', { name: 'Alex, online' })`. Each status also renders a distinct shape (filled / half / barred / hollow), so it survives colour-vision deficiency and greyscale; the dot stays `aria-hidden` so nothing is announced twice. |
|
|
28
|
+
| `tooltip` | `boolean` | no | Whether to wrap the avatar in a `<Tooltip>` showing `name`. Defaults to `false` (back-compat — existing renders don't gain a hover affordance). Inside `<AvatarGroup>`, the group's `tooltip` prop becomes the default (which itself defaults to `true` for grouped avatars); explicit per-child still wins. |
|
|
29
|
+
| …native | | | plus native `<span>` attributes |
|
|
30
|
+
|
|
31
|
+
<!-- props:end -->
|
|
32
|
+
|
|
33
|
+
- Inside `<AvatarGroup>`, the group's `size` and `tooltip` become defaults — explicit per-child props still win. The avatar also picks up a `--color-bg` ring so stacked siblings read as distinct.
|
|
34
|
+
- Use `avatarColorIndex(name)` if you need to match an avatar's color elsewhere (e.g. a chart segment).
|
|
35
|
+
|
|
36
|
+
#### When NOT to use
|
|
37
|
+
|
|
38
|
+
- Company logos: Avatars are for people. Use a `Logo` component (not yet shipped) or an `<img>` with rounded corners.
|
|
39
|
+
- As a clickable button. If clicking opens a profile, wrap the Avatar in a `<button>` or `<Link>`; don't make the Avatar itself interactive.
|
|
40
|
+
|
|
41
|
+
#### Anti-patterns
|
|
42
|
+
|
|
43
|
+
- ❌ `<Avatar name="" />`: `name` is required and is the accessible label.
|
|
44
|
+
- ❌ Relying on the status dot's colour, or re-tinting it. The distinct shape is the channel that survives colour-vision deficiency, and `away` vs `busy` collapses without it.
|
|
45
|
+
- ❌ Adding your own visually-hidden status text next to the avatar. It is announced twice, and inside the no-`src` branch (a `role="img"`) ARIA prunes children as presentational, so it would be silent there anyway.
|
|
46
|
+
- ❌ Using Avatar for a non-person icon. Use an icon component.
|
|
47
|
+
- ❌ Wrapping the result in `role="img"` again. With `src`, the inner `<img>` is the labeled image; without it, the wrapper has `role="img" aria-label={name}`, plus the localized `status` when set (`"Alex, online"`).
|
|
48
|
+
|
|
49
|
+
For an AI agent acting on behalf of a member, see [`AgentAvatar`](AgentAvatar.md).
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# `<AvatarGroup>` — Slack-style stacked row of avatars
|
|
2
|
+
|
|
3
|
+
```tsx
|
|
4
|
+
<AvatarGroup max={4} size="md" onOverflowClick={(_e, n) => openMembersPopover(n)}>
|
|
5
|
+
{team.map((m) => (
|
|
6
|
+
<Avatar key={m.id} name={m.name} src={m.avatarUrl} status={m.presence} />
|
|
7
|
+
))}
|
|
8
|
+
</AvatarGroup>
|
|
9
|
+
|
|
10
|
+
// Static: without onOverflowClick the +N is non-interactive
|
|
11
|
+
<AvatarGroup max={3}>
|
|
12
|
+
<Avatar name="Alex Rivera" />
|
|
13
|
+
<Avatar name="Priya Patel" />
|
|
14
|
+
<Avatar name="Tom Kim" />
|
|
15
|
+
<Avatar name="Sara Chen" />
|
|
16
|
+
</AvatarGroup>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
<!-- props:start -->
|
|
20
|
+
|
|
21
|
+
## Props
|
|
22
|
+
|
|
23
|
+
<!-- prettier-ignore -->
|
|
24
|
+
| Prop | Type | Required | Description |
|
|
25
|
+
|---|---|---|---|
|
|
26
|
+
| `children` | `ReactNode` | yes | Avatar children. |
|
|
27
|
+
| `size` | `'sm' \| 'md' \| 'lg' \| 'xl'` | no | Diameter default for child avatars. Defaults to `'md'`. Each child can still override via its own `size` prop (idiomatic React composition — explicit prop wins). For a strictly uniform group, don't set `size` on individual children. - `sm` (24px) — table rows, dense lists. - `md` (32px, default) — most uses. - `lg` (40px) — detail-page headers. - `xl` (80px) — member-card popovers / profile headers. |
|
|
28
|
+
| `max` | `number` | no | Maximum number of visible avatars. Children beyond this count collapse into a single `+N` button at the tail. Defaults to `4`. The `+N` only appears when there are STRICTLY more children than `max`. |
|
|
29
|
+
| `tooltip` | `boolean` | no | Whether to render a name tooltip on each child avatar. Defaults to `true` inside the group (a standalone `<Avatar tooltip>` is opt-in; group context flips that default). Set `false` on the group to suppress all tooltips; overridable per-child by setting `tooltip` on the Avatar. |
|
|
30
|
+
| `onOverflowClick` | `((event: MouseEvent<HTMLButtonElement, MouseEvent>, hiddenCount: number) => void)` | no | Fires when the user clicks the `+N` overflow button. The library does NOT render a popover — apps decide what happens (open a modal, navigate, etc.). When omitted, the `+N` is rendered as a non-interactive `<span>` (still labelled for AT). |
|
|
31
|
+
| …native | | | plus native `<div>` attributes |
|
|
32
|
+
|
|
33
|
+
<!-- props:end -->
|
|
34
|
+
|
|
35
|
+
- Horizontal row of overlapping `<Avatar>`s with a `+N` overflow control when child count exceeds `max` (default `4`).
|
|
36
|
+
- The group wrapper is `role="list"` and each visible avatar is wrapped in a `role="listitem"` div; the +N (button or span) is the last list item.
|
|
37
|
+
- forwardRef to the outer `<div>`. `className` is merged.
|
|
38
|
+
|
|
39
|
+
#### When NOT to use
|
|
40
|
+
|
|
41
|
+
- A single avatar: use `<Avatar>` directly.
|
|
42
|
+
- Showing member counts but not faces: use a `Badge` next to a label.
|
|
43
|
+
|
|
44
|
+
#### Anti-patterns
|
|
45
|
+
|
|
46
|
+
- ❌ Wrapping `<AvatarGroup>` in a `<button>` as one click target. The `+N` is the click affordance; the visible avatars are deliberately not interactive. Wrap individual avatars in `<button>` / `<Link>` if needed.
|
|
47
|
+
- ❌ Mixing avatar sizes inside one group on purpose. Per-child `size` wins, which is useful for emphasising one member but visually noisy if used carelessly.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# `<Badge>` — status / category pill
|
|
2
|
+
|
|
3
|
+
```tsx
|
|
4
|
+
<Badge tone="success">Active</Badge>
|
|
5
|
+
<Badge tone="danger">Churned</Badge>
|
|
6
|
+
|
|
7
|
+
// Stripe variant — rectangular category marker with left stripe:
|
|
8
|
+
<Badge variant="stripe" tone="info">Lead</Badge>
|
|
9
|
+
<Badge variant="stripe" tone="warning">Renewal due</Badge>
|
|
10
|
+
|
|
11
|
+
// Categorical palette color (non-semantic) — for tag-like labels:
|
|
12
|
+
<Badge color="amber">Marketing</Badge>
|
|
13
|
+
<Badge color="teal">Engineering</Badge>
|
|
14
|
+
<Badge variant="stripe" color="violet">Design</Badge>
|
|
15
|
+
|
|
16
|
+
// Tag list
|
|
17
|
+
<Cluster gap="xs">
|
|
18
|
+
<Badge tone="purple">Enterprise</Badge>
|
|
19
|
+
<Badge tone="info">Pipeline 2026</Badge>
|
|
20
|
+
</Cluster>
|
|
21
|
+
|
|
22
|
+
// Inside a heading line: align="middle" so the badge doesn't ride the heading's baseline
|
|
23
|
+
<Title order={1}>
|
|
24
|
+
<Text as="span" size="inherit" tone="muted">ENG-5</Text> Fix login flow{' '}
|
|
25
|
+
<Badge align="middle" tone="warning">In progress</Badge>
|
|
26
|
+
</Title>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
<!-- props:start -->
|
|
30
|
+
|
|
31
|
+
## Props
|
|
32
|
+
|
|
33
|
+
<!-- prettier-ignore -->
|
|
34
|
+
| Prop | Type | Required | Description |
|
|
35
|
+
|---|---|---|---|
|
|
36
|
+
| `tone` | `'neutral' \| 'info' \| 'success' \| 'warning' \| 'danger' \| 'purple'` | no | Semantic tone. - `neutral` (default) — generic tag, no semantic meaning. - `info` — new / in-progress / informational states ("Lead", "Pipeline 2026"). - `success` — positive states ("Active", "Won", "Healthy"). - `warning` — at-risk / pending states ("Renewal due", "Pending review"). - `danger` — negative states ("Churned", "Lost", "Blocked"). - `purple` — special / highlighted categories ("Enterprise", "VIP"). |
|
|
37
|
+
| `size` | `'sm' \| 'md'` | no | Pill height and emphasis. - `md` (20px, default) — the standard "loud label" pill. Uppercase, tracked, semibold. Reads as a deliberate status marker. - `sm` (16px) — quieter inline tag for tight table cells, compact toolbars, or anywhere a 20px uppercase pill visually crowds adjacent text. Drops the uppercase transform and caps tracking so it sits next to body copy without shouting; case is preserved as-typed (write `Active`, get `Active`). Same 11px size and semibold weight as `md`. |
|
|
38
|
+
| `dot` | `'start' \| 'end'` | no | Render a small filled circle in the badge's text color alongside the content. Useful when the dot is the primary status signal and the label is supporting context ("● Active", "Failed ●"). - `start` — dot before the content. - `end` — dot after the content. Omit for no dot. The dot is purely decorative (`aria-hidden`) — the badge text remains the accessible label. |
|
|
39
|
+
| `variant` | `'filled' \| 'stripe'` | no | Visual variant. - `'filled'` (default) — solid tone-tinted pill. The standard "loud status" look. - `'stripe'` — rectangular block with a tone-colored left stripe + soft tinted body. Use for category markers or sidebar labels where pill emphasis is too loud. |
|
|
40
|
+
| `color` | `PaletteColor` | no | Optional categorical palette color. When set, takes precedence over `tone`: the badge fills with the matching `--color-palette-<name>-bg/fg` tokens. Use for non-semantic categorical labels (e.g., audit event namespaces, tag colors) where the 6 semantic tones aren't enough. Both filled and stripe variants are supported; for stripe the left-border picks up the palette `fg` token. |
|
|
41
|
+
| `align` | `'baseline' \| 'middle'` | no | Vertical alignment within the surrounding line of text. - `baseline` (default) — rides the text baseline; right for body-size text. - `middle` — centers the badge on the line box; use for a badge inside a heading line (`<Title>` / `<PageHeader.Title>`), where baseline riding makes the badge look sunken next to large text. |
|
|
42
|
+
| …native | | | plus native `<span>` attributes |
|
|
43
|
+
|
|
44
|
+
<!-- props:end -->
|
|
45
|
+
|
|
46
|
+
- **Non-interactive.** If it's clickable, use `<Button>` instead.
|
|
47
|
+
- Doesn't auto-add `role="status"`. Wrap the badge (or a parent region) in `aria-live="polite"` if a state change should be announced. **This is not an inconsistency with `EntityChip`, which announces its own state** — a Badge tone is a durable property of the row you are looking at, not a change that happens while you are elsewhere, so there is nothing for the component to announce. See [Transient state and screen readers](../transient-state.md) for the rule both follow.
|
|
48
|
+
|
|
49
|
+
#### When NOT to use
|
|
50
|
+
|
|
51
|
+
- ❌ As a button. Badges are non-interactive labels; if it's clickable use a `Button` or `Link`.
|
|
52
|
+
- ❌ For long-form text. Badges are 1-2 words max.
|
|
53
|
+
- ❌ `align="middle"` outside a heading line. It fixes the baseline-vs-line-box mismatch next to large text; keep the default `baseline` next to body-size text.
|
|
54
|
+
|
|
55
|
+
#### Anti-patterns
|
|
56
|
+
|
|
57
|
+
- ❌ Mixing tone meanings across pages. `success` may mean "Won" on Deals and "Active" on Contacts, but never use `success` for anything negative.
|
|
58
|
+
- ❌ Stacking 4+ badges on a single row. If you have that many tags, the design problem is information density, not the badge.
|
|
59
|
+
- ❌ Wrapping a Badge in a `<button>` to make it clickable. Use a `Button` with an appropriate variant.
|
|
60
|
+
- ❌ Using `color` (palette) for status. Use `tone` (semantic); palette colors carry no built-in meaning and the mapping may shift over time.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# `<Banner>` — full-width system / app message bar
|
|
2
|
+
|
|
3
|
+
Tinted, full-width bar for messages about the system or account, not the page: maintenance, "Email sending suspended", "Sending restored". Mount it ONLY in `<AppLayout>`'s `banner` (system-wide, above everything) or `contextBanner` (module/route-scoped, under the top bar) slot. For page content use `<Alert>`.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { AppLayout, Banner, Link } from '@eocrm/design-system';
|
|
7
|
+
|
|
8
|
+
<AppLayout
|
|
9
|
+
banner={
|
|
10
|
+
<Banner
|
|
11
|
+
tone="warning"
|
|
12
|
+
title="Scheduled maintenance"
|
|
13
|
+
action={<Link href="/status">Details</Link>}
|
|
14
|
+
>
|
|
15
|
+
Sat 4 Oct, 22:00–23:00 CET. CRM will be read-only.
|
|
16
|
+
</Banner>
|
|
17
|
+
}
|
|
18
|
+
contextBanner={
|
|
19
|
+
inEmail &&
|
|
20
|
+
suspended && (
|
|
21
|
+
<Banner tone="danger" title="Email sending suspended.">
|
|
22
|
+
Bounce rate 7.2% (limit 5%).
|
|
23
|
+
</Banner>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
topBar={<TopBar />}
|
|
27
|
+
sidebar={<Rail>{nav}</Rail>}
|
|
28
|
+
>
|
|
29
|
+
{routes}
|
|
30
|
+
</AppLayout>;
|
|
31
|
+
|
|
32
|
+
// Module-scoped banner that appeared mid-session: danger + live is announced on insertion (role="alert")
|
|
33
|
+
<Banner
|
|
34
|
+
tone="danger"
|
|
35
|
+
live
|
|
36
|
+
title="Email sending suspended."
|
|
37
|
+
action={
|
|
38
|
+
<Button size="xs" variant="secondary">
|
|
39
|
+
Review bounces
|
|
40
|
+
</Button>
|
|
41
|
+
}
|
|
42
|
+
>
|
|
43
|
+
Bounce rate 7.2% (limit 5%).
|
|
44
|
+
</Banner>;
|
|
45
|
+
|
|
46
|
+
// Dismissible: the app persists the dismissal
|
|
47
|
+
{
|
|
48
|
+
!dismissed && (
|
|
49
|
+
<Banner tone="success" onDismiss={() => setDismissed(true)}>
|
|
50
|
+
Email sending restored.
|
|
51
|
+
</Banner>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
<!-- props:start -->
|
|
57
|
+
|
|
58
|
+
## Props
|
|
59
|
+
|
|
60
|
+
<!-- prettier-ignore -->
|
|
61
|
+
| Prop | Type | Required | Description |
|
|
62
|
+
|---|---|---|---|
|
|
63
|
+
| `tone` | `'info' \| 'success' \| 'warning' \| 'danger'` | no | Tone. Defaults to `'info'`. `info` — advance notice; `success` — condition resolved; `warning` — imminent or degrading; `danger` — broken or blocked now. See `BannerTone`. |
|
|
64
|
+
| `title` | `ReactNode` | no | Optional bold lead-in, rendered INLINE before `children` in the same text flow ("**Scheduled maintenance** Sat 22:00–23:00."). Keep it short — the banner is one line on desktop. Collapses the native HTML `title` (tooltip) attribute. |
|
|
65
|
+
| `children` | `ReactNode` | no | Message text. Wraps with the title. Keep to one sentence. |
|
|
66
|
+
| `icon` | `ReactNode` | no | Override the tone's default icon (any ReactNode, typically a 16px lucide icon with `aria-hidden`). `null` hides it. Defaults: `info` → `Info`, `success` → `CheckCircle2`, `warning` → `AlertTriangle`, `danger` → `XCircle`. |
|
|
67
|
+
| `action` | `ReactNode` | no | One action pinned to the end of the row — typically a `<Link>` ("Details") or a `<Button size="xs">` ("Review bounces"). Banner doesn't lay out multiple actions; pass one. |
|
|
68
|
+
| `live` | `boolean` | no | Whether the Banner is a live region. Defaults to `false`: `role="note"`, right for a banner that is present when the app loads. Pass `live` for a banner that APPEARS mid-session. Caveat: a mid-session Banner mounts together with its text, and a `role="status"` region that mounts with its text is not reliably announced. Only `tone="danger"` + `live` (`role="alert"`) is announced on insertion. For a must-hear non-danger message, pair the banner with a `<LiveRegion>` or a toast. Decide it at mount — flipping it on a mounted Banner announces nothing. |
|
|
69
|
+
| `onDismiss` | `(() => void)` | no | Called when the × is clicked. When set, the × renders. **Controlled** — Banner keeps no hidden state and persists nothing; the app decides what "dismissed" means (per session, per incident, per user) and stops rendering it. |
|
|
70
|
+
| …native | | | plus native `<div>` attributes |
|
|
71
|
+
|
|
72
|
+
<!-- props:end -->
|
|
73
|
+
|
|
74
|
+
- **The tone is spoken.** A visually hidden, localised prefix ("Warning: ") comes before the text.
|
|
75
|
+
- The tone is `danger`, not Alert's `error`.
|
|
76
|
+
- **`live` defaults to `false`** (`role="note"`), because banners are usually present at load. Pass `live` for one that appears mid-session — but a Banner mounts together with its text, so `role="status"` is not reliably announced; only `tone="danger"` + `live` (`role="alert"`) is. For a must-hear non-danger message, add a `<LiveRegion>` or a toast alongside the banner.
|
|
77
|
+
- Neither slot is sticky; only the TopBar pins.
|
|
78
|
+
|
|
79
|
+
**Anti-patterns**
|
|
80
|
+
|
|
81
|
+
- ❌ A Banner inside page content. Use `<Alert>`.
|
|
82
|
+
- ❌ `onDismiss` on a `danger` banner while the condition still holds.
|
|
83
|
+
- ❌ Routine stacks of banners in one slot. Show the most severe first.
|
|
84
|
+
- ❌ A system-wide message in `contextBanner`, or a route-specific one in `banner`.
|
|
85
|
+
- ❌ `live` on a banner that is present when the app loads.
|
|
86
|
+
- ❌ Relying on `live` for a non-danger banner to be announced on appearing. Add a `<LiveRegion>` or a toast.
|
|
87
|
+
- ❌ Multiple actions or a paragraph of text. Link to a details page.
|
|
88
|
+
|
|
89
|
+
**When NOT to use:** content about the current page or a section of it → `<Alert>`; a transient confirmation ("Saved") → `toast.success(...)`; anywhere other than `AppLayout`'s `banner` / `contextBanner` slots (inside page content it is just a borderless Alert).
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# `<BrandIcon>` — third-party brand marks
|
|
2
|
+
|
|
3
|
+
```tsx
|
|
4
|
+
<Button variant="secondary">
|
|
5
|
+
<BrandIcon name="google" size={16} /> Continue with Google
|
|
6
|
+
</Button>
|
|
7
|
+
|
|
8
|
+
// Standalone, labeled
|
|
9
|
+
<BrandIcon name="yandex" title="Yandex" size={24} />
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
<!-- props:start -->
|
|
13
|
+
|
|
14
|
+
## Props
|
|
15
|
+
|
|
16
|
+
<!-- prettier-ignore -->
|
|
17
|
+
| Prop | Type | Required | Description |
|
|
18
|
+
|---|---|---|---|
|
|
19
|
+
| `name` | `'google' \| 'yandex'` | yes | Which brand mark to render. |
|
|
20
|
+
| `size` | `number` | no | Square pixel size (width = height). Defaults to `20`. |
|
|
21
|
+
| `title` | `string` | no | Accessible name. Omit (default) for a decorative icon beside a text label — the icon renders `aria-hidden`. Set it for a standalone icon (e.g. an icon-only button) → `role="img"` + `aria-label`. |
|
|
22
|
+
| …native | | | plus native HTML attributes |
|
|
23
|
+
|
|
24
|
+
<!-- props:end -->
|
|
25
|
+
|
|
26
|
+
Full-color official brand marks for SSO buttons. Ships `google` + `yandex`.
|
|
27
|
+
|
|
28
|
+
- Decorative by default (`aria-hidden`); pass `title` for a labeled standalone icon (`role="img"`).
|
|
29
|
+
|
|
30
|
+
**When NOT to use:** generic UI glyphs → `lucide-react`. Don't recolor brand marks.
|
|
31
|
+
|
|
32
|
+
**Anti-pattern:** ❌ a decorative `BrandIcon` next to visible brand text AND a `title` double-announces ("Google Continue with Google"). Keep it `aria-hidden` (the default) beside a label.
|