@oxyhq/bloom 0.1.18 → 0.1.20

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 (58) hide show
  1. package/lib/commonjs/avatar/Avatar.js +10 -2
  2. package/lib/commonjs/avatar/Avatar.js.map +1 -1
  3. package/lib/commonjs/button/Button.js +14 -2
  4. package/lib/commonjs/button/Button.js.map +1 -1
  5. package/lib/commonjs/index.js +12 -0
  6. package/lib/commonjs/index.js.map +1 -1
  7. package/lib/commonjs/settings-list/SettingsList.js +215 -0
  8. package/lib/commonjs/settings-list/SettingsList.js.map +1 -0
  9. package/lib/commonjs/settings-list/index.js +25 -0
  10. package/lib/commonjs/settings-list/index.js.map +1 -0
  11. package/lib/commonjs/settings-list/types.js +6 -0
  12. package/lib/commonjs/settings-list/types.js.map +1 -0
  13. package/lib/module/avatar/Avatar.js +10 -2
  14. package/lib/module/avatar/Avatar.js.map +1 -1
  15. package/lib/module/button/Button.js +13 -1
  16. package/lib/module/button/Button.js.map +1 -1
  17. package/lib/module/index.js +3 -0
  18. package/lib/module/index.js.map +1 -1
  19. package/lib/module/settings-list/SettingsList.js +210 -0
  20. package/lib/module/settings-list/SettingsList.js.map +1 -0
  21. package/lib/module/settings-list/index.js +4 -0
  22. package/lib/module/settings-list/index.js.map +1 -0
  23. package/lib/module/settings-list/types.js +4 -0
  24. package/lib/module/settings-list/types.js.map +1 -0
  25. package/lib/typescript/commonjs/avatar/Avatar.d.ts.map +1 -1
  26. package/lib/typescript/commonjs/button/Button.d.ts +1 -0
  27. package/lib/typescript/commonjs/button/Button.d.ts.map +1 -1
  28. package/lib/typescript/commonjs/button/types.d.ts +1 -1
  29. package/lib/typescript/commonjs/button/types.d.ts.map +1 -1
  30. package/lib/typescript/commonjs/index.d.ts +1 -0
  31. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  32. package/lib/typescript/commonjs/settings-list/SettingsList.d.ts +6 -0
  33. package/lib/typescript/commonjs/settings-list/SettingsList.d.ts.map +1 -0
  34. package/lib/typescript/commonjs/settings-list/index.d.ts +3 -0
  35. package/lib/typescript/commonjs/settings-list/index.d.ts.map +1 -0
  36. package/lib/typescript/commonjs/settings-list/types.d.ts +36 -0
  37. package/lib/typescript/commonjs/settings-list/types.d.ts.map +1 -0
  38. package/lib/typescript/module/avatar/Avatar.d.ts.map +1 -1
  39. package/lib/typescript/module/button/Button.d.ts +1 -0
  40. package/lib/typescript/module/button/Button.d.ts.map +1 -1
  41. package/lib/typescript/module/button/types.d.ts +1 -1
  42. package/lib/typescript/module/button/types.d.ts.map +1 -1
  43. package/lib/typescript/module/index.d.ts +1 -0
  44. package/lib/typescript/module/index.d.ts.map +1 -1
  45. package/lib/typescript/module/settings-list/SettingsList.d.ts +6 -0
  46. package/lib/typescript/module/settings-list/SettingsList.d.ts.map +1 -0
  47. package/lib/typescript/module/settings-list/index.d.ts +3 -0
  48. package/lib/typescript/module/settings-list/index.d.ts.map +1 -0
  49. package/lib/typescript/module/settings-list/types.d.ts +36 -0
  50. package/lib/typescript/module/settings-list/types.d.ts.map +1 -0
  51. package/package.json +1 -1
  52. package/src/avatar/Avatar.tsx +8 -2
  53. package/src/button/Button.tsx +13 -1
  54. package/src/button/types.ts +1 -1
  55. package/src/index.ts +3 -0
  56. package/src/settings-list/SettingsList.tsx +233 -0
  57. package/src/settings-list/index.ts +2 -0
  58. package/src/settings-list/types.ts +38 -0
@@ -31,7 +31,7 @@ const SIZE_CONFIG = {
31
31
  const ICON_HIT_SLOP = { top: 10, bottom: 10, left: 10, right: 10 } as const;
32
32
 
33
33
  const PRESS_SCALE = 0.97;
34
- const SCALE_VARIANTS = new Set<string>(['primary', 'secondary']);
34
+ const SCALE_VARIANTS = new Set<string>(['primary', 'secondary', 'inverse']);
35
35
 
36
36
  const ButtonComponent: React.FC<ButtonProps> = ({
37
37
  onPress,
@@ -97,6 +97,10 @@ const ButtonComponent: React.FC<ButtonProps> = ({
97
97
  styles.borderColor = theme.colors.border;
98
98
  styles.borderRadius = 20;
99
99
  break;
100
+ case 'inverse':
101
+ styles.backgroundColor = '#FFFFFF';
102
+ styles.borderRadius = 20;
103
+ break;
100
104
  case 'icon':
101
105
  styles.backgroundColor = theme.colors.background;
102
106
  styles.borderWidth = 1;
@@ -134,6 +138,9 @@ const ButtonComponent: React.FC<ButtonProps> = ({
134
138
  case 'secondary':
135
139
  styles.color = theme.colors.text;
136
140
  break;
141
+ case 'inverse':
142
+ styles.color = '#000000';
143
+ break;
137
144
  case 'ghost':
138
145
  case 'text':
139
146
  styles.color = theme.colors.primary;
@@ -199,6 +206,11 @@ export const GhostButton = memo((props: Omit<ButtonProps, 'variant'>) => (
199
206
  ));
200
207
  GhostButton.displayName = 'GhostButton';
201
208
 
209
+ export const InverseButton = memo((props: Omit<ButtonProps, 'variant'>) => (
210
+ <Button {...props} variant="inverse" />
211
+ ));
212
+ InverseButton.displayName = 'InverseButton';
213
+
202
214
  export const TextButton = memo((props: Omit<ButtonProps, 'variant'>) => (
203
215
  <Button {...props} variant="text" />
204
216
  ));
@@ -1,6 +1,6 @@
1
1
  import type { StyleProp, ViewStyle, TextStyle } from 'react-native';
2
2
 
3
- export type ButtonVariant = 'primary' | 'secondary' | 'icon' | 'ghost' | 'text';
3
+ export type ButtonVariant = 'primary' | 'secondary' | 'inverse' | 'icon' | 'ghost' | 'text';
4
4
 
5
5
  export type ButtonSize = 'small' | 'medium' | 'large';
6
6
 
package/src/index.ts CHANGED
@@ -60,6 +60,9 @@ export * as Tabs from './tabs';
60
60
  export * from './checkbox';
61
61
  export * as Accordion from './accordion';
62
62
 
63
+ // Settings / Grouped list
64
+ export * from './settings-list';
65
+
63
66
  // Overlay components
64
67
  export * as Admonition from './admonition';
65
68
  export * as Menu from './menu';
@@ -0,0 +1,233 @@
1
+ import React, { memo } from 'react';
2
+ import { View, Text, Pressable, StyleSheet } from 'react-native';
3
+ import Svg, { Path } from 'react-native-svg';
4
+
5
+ import { useTheme } from '../theme/use-theme';
6
+ import type {
7
+ SettingsListItemProps,
8
+ SettingsListGroupProps,
9
+ SettingsListDividerProps,
10
+ } from './types';
11
+
12
+ // ── Chevron icon (inline to avoid external dependency) ──────────
13
+ const Chevron = memo(({ size = 16, color }: { size?: number; color: string }) => (
14
+ <Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
15
+ <Path
16
+ d="M9.29 6.71c-.39.39-.39 1.02 0 1.41L13.17 12l-3.88 3.88c-.39.39-.39 1.02 0 1.41.39.39 1.02.39 1.41 0l4.59-4.59c.39-.39.39-1.02 0-1.41L10.7 6.7c-.38-.38-1.02-.38-1.41.01z"
17
+ fill={color}
18
+ />
19
+ </Svg>
20
+ ));
21
+ Chevron.displayName = 'Chevron';
22
+
23
+ // ── SettingsListItem ────────────────────────────────────────────
24
+
25
+ export const SettingsListItem = memo<SettingsListItemProps>(function SettingsListItem({
26
+ icon,
27
+ title,
28
+ description,
29
+ value,
30
+ rightElement,
31
+ showChevron,
32
+ destructive = false,
33
+ onPress,
34
+ disabled = false,
35
+ }) {
36
+ const theme = useTheme();
37
+ const hasChevron = showChevron ?? Boolean(onPress);
38
+ const titleColor = destructive ? theme.colors.error : theme.colors.text;
39
+ const pressedOpacity = disabled ? 1 : 0.6;
40
+
41
+ const content = (
42
+ <View style={styles.itemContainer}>
43
+ {icon ? (
44
+ <View style={styles.iconContainer}>{icon}</View>
45
+ ) : null}
46
+
47
+ <View style={styles.textContainer}>
48
+ <Text
49
+ style={[styles.title, { color: titleColor }]}
50
+ numberOfLines={1}
51
+ >
52
+ {title}
53
+ </Text>
54
+ {description ? (
55
+ <Text
56
+ style={[styles.description, { color: theme.colors.textSecondary }]}
57
+ numberOfLines={2}
58
+ >
59
+ {description}
60
+ </Text>
61
+ ) : null}
62
+ </View>
63
+
64
+ {value ? (
65
+ <Text
66
+ style={[styles.value, { color: theme.colors.textSecondary }]}
67
+ numberOfLines={1}
68
+ >
69
+ {value}
70
+ </Text>
71
+ ) : null}
72
+
73
+ {rightElement}
74
+
75
+ {hasChevron ? (
76
+ <Chevron size={16} color={theme.colors.textTertiary} />
77
+ ) : null}
78
+ </View>
79
+ );
80
+
81
+ if (onPress) {
82
+ return (
83
+ <Pressable
84
+ onPress={onPress}
85
+ disabled={disabled}
86
+ android_ripple={{ color: theme.colors.border }}
87
+ style={({ pressed }) => [
88
+ disabled && styles.disabled,
89
+ pressed && { opacity: pressedOpacity },
90
+ ]}
91
+ >
92
+ {content}
93
+ </Pressable>
94
+ );
95
+ }
96
+
97
+ return content;
98
+ });
99
+
100
+ // ── SettingsListGroup ───────────────────────────────────────────
101
+
102
+ export const SettingsListGroup = memo<SettingsListGroupProps>(function SettingsListGroup({
103
+ title,
104
+ footer,
105
+ children,
106
+ style,
107
+ }) {
108
+ const theme = useTheme();
109
+ const filteredChildren = React.Children.toArray(children).filter(Boolean);
110
+
111
+ return (
112
+ <View style={[styles.groupContainer, style]}>
113
+ {title ? (
114
+ <Text style={[styles.groupTitle, { color: theme.colors.textSecondary }]}>
115
+ {title}
116
+ </Text>
117
+ ) : null}
118
+
119
+ <View
120
+ style={[
121
+ styles.groupCard,
122
+ { backgroundColor: theme.colors.backgroundSecondary },
123
+ ]}
124
+ >
125
+ {filteredChildren.map((child, index) => (
126
+ <React.Fragment key={index}>
127
+ {child}
128
+ {index < filteredChildren.length - 1 ? (
129
+ <View
130
+ style={[
131
+ styles.divider,
132
+ { backgroundColor: theme.colors.border, opacity: 0.3 },
133
+ ]}
134
+ />
135
+ ) : null}
136
+ </React.Fragment>
137
+ ))}
138
+ </View>
139
+
140
+ {footer ? (
141
+ <Text style={[styles.groupFooter, { color: theme.colors.textTertiary }]}>
142
+ {footer}
143
+ </Text>
144
+ ) : null}
145
+ </View>
146
+ );
147
+ });
148
+
149
+ // ── SettingsListDivider ─────────────────────────────────────────
150
+
151
+ export const SettingsListDivider = memo<SettingsListDividerProps>(
152
+ function SettingsListDivider({ inset = 52 }) {
153
+ const theme = useTheme();
154
+ return (
155
+ <View
156
+ style={[
157
+ styles.divider,
158
+ { marginLeft: inset, backgroundColor: theme.colors.border, opacity: 0.3 },
159
+ ]}
160
+ />
161
+ );
162
+ }
163
+ );
164
+
165
+ // ── Styles ──────────────────────────────────────────────────────
166
+
167
+ const styles = StyleSheet.create({
168
+ // Item
169
+ itemContainer: {
170
+ flexDirection: 'row',
171
+ alignItems: 'center',
172
+ paddingHorizontal: 16,
173
+ paddingVertical: 10,
174
+ minHeight: 44,
175
+ gap: 12,
176
+ },
177
+ iconContainer: {
178
+ width: 20,
179
+ height: 20,
180
+ alignItems: 'center',
181
+ justifyContent: 'center',
182
+ },
183
+ textContainer: {
184
+ flex: 1,
185
+ },
186
+ title: {
187
+ fontSize: 15,
188
+ fontWeight: '500',
189
+ lineHeight: 20,
190
+ },
191
+ description: {
192
+ fontSize: 13,
193
+ lineHeight: 17,
194
+ },
195
+ value: {
196
+ fontSize: 13,
197
+ lineHeight: 17,
198
+ },
199
+ disabled: {
200
+ opacity: 0.5,
201
+ },
202
+
203
+ // Group
204
+ groupContainer: {
205
+ marginBottom: 16,
206
+ },
207
+ groupCard: {
208
+ marginHorizontal: 16,
209
+ borderRadius: 16,
210
+ overflow: 'hidden',
211
+ },
212
+ groupTitle: {
213
+ fontSize: 11,
214
+ fontWeight: '600',
215
+ textTransform: 'uppercase',
216
+ letterSpacing: 0.6,
217
+ paddingHorizontal: 20,
218
+ paddingTop: 4,
219
+ paddingBottom: 6,
220
+ },
221
+ groupFooter: {
222
+ fontSize: 12,
223
+ lineHeight: 16,
224
+ paddingHorizontal: 20,
225
+ paddingTop: 6,
226
+ },
227
+
228
+ // Divider
229
+ divider: {
230
+ height: StyleSheet.hairlineWidth,
231
+ marginLeft: 16,
232
+ },
233
+ });
@@ -0,0 +1,2 @@
1
+ export { SettingsListItem, SettingsListGroup, SettingsListDivider } from './SettingsList';
2
+ export type { SettingsListItemProps, SettingsListGroupProps, SettingsListDividerProps } from './types';
@@ -0,0 +1,38 @@
1
+ import type { StyleProp, ViewStyle } from 'react-native';
2
+
3
+ export interface SettingsListItemProps {
4
+ /** Icon element (e.g. SVG icon component) or Ionicons-style string */
5
+ icon?: React.ReactNode;
6
+ /** Primary label */
7
+ title: string;
8
+ /** Secondary description text below title */
9
+ description?: string;
10
+ /** Right-side value text (e.g. "English", "On") */
11
+ value?: string;
12
+ /** Custom right-side element (toggle, badge, etc.) */
13
+ rightElement?: React.ReactNode;
14
+ /** Show trailing chevron (default: true when onPress is set) */
15
+ showChevron?: boolean;
16
+ /** Destructive action styling (red text) */
17
+ destructive?: boolean;
18
+ /** Press handler */
19
+ onPress?: () => void;
20
+ /** Disabled state */
21
+ disabled?: boolean;
22
+ }
23
+
24
+ export interface SettingsListGroupProps {
25
+ /** Optional section header text */
26
+ title?: string;
27
+ /** Optional section footer text */
28
+ footer?: string;
29
+ /** Group items */
30
+ children: React.ReactNode;
31
+ /** Override group container style */
32
+ style?: StyleProp<ViewStyle>;
33
+ }
34
+
35
+ export interface SettingsListDividerProps {
36
+ /** Inset from left edge to align with text (default: 52) */
37
+ inset?: number;
38
+ }