@helpwave/hightide-native 0.5.0 → 0.6.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 +5 -6
- package/src/components/chat/ChatMessageBubble.tsx +5 -9
- package/src/components/user-interaction/Input.tsx +23 -9
- package/src/components/user-interaction/MultiSelect.tsx +4 -3
- package/src/components/user-interaction/Select.tsx +3 -3
- package/src/components/user-interaction/Textarea.tsx +26 -0
- package/src/components/user-interaction/index.ts +1 -0
- package/src/theme/resolvers/index.ts +1 -0
- package/src/theme/resolvers/multiSelect.ts +0 -1
- package/src/theme/resolvers/textarea.ts +14 -0
- package/src/theme/themes/createHightideTheme.ts +6 -0
- package/src/theme/types/components/hightide.ts +2 -0
- package/src/theme/types/components/index.ts +1 -0
- package/src/theme/types/components/textarea.ts +8 -0
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.6.0",
|
|
14
14
|
"type": "module",
|
|
15
15
|
"files": [
|
|
16
16
|
"src"
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"lucide-react-native": "0.561.0",
|
|
50
|
-
"@helpwave/hightide-design": "0.
|
|
50
|
+
"@helpwave/hightide-design": "0.6.0",
|
|
51
51
|
"@helpwave/hightide-utils": "0.1.0"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
@@ -39,6 +39,7 @@ export type ChatAttachmentMessageBubbleProps = Omit<ChatMessageBubbleProps, 'chi
|
|
|
39
39
|
icon?: ReactNode,
|
|
40
40
|
downloadLabel?: string,
|
|
41
41
|
onDownload?: () => void,
|
|
42
|
+
style: ChatMessageBubbleProps['style'],
|
|
42
43
|
contentContainerStyle?: StyleOverwrite<
|
|
43
44
|
PressableState,
|
|
44
45
|
PressableContainerStyle
|
|
@@ -89,7 +90,6 @@ export const ChatAttachmentMessageBubble = ({
|
|
|
89
90
|
timestamp,
|
|
90
91
|
readReceipt,
|
|
91
92
|
style,
|
|
92
|
-
containerStyle,
|
|
93
93
|
bodyStyle,
|
|
94
94
|
bodyTextStyle,
|
|
95
95
|
metaDataContainerStyle,
|
|
@@ -137,9 +137,9 @@ export const ChatAttachmentMessageBubble = ({
|
|
|
137
137
|
const resolvedContainerStyle = useMemo(
|
|
138
138
|
() => mergeBubbleStyleOverwrite<ChatMessageBubbleContainerStyle>(
|
|
139
139
|
bubbleOverrides.container(state),
|
|
140
|
-
|
|
140
|
+
style
|
|
141
141
|
),
|
|
142
|
-
[bubbleOverrides, state,
|
|
142
|
+
[bubbleOverrides, state, style]
|
|
143
143
|
)
|
|
144
144
|
const resolvedBodyStyle = useMemo(
|
|
145
145
|
() => mergeBubbleStyleOverwrite<ChatMessageBubbleBodyStyle>(
|
|
@@ -183,8 +183,7 @@ export const ChatAttachmentMessageBubble = ({
|
|
|
183
183
|
direction={direction}
|
|
184
184
|
timestamp={timestamp}
|
|
185
185
|
readReceipt={readReceipt}
|
|
186
|
-
style={
|
|
187
|
-
containerStyle={resolvedContainerStyle}
|
|
186
|
+
style={resolvedContainerStyle}
|
|
188
187
|
bodyStyle={resolvedBodyStyle}
|
|
189
188
|
bodyTextStyle={resolvedBodyTextStyle}
|
|
190
189
|
metaDataContainerStyle={resolvedMetaDataContainerStyle}
|
|
@@ -219,7 +218,7 @@ export const ChatAttachmentMessageBubble = ({
|
|
|
219
218
|
/>
|
|
220
219
|
)}
|
|
221
220
|
</View>
|
|
222
|
-
<View style={{
|
|
221
|
+
<View style={{ gap: theme.spacing.xs }}>
|
|
223
222
|
{typeof name === 'string' || typeof name === 'number' ? (
|
|
224
223
|
<ThemedText style={resolvedFileNameTextStyle} numberOfLines={1}>{name}</ThemedText>
|
|
225
224
|
) : (
|
|
@@ -4,9 +4,7 @@ import {
|
|
|
4
4
|
} from 'react'
|
|
5
5
|
import {
|
|
6
6
|
View,
|
|
7
|
-
type
|
|
8
|
-
type ViewProps,
|
|
9
|
-
type ViewStyle
|
|
7
|
+
type ViewProps
|
|
10
8
|
} from 'react-native'
|
|
11
9
|
import { HightideIconRegistry } from '../../icons/HightideIconRegistry'
|
|
12
10
|
import { ThemedIcon } from '../visualization-and-display/ThemedIcon'
|
|
@@ -31,8 +29,7 @@ export type ChatMessageBubbleProps = Omit<ViewProps, 'children' | 'style'> & {
|
|
|
31
29
|
timestamp: ReactNode,
|
|
32
30
|
readReceipt?: ReactNode,
|
|
33
31
|
children?: ReactNode,
|
|
34
|
-
style?:
|
|
35
|
-
containerStyle?: StyleOverwrite<ChatMessageBubbleState, ChatMessageBubbleContainerStyle>,
|
|
32
|
+
style?: StyleOverwrite<ChatMessageBubbleState, ChatMessageBubbleContainerStyle>,
|
|
36
33
|
bodyStyle?: StyleOverwrite<ChatMessageBubbleState, ChatMessageBubbleBodyStyle>,
|
|
37
34
|
bodyTextStyle?: StyleOverwrite<ChatMessageBubbleState, ChatMessageBubbleBodyTextStyle>,
|
|
38
35
|
metaDataContainerStyle?: StyleOverwrite<ChatMessageBubbleState, ChatMessageBubbleMetaDataContainerStyle>,
|
|
@@ -46,7 +43,6 @@ export const ChatMessageBubble = ({
|
|
|
46
43
|
readReceipt,
|
|
47
44
|
children,
|
|
48
45
|
style,
|
|
49
|
-
containerStyle,
|
|
50
46
|
bodyStyle,
|
|
51
47
|
bodyTextStyle,
|
|
52
48
|
metaDataContainerStyle,
|
|
@@ -58,8 +54,8 @@ export const ChatMessageBubble = ({
|
|
|
58
54
|
const state = useMemo(() => ({ direction }), [direction])
|
|
59
55
|
|
|
60
56
|
const resolvedContainerStyle = useMemo(
|
|
61
|
-
() => theme.components.chat.messageBubble.container(state,
|
|
62
|
-
[theme, state,
|
|
57
|
+
() => theme.components.chat.messageBubble.container(state, style),
|
|
58
|
+
[theme, state, style]
|
|
63
59
|
)
|
|
64
60
|
const resolvedBodyStyle = useMemo(
|
|
65
61
|
() => theme.components.chat.messageBubble.body(state, bodyStyle),
|
|
@@ -87,7 +83,7 @@ export const ChatMessageBubble = ({
|
|
|
87
83
|
)
|
|
88
84
|
|
|
89
85
|
return (
|
|
90
|
-
<View {...props} style={
|
|
86
|
+
<View {...props} style={resolvedContainerStyle}>
|
|
91
87
|
<View style={resolvedBodyStyle}>
|
|
92
88
|
{typeof children === 'string' || typeof children === 'number' ? (
|
|
93
89
|
<ThemedText style={resolvedBodyTextStyle}>{children}</ThemedText>
|
|
@@ -3,11 +3,12 @@ import {
|
|
|
3
3
|
useMemo,
|
|
4
4
|
useState
|
|
5
5
|
} from 'react'
|
|
6
|
+
import type {
|
|
7
|
+
TextStyle } from 'react-native'
|
|
6
8
|
import {
|
|
9
|
+
StyleSheet,
|
|
7
10
|
TextInput,
|
|
8
|
-
type
|
|
9
|
-
type TextInputProps,
|
|
10
|
-
type TextStyle
|
|
11
|
+
type TextInputProps
|
|
11
12
|
} from 'react-native'
|
|
12
13
|
|
|
13
14
|
import {
|
|
@@ -51,8 +52,7 @@ export type InputProps = Omit<TextInputProps, 'value' | 'style'>
|
|
|
51
52
|
color?: ColorPairToken,
|
|
52
53
|
editCompleteOptions?: EditCompleteOptions,
|
|
53
54
|
initialValue?: string,
|
|
54
|
-
style?:
|
|
55
|
-
containerStyle?: StyleOverwrite<InputState, InputContainerStyle>,
|
|
55
|
+
style?: StyleOverwrite<InputState, InputContainerStyle>,
|
|
56
56
|
textStyle?: StyleOverwrite<InputState, InputTextStyle>,
|
|
57
57
|
}
|
|
58
58
|
|
|
@@ -68,7 +68,6 @@ export const Input = forwardRef<TextInput, InputProps>(function Input({
|
|
|
68
68
|
onEditComplete,
|
|
69
69
|
editCompleteOptions,
|
|
70
70
|
style,
|
|
71
|
-
containerStyle,
|
|
72
71
|
textStyle,
|
|
73
72
|
...props
|
|
74
73
|
}, ref) {
|
|
@@ -108,8 +107,8 @@ export const Input = forwardRef<TextInput, InputProps>(function Input({
|
|
|
108
107
|
}), [color, disabled, invalid, readOnly, interactive, isHovered, isPressed, isFocused])
|
|
109
108
|
|
|
110
109
|
const resolvedContainerStyle = useMemo(
|
|
111
|
-
() => theme.components.input.container(state,
|
|
112
|
-
[theme, state,
|
|
110
|
+
() => theme.components.input.container(state, style),
|
|
111
|
+
[theme, state, style]
|
|
113
112
|
)
|
|
114
113
|
const resolvedTextStyle = useMemo(
|
|
115
114
|
() => theme.components.input.text(state, textStyle),
|
|
@@ -120,6 +119,21 @@ export const Input = forwardRef<TextInput, InputProps>(function Input({
|
|
|
120
119
|
[theme, state]
|
|
121
120
|
)
|
|
122
121
|
|
|
122
|
+
|
|
123
|
+
const resolvedStyle = useMemo(
|
|
124
|
+
() => {
|
|
125
|
+
const resolvedSheet: TextStyle = StyleSheet.flatten<TextStyle>([
|
|
126
|
+
resolvedContainerStyle,
|
|
127
|
+
resolvedTextStyle,
|
|
128
|
+
])
|
|
129
|
+
// this is required for android to work
|
|
130
|
+
// TODO find a better solution like separating container and text input
|
|
131
|
+
delete resolvedSheet['boxShadow']
|
|
132
|
+
return resolvedSheet
|
|
133
|
+
},
|
|
134
|
+
[resolvedContainerStyle, resolvedTextStyle]
|
|
135
|
+
)
|
|
136
|
+
|
|
123
137
|
return (
|
|
124
138
|
<TextInput
|
|
125
139
|
{...props}
|
|
@@ -175,7 +189,7 @@ export const Input = forwardRef<TextInput, InputProps>(function Input({
|
|
|
175
189
|
}
|
|
176
190
|
}}
|
|
177
191
|
placeholderTextColor={resolvedPlaceholderStyle.color}
|
|
178
|
-
style={
|
|
192
|
+
style={resolvedStyle}
|
|
179
193
|
accessibilityState={{ disabled, selected: required }}
|
|
180
194
|
/>
|
|
181
195
|
)
|
|
@@ -36,7 +36,7 @@ export type MultiSelectProps = Partial<FormFieldDataHandling<string[]>>
|
|
|
36
36
|
value?: string[],
|
|
37
37
|
initialValue?: string[],
|
|
38
38
|
placeholder?: string,
|
|
39
|
-
|
|
39
|
+
searchableThreshold?: number,
|
|
40
40
|
color?: ColorPairToken,
|
|
41
41
|
style?: StyleProp<ViewStyle>,
|
|
42
42
|
}
|
|
@@ -46,7 +46,7 @@ export const MultiSelect = ({
|
|
|
46
46
|
value: controlledValue,
|
|
47
47
|
initialValue = [],
|
|
48
48
|
placeholder = 'Select…',
|
|
49
|
-
|
|
49
|
+
searchableThreshold = 6,
|
|
50
50
|
color,
|
|
51
51
|
disabled = false,
|
|
52
52
|
readOnly = false,
|
|
@@ -67,7 +67,7 @@ export const MultiSelect = ({
|
|
|
67
67
|
onEditComplete,
|
|
68
68
|
})
|
|
69
69
|
|
|
70
|
-
const isSearchVisible =
|
|
70
|
+
const isSearchVisible = options.length >= searchableThreshold
|
|
71
71
|
|
|
72
72
|
const selectedOptions = useMemo(() => {
|
|
73
73
|
return options.filter((option) => multiSelect.isSelected(option.id))
|
|
@@ -162,6 +162,7 @@ export const MultiSelect = ({
|
|
|
162
162
|
style={{
|
|
163
163
|
position: 'relative',
|
|
164
164
|
width: theme.icongraphy.sizes.sm,
|
|
165
|
+
height: theme.icongraphy.sizes.sm,
|
|
165
166
|
}}
|
|
166
167
|
>
|
|
167
168
|
<IconButton
|
|
@@ -37,7 +37,7 @@ export type SelectProps = Partial<FormFieldDataHandling<string>>
|
|
|
37
37
|
value?: string | null,
|
|
38
38
|
initialValue?: string | null,
|
|
39
39
|
placeholder?: string,
|
|
40
|
-
|
|
40
|
+
searchableThreshold?: number,
|
|
41
41
|
color?: ColorPairToken,
|
|
42
42
|
style?: StyleProp<ViewStyle>,
|
|
43
43
|
}
|
|
@@ -47,7 +47,7 @@ export const Select = ({
|
|
|
47
47
|
value: controlledValue,
|
|
48
48
|
initialValue = null,
|
|
49
49
|
placeholder = 'Select…',
|
|
50
|
-
|
|
50
|
+
searchableThreshold = 6,
|
|
51
51
|
color,
|
|
52
52
|
disabled = false,
|
|
53
53
|
readOnly = false,
|
|
@@ -68,7 +68,7 @@ export const Select = ({
|
|
|
68
68
|
onEditComplete,
|
|
69
69
|
})
|
|
70
70
|
|
|
71
|
-
const isSearchVisible =
|
|
71
|
+
const isSearchVisible = options.length >= searchableThreshold
|
|
72
72
|
|
|
73
73
|
const selectedLabel = useMemo(() => {
|
|
74
74
|
const selected = options.find((option) => option.id === select.value)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { forwardRef } from 'react'
|
|
2
|
+
import type { TextInput } from 'react-native'
|
|
3
|
+
|
|
4
|
+
import { useTheme } from '../../global-contexts/theme/ThemeContext'
|
|
5
|
+
import { Input, type InputProps } from './Input'
|
|
6
|
+
|
|
7
|
+
export type TextareaProps = InputProps
|
|
8
|
+
|
|
9
|
+
export const Textarea = forwardRef<TextInput, TextareaProps>(function Textarea({
|
|
10
|
+
style,
|
|
11
|
+
textStyle,
|
|
12
|
+
...props
|
|
13
|
+
}, ref) {
|
|
14
|
+
const { theme } = useTheme()
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<Input
|
|
18
|
+
{...props}
|
|
19
|
+
ref={ref}
|
|
20
|
+
multiline
|
|
21
|
+
textAlignVertical="top"
|
|
22
|
+
style={(state) => theme.components.textarea.container(state, style)}
|
|
23
|
+
textStyle={(state) => theme.components.textarea.text(state, textStyle)}
|
|
24
|
+
/>
|
|
25
|
+
)
|
|
26
|
+
})
|
|
@@ -135,7 +135,6 @@ export const toMultiSelectThemeResolvers: ComponentThemeResolver<MultiSelectThem
|
|
|
135
135
|
)),
|
|
136
136
|
overlay: createSimpleStyleResolver((): SelectOverlayStyle => ({
|
|
137
137
|
...StyleAdapterUtils.container(resolve().overlay),
|
|
138
|
-
flex: 1,
|
|
139
138
|
})),
|
|
140
139
|
menu: createStyleResolver((state: SelectMenuState): SelectMenuStyle => (
|
|
141
140
|
StyleAdapterUtils.container(resolve({ hasSearch: state.hasSearch }).menu)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { InputThemeResolvers } from '../types/components/input'
|
|
2
|
+
import type { ComponentThemeResolver } from '../types/resolver'
|
|
3
|
+
|
|
4
|
+
import { toInputThemeResolvers } from './input'
|
|
5
|
+
|
|
6
|
+
export const toTextareaThemeResolvers: ComponentThemeResolver<InputThemeResolvers> = (params) => (
|
|
7
|
+
toInputThemeResolvers({
|
|
8
|
+
...params,
|
|
9
|
+
componentTokens: {
|
|
10
|
+
...params.componentTokens,
|
|
11
|
+
input: params.componentTokens.textarea,
|
|
12
|
+
},
|
|
13
|
+
})
|
|
14
|
+
)
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
import { toMultiSelectThemeResolvers } from '../resolvers/multiSelect'
|
|
30
30
|
import { toSelectThemeResolvers } from '../resolvers/select'
|
|
31
31
|
import { toSwitchThemeResolvers } from '../resolvers/switch'
|
|
32
|
+
import { toTextareaThemeResolvers } from '../resolvers/textarea'
|
|
32
33
|
import { toThemedPressableThemeResolvers } from '../resolvers/themedPressable'
|
|
33
34
|
import type { HightideThemeSemantics } from '../types/semantics'
|
|
34
35
|
import type { HightideTheme } from '../types/theme'
|
|
@@ -172,6 +173,11 @@ export const createHightideTheme = (themeTokens: ThemeTokens): HightideTheme =>
|
|
|
172
173
|
semanticTokens: hightideSemanticTokenResolvers,
|
|
173
174
|
componentTokens: componentTokenResolvers,
|
|
174
175
|
}),
|
|
176
|
+
textarea: toTextareaThemeResolvers({
|
|
177
|
+
themeTokens,
|
|
178
|
+
semanticTokens: hightideSemanticTokenResolvers,
|
|
179
|
+
componentTokens: componentTokenResolvers,
|
|
180
|
+
}),
|
|
175
181
|
searchBar: toSearchBarThemeResolvers({
|
|
176
182
|
themeTokens,
|
|
177
183
|
semanticTokens: hightideSemanticTokenResolvers,
|
|
@@ -19,6 +19,7 @@ import type { ListItemThemeResolvers } from './listItem'
|
|
|
19
19
|
import type { MultiSelectThemeResolvers } from './multiSelect'
|
|
20
20
|
import type { SelectThemeResolvers } from './select'
|
|
21
21
|
import type { SwitchThemeResolvers } from './switch'
|
|
22
|
+
import type { TextareaThemeResolvers } from './textarea'
|
|
22
23
|
import type { ThemedPressableThemeResolvers } from './themedPressable'
|
|
23
24
|
|
|
24
25
|
export type IconThemeResolvers = Record<IconSize, IconTokens>
|
|
@@ -31,6 +32,7 @@ export type HightideComponentThemes = {
|
|
|
31
32
|
checkbox: CheckboxThemeResolvers,
|
|
32
33
|
switch: SwitchThemeResolvers,
|
|
33
34
|
input: InputThemeResolvers,
|
|
35
|
+
textarea: TextareaThemeResolvers,
|
|
34
36
|
searchBar: SearchBarThemeResolvers,
|
|
35
37
|
select: SelectThemeResolvers,
|
|
36
38
|
multiSelect: MultiSelectThemeResolvers,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
InputContainerStyle as TextareaContainerStyle,
|
|
3
|
+
InputIconStyle as TextareaIconStyle,
|
|
4
|
+
InputPlaceholderStyle as TextareaPlaceholderStyle,
|
|
5
|
+
InputState as TextareaState,
|
|
6
|
+
InputTextStyle as TextareaTextStyle,
|
|
7
|
+
InputThemeResolvers as TextareaThemeResolvers
|
|
8
|
+
} from './input'
|