@telus-uds/components-base 1.24.1 → 1.24.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/CHANGELOG.md +13 -2
- package/lib/Box/Box.js +3 -1
- package/lib/Divider/Divider.js +3 -0
- package/lib/FlexGrid/FlexGrid.js +3 -1
- package/lib/Link/InlinePressable.js +8 -1
- package/lib/Link/LinkBase.js +6 -8
- package/lib/StackView/StackView.js +3 -1
- package/lib/StackView/StackWrapBox.js +3 -1
- package/lib/StackView/StackWrapGap.js +3 -1
- package/lib/ThemeProvider/utils/styles.js +1 -3
- package/lib/Tooltip/Tooltip.js +85 -158
- package/lib/Tooltip/Tooltip.native.js +357 -0
- package/lib/Tooltip/index.js +6 -1
- package/lib/Tooltip/shared.js +39 -0
- package/lib/Typography/Typography.js +3 -1
- package/lib/utils/floating-ui/index.js +43 -0
- package/lib/utils/floating-ui/index.native.js +43 -0
- package/lib-module/Box/Box.js +3 -1
- package/lib-module/Divider/Divider.js +3 -0
- package/lib-module/FlexGrid/FlexGrid.js +3 -1
- package/lib-module/Link/InlinePressable.js +8 -1
- package/lib-module/Link/LinkBase.js +6 -8
- package/lib-module/StackView/StackView.js +3 -1
- package/lib-module/StackView/StackWrapBox.js +3 -1
- package/lib-module/StackView/StackWrapGap.js +3 -1
- package/lib-module/ThemeProvider/utils/styles.js +1 -3
- package/lib-module/Tooltip/Tooltip.js +85 -155
- package/lib-module/Tooltip/Tooltip.native.js +326 -0
- package/lib-module/Tooltip/index.js +4 -1
- package/lib-module/Tooltip/shared.js +27 -0
- package/lib-module/Typography/Typography.js +3 -1
- package/lib-module/utils/floating-ui/index.js +1 -0
- package/lib-module/utils/floating-ui/index.native.js +1 -0
- package/package.json +4 -2
- package/src/Box/Box.jsx +1 -0
- package/src/Divider/Divider.jsx +3 -0
- package/src/FlexGrid/FlexGrid.jsx +1 -0
- package/src/Link/InlinePressable.jsx +9 -3
- package/src/Link/LinkBase.jsx +17 -10
- package/src/StackView/StackView.jsx +1 -0
- package/src/StackView/StackWrapBox.jsx +1 -0
- package/src/StackView/StackWrapGap.jsx +1 -0
- package/src/ThemeProvider/utils/styles.js +1 -3
- package/src/Tooltip/Tooltip.jsx +79 -156
- package/src/Tooltip/Tooltip.native.jsx +283 -0
- package/src/Tooltip/index.js +5 -1
- package/src/Tooltip/shared.js +27 -0
- package/src/Typography/Typography.jsx +1 -0
- package/src/utils/floating-ui/index.js +1 -0
- package/src/utils/floating-ui/index.native.js +1 -0
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import React, { forwardRef, useEffect, useRef, useState } from 'react';
|
|
2
|
-
import Dimensions from "react-native-web/dist/exports/Dimensions";
|
|
3
2
|
import Platform from "react-native-web/dist/exports/Platform";
|
|
4
3
|
import Pressable from "react-native-web/dist/exports/Pressable";
|
|
5
4
|
import StyleSheet from "react-native-web/dist/exports/StyleSheet";
|
|
6
5
|
import Text from "react-native-web/dist/exports/Text";
|
|
7
6
|
import View from "react-native-web/dist/exports/View";
|
|
8
|
-
import
|
|
7
|
+
import propTypes from './shared';
|
|
8
|
+
import { useFloating, arrow, offset, shift, flip, autoPlacement } from '../utils/floating-ui';
|
|
9
9
|
import { applyShadowToken, applyTextStyles, useThemeTokens } from '../ThemeProvider';
|
|
10
|
-
import { a11yProps,
|
|
11
|
-
import Backdrop from './Backdrop';
|
|
12
|
-
import getTooltipPosition from './getTooltipPosition';
|
|
10
|
+
import { a11yProps, selectSystemProps, selectTokens, viewProps } from '../utils';
|
|
13
11
|
import TooltipButton from '../TooltipButton';
|
|
14
12
|
import useCopy from '../utils/useCopy';
|
|
15
13
|
import dictionary from './dictionary';
|
|
@@ -47,20 +45,20 @@ const selectTooltipShadowStyles = _ref2 => {
|
|
|
47
45
|
};
|
|
48
46
|
};
|
|
49
47
|
|
|
50
|
-
const
|
|
48
|
+
const selectMobileTooltipPositionStyles = _ref3 => {
|
|
51
49
|
let {
|
|
52
50
|
top,
|
|
53
51
|
left,
|
|
54
|
-
|
|
52
|
+
position
|
|
55
53
|
} = _ref3;
|
|
56
54
|
return {
|
|
57
55
|
top,
|
|
58
56
|
left,
|
|
59
|
-
|
|
57
|
+
position
|
|
60
58
|
};
|
|
61
59
|
};
|
|
62
60
|
|
|
63
|
-
const
|
|
61
|
+
const selectMobileArrowStyles = (_ref4, _ref5) => {
|
|
64
62
|
let {
|
|
65
63
|
backgroundColor,
|
|
66
64
|
arrowWidth,
|
|
@@ -68,56 +66,31 @@ const selectArrowStyles = (_ref4, _ref5) => {
|
|
|
68
66
|
shadow
|
|
69
67
|
} = _ref4;
|
|
70
68
|
let {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
69
|
+
placement,
|
|
70
|
+
x: arrowX,
|
|
71
|
+
y: arrowY
|
|
74
72
|
} = _ref5;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
// calculate the pixel values
|
|
82
|
-
|
|
83
|
-
const directionalStyles = {
|
|
84
|
-
above: {
|
|
85
|
-
bottom: verticalOffset,
|
|
86
|
-
left: tooltipWidth / 2 - horizontalOffset,
|
|
87
|
-
transform: [{
|
|
88
|
-
rotateZ: '45deg'
|
|
89
|
-
}]
|
|
90
|
-
},
|
|
91
|
-
below: {
|
|
92
|
-
top: verticalOffset,
|
|
93
|
-
left: tooltipWidth / 2 - horizontalOffset,
|
|
94
|
-
transform: [{
|
|
95
|
-
rotateZ: '-135deg'
|
|
96
|
-
}]
|
|
97
|
-
},
|
|
98
|
-
left: {
|
|
99
|
-
right: verticalOffset,
|
|
100
|
-
top: tooltipHeight / 2 - horizontalOffset,
|
|
101
|
-
transform: [{
|
|
102
|
-
rotateZ: '-45deg'
|
|
103
|
-
}]
|
|
104
|
-
},
|
|
105
|
-
right: {
|
|
106
|
-
left: verticalOffset,
|
|
107
|
-
top: tooltipHeight / 2 - horizontalOffset,
|
|
108
|
-
transform: [{
|
|
109
|
-
rotateZ: '135deg'
|
|
110
|
-
}]
|
|
111
|
-
}
|
|
112
|
-
};
|
|
73
|
+
const staticSide = {
|
|
74
|
+
top: 'bottom',
|
|
75
|
+
right: 'left',
|
|
76
|
+
bottom: 'top',
|
|
77
|
+
left: 'right'
|
|
78
|
+
}[placement.split('-')[0]];
|
|
113
79
|
return {
|
|
114
80
|
backgroundColor,
|
|
115
|
-
width:
|
|
116
|
-
height:
|
|
81
|
+
width: arrowWidth,
|
|
82
|
+
height: arrowWidth,
|
|
117
83
|
borderBottomRightRadius: arrowBorderRadius,
|
|
118
84
|
// this corner will be the arrow tip after rotation
|
|
85
|
+
transform: [{
|
|
86
|
+
rotateZ: '45deg'
|
|
87
|
+
}],
|
|
119
88
|
...applyShadowToken(shadow),
|
|
120
|
-
|
|
89
|
+
left: arrowX != null && arrowX,
|
|
90
|
+
top: arrowY != null && arrowY,
|
|
91
|
+
right: '',
|
|
92
|
+
bottom: '',
|
|
93
|
+
[staticSide]: '-4px'
|
|
121
94
|
};
|
|
122
95
|
};
|
|
123
96
|
|
|
@@ -155,40 +128,50 @@ const Tooltip = /*#__PURE__*/forwardRef((_ref6, ref) => {
|
|
|
155
128
|
...rest
|
|
156
129
|
} = _ref6;
|
|
157
130
|
const [isOpen, setIsOpen] = useState(false);
|
|
158
|
-
const
|
|
159
|
-
const [controlLayout, setControlLayout] = useState(null);
|
|
131
|
+
const arrowRef = useRef();
|
|
160
132
|
const [tooltipDimensions, setTooltipDimensions] = useState(null);
|
|
161
|
-
const
|
|
162
|
-
|
|
133
|
+
const positions = {
|
|
134
|
+
auto: undefined,
|
|
135
|
+
// Auto needs to specifically bet set to undefined
|
|
136
|
+
above: 'top',
|
|
137
|
+
right: 'right',
|
|
138
|
+
below: 'bottom',
|
|
139
|
+
left: 'left'
|
|
140
|
+
}; // "AutoPlacement" can't be used with "flip"
|
|
141
|
+
|
|
142
|
+
const additionalMiddleware = position !== 'auto' ? autoPlacement : flip;
|
|
143
|
+
const {
|
|
144
|
+
x,
|
|
145
|
+
y,
|
|
146
|
+
reference,
|
|
147
|
+
floating,
|
|
148
|
+
strategy,
|
|
149
|
+
placement,
|
|
150
|
+
middlewareData: {
|
|
151
|
+
arrow: arrowData = {}
|
|
152
|
+
}
|
|
153
|
+
} = useFloating({
|
|
154
|
+
placement: positions[position],
|
|
155
|
+
middleware: [shift(), additionalMiddleware(), offset(15), arrow({
|
|
156
|
+
element: arrowRef
|
|
157
|
+
})]
|
|
158
|
+
});
|
|
163
159
|
const getCopy = useCopy({
|
|
164
160
|
dictionary,
|
|
165
161
|
copy
|
|
166
162
|
});
|
|
167
163
|
const themeTokens = useThemeTokens('Tooltip', tokens, variant);
|
|
168
|
-
const {
|
|
169
|
-
arrowWidth,
|
|
170
|
-
arrowOffset
|
|
171
|
-
} = themeTokens;
|
|
172
|
-
useEffect(() => {
|
|
173
|
-
const subscription = Dimensions.addEventListener('change', _ref7 => {
|
|
174
|
-
let {
|
|
175
|
-
window
|
|
176
|
-
} = _ref7;
|
|
177
|
-
setWindowDimensions(window);
|
|
178
|
-
});
|
|
179
|
-
return () => subscription === null || subscription === void 0 ? void 0 : subscription.remove();
|
|
180
|
-
});
|
|
181
164
|
|
|
182
165
|
const toggleIsOpen = () => setIsOpen(!isOpen);
|
|
183
166
|
|
|
184
167
|
const close = () => setIsOpen(false);
|
|
185
168
|
|
|
186
|
-
const getPressableState =
|
|
169
|
+
const getPressableState = _ref7 => {
|
|
187
170
|
let {
|
|
188
171
|
pressed,
|
|
189
172
|
hovered,
|
|
190
173
|
focused
|
|
191
|
-
} =
|
|
174
|
+
} = _ref7;
|
|
192
175
|
return {
|
|
193
176
|
pressed,
|
|
194
177
|
hover: hovered,
|
|
@@ -196,7 +179,7 @@ const Tooltip = /*#__PURE__*/forwardRef((_ref6, ref) => {
|
|
|
196
179
|
};
|
|
197
180
|
};
|
|
198
181
|
|
|
199
|
-
const onTooltipLayout =
|
|
182
|
+
const onTooltipLayout = _ref8 => {
|
|
200
183
|
let {
|
|
201
184
|
nativeEvent: {
|
|
202
185
|
layout: {
|
|
@@ -204,7 +187,7 @@ const Tooltip = /*#__PURE__*/forwardRef((_ref6, ref) => {
|
|
|
204
187
|
height
|
|
205
188
|
}
|
|
206
189
|
}
|
|
207
|
-
} =
|
|
190
|
+
} = _ref8;
|
|
208
191
|
|
|
209
192
|
if (tooltipDimensions === null || tooltipDimensions.width !== width || tooltipDimensions.height !== height) {
|
|
210
193
|
setTooltipDimensions({
|
|
@@ -219,43 +202,10 @@ const Tooltip = /*#__PURE__*/forwardRef((_ref6, ref) => {
|
|
|
219
202
|
};
|
|
220
203
|
|
|
221
204
|
useEffect(() => {
|
|
222
|
-
if (isOpen) {
|
|
223
|
-
controlRef.current.measureInWindow((x, y, width, height) => {
|
|
224
|
-
setControlLayout({
|
|
225
|
-
x,
|
|
226
|
-
y,
|
|
227
|
-
width,
|
|
228
|
-
height
|
|
229
|
-
});
|
|
230
|
-
});
|
|
231
|
-
} else {
|
|
232
|
-
setControlLayout(null);
|
|
205
|
+
if (!isOpen) {
|
|
233
206
|
setTooltipDimensions(null);
|
|
234
|
-
setTooltipPosition(null);
|
|
235
207
|
}
|
|
236
208
|
}, [isOpen]);
|
|
237
|
-
useEffect(() => {
|
|
238
|
-
setIsOpen(false);
|
|
239
|
-
}, [windowDimensions]);
|
|
240
|
-
useEffect(() => {
|
|
241
|
-
if (tooltipPosition !== null && !(tooltipPosition !== null && tooltipPosition !== void 0 && tooltipPosition.isNormalized) || !isOpen || controlLayout === null || tooltipDimensions == null) {
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
const updatedPosition = getTooltipPosition(position, {
|
|
246
|
-
controlLayout,
|
|
247
|
-
tooltipDimensions,
|
|
248
|
-
windowDimensions,
|
|
249
|
-
arrowWidth,
|
|
250
|
-
arrowOffset
|
|
251
|
-
}); // avoid ending up in an infinite normalization loop
|
|
252
|
-
|
|
253
|
-
if (tooltipPosition !== null && tooltipPosition !== void 0 && tooltipPosition.isNormalized && updatedPosition.isNormalized) {
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
setTooltipPosition(updatedPosition);
|
|
258
|
-
}, [isOpen, position, tooltipDimensions, controlLayout, windowDimensions, arrowWidth, arrowOffset, tooltipPosition]);
|
|
259
209
|
const control = children !== undefined ? children : defaultControl;
|
|
260
210
|
const pressableStyles = control === defaultControl ? Platform.select({
|
|
261
211
|
web: {
|
|
@@ -271,67 +221,50 @@ const Tooltip = /*#__PURE__*/forwardRef((_ref6, ref) => {
|
|
|
271
221
|
return /*#__PURE__*/_jsxs(View, {
|
|
272
222
|
style: staticStyles.container,
|
|
273
223
|
...selectProps(rest),
|
|
224
|
+
ref: ref,
|
|
274
225
|
children: [/*#__PURE__*/_jsx(Pressable, {
|
|
275
226
|
onPress: toggleIsOpen,
|
|
276
|
-
ref:
|
|
227
|
+
ref: reference,
|
|
277
228
|
onBlur: close,
|
|
278
229
|
style: pressableStyles,
|
|
279
230
|
hitSlop: pressableHitSlop,
|
|
280
231
|
accessibilityLabel: getCopy('a11yText'),
|
|
281
232
|
accessibilityRole: "button",
|
|
282
233
|
children: typeof control === 'function' ? pressableState => control(getPressableState(pressableState), variant) : control
|
|
283
|
-
}), isOpen && /*#__PURE__*/
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
234
|
+
}), isOpen && /*#__PURE__*/_jsxs(View, {
|
|
235
|
+
ref: floating,
|
|
236
|
+
style: [selectTooltipShadowStyles(themeTokens), staticStyles.tooltip, // applied separately so that it doesn't cover the arrow
|
|
237
|
+
selectMobileTooltipPositionStyles({
|
|
238
|
+
position: strategy,
|
|
239
|
+
top: y !== null && y !== void 0 ? y : 0,
|
|
240
|
+
left: x !== null && x !== void 0 ? x : 0
|
|
241
|
+
})],
|
|
242
|
+
onLayout: onTooltipLayout,
|
|
243
|
+
accessibilityRole: "alert",
|
|
244
|
+
children: [/*#__PURE__*/_jsx(View, {
|
|
245
|
+
style: [staticStyles.arrow, selectMobileArrowStyles(themeTokens, { ...arrowData,
|
|
246
|
+
placement
|
|
247
|
+
})],
|
|
248
|
+
ref: arrowRef
|
|
249
|
+
}), /*#__PURE__*/_jsx(View, {
|
|
250
|
+
style: selectTooltipStyles(themeTokens),
|
|
251
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
252
|
+
style: selectTextStyles(themeTokens),
|
|
253
|
+
children: content
|
|
254
|
+
})
|
|
255
|
+
})]
|
|
302
256
|
})]
|
|
303
257
|
});
|
|
304
258
|
});
|
|
305
259
|
Tooltip.displayName = 'Tooltip';
|
|
306
260
|
Tooltip.propTypes = { ...selectedSystemPropTypes,
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* Used to render the control (i.e. tooltip trigger). If a render function is used it will receive the
|
|
310
|
-
* pressable state and tooltip variant as an argument.
|
|
311
|
-
*/
|
|
312
|
-
children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* The message. Can be raw text or text components.
|
|
316
|
-
*/
|
|
317
|
-
content: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* Select English or French copy for the accessible label.
|
|
321
|
-
*/
|
|
322
|
-
copy: PropTypes.oneOf(['en', 'fr']),
|
|
323
|
-
|
|
324
|
-
/**
|
|
325
|
-
* Use to place the tooltip in a specific location (only if it fits within viewport).
|
|
326
|
-
*/
|
|
327
|
-
position: PropTypes.oneOf(['auto', 'above', 'right', 'below', 'left']),
|
|
328
|
-
tokens: getTokensPropType('Tooltip'),
|
|
329
|
-
variant: variantProp.propType
|
|
261
|
+
...propTypes
|
|
330
262
|
};
|
|
331
263
|
export default Tooltip;
|
|
332
264
|
const staticStyles = StyleSheet.create({
|
|
333
265
|
container: {
|
|
334
|
-
alignItems: 'flex-start'
|
|
266
|
+
alignItems: 'flex-start',
|
|
267
|
+
position: 'absolute'
|
|
335
268
|
},
|
|
336
269
|
tooltip: {
|
|
337
270
|
position: 'absolute',
|
|
@@ -339,9 +272,6 @@ const staticStyles = StyleSheet.create({
|
|
|
339
272
|
top: 0,
|
|
340
273
|
left: 0
|
|
341
274
|
},
|
|
342
|
-
tooltipHidden: {
|
|
343
|
-
opacity: 0
|
|
344
|
-
},
|
|
345
275
|
arrow: {
|
|
346
276
|
position: 'absolute'
|
|
347
277
|
}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import React, { forwardRef, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import Dimensions from "react-native-web/dist/exports/Dimensions";
|
|
3
|
+
import Platform from "react-native-web/dist/exports/Platform";
|
|
4
|
+
import Pressable from "react-native-web/dist/exports/Pressable";
|
|
5
|
+
import StyleSheet from "react-native-web/dist/exports/StyleSheet";
|
|
6
|
+
import Text from "react-native-web/dist/exports/Text";
|
|
7
|
+
import View from "react-native-web/dist/exports/View";
|
|
8
|
+
import propTypes from './shared';
|
|
9
|
+
import { applyShadowToken, applyTextStyles, useThemeTokens } from '../ThemeProvider';
|
|
10
|
+
import { a11yProps, selectSystemProps, selectTokens, viewProps } from '../utils';
|
|
11
|
+
import Backdrop from './Backdrop';
|
|
12
|
+
import getTooltipPosition from './getTooltipPosition';
|
|
13
|
+
import TooltipButton from '../TooltipButton';
|
|
14
|
+
import useCopy from '../utils/useCopy';
|
|
15
|
+
import dictionary from './dictionary';
|
|
16
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
17
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
18
|
+
const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
|
|
19
|
+
|
|
20
|
+
const selectTooltipStyles = _ref => {
|
|
21
|
+
let {
|
|
22
|
+
backgroundColor,
|
|
23
|
+
paddingTop,
|
|
24
|
+
paddingBottom,
|
|
25
|
+
paddingLeft,
|
|
26
|
+
paddingRight,
|
|
27
|
+
borderRadius
|
|
28
|
+
} = _ref;
|
|
29
|
+
return {
|
|
30
|
+
backgroundColor,
|
|
31
|
+
paddingTop,
|
|
32
|
+
paddingBottom,
|
|
33
|
+
paddingLeft,
|
|
34
|
+
paddingRight,
|
|
35
|
+
borderRadius
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const selectTooltipShadowStyles = _ref2 => {
|
|
40
|
+
let {
|
|
41
|
+
shadow,
|
|
42
|
+
borderRadius
|
|
43
|
+
} = _ref2;
|
|
44
|
+
return {
|
|
45
|
+
borderRadius,
|
|
46
|
+
...applyShadowToken(shadow)
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const selectTooltipPositionStyles = _ref3 => {
|
|
51
|
+
let {
|
|
52
|
+
top,
|
|
53
|
+
left,
|
|
54
|
+
width
|
|
55
|
+
} = _ref3;
|
|
56
|
+
return {
|
|
57
|
+
top,
|
|
58
|
+
left,
|
|
59
|
+
width
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const selectArrowStyles = (_ref4, _ref5) => {
|
|
64
|
+
let {
|
|
65
|
+
backgroundColor,
|
|
66
|
+
arrowWidth,
|
|
67
|
+
arrowBorderRadius,
|
|
68
|
+
shadow
|
|
69
|
+
} = _ref4;
|
|
70
|
+
let {
|
|
71
|
+
position,
|
|
72
|
+
width: tooltipWidth,
|
|
73
|
+
height: tooltipHeight
|
|
74
|
+
} = _ref5;
|
|
75
|
+
// the arrow width is actually a diagonal of the rectangle that we'll use as a tip
|
|
76
|
+
const rectangleSide = Math.sqrt(arrowWidth * arrowWidth / 2); // position the arrow at the side and center of the tooltip - this happens before rotation
|
|
77
|
+
// so we use the rectangle size as basis
|
|
78
|
+
|
|
79
|
+
const verticalOffset = -1 * rectangleSide / 2;
|
|
80
|
+
const horizontalOffset = rectangleSide / 2; // percentage-based absolute positioning doesn't act well on native, so we have to
|
|
81
|
+
// calculate the pixel values
|
|
82
|
+
|
|
83
|
+
const directionalStyles = {
|
|
84
|
+
above: {
|
|
85
|
+
bottom: verticalOffset,
|
|
86
|
+
left: tooltipWidth / 2 - horizontalOffset,
|
|
87
|
+
transform: [{
|
|
88
|
+
rotateZ: '45deg'
|
|
89
|
+
}]
|
|
90
|
+
},
|
|
91
|
+
below: {
|
|
92
|
+
top: verticalOffset,
|
|
93
|
+
left: tooltipWidth / 2 - horizontalOffset,
|
|
94
|
+
transform: [{
|
|
95
|
+
rotateZ: '-135deg'
|
|
96
|
+
}]
|
|
97
|
+
},
|
|
98
|
+
left: {
|
|
99
|
+
right: verticalOffset,
|
|
100
|
+
top: tooltipHeight / 2 - horizontalOffset,
|
|
101
|
+
transform: [{
|
|
102
|
+
rotateZ: '-45deg'
|
|
103
|
+
}]
|
|
104
|
+
},
|
|
105
|
+
right: {
|
|
106
|
+
left: verticalOffset,
|
|
107
|
+
top: tooltipHeight / 2 - horizontalOffset,
|
|
108
|
+
transform: [{
|
|
109
|
+
rotateZ: '135deg'
|
|
110
|
+
}]
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
return {
|
|
114
|
+
backgroundColor,
|
|
115
|
+
width: rectangleSide,
|
|
116
|
+
height: rectangleSide,
|
|
117
|
+
borderBottomRightRadius: arrowBorderRadius,
|
|
118
|
+
// this corner will be the arrow tip after rotation
|
|
119
|
+
...applyShadowToken(shadow),
|
|
120
|
+
...directionalStyles[position]
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const selectTextStyles = tokens => applyTextStyles(selectTokens('Typography', tokens));
|
|
125
|
+
|
|
126
|
+
const defaultControl = (pressableState, variant) => /*#__PURE__*/_jsx(TooltipButton, {
|
|
127
|
+
pressableState: pressableState,
|
|
128
|
+
variant: variant
|
|
129
|
+
});
|
|
130
|
+
/**
|
|
131
|
+
* Tooltip provides a descriptive and detailed explanation or instructions. It can be used next to an input label
|
|
132
|
+
* to help a user fill it in, or as a standalone component.
|
|
133
|
+
*
|
|
134
|
+
* By default the TooltipButton component will be used as a control for triggering the tooltip, but you may attach
|
|
135
|
+
* a tooltip to any other component. A render function can be used to adjust the control's styling on state changes (hover, focus, etc.).
|
|
136
|
+
*
|
|
137
|
+
* ### Positioning
|
|
138
|
+
* By default a Tooltip will be automatically positioned in a way that ensures it fits within the viewport.
|
|
139
|
+
* You may suggest a position with a prop - it will be used, unless the tooltip would end up outside the viewport.
|
|
140
|
+
*
|
|
141
|
+
* ### Usage criteria
|
|
142
|
+
* - You may use one when the information is useful only to a small percentage of users (ie. tech savvy people wouldn't need this info).
|
|
143
|
+
* - Tooltips may also be useful when vertical space is an issue.
|
|
144
|
+
*/
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
const Tooltip = /*#__PURE__*/forwardRef((_ref6, ref) => {
|
|
148
|
+
let {
|
|
149
|
+
children,
|
|
150
|
+
content,
|
|
151
|
+
position = 'auto',
|
|
152
|
+
copy = 'en',
|
|
153
|
+
tokens,
|
|
154
|
+
variant,
|
|
155
|
+
...rest
|
|
156
|
+
} = _ref6;
|
|
157
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
158
|
+
const controlRef = useRef();
|
|
159
|
+
const [controlLayout, setControlLayout] = useState(null);
|
|
160
|
+
const [tooltipDimensions, setTooltipDimensions] = useState(null);
|
|
161
|
+
const [windowDimensions, setWindowDimensions] = useState(Dimensions.get('window'));
|
|
162
|
+
const [tooltipPosition, setTooltipPosition] = useState(null);
|
|
163
|
+
const getCopy = useCopy({
|
|
164
|
+
dictionary,
|
|
165
|
+
copy
|
|
166
|
+
});
|
|
167
|
+
const themeTokens = useThemeTokens('Tooltip', tokens, variant);
|
|
168
|
+
const {
|
|
169
|
+
arrowWidth,
|
|
170
|
+
arrowOffset
|
|
171
|
+
} = themeTokens;
|
|
172
|
+
useEffect(() => {
|
|
173
|
+
const subscription = Dimensions.addEventListener('change', _ref7 => {
|
|
174
|
+
let {
|
|
175
|
+
window
|
|
176
|
+
} = _ref7;
|
|
177
|
+
setWindowDimensions(window);
|
|
178
|
+
});
|
|
179
|
+
return () => subscription === null || subscription === void 0 ? void 0 : subscription.remove();
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const toggleIsOpen = () => setIsOpen(!isOpen);
|
|
183
|
+
|
|
184
|
+
const close = () => setIsOpen(false);
|
|
185
|
+
|
|
186
|
+
const getPressableState = _ref8 => {
|
|
187
|
+
let {
|
|
188
|
+
pressed,
|
|
189
|
+
hovered,
|
|
190
|
+
focused
|
|
191
|
+
} = _ref8;
|
|
192
|
+
return {
|
|
193
|
+
pressed,
|
|
194
|
+
hover: hovered,
|
|
195
|
+
focus: focused
|
|
196
|
+
};
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const onTooltipLayout = _ref9 => {
|
|
200
|
+
let {
|
|
201
|
+
nativeEvent: {
|
|
202
|
+
layout: {
|
|
203
|
+
width,
|
|
204
|
+
height
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
} = _ref9;
|
|
208
|
+
|
|
209
|
+
if (tooltipDimensions === null || tooltipDimensions.width !== width || tooltipDimensions.height !== height) {
|
|
210
|
+
setTooltipDimensions({
|
|
211
|
+
width: Platform.select({
|
|
212
|
+
web: width + 0.3,
|
|
213
|
+
// avoids often unnecessary line breaks due to subpixel rendering of fonts
|
|
214
|
+
native: width
|
|
215
|
+
}),
|
|
216
|
+
height
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
useEffect(() => {
|
|
222
|
+
if (isOpen) {
|
|
223
|
+
controlRef.current.measureInWindow((x, y, width, height) => {
|
|
224
|
+
setControlLayout({
|
|
225
|
+
x,
|
|
226
|
+
y,
|
|
227
|
+
width,
|
|
228
|
+
height
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
} else {
|
|
232
|
+
setControlLayout(null);
|
|
233
|
+
setTooltipDimensions(null);
|
|
234
|
+
setTooltipPosition(null);
|
|
235
|
+
}
|
|
236
|
+
}, [isOpen]);
|
|
237
|
+
useEffect(() => {
|
|
238
|
+
setIsOpen(false);
|
|
239
|
+
}, [windowDimensions]);
|
|
240
|
+
useEffect(() => {
|
|
241
|
+
if (tooltipPosition !== null && !(tooltipPosition !== null && tooltipPosition !== void 0 && tooltipPosition.isNormalized) || !isOpen || controlLayout === null || tooltipDimensions == null) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const updatedPosition = getTooltipPosition(position, {
|
|
246
|
+
controlLayout,
|
|
247
|
+
tooltipDimensions,
|
|
248
|
+
windowDimensions,
|
|
249
|
+
arrowWidth,
|
|
250
|
+
arrowOffset
|
|
251
|
+
}); // avoid ending up in an infinite normalization loop
|
|
252
|
+
|
|
253
|
+
if (tooltipPosition !== null && tooltipPosition !== void 0 && tooltipPosition.isNormalized && updatedPosition.isNormalized) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
setTooltipPosition(updatedPosition);
|
|
258
|
+
}, [isOpen, position, tooltipDimensions, controlLayout, windowDimensions, arrowWidth, arrowOffset, tooltipPosition]);
|
|
259
|
+
const control = children !== undefined ? children : defaultControl;
|
|
260
|
+
const pressableStyles = control === defaultControl ? Platform.select({
|
|
261
|
+
web: {
|
|
262
|
+
outline: 'none'
|
|
263
|
+
}
|
|
264
|
+
}) : undefined;
|
|
265
|
+
const pressableHitSlop = control === defaultControl ? {
|
|
266
|
+
top: 10,
|
|
267
|
+
bottom: 10,
|
|
268
|
+
left: 10,
|
|
269
|
+
right: 10
|
|
270
|
+
} : undefined;
|
|
271
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
272
|
+
style: staticStyles.container,
|
|
273
|
+
...selectProps(rest),
|
|
274
|
+
children: [/*#__PURE__*/_jsx(Pressable, {
|
|
275
|
+
onPress: toggleIsOpen,
|
|
276
|
+
ref: controlRef,
|
|
277
|
+
onBlur: close,
|
|
278
|
+
style: pressableStyles,
|
|
279
|
+
hitSlop: pressableHitSlop,
|
|
280
|
+
accessibilityLabel: getCopy('a11yText'),
|
|
281
|
+
accessibilityRole: "button",
|
|
282
|
+
children: typeof control === 'function' ? pressableState => control(getPressableState(pressableState), variant) : control
|
|
283
|
+
}), isOpen && /*#__PURE__*/_jsx(Backdrop, {
|
|
284
|
+
onPress: close,
|
|
285
|
+
children: /*#__PURE__*/_jsxs(View, {
|
|
286
|
+
ref: ref,
|
|
287
|
+
style: [staticStyles.tooltip, selectTooltipShadowStyles(themeTokens), // applied separately so that it doesn't cover the arrow
|
|
288
|
+
tooltipPosition && selectTooltipPositionStyles(tooltipPosition), (tooltipPosition === null || (tooltipPosition === null || tooltipPosition === void 0 ? void 0 : tooltipPosition.isNormalized)) && staticStyles.tooltipHidden // visually hide the tooltip until we have a final measurement
|
|
289
|
+
],
|
|
290
|
+
onLayout: onTooltipLayout,
|
|
291
|
+
accessibilityRole: "alert",
|
|
292
|
+
children: [/*#__PURE__*/_jsx(View, {
|
|
293
|
+
style: [staticStyles.arrow, tooltipPosition && selectArrowStyles(themeTokens, tooltipPosition)]
|
|
294
|
+
}), /*#__PURE__*/_jsx(View, {
|
|
295
|
+
style: selectTooltipStyles(themeTokens),
|
|
296
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
297
|
+
style: selectTextStyles(themeTokens),
|
|
298
|
+
children: content
|
|
299
|
+
})
|
|
300
|
+
})]
|
|
301
|
+
})
|
|
302
|
+
})]
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
Tooltip.displayName = 'NativeTooltip';
|
|
306
|
+
Tooltip.propTypes = { ...selectedSystemPropTypes,
|
|
307
|
+
...propTypes
|
|
308
|
+
};
|
|
309
|
+
export default Tooltip;
|
|
310
|
+
const staticStyles = StyleSheet.create({
|
|
311
|
+
container: {
|
|
312
|
+
alignItems: 'flex-start'
|
|
313
|
+
},
|
|
314
|
+
tooltip: {
|
|
315
|
+
position: 'absolute',
|
|
316
|
+
maxWidth: 240,
|
|
317
|
+
top: 0,
|
|
318
|
+
left: 0
|
|
319
|
+
},
|
|
320
|
+
tooltipHidden: {
|
|
321
|
+
opacity: 0
|
|
322
|
+
},
|
|
323
|
+
arrow: {
|
|
324
|
+
position: 'absolute'
|
|
325
|
+
}
|
|
326
|
+
});
|
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import Dimensions from "react-native-web/dist/exports/Dimensions";
|
|
2
|
+
import NonNativeTooltip from './Tooltip';
|
|
3
|
+
import NativeToolTip from './Tooltip.native';
|
|
4
|
+
const Tooltip = !Dimensions.get('screen').width <= 340 ? NativeToolTip : NonNativeTooltip;
|
|
2
5
|
export default Tooltip;
|