@helpwave/hightide-native 0.1.1 → 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 (65) hide show
  1. package/package.json +2 -2
  2. package/src/components/chat/ChatAttachmentMessageBubble.tsx +3 -0
  3. package/src/components/chat/ChatConversationRow.tsx +127 -27
  4. package/src/components/chat/ChatQuickReplyChip.tsx +7 -1
  5. package/src/components/chat/ChatThreadHeader.tsx +88 -48
  6. package/src/components/list/ListActionItem.tsx +60 -11
  7. package/src/components/list/ListItem.tsx +47 -21
  8. package/src/components/list/ListItemAccessory.tsx +34 -0
  9. package/src/components/list/ListItemTextContent.tsx +53 -0
  10. package/src/components/list/ListNavigationItem.tsx +60 -16
  11. package/src/components/list/index.ts +2 -0
  12. package/src/components/user-interaction/Button.tsx +6 -15
  13. package/src/components/user-interaction/IconButton.tsx +5 -7
  14. package/src/components/user-interaction/MultiSelect.tsx +72 -46
  15. package/src/components/user-interaction/Select.tsx +5 -1
  16. package/src/components/user-interaction/ThemedPressable.tsx +9 -4
  17. package/src/components/visualization-and-display/Chip.tsx +12 -5
  18. package/src/components/visualization-and-display/ThemedIcon.tsx +12 -8
  19. package/src/components/visualization-and-display/ThemedText.tsx +6 -2
  20. package/src/global-contexts/HightideProvider.tsx +9 -17
  21. package/src/global-contexts/content-theme/ContentThemeContext.ts +5 -2
  22. package/src/global-contexts/content-theme/ContentThemeProvider.tsx +64 -10
  23. package/src/global-contexts/theme/ThemeProvider.tsx +9 -4
  24. package/src/theme/adapters/index.ts +1 -4
  25. package/src/theme/adapters/style-adapter-utils.ts +433 -0
  26. package/src/theme/resolvers/avatar.ts +9 -10
  27. package/src/theme/resolvers/button.ts +6 -7
  28. package/src/theme/resolvers/card.ts +3 -2
  29. package/src/theme/resolvers/chat/attachment-message-bubble.ts +25 -37
  30. package/src/theme/resolvers/chat/conversation-list.ts +5 -4
  31. package/src/theme/resolvers/chat/conversation-row.ts +114 -14
  32. package/src/theme/resolvers/chat/date-divider.ts +4 -4
  33. package/src/theme/resolvers/chat/message-bubble.ts +9 -10
  34. package/src/theme/resolvers/chat/message-composer.ts +5 -6
  35. package/src/theme/resolvers/chat/message-list.ts +3 -2
  36. package/src/theme/resolvers/chat/quick-reply-chip.ts +12 -20
  37. package/src/theme/resolvers/chat/system-line.ts +5 -6
  38. package/src/theme/resolvers/chat/thread-header.ts +60 -12
  39. package/src/theme/resolvers/checkbox.ts +6 -6
  40. package/src/theme/resolvers/chip.ts +9 -4
  41. package/src/theme/resolvers/divider.ts +3 -3
  42. package/src/theme/resolvers/iconButton.ts +5 -10
  43. package/src/theme/resolvers/input.ts +12 -7
  44. package/src/theme/resolvers/listItem.ts +19 -16
  45. package/src/theme/resolvers/multiSelect.ts +23 -16
  46. package/src/theme/resolvers/searchBar.ts +14 -7
  47. package/src/theme/resolvers/select.ts +22 -15
  48. package/src/theme/resolvers/switch.ts +5 -4
  49. package/src/theme/resolvers/themedPressable.ts +10 -5
  50. package/src/theme/themes/createHightideTheme.ts +13 -4
  51. package/src/theme/types/components/chat.ts +63 -3
  52. package/src/theme/types/components/chip.ts +4 -0
  53. package/src/theme/types/components/iconButton.ts +1 -7
  54. package/src/theme/types/components/listItem.ts +5 -0
  55. package/src/theme/types/components/multiSelect.ts +1 -0
  56. package/src/theme/types/components/select.ts +1 -0
  57. package/src/theme/types/components/themedPressable.ts +4 -0
  58. package/src/theme/types/layout.ts +13 -7
  59. package/src/theme/types/semantics.ts +2 -1
  60. package/src/theme/types/theme.ts +40 -12
  61. package/src/theme/types/typography.ts +9 -4
  62. package/src/theme/adapters/container-adapter.ts +0 -323
  63. package/src/theme/adapters/defined.ts +0 -11
  64. package/src/theme/adapters/icon-style-adapter.ts +0 -10
  65. package/src/theme/adapters/text-style-adapter.ts +0 -16
@@ -12,7 +12,6 @@ import {
12
12
  import type { ColorPairToken } from '@helpwave/hightide-design/theme-tokens'
13
13
 
14
14
  import { useTheme } from '../../global-contexts/theme/ThemeContext'
15
- import { ThemedText } from '../visualization-and-display/ThemedText'
16
15
  import type {
17
16
  ListItemDescriptionStyle,
18
17
  ListItemState,
@@ -20,29 +19,38 @@ import type {
20
19
  ListItemTitleStyle
21
20
  } from '../../theme/types/components/listItem'
22
21
  import type { StyleOverwrite } from '../../theme/types/resolver'
22
+ import { ListItemAccessory } from './ListItemAccessory'
23
+ import {
24
+ ListItemTextContent,
25
+ type ListItemContentOrder
26
+ } from './ListItemTextContent'
23
27
 
24
28
  export type ListItemProps = Omit<ViewProps, 'style'> & {
25
- label: string,
26
- value: string,
29
+ title?: string,
30
+ subtitle?: string,
31
+ content?: ReactNode,
32
+ contentOrder?: ListItemContentOrder,
27
33
  leading?: ReactNode,
28
34
  trailing?: ReactNode,
29
35
  color?: ColorPairToken,
30
36
  style?: StyleProp<ViewStyle>,
31
37
  itemStyle?: StyleOverwrite<ListItemState, ListItemStyle>,
32
- labelStyle?: StyleOverwrite<ListItemState, ListItemDescriptionStyle>,
33
- valueStyle?: StyleOverwrite<ListItemState, ListItemTitleStyle>,
38
+ titleStyle?: StyleOverwrite<ListItemState, ListItemTitleStyle>,
39
+ subtitleStyle?: StyleOverwrite<ListItemState, ListItemDescriptionStyle>,
34
40
  }
35
41
 
36
42
  export const ListItem = ({
37
- label,
38
- value,
43
+ title,
44
+ subtitle,
45
+ content,
46
+ contentOrder = 'subtitleFirst',
39
47
  leading,
40
48
  trailing,
41
49
  color,
42
50
  style,
43
51
  itemStyle,
44
- labelStyle,
45
- valueStyle,
52
+ titleStyle,
53
+ subtitleStyle,
46
54
  ...props
47
55
  }: ListItemProps) => {
48
56
  const { theme } = useTheme()
@@ -66,30 +74,48 @@ export const ListItem = ({
66
74
  () => theme.components.listItem.default.trailingItemContainer(state),
67
75
  [theme, state]
68
76
  )
69
- const resolvedLabelStyle = useMemo(
70
- () => theme.components.listItem.default.descriptionText(state, labelStyle),
71
- [theme, state, labelStyle]
77
+ const resolvedTitleStyle = useMemo(
78
+ () => theme.components.listItem.default.titleText(state, titleStyle),
79
+ [theme, state, titleStyle]
80
+ )
81
+ const resolvedSubtitleStyle = useMemo(
82
+ () => theme.components.listItem.default.descriptionText(state, subtitleStyle),
83
+ [theme, state, subtitleStyle]
72
84
  )
73
- const resolvedValueStyle = useMemo(
74
- () => theme.components.listItem.default.titleText(state, valueStyle),
75
- [theme, state, valueStyle]
85
+ const resolvedIconStyle = useMemo(
86
+ () => theme.components.listItem.default.icon(state),
87
+ [theme, state]
76
88
  )
77
89
 
78
90
  return (
79
91
  <View {...props} style={[resolvedItemStyle, style]}>
80
92
  {leading != null && (
81
- <View style={resolvedLeadingItemContainerStyle}>
93
+ <ListItemAccessory
94
+ style={resolvedLeadingItemContainerStyle}
95
+ foreground={color?.onColor}
96
+ iconStyle={resolvedIconStyle}
97
+ >
82
98
  {leading}
83
- </View>
99
+ </ListItemAccessory>
84
100
  )}
85
101
  <View style={resolvedContentStyle}>
86
- <ThemedText style={resolvedLabelStyle}>{label}</ThemedText>
87
- <ThemedText style={resolvedValueStyle}>{value}</ThemedText>
102
+ <ListItemTextContent
103
+ title={title}
104
+ subtitle={subtitle}
105
+ content={content}
106
+ contentOrder={contentOrder}
107
+ titleStyle={resolvedTitleStyle}
108
+ subtitleStyle={resolvedSubtitleStyle}
109
+ />
88
110
  </View>
89
111
  {trailing != null && (
90
- <View style={resolvedTrailingItemContainerStyle}>
112
+ <ListItemAccessory
113
+ style={resolvedTrailingItemContainerStyle}
114
+ foreground={color?.onColor}
115
+ iconStyle={resolvedIconStyle}
116
+ >
91
117
  {trailing}
92
- </View>
118
+ </ListItemAccessory>
93
119
  )}
94
120
  </View>
95
121
  )
@@ -0,0 +1,34 @@
1
+ import type { ReactNode } from 'react'
2
+ import {
3
+ View,
4
+ type StyleProp,
5
+ type ViewStyle
6
+ } from 'react-native'
7
+
8
+ import type { HexColorToken } from '@helpwave/hightide-design/primitive-tokens'
9
+
10
+ import { ContentThemeOverrideProvider } from '../../global-contexts/content-theme/ContentThemeProvider'
11
+ import type { IconStyle } from '../../icons'
12
+
13
+ export type ListItemAccessoryProps = {
14
+ children: ReactNode,
15
+ style?: StyleProp<ViewStyle>,
16
+ foreground?: HexColorToken,
17
+ iconStyle: IconStyle,
18
+ }
19
+
20
+ export const ListItemAccessory = ({
21
+ children,
22
+ style,
23
+ foreground,
24
+ iconStyle,
25
+ }: ListItemAccessoryProps) => (
26
+ <View style={style}>
27
+ <ContentThemeOverrideProvider
28
+ foreground={foreground}
29
+ iconStyle={iconStyle}
30
+ >
31
+ {children}
32
+ </ContentThemeOverrideProvider>
33
+ </View>
34
+ )
@@ -0,0 +1,53 @@
1
+ import type { ReactNode } from 'react'
2
+ import { Fragment } from 'react'
3
+ import type { StyleProp, TextStyle } from 'react-native'
4
+
5
+ import { ThemedText } from '../visualization-and-display/ThemedText'
6
+
7
+ export type ListItemContentOrder = 'titleFirst' | 'subtitleFirst'
8
+
9
+ export type ListItemTextContentProps = {
10
+ title?: string,
11
+ subtitle?: string,
12
+ content?: ReactNode,
13
+ contentOrder?: ListItemContentOrder,
14
+ }
15
+
16
+ export const ListItemTextContent = ({
17
+ title,
18
+ subtitle,
19
+ content,
20
+ contentOrder = 'titleFirst',
21
+ titleStyle,
22
+ subtitleStyle,
23
+ }: ListItemTextContentProps & {
24
+ titleStyle?: StyleProp<TextStyle>,
25
+ subtitleStyle?: StyleProp<TextStyle>,
26
+ }) => {
27
+ if (content != null) {
28
+ return <>{content}</>
29
+ }
30
+
31
+ const titleNode = title != null
32
+ ? <ThemedText style={titleStyle}>{title}</ThemedText>
33
+ : null
34
+ const subtitleNode = subtitle != null
35
+ ? <ThemedText style={subtitleStyle}>{subtitle}</ThemedText>
36
+ : null
37
+
38
+ if (contentOrder === 'subtitleFirst') {
39
+ return (
40
+ <Fragment>
41
+ {subtitleNode}
42
+ {titleNode}
43
+ </Fragment>
44
+ )
45
+ }
46
+
47
+ return (
48
+ <Fragment>
49
+ {titleNode}
50
+ {subtitleNode}
51
+ </Fragment>
52
+ )
53
+ }
@@ -14,22 +14,34 @@ import type { ColorPairToken } from '@helpwave/hightide-design/theme-tokens'
14
14
 
15
15
  import { HightideIconRegistry } from '../../icons/HightideIconRegistry'
16
16
  import { ThemedIcon } from '../visualization-and-display/ThemedIcon'
17
- import { ThemedText } from '../visualization-and-display/ThemedText'
17
+ import { useDebugContext } from '../../global-contexts/debug'
18
18
  import { useTheme } from '../../global-contexts/theme/ThemeContext'
19
19
  import type {
20
+ ListNavigationItemDescriptionStyle,
20
21
  ListNavigationItemState,
21
22
  ListNavigationItemStyle,
22
23
  ListNavigationItemTitleStyle
23
24
  } from '../../theme/types/components/listItem'
24
25
  import type { StyleOverwrite } from '../../theme/types/resolver'
26
+ import { createHitBoxOverlayStyle } from '../../utils/hitBoxOverlay'
27
+ import { useMinimumTouchTargetHitSlop } from '../../utils/minimumTouchTargetHitSlop'
28
+ import { ListItemAccessory } from './ListItemAccessory'
29
+ import {
30
+ ListItemTextContent,
31
+ type ListItemContentOrder
32
+ } from './ListItemTextContent'
25
33
 
26
34
  export type ListNavigationItemProps = Omit<PressableProps, 'children' | 'style'> & {
27
- label: string,
35
+ title?: string,
36
+ subtitle?: string,
37
+ content?: ReactNode,
38
+ contentOrder?: ListItemContentOrder,
28
39
  leading?: ReactNode,
29
40
  color?: ColorPairToken,
30
41
  style?: StyleProp<ViewStyle>,
31
42
  itemStyle?: StyleOverwrite<ListNavigationItemState, ListNavigationItemStyle>,
32
- labelStyle?: StyleOverwrite<ListNavigationItemState, ListNavigationItemTitleStyle>,
43
+ titleStyle?: StyleOverwrite<ListNavigationItemState, ListNavigationItemTitleStyle>,
44
+ subtitleStyle?: StyleOverwrite<ListNavigationItemState, ListNavigationItemDescriptionStyle>,
33
45
  }
34
46
 
35
47
  type PressableInteraction = {
@@ -40,16 +52,28 @@ type PressableInteraction = {
40
52
  }
41
53
 
42
54
  export const ListNavigationItem = ({
43
- label,
55
+ title,
56
+ subtitle,
57
+ content,
58
+ contentOrder = 'titleFirst',
44
59
  leading,
45
60
  color,
46
61
  disabled,
47
62
  style,
48
63
  itemStyle,
49
- labelStyle,
64
+ titleStyle,
65
+ subtitleStyle,
66
+ hitSlop: providedHitSlop,
67
+ onLayout: providedOnLayout,
50
68
  ...props
51
69
  }: ListNavigationItemProps) => {
52
70
  const { theme } = useTheme()
71
+ const { hitBox } = useDebugContext()
72
+ const { hitSlop, onLayout } = useMinimumTouchTargetHitSlop({
73
+ touchTargetSize: theme.semantics.touchTargetSize({}),
74
+ hitSlop: providedHitSlop,
75
+ onLayout: providedOnLayout,
76
+ })
53
77
 
54
78
  const resolveState = (interaction: PressableInteraction): ListNavigationItemState => ({
55
79
  color,
@@ -64,6 +88,8 @@ export const ListNavigationItem = ({
64
88
  <Pressable
65
89
  {...props}
66
90
  disabled={disabled}
91
+ hitSlop={hitSlop}
92
+ onLayout={onLayout}
67
93
  style={(pressableState) => {
68
94
  const state = resolveState(pressableState as PressableInteraction)
69
95
  return [theme.components.listItem.navigation.container(state, itemStyle), style]
@@ -74,26 +100,44 @@ export const ListNavigationItem = ({
74
100
  const resolvedLeadingItemContainerStyle = theme.components.listItem.navigation.leadingItemContainer(state)
75
101
  const resolvedContentStyle = theme.components.listItem.navigation.content(state)
76
102
  const resolvedTrailingItemContainerStyle = theme.components.listItem.navigation.trailingItemContainer(state)
77
- const resolvedLabelStyle = theme.components.listItem.navigation.titleText(state, labelStyle)
78
- const resolvedIcon = theme.components.listItem.navigation.icon(state)
103
+ const resolvedTitleStyle = theme.components.listItem.navigation.titleText(state, titleStyle)
104
+ const resolvedSubtitleStyle = theme.components.listItem.navigation.descriptionText(state, subtitleStyle)
105
+ const resolvedIconStyle = theme.components.listItem.navigation.icon(state)
79
106
 
80
107
  return (
81
108
  <Fragment>
109
+ {hitBox.isVisualizing && (
110
+ <View
111
+ pointerEvents="none"
112
+ style={createHitBoxOverlayStyle(hitSlop, hitBox.color)}
113
+ />
114
+ )}
82
115
  {leading != null && (
83
- <View style={resolvedLeadingItemContainerStyle}>
116
+ <ListItemAccessory
117
+ style={resolvedLeadingItemContainerStyle}
118
+ foreground={color?.onColor}
119
+ iconStyle={resolvedIconStyle}
120
+ >
84
121
  {leading}
85
- </View>
122
+ </ListItemAccessory>
86
123
  )}
87
124
  <View style={resolvedContentStyle}>
88
- <ThemedText style={resolvedLabelStyle}>{label}</ThemedText>
89
- </View>
90
- <View style={resolvedTrailingItemContainerStyle}>
91
- <ThemedIcon
92
- icon={HightideIconRegistry.ChevronRight}
93
- size={resolvedIcon.size}
94
- color={resolvedIcon.color}
125
+ <ListItemTextContent
126
+ title={title}
127
+ subtitle={subtitle}
128
+ content={content}
129
+ contentOrder={contentOrder}
130
+ titleStyle={resolvedTitleStyle}
131
+ subtitleStyle={resolvedSubtitleStyle}
95
132
  />
96
133
  </View>
134
+ <ListItemAccessory
135
+ style={resolvedTrailingItemContainerStyle}
136
+ foreground={color?.onColor}
137
+ iconStyle={resolvedIconStyle}
138
+ >
139
+ <ThemedIcon icon={HightideIconRegistry.ChevronRight} />
140
+ </ListItemAccessory>
97
141
  </Fragment>
98
142
  )
99
143
  }}
@@ -1,3 +1,5 @@
1
1
  export * from './ListActionItem'
2
2
  export * from './ListItem'
3
+ export * from './ListItemAccessory'
4
+ export * from './ListItemTextContent'
3
5
  export * from './ListNavigationItem'
@@ -10,7 +10,7 @@ import {
10
10
  import type { ColorPairToken } from '@helpwave/hightide-design/theme-tokens'
11
11
  import type { ComponentSize, ButtonVariant } from '@helpwave/hightide-design/semantic-token-resolvers'
12
12
 
13
- import { ContentThemeProvider } from '../../global-contexts/content-theme/ContentThemeProvider'
13
+ import { ContentThemeOverrideProvider } from '../../global-contexts/content-theme/ContentThemeProvider'
14
14
  import { useDebugContext } from '../../global-contexts/debug'
15
15
  import { useTheme } from '../../global-contexts/theme/ThemeContext'
16
16
  import type { IconComponent } from '../../icons/types'
@@ -20,7 +20,6 @@ import type {
20
20
  ButtonTextStyle
21
21
  } from '../../theme/types/components/button'
22
22
  import type { StyleOverwrite } from '../../theme/types/resolver'
23
- import type { Color } from '../../theme/types/color'
24
23
  import { createHitBoxOverlayStyle } from '../../utils/hitBoxOverlay'
25
24
  import { useMinimumTouchTargetHitSlop } from '../../utils/minimumTouchTargetHitSlop'
26
25
  import { ThemedIcon } from '../visualization-and-display/ThemedIcon'
@@ -122,26 +121,18 @@ export const Button = forwardRef<React.ComponentRef<typeof Pressable>, ButtonPro
122
121
  pointerEvents="none"
123
122
  style={theme.components.button.stateLayer(state, stateLayerStyle)}
124
123
  />
125
- <ContentThemeProvider
126
- foregroundColor={resolvedText.color as Color}
124
+ <ContentThemeOverrideProvider
127
125
  textStyle={resolvedText}
126
+ iconStyle={resolvedIcon}
128
127
  >
129
128
  {leadingIcon !== undefined && (
130
- <ThemedIcon
131
- icon={leadingIcon}
132
- size={resolvedIcon.size}
133
- strokeWidth={resolvedIcon.strokeWidth}
134
- />
129
+ <ThemedIcon icon={leadingIcon} />
135
130
  )}
136
131
  <ThemedText>{children}</ThemedText>
137
132
  {trailingIcon !== undefined && (
138
- <ThemedIcon
139
- icon={trailingIcon}
140
- size={resolvedIcon.size}
141
- strokeWidth={resolvedIcon.strokeWidth}
142
- />
133
+ <ThemedIcon icon={trailingIcon} />
143
134
  )}
144
- </ContentThemeProvider>
135
+ </ContentThemeOverrideProvider>
145
136
  </Fragment>
146
137
  )
147
138
  }}
@@ -12,7 +12,7 @@ import type {
12
12
  IconButtonVariant
13
13
  } from '@helpwave/hightide-design/semantic-token-resolvers'
14
14
 
15
- import { ContentThemeProvider } from '../../global-contexts/content-theme/ContentThemeProvider'
15
+ import { ContentThemeOverrideProvider } from '../../global-contexts/content-theme/ContentThemeProvider'
16
16
  import { useDebugContext } from '../../global-contexts/debug'
17
17
  import { useTheme } from '../../global-contexts/theme/ThemeContext'
18
18
  import type {
@@ -105,7 +105,6 @@ export const IconButton = forwardRef<React.ComponentRef<typeof Pressable>, IconB
105
105
  {(pressableState) => {
106
106
  const state = resolveState(pressableState as PressableInteraction)
107
107
  const resolvedIcon = theme.components.iconButton.icon(state)
108
- const resolvedText = theme.components.iconButton.text(state)
109
108
 
110
109
  return (
111
110
  <Fragment>
@@ -119,12 +118,11 @@ export const IconButton = forwardRef<React.ComponentRef<typeof Pressable>, IconB
119
118
  pointerEvents="none"
120
119
  style={theme.components.iconButton.stateLayer(state, stateLayerStyle)}
121
120
  />
122
- <ContentThemeProvider
123
- foregroundColor={resolvedIcon.color ?? theme.colors.primary.color}
124
- textStyle={resolvedText}
121
+ <ContentThemeOverrideProvider
122
+ iconStyle={resolvedIcon}
125
123
  >
126
- <ThemedIcon icon={IconComponent} size={resolvedIcon.size} strokeWidth={resolvedIcon.strokeWidth}/>
127
- </ContentThemeProvider>
124
+ <ThemedIcon icon={IconComponent} />
125
+ </ContentThemeOverrideProvider>
128
126
  </Fragment>
129
127
  )
130
128
  }}
@@ -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
@@ -224,19 +252,17 @@ export const MultiSelect = ({
224
252
 
225
253
  return (
226
254
  <ListActionItem
227
- label={item.label ?? item.id}
255
+ title={item.label ?? item.id}
228
256
  color={optionColor}
229
257
  disabled={item.disabled}
230
258
  onPress={() => multiSelect.toggleSelection(item.id)}
231
259
  leading={(
232
260
  <View style={multiSelectTheme.checkbox(optionState)}>
233
- {checkboxIcon.visible && (
234
- <ThemedIcon
235
- icon={HightideIconRegistry.Check}
236
- size="sm"
237
- color={checkboxIcon.color}
238
- />
239
- )}
261
+ <ThemedIcon
262
+ icon={HightideIconRegistry.Check}
263
+ size="sm"
264
+ color={checkboxIcon.color}
265
+ />
240
266
  </View>
241
267
  )}
242
268
  />
@@ -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>
@@ -212,7 +216,7 @@ export const Select = ({
212
216
 
213
217
  return (
214
218
  <ListActionItem
215
- label={item.label ?? item.id}
219
+ title={item.label ?? item.id}
216
220
  color={optionColor}
217
221
  disabled={item.disabled}
218
222
  onPress={() => select.selectValue(item.id)}
@@ -12,10 +12,11 @@ import type {
12
12
  } from '@helpwave/hightide-design/semantic-token-resolvers'
13
13
  import type { ColorPairToken } from '@helpwave/hightide-design/theme-tokens'
14
14
 
15
- import { ContentThemeProvider } from '../../global-contexts/content-theme/ContentThemeProvider'
15
+ import { ContentThemeOverrideProvider } from '../../global-contexts/content-theme/ContentThemeProvider'
16
16
  import { useDebugContext } from '../../global-contexts/debug'
17
17
  import { useTheme } from '../../global-contexts/theme/ThemeContext'
18
18
  import type {
19
+ ThemedPressableIconStyle,
19
20
  ThemedPressableState,
20
21
  ThemedPressableStyle,
21
22
  ThemedPressableTextStyle
@@ -40,6 +41,7 @@ export type ThemedPressableProps = PressableProps & {
40
41
  hasAdditionalHorizontalPadding?: boolean,
41
42
  containerStyle?: StyleOverwrite<ThemedPressableState, ThemedPressableStyle>,
42
43
  stateLayerStyle?: StyleOverwrite<ThemedPressableState, ThemedPressableStyle>,
44
+ iconStyle?: StyleOverwrite<ThemedPressableState, ThemedPressableIconStyle>,
43
45
  textStyle?: StyleOverwrite<ThemedPressableState, ThemedPressableTextStyle>,
44
46
  }
45
47
 
@@ -61,6 +63,7 @@ export const ThemedPressable = forwardRef<React.ComponentRef<typeof Pressable>,
61
63
  style,
62
64
  containerStyle,
63
65
  stateLayerStyle,
66
+ iconStyle,
64
67
  textStyle,
65
68
  hitSlop: providedHitSlop,
66
69
  onLayout: providedOnLayout,
@@ -106,6 +109,7 @@ export const ThemedPressable = forwardRef<React.ComponentRef<typeof Pressable>,
106
109
  {(pressableState) => {
107
110
  const state = resolveState(pressableState as PressableInteraction)
108
111
  const resolvedText = theme.components.themedPressable.text(state, textStyle)
112
+ const resolvedIcon = theme.components.themedPressable.icon(state, iconStyle)
109
113
  const resolvedChildren = typeof children === 'function'
110
114
  ? children(pressableState)
111
115
  : children
@@ -122,12 +126,13 @@ export const ThemedPressable = forwardRef<React.ComponentRef<typeof Pressable>,
122
126
  pointerEvents="none"
123
127
  style={theme.components.themedPressable.stateLayer(state, stateLayerStyle)}
124
128
  />
125
- <ContentThemeProvider
126
- foregroundColor={resolvedText.color ?? theme.colors.surface.onColor}
129
+ <ContentThemeOverrideProvider
130
+ foreground={resolvedText.color}
127
131
  textStyle={resolvedText}
132
+ iconStyle={resolvedIcon}
128
133
  >
129
134
  {resolvedChildren}
130
- </ContentThemeProvider>
135
+ </ContentThemeOverrideProvider>
131
136
  </Fragment>
132
137
  )
133
138
  }}