@photon-ai/pho-ui 2.12.0 → 2.13.0

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.
Files changed (41) hide show
  1. package/dist/chunks/badge-DlLJPLeZ.js +78 -0
  2. package/dist/chunks/badge-DlLJPLeZ.js.map +1 -0
  3. package/dist/chunks/bar-chart-BJfr3eyd.js +190 -0
  4. package/dist/chunks/bar-chart-BJfr3eyd.js.map +1 -0
  5. package/dist/chunks/legend-C0mU7dqd.js +60 -0
  6. package/dist/chunks/legend-C0mU7dqd.js.map +1 -0
  7. package/dist/chunks/line-chart-CUR3AxrR.js +453 -0
  8. package/dist/chunks/line-chart-CUR3AxrR.js.map +1 -0
  9. package/dist/chunks/stat-hnNqjWVG.js +85 -0
  10. package/dist/chunks/stat-hnNqjWVG.js.map +1 -0
  11. package/dist/chunks/use-measure-CMVgLouO.js +616 -0
  12. package/dist/chunks/use-measure-CMVgLouO.js.map +1 -0
  13. package/dist/components/badge.js +5 -75
  14. package/dist/components/bar-chart.js +5 -0
  15. package/dist/components/chart.js +8 -0
  16. package/dist/components/line-chart.js +5 -0
  17. package/dist/components/stat.js +6 -0
  18. package/dist/components/toggle.js +14 -14
  19. package/dist/components/toggle.js.map +1 -1
  20. package/dist/index.js +89 -76
  21. package/dist/src/components/bar-chart/bar-chart.d.ts +9 -0
  22. package/dist/src/components/bar-chart/index.d.ts +2 -0
  23. package/dist/src/components/chart/axes.d.ts +22 -0
  24. package/dist/src/components/chart/color.d.ts +10 -0
  25. package/dist/src/components/chart/format.d.ts +25 -0
  26. package/dist/src/components/chart/index.d.ts +8 -0
  27. package/dist/src/components/chart/legend.d.ts +26 -0
  28. package/dist/src/components/chart/scale.d.ts +34 -0
  29. package/dist/src/components/chart/shell.d.ts +42 -0
  30. package/dist/src/components/chart/tooltip.d.ts +30 -0
  31. package/dist/src/components/chart/types.d.ts +76 -0
  32. package/dist/src/components/chart/use-measure.d.ts +8 -0
  33. package/dist/src/components/line-chart/index.d.ts +2 -0
  34. package/dist/src/components/line-chart/line-chart.d.ts +11 -0
  35. package/dist/src/components/stat/index.d.ts +1 -0
  36. package/dist/src/components/stat/stat.d.ts +61 -0
  37. package/dist/src/components/toggle/toggle.d.ts +5 -1
  38. package/dist/src/index.d.ts +4 -0
  39. package/package.json +23 -1
  40. package/src/styles/theme.css +51 -0
  41. package/dist/components/badge.js.map +0 -1
@@ -0,0 +1,26 @@
1
+ import { ComponentProps } from 'react';
2
+ /** A legend or tooltip key mirrors its mark: a square for a bar, a stroke for a line. */
3
+ export type ChartSwatchKind = "rect" | "line";
4
+ export interface ChartSwatchProps {
5
+ /** A CSS colour value — what `chartColor` returns. */
6
+ color: string;
7
+ kind?: ChartSwatchKind;
8
+ dashed?: boolean;
9
+ }
10
+ export declare function ChartSwatch({ color, kind, dashed, }: ChartSwatchProps): import("react").JSX.Element;
11
+ export interface ChartLegendItem {
12
+ label: string;
13
+ /** A CSS colour value — what `chartColor` returns. */
14
+ color: string;
15
+ kind?: ChartSwatchKind;
16
+ dashed?: boolean;
17
+ }
18
+ export interface ChartLegendProps extends Omit<ComponentProps<"ul">, "children"> {
19
+ items: ReadonlyArray<ChartLegendItem>;
20
+ }
21
+ /**
22
+ * The legend a chart of two or more series carries below the plot: a key
23
+ * per series, in series order, wrapping when the row runs out. Text stays
24
+ * in the UI ink; the swatch alone carries the colour.
25
+ */
26
+ export declare function ChartLegend({ items, className, ...props }: ChartLegendProps): import("react").JSX.Element;
@@ -0,0 +1,34 @@
1
+ /** Epoch milliseconds for a datum's `x`. */
2
+ export declare function toTime(x: Date | number): number;
3
+ /** A linear map from `domain` to `range`; a flat domain maps to its start. */
4
+ export declare function linearScale(domain: readonly [number, number], range: readonly [number, number]): (value: number) => number;
5
+ /**
6
+ * A y domain on round numbers with three to five ticks, the last tick on
7
+ * or above the data's high end so the top of the plot is labelled.
8
+ */
9
+ export declare function niceLinearDomain(min: number, max: number): {
10
+ domain: [number, number];
11
+ ticks: number[];
12
+ };
13
+ /** The gap between buckets, in ms — the median step, so one hole doesn't skew it. */
14
+ export declare function bucketSpan(times: ReadonlyArray<number>): number;
15
+ /**
16
+ * Time ticks for a plot `width` px wide: one every ~96px, on the round
17
+ * boundaries d3-time picks (5 minutes, an hour, a day, a month…).
18
+ */
19
+ export declare function timeAxisTicks(start: number, stop: number, width: number): Date[];
20
+ /**
21
+ * Labels a tick by the boundary it sits on: a time within a day, a date at
22
+ * midnight, month and year at a new year. `Intl` does the wording, so the
23
+ * labels follow the locale.
24
+ */
25
+ export declare function timeTickFormatter(locale?: string): (date: Date) => string;
26
+ /**
27
+ * The tooltip's heading for a bucket: the date alone when buckets are a
28
+ * day or longer, date and time below that.
29
+ */
30
+ export declare function bucketTimeFormatter(locale: string | undefined, span: number): (date: Date) => string;
31
+ /** Keep every nth tick so the labels fit the width without touching. */
32
+ export declare function thinTicks<T>(values: ReadonlyArray<T>, labelOf: (value: T) => string, width: number): T[];
33
+ /** The index of the position nearest `px` in an ascending list. */
34
+ export declare function nearestIndex(positions: ReadonlyArray<number>, px: number): number;
@@ -0,0 +1,42 @@
1
+ import { ComponentProps, PointerEvent, ReactNode, RefObject } from 'react';
2
+ /** The inner box the marks draw in, once the axes have taken their bands. */
3
+ export interface ChartLayout {
4
+ width: number;
5
+ height: number;
6
+ left: number;
7
+ top: number;
8
+ right: number;
9
+ bottom: number;
10
+ innerWidth: number;
11
+ innerHeight: number;
12
+ }
13
+ /** The plot's box for a measured width and the y-axis labels it must fit. */
14
+ export declare function chartLayout(width: number, height: number, yLabels: ReadonlyArray<string>): ChartLayout;
15
+ export interface ChartShellProps extends Omit<ComponentProps<"div">, "children"> {
16
+ /** The figure's `aria-label`. */
17
+ label: string;
18
+ /** The figure's spoken summary. */
19
+ description: string;
20
+ height: number;
21
+ /** The measured width; 0 before the browser has measured. */
22
+ width: number;
23
+ containerRef: RefObject<HTMLDivElement | null>;
24
+ legend?: ReactNode;
25
+ tooltip?: ReactNode;
26
+ loading?: boolean;
27
+ empty?: ReactNode;
28
+ /** The frame says `empty` instead of drawing. */
29
+ showEmpty?: boolean;
30
+ /** The pointer over the plot — the chart snaps to the nearest bucket. */
31
+ onPlotPointerMove?: (event: PointerEvent<SVGSVGElement>) => void;
32
+ onPlotPointerLeave?: () => void;
33
+ children?: ReactNode;
34
+ }
35
+ /**
36
+ * The frame every chart draws in: a fixed-height plot box holding the
37
+ * SVG (`role="img"`, labelled and described for assistive tech), the
38
+ * tooltip layer over it, the legend on its own line below. Width comes
39
+ * from the parent; `height` covers the plot and the x-axis band, so a
40
+ * card never grows a nested scroll.
41
+ */
42
+ export declare function ChartShell({ label, description, height, width, containerRef, legend, tooltip, loading, empty, showEmpty, onPlotPointerMove, onPlotPointerLeave, className, children, ...props }: ChartShellProps): import("react").JSX.Element;
@@ -0,0 +1,30 @@
1
+ import { ReactNode } from 'react';
2
+ import { ChartSwatchKind } from './legend';
3
+ import { ChartBreakdownItem } from './types';
4
+ export interface ChartTooltipRow {
5
+ label: string;
6
+ value: string;
7
+ color: string;
8
+ kind?: ChartSwatchKind;
9
+ dashed?: boolean;
10
+ }
11
+ export interface ChartTooltipProps {
12
+ /** The snapped x of the hovered bucket, in container px. */
13
+ x: number;
14
+ /** The container's width — the tooltip flips to the left past the middle. */
15
+ width: number;
16
+ /** Where the tooltip's top sits — the plot's top. */
17
+ top: number;
18
+ heading: string;
19
+ rows: ReadonlyArray<ChartTooltipRow>;
20
+ breakdown?: ReadonlyArray<ChartBreakdownItem>;
21
+ breakdownTitle?: ReactNode;
22
+ valueFormat: (value: number) => string;
23
+ }
24
+ /**
25
+ * The readout for one bucket: its time, then every series' value (values
26
+ * lead, in primary ink; labels follow), then the bucket's breakdown when
27
+ * it carries one. Presentational — the figure's description already
28
+ * speaks the data — and it never takes the pointer.
29
+ */
30
+ export declare function ChartTooltip({ x, width, top, heading, rows, breakdown, breakdownTitle, valueFormat, }: ChartTooltipProps): import("react").JSX.Element;
@@ -0,0 +1,76 @@
1
+ import { ComponentProps, ReactNode } from 'react';
2
+ /**
3
+ * A series colour: the six ordered categorical slots, the two status
4
+ * inks (a series that MEANS successful / failed), or the monochrome ink
5
+ * for a one-series chart on the site's black and white.
6
+ */
7
+ export type ChartColor = "chart-1" | "chart-2" | "chart-3" | "chart-4" | "chart-5" | "chart-6" | "success" | "error" | "ink";
8
+ /** One line of the per-bucket detail a tooltip lists under the series. */
9
+ export interface ChartBreakdownItem {
10
+ label: string;
11
+ value: number;
12
+ }
13
+ /**
14
+ * One bucket on the time axis. Series values sit beside `x` under the
15
+ * keys the `series` name — `{ x, inbound: 12, outbound: 4 }`.
16
+ */
17
+ export interface ChartDatum {
18
+ /** When the bucket starts: a `Date` or epoch milliseconds. */
19
+ x: Date | number;
20
+ /**
21
+ * Detail for this bucket — errors by route, say — listed in the tooltip
22
+ * under the series rows.
23
+ */
24
+ breakdown?: ReadonlyArray<ChartBreakdownItem>;
25
+ }
26
+ /** The keys of `T` that hold a number (or nothing yet). */
27
+ export type ChartValueKey<T> = {
28
+ [K in keyof T]-?: K extends "x" | "breakdown" ? never : T[K] extends number | null | undefined ? K : never;
29
+ }[keyof T] & string;
30
+ export interface ChartSeries<T extends ChartDatum = ChartDatum> {
31
+ /** The field of each datum this series plots. */
32
+ key: ChartValueKey<T>;
33
+ /** The name in the legend and the tooltip. */
34
+ label: string;
35
+ /** Defaults to the next categorical slot, in series order. */
36
+ color?: ChartColor;
37
+ /** Line charts: a dashed stroke — a min / max beside an average. */
38
+ dashed?: boolean;
39
+ }
40
+ /** What every chart takes, on top of the div it renders. */
41
+ export interface ChartBaseProps<T extends ChartDatum> extends Omit<ComponentProps<"div">, "children"> {
42
+ /** The buckets, oldest first, evenly spaced in time. */
43
+ data: ReadonlyArray<T>;
44
+ /**
45
+ * One entry per series, in drawing (and legend) order. `T` comes from
46
+ * `data`; the keys here are checked against it.
47
+ */
48
+ series: ReadonlyArray<ChartSeries<NoInfer<T>>>;
49
+ /** Names the chart for assistive tech — the figure's `aria-label`. */
50
+ label: string;
51
+ /**
52
+ * What the figure says about itself to assistive tech. Generated from
53
+ * the series (range, totals or extremes, peaks) unless given.
54
+ */
55
+ description?: string;
56
+ /** Plot plus x-axis band, in px; the legend adds its own line below. @default 200 */
57
+ height?: number;
58
+ /** A fixed width instead of measuring the parent — SSR renders, tests. */
59
+ width?: number;
60
+ /** Formats a value on the y-axis and in the tooltip. */
61
+ valueFormat?: (value: number) => string;
62
+ /** Formats a bucket's time in the tooltip heading and the description. */
63
+ timeFormat?: (date: Date) => string;
64
+ /** Locale for the default number and time formats. Runtime locale by default. */
65
+ locale?: string;
66
+ /** Show the legend. On for two or more series; a lone series is named by its title. */
67
+ legend?: boolean;
68
+ /** Where the y-axis starts: at zero, or at the data's low end. @default "zero" */
69
+ baseline?: "zero" | "auto";
70
+ /** A heading over a datum's `breakdown` list in the tooltip. */
71
+ breakdownTitle?: ReactNode;
72
+ /** Data is on its way: the chart holds its last render at half opacity. */
73
+ loading?: boolean;
74
+ /** What the frame says with no buckets to draw. */
75
+ empty?: ReactNode;
76
+ }
@@ -0,0 +1,8 @@
1
+ import { RefObject } from 'react';
2
+ /**
3
+ * The width of the element the ref lands on, kept current through a
4
+ * ResizeObserver. Nothing runs on the server: the first render is 0 wide
5
+ * and the plot fills in once the browser has measured. A `fixed` width
6
+ * skips measuring altogether.
7
+ */
8
+ export declare function useMeasuredWidth<T extends HTMLElement>(fixed?: number): [RefObject<T | null>, number];
@@ -0,0 +1,2 @@
1
+ export { LineChart, type LineChartProps } from './line-chart';
2
+ export type { ChartBreakdownItem, ChartColor, ChartDatum, ChartSeries, ChartValueKey, } from '../chart/types';
@@ -0,0 +1,11 @@
1
+ import { ChartBaseProps, ChartDatum } from '../chart/types';
2
+ export interface LineChartProps<T extends ChartDatum> extends ChartBaseProps<T> {
3
+ /** Straight segments, or a monotone curve that never overshoots a sample. @default "linear" */
4
+ curve?: "linear" | "monotone";
5
+ /** A soft fill under the first series (a lone one, as a rule). */
6
+ area?: boolean;
7
+ }
8
+ export declare function LineChart<T extends ChartDatum>({ data, series, label, description, height, width: fixedWidth, valueFormat, timeFormat, locale, legend, baseline, curve, area, breakdownTitle, loading, empty, className, ...props }: LineChartProps<T>): import("react").JSX.Element;
9
+ export declare namespace LineChart {
10
+ var displayName: string;
11
+ }
@@ -0,0 +1 @@
1
+ export { Stat, StatGroup, type StatDirection, type StatGroupProps, type StatProps, } from './stat';
@@ -0,0 +1,61 @@
1
+ import { ComponentProps, ReactNode } from 'react';
2
+ /**
3
+ * Stat — a figure with its name: a label, a value, and, when it
4
+ * matters, how the value moved and against what.
5
+ *
6
+ * Design notes:
7
+ * - The value is the loud element: display size, medium weight, the
8
+ * font's proportional figures (tabular digits make a big `121` look
9
+ * loose). Label and note stay in the UI inks; nothing else competes.
10
+ * - The delta is a low-contrast `Badge` beside the label, with an arrow so
11
+ * the direction never rides on colour alone. Its ink says whether the
12
+ * move is good news: `deltaGood` names the good direction, because a
13
+ * drop in bounce rate is green and a drop in visitors is red.
14
+ * - A tile has no chrome of its own. Alone, it sits in whatever card holds
15
+ * it; in a `Stat.Group` the tiles share one frame and hairlines.
16
+ */
17
+ export type StatDirection = "up" | "down" | "flat";
18
+ export interface StatProps extends Omit<ComponentProps<"div">, "children"> {
19
+ /** What the figure counts — "Visitors", "Active chats". */
20
+ label: ReactNode;
21
+ /** The figure, already formatted — "20.8K", "8m 32s", "1:1". */
22
+ value: ReactNode;
23
+ /**
24
+ * How the value moved: a number is a percentage change and renders
25
+ * signed with one decimal ("−42.0%"); a string renders as written and
26
+ * its sign sets the direction.
27
+ */
28
+ delta?: number | string;
29
+ /** The delta's direction, when the string can't say. Derived otherwise. */
30
+ deltaDirection?: StatDirection;
31
+ /**
32
+ * Which direction is good news: `up` (visitors, revenue), `down` (bounce
33
+ * rate, latency), `none` when a move is just information. @default "up"
34
+ */
35
+ deltaGood?: "up" | "down" | "none";
36
+ /** What the delta is against — "vs. 36.2K prior". */
37
+ note?: ReactNode;
38
+ }
39
+ export declare function Stat({ label, value, delta, deltaDirection, deltaGood, note, className, ...props }: StatProps): import("react").JSX.Element;
40
+ export declare namespace Stat {
41
+ var displayName: string;
42
+ var Group: typeof StatGroup;
43
+ }
44
+ export interface StatGroupProps extends ComponentProps<"div"> {
45
+ /**
46
+ * `card` frames the row as a card of its own; `plain` keeps only the
47
+ * hairlines between tiles, for a row inside a card or under a chart.
48
+ * @default "card"
49
+ */
50
+ variant?: "card" | "plain";
51
+ }
52
+ /**
53
+ * A row of tiles that reads as one card: hairlines between tiles, and
54
+ * between rows once the tiles wrap on a narrow width. Each tile takes an
55
+ * equal share of its row, never narrower than 12rem; a tile that wraps
56
+ * alone stretches across its line, so the frame never shows a hole.
57
+ */
58
+ export declare function StatGroup({ variant, className, ...props }: StatGroupProps): import("react").JSX.Element;
59
+ export declare namespace StatGroup {
60
+ var displayName: string;
61
+ }
@@ -1,6 +1,10 @@
1
1
  import { ComponentProps } from 'react';
2
2
  import { Toggle as BaseToggle } from '@base-ui/react/toggle';
3
- /** Size axis — three square icon sizes. `md` is default. */
3
+ /**
4
+ * Size axis — three heights. An icon-only toggle is square (the padding
5
+ * plus the icon add up to the height); one carrying a short label — a
6
+ * range picker's `7d` — grows to fit it. `md` is default.
7
+ */
4
8
  export declare const TOGGLE_SIZES: {
5
9
  sm: {
6
10
  classes: string;
@@ -16,3 +16,7 @@ export * from './components/status-view';
16
16
  export * from './components/error';
17
17
  export * from './sections/sidebar';
18
18
  export * from './sections/basic-page';
19
+ export * from './components/chart';
20
+ export * from './components/stat';
21
+ export * from './components/bar-chart';
22
+ export * from './components/line-chart';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@photon-ai/pho-ui",
3
- "version": "2.12.0",
3
+ "version": "2.13.0",
4
4
  "description": "Pho Design System — Photon's React component library, built on Base UI",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -407,6 +407,22 @@
407
407
  "./primitives/tooltip": {
408
408
  "types": "./dist/src/primitives/tooltip.d.ts",
409
409
  "import": "./dist/primitives/tooltip.js"
410
+ },
411
+ "./components/chart": {
412
+ "types": "./dist/src/components/chart/index.d.ts",
413
+ "import": "./dist/components/chart.js"
414
+ },
415
+ "./components/stat": {
416
+ "types": "./dist/src/components/stat/index.d.ts",
417
+ "import": "./dist/components/stat.js"
418
+ },
419
+ "./components/bar-chart": {
420
+ "types": "./dist/src/components/bar-chart/index.d.ts",
421
+ "import": "./dist/components/bar-chart.js"
422
+ },
423
+ "./components/line-chart": {
424
+ "types": "./dist/src/components/line-chart/index.d.ts",
425
+ "import": "./dist/components/line-chart.js"
410
426
  }
411
427
  },
412
428
  "peerDependencies": {
@@ -420,6 +436,9 @@
420
436
  "@testing-library/jest-dom": "^7.0.1",
421
437
  "@testing-library/react": "^16.3.2",
422
438
  "@testing-library/user-event": "^14.6.5",
439
+ "@types/d3-array": "3.2.2",
440
+ "@types/d3-shape": "3.2.0",
441
+ "@types/d3-time": "3.0.4",
423
442
  "@types/node": "^26.2.0",
424
443
  "@types/react": "^19.2.18",
425
444
  "@types/react-dom": "^19.2.4",
@@ -442,6 +461,9 @@
442
461
  "clsx": "^2.1.1",
443
462
  "cmdk": "^1.1.1",
444
463
  "country-flag-icons": "^1.6.20",
464
+ "d3-array": "3.2.4",
465
+ "d3-shape": "3.2.0",
466
+ "d3-time": "3.1.0",
445
467
  "libphonenumber-js": "^1.13.11",
446
468
  "motion": "^13.1.0",
447
469
  "tailwind-merge": "^3.6.0"
@@ -436,4 +436,55 @@
436
436
  opacity: 0.45;
437
437
  }
438
438
  }
439
+
440
+ /* ============================== Charts ============================== */
441
+ /* charts — series colours for BarChart / LineChart. An ORDERED set:
442
+ assign slot 1, 2, 3… in series order and never cycle past 6 (fold the
443
+ tail into "Other"). Validated with the dataviz six checks in both modes
444
+ against the card (#111) and page (#000 / #fff) surfaces: lightness band,
445
+ chroma floor, adjacent-pair CVD ΔE ≥ 8, normal-vision ΔE ≥ 15, 3:1 on
446
+ the surface. Slots 1–2 are the site's sky and orange (orange stepped
447
+ from L 0.70 to 0.66 so a bar clears 3:1 on white); 3–6 are purple,
448
+ olive, magenta, indigo. Dark values are the same hues re-stepped into
449
+ the dark band (L 0.48–0.67), not a flip. */
450
+ --color-pho-chart-1: light-dark(
451
+ oklch(0.6 0.101 239.2),
452
+ oklch(0.66 0.11 239.2)
453
+ );
454
+ --color-pho-chart-2: oklch(0.66 0.15 57.8);
455
+ --color-pho-chart-3: light-dark(oklch(0.62 0.2 307.5), oklch(0.66 0.2 307.5));
456
+ --color-pho-chart-4: oklch(0.66 0.14 106);
457
+ --color-pho-chart-5: light-dark(oklch(0.62 0.2 350), oklch(0.65 0.2 350));
458
+ --color-pho-chart-6: oklch(0.639 0.146 269.6);
459
+ /* Status series — successful vs failed. The state text inks, so a failed
460
+ segment is the same red as a Failed badge. Reserved: a series wears
461
+ these only when it MEANS good / bad, never as "series 4". */
462
+ --color-pho-chart-success: light-dark(
463
+ oklch(0.549 0.136 149.3),
464
+ oklch(0.735 0.162 150.5)
465
+ );
466
+ --color-pho-chart-error: light-dark(
467
+ oklch(0.565 0.212 20.2),
468
+ oklch(0.696 0.198 14.9)
469
+ );
470
+ /* The monochrome series — a one-series chart on the site's black/white. */
471
+ --color-pho-chart-ink: light-dark(oklch(0 0 0 / 0.8), oklch(1 0 0 / 0.8));
472
+ /* Chart furniture: gridlines one step off the surface, the crosshair and
473
+ baseline a touch stronger. Hairlines, solid, recessive. */
474
+ --color-pho-chart-grid: light-dark(oklch(0 0 0 / 0.06), oklch(1 0 0 / 0.08));
475
+ --color-pho-chart-crosshair: light-dark(
476
+ oklch(0 0 0 / 0.2),
477
+ oklch(1 0 0 / 0.25)
478
+ );
479
+
480
+ /* ==================== Animation: chart mount ==================== */
481
+ /* A chart surfaces once on mount — opacity only, nothing moves. The
482
+ global reduced-motion guard in pho.css collapses it. */
483
+ --animate-pho-chart-in: pho-chart-in 300ms ease-out both;
484
+
485
+ @keyframes pho-chart-in {
486
+ from {
487
+ opacity: 0;
488
+ }
489
+ }
439
490
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"badge.js","names":[],"sources":["../../src/components/badge/badge.tsx"],"sourcesContent":["import { type ComponentProps } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { resolveVariant, type VariantMap } from \"../../utils/resolve-variant\";\n\n/**\n * Design notes (Geist-aligned):\n * - ONE color axis, a tight semantic set (`gray` / `muted` / `brand` /\n * `success` / `warning` / `error`). Untitled UI's `type` triplication\n * (modern / pill / badge-color) is dropped.\n * - TWO contrasts (`high` / `low`, default `high`). High is the solid state\n * fill — the site's \"Most Popular\" pill — pairing each color's ink with\n * flipping on-ink content. Low is the soft accent material: fill and\n * hairline ring derive from `currentColor` at low opacity, so every color\n * shares one recipe.\n * - FOUR sizes (`xs` / `sm` / `md` / `lg`, default `md`). A badge is inherently\n * small; `lg` is the one step up, for a pill seated beside a page title.\n * - The richer Untitled features (dot, icon, avatar, count, dismiss button) are\n * deliberately left out — a dismissible or selectable chip is a Tag, not a\n * Badge, and a badge that triggers an action is a Button.\n */\n\nconst BADGE_BASE =\n \"inline-flex shrink-0 items-center whitespace-nowrap rounded-full font-medium\";\n\n/** Low-contrast material — tint and hairline derived from the color's text token. */\nconst LOW_MATERIAL = \"bg-current/10 ring-1 ring-current/20 ring-inset\";\n\n/**\n * Size axis — fixed heights on a 4px step (16 / 20 / 24 / 28) so the ladder\n * stays proportional. Horizontal padding and type scale with each step;\n * vertical centering comes from `items-center` on the base (no `py`).\n */\nexport const BADGE_SIZES = {\n xs: {\n classes: \"h-4 gap-0.5 px-1.5 text-[10px] leading-none\",\n description: \"Extra small — 16px; inline next to 14px labels (plan chips)\",\n },\n sm: {\n classes: \"h-5 gap-0.5 px-2 text-xs leading-none\",\n description: \"Small — 20px; dense rows, table cells\",\n },\n md: {\n classes: \"h-6 gap-1 px-2.5 text-xs leading-none\",\n description: \"Medium — 24px; the default\",\n },\n lg: {\n classes: \"h-7 gap-1 px-3 text-base leading-none\",\n description: \"Large — 28px; beside a page title (BasicPage.Title trailing)\",\n },\n} satisfies VariantMap;\n\n/**\n * Color axis — a closed semantic set with one recipe per contrast. `high`\n * fills with the state ink (text flips with it per mode); `low` tints text\n * only and lets `LOW_MATERIAL` derive fill and ring from it.\n */\nexport const BADGE_COLORS = {\n gray: {\n high: \"bg-pho-inverse text-pho-inverse\",\n low: \"text-pho-secondary\",\n description: \"Neutral — the low-signal default (solid inverse)\",\n },\n muted: {\n high: \"bg-pho-secondary text-pho-primary\",\n low: \"text-pho-description\",\n description: \"Muted — soft solid surface for quieter labels (Free, Shared)\",\n },\n brand: {\n high: \"bg-pho-brand-solid text-pho-on-brand\",\n low: \"text-pho-brand\",\n description: \"Brand — plan tiers, feature flags, highlights\",\n },\n success: {\n high: \"bg-pho-success-ink text-pho-on-brand\",\n low: \"text-pho-success\",\n description: \"Success — healthy, live, complete\",\n },\n warning: {\n high: \"bg-pho-warning-ink text-pho-on-brand\",\n low: \"text-pho-warning\",\n description: \"Warning — needs attention, degraded\",\n },\n error: {\n high: \"bg-pho-error-ink text-pho-on-brand\",\n low: \"text-pho-error\",\n description: \"Error — failed, blocked, over limit\",\n },\n} satisfies Record<string, { high: string; low: string; description: string }>;\n\nexport type BadgeSize = keyof typeof BADGE_SIZES;\nexport type BadgeColor = keyof typeof BADGE_COLORS;\nexport type BadgeContrast = \"high\" | \"low\";\n\n/** Compose the full class string for a badge — exported for reuse/testing. */\nexport function badgeVariants(opts?: {\n color?: BadgeColor;\n contrast?: BadgeContrast;\n size?: BadgeSize;\n className?: string;\n}): string {\n const {\n color = \"gray\",\n contrast = \"high\",\n size = \"md\",\n className,\n } = opts ?? {};\n const def = BADGE_COLORS[color] as\n (typeof BADGE_COLORS)[BadgeColor] | undefined;\n return cn(\n BADGE_BASE,\n resolveVariant(BADGE_SIZES, size),\n contrast === \"low\" ? cn(LOW_MATERIAL, def?.low) : def?.high,\n className,\n );\n}\n\ninterface BadgeBaseProps {\n /** Semantic color. @default \"gray\" */\n color?: BadgeColor;\n /** Solid state fill (`high`) or the soft currentColor tint (`low`). @default \"high\" */\n contrast?: BadgeContrast;\n /** Size variant. @default \"md\" */\n size?: BadgeSize;\n}\n\nexport interface BadgeProps\n extends BadgeBaseProps, Omit<ComponentProps<\"span\">, keyof BadgeBaseProps> {}\n\n/**\n * Badge — a small, non-interactive status label: a plan tier (`Pro`), a state\n * (`Live`, `Failed`), a count. It's a plain `<span>`, so it never takes focus or\n * handles clicks — for a dismissible or selectable chip reach for a `Tag`, and\n * for a standalone action use a `Button`. Let `color` carry the meaning and keep\n * the label to a word or two.\n */\nexport function Badge({\n color,\n contrast,\n size,\n className,\n children,\n ...props\n}: BadgeProps) {\n return (\n <span\n className={cn(badgeVariants({ color, contrast, size }), className)}\n {...props}\n >\n {children}\n </span>\n );\n}\n\nBadge.displayName = \"Badge\";\n"],"mappings":";;;;;AAqBA,IAAM,IACJ,gFAGI,IAAe,mDAOR,IAAc;AAAA,EACzB,IAAI;AAAA,IACF,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,IAAI;AAAA,IACF,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,IAAI;AAAA,IACF,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,IAAI;AAAA,IACF,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AACF,GAOa,IAAe;AAAA,EAC1B,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,EACf;AAAA,EACA,OAAO;AAAA,IACL,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,EACf;AAAA,EACA,OAAO;AAAA,IACL,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,EACf;AAAA,EACA,SAAS;AAAA,IACP,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,EACf;AAAA,EACA,SAAS;AAAA,IACP,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,EACf;AAAA,EACA,OAAO;AAAA,IACL,MAAM;AAAA,IACN,KAAK;AAAA,IACL,aAAa;AAAA,EACf;AACF;AAOA,SAAgB,EAAc,GAKnB;AACT,QAAM,EACJ,OAAA,IAAQ,QACR,UAAA,IAAW,QACX,MAAA,IAAO,MACP,WAAA,EAAA,IACE,KAAQ,CAAC,GACP,IAAM,EAAa,CAAA;AAEzB,SAAO,EACL,GACA,EAAe,GAAa,CAAI,GAChC,MAAa,QAAQ,EAAG,GAAc,GAAK,GAAG,IAAI,GAAK,MACvD,CACF;AACF;AAqBA,SAAgB,EAAM,EACpB,OAAA,GACA,UAAA,GACA,MAAA,GACA,WAAA,GACA,UAAA,GACA,GAAG,EAAA,GACU;AACb,SACE,gBAAA,EAAC,QAAD;AAAA,IACE,WAAW,EAAG,EAAc;AAAA,MAAE,OAAA;AAAA,MAAO,UAAA;AAAA,MAAU,MAAA;AAAA,IAAK,CAAC,GAAG,CAAS;AAAA,IACjE,GAAI;AAAA,IAEH,UAAA;AAAA,EACG,CAAA;AAEV;AAEA,EAAM,cAAc"}