@yahoo/uds 3.116.3 → 3.117.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 (150) hide show
  1. package/dist/automated-config/dist/generated/autoVariants.cjs +11 -0
  2. package/dist/automated-config/dist/generated/autoVariants.d.cts +11 -0
  3. package/dist/automated-config/dist/generated/autoVariants.d.ts +11 -0
  4. package/dist/automated-config/dist/generated/autoVariants.js +11 -0
  5. package/dist/automated-config/dist/generated/generatedConfigs.cjs +1890 -0
  6. package/dist/automated-config/dist/generated/generatedConfigs.d.cts +208 -1
  7. package/dist/automated-config/dist/generated/generatedConfigs.d.ts +208 -1
  8. package/dist/automated-config/dist/generated/generatedConfigs.js +1890 -1
  9. package/dist/automated-config/dist/generated/universalTokensConfigAuto.cjs +83 -0
  10. package/dist/automated-config/dist/generated/universalTokensConfigAuto.d.cts +2 -1
  11. package/dist/automated-config/dist/generated/universalTokensConfigAuto.d.ts +2 -1
  12. package/dist/automated-config/dist/generated/universalTokensConfigAuto.js +83 -0
  13. package/dist/cli/commands/sync.cjs +6 -0
  14. package/dist/cli/commands/sync.js +6 -0
  15. package/dist/components/SvgFloatingOverlay.cjs +122 -0
  16. package/dist/components/SvgFloatingOverlay.d.cts +42 -0
  17. package/dist/components/SvgFloatingOverlay.d.ts +42 -0
  18. package/dist/components/SvgFloatingOverlay.js +120 -0
  19. package/dist/components/client/Menu/Menu.ItemCheckbox.d.cts +1 -1
  20. package/dist/components/client/Menu/Menu.ItemCheckbox.d.ts +1 -1
  21. package/dist/components/client/Popover/Popover.cjs +96 -0
  22. package/dist/components/client/Popover/Popover.d.cts +55 -0
  23. package/dist/components/client/Popover/Popover.d.ts +55 -0
  24. package/dist/components/client/Popover/Popover.js +94 -0
  25. package/dist/components/client/Popover/PopoverContent.cjs +175 -0
  26. package/dist/components/client/Popover/PopoverContent.d.cts +26 -0
  27. package/dist/components/client/Popover/PopoverContent.d.ts +26 -0
  28. package/dist/components/client/Popover/PopoverContent.js +173 -0
  29. package/dist/components/client/Popover/PopoverContext.cjs +11 -0
  30. package/dist/components/client/Popover/PopoverContext.d.cts +21 -0
  31. package/dist/components/client/Popover/PopoverContext.d.ts +21 -0
  32. package/dist/components/client/Popover/PopoverContext.js +9 -0
  33. package/dist/components/client/Popover/PopoverTrigger.cjs +26 -0
  34. package/dist/components/client/Popover/PopoverTrigger.d.cts +14 -0
  35. package/dist/components/client/Popover/PopoverTrigger.d.ts +14 -0
  36. package/dist/components/client/Popover/PopoverTrigger.js +24 -0
  37. package/dist/components/client/Popover/UDSPopoverConfigProvider.cjs +40 -0
  38. package/dist/components/client/Popover/UDSPopoverConfigProvider.d.cts +28 -0
  39. package/dist/components/client/Popover/UDSPopoverConfigProvider.d.ts +28 -0
  40. package/dist/components/client/Popover/UDSPopoverConfigProvider.js +37 -0
  41. package/dist/components/client/Popover/index.cjs +12 -0
  42. package/dist/components/client/Popover/index.d.cts +7 -0
  43. package/dist/components/client/Popover/index.d.ts +7 -0
  44. package/dist/components/client/Popover/index.js +8 -0
  45. package/dist/components/client/Toast/Toast.cjs +3 -3
  46. package/dist/components/client/Toast/Toast.d.cts +1 -1
  47. package/dist/components/client/Toast/Toast.d.ts +1 -1
  48. package/dist/components/client/Toast/Toast.js +3 -3
  49. package/dist/components/client/Toast/index.d.cts +1 -1
  50. package/dist/components/client/Toast/index.d.ts +1 -1
  51. package/dist/components/client/Tooltip/Tooltip.cjs +6 -6
  52. package/dist/components/client/Tooltip/Tooltip.d.cts +1 -1
  53. package/dist/components/client/Tooltip/Tooltip.d.ts +1 -1
  54. package/dist/components/client/Tooltip/Tooltip.js +6 -6
  55. package/dist/components/client/Tooltip/TooltipContent.cjs +33 -96
  56. package/dist/components/client/Tooltip/TooltipContent.js +34 -97
  57. package/dist/components/client/Tooltip/tooltipContext.cjs +0 -1
  58. package/dist/components/client/Tooltip/tooltipContext.d.cts +1 -2
  59. package/dist/components/client/Tooltip/tooltipContext.d.ts +1 -2
  60. package/dist/components/client/Tooltip/tooltipContext.js +0 -1
  61. package/dist/components/client/index.cjs +8 -0
  62. package/dist/components/client/index.d.cts +10 -5
  63. package/dist/components/client/index.d.ts +10 -5
  64. package/dist/components/client/index.js +5 -1
  65. package/dist/components/client/providers/UDSConfigProvider.cjs +6 -2
  66. package/dist/components/client/providers/UDSConfigProvider.d.cts +2 -1
  67. package/dist/components/client/providers/UDSConfigProvider.d.ts +2 -1
  68. package/dist/components/client/providers/UDSConfigProvider.js +6 -2
  69. package/dist/components/experimental/client/{Popover.cjs → ExperimentalPopover.cjs} +10 -1
  70. package/dist/components/experimental/client/ExperimentalPopover.d.cts +40 -0
  71. package/dist/components/experimental/client/ExperimentalPopover.d.ts +40 -0
  72. package/dist/components/experimental/client/{Popover.js → ExperimentalPopover.js} +10 -1
  73. package/dist/components/experimental/client/index.cjs +10 -10
  74. package/dist/components/experimental/client/index.d.cts +2 -2
  75. package/dist/components/experimental/client/index.d.ts +2 -2
  76. package/dist/components/experimental/client/index.js +1 -1
  77. package/dist/components/experimental/index.cjs +19 -19
  78. package/dist/components/experimental/index.d.cts +2 -2
  79. package/dist/components/experimental/index.d.ts +2 -2
  80. package/dist/components/experimental/index.js +1 -1
  81. package/dist/components/index.cjs +12 -0
  82. package/dist/components/index.d.cts +9 -5
  83. package/dist/components/index.d.ts +9 -5
  84. package/dist/components/index.js +9 -1
  85. package/dist/config/dist/index.cjs +84 -1
  86. package/dist/config/dist/index.js +84 -1
  87. package/dist/hooks/useSvgFloatingContent.cjs +236 -0
  88. package/dist/hooks/useSvgFloatingContent.d.cts +74 -0
  89. package/dist/hooks/useSvgFloatingContent.d.ts +74 -0
  90. package/dist/{components/client/Tooltip/useTooltipContent.js → hooks/useSvgFloatingContent.js} +94 -44
  91. package/dist/index.cjs +21 -10
  92. package/dist/index.d.cts +15 -10
  93. package/dist/index.d.ts +15 -10
  94. package/dist/index.js +16 -11
  95. package/dist/runtime/index.cjs +2 -0
  96. package/dist/runtime/index.d.cts +3 -2
  97. package/dist/runtime/index.d.ts +3 -2
  98. package/dist/runtime/index.js +2 -1
  99. package/dist/runtime/popoverConfig.cjs +36 -0
  100. package/dist/runtime/popoverConfig.d.cts +25 -0
  101. package/dist/runtime/popoverConfig.d.ts +25 -0
  102. package/dist/runtime/popoverConfig.js +35 -0
  103. package/dist/runtime/udsConfig.cjs +3 -1
  104. package/dist/runtime/udsConfig.d.cts +3 -1
  105. package/dist/runtime/udsConfig.d.ts +3 -1
  106. package/dist/runtime/udsConfig.js +3 -1
  107. package/dist/styles/styler.d.cts +114 -103
  108. package/dist/styles/styler.d.ts +114 -103
  109. package/dist/styles/variants.d.cts +33 -0
  110. package/dist/styles/variants.d.ts +33 -0
  111. package/dist/tailwind/dist/tailwind/plugins/components.cjs +1 -0
  112. package/dist/tailwind/dist/tailwind/plugins/components.js +2 -1
  113. package/dist/tokens/automation/configs/index.cjs +1 -0
  114. package/dist/tokens/automation/configs/index.d.cts +2 -2
  115. package/dist/tokens/automation/configs/index.d.ts +2 -2
  116. package/dist/tokens/automation/configs/index.js +2 -2
  117. package/dist/tokens/automation/index.cjs +1 -0
  118. package/dist/tokens/automation/index.d.cts +2 -2
  119. package/dist/tokens/automation/index.d.ts +2 -2
  120. package/dist/tokens/automation/index.js +2 -2
  121. package/dist/tokens/index.cjs +2 -1
  122. package/dist/tokens/index.d.cts +3 -3
  123. package/dist/tokens/index.d.ts +3 -3
  124. package/dist/tokens/index.js +3 -3
  125. package/dist/tokens/types.d.cts +2 -2
  126. package/dist/tokens/types.d.ts +2 -2
  127. package/dist/types/dist/index.d.cts +51 -1
  128. package/dist/types/dist/index.d.ts +51 -1
  129. package/dist/uds/generated/componentData.cjs +1296 -1134
  130. package/dist/uds/generated/componentData.js +1262 -1136
  131. package/dist/uds/generated/tailwindPurge.cjs +55 -27
  132. package/dist/uds/generated/tailwindPurge.js +55 -27
  133. package/dist/utils/parseShadow.cjs +120 -0
  134. package/dist/utils/parseShadow.d.cts +33 -0
  135. package/dist/utils/parseShadow.d.ts +33 -0
  136. package/dist/utils/parseShadow.js +118 -0
  137. package/dist/utils/svgFloatingContentUtils.cjs +121 -0
  138. package/dist/{components/client/Tooltip/util.d.cts → utils/svgFloatingContentUtils.d.cts} +25 -62
  139. package/dist/{components/client/Tooltip/util.d.ts → utils/svgFloatingContentUtils.d.ts} +25 -62
  140. package/dist/utils/svgFloatingContentUtils.js +117 -0
  141. package/generated/componentData.json +1628 -1469
  142. package/generated/tailwindPurge.ts +4 -4
  143. package/package.json +1 -1
  144. package/dist/components/client/Tooltip/useTooltipContent.cjs +0 -186
  145. package/dist/components/client/Tooltip/useTooltipContent.d.cts +0 -66
  146. package/dist/components/client/Tooltip/useTooltipContent.d.ts +0 -66
  147. package/dist/components/client/Tooltip/util.cjs +0 -248
  148. package/dist/components/client/Tooltip/util.js +0 -240
  149. package/dist/components/experimental/client/Popover.d.cts +0 -29
  150. package/dist/components/experimental/client/Popover.d.ts +0 -29
@@ -0,0 +1,33 @@
1
+
2
+ //#region src/utils/parseShadow.d.ts
3
+ interface ParsedShadow {
4
+ /** Horizontal offset in px. */
5
+ x: number;
6
+ /** Vertical offset in px. */
7
+ y: number;
8
+ /** Blur radius in px. */
9
+ blur: number;
10
+ /** Spread radius in px (always 0 for drop-shadow filter functions). */
11
+ spread: number;
12
+ /** The raw CSS color string (e.g. `rgb(0 0 0 / 0.12)`, `#ff0000`, `red`). */
13
+ color: string;
14
+ }
15
+ /**
16
+ * Parses a CSS shadow value — which may contain multiple comma-separated
17
+ * layers — into an array of {@link ParsedShadow} objects.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * parseShadow('0px 4px 8px 0px rgb(0 0 0 / 0.12)')
22
+ * // → [{ x: 0, y: 4, blur: 8, spread: 0, color: 'rgb(0 0 0 / 0.12)' }]
23
+ *
24
+ * parseShadow('0px 4px 8px rgb(0 0 0 / 0.12), 0px 1px 2px rgba(0, 0, 0, 0.08)')
25
+ * // → [
26
+ * // { x: 0, y: 4, blur: 8, spread: 0, color: 'rgb(0 0 0 / 0.12)' },
27
+ * // { x: 0, y: 1, blur: 2, spread: 0, color: 'rgba(0, 0, 0, 0.08)' },
28
+ * // ]
29
+ * ```
30
+ */
31
+ declare function parseShadow(shadowValue: string): ParsedShadow[];
32
+ //#endregion
33
+ export { type ParsedShadow, parseShadow };
@@ -0,0 +1,118 @@
1
+ /*! © 2026 Yahoo, Inc. UDS v0.0.0-development */
2
+ //#region src/utils/parseShadow.ts
3
+ /**
4
+ * Splits a CSS multi-shadow value (comma-separated) into individual
5
+ * shadow strings, correctly handling commas nested inside color functions
6
+ * like `rgb()`, `rgba()`, `hsl()`, etc.
7
+ */
8
+ function splitShadows(value) {
9
+ const shadows = [];
10
+ let current = "";
11
+ let depth = 0;
12
+ for (let i = 0; i < value.length; i++) {
13
+ const char = value[i];
14
+ if (char === "(") {
15
+ depth++;
16
+ current += char;
17
+ } else if (char === ")") {
18
+ depth--;
19
+ current += char;
20
+ } else if (char === "," && depth === 0) {
21
+ if (current.trim()) shadows.push(current.trim());
22
+ current = "";
23
+ } else current += char;
24
+ }
25
+ if (current.trim()) shadows.push(current.trim());
26
+ return shadows;
27
+ }
28
+ /**
29
+ * Returns `true` if the token is a CSS color value.
30
+ *
31
+ * In a box-shadow context (after filtering `inset`), every token is either a
32
+ * length or a color. Colors always start with `#` (hex), a letter (named
33
+ * colors like `red`, `transparent`, `currentcolor`), or are a CSS function
34
+ * containing `(` (e.g. `rgb(…)`, `hsl(…)`, `oklch(…)`).
35
+ */
36
+ function isColorToken(token) {
37
+ const ch = token[0];
38
+ return ch !== "-" && ch !== "." && (ch < "0" || ch > "9");
39
+ }
40
+ /**
41
+ * Splits a CSS value on whitespace, but keeps parenthesized groups
42
+ * (e.g. `rgb(0, 0, 0)`) together as single tokens.
43
+ */
44
+ function tokenizeCSSValue(value) {
45
+ const tokens = [];
46
+ let current = "";
47
+ let depth = 0;
48
+ for (const char of value) {
49
+ if (char === "(") depth++;
50
+ if (char === ")") depth--;
51
+ if (char === " " && depth === 0) {
52
+ if (current) tokens.push(current);
53
+ current = "";
54
+ } else current += char;
55
+ }
56
+ if (current) tokens.push(current);
57
+ return tokens;
58
+ }
59
+ /**
60
+ * Parses a single CSS shadow value string into its constituent parts.
61
+ *
62
+ * Handles all standard CSS box-shadow / drop-shadow permutations:
63
+ * - Color before or after length values (`red 0 4px 8px` or `0 4px 8px red`)
64
+ * - Optional blur and spread radii (defaults to 0)
65
+ * - `inset` keyword at any position (silently ignored)
66
+ * - All color formats: named, hex (3/4/6/8-digit), rgb/rgba, hsl/hsla, oklch,
67
+ * oklab, lab, lch, color(), color-mix(), light-dark(), etc.
68
+ * - `none` / empty values → zeroed-out result with `transparent` color
69
+ */
70
+ function parseSingleShadow(shadow) {
71
+ const value = shadow.trim();
72
+ if (!value || value.toLowerCase() === "none") return {
73
+ x: 0,
74
+ y: 0,
75
+ blur: 0,
76
+ spread: 0,
77
+ color: "transparent"
78
+ };
79
+ const tokens = tokenizeCSSValue(value);
80
+ const lengths = [];
81
+ const colorParts = [];
82
+ for (const token of tokens) {
83
+ if (token.toLowerCase() === "inset") continue;
84
+ if (isColorToken(token)) colorParts.push(token);
85
+ else lengths.push(token);
86
+ }
87
+ return {
88
+ x: lengths.length > 0 ? parseFloat(lengths[0]) : 0,
89
+ y: lengths.length > 1 ? parseFloat(lengths[1]) : 0,
90
+ blur: lengths.length > 2 ? parseFloat(lengths[2]) : 0,
91
+ spread: lengths.length > 3 ? parseFloat(lengths[3]) : 0,
92
+ color: colorParts.length > 0 ? colorParts.join(" ") : "rgb(0 0 0 / 1)"
93
+ };
94
+ }
95
+ /**
96
+ * Parses a CSS shadow value — which may contain multiple comma-separated
97
+ * layers — into an array of {@link ParsedShadow} objects.
98
+ *
99
+ * @example
100
+ * ```ts
101
+ * parseShadow('0px 4px 8px 0px rgb(0 0 0 / 0.12)')
102
+ * // → [{ x: 0, y: 4, blur: 8, spread: 0, color: 'rgb(0 0 0 / 0.12)' }]
103
+ *
104
+ * parseShadow('0px 4px 8px rgb(0 0 0 / 0.12), 0px 1px 2px rgba(0, 0, 0, 0.08)')
105
+ * // → [
106
+ * // { x: 0, y: 4, blur: 8, spread: 0, color: 'rgb(0 0 0 / 0.12)' },
107
+ * // { x: 0, y: 1, blur: 2, spread: 0, color: 'rgba(0, 0, 0, 0.08)' },
108
+ * // ]
109
+ * ```
110
+ */
111
+ function parseShadow(shadowValue) {
112
+ const trimmed = shadowValue.trim();
113
+ if (!trimmed || trimmed.toLowerCase() === "none") return [];
114
+ return splitShadows(trimmed).map(parseSingleShadow);
115
+ }
116
+
117
+ //#endregion
118
+ export { parseShadow };
@@ -0,0 +1,121 @@
1
+ /*! © 2026 Yahoo, Inc. UDS v0.0.0-development */
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
3
+
4
+ //#region src/utils/svgFloatingContentUtils.ts
5
+ /**
6
+ * Returns the side of the element where the arrow should appear.
7
+ * The arrow points *toward* the trigger, so it lives on the opposite
8
+ * side of the placement direction.
9
+ */
10
+ function getArrowSide(placement) {
11
+ switch (placement.split("-")[0]) {
12
+ case "bottom": return "top";
13
+ case "top": return "bottom";
14
+ case "left": return "right";
15
+ case "right": return "left";
16
+ default: return "top";
17
+ }
18
+ }
19
+ /**
20
+ * Generates an SVG `<path>` string for a rounded rectangle with an arrow
21
+ * pointing toward the trigger element.
22
+ *
23
+ * The path is traced clockwise, inserting the arrow triangle on the
24
+ * appropriate edge. The arrow extends *outside* the 0,0 → width,height
25
+ * rectangle so the host `<svg>` must use `overflow: visible`.
26
+ */
27
+ function generateSvgFloatingContentPath({ width, height, placement, borderRadius, arrowWidth, arrowHeight, arrowTipRadiusFraction, arrowCenter, offsetX: ox = 0, offsetY: oy = 0 }) {
28
+ const aw = arrowWidth / 2;
29
+ const ah = arrowHeight;
30
+ const arrowSide = getArrowSide(placement);
31
+ const arrowEdge = arrowSide === "top" || arrowSide === "bottom" ? width : height;
32
+ const otherEdge = arrowSide === "top" || arrowSide === "bottom" ? height : width;
33
+ const maxRadius = Math.min(otherEdge / 2, (arrowEdge - arrowWidth) / 2);
34
+ const r = Math.max(0, Math.min(borderRadius, maxRadius));
35
+ const minAc = r + aw;
36
+ const edgeLength = arrowSide === "top" || arrowSide === "bottom" ? width : height;
37
+ let ac = arrowCenter ?? edgeLength / 2;
38
+ ac = Math.max(minAc, Math.min(edgeLength - minAc, ac));
39
+ const edgeLen = Math.sqrt(aw * aw + ah * ah);
40
+ const tipR = r > 0 && edgeLen > 0 ? Math.min(r * arrowTipRadiusFraction, edgeLen / 2) : 0;
41
+ const daw = edgeLen > 0 ? aw / edgeLen : 0;
42
+ const dah = edgeLen > 0 ? ah / edgeLen : 0;
43
+ const d = [];
44
+ d.push(`M ${r + ox} ${oy}`);
45
+ if (arrowSide === "top") {
46
+ d.push(`L ${ac - aw + ox} ${oy}`);
47
+ if (tipR > 0) {
48
+ d.push(`L ${ac - tipR * daw + ox} ${-ah + tipR * dah + oy}`);
49
+ d.push(`Q ${ac + ox} ${-ah + oy} ${ac + tipR * daw + ox} ${-ah + tipR * dah + oy}`);
50
+ } else d.push(`L ${ac + ox} ${-ah + oy}`);
51
+ d.push(`L ${ac + aw + ox} ${oy}`);
52
+ }
53
+ d.push(`L ${width - r + ox} ${oy}`);
54
+ d.push(`A ${r} ${r} 0 0 1 ${width + ox} ${r + oy}`);
55
+ if (arrowSide === "right") {
56
+ d.push(`L ${width + ox} ${ac - aw + oy}`);
57
+ if (tipR > 0) {
58
+ d.push(`L ${width + ah - tipR * dah + ox} ${ac - tipR * daw + oy}`);
59
+ d.push(`Q ${width + ah + ox} ${ac + oy} ${width + ah - tipR * dah + ox} ${ac + tipR * daw + oy}`);
60
+ } else d.push(`L ${width + ah + ox} ${ac + oy}`);
61
+ d.push(`L ${width + ox} ${ac + aw + oy}`);
62
+ }
63
+ d.push(`L ${width + ox} ${height - r + oy}`);
64
+ d.push(`A ${r} ${r} 0 0 1 ${width - r + ox} ${height + oy}`);
65
+ if (arrowSide === "bottom") {
66
+ d.push(`L ${ac + aw + ox} ${height + oy}`);
67
+ if (tipR > 0) {
68
+ d.push(`L ${ac + tipR * daw + ox} ${height + ah - tipR * dah + oy}`);
69
+ d.push(`Q ${ac + ox} ${height + ah + oy} ${ac - tipR * daw + ox} ${height + ah - tipR * dah + oy}`);
70
+ } else d.push(`L ${ac + ox} ${height + ah + oy}`);
71
+ d.push(`L ${ac - aw + ox} ${height + oy}`);
72
+ }
73
+ d.push(`L ${r + ox} ${height + oy}`);
74
+ d.push(`A ${r} ${r} 0 0 1 ${ox} ${height - r + oy}`);
75
+ if (arrowSide === "left") {
76
+ d.push(`L ${ox} ${ac + aw + oy}`);
77
+ if (tipR > 0) {
78
+ d.push(`L ${-ah + tipR * dah + ox} ${ac + tipR * daw + oy}`);
79
+ d.push(`Q ${-ah + ox} ${ac + oy} ${-ah + tipR * dah + ox} ${ac - tipR * daw + oy}`);
80
+ } else d.push(`L ${-ah + ox} ${ac + oy}`);
81
+ d.push(`L ${ox} ${ac - aw + oy}`);
82
+ }
83
+ d.push(`L ${ox} ${r + oy}`);
84
+ d.push(`A ${r} ${r} 0 0 1 ${r + ox} ${oy}`);
85
+ d.push("Z");
86
+ return d.join(" ");
87
+ }
88
+ /**
89
+ * Computes the pixel offset the tooltip must shift so that the SVG arrow
90
+ * (after clamping) visually aligns with the Ariakit arrow's actual position.
91
+ *
92
+ * When the desired `arrowCenter` falls inside the safe zone the delta is 0.
93
+ * When it falls outside (e.g. a tiny trigger near a rounded corner), the
94
+ * returned delta tells the caller how far to translate the tooltip element
95
+ * so the clamped arrow ends up over the trigger center.
96
+ */
97
+ function getArrowClampDelta({ width, height, placement, borderRadius, arrowCenter, arrowWidth }) {
98
+ const aw = arrowWidth / 2;
99
+ const side = getArrowSide(placement);
100
+ const arrowEdge = side === "top" || side === "bottom" ? width : height;
101
+ const otherEdge = side === "top" || side === "bottom" ? height : width;
102
+ const maxRadius = Math.min(otherEdge / 2, (arrowEdge - arrowWidth) / 2);
103
+ const minAc = Math.max(0, Math.min(borderRadius, maxRadius)) + aw;
104
+ const maxAc = arrowEdge - minAc;
105
+ let delta = 0;
106
+ if (arrowCenter < minAc) delta = arrowCenter - minAc;
107
+ else if (arrowCenter > maxAc) delta = arrowCenter - maxAc;
108
+ if (side === "top" || side === "bottom") return {
109
+ x: delta,
110
+ y: 0
111
+ };
112
+ return {
113
+ x: 0,
114
+ y: delta
115
+ };
116
+ }
117
+
118
+ //#endregion
119
+ exports.generateSvgFloatingContentPath = generateSvgFloatingContentPath;
120
+ exports.getArrowClampDelta = getArrowClampDelta;
121
+ exports.getArrowSide = getArrowSide;
@@ -1,62 +1,17 @@
1
1
 
2
- //#region src/components/client/Tooltip/util.d.ts
3
- interface ParsedShadow {
4
- /** Horizontal offset in px. */
5
- x: number;
6
- /** Vertical offset in px. */
7
- y: number;
8
- /** Blur radius in px. */
9
- blur: number;
10
- /** Spread radius in px (always 0 for drop-shadow filter functions). */
11
- spread: number;
12
- /** The raw CSS color string (e.g. `rgb(0 0 0 / 0.12)`, `#ff0000`, `red`). */
13
- color: string;
14
- }
15
- /**
16
- * Parses a CSS shadow value — which may contain multiple comma-separated
17
- * layers — into an array of {@link ParsedShadow} objects.
18
- *
19
- * @example
20
- * ```ts
21
- * parseShadow('0px 4px 8px 0px rgb(0 0 0 / 0.12)')
22
- * // → [{ x: 0, y: 4, blur: 8, spread: 0, color: 'rgb(0 0 0 / 0.12)' }]
23
- *
24
- * parseShadow('0px 4px 8px rgb(0 0 0 / 0.12), 0px 1px 2px rgba(0, 0, 0, 0.08)')
25
- * // → [
26
- * // { x: 0, y: 4, blur: 8, spread: 0, color: 'rgb(0 0 0 / 0.12)' },
27
- * // { x: 0, y: 1, blur: 2, spread: 0, color: 'rgba(0, 0, 0, 0.08)' },
28
- * // ]
29
- * ```
30
- */
31
- declare function parseShadow(shadowValue: string): ParsedShadow[];
32
- /** Base width of the arrow triangle (px). */
33
- declare const ARROW_WIDTH = 12;
34
- /** Height (protrusion) of the arrow triangle from the edge (px). */
35
- declare const ARROW_HEIGHT = 8;
36
- /**
37
- * Fraction of the container border-radius applied to round the arrow tip
38
- * (0 = sharp point, 1 = same radius as the container corners).
39
- */
40
- declare const ARROW_TIP_RADIUS_FRACTION = 0.5;
41
- type ArrowSide = 'top' | 'bottom' | 'left' | 'right';
42
- /**
43
- * Returns the side of the tooltip where the arrow should appear.
44
- * The arrow points *toward* the trigger, so it lives on the opposite
45
- * side of the placement direction.
46
- */
47
- declare function getArrowSide(placement: string): ArrowSide;
48
- interface TooltipPathOptions {
2
+ //#region src/utils/svgFloatingContentUtils.d.ts
3
+ interface SVGFloatingContentPathOptions {
49
4
  width: number;
50
5
  height: number;
51
6
  placement: string;
52
7
  borderRadius: number;
53
- arrowWidth?: number;
54
- arrowHeight?: number;
8
+ arrowWidth: number;
9
+ arrowHeight: number;
10
+ /** Fraction of the container border-radius applied to round the arrow tip (0 = sharp point, 1 = same radius as the container corners). */
11
+ arrowTipRadiusFraction: number;
55
12
  /**
56
13
  * Position of the arrow center along the arrow's edge (px), measured from
57
14
  * the start of that edge (left for top/bottom sides, top for left/right).
58
- * Typically derived from the Ariakit `TooltipArrow` element's position
59
- * within the tooltip.
60
15
  *
61
16
  * Clamped so the arrow never collides with the border-radius curve
62
17
  * Falls back to the midpoint of the edge when omitted.
@@ -67,6 +22,21 @@ interface TooltipPathOptions {
67
22
  /** Shift every coordinate in the path by this many pixels on the y-axis. */
68
23
  offsetY?: number;
69
24
  }
25
+ interface ArrowClampDeltaOptions {
26
+ width: number;
27
+ height: number;
28
+ placement: string;
29
+ borderRadius: number;
30
+ arrowCenter: number;
31
+ arrowWidth: number;
32
+ }
33
+ type ArrowSide = 'top' | 'bottom' | 'left' | 'right';
34
+ /**
35
+ * Returns the side of the element where the arrow should appear.
36
+ * The arrow points *toward* the trigger, so it lives on the opposite
37
+ * side of the placement direction.
38
+ */
39
+ declare function getArrowSide(placement: string): ArrowSide;
70
40
  /**
71
41
  * Generates an SVG `<path>` string for a rounded rectangle with an arrow
72
42
  * pointing toward the trigger element.
@@ -75,25 +45,18 @@ interface TooltipPathOptions {
75
45
  * appropriate edge. The arrow extends *outside* the 0,0 → width,height
76
46
  * rectangle so the host `<svg>` must use `overflow: visible`.
77
47
  */
78
- declare function generateTooltipPath({
48
+ declare function generateSvgFloatingContentPath({
79
49
  width,
80
50
  height,
81
51
  placement,
82
52
  borderRadius,
83
53
  arrowWidth,
84
54
  arrowHeight,
55
+ arrowTipRadiusFraction,
85
56
  arrowCenter,
86
57
  offsetX: ox,
87
58
  offsetY: oy
88
- }: TooltipPathOptions): string;
89
- interface ArrowClampDeltaOptions {
90
- width: number;
91
- height: number;
92
- placement: string;
93
- borderRadius: number;
94
- arrowCenter: number;
95
- arrowWidth?: number;
96
- }
59
+ }: SVGFloatingContentPathOptions): string;
97
60
  /**
98
61
  * Computes the pixel offset the tooltip must shift so that the SVG arrow
99
62
  * (after clamping) visually aligns with the Ariakit arrow's actual position.
@@ -115,4 +78,4 @@ declare function getArrowClampDelta({
115
78
  y: number;
116
79
  };
117
80
  //#endregion
118
- export { ARROW_HEIGHT, ARROW_TIP_RADIUS_FRACTION, ARROW_WIDTH, type ArrowClampDeltaOptions, type ArrowSide, type ParsedShadow, type TooltipPathOptions, generateTooltipPath, getArrowClampDelta, getArrowSide, parseShadow };
81
+ export { generateSvgFloatingContentPath, getArrowClampDelta, getArrowSide };
@@ -1,62 +1,17 @@
1
1
 
2
- //#region src/components/client/Tooltip/util.d.ts
3
- interface ParsedShadow {
4
- /** Horizontal offset in px. */
5
- x: number;
6
- /** Vertical offset in px. */
7
- y: number;
8
- /** Blur radius in px. */
9
- blur: number;
10
- /** Spread radius in px (always 0 for drop-shadow filter functions). */
11
- spread: number;
12
- /** The raw CSS color string (e.g. `rgb(0 0 0 / 0.12)`, `#ff0000`, `red`). */
13
- color: string;
14
- }
15
- /**
16
- * Parses a CSS shadow value — which may contain multiple comma-separated
17
- * layers — into an array of {@link ParsedShadow} objects.
18
- *
19
- * @example
20
- * ```ts
21
- * parseShadow('0px 4px 8px 0px rgb(0 0 0 / 0.12)')
22
- * // → [{ x: 0, y: 4, blur: 8, spread: 0, color: 'rgb(0 0 0 / 0.12)' }]
23
- *
24
- * parseShadow('0px 4px 8px rgb(0 0 0 / 0.12), 0px 1px 2px rgba(0, 0, 0, 0.08)')
25
- * // → [
26
- * // { x: 0, y: 4, blur: 8, spread: 0, color: 'rgb(0 0 0 / 0.12)' },
27
- * // { x: 0, y: 1, blur: 2, spread: 0, color: 'rgba(0, 0, 0, 0.08)' },
28
- * // ]
29
- * ```
30
- */
31
- declare function parseShadow(shadowValue: string): ParsedShadow[];
32
- /** Base width of the arrow triangle (px). */
33
- declare const ARROW_WIDTH = 12;
34
- /** Height (protrusion) of the arrow triangle from the edge (px). */
35
- declare const ARROW_HEIGHT = 8;
36
- /**
37
- * Fraction of the container border-radius applied to round the arrow tip
38
- * (0 = sharp point, 1 = same radius as the container corners).
39
- */
40
- declare const ARROW_TIP_RADIUS_FRACTION = 0.5;
41
- type ArrowSide = 'top' | 'bottom' | 'left' | 'right';
42
- /**
43
- * Returns the side of the tooltip where the arrow should appear.
44
- * The arrow points *toward* the trigger, so it lives on the opposite
45
- * side of the placement direction.
46
- */
47
- declare function getArrowSide(placement: string): ArrowSide;
48
- interface TooltipPathOptions {
2
+ //#region src/utils/svgFloatingContentUtils.d.ts
3
+ interface SVGFloatingContentPathOptions {
49
4
  width: number;
50
5
  height: number;
51
6
  placement: string;
52
7
  borderRadius: number;
53
- arrowWidth?: number;
54
- arrowHeight?: number;
8
+ arrowWidth: number;
9
+ arrowHeight: number;
10
+ /** Fraction of the container border-radius applied to round the arrow tip (0 = sharp point, 1 = same radius as the container corners). */
11
+ arrowTipRadiusFraction: number;
55
12
  /**
56
13
  * Position of the arrow center along the arrow's edge (px), measured from
57
14
  * the start of that edge (left for top/bottom sides, top for left/right).
58
- * Typically derived from the Ariakit `TooltipArrow` element's position
59
- * within the tooltip.
60
15
  *
61
16
  * Clamped so the arrow never collides with the border-radius curve
62
17
  * Falls back to the midpoint of the edge when omitted.
@@ -67,6 +22,21 @@ interface TooltipPathOptions {
67
22
  /** Shift every coordinate in the path by this many pixels on the y-axis. */
68
23
  offsetY?: number;
69
24
  }
25
+ interface ArrowClampDeltaOptions {
26
+ width: number;
27
+ height: number;
28
+ placement: string;
29
+ borderRadius: number;
30
+ arrowCenter: number;
31
+ arrowWidth: number;
32
+ }
33
+ type ArrowSide = 'top' | 'bottom' | 'left' | 'right';
34
+ /**
35
+ * Returns the side of the element where the arrow should appear.
36
+ * The arrow points *toward* the trigger, so it lives on the opposite
37
+ * side of the placement direction.
38
+ */
39
+ declare function getArrowSide(placement: string): ArrowSide;
70
40
  /**
71
41
  * Generates an SVG `<path>` string for a rounded rectangle with an arrow
72
42
  * pointing toward the trigger element.
@@ -75,25 +45,18 @@ interface TooltipPathOptions {
75
45
  * appropriate edge. The arrow extends *outside* the 0,0 → width,height
76
46
  * rectangle so the host `<svg>` must use `overflow: visible`.
77
47
  */
78
- declare function generateTooltipPath({
48
+ declare function generateSvgFloatingContentPath({
79
49
  width,
80
50
  height,
81
51
  placement,
82
52
  borderRadius,
83
53
  arrowWidth,
84
54
  arrowHeight,
55
+ arrowTipRadiusFraction,
85
56
  arrowCenter,
86
57
  offsetX: ox,
87
58
  offsetY: oy
88
- }: TooltipPathOptions): string;
89
- interface ArrowClampDeltaOptions {
90
- width: number;
91
- height: number;
92
- placement: string;
93
- borderRadius: number;
94
- arrowCenter: number;
95
- arrowWidth?: number;
96
- }
59
+ }: SVGFloatingContentPathOptions): string;
97
60
  /**
98
61
  * Computes the pixel offset the tooltip must shift so that the SVG arrow
99
62
  * (after clamping) visually aligns with the Ariakit arrow's actual position.
@@ -115,4 +78,4 @@ declare function getArrowClampDelta({
115
78
  y: number;
116
79
  };
117
80
  //#endregion
118
- export { ARROW_HEIGHT, ARROW_TIP_RADIUS_FRACTION, ARROW_WIDTH, type ArrowClampDeltaOptions, type ArrowSide, type ParsedShadow, type TooltipPathOptions, generateTooltipPath, getArrowClampDelta, getArrowSide, parseShadow };
81
+ export { generateSvgFloatingContentPath, getArrowClampDelta, getArrowSide };
@@ -0,0 +1,117 @@
1
+ /*! © 2026 Yahoo, Inc. UDS v0.0.0-development */
2
+ //#region src/utils/svgFloatingContentUtils.ts
3
+ /**
4
+ * Returns the side of the element where the arrow should appear.
5
+ * The arrow points *toward* the trigger, so it lives on the opposite
6
+ * side of the placement direction.
7
+ */
8
+ function getArrowSide(placement) {
9
+ switch (placement.split("-")[0]) {
10
+ case "bottom": return "top";
11
+ case "top": return "bottom";
12
+ case "left": return "right";
13
+ case "right": return "left";
14
+ default: return "top";
15
+ }
16
+ }
17
+ /**
18
+ * Generates an SVG `<path>` string for a rounded rectangle with an arrow
19
+ * pointing toward the trigger element.
20
+ *
21
+ * The path is traced clockwise, inserting the arrow triangle on the
22
+ * appropriate edge. The arrow extends *outside* the 0,0 → width,height
23
+ * rectangle so the host `<svg>` must use `overflow: visible`.
24
+ */
25
+ function generateSvgFloatingContentPath({ width, height, placement, borderRadius, arrowWidth, arrowHeight, arrowTipRadiusFraction, arrowCenter, offsetX: ox = 0, offsetY: oy = 0 }) {
26
+ const aw = arrowWidth / 2;
27
+ const ah = arrowHeight;
28
+ const arrowSide = getArrowSide(placement);
29
+ const arrowEdge = arrowSide === "top" || arrowSide === "bottom" ? width : height;
30
+ const otherEdge = arrowSide === "top" || arrowSide === "bottom" ? height : width;
31
+ const maxRadius = Math.min(otherEdge / 2, (arrowEdge - arrowWidth) / 2);
32
+ const r = Math.max(0, Math.min(borderRadius, maxRadius));
33
+ const minAc = r + aw;
34
+ const edgeLength = arrowSide === "top" || arrowSide === "bottom" ? width : height;
35
+ let ac = arrowCenter ?? edgeLength / 2;
36
+ ac = Math.max(minAc, Math.min(edgeLength - minAc, ac));
37
+ const edgeLen = Math.sqrt(aw * aw + ah * ah);
38
+ const tipR = r > 0 && edgeLen > 0 ? Math.min(r * arrowTipRadiusFraction, edgeLen / 2) : 0;
39
+ const daw = edgeLen > 0 ? aw / edgeLen : 0;
40
+ const dah = edgeLen > 0 ? ah / edgeLen : 0;
41
+ const d = [];
42
+ d.push(`M ${r + ox} ${oy}`);
43
+ if (arrowSide === "top") {
44
+ d.push(`L ${ac - aw + ox} ${oy}`);
45
+ if (tipR > 0) {
46
+ d.push(`L ${ac - tipR * daw + ox} ${-ah + tipR * dah + oy}`);
47
+ d.push(`Q ${ac + ox} ${-ah + oy} ${ac + tipR * daw + ox} ${-ah + tipR * dah + oy}`);
48
+ } else d.push(`L ${ac + ox} ${-ah + oy}`);
49
+ d.push(`L ${ac + aw + ox} ${oy}`);
50
+ }
51
+ d.push(`L ${width - r + ox} ${oy}`);
52
+ d.push(`A ${r} ${r} 0 0 1 ${width + ox} ${r + oy}`);
53
+ if (arrowSide === "right") {
54
+ d.push(`L ${width + ox} ${ac - aw + oy}`);
55
+ if (tipR > 0) {
56
+ d.push(`L ${width + ah - tipR * dah + ox} ${ac - tipR * daw + oy}`);
57
+ d.push(`Q ${width + ah + ox} ${ac + oy} ${width + ah - tipR * dah + ox} ${ac + tipR * daw + oy}`);
58
+ } else d.push(`L ${width + ah + ox} ${ac + oy}`);
59
+ d.push(`L ${width + ox} ${ac + aw + oy}`);
60
+ }
61
+ d.push(`L ${width + ox} ${height - r + oy}`);
62
+ d.push(`A ${r} ${r} 0 0 1 ${width - r + ox} ${height + oy}`);
63
+ if (arrowSide === "bottom") {
64
+ d.push(`L ${ac + aw + ox} ${height + oy}`);
65
+ if (tipR > 0) {
66
+ d.push(`L ${ac + tipR * daw + ox} ${height + ah - tipR * dah + oy}`);
67
+ d.push(`Q ${ac + ox} ${height + ah + oy} ${ac - tipR * daw + ox} ${height + ah - tipR * dah + oy}`);
68
+ } else d.push(`L ${ac + ox} ${height + ah + oy}`);
69
+ d.push(`L ${ac - aw + ox} ${height + oy}`);
70
+ }
71
+ d.push(`L ${r + ox} ${height + oy}`);
72
+ d.push(`A ${r} ${r} 0 0 1 ${ox} ${height - r + oy}`);
73
+ if (arrowSide === "left") {
74
+ d.push(`L ${ox} ${ac + aw + oy}`);
75
+ if (tipR > 0) {
76
+ d.push(`L ${-ah + tipR * dah + ox} ${ac + tipR * daw + oy}`);
77
+ d.push(`Q ${-ah + ox} ${ac + oy} ${-ah + tipR * dah + ox} ${ac - tipR * daw + oy}`);
78
+ } else d.push(`L ${-ah + ox} ${ac + oy}`);
79
+ d.push(`L ${ox} ${ac - aw + oy}`);
80
+ }
81
+ d.push(`L ${ox} ${r + oy}`);
82
+ d.push(`A ${r} ${r} 0 0 1 ${r + ox} ${oy}`);
83
+ d.push("Z");
84
+ return d.join(" ");
85
+ }
86
+ /**
87
+ * Computes the pixel offset the tooltip must shift so that the SVG arrow
88
+ * (after clamping) visually aligns with the Ariakit arrow's actual position.
89
+ *
90
+ * When the desired `arrowCenter` falls inside the safe zone the delta is 0.
91
+ * When it falls outside (e.g. a tiny trigger near a rounded corner), the
92
+ * returned delta tells the caller how far to translate the tooltip element
93
+ * so the clamped arrow ends up over the trigger center.
94
+ */
95
+ function getArrowClampDelta({ width, height, placement, borderRadius, arrowCenter, arrowWidth }) {
96
+ const aw = arrowWidth / 2;
97
+ const side = getArrowSide(placement);
98
+ const arrowEdge = side === "top" || side === "bottom" ? width : height;
99
+ const otherEdge = side === "top" || side === "bottom" ? height : width;
100
+ const maxRadius = Math.min(otherEdge / 2, (arrowEdge - arrowWidth) / 2);
101
+ const minAc = Math.max(0, Math.min(borderRadius, maxRadius)) + aw;
102
+ const maxAc = arrowEdge - minAc;
103
+ let delta = 0;
104
+ if (arrowCenter < minAc) delta = arrowCenter - minAc;
105
+ else if (arrowCenter > maxAc) delta = arrowCenter - maxAc;
106
+ if (side === "top" || side === "bottom") return {
107
+ x: delta,
108
+ y: 0
109
+ };
110
+ return {
111
+ x: 0,
112
+ y: delta
113
+ };
114
+ }
115
+
116
+ //#endregion
117
+ export { generateSvgFloatingContentPath, getArrowClampDelta, getArrowSide };