@helpwave/hightide-native 0.0.7 → 0.1.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 (125) hide show
  1. package/README.md +4 -4
  2. package/package.json +8 -19
  3. package/src/components/card/Card.tsx +41 -0
  4. package/src/components/card/index.ts +1 -0
  5. package/src/components/chat/ChatAttachmentMessageBubble.tsx +246 -0
  6. package/src/components/chat/ChatConversationRow.tsx +21 -16
  7. package/src/components/chat/ChatDateDivider.tsx +2 -2
  8. package/src/components/chat/ChatMessageBubble.tsx +62 -52
  9. package/src/components/chat/ChatMessageComposer.tsx +16 -14
  10. package/src/components/chat/ChatQuickReplyChip.tsx +41 -43
  11. package/src/components/chat/ChatSystemLine.tsx +14 -7
  12. package/src/components/chat/ChatThreadHeader.tsx +60 -6
  13. package/src/components/chat/index.ts +1 -2
  14. package/src/components/index.ts +3 -1
  15. package/src/components/layout/Divider.tsx +60 -0
  16. package/src/components/layout/index.ts +1 -0
  17. package/src/components/list/ListActionItem.tsx +99 -0
  18. package/src/components/list/ListItem.tsx +96 -0
  19. package/src/components/list/ListNavigationItem.tsx +102 -0
  20. package/src/components/list/index.ts +3 -0
  21. package/src/components/user-interaction/Button.tsx +80 -31
  22. package/src/components/user-interaction/Checkbox.tsx +72 -32
  23. package/src/components/user-interaction/IconButton.tsx +68 -30
  24. package/src/components/user-interaction/Input.tsx +61 -14
  25. package/src/components/user-interaction/MultiSelect.tsx +142 -75
  26. package/src/components/user-interaction/SearchBar.tsx +222 -0
  27. package/src/components/user-interaction/Select.tsx +129 -58
  28. package/src/components/user-interaction/Switch.tsx +207 -0
  29. package/src/components/user-interaction/ThemedPressable.tsx +136 -0
  30. package/src/components/user-interaction/index.ts +3 -0
  31. package/src/components/visualization-and-display/Avatar.tsx +112 -93
  32. package/src/components/visualization-and-display/Chip.tsx +22 -22
  33. package/src/components/visualization-and-display/ThemedIcon.tsx +72 -0
  34. package/src/components/visualization-and-display/ThemedText.tsx +38 -0
  35. package/src/components/visualization-and-display/index.ts +2 -1
  36. package/src/global-contexts/HightideProvider.tsx +19 -10
  37. package/src/global-contexts/content-theme/ContentThemeContext.ts +22 -0
  38. package/src/global-contexts/content-theme/ContentThemeProvider.tsx +32 -0
  39. package/src/global-contexts/content-theme/index.ts +2 -0
  40. package/src/global-contexts/debug/forward-exports.ts +10 -0
  41. package/src/global-contexts/debug/index.ts +1 -0
  42. package/src/global-contexts/index.ts +2 -0
  43. package/src/global-contexts/theme/ThemeProvider.tsx +7 -1
  44. package/src/icons/HightideIconRegistry.ts +33 -0
  45. package/src/icons/index.ts +2 -0
  46. package/src/icons/types.ts +11 -0
  47. package/src/theme/adapters/container-adapter.ts +323 -0
  48. package/src/theme/adapters/defined.ts +11 -0
  49. package/src/theme/adapters/icon-style-adapter.ts +10 -0
  50. package/src/theme/adapters/index.ts +4 -0
  51. package/src/theme/adapters/text-style-adapter.ts +16 -0
  52. package/src/theme/index.ts +1 -0
  53. package/src/theme/resolvers/avatar.ts +385 -186
  54. package/src/theme/resolvers/button.ts +50 -82
  55. package/src/theme/resolvers/card.ts +27 -0
  56. package/src/theme/resolvers/chat/attachment-message-bubble.ts +163 -0
  57. package/src/theme/resolvers/chat/chat-theme.ts +69 -0
  58. package/src/theme/resolvers/chat/conversation-list.ts +35 -0
  59. package/src/theme/resolvers/chat/conversation-row.ts +64 -0
  60. package/src/theme/resolvers/chat/date-divider.ts +31 -0
  61. package/src/theme/resolvers/chat/index.ts +11 -0
  62. package/src/theme/resolvers/chat/message-bubble.ts +57 -0
  63. package/src/theme/resolvers/chat/message-composer.ts +38 -0
  64. package/src/theme/resolvers/chat/message-list.ts +27 -0
  65. package/src/theme/resolvers/chat/quick-reply-chip.ts +80 -0
  66. package/src/theme/resolvers/chat/system-line.ts +41 -0
  67. package/src/theme/resolvers/chat/thread-header.ts +85 -0
  68. package/src/theme/resolvers/checkbox.ts +87 -74
  69. package/src/theme/resolvers/chip.ts +30 -67
  70. package/src/theme/resolvers/colorScheme.ts +136 -0
  71. package/src/theme/resolvers/divider.ts +34 -0
  72. package/src/theme/resolvers/icon.ts +21 -0
  73. package/src/theme/resolvers/iconButton.ts +45 -61
  74. package/src/theme/resolvers/index.ts +8 -2
  75. package/src/theme/resolvers/input.ts +64 -42
  76. package/src/theme/resolvers/listItem.ts +115 -0
  77. package/src/theme/resolvers/multiSelect.ts +159 -84
  78. package/src/theme/resolvers/searchBar.ts +121 -0
  79. package/src/theme/resolvers/select.ts +141 -66
  80. package/src/theme/resolvers/switch.ts +71 -0
  81. package/src/theme/resolvers/themedPressable.ts +54 -0
  82. package/src/theme/themes/createHightideTheme.ts +214 -49
  83. package/src/theme/themes/hightideThemes.ts +10 -6
  84. package/src/theme/types/color.ts +2 -59
  85. package/src/theme/types/components/avatar.ts +37 -36
  86. package/src/theme/types/components/button.ts +13 -14
  87. package/src/theme/types/components/card.ts +9 -0
  88. package/src/theme/types/components/chat.ts +124 -103
  89. package/src/theme/types/components/checkbox.ts +11 -12
  90. package/src/theme/types/components/chip.ts +8 -14
  91. package/src/theme/types/components/divider.ts +18 -0
  92. package/src/theme/types/components/hightide.ts +41 -40
  93. package/src/theme/types/components/iconButton.ts +19 -14
  94. package/src/theme/types/components/index.ts +6 -1
  95. package/src/theme/types/components/input.ts +17 -8
  96. package/src/theme/types/components/listItem.ts +86 -0
  97. package/src/theme/types/components/multiSelect.ts +14 -12
  98. package/src/theme/types/components/searchBar.ts +28 -0
  99. package/src/theme/types/components/select.ts +21 -9
  100. package/src/theme/types/components/switch.ts +22 -0
  101. package/src/theme/types/components/themedPressable.ts +31 -0
  102. package/src/theme/types/decoration.ts +1 -11
  103. package/src/theme/types/icongraphy.ts +8 -0
  104. package/src/theme/types/index.ts +2 -0
  105. package/src/theme/types/layout.ts +25 -57
  106. package/src/theme/types/resolver.ts +54 -1
  107. package/src/theme/types/semantics.ts +72 -0
  108. package/src/theme/types/theme.ts +26 -13
  109. package/src/theme/types/typography.ts +16 -17
  110. package/src/utils/hitBoxOverlay.ts +44 -0
  111. package/src/utils/index.ts +2 -0
  112. package/src/utils/minimumTouchTargetHitSlop.ts +68 -0
  113. package/src/components/chat/ChatAttachmentCard.tsx +0 -110
  114. package/src/components/chat/ChatMessageCard.tsx +0 -118
  115. package/src/components/menu/Menu.tsx +0 -61
  116. package/src/components/menu/MenuActionItem.tsx +0 -90
  117. package/src/components/menu/MenuItem.tsx +0 -73
  118. package/src/components/menu/MenuNavigationItem.tsx +0 -90
  119. package/src/components/menu/index.ts +0 -4
  120. package/src/components/visualization-and-display/Icon.tsx +0 -27
  121. package/src/storybook/helper.tsx +0 -36
  122. package/src/theme/resolvers/chat.ts +0 -418
  123. package/src/theme/resolvers/coloring.ts +0 -94
  124. package/src/theme/resolvers/menu.ts +0 -120
  125. package/src/theme/types/components/menu.ts +0 -57
@@ -1,231 +1,430 @@
1
- import type {
2
- ImageStyle,
3
- TextStyle,
4
- ViewStyle
5
- } from 'react-native'
6
-
7
1
  import {
8
- componentLayouts,
9
- fontWeights
10
- } from '@helpwave/hightide-design/tokens'
11
- import type {
12
- ElementSize,
13
- HightideThemeTokens as DesignTokensTheme,
14
- TypographyTokens
15
- } from '@helpwave/hightide-design/types'
2
+ avatarGroupMaxShown,
3
+ avatarGroupOverlap,
4
+ type AvatarOverrideTokens,
5
+ type AvatarSize,
6
+ type AvatarTokens,
7
+ type AvatarWithStatusOverrideTokens,
8
+ type AvatarWithStatusTokens
9
+ } from '@helpwave/hightide-design/component-token-resolvers'
16
10
 
17
- import { resolveColoringStyles } from './coloring'
18
- import type {
19
- ColorPalette,
20
- HightideSemanticColors
21
- } from '../types/color'
11
+ import { toContainerStyle } from '../adapters/container-adapter'
12
+ import { toIconStyle } from '../adapters/icon-style-adapter'
13
+ import { toTextStyle } from '../adapters/text-style-adapter'
22
14
  import type {
15
+ AvatarGroupContainerStyle,
16
+ AvatarGroupStackStyle,
23
17
  AvatarGroupState,
18
+ AvatarGroupTextStyle,
19
+ AvatarGroupThemeResolvers,
24
20
  AvatarIconStyle,
21
+ AvatarImageStyle,
25
22
  AvatarState,
26
- AvatarStatus,
27
- AvatarTheme,
28
- AvatarWithLabelState,
29
- AvatarWithStatusState
23
+ AvatarStatusDotStyle,
24
+ AvatarStyle,
25
+ AvatarTextStyle,
26
+ AvatarThemeResolvers,
27
+ AvatarWithStatusState,
28
+ AvatarWithStatusThemeResolvers
30
29
  } from '../types/components/avatar'
31
- import type { HightideComponentThemes } from '../types/components/hightide'
32
- import { createStyleResolver } from '../types/resolver'
33
-
34
- const avatarFontWeights: Record<ElementSize, string> = {
35
- xs: fontWeights.semibold,
36
- sm: fontWeights.semibold,
37
- md: fontWeights.semibold,
38
- lg: fontWeights.bold,
39
- }
30
+ import {
31
+ createStyleResolver,
32
+ createValueResolver,
33
+ type ComponentThemeResolver
34
+ } from '../types/resolver'
35
+ import type { ThemeTokens } from '@helpwave/hightide-design/theme-tokens'
40
36
 
41
- const statusColor = (
42
- status: AvatarStatus,
43
- semantic: HightideSemanticColors,
44
- gray: ColorPalette
45
- ): string => {
46
- switch (status) {
47
- case 'online':
48
- return semantic.positive
49
- case 'busy':
50
- return semantic.negative
51
- case 'away':
52
- return semantic.warning
53
- case 'offline':
54
- case 'unknown':
55
- default:
56
- return gray[500]
37
+ export const toDesignAvatarSize = (size?: AvatarSize | number): AvatarSize => (
38
+ typeof size === 'number' ? 'md' : (size ?? 'md')
39
+ )
40
+
41
+ export const mergeAvatarTokens = (
42
+ base: AvatarTokens,
43
+ override?: AvatarOverrideTokens
44
+ ): AvatarTokens => ({
45
+ container: {
46
+ ...base.container,
47
+ ...override?.container,
48
+ size: {
49
+ ...base.container.size,
50
+ ...override?.container?.size,
51
+ },
52
+ shape: {
53
+ ...base.container.shape,
54
+ ...override?.container?.shape,
55
+ },
56
+ layout: {
57
+ ...base.container.layout,
58
+ ...override?.container?.layout,
59
+ },
60
+ decoration: {
61
+ ...base.container.decoration,
62
+ ...override?.container?.decoration,
63
+ },
64
+ },
65
+ text: {
66
+ ...base.text,
67
+ ...override?.text,
68
+ },
69
+ icon: {
70
+ ...base.icon,
71
+ ...override?.icon,
72
+ },
73
+ })
74
+
75
+ export const withNumericAvatarSize = (
76
+ tokens: AvatarTokens,
77
+ size: number
78
+ ): AvatarTokens => {
79
+ const borderRadius = size / 2
80
+
81
+ return {
82
+ ...tokens,
83
+ container: {
84
+ ...tokens.container,
85
+ size: {
86
+ width: size,
87
+ height: size,
88
+ minWidth: size,
89
+ minHeight: size,
90
+ maxWidth: size,
91
+ maxHeight: size,
92
+ },
93
+ shape: {
94
+ ...tokens.container.shape,
95
+ borderRadius: { type: 'all', value: borderRadius },
96
+ },
97
+ },
98
+ icon: {
99
+ ...tokens.icon,
100
+ size,
101
+ },
57
102
  }
58
103
  }
59
104
 
60
- export type CreateAvatarThemeOptions = {
61
- semantic: HightideSemanticColors,
62
- coloring: HightideComponentThemes['coloring'],
63
- gray: ColorPalette,
64
- typography: TypographyTokens,
65
- }
105
+ export const createAvatarStyleResolvers = (
106
+ resolveTokens: (state: AvatarState) => AvatarTokens,
107
+ themeTokens: ThemeTokens
108
+ ): AvatarThemeResolvers => ({
109
+ container: createStyleResolver((state: AvatarState): AvatarStyle => {
110
+ const { container } = resolveTokens(state)
111
+ const width = typeof container.size?.width === 'number'
112
+ ? container.size.width
113
+ : 0
114
+ const groupIndex = state.groupIndex ?? 0
66
115
 
67
- export const createAvatarTheme = ({
68
- semantic,
69
- coloring,
70
- gray,
71
- typography,
72
- }: CreateAvatarThemeOptions): AvatarTheme => {
73
- const resolveAvatar = (state: AvatarState) => {
74
- const size = state.size ?? 'md'
75
- const layout = componentLayouts.avatar[size]
76
- const groupLayout = componentLayouts.avatarGroup
77
- const resolved = resolveColoringStyles(coloring.primary, 'solid', semantic)
78
- const borderRadius = layout.size / 2
79
-
80
- const avatar: ViewStyle = {
116
+ return {
117
+ ...toContainerStyle(container),
81
118
  position: state.isGrouped ? 'absolute' : 'relative',
82
- flexDirection: 'row',
83
- alignItems: 'center',
84
- justifyContent: 'center',
85
- width: layout.size,
86
- height: layout.size,
87
- padding: layout.padding,
88
- borderRadius,
89
- backgroundColor: resolved.backgroundColor,
90
119
  overflow: 'hidden',
91
120
  ...(state.isGrouped ? {
92
- left: (state.groupIndex ?? 0) * layout.size * groupLayout.overlap,
93
- zIndex: groupLayout.maxShown - (state.groupIndex ?? 0),
94
- shadowColor: '#000000',
95
- shadowOffset: { width: 4, height: 0 },
96
- shadowOpacity: 0.2,
97
- shadowRadius: 4,
98
- elevation: 4,
121
+ left: groupIndex * width * avatarGroupOverlap,
122
+ zIndex: avatarGroupMaxShown - groupIndex,
99
123
  } : {}),
100
124
  }
125
+ }),
126
+ image: createStyleResolver((state: AvatarState): AvatarImageStyle => {
127
+ const { container } = resolveTokens(state)
128
+ const width = container.size?.width
129
+ const height = container.size?.height
130
+ const borderRadius = container.shape?.borderRadius
101
131
 
102
- const image: ImageStyle = {
132
+ return {
103
133
  position: 'absolute',
104
134
  left: 0,
105
135
  top: 0,
106
- width: layout.size,
107
- height: layout.size,
108
- borderRadius,
136
+ width,
137
+ height,
138
+ borderRadius: borderRadius?.type === 'all'
139
+ ? borderRadius.value
140
+ : undefined,
109
141
  }
142
+ }),
143
+ text: createStyleResolver((state: AvatarState): AvatarTextStyle => ({
144
+ ...toTextStyle(resolveTokens(state).text),
145
+ textAlign: 'center',
146
+ })),
147
+ icon: createValueResolver((state: AvatarState): AvatarIconStyle => {
148
+ const { icon } = resolveTokens(state)
110
149
 
111
- const text: TextStyle = {
112
- color: resolved.color,
113
- fontSize: layout.fontSize,
114
- fontWeight: avatarFontWeights[size] as TextStyle['fontWeight'],
115
- textAlign: 'center',
116
- }
150
+ return toIconStyle({
151
+ size: icon.size ?? themeTokens.icongraphy.sizes.md,
152
+ strokeWidth: icon.strokeWidth ?? themeTokens.icongraphy.strokeWidth,
153
+ color: icon.color ?? themeTokens.color.primary.onColor,
154
+ })
155
+ }),
156
+ })
157
+
158
+ export const toAvatarThemeResolvers: ComponentThemeResolver<AvatarThemeResolvers> = ({
159
+ themeTokens,
160
+ semanticTokens,
161
+ componentTokens,
162
+ }) => {
163
+ const resolveTokens = (state: AvatarState): AvatarTokens => {
164
+ const tokens = componentTokens.avatar({
165
+ themeTokens,
166
+ semanticResolvers: semanticTokens,
167
+ config: {
168
+ isGrouped: state.isGrouped,
169
+ groupIndex: state.groupIndex,
170
+ },
171
+ overrides: {
172
+ color: state.color,
173
+ size: toDesignAvatarSize(state.size),
174
+ },
175
+ })
117
176
 
118
- const icon: AvatarIconStyle = {
119
- size: componentLayouts.icon[size].size,
120
- strokeWidth: componentLayouts.icon[size].strokeWidth,
121
- color: resolved.color,
177
+ if (typeof state.size === 'number') {
178
+ return withNumericAvatarSize(tokens, state.size)
122
179
  }
123
180
 
124
- return { avatar, image, text, icon }
181
+ return tokens
125
182
  }
126
183
 
127
- const resolveWithStatus = (state: AvatarWithStatusState) => {
128
- const size = state.size ?? 'md'
129
- const status = state.status ?? 'unknown'
130
- const layout = componentLayouts.avatar[size]
131
-
132
- const container: ViewStyle = {
133
- position: 'relative',
134
- alignSelf: 'flex-start',
135
- }
136
-
137
- const statusDot: ViewStyle = {
138
- position: 'absolute',
139
- right: 0,
140
- bottom: 0,
141
- zIndex: 1,
142
- width: layout.statusDotSize,
143
- height: layout.statusDotSize,
144
- borderRadius: layout.statusDotSize / 2,
145
- borderWidth: layout.statusDotBorderWidth,
146
- borderColor: semantic.background,
147
- backgroundColor: statusColor(status, semantic, gray),
148
- }
184
+ return createAvatarStyleResolvers(resolveTokens, themeTokens)
185
+ }
149
186
 
150
- return { container, statusDot }
187
+ export const createAvatarWithStatusThemeResolvers = (
188
+ resolve: (state: AvatarWithStatusState) => AvatarWithStatusTokens,
189
+ context: {
190
+ themeTokens: Parameters<ComponentThemeResolver<AvatarWithStatusThemeResolvers>>[0]['themeTokens'],
191
+ semanticTokens: Parameters<ComponentThemeResolver<AvatarWithStatusThemeResolvers>>[0]['semanticTokens'],
192
+ componentTokens: Parameters<ComponentThemeResolver<AvatarWithStatusThemeResolvers>>[0]['componentTokens'],
151
193
  }
194
+ ): AvatarWithStatusThemeResolvers => {
195
+ const { themeTokens, semanticTokens, componentTokens } = context
152
196
 
153
- const resolveWithLabel = (_state: AvatarWithLabelState) => {
154
- const bodyMedium = typography.scales.body.medium
197
+ return {
198
+ avatar: createValueResolver((state: AvatarWithStatusState) => {
199
+ const { avatarOverride } = resolve(state)
155
200
 
156
- const container: ViewStyle = {
157
- flexDirection: 'row',
158
- alignItems: 'center',
159
- gap: componentLayouts.avatarGroup.gap,
160
- }
201
+ const resolveTokens = (avatarState: AvatarState): AvatarTokens => {
202
+ const tokens = mergeAvatarTokens(
203
+ componentTokens.avatar({
204
+ themeTokens,
205
+ semanticResolvers: semanticTokens,
206
+ config: {
207
+ isGrouped: avatarState.isGrouped,
208
+ groupIndex: avatarState.groupIndex,
209
+ },
210
+ overrides: {
211
+ color: avatarState.color ?? avatarOverride.overrides?.color,
212
+ size: toDesignAvatarSize(avatarState.size ?? avatarOverride.overrides?.size),
213
+ },
214
+ }),
215
+ avatarOverride
216
+ )
161
217
 
162
- const text: TextStyle = {
163
- fontSize: bodyMedium.fontSize,
164
- fontWeight: bodyMedium.fontWeight as TextStyle['fontWeight'],
165
- lineHeight: bodyMedium.lineHeight,
166
- color: semantic.onBackground,
167
- flexShrink: 1,
168
- }
218
+ const size = avatarState.size ?? state.size
219
+ if (typeof size === 'number') {
220
+ return withNumericAvatarSize(tokens, size)
221
+ }
169
222
 
170
- return { container, text }
171
- }
223
+ return tokens
224
+ }
172
225
 
173
- const resolveGroup = (state: AvatarGroupState) => {
174
- const size = state.size ?? 'md'
175
- const layout = componentLayouts.avatar[size]
176
- const groupLayout = componentLayouts.avatarGroup
177
- const visibleCount = Math.min(state.count ?? groupLayout.maxShown, groupLayout.maxShown)
178
- const stackWidth = layout.size * (groupLayout.overlap * Math.max(visibleCount - 1, 0) + 1)
179
-
180
- const container: ViewStyle = {
181
- flexDirection: 'row',
182
- alignItems: 'center',
183
- gap: groupLayout.gap,
184
- height: layout.size,
185
- alignSelf: 'flex-start',
186
- }
226
+ return createAvatarStyleResolvers(resolveTokens, themeTokens)
227
+ }),
228
+ statusDot: createStyleResolver((state: AvatarWithStatusState): AvatarStatusDotStyle => {
229
+ const tokens = resolve(state)
230
+ const statusDot = tokens.statusDot
231
+ const size = typeof state.size === 'number'
232
+ ? Math.round(state.size / 10 * 4)
233
+ : undefined
187
234
 
188
- const stack: ViewStyle = {
189
- position: 'relative',
190
- width: stackWidth,
191
- height: layout.size,
192
- }
193
-
194
- const more: TextStyle = {
195
- fontSize: (layout.size * 2) / 3,
196
- color: semantic.onBackground,
197
- flexShrink: 1,
198
- }
199
-
200
- return { container, stack, more }
235
+ return {
236
+ ...toContainerStyle(
237
+ size === undefined
238
+ ? statusDot
239
+ : {
240
+ ...statusDot,
241
+ size: {
242
+ width: size,
243
+ height: size,
244
+ },
245
+ shape: {
246
+ ...statusDot.shape,
247
+ borderRadius: { type: 'all', value: size / 2 },
248
+ },
249
+ }
250
+ ),
251
+ position: 'absolute',
252
+ right: 0,
253
+ bottom: 0,
254
+ zIndex: 1,
255
+ }
256
+ }),
201
257
  }
258
+ }
202
259
 
203
- return {
204
- avatar: createStyleResolver((state) => resolveAvatar(state).avatar),
205
- image: createStyleResolver((state) => resolveAvatar(state).image),
206
- text: createStyleResolver((state) => resolveAvatar(state).text),
207
- icon: createStyleResolver((state) => resolveAvatar(state).icon),
208
- withStatus: {
209
- container: createStyleResolver((state) => resolveWithStatus(state).container),
210
- statusDot: createStyleResolver((state) => resolveWithStatus(state).statusDot),
260
+ export const mergeAvatarWithStatusTokens = (
261
+ base: AvatarWithStatusTokens,
262
+ override?: AvatarWithStatusOverrideTokens
263
+ ): AvatarWithStatusTokens => ({
264
+ avatarOverride: {
265
+ ...base.avatarOverride,
266
+ ...override?.avatarOverride,
267
+ overrides: {
268
+ ...base.avatarOverride.overrides,
269
+ ...override?.overrides,
270
+ ...override?.avatarOverride?.overrides,
211
271
  },
212
- withLabel: {
213
- container: createStyleResolver((state) => resolveWithLabel(state).container),
214
- text: createStyleResolver((state) => resolveWithLabel(state).text),
272
+ },
273
+ statusDot: {
274
+ ...base.statusDot,
275
+ ...override?.statusDot,
276
+ size: {
277
+ ...base.statusDot.size,
278
+ ...override?.statusDot?.size,
215
279
  },
216
- group: {
217
- container: createStyleResolver((state) => resolveGroup(state).container),
218
- stack: createStyleResolver((state) => resolveGroup(state).stack),
219
- more: createStyleResolver((state) => resolveGroup(state).more),
280
+ shape: {
281
+ ...base.statusDot.shape,
282
+ ...override?.statusDot?.shape,
220
283
  },
221
- }
284
+ border: {
285
+ ...base.statusDot.border,
286
+ ...override?.statusDot?.border,
287
+ },
288
+ },
289
+ })
290
+
291
+ export const toAvatarWithStatusThemeResolvers: ComponentThemeResolver<
292
+ AvatarWithStatusThemeResolvers
293
+ > = ({
294
+ themeTokens,
295
+ semanticTokens,
296
+ componentTokens,
297
+ }) => {
298
+ const resolve = (state: AvatarWithStatusState = {}) => componentTokens.avatarWithStatus({
299
+ themeTokens,
300
+ semanticResolvers: semanticTokens,
301
+ overrides: {
302
+ color: state.color,
303
+ size: toDesignAvatarSize(state.size),
304
+ },
305
+ state: {
306
+ status: state.status,
307
+ },
308
+ })
309
+
310
+ return createAvatarWithStatusThemeResolvers(resolve, {
311
+ themeTokens,
312
+ semanticTokens,
313
+ componentTokens,
314
+ })
222
315
  }
223
316
 
224
- export const createAvatarThemeFromDesign = (theme: DesignTokensTheme): AvatarTheme => {
225
- return createAvatarTheme({
226
- semantic: theme.semanticColors,
227
- coloring: theme.coloring as HightideComponentThemes['coloring'],
228
- gray: theme.colors.gray.value as ColorPalette,
229
- typography: theme.typography,
317
+ export const toAvatarGroupThemeResolvers: ComponentThemeResolver<
318
+ AvatarGroupThemeResolvers
319
+ > = ({
320
+ themeTokens,
321
+ semanticTokens,
322
+ componentTokens,
323
+ }) => {
324
+ const resolve = (state: AvatarGroupState = {}) => componentTokens.avatarGroup({
325
+ themeTokens,
326
+ semanticResolvers: semanticTokens,
327
+ config: {
328
+ groupCount: state.count,
329
+ },
330
+ overrides: {
331
+ color: state.color,
332
+ size: toDesignAvatarSize(state.size),
333
+ },
230
334
  })
335
+
336
+ return {
337
+ avatar: createValueResolver((state: AvatarGroupState) => {
338
+ const { avatarOverride } = resolve(state)
339
+
340
+ const resolveTokens = (avatarState: AvatarState): AvatarTokens => {
341
+ const tokens = mergeAvatarTokens(
342
+ componentTokens.avatar({
343
+ themeTokens,
344
+ semanticResolvers: semanticTokens,
345
+ config: {
346
+ isGrouped: true,
347
+ groupIndex: avatarState.groupIndex,
348
+ },
349
+ overrides: {
350
+ color: avatarState.color ?? avatarOverride.overrides?.color,
351
+ size: toDesignAvatarSize(avatarState.size ?? avatarOverride.overrides?.size),
352
+ },
353
+ }),
354
+ avatarOverride
355
+ )
356
+
357
+ const size = avatarState.size ?? state.size
358
+ if (typeof size === 'number') {
359
+ return withNumericAvatarSize(tokens, size)
360
+ }
361
+
362
+ return tokens
363
+ }
364
+
365
+ return createAvatarStyleResolvers(resolveTokens, themeTokens)
366
+ }),
367
+ container: createStyleResolver((state: AvatarGroupState): AvatarGroupContainerStyle => {
368
+ const tokens = resolve(state)
369
+ let container = tokens.container
370
+
371
+ if (typeof state.size === 'number') {
372
+ container = {
373
+ ...container,
374
+ size: {
375
+ ...container.size,
376
+ height: state.size,
377
+ },
378
+ }
379
+ }
380
+
381
+ return toContainerStyle(container)
382
+ }),
383
+ avatarStack: createStyleResolver((state: AvatarGroupState): AvatarGroupStackStyle => {
384
+ const tokens = resolve(state)
385
+ let avatarStack = tokens.avatarStack
386
+
387
+ if (typeof state.size === 'number') {
388
+ const visibleCount = Math.min(
389
+ avatarGroupMaxShown,
390
+ state.count ?? avatarGroupMaxShown
391
+ )
392
+ const stackWidth = state.size * (avatarGroupOverlap * Math.max(visibleCount - 1, 0) + 1)
393
+ avatarStack = {
394
+ ...avatarStack,
395
+ size: {
396
+ width: stackWidth,
397
+ height: state.size,
398
+ },
399
+ }
400
+ }
401
+
402
+ const width = avatarStack.size?.width
403
+ const height = avatarStack.size?.height
404
+
405
+ return {
406
+ ...toContainerStyle(avatarStack),
407
+ position: 'relative',
408
+ minWidth: width,
409
+ maxWidth: width,
410
+ minHeight: height,
411
+ maxHeight: height,
412
+ }
413
+ }),
414
+ text: createStyleResolver((state: AvatarGroupState): AvatarGroupTextStyle => {
415
+ const { text } = resolve(state)
416
+
417
+ return {
418
+ ...toTextStyle(
419
+ typeof state.size === 'number'
420
+ ? {
421
+ ...text,
422
+ fontSize: state.size * 2 / 3,
423
+ }
424
+ : text
425
+ ),
426
+ flexShrink: 1,
427
+ }
428
+ }),
429
+ }
231
430
  }