@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.
- package/package.json +2 -2
- package/src/components/chat/ChatAttachmentMessageBubble.tsx +3 -0
- package/src/components/chat/ChatConversationRow.tsx +127 -27
- package/src/components/chat/ChatQuickReplyChip.tsx +7 -1
- package/src/components/chat/ChatThreadHeader.tsx +88 -48
- package/src/components/list/ListActionItem.tsx +60 -11
- package/src/components/list/ListItem.tsx +47 -21
- package/src/components/list/ListItemAccessory.tsx +34 -0
- package/src/components/list/ListItemTextContent.tsx +53 -0
- package/src/components/list/ListNavigationItem.tsx +60 -16
- package/src/components/list/index.ts +2 -0
- package/src/components/user-interaction/Button.tsx +6 -15
- package/src/components/user-interaction/IconButton.tsx +5 -7
- package/src/components/user-interaction/MultiSelect.tsx +72 -46
- package/src/components/user-interaction/Select.tsx +5 -1
- package/src/components/user-interaction/ThemedPressable.tsx +9 -4
- package/src/components/visualization-and-display/Chip.tsx +12 -5
- package/src/components/visualization-and-display/ThemedIcon.tsx +12 -8
- package/src/components/visualization-and-display/ThemedText.tsx +6 -2
- package/src/global-contexts/HightideProvider.tsx +9 -17
- package/src/global-contexts/content-theme/ContentThemeContext.ts +5 -2
- package/src/global-contexts/content-theme/ContentThemeProvider.tsx +64 -10
- package/src/global-contexts/theme/ThemeProvider.tsx +9 -4
- package/src/theme/adapters/index.ts +1 -4
- package/src/theme/adapters/style-adapter-utils.ts +433 -0
- package/src/theme/resolvers/avatar.ts +9 -10
- package/src/theme/resolvers/button.ts +6 -7
- package/src/theme/resolvers/card.ts +3 -2
- package/src/theme/resolvers/chat/attachment-message-bubble.ts +25 -37
- package/src/theme/resolvers/chat/conversation-list.ts +5 -4
- package/src/theme/resolvers/chat/conversation-row.ts +114 -14
- package/src/theme/resolvers/chat/date-divider.ts +4 -4
- package/src/theme/resolvers/chat/message-bubble.ts +9 -10
- package/src/theme/resolvers/chat/message-composer.ts +5 -6
- package/src/theme/resolvers/chat/message-list.ts +3 -2
- package/src/theme/resolvers/chat/quick-reply-chip.ts +12 -20
- package/src/theme/resolvers/chat/system-line.ts +5 -6
- package/src/theme/resolvers/chat/thread-header.ts +60 -12
- package/src/theme/resolvers/checkbox.ts +6 -6
- package/src/theme/resolvers/chip.ts +9 -4
- package/src/theme/resolvers/divider.ts +3 -3
- package/src/theme/resolvers/iconButton.ts +5 -10
- package/src/theme/resolvers/input.ts +12 -7
- package/src/theme/resolvers/listItem.ts +19 -16
- package/src/theme/resolvers/multiSelect.ts +23 -16
- package/src/theme/resolvers/searchBar.ts +14 -7
- package/src/theme/resolvers/select.ts +22 -15
- package/src/theme/resolvers/switch.ts +5 -4
- package/src/theme/resolvers/themedPressable.ts +10 -5
- package/src/theme/themes/createHightideTheme.ts +13 -4
- package/src/theme/types/components/chat.ts +63 -3
- package/src/theme/types/components/chip.ts +4 -0
- package/src/theme/types/components/iconButton.ts +1 -7
- package/src/theme/types/components/listItem.ts +5 -0
- package/src/theme/types/components/multiSelect.ts +1 -0
- package/src/theme/types/components/select.ts +1 -0
- package/src/theme/types/components/themedPressable.ts +4 -0
- package/src/theme/types/layout.ts +13 -7
- package/src/theme/types/semantics.ts +2 -1
- package/src/theme/types/theme.ts +40 -12
- package/src/theme/types/typography.ts +9 -4
- package/src/theme/adapters/container-adapter.ts +0 -323
- package/src/theme/adapters/defined.ts +0 -11
- package/src/theme/adapters/icon-style-adapter.ts +0 -10
- 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.
|
|
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.
|
|
46
|
+
"@helpwave/hightide-design": "0.3.0",
|
|
47
47
|
"@helpwave/hightide-utils": "0.1.0"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
@@ -205,6 +205,9 @@ export const ChatAttachmentMessageBubble = ({
|
|
|
205
205
|
textStyle={(_, pressableState) => (
|
|
206
206
|
contentResolvers.text(pressableState)
|
|
207
207
|
)}
|
|
208
|
+
iconStyle={(_, pressableState) => (
|
|
209
|
+
contentResolvers.icon(pressableState)
|
|
210
|
+
)}
|
|
208
211
|
>
|
|
209
212
|
<View style={resolvedFileIconContainerStyle}>
|
|
210
213
|
{icon ?? (
|
|
@@ -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<
|
|
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
|
|
92
|
-
() => theme.components.chat.conversationRow.sentIndicator(staticState)
|
|
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={
|
|
179
|
+
style={style}
|
|
180
|
+
containerStyle={(_, pressableState) => {
|
|
101
181
|
const state = resolveState(pressableState as PressableInteraction)
|
|
102
|
-
return
|
|
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
|
-
{
|
|
114
|
-
<View style={
|
|
115
|
-
<View style={
|
|
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={
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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>
|
|
@@ -13,6 +13,7 @@ import { ThemedText } from '../visualization-and-display/ThemedText'
|
|
|
13
13
|
import type {
|
|
14
14
|
ChatQuickReplyChipState,
|
|
15
15
|
PressableContainerStyle,
|
|
16
|
+
PressableIconStyle,
|
|
16
17
|
PressableState,
|
|
17
18
|
PressableTextStyle
|
|
18
19
|
} from '../../theme/types/components/chat'
|
|
@@ -20,13 +21,14 @@ import type { StyleOverwrite } from '../../theme/types/resolver'
|
|
|
20
21
|
|
|
21
22
|
export type ChatQuickReplyChipProps = Omit<
|
|
22
23
|
ThemedPressableProps,
|
|
23
|
-
'children' | 'containerStyle' | 'textStyle' | 'stateLayerStyle'
|
|
24
|
+
'children' | 'containerStyle' | 'textStyle' | 'stateLayerStyle' | 'iconStyle'
|
|
24
25
|
> & {
|
|
25
26
|
isActive?: boolean,
|
|
26
27
|
children?: ReactNode,
|
|
27
28
|
style?: StyleProp<ViewStyle>,
|
|
28
29
|
containerStyle?: StyleOverwrite<PressableState, PressableContainerStyle>,
|
|
29
30
|
textStyle?: StyleOverwrite<PressableState, PressableTextStyle>,
|
|
31
|
+
iconStyle?: StyleOverwrite<PressableState, PressableIconStyle>,
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
export const ChatQuickReplyChip = ({
|
|
@@ -36,6 +38,7 @@ export const ChatQuickReplyChip = ({
|
|
|
36
38
|
style,
|
|
37
39
|
containerStyle,
|
|
38
40
|
textStyle,
|
|
41
|
+
iconStyle,
|
|
39
42
|
...props
|
|
40
43
|
}: ChatQuickReplyChipProps) => {
|
|
41
44
|
const { theme } = useTheme()
|
|
@@ -59,6 +62,9 @@ export const ChatQuickReplyChip = ({
|
|
|
59
62
|
textStyle={(_, pressableState) => (
|
|
60
63
|
pressableResolvers.text(pressableState, textStyle)
|
|
61
64
|
)}
|
|
65
|
+
iconStyle={(_, pressableState) => (
|
|
66
|
+
pressableResolvers.icon(pressableState, iconStyle)
|
|
67
|
+
)}
|
|
62
68
|
>
|
|
63
69
|
{typeof children === 'string' || typeof children === 'number' ? (
|
|
64
70
|
<ThemedText>{children}</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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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(
|
|
82
|
-
|
|
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
|
-
<
|
|
93
|
-
{
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
...avatarState,
|
|
104
|
-
size: resolvedAvatar.size,
|
|
105
|
-
},
|
|
106
|
-
resolvedAvatar.imageStyle
|
|
120
|
+
stateLayerStyle={(_, pressableState) => (
|
|
121
|
+
pressableResolvers.stateLayer(pressableState)
|
|
107
122
|
)}
|
|
108
|
-
textStyle={(_,
|
|
109
|
-
|
|
110
|
-
...avatarState,
|
|
111
|
-
size: resolvedAvatar.size,
|
|
112
|
-
},
|
|
113
|
-
resolvedAvatar.textStyle
|
|
123
|
+
textStyle={(_, pressableState) => (
|
|
124
|
+
pressableResolvers.text(pressableState)
|
|
114
125
|
)}
|
|
115
|
-
iconStyle={(_,
|
|
116
|
-
|
|
117
|
-
...avatarState,
|
|
118
|
-
size: resolvedAvatar.size,
|
|
119
|
-
},
|
|
120
|
-
resolvedAvatar.iconStyle
|
|
126
|
+
iconStyle={(_, pressableState) => (
|
|
127
|
+
pressableResolvers.icon(pressableState)
|
|
121
128
|
)}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
134
|
-
)
|
|
135
|
-
|
|
136
|
-
|
|
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}
|
|
@@ -12,23 +12,35 @@ import {
|
|
|
12
12
|
|
|
13
13
|
import type { ColorPairToken } from '@helpwave/hightide-design/theme-tokens'
|
|
14
14
|
|
|
15
|
+
import { useDebugContext } from '../../global-contexts/debug'
|
|
15
16
|
import { useTheme } from '../../global-contexts/theme/ThemeContext'
|
|
16
|
-
import { ThemedText } from '../visualization-and-display/ThemedText'
|
|
17
17
|
import type {
|
|
18
|
+
ListActionItemDescriptionStyle,
|
|
18
19
|
ListActionItemState,
|
|
19
20
|
ListActionItemStyle,
|
|
20
21
|
ListActionItemTitleStyle
|
|
21
22
|
} from '../../theme/types/components/listItem'
|
|
22
23
|
import type { StyleOverwrite } from '../../theme/types/resolver'
|
|
24
|
+
import { createHitBoxOverlayStyle } from '../../utils/hitBoxOverlay'
|
|
25
|
+
import { useMinimumTouchTargetHitSlop } from '../../utils/minimumTouchTargetHitSlop'
|
|
26
|
+
import { ListItemAccessory } from './ListItemAccessory'
|
|
27
|
+
import {
|
|
28
|
+
ListItemTextContent,
|
|
29
|
+
type ListItemContentOrder
|
|
30
|
+
} from './ListItemTextContent'
|
|
23
31
|
|
|
24
32
|
export type ListActionItemProps = Omit<PressableProps, 'children' | 'style'> & {
|
|
25
|
-
|
|
33
|
+
title?: string,
|
|
34
|
+
subtitle?: string,
|
|
35
|
+
content?: ReactNode,
|
|
36
|
+
contentOrder?: ListItemContentOrder,
|
|
26
37
|
leading?: ReactNode,
|
|
27
38
|
trailing?: ReactNode,
|
|
28
39
|
color?: ColorPairToken,
|
|
29
40
|
style?: StyleProp<ViewStyle>,
|
|
30
41
|
itemStyle?: StyleOverwrite<ListActionItemState, ListActionItemStyle>,
|
|
31
|
-
|
|
42
|
+
titleStyle?: StyleOverwrite<ListActionItemState, ListActionItemTitleStyle>,
|
|
43
|
+
subtitleStyle?: StyleOverwrite<ListActionItemState, ListActionItemDescriptionStyle>,
|
|
32
44
|
}
|
|
33
45
|
|
|
34
46
|
type PressableInteraction = {
|
|
@@ -39,17 +51,29 @@ type PressableInteraction = {
|
|
|
39
51
|
}
|
|
40
52
|
|
|
41
53
|
export const ListActionItem = ({
|
|
42
|
-
|
|
54
|
+
title,
|
|
55
|
+
subtitle,
|
|
56
|
+
content,
|
|
57
|
+
contentOrder = 'titleFirst',
|
|
43
58
|
leading,
|
|
44
59
|
trailing,
|
|
45
60
|
color,
|
|
46
61
|
disabled,
|
|
47
62
|
style,
|
|
48
63
|
itemStyle,
|
|
49
|
-
|
|
64
|
+
titleStyle,
|
|
65
|
+
subtitleStyle,
|
|
66
|
+
hitSlop: providedHitSlop,
|
|
67
|
+
onLayout: providedOnLayout,
|
|
50
68
|
...props
|
|
51
69
|
}: ListActionItemProps) => {
|
|
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): ListActionItemState => ({
|
|
55
79
|
color,
|
|
@@ -64,6 +88,8 @@ export const ListActionItem = ({
|
|
|
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.action.container(state, itemStyle), style]
|
|
@@ -74,22 +100,45 @@ export const ListActionItem = ({
|
|
|
74
100
|
const resolvedLeadingItemContainerStyle = theme.components.listItem.action.leadingItemContainer(state)
|
|
75
101
|
const resolvedContentStyle = theme.components.listItem.action.content(state)
|
|
76
102
|
const resolvedTrailingItemContainerStyle = theme.components.listItem.action.trailingItemContainer(state)
|
|
77
|
-
const
|
|
103
|
+
const resolvedTitleStyle = theme.components.listItem.action.titleText(state, titleStyle)
|
|
104
|
+
const resolvedSubtitleStyle = theme.components.listItem.action.descriptionText(state, subtitleStyle)
|
|
105
|
+
const resolvedIconStyle = theme.components.listItem.action.icon(state)
|
|
78
106
|
|
|
79
107
|
return (
|
|
80
108
|
<Fragment>
|
|
109
|
+
{hitBox.isVisualizing && (
|
|
110
|
+
<View
|
|
111
|
+
pointerEvents="none"
|
|
112
|
+
style={createHitBoxOverlayStyle(hitSlop, hitBox.color)}
|
|
113
|
+
/>
|
|
114
|
+
)}
|
|
81
115
|
{leading != null && (
|
|
82
|
-
<
|
|
116
|
+
<ListItemAccessory
|
|
117
|
+
style={resolvedLeadingItemContainerStyle}
|
|
118
|
+
foreground={color?.onColor}
|
|
119
|
+
iconStyle={resolvedIconStyle}
|
|
120
|
+
>
|
|
83
121
|
{leading}
|
|
84
|
-
</
|
|
122
|
+
</ListItemAccessory>
|
|
85
123
|
)}
|
|
86
124
|
<View style={resolvedContentStyle}>
|
|
87
|
-
<
|
|
125
|
+
<ListItemTextContent
|
|
126
|
+
title={title}
|
|
127
|
+
subtitle={subtitle}
|
|
128
|
+
content={content}
|
|
129
|
+
contentOrder={contentOrder}
|
|
130
|
+
titleStyle={resolvedTitleStyle}
|
|
131
|
+
subtitleStyle={resolvedSubtitleStyle}
|
|
132
|
+
/>
|
|
88
133
|
</View>
|
|
89
134
|
{trailing != null && (
|
|
90
|
-
<
|
|
135
|
+
<ListItemAccessory
|
|
136
|
+
style={resolvedTrailingItemContainerStyle}
|
|
137
|
+
foreground={color?.onColor}
|
|
138
|
+
iconStyle={resolvedIconStyle}
|
|
139
|
+
>
|
|
91
140
|
{trailing}
|
|
92
|
-
</
|
|
141
|
+
</ListItemAccessory>
|
|
93
142
|
)}
|
|
94
143
|
</Fragment>
|
|
95
144
|
)
|