@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 CHANGED
@@ -10,7 +10,7 @@
10
10
  "access": "public"
11
11
  },
12
12
  "license": "AGPL-3.0-only",
13
- "version": "0.5.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.5.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
- containerStyle
140
+ style
141
141
  ),
142
- [bubbleOverrides, state, containerStyle]
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={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={{ flex: 1, minWidth: 0, gap: 2 }}>
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 StyleProp,
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?: StyleProp<ViewStyle>,
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, containerStyle),
62
- [theme, state, containerStyle]
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={[resolvedContainerStyle, 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 StyleProp,
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?: StyleProp<TextStyle>,
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, containerStyle),
112
- [theme, state, containerStyle]
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={[resolvedContainerStyle, resolvedTextStyle, 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
- showSearch?: boolean,
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
- showSearch = true,
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 = showSearch && options.length >= 6
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
- showSearch?: boolean,
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
- showSearch = true,
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 = showSearch && options.length >= 6
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
+ })
@@ -6,4 +6,5 @@ export * from './MultiSelect'
6
6
  export * from './SearchBar'
7
7
  export * from './Select'
8
8
  export * from './Switch'
9
+ export * from './Textarea'
9
10
  export * from './ThemedPressable'
@@ -14,4 +14,5 @@ export * from './multiSelect'
14
14
  export * from './searchBar'
15
15
  export * from './select'
16
16
  export * from './switch'
17
+ export * from './textarea'
17
18
  export * from './themedPressable'
@@ -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,
@@ -13,4 +13,5 @@ export * from './multiSelect'
13
13
  export * from './searchBar'
14
14
  export * from './select'
15
15
  export * from './switch'
16
+ export * from './textarea'
16
17
  export * from './themedPressable'
@@ -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'