@helpwave/hightide-native 0.2.0 → 0.3.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 (44) hide show
  1. package/package.json +2 -2
  2. package/src/components/chat/ChatConversationRow.tsx +127 -27
  3. package/src/components/chat/ChatThreadHeader.tsx +88 -48
  4. package/src/components/user-interaction/MultiSelect.tsx +66 -38
  5. package/src/components/user-interaction/Select.tsx +4 -0
  6. package/src/global-contexts/HightideProvider.tsx +9 -17
  7. package/src/theme/adapters/index.ts +1 -4
  8. package/src/theme/adapters/style-adapter-utils.ts +433 -0
  9. package/src/theme/resolvers/avatar.ts +9 -10
  10. package/src/theme/resolvers/button.ts +6 -7
  11. package/src/theme/resolvers/card.ts +3 -2
  12. package/src/theme/resolvers/chat/attachment-message-bubble.ts +22 -38
  13. package/src/theme/resolvers/chat/conversation-list.ts +5 -4
  14. package/src/theme/resolvers/chat/conversation-row.ts +114 -14
  15. package/src/theme/resolvers/chat/date-divider.ts +4 -4
  16. package/src/theme/resolvers/chat/message-bubble.ts +9 -10
  17. package/src/theme/resolvers/chat/message-composer.ts +5 -6
  18. package/src/theme/resolvers/chat/message-list.ts +3 -2
  19. package/src/theme/resolvers/chat/quick-reply-chip.ts +9 -28
  20. package/src/theme/resolvers/chat/system-line.ts +5 -6
  21. package/src/theme/resolvers/chat/thread-header.ts +60 -12
  22. package/src/theme/resolvers/checkbox.ts +6 -6
  23. package/src/theme/resolvers/chip.ts +5 -6
  24. package/src/theme/resolvers/divider.ts +3 -3
  25. package/src/theme/resolvers/iconButton.ts +5 -5
  26. package/src/theme/resolvers/input.ts +12 -7
  27. package/src/theme/resolvers/listItem.ts +16 -17
  28. package/src/theme/resolvers/multiSelect.ts +23 -16
  29. package/src/theme/resolvers/searchBar.ts +14 -7
  30. package/src/theme/resolvers/select.ts +22 -15
  31. package/src/theme/resolvers/switch.ts +5 -4
  32. package/src/theme/resolvers/themedPressable.ts +6 -7
  33. package/src/theme/themes/createHightideTheme.ts +13 -4
  34. package/src/theme/types/components/chat.ts +54 -3
  35. package/src/theme/types/components/multiSelect.ts +1 -0
  36. package/src/theme/types/components/select.ts +1 -0
  37. package/src/theme/types/layout.ts +13 -7
  38. package/src/theme/types/semantics.ts +2 -1
  39. package/src/theme/types/theme.ts +40 -12
  40. package/src/theme/types/typography.ts +9 -4
  41. package/src/theme/adapters/container-adapter.ts +0 -323
  42. package/src/theme/adapters/defined.ts +0 -11
  43. package/src/theme/adapters/icon-style-adapter.ts +0 -10
  44. package/src/theme/adapters/text-style-adapter.ts +0 -16
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "access": "public"
11
11
  },
12
12
  "license": "AGPL-3.0-only",
13
- "version": "0.2.0",
13
+ "version": "0.3.0",
14
14
  "type": "module",
15
15
  "files": [
16
16
  "src"
@@ -43,7 +43,7 @@
43
43
  },
44
44
  "dependencies": {
45
45
  "lucide-react-native": "0.561.0",
46
- "@helpwave/hightide-design": "0.2.0",
46
+ "@helpwave/hightide-design": "0.3.0",
47
47
  "@helpwave/hightide-utils": "0.1.0"
48
48
  },
49
49
  "peerDependencies": {
@@ -1,6 +1,9 @@
1
1
  import {
2
+ cloneElement,
2
3
  Fragment,
4
+ isValidElement,
3
5
  useMemo,
6
+ type ReactElement,
4
7
  type ReactNode
5
8
  } from 'react'
6
9
  import {
@@ -14,14 +17,24 @@ import { ThemedIcon } from '../visualization-and-display/ThemedIcon'
14
17
  import { ThemedText } from '../visualization-and-display/ThemedText'
15
18
  import { useTheme } from '../../global-contexts/theme/ThemeContext'
16
19
  import type {
20
+ AvatarState,
21
+ AvatarStyle
22
+ } from '../../theme/types/components/avatar'
23
+ import type {
24
+ ChatConversationRowContentContainerStyle,
25
+ ChatConversationRowHeaderRowStyle,
26
+ ChatConversationRowMessageRowStyle,
17
27
  ChatConversationRowPreviewStyle,
28
+ ChatConversationRowSentIndicatorStyle,
18
29
  ChatConversationRowState,
19
- ChatConversationRowStyle,
20
30
  ChatConversationRowTimestampStyle,
21
- ChatConversationRowTitleStyle
31
+ ChatConversationRowTitleStyle,
32
+ PressableContainerStyle,
33
+ PressableState
22
34
  } from '../../theme/types/components/chat'
23
35
  import type { StyleOverwrite } from '../../theme/types/resolver'
24
36
  import { ThemedPressable } from '../user-interaction'
37
+ import type { AvatarProps } from '../visualization-and-display/Avatar'
25
38
 
26
39
  export type ChatConversationSentIndicator = 'sent' | 'sentAndReceived'
27
40
 
@@ -34,10 +47,15 @@ export type ChatConversationRowProps = Omit<PressableProps, 'children' | 'style'
34
47
  isSelected?: boolean,
35
48
  sentIndicator?: ChatConversationSentIndicator,
36
49
  style?: StyleProp<ViewStyle>,
37
- rowStyle?: StyleOverwrite<ChatConversationRowState, ChatConversationRowStyle>,
50
+ rowStyle?: StyleOverwrite<PressableState, PressableContainerStyle>,
51
+ contentContainerStyle?: StyleOverwrite<ChatConversationRowState, ChatConversationRowContentContainerStyle>,
52
+ headerRowStyle?: StyleOverwrite<ChatConversationRowState, ChatConversationRowHeaderRowStyle>,
53
+ messageRowStyle?: StyleOverwrite<ChatConversationRowState, ChatConversationRowMessageRowStyle>,
38
54
  titleStyle?: StyleOverwrite<ChatConversationRowState, ChatConversationRowTitleStyle>,
39
55
  timestampStyle?: StyleOverwrite<ChatConversationRowState, ChatConversationRowTimestampStyle>,
40
56
  previewStyle?: StyleOverwrite<ChatConversationRowState, ChatConversationRowPreviewStyle>,
57
+ sentIndicatorStyle?: StyleOverwrite<Record<string, never>, ChatConversationRowSentIndicatorStyle>,
58
+ avatarStyle?: StyleOverwrite<AvatarState, AvatarStyle>,
41
59
  }
42
60
 
43
61
  type PressableInteraction = {
@@ -47,6 +65,14 @@ type PressableInteraction = {
47
65
  focusVisible?: boolean,
48
66
  }
49
67
 
68
+ type AvatarElementProps = AvatarProps & {
69
+ avatarStyle?: AvatarProps['avatarStyle'],
70
+ }
71
+
72
+ const toNumericSize = (value: unknown): number | undefined => (
73
+ typeof value === 'number' ? value : undefined
74
+ )
75
+
50
76
  export const ChatConversationRow = ({
51
77
  avatar,
52
78
  title,
@@ -58,9 +84,14 @@ export const ChatConversationRow = ({
58
84
  disabled,
59
85
  style,
60
86
  rowStyle,
87
+ contentContainerStyle,
88
+ headerRowStyle,
89
+ messageRowStyle,
61
90
  titleStyle,
62
91
  timestampStyle,
63
92
  previewStyle,
93
+ sentIndicatorStyle,
94
+ avatarStyle,
64
95
  ...props
65
96
  }: ChatConversationRowProps) => {
66
97
  const { theme } = useTheme()
@@ -88,31 +119,101 @@ export const ChatConversationRow = ({
88
119
  () => theme.components.chat.conversationRow.unreadBadgeText(staticState),
89
120
  [theme, staticState]
90
121
  )
91
- const sentIndicatorColor = useMemo(
92
- () => theme.components.chat.conversationRow.sentIndicator(staticState).color,
122
+ const resolvedSentIndicator = useMemo(
123
+ () => theme.components.chat.conversationRow.sentIndicator(staticState, sentIndicatorStyle),
124
+ [theme, staticState, sentIndicatorStyle]
125
+ )
126
+ const avatarTheme = useMemo(
127
+ () => theme.components.chat.conversationRow.avatar(staticState),
93
128
  [theme, staticState]
94
129
  )
130
+ const avatarSize = useMemo(
131
+ () => toNumericSize(avatarTheme.container({}).width),
132
+ [avatarTheme]
133
+ )
134
+
135
+ const resolvedAvatar = useMemo(() => {
136
+ if (!isValidElement(avatar) || avatarSize === undefined) {
137
+ return avatar
138
+ }
139
+
140
+ const avatarElement = avatar as ReactElement<AvatarElementProps>
141
+
142
+ return cloneElement(avatarElement, {
143
+ size: avatarSize,
144
+ avatarStyle: (_, avatarState) => avatarTheme.container(
145
+ {
146
+ ...avatarState,
147
+ size: avatarSize,
148
+ },
149
+ avatarStyle ?? avatarElement.props.avatarStyle
150
+ ),
151
+ imageStyle: (_, avatarState) => avatarTheme.image(
152
+ {
153
+ ...avatarState,
154
+ size: avatarSize,
155
+ },
156
+ avatarElement.props.imageStyle
157
+ ),
158
+ textStyle: (_, avatarState) => avatarTheme.text(
159
+ {
160
+ ...avatarState,
161
+ size: avatarSize,
162
+ },
163
+ avatarElement.props.textStyle
164
+ ),
165
+ iconStyle: (_, avatarState) => avatarTheme.icon(
166
+ {
167
+ ...avatarState,
168
+ size: avatarSize,
169
+ },
170
+ avatarElement.props.iconStyle
171
+ ),
172
+ })
173
+ }, [avatar, avatarSize, avatarStyle, avatarTheme])
95
174
 
96
175
  return (
97
176
  <ThemedPressable
98
177
  {...props}
99
178
  disabled={disabled}
100
- style={(pressableState) => {
179
+ style={style}
180
+ containerStyle={(_, pressableState) => {
101
181
  const state = resolveState(pressableState as PressableInteraction)
102
- return [theme.components.chat.conversationRow.container(state, rowStyle), style]
182
+ return theme.components.chat.conversationRow.pressable(state).container(
183
+ pressableState,
184
+ rowStyle
185
+ )
186
+ }}
187
+ stateLayerStyle={(_, pressableState) => {
188
+ const state = resolveState(pressableState as PressableInteraction)
189
+ return theme.components.chat.conversationRow.pressable(state).stateLayer(pressableState)
190
+ }}
191
+ textStyle={(_, pressableState) => {
192
+ const state = resolveState(pressableState as PressableInteraction)
193
+ return theme.components.chat.conversationRow.pressable(state).text(pressableState)
194
+ }}
195
+ iconStyle={(_, pressableState) => {
196
+ const state = resolveState(pressableState as PressableInteraction)
197
+ return theme.components.chat.conversationRow.pressable(state).icon(pressableState)
103
198
  }}
104
199
  >
105
200
  {(pressableState) => {
106
201
  const state = resolveState(pressableState as PressableInteraction)
202
+ const resolvedContentContainer = theme.components.chat.conversationRow.contentContainer(
203
+ state,
204
+ contentContainerStyle
205
+ )
206
+ const resolvedHeaderRow = theme.components.chat.conversationRow.headerRow(state, headerRowStyle)
207
+ const resolvedMessageRow = theme.components.chat.conversationRow.messageRow(state, messageRowStyle)
107
208
  const resolvedTitle = theme.components.chat.conversationRow.title(state, titleStyle)
108
209
  const resolvedTimestamp = theme.components.chat.conversationRow.timestamp(state, timestampStyle)
109
210
  const resolvedPreview = theme.components.chat.conversationRow.preview(state, previewStyle)
110
211
 
111
212
  return (
112
213
  <Fragment>
113
- {avatar}
114
- <View style={{ flex: 1, minWidth: 0, gap: 5 }}>
115
- <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>
214
+ {resolvedAvatar}
215
+ <View style={resolvedContentContainer}>
216
+ <View style={resolvedHeaderRow}>
116
217
  {typeof title === 'string' || typeof title === 'number' ? (
117
218
  <ThemedText style={resolvedTitle} numberOfLines={1}>{title}</ThemedText>
118
219
  ) : (
@@ -126,23 +227,22 @@ export const ChatConversationRow = ({
126
227
  )
127
228
  )}
128
229
  </View>
129
- <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>
130
- <View style={{ flex: 1, flexDirection: 'row', alignItems: 'center', gap: 4, minWidth: 0 }}>
131
- {sentIndicator && (
132
- <ThemedIcon
133
- icon={sentIndicatorIcon}
134
- size={16}
135
- color={sentIndicatorColor}
136
- />
137
- )}
138
- {preview != null && (
139
- typeof preview === 'string' || typeof preview === 'number' ? (
140
- <ThemedText style={resolvedPreview} numberOfLines={1}>{preview}</ThemedText>
141
- ) : (
142
- preview
143
- )
144
- )}
145
- </View>
230
+ <View style={resolvedMessageRow}>
231
+ {sentIndicator && (
232
+ <ThemedIcon
233
+ icon={sentIndicatorIcon}
234
+ size={resolvedSentIndicator.size}
235
+ strokeWidth={resolvedSentIndicator.strokeWidth}
236
+ color={resolvedSentIndicator.color}
237
+ />
238
+ )}
239
+ {preview != null && (
240
+ typeof preview === 'string' || typeof preview === 'number' ? (
241
+ <ThemedText style={resolvedPreview} numberOfLines={1}>{preview}</ThemedText>
242
+ ) : (
243
+ preview
244
+ )
245
+ )}
146
246
  {isUnread && (
147
247
  <View style={unreadBadge}>
148
248
  <ThemedText style={unreadBadgeText}>{unreadCount}</ThemedText>
@@ -15,11 +15,17 @@ import {
15
15
  Avatar,
16
16
  type AvatarProps
17
17
  } from '../visualization-and-display/Avatar'
18
+ import {
19
+ ThemedPressable,
20
+ type ThemedPressableProps
21
+ } from '../user-interaction/ThemedPressable'
18
22
  import type {
19
23
  ChatThreadHeaderContentRowStyle,
20
24
  ChatThreadHeaderStyle,
21
25
  ChatThreadHeaderSubtitleStyle,
22
- ChatThreadHeaderTitleStyle
26
+ ChatThreadHeaderTitleStyle,
27
+ PressableContainerStyle,
28
+ PressableState
23
29
  } from '../../theme/types/components/chat'
24
30
  import type { StyleOverwrite } from '../../theme/types/resolver'
25
31
 
@@ -29,11 +35,14 @@ export type ChatThreadHeaderProps = Omit<ViewProps, 'style'> & {
29
35
  subtitle?: ReactNode,
30
36
  leftActions?: ReactNode,
31
37
  rightActions?: ReactNode,
38
+ onPress?: ThemedPressableProps['onPress'],
39
+ disabled?: boolean,
32
40
  style?: StyleProp<ViewStyle>,
33
41
  headerStyle?: StyleOverwrite<Record<string, never>, ChatThreadHeaderStyle>,
34
42
  contentRowStyle?: StyleOverwrite<Record<string, never>, ChatThreadHeaderContentRowStyle>,
35
43
  titleStyle?: StyleOverwrite<Record<string, never>, ChatThreadHeaderTitleStyle>,
36
44
  subtitleStyle?: StyleOverwrite<Record<string, never>, ChatThreadHeaderSubtitleStyle>,
45
+ pressableContainerStyle?: StyleOverwrite<PressableState, PressableContainerStyle>,
37
46
  }
38
47
 
39
48
  export const ChatThreadHeader = ({
@@ -42,11 +51,14 @@ export const ChatThreadHeader = ({
42
51
  subtitle,
43
52
  leftActions,
44
53
  rightActions,
54
+ onPress,
55
+ disabled,
45
56
  style,
46
57
  headerStyle,
47
58
  contentRowStyle,
48
59
  titleStyle,
49
60
  subtitleStyle,
61
+ pressableContainerStyle,
50
62
  ...props
51
63
  }: ChatThreadHeaderProps) => {
52
64
  const { theme } = useTheme()
@@ -71,16 +83,26 @@ export const ChatThreadHeader = ({
71
83
  () => theme.components.chat.threadHeader.subtitle(state, subtitleStyle),
72
84
  [theme, state, subtitleStyle]
73
85
  )
74
-
75
- const avatarThemeState = useMemo(() => ({
76
- size: resolvedAvatar.size,
77
- color: resolvedAvatar.color,
78
- }), [resolvedAvatar.size, resolvedAvatar.color])
86
+ const pressableResolvers = useMemo(
87
+ () => theme.components.chat.threadHeader.pressable(state),
88
+ [theme, state]
89
+ )
79
90
 
80
91
  const avatarTheme = useMemo(
81
- () => theme.components.chat.threadHeader.avatar(avatarThemeState),
82
- [theme, avatarThemeState]
92
+ () => theme.components.chat.threadHeader.avatar({
93
+ size: resolvedAvatar.size,
94
+ color: resolvedAvatar.color,
95
+ }),
96
+ [theme, resolvedAvatar.size, resolvedAvatar.color]
83
97
  )
98
+ const avatarSize = useMemo(() => {
99
+ if (typeof resolvedAvatar.size === 'number' || typeof resolvedAvatar.size === 'string') {
100
+ return resolvedAvatar.size
101
+ }
102
+
103
+ const width = avatarTheme.container({}).width
104
+ return typeof width === 'number' ? width : resolvedAvatar.size
105
+ }, [avatarTheme, resolvedAvatar.size])
84
106
 
85
107
  return (
86
108
  <View {...props} style={[resolvedHeaderStyle, style]}>
@@ -89,51 +111,69 @@ export const ChatThreadHeader = ({
89
111
  {leftActions}
90
112
  </View>
91
113
  )}
92
- <Avatar
93
- {...resolvedAvatar}
94
- avatarStyle={(_, avatarState) => avatarTheme.container(
95
- {
96
- ...avatarState,
97
- size: resolvedAvatar.size,
98
- },
99
- resolvedAvatar.avatarStyle
114
+ <ThemedPressable
115
+ onPress={onPress}
116
+ disabled={disabled}
117
+ containerStyle={(_, pressableState) => (
118
+ pressableResolvers.container(pressableState, pressableContainerStyle)
100
119
  )}
101
- imageStyle={(_, avatarState) => avatarTheme.image(
102
- {
103
- ...avatarState,
104
- size: resolvedAvatar.size,
105
- },
106
- resolvedAvatar.imageStyle
120
+ stateLayerStyle={(_, pressableState) => (
121
+ pressableResolvers.stateLayer(pressableState)
107
122
  )}
108
- textStyle={(_, avatarState) => avatarTheme.text(
109
- {
110
- ...avatarState,
111
- size: resolvedAvatar.size,
112
- },
113
- resolvedAvatar.textStyle
123
+ textStyle={(_, pressableState) => (
124
+ pressableResolvers.text(pressableState)
114
125
  )}
115
- iconStyle={(_, avatarState) => avatarTheme.icon(
116
- {
117
- ...avatarState,
118
- size: resolvedAvatar.size,
119
- },
120
- resolvedAvatar.iconStyle
126
+ iconStyle={(_, pressableState) => (
127
+ pressableResolvers.icon(pressableState)
121
128
  )}
122
- />
123
- <View style={resolvedContentRowStyle}>
124
- {typeof title === 'string' || typeof title === 'number' ? (
125
- <ThemedText style={resolvedTitleStyle} numberOfLines={1}>{title}</ThemedText>
126
- ) : (
127
- title
128
- )}
129
- {subtitle != null && (
130
- typeof subtitle === 'string' || typeof subtitle === 'number' ? (
131
- <ThemedText style={resolvedSubtitleStyle} numberOfLines={1}>{subtitle}</ThemedText>
129
+ >
130
+ <Avatar
131
+ {...resolvedAvatar}
132
+ size={avatarSize}
133
+ avatarStyle={(_, avatarState) => avatarTheme.container(
134
+ {
135
+ ...avatarState,
136
+ size: avatarSize,
137
+ },
138
+ resolvedAvatar.avatarStyle
139
+ )}
140
+ imageStyle={(_, avatarState) => avatarTheme.image(
141
+ {
142
+ ...avatarState,
143
+ size: avatarSize,
144
+ },
145
+ resolvedAvatar.imageStyle
146
+ )}
147
+ textStyle={(_, avatarState) => avatarTheme.text(
148
+ {
149
+ ...avatarState,
150
+ size: avatarSize,
151
+ },
152
+ resolvedAvatar.textStyle
153
+ )}
154
+ iconStyle={(_, avatarState) => avatarTheme.icon(
155
+ {
156
+ ...avatarState,
157
+ size: avatarSize,
158
+ },
159
+ resolvedAvatar.iconStyle
160
+ )}
161
+ />
162
+ <View style={resolvedContentRowStyle}>
163
+ {typeof title === 'string' || typeof title === 'number' ? (
164
+ <ThemedText style={resolvedTitleStyle} numberOfLines={1}>{title}</ThemedText>
132
165
  ) : (
133
- subtitle
134
- )
135
- )}
136
- </View>
166
+ title
167
+ )}
168
+ {subtitle != null && (
169
+ typeof subtitle === 'string' || typeof subtitle === 'number' ? (
170
+ <ThemedText style={resolvedSubtitleStyle} numberOfLines={1}>{subtitle}</ThemedText>
171
+ ) : (
172
+ subtitle
173
+ )
174
+ )}
175
+ </View>
176
+ </ThemedPressable>
137
177
  {rightActions != null && (
138
178
  <View style={{ flexDirection: 'row', alignItems: 'center', flexShrink: 0 }}>
139
179
  {rightActions}
@@ -1,4 +1,4 @@
1
- import { useMemo } from 'react'
1
+ import { Fragment, useMemo } from 'react'
2
2
  import {
3
3
  FlatList,
4
4
  Modal,
@@ -128,44 +128,72 @@ export const MultiSelect = ({
128
128
  })
129
129
  }}
130
130
  >
131
- {selectedOptions.length > 0
132
- ? (
133
- <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 6, flex: 1 }}>
134
- {selectedOptions.map((option) => (
135
- <Chip key={option.id} size="md" color={color} variant="tonal">
136
- <ThemedText>{option.label ?? option.id}</ThemedText>
137
- {!state.isReadonly && (
138
- <View
139
- style={{
140
- position: 'relative',
141
- width: theme.icongraphy.sizes.sm,
142
- }}
143
- >
144
- <IconButton
145
- accessibilityLabel={translation('remove')}
146
- size="sm"
147
- color={theme.colors.negative}
148
- variant="foreground"
149
- disabled={!interactive}
150
- onPress={() => multiSelect.toggleSelection(option.id, false)}
151
- style={{
152
- position: 'absolute',
153
- left: '50%',
154
- top: '50%',
155
- transform: [
156
- { translateX: '-50%' },
157
- { translateY: '-50%' },
158
- ],
159
- }}
160
- icon={HightideIconRegistry.X}
161
- />
162
- </View>
163
- )}
164
- </Chip>
165
- ))}
166
- </View>
131
+ {(pressableState) => {
132
+ const interaction = pressableState as {
133
+ pressed: boolean,
134
+ hovered?: boolean,
135
+ focused?: boolean,
136
+ focusVisible?: boolean,
137
+ }
138
+ const triggerState = {
139
+ ...state,
140
+ isPressed: interaction.pressed,
141
+ isHovered: !!interaction.hovered,
142
+ isFocused: !!interaction.focused,
143
+ isFocusVisible: !!interaction.focusVisible,
144
+ }
145
+
146
+ return (
147
+ <Fragment>
148
+ <View
149
+ pointerEvents="none"
150
+ style={multiSelectTheme.stateLayer(triggerState)}
151
+ />
152
+ {selectedOptions.length > 0
153
+ ? (
154
+ <View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: 6, flex: 1 }}>
155
+ {selectedOptions.map((option) => (
156
+ <Chip key={option.id} size="md" color={color} variant="tonal">
157
+ <ThemedText>{option.label ?? option.id}</ThemedText>
158
+ {!state.isReadonly && (
159
+ <View
160
+ style={{
161
+ position: 'relative',
162
+ width: theme.icongraphy.sizes.sm,
163
+ }}
164
+ >
165
+ <IconButton
166
+ accessibilityLabel={translation('remove')}
167
+ size="sm"
168
+ color={theme.colors.negative}
169
+ variant="foreground"
170
+ disabled={!interactive}
171
+ onPress={() => multiSelect.toggleSelection(option.id, false)}
172
+ style={{
173
+ position: 'absolute',
174
+ left: '50%',
175
+ top: '50%',
176
+ transform: [
177
+ { translateX: '-50%' },
178
+ { translateY: '-50%' },
179
+ ],
180
+ }}
181
+ icon={HightideIconRegistry.X}
182
+ />
183
+ </View>
184
+ )}
185
+ </Chip>
186
+ ))}
187
+ </View>
188
+ )
189
+ : (
190
+ <ThemedText style={multiSelectTheme.triggerText(triggerState)}>
191
+ {placeholder}
192
+ </ThemedText>
193
+ )}
194
+ </Fragment>
167
195
  )
168
- : <ThemedText style={multiSelectTheme.triggerText(state)}>{placeholder}</ThemedText>}
196
+ }}
169
197
  </Pressable>
170
198
 
171
199
  <Modal
@@ -149,6 +149,10 @@ export const Select = ({
149
149
 
150
150
  return (
151
151
  <Fragment>
152
+ <View
153
+ pointerEvents="none"
154
+ style={selectTheme.stateLayer(triggerState)}
155
+ />
152
156
  <ThemedText style={[selectTheme.triggerText(triggerState), { flex: 1 }]}>
153
157
  {selectedLabel}
154
158
  </ThemedText>
@@ -8,10 +8,6 @@ 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'
15
11
  import {
16
12
  LocalizationProvider,
17
13
  type LocalizationProviderProps
@@ -31,7 +27,6 @@ export type HightideProviderProps = PropsWithChildren & {
31
27
  theme?: Omit<ThemeProviderProps<HightideTheme>, 'children'>,
32
28
  locale?: Omit<LocalizationProviderProps, 'children'>,
33
29
  translation?: Omit<TranslationProviderProps, 'children' | 'locale'>,
34
- debug?: Omit<DebugProviderProps, 'children'>,
35
30
  }
36
31
 
37
32
  const HightideContextBridge = ({ children }: PropsWithChildren) => {
@@ -55,7 +50,6 @@ export const HightideProvider = ({
55
50
  theme,
56
51
  locale,
57
52
  translation,
58
- debug,
59
53
  }: HightideProviderProps) => {
60
54
  const resolvedTranslations = useMemo(() => [
61
55
  ...ArrayUtil.resolveSingleOrArray(translation?.translation ?? []),
@@ -63,16 +57,14 @@ export const HightideProvider = ({
63
57
  ], [translation?.translation])
64
58
 
65
59
  return (
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>
60
+ <LocalizationProvider {...locale}>
61
+ <TranslationProvider {...translation} translation={resolvedTranslations}>
62
+ <ThemeProvider {...theme}>
63
+ <HightideContextBridge>
64
+ {children}
65
+ </HightideContextBridge>
66
+ </ThemeProvider>
67
+ </TranslationProvider>
68
+ </LocalizationProvider>
77
69
  )
78
70
  }
@@ -1,4 +1 @@
1
- export * from './container-adapter'
2
- export * from './defined'
3
- export * from './icon-style-adapter'
4
- export * from './text-style-adapter'
1
+ export * from './style-adapter-utils'