@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,129 @@
|
|
|
1
|
+
import { forwardRef, type HTMLAttributes, type ReactNode } from 'react';
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import { useTranslation } from '../../i18n/useTranslation';
|
|
4
|
+
import styles from './Progress.module.scss';
|
|
5
|
+
|
|
6
|
+
/** Visual size — controls the track height. */
|
|
7
|
+
export type ProgressSize = 'sm' | 'md' | 'lg';
|
|
8
|
+
|
|
9
|
+
/** Color tone for the fill. Default `'default'` uses the accent color. */
|
|
10
|
+
export type ProgressTone = 'default' | 'success' | 'warning' | 'danger';
|
|
11
|
+
|
|
12
|
+
/** Label render mode. */
|
|
13
|
+
export type ProgressLabel = boolean | ReactNode;
|
|
14
|
+
|
|
15
|
+
export interface ProgressProps extends Omit<HTMLAttributes<HTMLDivElement>, 'role'> {
|
|
16
|
+
/**
|
|
17
|
+
* Current progress value. Omit (or pass `undefined`) to render the
|
|
18
|
+
* indeterminate animation.
|
|
19
|
+
*
|
|
20
|
+
* Two-channel behavior for out-of-range and degenerate values:
|
|
21
|
+
* - **Visual fill** is clamped to [0%, 100%]. Values outside [0, max]
|
|
22
|
+
* render at the nearest valid bound.
|
|
23
|
+
* - **ARIA `aria-valuenow`** reports the raw number — this is the right
|
|
24
|
+
* screen-reader behavior so consumer bugs (a value drifting past max)
|
|
25
|
+
* surface in audits and SR announcements.
|
|
26
|
+
*
|
|
27
|
+
* Degenerate inputs fall back to the indeterminate animation: `NaN`,
|
|
28
|
+
* `Infinity`, `-Infinity`, and the case where `max <= 0`. This is the
|
|
29
|
+
* common file-upload race condition (`bytes_uploaded / total_bytes`
|
|
30
|
+
* before `total_bytes` is known produces NaN).
|
|
31
|
+
*/
|
|
32
|
+
value?: number;
|
|
33
|
+
/**
|
|
34
|
+
* Upper bound. Defaults to `100`. Consumers using fraction values
|
|
35
|
+
* (0.0–1.0) pass `max={1}`. Consumers tracking a count ("3 of 10")
|
|
36
|
+
* pass `max={10}`.
|
|
37
|
+
*/
|
|
38
|
+
max?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Track height.
|
|
41
|
+
* - `sm` — 4px (compact / inside form rows)
|
|
42
|
+
* - `md` — 8px (default)
|
|
43
|
+
* - `lg` — 12px (page-level emphasis)
|
|
44
|
+
*/
|
|
45
|
+
size?: ProgressSize;
|
|
46
|
+
/**
|
|
47
|
+
* Fill color tone. Defaults to `'default'` (accent blue). Tone applies
|
|
48
|
+
* ONLY to determinate mode; indeterminate always uses the accent tone
|
|
49
|
+
* because state-color semantics don't apply to an unknown total.
|
|
50
|
+
* - `default` — `--color-accent`
|
|
51
|
+
* - `success` — `--color-success`
|
|
52
|
+
* - `warning` — `--color-warning-strong`
|
|
53
|
+
* - `danger` — `--color-danger`
|
|
54
|
+
*/
|
|
55
|
+
tone?: ProgressTone;
|
|
56
|
+
/**
|
|
57
|
+
* Optional label rendered to the RIGHT of the bar.
|
|
58
|
+
* - `false` (default) — no label
|
|
59
|
+
* - `true` — render `{Math.round((value / max) * 100)}%` when determinate.
|
|
60
|
+
* Auto-suppressed when indeterminate (there's no percentage to show).
|
|
61
|
+
* - `ReactNode` — render the node as-is, in BOTH determinate and
|
|
62
|
+
* indeterminate modes. Consumers wanting "Loading…" text next to an
|
|
63
|
+
* indeterminate bar pass `label="Loading…"`.
|
|
64
|
+
*/
|
|
65
|
+
label?: ProgressLabel;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const SIZE_CLASS: Record<ProgressSize, string> = {
|
|
69
|
+
sm: styles.sizeSm,
|
|
70
|
+
md: styles.sizeMd,
|
|
71
|
+
lg: styles.sizeLg,
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const TONE_CLASS: Record<ProgressTone, string> = {
|
|
75
|
+
default: styles.toneDefault,
|
|
76
|
+
success: styles.toneSuccess,
|
|
77
|
+
warning: styles.toneWarning,
|
|
78
|
+
danger: styles.toneDanger,
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Linear progress bar: determinate against `max`, or an indeterminate slide when `value` is omitted; `role="progressbar"` is locked.
|
|
83
|
+
* @see docs/components/Progress.md
|
|
84
|
+
*/
|
|
85
|
+
export const Progress = forwardRef<HTMLDivElement, ProgressProps>(function Progress(
|
|
86
|
+
{ value, max = 100, size = 'md', tone = 'default', label = false, className, ...rest },
|
|
87
|
+
ref,
|
|
88
|
+
) {
|
|
89
|
+
// Determinate requires a finite numeric value AND a positive max. NaN /
|
|
90
|
+
// Infinity / max<=0 are silent consumer-bug paths (esp. file-upload divides
|
|
91
|
+
// bytes_uploaded / total_bytes before total_bytes is known → NaN). Fall
|
|
92
|
+
// back to the indeterminate animation rather than rendering a vanished bar
|
|
93
|
+
// or announcing "NaN percent" to screen readers.
|
|
94
|
+
const determinate = typeof value === 'number' && Number.isFinite(value) && max > 0;
|
|
95
|
+
const indeterminate = !determinate;
|
|
96
|
+
// VISUAL clamp only — ARIA reports the raw `value` via aria-valuenow below.
|
|
97
|
+
const percent = determinate ? Math.max(0, Math.min(100, (value / max) * 100)) : 0;
|
|
98
|
+
// Indeterminate has no aria-valuenow, so aria-valuetext is the only thing a
|
|
99
|
+
// screen reader has. Prefer the consumer's aria-label; the translated
|
|
100
|
+
// fallback is what it hears when there isn't one. This was hardcoded English
|
|
101
|
+
// in both progress components (#503).
|
|
102
|
+
const t = useTranslation();
|
|
103
|
+
const ariaLabel = rest['aria-label'];
|
|
104
|
+
const valuetext = indeterminate ? ariaLabel || t('progress.indeterminate') : undefined;
|
|
105
|
+
|
|
106
|
+
// {...rest} last so consumer overrides win (Pattern A).
|
|
107
|
+
return (
|
|
108
|
+
<div
|
|
109
|
+
ref={ref}
|
|
110
|
+
role="progressbar"
|
|
111
|
+
className={clsx(styles.progress, SIZE_CLASS[size], TONE_CLASS[tone], className)}
|
|
112
|
+
aria-valuemin={0}
|
|
113
|
+
aria-valuemax={max}
|
|
114
|
+
aria-valuenow={determinate ? value : undefined}
|
|
115
|
+
aria-valuetext={valuetext}
|
|
116
|
+
{...rest}
|
|
117
|
+
>
|
|
118
|
+
<div className={styles.track}>
|
|
119
|
+
<div
|
|
120
|
+
className={clsx(styles.fill, indeterminate && styles.indeterminate)}
|
|
121
|
+
style={determinate ? { width: `${percent}%` } : undefined}
|
|
122
|
+
/>
|
|
123
|
+
</div>
|
|
124
|
+
{label !== false && !(label === true && indeterminate) && (
|
|
125
|
+
<span className={styles.label}>{label === true ? `${Math.round(percent)}%` : label}</span>
|
|
126
|
+
)}
|
|
127
|
+
</div>
|
|
128
|
+
);
|
|
129
|
+
});
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
@use '../../styles/mixins' as *;
|
|
2
|
+
@use './QrCode.tokens';
|
|
3
|
+
|
|
4
|
+
.root {
|
|
5
|
+
// Centres the painted frame, which is up to ONE MODULE narrower than the
|
|
6
|
+
// button once the symbol snaps to a whole number of device pixels per module
|
|
7
|
+
// — `side / dpr` CSS px, not a fraction of a pixel. The leftover shows as
|
|
8
|
+
// paper, reading as a slightly wider quiet zone.
|
|
9
|
+
display: flex;
|
|
10
|
+
justify-content: center;
|
|
11
|
+
width: 100%;
|
|
12
|
+
padding: 0;
|
|
13
|
+
border: 0;
|
|
14
|
+
border-radius: var(--qr-radius);
|
|
15
|
+
background: var(--qr-paper);
|
|
16
|
+
cursor: pointer;
|
|
17
|
+
|
|
18
|
+
&:focus-visible {
|
|
19
|
+
@include focus-ring;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Swapping the pair is the whole of the inverted state.
|
|
24
|
+
.root.inverted {
|
|
25
|
+
--qr-ink: var(--color-bg);
|
|
26
|
+
--qr-paper: var(--color-fg);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// The painted box. `relative` anchors the logo overlay — the one internal-anchor
|
|
30
|
+
// exception Rule 4 allows; it lives here rather than on `.root` so the overlay's
|
|
31
|
+
// percentage size resolves against the SYMBOL, not against the button.
|
|
32
|
+
.frame {
|
|
33
|
+
position: relative;
|
|
34
|
+
display: block;
|
|
35
|
+
max-width: 100%;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.svg {
|
|
39
|
+
display: block;
|
|
40
|
+
width: 100%;
|
|
41
|
+
height: auto;
|
|
42
|
+
border-radius: inherit;
|
|
43
|
+
|
|
44
|
+
// Forced-colors mode would override ink and paper with the same pair of
|
|
45
|
+
// system colours and flatten the symbol. For a QR code that is not "looks
|
|
46
|
+
// different", it is "does not scan" — the contrast IS the content.
|
|
47
|
+
forced-color-adjust: none;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.paper {
|
|
51
|
+
fill: var(--qr-paper);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.ink {
|
|
55
|
+
fill: var(--qr-ink);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// The mark is a CSS alpha mask filled with the current ink, so it recolours
|
|
59
|
+
// with the theme AND with the inverted state. `--qr-logo-src` / `--qr-logo-size`
|
|
60
|
+
// are set inline by the component.
|
|
61
|
+
.logo {
|
|
62
|
+
position: absolute;
|
|
63
|
+
top: 50%;
|
|
64
|
+
left: 50%;
|
|
65
|
+
width: var(--qr-logo-size, 0);
|
|
66
|
+
aspect-ratio: 1;
|
|
67
|
+
translate: -50% -50%;
|
|
68
|
+
background: var(--qr-ink);
|
|
69
|
+
mask-image: var(--qr-logo-src, none);
|
|
70
|
+
mask-repeat: no-repeat;
|
|
71
|
+
mask-position: center;
|
|
72
|
+
mask-size: contain;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Compound selector so it wins over `.root` regardless of source order.
|
|
76
|
+
.root.error {
|
|
77
|
+
display: flex;
|
|
78
|
+
align-items: center;
|
|
79
|
+
justify-content: center;
|
|
80
|
+
aspect-ratio: 1;
|
|
81
|
+
padding: var(--qr-error-padding);
|
|
82
|
+
background: var(--qr-error-bg);
|
|
83
|
+
color: var(--qr-error-fg);
|
|
84
|
+
font-size: var(--qr-error-font-size);
|
|
85
|
+
text-align: center;
|
|
86
|
+
|
|
87
|
+
// The plate's content is the consumer's name plus the message, and a name
|
|
88
|
+
// that is a URL or a bare ID is one unbreakable token — the default
|
|
89
|
+
// `overflow-wrap: normal` runs it straight out of the plate sideways.
|
|
90
|
+
// No `min-height` and no `overflow` here on purpose: `aspect-ratio` with an
|
|
91
|
+
// `auto` min-height already gives the box a content-based floor, so the
|
|
92
|
+
// square is a minimum and a wrapped name grows the plate instead of
|
|
93
|
+
// spilling. Pinning `min-height` to anything explicit REPLACES that
|
|
94
|
+
// automatic floor and reintroduces the vertical overflow.
|
|
95
|
+
overflow-wrap: anywhere;
|
|
96
|
+
cursor: default;
|
|
97
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// QrCode.tokens.scss — Component-scoped tokens for <QrCode>. Ink and paper
|
|
2
|
+
// follow the theme; the `.inverted` class swaps the pair, which is why they are
|
|
3
|
+
// a pair of aliases rather than fixed values. Symbol geometry (the 4-module
|
|
4
|
+
// quiet zone, the punch side) is computed from the module count, not tokenised.
|
|
5
|
+
:root {
|
|
6
|
+
// The code itself. Swapped wholesale by `.inverted`.
|
|
7
|
+
--qr-ink: var(--color-fg);
|
|
8
|
+
--qr-paper: var(--color-bg);
|
|
9
|
+
|
|
10
|
+
--qr-radius: var(--radius-sm);
|
|
11
|
+
|
|
12
|
+
// Error plate — shown when the value is empty or too long to encode.
|
|
13
|
+
--qr-error-bg: var(--color-bg-muted);
|
|
14
|
+
--qr-error-fg: var(--color-fg);
|
|
15
|
+
--qr-error-font-size: var(--font-size-sm);
|
|
16
|
+
--qr-error-padding: var(--space-3);
|
|
17
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import {
|
|
2
|
+
forwardRef,
|
|
3
|
+
useLayoutEffect,
|
|
4
|
+
useMemo,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
type ButtonHTMLAttributes,
|
|
8
|
+
type CSSProperties,
|
|
9
|
+
type Ref,
|
|
10
|
+
} from 'react';
|
|
11
|
+
import clsx from 'clsx';
|
|
12
|
+
import { mergeRefs } from '../_internal/refs';
|
|
13
|
+
import { useTranslation } from '../../i18n';
|
|
14
|
+
import { cssUrl, encodeQr, snapWidth, type QrCodeLevel } from './qr';
|
|
15
|
+
import styles from './QrCode.module.scss';
|
|
16
|
+
|
|
17
|
+
export type { QrCodeLevel };
|
|
18
|
+
|
|
19
|
+
/** Share of the punch-out the mark fills, leaving paper around it. */
|
|
20
|
+
const LOGO_FILL = 0.72;
|
|
21
|
+
|
|
22
|
+
export interface QrCodeProps extends Omit<
|
|
23
|
+
ButtonHTMLAttributes<HTMLButtonElement>,
|
|
24
|
+
'value' | 'type'
|
|
25
|
+
> {
|
|
26
|
+
/**
|
|
27
|
+
* The string to encode — a URL, an ID, a vCard. Encoded as UTF-8, so any
|
|
28
|
+
* script works.
|
|
29
|
+
*
|
|
30
|
+
* An empty string, or one past the largest symbol's capacity at the chosen
|
|
31
|
+
* `level` (~1273 bytes at `'H'`, ~2953 at `'L'`), renders a **disabled**
|
|
32
|
+
* button carrying a localized "unavailable" message instead of a code. It
|
|
33
|
+
* never throws — a long CRM field cannot white-screen a page.
|
|
34
|
+
*/
|
|
35
|
+
value: string;
|
|
36
|
+
/**
|
|
37
|
+
* URL of a **single-colour** SVG to place in the centre. It is used as a CSS
|
|
38
|
+
* alpha mask and filled with the code's current ink, so it recolours itself
|
|
39
|
+
* in both themes and in the inverted state — the file's own colours are
|
|
40
|
+
* discarded. Full-colour artwork and photographs are not supported.
|
|
41
|
+
*
|
|
42
|
+
* Setting this also raises the default `level` to `'H'`, because the
|
|
43
|
+
* punch-out destroys modules outright.
|
|
44
|
+
*/
|
|
45
|
+
logo?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Error-correction level — how much damage the symbol survives. `'L'` ~7%,
|
|
48
|
+
* `'M'` ~15%, `'Q'` ~25%, `'H'` ~30%. Higher levels need a larger symbol for
|
|
49
|
+
* the same data. Defaults to `'H'` when `logo` is set, `'M'` otherwise.
|
|
50
|
+
*/
|
|
51
|
+
level?: QrCodeLevel;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Snap the painted width down to a whole number of DEVICE pixels per module.
|
|
56
|
+
*
|
|
57
|
+
* A QR symbol only looks sharp when one module maps to an integer number of
|
|
58
|
+
* device pixels. It otherwise loses either way: `shape-rendering: crispEdges`
|
|
59
|
+
* snaps alternate modules to different widths (measured 4.878px/module
|
|
60
|
+
* rendering as a mix of 4px and 5px — a 20% jitter across the grid), while
|
|
61
|
+
* default antialiasing keeps the geometry even but spends most of a pixel
|
|
62
|
+
* blending every edge. Both read as a blurry code.
|
|
63
|
+
*
|
|
64
|
+
* So the component measures the box the parent gave it and paints the largest
|
|
65
|
+
* exact multiple that fits, centred, with the shortfall showing as a slightly
|
|
66
|
+
* wider quiet zone. The shortfall is up to ONE MODULE — `side / dpr` CSS px,
|
|
67
|
+
* not a fraction of a pixel — which is why `snapWidth` refuses the trade past
|
|
68
|
+
* a bounded share of the box.
|
|
69
|
+
*
|
|
70
|
+
* Returns `null` when it cannot measure (SSR, jsdom, a zero-width container,
|
|
71
|
+
* a box too small for even 1px per module) or when snapping would cost more
|
|
72
|
+
* than `snapWidth`'s budget; callers fall back to fluid `width: 100%` without
|
|
73
|
+
* `crispEdges`, which is the correct behaviour when the premise does not hold.
|
|
74
|
+
*/
|
|
75
|
+
function useSnappedWidth(side: number): [Ref<HTMLButtonElement>, number | null] {
|
|
76
|
+
const hostRef = useRef<HTMLButtonElement | null>(null);
|
|
77
|
+
const [width, setWidth] = useState<number | null>(null);
|
|
78
|
+
// Tracked in state so a zoom change re-runs the effect and re-subscribes the
|
|
79
|
+
// media query below to the NEW ratio. Seeded from the real ratio rather than
|
|
80
|
+
// from 1: starting at 1 made the effect run twice on mount at any other dpr,
|
|
81
|
+
// and made the FIRST subscription `(resolution: 1dppx)` — a query that never
|
|
82
|
+
// matches on a 2dppx screen, so the initial zoom listener was dead.
|
|
83
|
+
const [dpr, setDpr] = useState(() => globalThis.devicePixelRatio || 1);
|
|
84
|
+
|
|
85
|
+
useLayoutEffect(() => {
|
|
86
|
+
const host = hostRef.current;
|
|
87
|
+
if (!host || side <= 0) return;
|
|
88
|
+
|
|
89
|
+
const recompute = () => {
|
|
90
|
+
const ratio = window.devicePixelRatio || 1;
|
|
91
|
+
setDpr(ratio);
|
|
92
|
+
// The arithmetic lives in `snapWidth` so it can be unit-tested directly,
|
|
93
|
+
// without a stubbed layout, across ratios jsdom cannot report.
|
|
94
|
+
setWidth(snapWidth(host.getBoundingClientRect().width, side, ratio));
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
recompute();
|
|
98
|
+
|
|
99
|
+
const cleanups: Array<() => void> = [];
|
|
100
|
+
|
|
101
|
+
if (typeof ResizeObserver !== 'undefined') {
|
|
102
|
+
const observer = new ResizeObserver(recompute);
|
|
103
|
+
observer.observe(host);
|
|
104
|
+
cleanups.push(() => observer.disconnect());
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Zooming changes devicePixelRatio WITHOUT changing our CSS width, so the
|
|
108
|
+
// ResizeObserver never fires for it. This does.
|
|
109
|
+
if (typeof window.matchMedia === 'function') {
|
|
110
|
+
const query = window.matchMedia(`(resolution: ${dpr}dppx)`);
|
|
111
|
+
query.addEventListener('change', recompute);
|
|
112
|
+
cleanups.push(() => query.removeEventListener('change', recompute));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return () => {
|
|
116
|
+
for (const cleanup of cleanups) cleanup();
|
|
117
|
+
};
|
|
118
|
+
}, [side, dpr]);
|
|
119
|
+
|
|
120
|
+
return [hostRef, width];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* A scannable, theme-following QR code that fills its container; clicking swaps ink and paper.
|
|
125
|
+
* @see docs/components/QrCode.md
|
|
126
|
+
*/
|
|
127
|
+
export const QrCode = forwardRef<HTMLButtonElement, QrCodeProps>(function QrCode(
|
|
128
|
+
{ value, logo, level, className, style, onClick, title, 'aria-label': ariaLabel, ...props },
|
|
129
|
+
ref,
|
|
130
|
+
) {
|
|
131
|
+
const t = useTranslation();
|
|
132
|
+
const [inverted, setInverted] = useState(false);
|
|
133
|
+
|
|
134
|
+
// One truthiness rule for the whole component: an empty string is not a logo,
|
|
135
|
+
// not a name and not a tooltip. Splitting this across `logo ?` and
|
|
136
|
+
// `logo !== undefined` punches a hole into a level-M symbol with nothing to
|
|
137
|
+
// fill it; the same slip on the name is below, at the `||` fallbacks.
|
|
138
|
+
const hasLogo = Boolean(logo);
|
|
139
|
+
|
|
140
|
+
const resolvedLevel: QrCodeLevel = level ?? (hasLogo ? 'H' : 'M');
|
|
141
|
+
const matrix = useMemo(() => encodeQr(value, resolvedLevel), [value, resolvedLevel]);
|
|
142
|
+
|
|
143
|
+
// Called unconditionally, before the error branch returns. `0` parks the hook
|
|
144
|
+
// when there is no symbol to measure for.
|
|
145
|
+
const [hostRef, snappedWidth] = useSnappedWidth(matrix?.side ?? 0);
|
|
146
|
+
// Memoized so the merged callback ref keeps its identity across renders —
|
|
147
|
+
// a fresh function would detach and reattach the node on every render.
|
|
148
|
+
const rootRef = useMemo(() => mergeRefs(ref, hostRef), [ref, hostRef]);
|
|
149
|
+
|
|
150
|
+
// {...props} first so the ARIA contract below cannot be clobbered — the
|
|
151
|
+
// component owns `type`, `aria-pressed` and the toggle. `aria-label` and
|
|
152
|
+
// `title` are destructured out instead, so the consumer's value is a
|
|
153
|
+
// FALLBACK rather than something the spread order silently eats.
|
|
154
|
+
if (matrix === null) {
|
|
155
|
+
return (
|
|
156
|
+
<button
|
|
157
|
+
{...props}
|
|
158
|
+
ref={rootRef}
|
|
159
|
+
type="button"
|
|
160
|
+
disabled
|
|
161
|
+
className={clsx(styles.root, styles.error, className)}
|
|
162
|
+
style={style}
|
|
163
|
+
title={title}
|
|
164
|
+
>
|
|
165
|
+
{/* The consumer's name goes in as CONTENT, not as a competing
|
|
166
|
+
aria-label: name-from-content then reads "<name> — <message>", so a
|
|
167
|
+
browse-mode user can tell WHICH code failed without hearing the
|
|
168
|
+
message twice. */}
|
|
169
|
+
{ariaLabel ? `${ariaLabel} — ${t('qrCode.error')}` : t('qrCode.error')}
|
|
170
|
+
</button>
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const { side, path, punch } = matrix;
|
|
175
|
+
const offset = (side - punch) / 2;
|
|
176
|
+
|
|
177
|
+
const logoVars = logo
|
|
178
|
+
? ({
|
|
179
|
+
'--qr-logo-src': cssUrl(logo),
|
|
180
|
+
'--qr-logo-size': `${((punch * LOGO_FILL) / side) * 100}%`,
|
|
181
|
+
} as CSSProperties)
|
|
182
|
+
: undefined;
|
|
183
|
+
|
|
184
|
+
return (
|
|
185
|
+
<button
|
|
186
|
+
{...props}
|
|
187
|
+
ref={rootRef}
|
|
188
|
+
type="button"
|
|
189
|
+
aria-pressed={inverted}
|
|
190
|
+
// `||`, not `??`, on both: `aria-label={row.name ?? ''}` is ordinary
|
|
191
|
+
// consumer code, and an empty aria-label does not remove the name — it
|
|
192
|
+
// drops the computation through to `title`, making the invert hint the
|
|
193
|
+
// name. `title=""` likewise means "no tooltip", not "empty tooltip".
|
|
194
|
+
aria-label={ariaLabel || t('qrCode.label')}
|
|
195
|
+
// `title` with an aria-label present is exposed as the DESCRIPTION, not
|
|
196
|
+
// the name — so it explains what pressing does without touching the
|
|
197
|
+
// name-exact contract. Doubles as a tooltip for sighted mouse users.
|
|
198
|
+
title={title || t('qrCode.invertHint')}
|
|
199
|
+
className={clsx(styles.root, inverted && styles.inverted, className)}
|
|
200
|
+
style={{ ...logoVars, ...style }}
|
|
201
|
+
onClick={(event) => {
|
|
202
|
+
setInverted((current) => !current);
|
|
203
|
+
onClick?.(event);
|
|
204
|
+
}}
|
|
205
|
+
>
|
|
206
|
+
{/* The painted box, up to one module narrower than the button so the
|
|
207
|
+
symbol lands on an exact module grid. It also anchors the logo
|
|
208
|
+
overlay, whose size is a percentage of the SYMBOL — not of the
|
|
209
|
+
button, which is why the anchor moved here. */}
|
|
210
|
+
<span className={styles.frame} style={{ width: snappedWidth ?? '100%' }}>
|
|
211
|
+
<svg
|
|
212
|
+
className={styles.svg}
|
|
213
|
+
viewBox={`0 0 ${side} ${side}`}
|
|
214
|
+
// `crispEdges` ONLY once the width is snapped. Unsnapped it is
|
|
215
|
+
// actively worse than antialiasing: it rounds each module's edges to
|
|
216
|
+
// the pixel grid independently, so adjacent modules come out
|
|
217
|
+
// different widths. Snapped, every module is the same whole number of
|
|
218
|
+
// device pixels and the symbol is pixel-exact.
|
|
219
|
+
shapeRendering={snappedWidth === null ? undefined : 'crispEdges'}
|
|
220
|
+
aria-hidden="true"
|
|
221
|
+
focusable="false"
|
|
222
|
+
>
|
|
223
|
+
<rect className={styles.paper} width={side} height={side} />
|
|
224
|
+
<path className={styles.ink} d={path} />
|
|
225
|
+
{hasLogo && (
|
|
226
|
+
<rect className={styles.paper} x={offset} y={offset} width={punch} height={punch} />
|
|
227
|
+
)}
|
|
228
|
+
</svg>
|
|
229
|
+
{hasLogo && <span className={styles.logo} aria-hidden="true" />}
|
|
230
|
+
</span>
|
|
231
|
+
</button>
|
|
232
|
+
);
|
|
233
|
+
});
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import qrcode from 'qrcode-generator';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Error-correction level — how much of the symbol can be destroyed and still
|
|
5
|
+
* decode. `'L'` ~7%, `'M'` ~15%, `'Q'` ~25%, `'H'` ~30%.
|
|
6
|
+
*/
|
|
7
|
+
export type QrCodeLevel = 'L' | 'M' | 'Q' | 'H';
|
|
8
|
+
|
|
9
|
+
/** Quiet-zone width in modules. Four is the ISO/IEC 18004 minimum. */
|
|
10
|
+
const QUIET = 4;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The share of the symbol's WIDTH the centre punch-out occupies.
|
|
14
|
+
*
|
|
15
|
+
* Note the dimensions: level `'H'`'s ~30% recovery budget is a fraction of
|
|
16
|
+
* codewords, i.e. of AREA, so a width ratio is not directly comparable to it.
|
|
17
|
+
* After the odd-rounding below the realised width peaks at 28%, which is 7.8%
|
|
18
|
+
* of the area — nowhere near the limit. The real constraint on this number is
|
|
19
|
+
* visual, not arithmetic: bigger than about a quarter of the width and the
|
|
20
|
+
* mark stops reading as a mark in a code and starts reading as a hole punched
|
|
21
|
+
* through one.
|
|
22
|
+
*/
|
|
23
|
+
const PUNCH_RATIO = 0.24;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* UTF-8 bytes, re-expressed as a latin1 string.
|
|
27
|
+
*
|
|
28
|
+
* `qrcode-generator`'s default `stringToBytes` is `s.charCodeAt(i) & 0xff`,
|
|
29
|
+
* which silently truncates every code point above U+00FF — `'Привет'` encodes
|
|
30
|
+
* to garbage that still scans. Pre-encoding here turns that truncation into an
|
|
31
|
+
* identity function, because every char we hand it is already a single byte.
|
|
32
|
+
*
|
|
33
|
+
* The usual fix, assigning `qrcode.stringToBytes = …`, is shared mutable state
|
|
34
|
+
* on a module-level singleton: it would reach every other consumer of the
|
|
35
|
+
* package inside the host app. This stays local.
|
|
36
|
+
*/
|
|
37
|
+
export function toByteString(value: string): string {
|
|
38
|
+
return Array.from(new TextEncoder().encode(value), (byte) => String.fromCharCode(byte)).join('');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Characters that would terminate a CSS `url("…")` early. Includes `\f`
|
|
43
|
+
* (U+000C FORM FEED): CSS input preprocessing (CSS Syntax Level 3 §3.3)
|
|
44
|
+
* converts every form feed to a line feed before tokenizing, so it is a
|
|
45
|
+
* newline for this purpose and closes the same breakout as `\n` and `\r`.
|
|
46
|
+
*/
|
|
47
|
+
const CSS_URL_UNSAFE = /["'()\\\n\r\f]/g;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Wrap a consumer-supplied URL in a CSS `url("…")`, percent-encoding anything
|
|
51
|
+
* that could break out of the declaration. React does not sanitise custom
|
|
52
|
+
* properties, so this is a trust boundary.
|
|
53
|
+
*
|
|
54
|
+
* `encodeURI` is the wrong tool: it double-encodes `%` in already-encoded URLs
|
|
55
|
+
* and in `data:` URIs, which is most of what a logo is.
|
|
56
|
+
*/
|
|
57
|
+
export function cssUrl(src: string): string {
|
|
58
|
+
const safe = src.replace(
|
|
59
|
+
CSS_URL_UNSAFE,
|
|
60
|
+
(char) => `%${char.charCodeAt(0).toString(16).padStart(2, '0')}`,
|
|
61
|
+
);
|
|
62
|
+
return `url("${safe}")`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Geometry for one encoded symbol, in module units. */
|
|
66
|
+
export interface QrMatrix {
|
|
67
|
+
/** `viewBox` side — the module count plus both quiet zones. */
|
|
68
|
+
side: number;
|
|
69
|
+
/** A single SVG path covering every dark module, quiet-zone offset applied. */
|
|
70
|
+
path: string;
|
|
71
|
+
/** Side of the centred logo punch-out. Always odd, so it stays on module boundaries. */
|
|
72
|
+
punch: number;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Encode `value` and flatten it to SVG geometry. Returns `null` when there is
|
|
77
|
+
* nothing to encode (empty value) or the value exceeds the largest version at
|
|
78
|
+
* this level — both are the caller's error branch, never a throw.
|
|
79
|
+
*/
|
|
80
|
+
export function encodeQr(value: string, level: QrCodeLevel): QrMatrix | null {
|
|
81
|
+
if (value === '') return null;
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
const qr = qrcode(0, level); // 0 = pick the smallest version that fits
|
|
85
|
+
qr.addData(toByteString(value)); // always Byte mode; addData has no auto-detection
|
|
86
|
+
qr.make();
|
|
87
|
+
|
|
88
|
+
const count = qr.getModuleCount();
|
|
89
|
+
let path = '';
|
|
90
|
+
for (let row = 0; row < count; row += 1) {
|
|
91
|
+
for (let col = 0; col < count; col += 1) {
|
|
92
|
+
if (qr.isDark(row, col)) {
|
|
93
|
+
path += `M${col + QUIET} ${row + QUIET}h1v1h-1z`;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// `| 1` rounds up to the next odd number. The module count is always odd,
|
|
99
|
+
// so an odd punch keeps `(side - punch) / 2` integral. FLOOR, not ceil:
|
|
100
|
+
// double-rounding up puts a v1 symbol's punch at 33% of its width. Both
|
|
101
|
+
// variants sit inside level H's budget on area (7.8% flooring, ~11%
|
|
102
|
+
// ceiling, against ~30%) — flooring wins on looks, not on arithmetic. It
|
|
103
|
+
// caps the width at 28% for every version.
|
|
104
|
+
//
|
|
105
|
+
// Boundary the area maths does NOT cover: from version 7 up (59 bytes at
|
|
106
|
+
// level 'H', which is the default whenever `logo` is set) there is a centre
|
|
107
|
+
// alignment pattern, and the punch lands on it. Reed–Solomon protects data
|
|
108
|
+
// and ECC codewords, not function patterns, so no amount of budget buys
|
|
109
|
+
// that back. Decoders in practice extrapolate the module grid from the
|
|
110
|
+
// finder and timing patterns — which is why every commercial logo-QR
|
|
111
|
+
// generator gets away with it — but it is extrapolation, not recovery, and
|
|
112
|
+
// nothing here would notice it breaking. The playground demo carries an
|
|
113
|
+
// over-v7 logo example so the case is scannable by hand.
|
|
114
|
+
return { side: count + QUIET * 2, path, punch: Math.floor(count * PUNCH_RATIO) | 1 };
|
|
115
|
+
} catch {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* How much of the available width snapping may give up.
|
|
122
|
+
*
|
|
123
|
+
* Rounding down to a whole number of device pixels per module costs up to ONE
|
|
124
|
+
* MODULE — `side / dpr` CSS px, not a fraction of a pixel — and that is a
|
|
125
|
+
* share of the box, not a constant: at 41 modules and dpr 1 a 320px box loses
|
|
126
|
+
* 10%, a 200px box 18%, a 120px box 32%; a 177-module symbol in a 320px box
|
|
127
|
+
* loses 45%.
|
|
128
|
+
*
|
|
129
|
+
* A QR code exists to be scanned, and whether a camera resolves one depends on
|
|
130
|
+
* the ABSOLUTE size of a module, not on whether its edges are crisp. Unbounded
|
|
131
|
+
* snapping therefore shrinks the symbol hardest exactly where it is already
|
|
132
|
+
* tightest, and can push it under the ~100px floor the component's own docs
|
|
133
|
+
* warn about. An eighth is the line: past that, a bigger antialiased code
|
|
134
|
+
* beats a smaller pixel-exact one.
|
|
135
|
+
*/
|
|
136
|
+
const MAX_SNAP_SHORTFALL = 1 / 8;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* The largest painted width, in CSS pixels, that gives every module a whole
|
|
140
|
+
* number of DEVICE pixels — or `null` when that is impossible or too costly.
|
|
141
|
+
*
|
|
142
|
+
* A QR symbol only looks sharp at an integer scale. Off it, both rendering
|
|
143
|
+
* modes lose: `shape-rendering: crispEdges` rounds each module's edges to the
|
|
144
|
+
* pixel grid independently, so neighbours come out different widths (a measured
|
|
145
|
+
* 4.878px/module rendered as a mix of 4px and 5px), while default antialiasing
|
|
146
|
+
* keeps the geometry even but spends most of a pixel blending every edge.
|
|
147
|
+
*
|
|
148
|
+
* Returns `null` when there is nothing to measure (`available` of 0, as SSR and
|
|
149
|
+
* jsdom report), when the box cannot fit even one device pixel per module, or
|
|
150
|
+
* when the snap would cost more than `MAX_SNAP_SHORTFALL` of the width. In all
|
|
151
|
+
* three the caller should fall back to fluid width WITHOUT `crispEdges` —
|
|
152
|
+
* unsnapped, `crispEdges` is the worse of the two.
|
|
153
|
+
*/
|
|
154
|
+
export function snapWidth(available: number, side: number, dpr: number): number | null {
|
|
155
|
+
if (!(available > 0) || !(side > 0) || !(dpr > 0)) return null;
|
|
156
|
+
const scale = Math.floor((available * dpr) / side);
|
|
157
|
+
if (scale < 1) return null;
|
|
158
|
+
|
|
159
|
+
const painted = (side * scale) / dpr;
|
|
160
|
+
return available - painted <= available * MAX_SNAP_SHORTFALL ? painted : null;
|
|
161
|
+
}
|