@helpwave/hightide-native 0.0.1 → 0.0.5
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/README.md +56 -0
- package/package.json +3 -3
- package/src/components/chat/ChatAttachmentCard.tsx +110 -0
- package/src/components/chat/ChatConversationList.tsx +72 -0
- package/src/components/chat/ChatConversationRow.tsx +153 -0
- package/src/components/chat/ChatDateDivider.tsx +55 -0
- package/src/components/chat/ChatMessageBubble.tsx +112 -0
- package/src/components/chat/ChatMessageCard.tsx +118 -0
- package/src/components/chat/ChatMessageComposer.tsx +115 -0
- package/src/components/chat/ChatMessageList.tsx +66 -0
- package/src/components/chat/ChatQuickReplyChip.tsx +72 -0
- package/src/components/chat/ChatSystemLine.tsx +68 -0
- package/src/components/chat/ChatThreadHeader.tsx +90 -0
- package/src/components/chat/index.ts +11 -0
- package/src/components/index.ts +4 -7
- package/src/components/menu/Menu.tsx +61 -0
- package/src/components/menu/MenuActionItem.tsx +90 -0
- package/src/components/menu/MenuItem.tsx +73 -0
- package/src/components/menu/MenuNavigationItem.tsx +90 -0
- package/src/components/menu/index.ts +4 -0
- package/src/components/user-interaction/Button.tsx +101 -0
- package/src/components/{Checkbox → user-interaction}/Checkbox.tsx +38 -16
- package/src/components/user-interaction/IconButton.tsx +95 -0
- package/src/components/{Input → user-interaction}/Input.tsx +35 -14
- package/src/components/{MultiSelect → user-interaction}/MultiSelect.tsx +47 -15
- package/src/components/{Select → user-interaction}/Select.tsx +44 -12
- package/src/components/user-interaction/index.ts +6 -0
- package/src/components/{Chip → visualization-and-display}/Chip.tsx +36 -20
- package/src/components/visualization-and-display/Icon.tsx +27 -0
- package/src/components/{Chip → visualization-and-display}/index.ts +1 -0
- package/src/global-contexts/HightideContext.ts +4 -1
- package/src/global-contexts/HightideProvider.tsx +22 -10
- package/src/global-contexts/hightide-config/HightideConfigUtils.ts +15 -12
- package/src/global-contexts/localization/LocalizationProvider.tsx +4 -2
- package/src/global-contexts/localization/forward-exports.ts +1 -0
- package/src/global-contexts/theme/ThemeContext.ts +11 -6
- package/src/global-contexts/theme/ThemeProvider.tsx +35 -38
- package/src/hooks/useMultiSelect.ts +1 -0
- package/src/hooks/useNativeKeyValueStore.ts +7 -1
- package/src/hooks/useSelect.ts +1 -0
- package/src/theme/resolvers/button.ts +38 -23
- package/src/theme/resolvers/chat.ts +418 -0
- package/src/theme/resolvers/checkbox.ts +26 -21
- package/src/theme/resolvers/chip.ts +32 -22
- package/src/theme/resolvers/coloring.ts +21 -17
- package/src/theme/resolvers/iconButton.ts +28 -18
- package/src/theme/resolvers/index.ts +2 -0
- package/src/theme/resolvers/input.ts +28 -19
- package/src/theme/resolvers/menu.ts +120 -0
- package/src/theme/resolvers/multiSelect.ts +39 -34
- package/src/theme/resolvers/select.ts +35 -30
- package/src/theme/themes/createTheme.ts +44 -15
- package/src/theme/themes/nativeThemes.ts +7 -6
- package/src/theme/types/color.ts +60 -0
- package/src/theme/types/components/button.ts +30 -0
- package/src/theme/types/components/chat.ts +198 -0
- package/src/theme/types/components/checkbox.ts +33 -0
- package/src/theme/types/components/chip.ts +30 -0
- package/src/theme/types/components/hightide.ts +42 -0
- package/src/theme/types/components/iconButton.ts +30 -0
- package/src/theme/types/components/index.ts +10 -0
- package/src/theme/types/components/input.ts +19 -0
- package/src/theme/types/components/menu.ts +57 -0
- package/src/theme/types/components/multiSelect.ts +47 -0
- package/src/theme/types/components/select.ts +47 -0
- package/src/theme/types/decoration.ts +11 -0
- package/src/theme/types/index.ts +4 -7
- package/src/theme/types/layout.ts +46 -0
- package/src/theme/types/resolver.ts +43 -1
- package/src/theme/types/theme.ts +14 -10
- package/src/theme/types/typography.ts +18 -0
- package/src/components/Button/Button.tsx +0 -101
- package/src/components/Button/index.ts +0 -1
- package/src/components/Checkbox/index.ts +0 -1
- package/src/components/IconButton/IconButton.tsx +0 -89
- package/src/components/IconButton/index.ts +0 -1
- package/src/components/Input/index.ts +0 -1
- package/src/components/MultiSelect/index.ts +0 -1
- package/src/components/Select/index.ts +0 -1
- package/src/icons/Icon.tsx +0 -35
- package/src/icons/index.ts +0 -1
- package/src/theme/types/button.ts +0 -41
- package/src/theme/types/checkbox.ts +0 -36
- package/src/theme/types/chip.ts +0 -41
- package/src/theme/types/components.ts +0 -17
- package/src/theme/types/iconButton.ts +0 -34
- package/src/theme/types/input.ts +0 -25
- package/src/theme/types/multiSelect.ts +0 -65
- package/src/theme/types/select.ts +0 -77
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useMemo,
|
|
3
|
+
type ReactNode
|
|
4
|
+
} from 'react'
|
|
5
|
+
import {
|
|
6
|
+
TextInput,
|
|
7
|
+
View,
|
|
8
|
+
type StyleProp,
|
|
9
|
+
type ViewProps,
|
|
10
|
+
type ViewStyle
|
|
11
|
+
} from 'react-native'
|
|
12
|
+
import { SendHorizontal } from 'lucide-react-native'
|
|
13
|
+
|
|
14
|
+
import { useControlledState } from '@helpwave/hightide-utils/hooks'
|
|
15
|
+
|
|
16
|
+
import { IconButton } from '@/src/components/user-interaction/IconButton'
|
|
17
|
+
import { useTheme } from '@/src/global-contexts/theme/ThemeContext'
|
|
18
|
+
import type {
|
|
19
|
+
ChatMessageComposerInputStyle,
|
|
20
|
+
ChatMessageComposerStyle
|
|
21
|
+
} from '@/src/theme/types/components/chat'
|
|
22
|
+
import type { StyleOverwrite } from '@/src/theme/types/resolver'
|
|
23
|
+
|
|
24
|
+
export type ChatMessageComposerProps = Omit<ViewProps, 'style'> & {
|
|
25
|
+
value?: string,
|
|
26
|
+
initialValue?: string,
|
|
27
|
+
onValueChange?: (value: string) => void,
|
|
28
|
+
onSend: (value: string) => void,
|
|
29
|
+
placeholder?: string,
|
|
30
|
+
sendLabel?: string,
|
|
31
|
+
disabled?: boolean,
|
|
32
|
+
actions?: ReactNode,
|
|
33
|
+
trailing?: ReactNode,
|
|
34
|
+
style?: StyleProp<ViewStyle>,
|
|
35
|
+
composerStyle?: StyleOverwrite<Record<string, never>, ChatMessageComposerStyle>,
|
|
36
|
+
inputStyle?: StyleOverwrite<Record<string, never>, ChatMessageComposerInputStyle>,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const ChatMessageComposer = ({
|
|
40
|
+
value: controlledValue,
|
|
41
|
+
initialValue,
|
|
42
|
+
onValueChange,
|
|
43
|
+
onSend,
|
|
44
|
+
placeholder,
|
|
45
|
+
sendLabel = 'Send',
|
|
46
|
+
disabled = false,
|
|
47
|
+
actions,
|
|
48
|
+
trailing,
|
|
49
|
+
style,
|
|
50
|
+
composerStyle,
|
|
51
|
+
inputStyle,
|
|
52
|
+
...props
|
|
53
|
+
}: ChatMessageComposerProps) => {
|
|
54
|
+
const { theme } = useTheme()
|
|
55
|
+
const [value, setValue] = useControlledState({
|
|
56
|
+
value: controlledValue,
|
|
57
|
+
onValueChange,
|
|
58
|
+
defaultValue: initialValue ?? '',
|
|
59
|
+
})
|
|
60
|
+
const state = useMemo(() => ({}), [])
|
|
61
|
+
|
|
62
|
+
const resolvedComposerStyle = useMemo(
|
|
63
|
+
() => theme.components.chat.messageComposer.container(state, composerStyle),
|
|
64
|
+
[theme, state, composerStyle]
|
|
65
|
+
)
|
|
66
|
+
const resolvedInputStyle = useMemo(
|
|
67
|
+
() => theme.components.chat.messageComposer.input(state, inputStyle),
|
|
68
|
+
[theme, state, inputStyle]
|
|
69
|
+
)
|
|
70
|
+
const placeholderColor = useMemo(
|
|
71
|
+
() => theme.components.chat.messageComposer.placeholderColor(state),
|
|
72
|
+
[theme, state]
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
const send = () => {
|
|
76
|
+
const trimmed = (value ?? '').trim()
|
|
77
|
+
if (!trimmed || disabled) {
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
onSend(trimmed)
|
|
81
|
+
setValue('')
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<View {...props} style={[resolvedComposerStyle, style]}>
|
|
86
|
+
{actions != null && (
|
|
87
|
+
<View style={{ flexDirection: 'row', alignItems: 'flex-start' }}>
|
|
88
|
+
{actions}
|
|
89
|
+
</View>
|
|
90
|
+
)}
|
|
91
|
+
<TextInput
|
|
92
|
+
value={value ?? ''}
|
|
93
|
+
onChangeText={setValue}
|
|
94
|
+
placeholder={placeholder}
|
|
95
|
+
placeholderTextColor={placeholderColor}
|
|
96
|
+
editable={!disabled}
|
|
97
|
+
multiline
|
|
98
|
+
style={resolvedInputStyle}
|
|
99
|
+
onSubmitEditing={send}
|
|
100
|
+
returnKeyType="send"
|
|
101
|
+
/>
|
|
102
|
+
{trailing}
|
|
103
|
+
<IconButton
|
|
104
|
+
accessibilityLabel={sendLabel}
|
|
105
|
+
color="primary"
|
|
106
|
+
coloringStyle="solid"
|
|
107
|
+
disabled={disabled || !(value ?? '').trim()}
|
|
108
|
+
size="md"
|
|
109
|
+
onPress={send}
|
|
110
|
+
icon={SendHorizontal}
|
|
111
|
+
buttonStyle={(prev) => ({ ...prev, borderRadius: 999 })}
|
|
112
|
+
/>
|
|
113
|
+
</View>
|
|
114
|
+
)
|
|
115
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useEffect,
|
|
3
|
+
useMemo,
|
|
4
|
+
useRef,
|
|
5
|
+
type ReactNode
|
|
6
|
+
} from 'react'
|
|
7
|
+
import {
|
|
8
|
+
ScrollView,
|
|
9
|
+
type NativeScrollEvent,
|
|
10
|
+
type NativeSyntheticEvent,
|
|
11
|
+
type StyleProp,
|
|
12
|
+
type ViewStyle
|
|
13
|
+
} from 'react-native'
|
|
14
|
+
|
|
15
|
+
import { useTheme } from '@/src/global-contexts/theme/ThemeContext'
|
|
16
|
+
import type { ChatMessageListStyle } from '@/src/theme/types/components/chat'
|
|
17
|
+
import type { StyleOverwrite } from '@/src/theme/types/resolver'
|
|
18
|
+
|
|
19
|
+
export type ChatMessageListProps = {
|
|
20
|
+
autoScroll?: boolean,
|
|
21
|
+
children?: ReactNode,
|
|
22
|
+
style?: StyleProp<ViewStyle>,
|
|
23
|
+
listStyle?: StyleOverwrite<Record<string, never>, ChatMessageListStyle>,
|
|
24
|
+
contentContainerStyle?: StyleProp<ViewStyle>,
|
|
25
|
+
onScroll?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const ChatMessageList = ({
|
|
29
|
+
autoScroll = true,
|
|
30
|
+
children,
|
|
31
|
+
style,
|
|
32
|
+
listStyle,
|
|
33
|
+
contentContainerStyle,
|
|
34
|
+
onScroll,
|
|
35
|
+
}: ChatMessageListProps) => {
|
|
36
|
+
const { theme } = useTheme()
|
|
37
|
+
const scrollRef = useRef<ScrollView>(null)
|
|
38
|
+
const state = useMemo(() => ({}), [])
|
|
39
|
+
|
|
40
|
+
const resolvedListStyle = useMemo(
|
|
41
|
+
() => theme.components.chat.messageList.container(state, listStyle),
|
|
42
|
+
[theme, state, listStyle]
|
|
43
|
+
)
|
|
44
|
+
const listGap = useMemo(
|
|
45
|
+
() => (theme.components.chat.messageList.container(state) as ViewStyle).gap,
|
|
46
|
+
[theme, state]
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (autoScroll) {
|
|
51
|
+
scrollRef.current?.scrollToEnd({ animated: true })
|
|
52
|
+
}
|
|
53
|
+
}, [autoScroll, children])
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<ScrollView
|
|
57
|
+
ref={scrollRef}
|
|
58
|
+
style={[resolvedListStyle, style]}
|
|
59
|
+
contentContainerStyle={[{ gap: listGap }, contentContainerStyle]}
|
|
60
|
+
onScroll={onScroll}
|
|
61
|
+
scrollEventThrottle={16}
|
|
62
|
+
>
|
|
63
|
+
{children}
|
|
64
|
+
</ScrollView>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
import {
|
|
3
|
+
Pressable,
|
|
4
|
+
Text,
|
|
5
|
+
type PressableProps,
|
|
6
|
+
type StyleProp,
|
|
7
|
+
type ViewStyle
|
|
8
|
+
} from 'react-native'
|
|
9
|
+
|
|
10
|
+
import { useTheme } from '@/src/global-contexts/theme/ThemeContext'
|
|
11
|
+
import type {
|
|
12
|
+
ChatQuickReplyChipState,
|
|
13
|
+
ChatQuickReplyChipStyle,
|
|
14
|
+
ChatQuickReplyChipTextStyle
|
|
15
|
+
} from '@/src/theme/types/components/chat'
|
|
16
|
+
import type { StyleOverwrite } from '@/src/theme/types/resolver'
|
|
17
|
+
|
|
18
|
+
export type ChatQuickReplyChipProps = Omit<PressableProps, 'children' | 'style'> & {
|
|
19
|
+
isActive?: boolean,
|
|
20
|
+
children?: ReactNode,
|
|
21
|
+
style?: StyleProp<ViewStyle>,
|
|
22
|
+
chipStyle?: StyleOverwrite<ChatQuickReplyChipState, ChatQuickReplyChipStyle>,
|
|
23
|
+
textStyle?: StyleOverwrite<ChatQuickReplyChipState, ChatQuickReplyChipTextStyle>,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type PressableInteraction = {
|
|
27
|
+
pressed: boolean,
|
|
28
|
+
hovered?: boolean,
|
|
29
|
+
focused?: boolean,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const ChatQuickReplyChip = ({
|
|
33
|
+
isActive = false,
|
|
34
|
+
children,
|
|
35
|
+
disabled,
|
|
36
|
+
style,
|
|
37
|
+
chipStyle,
|
|
38
|
+
textStyle,
|
|
39
|
+
...props
|
|
40
|
+
}: ChatQuickReplyChipProps) => {
|
|
41
|
+
const { theme } = useTheme()
|
|
42
|
+
|
|
43
|
+
const resolveState = (interaction: PressableInteraction): ChatQuickReplyChipState => ({
|
|
44
|
+
isActive,
|
|
45
|
+
isDisabled: !!disabled,
|
|
46
|
+
isPressed: interaction.pressed,
|
|
47
|
+
isHovered: !!interaction.hovered,
|
|
48
|
+
isFocused: !!interaction.focused,
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<Pressable
|
|
53
|
+
{...props}
|
|
54
|
+
disabled={disabled}
|
|
55
|
+
style={(pressableState) => {
|
|
56
|
+
const state = resolveState(pressableState as PressableInteraction)
|
|
57
|
+
return [theme.components.chat.quickReplyChip.container(state, chipStyle), style]
|
|
58
|
+
}}
|
|
59
|
+
>
|
|
60
|
+
{(pressableState) => {
|
|
61
|
+
const state = resolveState(pressableState as PressableInteraction)
|
|
62
|
+
const resolvedText = theme.components.chat.quickReplyChip.text(state, textStyle)
|
|
63
|
+
|
|
64
|
+
if (typeof children === 'string' || typeof children === 'number') {
|
|
65
|
+
return <Text style={resolvedText}>{children}</Text>
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return children
|
|
69
|
+
}}
|
|
70
|
+
</Pressable>
|
|
71
|
+
)
|
|
72
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useMemo,
|
|
3
|
+
type ReactNode
|
|
4
|
+
} from 'react'
|
|
5
|
+
import {
|
|
6
|
+
Text,
|
|
7
|
+
View,
|
|
8
|
+
type StyleProp,
|
|
9
|
+
type ViewProps,
|
|
10
|
+
type ViewStyle
|
|
11
|
+
} from 'react-native'
|
|
12
|
+
import { CheckCheck } from 'lucide-react-native'
|
|
13
|
+
|
|
14
|
+
import type { ColoringType } from '@helpwave/hightide-design/helpers'
|
|
15
|
+
|
|
16
|
+
import { useTheme } from '@/src/global-contexts/theme/ThemeContext'
|
|
17
|
+
import type {
|
|
18
|
+
ChatSystemLineState,
|
|
19
|
+
ChatSystemLineStyle,
|
|
20
|
+
ChatSystemLineTextStyle
|
|
21
|
+
} from '@/src/theme/types/components/chat'
|
|
22
|
+
import type { StyleOverwrite } from '@/src/theme/types/resolver'
|
|
23
|
+
|
|
24
|
+
export type ChatSystemLineProps = Omit<ViewProps, 'children' | 'style'> & {
|
|
25
|
+
icon?: ReactNode,
|
|
26
|
+
color?: ColoringType,
|
|
27
|
+
children?: ReactNode,
|
|
28
|
+
style?: StyleProp<ViewStyle>,
|
|
29
|
+
lineStyle?: StyleOverwrite<ChatSystemLineState, ChatSystemLineStyle>,
|
|
30
|
+
textStyle?: StyleOverwrite<ChatSystemLineState, ChatSystemLineTextStyle>,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const ChatSystemLine = ({
|
|
34
|
+
icon,
|
|
35
|
+
color = 'primary',
|
|
36
|
+
children,
|
|
37
|
+
style,
|
|
38
|
+
lineStyle,
|
|
39
|
+
textStyle,
|
|
40
|
+
...props
|
|
41
|
+
}: ChatSystemLineProps) => {
|
|
42
|
+
const { theme } = useTheme()
|
|
43
|
+
const state = useMemo(() => ({ color }), [color])
|
|
44
|
+
|
|
45
|
+
const resolvedLineStyle = useMemo(
|
|
46
|
+
() => theme.components.chat.systemLine.container(state, lineStyle),
|
|
47
|
+
[theme, state, lineStyle]
|
|
48
|
+
)
|
|
49
|
+
const resolvedTextStyle = useMemo(
|
|
50
|
+
() => theme.components.chat.systemLine.text(state, textStyle),
|
|
51
|
+
[theme, state, textStyle]
|
|
52
|
+
)
|
|
53
|
+
const resolvedIcon = useMemo(
|
|
54
|
+
() => theme.components.chat.systemLine.icon(state),
|
|
55
|
+
[theme, state]
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<View {...props} style={[resolvedLineStyle, style]}>
|
|
60
|
+
{icon ?? <CheckCheck size={14} color={resolvedIcon.color} />}
|
|
61
|
+
{typeof children === 'string' || typeof children === 'number' ? (
|
|
62
|
+
<Text style={resolvedTextStyle}>{children}</Text>
|
|
63
|
+
) : (
|
|
64
|
+
children
|
|
65
|
+
)}
|
|
66
|
+
</View>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useMemo,
|
|
3
|
+
type ReactNode
|
|
4
|
+
} from 'react'
|
|
5
|
+
import {
|
|
6
|
+
Text,
|
|
7
|
+
View,
|
|
8
|
+
type StyleProp,
|
|
9
|
+
type ViewProps,
|
|
10
|
+
type ViewStyle
|
|
11
|
+
} from 'react-native'
|
|
12
|
+
|
|
13
|
+
import { useTheme } from '@/src/global-contexts/theme/ThemeContext'
|
|
14
|
+
import type {
|
|
15
|
+
ChatThreadHeaderStyle,
|
|
16
|
+
ChatThreadHeaderSubtitleStyle,
|
|
17
|
+
ChatThreadHeaderTitleStyle
|
|
18
|
+
} from '@/src/theme/types/components/chat'
|
|
19
|
+
import type { StyleOverwrite } from '@/src/theme/types/resolver'
|
|
20
|
+
|
|
21
|
+
export type ChatThreadHeaderProps = Omit<ViewProps, 'style'> & {
|
|
22
|
+
avatar?: ReactNode,
|
|
23
|
+
title: ReactNode,
|
|
24
|
+
subtitle?: ReactNode,
|
|
25
|
+
leftActions?: ReactNode,
|
|
26
|
+
rightActions?: ReactNode,
|
|
27
|
+
style?: StyleProp<ViewStyle>,
|
|
28
|
+
headerStyle?: StyleOverwrite<Record<string, never>, ChatThreadHeaderStyle>,
|
|
29
|
+
titleStyle?: StyleOverwrite<Record<string, never>, ChatThreadHeaderTitleStyle>,
|
|
30
|
+
subtitleStyle?: StyleOverwrite<Record<string, never>, ChatThreadHeaderSubtitleStyle>,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const ChatThreadHeader = ({
|
|
34
|
+
avatar,
|
|
35
|
+
title,
|
|
36
|
+
subtitle,
|
|
37
|
+
leftActions,
|
|
38
|
+
rightActions,
|
|
39
|
+
style,
|
|
40
|
+
headerStyle,
|
|
41
|
+
titleStyle,
|
|
42
|
+
subtitleStyle,
|
|
43
|
+
...props
|
|
44
|
+
}: ChatThreadHeaderProps) => {
|
|
45
|
+
const { theme } = useTheme()
|
|
46
|
+
const state = useMemo(() => ({}), [])
|
|
47
|
+
|
|
48
|
+
const resolvedHeaderStyle = useMemo(
|
|
49
|
+
() => theme.components.chat.threadHeader.container(state, headerStyle),
|
|
50
|
+
[theme, state, headerStyle]
|
|
51
|
+
)
|
|
52
|
+
const resolvedTitleStyle = useMemo(
|
|
53
|
+
() => theme.components.chat.threadHeader.title(state, titleStyle),
|
|
54
|
+
[theme, state, titleStyle]
|
|
55
|
+
)
|
|
56
|
+
const resolvedSubtitleStyle = useMemo(
|
|
57
|
+
() => theme.components.chat.threadHeader.subtitle(state, subtitleStyle),
|
|
58
|
+
[theme, state, subtitleStyle]
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<View {...props} style={[resolvedHeaderStyle, style]}>
|
|
63
|
+
{leftActions != null && (
|
|
64
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', flexShrink: 0 }}>
|
|
65
|
+
{leftActions}
|
|
66
|
+
</View>
|
|
67
|
+
)}
|
|
68
|
+
{avatar}
|
|
69
|
+
<View style={{ flex: 1, minWidth: 0, gap: 2 }}>
|
|
70
|
+
{typeof title === 'string' || typeof title === 'number' ? (
|
|
71
|
+
<Text style={resolvedTitleStyle} numberOfLines={1}>{title}</Text>
|
|
72
|
+
) : (
|
|
73
|
+
title
|
|
74
|
+
)}
|
|
75
|
+
{subtitle != null && (
|
|
76
|
+
typeof subtitle === 'string' || typeof subtitle === 'number' ? (
|
|
77
|
+
<Text style={resolvedSubtitleStyle} numberOfLines={1}>{subtitle}</Text>
|
|
78
|
+
) : (
|
|
79
|
+
subtitle
|
|
80
|
+
)
|
|
81
|
+
)}
|
|
82
|
+
</View>
|
|
83
|
+
{rightActions != null && (
|
|
84
|
+
<View style={{ flexDirection: 'row', alignItems: 'center', flexShrink: 0 }}>
|
|
85
|
+
{rightActions}
|
|
86
|
+
</View>
|
|
87
|
+
)}
|
|
88
|
+
</View>
|
|
89
|
+
)
|
|
90
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from './ChatAttachmentCard'
|
|
2
|
+
export * from './ChatConversationList'
|
|
3
|
+
export * from './ChatConversationRow'
|
|
4
|
+
export * from './ChatDateDivider'
|
|
5
|
+
export * from './ChatMessageBubble'
|
|
6
|
+
export * from './ChatMessageCard'
|
|
7
|
+
export * from './ChatMessageComposer'
|
|
8
|
+
export * from './ChatMessageList'
|
|
9
|
+
export * from './ChatQuickReplyChip'
|
|
10
|
+
export * from './ChatSystemLine'
|
|
11
|
+
export * from './ChatThreadHeader'
|
package/src/components/index.ts
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
export * from './
|
|
2
|
-
export * from './
|
|
3
|
-
export * from './
|
|
4
|
-
export * from './
|
|
5
|
-
export * from './Input'
|
|
6
|
-
export * from './MultiSelect'
|
|
7
|
-
export * from './Select'
|
|
1
|
+
export * from './chat'
|
|
2
|
+
export * from './menu'
|
|
3
|
+
export * from './user-interaction'
|
|
4
|
+
export * from './visualization-and-display'
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useMemo,
|
|
3
|
+
type ReactNode
|
|
4
|
+
} from 'react'
|
|
5
|
+
import {
|
|
6
|
+
Text,
|
|
7
|
+
View,
|
|
8
|
+
type StyleProp,
|
|
9
|
+
type ViewProps,
|
|
10
|
+
type ViewStyle
|
|
11
|
+
} from 'react-native'
|
|
12
|
+
|
|
13
|
+
import { useTheme } from '@/src/global-contexts/theme/ThemeContext'
|
|
14
|
+
import type {
|
|
15
|
+
MenuCardStyle,
|
|
16
|
+
MenuSectionStyle,
|
|
17
|
+
MenuSectionTitleStyle
|
|
18
|
+
} from '@/src/theme/types/components/menu'
|
|
19
|
+
import type { StyleOverwrite } from '@/src/theme/types/resolver'
|
|
20
|
+
|
|
21
|
+
export type MenuProps = Omit<ViewProps, 'children' | 'style'> & {
|
|
22
|
+
title: string,
|
|
23
|
+
children?: ReactNode,
|
|
24
|
+
style?: StyleProp<ViewStyle>,
|
|
25
|
+
sectionStyle?: StyleOverwrite<Record<string, never>, MenuSectionStyle>,
|
|
26
|
+
titleStyle?: StyleOverwrite<Record<string, never>, MenuSectionTitleStyle>,
|
|
27
|
+
cardStyle?: StyleOverwrite<Record<string, never>, MenuCardStyle>,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const Menu = ({
|
|
31
|
+
title,
|
|
32
|
+
children,
|
|
33
|
+
style,
|
|
34
|
+
sectionStyle,
|
|
35
|
+
titleStyle,
|
|
36
|
+
cardStyle,
|
|
37
|
+
...props
|
|
38
|
+
}: MenuProps) => {
|
|
39
|
+
const { theme } = useTheme()
|
|
40
|
+
const state = useMemo(() => ({}), [])
|
|
41
|
+
|
|
42
|
+
const resolvedSectionStyle = useMemo(
|
|
43
|
+
() => theme.components.menu.section(state, sectionStyle),
|
|
44
|
+
[theme, state, sectionStyle]
|
|
45
|
+
)
|
|
46
|
+
const resolvedTitleStyle = useMemo(
|
|
47
|
+
() => theme.components.menu.sectionTitle(state, titleStyle),
|
|
48
|
+
[theme, state, titleStyle]
|
|
49
|
+
)
|
|
50
|
+
const resolvedCardStyle = useMemo(
|
|
51
|
+
() => theme.components.menu.card(state, cardStyle),
|
|
52
|
+
[theme, state, cardStyle]
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<View {...props} style={[resolvedSectionStyle, style]}>
|
|
57
|
+
<Text style={resolvedTitleStyle}>{title}</Text>
|
|
58
|
+
<View style={resolvedCardStyle}>{children}</View>
|
|
59
|
+
</View>
|
|
60
|
+
)
|
|
61
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Fragment,
|
|
3
|
+
useMemo,
|
|
4
|
+
type ReactNode
|
|
5
|
+
} from 'react'
|
|
6
|
+
import {
|
|
7
|
+
Pressable,
|
|
8
|
+
Text,
|
|
9
|
+
View,
|
|
10
|
+
type PressableProps,
|
|
11
|
+
type StyleProp,
|
|
12
|
+
type ViewStyle
|
|
13
|
+
} from 'react-native'
|
|
14
|
+
|
|
15
|
+
import { useTheme } from '@/src/global-contexts/theme/ThemeContext'
|
|
16
|
+
import type {
|
|
17
|
+
MenuActionItemLabelStyle,
|
|
18
|
+
MenuActionItemState,
|
|
19
|
+
MenuActionItemStyle
|
|
20
|
+
} from '@/src/theme/types/components/menu'
|
|
21
|
+
import type { StyleOverwrite } from '@/src/theme/types/resolver'
|
|
22
|
+
|
|
23
|
+
export type MenuActionItemProps = Omit<PressableProps, 'children' | 'style'> & {
|
|
24
|
+
label: string,
|
|
25
|
+
leading?: ReactNode,
|
|
26
|
+
trailing?: ReactNode,
|
|
27
|
+
danger?: boolean,
|
|
28
|
+
style?: StyleProp<ViewStyle>,
|
|
29
|
+
itemStyle?: StyleOverwrite<MenuActionItemState, MenuActionItemStyle>,
|
|
30
|
+
labelStyle?: StyleOverwrite<MenuActionItemState, MenuActionItemLabelStyle>,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type PressableInteraction = {
|
|
34
|
+
pressed: boolean,
|
|
35
|
+
hovered?: boolean,
|
|
36
|
+
focused?: boolean,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const MenuActionItem = ({
|
|
40
|
+
label,
|
|
41
|
+
leading,
|
|
42
|
+
trailing,
|
|
43
|
+
danger = false,
|
|
44
|
+
disabled,
|
|
45
|
+
style,
|
|
46
|
+
itemStyle,
|
|
47
|
+
labelStyle,
|
|
48
|
+
...props
|
|
49
|
+
}: MenuActionItemProps) => {
|
|
50
|
+
const { theme } = useTheme()
|
|
51
|
+
|
|
52
|
+
const resolveState = (interaction: PressableInteraction): MenuActionItemState => ({
|
|
53
|
+
isDanger: danger,
|
|
54
|
+
isDisabled: !!disabled,
|
|
55
|
+
isPressed: interaction.pressed,
|
|
56
|
+
isHovered: !!interaction.hovered,
|
|
57
|
+
isFocused: !!interaction.focused,
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
const resolvedContentStyle = useMemo(
|
|
61
|
+
() => theme.components.menu.actionItemContent({}),
|
|
62
|
+
[theme]
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<Pressable
|
|
67
|
+
{...props}
|
|
68
|
+
disabled={disabled}
|
|
69
|
+
style={(pressableState) => {
|
|
70
|
+
const state = resolveState(pressableState as PressableInteraction)
|
|
71
|
+
return [theme.components.menu.actionItem(state, itemStyle), style]
|
|
72
|
+
}}
|
|
73
|
+
>
|
|
74
|
+
{(pressableState) => {
|
|
75
|
+
const state = resolveState(pressableState as PressableInteraction)
|
|
76
|
+
const resolvedLabelStyle = theme.components.menu.actionItemLabel(state, labelStyle)
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<Fragment>
|
|
80
|
+
{leading}
|
|
81
|
+
<View style={resolvedContentStyle}>
|
|
82
|
+
<Text style={resolvedLabelStyle}>{label}</Text>
|
|
83
|
+
</View>
|
|
84
|
+
{trailing}
|
|
85
|
+
</Fragment>
|
|
86
|
+
)
|
|
87
|
+
}}
|
|
88
|
+
</Pressable>
|
|
89
|
+
)
|
|
90
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useMemo,
|
|
3
|
+
type ReactNode
|
|
4
|
+
} from 'react'
|
|
5
|
+
import {
|
|
6
|
+
Text,
|
|
7
|
+
View,
|
|
8
|
+
type StyleProp,
|
|
9
|
+
type ViewProps,
|
|
10
|
+
type ViewStyle
|
|
11
|
+
} from 'react-native'
|
|
12
|
+
|
|
13
|
+
import { useTheme } from '@/src/global-contexts/theme/ThemeContext'
|
|
14
|
+
import type {
|
|
15
|
+
MenuItemLabelStyle,
|
|
16
|
+
MenuItemStyle,
|
|
17
|
+
MenuItemValueStyle
|
|
18
|
+
} from '@/src/theme/types/components/menu'
|
|
19
|
+
import type { StyleOverwrite } from '@/src/theme/types/resolver'
|
|
20
|
+
|
|
21
|
+
export type MenuItemProps = Omit<ViewProps, 'style'> & {
|
|
22
|
+
label: string,
|
|
23
|
+
value: string,
|
|
24
|
+
leading?: ReactNode,
|
|
25
|
+
trailing?: ReactNode,
|
|
26
|
+
style?: StyleProp<ViewStyle>,
|
|
27
|
+
itemStyle?: StyleOverwrite<Record<string, never>, MenuItemStyle>,
|
|
28
|
+
labelStyle?: StyleOverwrite<Record<string, never>, MenuItemLabelStyle>,
|
|
29
|
+
valueStyle?: StyleOverwrite<Record<string, never>, MenuItemValueStyle>,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const MenuItem = ({
|
|
33
|
+
label,
|
|
34
|
+
value,
|
|
35
|
+
leading,
|
|
36
|
+
trailing,
|
|
37
|
+
style,
|
|
38
|
+
itemStyle,
|
|
39
|
+
labelStyle,
|
|
40
|
+
valueStyle,
|
|
41
|
+
...props
|
|
42
|
+
}: MenuItemProps) => {
|
|
43
|
+
const { theme } = useTheme()
|
|
44
|
+
const state = useMemo(() => ({}), [])
|
|
45
|
+
|
|
46
|
+
const resolvedItemStyle = useMemo(
|
|
47
|
+
() => theme.components.menu.item(state, itemStyle),
|
|
48
|
+
[theme, state, itemStyle]
|
|
49
|
+
)
|
|
50
|
+
const resolvedContentStyle = useMemo(
|
|
51
|
+
() => theme.components.menu.itemContent(state),
|
|
52
|
+
[theme, state]
|
|
53
|
+
)
|
|
54
|
+
const resolvedLabelStyle = useMemo(
|
|
55
|
+
() => theme.components.menu.itemLabel(state, labelStyle),
|
|
56
|
+
[theme, state, labelStyle]
|
|
57
|
+
)
|
|
58
|
+
const resolvedValueStyle = useMemo(
|
|
59
|
+
() => theme.components.menu.itemValue(state, valueStyle),
|
|
60
|
+
[theme, state, valueStyle]
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<View {...props} style={[resolvedItemStyle, style]}>
|
|
65
|
+
{leading}
|
|
66
|
+
<View style={resolvedContentStyle}>
|
|
67
|
+
<Text style={resolvedLabelStyle}>{label}</Text>
|
|
68
|
+
<Text style={resolvedValueStyle}>{value}</Text>
|
|
69
|
+
</View>
|
|
70
|
+
{trailing}
|
|
71
|
+
</View>
|
|
72
|
+
)
|
|
73
|
+
}
|