react-native-varia 0.3.0 → 0.4.1

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 (45) hide show
  1. package/lib/components/Accordion.tsx +61 -21
  2. package/lib/components/Badge.tsx +9 -0
  3. package/lib/components/Button.tsx +19 -8
  4. package/lib/components/Checkbox.tsx +21 -12
  5. package/lib/components/CircleProgress.tsx +8 -0
  6. package/lib/components/Divider.tsx +6 -0
  7. package/lib/components/Drawer.tsx +16 -4
  8. package/lib/components/Field.tsx +4 -0
  9. package/lib/components/FloatingAction.tsx +215 -0
  10. package/lib/components/GradientBackground.tsx +3 -0
  11. package/lib/components/GradientText.tsx +27 -14
  12. package/lib/components/IconWrapper.tsx +4 -3
  13. package/lib/components/Input.tsx +47 -21
  14. package/lib/components/Link.tsx +4 -0
  15. package/lib/components/Modal.tsx +18 -5
  16. package/lib/components/NumberInput.tsx +27 -5
  17. package/lib/components/RadioGroup.tsx +16 -5
  18. package/lib/components/ReText.tsx +4 -1
  19. package/lib/components/Select.tsx +20 -0
  20. package/lib/components/Slider.tsx +59 -23
  21. package/lib/components/Slideshow.tsx +19 -3
  22. package/lib/components/Spinner.tsx +9 -3
  23. package/lib/components/Switch.tsx +57 -26
  24. package/lib/components/Text.tsx +3 -0
  25. package/lib/components/Toast.tsx +110 -36
  26. package/lib/patterns/index.tsx +299 -90
  27. package/lib/theme/Accordion.tsx +184 -0
  28. package/lib/theme/Button.recipe.tsx +24 -7
  29. package/lib/theme/Drawer.recipe.tsx +2 -4
  30. package/lib/theme/Field.recipe.tsx +45 -15
  31. package/lib/theme/FloatingAction.tsx +112 -0
  32. package/lib/theme/GradientText.recipe.tsx +103 -34
  33. package/lib/theme/Input.recipe.tsx +14 -6
  34. package/lib/theme/Select.recipe.tsx +3 -0
  35. package/lib/theme/Slider.recipe.tsx +86 -150
  36. package/lib/theme/Spinner.recipe.tsx +4 -0
  37. package/lib/theme/Switch.recipe.tsx +19 -0
  38. package/lib/theme/Text.recipe.tsx +63 -12
  39. package/lib/theme/Toast.recipe.tsx +40 -7
  40. package/lib/varia/types.ts +3 -0
  41. package/lib/varia/utils.ts +110 -18
  42. package/package.json +1 -1
  43. package/lib/components/OldSlider.tsx +0 -327
  44. package/lib/components/SlidingDrawer.tsx +0 -301
  45. package/lib/patterns/newPatterns.tsx +0 -285
@@ -1,4 +1,4 @@
1
- import { Platform } from 'react-native'
1
+ import {Platform} from 'react-native'
2
2
 
3
3
  type StyleObj = {
4
4
  variants?: Record<string, Record<string, Record<string, any>>>
@@ -92,29 +92,77 @@ export {
92
92
  heightFromPixel as hpx,
93
93
  }
94
94
 
95
- export const extractThemeColor = (color: ThemeColors, theme: any) => {
96
- return color.includes('.')
97
- ? (
98
- theme.colors[
99
- color.split('.')[0] as keyof typeof theme.colors
100
- ] as unknown as Record<string, string>
101
- )[color.split('.')[1]]
102
- : color in theme.colors &&
103
- typeof theme.colors[color as keyof typeof theme.colors] === 'string'
104
- ? (theme.colors[color as keyof typeof theme.colors] as string)
105
- : !(color in theme.colors)
106
- ? color
107
- : undefined
95
+ export const extractThemeColor = (
96
+ color: string,
97
+ theme: any,
98
+ ): string | undefined => {
99
+ if (color.includes('.')) {
100
+ const [paletteKey, shadeKey] = color.split('.')
101
+ const palette = theme.colors[paletteKey as keyof typeof theme.colors]
102
+ if (
103
+ palette &&
104
+ typeof palette === 'object' &&
105
+ shadeKey in (palette as Record<string, any>)
106
+ ) {
107
+ return (palette as Record<string, string>)[shadeKey]
108
+ }
109
+ // Si no existe la paleta o el shade, seguimos abajo para fallback
110
+ }
111
+
112
+ // Si el color corresponde a una clave simple en colors
113
+ if (color in theme.colors) {
114
+ const val = theme.colors[color as keyof typeof theme.colors]
115
+ if (typeof val === 'string') {
116
+ // Es un color simple → lo devolvemos
117
+ return val
118
+ } else {
119
+ // Es una paleta / objeto → devolvemos el string literal (p. ej. "pink") para fallback
120
+ return color
121
+ }
122
+ }
123
+
124
+ // Si no existe en theme.colors → asumimos que es un color CSS válido
125
+ return color
108
126
  }
109
127
 
128
+ // export const resolveColor = (
129
+ // value: string,
130
+ // colors: NestedColorsType,
131
+ // colorPalette?: string,
132
+ // ): string => {
133
+ // if (value.includes('.')) {
134
+ // const [palette, key] = value.split('.')
135
+
136
+ // if (palette === 'colorPalette' && colorPalette) {
137
+ // const paletteObj = (colors as any)[colorPalette]
138
+ // if (paletteObj && key in paletteObj) {
139
+ // return paletteObj[key]
140
+ // }
141
+ // }
142
+
143
+ // const paletteObj = (colors as any)[palette]
144
+ // if (paletteObj && key in paletteObj) {
145
+ // return paletteObj[key]
146
+ // }
147
+ // }
148
+
149
+ // if (value in colors) {
150
+ // return (colors as any)[value]
151
+ // }
152
+
153
+ // return value
154
+ // }
155
+
110
156
  export const resolveColor = (
111
157
  value: string,
112
158
  colors: NestedColorsType,
113
159
  colorPalette?: string,
114
160
  ): string => {
161
+ // Caso: value incluye punto → color anidado tipo "pink.10"
115
162
  if (value.includes('.')) {
116
163
  const [palette, key] = value.split('.')
117
164
 
165
+ // Caso especial cuando la paleta es "colorPalette"
118
166
  if (palette === 'colorPalette' && colorPalette) {
119
167
  const paletteObj = (colors as any)[colorPalette]
120
168
  if (paletteObj && key in paletteObj) {
@@ -128,10 +176,21 @@ export const resolveColor = (
128
176
  }
129
177
  }
130
178
 
179
+ // Caso: value es key simple dentro del theme
131
180
  if (value in colors) {
132
- return (colors as any)[value]
181
+ const result = (colors as any)[value]
182
+
183
+ // Si el resultado es un objeto → es una paleta, no un color
184
+ // → devolver el string original
185
+ if (typeof result === 'object' && result !== null) {
186
+ return value
187
+ }
188
+
189
+ // Si es un color simple → devolverlo
190
+ return result
133
191
  }
134
192
 
193
+ // Caso: no existe como key → asumir que es un color CSS válido
135
194
  return value
136
195
  }
137
196
 
@@ -320,12 +379,12 @@ export function computeFlexSync(
320
379
  export function getVariantValue<
321
380
  VariantGroup extends string,
322
381
  VariantValue extends string | undefined,
323
- PropName extends string
382
+ PropName extends string,
324
383
  >(
325
384
  styleObj: StyleObj,
326
385
  variantGroup: VariantGroup,
327
386
  variantValue: VariantValue,
328
- propName: PropName
387
+ propName: PropName,
329
388
  ): undefined | any {
330
389
  if (variantValue == null) {
331
390
  // Si no hay variante, devolvemos undefined
@@ -346,4 +405,37 @@ export function getVariantValue<
346
405
  } else {
347
406
  return (styleObj as any)[propName]
348
407
  }
349
- }
408
+ }
409
+
410
+ export function getCompoundVariantValue<
411
+ Conditions extends Record<string, any>,
412
+ PropName extends string,
413
+ >(
414
+ styleObj: StyleObj,
415
+ conditions: Conditions,
416
+ propName: PropName,
417
+ ): any | undefined {
418
+ if (Platform.OS !== 'web') {
419
+ // En nativo se comporta igual que los estilos planos
420
+ return (styleObj as any)[propName]
421
+ }
422
+
423
+ const compoundVariants = styleObj.compoundVariants
424
+ if (!Array.isArray(compoundVariants)) return undefined
425
+
426
+ // Buscar la variante que cumpla TODAS las condiciones
427
+ const match = compoundVariants.find(variant => {
428
+ for (const key in conditions) {
429
+ if (variant[key] !== conditions[key]) {
430
+ return false
431
+ }
432
+ }
433
+ return true
434
+ })
435
+
436
+ if (!match) return undefined
437
+
438
+ if (!match.styles) return undefined
439
+
440
+ return match.styles[propName]
441
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-varia",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "bin": {
5
5
  "varia": "bin/cli.js"
6
6
  },
@@ -1,327 +0,0 @@
1
- import React from 'react'
2
- import {type LayoutChangeEvent, View} from 'react-native'
3
- import {StyleSheet, UnistylesVariants} from 'react-native-unistyles'
4
- import {Gesture, GestureDetector} from 'react-native-gesture-handler'
5
- import Animated, {
6
- useAnimatedStyle,
7
- useSharedValue,
8
- } from 'react-native-reanimated'
9
- import {runOnJS} from 'react-native-worklets'
10
- import {SliderStyles, SliderDefaultVariants} from '../theme/OldSlider.recipe'
11
- import {PalettesWithNestedKeys} from '../style/varia/types'
12
-
13
- type SliderVariants = UnistylesVariants<typeof SliderStyles>
14
-
15
- type SliderProps = SliderVariants & {
16
- colorPalette?: PalettesWithNestedKeys
17
- thickness?: number
18
- minimumTrackThickness?: number
19
- minimumTrackColor?: string
20
- displayStepsOnMinimumTrack?: boolean
21
- thumbSize?: {
22
- width: number
23
- height: number
24
- }
25
- thumbChildren?: React.ReactNode
26
- axis: 'x' | 'y'
27
- steps?: number
28
- value?: number
29
- onValueChange?: (value: number) => void
30
- onSlidingComplete?: (value: number) => void
31
- }
32
-
33
- const OldSlider = ({
34
- colorPalette = 'accent',
35
- variant = SliderDefaultVariants.variant,
36
- size = SliderDefaultVariants.size,
37
- displayStepsOnMinimumTrack = false,
38
- thumbChildren: ThumbChildren,
39
- axis = 'x',
40
- value = 0,
41
- steps,
42
- onValueChange,
43
- onSlidingComplete,
44
- }: SliderProps) => {
45
- let styles
46
- if (axis === 'x') {
47
- styles = stylesX
48
- } else {
49
- styles = stylesY
50
- }
51
- SliderStyles.useVariants({
52
- size,
53
- variant,
54
- })
55
- const thumbStyle = SliderStyles.thumb(colorPalette)
56
- const maximumTrackStyle = SliderStyles.maximumTrack(colorPalette)
57
- const minimumTrackStyle = SliderStyles.minimumTrack(colorPalette, false)
58
-
59
- const halfSize = (thumbStyle.width ?? 0) / 2
60
- const thumbSize = {
61
- width: thumbStyle.width,
62
- height: thumbStyle.height,
63
- }
64
- const maximumTrackWidth = maximumTrackStyle.height ?? 30
65
- const minimumTrackWidth = minimumTrackStyle.height ?? 30
66
- const context = useSharedValue({x: 0})
67
- const translate = useSharedValue(value)
68
- const trackLength = useSharedValue(0)
69
- const isInsideChild = useSharedValue(false)
70
-
71
- const handleTrackLayout = (event: LayoutChangeEvent) => {
72
- const {width, height} = event.nativeEvent.layout
73
- trackLength.value = axis === 'x' ? width : height
74
- }
75
-
76
- const slideGesture = Gesture.Pan()
77
- .onTouchesDown(() => {
78
- isInsideChild.value = true
79
- })
80
- .onBegin(() => {
81
- context.value.x = translate.value
82
- })
83
- .onUpdate(e => {
84
- const stepLength = steps ? trackLength.value / steps : 1
85
- const delta = axis === 'y' ? -e.translationY : e.translationX
86
-
87
- const rawValue = Math.max(
88
- 0,
89
- Math.min(context.value.x + delta, trackLength.value),
90
- )
91
-
92
- const snappedValue = steps
93
- ? Math.round(rawValue / stepLength) * stepLength
94
- : rawValue
95
-
96
- translate.value = snappedValue
97
-
98
- if (steps) {
99
- const stepIndex = Math.round(snappedValue / stepLength)
100
- onValueChange && runOnJS(onValueChange)(stepIndex)
101
- } else {
102
- onValueChange &&
103
- runOnJS(onValueChange)(
104
- Math.round((snappedValue / trackLength.value) * 100) / 100,
105
- )
106
- }
107
- })
108
- .onEnd(() => {
109
- const stepLength = steps ? trackLength.value / steps : 1
110
-
111
- const snappedValue = steps
112
- ? Math.round(translate.value / stepLength)
113
- : translate.value / trackLength.value
114
-
115
- onSlidingComplete && runOnJS(onSlidingComplete)(snappedValue)
116
- })
117
- .onFinalize(() => {
118
- isInsideChild.value = false
119
- })
120
- const tapGesture = Gesture.Tap()
121
- .onBegin(e => {
122
- if (isInsideChild.value) {
123
- return
124
- }
125
- const tapPosition = axis === 'x' ? e.x : e.y
126
- const stepLength = steps ? trackLength.value / steps : 1
127
-
128
- const rawValue =
129
- axis === 'y'
130
- ? Math.max(
131
- 0,
132
- Math.min(trackLength.value - tapPosition, trackLength.value),
133
- )
134
- : Math.max(0, Math.min(tapPosition, trackLength.value))
135
-
136
- const snappedValue = steps
137
- ? Math.round(rawValue / stepLength) * stepLength
138
- : rawValue
139
-
140
- translate.value = snappedValue
141
-
142
- if (steps) {
143
- const stepIndex = Math.round(snappedValue / stepLength)
144
- onValueChange && runOnJS(onValueChange)(stepIndex)
145
- onSlidingComplete && runOnJS(onSlidingComplete)(stepIndex)
146
- } else {
147
- const normalizedValue =
148
- Math.round((snappedValue / trackLength.value) * 100) / 100
149
- onValueChange && runOnJS(onValueChange)(normalizedValue)
150
- onSlidingComplete && runOnJS(onSlidingComplete)(normalizedValue)
151
- }
152
- })
153
- .simultaneousWithExternalGesture(slideGesture)
154
-
155
- const animatedTrack = useAnimatedStyle(() => ({
156
- [axis === 'y' ? 'height' : 'width']: translate.value + halfSize,
157
- }))
158
-
159
- const opacityTrack = steps !== undefined && displayStepsOnMinimumTrack
160
-
161
- return (
162
- <View
163
- style={[
164
- styles.container(maximumTrackWidth, halfSize),
165
- SliderStyles.container(colorPalette),
166
- ]}>
167
- {axis === 'x' && <View style={{width: halfSize}} />}
168
- <GestureDetector gesture={tapGesture}>
169
- <View
170
- style={[
171
- styles.maximumTrack(maximumTrackWidth),
172
- SliderStyles.maximumTrack(colorPalette),
173
- ]}
174
- onLayout={handleTrackLayout}>
175
- {steps && (
176
- <View style={styles.steps}>
177
- {Array.from({length: steps + 1}, (_, index) => index).map(
178
- (_, i) => (
179
- <View
180
- key={i}
181
- style={[
182
- styles.step(i, steps),
183
- SliderStyles.step(colorPalette),
184
- ]}
185
- />
186
- ),
187
- )}
188
- </View>
189
- )}
190
- <Animated.View
191
- style={[
192
- styles.minimumTrack(halfSize, minimumTrackWidth),
193
- SliderStyles.minimumTrack(colorPalette, opacityTrack),
194
- animatedTrack,
195
- ]}>
196
- <GestureDetector gesture={slideGesture}>
197
- <View
198
- style={[
199
- SliderStyles.thumb(colorPalette),
200
- styles.thumb(halfSize, thumbSize),
201
- ]}>
202
- {ThumbChildren || null}
203
- </View>
204
- </GestureDetector>
205
- </Animated.View>
206
- </View>
207
- </GestureDetector>
208
- {axis === 'y' && <View style={{height: halfSize}} />}
209
- </View>
210
- )
211
- }
212
-
213
- const stylesX = StyleSheet.create(theme => ({
214
- container: (thickness, halfSize) => ({
215
- flex: thickness ? 1 : 1,
216
- maxWidth: 'auto',
217
- borderRadius: 22,
218
- width: '100%',
219
- height: '100%',
220
- paddingTop: 0,
221
- paddingRight: halfSize,
222
- flexDirection: 'row',
223
- }),
224
- maximumTrack: thickness => ({
225
- flex: thickness ? 1 : 1,
226
- justifyContent: 'center',
227
- position: 'relative',
228
- borderRadius: 22,
229
- }),
230
- minimumTrack: (halfSize, minimumTrackThickness) => ({
231
- height: minimumTrackThickness,
232
- width: '100%',
233
- left: halfSize * -1,
234
- bottom: 'auto',
235
- paddingBottom: 0,
236
- paddingLeft: halfSize,
237
- borderBottomLeftRadius: 20,
238
- borderBottomRightRadius: 0,
239
- borderTopLeftRadius: 20,
240
- justifyContent: 'center',
241
- }),
242
- thumb: (halfSize, thumbSize) => ({
243
- width: halfSize ? thumbSize.width : thumbSize.width,
244
- height: thumbSize.height,
245
- borderRadius: 25,
246
- position: 'absolute',
247
- right: 0,
248
- zIndex: 2,
249
- transform: [{translateX: '50%'}],
250
- }),
251
- steps: {
252
- width: '100%',
253
- height: '100%',
254
- flexDirection: 'row',
255
- justifyContent: 'space-between',
256
- alignItems: 'center',
257
- zIndex: 0,
258
- position: 'absolute',
259
- },
260
- step: (index, length) => ({
261
- width: 1,
262
- height: '100%',
263
- backgroundColor:
264
- index === 0 || index === length ? 'transparent' : theme.colors.fg.default,
265
- }),
266
- }))
267
- const stylesY = StyleSheet.create(theme => ({
268
- container: (maximumTrackWidth, halfSize) => ({
269
- flex: 1,
270
- maxWidth: maximumTrackWidth,
271
- maxHeight: 'auto',
272
- borderRadius: 22,
273
- width: '100%',
274
- height: '100%',
275
- paddingTop: halfSize,
276
- paddingRight: 0,
277
- flexDirection: 'column',
278
- borderWidth: 1,
279
- borderColor: 'yellow',
280
- // minWidth: 30,
281
- }),
282
- maximumTrack: maximumTrackWidth => ({
283
- flex: 1,
284
- justifyContent: 'center',
285
- alignItems: 'center',
286
- position: 'relative',
287
- maxWidth: maximumTrackWidth,
288
- }),
289
- minimumTrack: (halfSize, minimumTrackWidth) => ({
290
- position: 'absolute',
291
- height: '100%',
292
- width: minimumTrackWidth,
293
- left: 'auto',
294
- bottom: halfSize * -1,
295
- paddingBottom: halfSize,
296
- paddingLeft: 0,
297
- borderBottomLeftRadius: 20,
298
- borderBottomRightRadius: 20,
299
- borderTopLeftRadius: 0,
300
- alignItems: 'center',
301
- }),
302
- thumb: (halfSize, thumbSize) => ({
303
- borderRadius: 25,
304
- position: 'absolute',
305
- top: 0,
306
- zIndex: 2,
307
- transform: [{translateY: halfSize * -1}],
308
- width: thumbSize.height,
309
- height: thumbSize.width,
310
- }),
311
- steps: {
312
- width: '100%',
313
- height: '100%',
314
- flexDirection: 'column',
315
- justifyContent: 'space-between',
316
- alignItems: 'center',
317
- zIndex: 0,
318
- },
319
- step: (index, length) => ({
320
- width: '100%',
321
- height: 1,
322
- backgroundColor:
323
- index === 0 || index === length ? 'transparent' : theme.colors.fg.default,
324
- }),
325
- }))
326
-
327
- export default OldSlider