@flikk/ui 1.0.0-beta.30 → 1.0.0-beta.31
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/CHANGELOG.md +97 -0
- package/dist/components/ai/AgentStatus/AgentStatus.d.ts +3 -0
- package/dist/components/ai/AgentStatus/AgentStatus.js +144 -0
- package/dist/components/ai/AgentStatus/AgentStatus.theme.d.ts +2 -0
- package/dist/components/ai/AgentStatus/AgentStatus.theme.js +37 -0
- package/dist/components/ai/AgentStatus/AgentStatus.types.d.ts +116 -0
- package/dist/components/ai/AgentStatus/index.d.ts +3 -0
- package/dist/components/ai/CodeBlock/CodeBlock.js +3 -3
- package/dist/components/ai/CodeBlock/CodeBlock.theme.js +1 -1
- package/dist/components/ai/PromptInput/PromptInput.js +13 -14
- package/dist/components/ai/PromptInput/PromptInput.theme.js +1 -1
- package/dist/components/ai/PromptInput/VoiceRecorder.js +3 -3
- package/dist/components/ai/Reasoning/Reasoning.d.ts +5 -0
- package/dist/components/ai/Reasoning/Reasoning.js +16 -0
- package/dist/components/ai/Reasoning/Reasoning.types.d.ts +20 -0
- package/dist/components/ai/Reasoning/index.d.ts +2 -0
- package/dist/components/ai/SourceCitation/SourceCitation.d.ts +4 -0
- package/dist/components/ai/SourceCitation/SourceCitation.js +29 -0
- package/dist/components/ai/SourceCitation/SourceCitation.theme.d.ts +2 -0
- package/dist/components/ai/SourceCitation/SourceCitation.theme.js +23 -0
- package/dist/components/ai/SourceCitation/SourceCitation.types.d.ts +55 -0
- package/dist/components/ai/SourceCitation/index.d.ts +3 -0
- package/dist/components/ai/StreamingResponse/ErrorDisplay.js +2 -2
- package/dist/components/ai/StreamingResponse/StreamingResponse.js +1 -3
- package/dist/components/ai/StreamingResponse/StreamingResponse.theme.js +3 -1
- package/dist/components/ai/TokenCounter/TokenCounter.js +2 -2
- package/dist/components/ai/index.d.ts +3 -0
- package/dist/components/ai/index.js +6 -3
- package/dist/components/ai/shared/StatusIndicator.d.ts +8 -0
- package/dist/components/ai/shared/StatusIndicator.js +34 -0
- package/dist/components/canvas/CanvasToolbar.js +2 -2
- package/dist/components/canvas/NodeControls.js +2 -2
- package/dist/components/canvas/NodeRenderer.js +2 -2
- package/dist/components/charts/ActivityRings/ActivityRings.js +2 -2
- package/dist/components/charts/AreaChart/AreaChart.js +21 -32
- package/dist/components/charts/AreaChart/AreaChart.theme.d.ts +1 -6
- package/dist/components/charts/AreaChart/AreaChart.theme.js +7 -19
- package/dist/components/charts/AreaChart/AreaChart.types.d.ts +18 -11
- package/dist/components/charts/AreaChart/AreaChart.types.js +10 -3
- package/dist/components/charts/AreaChart/index.d.ts +1 -1
- package/dist/components/charts/BarChart/BarChart.js +9 -28
- package/dist/components/charts/BarChart/BarChart.types.d.ts +20 -16
- package/dist/components/charts/BarChart/BarChart.types.js +11 -4
- package/dist/components/charts/ComboChart/BarRenderer.js +8 -5
- package/dist/components/charts/ComboChart/ComboChart.js +11 -24
- package/dist/components/charts/ComboChart/ComboChart.types.d.ts +15 -13
- package/dist/components/charts/ComboChart/ComboChart.types.js +14 -2
- package/dist/components/charts/ComboChart/LineRenderer.js +15 -10
- package/dist/components/charts/HorizontalBarChart/HorizontalBarChart.d.ts +16 -0
- package/dist/components/charts/HorizontalBarChart/HorizontalBarChart.js +237 -0
- package/dist/components/charts/HorizontalBarChart/HorizontalBarChart.types.d.ts +62 -0
- package/dist/components/charts/HorizontalBarChart/HorizontalBarChart.types.js +21 -0
- package/dist/components/charts/HorizontalBarChart/index.d.ts +3 -0
- package/dist/components/charts/LineChart/LineChart.js +9 -26
- package/dist/components/charts/LineChart/LineChart.types.d.ts +11 -14
- package/dist/components/charts/LineChart/LineChart.types.js +11 -3
- package/dist/components/charts/LollipopChart/LollipopChart.d.ts +3 -0
- package/dist/components/charts/LollipopChart/LollipopChart.js +361 -0
- package/dist/components/charts/LollipopChart/LollipopChart.types.d.ts +42 -0
- package/dist/components/charts/LollipopChart/LollipopChart.types.js +12 -0
- package/dist/components/charts/LollipopChart/index.d.ts +3 -0
- package/dist/components/charts/ScatterPlot/ScatterPlot.js +5 -4
- package/dist/components/charts/ScatterPlot/ScatterPlot.types.d.ts +14 -19
- package/dist/components/charts/ScatterPlot/ScatterPlot.types.js +9 -2
- package/dist/components/charts/StackedBarChart/StackedBarChart.js +150 -132
- package/dist/components/charts/StackedBarChart/StackedBarChart.types.d.ts +20 -18
- package/dist/components/charts/StackedBarChart/StackedBarChart.types.js +10 -4
- package/dist/components/charts/hooks/useChartAccessibility.d.ts +1 -1
- package/dist/components/charts/hooks/useChartAccessibility.js +3 -0
- package/dist/components/charts/hooks/useTooltipPosition.js +18 -6
- package/dist/components/charts/index.d.ts +6 -0
- package/dist/components/charts/index.js +6 -0
- package/dist/components/charts/shared/ChartAxis/CategoryYAxis.d.ts +14 -0
- package/dist/components/charts/shared/ChartAxis/CategoryYAxis.js +35 -0
- package/dist/components/charts/shared/ChartAxis/ChartAxis.types.d.ts +69 -8
- package/dist/components/charts/shared/ChartAxis/ValueXAxis.d.ts +14 -0
- package/dist/components/charts/shared/ChartAxis/ValueXAxis.js +64 -0
- package/dist/components/charts/shared/ChartAxis/XAxis.js +1 -1
- package/dist/components/charts/shared/ChartAxis/YAxis.js +2 -2
- package/dist/components/charts/shared/ChartAxis/index.d.ts +3 -1
- package/dist/components/charts/shared/ChartGrid/HorizontalGrid.d.ts +0 -2
- package/dist/components/charts/shared/ChartGrid/HorizontalGrid.js +1 -1
- package/dist/components/charts/shared/ChartGrid/VerticalGrid.d.ts +29 -0
- package/dist/components/charts/shared/ChartGrid/VerticalGrid.js +26 -0
- package/dist/components/charts/shared/ChartGrid/index.d.ts +2 -0
- package/dist/components/charts/shared/ChartMarker/ChartMarker.js +7 -1
- package/dist/components/charts/theme/chart.theme.js +18 -23
- package/dist/components/charts/types/chart.types.d.ts +9 -5
- package/dist/components/charts/types/chart.types.js +1 -7
- package/dist/components/charts/utils/animation-utils.js +1 -3
- package/dist/components/charts/utils/bar-emphasis.d.ts +32 -3
- package/dist/components/charts/utils/bar-emphasis.js +24 -8
- package/dist/components/charts/utils/index.d.ts +1 -0
- package/dist/components/charts/utils/series-color.d.ts +7 -0
- package/dist/components/charts/utils/series-color.js +20 -0
- package/dist/components/core/Accordion/Accordion.theme.js +2 -2
- package/dist/components/core/Accordion/AccordionTrigger.js +2 -2
- package/dist/components/core/Alert/Alert.animations.d.ts +2 -1
- package/dist/components/core/Alert/Alert.animations.js +21 -6
- package/dist/components/core/Alert/Alert.js +7 -7
- package/dist/components/core/Avatar/Avatar.js +50 -42
- package/dist/components/core/Avatar/Avatar.theme.d.ts +8 -4
- package/dist/components/core/Avatar/Avatar.theme.js +64 -8
- package/dist/components/core/Avatar/Avatar.types.d.ts +13 -2
- package/dist/components/core/Avatar/index.d.ts +1 -1
- package/dist/components/core/AvatarGroup/AvatarGroup.js +6 -8
- package/dist/components/core/AvatarGroup/AvatarGroup.theme.d.ts +1 -2
- package/dist/components/core/AvatarGroup/AvatarGroup.theme.js +2 -6
- package/dist/components/core/AvatarGroup/AvatarGroup.types.d.ts +0 -6
- package/dist/components/core/AvatarGroup/index.d.ts +1 -1
- package/dist/components/core/Badge/Badge.animations.js +4 -2
- package/dist/components/core/Badge/Badge.js +9 -4
- package/dist/components/core/Badge/Badge.theme.js +18 -12
- package/dist/components/core/Breadcrumbs/Breadcrumbs.theme.d.ts +1 -4
- package/dist/components/core/Breadcrumbs/Breadcrumbs.theme.js +2 -2
- package/dist/components/core/Button/Button.js +9 -17
- package/dist/components/core/Button/Button.ripple.js +2 -2
- package/dist/components/core/Button/Button.theme.d.ts +2 -0
- package/dist/components/core/Button/Button.theme.js +34 -21
- package/dist/components/core/Button/Button.types.d.ts +18 -1
- package/dist/components/core/Calendar/Calendar.js +20 -5
- package/dist/components/core/Calendar/Calendar.theme.js +14 -22
- package/dist/components/core/Calendar/Calendar.types.d.ts +0 -1
- package/dist/components/core/Calendar/CalendarMini/CalendarMini.js +3 -3
- package/dist/components/core/Card/Card.theme.js +12 -4
- package/dist/components/core/Card/CardBody.js +1 -1
- package/dist/components/core/Card/CardFooter.js +1 -1
- package/dist/components/core/Card/CardHeader.js +1 -1
- package/dist/components/core/Carousel/CarouselNext.js +2 -2
- package/dist/components/core/Carousel/CarouselPrevious.js +2 -2
- package/dist/components/core/CommandPalette/CommandPalette.js +5 -4
- package/dist/components/core/CommandPalette/CommandPalette.theme.js +3 -7
- package/dist/components/core/CommandPalette/CommandPalette.types.d.ts +2 -0
- package/dist/components/core/ContextMenu/ContextMenuItem.js +2 -2
- package/dist/components/core/ContextMenu/ContextMenuSubTrigger.js +2 -2
- package/dist/components/core/Drawer/Drawer.theme.js +7 -4
- package/dist/components/core/Drawer/DrawerHeader.js +2 -2
- package/dist/components/core/Dropdown/Dropdown.theme.js +3 -1
- package/dist/components/core/Dropdown/DropdownItem.js +2 -2
- package/dist/components/core/Dropdown/DropdownTrigger.js +16 -5
- package/dist/components/core/HeroCard/HeroCard.theme.js +5 -2
- package/dist/components/core/Kbd/Kbd.theme.js +4 -2
- package/dist/components/core/Link/Link.js +6 -4
- package/dist/components/core/Message/Message.js +2 -2
- package/dist/components/core/Message/Message.theme.js +5 -7
- package/dist/components/core/Message/MessageAudio.js +2 -2
- package/dist/components/core/Message/MessageFile.js +2 -2
- package/dist/components/core/Message/MessageLink.js +2 -2
- package/dist/components/core/Message/MessageStatusIndicator.js +5 -5
- package/dist/components/core/Modal/Modal.d.ts +1 -1
- package/dist/components/core/Modal/Modal.js +2 -2
- package/dist/components/core/Modal/Modal.theme.js +9 -4
- package/dist/components/core/Modal/ModalHeader.js +2 -2
- package/dist/components/core/ModalStack/ModalStackModal.js +2 -2
- package/dist/components/core/NavItem/NavItem.js +6 -6
- package/dist/components/core/OfflineIndicator/OfflineIndicator.animations.d.ts +22 -0
- package/dist/components/core/OfflineIndicator/OfflineIndicator.animations.js +54 -0
- package/dist/components/core/OfflineIndicator/OfflineIndicator.js +23 -24
- package/dist/components/core/OfflineIndicator/OfflineIndicator.theme.js +25 -6
- package/dist/components/core/OfflineIndicator/OfflineIndicator.types.d.ts +17 -0
- package/dist/components/core/PageHeading/PageHeading.theme.js +1 -1
- package/dist/components/core/PageHeading/PageHeadingBackButton.js +2 -2
- package/dist/components/core/Pagination/Pagination.js +3 -3
- package/dist/components/core/Pagination/Pagination.theme.js +3 -3
- package/dist/components/core/Pill/Pill.js +4 -2
- package/dist/components/core/Pill/Pill.theme.js +6 -4
- package/dist/components/core/Rating/Rating.js +4 -5
- package/dist/components/core/Rating/Rating.theme.js +1 -1
- package/dist/components/core/Segmented/Segmented.theme.js +9 -6
- package/dist/components/core/Sidebar/Sidebar.types.d.ts +4 -2
- package/dist/components/core/Sidebar/SidebarHeader.js +2 -2
- package/dist/components/core/Sidebar/SidebarMobileTrigger.js +2 -2
- package/dist/components/core/Sidebar/SidebarSearch.js +3 -3
- package/dist/components/core/Sidebar/SidebarToggle.js +3 -3
- package/dist/components/core/Sidebar/SidebarUserProfile.js +2 -2
- package/dist/components/core/Sortable/Sortable.js +2 -2
- package/dist/components/core/Tag/Tag.js +2 -2
- package/dist/components/core/Tag/Tag.theme.js +4 -2
- package/dist/components/core/Toast/Toast.js +3 -3
- package/dist/components/core/Toast/Toast.types.d.ts +1 -1
- package/dist/components/core/Toast/useToast.js +5 -5
- package/dist/components/core/Tooltip/Tooltip.d.ts +1 -1
- package/dist/components/core/Tooltip/Tooltip.js +42 -10
- package/dist/components/core/Tooltip/Tooltip.types.d.ts +25 -0
- package/dist/components/core/Tree/Tree.js +2 -2
- package/dist/components/core/shared/interaction.animations.d.ts +51 -0
- package/dist/components/core/shared/interaction.animations.js +59 -0
- package/dist/components/data-display/Feed/Feed.js +2 -2
- package/dist/components/data-display/GanttChart/GanttChart.theme.js +1 -1
- package/dist/components/data-display/GanttChart/GanttChartPanel.js +2 -2
- package/dist/components/data-display/GanttChart/GanttTableGroupRow.js +3 -3
- package/dist/components/data-display/GanttChart/GanttTaskContextMenu.js +2 -2
- package/dist/components/data-display/GanttChart/GanttToolbar.js +2 -2
- package/dist/components/data-display/KPI/KPI.js +2 -2
- package/dist/components/data-display/KPI/KPI.theme.js +4 -4
- package/dist/components/data-display/Metric/Metric.js +2 -2
- package/dist/components/data-display/Metric/Metric.theme.js +25 -8
- package/dist/components/data-display/Metric/Metric.types.d.ts +1 -1
- package/dist/components/data-display/Table/Table.theme.js +9 -9
- package/dist/components/data-display/Table/TableActions.js +2 -2
- package/dist/components/data-display/Table/TableActionsMenu.js +1 -1
- package/dist/components/data-display/Table/TableColumnManager.js +2 -2
- package/dist/components/data-display/Table/TableDeclarative.js +3 -3
- package/dist/components/data-display/Table/TableFilter.js +4 -4
- package/dist/components/data-display/Table/TableHeader.js +3 -3
- package/dist/components/data-display/Table/TableRow.js +2 -2
- package/dist/components/data-display/Table/TableSelectionHeader.js +3 -3
- package/dist/components/data-display/Timeline/TimelineMarker.js +2 -2
- package/dist/components/effects/CustomCursor/CustomCursor.js +2 -2
- package/dist/components/effects/CustomCursor/CustomCursor.theme.js +2 -2
- package/dist/components/effects/DotPattern/DotPattern.js +6 -0
- package/dist/components/effects/DotPattern/DotPattern.types.d.ts +4 -2
- package/dist/components/effects/MeshGradient/MeshGradient.utils.d.ts +1 -1
- package/dist/components/effects/MeshGradient/MeshGradient.utils.js +1 -1
- package/dist/components/forms/Checkbox/Checkbox.js +2 -1
- package/dist/components/forms/Checkbox/Checkbox.theme.js +2 -2
- package/dist/components/forms/ColorPicker/ColorPickerEyeDropper.js +2 -2
- package/dist/components/forms/ColorPicker/ColorPickerGradient.js +2 -2
- package/dist/components/forms/ColorPicker/ColorPickerHeader.js +2 -2
- package/dist/components/forms/ColorPicker/ColorPickerImage.js +3 -3
- package/dist/components/forms/ColorPicker/ColorPickerInput.js +2 -2
- package/dist/components/forms/ColorPicker/ColorPickerPresets.js +2 -2
- package/dist/components/forms/ColorPicker/ColorPickerSavedColors.js +2 -2
- package/dist/components/forms/Combobox/Combobox.js +3 -3
- package/dist/components/forms/Combobox/Combobox.theme.js +2 -2
- package/dist/components/forms/DatePicker/DatePicker.animations.js +16 -25
- package/dist/components/forms/DatePicker/DatePicker.theme.js +2 -2
- package/dist/components/forms/DatePicker/DatePickerBody.js +1 -1
- package/dist/components/forms/DatePicker/DatePickerTrigger.js +2 -2
- package/dist/components/forms/DateRangePicker/DateRangePicker.theme.js +3 -3
- package/dist/components/forms/DateRangePicker/DateRangePickerBody.js +1 -1
- package/dist/components/forms/DateRangePicker/DateRangePickerPresets.js +2 -2
- package/dist/components/forms/DateRangePicker/DateRangePickerTrigger.js +2 -2
- package/dist/components/forms/FileUpload/FileUpload.js +4 -4
- package/dist/components/forms/FileUpload/FileUpload.theme.js +5 -1
- package/dist/components/forms/FileUpload/FileUploadProgress.js +7 -7
- package/dist/components/forms/Input/Input.js +6 -7
- package/dist/components/forms/Input/Input.theme.js +19 -15
- package/dist/components/forms/InputAddress/InputAddress.js +10 -3
- package/dist/components/forms/InputCounter/InputCounter.js +3 -3
- package/dist/components/forms/InputOTP/InputOTP.d.ts +3 -12
- package/dist/components/forms/InputOTP/InputOTP.js +140 -151
- package/dist/components/forms/InputOTP/InputOTP.theme.d.ts +5 -1
- package/dist/components/forms/InputOTP/InputOTP.theme.js +40 -5
- package/dist/components/forms/InputOTP/InputOTP.types.d.ts +45 -2
- package/dist/components/forms/InputOTP/index.d.ts +1 -1
- package/dist/components/forms/InputTag/InputTag.js +5 -5
- package/dist/components/forms/InputTag/InputTag.theme.js +1 -2
- package/dist/components/forms/Mention/Mention.theme.js +3 -5
- package/dist/components/forms/Radio/Radio.js +3 -2
- package/dist/components/forms/Radio/Radio.theme.js +2 -2
- package/dist/components/forms/RichTextEditor/RichTextEditor.js +22 -23
- package/dist/components/forms/Select/Select.d.ts +5 -1
- package/dist/components/forms/Select/Select.js +12 -11
- package/dist/components/forms/Select/Select.theme.js +4 -4
- package/dist/components/forms/Select/Select.types.d.ts +4 -1
- package/dist/components/forms/SelectableCard/SelectableCard.animations.d.ts +1 -2
- package/dist/components/forms/SelectableCard/SelectableCard.animations.js +6 -7
- package/dist/components/forms/SelectableCard/SelectableCard.js +1 -1
- package/dist/components/forms/Signature/Signature.js +1 -1
- package/dist/components/forms/Slider/Slider.js +24 -11
- package/dist/components/forms/Slider/Slider.theme.js +9 -3
- package/dist/components/forms/Switch/Switch.theme.js +1 -1
- package/dist/components/forms/TimePicker/TimePicker.theme.js +6 -7
- package/dist/components/forms/TimePicker/TimePickerContent.js +1 -1
- package/dist/components/forms/TimePicker/TimePickerTrigger.js +3 -3
- package/dist/components/forms/forms.theme.js +23 -12
- package/dist/components/forms/index.js +1 -1
- package/dist/components/generative/registry.js +195 -2
- package/dist/components/generative/resolvers.js +16 -16
- package/dist/components/generative/schema.generated.js +2687 -379
- package/dist/components/layout/Section/Section.theme.js +1 -1
- package/dist/generative.schema.json +2687 -379
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/useElapsedTime.d.ts +28 -0
- package/dist/hooks/useElapsedTime.js +37 -0
- package/dist/hooks/useSelectPortal.d.ts +10 -2
- package/dist/hooks/useSelectPortal.js +6 -3
- package/dist/index.js +1 -1
- package/dist/registry.json +835 -261
- package/dist/shadcn-compat.css +1 -1
- package/dist/styles.css +1 -1
- package/dist/theme.css +5 -1
- package/dist/themes/ember.css +131 -0
- package/dist/themes/iris.css +94 -0
- package/dist/themes/jade.css +139 -0
- package/dist/tools.json +2705 -361
- package/dist/utils/colorUtils.d.ts +6 -6
- package/dist/utils/colorUtils.js +8 -8
- package/package.json +9 -6
- package/src/global.scss +15 -4
- package/src/styles/theme.css +440 -152
- package/tailwind.preset.cjs +52 -34
- package/dist/components/core/Pill/Pill.animations.d.ts +0 -32
- package/dist/components/core/Pill/Pill.animations.js +0 -16
|
@@ -4,9 +4,9 @@ const accordionTheme = {
|
|
|
4
4
|
// Base container styles for stacked variant (unified card)
|
|
5
5
|
baseStyleStacked: "w-full border border-[var(--color-border)] rounded-[var(--form-radius)] overflow-hidden",
|
|
6
6
|
// Item styles for separated variant (individual cards with spacing)
|
|
7
|
-
itemStyle: "border border-[var(--color-border)] rounded-[var(--form-radius)] bg-
|
|
7
|
+
itemStyle: "border border-[var(--color-border)] rounded-[var(--form-radius)] bg-[var(--color-surface)] overflow-hidden mb-2 last-of-type:mb-0",
|
|
8
8
|
// Item styles for stacked variant (shared borders, no spacing)
|
|
9
|
-
itemStyleStacked: "bg-
|
|
9
|
+
itemStyleStacked: "bg-[var(--color-surface)] border-t border-[var(--color-border)] first-of-type:border-t-0",
|
|
10
10
|
// Trigger styles (shared across variants)
|
|
11
11
|
triggerStyle: "flex w-full gap-3 items-center px-4 py-2.5 text-left font-medium transition-all duration-200 cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-primary)] focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 text-[var(--color-text-primary)] text-sm",
|
|
12
12
|
// Body styles (shared across variants)
|
|
@@ -5,7 +5,7 @@ import { cn } from '../../../utils/cn.js';
|
|
|
5
5
|
import { useAccordionContext } from './AccordionContext.js';
|
|
6
6
|
import { useAccordionItemContext } from './AccordionItem.js';
|
|
7
7
|
import { motion } from 'motion/react';
|
|
8
|
-
import {
|
|
8
|
+
import { Plus } from '@phosphor-icons/react';
|
|
9
9
|
import { createTriggerIconVariants } from './Accordion.animations.js';
|
|
10
10
|
|
|
11
11
|
const AccordionTrigger = React__default.forwardRef(({ children, className, chevron, chevronRotation, iconRotation, chevronPosition = "right", ...props }, ref) => {
|
|
@@ -14,7 +14,7 @@ const AccordionTrigger = React__default.forwardRef(({ children, className, chevr
|
|
|
14
14
|
const { isOpen, disabled, toggle, triggerId, contentId } = useAccordionItemContext();
|
|
15
15
|
const resolvedChevronRotation = (_a = chevronRotation !== null && chevronRotation !== void 0 ? chevronRotation : iconRotation) !== null && _a !== void 0 ? _a : 45;
|
|
16
16
|
// Resolve the indicator icon
|
|
17
|
-
const defaultChevronIcon = jsx(
|
|
17
|
+
const defaultChevronIcon = jsx(Plus, { className: "size-4", weight: "bold" });
|
|
18
18
|
const resolvedChevron = chevron === null
|
|
19
19
|
? null
|
|
20
20
|
: chevron !== undefined
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Variants } from "motion/react";
|
|
2
2
|
/**
|
|
3
3
|
* Alert entrance/exit animation variants
|
|
4
|
-
* Entrance: slide down from -
|
|
4
|
+
* Entrance: slide down from -12px + fade in
|
|
5
5
|
* Exit: fade out + collapse height
|
|
6
|
+
* Uses library spring presets for a premium feel
|
|
6
7
|
*/
|
|
7
8
|
export declare const alertVariants: Variants;
|
|
8
9
|
/**
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import '../../../theme/ThemeContext.js';
|
|
3
|
+
import '../../../theme/SurfaceContext.js';
|
|
4
|
+
import { springs } from '../../../theme/animations.js';
|
|
5
|
+
|
|
2
6
|
/**
|
|
3
7
|
* Alert entrance/exit animation variants
|
|
4
|
-
* Entrance: slide down from -
|
|
8
|
+
* Entrance: slide down from -12px + fade in
|
|
5
9
|
* Exit: fade out + collapse height
|
|
10
|
+
* Uses library spring presets for a premium feel
|
|
6
11
|
*/
|
|
7
12
|
const alertVariants = {
|
|
8
13
|
hidden: {
|
|
9
14
|
opacity: 0,
|
|
10
|
-
y: -
|
|
15
|
+
y: -12,
|
|
11
16
|
height: 0,
|
|
12
17
|
paddingTop: 0,
|
|
13
18
|
paddingBottom: 0,
|
|
@@ -15,8 +20,13 @@ const alertVariants = {
|
|
|
15
20
|
marginBottom: 0,
|
|
16
21
|
overflow: "hidden",
|
|
17
22
|
transition: {
|
|
18
|
-
|
|
19
|
-
|
|
23
|
+
height: springs.smooth,
|
|
24
|
+
paddingTop: springs.smooth,
|
|
25
|
+
paddingBottom: springs.smooth,
|
|
26
|
+
marginTop: springs.smooth,
|
|
27
|
+
marginBottom: springs.smooth,
|
|
28
|
+
y: springs.premium,
|
|
29
|
+
opacity: { duration: 0.15 },
|
|
20
30
|
},
|
|
21
31
|
},
|
|
22
32
|
visible: {
|
|
@@ -29,8 +39,13 @@ const alertVariants = {
|
|
|
29
39
|
marginBottom: undefined,
|
|
30
40
|
overflow: "visible",
|
|
31
41
|
transition: {
|
|
32
|
-
|
|
33
|
-
|
|
42
|
+
y: springs.premium,
|
|
43
|
+
height: springs.smooth,
|
|
44
|
+
paddingTop: springs.smooth,
|
|
45
|
+
paddingBottom: springs.smooth,
|
|
46
|
+
marginTop: springs.smooth,
|
|
47
|
+
marginBottom: springs.smooth,
|
|
48
|
+
opacity: { duration: 0.25 },
|
|
34
49
|
},
|
|
35
50
|
},
|
|
36
51
|
};
|
|
@@ -5,15 +5,15 @@ import { useReducedMotion, motion } from 'motion/react';
|
|
|
5
5
|
import { cn } from '../../../utils/cn.js';
|
|
6
6
|
import { alertTheme } from './Alert.theme.js';
|
|
7
7
|
import { reducedMotionAlertVariants, alertVariants } from './Alert.animations.js';
|
|
8
|
-
import {
|
|
8
|
+
import { X, XCircle, Warning, CheckCircle, Info, Bell } from '@phosphor-icons/react';
|
|
9
9
|
import { Button } from '../Button/Button.js';
|
|
10
10
|
|
|
11
11
|
const defaultIcons = {
|
|
12
|
-
|
|
13
|
-
primary:
|
|
14
|
-
success:
|
|
15
|
-
warning:
|
|
16
|
-
danger:
|
|
12
|
+
mono: Bell,
|
|
13
|
+
primary: Info,
|
|
14
|
+
success: CheckCircle,
|
|
15
|
+
warning: Warning,
|
|
16
|
+
danger: XCircle,
|
|
17
17
|
};
|
|
18
18
|
const Alert = React__default.forwardRef(({ color = "primary", title, subtitle, dismissible, icon, showIcon = true, avatar, actions, onDismiss, className, children, darkMode = false, ...props }, ref) => {
|
|
19
19
|
var _a;
|
|
@@ -46,7 +46,7 @@ const Alert = React__default.forwardRef(({ color = "primary", title, subtitle, d
|
|
|
46
46
|
? jsx("div", { className: cn("font-semibold text-[var(--color-text-primary)]"), children: title })
|
|
47
47
|
: title), subtitle && (typeof subtitle === 'string'
|
|
48
48
|
? jsx("div", { className: "text-[var(--color-text-secondary)]/90 mt-1", children: subtitle })
|
|
49
|
-
: subtitle), children && jsx("div", { className: "mt-2", children: children }), actions && (jsx("div", { className: "flex items-center gap-2 mt-6", children: actions }))] }), showDismiss && (jsx(Button, { variant: "ghost", color: "
|
|
49
|
+
: subtitle), children && jsx("div", { className: "mt-2", children: children }), actions && (jsx("div", { className: "flex items-center gap-2 mt-6", children: actions }))] }), showDismiss && (jsx(Button, { variant: "ghost", color: "mono", size: "sm", iconOnly: true, onClick: handleDismiss, "aria-label": "Dismiss alert", className: "rounded-full absolute top-2 right-2 flex-shrink-0", children: jsx(X, { className: "size-4", weight: "bold" }) }))] }));
|
|
50
50
|
const alertNode = (jsx(motion.div, { variants: shouldReduceMotion ? reducedMotionAlertVariants : alertVariants, initial: "hidden", animate: isDismissing ? "hidden" : "visible", onAnimationComplete: (definition) => {
|
|
51
51
|
if (definition === "hidden" && isDismissing) {
|
|
52
52
|
onDismiss === null || onDismiss === void 0 ? void 0 : onDismiss();
|
|
@@ -7,25 +7,64 @@ import { Tooltip } from '../Tooltip/Tooltip.js';
|
|
|
7
7
|
import '../Tooltip/Tooltip.animations.js';
|
|
8
8
|
import { MeshGradient } from '../../effects/MeshGradient/MeshGradient.js';
|
|
9
9
|
import { MESH_PALETTE_BY_NAME, DEFAULT_MESH_COLORS, getMeshTextTone } from '../../effects/MeshGradient/MeshGradient.utils.js';
|
|
10
|
-
import {
|
|
10
|
+
import { SealCheck, User } from '@phosphor-icons/react';
|
|
11
11
|
|
|
12
12
|
// Number of marble shapes the gradient renders — shared by the render and the
|
|
13
13
|
// text-tone calc so both read the same deterministic field. The gradient's blur
|
|
14
14
|
// is resolution-independent (SVG viewBox units), so no per-size tuning is needed.
|
|
15
15
|
const MESH_COUNT = 2;
|
|
16
|
+
// Each token's geometry, as the CSS variable that carries its px value. Keeping
|
|
17
|
+
// the indirection means a themed `--avatar-size-*` still flows through.
|
|
18
|
+
const SIZE_TOKEN_VAR = {
|
|
19
|
+
xs: "var(--avatar-size-xs)",
|
|
20
|
+
sm: "var(--avatar-size-sm)",
|
|
21
|
+
md: "var(--avatar-size-md)",
|
|
22
|
+
lg: "var(--avatar-size-lg)",
|
|
23
|
+
xl: "var(--avatar-size-xl)",
|
|
24
|
+
};
|
|
25
|
+
// The default px ladder from `theme.css`, mirrored here for ONE purpose: picking
|
|
26
|
+
// which rung a numeric size's decorations (glyph, status dot, verified badge,
|
|
27
|
+
// text gap) should borrow. Geometry never reads this — it always resolves
|
|
28
|
+
// through the CSS variable above — so a theme that retunes the ladder still
|
|
29
|
+
// sizes correctly; only the rung chosen for a numeric size reads the defaults.
|
|
30
|
+
const SIZE_TOKEN_PX = {
|
|
31
|
+
xs: 24,
|
|
32
|
+
sm: 32,
|
|
33
|
+
md: 40,
|
|
34
|
+
lg: 48,
|
|
35
|
+
xl: 64,
|
|
36
|
+
};
|
|
37
|
+
const isSizeToken = (size) => typeof size === "string";
|
|
38
|
+
/** The rung a size sits closest to. Identity for tokens. */
|
|
39
|
+
const nearestSizeToken = (size) => {
|
|
40
|
+
if (isSizeToken(size))
|
|
41
|
+
return size;
|
|
42
|
+
return Object.keys(SIZE_TOKEN_PX).reduce((best, token) => Math.abs(SIZE_TOKEN_PX[token] - size) < Math.abs(SIZE_TOKEN_PX[best] - size)
|
|
43
|
+
? token
|
|
44
|
+
: best);
|
|
45
|
+
};
|
|
16
46
|
const Avatar = React__default.forwardRef(({ src, name, size = "md", status = "none", fallback, gradient = false, gradientVariant = "marble", gradientColors, className, role, lazyLoad = true, retryCount = 0, retryDelay = 1000, contextualLabel, groupPosition, showTooltip = false, initialsCount = 2, title, subtitle, verified = false, ...props }, ref) => {
|
|
17
47
|
var _a;
|
|
18
48
|
const [imageError, setImageError] = useState(false);
|
|
19
49
|
const [retries, setRetries] = useState(0);
|
|
20
50
|
const retryTimerRef = useRef(null);
|
|
51
|
+
if (process.env.NODE_ENV !== "production" &&
|
|
52
|
+
typeof size === "number" &&
|
|
53
|
+
(!Number.isFinite(size) || size <= 0)) {
|
|
54
|
+
// eslint-disable-next-line no-console
|
|
55
|
+
console.warn(`[Avatar]: \`size\` must be a positive number of pixels; received ${size}.`);
|
|
56
|
+
}
|
|
57
|
+
// Exact geometry for the circle; nearest rung for everything hung off it.
|
|
58
|
+
const sizeValue = isSizeToken(size) ? SIZE_TOKEN_VAR[size] : `${size}px`;
|
|
59
|
+
const sizeToken = nearestSizeToken(size);
|
|
21
60
|
// Normalize null to undefined for internal use
|
|
22
61
|
const resolvedName = name !== null && name !== void 0 ? name : undefined;
|
|
23
|
-
// Initials derived from the name (xs shows a single initial).
|
|
62
|
+
// Initials derived from the name (the xs rung shows a single initial).
|
|
24
63
|
const initials = resolvedName
|
|
25
64
|
? resolvedName
|
|
26
65
|
.split(" ")
|
|
27
66
|
.map((part) => part[0])
|
|
28
|
-
.slice(0,
|
|
67
|
+
.slice(0, sizeToken === "xs" ? 1 : initialsCount)
|
|
29
68
|
.join("")
|
|
30
69
|
.toUpperCase()
|
|
31
70
|
: "";
|
|
@@ -40,8 +79,8 @@ const Avatar = React__default.forwardRef(({ src, name, size = "md", status = "no
|
|
|
40
79
|
// theme-stable neutral tokens (so the choice holds in light AND dark mode).
|
|
41
80
|
const gradientTextColor = gradient
|
|
42
81
|
? getMeshTextTone(gradientSeed, gradientPalette, MESH_COUNT) === "dark"
|
|
43
|
-
? "text-[var(--color-
|
|
44
|
-
: "text-[var(--color-
|
|
82
|
+
? "text-[var(--color-mono-950)]"
|
|
83
|
+
: "text-[var(--color-mono-50)]"
|
|
45
84
|
: undefined;
|
|
46
85
|
// Glyph shown over the gradient: explicit fallback, else initials, else none
|
|
47
86
|
// (a clean gradient — no default user icon when there's no name).
|
|
@@ -88,41 +127,7 @@ const Avatar = React__default.forwardRef(({ src, name, size = "md", status = "no
|
|
|
88
127
|
return jsx("span", { "aria-hidden": "true", children: initials });
|
|
89
128
|
}
|
|
90
129
|
// 3. Default placeholder icon
|
|
91
|
-
return jsx(
|
|
92
|
-
};
|
|
93
|
-
// Determine status indicator size based on avatar size
|
|
94
|
-
const getStatusSize = () => {
|
|
95
|
-
switch (size) {
|
|
96
|
-
case "xs":
|
|
97
|
-
return "size-1.5 left-0 bottom-0.5";
|
|
98
|
-
case "sm":
|
|
99
|
-
return "size-2 left-0 bottom-0";
|
|
100
|
-
case "md":
|
|
101
|
-
return "size-2 left-0 bottom-0";
|
|
102
|
-
case "lg":
|
|
103
|
-
return "size-2.5 left-0 bottom-0";
|
|
104
|
-
case "xl":
|
|
105
|
-
return "size-3 left-0 bottom-0";
|
|
106
|
-
default:
|
|
107
|
-
return "size-2 left-0 bottom-0";
|
|
108
|
-
}
|
|
109
|
-
};
|
|
110
|
-
// Determine verified badge size based on avatar size
|
|
111
|
-
const getVerifiedBadgeSize = () => {
|
|
112
|
-
switch (size) {
|
|
113
|
-
case "xs":
|
|
114
|
-
return "size-2.5 -right-0.5 -bottom-0.5";
|
|
115
|
-
case "sm":
|
|
116
|
-
return "size-3.5 -right-0.5 -bottom-0.5";
|
|
117
|
-
case "md":
|
|
118
|
-
return "size-4 -right-0.5 -bottom-0.5";
|
|
119
|
-
case "lg":
|
|
120
|
-
return "size-5 -right-1 -bottom-1";
|
|
121
|
-
case "xl":
|
|
122
|
-
return "size-6 -right-1.5 -bottom-1.5";
|
|
123
|
-
default:
|
|
124
|
-
return "size-4 -right-0.5 -bottom-0.5";
|
|
125
|
-
}
|
|
130
|
+
return jsx(User, { className: "w-1/2 h-1/2" });
|
|
126
131
|
};
|
|
127
132
|
// Create full accessibility label including context if provided
|
|
128
133
|
const accessibilityLabel = contextualLabel
|
|
@@ -133,7 +138,10 @@ const Avatar = React__default.forwardRef(({ src, name, size = "md", status = "no
|
|
|
133
138
|
const resolvedTitle = title !== null && title !== void 0 ? title : (subtitle ? resolvedName : undefined);
|
|
134
139
|
const hasTextSection = !!(resolvedTitle || subtitle);
|
|
135
140
|
// The avatar circle with status indicator
|
|
136
|
-
const avatarCircle = (jsxs("div", { className: cn("relative inline-flex avatar-wrapper shrink-0", groupPosition), children: [jsxs("div", { className: cn(avatarTheme.baseStyle, avatarTheme.
|
|
141
|
+
const avatarCircle = (jsxs("div", { className: cn("relative inline-flex avatar-wrapper shrink-0", groupPosition), children: [jsxs("div", { className: cn(avatarTheme.baseStyle, avatarTheme.sizeStyle, avatarTheme.glyphSizes[sizeToken], className), style: { "--avatar-size": sizeValue }, "aria-hidden": ariaRole === "presentation" ? "true" : undefined, "data-size": size, children: [gradient && (jsx(MeshGradient, { seed: gradientSeed, colors: gradientPalette, count: MESH_COUNT })), src && !imageError ? (jsx("img", { src: src, alt: resolvedName !== null && resolvedName !== void 0 ? resolvedName : "Avatar", className: "relative z-10 size-full object-cover object-top", onError: handleImageError, loading: lazyLoad ? "lazy" : undefined, "aria-hidden": ariaRole === "presentation" ? "true" : undefined, "data-loaded": "true" })) : (jsx("div", { className: cn("avatar-fallback relative z-10 flex items-center justify-center size-full", gradient && cn(gradientTextColor, "drop-shadow")), children: gradient ? gradientGlyph : getFallbackContent() }))] }), status !== "none" && (jsx("span", { className: cn(
|
|
142
|
+
// Ring WIDTH ramps per size (see `statusSizes`); only its colour
|
|
143
|
+
// is shared here.
|
|
144
|
+
"absolute rounded-full ring-[var(--color-background)]", avatarTheme.statusPositionStyle, avatarTheme.statusSizes[sizeToken], avatarTheme.statuses[status]), "aria-label": `Status: ${status}`, role: "status", "data-status": status })), verified && (jsx("span", { className: cn("absolute rounded-full z-10 inline-grid place-items-center ring-2 ring-[var(--color-background)] bg-white dark:bg-[var(--color-surface)] shadow-sm", avatarTheme.verifiedSizes[sizeToken]), "aria-label": "Verified", children: jsx(SealCheck, { className: "size-full text-[var(--color-primary)]" }) }))] }));
|
|
137
145
|
// Wrap circle in tooltip if needed
|
|
138
146
|
const avatarWithTooltip = showTooltip && resolvedName ? (jsx(Tooltip, { content: resolvedName, placement: "top", children: avatarCircle })) : (avatarCircle);
|
|
139
147
|
// If no text section, render just the avatar
|
|
@@ -145,7 +153,7 @@ const Avatar = React__default.forwardRef(({ src, name, size = "md", status = "no
|
|
|
145
153
|
className: "inline-flex", role: ariaRole, "aria-label": ariaRole === "presentation" ? undefined : accessibilityLabel, "data-size": size, "data-status": status, "data-error": imageError ? "true" : "false", ...props, children: avatarWithTooltip }));
|
|
146
154
|
}
|
|
147
155
|
// Render avatar with text section
|
|
148
|
-
return (jsxs("div", { ref: ref, className: cn("inline-flex items-center", avatarTheme.gapSizes[
|
|
156
|
+
return (jsxs("div", { ref: ref, className: cn("inline-flex items-center", avatarTheme.gapSizes[sizeToken]), role: ariaRole, "aria-label": ariaRole === "presentation" ? undefined : accessibilityLabel, "data-size": size, "data-status": status, "data-error": imageError ? "true" : "false", ...props, children: [avatarWithTooltip, jsxs("div", { className: "min-w-0", children: [resolvedTitle && (jsx("div", { className: cn(avatarTheme.titleStyle, avatarTheme.textSizes[sizeToken]), children: resolvedTitle })), subtitle && (jsx("div", { className: cn(avatarTheme.subtitleStyle, avatarTheme.textSizes[sizeToken]), children: subtitle }))] })] }));
|
|
149
157
|
});
|
|
150
158
|
// Set display name for dev tools and Storybook
|
|
151
159
|
Avatar.displayName = "Avatar";
|
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AvatarSizeToken, AvatarStatus } from "./Avatar.types";
|
|
2
2
|
interface AvatarTheme {
|
|
3
3
|
baseStyle: string;
|
|
4
|
-
|
|
4
|
+
sizeStyle: string;
|
|
5
|
+
glyphSizes: Record<AvatarSizeToken, string>;
|
|
5
6
|
statuses: Record<AvatarStatus, string>;
|
|
7
|
+
statusPositionStyle: string;
|
|
8
|
+
statusSizes: Record<AvatarSizeToken, string>;
|
|
9
|
+
verifiedSizes: Record<AvatarSizeToken, string>;
|
|
6
10
|
titleStyle: string;
|
|
7
11
|
subtitleStyle: string;
|
|
8
|
-
textSizes: Record<
|
|
9
|
-
gapSizes: Record<
|
|
12
|
+
textSizes: Record<AvatarSizeToken, string>;
|
|
13
|
+
gapSizes: Record<AvatarSizeToken, string>;
|
|
10
14
|
}
|
|
11
15
|
export declare const avatarTheme: AvatarTheme;
|
|
12
16
|
export {};
|
|
@@ -1,19 +1,75 @@
|
|
|
1
1
|
const avatarTheme = {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
// `isolate` is load-bearing, not decoration: the circle's contents raise
|
|
3
|
+
// themselves above the gradient with `z-10`, and a `relative` box with
|
|
4
|
+
// `z-index: auto` does NOT create a stacking context — so that z-10 escaped
|
|
5
|
+
// into the WRAPPER's context and painted over the sibling status dot, which
|
|
6
|
+
// sits at `z-index: auto`. Isolating keeps the circle's internal layering
|
|
7
|
+
// internal, so the dot and badge stack by DOM order as intended.
|
|
8
|
+
baseStyle: "relative isolate inline-flex items-center justify-center overflow-hidden bg-[var(--color-surface)] text-[var(--color-text-primary)] font-bold tracking-tighter rounded-[var(--avatar-radius)] border border-[var(--color-border)] transition-all duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-primary)] focus-visible:ring-offset-2",
|
|
9
|
+
// One geometry class for EVERY size. The px value arrives through the
|
|
10
|
+
// `--avatar-size` custom property, which Avatar sets inline: a token maps to
|
|
11
|
+
// its `--avatar-size-*` var, a number to `${n}px`. It has to travel as a
|
|
12
|
+
// variable rather than a generated `size-[30px]` class because per-value
|
|
13
|
+
// classes can't be interpolated — Tailwind only ships what it finds in this
|
|
14
|
+
// library's source, so an interpolated class would resolve to nothing.
|
|
15
|
+
sizeStyle: "size-[var(--avatar-size)]",
|
|
16
|
+
// The circle's own glyph (initials) — distinct from `textSizes`, which sizes
|
|
17
|
+
// the adjacent title/subtitle block.
|
|
18
|
+
glyphSizes: {
|
|
19
|
+
xs: "text-xs",
|
|
20
|
+
sm: "text-xs",
|
|
21
|
+
md: "text-sm",
|
|
22
|
+
lg: "text-sm",
|
|
23
|
+
xl: "text-sm",
|
|
9
24
|
},
|
|
10
25
|
statuses: {
|
|
11
26
|
online: "bg-[var(--color-success)]",
|
|
12
|
-
|
|
27
|
+
// NOT `--color-text-muted`: the text tokens are alpha derivations of
|
|
28
|
+
// `--color-text-primary` (57% opaque in light, 50% in dark), so using one
|
|
29
|
+
// as a FILL let the avatar photo show straight through the dot.
|
|
30
|
+
//
|
|
31
|
+
// A ramp rung rather than `--color-mono` (the §3 filled-neutral role) is a
|
|
32
|
+
// deliberate, measured exception — see CLAUDE.md §8. `--color-mono` is
|
|
33
|
+
// mono-900/200, tuned to carry `-contrast` TEXT; a status dot carries none,
|
|
34
|
+
// and at that weight the least-active state out-shouts the active ones.
|
|
35
|
+
// mono-500 is the only rung clearing WCAG 1.4.11's 3:1 against the dot's
|
|
36
|
+
// adjacent colour (its `--color-background` ring) in BOTH modes — 4.54
|
|
37
|
+
// light, 3.78 dark — which also puts it in the same band as its siblings
|
|
38
|
+
// (success 3.52, danger 4.57). One value, no `dark:` variant needed.
|
|
39
|
+
offline: "bg-[var(--color-mono-500)]",
|
|
13
40
|
busy: "bg-[var(--color-danger)]",
|
|
14
41
|
away: "bg-[var(--color-warning)]",
|
|
15
42
|
none: "",
|
|
16
43
|
},
|
|
44
|
+
// Placement, shared by every size. The avatar is a circle
|
|
45
|
+
// (`--avatar-radius: 50%`), so anchoring the dot to the bounding-box corner
|
|
46
|
+
// strands it outside the arc — the corner sits ~41% further from the centre
|
|
47
|
+
// than the edge does. The 45° point of a circle is `r(1 − 1/√2)` ≈ 14.6% of
|
|
48
|
+
// the diameter in from each edge; the half-translates then centre the dot on
|
|
49
|
+
// it, so it reads as attached at EVERY size, including arbitrary numeric ones
|
|
50
|
+
// (a px inset per rung can only ever be right for that rung's exact diameter).
|
|
51
|
+
statusPositionStyle: "left-[14.6%] bottom-[14.6%] -translate-x-1/2 translate-y-1/2",
|
|
52
|
+
// Dot AND ring ramp together, because what the eye reads is the badge's total
|
|
53
|
+
// footprint — and the ring used to be a flat 2px at every rung, making it
|
|
54
|
+
// proportionally 3.3× heavier on xs than on xl. Dot tracks ~20% of the
|
|
55
|
+
// diameter, ring ~5%, so the footprint stays near 30% across the ladder
|
|
56
|
+
// (was 42%→25%). xs is the one deliberate outlier at 33%: 20% of 24px is
|
|
57
|
+
// under 5px, which stops reading as a status colour at all, so the dot holds
|
|
58
|
+
// a 6px legibility floor there rather than scaling honestly.
|
|
59
|
+
statusSizes: {
|
|
60
|
+
xs: "size-1.5 ring-1",
|
|
61
|
+
sm: "size-1.5 ring-[1.5px]",
|
|
62
|
+
md: "size-2 ring-2",
|
|
63
|
+
lg: "size-2.5 ring-2",
|
|
64
|
+
xl: "size-3 ring-[3px]",
|
|
65
|
+
},
|
|
66
|
+
verifiedSizes: {
|
|
67
|
+
xs: "size-2.5 -right-0.5 -bottom-0.5",
|
|
68
|
+
sm: "size-3.5 -right-0.5 -bottom-0.5",
|
|
69
|
+
md: "size-4 -right-0.5 -bottom-0.5",
|
|
70
|
+
lg: "size-5 -right-0.5 -bottom-0.5",
|
|
71
|
+
xl: "size-5 -right-0.5 -bottom-0.5",
|
|
72
|
+
},
|
|
17
73
|
titleStyle: "font-medium leading-tight text-[var(--color-text-primary)] truncate",
|
|
18
74
|
subtitleStyle: "leading-tight text-[var(--color-text-muted)] truncate",
|
|
19
75
|
textSizes: {
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { ReactNode } from "react";
|
|
2
2
|
import type { MeshPaletteName } from "../../effects/MeshGradient";
|
|
3
|
-
|
|
3
|
+
/** The named rungs of the avatar size ladder (px values live in `theme.css`). */
|
|
4
|
+
export type AvatarSizeToken = "xs" | "sm" | "md" | "lg" | "xl";
|
|
5
|
+
/**
|
|
6
|
+
* A named rung, or an exact pixel size. A number sizes the circle precisely
|
|
7
|
+
* (`size={30}` → 30px) while its decorations — glyph, status dot, verified
|
|
8
|
+
* badge, text gap — snap to the nearest rung.
|
|
9
|
+
*/
|
|
10
|
+
export type AvatarSize = AvatarSizeToken | number;
|
|
4
11
|
export type AvatarStatus = "online" | "offline" | "busy" | "away" | "none";
|
|
5
12
|
export interface AvatarProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
6
13
|
/**
|
|
@@ -14,7 +21,11 @@ export interface AvatarProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
|
14
21
|
*/
|
|
15
22
|
name?: string | null;
|
|
16
23
|
/**
|
|
17
|
-
* The size of the avatar
|
|
24
|
+
* The size of the avatar — a named rung (`"md"`) or an exact pixel size
|
|
25
|
+
* (`30`). Prefer this over a `className="size-[30px]"` override: the shipped
|
|
26
|
+
* stylesheet only contains the classes used by this library's own source, so
|
|
27
|
+
* an arbitrary size utility resolves to nothing for consumers who don't run
|
|
28
|
+
* Tailwind themselves.
|
|
18
29
|
* @default "md"
|
|
19
30
|
*/
|
|
20
31
|
size?: AvatarSize;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { Avatar } from './Avatar';
|
|
2
|
-
export type { AvatarProps, AvatarSize, AvatarStatus, } from './Avatar.types';
|
|
2
|
+
export type { AvatarProps, AvatarSize, AvatarSizeToken, AvatarStatus, } from './Avatar.types';
|
|
@@ -7,7 +7,7 @@ import { cn } from '../../../utils/cn.js';
|
|
|
7
7
|
import { avatarGroupTheme } from './AvatarGroup.theme.js';
|
|
8
8
|
import { getAnimationState, getAvatarGroupVariants } from './AvatarGroup.animations.js';
|
|
9
9
|
|
|
10
|
-
const AvatarGroup = React__default.forwardRef(({ avatars = [], max = 5, size = "md",
|
|
10
|
+
const AvatarGroup = React__default.forwardRef(({ avatars = [], max = 5, size = "md", direction = "row", overflowIndicator, className, ariaLabel = "Group of users", shouldAnimate = true, ...props }, ref) => {
|
|
11
11
|
const [hoveredIndex, setHoveredIndex] = useState(null);
|
|
12
12
|
const shouldReduceMotion = useReducedMotion();
|
|
13
13
|
const enableAnimations = shouldAnimate && !shouldReduceMotion;
|
|
@@ -15,7 +15,6 @@ const AvatarGroup = React__default.forwardRef(({ avatars = [], max = 5, size = "
|
|
|
15
15
|
const memoizedData = useMemo(() => {
|
|
16
16
|
const visibleAvatars = avatars.slice(0, max);
|
|
17
17
|
const overflowCount = avatars.length > max ? avatars.length - max : 0;
|
|
18
|
-
const spacingClass = avatarGroupTheme.spacingStyles[spacing];
|
|
19
18
|
const directionClass = direction === "row-reverse" ? "flex-row-reverse" : "flex-row";
|
|
20
19
|
const totalPeople = avatars.length;
|
|
21
20
|
const groupDescription = avatars
|
|
@@ -24,14 +23,13 @@ const AvatarGroup = React__default.forwardRef(({ avatars = [], max = 5, size = "
|
|
|
24
23
|
return {
|
|
25
24
|
visibleAvatars,
|
|
26
25
|
overflowCount,
|
|
27
|
-
spacingClass,
|
|
28
26
|
directionClass,
|
|
29
27
|
totalPeople,
|
|
30
28
|
groupDescription,
|
|
31
29
|
};
|
|
32
|
-
}, [avatars, max,
|
|
33
|
-
const { visibleAvatars, overflowCount,
|
|
34
|
-
return (jsxs("div", { ref: ref, className: cn(avatarGroupTheme.baseStyle, directionClass, className), role: "group", "aria-label": ariaLabel, "aria-description": `Contains ${totalPeople} users: ${groupDescription}`, "data-size": size, "data-
|
|
30
|
+
}, [avatars, max, direction]);
|
|
31
|
+
const { visibleAvatars, overflowCount, directionClass, totalPeople, groupDescription, } = memoizedData;
|
|
32
|
+
return (jsxs("div", { ref: ref, className: cn(avatarGroupTheme.baseStyle, directionClass, className), role: "group", "aria-label": ariaLabel, "aria-description": `Contains ${totalPeople} users: ${groupDescription}`, "data-size": size, "data-direction": direction, "data-total": totalPeople, "data-visible": visibleAvatars.length, "data-overflow": overflowCount > 0 ? "true" : "false", onMouseLeave: () => enableAnimations && setHoveredIndex(null), ...props, children: [visibleAvatars.map((avatarProps, index) => {
|
|
35
33
|
// Create contextual label for individual avatars in a group
|
|
36
34
|
const position = index + 1;
|
|
37
35
|
const contextLabel = avatarProps.contextualLabel || ` (${position} of ${totalPeople})`;
|
|
@@ -50,10 +48,10 @@ const AvatarGroup = React__default.forwardRef(({ avatars = [], max = 5, size = "
|
|
|
50
48
|
// inflating the row and dropping the self-centered overflow chip.
|
|
51
49
|
"flex",
|
|
52
50
|
// First avatar doesn't need negative margin
|
|
53
|
-
index > 0 ?
|
|
51
|
+
index > 0 ? avatarGroupTheme.spacingStyle : "z-10"), "data-position": position, "data-index": index, ...motionProps, children: jsx(Avatar, { size: size, ...avatarProps, contextualLabel: contextLabel,
|
|
54
52
|
// Ensure that groupPosition is not passed to avoid conflicts
|
|
55
53
|
groupPosition: undefined }) }, index));
|
|
56
|
-
}), overflowCount > 0 && (jsx("div", { className: cn("flex",
|
|
54
|
+
}), overflowCount > 0 && (jsx("div", { className: cn("flex", avatarGroupTheme.spacingStyle), "data-overflow-count": overflowCount, children: overflowIndicator ? (overflowIndicator(overflowCount)) : (jsx(Avatar, { size: size, name: `+${overflowCount} more`, fallback: jsxs("span", { children: ["+", overflowCount] }), className: avatarGroupTheme.overflowAvatarStyle, role: "img", "aria-label": `+ ${overflowCount} more users` })) }))] }));
|
|
57
55
|
});
|
|
58
56
|
// Set display name for dev tools and Storybook
|
|
59
57
|
AvatarGroup.displayName = "AvatarGroup";
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { AvatarGroupSpacing } from "./AvatarGroup.types";
|
|
2
1
|
/**
|
|
3
2
|
* Internal theme structure for the AvatarGroup component
|
|
4
3
|
*/
|
|
5
4
|
interface AvatarGroupTheme {
|
|
6
5
|
baseStyle: string;
|
|
7
|
-
|
|
6
|
+
spacingStyle: string;
|
|
8
7
|
overflowAvatarStyle: string;
|
|
9
8
|
}
|
|
10
9
|
/**
|
|
@@ -6,12 +6,8 @@ const avatarGroupTheme = {
|
|
|
6
6
|
baseStyle: "flex items-center",
|
|
7
7
|
// NOTE: items-center only works because Avatar's wrapper no longer leaves a
|
|
8
8
|
// baseline descender below the circle (Avatar.tsx wrapper is inline-flex).
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
tight: "-ml-5 z-10",
|
|
12
|
-
normal: "-ml-3 z-10",
|
|
13
|
-
loose: "-ml-2 z-10",
|
|
14
|
-
},
|
|
9
|
+
// Overlap between stacked avatars — override per call site with className.
|
|
10
|
+
spacingStyle: "-ml-3 z-10",
|
|
15
11
|
// Overflow avatar styles
|
|
16
12
|
overflowAvatarStyle: "bg-[var(--color-surface)]/60 backdrop-blur-sm text-[var(--color-text-secondary)] font-bold tracking-tight text-[10px] flex items-center justify-center size-6",
|
|
17
13
|
};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
import type { AvatarProps } from '../Avatar/Avatar.types';
|
|
3
|
-
export type AvatarGroupSpacing = 'tight' | 'normal' | 'loose';
|
|
4
3
|
export type AvatarGroupDirection = 'row' | 'row-reverse';
|
|
5
4
|
export interface AvatarGroupProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
6
5
|
/**
|
|
@@ -17,11 +16,6 @@ export interface AvatarGroupProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
|
17
16
|
* @default "md"
|
|
18
17
|
*/
|
|
19
18
|
size?: AvatarProps["size"];
|
|
20
|
-
/**
|
|
21
|
-
* Amount of overlap between avatars (higher value = more overlap)
|
|
22
|
-
* @default "normal"
|
|
23
|
-
*/
|
|
24
|
-
spacing?: AvatarGroupSpacing;
|
|
25
19
|
/**
|
|
26
20
|
* Direction of the avatar stack
|
|
27
21
|
* @default "row"
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { AvatarGroup } from './AvatarGroup';
|
|
2
|
-
export type { AvatarGroupProps,
|
|
2
|
+
export type { AvatarGroupProps, AvatarGroupDirection } from './AvatarGroup.types';
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { PRESS_TRANSITION, HOVER_TRANSITION } from '../shared/interaction.animations.js';
|
|
3
|
+
|
|
2
4
|
// Main badge variants with optimized transitions
|
|
3
5
|
const badgeVariants = {
|
|
4
6
|
initial: {
|
|
@@ -58,11 +60,11 @@ const dismissButtonVariants = {
|
|
|
58
60
|
},
|
|
59
61
|
hover: {
|
|
60
62
|
opacity: 1,
|
|
61
|
-
transition:
|
|
63
|
+
transition: HOVER_TRANSITION,
|
|
62
64
|
},
|
|
63
65
|
tap: {
|
|
64
66
|
scale: 0.95,
|
|
65
|
-
transition:
|
|
67
|
+
transition: PRESS_TRANSITION,
|
|
66
68
|
},
|
|
67
69
|
};
|
|
68
70
|
// Optimized dismiss animation sequence
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
3
|
import React__default, { useState, useCallback } from 'react';
|
|
4
4
|
import { useReducedMotion, AnimatePresence, motion } from 'motion/react';
|
|
5
|
-
import {
|
|
5
|
+
import { X } from '@phosphor-icons/react';
|
|
6
6
|
import { cn } from '../../../utils/cn.js';
|
|
7
7
|
import { Avatar } from '../Avatar/Avatar.js';
|
|
8
8
|
import { animationTiming, dismissVariants, badgeVariants, dismissButtonVariants, dotVariants } from './Badge.animations.js';
|
|
@@ -131,7 +131,12 @@ const Badge = React__default.memo(React__default.forwardRef(({ children, variant
|
|
|
131
131
|
return (jsx("span", { className: cn("inline-flex items-center justify-center shrink-0 overflow-hidden [&_div]:!size-full", (_a = theme.avatarSizes) === null || _a === void 0 ? void 0 : _a[size], pill ? "rounded-full -ml-2" : "rounded-full -ml-0"), children: jsx(Avatar, { src: avatar.src, name: avatar.name, gradient: avatar.gradient, gradientVariant: avatar.gradientVariant, gradientColors: avatar.gradientColors, fallback: avatar.fallback, initialsCount: avatar.initialsCount, size: "xs", className: cn("!size-full border-0", pill ? "rounded-full" : "rounded-md ring-0") }) }));
|
|
132
132
|
}
|
|
133
133
|
if (iconStart) {
|
|
134
|
-
return (jsx("span", { className: cn("inline-flex items-center justify-center flex-shrink-0
|
|
134
|
+
return (jsx("span", { className: cn("inline-flex items-center justify-center flex-shrink-0",
|
|
135
|
+
// Optical tuck into the cap curve — pill only (it has the extra
|
|
136
|
+
// pillSizes padding to absorb it). On a regular badge the base
|
|
137
|
+
// px-1 padding is 4px, so -ml-1 left the icon ~1px from the
|
|
138
|
+
// border. Mirrors the avatar path's pill/non-pill conditional.
|
|
139
|
+
pill ? "-ml-1" : "-ml-0", (_b = theme.iconSizes) === null || _b === void 0 ? void 0 : _b[size]), "aria-hidden": "true", children: iconStart }));
|
|
135
140
|
}
|
|
136
141
|
if (withDot) {
|
|
137
142
|
if (shouldAnimate) {
|
|
@@ -151,7 +156,7 @@ const Badge = React__default.memo(React__default.forwardRef(({ children, variant
|
|
|
151
156
|
? `${maxWidth}px`
|
|
152
157
|
: maxWidth,
|
|
153
158
|
}
|
|
154
|
-
: undefined, children: children }), dismissible && (jsx("button", { type: "button", onClick: handleDismiss, className: "badge-dismiss-btn -mr-0.5 rounded-full p-0.5 cursor-pointer", "aria-label": "Dismiss", disabled: disabled, children: jsx(
|
|
159
|
+
: undefined, children: children }), dismissible && (jsx("button", { type: "button", onClick: handleDismiss, className: "badge-dismiss-btn -mr-0.5 rounded-full p-0.5 cursor-pointer", "aria-label": "Dismiss", disabled: disabled, children: jsx(X, { className: cn("badge-dismiss-icon", (_p = theme.dismissSizes) === null || _p === void 0 ? void 0 : _p[size]), "aria-hidden": "true" }) }))] })) : null;
|
|
155
160
|
}
|
|
156
161
|
return (jsx(AnimatePresence, { mode: "wait", children: isVisible && (jsxs(motion.span, { ref: ref, className: baseStyles, onClick: handleClick, "aria-disabled": disabled || undefined, variants: dismissible ? dismissVariants : badgeVariants, initial: "initial", animate: "animate", exit: "exit", ...dataAttributes, ...props, children: [renderLeftContent(), jsx("span", { className: cn("badge-content", maxWidth && "truncate"), style: maxWidth
|
|
157
162
|
? {
|
|
@@ -159,7 +164,7 @@ const Badge = React__default.memo(React__default.forwardRef(({ children, variant
|
|
|
159
164
|
? `${maxWidth}px`
|
|
160
165
|
: maxWidth,
|
|
161
166
|
}
|
|
162
|
-
: undefined, children: children }), dismissible && (jsx(motion.button, { type: "button", onClick: handleDismiss, className: "badge-dismiss-btn -mr-0.5 rounded-full p-0.5 cursor-pointer", "aria-label": "Dismiss", disabled: disabled, variants: dismissButtonVariants, initial: "initial", whileHover: "hover", whileTap: "tap", children: jsx(
|
|
167
|
+
: undefined, children: children }), dismissible && (jsx(motion.button, { type: "button", onClick: handleDismiss, className: "badge-dismiss-btn -mr-0.5 rounded-full p-0.5 cursor-pointer", "aria-label": "Dismiss", disabled: disabled, variants: dismissButtonVariants, initial: "initial", whileHover: "hover", whileTap: "tap", children: jsx(X, { className: cn("badge-dismiss-icon", (_q = theme.dismissSizes) === null || _q === void 0 ? void 0 : _q[size]), "aria-hidden": "true", weight: "bold" }) }))] })) }));
|
|
163
168
|
}));
|
|
164
169
|
Badge.displayName = "Badge";
|
|
165
170
|
|