@telus-uds/components-base 3.12.2 → 3.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/CHANGELOG.md +37 -2
  2. package/lib/cjs/BaseProvider/index.js +4 -1
  3. package/lib/cjs/Button/ButtonDropdown.js +105 -12
  4. package/lib/cjs/Card/Card.js +23 -4
  5. package/lib/cjs/Card/CardBase.js +170 -19
  6. package/lib/cjs/Card/PressableCardBase.js +19 -5
  7. package/lib/cjs/Card/backgroundImageStylesMap.js +197 -0
  8. package/lib/cjs/ExpandCollapse/ExpandCollapse.js +3 -1
  9. package/lib/cjs/ExpandCollapseMini/ExpandCollapseMini.js +1 -1
  10. package/lib/cjs/ExpandCollapseMini/ExpandCollapseMiniControl.js +30 -6
  11. package/lib/cjs/FlexGrid/FlexGrid.js +71 -6
  12. package/lib/cjs/Icon/Icon.js +3 -1
  13. package/lib/cjs/InputLabel/InputLabel.js +1 -1
  14. package/lib/cjs/InputSupports/InputSupports.js +1 -1
  15. package/lib/cjs/Notification/Notification.js +27 -8
  16. package/lib/cjs/Tabs/Tabs.js +34 -2
  17. package/lib/cjs/Tabs/TabsDropdown.js +252 -0
  18. package/lib/cjs/Tabs/TabsItem.js +4 -2
  19. package/lib/cjs/Tabs/dictionary.js +14 -0
  20. package/lib/cjs/ViewportProvider/ViewportProvider.js +9 -3
  21. package/lib/cjs/utils/props/inputSupportsProps.js +1 -1
  22. package/lib/esm/BaseProvider/index.js +4 -1
  23. package/lib/esm/Button/ButtonDropdown.js +107 -14
  24. package/lib/esm/Card/Card.js +21 -4
  25. package/lib/esm/Card/CardBase.js +169 -19
  26. package/lib/esm/Card/PressableCardBase.js +19 -5
  27. package/lib/esm/Card/backgroundImageStylesMap.js +190 -0
  28. package/lib/esm/ExpandCollapse/ExpandCollapse.js +4 -2
  29. package/lib/esm/ExpandCollapseMini/ExpandCollapseMini.js +2 -2
  30. package/lib/esm/ExpandCollapseMini/ExpandCollapseMiniControl.js +30 -6
  31. package/lib/esm/FlexGrid/FlexGrid.js +72 -7
  32. package/lib/esm/Icon/Icon.js +3 -1
  33. package/lib/esm/InputLabel/InputLabel.js +1 -1
  34. package/lib/esm/InputSupports/InputSupports.js +1 -1
  35. package/lib/esm/Notification/Notification.js +27 -8
  36. package/lib/esm/Tabs/Tabs.js +35 -3
  37. package/lib/esm/Tabs/TabsDropdown.js +245 -0
  38. package/lib/esm/Tabs/TabsItem.js +4 -2
  39. package/lib/esm/Tabs/dictionary.js +8 -0
  40. package/lib/esm/ViewportProvider/ViewportProvider.js +9 -3
  41. package/lib/esm/utils/props/inputSupportsProps.js +1 -1
  42. package/lib/package.json +2 -2
  43. package/package.json +2 -2
  44. package/src/BaseProvider/index.jsx +4 -2
  45. package/src/Button/ButtonDropdown.jsx +109 -16
  46. package/src/Card/Card.jsx +27 -3
  47. package/src/Card/CardBase.jsx +165 -19
  48. package/src/Card/PressableCardBase.jsx +31 -4
  49. package/src/Card/backgroundImageStylesMap.js +41 -0
  50. package/src/ExpandCollapse/ExpandCollapse.jsx +5 -2
  51. package/src/ExpandCollapseMini/ExpandCollapseMini.jsx +2 -2
  52. package/src/ExpandCollapseMini/ExpandCollapseMiniControl.jsx +39 -9
  53. package/src/FlexGrid/FlexGrid.jsx +80 -7
  54. package/src/Icon/Icon.jsx +3 -1
  55. package/src/InputLabel/InputLabel.jsx +1 -1
  56. package/src/InputSupports/InputSupports.jsx +1 -1
  57. package/src/Notification/Notification.jsx +58 -9
  58. package/src/Tabs/Tabs.jsx +36 -2
  59. package/src/Tabs/TabsDropdown.jsx +265 -0
  60. package/src/Tabs/TabsItem.jsx +4 -2
  61. package/src/Tabs/dictionary.js +8 -0
  62. package/src/ViewportProvider/ViewportProvider.jsx +8 -3
  63. package/src/utils/props/inputSupportsProps.js +1 -1
@@ -3,15 +3,112 @@ import PropTypes from 'prop-types';
3
3
  import View from "react-native-web/dist/exports/View";
4
4
  import Platform from "react-native-web/dist/exports/Platform";
5
5
  import ImageBackground from "react-native-web/dist/exports/ImageBackground";
6
+ import Image from "react-native-web/dist/exports/Image";
6
7
  import StyleSheet from "react-native-web/dist/exports/StyleSheet";
7
8
  import { applyShadowToken } from '../ThemeProvider';
8
9
  import { getTokensPropType, responsiveProps, useResponsiveProp, formatImageSource } from '../utils';
9
10
  import { a11yProps, viewProps, selectSystemProps } from '../utils/props';
10
- import { jsx as _jsx } from "react/jsx-runtime";
11
+ import backgroundImageStylesMap from './backgroundImageStylesMap';
12
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
13
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps]);
14
+ const setBackgroundImage = _ref => {
15
+ let {
16
+ src,
17
+ alt,
18
+ backgroundImageResizeMode,
19
+ backgroundImagePosition,
20
+ backgroundImageAlign,
21
+ content,
22
+ cardStyle
23
+ } = _ref;
24
+ const borderRadius = cardStyle?.borderRadius || 0;
25
+ const borderWidth = cardStyle?.borderWidth || 0;
26
+ const adjustedBorderRadius = Math.max(0, borderRadius - borderWidth);
27
+
28
+ // For contain mode with position and align, use CSS background properties for web
29
+ if (backgroundImageResizeMode === 'contain' && backgroundImagePosition && backgroundImageAlign) {
30
+ const positionKey = `${backgroundImagePosition}-${backgroundImageAlign}`;
31
+ if (Platform.OS === 'web') {
32
+ // Create background position based on position and align
33
+ let backgroundPosition;
34
+ switch (positionKey) {
35
+ case 'top-start':
36
+ backgroundPosition = 'left top';
37
+ break;
38
+ case 'top-center':
39
+ backgroundPosition = 'center top';
40
+ break;
41
+ case 'top-end':
42
+ backgroundPosition = 'right top';
43
+ break;
44
+ case 'bottom-start':
45
+ backgroundPosition = 'left bottom';
46
+ break;
47
+ case 'bottom-center':
48
+ backgroundPosition = 'center bottom';
49
+ break;
50
+ case 'bottom-end':
51
+ backgroundPosition = 'right bottom';
52
+ break;
53
+ case 'left-center':
54
+ backgroundPosition = 'left center';
55
+ break;
56
+ case 'right-center':
57
+ backgroundPosition = 'right center';
58
+ break;
59
+ default:
60
+ backgroundPosition = 'center center';
61
+ }
62
+ const backgroundImageStyle = {
63
+ backgroundImage: `url(${src})`,
64
+ backgroundSize: 'contain',
65
+ backgroundRepeat: 'no-repeat',
66
+ backgroundPosition,
67
+ borderRadius: adjustedBorderRadius
68
+ };
69
+ return /*#__PURE__*/_jsx(View, {
70
+ style: [staticStyles.imageBackground, backgroundImageStyle],
71
+ role: "img",
72
+ "aria-label": alt,
73
+ children: content
74
+ });
75
+ }
76
+ // For React Native, apply positioning styles with full dimensions
77
+ const positionStyles = backgroundImageStylesMap[positionKey] || {};
78
+ return /*#__PURE__*/_jsxs(View, {
79
+ style: [staticStyles.containContainer, {
80
+ borderRadius: adjustedBorderRadius
81
+ }],
82
+ children: [/*#__PURE__*/_jsx(Image, {
83
+ source: src,
84
+ resizeMode: backgroundImageResizeMode,
85
+ style: [staticStyles.containImage, positionStyles],
86
+ accessible: true,
87
+ accessibilityLabel: alt,
88
+ accessibilityIgnoresInvertColors: true
89
+ }), /*#__PURE__*/_jsx(View, {
90
+ style: staticStyles.contentOverlay,
91
+ children: content
92
+ })]
93
+ });
94
+ }
95
+
96
+ // Use ImageBackground for all other resize modes and React Native
97
+ return /*#__PURE__*/_jsx(ImageBackground, {
98
+ source: src,
99
+ imageStyle: {
100
+ borderRadius: adjustedBorderRadius
101
+ },
102
+ resizeMode: backgroundImageResizeMode,
103
+ style: staticStyles.imageBackground,
104
+ accessible: true,
105
+ accessibilityLabel: alt,
106
+ children: content
107
+ });
108
+ };
12
109
 
13
110
  // Ensure explicit selection of tokens
14
- const selectStyles = _ref => {
111
+ export const selectStyles = _ref2 => {
15
112
  let {
16
113
  flex,
17
114
  backgroundColor,
@@ -28,7 +125,7 @@ const selectStyles = _ref => {
28
125
  gradient,
29
126
  maxHeight,
30
127
  overflowY
31
- } = _ref;
128
+ } = _ref2;
32
129
  return {
33
130
  flex,
34
131
  backgroundColor,
@@ -61,46 +158,97 @@ const selectStyles = _ref => {
61
158
  * A themeless base component for Card which components can apply theme tokens to. Not
62
159
  * intended to be used in apps or sites directly: build themed components on top of this.
63
160
  */
64
- const CardBase = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
161
+ const CardBase = /*#__PURE__*/React.forwardRef((_ref3, ref) => {
65
162
  let {
66
163
  children,
67
164
  tokens,
68
165
  dataSet,
69
166
  backgroundImage,
70
167
  ...rest
71
- } = _ref2;
168
+ } = _ref3;
72
169
  const cardStyle = selectStyles(typeof tokens === 'function' ? tokens() : tokens);
73
170
  const props = selectProps(rest);
171
+ let content = children;
74
172
  const {
75
173
  src = '',
76
174
  alt = '',
77
- resizeMode = ''
175
+ resizeMode = '',
176
+ position = '',
177
+ align = ''
78
178
  } = backgroundImage || {};
79
179
  const backgroundImageResizeMode = useResponsiveProp(resizeMode, 'cover');
180
+ const backgroundImagePosition = useResponsiveProp(position);
181
+ const backgroundImageAlign = useResponsiveProp(align);
80
182
  const imageSourceViewport = formatImageSource(useResponsiveProp(src));
183
+ if (backgroundImage && src) {
184
+ // When there's a background image, separate the padding from the container style
185
+ // so the image can fill the entire container without padding interference
186
+ const {
187
+ paddingTop,
188
+ paddingBottom,
189
+ paddingLeft,
190
+ paddingRight,
191
+ ...containerStyle
192
+ } = cardStyle;
193
+
194
+ // Only create padding wrapper if there's actually padding defined
195
+ const hasPadding = paddingTop || paddingBottom || paddingLeft || paddingRight;
196
+ const paddedContent = hasPadding ? /*#__PURE__*/_jsx(View, {
197
+ style: {
198
+ paddingTop,
199
+ paddingBottom,
200
+ paddingLeft,
201
+ paddingRight
202
+ },
203
+ children: children
204
+ }) : children;
205
+ content = setBackgroundImage({
206
+ src: imageSourceViewport,
207
+ alt,
208
+ backgroundImageResizeMode,
209
+ backgroundImagePosition,
210
+ backgroundImageAlign,
211
+ content: paddedContent,
212
+ cardStyle: containerStyle
213
+ });
214
+ return /*#__PURE__*/_jsx(View, {
215
+ style: containerStyle,
216
+ dataSet: dataSet,
217
+ ref: ref,
218
+ ...props,
219
+ children: content
220
+ });
221
+ }
81
222
  return /*#__PURE__*/_jsx(View, {
82
223
  style: cardStyle,
83
224
  dataSet: dataSet,
84
225
  ref: ref,
85
226
  ...props,
86
- children: src ? /*#__PURE__*/_jsx(ImageBackground, {
87
- source: imageSourceViewport,
88
- imageStyle: {
89
- borderRadius: cardStyle?.borderRadius - cardStyle?.borderWidth
90
- },
91
- resizeMode: backgroundImageResizeMode,
92
- style: styles.imageBackground,
93
- accessible: true,
94
- accessibilityLabel: alt,
95
- children: children
96
- }) : children
227
+ children: content
97
228
  });
98
229
  });
99
230
  CardBase.displayName = 'CardBase';
100
- const styles = StyleSheet.create({
231
+ const staticStyles = StyleSheet.create({
101
232
  imageBackground: {
102
233
  width: '100%',
103
234
  height: '100%'
235
+ },
236
+ contentOverlay: {
237
+ position: 'relative',
238
+ width: '100%',
239
+ height: '100%',
240
+ zIndex: 1
241
+ },
242
+ containContainer: {
243
+ width: '100%',
244
+ height: '100%',
245
+ overflow: 'hidden',
246
+ position: 'relative'
247
+ },
248
+ containImage: {
249
+ position: 'absolute',
250
+ width: '100%',
251
+ height: '100%'
104
252
  }
105
253
  });
106
254
  CardBase.propTypes = {
@@ -115,7 +263,9 @@ CardBase.propTypes = {
115
263
  // src is an object when used responsively to provide different image sources for different screen sizes
116
264
  src: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.object]).isRequired,
117
265
  alt: PropTypes.string,
118
- resizeMode: responsiveProps.getTypeOptionallyByViewport(PropTypes.oneOf(['cover', 'contain', 'stretch', 'repeat', 'center']))
266
+ resizeMode: responsiveProps.getTypeOptionallyByViewport(PropTypes.oneOf(['cover', 'contain', 'stretch', 'repeat', 'center'])),
267
+ position: responsiveProps.getTypeOptionallyByViewport(PropTypes.oneOf(['bottom', 'left', 'right', 'top'])),
268
+ align: responsiveProps.getTypeOptionallyByViewport(PropTypes.oneOf(['start', 'end', 'center', 'stretch']))
119
269
  })
120
270
  };
121
271
  export default CardBase;
@@ -31,6 +31,7 @@ const PressableCardBase = /*#__PURE__*/React.forwardRef((_ref, ref) => {
31
31
  href,
32
32
  hrefAttrs,
33
33
  dataSet,
34
+ backgroundImage,
34
35
  accessibilityRole = href ? 'link' : undefined,
35
36
  ...rawRest
36
37
  } = _ref;
@@ -125,10 +126,7 @@ const PressableCardBase = /*#__PURE__*/React.forwardRef((_ref, ref) => {
125
126
  setFocused(false);
126
127
  setPressed(false);
127
128
  },
128
- style: {
129
- ...staticStyles.container,
130
- textDecoration: 'none'
131
- },
129
+ style: staticStyles.linkContainer,
132
130
  ...(hrefAttrs || {}),
133
131
  children: /*#__PURE__*/_jsx(CardBase, {
134
132
  tokens: getCardTokens({
@@ -136,6 +134,7 @@ const PressableCardBase = /*#__PURE__*/React.forwardRef((_ref, ref) => {
136
134
  focused,
137
135
  hovered
138
136
  }),
137
+ backgroundImage: backgroundImage,
139
138
  children: typeof children === 'function' ? children(getCardState({
140
139
  pressed,
141
140
  focused,
@@ -159,6 +158,7 @@ const PressableCardBase = /*#__PURE__*/React.forwardRef((_ref, ref) => {
159
158
  }),
160
159
  children: pressableState => /*#__PURE__*/_jsx(CardBase, {
161
160
  tokens: getCardTokens(pressableState),
161
+ backgroundImage: backgroundImage,
162
162
  children: typeof children === 'function' ? children(getCardState(pressableState)) : children
163
163
  })
164
164
  });
@@ -167,6 +167,11 @@ const staticStyles = StyleSheet.create({
167
167
  container: {
168
168
  flex: 1,
169
169
  display: 'flex'
170
+ },
171
+ linkContainer: {
172
+ flex: 1,
173
+ display: 'flex',
174
+ textDecoration: 'none'
170
175
  }
171
176
  });
172
177
  PressableCardBase.displayName = 'PressableCardBase';
@@ -177,6 +182,15 @@ PressableCardBase.propTypes = {
177
182
  partial: true,
178
183
  allowFunction: true
179
184
  }),
180
- variant: variantProp.propType
185
+ variant: variantProp.propType,
186
+ backgroundImage: PropTypes.shape({
187
+ // The image src is either a URI string or a number (when a local image src is bundled in IOS or Android app)
188
+ // src is an object when used responsively to provide different image sources for different screen sizes
189
+ src: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.object]).isRequired,
190
+ alt: PropTypes.string,
191
+ resizeMode: PropTypes.oneOfType([PropTypes.oneOf(['cover', 'contain', 'stretch', 'repeat', 'center']), PropTypes.object]),
192
+ position: PropTypes.oneOfType([PropTypes.oneOf(['bottom', 'left', 'right', 'top']), PropTypes.object]),
193
+ align: PropTypes.oneOfType([PropTypes.oneOf(['start', 'end', 'center', 'stretch']), PropTypes.object])
194
+ })
181
195
  };
182
196
  export default withLinkRouter(PressableCardBase);
@@ -0,0 +1,190 @@
1
+ import Platform from "react-native-web/dist/exports/Platform";
2
+ const webStyles = {
3
+ 'top-start': {
4
+ top: 0,
5
+ left: 0
6
+ },
7
+ 'top-center': {
8
+ top: 0,
9
+ left: '50%',
10
+ transform: [{
11
+ translateX: '-50%'
12
+ }]
13
+ },
14
+ 'top-end': {
15
+ top: 0,
16
+ right: 0
17
+ },
18
+ 'right-start': {
19
+ top: 0,
20
+ right: 0
21
+ },
22
+ 'left-start': {
23
+ top: 0,
24
+ left: 0
25
+ },
26
+ 'left-center': {
27
+ top: '50%',
28
+ left: 0,
29
+ transform: [{
30
+ translateY: '-50%'
31
+ }]
32
+ },
33
+ 'right-center': {
34
+ top: '50%',
35
+ right: 0,
36
+ transform: [{
37
+ translateY: '-50%'
38
+ }]
39
+ },
40
+ 'bottom-start': {
41
+ bottom: 0,
42
+ left: 0
43
+ },
44
+ 'left-end': {
45
+ bottom: 0,
46
+ left: 0
47
+ },
48
+ 'bottom-center': {
49
+ bottom: 0,
50
+ left: '50%',
51
+ transform: [{
52
+ translateX: '-50%'
53
+ }]
54
+ },
55
+ 'bottom-end': {
56
+ bottom: 0,
57
+ right: 0
58
+ },
59
+ 'right-end': {
60
+ bottom: 0,
61
+ right: 0
62
+ },
63
+ 'top-stretch': {
64
+ top: 0,
65
+ left: 0,
66
+ right: 0,
67
+ width: '100%'
68
+ },
69
+ 'left-stretch': {
70
+ top: 0,
71
+ bottom: 0,
72
+ left: 0,
73
+ height: '100%'
74
+ },
75
+ 'right-stretch': {
76
+ top: 0,
77
+ bottom: 0,
78
+ right: 0,
79
+ height: '100%'
80
+ },
81
+ 'bottom-stretch': {
82
+ bottom: 0,
83
+ left: 0,
84
+ right: 0,
85
+ width: '100%'
86
+ }
87
+ };
88
+ const nativeStyles = {
89
+ 'top-start': {
90
+ top: 0,
91
+ left: 0,
92
+ width: 150,
93
+ height: 200
94
+ },
95
+ 'top-center': {
96
+ top: 0,
97
+ left: '50%',
98
+ marginLeft: -75,
99
+ width: 150,
100
+ height: 200
101
+ },
102
+ 'top-end': {
103
+ top: 0,
104
+ right: 0,
105
+ width: 150,
106
+ height: 200
107
+ },
108
+ 'right-start': {
109
+ top: 0,
110
+ right: 0,
111
+ width: 150,
112
+ height: 200
113
+ },
114
+ 'left-start': {
115
+ top: 0,
116
+ left: 0,
117
+ width: 150,
118
+ height: 200
119
+ },
120
+ 'left-center': {
121
+ left: 0,
122
+ top: '50%',
123
+ marginTop: -100,
124
+ width: 150,
125
+ height: 200
126
+ },
127
+ 'right-center': {
128
+ right: 0,
129
+ top: '50%',
130
+ marginTop: -100,
131
+ width: 150,
132
+ height: 200
133
+ },
134
+ 'bottom-start': {
135
+ bottom: 0,
136
+ left: 0,
137
+ width: 150,
138
+ height: 200
139
+ },
140
+ 'left-end': {
141
+ bottom: 0,
142
+ left: 0,
143
+ width: 150,
144
+ height: 200
145
+ },
146
+ 'bottom-center': {
147
+ bottom: 0,
148
+ left: '50%',
149
+ marginLeft: -75,
150
+ width: 150,
151
+ height: 200
152
+ },
153
+ 'bottom-end': {
154
+ bottom: 0,
155
+ right: 0,
156
+ width: 150,
157
+ height: 200
158
+ },
159
+ 'right-end': {
160
+ bottom: 0,
161
+ right: 0,
162
+ width: 150,
163
+ height: 200
164
+ },
165
+ 'top-stretch': {
166
+ top: 0,
167
+ left: 0,
168
+ right: 0,
169
+ width: '100%'
170
+ },
171
+ 'left-stretch': {
172
+ top: 0,
173
+ bottom: 0,
174
+ left: 0,
175
+ height: '100%'
176
+ },
177
+ 'right-stretch': {
178
+ top: 0,
179
+ bottom: 0,
180
+ right: 0,
181
+ height: '100%'
182
+ },
183
+ 'bottom-stretch': {
184
+ bottom: 0,
185
+ left: 0,
186
+ right: 0,
187
+ width: '100%'
188
+ }
189
+ };
190
+ export default Platform.OS === 'web' ? webStyles : nativeStyles;
@@ -3,7 +3,7 @@ import View from "react-native-web/dist/exports/View";
3
3
  import StyleSheet from "react-native-web/dist/exports/StyleSheet";
4
4
  import PropTypes from 'prop-types';
5
5
  import { useThemeTokens } from '../ThemeProvider';
6
- import { a11yProps, getTokensPropType, selectSystemProps, useMultipleInputValues, variantProp, viewProps, contentfulProps } from '../utils';
6
+ import { a11yProps, getTokensPropType, selectSystemProps, useMultipleInputValues, variantProp, viewProps, contentfulProps, useUniqueId } from '../utils';
7
7
  import { jsx as _jsx } from "react/jsx-runtime";
8
8
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps, contentfulProps]);
9
9
  function selectBorderStyles(tokens) {
@@ -33,6 +33,7 @@ const ExpandCollapse = /*#__PURE__*/React.forwardRef((_ref, ref) => {
33
33
  dataSet,
34
34
  ...rest
35
35
  } = _ref;
36
+ const instanceId = useUniqueId('ExpandCollapse');
36
37
  const {
37
38
  currentValues: openIds,
38
39
  toggleOneValue: onToggle,
@@ -58,7 +59,8 @@ const ExpandCollapse = /*#__PURE__*/React.forwardRef((_ref, ref) => {
58
59
  onToggle,
59
60
  resetValues,
60
61
  setValues,
61
- unsetValues
62
+ unsetValues,
63
+ instanceId
62
64
  }) : children
63
65
  })
64
66
  });
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import ExpandCollapse from '../ExpandCollapse';
4
- import { getTokensPropType, selectSystemProps, contentfulProps } from '../utils';
4
+ import { getTokensPropType, selectSystemProps, contentfulProps, useUniqueId } from '../utils';
5
5
  import { variantProp } from '../utils/props';
6
6
  import ExpandCollapseMiniControl from './ExpandCollapseMiniControl';
7
7
  import { jsx as _jsx } from "react/jsx-runtime";
@@ -16,7 +16,7 @@ const ExpandCollapseMini = /*#__PURE__*/React.forwardRef((_ref, ref) => {
16
16
  dataSet,
17
17
  ...rest
18
18
  } = _ref;
19
- const expandCollapeMiniPanelId = 'ExpandCollapseMiniPanel';
19
+ const expandCollapeMiniPanelId = useUniqueId('ExpandCollapseMiniPanel');
20
20
  const handleChange = (openPanels, event) => {
21
21
  if (typeof onToggle === 'function') {
22
22
  const isOpen = openPanels.length > 0;
@@ -45,7 +45,9 @@ const ExpandCollapseMiniControl = /*#__PURE__*/React.forwardRef((_ref, ref) => {
45
45
  pressed
46
46
  });
47
47
  const {
48
- size,
48
+ fontSize,
49
+ lineHeight,
50
+ iconSize,
49
51
  icon
50
52
  } = useThemeTokens('ExpandCollapseMiniControl', tokens, variant, {
51
53
  expanded,
@@ -62,18 +64,38 @@ const ExpandCollapseMiniControl = /*#__PURE__*/React.forwardRef((_ref, ref) => {
62
64
  hover: linkHover
63
65
  } = linkState || {};
64
66
  const isHovered = hover || linkHover;
67
+ const iconBaselineOffset = 0;
68
+ const hoverTranslateY = 4;
69
+
70
+ // Calculate baseline alignment to vertically center icon with text
71
+ // This combines font and icon metrics with adjustments for visual balance
72
+ const fontBaseline = fontSize / hoverTranslateY; // Quarter of font size - adjusts for text's visual center point
73
+ const iconBaseline = iconSize / hoverTranslateY; // Quarter of icon size - adjusts for icon's visual center point
74
+ const staticOffset = hoverTranslateY; // Fixed downward adjustment to fine-tune vertical alignment
75
+ const sizeCompensation = -Math.abs(iconSize - fontSize); // Compensates when icon and text sizes differ significantly
76
+
77
+ const baselineAlignment = fontBaseline + iconBaseline - staticOffset + sizeCompensation;
65
78
  if (Platform.OS !== 'web') {
79
+ // For native platforms, use baseline alignment with optional offset
66
80
  return {
67
- iconTranslateY: -1
81
+ iconTranslateY: baselineAlignment + iconBaselineOffset
68
82
  };
69
83
  }
70
84
  if (isHovered) {
71
- // Include vertical icon animation on hover alongside built-in Link theme, the size is size4
85
+ // Apply animation offset to the baseline-aligned position
86
+ // When expanded: move icon UP (1.3 the hover distance for clear movement)
87
+ // When collapsed: move icon DOWN (single hover distance)
88
+ const hoverMovementDistance = 1.3;
89
+ const animationOffset = expanded ? -(hoverTranslateY * hoverMovementDistance) : hoverTranslateY;
72
90
  return {
73
- iconTranslateY: (expanded ? -1 : 1) * size
91
+ iconTranslateY: baselineAlignment + iconBaselineOffset + animationOffset
74
92
  };
75
93
  }
76
- return {};
94
+
95
+ // Default state uses baseline alignment with optional offset
96
+ return {
97
+ iconTranslateY: baselineAlignment + iconBaselineOffset
98
+ };
77
99
  };
78
100
  return /*#__PURE__*/_jsx(Link, {
79
101
  variant: appearance,
@@ -81,7 +103,9 @@ const ExpandCollapseMiniControl = /*#__PURE__*/React.forwardRef((_ref, ref) => {
81
103
  iconPosition: iconPosition,
82
104
  tokens: linkState => ({
83
105
  ...linkTokens,
84
- ...getTokens(linkState)
106
+ ...getTokens(linkState),
107
+ iconSize,
108
+ blockLineHeight: lineHeight
85
109
  }),
86
110
  ref: ref,
87
111
  ...presentationOnly,