@expo/ui 56.0.3 → 56.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.
Files changed (71) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/android/build.gradle +2 -2
  3. package/build/swift-ui/ScrollView/index.d.ts +3 -2
  4. package/build/swift-ui/ScrollView/index.d.ts.map +1 -1
  5. package/build/swift-ui/modifiers/index.d.ts +15 -1
  6. package/build/swift-ui/modifiers/index.d.ts.map +1 -1
  7. package/build/universal/Button/index.d.ts.map +1 -1
  8. package/build/universal/Checkbox/index.d.ts.map +1 -1
  9. package/build/universal/Column/index.d.ts.map +1 -1
  10. package/build/universal/FieldGroup/FieldGroup.d.ts.map +1 -1
  11. package/build/universal/FieldGroup/FieldSection.d.ts.map +1 -1
  12. package/build/universal/Row/index.d.ts.map +1 -1
  13. package/build/universal/ScrollView/index.d.ts.map +1 -1
  14. package/build/universal/Slider/index.d.ts.map +1 -1
  15. package/build/universal/Spacer/index.d.ts.map +1 -1
  16. package/build/universal/Switch/index.d.ts.map +1 -1
  17. package/build/universal/Text/index.d.ts.map +1 -1
  18. package/expo-module.config.json +1 -1
  19. package/ios/Convertibles/AxisOptions.swift +19 -0
  20. package/ios/Convertibles/ScrollIndicatorVisibilityOptions.swift +22 -0
  21. package/ios/Modifiers/ContainerRelativeFrameModifier.swift +1 -18
  22. package/ios/Modifiers/ScrollIndicatorsModifier.swift +13 -0
  23. package/ios/Modifiers/ViewModifierRegistry.swift +4 -17
  24. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.3/expo.modules.ui-56.0.3.module → 56.0.5/expo.modules.ui-56.0.5.module} +7 -7
  25. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.5/expo.modules.ui-56.0.5.module.md5 +1 -0
  26. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.5/expo.modules.ui-56.0.5.module.sha1 +1 -0
  27. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.5/expo.modules.ui-56.0.5.module.sha256 +1 -0
  28. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.5/expo.modules.ui-56.0.5.module.sha512 +1 -0
  29. package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.3/expo.modules.ui-56.0.3.pom → 56.0.5/expo.modules.ui-56.0.5.pom} +1 -1
  30. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.5/expo.modules.ui-56.0.5.pom.md5 +1 -0
  31. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.5/expo.modules.ui-56.0.5.pom.sha1 +1 -0
  32. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.5/expo.modules.ui-56.0.5.pom.sha256 +1 -0
  33. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.5/expo.modules.ui-56.0.5.pom.sha512 +1 -0
  34. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml +4 -4
  35. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.md5 +1 -1
  36. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha1 +1 -1
  37. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha256 +1 -1
  38. package/local-maven-repo/expo/modules/ui/expo.modules.ui/maven-metadata.xml.sha512 +1 -1
  39. package/package.json +4 -4
  40. package/src/swift-ui/ScrollView/index.tsx +3 -2
  41. package/src/swift-ui/modifiers/index.ts +19 -0
  42. package/src/ts-declarations/react-native-web.d.ts +112 -0
  43. package/src/universal/Button/index.tsx +37 -50
  44. package/src/universal/Checkbox/index.tsx +31 -18
  45. package/src/universal/Column/index.tsx +29 -18
  46. package/src/universal/FieldGroup/FieldGroup.tsx +24 -28
  47. package/src/universal/FieldGroup/FieldSection.tsx +62 -80
  48. package/src/universal/Row/index.tsx +37 -24
  49. package/src/universal/ScrollView/index.tsx +26 -15
  50. package/src/universal/Slider/index.tsx +21 -16
  51. package/src/universal/Spacer/index.tsx +18 -10
  52. package/src/universal/Switch/index.tsx +15 -10
  53. package/src/universal/Text/index.tsx +18 -37
  54. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.3/expo.modules.ui-56.0.3.module.md5 +0 -1
  55. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.3/expo.modules.ui-56.0.3.module.sha1 +0 -1
  56. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.3/expo.modules.ui-56.0.3.module.sha256 +0 -1
  57. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.3/expo.modules.ui-56.0.3.module.sha512 +0 -1
  58. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.3/expo.modules.ui-56.0.3.pom.md5 +0 -1
  59. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.3/expo.modules.ui-56.0.3.pom.sha1 +0 -1
  60. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.3/expo.modules.ui-56.0.3.pom.sha256 +0 -1
  61. package/local-maven-repo/expo/modules/ui/expo.modules.ui/56.0.3/expo.modules.ui-56.0.3.pom.sha512 +0 -1
  62. /package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.3/expo.modules.ui-56.0.3-sources.jar → 56.0.5/expo.modules.ui-56.0.5-sources.jar} +0 -0
  63. /package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.3/expo.modules.ui-56.0.3-sources.jar.md5 → 56.0.5/expo.modules.ui-56.0.5-sources.jar.md5} +0 -0
  64. /package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.3/expo.modules.ui-56.0.3-sources.jar.sha1 → 56.0.5/expo.modules.ui-56.0.5-sources.jar.sha1} +0 -0
  65. /package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.3/expo.modules.ui-56.0.3-sources.jar.sha256 → 56.0.5/expo.modules.ui-56.0.5-sources.jar.sha256} +0 -0
  66. /package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.3/expo.modules.ui-56.0.3-sources.jar.sha512 → 56.0.5/expo.modules.ui-56.0.5-sources.jar.sha512} +0 -0
  67. /package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.3/expo.modules.ui-56.0.3.aar → 56.0.5/expo.modules.ui-56.0.5.aar} +0 -0
  68. /package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.3/expo.modules.ui-56.0.3.aar.md5 → 56.0.5/expo.modules.ui-56.0.5.aar.md5} +0 -0
  69. /package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.3/expo.modules.ui-56.0.3.aar.sha1 → 56.0.5/expo.modules.ui-56.0.5.aar.sha1} +0 -0
  70. /package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.3/expo.modules.ui-56.0.3.aar.sha256 → 56.0.5/expo.modules.ui-56.0.5.aar.sha256} +0 -0
  71. /package/local-maven-repo/expo/modules/ui/expo.modules.ui/{56.0.3/expo.modules.ui-56.0.3.aar.sha512 → 56.0.5/expo.modules.ui-56.0.5.aar.sha512} +0 -0
@@ -0,0 +1,112 @@
1
+ import * as ReactNative from 'react-native';
2
+
3
+ declare module 'react-native' {
4
+ export const unstable_createElement: <P>(
5
+ type: React.ElementType,
6
+ props?: P
7
+ ) => React.ReactElement<P>;
8
+
9
+ type DisplayValue = ReactNative.FlexStyle['display'] | 'inline-flex';
10
+
11
+ type WebRole =
12
+ | ReactNative.Role
13
+ /**
14
+ * Accessibility roles mapped to components
15
+ * @see https://github.com/necolas/react-native-web/blob/0.19.1/packages/react-native-web/src/modules/AccessibilityUtil/propsToAccessibilityComponent.js
16
+ */
17
+ | 'article' // <article />
18
+ | 'banner' // <header />
19
+ | 'blockquote' // <blockquote />
20
+ | 'button' // <button />
21
+ | 'code' // <code />
22
+ | 'complementary' // <aside />
23
+ | 'contentinfo' // <footer />
24
+ | 'deletion' // <del />
25
+ | 'emphasis' // <em />
26
+ | 'figure' // <figure />
27
+ | 'form' // <form />
28
+ | 'heading' // <h{1,6} />
29
+ | 'insertion' // <ins />
30
+ | 'label' // <label />
31
+ | 'list' // <ul />
32
+ | 'listitem' // <li />
33
+ | 'main' // <main />
34
+ | 'navigation' // <nav />
35
+ | 'paragraph' // <p />
36
+ | 'region' // <section />
37
+ | 'strong'; // <strong />
38
+
39
+ interface WebAccessibilityProps {
40
+ /**
41
+ * Additional accessibility props
42
+ */
43
+ tabIndex?: 0 | -1;
44
+
45
+ /**
46
+ * Aria props (additional, minus existants)
47
+ * @see https://necolas.github.io/react-native-web/docs/accessibility
48
+ * @see https://reactnative.dev/docs/accessibility#aria-valuemax
49
+ */
50
+ 'aria-activedescendant'?: string;
51
+ 'aria-atomic'?: boolean;
52
+ 'aria-autocomplete'?: string;
53
+ 'aria-colcount'?: number;
54
+ 'aria-colindex'?: number;
55
+ 'aria-colspan'?: number;
56
+ 'aria-controls'?: string;
57
+ 'aria-current'?: boolean | 'page' | 'step' | 'location' | 'date' | 'time';
58
+ 'aria-describedby'?: string;
59
+ 'aria-details'?: string;
60
+ 'aria-errormessage'?: string;
61
+ 'aria-flowto'?: string;
62
+ 'aria-haspopup'?: string;
63
+ 'aria-invalid'?: boolean;
64
+ 'aria-keyshortcuts'?: string;
65
+ 'aria-level'?: number;
66
+ 'aria-multiline'?: boolean;
67
+ 'aria-multiselectable'?: boolean;
68
+ 'aria-orientation'?: 'horizontal' | 'vertical';
69
+ 'aria-owns'?: string;
70
+ 'aria-placeholder'?: string;
71
+ 'aria-posinset'?: number;
72
+ 'aria-pressed'?: boolean;
73
+ 'aria-readonly'?: boolean;
74
+ 'aria-required'?: boolean;
75
+ 'aria-roledescription'?: string;
76
+ 'aria-rowcount'?: number;
77
+ 'aria-rowindex'?: number;
78
+ 'aria-rowspan'?: number;
79
+ 'aria-setsize'?: number;
80
+ 'aria-sort'?: 'ascending' | 'descending' | 'none' | 'other';
81
+ }
82
+
83
+ export interface PressableStateCallbackType {
84
+ readonly focused: boolean;
85
+ readonly hovered: boolean;
86
+ readonly pressed: boolean;
87
+ }
88
+
89
+ export interface ImageProps extends WebAccessibilityProps {
90
+ role?: WebRole;
91
+ }
92
+
93
+ export interface TextProps extends WebAccessibilityProps {
94
+ role?: WebRole;
95
+ }
96
+
97
+ export interface ViewProps extends WebAccessibilityProps {
98
+ role?: WebRole;
99
+ }
100
+
101
+ export interface ImageStyle {
102
+ display?: DisplayValue;
103
+ }
104
+
105
+ export interface TextStyle {
106
+ display?: DisplayValue;
107
+ }
108
+
109
+ export interface ViewStyle {
110
+ display?: DisplayValue;
111
+ }
112
+ }
@@ -1,48 +1,40 @@
1
- import { useState } from 'react';
2
- import { Pressable, Text, type ViewStyle } from 'react-native';
1
+ import { Pressable, StyleSheet, Text, type TextStyle, type ViewStyle } from 'react-native';
3
2
 
4
3
  import type { ButtonProps, ButtonVariant } from './types';
5
4
  import { useUniversalLifecycle } from '../hooks';
6
5
 
7
- const variantStyles: Record<ButtonVariant, ViewStyle> = {
8
- filled: {
9
- backgroundColor: '#007AFF',
6
+ const styles = StyleSheet.create({
7
+ button: {
10
8
  paddingHorizontal: 16,
11
9
  paddingVertical: 10,
12
10
  borderRadius: 8,
11
+ userSelect: 'none',
13
12
  },
13
+ disabled: { opacity: 0.5 },
14
+ hidden: { display: 'none' },
15
+ label: { textAlign: 'center' },
16
+ });
17
+
18
+ const variantStyles = StyleSheet.create({
19
+ filled: { backgroundColor: '#007AFF' },
14
20
  outlined: {
15
- backgroundColor: 'transparent',
16
- paddingHorizontal: 16,
17
- paddingVertical: 10,
18
- borderRadius: 8,
19
- borderWidth: 1,
20
21
  borderColor: '#007AFF',
22
+ borderWidth: 1,
21
23
  },
22
- text: {
23
- backgroundColor: 'transparent',
24
- paddingHorizontal: 16,
25
- paddingVertical: 10,
26
- },
27
- };
24
+ text: {},
25
+ } satisfies Record<ButtonVariant, ViewStyle>);
28
26
 
29
- const variantHoverStyles: Record<ButtonVariant, ViewStyle> = {
30
- filled: {
31
- backgroundColor: '#0066DB',
32
- },
33
- outlined: {
34
- backgroundColor: 'rgba(0, 122, 255, 0.08)',
35
- },
36
- text: {
37
- backgroundColor: 'rgba(0, 122, 255, 0.08)',
38
- },
39
- };
27
+ const variantHoverStyles = StyleSheet.create({
28
+ filled: { backgroundColor: '#0066DB' },
29
+ outlined: { backgroundColor: 'rgba(0, 122, 255, 0.08)' },
30
+ text: { backgroundColor: 'rgba(0, 122, 255, 0.08)' },
31
+ } satisfies Record<ButtonVariant, ViewStyle>);
40
32
 
41
- const variantTextColors: Record<ButtonVariant, string> = {
42
- filled: '#FFFFFF',
43
- outlined: '#007AFF',
44
- text: '#007AFF',
45
- };
33
+ const variantLabelStyles = StyleSheet.create({
34
+ filled: { color: '#FFFFFF' },
35
+ outlined: { color: '#007AFF' },
36
+ text: { color: '#007AFF' },
37
+ } satisfies Record<ButtonVariant, TextStyle>);
46
38
 
47
39
  /**
48
40
  * A pressable button that supports multiple visual variants.
@@ -55,32 +47,27 @@ export function Button({
55
47
  style,
56
48
  onAppear,
57
49
  onDisappear,
58
- disabled,
59
- hidden,
50
+ disabled = false,
51
+ hidden = false,
60
52
  testID,
61
53
  }: ButtonProps) {
62
54
  useUniversalLifecycle(onAppear, onDisappear);
63
- const [hovered, setHovered] = useState(false);
64
-
65
- const pressableStyle: ViewStyle = {
66
- ...variantStyles[variant],
67
- ...style,
68
- ...(hovered && !disabled ? variantHoverStyles[variant] : undefined),
69
- ...(hidden ? { display: 'none' } : undefined),
70
- ...(disabled ? { opacity: 0.5 } : undefined),
71
- };
72
55
 
73
56
  return (
74
57
  <Pressable
75
- style={pressableStyle}
58
+ role="button"
76
59
  onPress={onPress}
77
- onHoverIn={() => setHovered(true)}
78
- onHoverOut={() => setHovered(false)}
79
60
  disabled={disabled}
80
- testID={testID}>
81
- {children ?? (
82
- <Text style={{ color: variantTextColors[variant], textAlign: 'center' }}>{label}</Text>
83
- )}
61
+ testID={testID}
62
+ style={({ hovered }) => [
63
+ styles.button,
64
+ variantStyles[variant],
65
+ style,
66
+ hovered && !disabled && variantHoverStyles[variant],
67
+ hidden && styles.hidden,
68
+ disabled && styles.disabled,
69
+ ]}>
70
+ {children ?? <Text style={[styles.label, variantLabelStyles[variant]]}>{label}</Text>}
84
71
  </Pressable>
85
72
  );
86
73
  }
@@ -1,33 +1,46 @@
1
+ import type { ComponentProps } from 'react';
2
+ import { StyleSheet, Text, unstable_createElement, View, type ViewProps } from 'react-native';
3
+
1
4
  import type { CheckboxProps } from './types';
2
5
 
6
+ const styles = StyleSheet.create({
7
+ label: {
8
+ flexDirection: 'row',
9
+ display: 'inline-flex',
10
+ alignItems: 'center',
11
+ gap: 8,
12
+ cursor: 'pointer',
13
+ },
14
+ disabled: {
15
+ opacity: 0.5,
16
+ cursor: 'auto',
17
+ },
18
+ cursorInherit: {
19
+ // @ts-expect-error
20
+ cursor: 'inherit',
21
+ },
22
+ });
23
+
24
+ const NativeCheckbox = (
25
+ props: Omit<ComponentProps<'input'>, 'style' | 'type'> & { style?: ViewProps['style'] }
26
+ ) => unstable_createElement('input', { ...props, type: 'checkbox' });
27
+
3
28
  /**
4
29
  * A toggle control that represents a checked or unchecked state.
5
30
  */
6
- export function Checkbox({ value, onValueChange, label, disabled, testID }: CheckboxProps) {
31
+ export function Checkbox({ value, onValueChange, label, disabled = false, testID }: CheckboxProps) {
7
32
  return (
8
- <label style={{ ...labelStyle, ...(disabled ? disabledStyle : undefined) }}>
9
- <input
10
- type="checkbox"
33
+ <View role="label" aria-disabled={disabled} style={[styles.label, disabled && styles.disabled]}>
34
+ <NativeCheckbox
11
35
  checked={value}
12
36
  onChange={(e) => onValueChange(e.target.checked)}
13
37
  disabled={disabled}
14
38
  data-testid={testID}
39
+ style={styles.cursorInherit}
15
40
  />
16
- {label != null && <span>{label}</span>}
17
- </label>
41
+ {label != null && <Text>{label}</Text>}
42
+ </View>
18
43
  );
19
44
  }
20
45
 
21
- const labelStyle: React.CSSProperties = {
22
- display: 'inline-flex',
23
- alignItems: 'center',
24
- gap: 8,
25
- cursor: 'pointer',
26
- };
27
-
28
- const disabledStyle: React.CSSProperties = {
29
- opacity: 0.5,
30
- cursor: 'default',
31
- };
32
-
33
46
  export * from './types';
@@ -1,14 +1,23 @@
1
- import { Pressable, View, type ViewStyle } from 'react-native';
1
+ import { Pressable, StyleSheet, View, type ViewStyle } from 'react-native';
2
2
 
3
3
  import type { ColumnProps } from './types';
4
4
  import { useUniversalLifecycle } from '../hooks';
5
5
  import type { UniversalAlignment } from '../types';
6
6
 
7
- const alignmentMap: Record<UniversalAlignment, ViewStyle['alignItems']> = {
8
- start: 'flex-start',
9
- center: 'center',
10
- end: 'flex-end',
11
- };
7
+ const styles = StyleSheet.create({
8
+ column: { flexDirection: 'column' },
9
+ hidden: { display: 'none' },
10
+ disabled: {
11
+ opacity: 0.5,
12
+ pointerEvents: 'none',
13
+ },
14
+ });
15
+
16
+ const alignmentStyles = StyleSheet.create({
17
+ start: { alignItems: 'flex-start' },
18
+ center: { alignItems: 'center' },
19
+ end: { alignItems: 'flex-end' },
20
+ } satisfies Record<UniversalAlignment, ViewStyle>);
12
21
 
13
22
  /**
14
23
  * A vertical layout container that arranges its children from top to bottom.
@@ -21,25 +30,27 @@ export function Column({
21
30
  onPress,
22
31
  onAppear,
23
32
  onDisappear,
24
- disabled,
25
- hidden,
33
+ disabled = false,
34
+ hidden = false,
26
35
  testID,
27
36
  }: ColumnProps) {
28
37
  useUniversalLifecycle(onAppear, onDisappear);
29
38
 
30
- const viewStyle: ViewStyle = {
31
- flexDirection: 'column',
32
- alignItems: alignmentMap[alignment],
33
- gap: spacing,
34
- ...style,
35
- ...(hidden ? { display: 'none' } : undefined),
36
- ...(disabled ? { opacity: 0.5, pointerEvents: 'none' } : undefined),
37
- };
38
-
39
39
  const Container = onPress ? Pressable : View;
40
40
 
41
41
  return (
42
- <Container style={viewStyle} onPress={onPress} disabled={disabled} testID={testID}>
42
+ <Container
43
+ onPress={onPress}
44
+ disabled={disabled}
45
+ testID={testID}
46
+ style={[
47
+ styles.column,
48
+ alignmentStyles[alignment],
49
+ spacing != null && { gap: spacing },
50
+ style,
51
+ hidden && styles.hidden,
52
+ disabled && styles.disabled,
53
+ ]}>
43
54
  {children}
44
55
  </Container>
45
56
  );
@@ -1,9 +1,27 @@
1
- import { ScrollView, useColorScheme, type ViewStyle } from 'react-native';
1
+ import { ScrollView, StyleSheet, useColorScheme } from 'react-native';
2
2
 
3
3
  import { groupFieldGroupChildren } from './groupChildren';
4
4
  import type { FieldGroupProps } from './types';
5
5
  import { useUniversalLifecycle } from '../hooks';
6
6
 
7
+ const styles = StyleSheet.create({
8
+ container: {
9
+ backgroundColor: '#f2f2f7',
10
+ flex: 1,
11
+ },
12
+ containerDark: {
13
+ backgroundColor: '#000000',
14
+ },
15
+ hidden: {
16
+ display: 'none',
17
+ },
18
+ contentContainer: {
19
+ paddingHorizontal: 16,
20
+ paddingVertical: 16,
21
+ gap: 24,
22
+ },
23
+ });
24
+
7
25
  /**
8
26
  * A scrollable container for grouped settings-style rows, mirroring the look
9
27
  * of an iOS Settings screen. Render one or more
@@ -16,7 +34,7 @@ export function FieldGroup({
16
34
  style,
17
35
  onAppear,
18
36
  onDisappear,
19
- hidden,
37
+ hidden = false,
20
38
  testID,
21
39
  }: FieldGroupProps) {
22
40
  useUniversalLifecycle(onAppear, onDisappear);
@@ -26,36 +44,14 @@ export function FieldGroup({
26
44
  return (
27
45
  <ScrollView
28
46
  style={[
29
- containerBaseStyle,
30
- isDarkScheme ? containerDarkStyle : containerLightStyle,
47
+ styles.container,
48
+ isDarkScheme && styles.containerDark,
31
49
  style,
32
- hidden ? hiddenStyle : null,
50
+ hidden && styles.hidden,
33
51
  ]}
34
- contentContainerStyle={contentContainerStyle}
52
+ contentContainerStyle={styles.contentContainer}
35
53
  testID={testID}>
36
54
  {groupFieldGroupChildren(children)}
37
55
  </ScrollView>
38
56
  );
39
57
  }
40
-
41
- const containerBaseStyle: ViewStyle = {
42
- flex: 1,
43
- };
44
-
45
- const containerLightStyle: ViewStyle = {
46
- backgroundColor: '#f2f2f7',
47
- };
48
-
49
- const containerDarkStyle: ViewStyle = {
50
- backgroundColor: '#000000',
51
- };
52
-
53
- const hiddenStyle: ViewStyle = {
54
- display: 'none',
55
- };
56
-
57
- const contentContainerStyle: ViewStyle = {
58
- paddingHorizontal: 16,
59
- paddingVertical: 16,
60
- gap: 24,
61
- };
@@ -1,10 +1,62 @@
1
1
  import { Fragment } from 'react';
2
- import { Text, useColorScheme, View, type TextStyle, type ViewStyle } from 'react-native';
2
+ import { StyleSheet, Text, useColorScheme, View } from 'react-native';
3
3
 
4
4
  import { extractFieldSectionSlots } from './FieldSectionSlots';
5
5
  import type { FieldSectionProps } from './types';
6
6
  import { useUniversalLifecycle } from '../hooks';
7
7
 
8
+ const styles = StyleSheet.create({
9
+ hidden: {
10
+ display: 'none',
11
+ },
12
+ headerContainer: {
13
+ paddingHorizontal: 16,
14
+ paddingBottom: 8,
15
+ },
16
+ headerText: {
17
+ color: '#6c6c70',
18
+ fontSize: 14,
19
+ fontWeight: '500',
20
+ },
21
+ headerTextDark: {
22
+ color: '#98989e',
23
+ },
24
+ headerTextUppercase: {
25
+ textTransform: 'uppercase',
26
+ letterSpacing: 0.5,
27
+ fontSize: 13,
28
+ },
29
+ card: {
30
+ backgroundColor: '#ffffff',
31
+ borderRadius: 12,
32
+ overflow: 'hidden',
33
+ },
34
+ cardDark: {
35
+ backgroundColor: '#1c1c1e',
36
+ },
37
+ // Gives each row the same minimum height SwiftUI `Form` uses for single-line
38
+ // rows on iOS, so a text-only row doesn't collapse to its text intrinsic
39
+ // size. Taller content (e.g. a Switch) grows the row naturally. Horizontal
40
+ // padding matches SwiftUI `Form`'s built-in row leading/trailing inset.
41
+ rowWrapper: {
42
+ minHeight: 44,
43
+ justifyContent: 'center',
44
+ paddingHorizontal: 16,
45
+ },
46
+ divider: {
47
+ backgroundColor: '#e5e5ea',
48
+ height: 1,
49
+ marginLeft: 16,
50
+ },
51
+ dividerDark: {
52
+ backgroundColor: '#38383a',
53
+ },
54
+ footerContainer: {
55
+ paddingHorizontal: 16,
56
+ paddingTop: 8,
57
+ },
58
+ });
59
+
8
60
  /**
9
61
  * A grouped list of rows within a [`FieldGroup`](#fieldgroup). Each direct
10
62
  * child is rendered as a single row. Use `title` for a simple caption header,
@@ -27,105 +79,35 @@ export function FieldSection({
27
79
 
28
80
  const { header, footer, rows } = extractFieldSectionSlots(children);
29
81
 
30
- const containerStyle: ViewStyle = {
31
- ...style,
32
- ...(hidden ? { display: 'none' } : undefined),
33
- };
34
-
35
82
  const headerNode =
36
83
  header ??
37
84
  (title ? (
38
85
  <Text
39
86
  style={[
40
- headerTextStyle,
41
- isDarkScheme ? headerTextDarkStyle : headerTextLightStyle,
42
- titleUppercase ? headerTextUppercaseStyle : null,
87
+ styles.headerText,
88
+ isDarkScheme && styles.headerTextDark,
89
+ titleUppercase && styles.headerTextUppercase,
43
90
  ]}>
44
91
  {title}
45
92
  </Text>
46
93
  ) : null);
47
94
 
48
95
  return (
49
- <View style={containerStyle} testID={testID}>
50
- {headerNode ? <View style={headerContainerStyle}>{headerNode}</View> : null}
96
+ <View style={[style, hidden && styles.hidden]} testID={testID}>
97
+ {headerNode ? <View style={styles.headerContainer}>{headerNode}</View> : null}
51
98
  {rows.length > 0 ? (
52
- <View style={[cardBaseStyle, isDarkScheme ? cardDarkStyle : cardLightStyle]}>
99
+ <View style={[styles.card, isDarkScheme && styles.cardDark]}>
53
100
  {rows.map((child, index) => (
54
101
  <Fragment key={index}>
55
- <View style={rowWrapperStyle}>{child}</View>
102
+ <View style={styles.rowWrapper}>{child}</View>
56
103
  {index < rows.length - 1 ? (
57
- <View style={[dividerStyle, isDarkScheme ? dividerDarkStyle : dividerLightStyle]} />
104
+ <View style={[styles.divider, isDarkScheme && styles.dividerDark]} />
58
105
  ) : null}
59
106
  </Fragment>
60
107
  ))}
61
108
  </View>
62
109
  ) : null}
63
- {footer ? <View style={footerContainerStyle}>{footer}</View> : null}
110
+ {footer ? <View style={styles.footerContainer}>{footer}</View> : null}
64
111
  </View>
65
112
  );
66
113
  }
67
-
68
- const headerContainerStyle: ViewStyle = {
69
- paddingHorizontal: 16,
70
- paddingBottom: 8,
71
- };
72
-
73
- const headerTextStyle: TextStyle = {
74
- fontSize: 14,
75
- fontWeight: '500',
76
- };
77
-
78
- const headerTextLightStyle: TextStyle = {
79
- color: '#6c6c70',
80
- };
81
-
82
- const headerTextDarkStyle: TextStyle = {
83
- color: '#98989e',
84
- };
85
-
86
- const headerTextUppercaseStyle: TextStyle = {
87
- textTransform: 'uppercase',
88
- letterSpacing: 0.5,
89
- fontSize: 13,
90
- };
91
-
92
- const cardBaseStyle: ViewStyle = {
93
- borderRadius: 12,
94
- overflow: 'hidden',
95
- };
96
-
97
- const cardLightStyle: ViewStyle = {
98
- backgroundColor: '#ffffff',
99
- };
100
-
101
- const cardDarkStyle: ViewStyle = {
102
- backgroundColor: '#1c1c1e',
103
- };
104
-
105
- // Gives each row the same minimum height SwiftUI `Form` uses for single-line
106
- // rows on iOS, so a text-only row doesn't collapse to its text intrinsic
107
- // size. Taller content (e.g. a Switch) grows the row naturally. Horizontal
108
- // padding matches SwiftUI `Form`'s built-in row leading/trailing inset.
109
- const rowWrapperStyle: ViewStyle = {
110
- minHeight: 44,
111
- justifyContent: 'center',
112
- paddingHorizontal: 16,
113
- };
114
-
115
- const dividerStyle: ViewStyle = {
116
- height: 1,
117
- marginLeft: 16,
118
- };
119
-
120
- const dividerLightStyle: ViewStyle = {
121
- backgroundColor: '#e5e5ea',
122
- };
123
-
124
- const dividerDarkStyle: ViewStyle = {
125
- backgroundColor: '#38383a',
126
- };
127
-
128
- const footerContainerStyle: ViewStyle = {
129
- paddingHorizontal: 16,
130
- paddingTop: 8,
131
- };