@12-apps/ui 6.53.0 → 6.54.1
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/dist/_internal/components/data-display/AlertDialog/AlertDialog.js +2 -1
- package/dist/_internal/components/data-display/AlertDialog/AlertDialog.parts.js +9 -0
- package/dist/_internal/components/data-display/AlertDialog/AlertDialog.parts.js.map +1 -0
- package/dist/_internal/components/data-display/CardKit/use-remove-confirm.js +7 -6
- package/dist/_internal/components/data-display/CardKit/use-row-confirm.js +7 -6
- package/dist/_internal/components/data-display/DataViews/DataViewsTableBase.js +9 -8
- package/dist/_internal/components/data-display/DataViews/ManageViewsDialog.js +7 -6
- package/dist/_internal/components/data-display/DataViews/data-views-table-parts.js +8 -7
- package/dist/_internal/components/data-display/InteractiveTooltip/InteractiveTooltip.js +2 -2
- package/dist/_internal/components/data-display/InteractiveTooltip/InteractiveTooltip.styles.js +1 -1
- package/dist/_internal/components/data-display/Tooltip/Tooltip.js +1 -1
- package/dist/_internal/components/data-display/index.js +5 -4
- package/dist/_internal/components/feedback/ConfirmAction/ConfirmAction.js +4 -3
- package/dist/_internal/components/feedback/ConfirmAction/ConfirmAction.parts.js +3 -2
- package/dist/_internal/components/feedback/ConfirmAction/ConfirmButton.js +5 -4
- package/dist/_internal/components/feedback/ConfirmAction/with-confirmation.js +4 -3
- package/dist/_internal/components/feedback/index.js +6 -5
- package/dist/_internal/components/index.js +14 -13
- package/dist/_internal/components/layout/SettingCard/SettingCard.js +4 -4
- package/dist/_internal/components/layout/SettingCard/SettingCard.summary.js +3 -3
- package/dist/_internal/components/layout/SettingCard/SettingGroup.js +5 -5
- package/dist/_internal/components/layout/SettingCard/SettingSwitch.js +4 -4
- package/dist/_internal/components/layout/SettingCard/SettingToggle.js +5 -5
- package/dist/_internal/components/layout/index.js +7 -7
- package/dist/{chunk-QOLIBVGS.js → chunk-3CIIB74Y.js} +2 -2
- package/dist/{chunk-OG6LYQ7C.js → chunk-4PFKMNLP.js} +8 -68
- package/dist/chunk-4PFKMNLP.js.map +1 -0
- package/dist/{chunk-TQCQCW2P.js → chunk-524PRSSD.js} +3 -3
- package/dist/chunk-524PRSSD.js.map +1 -0
- package/dist/{chunk-Y5QDXG5N.js → chunk-6WNMVWAV.js} +2 -2
- package/dist/{chunk-OT3KFZRE.js → chunk-7WWPKA7Y.js} +2 -2
- package/dist/chunk-AET54GT6.js +99 -0
- package/dist/chunk-AET54GT6.js.map +1 -0
- package/dist/{chunk-ZXEDKRRT.js → chunk-ECVNRLKD.js} +2 -2
- package/dist/{chunk-JUHNEYCH.js → chunk-GCH7CLM4.js} +2 -2
- package/dist/{chunk-HCEJT65A.js → chunk-HKD4TDZP.js} +2 -2
- package/dist/{chunk-3GVMUASU.js → chunk-HV7HIMMY.js} +2 -2
- package/dist/chunk-IQQX3NDO.js +96 -0
- package/dist/chunk-IQQX3NDO.js.map +1 -0
- package/dist/{chunk-OESAYTNG.js → chunk-JDVHNDY3.js} +2 -2
- package/dist/{chunk-TYBZ4XZZ.js → chunk-KRIPQUFB.js} +27 -32
- package/dist/chunk-KRIPQUFB.js.map +1 -0
- package/dist/{chunk-4WCDMCIS.js → chunk-PI3GPLE2.js} +2 -2
- package/dist/{chunk-N4R6T5K7.js → chunk-PKSU3RBT.js} +2 -2
- package/dist/{chunk-IRNKHHQM.js → chunk-QKMXMBUK.js} +2 -2
- package/dist/{chunk-FWPFCK4D.js → chunk-S6G3CJFP.js} +2 -2
- package/dist/{chunk-BJZOBG24.js → chunk-UJHXEOU3.js} +2 -2
- package/dist/{chunk-U4QUDFPJ.js → chunk-VKVOP4OK.js} +2 -2
- package/dist/data-display/AlertDialog.js +2 -1
- package/dist/data-display/CardKit.js +8 -7
- package/dist/data-display/DataViews.js +9 -8
- package/dist/data-display/InteractiveTooltip.js +2 -2
- package/dist/data-display/Tooltip.js +1 -1
- package/dist/feedback/ConfirmAction.js +6 -5
- package/dist/layout/SettingCard.js +7 -7
- package/dist/types/components/data-display/AlertDialog/AlertDialog.d.ts.map +1 -1
- package/dist/types/components/data-display/AlertDialog/AlertDialog.parts.d.ts +13 -0
- package/dist/types/components/data-display/AlertDialog/AlertDialog.parts.d.ts.map +1 -0
- package/dist/types/components/data-display/AlertDialog/AlertDialog.types.d.ts +16 -3
- package/dist/types/components/data-display/AlertDialog/AlertDialog.types.d.ts.map +1 -1
- package/dist/types/components/data-display/InteractiveTooltip/InteractiveTooltip.d.ts +2 -2
- package/dist/types/components/data-display/InteractiveTooltip/InteractiveTooltip.d.ts.map +1 -1
- package/dist/types/components/data-display/Tooltip/Tooltip.d.ts +2 -2
- package/dist/types/components/data-display/Tooltip/Tooltip.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/data-display/AlertDialog/AlertDialog.md +30 -0
- package/src/components/data-display/AlertDialog/AlertDialog.parts.tsx +180 -0
- package/src/components/data-display/AlertDialog/AlertDialog.tsx +5 -85
- package/src/components/data-display/AlertDialog/AlertDialog.types.ts +17 -3
- package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.styles.ts +2 -2
- package/src/components/data-display/InteractiveTooltip/InteractiveTooltip.tsx +102 -99
- package/src/components/data-display/Tooltip/Tooltip.tsx +64 -56
- package/dist/chunk-OG6LYQ7C.js.map +0 -1
- package/dist/chunk-TQCQCW2P.js.map +0 -1
- package/dist/chunk-TYBZ4XZZ.js.map +0 -1
- package/dist/chunk-XM7QCNZQ.js +0 -110
- package/dist/chunk-XM7QCNZQ.js.map +0 -1
- /package/dist/{chunk-QOLIBVGS.js.map → chunk-3CIIB74Y.js.map} +0 -0
- /package/dist/{chunk-Y5QDXG5N.js.map → chunk-6WNMVWAV.js.map} +0 -0
- /package/dist/{chunk-OT3KFZRE.js.map → chunk-7WWPKA7Y.js.map} +0 -0
- /package/dist/{chunk-ZXEDKRRT.js.map → chunk-ECVNRLKD.js.map} +0 -0
- /package/dist/{chunk-JUHNEYCH.js.map → chunk-GCH7CLM4.js.map} +0 -0
- /package/dist/{chunk-HCEJT65A.js.map → chunk-HKD4TDZP.js.map} +0 -0
- /package/dist/{chunk-3GVMUASU.js.map → chunk-HV7HIMMY.js.map} +0 -0
- /package/dist/{chunk-OESAYTNG.js.map → chunk-JDVHNDY3.js.map} +0 -0
- /package/dist/{chunk-4WCDMCIS.js.map → chunk-PI3GPLE2.js.map} +0 -0
- /package/dist/{chunk-N4R6T5K7.js.map → chunk-PKSU3RBT.js.map} +0 -0
- /package/dist/{chunk-IRNKHHQM.js.map → chunk-QKMXMBUK.js.map} +0 -0
- /package/dist/{chunk-FWPFCK4D.js.map → chunk-S6G3CJFP.js.map} +0 -0
- /package/dist/{chunk-BJZOBG24.js.map → chunk-UJHXEOU3.js.map} +0 -0
- /package/dist/{chunk-U4QUDFPJ.js.map → chunk-VKVOP4OK.js.map} +0 -0
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
import Close from '@mui/icons-material/Close';
|
|
2
2
|
import Error from '@mui/icons-material/Error';
|
|
3
3
|
import Info from '@mui/icons-material/Info';
|
|
4
|
-
import type { ButtonProps } from '@mui/material/Button/index.js';
|
|
5
|
-
import Button from '@mui/material/Button/index.js';
|
|
6
|
-
import CircularProgress from '@mui/material/CircularProgress/index.js';
|
|
7
4
|
import Dialog from '@mui/material/Dialog/index.js';
|
|
8
|
-
import DialogActions from '@mui/material/DialogActions/index.js';
|
|
9
5
|
import DialogContent from '@mui/material/DialogContent/index.js';
|
|
10
6
|
import DialogContentText from '@mui/material/DialogContentText/index.js';
|
|
11
7
|
import DialogTitle from '@mui/material/DialogTitle/index.js';
|
|
12
8
|
import IconButton from '@mui/material/IconButton/index.js';
|
|
13
9
|
import Typography from '@mui/material/Typography/index.js';
|
|
14
|
-
import { alpha, keyframes, styled
|
|
10
|
+
import { alpha, keyframes, styled } from '@mui/material/styles/index.js';
|
|
15
11
|
import type { Theme } from '@mui/material/styles/index.js';
|
|
16
12
|
import React from 'react';
|
|
17
13
|
|
|
18
14
|
import { neutralTones } from '../../../tokens/ink';
|
|
19
15
|
|
|
16
|
+
import { AlertDialogFooter } from './AlertDialog.parts';
|
|
20
17
|
import type { AlertDialogProps } from './AlertDialog.types';
|
|
21
18
|
import { rem, rems } from '../../../tokens/relative';
|
|
22
19
|
|
|
@@ -140,11 +137,6 @@ const StyledDialogContent = styled(DialogContent)(({ theme }) => ({
|
|
|
140
137
|
paddingBottom: theme.spacing(2),
|
|
141
138
|
}));
|
|
142
139
|
|
|
143
|
-
const StyledDialogActions = styled(DialogActions)(({ theme }) => ({
|
|
144
|
-
padding: theme.spacing(2, 3, 3, 3),
|
|
145
|
-
gap: theme.spacing(1),
|
|
146
|
-
}));
|
|
147
|
-
|
|
148
140
|
const CloseButton = styled(IconButton)(({ theme }) => ({
|
|
149
141
|
position: 'absolute',
|
|
150
142
|
right: theme.spacing(1),
|
|
@@ -208,11 +200,6 @@ function ariaSlotProps(
|
|
|
208
200
|
};
|
|
209
201
|
}
|
|
210
202
|
|
|
211
|
-
/** Both variants render a filled confirm; only the colour distinguishes them. */
|
|
212
|
-
const CONFIRM_BUTTON_VARIANT: ButtonProps['variant'] = 'contained';
|
|
213
|
-
const confirmButtonColor = (variant: AlertDialogProps['variant']): 'error' | 'primary' =>
|
|
214
|
-
variant === 'destructive' ? 'error' : 'primary';
|
|
215
|
-
|
|
216
203
|
/** Title row: the variant icon (unless explicitly suppressed with `null`) + the title. */
|
|
217
204
|
function AlertDialogHeader({
|
|
218
205
|
title,
|
|
@@ -238,70 +225,6 @@ function AlertDialogHeader({
|
|
|
238
225
|
);
|
|
239
226
|
}
|
|
240
227
|
|
|
241
|
-
/** Action row: the optional cancel button and the (variant-coloured) confirm. */
|
|
242
|
-
function AlertDialogFooter({
|
|
243
|
-
variant,
|
|
244
|
-
cancelText = 'Cancel',
|
|
245
|
-
confirmText = 'Confirm',
|
|
246
|
-
showCancel = true,
|
|
247
|
-
loading = false,
|
|
248
|
-
confirmDisabled = false,
|
|
249
|
-
initialFocus = 'confirm',
|
|
250
|
-
onCancel,
|
|
251
|
-
onConfirm,
|
|
252
|
-
dataTestId,
|
|
253
|
-
}: Pick<
|
|
254
|
-
AlertDialogProps,
|
|
255
|
-
| 'variant'
|
|
256
|
-
| 'cancelText'
|
|
257
|
-
| 'confirmText'
|
|
258
|
-
| 'showCancel'
|
|
259
|
-
| 'loading'
|
|
260
|
-
| 'confirmDisabled'
|
|
261
|
-
| 'initialFocus'
|
|
262
|
-
> & {
|
|
263
|
-
onCancel: () => void;
|
|
264
|
-
onConfirm: () => void;
|
|
265
|
-
dataTestId: string;
|
|
266
|
-
}): React.ReactElement {
|
|
267
|
-
const theme = useTheme();
|
|
268
|
-
return (
|
|
269
|
-
<StyledDialogActions data-testid={`${dataTestId}-actions`}>
|
|
270
|
-
{showCancel && (
|
|
271
|
-
<Button
|
|
272
|
-
onClick={onCancel}
|
|
273
|
-
variant="outlined"
|
|
274
|
-
color="inherit"
|
|
275
|
-
disabled={loading}
|
|
276
|
-
autoFocus={initialFocus === 'cancel'}
|
|
277
|
-
data-testid={`${dataTestId}-cancel-button`}
|
|
278
|
-
>
|
|
279
|
-
{cancelText}
|
|
280
|
-
</Button>
|
|
281
|
-
)}
|
|
282
|
-
<Button
|
|
283
|
-
onClick={onConfirm}
|
|
284
|
-
variant={CONFIRM_BUTTON_VARIANT}
|
|
285
|
-
color={confirmButtonColor(variant)}
|
|
286
|
-
disabled={confirmDisabled || loading}
|
|
287
|
-
startIcon={
|
|
288
|
-
loading ? (
|
|
289
|
-
<CircularProgress
|
|
290
|
-
size={rem(theme, 16)}
|
|
291
|
-
color="inherit"
|
|
292
|
-
data-testid={`${dataTestId}-loading-spinner`}
|
|
293
|
-
/>
|
|
294
|
-
) : undefined
|
|
295
|
-
}
|
|
296
|
-
autoFocus={initialFocus !== 'cancel'}
|
|
297
|
-
data-testid={`${dataTestId}-confirm-button`}
|
|
298
|
-
>
|
|
299
|
-
{confirmText}
|
|
300
|
-
</Button>
|
|
301
|
-
</StyledDialogActions>
|
|
302
|
-
);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
228
|
export const AlertDialog = React.forwardRef<HTMLDivElement, AlertDialogProps>(
|
|
306
229
|
({
|
|
307
230
|
variant = 'default',
|
|
@@ -318,6 +241,7 @@ export const AlertDialog = React.forwardRef<HTMLDivElement, AlertDialogProps>(
|
|
|
318
241
|
loading,
|
|
319
242
|
confirmDisabled,
|
|
320
243
|
initialFocus,
|
|
244
|
+
emphasis,
|
|
321
245
|
children,
|
|
322
246
|
onClose,
|
|
323
247
|
'data-testid': dataTestId = 'alert-dialog',
|
|
@@ -348,12 +272,7 @@ export const AlertDialog = React.forwardRef<HTMLDivElement, AlertDialogProps>(
|
|
|
348
272
|
<Close />
|
|
349
273
|
</CloseButton>
|
|
350
274
|
|
|
351
|
-
<AlertDialogHeader
|
|
352
|
-
title={title}
|
|
353
|
-
icon={icon}
|
|
354
|
-
variant={variant}
|
|
355
|
-
dataTestId={dataTestId}
|
|
356
|
-
/>
|
|
275
|
+
<AlertDialogHeader title={title} icon={icon} variant={variant} dataTestId={dataTestId} />
|
|
357
276
|
|
|
358
277
|
<StyledDialogContent data-testid={`${dataTestId}-content`}>
|
|
359
278
|
{description && (
|
|
@@ -375,6 +294,7 @@ export const AlertDialog = React.forwardRef<HTMLDivElement, AlertDialogProps>(
|
|
|
375
294
|
loading={loading}
|
|
376
295
|
confirmDisabled={confirmDisabled}
|
|
377
296
|
initialFocus={initialFocus}
|
|
297
|
+
emphasis={emphasis}
|
|
378
298
|
onCancel={handleCancel}
|
|
379
299
|
onConfirm={() => onConfirm?.()}
|
|
380
300
|
dataTestId={dataTestId}
|
|
@@ -68,12 +68,26 @@ export interface AlertDialogProps extends Omit<MuiDialogProps, 'variant'> {
|
|
|
68
68
|
confirmDisabled?: boolean;
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
|
-
* Which button owns focus when the dialog opens. Defaults to
|
|
72
|
-
*
|
|
73
|
-
*
|
|
71
|
+
* Which button owns focus when the dialog opens. Defaults to the emphasised
|
|
72
|
+
* one — `confirm`, unless `emphasis="cancel"` — which is right for an
|
|
73
|
+
* acknowledgement; a destructive dialog should pass `cancel` so a stray Enter
|
|
74
|
+
* on the trigger cannot complete the action.
|
|
74
75
|
*/
|
|
75
76
|
initialFocus?: 'confirm' | 'cancel';
|
|
76
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Which button is the filled, primary one. Defaults to `confirm`. Pass
|
|
80
|
+
* `cancel` when backing out is the action to steer toward — a "discard what
|
|
81
|
+
* you wrote?" question, where the confirm is the loss: cancel is then filled
|
|
82
|
+
* and takes the primary slot (right, or on top once the row stacks), and the
|
|
83
|
+
* confirm steps down to the neutral outline cancel normally wears — primary
|
|
84
|
+
* and neutral, whatever the variant. The two buttons then grow to share the
|
|
85
|
+
* row, so once it stacks each takes its full width; focus opens on the cancel
|
|
86
|
+
* unless `initialFocus` says otherwise. With `showCancel={false}` it has no
|
|
87
|
+
* effect: the confirm keeps its default, filled look.
|
|
88
|
+
*/
|
|
89
|
+
emphasis?: 'confirm' | 'cancel';
|
|
90
|
+
|
|
77
91
|
/**
|
|
78
92
|
* Optional data-testid attribute for testing
|
|
79
93
|
*/
|
|
@@ -34,7 +34,7 @@ export const variantStyles = (theme: Theme, variant?: string): CSSObject => {
|
|
|
34
34
|
switch (variant) {
|
|
35
35
|
case 'default':
|
|
36
36
|
return {
|
|
37
|
-
backgroundColor:
|
|
37
|
+
backgroundColor: neutralTones(theme).inverseSurface,
|
|
38
38
|
color: absoluteInk(theme).white,
|
|
39
39
|
boxShadow: `${rems(theme, 0, 4, 12)} ${shadowInk(theme, 0.3)}`,
|
|
40
40
|
};
|
|
@@ -98,6 +98,6 @@ export const arrowColor = (theme: Theme, variant?: string): string => {
|
|
|
98
98
|
case 'dark':
|
|
99
99
|
return absoluteInk(theme).black;
|
|
100
100
|
default:
|
|
101
|
-
return
|
|
101
|
+
return neutralTones(theme).inverseSurface;
|
|
102
102
|
}
|
|
103
103
|
};
|
|
@@ -1,124 +1,127 @@
|
|
|
1
|
-
import MuiTooltip from
|
|
2
|
-
import {
|
|
3
|
-
import React from
|
|
1
|
+
import MuiTooltip from "@mui/material/Tooltip/index.js";
|
|
2
|
+
import type { CSSObject, Theme } from "@mui/material/styles/index.js";
|
|
3
|
+
import React from "react";
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
resolveTooltipProps,
|
|
7
|
+
useHoverOpen,
|
|
8
|
+
usePinnedTooltip,
|
|
9
|
+
} from "./InteractiveTooltip.hooks";
|
|
6
10
|
import {
|
|
7
11
|
arrowColor,
|
|
8
12
|
emphasisStyles,
|
|
9
13
|
getSizeStyles,
|
|
10
14
|
tooltipCap,
|
|
11
15
|
variantStyles,
|
|
12
|
-
} from
|
|
13
|
-
import type { InteractiveTooltipProps } from
|
|
14
|
-
|
|
15
|
-
const StyledTooltip = styled(MuiTooltip, {
|
|
16
|
-
shouldForwardProp: (prop) =>
|
|
17
|
-
!['customVariant', 'customSize', 'glow', 'pulse'].includes(prop as string),
|
|
18
|
-
})<{
|
|
19
|
-
customVariant?: string;
|
|
20
|
-
customSize?: string;
|
|
21
|
-
glow?: boolean;
|
|
22
|
-
pulse?: boolean;
|
|
23
|
-
}>(({ theme, customVariant, customSize, glow, pulse }) => {
|
|
24
|
-
const sizeStyles = getSizeStyles(customSize);
|
|
16
|
+
} from "./InteractiveTooltip.styles";
|
|
17
|
+
import type { InteractiveTooltipProps } from "./InteractiveTooltip.types";
|
|
25
18
|
|
|
26
|
-
|
|
27
|
-
|
|
19
|
+
/**
|
|
20
|
+
* The bubble's own look, applied to the BUBBLE (FUT-3093) — see `Tooltip`'s
|
|
21
|
+
* `bubbleSx`: a `& .MuiTooltip-tooltip` rule on the root never reached the
|
|
22
|
+
* portalled bubble, so the variants had never been on screen.
|
|
23
|
+
*/
|
|
24
|
+
const bubbleSx =
|
|
25
|
+
(
|
|
26
|
+
variant: string | undefined,
|
|
27
|
+
size: string | undefined,
|
|
28
|
+
glow: boolean | undefined,
|
|
29
|
+
pulse: boolean | undefined,
|
|
30
|
+
maxWidth: number | undefined,
|
|
31
|
+
) =>
|
|
32
|
+
(theme: Theme): CSSObject => {
|
|
33
|
+
const sizeStyles = getSizeStyles(size);
|
|
34
|
+
return {
|
|
28
35
|
borderRadius: theme.spacing(1),
|
|
29
36
|
fontSize: sizeStyles.fontSize(theme),
|
|
30
37
|
padding: sizeStyles.padding(theme),
|
|
31
38
|
fontWeight: 500,
|
|
32
|
-
transition:
|
|
33
|
-
position:
|
|
34
|
-
|
|
35
|
-
...variantStyles(theme,
|
|
39
|
+
transition: "all 0.3s ease",
|
|
40
|
+
position: "relative",
|
|
41
|
+
maxWidth: tooltipCap(maxWidth)(theme),
|
|
42
|
+
...variantStyles(theme, variant),
|
|
36
43
|
...emphasisStyles(theme, glow, pulse),
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
'& .MuiTooltip-arrow': {
|
|
40
|
-
color: arrowColor(theme, customVariant),
|
|
41
|
-
},
|
|
44
|
+
};
|
|
42
45
|
};
|
|
43
|
-
});
|
|
44
46
|
|
|
45
|
-
export const InteractiveTooltip = React.forwardRef<
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
47
|
+
export const InteractiveTooltip = React.forwardRef<
|
|
48
|
+
HTMLDivElement,
|
|
49
|
+
InteractiveTooltipProps
|
|
50
|
+
>((rawProps, ref) => {
|
|
51
|
+
const {
|
|
52
|
+
variant,
|
|
53
|
+
size,
|
|
54
|
+
glow,
|
|
55
|
+
pulse,
|
|
56
|
+
maxWidth,
|
|
57
|
+
dataTestId,
|
|
58
|
+
hoverContent,
|
|
59
|
+
pinnedContent,
|
|
60
|
+
clickable,
|
|
61
|
+
onPin,
|
|
62
|
+
onUnpin,
|
|
63
|
+
className,
|
|
64
|
+
children,
|
|
65
|
+
onOpen,
|
|
66
|
+
onClose,
|
|
67
|
+
...props
|
|
68
|
+
} = resolveTooltipProps(rawProps);
|
|
65
69
|
|
|
66
|
-
|
|
70
|
+
const { isPinned, isControlledOpen, wrapperRef, handleClick } =
|
|
71
|
+
usePinnedTooltip({
|
|
67
72
|
clickable,
|
|
68
73
|
onPin,
|
|
69
74
|
onUnpin,
|
|
70
75
|
});
|
|
71
|
-
|
|
76
|
+
const hover = useHoverOpen(isPinned, onOpen, onClose);
|
|
72
77
|
|
|
73
|
-
|
|
74
|
-
|
|
78
|
+
// Determine which content to show
|
|
79
|
+
const tooltipContent = isPinned ? pinnedContent : hoverContent;
|
|
75
80
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
+
// Child with click handler and ref
|
|
82
|
+
const childElement = children as React.ReactElement<{
|
|
83
|
+
onClick?: (e: React.MouseEvent) => void;
|
|
84
|
+
"data-testid"?: string;
|
|
85
|
+
}>;
|
|
81
86
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
const childWithProps = React.cloneElement(childElement, {
|
|
88
|
+
onClick: (e: React.MouseEvent) => {
|
|
89
|
+
handleClick(e);
|
|
90
|
+
// Call original onClick if it exists
|
|
91
|
+
childElement.props.onClick?.(e);
|
|
92
|
+
},
|
|
93
|
+
"data-testid": dataTestId ? `${dataTestId}-trigger` : undefined,
|
|
94
|
+
});
|
|
90
95
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
},
|
|
122
|
-
);
|
|
96
|
+
return (
|
|
97
|
+
<div ref={wrapperRef} className={className}>
|
|
98
|
+
<MuiTooltip
|
|
99
|
+
ref={ref}
|
|
100
|
+
title={tooltipContent}
|
|
101
|
+
open={isPinned ? isControlledOpen : hover.open}
|
|
102
|
+
onOpen={hover.onOpen}
|
|
103
|
+
onClose={hover.onClose}
|
|
104
|
+
enterDelay={isPinned ? 0 : 100}
|
|
105
|
+
leaveDelay={0}
|
|
106
|
+
disableHoverListener={isPinned}
|
|
107
|
+
disableFocusListener={isPinned}
|
|
108
|
+
disableTouchListener={isPinned}
|
|
109
|
+
slotProps={{
|
|
110
|
+
tooltip: {
|
|
111
|
+
sx: bubbleSx(variant, size, glow, pulse, maxWidth),
|
|
112
|
+
role: "tooltip",
|
|
113
|
+
...(dataTestId && { "data-testid": `${dataTestId}-content` }),
|
|
114
|
+
},
|
|
115
|
+
arrow: {
|
|
116
|
+
sx: (theme: Theme) => ({ color: arrowColor(theme, variant) }),
|
|
117
|
+
},
|
|
118
|
+
}}
|
|
119
|
+
{...props}
|
|
120
|
+
>
|
|
121
|
+
{childWithProps}
|
|
122
|
+
</MuiTooltip>
|
|
123
|
+
</div>
|
|
124
|
+
);
|
|
125
|
+
});
|
|
123
126
|
|
|
124
|
-
InteractiveTooltip.displayName =
|
|
127
|
+
InteractiveTooltip.displayName = "InteractiveTooltip";
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import MuiTooltip from
|
|
2
|
-
import { alpha, keyframes
|
|
3
|
-
import type { CSSObject, Theme } from
|
|
4
|
-
import React from
|
|
1
|
+
import MuiTooltip from "@mui/material/Tooltip/index.js";
|
|
2
|
+
import { alpha, keyframes } from "@mui/material/styles/index.js";
|
|
3
|
+
import type { CSSObject, Theme } from "@mui/material/styles/index.js";
|
|
4
|
+
import React from "react";
|
|
5
5
|
|
|
6
|
-
import type { TooltipProps } from
|
|
7
|
-
import { absoluteInk, neutralTones } from
|
|
8
|
-
import { rem, rems, sxRem } from
|
|
6
|
+
import type { TooltipProps } from "./Tooltip.types";
|
|
7
|
+
import { absoluteInk, neutralTones } from "../../../tokens/ink";
|
|
8
|
+
import { rem, rems, sxRem } from "../../../tokens/relative";
|
|
9
9
|
|
|
10
10
|
// Define pulse animation
|
|
11
11
|
const pulseAnimation = keyframes`
|
|
@@ -36,24 +36,24 @@ const getSizeStyles = (
|
|
|
36
36
|
|
|
37
37
|
const variantStyles = (theme: Theme, variant?: string): CSSObject => {
|
|
38
38
|
switch (variant) {
|
|
39
|
-
case
|
|
39
|
+
case "default":
|
|
40
40
|
return {
|
|
41
|
-
backgroundColor:
|
|
41
|
+
backgroundColor: neutralTones(theme).inverseSurface,
|
|
42
42
|
color: absoluteInk(theme).white,
|
|
43
43
|
};
|
|
44
|
-
case
|
|
44
|
+
case "dark":
|
|
45
45
|
return {
|
|
46
46
|
backgroundColor: neutralTones(theme).inverseSurface,
|
|
47
47
|
color: absoluteInk(theme).white,
|
|
48
48
|
};
|
|
49
|
-
case
|
|
49
|
+
case "light":
|
|
50
50
|
return {
|
|
51
51
|
backgroundColor: absoluteInk(theme).white,
|
|
52
52
|
color: theme.palette.text.primary,
|
|
53
53
|
border: `1px solid ${alpha(theme.palette.divider, 0.2)}`,
|
|
54
54
|
boxShadow: theme.shadows[4],
|
|
55
55
|
};
|
|
56
|
-
case
|
|
56
|
+
case "glass":
|
|
57
57
|
return {
|
|
58
58
|
backgroundColor: alpha(theme.palette.background.paper, 0.1),
|
|
59
59
|
backdropFilter: `blur(${rem(theme, 20)})`,
|
|
@@ -67,10 +67,14 @@ const variantStyles = (theme: Theme, variant?: string): CSSObject => {
|
|
|
67
67
|
|
|
68
68
|
// glow and pulse are independent flags. The three combinations used to be spelled
|
|
69
69
|
// out one by one, but each is just the union of whichever flags are set.
|
|
70
|
-
const emphasisStyles = (
|
|
70
|
+
const emphasisStyles = (
|
|
71
|
+
theme: Theme,
|
|
72
|
+
glow?: boolean,
|
|
73
|
+
pulse?: boolean,
|
|
74
|
+
): CSSObject => ({
|
|
71
75
|
...(glow && {
|
|
72
76
|
boxShadow: `${rems(theme, 0, 0, 15, 3)} ${alpha(theme.palette.primary.main, 0.4)} !important`,
|
|
73
|
-
filter:
|
|
77
|
+
filter: "brightness(1.05)",
|
|
74
78
|
}),
|
|
75
79
|
...(pulse && {
|
|
76
80
|
animation: `${pulseAnimation} 2s infinite`,
|
|
@@ -79,46 +83,48 @@ const emphasisStyles = (theme: Theme, glow?: boolean, pulse?: boolean): CSSObjec
|
|
|
79
83
|
|
|
80
84
|
const arrowColor = (theme: Theme, variant?: string): string => {
|
|
81
85
|
switch (variant) {
|
|
82
|
-
case
|
|
86
|
+
case "light":
|
|
83
87
|
return absoluteInk(theme).white;
|
|
84
|
-
case
|
|
88
|
+
case "glass":
|
|
85
89
|
return alpha(theme.palette.background.paper, 0.1);
|
|
86
|
-
case
|
|
90
|
+
case "dark":
|
|
87
91
|
return neutralTones(theme).inverseSurface;
|
|
88
92
|
default:
|
|
89
|
-
return
|
|
93
|
+
return neutralTones(theme).inverseSurface;
|
|
90
94
|
}
|
|
91
95
|
};
|
|
92
96
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
97
|
+
/**
|
|
98
|
+
* The bubble's own look, applied to the BUBBLE (FUT-3093).
|
|
99
|
+
*
|
|
100
|
+
* It used to sit on a `styled(MuiTooltip)` root as `& .MuiTooltip-tooltip`, but
|
|
101
|
+
* MUI renders the bubble in a portal, outside that root — so none of it ever
|
|
102
|
+
* reached the screen, and every tooltip wore MUI's see-through grey instead of
|
|
103
|
+
* the variant it asked for. `slotProps.tooltip.sx` lands on the bubble itself.
|
|
104
|
+
* No `overflow: hidden`: now that it applies, it would clip an `arrow`.
|
|
105
|
+
*/
|
|
106
|
+
const bubbleSx =
|
|
107
|
+
(
|
|
108
|
+
variant: string,
|
|
109
|
+
size: string,
|
|
110
|
+
glow: boolean,
|
|
111
|
+
pulse: boolean,
|
|
112
|
+
maxWidth: number | undefined,
|
|
113
|
+
) =>
|
|
114
|
+
(theme: Theme): CSSObject => {
|
|
115
|
+
const sizeStyles = getSizeStyles(size);
|
|
116
|
+
return {
|
|
106
117
|
borderRadius: theme.spacing(1),
|
|
107
118
|
fontSize: sizeStyles.fontSize(theme),
|
|
108
119
|
padding: sizeStyles.padding(theme),
|
|
109
120
|
fontWeight: 500,
|
|
110
|
-
transition:
|
|
111
|
-
position:
|
|
112
|
-
|
|
113
|
-
...variantStyles(theme,
|
|
121
|
+
transition: "all 0.3s ease",
|
|
122
|
+
position: "relative",
|
|
123
|
+
maxWidth: tooltipCap(maxWidth)(theme),
|
|
124
|
+
...variantStyles(theme, variant),
|
|
114
125
|
...emphasisStyles(theme, glow, pulse),
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
'& .MuiTooltip-arrow': {
|
|
118
|
-
color: arrowColor(theme, customVariant),
|
|
119
|
-
},
|
|
126
|
+
};
|
|
120
127
|
};
|
|
121
|
-
});
|
|
122
128
|
|
|
123
129
|
/**
|
|
124
130
|
* The tooltip's cap: 300 design px unless the caller says otherwise. `sx` has
|
|
@@ -134,8 +140,8 @@ const tooltipCap =
|
|
|
134
140
|
export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
|
|
135
141
|
(
|
|
136
142
|
{
|
|
137
|
-
variant =
|
|
138
|
-
size =
|
|
143
|
+
variant = "default",
|
|
144
|
+
size = "md",
|
|
139
145
|
glow = false,
|
|
140
146
|
pulse = false,
|
|
141
147
|
maxWidth,
|
|
@@ -146,18 +152,17 @@ export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
|
|
|
146
152
|
ref,
|
|
147
153
|
) => {
|
|
148
154
|
const childWithTestId = dataTestId
|
|
149
|
-
? React.cloneElement(
|
|
150
|
-
|
|
151
|
-
|
|
155
|
+
? React.cloneElement(
|
|
156
|
+
children as React.ReactElement<{ "data-testid"?: string }>,
|
|
157
|
+
{
|
|
158
|
+
"data-testid": `${dataTestId}-trigger`,
|
|
159
|
+
},
|
|
160
|
+
)
|
|
152
161
|
: children;
|
|
153
162
|
|
|
154
163
|
return (
|
|
155
|
-
<
|
|
164
|
+
<MuiTooltip
|
|
156
165
|
ref={ref}
|
|
157
|
-
customVariant={variant}
|
|
158
|
-
customSize={size}
|
|
159
|
-
glow={glow}
|
|
160
|
-
pulse={pulse}
|
|
161
166
|
enterDelay={0}
|
|
162
167
|
leaveDelay={0}
|
|
163
168
|
disableHoverListener={false}
|
|
@@ -165,17 +170,20 @@ export const Tooltip = React.forwardRef<HTMLDivElement, TooltipProps>(
|
|
|
165
170
|
disableTouchListener={false}
|
|
166
171
|
slotProps={{
|
|
167
172
|
tooltip: {
|
|
168
|
-
sx:
|
|
169
|
-
role:
|
|
170
|
-
...(dataTestId && {
|
|
173
|
+
sx: bubbleSx(variant, size, glow, pulse, maxWidth),
|
|
174
|
+
role: "tooltip",
|
|
175
|
+
...(dataTestId && { "data-testid": `${dataTestId}-content` }),
|
|
176
|
+
},
|
|
177
|
+
arrow: {
|
|
178
|
+
sx: (theme: Theme) => ({ color: arrowColor(theme, variant) }),
|
|
171
179
|
},
|
|
172
180
|
}}
|
|
173
181
|
{...props}
|
|
174
182
|
>
|
|
175
183
|
{childWithTestId}
|
|
176
|
-
</
|
|
184
|
+
</MuiTooltip>
|
|
177
185
|
);
|
|
178
186
|
},
|
|
179
187
|
);
|
|
180
188
|
|
|
181
|
-
Tooltip.displayName =
|
|
189
|
+
Tooltip.displayName = "Tooltip";
|