rnnovaui 0.1.3 → 0.1.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.
package/package.json CHANGED
@@ -1,18 +1,26 @@
1
1
  {
2
2
  "name": "rnnovaui",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "react-native": "src/index.js",
7
7
  "module": "src/index.js",
8
+ "exports": {
9
+ ".": {
10
+ "react-native": "./src/index.js",
11
+ "import": "./src/index.js",
12
+ "require": "./src/index.js",
13
+ "default": "./src/index.js"
14
+ }
15
+ },
8
16
  "files": [
9
17
  "src",
10
18
  "README.md",
11
19
  "LICENSE"
12
20
  ],
13
21
  "scripts": {
14
- "test": "echo \"Error: no test specified\" && exit 1",
15
- "prepare": "echo \"Package ready\""
22
+ "test": "node -e \"console.log('rnnovaui test passed')\"",
23
+ "prepare": "echo \"rnnovaui Package ready\""
16
24
  },
17
25
  "keywords": [
18
26
  "react-native",
@@ -33,7 +41,7 @@
33
41
  "react-native-reanimated": "^4.5.3"
34
42
  },
35
43
  "peerDependencies": {
36
- "react": "^19.2.3",
37
- "react-native": "^0.86.2"
44
+ "react": ">=19.0.0",
45
+ "react-native": ">=0.76.0"
38
46
  }
39
47
  }
@@ -10,7 +10,7 @@ import {
10
10
 
11
11
  import { useUITheme } from "../../theme";
12
12
 
13
- const VARIANTS = {
13
+ const BUTTON_VARIANTS = {
14
14
  solid: "solid",
15
15
  outline: "outline",
16
16
  ghost: "ghost",
@@ -19,7 +19,7 @@ const VARIANTS = {
19
19
  success: "success",
20
20
  };
21
21
 
22
- const SIZES = {
22
+ const BUTTON_SIZES = {
23
23
  sm: "sm",
24
24
  md: "md",
25
25
  lg: "lg",
@@ -35,10 +35,11 @@ function UIButtonComponent({
35
35
 
36
36
  disabled = false,
37
37
  loading = false,
38
+
38
39
  fullWidth = false,
39
40
 
40
- leftIcon,
41
- rightIcon,
41
+ leftIcon = null,
42
+ rightIcon = null,
42
43
 
43
44
  onPress,
44
45
  onLongPress,
@@ -52,30 +53,35 @@ function UIButtonComponent({
52
53
  activeOpacity = 0.82,
53
54
 
54
55
  testID,
56
+
55
57
  accessibilityLabel,
56
58
  accessibilityHint,
57
59
 
58
60
  ...props
59
61
  }) {
60
- const themeResult = useUITheme();
61
-
62
62
  /*
63
- * Support both possible provider shapes:
63
+ * YOUR UIProvider RETURNS:
64
64
  *
65
- * themeResult.theme.colors
66
- * themeResult.colors
65
+ * {
66
+ * theme,
67
+ * mode,
68
+ * selectedMode,
69
+ * setMode
70
+ * }
67
71
  */
68
- const theme = themeResult?.theme || themeResult || {};
72
+ const { theme } = useUITheme();
69
73
 
70
- const colors = theme.colors || {};
74
+ const colors = theme.colors;
71
75
 
72
- const safeVariant = VARIANTS[variant] || VARIANTS.solid;
76
+ const safeVariant = BUTTON_VARIANTS[variant]
77
+ ? variant
78
+ : BUTTON_VARIANTS.solid;
73
79
 
74
- const safeSize = SIZES[size] || SIZES.md;
80
+ const safeSize = BUTTON_SIZES[size] ? size : BUTTON_SIZES.md;
75
81
 
76
- const buttonColors = getButtonColors(safeVariant, colors);
82
+ const variantStyle = getVariantStyle(safeVariant, colors);
77
83
 
78
- const dimensions = getButtonDimensions(safeSize);
84
+ const sizeStyle = getSizeStyle(safeSize, theme);
79
85
 
80
86
  const isDisabled = disabled || loading;
81
87
 
@@ -147,19 +153,19 @@ function UIButtonComponent({
147
153
  styles.button,
148
154
 
149
155
  {
150
- height: dimensions.height,
156
+ height: sizeStyle.height,
151
157
 
152
- minHeight: dimensions.height,
158
+ minHeight: sizeStyle.height,
153
159
 
154
- paddingHorizontal: dimensions.paddingHorizontal,
160
+ paddingHorizontal: sizeStyle.paddingHorizontal,
155
161
 
156
- borderRadius: dimensions.borderRadius,
162
+ borderRadius: sizeStyle.borderRadius,
157
163
 
158
- backgroundColor: buttonColors.background,
164
+ backgroundColor: variantStyle.backgroundColor,
159
165
 
160
- borderColor: buttonColors.border,
166
+ borderColor: variantStyle.borderColor,
161
167
 
162
- borderWidth: buttonColors.border ? 1 : 0,
168
+ borderWidth: variantStyle.borderWidth,
163
169
 
164
170
  width: fullWidth ? "100%" : undefined,
165
171
 
@@ -174,164 +180,217 @@ function UIButtonComponent({
174
180
  styles.content,
175
181
 
176
182
  {
177
- gap: dimensions.gap,
183
+ gap: sizeStyle.gap,
178
184
  },
179
185
 
180
186
  contentStyle,
181
187
  ]}
182
188
  >
183
189
  {loading ? (
184
- <ActivityIndicator size="small" color={buttonColors.text} />
185
- ) : leftIcon ? (
186
- <View style={styles.icon}>{leftIcon}</View>
187
- ) : null}
190
+ <ActivityIndicator size="small" color={variantStyle.textColor} />
191
+ ) : (
192
+ leftIcon && <View style={styles.icon}>{leftIcon}</View>
193
+ )}
188
194
 
189
195
  {hasChildren ? (
190
196
  <View style={styles.children}>{children}</View>
191
- ) : title ? (
192
- <Text
193
- numberOfLines={1}
194
- style={[
195
- styles.text,
196
-
197
- {
198
- color: buttonColors.text,
199
-
200
- fontSize: dimensions.fontSize,
201
-
202
- lineHeight: dimensions.lineHeight,
203
- },
204
-
205
- textStyle,
206
- ]}
207
- >
208
- {title}
209
- </Text>
210
- ) : null}
211
-
212
- {!loading && rightIcon ? (
213
- <View style={styles.icon}>{rightIcon}</View>
214
- ) : null}
197
+ ) : (
198
+ title !== undefined &&
199
+ title !== null && (
200
+ <Text
201
+ numberOfLines={1}
202
+ style={[
203
+ styles.text,
204
+
205
+ {
206
+ color: variantStyle.textColor,
207
+
208
+ fontSize: sizeStyle.fontSize,
209
+
210
+ lineHeight: sizeStyle.lineHeight,
211
+ },
212
+
213
+ textStyle,
214
+ ]}
215
+ >
216
+ {title}
217
+ </Text>
218
+ )
219
+ )}
220
+
221
+ {!loading && rightIcon && <View style={styles.icon}>{rightIcon}</View>}
215
222
  </View>
216
223
  </Pressable>
217
224
  );
218
225
  }
219
226
 
220
- /**
221
- * IMPORTANT:
222
- *
223
- * Variant resolution is completely independent
224
- * from spacing/radius/typography.
225
- *
226
- * Therefore even if the theme is incomplete,
227
- * variants still work.
228
- */
229
- function getButtonColors(variant, colors) {
230
- const primary = colors.primary || "#FF5A1F";
231
-
232
- const primarySoft = colors.primarySoft || "#FFF0EA";
227
+ function getVariantStyle(variant, colors) {
228
+ /*
229
+ * These are FALLBACKS.
230
+ *
231
+ * Your colors from colors.js
232
+ * will always win.
233
+ */
233
234
 
234
- const success = colors.success || "#16A34A";
235
+ const primary = colors?.primary || "#FF5A1F";
235
236
 
236
- const danger = colors.danger || "#DC2626";
237
+ const primarySoft = colors?.primarySoft || "#FFF0EA";
237
238
 
238
- const text = colors.text || "#111111";
239
+ const success = colors?.success || "#16A34A";
239
240
 
240
- const onPrimary = colors.onPrimary || "#FFFFFF";
241
+ const danger = colors?.danger || "#DC2626";
241
242
 
242
- const border = colors.border || "#E5E5E5";
243
+ const onPrimary = colors?.onPrimary || "#FFFFFF";
243
244
 
244
245
  switch (variant) {
245
- case VARIANTS.outline:
246
+ case "outline":
246
247
  return {
247
- background: "transparent",
248
- border: primary,
249
- text: primary,
248
+ backgroundColor: "transparent",
249
+
250
+ borderColor: primary,
251
+
252
+ borderWidth: 1,
253
+
254
+ textColor: primary,
250
255
  };
251
256
 
252
- case VARIANTS.ghost:
257
+ case "ghost":
253
258
  return {
254
- background: "transparent",
255
- border: null,
256
- text: primary,
259
+ backgroundColor: "transparent",
260
+
261
+ borderColor: "transparent",
262
+
263
+ borderWidth: 0,
264
+
265
+ textColor: primary,
257
266
  };
258
267
 
259
- case VARIANTS.soft:
268
+ case "soft":
260
269
  return {
261
- background: primarySoft,
262
- border: null,
263
- text: primary,
270
+ backgroundColor: primarySoft,
271
+
272
+ borderColor: primarySoft,
273
+
274
+ borderWidth: 1,
275
+
276
+ textColor: primary,
264
277
  };
265
278
 
266
- case VARIANTS.danger:
279
+ case "danger":
267
280
  return {
268
- background: danger,
269
- border: danger,
270
- text: onPrimary,
281
+ backgroundColor: danger,
282
+
283
+ borderColor: danger,
284
+
285
+ borderWidth: 1,
286
+
287
+ textColor: onPrimary,
271
288
  };
272
289
 
273
- case VARIANTS.success:
290
+ case "success":
274
291
  return {
275
- background: success,
276
- border: success,
277
- text: onPrimary,
292
+ backgroundColor: success,
293
+
294
+ borderColor: success,
295
+
296
+ borderWidth: 1,
297
+
298
+ textColor: onPrimary,
278
299
  };
279
300
 
280
- case VARIANTS.solid:
301
+ case "solid":
281
302
  default:
282
303
  return {
283
- background: primary,
284
- border: primary,
285
- text: onPrimary,
304
+ backgroundColor: primary,
305
+
306
+ borderColor: primary,
307
+
308
+ borderWidth: 1,
309
+
310
+ textColor: onPrimary,
286
311
  };
287
312
  }
288
313
  }
289
314
 
290
- /**
291
- * Sizes intentionally use fixed fallback
292
- * values so the button cannot break when
293
- * theme.sizes is missing.
294
- */
295
- function getButtonDimensions(size) {
315
+ function getSizeStyle(size, theme) {
316
+ /*
317
+ * Use theme sizes when available,
318
+ * otherwise safe fallback.
319
+ */
320
+
321
+ const buttonSizes = theme?.sizes?.button || {};
322
+
323
+ const buttonRadius = theme?.radius?.button ?? 12;
324
+
325
+ const spacing = theme?.spacing || {};
326
+
327
+ const paddingSmall = spacing.sm ?? 12;
328
+
329
+ const paddingMedium = spacing.md ?? 16;
330
+
331
+ const paddingLarge = spacing.lg ?? 20;
332
+
333
+ const paddingXL = spacing.xl ?? 24;
334
+
296
335
  switch (size) {
297
- case SIZES.sm:
336
+ case "sm":
298
337
  return {
299
- height: 36,
300
- paddingHorizontal: 14,
338
+ height: buttonSizes.sm ?? 36,
339
+
340
+ paddingHorizontal: paddingSmall,
341
+
301
342
  gap: 6,
302
- borderRadius: 10,
343
+
344
+ borderRadius: buttonRadius,
345
+
303
346
  fontSize: 13,
347
+
304
348
  lineHeight: 18,
305
349
  };
306
350
 
307
- case SIZES.lg:
351
+ case "lg":
308
352
  return {
309
- height: 52,
310
- paddingHorizontal: 22,
353
+ height: buttonSizes.lg ?? 52,
354
+
355
+ paddingHorizontal: paddingLarge,
356
+
311
357
  gap: 8,
312
- borderRadius: 14,
358
+
359
+ borderRadius: buttonRadius,
360
+
313
361
  fontSize: 16,
362
+
314
363
  lineHeight: 22,
315
364
  };
316
365
 
317
- case SIZES.xl:
366
+ case "xl":
318
367
  return {
319
- height: 60,
320
- paddingHorizontal: 26,
368
+ height: buttonSizes.xl ?? 60,
369
+
370
+ paddingHorizontal: paddingXL,
371
+
321
372
  gap: 8,
322
- borderRadius: 16,
373
+
374
+ borderRadius: buttonRadius,
375
+
323
376
  fontSize: 17,
377
+
324
378
  lineHeight: 24,
325
379
  };
326
380
 
327
- case SIZES.md:
381
+ case "md":
328
382
  default:
329
383
  return {
330
- height: 44,
331
- paddingHorizontal: 18,
384
+ height: buttonSizes.md ?? 44,
385
+
386
+ paddingHorizontal: paddingMedium,
387
+
332
388
  gap: 8,
333
- borderRadius: 12,
389
+
390
+ borderRadius: buttonRadius,
391
+
334
392
  fontSize: 14,
393
+
335
394
  lineHeight: 20,
336
395
  };
337
396
  }
@@ -340,34 +399,45 @@ function getButtonDimensions(size) {
340
399
  const styles = StyleSheet.create({
341
400
  button: {
342
401
  flexShrink: 0,
402
+
343
403
  alignItems: "center",
404
+
344
405
  justifyContent: "center",
406
+
345
407
  overflow: "hidden",
346
408
  },
347
409
 
348
410
  content: {
349
411
  flexDirection: "row",
412
+
350
413
  alignItems: "center",
414
+
351
415
  justifyContent: "center",
416
+
352
417
  flexShrink: 1,
353
418
  },
354
419
 
355
420
  children: {
356
- flexShrink: 1,
357
421
  alignItems: "center",
422
+
358
423
  justifyContent: "center",
424
+
425
+ flexShrink: 1,
359
426
  },
360
427
 
361
428
  icon: {
362
429
  alignItems: "center",
430
+
363
431
  justifyContent: "center",
364
- flexShrink: 0,
365
432
  },
366
433
 
367
434
  text: {
368
435
  flexShrink: 1,
436
+
369
437
  fontWeight: "600",
438
+
370
439
  textAlign: "center",
440
+
371
441
  includeFontPadding: false,
372
442
  },
373
443
  });
@@ -376,6 +446,4 @@ export const UIButton = memo(UIButtonComponent);
376
446
 
377
447
  UIButton.displayName = "UIButton";
378
448
 
379
- export const UIButtonVariants = VARIANTS;
380
-
381
- export const UIButtonSizes = SIZES;
449
+ export { BUTTON_VARIANTS as UIButtonVariants, BUTTON_SIZES as UIButtonSizes };
@@ -1,4 +1,4 @@
1
- import React, { createContext, useContext, useMemo } from "react";
1
+ import React, { createContext, useCallback, useContext, useMemo } from "react";
2
2
 
3
3
  import { useColorScheme } from "react-native";
4
4
 
@@ -21,45 +21,106 @@ import { breakpoints } from "./breakpoints";
21
21
  const UIContext = createContext(null);
22
22
 
23
23
  function createTheme(mode, overrides) {
24
- const colors = mode === "dark" ? darkColors : lightColors;
24
+ const baseColors = mode === "dark" ? darkColors : lightColors;
25
25
 
26
26
  return {
27
27
  mode,
28
28
 
29
29
  colors: {
30
- ...colors,
31
- ...overrides?.colors,
30
+ ...baseColors,
31
+
32
+ ...(overrides?.colors || {}),
33
+ },
34
+
35
+ spacing: {
36
+ ...spacing,
37
+
38
+ ...(overrides?.spacing || {}),
32
39
  },
33
40
 
34
- spacing,
35
- radius,
36
- typography,
37
- sizes,
38
- shadows,
39
- animation,
40
- breakpoints,
41
+ radius: {
42
+ ...radius,
43
+
44
+ ...(overrides?.radius || {}),
45
+ },
46
+
47
+ typography: {
48
+ ...typography,
49
+
50
+ ...(overrides?.typography || {}),
51
+ },
52
+
53
+ sizes: {
54
+ ...sizes,
55
+
56
+ ...(overrides?.sizes || {}),
57
+ },
58
+
59
+ shadows: {
60
+ ...shadows,
61
+
62
+ ...(overrides?.shadows || {}),
63
+ },
64
+
65
+ animation: {
66
+ ...animation,
67
+
68
+ ...(overrides?.animation || {}),
69
+ },
70
+
71
+ breakpoints: {
72
+ ...breakpoints,
73
+
74
+ ...(overrides?.breakpoints || {}),
75
+ },
41
76
  };
42
77
  }
43
78
 
44
- export function UIProvider({ children, mode = "system", onModeChange, theme }) {
79
+ export function UIProvider({
80
+ children,
81
+
82
+ mode = "system",
83
+
84
+ onModeChange,
85
+
86
+ theme,
87
+ }) {
45
88
  const systemScheme = useColorScheme();
46
89
 
47
90
  const resolvedMode =
48
- mode === "system" ? (systemScheme === "dark" ? "dark" : "light") : mode;
49
-
50
- const setMode = (nextMode) => {
51
- if (nextMode !== "light" && nextMode !== "dark" && nextMode !== "system") {
52
- return;
53
- }
91
+ mode === "system"
92
+ ? systemScheme === "dark"
93
+ ? "dark"
94
+ : "light"
95
+ : mode === "dark"
96
+ ? "dark"
97
+ : "light";
98
+
99
+ const setMode = useCallback(
100
+ (nextMode) => {
101
+ if (
102
+ nextMode !== "light" &&
103
+ nextMode !== "dark" &&
104
+ nextMode !== "system"
105
+ ) {
106
+ return;
107
+ }
108
+
109
+ if (typeof onModeChange === "function") {
110
+ onModeChange(nextMode);
111
+ }
112
+ },
113
+ [onModeChange],
114
+ );
54
115
 
55
- if (onModeChange) {
56
- onModeChange(nextMode);
57
- }
58
- };
116
+ const resolvedTheme = useMemo(
117
+ () => createTheme(resolvedMode, theme),
118
+ [resolvedMode, theme],
119
+ );
59
120
 
60
121
  const contextValue = useMemo(
61
122
  () => ({
62
- theme: createTheme(resolvedMode, theme),
123
+ theme: resolvedTheme,
63
124
 
64
125
  mode: resolvedMode,
65
126
 
@@ -67,7 +128,7 @@ export function UIProvider({ children, mode = "system", onModeChange, theme }) {
67
128
 
68
129
  setMode,
69
130
  }),
70
- [resolvedMode, mode, theme, onModeChange],
131
+ [resolvedTheme, resolvedMode, mode, setMode],
71
132
  );
72
133
 
73
134
  return (