@viraui/react 2026.10.0-next.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,8 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
3
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error("Calling `require` for \"" + x + "\" in an environment that doesn't expose the `require` function. See https://rolldown.rs/in-depth/bundling-cjs#require-external-modules for more details.");
6
+ });
7
+ //#endregion
8
+ export { __commonJSMin, __require };
package/dist/catalog.json CHANGED
@@ -122,6 +122,12 @@
122
122
  "summary": "Decorative pointer-driven border highlight on a transparent Surface shell (radius only). Put the visual card as children; a shared rAF tracker paints CSS variables without React re-renders. Memory/CPU-heavy — use sparingly; prefer one focal instance per view.",
123
123
  "agentDocs": "./specs/components/glow/meta.xml"
124
124
  },
125
+ {
126
+ "id": "gauge",
127
+ "name": "Gauge",
128
+ "summary": "Composable round dial for determinate readings and optional polar slider control, built from SVG parts sharing one domain context. Linear bars stay on Slider.",
129
+ "agentDocs": "./specs/components/gauge/meta.xml"
130
+ },
125
131
  {
126
132
  "id": "grid",
127
133
  "name": "Grid",
@@ -134,6 +140,12 @@
134
140
  "summary": "Icon-only button that shares Button visual chrome (--vui-button-* CSS) and requires an accessible name through aria-label or aria-labelledby.",
135
141
  "agentDocs": "./specs/components/icon-button/meta.xml"
136
142
  },
143
+ {
144
+ "id": "icon-meter",
145
+ "name": "IconMeter",
146
+ "summary": "Display-only discrete icon row for scores and levels (empty / half / full), with APG Meter semantics. Distinct from track Meter, arc Gauge, and LinearProgress.",
147
+ "agentDocs": "./specs/components/icon-meter/meta.xml"
148
+ },
137
149
  {
138
150
  "id": "linear-progress",
139
151
  "name": "LinearProgress",
@@ -32,6 +32,12 @@ export type AvatarProps = React.ComponentPropsWithRef<'span'> & {
32
32
  * @defaultValue 's'
33
33
  */
34
34
  size?: 'xs' | 's' | 'm' | 'l';
35
+ /**
36
+ * Controls native image lazy-loading on the avatar `<img>`.
37
+ *
38
+ * @defaultValue 'eager'
39
+ */
40
+ loading?: React.ComponentProps<'img'>['loading'];
35
41
  /**
36
42
  * Ref forwarded to the rendered avatar root element.
37
43
  *
@@ -11,7 +11,7 @@ var fallbackTextSizeByAvatarSize = {
11
11
  m: "s",
12
12
  l: "2xl"
13
13
  };
14
- var Avatar$1 = ({ alt = "", className, fallback, ref, size = "s", src, ...otherProps }) => {
14
+ var Avatar$1 = ({ alt = "", className, fallback, loading = "eager", ref, size = "s", src, ...otherProps }) => {
15
15
  const hasImage = Boolean(src);
16
16
  return /* @__PURE__ */ jsxs(Avatar.Root, {
17
17
  ...otherProps,
@@ -23,7 +23,7 @@ var Avatar$1 = ({ alt = "", className, fallback, ref, size = "s", src, ...otherP
23
23
  children: [hasImage ? /* @__PURE__ */ jsx(Avatar.Image, {
24
24
  draggable: false,
25
25
  decoding: "async",
26
- loading: "eager",
26
+ loading,
27
27
  alt,
28
28
  className: avatar_module_default.Image,
29
29
  src
@@ -1 +1 @@
1
- @layer viraui.preflight, viraui.components, viraui.overrides;@layer viraui.components{@property --vui-badge-size{syntax:"<length>";inherits:true;initial-value:.5rem}@property --vui-badge-color{syntax:"<color>";inherits:true;initial-value:#292929}@property --vui-badge-position-x{syntax:"<length-percentage>";inherits:true;initial-value:100%}@property --vui-badge-position-y{syntax:"<length-percentage>";inherits:true;initial-value:0%}@property --__badge-gap{syntax:"<length>";inherits:true;initial-value:0}@property --__badge-mask-size{syntax:"<length>";inherits:true;initial-value:0}@property --__badge-hole-progress{syntax:"<number>";inherits:true;initial-value:0}@property --__badge-motion-delay{syntax:"<time>";inherits:true;initial-value:.16s}.viraui__badge-module__Badge_lJqlY{--vui-badge-color:var(--global-primary);--__badge-gap:calc(var(--vui-badge-size) * 0.25);--__badge-mask-size:calc(var(--vui-badge-size) / 2 + var(--__badge-gap));position:relative;display:inline-flex;&[data-position=top-end]{--vui-badge-position-x:100%;--vui-badge-position-y:0%}&[data-position=top-start]{--vui-badge-position-x:0%;--vui-badge-position-y:0%}&[data-position=bottom-end]{--vui-badge-position-x:100%;--vui-badge-position-y:100%}&[data-position=bottom-start]{--vui-badge-position-x:0%;--vui-badge-position-y:100%}&>*{outline-offset:-4px}&[data-show=true]{&:after{--__corner-shape:round;content:"";width:var(--vui-badge-size);aspect-ratio:1;display:block;border-radius:100000px;background:var(--vui-badge-color);position:absolute;left:calc(var(--vui-badge-position-x) - min(var(--vui-badge-position-x), var(--vui-badge-size)));top:calc(var(--vui-badge-position-y) - min(var(--vui-badge-position-y), var(--vui-badge-size)));pointer-events:none;transform-origin:center}&:not([data-exit]):after{transform:scale(0);animation:viraui__badge-module__badge-dot-in_SKMx8 var(--duration-fast) var(--easing-entrance) forwards;animation-delay:var(--__badge-motion-delay)}&[data-exit]:after{transform:scale(1);animation:viraui__badge-module__badge-dot-out_vWhcP var(--duration-fast) var(--easing-entrance) forwards}&>*{mask-image:radial-gradient(circle at calc(var(--vui-badge-position-x) - min(var(--vui-badge-position-x), var(--vui-badge-size)) + (var(--vui-badge-size) / 2)) calc(var(--vui-badge-position-y) - min(var(--vui-badge-position-y), var(--vui-badge-size)) + (var(--vui-badge-size) / 2)),#0000 0 calc(var(--__badge-mask-size) * var(--__badge-hole-progress)),red calc(var(--__badge-mask-size) * var(--__badge-hole-progress) + 1px))}&:not([data-exit])>*{animation:viraui__badge-module__badge-hole-in_71iZP var(--duration-fast) var(--easing-entrance) forwards}&[data-exit]>*{--__badge-hole-progress:1;animation:viraui__badge-module__badge-hole-out_JCHCf var(--duration-fast) var(--easing-entrance) forwards;animation-delay:var(--__badge-motion-delay)}@media (prefers-reduced-motion:reduce){&:after{transform:none}&>*{--__badge-hole-progress:1}&[data-exit]>*{--__badge-hole-progress:0}}}}@keyframes viraui__badge-module__badge-hole-in_71iZP{0%{--__badge-hole-progress:0}to{--__badge-hole-progress:1}}@keyframes viraui__badge-module__badge-hole-out_JCHCf{0%{--__badge-hole-progress:1}to{--__badge-hole-progress:0}}@keyframes viraui__badge-module__badge-dot-in_SKMx8{0%{transform:scale(0)}to{transform:scale(1)}}@keyframes viraui__badge-module__badge-dot-out_vWhcP{0%{transform:scale(1)}to{transform:scale(0)}}}
1
+ @layer viraui.preflight, viraui.components, viraui.overrides;@layer viraui.components{@property --vui-badge-size{syntax:"<length>";inherits:true;initial-value:.5rem}@property --vui-badge-color{syntax:"<color>";inherits:true;initial-value:#292929}@property --vui-badge-position-x{syntax:"<length-percentage>";inherits:true;initial-value:100%}@property --vui-badge-position-y{syntax:"<length-percentage>";inherits:true;initial-value:0%}@property --__badge-gap{syntax:"<length>";inherits:true;initial-value:0}@property --__badge-mask-size{syntax:"<length>";inherits:true;initial-value:0}@property --__badge-hole-progress{syntax:"<number>";inherits:true;initial-value:0}@property --__badge-motion-delay{syntax:"<time>";inherits:true;initial-value:.08s}.viraui__badge-module__Badge_lJqlY{--vui-badge-color:var(--global-primary);--__badge-gap:calc(var(--vui-badge-size) * 0.25);--__badge-mask-size:calc(var(--vui-badge-size) / 2 + var(--__badge-gap));--__badge-motion-delay:calc(var(--duration-fast) * 0.4);position:relative;display:inline-flex;inline-size:max-content;&[data-position=top-end]{--vui-badge-position-x:100%;--vui-badge-position-y:0%}&[data-position=top-start]{--vui-badge-position-x:0%;--vui-badge-position-y:0%}&[data-position=bottom-end]{--vui-badge-position-x:100%;--vui-badge-position-y:100%}&[data-position=bottom-start]{--vui-badge-position-x:0%;--vui-badge-position-y:100%}&>*{outline-offset:-4px}&[data-show=true]{&:after{--__corner-shape:round;content:"";width:var(--vui-badge-size);aspect-ratio:1;display:block;border-radius:100000px;background:var(--vui-badge-color);position:absolute;left:calc(var(--vui-badge-position-x) - min(var(--vui-badge-position-x), var(--vui-badge-size)));top:calc(var(--vui-badge-position-y) - min(var(--vui-badge-position-y), var(--vui-badge-size)));pointer-events:none;transform-origin:center}&:not([data-exit]):after{transform:scale(0);animation:viraui__badge-module__badge-dot-in_SKMx8 var(--duration-fast) var(--easing-entrance) forwards;animation-delay:var(--__badge-motion-delay)}&[data-exit]:after{transform:scale(1);animation:viraui__badge-module__badge-dot-out_vWhcP var(--duration-fast) var(--easing-entrance) forwards}&>*{mask-image:radial-gradient(circle at calc(var(--vui-badge-position-x) - min(var(--vui-badge-position-x), var(--vui-badge-size)) + (var(--vui-badge-size) / 2)) calc(var(--vui-badge-position-y) - min(var(--vui-badge-position-y), var(--vui-badge-size)) + (var(--vui-badge-size) / 2)),#0000 0 calc(var(--__badge-mask-size) * var(--__badge-hole-progress)),red calc(var(--__badge-mask-size) * var(--__badge-hole-progress) + 1px))}&:not([data-exit])>*{animation:viraui__badge-module__badge-hole-in_71iZP var(--duration-fast) var(--easing-entrance) forwards}&[data-exit]>*{--__badge-hole-progress:1;animation:viraui__badge-module__badge-hole-out_JCHCf var(--duration-fast) var(--easing-entrance) forwards;animation-delay:var(--__badge-motion-delay)}@media (prefers-reduced-motion:reduce){&:not([data-exit]):after,&[data-exit]:after{transform:none;animation:none}&:not([data-exit])>*{--__badge-hole-progress:1;animation:none}&[data-exit]>*{--__badge-hole-progress:0;animation:none}}}}@keyframes viraui__badge-module__badge-hole-in_71iZP{0%{--__badge-hole-progress:0}to{--__badge-hole-progress:1}}@keyframes viraui__badge-module__badge-hole-out_JCHCf{0%{--__badge-hole-progress:1}to{--__badge-hole-progress:0}}@keyframes viraui__badge-module__badge-dot-in_SKMx8{0%{transform:scale(0)}to{transform:scale(1)}}@keyframes viraui__badge-module__badge-dot-out_vWhcP{0%{transform:scale(1)}to{transform:scale(0)}}}
@@ -1 +1 @@
1
- @layer viraui.preflight, viraui.components, viraui.overrides;@layer viraui.components{@property --vui-button-background{syntax:"<color>";inherits:false;initial-value:#0000}@property --vui-button-color{syntax:"<color>";inherits:false;initial-value:#0000}@property --vui-button-height{syntax:"<length>";inherits:false;initial-value:0}@property --vui-button-padding-inline{syntax:"<length>";inherits:false;initial-value:0}@property --vui-button-padding-block{syntax:"<length>";inherits:false;initial-value:0}@property --vui-button-spinner-color{syntax:"<color>";inherits:true;initial-value:#0000}@property --vui-button-icon-size{syntax:"<length>";inherits:true;initial-value:0}@property --vui-button-radius{syntax:"<length-percentage>";inherits:false;initial-value:0}.viraui__button-module__Button_oPhrP{--vui-button-background:var(--global-primary);--vui-button-color:var(--base-1);--vui-button-height:var(--space-2xl);--vui-button-padding-inline:calc(var(--space-s) + var(--space-2xs));--vui-button-padding-block:var(--space-s);--vui-button-icon-size:calc(var(--space-xl) - var(--space-s) - var(--space-xs) - var(--space-2xs));--vui-button-spinner-color:currentColor;--vui-button-radius:var(--radius-m);max-block-size:min-content;-webkit-user-select:none;user-select:none;margin-block:0;margin-inline:0;cursor:pointer;font-family:inherit;display:inline-flex;align-items:center;min-inline-size:fit-content;min-block-size:round(to-zero,var(--vui-button-height),2px);block-size:round(to-zero,var(--vui-button-height),2px);justify-content:center;text-decoration:none;white-space:nowrap;appearance:button;overflow:hidden;position:relative;padding:var(--vui-button-padding-block) var(--vui-button-padding-inline);font-weight:400;font-variation-settings:"wght" 500;gap:var(--space-s);border:1px solid #0000;background:var(--vui-button-background);color:var(--vui-button-color);font-size:var(--font-scale-body-m);border-radius:var(--vui-button-radius);transition:translate var(--easing-elastic) var(--duration-instant);will-change:translate;&:not(:disabled){background-clip:padding-box}&:not(:disabled,[aria-disabled=true]):active{translate:0 1px}&[data-pill=true]{--__corner-shape:round;border-radius:var(--radius-full)}&[data-size=l]{--vui-button-height:calc(var(--space-2xl) + var(--space-xs));--vui-button-padding-inline:var(--space-m);--vui-button-padding-block:var(--space-s);--vui-button-radius:var(--radius-m);--vui-button-icon-size:calc(var(--space-l) - var(--space-xs))}&[data-size=s]{--vui-button-height:var(--space-xl);--vui-button-padding-inline:var(--space-s);--vui-button-padding-block:var(--space-xs);--vui-button-radius:var(--radius-s);--vui-button-icon-size:calc(var(--space-l) - var(--space-s));gap:var(--space-xs);font-size:var(--font-scale-body-s)}&[data-variant=primary]{--vui-button-background:var(--global-primary);--vui-button-color:contrast-color(var(--global-primary));&:hover{--vui-button-background:oklab(from var(--global-primary) calc(l + 0.05) a b)}}&[data-variant=secondary]{--vui-button-background:var(--global-contrast);--vui-button-color:var(--global-foreground);background-clip:unset;&:hover{--vui-button-background:oklab(from var(--global-contrast) l a b / calc(alpha + 0.03))}@container root style(--is-light-mode: black){--vui-button-background:oklab(from var(--global-contrast) l a b / 0.1)}}&[data-variant=flat]{--vui-button-background:#0000;--vui-button-color:var(--global-foreground);background-clip:unset;&:hover{--vui-button-background:var(--global-contrast);--vui-button-color:var(--base-7)}}&[data-variant=destructive]{--vui-button-background:oklab(from var(--highlight-red) l a b / 12%);--vui-button-color:var(--highlight-red);outline-color:var(--highlight-red);&:hover{--vui-button-background:oklab(from var(--highlight-red) l a b / 30%)}}&[data-addon-position=start]{flex-direction:row;padding-inline-end:calc(var(--vui-button-padding-inline) * 1.2)}&[data-addon-position=end]{flex-direction:row-reverse;padding-inline-start:calc(var(--vui-button-padding-inline) * 1.2)}&[data-icon-only=true]{gap:0;padding-block:var(--vui-button-padding-block);padding-inline:var(--vui-button-padding-block);inline-size:var(--vui-button-height);min-inline-size:var(--vui-button-height);max-inline-size:var(--vui-button-height);aspect-ratio:1/1}&:disabled,&:disabled:active,&:disabled:hover,&[aria-disabled=true],&[aria-disabled=true]:active,&[aria-disabled=true]:hover{--vui-button-background:var(--global-disabled-background);--vui-button-color:var(--global-disabled-foreground);cursor:not-allowed;box-shadow:none;translate:0}&[data-variant=flat]:is(:disabled,[aria-disabled=true]),&[data-variant=flat]:is(:disabled,[aria-disabled=true]):active,&[data-variant=flat]:is(:disabled,[aria-disabled=true]):hover{--vui-button-background:#0000}&[data-loading=true]:is(:disabled,[data-disabled],[aria-disabled=true]){pointer-events:none;border:0;box-shadow:none;--vui-button-color:var(--base-7)}}.viraui__button-module__Content_w-pX-{display:inline-grid;place-items:center;&[data-loading=true]{opacity:0}&:first-child>svg{block-size:round(to-zero,var(--vui-button-icon-size),2px);inline-size:round(to-zero,var(--vui-button-icon-size),2px);flex-shrink:0}}.viraui__button-module__SpinnerOverlay_vKqw1{position:absolute;inset:0;display:grid;place-content:center;pointer-events:none;color:var(--vui-button-spinner-color)}}
1
+ @layer viraui.preflight, viraui.components, viraui.overrides;@layer viraui.components{@property --vui-button-background{syntax:"<color>";inherits:false;initial-value:#0000}@property --vui-button-color{syntax:"<color>";inherits:false;initial-value:#0000}@property --vui-button-height{syntax:"<length>";inherits:false;initial-value:0}@property --vui-button-padding-inline{syntax:"<length>";inherits:false;initial-value:0}@property --vui-button-padding-block{syntax:"<length>";inherits:false;initial-value:0}@property --vui-button-spinner-color{syntax:"<color>";inherits:true;initial-value:#0000}@property --vui-button-icon-size{syntax:"<length>";inherits:true;initial-value:0}@property --vui-button-radius{syntax:"<length-percentage>";inherits:false;initial-value:0}.viraui__button-module__Button_oPhrP{--vui-button-background:var(--global-primary);--vui-button-color:var(--base-1);--vui-button-height:var(--space-2xl);--vui-button-padding-inline:calc(var(--space-s) + var(--space-2xs));--vui-button-padding-block:var(--space-s);--vui-button-icon-size:calc(var(--vui-button-height) * 0.45);--vui-button-spinner-color:currentColor;--vui-button-radius:var(--radius-m);max-block-size:min-content;-webkit-user-select:none;user-select:none;margin-block:0;margin-inline:0;cursor:pointer;font-family:inherit;display:inline-flex;align-items:center;min-inline-size:fit-content;min-block-size:round(to-zero,var(--vui-button-height),2px);block-size:round(to-zero,var(--vui-button-height),2px);justify-content:center;text-decoration:none;white-space:nowrap;appearance:button;overflow:hidden;position:relative;padding:var(--vui-button-padding-block) var(--vui-button-padding-inline);font-weight:400;font-variation-settings:"wght" 500;gap:var(--space-s);border:1px solid #0000;background:var(--vui-button-background);color:var(--vui-button-color);font-size:var(--font-scale-body-m);border-radius:var(--vui-button-radius);transition:translate var(--easing-elastic) var(--duration-instant);will-change:translate;&:not(:disabled){background-clip:padding-box}&:not(:disabled,[aria-disabled=true]):active{translate:0 1px}&[data-pill=true]{--__corner-shape:round;border-radius:var(--radius-full)}&[data-size=l]{--vui-button-height:calc(var(--space-2xl) + var(--space-xs));--vui-button-padding-inline:var(--space-m);--vui-button-padding-block:var(--space-s);--vui-button-radius:var(--radius-m)}&[data-size=s]{--vui-button-height:var(--space-xl);--vui-button-padding-inline:var(--space-s);--vui-button-padding-block:var(--space-xs);--vui-button-radius:var(--radius-s);gap:var(--space-xs);font-size:var(--font-scale-body-s)}&[data-variant=primary]{--vui-button-background:var(--global-primary);--vui-button-color:contrast-color(var(--global-primary));&:hover{--vui-button-background:oklab(from var(--global-primary) calc(l + 0.05) a b)}}&[data-variant=secondary]{--vui-button-background:var(--global-contrast);--vui-button-color:var(--global-foreground);background-clip:unset;&:hover{--vui-button-background:oklab(from var(--global-contrast) l a b / calc(alpha + 0.03))}@container root style(--is-light-mode: black){--vui-button-background:oklab(from var(--global-contrast) l a b / 0.1)}}&[data-variant=flat]{--vui-button-background:#0000;--vui-button-color:var(--global-foreground);background-clip:unset;&:hover{--vui-button-background:var(--global-contrast);--vui-button-color:var(--base-7)}}&[data-variant=destructive]{--vui-button-background:oklab(from var(--highlight-red) l a b / 12%);--vui-button-color:var(--highlight-red);outline-color:var(--highlight-red);&:hover{--vui-button-background:oklab(from var(--highlight-red) l a b / 30%)}}&[data-addon-position=start]{flex-direction:row;padding-inline-end:calc(var(--vui-button-padding-inline) * 1.2)}&[data-addon-position=end]{flex-direction:row-reverse;padding-inline-start:calc(var(--vui-button-padding-inline) * 1.2)}&[data-icon-only=true]{gap:0;padding-block:var(--vui-button-padding-block);padding-inline:var(--vui-button-padding-block);inline-size:var(--vui-button-height);min-inline-size:var(--vui-button-height);max-inline-size:var(--vui-button-height);aspect-ratio:1/1}&:disabled,&:disabled:active,&:disabled:hover,&[aria-disabled=true],&[aria-disabled=true]:active,&[aria-disabled=true]:hover{--vui-button-background:var(--global-disabled-background);--vui-button-color:var(--global-disabled-foreground);cursor:not-allowed;box-shadow:none;translate:0}&[data-variant=flat]:is(:disabled,[aria-disabled=true]),&[data-variant=flat]:is(:disabled,[aria-disabled=true]):active,&[data-variant=flat]:is(:disabled,[aria-disabled=true]):hover{--vui-button-background:#0000}&[data-loading=true]:is(:disabled,[data-disabled],[aria-disabled=true]){pointer-events:none;border:0;box-shadow:none;--vui-button-color:var(--base-7)}}.viraui__button-module__Content_w-pX-{display:inline-grid;place-items:center;&[data-loading=true]{opacity:0}&:first-child>svg{block-size:round(to-zero,var(--vui-button-icon-size),2px);inline-size:round(to-zero,var(--vui-button-icon-size),2px);flex-shrink:0}}.viraui__button-module__SpinnerOverlay_vKqw1{position:absolute;inset:0;display:grid;place-content:center;pointer-events:none;color:var(--vui-button-spinner-color)}}
@@ -38,7 +38,7 @@ export type DialogSheetProps = {
38
38
  *
39
39
  * @defaultValue `start`
40
40
  */
41
- headerAlign?: Extract<StackProps['hAlign'], 'start' | 'center' | 'end'>;
41
+ headerAlign?: 'start' | 'center' | 'end';
42
42
  /**
43
43
  * Whether to show the close button.
44
44
  *
@@ -0,0 +1,134 @@
1
+ import { StrokeCap, Zone } from './utils';
2
+ import * as React from 'react';
3
+ type ArcStyleProps = {
4
+ /**
5
+ * Stroke width in SVG units.
6
+ * @defaultValue 24
7
+ */
8
+ width?: number;
9
+ /**
10
+ * Stroke colour. Track defaults to contrast; fill arcs to primary.
11
+ * @defaultValue `var(--global-contrast)` on `GaugeTrack`; `var(--global-primary)` on fill arcs
12
+ */
13
+ color?: string;
14
+ /**
15
+ * Stroke opacity.
16
+ * @defaultValue 1
17
+ */
18
+ opacity?: number;
19
+ /**
20
+ * Stroke line cap.
21
+ * @defaultValue `'round'` on track/arc; `'butt'` on stack/zones
22
+ */
23
+ cap?: StrokeCap;
24
+ /**
25
+ * Offset from the scale's reference line: radial on a dial, across a
26
+ * bar. Positive moves outward, away from the centre or up from a bar.
27
+ * @defaultValue 0
28
+ */
29
+ offset?: number;
30
+ };
31
+ /** The whole scale as a background: a dial's full sweep, a bar's length. */
32
+ export declare const GaugeTrack: React.FC<ArcStyleProps>;
33
+ export type GaugeArcProps = ArcStyleProps & {
34
+ /**
35
+ * Domain value the arc is anchored at. Defaults to `min`, or to the start
36
+ * of the gauge's range.
37
+ * @defaultValue gauge `from` (min on a single value)
38
+ */
39
+ from?: number;
40
+ /**
41
+ * Domain value the arc ends at. Defaults to the current value.
42
+ * @defaultValue current gauge `value`
43
+ */
44
+ to?: number;
45
+ /**
46
+ * Grow the arc back from the end of the sweep instead of forward from its
47
+ * start. The length is the same; only the end it is anchored to changes,
48
+ * which is what a dial filling towards its start needs.
49
+ * @defaultValue false
50
+ */
51
+ reverse?: boolean;
52
+ /**
53
+ * Carry the arc on round a closed ring instead of clamping it at the
54
+ * domain: a span from eleven at night to two in the morning crosses the
55
+ * seam in one piece. The arc runs forward from `from` however far past
56
+ * `max` the `to` reaches.
57
+ * @defaultValue false
58
+ */
59
+ wrap?: boolean;
60
+ };
61
+ /**
62
+ * The filled arc, from `min` (or `from`) up to the current value (or `to`).
63
+ * The ends may arrive either way round: an arc anchored above the value
64
+ * grows back towards it, which is what a dial centred on zero needs. On a
65
+ * gauge showing a range it fills between the two ends, and on a wrapped one
66
+ * runs forward from the start across the seam where the range does.
67
+ */
68
+ export declare const GaugeArc: React.FC<GaugeArcProps>;
69
+ export type GaugeStackPart = {
70
+ /**
71
+ * Share of the fill. Weighted parts split the value between them.
72
+ * @defaultValue 0 when omitted (fixed `to` parts use cutoff instead)
73
+ */
74
+ weight?: number;
75
+ /**
76
+ * Fixed upper cutoff in domain units, for parts that stand still while
77
+ * the value sweeps across them. Runs from the end of the part before.
78
+ * @defaultValue derived from weight share of fill
79
+ */
80
+ to?: number;
81
+ /**
82
+ * Stroke colour for this part.
83
+ */
84
+ color: string;
85
+ /**
86
+ * Per-part stroke opacity.
87
+ * @defaultValue inherits path paint (undefined)
88
+ */
89
+ opacity?: number;
90
+ };
91
+ export type GaugeStackProps = Omit<ArcStyleProps, 'color'> & {
92
+ /**
93
+ * Ordered stack segments (weights and/or fixed cutoffs).
94
+ */
95
+ parts: GaugeStackPart[];
96
+ /**
97
+ * Domain value the stack starts at. Defaults to `min`, or to the start of
98
+ * the gauge's range.
99
+ * @defaultValue gauge `from`
100
+ */
101
+ from?: number;
102
+ };
103
+ /**
104
+ * The value arc split into parts that sweep as one. A part with a `weight`
105
+ * takes that share of the fill, so a memory ring keeps wired, app and cache
106
+ * in proportion frame by frame; a part with a fixed `to` runs from the end
107
+ * of the one before and is clipped to the value, so a night of sleep stages
108
+ * draws on in order as the dial sweeps in. Use one kind or the other in a
109
+ * single stack.
110
+ */
111
+ export declare const GaugeStack: React.FC<GaugeStackProps>;
112
+ export type GaugeZonesProps = Omit<ArcStyleProps, 'color'> & {
113
+ /**
114
+ * Ordered cutoffs. Each zone runs from the previous zone's `to` (or `min`)
115
+ * up to its own `to`; the last zone always extends to `max`.
116
+ */
117
+ zones: Zone[];
118
+ /**
119
+ * Gap between neighbouring zones, in the scale's units: degrees round a
120
+ * dial, SVG units along a bar.
121
+ * @defaultValue 0
122
+ */
123
+ gap?: number;
124
+ /**
125
+ * Cap on the two outer ends of the whole band (the start of the first zone
126
+ * and the end of the last). `cap` covers the joints between zones. Defaults
127
+ * to `cap`, so a single value still rounds or squares every end.
128
+ * @defaultValue same as `cap`
129
+ */
130
+ endCap?: StrokeCap;
131
+ };
132
+ /** Coloured bands marking the cutoffs of the domain. */
133
+ export declare const GaugeZones: React.FC<GaugeZonesProps>;
134
+ export {};
@@ -0,0 +1,145 @@
1
+ "use client";
2
+ import { foldValue } from "./math.js";
3
+ import { useGauge } from "./context.js";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ import "react";
6
+ //#region src/components/gauge/arcs.tsx
7
+ var CONTRAST = "var(--global-contrast)";
8
+ var PRIMARY = "var(--global-primary)";
9
+ /** The whole scale as a background: a dial's full sweep, a bar's length. */
10
+ var GaugeTrack = ({ width = 24, color = CONTRAST, opacity = 1, cap = "round", offset = 0 }) => {
11
+ const { scale } = useGauge();
12
+ return /* @__PURE__ */ jsx("path", {
13
+ d: scale.path(scale.start, scale.end, offset),
14
+ fill: "none",
15
+ stroke: color,
16
+ strokeOpacity: opacity,
17
+ strokeWidth: width,
18
+ strokeLinecap: cap
19
+ });
20
+ };
21
+ /**
22
+ * The filled arc, from `min` (or `from`) up to the current value (or `to`).
23
+ * The ends may arrive either way round: an arc anchored above the value
24
+ * grows back towards it, which is what a dial centred on zero needs. On a
25
+ * gauge showing a range it fills between the two ends, and on a wrapped one
26
+ * runs forward from the start across the seam where the range does.
27
+ */
28
+ var GaugeArc = ({ width = 24, color = PRIMARY, opacity = 1, cap = "round", offset = 0, from, to, reverse = false, wrap = false }) => {
29
+ const gauge = useGauge();
30
+ const { min, max, value, scale, positionOf } = gauge;
31
+ const tail = from ?? gauge.from;
32
+ let head = to ?? value;
33
+ const forward = gauge.range && gauge.wrap && from === void 0 && to === void 0;
34
+ if (forward) head = tail + foldValue(head - tail, 0, Math.abs(max - min));
35
+ const lo = Math.min(tail, head);
36
+ const hi = Math.max(tail, head);
37
+ let a0;
38
+ let a1;
39
+ if (wrap || forward) {
40
+ const span = max - min;
41
+ if (span <= 0) return null;
42
+ const sweep = scale.end - scale.start;
43
+ const perUnit = sweep / span;
44
+ a0 = scale.start + ((lo - min) % span + span) % span * perUnit;
45
+ a1 = a0 + Math.min((hi - lo) * perUnit, sweep);
46
+ } else [a0, a1] = reverse ? [positionOf(max - (hi - lo)), positionOf(max)] : [positionOf(lo), positionOf(hi)];
47
+ if (a1 <= a0) return null;
48
+ return /* @__PURE__ */ jsx("path", {
49
+ d: scale.path(a0, a1, offset),
50
+ fill: "none",
51
+ stroke: color,
52
+ strokeOpacity: opacity,
53
+ strokeWidth: width,
54
+ strokeLinecap: cap
55
+ });
56
+ };
57
+ /**
58
+ * The value arc split into parts that sweep as one. A part with a `weight`
59
+ * takes that share of the fill, so a memory ring keeps wired, app and cache
60
+ * in proportion frame by frame; a part with a fixed `to` runs from the end
61
+ * of the one before and is clipped to the value, so a night of sleep stages
62
+ * draws on in order as the dial sweeps in. Use one kind or the other in a
63
+ * single stack.
64
+ */
65
+ var GaugeStack = ({ parts, width = 24, opacity = 1, cap = "butt", offset = 0, from }) => {
66
+ const gauge = useGauge();
67
+ const { value, scale, positionOf } = gauge;
68
+ if (parts.length === 0) return null;
69
+ const total = parts.reduce((sum, p) => sum + (p.weight ?? 0), 0);
70
+ const start = from ?? gauge.from;
71
+ const fill = value - start;
72
+ const ends = parts.reduce((acc, p) => [...acc, p.to ?? (acc.at(-1) ?? start) + (total > 0 ? fill * (p.weight ?? 0) / total : 0)], []);
73
+ return /* @__PURE__ */ jsx("g", {
74
+ opacity,
75
+ children: parts.map((p, i) => {
76
+ const end = ends[i];
77
+ if (end === void 0) return null;
78
+ const prev = i === 0 ? start : ends[i - 1];
79
+ if (prev === void 0) return null;
80
+ const a0 = positionOf(prev);
81
+ const a1 = positionOf(Math.min(end, value));
82
+ if (a1 <= a0) return null;
83
+ return /* @__PURE__ */ jsx("path", {
84
+ d: scale.path(a0, a1, offset),
85
+ fill: "none",
86
+ stroke: p.color,
87
+ strokeOpacity: p.opacity,
88
+ strokeWidth: width,
89
+ strokeLinecap: cap
90
+ }, end);
91
+ })
92
+ });
93
+ };
94
+ /** Coloured bands marking the cutoffs of the domain. */
95
+ var GaugeZones = ({ zones, width = 8, opacity = 1, cap = "butt", endCap = cap, offset = 0, gap = 0 }) => {
96
+ const { min, max, scale, positionOf } = useGauge();
97
+ if (zones.length === 0) return null;
98
+ const dot = (position, color, key) => {
99
+ const { x, y } = scale.point(position, offset);
100
+ return /* @__PURE__ */ jsx("circle", {
101
+ cx: x,
102
+ cy: y,
103
+ r: width / 2,
104
+ fill: color
105
+ }, key);
106
+ };
107
+ const segments = zones.reduce((acc, zone, i) => {
108
+ const prev = acc[i - 1];
109
+ const from = i === 0 || !prev ? min : prev.to;
110
+ const to = i === zones.length - 1 ? max : Math.min(Math.max(zone.to, from), max);
111
+ acc.push({
112
+ from,
113
+ to
114
+ });
115
+ return acc;
116
+ }, []);
117
+ return /* @__PURE__ */ jsx("g", {
118
+ opacity,
119
+ children: zones.map((zone, i) => {
120
+ const isFirst = i === 0;
121
+ const isLast = i === zones.length - 1;
122
+ const segment = segments[i];
123
+ if (!segment) return null;
124
+ const { from, to } = segment;
125
+ const a0 = positionOf(from) + (isFirst ? 0 : gap / 2);
126
+ const a1 = positionOf(to) - (isLast ? 0 : gap / 2);
127
+ if (a1 <= a0) return null;
128
+ const startCap = isFirst ? endCap : cap;
129
+ const finishCap = isLast ? endCap : cap;
130
+ return /* @__PURE__ */ jsxs("g", { children: [
131
+ /* @__PURE__ */ jsx("path", {
132
+ d: scale.path(a0, a1, offset),
133
+ fill: "none",
134
+ stroke: zone.color,
135
+ strokeWidth: width,
136
+ strokeLinecap: "butt"
137
+ }),
138
+ startCap === "round" && dot(a0, zone.color, "start"),
139
+ finishCap === "round" && dot(a1, zone.color, "end")
140
+ ] }, to);
141
+ })
142
+ });
143
+ };
144
+ //#endregion
145
+ export { GaugeArc, GaugeStack, GaugeTrack, GaugeZones };
@@ -0,0 +1,134 @@
1
+ import { GaugeScale } from './scale';
2
+ import { GaugeTransition } from './transition';
3
+ import * as React from 'react';
4
+ /**
5
+ * A span of the domain rather than a single reading: a forecast's low and
6
+ * high, a target band, a sleep window. Pass it as a gauge's `value` and the
7
+ * arc fills between the two ends.
8
+ */
9
+ export type GaugeRange = readonly [from: number, to: number];
10
+ /** The reading and domain, the same on every gauge whatever its shape. */
11
+ export type GaugeReading = {
12
+ /**
13
+ * Value being drawn this frame, clamped to the domain. Animated when the
14
+ * gauge has a transition. For a range, its far end.
15
+ */
16
+ value: number;
17
+ /** Value the gauge is heading for; equals `value` once settled. */
18
+ target: number;
19
+ /**
20
+ * Where a range starts, animated alongside `value`. On a single value it
21
+ * is `min`, so the stretch from here to `value` is the fill either way.
22
+ */
23
+ from: number;
24
+ /** Where `from` is heading for. */
25
+ targetFrom: number;
26
+ /** Whether the gauge shows a range rather than a single value. */
27
+ range: boolean;
28
+ /**
29
+ * Whether the domain is a closed ring, as the root's `wrap` sets. A range
30
+ * on one runs forward from `from` to `value`, across the seam if need be.
31
+ */
32
+ wrap: boolean;
33
+ min: number;
34
+ max: number;
35
+ };
36
+ /** What a round `Gauge` adds: the angles and radius its scale is bent to. */
37
+ export type GaugeRoundShape = {
38
+ shape: 'round';
39
+ startAngle: number;
40
+ endAngle: number;
41
+ /** Radius of the gauge's reference circle in SVG units. */
42
+ radius: number;
43
+ /** Maps a domain value onto a gauge angle. The same as `positionOf`. */
44
+ angleOf: (value: number) => number;
45
+ };
46
+ export type GaugeContextValue = GaugeReading & GaugeRoundShape & {
47
+ /**
48
+ * The arc the domain is laid out along, which every part that draws
49
+ * along it asks for its geometry.
50
+ */
51
+ scale: GaugeScale;
52
+ /** Maps a domain value onto a position on the scale. */
53
+ positionOf: (value: number) => number;
54
+ };
55
+ export declare const GaugeContext: React.Context<GaugeContextValue | null>;
56
+ export declare const useGauge: () => GaugeContextValue;
57
+ /**
58
+ * Alias of `useGauge` for parts that historically required a round root
59
+ * (needle). All public gauges are round; linear bars stay on `Slider`.
60
+ */
61
+ export declare const useRoundGauge: (part: string) => GaugeContextValue;
62
+ /** The reading, domain and animation every gauge root takes. */
63
+ export type GaugeReadingProps = {
64
+ /**
65
+ * The reading, or a `[from, to]` range to fill between. On an open domain
66
+ * the two ends are put in order; on a wrapped one the range runs forward
67
+ * from `from`, so `[22, 6]` on a 24-hour ring is the night, not the day.
68
+ * @defaultValue Required — no default.
69
+ */
70
+ value: number | GaugeRange;
71
+ /**
72
+ * Lower bound of the domain.
73
+ * @defaultValue 0
74
+ */
75
+ min?: number;
76
+ /**
77
+ * Upper bound of the domain.
78
+ * @defaultValue 100
79
+ */
80
+ max?: number;
81
+ /**
82
+ * Animate towards new values. `true` uses a gentle spring; pass a
83
+ * `GaugeTransition` to tune it or switch to a timed tween. Omit for
84
+ * instant updates.
85
+ * @defaultValue undefined — instant updates
86
+ */
87
+ transition?: GaugeTransition | boolean;
88
+ /**
89
+ * Where the value starts on mount when animating, for a sweep-in effect.
90
+ * A single number starts both ends of a range there, so it opens out.
91
+ * @defaultValue undefined — starts at the target value
92
+ */
93
+ initialValue?: number | GaugeRange;
94
+ /**
95
+ * Treat the domain as circular, as a compass rose or clock face is: the
96
+ * value is mapped into the domain modulo its span and animates the
97
+ * shortest way round, so a heading crossing north never swings the long
98
+ * way back. Meant for closed rings, where `min` and `max` read the same.
99
+ * @defaultValue false
100
+ */
101
+ wrap?: boolean;
102
+ };
103
+ /** The domain, geometry and animation a round gauge takes. */
104
+ export type GaugeDomainProps = GaugeReadingProps & {
105
+ /**
106
+ * Gauge degrees, clockwise from six o'clock — sweep start.
107
+ * @defaultValue 40
108
+ */
109
+ startAngle?: number;
110
+ /**
111
+ * Gauge degrees, clockwise from six o'clock — sweep end.
112
+ * @defaultValue 320
113
+ */
114
+ endAngle?: number;
115
+ /**
116
+ * Reference radius in SVG units. Every child positions itself against this.
117
+ * @defaultValue 200
118
+ */
119
+ radius?: number;
120
+ /**
121
+ * Turn the whole scale this many degrees clockwise, the way a compass card
122
+ * turns under a fixed lubber line. Animated by `transition`, always the
123
+ * shortest way round.
124
+ * @defaultValue 0
125
+ */
126
+ rotate?: number;
127
+ };
128
+ /**
129
+ * The context a round gauge root publishes to its children. Shared by
130
+ * `Gauge` and `GaugeInset` so a gauge nested inside another animates and
131
+ * maps values exactly like a top-level one, and so the defaults live in one
132
+ * place.
133
+ */
134
+ export declare const useGaugeContextValue: ({ startAngle, endAngle, radius, rotate, ...domain }: GaugeDomainProps) => GaugeContextValue & GaugeRoundShape;
@@ -0,0 +1,83 @@
1
+ "use client";
2
+ import { clamp, foldValue, valueToAngle } from "./math.js";
3
+ import { arcScale } from "./scale.js";
4
+ import { useAnimatedValue } from "./use-animated-value.js";
5
+ import * as React from "react";
6
+ //#region src/components/gauge/context.tsx
7
+ var GaugeContext = React.createContext(null);
8
+ var useGauge = () => {
9
+ const ctx = React.useContext(GaugeContext);
10
+ if (!ctx) throw new Error("Gauge primitives must be rendered inside <Gauge>.");
11
+ return ctx;
12
+ };
13
+ /**
14
+ * Alias of `useGauge` for parts that historically required a round root
15
+ * (needle). All public gauges are round; linear bars stay on `Slider`.
16
+ */
17
+ var useRoundGauge = (part) => {
18
+ return useGauge();
19
+ };
20
+ /**
21
+ * Resolves the domain and follows the value: the half of a gauge's context
22
+ * that is the same whatever its shape.
23
+ */
24
+ var useGaugeReading = ({ value, min = 0, max = 100, transition, initialValue, wrap = false }) => {
25
+ const lo = Math.min(min, max);
26
+ const hi = Math.max(min, max);
27
+ const span = hi - lo;
28
+ const fold = (v) => foldValue(v, lo, hi);
29
+ const settle = wrap ? fold : (v) => clamp(v, lo, hi);
30
+ const range = typeof value !== "number";
31
+ const ends = (pair) => wrap ? [settle(pair[0]), settle(pair[1])] : [settle(Math.min(pair[0], pair[1])), settle(Math.max(pair[0], pair[1]))];
32
+ const [targetFrom, target] = range ? ends(value) : [min, settle(value)];
33
+ let initialFrom;
34
+ let initialTo;
35
+ if (initialValue === void 0) {
36
+ initialFrom = void 0;
37
+ initialTo = void 0;
38
+ } else if (typeof initialValue === "number") {
39
+ initialFrom = settle(initialValue);
40
+ initialTo = settle(initialValue);
41
+ } else if (range) [initialFrom, initialTo] = ends(initialValue);
42
+ else {
43
+ initialFrom = void 0;
44
+ initialTo = settle(initialValue[1]);
45
+ }
46
+ const shown = useAnimatedValue(target, transition, span, initialTo, wrap);
47
+ const shownFrom = useAnimatedValue(targetFrom, transition, span, range ? initialFrom : void 0, wrap);
48
+ return {
49
+ value: wrap ? fold(shown) : shown,
50
+ target,
51
+ from: range && wrap ? fold(shownFrom) : shownFrom,
52
+ targetFrom,
53
+ range,
54
+ wrap,
55
+ min,
56
+ max
57
+ };
58
+ };
59
+ /**
60
+ * The context a round gauge root publishes to its children. Shared by
61
+ * `Gauge` and `GaugeInset` so a gauge nested inside another animates and
62
+ * maps values exactly like a top-level one, and so the defaults live in one
63
+ * place.
64
+ */
65
+ var useGaugeContextValue = ({ startAngle = 40, endAngle = 320, radius = 200, rotate = 0, ...domain }) => {
66
+ const reading = useGaugeReading(domain);
67
+ const turned = useAnimatedValue(rotate, domain.transition, 360, void 0, true);
68
+ const a0 = startAngle + turned;
69
+ const a1 = endAngle + turned;
70
+ const angleOf = (v) => valueToAngle(v, reading.min, reading.max, a0, a1);
71
+ return {
72
+ ...reading,
73
+ shape: "round",
74
+ scale: arcScale(radius, a0, a1),
75
+ positionOf: angleOf,
76
+ startAngle: a0,
77
+ endAngle: a1,
78
+ radius,
79
+ angleOf
80
+ };
81
+ };
82
+ //#endregion
83
+ export { GaugeContext, useGauge, useGaugeContextValue, useRoundGauge };