@syscore/ui-library 1.27.3 → 2.0.0-beta-publish-workflow-beta.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.
Files changed (169) hide show
  1. package/client/App.tsx +0 -2
  2. package/client/assets/alert/icon-clear.svg +4 -0
  3. package/client/assets/alert/icon-done.svg +3 -0
  4. package/client/assets/avatar-identity/photo.png +0 -0
  5. package/client/assets/avatar-identity/placeholder-icon.svg +8 -0
  6. package/client/assets/breadcrumb/icon-ellipsis.svg +5 -0
  7. package/client/assets/breadcrumb/icon-home.svg +3 -0
  8. package/client/assets/breadcrumb/icon-separator.svg +3 -0
  9. package/client/assets/button-v01/icon-back.svg +4 -0
  10. package/client/assets/button-v01/icon-check.svg +3 -0
  11. package/client/assets/button-v01/icon-close.svg +4 -0
  12. package/client/assets/button-v01/icon-delete.svg +3 -0
  13. package/client/assets/button-v01/icon-device.svg +3 -0
  14. package/client/assets/dropdown/icon-carrot-contract.svg +3 -0
  15. package/client/assets/dropdown/icon-carrot-expand.svg +3 -0
  16. package/client/assets/dropdown/icon-clear.svg +4 -0
  17. package/client/assets/dropdown/icon-enter.svg +3 -0
  18. package/client/components/icons/UtilityChangeType.tsx +21 -0
  19. package/client/components/ui/alert-dialog-panel.tsx +246 -0
  20. package/client/components/ui/alert-v02.tsx +214 -0
  21. package/client/components/ui/app-bar.tsx +299 -0
  22. package/client/components/ui/avatar-identity.tsx +122 -0
  23. package/client/components/ui/banner.tsx +108 -0
  24. package/client/components/ui/bottom-navigation.tsx +223 -0
  25. package/client/components/ui/breadcrumb-trail.tsx +170 -0
  26. package/client/components/ui/button-v01.tsx +238 -0
  27. package/client/components/ui/button.tsx +2 -0
  28. package/client/components/ui/card.tsx +108 -26
  29. package/client/components/ui/code-badge.tsx +62 -0
  30. package/client/components/ui/date-picker.tsx +188 -0
  31. package/client/components/ui/dropdown.tsx +448 -0
  32. package/client/components/ui/empty-state.tsx +197 -0
  33. package/client/components/ui/file-upload.tsx +214 -0
  34. package/client/components/ui/footer.tsx +179 -0
  35. package/client/components/ui/layout.tsx +1182 -0
  36. package/client/components/ui/pagination-v01.tsx +157 -0
  37. package/client/components/ui/password-field-v01.tsx +67 -0
  38. package/client/components/ui/search-field-v01.tsx +192 -0
  39. package/client/components/ui/section-heading.tsx +49 -0
  40. package/client/components/ui/sidebar.tsx +2 -3
  41. package/client/components/ui/standard-navigation.tsx +8 -2
  42. package/client/components/ui/stepper.tsx +148 -0
  43. package/client/components/ui/switch-v01.tsx +71 -0
  44. package/client/components/ui/system-bar.tsx +119 -0
  45. package/client/components/ui/table-v01.tsx +230 -0
  46. package/client/components/ui/tabs-v01.tsx +99 -0
  47. package/client/components/ui/tag-v02.tsx +113 -0
  48. package/client/components/ui/text-area-v01.tsx +33 -0
  49. package/client/components/ui/text-field-v01.tsx +108 -0
  50. package/client/components/ui/timeline.tsx +181 -0
  51. package/client/components/ui/toast-v01.tsx +206 -0
  52. package/client/components/ui/tooltip.tsx +264 -71
  53. package/client/global.css +9565 -4378
  54. package/client/storybook.css +125 -0
  55. package/client/ui/Accordion.stories.tsx +1 -1
  56. package/client/ui/AlertDialogPanel/AlertDialogPanel.stories.tsx +150 -0
  57. package/client/ui/AlertV02/AlertV02.stories.tsx +188 -0
  58. package/client/ui/AppBar/app-bar.stories.tsx +280 -0
  59. package/client/ui/AspectRatio.stories.tsx +1 -1
  60. package/client/ui/AvatarIdentity/AvatarIdentity.stories.tsx +77 -0
  61. package/client/ui/Banner/banner.stories.tsx +238 -0
  62. package/client/ui/BottomNavigation/bottom-navigation.stories.tsx +285 -0
  63. package/client/ui/BreadcrumbTrail/BreadcrumbTrail.stories.tsx +232 -0
  64. package/client/ui/Button.stories.tsx +1 -1
  65. package/client/ui/ButtonV01/ButtonV01.stories.tsx +417 -0
  66. package/client/ui/Calendar/Calendar.stories.tsx +1 -1
  67. package/client/ui/Card.stories.tsx +285 -168
  68. package/client/ui/Carousel/Carousel.stories.tsx +1 -1
  69. package/client/ui/Chart/Chart.stories.tsx +1 -1
  70. package/client/ui/Checkbox.stories.tsx +1 -1
  71. package/client/ui/Collapsible/Collapsible.stories.tsx +1 -1
  72. package/client/ui/Colors.stories.tsx +1 -1
  73. package/client/ui/Command/Command.stories.tsx +1 -1
  74. package/client/ui/ContextMenu/ContextMenu.stories.tsx +1 -1
  75. package/client/ui/DatePicker/DatePicker.stories.tsx +293 -0
  76. package/client/ui/Dialog.stories.tsx +1 -1
  77. package/client/ui/Drawer/Drawer.stories.tsx +1 -1
  78. package/client/ui/Dropdown/Dropdown.stories.tsx +205 -0
  79. package/client/ui/EmptyState/empty-state.stories.tsx +251 -0
  80. package/client/ui/FileUpload/FileUpload.stories.tsx +218 -0
  81. package/client/ui/Footer/footer.stories.tsx +194 -0
  82. package/client/ui/Form/Form.stories.tsx +1 -1
  83. package/client/ui/Hero.stories.tsx +1 -1
  84. package/client/ui/HoverCard/HoverCard.stories.tsx +1 -1
  85. package/client/ui/Icons.stories.tsx +1 -1
  86. package/client/ui/Input.stories.tsx +1 -1
  87. package/client/ui/InputOTP/InputOTP.stories.tsx +1 -1
  88. package/client/ui/Label.stories.tsx +1 -1
  89. package/client/ui/Layout/Center.stories.tsx +100 -0
  90. package/client/ui/Layout/Columns.stories.tsx +136 -0
  91. package/client/ui/Layout/Container.stories.tsx +104 -0
  92. package/client/ui/Layout/FormLayout.stories.tsx +144 -0
  93. package/client/ui/Layout/Grid.stories.tsx +158 -0
  94. package/client/ui/Layout/GridSpan.stories.tsx +75 -0
  95. package/client/ui/Layout/HStack.stories.tsx +128 -0
  96. package/client/ui/Layout/Layout.stories.tsx +287 -0
  97. package/client/ui/Layout/LayoutContent.stories.tsx +91 -0
  98. package/client/ui/Layout/LayoutFooter.stories.tsx +77 -0
  99. package/client/ui/Layout/LayoutHeader.stories.tsx +104 -0
  100. package/client/ui/Layout/LayoutPanel.stories.tsx +143 -0
  101. package/client/ui/Layout/Positioned.stories.tsx +113 -0
  102. package/client/ui/Layout/Section.stories.tsx +84 -0
  103. package/client/ui/Layout/ShowHide.stories.tsx +99 -0
  104. package/client/ui/Layout/Spacer.stories.tsx +149 -0
  105. package/client/ui/Layout/Stack.stories.tsx +69 -0
  106. package/client/ui/Layout/StackItem.stories.tsx +124 -0
  107. package/client/ui/Layout/VStack.stories.tsx +101 -0
  108. package/client/ui/Layout/controls.ts +33 -0
  109. package/client/ui/Layout/demo.tsx +37 -0
  110. package/client/ui/Menubar/Menubar.stories.tsx +1 -1
  111. package/client/ui/MobileNav.stories.tsx +1 -1
  112. package/client/ui/Navigation.stories.tsx +1 -1
  113. package/client/ui/NavigationMenu/NavigationMenu.stories.tsx +1 -1
  114. package/client/ui/PageHeader.stories.tsx +13 -13
  115. package/client/ui/Pagination/Pagination.stories.tsx +178 -98
  116. package/client/ui/Pagination/PaginationLegacy.stories.tsx +121 -0
  117. package/client/ui/Panel.stories.tsx +1 -1
  118. package/client/ui/Popover/Popover.stories.tsx +1 -1
  119. package/client/ui/Progress/Progress.stories.tsx +1 -1
  120. package/client/ui/RadioGroup/RadioGroup.stories.tsx +1 -1
  121. package/client/ui/Resizable/Resizable.stories.tsx +1 -1
  122. package/client/ui/ScrollArea/ScrollArea.stories.tsx +1 -1
  123. package/client/ui/SearchField/SearchField.stories.tsx +153 -0
  124. package/client/ui/{SearchField.stories.tsx → SearchField/SearchFieldLegacy.stories.tsx} +2 -2
  125. package/client/ui/SectionBadge.stories.tsx +1 -1
  126. package/client/ui/Select.stories.tsx +1 -1
  127. package/client/ui/Separator/Separator.stories.tsx +1 -1
  128. package/client/ui/Sheet/Sheet.stories.tsx +1 -1
  129. package/client/ui/Sidebar/Sidebar.stories.tsx +57 -8
  130. package/client/ui/Skeleton/Skeleton.stories.tsx +1 -1
  131. package/client/ui/Slider/Slider.stories.tsx +1 -1
  132. package/client/ui/StandardNavigation.stories.tsx +1 -1
  133. package/client/ui/Stepper/Stepper.stories.tsx +344 -0
  134. package/client/ui/Switch.stories.tsx +1 -1
  135. package/client/ui/SwitchV01/SwitchV01.stories.tsx +152 -0
  136. package/client/ui/SystemBar/system-bar.stories.tsx +179 -0
  137. package/client/ui/Table/TableV01.stories.tsx +256 -0
  138. package/client/ui/Tabs.stories.tsx +1 -1
  139. package/client/ui/TabsV01/TabsV01.stories.tsx +144 -0
  140. package/client/ui/Tag.stories.tsx +1 -1
  141. package/client/ui/TagV02/TagStatusV02.stories.tsx +132 -0
  142. package/client/ui/TagV02/TagV02.stories.tsx +142 -0
  143. package/client/ui/TextAreaV01/TextAreaV01.stories.tsx +146 -0
  144. package/client/ui/TextFieldV01/PasswordFieldV01.stories.tsx +161 -0
  145. package/client/ui/TextFieldV01/TextFieldV01.stories.tsx +160 -0
  146. package/client/ui/Textarea.stories.tsx +1 -1
  147. package/client/ui/Timeline/timeline.stories.tsx +404 -0
  148. package/client/ui/Toast/Toast.stories.tsx +1 -1
  149. package/client/ui/ToastV01/ToastV01.stories.tsx +201 -0
  150. package/client/ui/Toaster/Toaster.stories.tsx +1 -1
  151. package/client/ui/Toggle.stories.tsx +1 -1
  152. package/client/ui/Tooltip.stories.tsx +287 -114
  153. package/client/ui/Typography.stories.tsx +1 -1
  154. package/dist/index.cjs.js +1 -1
  155. package/dist/index.d.ts +649 -13
  156. package/dist/index.es.js +4405 -653
  157. package/package.json +1 -1
  158. package/client/components/ui/badge.tsx +0 -33
  159. package/client/components/ui/sonner.tsx +0 -26
  160. package/client/components/ui/toggle-group.tsx +0 -76
  161. package/client/ui/Alert/Alert.stories.tsx +0 -82
  162. package/client/ui/AlertDialog/AlertDialog.stories.tsx +0 -106
  163. package/client/ui/Avatar/Avatar.stories.tsx +0 -94
  164. package/client/ui/Badge/Badge.stories.tsx +0 -60
  165. package/client/ui/Breadcrumb/Breadcrumb.stories.tsx +0 -97
  166. package/client/ui/DropdownMenu/DropdownMenu.stories.tsx +0 -139
  167. package/client/ui/Sonner/Sonner.stories.tsx +0 -48
  168. package/client/ui/Table/Table.stories.tsx +0 -135
  169. package/client/ui/ToggleGroup/ToggleGroup.stories.tsx +0 -88
@@ -5,33 +5,57 @@ import * as TooltipPrimitive from "@radix-ui/react-tooltip";
5
5
  import { cva, type VariantProps } from "class-variance-authority";
6
6
 
7
7
  import { cn } from "@/lib/utils";
8
- import { UtilityClose } from "@/components/icons/UtilityClose";
8
+ import { Button } from "@/components/ui/button";
9
9
 
10
- const tooltipContentVariants = cva("tooltip-content", {
10
+ const tooltipContentVariants = cva("tooltip-content font-mazzardSoft", {
11
11
  variants: {
12
- variant: {
13
- default: "tooltip-content--default",
14
- simple: "tooltip-content--simple",
12
+ tone: {
13
+ dark: "tooltip-content--dark",
14
+ light: "tooltip-content--light",
15
15
  },
16
16
  },
17
17
  defaultVariants: {
18
- variant: "default",
18
+ tone: "dark",
19
19
  },
20
20
  });
21
21
 
22
+ export type TooltipTone = NonNullable<
23
+ VariantProps<typeof tooltipContentVariants>["tone"]
24
+ >;
25
+
26
+ export type TooltipDirection = "top" | "bottom" | "left" | "right";
27
+
28
+ const MARKER_INSET = { inline: 54, block: 53 } as const;
29
+ const MARKER_HALF = 5;
30
+
31
+ const directionToSide: Record<
32
+ TooltipDirection,
33
+ "top" | "bottom" | "left" | "right"
34
+ > = {
35
+ top: "bottom",
36
+ bottom: "top",
37
+ left: "right",
38
+ right: "left",
39
+ };
40
+
22
41
  type TriggerMode = "hover" | "click";
23
42
 
24
- // Context for passing trigger mode and close function to children
43
+ type AnchorSize = { width: number; height: number };
44
+
25
45
  const TooltipContext = React.createContext<{
26
46
  trigger: TriggerMode;
27
47
  toggle: () => void;
28
48
  close: () => void;
29
49
  triggerRef: React.RefObject<HTMLButtonElement | null>;
50
+ anchorSize: AnchorSize;
51
+ setAnchorSize: (size: AnchorSize) => void;
30
52
  }>({
31
53
  trigger: "hover",
32
54
  toggle: () => {},
33
55
  close: () => {},
34
56
  triggerRef: { current: null },
57
+ anchorSize: { width: 0, height: 0 },
58
+ setAnchorSize: () => {},
35
59
  });
36
60
 
37
61
  function TooltipProvider({
@@ -52,20 +76,13 @@ interface TooltipProps
52
76
  React.ComponentProps<typeof TooltipPrimitive.Root>,
53
77
  "open" | "onOpenChange"
54
78
  > {
55
- /**
56
- * Trigger mode: "hover" (default) or "click"
57
- * - hover: Opens on mouse hover, closes on hover off (default tooltip behavior)
58
- * - click: Opens on click, closes on trigger click or click outside
59
- */
60
79
  trigger?: TriggerMode;
61
- /** Controlled open state (optional) */
62
80
  open?: boolean;
63
- /** Callback when open state changes (optional) */
64
81
  onOpenChange?: (open: boolean) => void;
65
82
  }
66
83
 
67
84
  function Tooltip({
68
- trigger = "hover",
85
+ trigger = "click",
69
86
  open: controlledOpen,
70
87
  onOpenChange,
71
88
  children,
@@ -73,14 +90,16 @@ function Tooltip({
73
90
  }: TooltipProps) {
74
91
  const [uncontrolledOpen, setUncontrolledOpen] = React.useState(false);
75
92
  const triggerRef = React.useRef<HTMLButtonElement>(null);
93
+ const [anchorSize, setAnchorSize] = React.useState<AnchorSize>({
94
+ width: 0,
95
+ height: 0,
96
+ });
76
97
 
77
- // Use controlled or uncontrolled state
78
98
  const isControlled = controlledOpen !== undefined;
79
99
  const isOpen = isControlled ? controlledOpen : uncontrolledOpen;
80
100
 
81
101
  const handleOpenChange = React.useCallback(
82
102
  (newOpen: boolean) => {
83
- // For click mode, ignore hover-triggered changes
84
103
  if (trigger === "click") return;
85
104
 
86
105
  if (!isControlled) {
@@ -106,7 +125,6 @@ function Tooltip({
106
125
  onOpenChange?.(newOpen);
107
126
  }, [isOpen, isControlled, onOpenChange]);
108
127
 
109
- // For click mode, we fully control open state
110
128
  const tooltipProps =
111
129
  trigger === "click"
112
130
  ? {
@@ -119,7 +137,9 @@ function Tooltip({
119
137
  };
120
138
 
121
139
  return (
122
- <TooltipContext.Provider value={{ trigger, toggle, close, triggerRef }}>
140
+ <TooltipContext.Provider
141
+ value={{ trigger, toggle, close, triggerRef, anchorSize, setAnchorSize }}
142
+ >
123
143
  <TooltipProvider delayDuration={trigger === "click" ? 100000 : 0}>
124
144
  <TooltipPrimitive.Root data-slot="tooltip" {...tooltipProps} {...props}>
125
145
  {children}
@@ -133,7 +153,21 @@ function TooltipTrigger({
133
153
  onClick,
134
154
  ...props
135
155
  }: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
136
- const { trigger, toggle, triggerRef } = React.useContext(TooltipContext);
156
+ const { trigger, toggle, triggerRef, setAnchorSize } =
157
+ React.useContext(TooltipContext);
158
+
159
+ React.useLayoutEffect(() => {
160
+ const el = triggerRef.current;
161
+ if (!el) return;
162
+ const measure = () => {
163
+ const { width, height } = el.getBoundingClientRect();
164
+ setAnchorSize({ width, height });
165
+ };
166
+ measure();
167
+ const observer = new ResizeObserver(measure);
168
+ observer.observe(el);
169
+ return () => observer.disconnect();
170
+ }, [triggerRef, setAnchorSize]);
137
171
 
138
172
  const handleClick = React.useCallback(
139
173
  (e: React.MouseEvent<HTMLButtonElement>) => {
@@ -157,26 +191,191 @@ function TooltipTrigger({
157
191
  );
158
192
  }
159
193
 
194
+ function TooltipMarker() {
195
+ return (
196
+ <svg
197
+ className="tooltip-marker"
198
+ data-slot="tooltip-marker"
199
+ width="10"
200
+ height="5"
201
+ viewBox="0 0 10 5"
202
+ fill="none"
203
+ xmlns="http://www.w3.org/2000/svg"
204
+ aria-hidden
205
+ >
206
+ <path
207
+ d="M10 0L0 0C0 2.76142 2.23858 5 5 5C7.76142 5 10 2.76142 10 0Z"
208
+ fill="currentColor"
209
+ />
210
+ </svg>
211
+ );
212
+ }
213
+
214
+ function textOf(node: React.ReactNode): string {
215
+ if (node == null || typeof node === "boolean") return "";
216
+ if (typeof node === "string" || typeof node === "number") return String(node);
217
+ if (Array.isArray(node)) return node.map(textOf).filter(Boolean).join(" ");
218
+ if (React.isValidElement(node)) {
219
+ return textOf((node.props as { children?: React.ReactNode }).children);
220
+ }
221
+ return "";
222
+ }
223
+
224
+ export interface TooltipPanelProps
225
+ extends React.HTMLAttributes<HTMLDivElement>,
226
+ VariantProps<typeof tooltipContentVariants> {
227
+ direction?: TooltipDirection;
228
+ heading?: React.ReactNode;
229
+ body?: React.ReactNode;
230
+ conditionalText?: React.ReactNode;
231
+ primaryLabel?: React.ReactNode;
232
+ secondaryLabel?: React.ReactNode;
233
+ onPrimaryAction?: () => void;
234
+ onSecondaryAction?: () => void;
235
+ }
236
+
237
+ const TooltipPanel = React.forwardRef<HTMLDivElement, TooltipPanelProps>(
238
+ (
239
+ {
240
+ className,
241
+ tone = "dark",
242
+ direction = "top",
243
+ heading,
244
+ body,
245
+ conditionalText,
246
+ primaryLabel,
247
+ secondaryLabel,
248
+ onPrimaryAction,
249
+ onSecondaryAction,
250
+ children,
251
+ ...props
252
+ },
253
+ ref,
254
+ ) => {
255
+ const showHeader = heading != null || body != null;
256
+ const showActions = primaryLabel != null || secondaryLabel != null;
257
+
258
+ return (
259
+ <div
260
+ ref={ref}
261
+ data-slot="tooltip-content"
262
+ data-direction={direction}
263
+ className={cn(tooltipContentVariants({ tone }), className)}
264
+ {...props}
265
+ >
266
+ <TooltipMarker />
267
+
268
+ {showHeader && (
269
+ <div className="tooltip-header" data-slot="tooltip-header">
270
+ {heading != null && (
271
+ <p className="tooltip-heading" data-slot="tooltip-heading">
272
+ {heading}
273
+ </p>
274
+ )}
275
+ {body != null && (
276
+ <p className="tooltip-body" data-slot="tooltip-body">
277
+ {body}
278
+ </p>
279
+ )}
280
+ </div>
281
+ )}
282
+
283
+ {conditionalText != null && (
284
+ <>
285
+ <span
286
+ className="tooltip-separator"
287
+ data-slot="tooltip-separator"
288
+ aria-hidden
289
+ />
290
+ <p className="tooltip-body" data-slot="tooltip-conditional-text">
291
+ {conditionalText}
292
+ </p>
293
+ </>
294
+ )}
295
+
296
+ {children}
297
+
298
+ {showActions && (
299
+ <div className="tooltip-actions" data-slot="tooltip-actions">
300
+ {primaryLabel != null && (
301
+ <Button
302
+ type="button"
303
+ size="utility"
304
+ variant={
305
+ tone === "light" ? "tooltip-primary-light" : "tooltip-primary"
306
+ }
307
+ onClick={onPrimaryAction}
308
+ >
309
+ {primaryLabel}
310
+ </Button>
311
+ )}
312
+ {secondaryLabel != null && (
313
+ <Button
314
+ type="button"
315
+ size="utility"
316
+ variant={
317
+ tone === "light"
318
+ ? "tooltip-secondary-light"
319
+ : "tooltip-secondary"
320
+ }
321
+ onClick={onSecondaryAction}
322
+ >
323
+ {secondaryLabel}
324
+ </Button>
325
+ )}
326
+ </div>
327
+ )}
328
+ </div>
329
+ );
330
+ },
331
+ );
332
+ TooltipPanel.displayName = "TooltipPanel";
333
+
334
+ export interface TooltipContentProps
335
+ extends Omit<
336
+ React.ComponentProps<typeof TooltipPrimitive.Content>,
337
+ "content" | "side"
338
+ >,
339
+ VariantProps<typeof tooltipContentVariants> {
340
+ direction?: TooltipDirection;
341
+ heading?: React.ReactNode;
342
+ body?: React.ReactNode;
343
+ conditionalText?: React.ReactNode;
344
+ primaryLabel?: React.ReactNode;
345
+ secondaryLabel?: React.ReactNode;
346
+ onPrimaryAction?: () => void;
347
+ onSecondaryAction?: () => void;
348
+ }
349
+
160
350
  function TooltipContent({
161
351
  className,
162
- variant,
163
- sideOffset = 0,
164
- side = "bottom",
352
+ tone = "dark",
353
+ sideOffset = 12,
354
+ direction = "top",
355
+ align = "start",
356
+ alignOffset,
357
+ heading,
358
+ body,
359
+ conditionalText,
360
+ primaryLabel,
361
+ secondaryLabel,
362
+ onPrimaryAction,
363
+ onSecondaryAction,
364
+ "aria-label": ariaLabel,
165
365
  children,
166
- alignOffset = 0,
167
- hideClose = false,
168
366
  ...props
169
- }: React.ComponentProps<typeof TooltipPrimitive.Content> &
170
- VariantProps<typeof tooltipContentVariants> & {
171
- hideClose?: boolean;
172
- }) {
173
- const { trigger, close, triggerRef } = React.useContext(TooltipContext);
367
+ }: TooltipContentProps) {
368
+ const { trigger, close, triggerRef, anchorSize } =
369
+ React.useContext(TooltipContext);
174
370
  const contentRef = React.useRef<HTMLDivElement>(null);
175
371
 
176
- // Click outside detection for click mode
177
372
  React.useEffect(() => {
178
373
  if (trigger !== "click") return;
179
374
 
375
+ const handleKeyDown = (event: KeyboardEvent) => {
376
+ if (event.key === "Escape") close();
377
+ };
378
+
180
379
  const handleClickOutside = (event: MouseEvent) => {
181
380
  const target = event.target as Node;
182
381
  const isOutsideContent =
@@ -189,72 +388,66 @@ function TooltipContent({
189
388
  }
190
389
  };
191
390
 
192
- // Small delay to avoid closing immediately on the same click that opened it
193
391
  const timeoutId = setTimeout(() => {
194
392
  document.addEventListener("click", handleClickOutside);
195
393
  }, 0);
394
+ document.addEventListener("keydown", handleKeyDown);
196
395
 
197
396
  return () => {
198
397
  clearTimeout(timeoutId);
199
398
  document.removeEventListener("click", handleClickOutside);
399
+ document.removeEventListener("keydown", handleKeyDown);
200
400
  };
201
401
  }, [trigger, close, triggerRef]);
202
402
 
403
+ const isVertical = direction === "top" || direction === "bottom";
404
+ const markerCentre =
405
+ (isVertical ? MARKER_INSET.inline : MARKER_INSET.block) + MARKER_HALF;
406
+ const anchorExtent = isVertical ? anchorSize.width : anchorSize.height;
407
+ const resolvedAlignOffset =
408
+ alignOffset ?? (anchorExtent > 0 ? anchorExtent / 2 - markerCentre : 0);
409
+
410
+ const derivedLabel = [heading, body, conditionalText]
411
+ .map(textOf)
412
+ .filter(Boolean)
413
+ .join(". ");
414
+
203
415
  return (
204
416
  <TooltipPrimitive.Portal>
205
417
  <TooltipPrimitive.Content
418
+ asChild
206
419
  ref={contentRef}
207
- data-slot="tooltip-content"
208
420
  sideOffset={sideOffset}
209
- alignOffset={alignOffset}
210
- side={side}
211
- className={cn(tooltipContentVariants({ variant }), className)}
421
+ alignOffset={resolvedAlignOffset}
422
+ side={directionToSide[direction]}
423
+ align={align}
424
+ aria-label={ariaLabel ?? (derivedLabel || undefined)}
212
425
  {...props}
213
426
  >
214
- {variant !== "simple" && (
215
- <div className="tooltip-arrow">
216
- <svg
217
- xmlns="http://www.w3.org/2000/svg"
218
- width="10"
219
- height="5"
220
- viewBox="0 0 10 5"
221
- fill="none"
222
- >
223
- <path
224
- d="M10 -1.74846e-06L-7.36867e-07 0C-3.29906e-07 2.76142 2.23858 5 5 5C7.76142 5 10 2.76142 10 -1.74846e-06Z"
225
- fill="currentColor"
226
- />
227
- </svg>
228
- </div>
229
- )}
230
- {variant !== "simple" && !hideClose && (
231
- <ToggleClose />
232
- )}
233
- {children}
427
+ <TooltipPanel
428
+ tone={tone}
429
+ direction={direction}
430
+ className={className}
431
+ heading={heading}
432
+ body={body}
433
+ conditionalText={conditionalText}
434
+ primaryLabel={primaryLabel}
435
+ secondaryLabel={secondaryLabel}
436
+ onPrimaryAction={onPrimaryAction}
437
+ onSecondaryAction={onSecondaryAction}
438
+ >
439
+ {children}
440
+ </TooltipPanel>
234
441
  </TooltipPrimitive.Content>
235
442
  </TooltipPrimitive.Portal>
236
443
  );
237
444
  }
238
445
 
239
- function ToggleClose({ className }: { className?: string }) {
240
- const { close } = React.useContext(TooltipContext);
241
-
242
- return (
243
- <button
244
- data-slot="tooltip-close"
245
- onClick={close}
246
- className={className}
247
- >
248
- <UtilityClose className="text-inherit" />
249
- </button>
250
- );
251
- }
252
-
253
446
  export {
254
447
  Tooltip,
255
448
  TooltipTrigger,
256
449
  TooltipContent,
450
+ TooltipPanel,
257
451
  tooltipContentVariants,
258
452
  TooltipProvider,
259
- ToggleClose,
260
453
  };