@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.
- package/client/App.tsx +0 -2
- package/client/assets/alert/icon-clear.svg +4 -0
- package/client/assets/alert/icon-done.svg +3 -0
- package/client/assets/avatar-identity/photo.png +0 -0
- package/client/assets/avatar-identity/placeholder-icon.svg +8 -0
- package/client/assets/breadcrumb/icon-ellipsis.svg +5 -0
- package/client/assets/breadcrumb/icon-home.svg +3 -0
- package/client/assets/breadcrumb/icon-separator.svg +3 -0
- package/client/assets/button-v01/icon-back.svg +4 -0
- package/client/assets/button-v01/icon-check.svg +3 -0
- package/client/assets/button-v01/icon-close.svg +4 -0
- package/client/assets/button-v01/icon-delete.svg +3 -0
- package/client/assets/button-v01/icon-device.svg +3 -0
- package/client/assets/dropdown/icon-carrot-contract.svg +3 -0
- package/client/assets/dropdown/icon-carrot-expand.svg +3 -0
- package/client/assets/dropdown/icon-clear.svg +4 -0
- package/client/assets/dropdown/icon-enter.svg +3 -0
- package/client/components/icons/UtilityChangeType.tsx +21 -0
- package/client/components/ui/alert-dialog-panel.tsx +246 -0
- package/client/components/ui/alert-v02.tsx +214 -0
- package/client/components/ui/app-bar.tsx +299 -0
- package/client/components/ui/avatar-identity.tsx +122 -0
- package/client/components/ui/banner.tsx +108 -0
- package/client/components/ui/bottom-navigation.tsx +223 -0
- package/client/components/ui/breadcrumb-trail.tsx +170 -0
- package/client/components/ui/button-v01.tsx +238 -0
- package/client/components/ui/button.tsx +2 -0
- package/client/components/ui/card.tsx +108 -26
- package/client/components/ui/code-badge.tsx +62 -0
- package/client/components/ui/date-picker.tsx +188 -0
- package/client/components/ui/dropdown.tsx +448 -0
- package/client/components/ui/empty-state.tsx +197 -0
- package/client/components/ui/file-upload.tsx +214 -0
- package/client/components/ui/footer.tsx +179 -0
- package/client/components/ui/layout.tsx +1182 -0
- package/client/components/ui/pagination-v01.tsx +157 -0
- package/client/components/ui/password-field-v01.tsx +67 -0
- package/client/components/ui/search-field-v01.tsx +192 -0
- package/client/components/ui/section-heading.tsx +49 -0
- package/client/components/ui/sidebar.tsx +2 -3
- package/client/components/ui/standard-navigation.tsx +8 -2
- package/client/components/ui/stepper.tsx +148 -0
- package/client/components/ui/switch-v01.tsx +71 -0
- package/client/components/ui/system-bar.tsx +119 -0
- package/client/components/ui/table-v01.tsx +230 -0
- package/client/components/ui/tabs-v01.tsx +99 -0
- package/client/components/ui/tag-v02.tsx +113 -0
- package/client/components/ui/text-area-v01.tsx +33 -0
- package/client/components/ui/text-field-v01.tsx +108 -0
- package/client/components/ui/timeline.tsx +181 -0
- package/client/components/ui/toast-v01.tsx +206 -0
- package/client/components/ui/tooltip.tsx +264 -71
- package/client/global.css +9565 -4378
- package/client/storybook.css +125 -0
- package/client/ui/Accordion.stories.tsx +1 -1
- package/client/ui/AlertDialogPanel/AlertDialogPanel.stories.tsx +150 -0
- package/client/ui/AlertV02/AlertV02.stories.tsx +188 -0
- package/client/ui/AppBar/app-bar.stories.tsx +280 -0
- package/client/ui/AspectRatio.stories.tsx +1 -1
- package/client/ui/AvatarIdentity/AvatarIdentity.stories.tsx +77 -0
- package/client/ui/Banner/banner.stories.tsx +238 -0
- package/client/ui/BottomNavigation/bottom-navigation.stories.tsx +285 -0
- package/client/ui/BreadcrumbTrail/BreadcrumbTrail.stories.tsx +232 -0
- package/client/ui/Button.stories.tsx +1 -1
- package/client/ui/ButtonV01/ButtonV01.stories.tsx +417 -0
- package/client/ui/Calendar/Calendar.stories.tsx +1 -1
- package/client/ui/Card.stories.tsx +285 -168
- package/client/ui/Carousel/Carousel.stories.tsx +1 -1
- package/client/ui/Chart/Chart.stories.tsx +1 -1
- package/client/ui/Checkbox.stories.tsx +1 -1
- package/client/ui/Collapsible/Collapsible.stories.tsx +1 -1
- package/client/ui/Colors.stories.tsx +1 -1
- package/client/ui/Command/Command.stories.tsx +1 -1
- package/client/ui/ContextMenu/ContextMenu.stories.tsx +1 -1
- package/client/ui/DatePicker/DatePicker.stories.tsx +293 -0
- package/client/ui/Dialog.stories.tsx +1 -1
- package/client/ui/Drawer/Drawer.stories.tsx +1 -1
- package/client/ui/Dropdown/Dropdown.stories.tsx +205 -0
- package/client/ui/EmptyState/empty-state.stories.tsx +251 -0
- package/client/ui/FileUpload/FileUpload.stories.tsx +218 -0
- package/client/ui/Footer/footer.stories.tsx +194 -0
- package/client/ui/Form/Form.stories.tsx +1 -1
- package/client/ui/Hero.stories.tsx +1 -1
- package/client/ui/HoverCard/HoverCard.stories.tsx +1 -1
- package/client/ui/Icons.stories.tsx +1 -1
- package/client/ui/Input.stories.tsx +1 -1
- package/client/ui/InputOTP/InputOTP.stories.tsx +1 -1
- package/client/ui/Label.stories.tsx +1 -1
- package/client/ui/Layout/Center.stories.tsx +100 -0
- package/client/ui/Layout/Columns.stories.tsx +136 -0
- package/client/ui/Layout/Container.stories.tsx +104 -0
- package/client/ui/Layout/FormLayout.stories.tsx +144 -0
- package/client/ui/Layout/Grid.stories.tsx +158 -0
- package/client/ui/Layout/GridSpan.stories.tsx +75 -0
- package/client/ui/Layout/HStack.stories.tsx +128 -0
- package/client/ui/Layout/Layout.stories.tsx +287 -0
- package/client/ui/Layout/LayoutContent.stories.tsx +91 -0
- package/client/ui/Layout/LayoutFooter.stories.tsx +77 -0
- package/client/ui/Layout/LayoutHeader.stories.tsx +104 -0
- package/client/ui/Layout/LayoutPanel.stories.tsx +143 -0
- package/client/ui/Layout/Positioned.stories.tsx +113 -0
- package/client/ui/Layout/Section.stories.tsx +84 -0
- package/client/ui/Layout/ShowHide.stories.tsx +99 -0
- package/client/ui/Layout/Spacer.stories.tsx +149 -0
- package/client/ui/Layout/Stack.stories.tsx +69 -0
- package/client/ui/Layout/StackItem.stories.tsx +124 -0
- package/client/ui/Layout/VStack.stories.tsx +101 -0
- package/client/ui/Layout/controls.ts +33 -0
- package/client/ui/Layout/demo.tsx +37 -0
- package/client/ui/Menubar/Menubar.stories.tsx +1 -1
- package/client/ui/MobileNav.stories.tsx +1 -1
- package/client/ui/Navigation.stories.tsx +1 -1
- package/client/ui/NavigationMenu/NavigationMenu.stories.tsx +1 -1
- package/client/ui/PageHeader.stories.tsx +13 -13
- package/client/ui/Pagination/Pagination.stories.tsx +178 -98
- package/client/ui/Pagination/PaginationLegacy.stories.tsx +121 -0
- package/client/ui/Panel.stories.tsx +1 -1
- package/client/ui/Popover/Popover.stories.tsx +1 -1
- package/client/ui/Progress/Progress.stories.tsx +1 -1
- package/client/ui/RadioGroup/RadioGroup.stories.tsx +1 -1
- package/client/ui/Resizable/Resizable.stories.tsx +1 -1
- package/client/ui/ScrollArea/ScrollArea.stories.tsx +1 -1
- package/client/ui/SearchField/SearchField.stories.tsx +153 -0
- package/client/ui/{SearchField.stories.tsx → SearchField/SearchFieldLegacy.stories.tsx} +2 -2
- package/client/ui/SectionBadge.stories.tsx +1 -1
- package/client/ui/Select.stories.tsx +1 -1
- package/client/ui/Separator/Separator.stories.tsx +1 -1
- package/client/ui/Sheet/Sheet.stories.tsx +1 -1
- package/client/ui/Sidebar/Sidebar.stories.tsx +57 -8
- package/client/ui/Skeleton/Skeleton.stories.tsx +1 -1
- package/client/ui/Slider/Slider.stories.tsx +1 -1
- package/client/ui/StandardNavigation.stories.tsx +1 -1
- package/client/ui/Stepper/Stepper.stories.tsx +344 -0
- package/client/ui/Switch.stories.tsx +1 -1
- package/client/ui/SwitchV01/SwitchV01.stories.tsx +152 -0
- package/client/ui/SystemBar/system-bar.stories.tsx +179 -0
- package/client/ui/Table/TableV01.stories.tsx +256 -0
- package/client/ui/Tabs.stories.tsx +1 -1
- package/client/ui/TabsV01/TabsV01.stories.tsx +144 -0
- package/client/ui/Tag.stories.tsx +1 -1
- package/client/ui/TagV02/TagStatusV02.stories.tsx +132 -0
- package/client/ui/TagV02/TagV02.stories.tsx +142 -0
- package/client/ui/TextAreaV01/TextAreaV01.stories.tsx +146 -0
- package/client/ui/TextFieldV01/PasswordFieldV01.stories.tsx +161 -0
- package/client/ui/TextFieldV01/TextFieldV01.stories.tsx +160 -0
- package/client/ui/Textarea.stories.tsx +1 -1
- package/client/ui/Timeline/timeline.stories.tsx +404 -0
- package/client/ui/Toast/Toast.stories.tsx +1 -1
- package/client/ui/ToastV01/ToastV01.stories.tsx +201 -0
- package/client/ui/Toaster/Toaster.stories.tsx +1 -1
- package/client/ui/Toggle.stories.tsx +1 -1
- package/client/ui/Tooltip.stories.tsx +287 -114
- package/client/ui/Typography.stories.tsx +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.d.ts +649 -13
- package/dist/index.es.js +4405 -653
- package/package.json +1 -1
- package/client/components/ui/badge.tsx +0 -33
- package/client/components/ui/sonner.tsx +0 -26
- package/client/components/ui/toggle-group.tsx +0 -76
- package/client/ui/Alert/Alert.stories.tsx +0 -82
- package/client/ui/AlertDialog/AlertDialog.stories.tsx +0 -106
- package/client/ui/Avatar/Avatar.stories.tsx +0 -94
- package/client/ui/Badge/Badge.stories.tsx +0 -60
- package/client/ui/Breadcrumb/Breadcrumb.stories.tsx +0 -97
- package/client/ui/DropdownMenu/DropdownMenu.stories.tsx +0 -139
- package/client/ui/Sonner/Sonner.stories.tsx +0 -48
- package/client/ui/Table/Table.stories.tsx +0 -135
- 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 {
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
tone: {
|
|
13
|
+
dark: "tooltip-content--dark",
|
|
14
|
+
light: "tooltip-content--light",
|
|
15
15
|
},
|
|
16
16
|
},
|
|
17
17
|
defaultVariants: {
|
|
18
|
-
|
|
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
|
-
|
|
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 = "
|
|
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
|
|
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 } =
|
|
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
|
-
|
|
163
|
-
sideOffset =
|
|
164
|
-
|
|
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
|
-
}:
|
|
170
|
-
|
|
171
|
-
|
|
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={
|
|
210
|
-
side={
|
|
211
|
-
|
|
421
|
+
alignOffset={resolvedAlignOffset}
|
|
422
|
+
side={directionToSide[direction]}
|
|
423
|
+
align={align}
|
|
424
|
+
aria-label={ariaLabel ?? (derivedLabel || undefined)}
|
|
212
425
|
{...props}
|
|
213
426
|
>
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
};
|