@seekalfred/primitives 0.6.0 → 0.7.1
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/README.md +19 -11
- package/dist/activity-heatmap/activity-heatmap.d.ts +92 -0
- package/dist/activity-heatmap/activity-heatmap.js +699 -0
- package/dist/activity-heatmap/activity-heatmap.js.map +1 -0
- package/dist/activity-heatmap/index.d.ts +2 -0
- package/dist/activity-heatmap/index.js +2 -0
- package/dist/alert/alert.d.ts +5 -1
- package/dist/alert/alert.js +68 -7
- package/dist/alert/alert.js.map +1 -1
- package/dist/alert-dialog/alert-dialog.d.ts +45 -18
- package/dist/alert-dialog/alert-dialog.js +128 -9
- package/dist/alert-dialog/alert-dialog.js.map +1 -1
- package/dist/alert-dialog/index.d.ts +2 -2
- package/dist/alert-dialog/index.js +2 -2
- package/dist/animated-counter/animated-counter.d.ts +38 -0
- package/dist/animated-counter/animated-counter.js +230 -0
- package/dist/animated-counter/animated-counter.js.map +1 -0
- package/dist/animated-counter/index.d.ts +2 -0
- package/dist/animated-counter/index.js +2 -0
- package/dist/announcement-bar/announcement-bar.d.ts +67 -0
- package/dist/announcement-bar/announcement-bar.js +364 -0
- package/dist/announcement-bar/announcement-bar.js.map +1 -0
- package/dist/announcement-bar/index.d.ts +2 -0
- package/dist/announcement-bar/index.js +2 -0
- package/dist/badge/badge.d.ts +1 -1
- package/dist/bar-chart/bar-chart.d.ts +51 -0
- package/dist/bar-chart/bar-chart.js +565 -0
- package/dist/bar-chart/bar-chart.js.map +1 -0
- package/dist/bar-chart/index.d.ts +2 -0
- package/dist/bar-chart/index.js +2 -0
- package/dist/breadcrumb/breadcrumb.d.ts +2 -2
- package/dist/breadcrumb/breadcrumb.js +12 -3
- package/dist/breadcrumb/breadcrumb.js.map +1 -1
- package/dist/brush-chart/brush-chart.d.ts +64 -0
- package/dist/brush-chart/brush-chart.js +1056 -0
- package/dist/brush-chart/brush-chart.js.map +1 -0
- package/dist/brush-chart/index.d.ts +2 -0
- package/dist/brush-chart/index.js +2 -0
- package/dist/button/button.d.ts +2 -2
- package/dist/button/button.js +31 -21
- package/dist/button/button.js.map +1 -1
- package/dist/button-group/button-group.js +3 -4
- package/dist/button-group/button-group.js.map +1 -1
- package/dist/card/card.js +1 -1
- package/dist/card/card.js.map +1 -1
- package/dist/card-stack/card-stack.d.ts +54 -0
- package/dist/card-stack/card-stack.js +492 -0
- package/dist/card-stack/card-stack.js.map +1 -0
- package/dist/card-stack/index.d.ts +2 -0
- package/dist/card-stack/index.js +2 -0
- package/dist/carousel/carousel.d.ts +56 -0
- package/dist/carousel/carousel.js +389 -0
- package/dist/carousel/carousel.js.map +1 -0
- package/dist/carousel/index.d.ts +2 -0
- package/dist/carousel/index.js +2 -0
- package/dist/checkbox/checkbox.js +4 -4
- package/dist/checkbox/checkbox.js.map +1 -1
- package/dist/chip-group/chip-group.d.ts +37 -0
- package/dist/chip-group/chip-group.js +148 -0
- package/dist/chip-group/chip-group.js.map +1 -0
- package/dist/chip-group/index.d.ts +2 -0
- package/dist/chip-group/index.js +2 -0
- package/dist/countdown/countdown.d.ts +45 -0
- package/dist/countdown/countdown.js +298 -0
- package/dist/countdown/countdown.js.map +1 -0
- package/dist/countdown/index.d.ts +2 -0
- package/dist/countdown/index.js +2 -0
- package/dist/date-picker/picker-parts.js +1 -1
- package/dist/date-picker/picker-parts.js.map +1 -1
- package/dist/dialog/dialog.js +1 -1
- package/dist/dialog/dialog.js.map +1 -1
- package/dist/donut-chart/donut-chart.d.ts +64 -0
- package/dist/donut-chart/donut-chart.js +576 -0
- package/dist/donut-chart/donut-chart.js.map +1 -0
- package/dist/donut-chart/index.d.ts +2 -0
- package/dist/donut-chart/index.js +2 -0
- package/dist/empty-state/empty-state.d.ts +21 -10
- package/dist/empty-state/empty-state.js +32 -26
- package/dist/empty-state/empty-state.js.map +1 -1
- package/dist/empty-state/index.d.ts +2 -2
- package/dist/empty-state/index.js +2 -2
- package/dist/file-drop-zone/file-drop-zone.js +1 -1
- package/dist/file-drop-zone/file-drop-zone.js.map +1 -1
- package/dist/filter-bar/filter-bar.d.ts +47 -26
- package/dist/filter-bar/filter-bar.js +272 -156
- package/dist/filter-bar/filter-bar.js.map +1 -1
- package/dist/gauge/gauge.d.ts +45 -0
- package/dist/gauge/gauge.js +219 -0
- package/dist/gauge/gauge.js.map +1 -0
- package/dist/gauge/index.d.ts +2 -0
- package/dist/gauge/index.js +2 -0
- package/dist/in-view-title/in-view-title.d.ts +33 -0
- package/dist/in-view-title/in-view-title.js +119 -0
- package/dist/in-view-title/in-view-title.js.map +1 -0
- package/dist/in-view-title/index.d.ts +2 -0
- package/dist/in-view-title/index.js +2 -0
- package/dist/index.d.ts +49 -6
- package/dist/index.js +48 -8
- package/dist/lib/cn.js +11 -1
- package/dist/lib/cn.js.map +1 -1
- package/dist/lib/field-message.js +28 -10
- package/dist/lib/field-message.js.map +1 -1
- package/dist/lib/field-surface.js +9 -8
- package/dist/lib/field-surface.js.map +1 -1
- package/dist/lib/flag-emoji.js +50 -0
- package/dist/lib/flag-emoji.js.map +1 -0
- package/dist/lib/flip.js +11 -3
- package/dist/lib/flip.js.map +1 -1
- package/dist/lib/menu-highlight.js +64 -0
- package/dist/lib/menu-highlight.js.map +1 -0
- package/dist/lib/motion.js +9 -2
- package/dist/lib/motion.js.map +1 -1
- package/dist/lib/rolling-value.js +1 -1
- package/dist/lib/rolling-value.js.map +1 -1
- package/dist/lib/spring-tween.js +500 -0
- package/dist/lib/spring-tween.js.map +1 -0
- package/dist/lib/swap-text.js +3 -1
- package/dist/lib/swap-text.js.map +1 -1
- package/dist/line-chart/index.d.ts +2 -0
- package/dist/line-chart/index.js +2 -0
- package/dist/line-chart/line-chart.d.ts +76 -0
- package/dist/line-chart/line-chart.js +611 -0
- package/dist/line-chart/line-chart.js.map +1 -0
- package/dist/mention-input/index.d.ts +2 -0
- package/dist/mention-input/index.js +2 -0
- package/dist/mention-input/mention-input.d.ts +98 -0
- package/dist/mention-input/mention-input.js +576 -0
- package/dist/mention-input/mention-input.js.map +1 -0
- package/dist/menu/menu.d.ts +2 -0
- package/dist/menu/menu.js +23 -12
- package/dist/menu/menu.js.map +1 -1
- package/dist/money-input/index.d.ts +2 -0
- package/dist/money-input/index.js +2 -0
- package/dist/money-input/money-input.d.ts +65 -0
- package/dist/money-input/money-input.js +607 -0
- package/dist/money-input/money-input.js.map +1 -0
- package/dist/morph-select/index.d.ts +2 -0
- package/dist/morph-select/index.js +2 -0
- package/dist/morph-select/morph-select.d.ts +71 -0
- package/dist/morph-select/morph-select.js +693 -0
- package/dist/morph-select/morph-select.js.map +1 -0
- package/dist/multi-select/index.d.ts +2 -0
- package/dist/multi-select/index.js +2 -0
- package/dist/multi-select/multi-select.d.ts +36 -0
- package/dist/multi-select/multi-select.js +152 -0
- package/dist/multi-select/multi-select.js.map +1 -0
- package/dist/number-field/index.d.ts +2 -0
- package/dist/number-field/index.js +2 -0
- package/dist/number-field/number-field.d.ts +42 -0
- package/dist/number-field/number-field.js +199 -0
- package/dist/number-field/number-field.js.map +1 -0
- package/dist/onboarding-checklist/index.d.ts +2 -0
- package/dist/onboarding-checklist/index.js +2 -0
- package/dist/onboarding-checklist/onboarding-checklist.d.ts +55 -0
- package/dist/onboarding-checklist/onboarding-checklist.js +577 -0
- package/dist/onboarding-checklist/onboarding-checklist.js.map +1 -0
- package/dist/password-field/password-field.js +2 -2
- package/dist/password-field/password-field.js.map +1 -1
- package/dist/phone-input/index.d.ts +3 -0
- package/dist/phone-input/index.js +3 -0
- package/dist/phone-input/phone-countries.d.ts +29 -0
- package/dist/phone-input/phone-countries.js +170 -0
- package/dist/phone-input/phone-countries.js.map +1 -0
- package/dist/phone-input/phone-input.d.ts +66 -0
- package/dist/phone-input/phone-input.js +440 -0
- package/dist/phone-input/phone-input.js.map +1 -0
- package/dist/popover/popover.d.ts +12 -0
- package/dist/popover/popover.js +31 -7
- package/dist/popover/popover.js.map +1 -1
- package/dist/progress/progress.js +1 -1
- package/dist/radio-group/radio-group.js +1 -1
- package/dist/radio-group/radio-group.js.map +1 -1
- package/dist/reactions/index.d.ts +2 -0
- package/dist/reactions/index.js +2 -0
- package/dist/reactions/reactions.d.ts +50 -0
- package/dist/reactions/reactions.js +315 -0
- package/dist/reactions/reactions.js.map +1 -0
- package/dist/rich-text-editor/index.d.ts +3 -0
- package/dist/rich-text-editor/index.js +2 -0
- package/dist/rich-text-editor/rich-text-editor.d.ts +70 -0
- package/dist/rich-text-editor/rich-text-editor.js +909 -0
- package/dist/rich-text-editor/rich-text-editor.js.map +1 -0
- package/dist/rich-text-editor/rich-text-engine.d.ts +16 -0
- package/dist/rich-text-editor/rich-text-engine.js +366 -0
- package/dist/rich-text-editor/rich-text-engine.js.map +1 -0
- package/dist/rich-text-editor/rich-text-html.js +174 -0
- package/dist/rich-text-editor/rich-text-html.js.map +1 -0
- package/dist/rich-text-editor/rich-text-icons.js +43 -0
- package/dist/rich-text-editor/rich-text-icons.js.map +1 -0
- package/dist/ridgeline/index.d.ts +2 -0
- package/dist/ridgeline/index.js +2 -0
- package/dist/ridgeline/ridgeline.d.ts +55 -0
- package/dist/ridgeline/ridgeline.js +701 -0
- package/dist/ridgeline/ridgeline.js.map +1 -0
- package/dist/scroll-area/index.d.ts +2 -2
- package/dist/scroll-area/index.js +2 -2
- package/dist/scroll-area/scroll-area.d.ts +68 -31
- package/dist/scroll-area/scroll-area.js +208 -59
- package/dist/scroll-area/scroll-area.js.map +1 -1
- package/dist/scrub-input/evaluate.d.ts +14 -0
- package/dist/scrub-input/evaluate.js +72 -0
- package/dist/scrub-input/evaluate.js.map +1 -0
- package/dist/scrub-input/index.d.ts +3 -0
- package/dist/scrub-input/index.js +3 -0
- package/dist/scrub-input/scrub-input.d.ts +74 -0
- package/dist/scrub-input/scrub-input.js +486 -0
- package/dist/scrub-input/scrub-input.js.map +1 -0
- package/dist/search-field/search-field.d.ts +2 -1
- package/dist/search-field/search-field.js +4 -4
- package/dist/search-field/search-field.js.map +1 -1
- package/dist/select/select.d.ts +3 -1
- package/dist/select/select.js +20 -8
- package/dist/select/select.js.map +1 -1
- package/dist/separator/separator.d.ts +1 -1
- package/dist/sheet/sheet.js +2 -1
- package/dist/sheet/sheet.js.map +1 -1
- package/dist/shortcut-recorder/index.d.ts +3 -0
- package/dist/shortcut-recorder/index.js +3 -0
- package/dist/shortcut-recorder/shortcut-model.d.ts +27 -0
- package/dist/shortcut-recorder/shortcut-model.js +206 -0
- package/dist/shortcut-recorder/shortcut-model.js.map +1 -0
- package/dist/shortcut-recorder/shortcut-recorder.d.ts +93 -0
- package/dist/shortcut-recorder/shortcut-recorder.js +489 -0
- package/dist/shortcut-recorder/shortcut-recorder.js.map +1 -0
- package/dist/slider/index.d.ts +2 -2
- package/dist/slider/slider.d.ts +46 -14
- package/dist/slider/slider.js +151 -67
- package/dist/slider/slider.js.map +1 -1
- package/dist/slope-chart/index.d.ts +2 -0
- package/dist/slope-chart/index.js +2 -0
- package/dist/slope-chart/slope-chart.d.ts +54 -0
- package/dist/slope-chart/slope-chart.js +568 -0
- package/dist/slope-chart/slope-chart.js.map +1 -0
- package/dist/sparkline/index.d.ts +2 -0
- package/dist/sparkline/index.js +2 -0
- package/dist/sparkline/sparkline.d.ts +46 -0
- package/dist/sparkline/sparkline.js +390 -0
- package/dist/sparkline/sparkline.js.map +1 -0
- package/dist/split-bar/index.d.ts +2 -0
- package/dist/split-bar/index.js +2 -0
- package/dist/split-bar/split-bar.d.ts +62 -0
- package/dist/split-bar/split-bar.js +513 -0
- package/dist/split-bar/split-bar.js.map +1 -0
- package/dist/split-button/split-button.d.ts +1 -1
- package/dist/split-button/split-button.js +12 -11
- package/dist/split-button/split-button.js.map +1 -1
- package/dist/stat-card/index.d.ts +2 -2
- package/dist/stat-card/index.js +2 -2
- package/dist/stat-card/stat-card.d.ts +24 -4
- package/dist/stat-card/stat-card.js +114 -17
- package/dist/stat-card/stat-card.js.map +1 -1
- package/dist/stepper/index.d.ts +2 -0
- package/dist/stepper/index.js +2 -0
- package/dist/stepper/stepper.d.ts +51 -0
- package/dist/stepper/stepper.js +215 -0
- package/dist/stepper/stepper.js.map +1 -0
- package/dist/streamgraph/index.d.ts +2 -0
- package/dist/streamgraph/index.js +2 -0
- package/dist/streamgraph/streamgraph.d.ts +70 -0
- package/dist/streamgraph/streamgraph.js +613 -0
- package/dist/streamgraph/streamgraph.js.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/swipe-actions/index.d.ts +2 -0
- package/dist/swipe-actions/index.js +2 -0
- package/dist/swipe-actions/swipe-actions.d.ts +67 -0
- package/dist/swipe-actions/swipe-actions.js +552 -0
- package/dist/swipe-actions/swipe-actions.js.map +1 -0
- package/dist/switch/switch.js +2 -2
- package/dist/switch/switch.js.map +1 -1
- package/dist/table/index.d.ts +2 -2
- package/dist/table/index.js +2 -2
- package/dist/table/table.d.ts +56 -4
- package/dist/table/table.js +340 -165
- package/dist/table/table.js.map +1 -1
- package/dist/tabs/tabs.js +2 -2
- package/dist/tabs/tabs.js.map +1 -1
- package/dist/tag-input/index.d.ts +2 -0
- package/dist/tag-input/index.js +2 -0
- package/dist/tag-input/tag-input.d.ts +40 -0
- package/dist/tag-input/tag-input.js +269 -0
- package/dist/tag-input/tag-input.js.map +1 -0
- package/dist/text-morph/index.d.ts +2 -0
- package/dist/text-morph/index.js +2 -0
- package/dist/text-morph/text-morph.d.ts +23 -0
- package/dist/text-morph/text-morph.js +21 -0
- package/dist/text-morph/text-morph.js.map +1 -0
- package/dist/text-reveal/index.d.ts +2 -0
- package/dist/text-reveal/index.js +2 -0
- package/dist/text-reveal/text-reveal.d.ts +27 -0
- package/dist/text-reveal/text-reveal.js +51 -0
- package/dist/text-reveal/text-reveal.js.map +1 -0
- package/dist/text-shimmer/index.d.ts +2 -0
- package/dist/text-shimmer/index.js +2 -0
- package/dist/text-shimmer/text-shimmer.d.ts +29 -0
- package/dist/text-shimmer/text-shimmer.js +29 -0
- package/dist/text-shimmer/text-shimmer.js.map +1 -0
- package/dist/time-picker/time-picker.js +2 -2
- package/dist/time-picker/time-picker.js.map +1 -1
- package/dist/toast/index.d.ts +2 -2
- package/dist/toast/index.js +2 -2
- package/dist/toast/toast.d.ts +6 -0
- package/dist/toast/toast.js +82 -40
- package/dist/toast/toast.js.map +1 -1
- package/dist/tooltip/tooltip.d.ts +9 -2
- package/dist/tooltip/tooltip.js +7 -7
- package/dist/tooltip/tooltip.js.map +1 -1
- package/dist/treemap/index.d.ts +2 -0
- package/dist/treemap/index.js +2 -0
- package/dist/treemap/treemap.d.ts +53 -0
- package/dist/treemap/treemap.js +699 -0
- package/dist/treemap/treemap.js.map +1 -0
- package/dist/usage-meter/index.d.ts +2 -0
- package/dist/usage-meter/index.js +2 -0
- package/dist/usage-meter/usage-meter.d.ts +50 -0
- package/dist/usage-meter/usage-meter.js +440 -0
- package/dist/usage-meter/usage-meter.js.map +1 -0
- package/dist/waffle-chart/index.d.ts +2 -0
- package/dist/waffle-chart/index.js +2 -0
- package/dist/waffle-chart/waffle-chart.d.ts +57 -0
- package/dist/waffle-chart/waffle-chart.js +426 -0
- package/dist/waffle-chart/waffle-chart.js.map +1 -0
- package/package.json +165 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"treemap.js","names":[],"sources":["../../src/treemap/treemap.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type KeyboardEvent,\n type PointerEvent,\n} from \"react\";\n\nimport { cn } from \"../lib/cn.js\";\nimport { mergeRefs } from \"../lib/merge-refs.js\";\nimport { prefersReducedMotion, useIsomorphicLayoutEffect } from \"../lib/motion.js\";\nimport { REVEAL, SWELL, tween } from \"../lib/spring-tween.js\";\nimport { SwapText } from \"../lib/swap-text.js\";\n\n/**\n * Treemap: a hierarchy of parts, such as revenue by region, country and plan,\n * when sizes matter more than exact comparisons. Tiles are laid out by the\n * squarified algorithm (rows along the short side, each closed when another tile\n * would make the worst aspect ratio worse), two levels at a time: the branches\n * of the node in view, each holding its own children under a header.\n *\n * Tiles grow in a quick wave from the largest the first time the chart is seen.\n * Pressing a branch (or Enter) zooms into it: the tile grows to fill the view and\n * its children open inside it; Escape or a breadcrumb zooms back out, the view\n * shrinking into its tile. New data moves every tile from where it is on screen.\n * Hovering or the arrow keys read a tile in a tooltip, and the rest of its branch\n * steps back. An optional second measure (growth, say) shades tiles in steps.\n *\n * Tiles are absolutely placed and sized each frame by one small spring tween, so\n * their text never scales while they grow or shrink.\n */\nexport interface TreemapNode {\n /** Stable identity, unique in the tree. A tile that survives a data change morphs to its new size. */\n id: string;\n label: string;\n /** Leaf size. A branch's size is the sum of its children. */\n value?: number;\n /** Optional second measure for shading, such as growth. A branch uses the size weighted mean of its children. */\n color?: number;\n children?: TreemapNode[];\n}\n\nexport interface TreemapProps {\n /** The root. Its label names the whole in the first breadcrumb. */\n data: TreemapNode;\n /** What the whole is, such as \"Revenue\". Names the chart for assistive technology. */\n label: string;\n formatValue?: (value: number) => string;\n /** Names the shading measure in the tooltip and scale, such as \"Growth\". Tiles take the hue of their top level branch and grow deeper with the measure. Leave it out to draw every tile at one depth. */\n colorLabel?: string;\n formatColor?: (value: number) => string;\n /** Range of the colour measure. Defaults to the range of the leaves. */\n colorDomain?: [number, number];\n /** Controlled id of the node that fills the view. */\n focus?: string;\n defaultFocus?: string;\n onFocusChange?: (id: string) => void;\n /** Height of the tiles in pixels. @default 420 */\n height?: number;\n /** @default \"No data yet\" */\n emptyLabel?: string;\n /** Extra classes for the root. Merged last. */\n className?: string;\n}\n\ntype Rect = { x: number; y: number; w: number; h: number };\ntype Flat = { id: string; label: string; value: number; color: number | null; depth: number; parent: string | null; children: string[]; path: string[] };\ntype Drawn = Rect & { o: number };\n\n/** Top level branches take chart-1 to chart-4 in order; a fifth and beyond stay neutral rather than reuse a hue. */\nconst hueOf = (index: number) => (index < 0 ? \"var(--color-chart-1)\" : index < 4 ? `var(--color-chart-${index + 1})` : \"var(--color-foreground)\");\n/** The gap between tiles, the padding of a branch, the height of a branch header, the gap to the tooltip, and the shading steps. */\nconst GAP = 2;\nconst PAD = 3;\nconst HEADER = 22;\nconst TIP = 14;\nconst STEPS = 5;\nconst GROUPED = new Intl.NumberFormat(\"en-US\", { maximumFractionDigits: 1 });\nconst PERCENT = new Intl.NumberFormat(\"en-US\", { style: \"percent\", maximumFractionDigits: 1 });\nconst clamp = (value: number, low: number, high: number) => Math.min(high, Math.max(low, value));\nconst mix = (a: number, b: number, t: number) => a + (b - a) * t;\nconst inset = (r: Rect, top: number, side: number): Rect => ({ x: r.x + side, y: r.y + top, w: Math.max(0, r.w - side * 2), h: Math.max(0, r.h - top - side) });\n\nfunction flatten(root: TreemapNode) {\n const flat = new Map<string, Flat>();\n const visit = (node: TreemapNode, depth: number, parent: string | null, path: string[]): { value: number; weighted: number; colored: number } => {\n const kids = node.children ?? [];\n let value = 0;\n let weighted = 0;\n let colored = 0;\n if (kids.length) {\n for (const kid of kids) {\n const r = visit(kid, depth + 1, node.id, depth === 0 ? [] : [...path, node.label]);\n value += r.value;\n weighted += r.weighted;\n colored += r.colored;\n }\n } else {\n value = Math.max(0, node.value ?? 0);\n if (node.color !== undefined && Number.isFinite(node.color)) {\n weighted = node.color * value;\n colored = value;\n }\n }\n flat.set(node.id, {\n id: node.id,\n label: node.label,\n value,\n color: colored ? weighted / colored : null,\n depth,\n parent,\n children: kids.map((kid) => kid.id),\n path: depth === 0 ? [node.label] : [...path, node.label],\n });\n return { value, weighted, colored };\n };\n visit(root, 0, null, []);\n return flat;\n}\n\n/** Squarified layout: rows of tiles along the short side, each row closed when adding a tile would make the worst aspect ratio worse. */\nfunction squarify(items: { id: string; value: number }[], rect: Rect, out: Map<string, Rect>) {\n const list = items.filter((item) => item.value > 0).sort((a, b) => b.value - a.value);\n const total = list.reduce((sum, item) => sum + item.value, 0);\n if (!total || rect.w <= 0 || rect.h <= 0) {\n for (const item of items) out.set(item.id, { x: rect.x, y: rect.y, w: 0, h: 0 });\n return;\n }\n const scale = (rect.w * rect.h) / total;\n let { x, y, w, h } = rect;\n let i = 0;\n const worst = (row: number[], side: number) => {\n const sum = row.reduce((a, b) => a + b, 0);\n return Math.max((side * side * Math.max(...row)) / (sum * sum), (sum * sum) / (side * side * Math.min(...row)));\n };\n while (i < list.length) {\n const side = Math.min(w, h);\n const row: number[] = [list[i]!.value * scale];\n let j = i + 1;\n while (j < list.length) {\n const next = [...row, list[j]!.value * scale];\n if (worst(next, side) > worst(row, side)) break;\n row.push(list[j]!.value * scale);\n j++;\n }\n const sum = row.reduce((a, b) => a + b, 0);\n if (w >= h) {\n const cw = sum / h;\n let cy = y;\n for (let k = i; k < j; k++) {\n const th = (list[k]!.value * scale) / cw;\n out.set(list[k]!.id, { x, y: cy, w: cw, h: th });\n cy += th;\n }\n x += cw;\n w -= cw;\n } else {\n const rh = sum / w;\n let cx = x;\n for (let k = i; k < j; k++) {\n const tw = (list[k]!.value * scale) / rh;\n out.set(list[k]!.id, { x: cx, y, w: tw, h: rh });\n cx += tw;\n }\n y += rh;\n h -= rh;\n }\n i = j;\n }\n for (const item of items) if (!out.has(item.id)) out.set(item.id, { x: rect.x, y: rect.y, w: 0, h: 0 });\n}\n\n/** Rects of every descendant when `focus` fills the view: its children fill the view, and each branch holds its own children under a header. */\nfunction layoutFrom(flat: Map<string, Flat>, focus: string, view: Rect) {\n const out = new Map<string, Rect>();\n const nest = (id: string, rect: Rect, top: boolean) => {\n const node = flat.get(id)!;\n if (!node.children.length) return;\n const room = top ? rect : inset(rect, rect.h > 48 && rect.w > 64 ? HEADER : PAD, PAD);\n const placed = new Map<string, Rect>();\n squarify(node.children.map((child) => ({ id: child, value: flat.get(child)!.value })), room, placed);\n for (const [child, r] of placed) {\n const gapped = { x: r.x + GAP / 2, y: r.y + GAP / 2, w: Math.max(0, r.w - GAP), h: Math.max(0, r.h - GAP) };\n out.set(child, gapped);\n nest(child, gapped, false);\n }\n };\n nest(focus, view, true);\n return out;\n}\n/** The affine map that carries rect `from` onto rect `to`, applied to `r`. */\nconst carry = (r: Rect, from: Rect, to: Rect): Rect => {\n const sx = to.w / (from.w || 1);\n const sy = to.h / (from.h || 1);\n return { x: to.x + (r.x - from.x) * sx, y: to.y + (r.y - from.y) * sy, w: r.w * sx, h: r.h * sy };\n};\n\nexport const Treemap = forwardRef<HTMLElement, TreemapProps>(function Treemap(\n {\n data,\n label,\n formatValue = (value) => GROUPED.format(value),\n colorLabel,\n formatColor = (value) => GROUPED.format(value),\n colorDomain,\n focus,\n defaultFocus,\n onFocusChange,\n height = 420,\n emptyLabel = \"No data yet\",\n className,\n },\n ref,\n) {\n const figure = useRef<HTMLElement | null>(null);\n const refs = useMemo(() => mergeRefs(ref, figure), [ref]);\n const stage = useRef<HTMLDivElement>(null);\n const tip = useRef<HTMLDivElement>(null);\n const tiles = useRef(new Map<string, HTMLDivElement>());\n\n const signature = JSON.stringify(data);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const flat = useMemo(() => flatten(data), [signature]);\n const root = flat.get(data.id)!;\n const nodes = useMemo(() => [...flat.values()].filter((node) => node.depth > 0), [flat]);\n const [ownFocus, setOwnFocus] = useState(defaultFocus ?? data.id);\n const wanted = flat.get(focus ?? ownFocus);\n const center = wanted && wanted.children.length ? wanted : root;\n const zoomTo = (id: string) => {\n if (focus === undefined) setOwnFocus(id);\n onFocusChange?.(id);\n };\n const [active, setActive] = useState<string | null>(null);\n const [pointer, setPointer] = useState<{ x: number; y: number } | null>(null);\n\n const [width, setWidth] = useState(0);\n useIsomorphicLayoutEffect(() => {\n const node = stage.current;\n if (!node || typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(() => setWidth(node.clientWidth));\n observer.observe(node);\n setWidth(node.clientWidth);\n return () => observer.disconnect();\n }, []);\n const view = useMemo<Rect>(() => ({ x: 0, y: 0, w: width, h: height }), [width, height]);\n const target = useMemo(() => layoutFrom(flat, center.id, view), [flat, center.id, view]);\n const levelOf = (node: Flat) => node.depth - center.depth;\n\n // Colour scale: a few steps of the branch's hue from light to strong, so shade reads as more.\n const leaves = nodes.filter((node) => !node.children.length && node.color !== null);\n const [lo, hi] = colorDomain ?? (leaves.length ? [Math.min(...leaves.map((node) => node.color!)), Math.max(...leaves.map((node) => node.color!))] : [0, 1]);\n const colored = colorLabel !== undefined && leaves.length > 0;\n const shade = (value: number | null) => {\n if (!colored || value === null) return 34;\n const t = clamp((value - lo) / (hi - lo || 1), 0, 1);\n return Math.round(14 + (Math.round(t * (STEPS - 1)) / (STEPS - 1)) * 44);\n };\n const branchOf = useMemo(() => {\n const map = new Map<string, number>();\n root.children.forEach((id, index) => {\n const walk = (at: string) => {\n map.set(at, index);\n flat.get(at)?.children.forEach(walk);\n };\n walk(id);\n });\n return map;\n }, [flat, root]);\n\n // Every tile keeps its on screen rect; a spring carries it to the new layout. Tiles entering or leaving ride the same zoom as the tile they belong to.\n const drawn = useRef(new Map<string, Drawn>());\n const levels = useRef(new Map<string, number>());\n const lastFocus = useRef<{ id: string; layout: Map<string, Rect> } | null>(null);\n const nodesRef = useRef(nodes);\n useIsomorphicLayoutEffect(() => {\n nodesRef.current = nodes;\n });\n const paint = useCallback(() => {\n for (const node of nodesRef.current) {\n const el = tiles.current.get(node.id);\n const d = drawn.current.get(node.id);\n if (!el) continue;\n if (!d || d.o < 0.01 || d.w < 0.5 || d.h < 0.5) {\n el.style.visibility = \"hidden\";\n el.style.pointerEvents = \"none\";\n continue;\n }\n el.style.visibility = \"visible\";\n el.style.transform = `translate3d(${d.x.toFixed(2)}px, ${d.y.toFixed(2)}px, 0)`;\n el.style.width = `${d.w.toFixed(2)}px`;\n el.style.height = `${d.h.toFixed(2)}px`;\n el.style.opacity = d.o.toFixed(3);\n el.style.pointerEvents = d.o > 0.6 ? \"auto\" : \"none\";\n el.dataset.level = String(levels.current.get(node.id) ?? 0);\n el.dataset.room = d.w > 64 && d.h > 38 ? (d.h > 54 ? \"full\" : \"name\") : \"none\";\n }\n }, []);\n useIsomorphicLayoutEffect(paint);\n\n // The wave starts once a quarter of the chart is in view.\n const [inView, setInView] = useState(false);\n useEffect(() => {\n const node = figure.current;\n if (!node || prefersReducedMotion() || typeof IntersectionObserver === \"undefined\") {\n setInView(true);\n return;\n }\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (!entry?.isIntersecting) return;\n observer.disconnect();\n setInView(true);\n },\n { threshold: 0.25 },\n );\n observer.observe(node);\n return () => observer.disconnect();\n }, []);\n\n const shown = inView && width > 0;\n useEffect(() => {\n if (!shown) return;\n const prev = lastFocus.current;\n const isUnder = (id: string, ancestor: string) => {\n for (let at: Flat | undefined = flat.get(id); at; at = at.parent ? flat.get(at.parent) : undefined) if (at.id === ancestor) return true;\n return false;\n };\n const visible = (node: Flat) => {\n const l = node.depth - center.depth;\n return l >= 1 && l <= 2 && isUnder(node.id, center.id);\n };\n const nextVisible = new Set(nodes.filter(visible).map((node) => node.id));\n const before = new Map(drawn.current);\n const wasVisible = new Set([...before].filter(([, d]) => d.o > 0.5).map(([id]) => id));\n // The pivot: the tile that grows to fill the view, or the view that shrinks back into its tile.\n let toOld: ((r: Rect) => Rect) | null = null;\n let toNew: ((r: Rect) => Rect) | null = null;\n if (prev && prev.id !== center.id && before.size) {\n if (isUnder(center.id, prev.id)) {\n const inOld = prev.layout.get(center.id);\n if (inOld) {\n toOld = (r) => carry(r, view, inOld);\n toNew = (r) => carry(r, inOld, view);\n }\n } else if (isUnder(prev.id, center.id)) {\n const inNew = target.get(prev.id);\n if (inNew) {\n toOld = (r) => carry(r, inNew, view);\n toNew = (r) => carry(r, view, inNew);\n }\n }\n }\n const from = new Map<string, Drawn>();\n const to = new Map<string, Drawn>();\n const first = !before.size;\n for (const node of nodes) {\n const goal = target.get(node.id);\n const now = before.get(node.id);\n const inNext = nextVisible.has(node.id);\n const inPrev = wasVisible.has(node.id);\n if (inNext && goal) {\n to.set(node.id, { ...goal, o: 1 });\n if (inPrev && now) from.set(node.id, now);\n else if (first) from.set(node.id, { x: goal.x + goal.w / 2, y: goal.y + goal.h / 2, w: 0, h: 0, o: 0 });\n else from.set(node.id, { ...(toOld ? toOld(goal) : goal), o: 0 });\n levels.current.set(node.id, levelOf(node));\n } else if (inPrev && now) {\n from.set(node.id, now);\n to.set(node.id, { ...(toNew ? toNew(now) : now), o: 0 });\n }\n }\n for (const node of nodes) if (!from.has(node.id)) drawn.current.set(node.id, { x: 0, y: 0, w: 0, h: 0, o: 0 });\n lastFocus.current = { id: center.id, layout: target };\n return tween(\n first ? SWELL : REVEAL,\n (t) => {\n for (const [id, a] of from) {\n const b = to.get(id)!;\n // On the first reveal, tiles grow in a quick wave from the largest.\n const local = first ? clamp((t - Math.min(0.3, (1 - (b.w * b.h) / (view.w * view.h || 1)) * 0.3)) / 0.7, 0, 1) : t;\n drawn.current.set(id, { x: mix(a.x, b.x, local), y: mix(a.y, b.y, local), w: mix(a.w, b.w, local), h: mix(a.h, b.h, local), o: mix(a.o, b.o, local) });\n }\n paint();\n },\n () => {\n for (const [id, d] of to) drawn.current.set(id, d);\n paint();\n },\n );\n // The target stands for the tree, the focus and the view.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [shown, target]);\n\n const activeNode = active ? (flat.get(active) ?? null) : null;\n const parentOf = (node: Flat) => (node.parent ? flat.get(node.parent)! : node);\n const share = (node: Flat, of: Flat) => (of.value ? node.value / of.value : 0);\n\n // The tooltip trails the pointer, or sits by the tile in focus, and stays inside the chart; it glides between tiles once showing.\n const shownBefore = useRef(false);\n useIsomorphicLayoutEffect(() => {\n const bubble = tip.current;\n if (!bubble || !activeNode || !width) {\n shownBefore.current = false;\n return;\n }\n const r = target.get(activeNode.id);\n const ax = pointer?.x ?? (r ? r.x + r.w / 2 : 0);\n const ay = pointer?.y ?? (r ? r.y + Math.min(r.h / 2, 28) : 0);\n const tw = bubble.offsetWidth;\n const th = bubble.offsetHeight;\n let left = ax + TIP;\n if (left + tw > width) left = ax - TIP - tw;\n let top = ay + TIP;\n if (top + th > height) top = ay - TIP - th;\n if (!shownBefore.current) bubble.style.transition = \"none\";\n bubble.style.translate = `${clamp(left, 0, Math.max(0, width - tw))}px ${clamp(top, 0, Math.max(0, height - th))}px`;\n if (!shownBefore.current) {\n void bubble.offsetWidth;\n bubble.style.transition = \"\";\n }\n shownBefore.current = true;\n });\n\n // Keyboard: arrows hop to the nearest tile in that direction on the top level, Enter zooms in, Escape zooms out.\n const topTiles = center.children.map((id) => flat.get(id)!).filter((node) => node.value > 0);\n const topOf = (node: Flat | null) => {\n let at = node;\n while (at && at.parent && at.parent !== center.id) at = flat.get(at.parent) ?? null;\n return at && at.parent === center.id ? at : null;\n };\n const onKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n const current = topOf(activeNode);\n const dirs: Record<string, [number, number]> = { ArrowRight: [1, 0], ArrowLeft: [-1, 0], ArrowDown: [0, 1], ArrowUp: [0, -1] };\n if (event.key in dirs) {\n event.preventDefault();\n setPointer(null);\n if (!current) {\n setActive(topTiles[0]?.id ?? null);\n return;\n }\n const [dx, dy] = dirs[event.key]!;\n const o = target.get(current.id)!;\n const ox = o.x + o.w / 2;\n const oy = o.y + o.h / 2;\n let best: Flat | null = null;\n let score = Infinity;\n for (const node of topTiles) {\n if (node.id === current.id) continue;\n const r = target.get(node.id);\n if (!r) continue;\n const vx = r.x + r.w / 2 - ox;\n const vy = r.y + r.h / 2 - oy;\n const along = vx * dx + vy * dy;\n const across = Math.abs(vx * dy - vy * dx);\n if (along <= 1) continue;\n const s = along + across * 1.5;\n if (s < score) {\n score = s;\n best = node;\n }\n }\n if (best) setActive(best.id);\n } else if (event.key === \"Home\" || event.key === \"End\") {\n event.preventDefault();\n setPointer(null);\n setActive((event.key === \"Home\" ? topTiles[0] : topTiles[topTiles.length - 1])?.id ?? null);\n } else if (event.key === \"Enter\" || event.key === \" \") {\n if (current?.children.length) {\n event.preventDefault();\n zoomTo(current.id);\n setActive(current.children[0]!);\n }\n } else if (event.key === \"Escape\" || event.key === \"Backspace\") {\n if (center.parent) {\n event.preventDefault();\n zoomTo(center.parent);\n setActive(center.id);\n } else if (active) {\n event.preventDefault();\n setActive(null);\n }\n }\n };\n const onTilePointerMove = (event: PointerEvent<HTMLDivElement>, id: string) => {\n if (event.pointerType !== \"mouse\") return;\n const rect = stage.current?.getBoundingClientRect();\n if (rect) setPointer({ x: event.clientX - rect.left, y: event.clientY - rect.top });\n setActive(id);\n };\n const onTileClick = (node: Flat) => {\n const top = topOf(node);\n if (top?.children.length) zoomTo(top.id);\n else if (!activeNode || activeNode.id !== node.id) setActive(node.id);\n };\n\n const trail: Flat[] = [];\n for (let at: Flat | undefined = center; at; at = at.parent ? flat.get(at.parent) : undefined) trail.unshift(at);\n const empty = !root.value;\n const describe = (node: Flat) =>\n `${node.path.join(\", \")}, ${formatValue(node.value)}, ${PERCENT.format(share(node, parentOf(node)))} of ${parentOf(node).label}${\n colored && node.color !== null ? `, ${colorLabel} ${formatColor(node.color)}` : \"\"\n }`;\n const focusTop = topOf(activeNode);\n const summary = `${label}, ${center.label}, ${formatValue(center.value)}`;\n\n return (\n <figure ref={refs} data-slot=\"treemap\" aria-label={label} className={cn(\"treemap box-border font-sans text-foreground\", className)}>\n <div className=\"flex min-w-0 items-center justify-between gap-3\">\n <nav aria-label={`${label} path`} className=\"-ms-1 min-w-0\">\n <ol className=\"m-0 flex min-h-8 min-w-0 list-none flex-wrap items-center p-0\">\n {trail.map((node, index) => (\n <li key={node.id} className=\"inline-flex min-w-0 items-center motion-safe:animate-fade-in\">\n {index > 0 ? (\n <span aria-hidden=\"true\" className=\"px-1 text-sm text-muted-foreground\">\n /\n </span>\n ) : null}\n {index < trail.length - 1 ? (\n <button\n type=\"button\"\n className=\"treemap-crumb\"\n onClick={() => {\n zoomTo(node.id);\n setActive(null);\n }}\n >\n {node.label}\n </button>\n ) : (\n <span aria-current=\"location\" className=\"px-1 text-sm font-medium whitespace-nowrap\">\n {node.label}\n </span>\n )}\n </li>\n ))}\n </ol>\n </nav>\n <p data-slot=\"treemap-total\" className=\"m-0 flex-none text-sm font-medium tabular-nums\">\n <SwapText swapKey={formatValue(center.value)} motion=\"rise\">\n {formatValue(center.value)}\n </SwapText>\n </p>\n </div>\n <div\n ref={stage}\n data-slot=\"treemap-stage\"\n data-active={activeNode ? \"\" : undefined}\n data-scrubbing={activeNode ? \"\" : undefined}\n role={empty ? undefined : \"application\"}\n tabIndex={empty ? undefined : 0}\n aria-roledescription={empty ? undefined : \"treemap\"}\n aria-label={empty ? undefined : `${label}, ${center.label}. Arrow keys move between tiles, Enter zooms in, Escape zooms out.`}\n className=\"treemap-stage\"\n style={{ height }}\n onKeyDown={onKeyDown}\n onBlur={() => setActive(null)}\n onPointerLeave={() => {\n setActive(null);\n setPointer(null);\n }}\n >\n {nodes.map((node) => {\n const top = topOf(node);\n const hue = hueOf(branchOf.get(node.id) ?? -1);\n return (\n <div\n key={node.id}\n ref={(element) => {\n if (element) tiles.current.set(node.id, element);\n else tiles.current.delete(node.id);\n }}\n aria-hidden=\"true\"\n data-tile={node.id}\n data-branch={node.children.length > 0 || undefined}\n data-active={active === node.id || undefined}\n data-lineage={(focusTop?.id === node.id && active !== node.id) || undefined}\n data-dim={(focusTop !== null && top !== null && focusTop.id !== top.id) || undefined}\n className=\"treemap-tile\"\n style={{ \"--hue\": hue, \"--fill\": `color-mix(in oklab, ${hue} ${shade(node.color)}%, var(--color-surface))`, zIndex: node.depth } as CSSProperties}\n onPointerMove={(event) => {\n event.stopPropagation();\n onTilePointerMove(event, node.id);\n }}\n onClick={(event) => {\n event.stopPropagation();\n onTileClick(node);\n }}\n >\n <span className=\"treemap-tile-label\">{node.label}</span>\n <span className=\"treemap-tile-value\">{formatValue(node.value)}</span>\n </div>\n );\n })}\n {empty ? <p className=\"pointer-events-none absolute inset-0 m-0 grid place-items-center text-sm text-muted-foreground\">{emptyLabel}</p> : null}\n <div className=\"pointer-events-none absolute inset-0 z-50\">\n <div ref={tip} aria-hidden=\"true\" data-slot=\"treemap-tooltip\" className=\"line-chart-tooltip\">\n {activeNode ? (\n <>\n <p className=\"m-0 truncate text-xs text-muted-foreground\">{activeNode.path.slice(0, -1).join(\" / \") || root.label}</p>\n <p className=\"m-0 flex items-baseline justify-between gap-3 text-sm\">\n <span className=\"min-w-0 truncate\">{activeNode.label}</span>\n <span className=\"flex-none font-medium tabular-nums\">{formatValue(activeNode.value)}</span>\n </p>\n <p className=\"m-0 text-xs tabular-nums text-muted-foreground\">\n {PERCENT.format(share(activeNode, parentOf(activeNode)))} of {parentOf(activeNode).label}\n </p>\n {colored && activeNode.color !== null ? (\n <p className=\"m-0 text-xs tabular-nums text-muted-foreground\">\n {colorLabel} {formatColor(activeNode.color)}\n </p>\n ) : null}\n </>\n ) : null}\n </div>\n </div>\n </div>\n {colored ? (\n <div aria-hidden=\"true\" className=\"flex min-w-0 flex-wrap items-center gap-2 text-xs tabular-nums text-muted-foreground\">\n <span className=\"me-1 text-text-secondary\">{colorLabel}</span>\n <span>{formatColor(lo)}</span>\n <span className=\"inline-flex gap-0.5\">\n {Array.from({ length: STEPS }, (_, i) => (\n <span\n key={i}\n className=\"treemap-step\"\n style={{ \"--fill\": `color-mix(in oklab, ${center.depth === 0 ? \"var(--color-foreground)\" : hueOf(branchOf.get(center.id) ?? -1)} ${Math.round(14 + (i / (STEPS - 1)) * 44)}%, var(--color-surface))` } as CSSProperties}\n />\n ))}\n </span>\n <span>{formatColor(hi)}</span>\n </div>\n ) : null}\n <p className=\"sr-only\">{summary}</p>\n <p className=\"sr-only\" aria-live=\"polite\" aria-atomic=\"true\">\n {activeNode ? describe(activeNode) : \"\"}\n </p>\n {empty ? null : (\n <div className=\"sr-only\">\n <table>\n <caption>{label}</caption>\n <thead>\n <tr>\n <th scope=\"col\">Path</th>\n <th scope=\"col\">Value</th>\n <th scope=\"col\">Share of parent</th>\n {colored ? <th scope=\"col\">{colorLabel}</th> : null}\n </tr>\n </thead>\n <tbody>\n {nodes.map((node) => (\n <tr key={node.id}>\n <th scope=\"row\">{node.path.join(\" / \")}</th>\n <td>{formatValue(node.value)}</td>\n <td>{PERCENT.format(share(node, parentOf(node)))}</td>\n {colored ? <td>{node.color === null ? \"\" : formatColor(node.color)}</td> : null}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </figure>\n );\n});\n"],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { ComponentPropsWithoutRef } from "react";
|
|
2
|
+
//#region src/usage-meter/usage-meter.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* UsageMeter: a fixed allowance, such as storage or seats, when people need to
|
|
5
|
+
* see what takes the space and how close they are to the limit.
|
|
6
|
+
*
|
|
7
|
+
* The segments grow into place from the left once the meter is seen, each a
|
|
8
|
+
* beat after the one before. After that every change re-flows them: a segment
|
|
9
|
+
* that grows pushes the ones after it along in the same frame. The bar spans
|
|
10
|
+
* the limit, or everything used once that is more; past the limit the overflow
|
|
11
|
+
* keeps its colours but is struck through with a hatch, and a marker shows
|
|
12
|
+
* where the limit was, so "over" never rests on colour alone. The headline and
|
|
13
|
+
* every legend value count to their new number, and the status mark swaps its
|
|
14
|
+
* words and icon while its width follows.
|
|
15
|
+
*
|
|
16
|
+
* Hover previews a category, focus previews it too, and a press pins it until
|
|
17
|
+
* pressed again or Escape. The legend is the keyboard path: arrow keys move
|
|
18
|
+
* between categories. The bar names every amount for assistive technology,
|
|
19
|
+
* and a change of status (almost full, over, back under) is announced.
|
|
20
|
+
*
|
|
21
|
+
* Plain DOM and CSS; one small spring tween writes the segments straight to
|
|
22
|
+
* their elements.
|
|
23
|
+
*/
|
|
24
|
+
export interface UsageMeterSegment {
|
|
25
|
+
/** Stable identity. Keep it the same while the value changes, so the segment re-flows instead of being replaced. */
|
|
26
|
+
id: string;
|
|
27
|
+
label: string;
|
|
28
|
+
value: number;
|
|
29
|
+
}
|
|
30
|
+
export interface UsageMeterProps extends Omit<ComponentPropsWithoutRef<"div">, "children"> {
|
|
31
|
+
/** What is measured, such as "Workspace storage". */
|
|
32
|
+
label: string;
|
|
33
|
+
/** Used amounts in display order. Up to four read clearly. */
|
|
34
|
+
segments: UsageMeterSegment[];
|
|
35
|
+
/** The plan limit, in the same unit as the segments. */
|
|
36
|
+
limit: number;
|
|
37
|
+
/** Unit after each number, such as "GB". */
|
|
38
|
+
unit?: string;
|
|
39
|
+
/** Digits after the decimal point. @default 1 */
|
|
40
|
+
decimals?: number;
|
|
41
|
+
/** Name of the unused share. @default "Free" */
|
|
42
|
+
freeLabel?: string;
|
|
43
|
+
/** Name of the share past the limit. @default "Over limit" */
|
|
44
|
+
overLabel?: string;
|
|
45
|
+
/** Share of the limit at which the meter warns that it is almost full. @default 0.9 */
|
|
46
|
+
warnAt?: number;
|
|
47
|
+
}
|
|
48
|
+
export declare const UsageMeter: import("react").ForwardRefExoticComponent<UsageMeterProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
49
|
+
//#endregion
|
|
50
|
+
//# sourceMappingURL=usage-meter.d.ts.map
|
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "../lib/cn.js";
|
|
3
|
+
import { mergeRefs } from "../lib/merge-refs.js";
|
|
4
|
+
import { prefersReducedMotion, useIsomorphicLayoutEffect } from "../lib/motion.js";
|
|
5
|
+
import { useCountTo } from "../lib/count-to.js";
|
|
6
|
+
import { SwapText } from "../lib/swap-text.js";
|
|
7
|
+
import { Badge } from "../badge/badge.js";
|
|
8
|
+
import { FILL, SMOOTH, live, retarget } from "../lib/spring-tween.js";
|
|
9
|
+
import { useSvgId } from "../lib/svg-id.js";
|
|
10
|
+
import { forwardRef, useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
|
|
11
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
12
|
+
//#region src/usage-meter/usage-meter.tsx
|
|
13
|
+
/** A segment stops two pixels short of the next, so neighbours read apart without an outline. */
|
|
14
|
+
const GAP = 2;
|
|
15
|
+
/** Stagger of each segment's first growth. */
|
|
16
|
+
const STAGGER_MS = 70;
|
|
17
|
+
const formats = /* @__PURE__ */ new Map();
|
|
18
|
+
function numberFormat(decimals) {
|
|
19
|
+
let format = formats.get(decimals);
|
|
20
|
+
if (!format) {
|
|
21
|
+
format = new Intl.NumberFormat("en-US", {
|
|
22
|
+
minimumFractionDigits: decimals,
|
|
23
|
+
maximumFractionDigits: decimals
|
|
24
|
+
});
|
|
25
|
+
formats.set(decimals, format);
|
|
26
|
+
}
|
|
27
|
+
return format;
|
|
28
|
+
}
|
|
29
|
+
/** A number that counts to each new value on the smooth spring. useCountTo counts whole steps, so decimals are counted in units of the last place. */
|
|
30
|
+
function Count({ value, decimals }) {
|
|
31
|
+
const scale = 10 ** decimals;
|
|
32
|
+
const shown = useCountTo(Math.round(value * scale));
|
|
33
|
+
return /* @__PURE__ */ jsx(Fragment$1, { children: numberFormat(decimals).format(shown / scale) });
|
|
34
|
+
}
|
|
35
|
+
function AlertCircleIcon() {
|
|
36
|
+
return /* @__PURE__ */ jsxs("svg", {
|
|
37
|
+
viewBox: "0 0 24 24",
|
|
38
|
+
fill: "none",
|
|
39
|
+
stroke: "currentColor",
|
|
40
|
+
strokeWidth: "2",
|
|
41
|
+
strokeLinecap: "round",
|
|
42
|
+
strokeLinejoin: "round",
|
|
43
|
+
"aria-hidden": "true",
|
|
44
|
+
className: "size-3.5",
|
|
45
|
+
children: [/* @__PURE__ */ jsx("circle", {
|
|
46
|
+
cx: "12",
|
|
47
|
+
cy: "12",
|
|
48
|
+
r: "10"
|
|
49
|
+
}), /* @__PURE__ */ jsx("path", { d: "M12 8v4M12 16h.01" })]
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function AlertTriangleIcon() {
|
|
53
|
+
return /* @__PURE__ */ jsxs("svg", {
|
|
54
|
+
viewBox: "0 0 24 24",
|
|
55
|
+
fill: "none",
|
|
56
|
+
stroke: "currentColor",
|
|
57
|
+
strokeWidth: "2",
|
|
58
|
+
strokeLinecap: "round",
|
|
59
|
+
strokeLinejoin: "round",
|
|
60
|
+
"aria-hidden": "true",
|
|
61
|
+
className: "size-3.5",
|
|
62
|
+
children: [/* @__PURE__ */ jsx("path", { d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" }), /* @__PURE__ */ jsx("path", { d: "M12 9v4M12 17h.01" })]
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
const UsageMeter = forwardRef(function UsageMeter({ label, segments, limit, unit = "", decimals = 1, freeLabel = "Free", overLabel = "Over limit", warnAt = .9, className, ...props }, ref) {
|
|
66
|
+
const root = useRef(null);
|
|
67
|
+
const refs = useMemo(() => mergeRefs(ref, root), [ref]);
|
|
68
|
+
const bar = useRef(null);
|
|
69
|
+
const track = useRef(null);
|
|
70
|
+
const marker = useRef(null);
|
|
71
|
+
const overflow = useRef(null);
|
|
72
|
+
const legend = useRef(null);
|
|
73
|
+
const titleId = useId();
|
|
74
|
+
const hatchId = `hatch${useSvgId()}`;
|
|
75
|
+
const round = (value) => Math.round(value * 10 ** decimals) / 10 ** decimals;
|
|
76
|
+
const total = round(segments.reduce((sum, segment) => sum + segment.value, 0));
|
|
77
|
+
const free = round(Math.max(0, limit - total));
|
|
78
|
+
const over = round(Math.max(0, total - limit));
|
|
79
|
+
const status = total > limit ? "over" : total >= limit * warnAt ? "near" : "ok";
|
|
80
|
+
const format = (value) => `${numberFormat(decimals).format(value)}${unit ? ` ${unit}` : ""}`;
|
|
81
|
+
const limitText = format(limit).replace(/\.0+(?= |$)/, "");
|
|
82
|
+
const [ready, setReady] = useState(false);
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
const node = root.current;
|
|
85
|
+
if (!node || prefersReducedMotion() || typeof IntersectionObserver === "undefined") {
|
|
86
|
+
setReady(true);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const observer = new IntersectionObserver(([entry]) => {
|
|
90
|
+
if (!entry?.isIntersecting) return;
|
|
91
|
+
observer.disconnect();
|
|
92
|
+
setReady(true);
|
|
93
|
+
}, { threshold: .4 });
|
|
94
|
+
observer.observe(node);
|
|
95
|
+
return () => observer.disconnect();
|
|
96
|
+
}, []);
|
|
97
|
+
const [amounts] = useState(() => /* @__PURE__ */ new Map());
|
|
98
|
+
const shownLimit = useRef(live(limit));
|
|
99
|
+
const width = useRef(0);
|
|
100
|
+
/** One writer: every segment's place and length, the limit marker and the overflow hatch. */
|
|
101
|
+
const paint = useCallback(() => {
|
|
102
|
+
const frame = track.current;
|
|
103
|
+
if (!frame) return;
|
|
104
|
+
const w = width.current;
|
|
105
|
+
const nodes = frame.querySelectorAll("[data-segment]");
|
|
106
|
+
let used = 0;
|
|
107
|
+
for (const node of nodes) used += amounts.get(node.dataset.segment)?.value ?? 0;
|
|
108
|
+
const limitNow = shownLimit.current.value;
|
|
109
|
+
const span = Math.max(limitNow, used, 1e-6);
|
|
110
|
+
let start = 0;
|
|
111
|
+
for (const node of nodes) {
|
|
112
|
+
const amount = amounts.get(node.dataset.segment)?.value ?? 0;
|
|
113
|
+
const x = start / span * w;
|
|
114
|
+
const size = amount / span * w;
|
|
115
|
+
const rest = w - x - size;
|
|
116
|
+
const scale = w ? Math.max(0, size - Math.min(GAP, size, Math.max(0, rest))) / w : 0;
|
|
117
|
+
node.style.transform = `translateX(${x.toFixed(2)}px) scaleX(${scale.toFixed(4)})`;
|
|
118
|
+
start += amount;
|
|
119
|
+
}
|
|
120
|
+
const limitX = Math.round(limitNow / span * w);
|
|
121
|
+
if (marker.current) marker.current.style.translate = `${limitX}px 0`;
|
|
122
|
+
if (overflow.current) overflow.current.style.clipPath = `inset(0 0 0 ${limitX + GAP}px)`;
|
|
123
|
+
if (bar.current) {
|
|
124
|
+
if (used > limitNow) bar.current.setAttribute("data-over", "");
|
|
125
|
+
else bar.current.removeAttribute("data-over");
|
|
126
|
+
}
|
|
127
|
+
}, [amounts]);
|
|
128
|
+
useIsomorphicLayoutEffect(() => {
|
|
129
|
+
const node = track.current;
|
|
130
|
+
if (!node) return;
|
|
131
|
+
const read = () => {
|
|
132
|
+
width.current = node.clientWidth;
|
|
133
|
+
paint();
|
|
134
|
+
};
|
|
135
|
+
read();
|
|
136
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
137
|
+
const observer = new ResizeObserver(read);
|
|
138
|
+
observer.observe(node);
|
|
139
|
+
return () => observer.disconnect();
|
|
140
|
+
}, [paint]);
|
|
141
|
+
useIsomorphicLayoutEffect(paint);
|
|
142
|
+
const values = segments.map((segment) => `${segment.id}:${segment.value}`).join("|");
|
|
143
|
+
const revealed = useRef(false);
|
|
144
|
+
const latest = useRef(segments);
|
|
145
|
+
useIsomorphicLayoutEffect(() => {
|
|
146
|
+
latest.current = segments;
|
|
147
|
+
});
|
|
148
|
+
useEffect(() => {
|
|
149
|
+
if (!ready) return;
|
|
150
|
+
const first = !revealed.current;
|
|
151
|
+
revealed.current = true;
|
|
152
|
+
latest.current.forEach((segment, index) => {
|
|
153
|
+
let amount = amounts.get(segment.id);
|
|
154
|
+
if (!amount) {
|
|
155
|
+
amount = live(0);
|
|
156
|
+
amounts.set(segment.id, amount);
|
|
157
|
+
}
|
|
158
|
+
retarget(amount, segment.value, first ? FILL : SMOOTH, paint, first ? index * STAGGER_MS : 0);
|
|
159
|
+
});
|
|
160
|
+
}, [
|
|
161
|
+
values,
|
|
162
|
+
ready,
|
|
163
|
+
paint,
|
|
164
|
+
amounts
|
|
165
|
+
]);
|
|
166
|
+
useEffect(() => {
|
|
167
|
+
retarget(shownLimit.current, limit, SMOOTH, paint);
|
|
168
|
+
}, [limit, paint]);
|
|
169
|
+
useEffect(() => {
|
|
170
|
+
const bound = shownLimit.current;
|
|
171
|
+
return () => {
|
|
172
|
+
for (const amount of amounts.values()) amount.stop();
|
|
173
|
+
bound.stop();
|
|
174
|
+
};
|
|
175
|
+
}, [amounts]);
|
|
176
|
+
const [hovered, setHovered] = useState(null);
|
|
177
|
+
const [focused, setFocused] = useState(null);
|
|
178
|
+
const [pinned, setPinned] = useState(null);
|
|
179
|
+
const [roving, setRoving] = useState(0);
|
|
180
|
+
const items = [...segments.map((segment) => ({
|
|
181
|
+
id: segment.id,
|
|
182
|
+
label: segment.label,
|
|
183
|
+
value: segment.value
|
|
184
|
+
})), {
|
|
185
|
+
id: "free",
|
|
186
|
+
label: status === "over" ? overLabel : freeLabel,
|
|
187
|
+
value: status === "over" ? over : free
|
|
188
|
+
}];
|
|
189
|
+
const active = hovered ?? focused ?? pinned;
|
|
190
|
+
const activeItem = items.find((item) => item.id === active) ?? null;
|
|
191
|
+
const idAt = (clientX) => {
|
|
192
|
+
const rect = track.current?.getBoundingClientRect();
|
|
193
|
+
if (!rect?.width) return null;
|
|
194
|
+
const at = (clientX - rect.left) / rect.width * Math.max(limit, total);
|
|
195
|
+
if (at > limit) return "free";
|
|
196
|
+
let end = 0;
|
|
197
|
+
for (const segment of segments) {
|
|
198
|
+
end += segment.value;
|
|
199
|
+
if (at <= end) return segment.id;
|
|
200
|
+
}
|
|
201
|
+
return "free";
|
|
202
|
+
};
|
|
203
|
+
const onTrackMove = (event) => {
|
|
204
|
+
if (event.pointerType === "mouse") setHovered(idAt(event.clientX));
|
|
205
|
+
};
|
|
206
|
+
const onTrackTap = (event) => {
|
|
207
|
+
if (event.pointerType === "mouse") return;
|
|
208
|
+
const id = idAt(event.clientX);
|
|
209
|
+
setPinned((current) => current === id ? null : id);
|
|
210
|
+
};
|
|
211
|
+
const onLegendKey = (event) => {
|
|
212
|
+
if (event.key === "Escape") {
|
|
213
|
+
if (pinned || focused) {
|
|
214
|
+
event.preventDefault();
|
|
215
|
+
setPinned(null);
|
|
216
|
+
setFocused(null);
|
|
217
|
+
}
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const next = {
|
|
221
|
+
ArrowRight: roving + 1,
|
|
222
|
+
ArrowDown: roving + 1,
|
|
223
|
+
ArrowLeft: roving - 1,
|
|
224
|
+
ArrowUp: roving - 1,
|
|
225
|
+
Home: 0,
|
|
226
|
+
End: items.length - 1
|
|
227
|
+
}[event.key];
|
|
228
|
+
if (next === void 0) return;
|
|
229
|
+
event.preventDefault();
|
|
230
|
+
const index = (next + items.length) % items.length;
|
|
231
|
+
setRoving(index);
|
|
232
|
+
legend.current?.querySelectorAll("button")[index]?.focus();
|
|
233
|
+
};
|
|
234
|
+
const headline = ready ? activeItem ? activeItem.value : total : 0;
|
|
235
|
+
const share = activeItem ? Math.round(activeItem.value / limit * 100) : 0;
|
|
236
|
+
const caption = !activeItem ? `${unit} of ${limitText} used` : activeItem.id === "free" ? `${unit} ${status === "over" ? "over the limit" : "free"}` : `${unit} in ${activeItem.label} · ${share}% of plan`;
|
|
237
|
+
const badge = status === "over" ? `${format(over)} over` : status === "near" ? "Almost full" : `${format(free)} free`;
|
|
238
|
+
const summary = `${label}: ${format(total)} of ${limitText} used. ${segments.map((segment) => `${segment.label} ${format(segment.value)}`).join(", ")}. ${status === "over" ? `Over the limit by ${format(over)}.` : `${format(free)} free${status === "near" ? ", almost full" : ""}.`}`;
|
|
239
|
+
const notice = status === "over" ? `over:${over}` : status;
|
|
240
|
+
const [announced, setAnnounced] = useState({
|
|
241
|
+
notice,
|
|
242
|
+
message: ""
|
|
243
|
+
});
|
|
244
|
+
if (announced.notice !== notice) setAnnounced({
|
|
245
|
+
notice,
|
|
246
|
+
message: status === "over" ? `Over the plan limit by ${format(over)}.` : status === "near" ? `Almost full. ${format(free)} free.` : `Under the limit. ${format(free)} free.`
|
|
247
|
+
});
|
|
248
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
249
|
+
ref: refs,
|
|
250
|
+
...props,
|
|
251
|
+
role: "group",
|
|
252
|
+
"aria-labelledby": titleId,
|
|
253
|
+
"data-slot": "usage-meter",
|
|
254
|
+
"data-status": status,
|
|
255
|
+
className: cn("usage-meter", className),
|
|
256
|
+
children: [
|
|
257
|
+
/* @__PURE__ */ jsxs("div", {
|
|
258
|
+
className: "flex min-h-7 min-w-0 items-center justify-between gap-3",
|
|
259
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
260
|
+
id: titleId,
|
|
261
|
+
className: "min-w-0 text-base font-medium text-foreground text-balance",
|
|
262
|
+
children: label
|
|
263
|
+
}), /* @__PURE__ */ jsx(Badge, {
|
|
264
|
+
tone: status === "over" ? "danger" : status === "near" ? "warning" : "neutral",
|
|
265
|
+
shape: "pill",
|
|
266
|
+
size: "lg",
|
|
267
|
+
icon: status === "over" ? /* @__PURE__ */ jsx(AlertTriangleIcon, {}) : status === "near" ? /* @__PURE__ */ jsx(AlertCircleIcon, {}) : void 0,
|
|
268
|
+
children: badge
|
|
269
|
+
})]
|
|
270
|
+
}),
|
|
271
|
+
/* @__PURE__ */ jsxs("p", {
|
|
272
|
+
className: "m-0 flex min-w-0 items-baseline gap-2",
|
|
273
|
+
children: [
|
|
274
|
+
/* @__PURE__ */ jsxs("span", {
|
|
275
|
+
className: "sr-only",
|
|
276
|
+
children: [
|
|
277
|
+
format(total),
|
|
278
|
+
" of ",
|
|
279
|
+
limitText,
|
|
280
|
+
" used"
|
|
281
|
+
]
|
|
282
|
+
}),
|
|
283
|
+
/* @__PURE__ */ jsx("span", {
|
|
284
|
+
"data-slot": "usage-meter-amount",
|
|
285
|
+
"aria-hidden": "true",
|
|
286
|
+
className: "flex-none font-display text-h2 font-medium tabular-nums",
|
|
287
|
+
children: /* @__PURE__ */ jsx(Count, {
|
|
288
|
+
value: headline,
|
|
289
|
+
decimals
|
|
290
|
+
})
|
|
291
|
+
}),
|
|
292
|
+
/* @__PURE__ */ jsx("span", {
|
|
293
|
+
"aria-hidden": "true",
|
|
294
|
+
className: "min-w-0 flex-1 text-sm text-text-secondary",
|
|
295
|
+
children: /* @__PURE__ */ jsx(SwapText, {
|
|
296
|
+
swapKey: caption,
|
|
297
|
+
className: "block truncate",
|
|
298
|
+
children: caption
|
|
299
|
+
})
|
|
300
|
+
})
|
|
301
|
+
]
|
|
302
|
+
}),
|
|
303
|
+
/* @__PURE__ */ jsxs("div", {
|
|
304
|
+
ref: bar,
|
|
305
|
+
className: "usage-meter-bar",
|
|
306
|
+
"data-slot": "usage-meter-bar",
|
|
307
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
308
|
+
ref: track,
|
|
309
|
+
role: "img",
|
|
310
|
+
"aria-label": summary,
|
|
311
|
+
"data-active": active ?? void 0,
|
|
312
|
+
onPointerMove: onTrackMove,
|
|
313
|
+
onPointerLeave: () => setHovered(null),
|
|
314
|
+
onPointerUp: onTrackTap,
|
|
315
|
+
className: "usage-meter-track",
|
|
316
|
+
children: [segments.map((segment, index) => /* @__PURE__ */ jsx("span", {
|
|
317
|
+
"data-segment": segment.id,
|
|
318
|
+
"data-index": index,
|
|
319
|
+
"data-highlight": active ? active === segment.id ? "on" : "off" : void 0,
|
|
320
|
+
className: "usage-meter-segment"
|
|
321
|
+
}, segment.id)), /* @__PURE__ */ jsxs("svg", {
|
|
322
|
+
ref: overflow,
|
|
323
|
+
"aria-hidden": "true",
|
|
324
|
+
focusable: "false",
|
|
325
|
+
"data-highlight": active ? active === "free" ? "on" : "off" : void 0,
|
|
326
|
+
className: "usage-meter-overflow",
|
|
327
|
+
children: [
|
|
328
|
+
/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx("pattern", {
|
|
329
|
+
id: hatchId,
|
|
330
|
+
width: 6,
|
|
331
|
+
height: 6,
|
|
332
|
+
patternUnits: "userSpaceOnUse",
|
|
333
|
+
patternTransform: "rotate(45)",
|
|
334
|
+
children: /* @__PURE__ */ jsx("rect", {
|
|
335
|
+
width: 2.5,
|
|
336
|
+
height: 6,
|
|
337
|
+
className: "usage-meter-hatch"
|
|
338
|
+
})
|
|
339
|
+
}) }),
|
|
340
|
+
/* @__PURE__ */ jsx("rect", {
|
|
341
|
+
width: "100%",
|
|
342
|
+
height: "100%",
|
|
343
|
+
className: "usage-meter-over-tint"
|
|
344
|
+
}),
|
|
345
|
+
/* @__PURE__ */ jsx("rect", {
|
|
346
|
+
width: "100%",
|
|
347
|
+
height: "100%",
|
|
348
|
+
fill: `url(#${hatchId})`
|
|
349
|
+
})
|
|
350
|
+
]
|
|
351
|
+
})]
|
|
352
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
353
|
+
ref: marker,
|
|
354
|
+
"aria-hidden": "true",
|
|
355
|
+
className: "usage-meter-limit"
|
|
356
|
+
})]
|
|
357
|
+
}),
|
|
358
|
+
/* @__PURE__ */ jsx("div", {
|
|
359
|
+
ref: legend,
|
|
360
|
+
role: "group",
|
|
361
|
+
"aria-label": `${label} by category`,
|
|
362
|
+
onKeyDown: onLegendKey,
|
|
363
|
+
className: "usage-meter-legend",
|
|
364
|
+
children: items.map((item, index) => {
|
|
365
|
+
const warning = item.id === "free" && status === "over";
|
|
366
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
367
|
+
type: "button",
|
|
368
|
+
"data-slot": "usage-meter-item",
|
|
369
|
+
"data-highlight": active ? active === item.id ? "on" : "off" : void 0,
|
|
370
|
+
"aria-pressed": pinned === item.id,
|
|
371
|
+
"aria-label": `${item.label}, ${format(item.value)}`,
|
|
372
|
+
tabIndex: index === roving ? 0 : -1,
|
|
373
|
+
onClick: () => {
|
|
374
|
+
setRoving(index);
|
|
375
|
+
setPinned((current) => current === item.id ? null : item.id);
|
|
376
|
+
},
|
|
377
|
+
onFocus: (event) => {
|
|
378
|
+
setRoving(index);
|
|
379
|
+
if (event.currentTarget.matches(":focus-visible")) setFocused(item.id);
|
|
380
|
+
},
|
|
381
|
+
onBlur: () => setFocused(null),
|
|
382
|
+
onPointerEnter: (event) => {
|
|
383
|
+
if (event.pointerType === "mouse") setHovered(item.id);
|
|
384
|
+
},
|
|
385
|
+
onPointerLeave: (event) => {
|
|
386
|
+
if (event.pointerType === "mouse") setHovered(null);
|
|
387
|
+
},
|
|
388
|
+
className: "usage-meter-item",
|
|
389
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
390
|
+
className: "flex min-w-0 max-w-full items-center gap-2 text-sm text-text-secondary",
|
|
391
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
392
|
+
"aria-hidden": "true",
|
|
393
|
+
className: "usage-meter-key",
|
|
394
|
+
children: /* @__PURE__ */ jsx(SwapText, {
|
|
395
|
+
swapKey: warning ? "alert" : "swatch",
|
|
396
|
+
motion: "glyph",
|
|
397
|
+
className: "grid place-items-center",
|
|
398
|
+
children: warning ? /* @__PURE__ */ jsx("span", {
|
|
399
|
+
className: "text-destructive-strong",
|
|
400
|
+
children: /* @__PURE__ */ jsx(AlertTriangleIcon, {})
|
|
401
|
+
}) : /* @__PURE__ */ jsx("span", {
|
|
402
|
+
"data-index": item.id === "free" ? "free" : index,
|
|
403
|
+
className: "usage-meter-swatch"
|
|
404
|
+
})
|
|
405
|
+
})
|
|
406
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
407
|
+
className: "min-w-0 flex-1",
|
|
408
|
+
children: /* @__PURE__ */ jsx(SwapText, {
|
|
409
|
+
swapKey: item.label,
|
|
410
|
+
className: "block truncate",
|
|
411
|
+
children: item.label
|
|
412
|
+
})
|
|
413
|
+
})]
|
|
414
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
415
|
+
"aria-hidden": "true",
|
|
416
|
+
className: "flex items-baseline gap-1 ps-5 text-base font-medium text-foreground tabular-nums",
|
|
417
|
+
children: [/* @__PURE__ */ jsx(Count, {
|
|
418
|
+
value: item.value,
|
|
419
|
+
decimals
|
|
420
|
+
}), unit ? /* @__PURE__ */ jsx("span", {
|
|
421
|
+
className: "text-sm font-normal text-text-secondary",
|
|
422
|
+
children: unit
|
|
423
|
+
}) : null]
|
|
424
|
+
})]
|
|
425
|
+
}, item.id);
|
|
426
|
+
})
|
|
427
|
+
}),
|
|
428
|
+
/* @__PURE__ */ jsx("p", {
|
|
429
|
+
className: "sr-only",
|
|
430
|
+
"aria-live": "polite",
|
|
431
|
+
"aria-atomic": "true",
|
|
432
|
+
children: announced.message
|
|
433
|
+
})
|
|
434
|
+
]
|
|
435
|
+
});
|
|
436
|
+
});
|
|
437
|
+
//#endregion
|
|
438
|
+
export { UsageMeter };
|
|
439
|
+
|
|
440
|
+
//# sourceMappingURL=usage-meter.js.map
|