@helpwave/hightide-native 0.0.8 → 0.1.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.
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 +110 -80
  29. package/src/components/user-interaction/ThemedPressable.tsx +136 -0
  30. package/src/components/user-interaction/index.ts +2 -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 +7 -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 +59 -53
  81. package/src/theme/resolvers/themedPressable.ts +54 -0
  82. package/src/theme/themes/createHightideTheme.ts +214 -51
  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 -42
  93. package/src/theme/types/components/iconButton.ts +19 -14
  94. package/src/theme/types/components/index.ts +5 -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 +12 -5
  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
@@ -2,39 +2,43 @@ import {
2
2
  useEffect,
3
3
  useMemo,
4
4
  useState,
5
- type ComponentType,
6
- type ReactNode
5
+ type ComponentType
7
6
  } from 'react'
8
7
  import {
9
8
  Image,
10
- Text,
11
9
  View,
12
10
  type ImageProps,
13
11
  type StyleProp,
14
12
  type ViewProps,
15
13
  type ViewStyle
16
14
  } from 'react-native'
17
- import { User } from 'lucide-react-native'
18
-
19
- import type { ElementSize } from '@helpwave/hightide-design/types'
15
+ import { HightideIconRegistry } from '../../icons/HightideIconRegistry'
16
+ import { ThemedIcon } from './ThemedIcon'
17
+ import { ThemedText } from './ThemedText'
18
+ import {
19
+ avatarSizes,
20
+ type AvatarSize as AvatarSizeToken
21
+ } from '@helpwave/hightide-design/component-token-resolvers'
22
+ import type { ColorPairToken } from '@helpwave/hightide-design/theme-tokens'
20
23
 
21
24
  import { useTheme } from '../../global-contexts/theme/ThemeContext'
22
25
  import type {
23
- AvatarGroupMoreStyle,
26
+ AvatarGroupContainerStyle,
24
27
  AvatarGroupStackStyle,
28
+ AvatarGroupTextStyle,
25
29
  AvatarImageStyle,
26
30
  AvatarIconStyle,
31
+ AvatarState,
27
32
  AvatarStatus,
28
33
  AvatarStyle,
29
34
  AvatarTextStyle,
30
- AvatarWithLabelContainerStyle,
31
- AvatarWithLabelTextStyle,
32
- AvatarWithStatusContainerStyle,
33
- AvatarStatusDotStyle
35
+ AvatarStatusDotStyle,
36
+ AvatarWithStatusState
34
37
  } from '../../theme/types/components/avatar'
38
+ import { avatarStatuses } from '../../theme/types/components/avatar'
35
39
  import type { StyleOverwrite } from '../../theme/types/resolver'
36
40
 
37
- export type AvatarSize = ElementSize
41
+ export type AvatarSize = AvatarSizeToken | number
38
42
 
39
43
  type ImageConfig = {
40
44
  avatarUrl: string,
@@ -54,20 +58,21 @@ const DefaultAvatarImage: ComponentType<AvatarImageProps> = ({ alt, ...props })
54
58
  )
55
59
 
56
60
  export const AvatarUtil = {
57
- sizes: ['xs', 'sm', 'md', 'lg'] as const satisfies readonly ElementSize[],
58
- statuses: ['online', 'offline', 'away', 'busy', 'unknown'] as const satisfies readonly AvatarStatus[],
61
+ sizes: avatarSizes,
62
+ statuses: avatarStatuses,
59
63
  }
60
64
 
61
65
  export type AvatarProps = Omit<ViewProps, 'children' | 'style'> & {
62
66
  image?: ImageConfig,
63
67
  name?: string,
64
68
  size?: AvatarSize,
69
+ color?: ColorPairToken,
65
70
  ImageComponent?: ComponentType<AvatarImageProps>,
66
71
  style?: StyleProp<ViewStyle>,
67
- avatarStyle?: StyleOverwrite<{ size?: ElementSize, isGrouped?: boolean, groupIndex?: number }, AvatarStyle>,
68
- imageStyle?: StyleOverwrite<{ size?: ElementSize, isGrouped?: boolean, groupIndex?: number }, AvatarImageStyle>,
69
- textStyle?: StyleOverwrite<{ size?: ElementSize, isGrouped?: boolean, groupIndex?: number }, AvatarTextStyle>,
70
- iconStyle?: StyleOverwrite<{ size?: ElementSize, isGrouped?: boolean, groupIndex?: number }, AvatarIconStyle>,
72
+ avatarStyle?: StyleOverwrite<AvatarState, AvatarStyle>,
73
+ imageStyle?: StyleOverwrite<AvatarState, AvatarImageStyle>,
74
+ textStyle?: StyleOverwrite<AvatarState, AvatarTextStyle>,
75
+ iconStyle?: StyleOverwrite<AvatarState, AvatarIconStyle>,
71
76
  isGrouped?: boolean,
72
77
  groupIndex?: number,
73
78
  }
@@ -76,6 +81,7 @@ export const Avatar = ({
76
81
  image: initialImage,
77
82
  name,
78
83
  size = 'md',
84
+ color,
79
85
  ImageComponent = DefaultAvatarImage,
80
86
  style,
81
87
  avatarStyle,
@@ -92,11 +98,12 @@ export const Avatar = ({
92
98
  const [image, setImage] = useState(initialImage)
93
99
  const ImageElement = ImageComponent
94
100
 
95
- const state = useMemo(() => ({
101
+ const state = useMemo((): AvatarState => ({
96
102
  size,
97
103
  isGrouped,
98
104
  groupIndex,
99
- }), [size, isGrouped, groupIndex])
105
+ color,
106
+ }), [size, isGrouped, groupIndex, color])
100
107
 
101
108
  const displayName = useMemo(() => {
102
109
  const maxLetters = size === 'sm' ? 1 : 2
@@ -119,7 +126,7 @@ export const Avatar = ({
119
126
  setImage(initialImage)
120
127
  }, [image?.avatarUrl, initialImage])
121
128
 
122
- const resolvedAvatar = theme.components.avatar.avatar(state, avatarStyle)
129
+ const resolvedAvatar = theme.components.avatar.container(state, avatarStyle)
123
130
  const resolvedImage = theme.components.avatar.image(state, imageStyle)
124
131
  const resolvedText = theme.components.avatar.text(state, textStyle)
125
132
  const resolvedIcon = theme.components.avatar.icon(state, iconStyle)
@@ -141,11 +148,11 @@ export const Avatar = ({
141
148
  )}
142
149
  {isShowingFallback && (
143
150
  name ? (
144
- <Text style={resolvedText}>{displayName}</Text>
151
+ <ThemedText style={resolvedText}>{displayName}</ThemedText>
145
152
  ) : (
146
- <User
153
+ <ThemedIcon
154
+ icon={HightideIconRegistry.User}
147
155
  size={resolvedIcon.size}
148
- strokeWidth={resolvedIcon.strokeWidth}
149
156
  color={resolvedIcon.color}
150
157
  />
151
158
  )
@@ -158,57 +165,91 @@ export type AvatarGroupProps = Omit<ViewProps, 'children' | 'style'> & {
158
165
  avatars: Omit<AvatarProps, 'size' | 'isGrouped' | 'groupIndex'>[],
159
166
  showTotalNumber?: boolean,
160
167
  size?: AvatarSize,
168
+ color?: ColorPairToken,
161
169
  ImageComponent?: ComponentType<AvatarImageProps>,
162
170
  style?: StyleProp<ViewStyle>,
163
- stackStyle?: StyleOverwrite<{ size?: ElementSize, count?: number }, AvatarGroupStackStyle>,
164
- moreStyle?: StyleOverwrite<{ size?: ElementSize, count?: number }, AvatarGroupMoreStyle>,
171
+ containerStyle?: StyleOverwrite<{ size?: AvatarSize, count?: number }, AvatarGroupContainerStyle>,
172
+ avatarStackStyle?: StyleOverwrite<{ size?: AvatarSize, count?: number }, AvatarGroupStackStyle>,
173
+ textStyle?: StyleOverwrite<{ size?: AvatarSize, count?: number }, AvatarGroupTextStyle>,
165
174
  }
166
175
 
167
176
  export const AvatarGroup = ({
168
177
  avatars,
169
178
  showTotalNumber = true,
170
179
  size = 'md',
180
+ color,
171
181
  ImageComponent,
172
182
  style,
173
- stackStyle,
174
- moreStyle,
183
+ containerStyle,
184
+ avatarStackStyle,
185
+ textStyle,
175
186
  ...props
176
187
  }: AvatarGroupProps) => {
177
188
  const { theme } = useTheme()
178
- const maxShownProfiles = theme.layout.avatarGroup.maxShown
189
+ const maxShownProfiles = 5
179
190
  const displayedProfiles = avatars.length < maxShownProfiles ? avatars : avatars.slice(0, maxShownProfiles)
180
191
  const notDisplayedProfiles = avatars.length - maxShownProfiles
181
192
 
182
193
  const state = useMemo(() => ({
183
194
  size,
195
+ color,
184
196
  count: displayedProfiles.length,
185
- }), [size, displayedProfiles.length])
197
+ }), [size, color, displayedProfiles.length])
186
198
 
187
- const resolvedContainer = theme.components.avatar.group.container(state)
188
- const resolvedStack = theme.components.avatar.group.stack(state, stackStyle)
189
- const resolvedMore = theme.components.avatar.group.more(state, moreStyle)
199
+ const avatarResolvers = useMemo(
200
+ () => theme.components.avatarGroup.avatar(state),
201
+ [theme, state]
202
+ )
203
+ const resolvedContainer = theme.components.avatarGroup.container(state, containerStyle)
204
+ const resolvedAvatarStack = theme.components.avatarGroup.avatarStack(state, avatarStackStyle)
205
+ const resolvedText = theme.components.avatarGroup.text(state, textStyle)
190
206
 
191
207
  return (
192
208
  <View
193
209
  {...props}
194
210
  style={[resolvedContainer, style]}
195
211
  >
196
- <View style={resolvedStack}>
197
- {displayedProfiles.map((avatar, index) => (
198
- <Avatar
199
- {...avatar}
200
- key={index}
201
- size={size}
202
- isGrouped
203
- groupIndex={index}
204
- ImageComponent={avatar.ImageComponent ?? ImageComponent}
205
- />
206
- ))}
212
+ <View style={resolvedAvatarStack}>
213
+ {displayedProfiles.map((avatar, index) => {
214
+ const avatarState: AvatarState = {
215
+ size,
216
+ isGrouped: true,
217
+ groupIndex: index,
218
+ color: avatar.color ?? color,
219
+ }
220
+
221
+ return (
222
+ <Avatar
223
+ {...avatar}
224
+ key={index}
225
+ size={size}
226
+ isGrouped
227
+ groupIndex={index}
228
+ ImageComponent={avatar.ImageComponent ?? ImageComponent}
229
+ avatarStyle={(_, state) => avatarResolvers.container({
230
+ ...state,
231
+ ...avatarState,
232
+ })}
233
+ imageStyle={(_, state) => avatarResolvers.image({
234
+ ...state,
235
+ ...avatarState,
236
+ })}
237
+ textStyle={(_, state) => avatarResolvers.text({
238
+ ...state,
239
+ ...avatarState,
240
+ })}
241
+ iconStyle={(_, state) => avatarResolvers.icon({
242
+ ...state,
243
+ ...avatarState,
244
+ })}
245
+ />
246
+ )
247
+ })}
207
248
  </View>
208
249
  {showTotalNumber && notDisplayedProfiles > 0 && (
209
- <Text style={resolvedMore}>
210
- {`+ ${notDisplayedProfiles}`}
211
- </Text>
250
+ <ThemedText style={resolvedText}>
251
+ {`+${notDisplayedProfiles}`}
252
+ </ThemedText>
212
253
  )}
213
254
  </View>
214
255
  )
@@ -218,69 +259,47 @@ export type { AvatarStatus }
218
259
 
219
260
  export type AvatarWithStatusProps = AvatarProps & {
220
261
  status?: AvatarStatus,
221
- containerStyle?: StyleOverwrite<{ size?: ElementSize, status?: AvatarStatus }, AvatarWithStatusContainerStyle>,
222
- statusDotStyle?: StyleOverwrite<{ size?: ElementSize, status?: AvatarStatus }, AvatarStatusDotStyle>,
262
+ statusDotStyle?: StyleOverwrite<AvatarWithStatusState, AvatarStatusDotStyle>,
223
263
  }
224
264
 
225
265
  export const AvatarWithStatus = ({
226
266
  status = 'unknown',
227
267
  size = 'md',
268
+ color,
228
269
  style,
229
- containerStyle,
270
+ avatarStyle,
271
+ imageStyle,
272
+ textStyle,
273
+ iconStyle,
230
274
  statusDotStyle,
231
275
  ...avatarProps
232
276
  }: AvatarWithStatusProps) => {
233
277
  const { theme } = useTheme()
234
278
 
235
- const state = useMemo(() => ({
279
+ const state = useMemo((): AvatarWithStatusState => ({
236
280
  size,
281
+ color,
237
282
  status,
238
- }), [size, status])
239
-
240
- const resolvedContainer = theme.components.avatar.withStatus.container(state, containerStyle)
241
- const resolvedStatusDot = theme.components.avatar.withStatus.statusDot(state, statusDotStyle)
283
+ }), [size, color, status])
242
284
 
243
- return (
244
- <View style={[resolvedContainer, style]}>
245
- <Avatar {...avatarProps} size={size} />
246
- <View style={resolvedStatusDot} />
247
- </View>
285
+ const avatarResolvers = useMemo(
286
+ () => theme.components.avatarWithStatus.avatar(state),
287
+ [theme, state]
248
288
  )
249
- }
250
-
251
- type AvatarWithLabelPosition = 'left' | 'right'
252
-
253
- export type AvatarWithLabelProps = AvatarProps & {
254
- label: ReactNode,
255
- labelPosition?: AvatarWithLabelPosition,
256
- containerStyle?: StyleOverwrite<{ size?: ElementSize }, AvatarWithLabelContainerStyle>,
257
- labelStyle?: StyleOverwrite<{ size?: ElementSize }, AvatarWithLabelTextStyle>,
258
- }
259
-
260
- export const AvatarWithLabel = ({
261
- label,
262
- labelPosition = 'left',
263
- size = 'md',
264
- style,
265
- containerStyle,
266
- labelStyle,
267
- ...avatarProps
268
- }: AvatarWithLabelProps) => {
269
- const { theme } = useTheme()
270
-
271
- const state = useMemo(() => ({ size }), [size])
272
- const resolvedContainer = theme.components.avatar.withLabel.container(state, containerStyle)
273
- const resolvedLabel = theme.components.avatar.withLabel.text(state, labelStyle)
274
-
275
- const avatar = <Avatar {...avatarProps} size={size} />
276
- const labelElement = typeof label === 'string' || typeof label === 'number'
277
- ? <Text style={resolvedLabel}>{label}</Text>
278
- : label
289
+ const resolvedStatusDot = theme.components.avatarWithStatus.statusDot(state, statusDotStyle)
279
290
 
280
291
  return (
281
- <View style={[resolvedContainer, style]}>
282
- {labelPosition === 'left' ? labelElement : avatar}
283
- {labelPosition === 'left' ? avatar : labelElement}
292
+ <View style={[{ position: 'relative' }, style]}>
293
+ <Avatar
294
+ {...avatarProps}
295
+ size={size}
296
+ color={color}
297
+ avatarStyle={(_, state) => avatarResolvers.container(state, avatarStyle)}
298
+ imageStyle={(_, state) => avatarResolvers.image(state, imageStyle)}
299
+ textStyle={(_, state) => avatarResolvers.text(state, textStyle)}
300
+ iconStyle={(_, state) => avatarResolvers.icon(state, iconStyle)}
301
+ />
302
+ <View style={resolvedStatusDot} />
284
303
  </View>
285
304
  )
286
305
  }
@@ -3,22 +3,16 @@ import {
3
3
  type ReactNode
4
4
  } from 'react'
5
5
  import {
6
- Text,
7
6
  View,
8
7
  type StyleProp,
9
8
  type ViewProps,
10
9
  type ViewStyle
11
10
  } from 'react-native'
12
11
 
13
- import {
14
- coloringTypes,
15
- type ColoringType
16
- } from '@helpwave/hightide-design/utils'
17
- import type {
18
- ChipColoringStyle,
19
- ElementSize
20
- } from '@helpwave/hightide-design/types'
12
+ import type { ColorPairToken } from '@helpwave/hightide-design/theme-tokens'
13
+ import type { ChipVariant, ComponentSize } from '@helpwave/hightide-design/semantic-token-resolvers'
21
14
 
15
+ import { ContentThemeProvider } from '../../global-contexts/content-theme/ContentThemeProvider'
22
16
  import { useTheme } from '../../global-contexts/theme/ThemeContext'
23
17
  import type {
24
18
  ChipState,
@@ -26,20 +20,21 @@ import type {
26
20
  ChipTextStyle
27
21
  } from '../../theme/types/components/chip'
28
22
  import type { StyleOverwrite } from '../../theme/types/resolver'
23
+ import type { Color } from '../../theme/types/color'
24
+ import { ThemedText } from './ThemedText'
29
25
 
30
- export type ChipSize = ElementSize
26
+ export type ChipSize = ComponentSize
31
27
 
32
- export type ChipColor = ColoringType
28
+ export type ChipColor = ColorPairToken
33
29
 
34
30
  export const ChipUtil = {
35
- colors: coloringTypes,
36
- sizes: ['xs', 'sm', 'md', 'lg'] as const satisfies readonly ElementSize[],
37
- coloringStyles: ['solid', 'tonal', 'outline', 'tonal-outline'] as const satisfies readonly ChipColoringStyle[],
31
+ sizes: ['sm', 'md', 'lg'] as const satisfies readonly ComponentSize[],
32
+ variants: ['filled', 'tonal'] as const satisfies readonly ChipVariant[],
38
33
  }
39
34
 
40
35
  export type ChipProps = Omit<ViewProps, 'children' | 'style'> & {
41
36
  color?: ChipColor,
42
- coloringStyle?: ChipColoringStyle,
37
+ variant?: ChipVariant,
43
38
  size?: ChipSize,
44
39
  children?: ReactNode,
45
40
  style?: StyleProp<ViewStyle>,
@@ -49,8 +44,8 @@ export type ChipProps = Omit<ViewProps, 'children' | 'style'> & {
49
44
 
50
45
  export const Chip = ({
51
46
  children,
52
- color = 'neutral',
53
- coloringStyle = 'solid',
47
+ color,
48
+ variant = 'filled',
54
49
  size = 'md',
55
50
  style,
56
51
  chipStyle,
@@ -62,8 +57,8 @@ export const Chip = ({
62
57
  const state = useMemo((): ChipState => ({
63
58
  size,
64
59
  color,
65
- coloringStyle,
66
- }), [size, color, coloringStyle])
60
+ variant,
61
+ }), [size, color, variant])
67
62
 
68
63
  const resolvedChipStyle = useMemo(
69
64
  () => theme.components.chip.chip(state, chipStyle),
@@ -79,9 +74,14 @@ export const Chip = ({
79
74
  {...props}
80
75
  style={[resolvedChipStyle, style]}
81
76
  >
82
- {typeof children === 'string' || typeof children === 'number'
83
- ? <Text style={resolvedTextStyle}>{children}</Text>
84
- : children}
77
+ <ContentThemeProvider
78
+ foregroundColor={resolvedTextStyle.color as Color}
79
+ textStyle={resolvedTextStyle}
80
+ >
81
+ {typeof children === 'string' || typeof children === 'number'
82
+ ? <ThemedText>{children}</ThemedText>
83
+ : children}
84
+ </ContentThemeProvider>
85
85
  </View>
86
86
  )
87
87
  }
@@ -0,0 +1,72 @@
1
+ import {
2
+ View,
3
+ type ViewProps
4
+ } from 'react-native'
5
+
6
+ import type { HexColorToken } from '@helpwave/hightide-design/primitive-tokens'
7
+ import type { Appearance } from '@helpwave/hightide-design/semantic-token-resolvers'
8
+ import type { IconSize } from '@helpwave/hightide-design/theme-tokens'
9
+
10
+ import type { IconComponent } from '../../icons/types'
11
+ import { useContentTheme } from '../../global-contexts/content-theme/ContentThemeContext'
12
+ import { useTheme } from '../../global-contexts/theme/ThemeContext'
13
+
14
+ export type ThemedIconAppearance = Appearance
15
+
16
+ export type ThemedIconProps = Omit<ViewProps, 'children'> & {
17
+ icon: IconComponent,
18
+ size?: IconSize | number,
19
+ color?: string,
20
+ strokeWidth?: number,
21
+ appearance?: ThemedIconAppearance,
22
+ }
23
+
24
+ export const ThemedIcon = ({
25
+ icon: Glyph,
26
+ size = 'md',
27
+ color,
28
+ strokeWidth,
29
+ appearance = 'normal',
30
+ style,
31
+ ...props
32
+ }: ThemedIconProps) => {
33
+ const { theme } = useTheme()
34
+ const { foregroundColor } = useContentTheme()
35
+ const iconToken = typeof size === 'number'
36
+ ? {
37
+ size,
38
+ strokeWidth: strokeWidth ?? theme.components.icon.md.strokeWidth,
39
+ }
40
+ : theme.components.icon[size]
41
+
42
+ const baseColor = (color ?? foregroundColor) as HexColorToken
43
+ const resolvedColor = appearance === 'normal'
44
+ ? baseColor
45
+ : theme.semantics.withAppearance({ color: baseColor, appearance })
46
+
47
+ return (
48
+ <View
49
+ {...props}
50
+ style={[
51
+ {
52
+ width: iconToken.size,
53
+ height: iconToken.size,
54
+ minWidth: iconToken.size,
55
+ maxWidth: iconToken.size,
56
+ minHeight: iconToken.size,
57
+ maxHeight: iconToken.size,
58
+ alignItems: 'center',
59
+ justifyContent: 'center',
60
+ overflow: 'hidden',
61
+ },
62
+ style,
63
+ ]}
64
+ >
65
+ <Glyph
66
+ size={iconToken.size}
67
+ strokeWidth={strokeWidth ?? iconToken.strokeWidth}
68
+ color={resolvedColor}
69
+ />
70
+ </View>
71
+ )
72
+ }
@@ -0,0 +1,38 @@
1
+ import {
2
+ forwardRef
3
+ } from 'react'
4
+ import {
5
+ Text as RNText,
6
+ type TextProps
7
+ } from 'react-native'
8
+
9
+ import type { HexColorToken } from '@helpwave/hightide-design/primitive-tokens'
10
+
11
+ import { useContentTheme } from '../../global-contexts/content-theme/ContentThemeContext'
12
+ import { useTheme } from '../../global-contexts/theme/ThemeContext'
13
+
14
+ export type ThemedTextAppearance = 'normal' | 'description'
15
+
16
+ export type ThemedTextProps = TextProps & {
17
+ appearance?: ThemedTextAppearance,
18
+ }
19
+
20
+ export const ThemedText = forwardRef<React.ComponentRef<typeof RNText>, ThemedTextProps>(function ThemedText({
21
+ appearance = 'normal',
22
+ style,
23
+ ...props
24
+ }, ref) {
25
+ const { theme } = useTheme()
26
+ const { foregroundColor, textStyle } = useContentTheme()
27
+ const color = appearance === 'description'
28
+ ? theme.semantics.asDescription({ color: foregroundColor as HexColorToken })
29
+ : foregroundColor
30
+
31
+ return (
32
+ <RNText
33
+ {...props}
34
+ ref={ref}
35
+ style={[textStyle, { color }, style]}
36
+ />
37
+ )
38
+ })
@@ -1,3 +1,4 @@
1
1
  export * from './Avatar'
2
2
  export * from './Chip'
3
- export * from './Icon'
3
+ export * from './ThemedIcon'
4
+ export * from './ThemedText'
@@ -8,6 +8,10 @@ import { hightideTranslation } from '@helpwave/hightide-utils/i18n'
8
8
  import { ArrayUtil } from '@helpwave/hightide-utils/utils'
9
9
 
10
10
  import { HightideContext } from './HightideContext'
11
+ import {
12
+ DebugProvider,
13
+ type DebugProviderProps
14
+ } from './debug'
11
15
  import {
12
16
  LocalizationProvider,
13
17
  type LocalizationProviderProps
@@ -21,11 +25,13 @@ import {
21
25
  TranslationProvider,
22
26
  type TranslationProviderProps
23
27
  } from './translation/forward-exports'
28
+ import type { HightideTheme } from '../theme'
24
29
 
25
30
  export type HightideProviderProps = PropsWithChildren & {
26
- theme?: Omit<ThemeProviderProps, 'children'>,
31
+ theme?: Omit<ThemeProviderProps<HightideTheme>, 'children'>,
27
32
  locale?: Omit<LocalizationProviderProps, 'children'>,
28
33
  translation?: Omit<TranslationProviderProps, 'children' | 'locale'>,
34
+ debug?: Omit<DebugProviderProps, 'children'>,
29
35
  }
30
36
 
31
37
  const HightideContextBridge = ({ children }: PropsWithChildren) => {
@@ -49,6 +55,7 @@ export const HightideProvider = ({
49
55
  theme,
50
56
  locale,
51
57
  translation,
58
+ debug,
52
59
  }: HightideProviderProps) => {
53
60
  const resolvedTranslations = useMemo(() => [
54
61
  ...ArrayUtil.resolveSingleOrArray(translation?.translation ?? []),
@@ -56,14 +63,16 @@ export const HightideProvider = ({
56
63
  ], [translation?.translation])
57
64
 
58
65
  return (
59
- <LocalizationProvider {...locale}>
60
- <TranslationProvider {...translation} translation={resolvedTranslations}>
61
- <ThemeProvider {...theme}>
62
- <HightideContextBridge>
63
- {children}
64
- </HightideContextBridge>
65
- </ThemeProvider>
66
- </TranslationProvider>
67
- </LocalizationProvider>
66
+ <DebugProvider {...debug}>
67
+ <LocalizationProvider {...locale}>
68
+ <TranslationProvider {...translation} translation={resolvedTranslations}>
69
+ <ThemeProvider {...theme}>
70
+ <HightideContextBridge>
71
+ {children}
72
+ </HightideContextBridge>
73
+ </ThemeProvider>
74
+ </TranslationProvider>
75
+ </LocalizationProvider>
76
+ </DebugProvider>
68
77
  )
69
78
  }
@@ -0,0 +1,22 @@
1
+ import {
2
+ createContext,
3
+ useContext
4
+ } from 'react'
5
+ import type { ColorValue, TextStyle } from 'react-native'
6
+
7
+ export type ContentThemeContextValue = {
8
+ foregroundColor: ColorValue,
9
+ textStyle: TextStyle,
10
+ }
11
+
12
+ export const ContentThemeContext = createContext<ContentThemeContextValue | null>(null)
13
+
14
+ export const useContentTheme = (): ContentThemeContextValue => {
15
+ const context = useContext(ContentThemeContext)
16
+ if (!context) {
17
+ throw new Error(
18
+ 'useContentTheme must be used within ContentThemeProvider. Try adding a ContentThemeProvider around your app.'
19
+ )
20
+ }
21
+ return context
22
+ }
@@ -0,0 +1,32 @@
1
+ import {
2
+ useMemo,
3
+ type PropsWithChildren
4
+ } from 'react'
5
+ import type { TextStyle, ColorValue } from 'react-native'
6
+
7
+ import {
8
+ ContentThemeContext,
9
+ type ContentThemeContextValue
10
+ } from './ContentThemeContext'
11
+
12
+ export type ContentThemeProviderProps = PropsWithChildren & {
13
+ foregroundColor: ColorValue,
14
+ textStyle: TextStyle,
15
+ }
16
+
17
+ export const ContentThemeProvider = ({
18
+ children,
19
+ foregroundColor,
20
+ textStyle,
21
+ }: ContentThemeProviderProps) => {
22
+ const value = useMemo((): ContentThemeContextValue => ({
23
+ foregroundColor,
24
+ textStyle,
25
+ }), [foregroundColor, textStyle])
26
+
27
+ return (
28
+ <ContentThemeContext.Provider value={value}>
29
+ {children}
30
+ </ContentThemeContext.Provider>
31
+ )
32
+ }
@@ -0,0 +1,2 @@
1
+ export * from './ContentThemeContext'
2
+ export * from './ContentThemeProvider'
@@ -0,0 +1,10 @@
1
+ export {
2
+ DebugContext,
3
+ DebugProvider,
4
+ useDebugContext,
5
+ DEFAULT_HIT_BOX_COLOR,
6
+ defaultDebugContextValue,
7
+ type DebugContextValue,
8
+ type DebugHitBox,
9
+ type DebugProviderProps,
10
+ } from '@helpwave/hightide-utils/context/debug'
@@ -0,0 +1 @@
1
+ export * from './forward-exports'