@viraui/react 2026.11.0-next.1 → 2026.11.0-next.2

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.
@@ -0,0 +1,10 @@
1
+ import * as React from 'react';
2
+ type TooltipPortalDemoProps = {
3
+ value?: number;
4
+ min?: number;
5
+ max?: number;
6
+ label?: string;
7
+ };
8
+ /** Forced-open tooltip portalled into a custom host (iframe / sandbox body). */
9
+ export declare function TooltipPortalDemo({ value, min, max, label }: TooltipPortalDemoProps): React.JSX.Element;
10
+ export {};
@@ -1 +1 @@
1
- @layer viraui.preflight, viraui.components, viraui.overrides;@layer viraui.components{@property --vui-gauge-aspect-ratio{syntax:"*";inherits:false;initial-value:1}.viraui__gauge-module__Gauge_XrCKK{overflow:visible;color:var(--global-foreground);&[data-fit=square]{inline-size:100%;block-size:100%}&[data-fit=content]{inline-size:100%;max-block-size:100%;aspect-ratio:var(--vui-gauge-aspect-ratio)}}.viraui__gauge-module__Control_33uqc{position:relative;touch-action:none;-webkit-user-select:none;user-select:none;outline:none;cursor:grab;aspect-ratio:1;border-radius:var(--radius-full);&[data-disabled=true]{cursor:default}&:not([data-disabled=true]):active{cursor:grabbing}&:focus-visible,&:has(:focus-visible:not([data-pointer])){outline:var(--outline);outline-offset:var(--outline-offset)}}.viraui__gauge-module__VisuallyHidden_LUbhj{position:absolute;inline-size:1px;block-size:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0;&:focus-visible{outline:none}}.viraui__gauge-module__TooltipPositioner_5wZsF{position:absolute;inset-block-start:0;inset-inline-start:0;z-index:50;pointer-events:none}.viraui__gauge-module__Tooltip_BRUR6{box-sizing:border-box;inline-size:max-content;max-inline-size:20rem;padding-block:var(--space-2xs);padding-inline:var(--space-s);border-radius:var(--radius-m);background:var(--global-foreground);color:var(--global-background);font-size:var(--font-scale-body-s);animation:viraui__gauge-module__gauge-tooltip-in_5JTLs var(--duration-fast) var(--easing-entrance);@media (prefers-reduced-motion:reduce){animation:none}}.viraui__gauge-module__TooltipBody_44-M9{display:flex;flex-direction:column;align-items:center;line-height:1.15}.viraui__gauge-module__TooltipLabel_T-Hp5{opacity:.7}.viraui__gauge-module__TooltipValue_gAqRL{font-size:var(--font-scale-body-m);font-weight:600;font-variation-settings:"wght" 600;font-variant-numeric:tabular-nums}.viraui__gauge-module__TooltipUnit_iIW0i{margin-inline-start:var(--space-2xs);font-size:var(--font-scale-body-s);font-weight:500;font-variation-settings:"wght" 500;opacity:.7}@keyframes viraui__gauge-module__gauge-tooltip-in_5JTLs{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}}
1
+ @layer viraui.preflight, viraui.components, viraui.overrides;@layer viraui.components{@property --vui-gauge-aspect-ratio{syntax:"*";inherits:false;initial-value:1}.viraui__gauge-module__Gauge_XrCKK{overflow:visible;color:var(--global-foreground);&[data-fit=square]{inline-size:100%;block-size:100%}&[data-fit=content]{inline-size:100%;max-block-size:100%;aspect-ratio:var(--vui-gauge-aspect-ratio)}}.viraui__gauge-module__Control_33uqc{position:relative;touch-action:none;-webkit-user-select:none;user-select:none;outline:none;cursor:grab;aspect-ratio:1;border-radius:var(--radius-full);&[data-disabled=true]{cursor:default}&:not([data-disabled=true]):active{cursor:grabbing}&:focus-visible,&:has(:focus-visible:not([data-pointer])){outline:var(--outline);outline-offset:var(--outline-offset)}}.viraui__gauge-module__VisuallyHidden_LUbhj{position:absolute;inline-size:1px;block-size:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0;&:focus-visible{outline:none}}.viraui__gauge-module__TooltipPositioner_5wZsF{position:absolute;inset-block-start:0;inset-inline-start:0;z-index:50;pointer-events:none}.viraui__gauge-module__Tooltip_BRUR6{box-sizing:border-box;inline-size:max-content;max-inline-size:20rem;padding-block:var(--space-2xs);padding-inline:var(--space-s);border-radius:var(--radius-s);background:var(--global-foreground);color:var(--global-background);font-size:var(--font-scale-body-s);animation:viraui__gauge-module__gauge-tooltip-in_5JTLs var(--duration-fast) var(--easing-entrance);@media (prefers-reduced-motion:reduce){animation:none}}.viraui__gauge-module__TooltipBody_44-M9{display:flex;flex-direction:column;align-items:center;line-height:1.15}.viraui__gauge-module__TooltipLabel_T-Hp5{opacity:.7}.viraui__gauge-module__TooltipValue_gAqRL{font-size:var(--font-scale-body-m);font-weight:600;font-variation-settings:"wght" 600;font-variant-numeric:tabular-nums}.viraui__gauge-module__TooltipUnit_iIW0i{margin-inline-start:var(--space-2xs);font-size:var(--font-scale-body-s);font-weight:500;font-variation-settings:"wght" 500;opacity:.7}@keyframes viraui__gauge-module__gauge-tooltip-in_5JTLs{0%{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}}
@@ -61,6 +61,13 @@ export type GaugeTooltipProps = {
61
61
  * @defaultValue undefined
62
62
  */
63
63
  open?: boolean;
64
+ /**
65
+ * DOM node the bubble portals into. Defaults to `document.body`. Pass the
66
+ * sandbox / iframe body (or another host) when the dial is not in the same
67
+ * document as the page shell — otherwise the bubble paints off-canvas.
68
+ * @defaultValue `document.body`
69
+ */
70
+ container?: HTMLElement | null;
64
71
  /**
65
72
  * Small line above the value. Leave out for the value on its own.
66
73
  * @defaultValue undefined
@@ -110,7 +110,7 @@ var DefaultContent = ({ text, label, unit }) => /* @__PURE__ */ jsxs("div", {
110
110
  * transform, so it is never clipped by the gauge and keeps its own CSS size
111
111
  * however the gauge, or an inset it sits in, is scaled.
112
112
  */
113
- var GaugeTooltip = ({ position = "value", side = "auto", at, offset = 24, gap = 8, avoidCollisions = true, collisionPadding = 8, open, label, unit, decimals = 0, format, separator = "–", className, style, children }) => {
113
+ var GaugeTooltip = ({ position = "value", side = "auto", at, offset = 24, gap = 8, avoidCollisions = true, collisionPadding = 8, open, container = null, label, unit, decimals = 0, format, separator = "–", className, style, children }) => {
114
114
  const gauge = useGauge();
115
115
  const isClient = useIsClient();
116
116
  const anchor = React.useRef(null);
@@ -181,6 +181,8 @@ var GaugeTooltip = ({ position = "value", side = "auto", at, offset = 24, gap =
181
181
  el.style.visibility = "hidden";
182
182
  return;
183
183
  }
184
+ const doc = el.ownerDocument;
185
+ const win = doc.defaultView ?? window;
184
186
  const size = {
185
187
  width: el.offsetWidth,
186
188
  height: el.offsetHeight
@@ -189,8 +191,8 @@ var GaugeTooltip = ({ position = "value", side = "auto", at, offset = 24, gap =
189
191
  let atPoint = corner(p, d, size, gap);
190
192
  if (avoidCollisions) {
191
193
  const view = {
192
- width: document.documentElement.clientWidth,
193
- height: document.documentElement.clientHeight
194
+ width: doc.documentElement.clientWidth,
195
+ height: doc.documentElement.clientHeight
194
196
  };
195
197
  const pad = collisionPadding;
196
198
  const spill = (spot, axis) => {
@@ -223,8 +225,8 @@ var GaugeTooltip = ({ position = "value", side = "auto", at, offset = 24, gap =
223
225
  }
224
226
  }
225
227
  el.style.visibility = "";
226
- el.style.left = `${atPoint.x + window.scrollX}px`;
227
- el.style.top = `${atPoint.y + window.scrollY}px`;
228
+ el.style.left = `${atPoint.x + win.scrollX}px`;
229
+ el.style.top = `${atPoint.y + win.scrollY}px`;
228
230
  });
229
231
  React.useEffect(() => {
230
232
  const svg = anchor.current?.ownerSVGElement;
@@ -319,7 +321,8 @@ var GaugeTooltip = ({ position = "value", side = "auto", at, offset = 24, gap =
319
321
  label,
320
322
  unit
321
323
  });
322
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("g", { ref: anchor }), shown && isClient && createPortal(/* @__PURE__ */ jsx("div", {
324
+ const portalRoot = container ?? (typeof document !== "undefined" ? document.body : null);
325
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("g", { ref: anchor }), shown && isClient && portalRoot && createPortal(/* @__PURE__ */ jsx("div", {
323
326
  ref: box,
324
327
  className: gauge_module_default.TooltipPositioner,
325
328
  children: /* @__PURE__ */ jsx("div", {
@@ -330,7 +333,7 @@ var GaugeTooltip = ({ position = "value", side = "auto", at, offset = 24, gap =
330
333
  className: clsx(gauge_module_default.Tooltip, className),
331
334
  children: content
332
335
  })
333
- }), document.body)] });
336
+ }), portalRoot)] });
334
337
  };
335
338
  //#endregion
336
339
  export { GaugeTooltip };
@@ -417,6 +417,9 @@
417
417
  <prop name="GaugeTooltip.open" kind="react">
418
418
  <description>Force open or shut instead of hover/press/focus.</description>
419
419
  </prop>
420
+ <prop name="GaugeTooltip.container" kind="react">
421
+ <description>Portal host for the bubble. Default `document.body`. Pass an iframe/sandbox body when the dial is not in the page document.</description>
422
+ </prop>
420
423
  <prop name="GaugeTooltip.label" kind="react">
421
424
  <description>Small line above the value.</description>
422
425
  </prop>
@@ -6,6 +6,7 @@
6
6
  <do>When you need paint, pass theme CSS vars as strings on `color` (and Needle `tailColor`, TickFill `dimColor`, Dot `haloColor`, Zones `zones[].color`): `--global-*` for chrome/labels (`foreground`, `background`, `primary`, `contrast`, `muted`), `--highlight-*` for status/decorative bands (`red`, `green`, `yellow`, `cyan`, `acid`, `blue`, `purple`, …)</do>
7
7
  <do>Zone-driven readings: put token colors on `zones[].color`, then reuse with `zoneColor(zones, value)` (or `zoneCutoffs`) on Arc / Marks so the live fill matches the active band</do>
8
8
  <do>Pass `label` on read-only roots (or `aria-label`) and always pass `label` on `GaugeControl`</do>
9
+ <do>When the dial lives in an iframe or docs sandbox, pass `GaugeTooltip` `container` to that document's `body` so the bubble portals with the dial</do>
9
10
  <do>Prefer `Meter` for compact horizontal capacity chrome; prefer `Slider` for linear interactive ranges; prefer Gauge when polar composition or dial Control is needed</do>
10
11
  <do>Needle visual style prop is `kind` (not upstream `style`) to avoid clashing with React `style`</do>
11
12
  <dont>Using Gauge / GaugeControl as a linear bar — use Slider (or Meter / LinearProgress for non-interactive)</dont>
@@ -1,5 +1,5 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
- <index package="@viraui/react" version="2026.11.0-next.1" generated="2026-10-28T10:01:21.307Z">
2
+ <index package="@viraui/react" version="2026.11.0-next.2" generated="2026-10-28T10:39:16.158Z">
3
3
  <categories>
4
4
  <category id="layout" />
5
5
  <category id="action" />
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@viraui/react",
3
- "version": "2026.11.0-next.1",
3
+ "version": "2026.11.0-next.2",
4
4
  "private": false,
5
5
  "files": [
6
6
  "dist"
@@ -28,7 +28,7 @@
28
28
  },
29
29
  "devDependencies": {
30
30
  "@base-ui/react": ">=1.8.0",
31
- "@viraui/foundation": "2026.11.0-next.1",
31
+ "@viraui/foundation": "2026.11.0-next.2",
32
32
  "@viraui/icons": "2026.1.0",
33
33
  "@viraui/postcss-config": "2026.0.0",
34
34
  "@viraui/tsconfig": "2026.0.0",