@viraui/react 2026.10.0 → 2026.11.0-next.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 (127) hide show
  1. package/dist/_virtual/_rolldown/runtime.js +8 -0
  2. package/dist/catalog.json +12 -0
  3. package/dist/components/avatar/avatar.d.ts +6 -0
  4. package/dist/components/avatar/avatar.js +2 -2
  5. package/dist/components/badge/badge.css +1 -1
  6. package/dist/components/button/button.css +1 -1
  7. package/dist/components/dialog/dialog-sheet.d.ts +1 -1
  8. package/dist/components/gauge/arcs.d.ts +134 -0
  9. package/dist/components/gauge/arcs.js +145 -0
  10. package/dist/components/gauge/context.d.ts +134 -0
  11. package/dist/components/gauge/context.js +83 -0
  12. package/dist/components/gauge/control.d.ts +107 -0
  13. package/dist/components/gauge/control.js +223 -0
  14. package/dist/components/gauge/demos/_shared.d.ts +28 -0
  15. package/dist/components/gauge/demos/activity-ring.d.ts +9 -0
  16. package/dist/components/gauge/demos/air-quality.d.ts +9 -0
  17. package/dist/components/gauge/demos/analog-dial.d.ts +9 -0
  18. package/dist/components/gauge/demos/battery.d.ts +9 -0
  19. package/dist/components/gauge/demos/chronograph.d.ts +9 -0
  20. package/dist/components/gauge/demos/clock.d.ts +9 -0
  21. package/dist/components/gauge/demos/compass.d.ts +9 -0
  22. package/dist/components/gauge/demos/data-usage.d.ts +9 -0
  23. package/dist/components/gauge/demos/dimmer.d.ts +9 -0
  24. package/dist/components/gauge/demos/fuel.d.ts +9 -0
  25. package/dist/components/gauge/demos/heart-rate.d.ts +9 -0
  26. package/dist/components/gauge/demos/internet-speed.d.ts +9 -0
  27. package/dist/components/gauge/demos/nested-rings.d.ts +11 -0
  28. package/dist/components/gauge/demos/orrery.d.ts +12 -0
  29. package/dist/components/gauge/demos/power-meter.d.ts +9 -0
  30. package/dist/components/gauge/demos/progress-ring.d.ts +9 -0
  31. package/dist/components/gauge/demos/range.d.ts +10 -0
  32. package/dist/components/gauge/demos/saas-metric.d.ts +9 -0
  33. package/dist/components/gauge/demos/segments.d.ts +9 -0
  34. package/dist/components/gauge/demos/simple.d.ts +9 -0
  35. package/dist/components/gauge/demos/speed-fuel.d.ts +10 -0
  36. package/dist/components/gauge/demos/speedometer.d.ts +9 -0
  37. package/dist/components/gauge/demos/sprint.d.ts +9 -0
  38. package/dist/components/gauge/demos/sun-path.d.ts +9 -0
  39. package/dist/components/gauge/demos/system.d.ts +11 -0
  40. package/dist/components/gauge/demos/tachometer.d.ts +9 -0
  41. package/dist/components/gauge/demos/temperature.d.ts +9 -0
  42. package/dist/components/gauge/demos/thermostat.d.ts +9 -0
  43. package/dist/components/gauge/demos/timer.d.ts +9 -0
  44. package/dist/components/gauge/demos/volume.d.ts +10 -0
  45. package/dist/components/gauge/demos/wind.d.ts +9 -0
  46. package/dist/components/gauge/dot.d.ts +55 -0
  47. package/dist/components/gauge/dot.js +34 -0
  48. package/dist/components/gauge/gauge.css +1 -0
  49. package/dist/components/gauge/gauge.d.ts +84 -0
  50. package/dist/components/gauge/gauge.js +62 -0
  51. package/dist/components/gauge/gauge.module.js +24 -0
  52. package/dist/components/gauge/index.d.ts +15 -0
  53. package/dist/components/gauge/inset.d.ts +30 -0
  54. package/dist/components/gauge/inset.js +39 -0
  55. package/dist/components/gauge/math.d.ts +69 -0
  56. package/dist/components/gauge/math.js +135 -0
  57. package/dist/components/gauge/needle.d.ts +81 -0
  58. package/dist/components/gauge/needle.js +162 -0
  59. package/dist/components/gauge/scale.d.ts +44 -0
  60. package/dist/components/gauge/scale.js +21 -0
  61. package/dist/components/gauge/text.d.ts +79 -0
  62. package/dist/components/gauge/text.js +42 -0
  63. package/dist/components/gauge/ticks.d.ts +169 -0
  64. package/dist/components/gauge/ticks.js +124 -0
  65. package/dist/components/gauge/tooltip.d.ts +117 -0
  66. package/dist/components/gauge/tooltip.js +337 -0
  67. package/dist/components/gauge/transition.d.ts +60 -0
  68. package/dist/components/gauge/transition.js +129 -0
  69. package/dist/components/gauge/use-animated-value.d.ts +14 -0
  70. package/dist/components/gauge/use-animated-value.js +111 -0
  71. package/dist/components/gauge/utils.d.ts +51 -0
  72. package/dist/components/gauge/utils.js +80 -0
  73. package/dist/components/icon-meter/icon-meter.css +1 -0
  74. package/dist/components/icon-meter/icon-meter.d.ts +62 -0
  75. package/dist/components/icon-meter/icon-meter.js +105 -0
  76. package/dist/components/icon-meter/icon-meter.module.js +11 -0
  77. package/dist/components/icon-meter/index.d.ts +2 -0
  78. package/dist/components/icon-meter/utils.d.ts +22 -0
  79. package/dist/components/icon-meter/utils.js +40 -0
  80. package/dist/components/index.d.ts +2 -0
  81. package/dist/components/popover/popover-sheet.d.ts +1 -2
  82. package/dist/components/stack/stack.d.ts +9 -2
  83. package/dist/components/tabs/tabs.d.ts +1 -2
  84. package/dist/index.js +17 -1
  85. package/dist/internal/chrome-icons/chrome-icons.d.ts +4 -0
  86. package/dist/internal/chrome-icons/chrome-icons.js +12 -4
  87. package/dist/node_modules/.pnpm/react-dom@19.3.0_react@19.3.0/node_modules/react-dom/cjs/react-dom.development.js +192 -0
  88. package/dist/node_modules/.pnpm/react-dom@19.3.0_react@19.3.0/node_modules/react-dom/cjs/react-dom.production.js +156 -0
  89. package/dist/node_modules/.pnpm/react-dom@19.3.0_react@19.3.0/node_modules/react-dom/index.js +22 -0
  90. package/dist/preflight.css +1 -1
  91. package/dist/specs/components/avatar/props.xml +3 -0
  92. package/dist/specs/components/avatar/usage.xml +1 -0
  93. package/dist/specs/components/badge/a11y.xml +1 -1
  94. package/dist/specs/components/button/props.xml +2 -2
  95. package/dist/specs/components/button/usage.xml +1 -1
  96. package/dist/specs/components/gauge/a11y.xml +8 -0
  97. package/dist/specs/components/gauge/meta.xml +38 -0
  98. package/dist/specs/components/gauge/patterns.xml +1158 -0
  99. package/dist/specs/components/gauge/props.xml +452 -0
  100. package/dist/specs/components/gauge/usage.xml +21 -0
  101. package/dist/specs/components/icon-button/props.xml +2 -2
  102. package/dist/specs/components/icon-button/usage.xml +1 -1
  103. package/dist/specs/components/icon-meter/a11y.xml +9 -0
  104. package/dist/specs/components/icon-meter/meta.xml +21 -0
  105. package/dist/specs/components/icon-meter/patterns.xml +23 -0
  106. package/dist/specs/components/icon-meter/props.xml +44 -0
  107. package/dist/specs/components/icon-meter/usage.xml +13 -0
  108. package/dist/specs/components/linear-progress/usage.xml +1 -0
  109. package/dist/specs/components/meter/meta.xml +1 -0
  110. package/dist/specs/components/meter/props.xml +1 -0
  111. package/dist/specs/components/meter/usage.xml +1 -0
  112. package/dist/specs/components/stack/patterns.xml +21 -21
  113. package/dist/specs/components/stack/props.xml +6 -6
  114. package/dist/specs/components/stack/usage.xml +4 -2
  115. package/dist/specs/components/text/patterns.xml +1 -1
  116. package/dist/specs/components/text/usage.xml +1 -1
  117. package/dist/specs/components/toggle-button/props.xml +1 -1
  118. package/dist/specs/components/toggle-button/usage.xml +1 -1
  119. package/dist/specs/components/toggle-text-button/props.xml +1 -1
  120. package/dist/specs/foundation/attributes.xml +3 -3
  121. package/dist/specs/foundation/modules.xml +1 -1
  122. package/dist/specs/foundation/theme-topics.xml +7 -2
  123. package/dist/specs/index.xml +21 -1
  124. package/dist/specs/patterns/compose-layout-first.xml +1 -1
  125. package/dist/specs/patterns/settings-panel.xml +1 -1
  126. package/dist/specs/patterns/settings-row.xml +1 -1
  127. package/package.json +2 -2
@@ -0,0 +1,162 @@
1
+ "use client";
2
+ import { polar, valueToTurnedAngle } from "./math.js";
3
+ import { useRoundGauge } from "./context.js";
4
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
+ import * as React from "react";
6
+ //#region src/components/gauge/needle.tsx
7
+ var PRIMARY = "var(--global-primary)";
8
+ var FOREGROUND = "var(--global-foreground)";
9
+ var pts = (points) => points.map((p) => `${p.x},${p.y}`).join(" ");
10
+ var NeedleShape = ({ kind = "line", length = .85, width = 6, color = PRIMARY, tail = 30, tailColor = color, tailDot = 0, turns = 1, end = "to" }) => {
11
+ const gauge = useRoundGauge("GaugeNeedle");
12
+ const { radius, min, max, startAngle, endAngle } = gauge;
13
+ const value = end === "from" ? gauge.from : gauge.value;
14
+ const angle = valueToTurnedAngle(value, min, max, startAngle, endAngle, turns);
15
+ const tip = polar(radius * length, angle);
16
+ const back = polar(-tail, angle);
17
+ const endDot = tail > 0 && tailDot > 0 ? /* @__PURE__ */ jsx("circle", {
18
+ cx: back.x,
19
+ cy: back.y,
20
+ r: tailDot,
21
+ fill: tailColor
22
+ }) : null;
23
+ if (kind === "arrow") {
24
+ const reach = radius * length;
25
+ const headLength = Math.min(width * 4, reach);
26
+ const base = polar(reach - headLength, angle);
27
+ const shaftLeft = polar(width / 2, angle - 90);
28
+ const shaftRight = polar(width / 2, angle + 90);
29
+ const headLeft = polar(width * 2, angle - 90);
30
+ const headRight = polar(width * 2, angle + 90);
31
+ const offset = (p, d) => ({
32
+ x: p.x + d.x,
33
+ y: p.y + d.y
34
+ });
35
+ return /* @__PURE__ */ jsxs("g", { children: [
36
+ tail > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("polygon", {
37
+ points: pts([
38
+ shaftLeft,
39
+ shaftRight,
40
+ offset(back, shaftRight),
41
+ offset(back, shaftLeft)
42
+ ]),
43
+ fill: tailColor
44
+ }), /* @__PURE__ */ jsx("circle", {
45
+ cx: back.x,
46
+ cy: back.y,
47
+ r: width / 2,
48
+ fill: tailColor
49
+ })] }),
50
+ /* @__PURE__ */ jsx("polygon", {
51
+ points: pts([
52
+ shaftLeft,
53
+ offset(base, shaftLeft),
54
+ offset(base, headLeft),
55
+ tip,
56
+ offset(base, headRight),
57
+ offset(base, shaftRight),
58
+ shaftRight
59
+ ]),
60
+ fill: color
61
+ }),
62
+ endDot
63
+ ] });
64
+ }
65
+ if (kind === "line") return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("line", {
66
+ x1: back.x,
67
+ y1: back.y,
68
+ x2: tip.x,
69
+ y2: tip.y,
70
+ stroke: color,
71
+ strokeWidth: width,
72
+ strokeLinecap: "round"
73
+ }), endDot] });
74
+ const left = polar(width / 2, angle - 90);
75
+ const right = polar(width / 2, angle + 90);
76
+ const endRadius = width * .2;
77
+ const endLeft = polar(endRadius, angle - 90);
78
+ const endRight = polar(endRadius, angle + 90);
79
+ const blade = (endPoint) => pts([
80
+ {
81
+ x: endPoint.x + endLeft.x,
82
+ y: endPoint.y + endLeft.y
83
+ },
84
+ {
85
+ x: endPoint.x + endRight.x,
86
+ y: endPoint.y + endRight.y
87
+ },
88
+ right,
89
+ left
90
+ ]);
91
+ const rear = kind === "compass" ? blade(back) : pts([
92
+ {
93
+ x: back.x + left.x * .6,
94
+ y: back.y + left.y * .6
95
+ },
96
+ {
97
+ x: back.x + right.x * .6,
98
+ y: back.y + right.y * .6
99
+ },
100
+ right,
101
+ left
102
+ ]);
103
+ return /* @__PURE__ */ jsxs("g", { children: [
104
+ tail > 0 && /* @__PURE__ */ jsx("polygon", {
105
+ points: rear,
106
+ fill: tailColor
107
+ }),
108
+ tail > 0 && kind === "compass" && /* @__PURE__ */ jsx("circle", {
109
+ cx: back.x,
110
+ cy: back.y,
111
+ r: endRadius,
112
+ fill: tailColor
113
+ }),
114
+ /* @__PURE__ */ jsx("polygon", {
115
+ points: blade(tip),
116
+ fill: color
117
+ }),
118
+ /* @__PURE__ */ jsx("circle", {
119
+ cx: tip.x,
120
+ cy: tip.y,
121
+ r: endRadius,
122
+ fill: color
123
+ }),
124
+ endDot
125
+ ] });
126
+ };
127
+ /**
128
+ * A needle pointing at the current value. Add a `GaugeHub` for the pivot, or
129
+ * a `gap` to clear the centre for a readout instead.
130
+ */
131
+ var GaugeNeedle = ({ gap = 0, ...props }) => {
132
+ const maskId = `gauge-needle-gap${React.useId().replace(/\W/g, "")}`;
133
+ if (gap <= 0) return /* @__PURE__ */ jsx(NeedleShape, { ...props });
134
+ return /* @__PURE__ */ jsxs("g", { children: [/* @__PURE__ */ jsxs("mask", {
135
+ id: maskId,
136
+ maskUnits: "userSpaceOnUse",
137
+ x: -1e4,
138
+ y: -1e4,
139
+ width: 2e4,
140
+ height: 2e4,
141
+ children: [/* @__PURE__ */ jsx("rect", {
142
+ x: -1e4,
143
+ y: -1e4,
144
+ width: 2e4,
145
+ height: 2e4,
146
+ fill: "white"
147
+ }), /* @__PURE__ */ jsx("circle", {
148
+ r: gap,
149
+ fill: "black"
150
+ })]
151
+ }), /* @__PURE__ */ jsx("g", {
152
+ mask: `url(#${maskId})`,
153
+ children: /* @__PURE__ */ jsx(NeedleShape, { ...props })
154
+ })] });
155
+ };
156
+ /** The pivot disc at the centre, drawn once however many needles there are. */
157
+ var GaugeHub = ({ radius = 12, color = FOREGROUND }) => radius > 0 ? /* @__PURE__ */ jsx("circle", {
158
+ r: radius,
159
+ fill: color
160
+ }) : null;
161
+ //#endregion
162
+ export { GaugeHub, GaugeNeedle };
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Arc scale for a round dial. Parts ask the scale where a domain position
3
+ * lands rather than doing polar math themselves.
4
+ *
5
+ * Positions are gauge degrees. An offset moves a point outward from the
6
+ * reference circle (away from centre).
7
+ */
8
+ export type GaugePoint = {
9
+ x: number;
10
+ y: number;
11
+ };
12
+ export type GaugeBox = {
13
+ x: number;
14
+ y: number;
15
+ width: number;
16
+ height: number;
17
+ };
18
+ export type GaugeScale = {
19
+ /** Position of the start of the domain. */
20
+ start: number;
21
+ /** Position of the end of the domain. */
22
+ end: number;
23
+ /**
24
+ * The scale runs right round and meets itself, as a full ring does, so
25
+ * its two ends land on the same spot.
26
+ */
27
+ closed: boolean;
28
+ /** The point at a position, `offset` outward from the reference circle. */
29
+ point: (position: number, offset?: number) => GaugePoint;
30
+ /**
31
+ * SVG path along the scale between two positions, `offset` outward. Empty
32
+ * when `to` is not past `from`.
33
+ */
34
+ path: (from: number, to: number, offset?: number) => string;
35
+ /** The outward direction at a position, as a unit vector. */
36
+ normal: (position: number) => GaugePoint;
37
+ /**
38
+ * Box round the whole scale with its reference circle moved `offset`
39
+ * outward. Includes the centre, where a needle pivots.
40
+ */
41
+ box: (offset?: number) => GaugeBox;
42
+ };
43
+ /** A dial's scale: a circle of `radius` swept from one gauge angle to another. */
44
+ export declare const arcScale: (radius: number, startAngle: number, endAngle: number) => GaugeScale;
@@ -0,0 +1,21 @@
1
+ import { arcBox, arcPath, polar } from "./math.js";
2
+ //#region src/components/gauge/scale.ts
3
+ /**
4
+ * Arc scale for a round dial. Parts ask the scale where a domain position
5
+ * lands rather than doing polar math themselves.
6
+ *
7
+ * Positions are gauge degrees. An offset moves a point outward from the
8
+ * reference circle (away from centre).
9
+ */
10
+ /** A dial's scale: a circle of `radius` swept from one gauge angle to another. */
11
+ var arcScale = (radius, startAngle, endAngle) => ({
12
+ start: startAngle,
13
+ end: endAngle,
14
+ closed: endAngle - startAngle >= 360,
15
+ point: (angle, offset = 0) => polar(radius + offset, angle),
16
+ path: (from, to, offset = 0) => arcPath(radius + offset, from, to),
17
+ normal: (angle) => polar(1, angle),
18
+ box: (offset = 0) => arcBox(radius + offset, startAngle, endAngle)
19
+ });
20
+ //#endregion
21
+ export { arcScale };
@@ -0,0 +1,79 @@
1
+ import { FontFamily, FontWeight, GaugeEnd, TextAnchor } from './utils';
2
+ import * as React from 'react';
3
+ export type GaugeTextProps = {
4
+ /**
5
+ * Position relative to the gauge centre in SVG units. Positive y is down.
6
+ * @defaultValue 0
7
+ */
8
+ x?: number;
9
+ /**
10
+ * Position relative to the gauge centre in SVG units. Positive y is down.
11
+ * @defaultValue 0
12
+ */
13
+ y?: number;
14
+ /**
15
+ * Font size in SVG units.
16
+ * @defaultValue 24
17
+ */
18
+ fontSize?: number;
19
+ /**
20
+ * Fill colour. Unset uses theme foreground.
21
+ * @defaultValue `var(--global-foreground)`
22
+ */
23
+ color?: string;
24
+ /**
25
+ * Typeface map: sans/rounded → body; mono → mono token.
26
+ * @defaultValue `'sans'`
27
+ */
28
+ font?: FontFamily;
29
+ /**
30
+ * Font weight token.
31
+ * @defaultValue `'medium'`
32
+ */
33
+ weight?: FontWeight;
34
+ /**
35
+ * Which edge of the text sits at `(x, y)`.
36
+ * @defaultValue `'middle'`
37
+ */
38
+ anchor?: TextAnchor;
39
+ /**
40
+ * Extra class names on the SVG `text` node.
41
+ * @defaultValue undefined
42
+ */
43
+ className?: string;
44
+ /**
45
+ * Inline styles merged onto the SVG `text` node (e.g. tabular nums).
46
+ * @defaultValue undefined
47
+ */
48
+ style?: React.CSSProperties;
49
+ /**
50
+ * Label content.
51
+ */
52
+ children: React.ReactNode;
53
+ };
54
+ /** A free-standing label anywhere on the gauge. */
55
+ export declare const GaugeText: React.FC<GaugeTextProps>;
56
+ export type GaugeValueProps = Omit<GaugeTextProps, 'children'> & {
57
+ /**
58
+ * Fixed decimal places when `format` is omitted.
59
+ * @defaultValue 0
60
+ */
61
+ decimals?: number;
62
+ /**
63
+ * Formats each number shown; a range formats its two ends apart.
64
+ * @defaultValue `value.toFixed(decimals)`
65
+ */
66
+ format?: (value: number) => string;
67
+ /**
68
+ * Placed between the two ends of a range.
69
+ * @defaultValue `'–'`
70
+ */
71
+ separator?: string;
72
+ /**
73
+ * Show only one end of a range rather than both.
74
+ * @defaultValue both ends when `range`, else `value`
75
+ */
76
+ end?: GaugeEnd;
77
+ };
78
+ /** The current value as text, or a range as its two ends. */
79
+ export declare const GaugeValue: React.FC<GaugeValueProps>;
@@ -0,0 +1,42 @@
1
+ "use client";
2
+ import { useGauge } from "./context.js";
3
+ import { fontFamily, fontWeight } from "./utils.js";
4
+ import { jsx } from "react/jsx-runtime";
5
+ import "react";
6
+ //#region src/components/gauge/text.tsx
7
+ var FOREGROUND = "var(--global-foreground)";
8
+ /** A free-standing label anywhere on the gauge. */
9
+ var GaugeText = ({ x = 0, y = 0, fontSize = 24, color = FOREGROUND, font = "sans", weight = "medium", anchor = "middle", className, style, children }) => /* @__PURE__ */ jsx("text", {
10
+ x,
11
+ y,
12
+ fill: color,
13
+ fontSize,
14
+ fontFamily: fontFamily(font),
15
+ fontWeight: fontWeight(weight),
16
+ textAnchor: anchor,
17
+ dominantBaseline: "central",
18
+ className,
19
+ style,
20
+ children
21
+ });
22
+ /** The current value as text, or a range as its two ends. */
23
+ var GaugeValue = ({ decimals = 0, format, separator = "–", end, className, style, fontSize = 96, weight = "semibold", ...props }) => {
24
+ const { value, from, range } = useGauge();
25
+ const text = (v) => format ? format(v) : v.toFixed(decimals);
26
+ let valueText = text(value);
27
+ if (end === "from") valueText = text(from);
28
+ else if (range && !end) valueText = `${text(from)}${separator}${text(value)}`;
29
+ return /* @__PURE__ */ jsx(GaugeText, {
30
+ fontSize,
31
+ weight,
32
+ className,
33
+ style: {
34
+ fontVariantNumeric: "tabular-nums",
35
+ ...style
36
+ },
37
+ ...props,
38
+ children: valueText
39
+ });
40
+ };
41
+ //#endregion
42
+ export { GaugeText, GaugeValue };
@@ -0,0 +1,169 @@
1
+ import { FontFamily, FontWeight, StrokeCap, TextAnchor, ValueColor } from './utils';
2
+ import * as React from 'react';
3
+ type CrossLineProps = {
4
+ /**
5
+ * Length of each line across the scale: along the radius of a dial.
6
+ * @defaultValue 12
7
+ */
8
+ length?: number;
9
+ /**
10
+ * Stroke width of each tick line.
11
+ * @defaultValue 2
12
+ */
13
+ width?: number;
14
+ /**
15
+ * One colour, or a function of each line's value to colour some apart:
16
+ * the ticks inside a range, or each in the colour of its zone.
17
+ * @defaultValue `var(--global-foreground)`
18
+ */
19
+ color?: ValueColor;
20
+ /**
21
+ * Stroke opacity for the tick group.
22
+ * @defaultValue 1
23
+ */
24
+ opacity?: number;
25
+ /**
26
+ * Stroke line cap.
27
+ * @defaultValue `'round'`
28
+ */
29
+ cap?: StrokeCap;
30
+ /**
31
+ * Offset of the line's midpoint from the scale's reference line.
32
+ * @defaultValue 0
33
+ */
34
+ offset?: number;
35
+ };
36
+ export type GaugeTicksProps = CrossLineProps & {
37
+ /**
38
+ * Number of intervals across the domain; renders `count + 1` ticks.
39
+ * @defaultValue 10
40
+ */
41
+ count?: number;
42
+ };
43
+ /** Evenly spaced tick marks along the whole scale. */
44
+ export declare const GaugeTicks: React.FC<GaugeTicksProps>;
45
+ export type GaugeTickFillProps = Omit<CrossLineProps, 'color'> & {
46
+ /**
47
+ * Number of intervals across the domain; renders `count + 1` ticks.
48
+ * @defaultValue 40
49
+ */
50
+ count?: number;
51
+ /**
52
+ * Where the lit stretch starts. Defaults to the start of the gauge's
53
+ * range, or `min` on a single value.
54
+ * @defaultValue gauge `from`
55
+ */
56
+ from?: number;
57
+ /**
58
+ * Where it ends. Defaults to the current value, or a range's far end.
59
+ * @defaultValue current gauge `value`
60
+ */
61
+ to?: number;
62
+ /**
63
+ * `fill` lights every tick from `from` to `to`, as an arc would fill.
64
+ * `nearest` lights only the tick nearest each end: on a single value a
65
+ * pointer made of ticks, on a range one at either end.
66
+ * @defaultValue `'fill'`
67
+ */
68
+ mode?: 'fill' | 'nearest';
69
+ /**
70
+ * Colour of the lit ticks: one, or a function of each tick's value, so a
71
+ * fill can run through zones or a gradient.
72
+ * @defaultValue `var(--global-foreground)`
73
+ */
74
+ color?: ValueColor;
75
+ /**
76
+ * Colour of the ticks left unlit. Defaults to a faint theme foreground.
77
+ * @defaultValue `fadeColor('var(--global-foreground)', 15)`
78
+ */
79
+ dimColor?: ValueColor;
80
+ /**
81
+ * Length of a lit tick, for meters whose live segments stand out.
82
+ * Defaults to `length`.
83
+ * @defaultValue same as `length`
84
+ */
85
+ litLength?: number;
86
+ };
87
+ /**
88
+ * Ticks that are the fill: the whole sweep is ticked, and those between the
89
+ * ends of the value are lit while the rest stay dim. It follows the value
90
+ * frame by frame as `GaugeArc` does, fills a range between its ends, and on
91
+ * a wrapped gauge runs forward from the start across the seam. With
92
+ * `mode="nearest"` it lights a single tick at each end instead, for a
93
+ * pointer drawn in ticks. Lit and dim colours can each be a function of the
94
+ * tick's value, so one primitive covers segmented meters, LED rings, zoned
95
+ * dials and dot-matrix pointers.
96
+ */
97
+ export declare const GaugeTickFill: React.FC<GaugeTickFillProps>;
98
+ export type GaugeMarksProps = CrossLineProps & {
99
+ /**
100
+ * Domain values to mark, for example the cutoffs between zones.
101
+ * @defaultValue derived from `count`, or empty
102
+ */
103
+ values?: number[];
104
+ /**
105
+ * Cut the domain into this many equal segments instead: the marks fall
106
+ * between the segments, none at the two ends. On a closed ring the seam is
107
+ * cut too, since the last segment meets the first there. Drawn in the
108
+ * surface's colour over a band, they slice it the way a water ring is cut
109
+ * into glasses.
110
+ * @defaultValue omitted (use `values`)
111
+ */
112
+ count?: number;
113
+ };
114
+ /**
115
+ * Marks across the scale at arbitrary domain values, or at equal cuts of
116
+ * the domain.
117
+ */
118
+ export declare const GaugeMarks: React.FC<GaugeMarksProps>;
119
+ export type GaugeTickLabelsProps = {
120
+ /**
121
+ * Number of intervals across the domain; renders `count + 1` labels.
122
+ * @defaultValue 10
123
+ */
124
+ count?: number;
125
+ /**
126
+ * Offset of the label from the scale's reference line.
127
+ * @defaultValue -48
128
+ */
129
+ offset?: number;
130
+ /**
131
+ * Which edge of each label sits at its point: `end` lines labels up
132
+ * against the left of a vertical bar.
133
+ * @defaultValue `'middle'`
134
+ */
135
+ anchor?: TextAnchor;
136
+ /**
137
+ * Font size in SVG units.
138
+ * @defaultValue 16
139
+ */
140
+ fontSize?: number;
141
+ /**
142
+ * One colour, or a function of each label's value.
143
+ * @defaultValue `var(--global-foreground)`
144
+ */
145
+ color?: ValueColor;
146
+ /**
147
+ * Typeface map: sans/rounded → body; mono → mono token.
148
+ * @defaultValue `'sans'`
149
+ */
150
+ font?: FontFamily;
151
+ /**
152
+ * Font weight token.
153
+ * @defaultValue `'medium'`
154
+ */
155
+ weight?: FontWeight;
156
+ /**
157
+ * Fixed decimal places when `format` is omitted.
158
+ * @defaultValue 0
159
+ */
160
+ decimals?: number;
161
+ /**
162
+ * Custom label formatter. Overrides `decimals` when set.
163
+ * @defaultValue `value.toFixed(decimals)`
164
+ */
165
+ format?: (value: number) => string;
166
+ };
167
+ /** Numeric labels at evenly spaced values along the scale. */
168
+ export declare const GaugeTickLabels: React.FC<GaugeTickLabelsProps>;
169
+ export {};
@@ -0,0 +1,124 @@
1
+ "use client";
2
+ import { cutValues, foldValue, stepValues } from "./math.js";
3
+ import { useGauge } from "./context.js";
4
+ import { fadeColor, fontFamily, fontWeight } from "./utils.js";
5
+ import { jsx } from "react/jsx-runtime";
6
+ import "react";
7
+ //#region src/components/gauge/ticks.tsx
8
+ var FOREGROUND = "var(--global-foreground)";
9
+ /**
10
+ * Evenly spaced domain values for ticks. On a closed ring the last step lands
11
+ * on the first, so it is dropped rather than drawn twice.
12
+ */
13
+ var tickValues = ({ min, max, scale }, count) => {
14
+ const values = stepValues(min, max, count);
15
+ if (scale.closed) values.pop();
16
+ return values;
17
+ };
18
+ /** Lines standing across the scale at domain values, as ticks do. */
19
+ var CrossLines = ({ values, length = 12, lengthOf, width = 2, color = FOREGROUND, opacity = 1, cap = "round", offset = 0 }) => {
20
+ const { scale, positionOf } = useGauge();
21
+ const each = typeof color === "function" ? color : void 0;
22
+ return /* @__PURE__ */ jsx("g", {
23
+ stroke: typeof color === "string" ? color : void 0,
24
+ strokeOpacity: opacity,
25
+ strokeWidth: width,
26
+ strokeLinecap: cap,
27
+ children: values.map((value) => {
28
+ const half = (lengthOf?.(value) ?? length) / 2;
29
+ const a = scale.point(positionOf(value), offset - half);
30
+ const b = scale.point(positionOf(value), offset + half);
31
+ return /* @__PURE__ */ jsx("line", {
32
+ x1: a.x,
33
+ y1: a.y,
34
+ x2: b.x,
35
+ y2: b.y,
36
+ stroke: each?.(value)
37
+ }, value);
38
+ })
39
+ });
40
+ };
41
+ /** Evenly spaced tick marks along the whole scale. */
42
+ var GaugeTicks = ({ count = 10, ...props }) => {
43
+ const gauge = useGauge();
44
+ return /* @__PURE__ */ jsx(CrossLines, {
45
+ values: tickValues(gauge, count),
46
+ ...props
47
+ });
48
+ };
49
+ /**
50
+ * Ticks that are the fill: the whole sweep is ticked, and those between the
51
+ * ends of the value are lit while the rest stay dim. It follows the value
52
+ * frame by frame as `GaugeArc` does, fills a range between its ends, and on
53
+ * a wrapped gauge runs forward from the start across the seam. With
54
+ * `mode="nearest"` it lights a single tick at each end instead, for a
55
+ * pointer drawn in ticks. Lit and dim colours can each be a function of the
56
+ * tick's value, so one primitive covers segmented meters, LED rings, zoned
57
+ * dials and dot-matrix pointers.
58
+ */
59
+ var GaugeTickFill = ({ count = 40, from, to, mode = "fill", color = FOREGROUND, dimColor = fadeColor(FOREGROUND, 15), length = 12, litLength = length, ...props }) => {
60
+ const gauge = useGauge();
61
+ const values = tickValues(gauge, count);
62
+ const tail = from ?? gauge.from;
63
+ const head = to ?? gauge.value;
64
+ const span = Math.abs(gauge.max - gauge.min);
65
+ const eps = span * 1e-6;
66
+ const ahead = (v) => foldValue(v - tail + eps, 0, span);
67
+ const between = (v) => gauge.wrap ? ahead(v) <= ahead(head) + eps : v >= Math.min(tail, head) - eps && v <= Math.max(tail, head) + eps;
68
+ const nearestTo = (target) => {
69
+ const gap = (v) => {
70
+ const d = Math.abs(v - target);
71
+ return gauge.wrap ? Math.min(d, span - d) : d;
72
+ };
73
+ return values.reduce((best, v) => gap(v) < gap(best) ? v : best);
74
+ };
75
+ const ends = mode === "nearest" ? new Set(gauge.range || from !== void 0 ? [nearestTo(tail), nearestTo(head)] : [nearestTo(head)]) : null;
76
+ const lit = new Map(values.map((v) => [v, ends ? ends.has(v) : between(v)]));
77
+ const paint = (c, v) => typeof c === "function" ? c(v) : c;
78
+ return /* @__PURE__ */ jsx(CrossLines, {
79
+ values,
80
+ length,
81
+ lengthOf: (v) => lit.get(v) ? litLength : length,
82
+ color: (v) => paint(lit.get(v) ? color : dimColor, v),
83
+ ...props
84
+ });
85
+ };
86
+ /**
87
+ * Marks across the scale at arbitrary domain values, or at equal cuts of
88
+ * the domain.
89
+ */
90
+ var GaugeMarks = ({ values, count, ...props }) => {
91
+ const { min, max, scale } = useGauge();
92
+ const marks = values ?? (count && count > 1 ? [...scale.closed ? [min] : [], ...cutValues(min, max, count)] : []);
93
+ return /* @__PURE__ */ jsx(CrossLines, {
94
+ values: marks,
95
+ ...props
96
+ });
97
+ };
98
+ /** Numeric labels at evenly spaced values along the scale. */
99
+ var GaugeTickLabels = ({ count = 10, offset = -48, anchor = "middle", fontSize = 16, color = FOREGROUND, font = "sans", weight = "medium", decimals = 0, format }) => {
100
+ const gauge = useGauge();
101
+ const { scale, positionOf } = gauge;
102
+ const values = tickValues(gauge, count);
103
+ const each = typeof color === "function" ? color : void 0;
104
+ return /* @__PURE__ */ jsx("g", {
105
+ fill: typeof color === "string" ? color : void 0,
106
+ fontSize,
107
+ fontFamily: fontFamily(font),
108
+ fontWeight: fontWeight(weight),
109
+ textAnchor: anchor,
110
+ dominantBaseline: "central",
111
+ style: { fontVariantNumeric: "tabular-nums" },
112
+ children: values.map((value) => {
113
+ const p = scale.point(positionOf(value), offset);
114
+ return /* @__PURE__ */ jsx("text", {
115
+ x: p.x,
116
+ y: p.y,
117
+ fill: each?.(value),
118
+ children: format ? format(value) : value.toFixed(decimals)
119
+ }, value);
120
+ })
121
+ });
122
+ };
123
+ //#endregion
124
+ export { GaugeMarks, GaugeTickFill, GaugeTickLabels, GaugeTicks };