@chem-po/react-native 0.0.28 → 0.0.30

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 (149) hide show
  1. package/lib/commonjs/components/button/Toggle.js +19 -16
  2. package/lib/commonjs/components/button/Toggle.js.map +1 -1
  3. package/lib/commonjs/components/form/input/Editable.js +62 -93
  4. package/lib/commonjs/components/form/input/Editable.js.map +1 -1
  5. package/lib/commonjs/components/form/input/StandaloneInput.js +5 -1
  6. package/lib/commonjs/components/form/input/StandaloneInput.js.map +1 -1
  7. package/lib/commonjs/components/form/input/datetime/index.js +7 -2
  8. package/lib/commonjs/components/form/input/datetime/index.js.map +1 -1
  9. package/lib/commonjs/components/form/input/hooks/useInputStyles.js +2 -1
  10. package/lib/commonjs/components/form/input/hooks/useInputStyles.js.map +1 -1
  11. package/lib/commonjs/components/form/input/multipleSelect/index.js +25 -11
  12. package/lib/commonjs/components/form/input/multipleSelect/index.js.map +1 -1
  13. package/lib/commonjs/components/form/input/number/index.js +8 -4
  14. package/lib/commonjs/components/form/input/number/index.js.map +1 -1
  15. package/lib/commonjs/components/form/input/select/index.js +13 -3
  16. package/lib/commonjs/components/form/input/select/index.js.map +1 -1
  17. package/lib/commonjs/components/form/input/text/AutoResizeTextarea.js +54 -0
  18. package/lib/commonjs/components/form/input/text/AutoResizeTextarea.js.map +1 -0
  19. package/lib/commonjs/components/form/input/text/index.js +24 -12
  20. package/lib/commonjs/components/form/input/text/index.js.map +1 -1
  21. package/lib/commonjs/components/form/input/text/textarea.js +13 -33
  22. package/lib/commonjs/components/form/input/text/textarea.js.map +1 -1
  23. package/lib/commonjs/components/form/input/text/useWebAutoResize.js +64 -0
  24. package/lib/commonjs/components/form/input/text/useWebAutoResize.js.map +1 -0
  25. package/lib/commonjs/components/form/view/file.js +21 -12
  26. package/lib/commonjs/components/form/view/file.js.map +1 -1
  27. package/lib/commonjs/components/form/view/index.js +29 -18
  28. package/lib/commonjs/components/form/view/index.js.map +1 -1
  29. package/lib/commonjs/components/form/view/multipleSelect.js +17 -13
  30. package/lib/commonjs/components/form/view/multipleSelect.js.map +1 -1
  31. package/lib/commonjs/components/form/view/select.js +14 -7
  32. package/lib/commonjs/components/form/view/select.js.map +1 -1
  33. package/lib/commonjs/components/form/view/styles.js +0 -3
  34. package/lib/commonjs/components/form/view/styles.js.map +1 -1
  35. package/lib/commonjs/components/layout/CollapseHorizontal.js +72 -0
  36. package/lib/commonjs/components/layout/CollapseHorizontal.js.map +1 -0
  37. package/lib/commonjs/contexts/root.js +94 -1
  38. package/lib/commonjs/contexts/root.js.map +1 -1
  39. package/lib/commonjs/index.js +11 -0
  40. package/lib/commonjs/index.js.map +1 -1
  41. package/lib/commonjs/types/forms.js +6 -0
  42. package/lib/commonjs/types/forms.js.map +1 -0
  43. package/lib/commonjs/types/index.js +17 -0
  44. package/lib/commonjs/types/index.js.map +1 -0
  45. package/lib/module/components/button/Toggle.js +21 -18
  46. package/lib/module/components/button/Toggle.js.map +1 -1
  47. package/lib/module/components/form/input/Editable.js +62 -93
  48. package/lib/module/components/form/input/Editable.js.map +1 -1
  49. package/lib/module/components/form/input/StandaloneInput.js +5 -1
  50. package/lib/module/components/form/input/StandaloneInput.js.map +1 -1
  51. package/lib/module/components/form/input/datetime/index.js +7 -2
  52. package/lib/module/components/form/input/datetime/index.js.map +1 -1
  53. package/lib/module/components/form/input/hooks/useInputStyles.js +2 -1
  54. package/lib/module/components/form/input/hooks/useInputStyles.js.map +1 -1
  55. package/lib/module/components/form/input/multipleSelect/index.js +25 -11
  56. package/lib/module/components/form/input/multipleSelect/index.js.map +1 -1
  57. package/lib/module/components/form/input/number/index.js +8 -4
  58. package/lib/module/components/form/input/number/index.js.map +1 -1
  59. package/lib/module/components/form/input/select/index.js +14 -4
  60. package/lib/module/components/form/input/select/index.js.map +1 -1
  61. package/lib/module/components/form/input/text/AutoResizeTextarea.js +48 -0
  62. package/lib/module/components/form/input/text/AutoResizeTextarea.js.map +1 -0
  63. package/lib/module/components/form/input/text/index.js +24 -12
  64. package/lib/module/components/form/input/text/index.js.map +1 -1
  65. package/lib/module/components/form/input/text/textarea.js +15 -35
  66. package/lib/module/components/form/input/text/textarea.js.map +1 -1
  67. package/lib/module/components/form/input/text/useWebAutoResize.js +58 -0
  68. package/lib/module/components/form/input/text/useWebAutoResize.js.map +1 -0
  69. package/lib/module/components/form/view/file.js +15 -6
  70. package/lib/module/components/form/view/file.js.map +1 -1
  71. package/lib/module/components/form/view/index.js +19 -8
  72. package/lib/module/components/form/view/index.js.map +1 -1
  73. package/lib/module/components/form/view/multipleSelect.js +18 -14
  74. package/lib/module/components/form/view/multipleSelect.js.map +1 -1
  75. package/lib/module/components/form/view/select.js +14 -7
  76. package/lib/module/components/form/view/select.js.map +1 -1
  77. package/lib/module/components/form/view/styles.js +0 -3
  78. package/lib/module/components/form/view/styles.js.map +1 -1
  79. package/lib/module/components/layout/CollapseHorizontal.js +64 -0
  80. package/lib/module/components/layout/CollapseHorizontal.js.map +1 -0
  81. package/lib/module/contexts/root.js +95 -2
  82. package/lib/module/contexts/root.js.map +1 -1
  83. package/lib/module/index.js +1 -0
  84. package/lib/module/index.js.map +1 -1
  85. package/lib/module/types/forms.js +2 -0
  86. package/lib/module/types/forms.js.map +1 -0
  87. package/lib/module/types/index.js +2 -0
  88. package/lib/module/types/index.js.map +1 -0
  89. package/lib/typescript/components/button/Toggle.d.ts +5 -2
  90. package/lib/typescript/components/button/Toggle.d.ts.map +1 -1
  91. package/lib/typescript/components/form/input/Editable.d.ts +2 -2
  92. package/lib/typescript/components/form/input/Editable.d.ts.map +1 -1
  93. package/lib/typescript/components/form/input/StandaloneInput.d.ts +5 -12
  94. package/lib/typescript/components/form/input/StandaloneInput.d.ts.map +1 -1
  95. package/lib/typescript/components/form/input/datetime/index.d.ts.map +1 -1
  96. package/lib/typescript/components/form/input/multipleSelect/index.d.ts.map +1 -1
  97. package/lib/typescript/components/form/input/number/index.d.ts.map +1 -1
  98. package/lib/typescript/components/form/input/select/index.d.ts.map +1 -1
  99. package/lib/typescript/components/form/input/text/AutoResizeTextarea.d.ts +4 -0
  100. package/lib/typescript/components/form/input/text/AutoResizeTextarea.d.ts.map +1 -0
  101. package/lib/typescript/components/form/input/text/index.d.ts.map +1 -1
  102. package/lib/typescript/components/form/input/text/textarea.d.ts +2 -2
  103. package/lib/typescript/components/form/input/text/textarea.d.ts.map +1 -1
  104. package/lib/typescript/components/form/input/text/useWebAutoResize.d.ts +8 -0
  105. package/lib/typescript/components/form/input/text/useWebAutoResize.d.ts.map +1 -0
  106. package/lib/typescript/components/form/types.d.ts +2 -2
  107. package/lib/typescript/components/form/types.d.ts.map +1 -1
  108. package/lib/typescript/components/form/view/file.d.ts.map +1 -1
  109. package/lib/typescript/components/form/view/index.d.ts +3 -2
  110. package/lib/typescript/components/form/view/index.d.ts.map +1 -1
  111. package/lib/typescript/components/form/view/multipleSelect.d.ts.map +1 -1
  112. package/lib/typescript/components/form/view/select.d.ts +2 -1
  113. package/lib/typescript/components/form/view/select.d.ts.map +1 -1
  114. package/lib/typescript/components/form/view/styles.d.ts +0 -3
  115. package/lib/typescript/components/form/view/styles.d.ts.map +1 -1
  116. package/lib/typescript/components/layout/CollapseHorizontal.d.ts +9 -0
  117. package/lib/typescript/components/layout/CollapseHorizontal.d.ts.map +1 -0
  118. package/lib/typescript/contexts/root.d.ts +4 -1
  119. package/lib/typescript/contexts/root.d.ts.map +1 -1
  120. package/lib/typescript/index.d.ts +1 -0
  121. package/lib/typescript/index.d.ts.map +1 -1
  122. package/lib/typescript/types/forms.d.ts +5 -0
  123. package/lib/typescript/types/forms.d.ts.map +1 -0
  124. package/lib/typescript/types/index.d.ts +2 -0
  125. package/lib/typescript/types/index.d.ts.map +1 -0
  126. package/package.json +3 -3
  127. package/src/components/button/Toggle.tsx +21 -17
  128. package/src/components/form/input/Editable.tsx +57 -113
  129. package/src/components/form/input/StandaloneInput.tsx +26 -13
  130. package/src/components/form/input/datetime/index.tsx +7 -2
  131. package/src/components/form/input/hooks/useInputStyles.ts +1 -1
  132. package/src/components/form/input/multipleSelect/index.tsx +34 -20
  133. package/src/components/form/input/number/index.tsx +20 -4
  134. package/src/components/form/input/select/index.tsx +15 -3
  135. package/src/components/form/input/text/AutoResizeTextarea.tsx +45 -0
  136. package/src/components/form/input/text/index.tsx +35 -17
  137. package/src/components/form/input/text/textarea.tsx +10 -36
  138. package/src/components/form/input/text/useWebAutoResize.tsx +71 -0
  139. package/src/components/form/types.ts +6 -2
  140. package/src/components/form/view/file.tsx +9 -5
  141. package/src/components/form/view/index.tsx +26 -7
  142. package/src/components/form/view/multipleSelect.tsx +11 -9
  143. package/src/components/form/view/select.tsx +7 -2
  144. package/src/components/form/view/styles.ts +0 -3
  145. package/src/components/layout/CollapseHorizontal.tsx +82 -0
  146. package/src/contexts/root.tsx +101 -3
  147. package/src/index.ts +1 -0
  148. package/src/types/forms.ts +14 -0
  149. package/src/types/index.ts +1 -0
@@ -8,49 +8,67 @@ import { useInputStyles } from '../hooks/useInputStyles'
8
8
  import { TextAreaComponent } from './textarea'
9
9
 
10
10
  export const TextComponent = forwardRef<InputRef, FieldProps<TextField>>(
11
- ({ input, inEditable, meta, field, style, formSize }, ref) => {
11
+ ({ input, inEditable, meta, field, style, formSize, inputStyle }, ref) => {
12
12
  const { placeholder, type } = field
13
13
  const [isHidden, setIsHidden] = useState(type === 'password')
14
14
  const { value, onChange, ...inputProps } = input
15
15
  const inputRef = useRef<TextInput>(null)
16
- const { container: inputStyles } = useInputStyles(inEditable, field.size, formSize)
16
+ const { container: conatinerStyles, text: inputStyles } = useInputStyles(
17
+ inEditable,
18
+ field.size,
19
+ formSize,
20
+ )
17
21
 
18
22
  useImperativeHandle(ref, () => ({
19
23
  focus: () => {
20
24
  inputRef.current?.focus()
21
25
  },
22
26
  blur: () => {
23
- inputRef.current?.blur()
27
+ inputRef.current?.clear()
24
28
  },
25
29
  }))
26
30
 
27
31
  const placeholderColor = usePlaceholderColor()
28
32
  const color = useTextColor()
29
- const body =
30
- type === 'textarea' ? (
31
- <TextAreaComponent inEditable={inEditable} field={field} input={input} meta={meta} />
32
- ) : (
33
- <TextInput
33
+
34
+ if (type === 'textarea') {
35
+ return (
36
+ <TextAreaComponent
34
37
  ref={inputRef}
35
- style={[inputStyles, { color }, style]}
36
- secureTextEntry={isHidden}
37
- placeholderTextColor={placeholderColor}
38
- placeholder={placeholder}
39
- value={value ?? ''}
40
- onChangeText={onChange}
41
- {...inputProps}
38
+ formSize={formSize}
39
+ inputStyle={inputStyle}
40
+ inEditable={inEditable}
41
+ field={field}
42
+ style={style}
43
+ input={input}
44
+ meta={meta}
42
45
  />
43
46
  )
47
+ }
48
+
49
+ const body = (
50
+ <TextInput
51
+ ref={inputRef}
52
+ style={[inputStyles, { color }, inputStyle]}
53
+ secureTextEntry={isHidden}
54
+ selectTextOnFocus={inEditable}
55
+ placeholderTextColor={placeholderColor}
56
+ placeholder={placeholder}
57
+ value={value ?? ''}
58
+ onChangeText={onChange}
59
+ {...inputProps}
60
+ />
61
+ )
44
62
 
45
63
  return type === 'password' ? (
46
- <View style={styles.passwordContainer}>
64
+ <View style={[conatinerStyles, styles.passwordContainer, style]}>
47
65
  {body}
48
66
  <TouchableOpacity style={styles.eyeIcon} onPress={() => setIsHidden(!isHidden)}>
49
67
  <Ionicons name={isHidden ? 'eye' : 'eye-off'} size={20} color="#666" />
50
68
  </TouchableOpacity>
51
69
  </View>
52
70
  ) : (
53
- body
71
+ <View style={[conatinerStyles, style]}>{body}</View>
54
72
  )
55
73
  },
56
74
  )
@@ -1,38 +1,25 @@
1
- import { InputRef } from '@chem-po/core'
2
1
  import { TextField, usePlaceholderColor, useTextColor } from '@chem-po/react'
3
- import React, { forwardRef, useImperativeHandle, useRef } from 'react'
4
- import { StyleSheet, TextInput, View } from 'react-native'
2
+ import React, { forwardRef } from 'react'
3
+ import { TextInput, View } from 'react-native'
5
4
  import { FieldProps } from '../../types'
6
5
  import { useInputStyles } from '../hooks/useInputStyles'
6
+ import { AutoResizeTextarea } from './AutoResizeTextarea'
7
7
 
8
- export const TextAreaComponent = forwardRef<InputRef, FieldProps<TextField>>(
9
- ({ input, field, inEditable, formSize }, ref) => {
10
- const inputRef = useRef<TextInput>(null)
11
- const { container: inputStyles } = useInputStyles(inEditable, field.size, formSize)
8
+ export const TextAreaComponent = forwardRef<TextInput, FieldProps<TextField>>(
9
+ ({ input, field, inEditable, formSize, inputStyle, style }, ref) => {
10
+ const { text, container } = useInputStyles(inEditable, field.size, formSize)
12
11
  const { value, onChange, ...inputProps } = input
13
-
14
- useImperativeHandle(ref, () => ({
15
- focus: () => {
16
- inputRef.current?.focus()
17
- },
18
- blur: () => {
19
- inputRef.current?.blur()
20
- },
21
- }))
22
-
23
12
  const placeholderColor = usePlaceholderColor()
24
13
  const color = useTextColor()
25
14
 
26
15
  return (
27
- <View style={inputStyles}>
28
- <TextInput
29
- ref={inputRef}
30
- style={[styles.textarea, inEditable && styles.inEditable, { color }]}
16
+ <View style={[container, style]}>
17
+ <AutoResizeTextarea
18
+ ref={ref}
19
+ style={[{ color, ...text }, inputStyle]}
31
20
  placeholder={field.placeholder}
32
21
  placeholderTextColor={placeholderColor}
33
22
  multiline
34
- maxLength={200}
35
- textAlignVertical="top"
36
23
  value={value ?? ''}
37
24
  onChangeText={onChange}
38
25
  {...inputProps}
@@ -43,16 +30,3 @@ export const TextAreaComponent = forwardRef<InputRef, FieldProps<TextField>>(
43
30
  )
44
31
 
45
32
  TextAreaComponent.displayName = 'TextAreaComponent'
46
-
47
- const styles = StyleSheet.create({
48
- textarea: {
49
- width: '100%',
50
- minHeight: 80,
51
- backgroundColor: 'transparent',
52
- textAlignVertical: 'top',
53
- },
54
- inEditable: {
55
- paddingHorizontal: 0,
56
- paddingVertical: 0,
57
- },
58
- })
@@ -0,0 +1,71 @@
1
+ import { useEffect, useMemo, useRef, useState } from 'react'
2
+ import { Platform, StyleProp, StyleSheet, TextStyle } from 'react-native'
3
+
4
+ // creates a span element with the text and style and returns the height
5
+ const getWebTextHeight = (text: string, spanRef: React.RefObject<HTMLSpanElement | null>) => {
6
+ if (!spanRef.current) return null
7
+ const span = spanRef.current
8
+ span.innerText = `${text}\n`
9
+ document.body.appendChild(span)
10
+ const height = span.offsetHeight
11
+ document.body.removeChild(span)
12
+ return height
13
+ }
14
+
15
+ const updateSpanStyle = (span: HTMLSpanElement, style: TextStyle | undefined) => {
16
+ span.style.whiteSpace = 'pre-wrap'
17
+ if (style?.fontFamily) {
18
+ span.style.fontFamily = style.fontFamily
19
+ }
20
+ if (style?.fontSize) {
21
+ span.style.fontSize = style.fontSize.toString() + 'px'
22
+ }
23
+ if (style?.fontWeight) {
24
+ span.style.fontWeight = style.fontWeight.toString()
25
+ }
26
+ if (style?.lineHeight) {
27
+ span.style.lineHeight = style.lineHeight.toString()
28
+ }
29
+ if (style?.letterSpacing) {
30
+ span.style.letterSpacing = style.letterSpacing.toString()
31
+ }
32
+ if (style?.textAlign) {
33
+ span.style.textAlign = style.textAlign
34
+ }
35
+ }
36
+
37
+ export const useWebAutoResize = (
38
+ onChangeText: ((e: string) => void) | undefined,
39
+ style: StyleProp<TextStyle> | undefined,
40
+ ) => {
41
+ const flattenedStyle = useMemo(() => StyleSheet.flatten(style), [style])
42
+ const [height, setHeight] = useState((flattenedStyle.fontSize ?? 18) + 4)
43
+ const spanRef = useRef<HTMLSpanElement>(
44
+ Platform.OS === 'web' ? document.createElement('span') : null,
45
+ )
46
+
47
+ useEffect(() => {
48
+ if (Platform.OS === 'web' && spanRef.current) {
49
+ updateSpanStyle(spanRef.current, flattenedStyle)
50
+ }
51
+ }, [flattenedStyle])
52
+ const handleChange = useMemo(
53
+ () =>
54
+ Platform.OS === 'web'
55
+ ? (e: string) => {
56
+ if (spanRef.current) {
57
+ const height = getWebTextHeight(e, spanRef)
58
+ setHeight((height ?? 18) + 4)
59
+ if (onChangeText) onChangeText(e)
60
+ }
61
+ }
62
+ : onChangeText,
63
+ [onChangeText],
64
+ )
65
+ return {
66
+ getWebTextHeight,
67
+ handleChange,
68
+ height,
69
+ setHeight,
70
+ }
71
+ }
@@ -1,4 +1,8 @@
1
1
  import { BaseFieldProps, Field } from '@chem-po/react'
2
- import { StyleProp } from 'react-native'
2
+ import { StyleProp, TextStyle, ViewStyle } from 'react-native'
3
3
 
4
- export type FieldProps<T extends Field> = BaseFieldProps<StyleProp<any>, T>
4
+ export type FieldProps<
5
+ T extends Field,
6
+ ContainerStyle extends ViewStyle = ViewStyle,
7
+ InputStyle extends TextStyle = TextStyle,
8
+ > = BaseFieldProps<StyleProp<ContainerStyle>, StyleProp<InputStyle>, T>
@@ -1,8 +1,10 @@
1
1
  import { ImageViewOptions, InputSize } from '@chem-po/core'
2
- import { FileField } from '@chem-po/react'
2
+ import { FileField, usePlaceholderColor } from '@chem-po/react'
3
3
  import React, { useMemo } from 'react'
4
4
  import { StyleSheet, Text, View, ViewStyle } from 'react-native'
5
+ import { Txt } from '../../text/Txt'
5
6
  import { FileView } from '../input/file'
7
+ import { useInputStyles } from '../input/hooks/useInputStyles'
6
8
 
7
9
  const styles = StyleSheet.create({
8
10
  container: {
@@ -16,9 +18,6 @@ const styles = StyleSheet.create({
16
18
  opacity: 0.7,
17
19
  fontWeight: '600',
18
20
  },
19
- emptyText: {
20
- opacity: 0.7,
21
- },
22
21
  fileContainer: {
23
22
  padding: 4,
24
23
  },
@@ -59,6 +58,9 @@ export const FileFieldView = ({
59
58
  [imageOptions, size, sizeProp],
60
59
  )
61
60
 
61
+ const { text, container } = useInputStyles(false, field.size, sizeProp)
62
+ const placeholderColor = usePlaceholderColor()
63
+
62
64
  return (
63
65
  <View style={[value ? styles.container : styles.rowContainer, style]}>
64
66
  {!noLabel && <Text style={styles.label}>{placeholder}</Text>}
@@ -67,7 +69,9 @@ export const FileFieldView = ({
67
69
  <FileView imageOptions={options} value={value} />
68
70
  </View>
69
71
  ) : (
70
- <Text style={styles.emptyText}>None</Text>
72
+ <View style={container}>
73
+ <Txt style={[text, { color: placeholderColor }]}>None</Txt>
74
+ </View>
71
75
  )}
72
76
  </View>
73
77
  )
@@ -1,5 +1,5 @@
1
1
  import { displayField, InputSize } from '@chem-po/core'
2
- import { Field, FormatField } from '@chem-po/react'
2
+ import { Field, FormatField, usePlaceholderColor } from '@chem-po/react'
3
3
  import React, { useMemo } from 'react'
4
4
  import { StyleSheet, View, ViewStyle } from 'react-native'
5
5
  import { Txt } from '../../text'
@@ -22,30 +22,33 @@ const DefaultFieldView = ({
22
22
  noLabel,
23
23
  style,
24
24
  size: sizeProp,
25
+ inEditable,
25
26
  }: {
26
27
  field: Field
27
28
  value: any
28
29
  noLabel?: boolean
29
30
  style?: ViewStyle
30
31
  size?: InputSize
32
+ inEditable?: boolean
31
33
  }) => {
32
34
  const { placeholder } = field
33
35
 
34
- const { text } = useInputStyles(false, field.size, sizeProp)
36
+ const { text, container: containerStyles } = useInputStyles(inEditable, field.size, sizeProp)
35
37
  const formatted = useMemo(() => {
36
38
  const format = displayField[field._type] as FormatField<Field>
37
39
  if (!format) return value
38
40
  return format(field, value)
39
41
  }, [value, field])
40
42
 
43
+ const placeholderColor = usePlaceholderColor()
41
44
  const hasValue = useMemo(() => {
42
45
  return value !== null && value !== undefined && value !== ''
43
46
  }, [value])
44
47
 
45
48
  return (
46
- <View style={[styles.container, style]}>
49
+ <View style={[styles.container, containerStyles, { backgroundColor: 'transparent' }, style]}>
47
50
  {!noLabel && <Txt style={[inputViewStyles.label, text]}>{placeholder}</Txt>}
48
- <Txt style={[hasValue ? inputViewStyles.value : inputViewStyles.valueEmpty, text]}>
51
+ <Txt style={[text, { color: hasValue ? undefined : placeholderColor }]}>
49
52
  {hasValue ? formatted : 'None'}
50
53
  </Txt>
51
54
  </View>
@@ -58,17 +61,26 @@ export const FieldView = ({
58
61
  noLabel,
59
62
  style,
60
63
  size,
64
+ inEditable,
61
65
  }: {
62
66
  field: Field
63
- value: any
67
+ value?: any
64
68
  noLabel?: boolean
65
69
  style?: ViewStyle
66
70
  size?: InputSize
71
+ inEditable?: boolean
67
72
  }) => {
68
73
  switch (field._type) {
69
74
  case 'select':
70
75
  return (
71
- <SelectFieldView style={style} field={field} value={value} noLabel={noLabel} size={size} />
76
+ <SelectFieldView
77
+ style={style}
78
+ field={field}
79
+ value={value}
80
+ noLabel={noLabel}
81
+ size={size}
82
+ inEditable={inEditable}
83
+ />
72
84
  )
73
85
  case 'multipleSelect':
74
86
  return (
@@ -86,7 +98,14 @@ export const FieldView = ({
86
98
  )
87
99
  default:
88
100
  return (
89
- <DefaultFieldView style={style} field={field} value={value} noLabel={noLabel} size={size} />
101
+ <DefaultFieldView
102
+ style={style}
103
+ field={field}
104
+ value={value}
105
+ noLabel={noLabel}
106
+ size={size}
107
+ inEditable={inEditable}
108
+ />
90
109
  )
91
110
  }
92
111
  }
@@ -1,7 +1,8 @@
1
1
  import { InputSize } from '@chem-po/core'
2
- import { MultipleSelectField, useColorMode } from '@chem-po/react'
2
+ import { MultipleSelectField, useColorMode, usePlaceholderColor } from '@chem-po/react'
3
3
  import { useMemo } from 'react'
4
4
  import React, { StyleSheet, Text, View, ViewStyle } from 'react-native'
5
+ import { Txt } from '../../text/Txt'
5
6
  import { useInputStyles } from '../input/hooks/useInputStyles'
6
7
  import { SelectedOptionContainer } from '../input/multipleSelect'
7
8
  import { DefaultRenderOption } from './select'
@@ -12,6 +13,7 @@ const styles = StyleSheet.create({
12
13
  flexWrap: 'wrap',
13
14
  alignItems: 'center',
14
15
  maxWidth: '100%',
16
+ backgroundColor: 'transparent',
15
17
  },
16
18
  label: {
17
19
  paddingRight: 8,
@@ -27,9 +29,6 @@ const styles = StyleSheet.create({
27
29
  marginTop: 2,
28
30
  marginHorizontal: 2,
29
31
  },
30
- emptyText: {
31
- opacity: 0.6,
32
- },
33
32
  })
34
33
 
35
34
  export const MultipleSelectFieldView = <F extends MultipleSelectField>({
@@ -48,13 +47,14 @@ export const MultipleSelectFieldView = <F extends MultipleSelectField>({
48
47
  const { placeholder, renderOption: customRender, options } = field
49
48
  const selectedOptions = options.filter(o => value?.includes(o.value))
50
49
  const colorMode = useColorMode()
51
- const { size, container: inputStyles, text } = useInputStyles(undefined, field.size, sizeProp)
50
+ const { size, text, container } = useInputStyles(true, field.size, sizeProp)
51
+ const placeholderColor = usePlaceholderColor()
52
52
  const selectedOptionStyle = useMemo<ViewStyle>(() => {
53
53
  return {
54
- paddingHorizontal: inputStyles.paddingHorizontal,
55
- paddingVertical: inputStyles.paddingVertical,
54
+ paddingHorizontal: container.paddingHorizontal,
55
+ paddingVertical: container.paddingVertical,
56
56
  }
57
- }, [inputStyles])
57
+ }, [container])
58
58
  const RenderOption = useMemo(() => customRender ?? DefaultRenderOption, [customRender])
59
59
  return (
60
60
  <View style={[styles.container, style]}>
@@ -74,7 +74,9 @@ export const MultipleSelectFieldView = <F extends MultipleSelectField>({
74
74
  ))}
75
75
  </View>
76
76
  ) : (
77
- <Text style={[styles.emptyText, text]}>None</Text>
77
+ <View style={container}>
78
+ <Txt style={[text, { color: placeholderColor }]}>None</Txt>
79
+ </View>
78
80
  )}
79
81
  </View>
80
82
  )
@@ -10,6 +10,7 @@ const styles = StyleSheet.create({
10
10
  container: {
11
11
  flexDirection: 'row',
12
12
  alignItems: 'center',
13
+ backgroundColor: 'transparent',
13
14
  },
14
15
  label: {
15
16
  paddingRight: 10,
@@ -32,17 +33,19 @@ export const SelectFieldView = ({
32
33
  noLabel,
33
34
  style,
34
35
  size: sizeProp,
36
+ inEditable,
35
37
  }: {
36
38
  field: SelectField
37
39
  value: any
38
40
  noLabel?: boolean
39
41
  style?: ViewStyle
40
42
  size?: InputSize
43
+ inEditable?: boolean
41
44
  }) => {
42
45
  const { placeholder, renderOption: customRender } = field
43
46
  const colorMode = useColorMode()
44
47
  const placeholderColor = usePlaceholderColor()
45
- const { size, text } = useInputStyles(false, field.size, sizeProp)
48
+ const { size, text, container } = useInputStyles(inEditable, field.size, sizeProp)
46
49
  const selectedOption = field.options.find(o => o.value === value)
47
50
  const RenderOption = customRender ?? DefaultRenderOption
48
51
  return (
@@ -59,7 +62,9 @@ export const SelectFieldView = ({
59
62
  size={size}
60
63
  />
61
64
  ) : (
62
- <Txt style={inputViewStyles.valueEmpty}>None</Txt>
65
+ <View style={container}>
66
+ <Txt style={[text, { color: placeholderColor }]}>None</Txt>
67
+ </View>
63
68
  )}
64
69
  </View>
65
70
  )
@@ -9,7 +9,4 @@ export const inputViewStyles = StyleSheet.create({
9
9
  value: {
10
10
  opacity: 1,
11
11
  },
12
- valueEmpty: {
13
- opacity: 0.6,
14
- },
15
12
  })
@@ -0,0 +1,82 @@
1
+ import React, { useCallback } from 'react'
2
+ import { LayoutChangeEvent, StyleProp, View, ViewStyle } from 'react-native'
3
+ import Animated, {
4
+ useAnimatedStyle,
5
+ useSharedValue,
6
+ withSequence,
7
+ withTiming,
8
+ } from 'react-native-reanimated'
9
+
10
+ export const CollapseHorizontal = ({
11
+ children,
12
+ in: isIn,
13
+ duration = 200,
14
+ style,
15
+ }: {
16
+ children: React.ReactNode
17
+ in: boolean
18
+ duration?: number
19
+ style?: StyleProp<ViewStyle>
20
+ }) => {
21
+ const contentWidth = useSharedValue(0)
22
+ const width = useSharedValue(0)
23
+ const opacity = useSharedValue(0)
24
+
25
+ const handleLayout = useCallback(
26
+ (event: LayoutChangeEvent) => {
27
+ contentWidth.value = event.nativeEvent.layout.width
28
+ // Initialize width if component is initially visible
29
+ if (isIn && width.value === 0) {
30
+ width.value = contentWidth.value
31
+ opacity.value = 1
32
+ }
33
+ },
34
+ [isIn, contentWidth, width, opacity],
35
+ )
36
+
37
+ React.useEffect(() => {
38
+ if (isIn) {
39
+ // Fade in: width first, then opacity
40
+ width.value = withTiming(contentWidth.value, {
41
+ duration,
42
+ })
43
+ opacity.value = withSequence(
44
+ withTiming(0, { duration: 0 }), // Reset opacity
45
+ withTiming(1, {
46
+ duration,
47
+ }),
48
+ )
49
+ } else {
50
+ // Fade out: opacity first, then width
51
+ opacity.value = withTiming(0, {
52
+ duration,
53
+ })
54
+ width.value = withSequence(
55
+ withTiming(width.value, { duration: 0 }), // Ensure we start from current width
56
+ withTiming(0, {
57
+ duration,
58
+ }),
59
+ )
60
+ }
61
+ }, [isIn, contentWidth.value, duration, width, opacity])
62
+
63
+ const animatedStyle = useAnimatedStyle(() => ({
64
+ width: width.value,
65
+ opacity: opacity.value,
66
+ }))
67
+
68
+ return (
69
+ <Animated.View
70
+ style={[
71
+ {
72
+ overflow: 'hidden',
73
+ pointerEvents: isIn ? 'auto' : 'none',
74
+ },
75
+ animatedStyle,
76
+ ]}>
77
+ <View style={[{ position: 'absolute' }, style]} onLayout={handleLayout}>
78
+ {children}
79
+ </View>
80
+ </Animated.View>
81
+ )
82
+ }
@@ -1,24 +1,121 @@
1
1
  import { BackendAdapterInterface, ColorMode, Theme } from '@chem-po/core'
2
- import { ChempoProps, ChempoProvider } from '@chem-po/react'
2
+ import { ChempoProps, ChempoProvider, useTheme } from '@chem-po/react'
3
3
  import React, { PropsWithChildren, useMemo } from 'react'
4
4
  import { NotifierWrapper } from 'react-native-notifier'
5
+ import { configureFonts, PaperProvider, Props as PaperProviderProps } from 'react-native-paper'
5
6
  import { en, registerTranslation } from 'react-native-paper-dates'
7
+ import { MD3Type, MD3TypescaleKey } from 'react-native-paper/lib/typescript/types'
6
8
  import { nativeToast } from '../constants/toast'
7
9
  import { useThemeState } from '../hooks/useThemeState'
8
10
  import { initializeScreen } from '../store/useScreen'
9
11
 
10
12
  registerTranslation('en', en)
11
13
 
14
+ export type FontConfig = Partial<Record<MD3TypescaleKey, Partial<MD3Type>>>
12
15
  export interface ChempoNativeProviderProps<BackendAdapter extends BackendAdapterInterface>
13
16
  extends PropsWithChildren<Pick<ChempoProps<BackendAdapter>, 'backendAdapter' | 'assets'>> {
14
17
  theme?: Theme
18
+ fonts?: FontConfig
15
19
  initialColorMode?: ColorMode
16
20
  }
21
+ const createPaperTheme = (
22
+ theme: Theme,
23
+ fonts: FontConfig | undefined,
24
+ colorMode: ColorMode,
25
+ ): PaperProviderProps['theme'] => {
26
+ const { colors } = theme
27
+
28
+ return {
29
+ dark: colorMode === 'dark',
30
+ colors: {
31
+ primary: colors.accent[300],
32
+ onPrimary: colors.text[900],
33
+ primaryContainer: colors.accent[800],
34
+ onPrimaryContainer: colors.text[150],
35
+
36
+ secondary: colors.background[150],
37
+ onSecondary: colors.text[100],
38
+ secondaryContainer: colors.accent[950],
39
+ onSecondaryContainer: colors.text[150],
40
+
41
+ tertiary: colors.accent[600],
42
+ onTertiary: colors.text[50],
43
+ tertiaryContainer: colors.accent[300],
44
+ onTertiaryContainer: colors.text[700],
45
+
46
+ error: colors.error[500],
47
+ onError: colors.text[50],
48
+ errorContainer: colors.error[100],
49
+ onErrorContainer: colors.error[900],
50
+
51
+ background: colors.background[50],
52
+ onBackground: colors.text[100],
53
+ surface: colors.background[100],
54
+ onSurface: colors.text[100],
55
+ surfaceVariant: colors.background[200],
56
+ onSurfaceVariant: colors.text[300],
57
+ outline: colors.text[500],
58
+ outlineVariant: colors.accent[300],
59
+
60
+ surfaceDisabled: colors.background[200],
61
+ onSurfaceDisabled: colors.text[400],
62
+ backdrop: '#000000aa', // 60% opacity
63
+
64
+ inverseSurface: colors.background[900],
65
+ inverseOnSurface: colors.text[50],
66
+ inversePrimary: colors.accent[200],
67
+ shadow: '#00000099',
68
+ scrim: '#00000099', // Semi-transparent dark overlay
69
+
70
+ elevation: {
71
+ level0: 'transparent',
72
+ level1: colors.background[100],
73
+ level2: colors.background[200],
74
+ level3: colors.background[300],
75
+ level4: colors.background[400],
76
+ level5: colors.background[500],
77
+ },
78
+ },
79
+ roundness: 4,
80
+ animation: {
81
+ scale: 1.0,
82
+ },
83
+ fonts: configureFonts({
84
+ config: {
85
+ ...fonts,
86
+ bodyLarge: {
87
+ fontSize: 18,
88
+ ...fonts?.bodyLarge,
89
+ },
90
+ bodyMedium: {
91
+ fontSize: 17,
92
+ ...fonts?.bodyMedium,
93
+ },
94
+ bodySmall: {
95
+ fontSize: 16,
96
+ ...fonts?.bodySmall,
97
+ },
98
+ },
99
+ }),
100
+ }
101
+ }
102
+
103
+ const ChempoPaperProvider = ({ children, fonts }: PropsWithChildren<{ fonts?: FontConfig }>) => {
104
+ const { theme, colorMode } = useTheme()
105
+
106
+ const paperTheme = useMemo(
107
+ () => createPaperTheme(theme, fonts, colorMode),
108
+ [theme, fonts, colorMode],
109
+ )
110
+ return <PaperProvider theme={paperTheme}>{children}</PaperProvider>
111
+ }
17
112
  export const ChempoNativeProvider = <
18
113
  BackendAdapter extends BackendAdapterInterface<any, any, any, any, any, any>,
19
114
  >({
20
115
  theme: themeProp,
21
116
  initialColorMode,
117
+ children,
118
+ fonts,
22
119
  ...props
23
120
  }: ChempoNativeProviderProps<BackendAdapter>) => {
24
121
  const useThemeProps = useMemo(
@@ -32,8 +129,9 @@ export const ChempoNativeProvider = <
32
129
  toast={nativeToast}
33
130
  theme={theme}
34
131
  initializeScreen={initializeScreen}
35
- {...props}
36
- />
132
+ {...props}>
133
+ <ChempoPaperProvider fonts={fonts}>{children}</ChempoPaperProvider>
134
+ </ChempoProvider>
37
135
  </NotifierWrapper>
38
136
  )
39
137
  }