orn-ui 0.1.0 → 0.1.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 (143) hide show
  1. package/lib/commonjs/atoms/Avatar.js +1 -1
  2. package/lib/commonjs/atoms/Avatar.js.map +1 -1
  3. package/lib/commonjs/atoms/Badge.js +10 -8
  4. package/lib/commonjs/atoms/Badge.js.map +1 -1
  5. package/lib/commonjs/atoms/Button.js +8 -6
  6. package/lib/commonjs/atoms/Button.js.map +1 -1
  7. package/lib/commonjs/atoms/Checkbox.js +4 -1
  8. package/lib/commonjs/atoms/Checkbox.js.map +1 -1
  9. package/lib/commonjs/atoms/Fab.js +1 -1
  10. package/lib/commonjs/atoms/Fab.js.map +1 -1
  11. package/lib/commonjs/atoms/Image.js +2 -2
  12. package/lib/commonjs/atoms/Image.js.map +1 -1
  13. package/lib/commonjs/atoms/Input.js +2 -2
  14. package/lib/commonjs/atoms/Input.js.map +1 -1
  15. package/lib/commonjs/molecules/AvatarHeader.js +2 -2
  16. package/lib/commonjs/molecules/AvatarHeader.js.map +1 -1
  17. package/lib/commonjs/molecules/OptionCard.js +2 -2
  18. package/lib/commonjs/molecules/OptionCard.js.map +1 -1
  19. package/lib/commonjs/molecules/Steps.js +2 -2
  20. package/lib/commonjs/molecules/Steps.js.map +1 -1
  21. package/lib/commonjs/molecules/ThemeToggle.js +1 -1
  22. package/lib/commonjs/molecules/ThemeToggle.js.map +1 -1
  23. package/lib/commonjs/organisms/Alert.js +6 -5
  24. package/lib/commonjs/organisms/Alert.js.map +1 -1
  25. package/lib/commonjs/organisms/DateField.js +2 -2
  26. package/lib/commonjs/organisms/DateField.js.map +1 -1
  27. package/lib/commonjs/organisms/DatePicker.js +3 -3
  28. package/lib/commonjs/organisms/DatePicker.js.map +1 -1
  29. package/lib/commonjs/organisms/List.js +1 -1
  30. package/lib/commonjs/organisms/List.js.map +1 -1
  31. package/lib/commonjs/organisms/SearchList.js +2 -2
  32. package/lib/commonjs/organisms/SearchList.js.map +1 -1
  33. package/lib/commonjs/organisms/Select.js +4 -4
  34. package/lib/commonjs/organisms/Select.js.map +1 -1
  35. package/lib/commonjs/organisms/Toast.js +12 -3
  36. package/lib/commonjs/organisms/Toast.js.map +1 -1
  37. package/lib/commonjs/safe-area/SafeAreaUIProvider.js +65 -0
  38. package/lib/commonjs/safe-area/SafeAreaUIProvider.js.map +1 -0
  39. package/lib/commonjs/safe-area/index.js +13 -0
  40. package/lib/commonjs/safe-area/index.js.map +1 -0
  41. package/lib/commonjs/theme/UIProvider.js +11 -0
  42. package/lib/commonjs/theme/UIProvider.js.map +1 -1
  43. package/lib/commonjs/theme/palettes.js +70 -11
  44. package/lib/commonjs/theme/palettes.js.map +1 -1
  45. package/lib/module/atoms/Avatar.js +1 -1
  46. package/lib/module/atoms/Avatar.js.map +1 -1
  47. package/lib/module/atoms/Badge.js +10 -8
  48. package/lib/module/atoms/Badge.js.map +1 -1
  49. package/lib/module/atoms/Button.js +8 -6
  50. package/lib/module/atoms/Button.js.map +1 -1
  51. package/lib/module/atoms/Checkbox.js +4 -1
  52. package/lib/module/atoms/Checkbox.js.map +1 -1
  53. package/lib/module/atoms/Fab.js +1 -1
  54. package/lib/module/atoms/Fab.js.map +1 -1
  55. package/lib/module/atoms/Image.js +2 -2
  56. package/lib/module/atoms/Image.js.map +1 -1
  57. package/lib/module/atoms/Input.js +2 -2
  58. package/lib/module/atoms/Input.js.map +1 -1
  59. package/lib/module/molecules/AvatarHeader.js +2 -2
  60. package/lib/module/molecules/AvatarHeader.js.map +1 -1
  61. package/lib/module/molecules/OptionCard.js +2 -2
  62. package/lib/module/molecules/OptionCard.js.map +1 -1
  63. package/lib/module/molecules/Steps.js +2 -2
  64. package/lib/module/molecules/Steps.js.map +1 -1
  65. package/lib/module/molecules/ThemeToggle.js +1 -1
  66. package/lib/module/molecules/ThemeToggle.js.map +1 -1
  67. package/lib/module/organisms/Alert.js +6 -5
  68. package/lib/module/organisms/Alert.js.map +1 -1
  69. package/lib/module/organisms/DateField.js +2 -2
  70. package/lib/module/organisms/DateField.js.map +1 -1
  71. package/lib/module/organisms/DatePicker.js +3 -3
  72. package/lib/module/organisms/DatePicker.js.map +1 -1
  73. package/lib/module/organisms/List.js +1 -1
  74. package/lib/module/organisms/List.js.map +1 -1
  75. package/lib/module/organisms/SearchList.js +2 -2
  76. package/lib/module/organisms/SearchList.js.map +1 -1
  77. package/lib/module/organisms/Select.js +4 -4
  78. package/lib/module/organisms/Select.js.map +1 -1
  79. package/lib/module/organisms/Toast.js +12 -3
  80. package/lib/module/organisms/Toast.js.map +1 -1
  81. package/lib/module/safe-area/SafeAreaUIProvider.js +60 -0
  82. package/lib/module/safe-area/SafeAreaUIProvider.js.map +1 -0
  83. package/lib/module/safe-area/index.js +4 -0
  84. package/lib/module/safe-area/index.js.map +1 -0
  85. package/lib/module/theme/UIProvider.js +11 -0
  86. package/lib/module/theme/UIProvider.js.map +1 -1
  87. package/lib/module/theme/palettes.js +70 -11
  88. package/lib/module/theme/palettes.js.map +1 -1
  89. package/lib/typescript/atoms/Badge.d.ts.map +1 -1
  90. package/lib/typescript/atoms/Button.d.ts.map +1 -1
  91. package/lib/typescript/atoms/Checkbox.d.ts.map +1 -1
  92. package/lib/typescript/organisms/Alert.d.ts.map +1 -1
  93. package/lib/typescript/organisms/Toast.d.ts.map +1 -1
  94. package/lib/typescript/safe-area/SafeAreaUIProvider.d.ts +46 -0
  95. package/lib/typescript/safe-area/SafeAreaUIProvider.d.ts.map +1 -0
  96. package/lib/typescript/safe-area/index.d.ts +3 -0
  97. package/lib/typescript/safe-area/index.d.ts.map +1 -0
  98. package/lib/typescript/theme/UIProvider.d.ts.map +1 -1
  99. package/lib/typescript/theme/palettes.d.ts +41 -3
  100. package/lib/typescript/theme/palettes.d.ts.map +1 -1
  101. package/package.json +36 -14
  102. package/registry/alert.json +1 -1
  103. package/registry/avatar-header.json +1 -1
  104. package/registry/avatar.json +1 -1
  105. package/registry/badge.json +1 -1
  106. package/registry/button.json +1 -1
  107. package/registry/checkbox.json +1 -1
  108. package/registry/core.json +2 -2
  109. package/registry/date-field.json +1 -1
  110. package/registry/date-picker.json +1 -1
  111. package/registry/fab.json +1 -1
  112. package/registry/image.json +1 -1
  113. package/registry/index.json +19 -19
  114. package/registry/input.json +1 -1
  115. package/registry/list.json +1 -1
  116. package/registry/option-card.json +1 -1
  117. package/registry/search-list.json +1 -1
  118. package/registry/select.json +1 -1
  119. package/registry/steps.json +1 -1
  120. package/registry/theme-toggle.json +1 -1
  121. package/registry/toast.json +1 -1
  122. package/src/atoms/Avatar.tsx +1 -1
  123. package/src/atoms/Badge.tsx +6 -4
  124. package/src/atoms/Button.tsx +8 -6
  125. package/src/atoms/Checkbox.tsx +4 -1
  126. package/src/atoms/Fab.tsx +1 -1
  127. package/src/atoms/Image.tsx +2 -2
  128. package/src/atoms/Input.tsx +2 -2
  129. package/src/molecules/AvatarHeader.tsx +2 -2
  130. package/src/molecules/OptionCard.tsx +2 -2
  131. package/src/molecules/Steps.tsx +2 -2
  132. package/src/molecules/ThemeToggle.tsx +1 -1
  133. package/src/organisms/Alert.tsx +6 -5
  134. package/src/organisms/DateField.tsx +2 -2
  135. package/src/organisms/DatePicker.tsx +3 -3
  136. package/src/organisms/List.tsx +1 -1
  137. package/src/organisms/SearchList.tsx +2 -2
  138. package/src/organisms/Select.tsx +4 -4
  139. package/src/organisms/Toast.tsx +11 -3
  140. package/src/safe-area/SafeAreaUIProvider.tsx +67 -0
  141. package/src/safe-area/index.ts +2 -0
  142. package/src/theme/UIProvider.tsx +15 -0
  143. package/src/theme/palettes.ts +105 -11
@@ -8,6 +8,9 @@ import { zeroInsets } from './insets';
8
8
  import { renderDefaultIcon } from '../icons/defaultIcons';
9
9
  import { jsx as _jsx } from "react/jsx-runtime";
10
10
  const UIContext = /*#__PURE__*/createContext(undefined);
11
+
12
+ /** Una sola vez por sesión: en un re-render el aviso no aporta nada nuevo. */
13
+ let warnedAboutInsets = false;
11
14
  /**
12
15
  * UIProvider
13
16
  * Único punto de entrada de la librería: resuelve el theme activo (sistema u
@@ -34,6 +37,14 @@ export function UIProvider({
34
37
  if (!isControlled) setUncontrolledMode(next);
35
38
  onModeChange?.(next);
36
39
  }, [isControlled, onModeChange]);
40
+
41
+ // Olvidar `insets` no rompe nada visible al montar: el default de ceros
42
+ // recién se nota cuando abrís un Modal `full` (se mete abajo del notch) o un
43
+ // BottomSheet (queda pegado a la barra de gestos). El aviso llega antes.
44
+ if (__DEV__ && insets === zeroInsets && !warnedAboutInsets) {
45
+ warnedAboutInsets = true;
46
+ console.warn('orn-ui: <UIProvider> sin `insets`, se usan {top:0,bottom:0,left:0,right:0}. ' + 'Los modales y el Screen quedan sin safe area. Pasá `insets={useSafeAreaInsets()}` ' + "o usá <SafeAreaUIProvider> de 'orn-ui/safe-area', que lo cablea solo.");
47
+ }
37
48
  const scheme = mode === 'system' ? systemScheme === 'dark' ? 'dark' : 'light' : mode;
38
49
  const resolvedTheme = theme[scheme];
39
50
  const labels = useMemo(() => labelOverrides ? {
@@ -1 +1 @@
1
- {"version":3,"names":["React","createContext","useContext","useMemo","useState","useCallback","Appearance","useColorScheme","defaultTheme","defaultLabels","zeroInsets","renderDefaultIcon","jsx","_jsx","UIContext","undefined","UIProvider","children","theme","defaultMode","mode","controlledMode","onModeChange","icons","insets","labels","labelOverrides","allowFontScaling","systemScheme","uncontrolledMode","setUncontrolledMode","isControlled","setMode","next","scheme","resolvedTheme","value","Provider","useUIContext","ctx","Error","useTheme","useColors","colors","useThemeMode","useIcons","useInsets","useLabels","useAllowFontScaling","__testUtils"],"sourceRoot":"../../../src","sources":["theme/UIProvider.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["React","createContext","useContext","useMemo","useState","useCallback","Appearance","useColorScheme","defaultTheme","defaultLabels","zeroInsets","renderDefaultIcon","jsx","_jsx","UIContext","undefined","warnedAboutInsets","UIProvider","children","theme","defaultMode","mode","controlledMode","onModeChange","icons","insets","labels","labelOverrides","allowFontScaling","systemScheme","uncontrolledMode","setUncontrolledMode","isControlled","setMode","next","__DEV__","console","warn","scheme","resolvedTheme","value","Provider","useUIContext","ctx","Error","useTheme","useColors","colors","useThemeMode","useIcons","useInsets","useLabels","useAllowFontScaling","__testUtils"],"sourceRoot":"../../../src","sources":["theme/UIProvider.tsx"],"mappings":"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","ignoreList":[]}
@@ -4,35 +4,94 @@
4
4
  * Cada color es `string`, no un literal (`as const`): light y dark tienen
5
5
  * valores distintos para la misma clave, y createTheme() debe poder
6
6
  * aceptar cualquier hex/rgba en los overrides — literal typing lo impediría.
7
+ *
8
+ * Cada acento viene en cuatro roles, y no son intercambiables:
9
+ *
10
+ * `primary` relleno sólido (fondo de botón, día seleccionado, FAB).
11
+ * `onPrimary` texto/ícono *encima* de ese relleno.
12
+ * `primarySoft` fondo teñido para estados seleccionados y pills.
13
+ * `primaryText` texto/ícono del acento sobre `surface`, `background`
14
+ * o sobre su propio `primarySoft`.
15
+ *
16
+ * El motivo es el contraste: un acento que se lee bien como relleno no se
17
+ * lee como texto. Antes los componentes usaban `colors.primary` para las dos
18
+ * cosas y lo teñían concatenando alpha al hex (`colors.primary + '15'`), lo
19
+ * que en dark daba 2.6:1 sobre la superficie — muy por debajo del 4.5:1 de
20
+ * WCAG AA. Todos los pares de esta paleta pasan AA en ambos esquemas.
7
21
  */
8
22
 
9
- const shared = {
10
- primary: '#004cef',
11
- secondary: '#00cae1',
23
+ const neutral = {
12
24
  white: '#ffffff',
13
- black: '#000000',
14
- error: '#ff3b30',
15
- success: '#34c759',
16
- warning: '#f59e0b'
25
+ black: '#000000'
17
26
  };
18
27
  export const lightPalette = {
19
- ...shared,
28
+ ...neutral,
29
+ primary: '#004cef',
30
+ onPrimary: '#ffffff',
31
+ primarySoft: '#e6edfe',
32
+ primaryText: '#00369f',
33
+ secondary: '#00cae1',
34
+ onSecondary: '#00363d',
35
+ secondarySoft: '#dcf6f9',
36
+ secondaryText: '#00707d',
37
+ success: '#34c759',
38
+ onSuccess: '#00320f',
39
+ successSoft: '#e3f6e9',
40
+ successText: '#116a30',
41
+ error: '#ff3b30',
42
+ onError: '#3d0300',
43
+ errorSoft: '#fdeaea',
44
+ errorText: '#c62222',
45
+ warning: '#f59e0b',
46
+ onWarning: '#3d2600',
47
+ warningSoft: '#fdf1de',
48
+ warningText: '#8a4b00',
20
49
  background: '#f8f9fa',
21
50
  surface: '#ffffff',
22
51
  text: '#333333',
23
52
  textLight: '#666666',
24
53
  border: '#e5e7eb',
25
54
  inputBackground: '#f5f7fa',
26
- overlay: 'rgba(0,0,0,0.5)'
55
+ overlay: 'rgba(0,0,0,0.5)',
56
+ scrim: 'rgba(248,249,250,0.8)',
57
+ skeleton: '#eceef1'
27
58
  };
59
+
60
+ /**
61
+ * Los acentos de dark NO son los de light: sobre `#1e1e1e` hay que subir la
62
+ * luminosidad del acento y bajar la del texto que va encima (por eso
63
+ * `onPrimary` es azul oscuro acá y blanco en light).
64
+ */
28
65
  export const darkPalette = {
29
- ...shared,
66
+ ...neutral,
67
+ primary: '#7ba4ff',
68
+ onPrimary: '#00205c',
69
+ primarySoft: '#1b2740',
70
+ primaryText: '#a9c3ff',
71
+ secondary: '#4dd8e8',
72
+ onSecondary: '#00363d',
73
+ secondarySoft: '#12333a',
74
+ secondaryText: '#7fe6f2',
75
+ success: '#5ddb84',
76
+ onSuccess: '#003514',
77
+ successSoft: '#13301e',
78
+ successText: '#7fe6a1',
79
+ error: '#ff7b72',
80
+ onError: '#4d0500',
81
+ errorSoft: '#3a1c1a',
82
+ errorText: '#ffa9a3',
83
+ warning: '#f0b458',
84
+ onWarning: '#3d2600',
85
+ warningSoft: '#33260f',
86
+ warningText: '#f5c97e',
30
87
  background: '#121212',
31
88
  surface: '#1e1e1e',
32
89
  text: '#ecedee',
33
90
  textLight: '#9ba1a6',
34
91
  border: '#2a2a2e',
35
92
  inputBackground: '#232326',
36
- overlay: 'rgba(0,0,0,0.6)'
93
+ overlay: 'rgba(0,0,0,0.6)',
94
+ scrim: 'rgba(18,18,18,0.8)',
95
+ skeleton: '#26262a'
37
96
  };
38
97
  //# sourceMappingURL=palettes.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["shared","primary","secondary","white","black","error","success","warning","lightPalette","background","surface","text","textLight","border","inputBackground","overlay","darkPalette"],"sourceRoot":"../../../src","sources":["theme/palettes.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;;AAkBA,MAAMA,MAAM,GAAG;EACbC,OAAO,EAAE,SAAS;EAClBC,SAAS,EAAE,SAAS;EACpBC,KAAK,EAAE,SAAS;EAChBC,KAAK,EAAE,SAAS;EAChBC,KAAK,EAAE,SAAS;EAChBC,OAAO,EAAE,SAAS;EAClBC,OAAO,EAAE;AACX,CAAC;AAED,OAAO,MAAMC,YAAyB,GAAG;EACvC,GAAGR,MAAM;EACTS,UAAU,EAAE,SAAS;EACrBC,OAAO,EAAE,SAAS;EAClBC,IAAI,EAAE,SAAS;EACfC,SAAS,EAAE,SAAS;EACpBC,MAAM,EAAE,SAAS;EACjBC,eAAe,EAAE,SAAS;EAC1BC,OAAO,EAAE;AACX,CAAC;AAED,OAAO,MAAMC,WAAwB,GAAG;EACtC,GAAGhB,MAAM;EACTS,UAAU,EAAE,SAAS;EACrBC,OAAO,EAAE,SAAS;EAClBC,IAAI,EAAE,SAAS;EACfC,SAAS,EAAE,SAAS;EACpBC,MAAM,EAAE,SAAS;EACjBC,eAAe,EAAE,SAAS;EAC1BC,OAAO,EAAE;AACX,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["neutral","white","black","lightPalette","primary","onPrimary","primarySoft","primaryText","secondary","onSecondary","secondarySoft","secondaryText","success","onSuccess","successSoft","successText","error","onError","errorSoft","errorText","warning","onWarning","warningSoft","warningText","background","surface","text","textLight","border","inputBackground","overlay","scrim","skeleton","darkPalette"],"sourceRoot":"../../../src","sources":["theme/palettes.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AA0CA,MAAMA,OAAO,GAAG;EACdC,KAAK,EAAE,SAAS;EAChBC,KAAK,EAAE;AACT,CAAC;AAED,OAAO,MAAMC,YAAyB,GAAG;EACvC,GAAGH,OAAO;EAEVI,OAAO,EAAE,SAAS;EAClBC,SAAS,EAAE,SAAS;EACpBC,WAAW,EAAE,SAAS;EACtBC,WAAW,EAAE,SAAS;EAEtBC,SAAS,EAAE,SAAS;EACpBC,WAAW,EAAE,SAAS;EACtBC,aAAa,EAAE,SAAS;EACxBC,aAAa,EAAE,SAAS;EAExBC,OAAO,EAAE,SAAS;EAClBC,SAAS,EAAE,SAAS;EACpBC,WAAW,EAAE,SAAS;EACtBC,WAAW,EAAE,SAAS;EAEtBC,KAAK,EAAE,SAAS;EAChBC,OAAO,EAAE,SAAS;EAClBC,SAAS,EAAE,SAAS;EACpBC,SAAS,EAAE,SAAS;EAEpBC,OAAO,EAAE,SAAS;EAClBC,SAAS,EAAE,SAAS;EACpBC,WAAW,EAAE,SAAS;EACtBC,WAAW,EAAE,SAAS;EAEtBC,UAAU,EAAE,SAAS;EACrBC,OAAO,EAAE,SAAS;EAClBC,IAAI,EAAE,SAAS;EACfC,SAAS,EAAE,SAAS;EACpBC,MAAM,EAAE,SAAS;EACjBC,eAAe,EAAE,SAAS;EAC1BC,OAAO,EAAE,iBAAiB;EAC1BC,KAAK,EAAE,uBAAuB;EAC9BC,QAAQ,EAAE;AACZ,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,WAAwB,GAAG;EACtC,GAAGjC,OAAO;EAEVI,OAAO,EAAE,SAAS;EAClBC,SAAS,EAAE,SAAS;EACpBC,WAAW,EAAE,SAAS;EACtBC,WAAW,EAAE,SAAS;EAEtBC,SAAS,EAAE,SAAS;EACpBC,WAAW,EAAE,SAAS;EACtBC,aAAa,EAAE,SAAS;EACxBC,aAAa,EAAE,SAAS;EAExBC,OAAO,EAAE,SAAS;EAClBC,SAAS,EAAE,SAAS;EACpBC,WAAW,EAAE,SAAS;EACtBC,WAAW,EAAE,SAAS;EAEtBC,KAAK,EAAE,SAAS;EAChBC,OAAO,EAAE,SAAS;EAClBC,SAAS,EAAE,SAAS;EACpBC,SAAS,EAAE,SAAS;EAEpBC,OAAO,EAAE,SAAS;EAClBC,SAAS,EAAE,SAAS;EACpBC,WAAW,EAAE,SAAS;EACtBC,WAAW,EAAE,SAAS;EAEtBC,UAAU,EAAE,SAAS;EACrBC,OAAO,EAAE,SAAS;EAClBC,IAAI,EAAE,SAAS;EACfC,SAAS,EAAE,SAAS;EACpBC,MAAM,EAAE,SAAS;EACjBC,eAAe,EAAE,SAAS;EAC1BC,OAAO,EAAE,iBAAiB;EAC1BC,KAAK,EAAE,oBAAoB;EAC3BC,QAAQ,EAAE;AACZ,CAAC","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"Badge.d.ts","sourceRoot":"","sources":["../../../src/atoms/Badge.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAe,MAAM,OAAO,CAAC;AACpC,OAAO,EAAc,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAI1E,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;AAEhF,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,6CAA6C;IAC7C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAYD;;;;;GAKG;AACH,eAAO,MAAM,KAAK,oFAA4E,UAAU,uBAqBtG,CAAC"}
1
+ {"version":3,"file":"Badge.d.ts","sourceRoot":"","sources":["../../../src/atoms/Badge.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAe,MAAM,OAAO,CAAC;AACpC,OAAO,EAAc,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAI1E,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;AAEhF,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,6CAA6C;IAC7C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAYD;;;;;GAKG;AACH,eAAO,MAAM,KAAK,oFAA4E,UAAU,uBAuBtG,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"Button.d.ts","sourceRoot":"","sources":["../../../src/atoms/Button.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuB,MAAM,OAAO,CAAC;AAC5C,OAAO,EAKL,KAAK,qBAAqB,EAC1B,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAItB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAI/C,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,aAAa,CAAC;AACnG,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE5C,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,CAAC,CAAC,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAC5C,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC5B,YAAY,CAAC,EAAE,QAAQ,CAAC;IACxB,aAAa,CAAC,EAAE,QAAQ,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AA8CD;;;;;GAKG;AACH,eAAO,MAAM,MAAM,+LAiBd,WAAW,uBA8Ef,CAAC"}
1
+ {"version":3,"file":"Button.d.ts","sourceRoot":"","sources":["../../../src/atoms/Button.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuB,MAAM,OAAO,CAAC;AAC5C,OAAO,EAKL,KAAK,qBAAqB,EAC1B,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAItB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAI/C,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,aAAa,CAAC;AACnG,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE5C,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,CAAC,CAAC,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAC5C,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC5B,YAAY,CAAC,EAAE,QAAQ,CAAC;IACxB,aAAa,CAAC,EAAE,QAAQ,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAgDD;;;;;GAKG;AACH,eAAO,MAAM,MAAM,+LAiBd,WAAW,uBA8Ef,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"Checkbox.d.ts","sourceRoot":"","sources":["../../../src/atoms/Checkbox.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkC,MAAM,OAAO,CAAC;AAMvD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,OAAO,CAAC;IACf,aAAa,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAgBD,eAAO,MAAM,QAAQ,uFAAmE,aAAa,uBA4CnG,CAAC"}
1
+ {"version":3,"file":"Checkbox.d.ts","sourceRoot":"","sources":["../../../src/atoms/Checkbox.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkC,MAAM,OAAO,CAAC;AAMvD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,OAAO,CAAC;IACf,aAAa,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAgBD,eAAO,MAAM,QAAQ,uFAAmE,aAAa,uBA+CnG,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"Alert.d.ts","sourceRoot":"","sources":["../../../src/organisms/Alert.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAe,MAAM,OAAO,CAAC;AAIpC,OAAO,EAAU,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAI7D,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,UAAU,CAAC;AAE9E,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,aAAa,CAAC;IAC7C,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAiDD;;;;GAIG;AACH,eAAO,MAAM,KAAK,oCAAgB,UAAU,8BA6E1C,CAAC"}
1
+ {"version":3,"file":"Alert.d.ts","sourceRoot":"","sources":["../../../src/organisms/Alert.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAe,MAAM,OAAO,CAAC;AAIpC,OAAO,EAAU,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAI7D,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,UAAU,CAAC;AAE9E,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,aAAa,CAAC;IAC7C,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAiDD;;;;GAIG;AACH,eAAO,MAAM,KAAK,oCAAgB,UAAU,8BA8E1C,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"Toast.d.ts","sourceRoot":"","sources":["../../../src/organisms/Toast.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkC,MAAM,OAAO,CAAC;AACvD,OAAO,EAAmC,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAM/F,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAEpE,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,yEAAyE;IACzE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAwCD;;;;;GAKG;AACH,eAAO,MAAM,KAAK,8GAC2E,UAAU,uBA0CtG,CAAC;AAGF,sFAAsF;AACtF,eAAO,MAAM,aAAa,qDACC;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,uBAmBvF,CAAC"}
1
+ {"version":3,"file":"Toast.d.ts","sourceRoot":"","sources":["../../../src/organisms/Toast.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkC,MAAM,OAAO,CAAC;AACvD,OAAO,EAAmC,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAM/F,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAEpE,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,yEAAyE;IACzE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAwCD;;;;;GAKG;AACH,eAAO,MAAM,KAAK,8GAC2E,UAAU,uBAkDtG,CAAC;AAGF,sFAAsF;AACtF,eAAO,MAAM,aAAa,qDACC;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,uBAmBvF,CAAC"}
@@ -0,0 +1,46 @@
1
+ import React from 'react';
2
+ import { type UIProviderProps } from '../theme/UIProvider';
3
+ export interface SafeAreaUIProviderProps extends Omit<UIProviderProps, 'insets'> {
4
+ /**
5
+ * Insets explícitos. Ganan sobre los medidos — útil para tests, storybook o
6
+ * una pantalla embebida que no ocupa la ventana entera.
7
+ */
8
+ insets?: UIProviderProps['insets'];
9
+ /**
10
+ * Montar también el `<SafeAreaProvider>`. Ponelo en false si tu app ya tiene
11
+ * uno arriba (React Navigation lo monta solo en algunas plantillas):
12
+ * un provider anidado mide el frame de *su* View, no el de la ventana, así
13
+ * que ahí adentro los insets darían cero.
14
+ * @default true
15
+ */
16
+ mountSafeAreaProvider?: boolean;
17
+ }
18
+ /**
19
+ * SafeAreaUIProvider
20
+ * `UIProvider` con los insets ya cableados desde `react-native-safe-area-context`.
21
+ *
22
+ * Vive en su propio subpath (`orn-ui/safe-area`) a propósito: es el único
23
+ * archivo de la librería que importa un paquete de terceros. Nada del entry
24
+ * principal lo referencia, así que quien hace `import { Button } from 'orn-ui'`
25
+ * nunca lo resuelve y el bundle sigue sin dependencias de runtime.
26
+ *
27
+ * ```tsx
28
+ * import { SafeAreaUIProvider } from 'orn-ui/safe-area';
29
+ *
30
+ * export default function App() {
31
+ * return (
32
+ * <SafeAreaUIProvider defaultMode="system">
33
+ * <RootNavigator />
34
+ * </SafeAreaUIProvider>
35
+ * );
36
+ * }
37
+ * ```
38
+ *
39
+ * El equivalente a mano — pasarle `insets={useSafeAreaInsets()}` a `UIProvider`
40
+ * desde un componente que ya esté dentro de `<SafeAreaProvider>` — sigue
41
+ * siendo válido. Esto sólo evita el error silencioso de olvidarlo: `insets`
42
+ * por defecto es `{0,0,0,0}`, y con ceros el bottom sheet queda pegado contra
43
+ * la barra de gestos y el modal `full` se mete abajo del notch.
44
+ */
45
+ export declare function SafeAreaUIProvider({ mountSafeAreaProvider, ...props }: SafeAreaUIProviderProps): React.JSX.Element;
46
+ //# sourceMappingURL=SafeAreaUIProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SafeAreaUIProvider.d.ts","sourceRoot":"","sources":["../../../src/safe-area/SafeAreaUIProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAc,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEvE,MAAM,WAAW,uBAAwB,SAAQ,IAAI,CAAC,eAAe,EAAE,QAAQ,CAAC;IAC9E;;;OAGG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC,QAAQ,CAAC,CAAC;IACnC;;;;;;OAMG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAWD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,qBAA4B,EAC5B,GAAG,KAAK,EACT,EAAE,uBAAuB,qBAOzB"}
@@ -0,0 +1,3 @@
1
+ export { SafeAreaUIProvider } from './SafeAreaUIProvider';
2
+ export type { SafeAreaUIProviderProps } from './SafeAreaUIProvider';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/safe-area/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,YAAY,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"UIProvider.d.ts","sourceRoot":"","sources":["../../../src/theme/UIProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoE,MAAM,OAAO,CAAC;AACzF,OAAO,EAAE,UAAU,EAAkB,MAAM,cAAc,CAAC;AAE1D,OAAO,EAAiB,KAAK,MAAM,EAAE,MAAM,UAAU,CAAC;AACtD,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEhD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;AAcpD,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,+FAA+F;IAC/F,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,2EAA2E;IAC3E,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,+FAA+F;IAC/F,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IACzC,yEAAyE;IACzE,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACzB,wFAAwF;IACxF,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,KAAoB,EACpB,WAAsB,EACtB,IAAI,EAAE,cAAc,EACpB,YAAY,EACZ,KAAyB,EACzB,MAAmB,EACnB,MAAM,EAAE,cAAc,EACtB,gBAAwB,GACzB,EAAE,eAAe,qBA8BjB;AAUD,wBAAgB,QAAQ,IAAI,KAAK,CAEhC;AAED,wBAAgB,SAAS,IAAI,KAAK,CAAC,QAAQ,CAAC,CAE3C;AAED,wBAAgB,YAAY;;oBA5FV,SAAS,KAAK,IAAI;EA+FnC;AAED,wBAAgB,QAAQ,IAAI,YAAY,CAEvC;AAED,wBAAgB,SAAS,IAAI,UAAU,CAEtC;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,wBAAgB,mBAAmB,IAAI,OAAO,CAE7C;AAED,8FAA8F;AAC9F,eAAO,MAAM,WAAW;;CAAiB,CAAC"}
1
+ {"version":3,"file":"UIProvider.d.ts","sourceRoot":"","sources":["../../../src/theme/UIProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoE,MAAM,OAAO,CAAC;AACzF,OAAO,EAAE,UAAU,EAAkB,MAAM,cAAc,CAAC;AAE1D,OAAO,EAAiB,KAAK,MAAM,EAAE,MAAM,UAAU,CAAC;AACtD,OAAO,EAAc,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEhD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;AAiBpD,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,+FAA+F;IAC/F,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,2EAA2E;IAC3E,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,+FAA+F;IAC/F,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IACzC,yEAAyE;IACzE,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACzB,wFAAwF;IACxF,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,KAAoB,EACpB,WAAsB,EACtB,IAAI,EAAE,cAAc,EACpB,YAAY,EACZ,KAAyB,EACzB,MAAmB,EACnB,MAAM,EAAE,cAAc,EACtB,gBAAwB,GACzB,EAAE,eAAe,qBA0CjB;AAUD,wBAAgB,QAAQ,IAAI,KAAK,CAEhC;AAED,wBAAgB,SAAS,IAAI,KAAK,CAAC,QAAQ,CAAC,CAE3C;AAED,wBAAgB,YAAY;;oBA3GV,SAAS,KAAK,IAAI;EA8GnC;AAED,wBAAgB,QAAQ,IAAI,YAAY,CAEvC;AAED,wBAAgB,SAAS,IAAI,UAAU,CAEtC;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,wBAAgB,mBAAmB,IAAI,OAAO,CAE7C;AAED,8FAA8F;AAC9F,eAAO,MAAM,WAAW;;CAAiB,CAAC"}
@@ -2,15 +2,44 @@
2
2
  * Cada color es `string`, no un literal (`as const`): light y dark tienen
3
3
  * valores distintos para la misma clave, y createTheme() debe poder
4
4
  * aceptar cualquier hex/rgba en los overrides — literal typing lo impediría.
5
+ *
6
+ * Cada acento viene en cuatro roles, y no son intercambiables:
7
+ *
8
+ * `primary` relleno sólido (fondo de botón, día seleccionado, FAB).
9
+ * `onPrimary` texto/ícono *encima* de ese relleno.
10
+ * `primarySoft` fondo teñido para estados seleccionados y pills.
11
+ * `primaryText` texto/ícono del acento sobre `surface`, `background`
12
+ * o sobre su propio `primarySoft`.
13
+ *
14
+ * El motivo es el contraste: un acento que se lee bien como relleno no se
15
+ * lee como texto. Antes los componentes usaban `colors.primary` para las dos
16
+ * cosas y lo teñían concatenando alpha al hex (`colors.primary + '15'`), lo
17
+ * que en dark daba 2.6:1 sobre la superficie — muy por debajo del 4.5:1 de
18
+ * WCAG AA. Todos los pares de esta paleta pasan AA en ambos esquemas.
5
19
  */
6
20
  export interface ThemeColors {
7
21
  primary: string;
22
+ onPrimary: string;
23
+ primarySoft: string;
24
+ primaryText: string;
8
25
  secondary: string;
9
- white: string;
10
- black: string;
11
- error: string;
26
+ onSecondary: string;
27
+ secondarySoft: string;
28
+ secondaryText: string;
12
29
  success: string;
30
+ onSuccess: string;
31
+ successSoft: string;
32
+ successText: string;
33
+ error: string;
34
+ onError: string;
35
+ errorSoft: string;
36
+ errorText: string;
13
37
  warning: string;
38
+ onWarning: string;
39
+ warningSoft: string;
40
+ warningText: string;
41
+ white: string;
42
+ black: string;
14
43
  background: string;
15
44
  surface: string;
16
45
  text: string;
@@ -18,7 +47,16 @@ export interface ThemeColors {
18
47
  border: string;
19
48
  inputBackground: string;
20
49
  overlay: string;
50
+ /** Velo semitransparente sobre contenido propio (footer de carga de List). */
51
+ scrim: string;
52
+ /** Fondo de placeholder mientras carga una imagen. */
53
+ skeleton: string;
21
54
  }
22
55
  export declare const lightPalette: ThemeColors;
56
+ /**
57
+ * Los acentos de dark NO son los de light: sobre `#1e1e1e` hay que subir la
58
+ * luminosidad del acento y bajar la del texto que va encima (por eso
59
+ * `onPrimary` es azul oscuro acá y blanco en light).
60
+ */
23
61
  export declare const darkPalette: ThemeColors;
24
62
  //# sourceMappingURL=palettes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"palettes.d.ts","sourceRoot":"","sources":["../../../src/theme/palettes.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB;AAYD,eAAO,MAAM,YAAY,EAAE,WAS1B,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,WASzB,CAAC"}
1
+ {"version":3,"file":"palettes.d.ts","sourceRoot":"","sources":["../../../src/theme/palettes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IAEpB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IAEtB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IAEpB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAElB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IAEpB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,KAAK,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAOD,eAAO,MAAM,YAAY,EAAE,WAqC1B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,WAqCzB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orn-ui",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Fast, tree-shakeable, atomic-design component library for React Native. Zero runtime dependencies.",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -255,22 +255,45 @@
255
255
  "react-native": "./src/icons/index.ts",
256
256
  "default": "./lib/module/icons/index.js"
257
257
  },
258
+ "./safe-area": {
259
+ "types": "./lib/typescript/safe-area/index.d.ts",
260
+ "react-native": "./src/safe-area/index.ts",
261
+ "default": "./lib/module/safe-area/index.js"
262
+ },
258
263
  "./package.json": "./package.json"
259
264
  },
265
+ "scripts": {
266
+ "gen:exports": "node scripts/gen-exports.mjs",
267
+ "gen:exports:check": "node scripts/gen-exports.mjs --check",
268
+ "build:registry": "node scripts/build-registry.mjs",
269
+ "test:cli": "node scripts/smoke-test-cli.mjs",
270
+ "build": "node scripts/gen-exports.mjs && node scripts/build-registry.mjs && bob build",
271
+ "typecheck": "tsc --noEmit",
272
+ "test": "jest",
273
+ "test:coverage": "jest --coverage",
274
+ "prepare": "node scripts/gen-exports.mjs && node scripts/build-registry.mjs && bob build"
275
+ },
260
276
  "peerDependencies": {
261
277
  "react": ">=18.0.0",
262
- "react-native": ">=0.73.0"
278
+ "react-native": ">=0.73.0",
279
+ "react-native-safe-area-context": ">=4.0.0"
280
+ },
281
+ "peerDependenciesMeta": {
282
+ "react-native-safe-area-context": {
283
+ "optional": true
284
+ }
263
285
  },
264
286
  "devDependencies": {
265
287
  "@react-native/jest-preset": "^0.86.2",
266
- "@types/jest": "^29.5.14",
267
288
  "@testing-library/react-native": "^13.3.3",
289
+ "@types/jest": "^29.5.14",
268
290
  "@types/react": "~19.2.14",
269
291
  "@types/react-test-renderer": "^19.1.0",
270
292
  "jest": "^29.7.0",
271
293
  "react": "19.2.3",
272
294
  "react-native": "0.86.2",
273
295
  "react-native-builder-bob": "^0.35.3",
296
+ "react-native-safe-area-context": "~5.8.1",
274
297
  "react-test-renderer": "19.2.3",
275
298
  "typescript": "~5.9.3"
276
299
  },
@@ -281,6 +304,15 @@
281
304
  "atomic-design"
282
305
  ],
283
306
  "license": "MIT",
307
+ "repository": {
308
+ "type": "git",
309
+ "url": "git+https://github.com/DavidTrujillo123/orn-ui.git",
310
+ "directory": "packages/ui"
311
+ },
312
+ "homepage": "https://github.com/DavidTrujillo123/orn-ui#readme",
313
+ "bugs": {
314
+ "url": "https://github.com/DavidTrujillo123/orn-ui/issues"
315
+ },
284
316
  "publishConfig": {
285
317
  "access": "public"
286
318
  },
@@ -297,15 +329,5 @@
297
329
  }
298
330
  ]
299
331
  ]
300
- },
301
- "scripts": {
302
- "gen:exports": "node scripts/gen-exports.mjs",
303
- "gen:exports:check": "node scripts/gen-exports.mjs --check",
304
- "build:registry": "node scripts/build-registry.mjs",
305
- "test:cli": "node scripts/smoke-test-cli.mjs",
306
- "build": "node scripts/gen-exports.mjs && node scripts/build-registry.mjs && bob build",
307
- "typecheck": "tsc --noEmit",
308
- "test": "jest",
309
- "test:coverage": "jest --coverage"
310
332
  }
311
- }
333
+ }
@@ -9,7 +9,7 @@
9
9
  "files": [
10
10
  {
11
11
  "path": "organisms/Alert.tsx",
12
- "content": "import React, { memo } from 'react';\nimport { Modal, Text, TouchableWithoutFeedback, View } from 'react-native';\nimport { createStyles } from '../theme/createStyles';\nimport { useAllowFontScaling, useColors, useLabels } from '../theme/UIProvider';\nimport { Button, type ButtonVariant } from '../atoms/Button';\nimport { Icon } from '../icons/Icon';\nimport type { IconName } from '../icons/types';\n\nexport type AlertType = 'success' | 'error' | 'warning' | 'info' | 'question';\n\nexport interface AlertButton {\n text: string;\n onPress?: () => void;\n style?: 'cancel' | 'default' | 'destructive';\n variant?: ButtonVariant;\n}\n\nexport interface AlertProps {\n visible: boolean;\n title: string;\n message?: string;\n type?: AlertType;\n onClose?: () => void;\n buttons?: AlertButton[];\n confirmText?: string;\n cancelText?: string;\n onConfirm?: () => void;\n onCancel?: () => void;\n inline?: boolean;\n}\n\nconst ICON_BY_TYPE: Record<AlertType, IconName> = {\n success: 'check',\n error: 'alert',\n warning: 'warning',\n question: 'question',\n info: 'info',\n};\n\nconst useStyles = createStyles((theme) => ({\n overlay: { flex: 1, backgroundColor: theme.colors.overlay, justifyContent: 'center', alignItems: 'center', padding: theme.tokens.spacing.xl },\n overlayInline: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, zIndex: 9999 },\n container: {\n backgroundColor: theme.colors.surface,\n borderRadius: theme.tokens.radius.xl,\n padding: theme.tokens.spacing.xl,\n width: '100%',\n maxWidth: 340,\n alignItems: 'center',\n ...theme.tokens.shadow.md,\n },\n iconContainer: { marginBottom: theme.tokens.spacing.lg - 1 },\n title: { fontSize: theme.tokens.fontSize.xl, fontWeight: theme.tokens.fontWeight.bold, color: theme.colors.text, textAlign: 'center', marginBottom: theme.tokens.spacing.md - 2 },\n message: { fontSize: theme.tokens.fontSize.md, color: theme.colors.textLight, textAlign: 'center', marginBottom: theme.tokens.spacing.xl, lineHeight: theme.tokens.lineHeight.md },\n buttonContainer: { flexDirection: 'row', justifyContent: 'center', gap: theme.tokens.spacing.sm + 2, width: '100%' },\n buttonContainerVertical: { flexDirection: 'column' },\n button: { paddingVertical: theme.tokens.spacing.md - 2, paddingHorizontal: theme.tokens.spacing.xl },\n buttonHorizontal: { flex: 1 },\n buttonVertical: { width: '100%' },\n}));\n\n/** Deriva la lista de botones a partir de props, sin mutar `buttons` recibido por props. */\nfunction resolveButtons(props: AlertProps, labels: { confirm: string; cancel: string; close: string }): AlertButton[] {\n const { buttons, confirmText, cancelText, onConfirm, onCancel, onClose } = props;\n if (buttons && buttons.length > 0) return buttons;\n\n if (confirmText || onConfirm) {\n const list: AlertButton[] = [];\n if (cancelText || onCancel) {\n list.push({ text: cancelText || labels.cancel, onPress: onCancel || onClose, variant: 'outline', style: 'cancel' });\n }\n list.push({ text: confirmText || labels.confirm, onPress: onConfirm, variant: 'primary' });\n return list;\n }\n\n return [{ text: labels.close, onPress: onClose, variant: 'outline' }];\n}\n\n/**\n * Alert\n * Componente declarativo de diálogo. Para uso imperativo (mostrar desde\n * un handler sin levantar estado propio), usar AlertProvider + useAlert().\n */\nexport const Alert = memo((props: AlertProps) => {\n const { visible, title, message, type = 'info', onClose, inline = false } = props;\n const colors = useColors();\n const styles = useStyles();\n const labels = useLabels();\n const allowFontScaling = useAllowFontScaling();\n\n if (!visible) return null;\n\n const iconColor: Record<AlertType, string> = {\n success: colors.success,\n error: colors.error,\n warning: colors.warning,\n question: colors.primary,\n info: colors.secondary,\n };\n\n const effectiveButtons = resolveButtons(props, { confirm: labels.confirm, cancel: labels.cancel, close: labels.close });\n const vertical = effectiveButtons.length > 2;\n\n const content = (\n <TouchableWithoutFeedback onPress={onClose} accessibilityRole=\"none\">\n <View style={[styles.overlay, inline && styles.overlayInline]}>\n <TouchableWithoutFeedback>\n <View style={styles.container} accessibilityRole=\"alert\" accessibilityViewIsModal>\n <View style={styles.iconContainer}>\n <Icon name={ICON_BY_TYPE[type]} size={48} color={iconColor[type]} />\n </View>\n\n <Text allowFontScaling={allowFontScaling} style={styles.title}>\n {title}\n </Text>\n\n {!!message && (\n <Text allowFontScaling={allowFontScaling} style={styles.message}>\n {message}\n </Text>\n )}\n\n <View style={[styles.buttonContainer, vertical && styles.buttonContainerVertical]}>\n {effectiveButtons.map((btn, index) => {\n const isCancel = btn.style === 'cancel';\n let variant: ButtonVariant = btn.variant || 'primary';\n if (!btn.variant && isCancel) variant = 'outline';\n if (!btn.variant && btn.style === 'destructive') variant = 'destructive';\n\n return (\n <Button\n key={index}\n title={btn.text}\n onPress={btn.onPress || onClose || (() => {})}\n variant={variant}\n style={[styles.button, vertical ? styles.buttonVertical : styles.buttonHorizontal]}\n textStyle={{ fontSize: 14 }}\n />\n );\n })}\n </View>\n </View>\n </TouchableWithoutFeedback>\n </View>\n </TouchableWithoutFeedback>\n );\n\n if (inline) {\n return (\n <View style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }} pointerEvents=\"box-none\">\n {content}\n </View>\n );\n }\n\n return (\n <Modal transparent visible={visible} animationType=\"fade\" statusBarTranslucent presentationStyle=\"overFullScreen\" onRequestClose={onClose || (() => {})}>\n {content}\n </Modal>\n );\n});\nAlert.displayName = 'Alert';\n"
12
+ "content": "import React, { memo } from 'react';\nimport { Modal, Text, TouchableWithoutFeedback, View } from 'react-native';\nimport { createStyles } from '../theme/createStyles';\nimport { useAllowFontScaling, useColors, useLabels } from '../theme/UIProvider';\nimport { Button, type ButtonVariant } from '../atoms/Button';\nimport { Icon } from '../icons/Icon';\nimport type { IconName } from '../icons/types';\n\nexport type AlertType = 'success' | 'error' | 'warning' | 'info' | 'question';\n\nexport interface AlertButton {\n text: string;\n onPress?: () => void;\n style?: 'cancel' | 'default' | 'destructive';\n variant?: ButtonVariant;\n}\n\nexport interface AlertProps {\n visible: boolean;\n title: string;\n message?: string;\n type?: AlertType;\n onClose?: () => void;\n buttons?: AlertButton[];\n confirmText?: string;\n cancelText?: string;\n onConfirm?: () => void;\n onCancel?: () => void;\n inline?: boolean;\n}\n\nconst ICON_BY_TYPE: Record<AlertType, IconName> = {\n success: 'check',\n error: 'alert',\n warning: 'warning',\n question: 'question',\n info: 'info',\n};\n\nconst useStyles = createStyles((theme) => ({\n overlay: { flex: 1, backgroundColor: theme.colors.overlay, justifyContent: 'center', alignItems: 'center', padding: theme.tokens.spacing.xl },\n overlayInline: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, zIndex: 9999 },\n container: {\n backgroundColor: theme.colors.surface,\n borderRadius: theme.tokens.radius.xl,\n padding: theme.tokens.spacing.xl,\n width: '100%',\n maxWidth: 340,\n alignItems: 'center',\n ...theme.tokens.shadow.md,\n },\n iconContainer: { marginBottom: theme.tokens.spacing.lg - 1 },\n title: { fontSize: theme.tokens.fontSize.xl, fontWeight: theme.tokens.fontWeight.bold, color: theme.colors.text, textAlign: 'center', marginBottom: theme.tokens.spacing.md - 2 },\n message: { fontSize: theme.tokens.fontSize.md, color: theme.colors.textLight, textAlign: 'center', marginBottom: theme.tokens.spacing.xl, lineHeight: theme.tokens.lineHeight.md },\n buttonContainer: { flexDirection: 'row', justifyContent: 'center', gap: theme.tokens.spacing.sm + 2, width: '100%' },\n buttonContainerVertical: { flexDirection: 'column' },\n button: { paddingVertical: theme.tokens.spacing.md - 2, paddingHorizontal: theme.tokens.spacing.xl },\n buttonHorizontal: { flex: 1 },\n buttonVertical: { width: '100%' },\n}));\n\n/** Deriva la lista de botones a partir de props, sin mutar `buttons` recibido por props. */\nfunction resolveButtons(props: AlertProps, labels: { confirm: string; cancel: string; close: string }): AlertButton[] {\n const { buttons, confirmText, cancelText, onConfirm, onCancel, onClose } = props;\n if (buttons && buttons.length > 0) return buttons;\n\n if (confirmText || onConfirm) {\n const list: AlertButton[] = [];\n if (cancelText || onCancel) {\n list.push({ text: cancelText || labels.cancel, onPress: onCancel || onClose, variant: 'outline', style: 'cancel' });\n }\n list.push({ text: confirmText || labels.confirm, onPress: onConfirm, variant: 'primary' });\n return list;\n }\n\n return [{ text: labels.close, onPress: onClose, variant: 'outline' }];\n}\n\n/**\n * Alert\n * Componente declarativo de diálogo. Para uso imperativo (mostrar desde\n * un handler sin levantar estado propio), usar AlertProvider + useAlert().\n */\nexport const Alert = memo((props: AlertProps) => {\n const { visible, title, message, type = 'info', onClose, inline = false } = props;\n const colors = useColors();\n const styles = useStyles();\n const labels = useLabels();\n const allowFontScaling = useAllowFontScaling();\n\n if (!visible) return null;\n\n const iconColor: Record<AlertType, string> = {\n // Íconos sobre la superficie del diálogo: rol `*Text`, no el acento pleno.\n success: colors.successText,\n error: colors.errorText,\n warning: colors.warningText,\n question: colors.primaryText,\n info: colors.secondaryText,\n };\n\n const effectiveButtons = resolveButtons(props, { confirm: labels.confirm, cancel: labels.cancel, close: labels.close });\n const vertical = effectiveButtons.length > 2;\n\n const content = (\n <TouchableWithoutFeedback onPress={onClose} accessibilityRole=\"none\">\n <View style={[styles.overlay, inline && styles.overlayInline]}>\n <TouchableWithoutFeedback>\n <View style={styles.container} accessibilityRole=\"alert\" accessibilityViewIsModal>\n <View style={styles.iconContainer}>\n <Icon name={ICON_BY_TYPE[type]} size={48} color={iconColor[type]} />\n </View>\n\n <Text allowFontScaling={allowFontScaling} style={styles.title}>\n {title}\n </Text>\n\n {!!message && (\n <Text allowFontScaling={allowFontScaling} style={styles.message}>\n {message}\n </Text>\n )}\n\n <View style={[styles.buttonContainer, vertical && styles.buttonContainerVertical]}>\n {effectiveButtons.map((btn, index) => {\n const isCancel = btn.style === 'cancel';\n let variant: ButtonVariant = btn.variant || 'primary';\n if (!btn.variant && isCancel) variant = 'outline';\n if (!btn.variant && btn.style === 'destructive') variant = 'destructive';\n\n return (\n <Button\n key={index}\n title={btn.text}\n onPress={btn.onPress || onClose || (() => {})}\n variant={variant}\n style={[styles.button, vertical ? styles.buttonVertical : styles.buttonHorizontal]}\n textStyle={{ fontSize: 14 }}\n />\n );\n })}\n </View>\n </View>\n </TouchableWithoutFeedback>\n </View>\n </TouchableWithoutFeedback>\n );\n\n if (inline) {\n return (\n <View style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }} pointerEvents=\"box-none\">\n {content}\n </View>\n );\n }\n\n return (\n <Modal transparent visible={visible} animationType=\"fade\" statusBarTranslucent presentationStyle=\"overFullScreen\" onRequestClose={onClose || (() => {})}>\n {content}\n </Modal>\n );\n});\nAlert.displayName = 'Alert';\n"
13
13
  }
14
14
  ],
15
15
  "registryDependencies": [
@@ -9,7 +9,7 @@
9
9
  "files": [
10
10
  {
11
11
  "path": "molecules/AvatarHeader.tsx",
12
- "content": "import React, { memo } from 'react';\nimport { Text, View, type StyleProp, type ViewStyle } from 'react-native';\nimport { createStyles } from '../theme/createStyles';\nimport { useAllowFontScaling, useColors } from '../theme/UIProvider';\nimport { Icon } from '../icons/Icon';\nimport type { IconName } from '../icons/types';\n\nexport interface AvatarHeaderProps {\n /** Ícono a mostrar dentro del círculo. Si se omite, se usan `initials`. */\n iconName?: IconName;\n iconColor?: string;\n /** Texto corto (ej. 2 letras) mostrado si no se pasa `iconName`. */\n initials?: string;\n title: string;\n subtitle?: React.ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\nconst useStyles = createStyles((theme) => ({\n container: { alignItems: 'center', marginBottom: theme.tokens.spacing.xl },\n avatar: {\n width: 80,\n height: 80,\n borderRadius: 40,\n backgroundColor: theme.colors.primary + '20',\n justifyContent: 'center',\n alignItems: 'center',\n marginBottom: theme.tokens.spacing.md,\n },\n initials: { fontSize: theme.tokens.fontSize.xxl, fontWeight: theme.tokens.fontWeight.bold, color: theme.colors.primary },\n title: { fontSize: theme.tokens.fontSize.xl, fontWeight: theme.tokens.fontWeight.bold, textAlign: 'center', color: theme.colors.text, marginBottom: theme.tokens.spacing.xs },\n subtitle: { fontSize: theme.tokens.fontSize.md, color: theme.colors.textLight },\n}));\n\n/**\n * AvatarHeader\n * Círculo 80x80 con ícono o iniciales + título/subtítulo centrado. Header\n * típico de modales de detalle (cliente, producto, etc).\n */\nexport const AvatarHeader = memo(({ iconName, iconColor, initials, title, subtitle, style }: AvatarHeaderProps) => {\n const colors = useColors();\n const styles = useStyles();\n const allowFontScaling = useAllowFontScaling();\n\n return (\n <View style={[styles.container, style]}>\n <View style={styles.avatar}>\n {iconName ? (\n <Icon name={iconName} size={40} color={iconColor || colors.primary} />\n ) : (\n <Text allowFontScaling={allowFontScaling} style={styles.initials}>\n {initials}\n </Text>\n )}\n </View>\n <Text allowFontScaling={allowFontScaling} style={styles.title}>\n {title}\n </Text>\n {typeof subtitle === 'string' ? (\n <Text allowFontScaling={allowFontScaling} style={styles.subtitle}>\n {subtitle}\n </Text>\n ) : (\n subtitle\n )}\n </View>\n );\n});\nAvatarHeader.displayName = 'AvatarHeader';\n"
12
+ "content": "import React, { memo } from 'react';\nimport { Text, View, type StyleProp, type ViewStyle } from 'react-native';\nimport { createStyles } from '../theme/createStyles';\nimport { useAllowFontScaling, useColors } from '../theme/UIProvider';\nimport { Icon } from '../icons/Icon';\nimport type { IconName } from '../icons/types';\n\nexport interface AvatarHeaderProps {\n /** Ícono a mostrar dentro del círculo. Si se omite, se usan `initials`. */\n iconName?: IconName;\n iconColor?: string;\n /** Texto corto (ej. 2 letras) mostrado si no se pasa `iconName`. */\n initials?: string;\n title: string;\n subtitle?: React.ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\nconst useStyles = createStyles((theme) => ({\n container: { alignItems: 'center', marginBottom: theme.tokens.spacing.xl },\n avatar: {\n width: 80,\n height: 80,\n borderRadius: 40,\n backgroundColor: theme.colors.primarySoft,\n justifyContent: 'center',\n alignItems: 'center',\n marginBottom: theme.tokens.spacing.md,\n },\n initials: { fontSize: theme.tokens.fontSize.xxl, fontWeight: theme.tokens.fontWeight.bold, color: theme.colors.primaryText },\n title: { fontSize: theme.tokens.fontSize.xl, fontWeight: theme.tokens.fontWeight.bold, textAlign: 'center', color: theme.colors.text, marginBottom: theme.tokens.spacing.xs },\n subtitle: { fontSize: theme.tokens.fontSize.md, color: theme.colors.textLight },\n}));\n\n/**\n * AvatarHeader\n * Círculo 80x80 con ícono o iniciales + título/subtítulo centrado. Header\n * típico de modales de detalle (cliente, producto, etc).\n */\nexport const AvatarHeader = memo(({ iconName, iconColor, initials, title, subtitle, style }: AvatarHeaderProps) => {\n const colors = useColors();\n const styles = useStyles();\n const allowFontScaling = useAllowFontScaling();\n\n return (\n <View style={[styles.container, style]}>\n <View style={styles.avatar}>\n {iconName ? (\n <Icon name={iconName} size={40} color={iconColor || colors.primary} />\n ) : (\n <Text allowFontScaling={allowFontScaling} style={styles.initials}>\n {initials}\n </Text>\n )}\n </View>\n <Text allowFontScaling={allowFontScaling} style={styles.title}>\n {title}\n </Text>\n {typeof subtitle === 'string' ? (\n <Text allowFontScaling={allowFontScaling} style={styles.subtitle}>\n {subtitle}\n </Text>\n ) : (\n subtitle\n )}\n </View>\n );\n});\nAvatarHeader.displayName = 'AvatarHeader';\n"
13
13
  }
14
14
  ],
15
15
  "registryDependencies": [
@@ -9,7 +9,7 @@
9
9
  "files": [
10
10
  {
11
11
  "path": "atoms/Avatar.tsx",
12
- "content": "import React, { memo } from 'react';\nimport { StyleSheet, View, type StyleProp, type ViewStyle } from 'react-native';\nimport { useColors } from '../theme/UIProvider';\n\nexport interface AvatarProps {\n size?: number;\n backgroundColor?: string;\n children: React.ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\nconst styles = StyleSheet.create({\n circle: { justifyContent: 'center', alignItems: 'center' },\n});\n\n/**\n * Avatar\n * Círculo con ícono/iniciales adentro. Usado a la izquierda de filas de\n * lista o como header de detalle (junto a AvatarHeader).\n */\nexport const Avatar = memo(({ size = 44, backgroundColor, children, style }: AvatarProps) => {\n const colors = useColors();\n return (\n <View\n style={[\n styles.circle,\n {\n width: size,\n height: size,\n borderRadius: size / 2,\n backgroundColor: backgroundColor || colors.primary + '15',\n },\n style,\n ]}\n >\n {children}\n </View>\n );\n});\nAvatar.displayName = 'Avatar';\n"
12
+ "content": "import React, { memo } from 'react';\nimport { StyleSheet, View, type StyleProp, type ViewStyle } from 'react-native';\nimport { useColors } from '../theme/UIProvider';\n\nexport interface AvatarProps {\n size?: number;\n backgroundColor?: string;\n children: React.ReactNode;\n style?: StyleProp<ViewStyle>;\n}\n\nconst styles = StyleSheet.create({\n circle: { justifyContent: 'center', alignItems: 'center' },\n});\n\n/**\n * Avatar\n * Círculo con ícono/iniciales adentro. Usado a la izquierda de filas de\n * lista o como header de detalle (junto a AvatarHeader).\n */\nexport const Avatar = memo(({ size = 44, backgroundColor, children, style }: AvatarProps) => {\n const colors = useColors();\n return (\n <View\n style={[\n styles.circle,\n {\n width: size,\n height: size,\n borderRadius: size / 2,\n backgroundColor: backgroundColor || colors.primarySoft,\n },\n style,\n ]}\n >\n {children}\n </View>\n );\n});\nAvatar.displayName = 'Avatar';\n"
13
13
  }
14
14
  ],
15
15
  "registryDependencies": [
@@ -9,7 +9,7 @@
9
9
  "files": [
10
10
  {
11
11
  "path": "atoms/Badge.tsx",
12
- "content": "import React, { memo } from 'react';\nimport { Text, View, type StyleProp, type ViewStyle } from 'react-native';\nimport { createStyles } from '../theme/createStyles';\nimport { useAllowFontScaling, useTheme } from '../theme/UIProvider';\n\nexport type BadgeVariant = 'success' | 'error' | 'warning' | 'info' | 'neutral';\n\nexport interface BadgeProps {\n label: string;\n variant?: BadgeVariant;\n /** Override manual, gana sobre `variant`. */\n backgroundColor?: string;\n textColor?: string;\n style?: StyleProp<ViewStyle>;\n}\n\nconst useStyles = createStyles((theme) => ({\n badge: {\n paddingHorizontal: theme.tokens.spacing.md - 2,\n paddingVertical: theme.tokens.spacing.xs + 1,\n borderRadius: theme.tokens.radius.full,\n alignSelf: 'flex-start',\n },\n text: { fontSize: theme.tokens.fontSize.xs, fontWeight: theme.tokens.fontWeight.bold, letterSpacing: 0.3 },\n}));\n\n/**\n * Badge\n * Pill de estado. `variant` cubre los casos comunes (éxito/error/advertencia/\n * info/neutral); `backgroundColor`/`textColor` quedan para casos que no\n * encajan en el set semántico.\n */\nexport const Badge = memo(({ label, variant = 'neutral', backgroundColor, textColor, style }: BadgeProps) => {\n const theme = useTheme();\n const styles = useStyles();\n const allowFontScaling = useAllowFontScaling();\n const { colors } = theme;\n\n const palette: Record<BadgeVariant, { bg: string; fg: string }> = {\n success: { bg: colors.success + '20', fg: colors.success },\n error: { bg: colors.error + '20', fg: colors.error },\n warning: { bg: colors.warning + '20', fg: colors.warning },\n info: { bg: colors.secondary + '20', fg: colors.secondary },\n neutral: { bg: colors.inputBackground, fg: colors.textLight },\n };\n\n return (\n <View style={[styles.badge, { backgroundColor: backgroundColor ?? palette[variant].bg }, style]}>\n <Text allowFontScaling={allowFontScaling} style={[styles.text, { color: textColor ?? palette[variant].fg }]}>\n {label}\n </Text>\n </View>\n );\n});\nBadge.displayName = 'Badge';\n"
12
+ "content": "import React, { memo } from 'react';\nimport { Text, View, type StyleProp, type ViewStyle } from 'react-native';\nimport { createStyles } from '../theme/createStyles';\nimport { useAllowFontScaling, useTheme } from '../theme/UIProvider';\n\nexport type BadgeVariant = 'success' | 'error' | 'warning' | 'info' | 'neutral';\n\nexport interface BadgeProps {\n label: string;\n variant?: BadgeVariant;\n /** Override manual, gana sobre `variant`. */\n backgroundColor?: string;\n textColor?: string;\n style?: StyleProp<ViewStyle>;\n}\n\nconst useStyles = createStyles((theme) => ({\n badge: {\n paddingHorizontal: theme.tokens.spacing.md - 2,\n paddingVertical: theme.tokens.spacing.xs + 1,\n borderRadius: theme.tokens.radius.full,\n alignSelf: 'flex-start',\n },\n text: { fontSize: theme.tokens.fontSize.xs, fontWeight: theme.tokens.fontWeight.bold, letterSpacing: 0.3 },\n}));\n\n/**\n * Badge\n * Pill de estado. `variant` cubre los casos comunes (éxito/error/advertencia/\n * info/neutral); `backgroundColor`/`textColor` quedan para casos que no\n * encajan en el set semántico.\n */\nexport const Badge = memo(({ label, variant = 'neutral', backgroundColor, textColor, style }: BadgeProps) => {\n const theme = useTheme();\n const styles = useStyles();\n const allowFontScaling = useAllowFontScaling();\n const { colors } = theme;\n\n const palette: Record<BadgeVariant, { bg: string; fg: string }> = {\n // fg es el rol `*Text`, no el acento pleno: el acento sobre su propio\n // tinte da ~2.2:1 en light — el label quedaba ilegible.\n success: { bg: colors.successSoft, fg: colors.successText },\n error: { bg: colors.errorSoft, fg: colors.errorText },\n warning: { bg: colors.warningSoft, fg: colors.warningText },\n info: { bg: colors.secondarySoft, fg: colors.secondaryText },\n neutral: { bg: colors.inputBackground, fg: colors.textLight },\n };\n\n return (\n <View style={[styles.badge, { backgroundColor: backgroundColor ?? palette[variant].bg }, style]}>\n <Text allowFontScaling={allowFontScaling} style={[styles.text, { color: textColor ?? palette[variant].fg }]}>\n {label}\n </Text>\n </View>\n );\n});\nBadge.displayName = 'Badge';\n"
13
13
  }
14
14
  ],
15
15
  "registryDependencies": [
@@ -9,7 +9,7 @@
9
9
  "files": [
10
10
  {
11
11
  "path": "atoms/Button.tsx",
12
- "content": "import React, { memo, useRef } from 'react';\nimport {\n ActivityIndicator,\n Animated,\n Pressable,\n Text,\n type GestureResponderEvent,\n type StyleProp,\n type TextStyle,\n type ViewStyle,\n} from 'react-native';\nimport { createStyles } from '../theme/createStyles';\nimport { useAllowFontScaling, useColors, useTheme } from '../theme/UIProvider';\nimport { Icon } from '../icons/Icon';\nimport type { IconName } from '../icons/types';\n\nconst AnimatedPressable = Animated.createAnimatedComponent(Pressable);\n\nexport type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost' | 'link' | 'destructive';\nexport type ButtonSize = 'sm' | 'md' | 'lg';\n\nexport interface ButtonProps {\n title: string;\n onPress: (e: GestureResponderEvent) => void;\n variant?: ButtonVariant;\n size?: ButtonSize;\n loading?: boolean;\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n textStyle?: StyleProp<TextStyle>;\n leftIcon?: React.ReactNode;\n rightIcon?: React.ReactNode;\n leftIconName?: IconName;\n rightIconName?: IconName;\n iconColor?: string;\n testID?: string;\n accessibilityLabel?: string;\n}\n\nconst useStyles = createStyles((theme) => ({\n base: {\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: theme.tokens.radius.lg,\n borderWidth: 0,\n },\n sm: { paddingVertical: theme.tokens.spacing.sm, paddingHorizontal: theme.tokens.spacing.md },\n md: { paddingVertical: theme.tokens.spacing.lg - 1, paddingHorizontal: theme.tokens.spacing.xl },\n lg: { paddingVertical: theme.tokens.spacing.lg + 2, paddingHorizontal: theme.tokens.spacing.xxl },\n link: { paddingVertical: theme.tokens.spacing.xs, paddingHorizontal: 0, justifyContent: 'flex-start' },\n outline: { borderWidth: 1 },\n filledShadow: { ...theme.tokens.shadow.md, shadowColor: '#000' },\n text: { fontWeight: theme.tokens.fontWeight.semibold, textAlign: 'center' },\n textSm: { fontSize: theme.tokens.fontSize.sm },\n textMd: { fontSize: theme.tokens.fontSize.lg },\n textLg: { fontSize: theme.tokens.fontSize.xl },\n underline: { textDecorationLine: 'underline' },\n iconLeft: { marginRight: theme.tokens.spacing.sm },\n iconRight: { marginLeft: theme.tokens.spacing.sm },\n}));\n\nfunction resolveColors(variant: ButtonVariant, disabled: boolean, colors: ReturnType<typeof useColors>) {\n if (disabled) {\n return { background: colors.inputBackground, text: colors.textLight, border: 'transparent' };\n }\n switch (variant) {\n case 'outline':\n return { background: 'transparent', text: colors.primary, border: colors.primary };\n case 'ghost':\n return { background: 'transparent', text: colors.primary, border: 'transparent' };\n case 'link':\n return { background: 'transparent', text: colors.primary, border: 'transparent' };\n case 'secondary':\n return { background: colors.secondary, text: colors.white, border: 'transparent' };\n case 'destructive':\n return { background: colors.error, text: colors.white, border: 'transparent' };\n case 'primary':\n default:\n return { background: colors.primary, text: colors.white, border: 'transparent' };\n }\n}\n\n/**\n * Button\n * Único componente de botón de la librería. `variant` decide el estilo\n * semántico, `size` la densidad. El feedback de \"presionado\" anima solo\n * `transform` (Animated.spring, useNativeDriver) — nunca opacity ni layout.\n */\nexport const Button = memo(\n ({\n title,\n onPress,\n variant = 'primary',\n size = 'md',\n loading = false,\n disabled = false,\n style,\n textStyle,\n leftIcon,\n rightIcon,\n leftIconName,\n rightIconName,\n iconColor,\n testID,\n accessibilityLabel,\n }: ButtonProps) => {\n const styles = useStyles();\n const colors = useColors();\n const theme = useTheme();\n const allowFontScaling = useAllowFontScaling();\n const scale = useRef(new Animated.Value(1)).current;\n const isDisabled = disabled || loading;\n const isFilled = variant === 'primary' || variant === 'secondary' || variant === 'destructive';\n\n const resolved = resolveColors(variant, disabled, colors);\n const effectiveIconColor = iconColor ?? resolved.text;\n\n const animateTo = (value: number) => {\n Animated.spring(scale, { toValue: value, useNativeDriver: true, speed: 50, bounciness: 6 }).start();\n };\n\n const containerStyle: StyleProp<ViewStyle> = [\n styles.base,\n styles[size],\n variant === 'link' && styles.link,\n variant === 'outline' && [styles.outline, { borderColor: resolved.border }],\n { backgroundColor: resolved.background },\n isFilled && !disabled && styles.filledShadow,\n { opacity: isDisabled ? 0.7 : 1 },\n style,\n ];\n\n const labelStyle: StyleProp<TextStyle> = [\n styles.text,\n size === 'sm' ? styles.textSm : size === 'lg' ? styles.textLg : styles.textMd,\n { color: resolved.text },\n variant === 'link' && styles.underline,\n textStyle,\n ];\n\n const iconSize = size === 'sm' ? 16 : size === 'lg' ? 22 : 20;\n\n return (\n // El Pressable ES el contenedor animado, no un wrapper alrededor de uno.\n // Con un Animated.View interno, un `style` de layout (flex, width,\n // margin) caía en el hijo mientras el Pressable — que es quien ocupa\n // lugar en el layout del padre — quedaba sin él: el ancho colapsaba y\n // el label desaparecía (visible en los botones en fila del Alert).\n <AnimatedPressable\n onPress={onPress}\n onPressIn={() => {\n if (!isDisabled) animateTo(0.95);\n }}\n onPressOut={() => {\n if (!isDisabled) animateTo(1);\n }}\n disabled={isDisabled}\n testID={testID}\n accessibilityRole=\"button\"\n accessibilityLabel={accessibilityLabel ?? title}\n accessibilityState={{ disabled: isDisabled, busy: loading }}\n style={[containerStyle, { transform: [{ scale }] }]}\n >\n {loading ? (\n <ActivityIndicator size=\"small\" color={resolved.text} />\n ) : (\n <>\n {leftIcon}\n {!leftIcon && leftIconName && (\n <Icon name={leftIconName} size={iconSize} color={effectiveIconColor} style={styles.iconLeft} />\n )}\n <Text allowFontScaling={allowFontScaling} style={labelStyle}>\n {title}\n </Text>\n {rightIcon}\n {!rightIcon && rightIconName && (\n <Icon name={rightIconName} size={iconSize} color={effectiveIconColor} style={styles.iconRight} />\n )}\n </>\n )}\n </AnimatedPressable>\n );\n }\n);\nButton.displayName = 'Button';\n"
12
+ "content": "import React, { memo, useRef } from 'react';\nimport {\n ActivityIndicator,\n Animated,\n Pressable,\n Text,\n type GestureResponderEvent,\n type StyleProp,\n type TextStyle,\n type ViewStyle,\n} from 'react-native';\nimport { createStyles } from '../theme/createStyles';\nimport { useAllowFontScaling, useColors, useTheme } from '../theme/UIProvider';\nimport { Icon } from '../icons/Icon';\nimport type { IconName } from '../icons/types';\n\nconst AnimatedPressable = Animated.createAnimatedComponent(Pressable);\n\nexport type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'ghost' | 'link' | 'destructive';\nexport type ButtonSize = 'sm' | 'md' | 'lg';\n\nexport interface ButtonProps {\n title: string;\n onPress: (e: GestureResponderEvent) => void;\n variant?: ButtonVariant;\n size?: ButtonSize;\n loading?: boolean;\n disabled?: boolean;\n style?: StyleProp<ViewStyle>;\n textStyle?: StyleProp<TextStyle>;\n leftIcon?: React.ReactNode;\n rightIcon?: React.ReactNode;\n leftIconName?: IconName;\n rightIconName?: IconName;\n iconColor?: string;\n testID?: string;\n accessibilityLabel?: string;\n}\n\nconst useStyles = createStyles((theme) => ({\n base: {\n flexDirection: 'row',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: theme.tokens.radius.lg,\n borderWidth: 0,\n },\n sm: { paddingVertical: theme.tokens.spacing.sm, paddingHorizontal: theme.tokens.spacing.md },\n md: { paddingVertical: theme.tokens.spacing.lg - 1, paddingHorizontal: theme.tokens.spacing.xl },\n lg: { paddingVertical: theme.tokens.spacing.lg + 2, paddingHorizontal: theme.tokens.spacing.xxl },\n link: { paddingVertical: theme.tokens.spacing.xs, paddingHorizontal: 0, justifyContent: 'flex-start' },\n outline: { borderWidth: 1 },\n filledShadow: { ...theme.tokens.shadow.md, shadowColor: '#000' },\n text: { fontWeight: theme.tokens.fontWeight.semibold, textAlign: 'center' },\n textSm: { fontSize: theme.tokens.fontSize.sm },\n textMd: { fontSize: theme.tokens.fontSize.lg },\n textLg: { fontSize: theme.tokens.fontSize.xl },\n underline: { textDecorationLine: 'underline' },\n iconLeft: { marginRight: theme.tokens.spacing.sm },\n iconRight: { marginLeft: theme.tokens.spacing.sm },\n}));\n\nfunction resolveColors(variant: ButtonVariant, disabled: boolean, colors: ReturnType<typeof useColors>) {\n if (disabled) {\n return { background: colors.inputBackground, text: colors.textLight, border: 'transparent' };\n }\n switch (variant) {\n // Las variantes sin relleno pintan texto sobre el fondo de la pantalla:\n // van con el rol `*Text`. Las rellenas van con `on*` sobre el acento.\n case 'outline':\n return { background: 'transparent', text: colors.primaryText, border: colors.primary };\n case 'ghost':\n return { background: 'transparent', text: colors.primaryText, border: 'transparent' };\n case 'link':\n return { background: 'transparent', text: colors.primaryText, border: 'transparent' };\n case 'secondary':\n return { background: colors.secondary, text: colors.onSecondary, border: 'transparent' };\n case 'destructive':\n return { background: colors.error, text: colors.onError, border: 'transparent' };\n case 'primary':\n default:\n return { background: colors.primary, text: colors.onPrimary, border: 'transparent' };\n }\n}\n\n/**\n * Button\n * Único componente de botón de la librería. `variant` decide el estilo\n * semántico, `size` la densidad. El feedback de \"presionado\" anima solo\n * `transform` (Animated.spring, useNativeDriver) — nunca opacity ni layout.\n */\nexport const Button = memo(\n ({\n title,\n onPress,\n variant = 'primary',\n size = 'md',\n loading = false,\n disabled = false,\n style,\n textStyle,\n leftIcon,\n rightIcon,\n leftIconName,\n rightIconName,\n iconColor,\n testID,\n accessibilityLabel,\n }: ButtonProps) => {\n const styles = useStyles();\n const colors = useColors();\n const theme = useTheme();\n const allowFontScaling = useAllowFontScaling();\n const scale = useRef(new Animated.Value(1)).current;\n const isDisabled = disabled || loading;\n const isFilled = variant === 'primary' || variant === 'secondary' || variant === 'destructive';\n\n const resolved = resolveColors(variant, disabled, colors);\n const effectiveIconColor = iconColor ?? resolved.text;\n\n const animateTo = (value: number) => {\n Animated.spring(scale, { toValue: value, useNativeDriver: true, speed: 50, bounciness: 6 }).start();\n };\n\n const containerStyle: StyleProp<ViewStyle> = [\n styles.base,\n styles[size],\n variant === 'link' && styles.link,\n variant === 'outline' && [styles.outline, { borderColor: resolved.border }],\n { backgroundColor: resolved.background },\n isFilled && !disabled && styles.filledShadow,\n { opacity: isDisabled ? 0.7 : 1 },\n style,\n ];\n\n const labelStyle: StyleProp<TextStyle> = [\n styles.text,\n size === 'sm' ? styles.textSm : size === 'lg' ? styles.textLg : styles.textMd,\n { color: resolved.text },\n variant === 'link' && styles.underline,\n textStyle,\n ];\n\n const iconSize = size === 'sm' ? 16 : size === 'lg' ? 22 : 20;\n\n return (\n // El Pressable ES el contenedor animado, no un wrapper alrededor de uno.\n // Con un Animated.View interno, un `style` de layout (flex, width,\n // margin) caía en el hijo mientras el Pressable — que es quien ocupa\n // lugar en el layout del padre — quedaba sin él: el ancho colapsaba y\n // el label desaparecía (visible en los botones en fila del Alert).\n <AnimatedPressable\n onPress={onPress}\n onPressIn={() => {\n if (!isDisabled) animateTo(0.95);\n }}\n onPressOut={() => {\n if (!isDisabled) animateTo(1);\n }}\n disabled={isDisabled}\n testID={testID}\n accessibilityRole=\"button\"\n accessibilityLabel={accessibilityLabel ?? title}\n accessibilityState={{ disabled: isDisabled, busy: loading }}\n style={[containerStyle, { transform: [{ scale }] }]}\n >\n {loading ? (\n <ActivityIndicator size=\"small\" color={resolved.text} />\n ) : (\n <>\n {leftIcon}\n {!leftIcon && leftIconName && (\n <Icon name={leftIconName} size={iconSize} color={effectiveIconColor} style={styles.iconLeft} />\n )}\n <Text allowFontScaling={allowFontScaling} style={labelStyle}>\n {title}\n </Text>\n {rightIcon}\n {!rightIcon && rightIconName && (\n <Icon name={rightIconName} size={iconSize} color={effectiveIconColor} style={styles.iconRight} />\n )}\n </>\n )}\n </AnimatedPressable>\n );\n }\n);\nButton.displayName = 'Button';\n"
13
13
  }
14
14
  ],
15
15
  "registryDependencies": [
@@ -9,7 +9,7 @@
9
9
  "files": [
10
10
  {
11
11
  "path": "atoms/Checkbox.tsx",
12
- "content": "import React, { memo, useEffect, useRef } from 'react';\nimport { Animated, Text, TouchableOpacity } from 'react-native';\nimport { createStyles } from '../theme/createStyles';\nimport { useAllowFontScaling, useColors } from '../theme/UIProvider';\nimport { Icon } from '../icons/Icon';\n\nexport interface CheckboxProps {\n value: boolean;\n onValueChange: (value: boolean) => void;\n label?: string;\n color?: string;\n disabled?: boolean;\n testID?: string;\n}\n\nconst useStyles = createStyles((theme) => ({\n container: { flexDirection: 'row', alignItems: 'center', marginBottom: theme.tokens.spacing.sm },\n box: {\n width: 24,\n height: 24,\n borderRadius: theme.tokens.radius.sm,\n borderWidth: 2,\n justifyContent: 'center',\n alignItems: 'center',\n marginRight: theme.tokens.spacing.sm + 2,\n },\n label: { fontSize: theme.tokens.fontSize.md, color: theme.colors.text },\n}));\n\nexport const Checkbox = memo(({ value, onValueChange, label, color, disabled, testID }: CheckboxProps) => {\n const colors = useColors();\n const styles = useStyles();\n const allowFontScaling = useAllowFontScaling();\n const activeColor = color || colors.primary;\n const progress = useRef(new Animated.Value(value ? 1 : 0)).current;\n const checkScale = useRef(new Animated.Value(value ? 1 : 0)).current;\n\n useEffect(() => {\n Animated.timing(progress, { toValue: value ? 1 : 0, duration: 150, useNativeDriver: false }).start();\n Animated.spring(checkScale, {\n toValue: value ? 1 : 0,\n useNativeDriver: true,\n speed: 40,\n bounciness: 10,\n }).start();\n }, [value]);\n\n const backgroundColor = progress.interpolate({ inputRange: [0, 1], outputRange: [colors.surface, activeColor] });\n const borderColor = progress.interpolate({ inputRange: [0, 1], outputRange: [colors.border, activeColor] });\n\n return (\n <TouchableOpacity\n style={styles.container}\n activeOpacity={0.7}\n disabled={disabled}\n onPress={() => onValueChange(!value)}\n accessibilityRole=\"checkbox\"\n accessibilityState={{ checked: value, disabled: !!disabled }}\n accessibilityLabel={label}\n testID={testID}\n >\n <Animated.View style={[styles.box, { backgroundColor, borderColor, opacity: disabled ? 0.5 : 1 }]}>\n <Animated.View style={{ transform: [{ scale: checkScale }] }}>\n <Icon name=\"check\" size={16} color={colors.white} />\n </Animated.View>\n </Animated.View>\n {label && (\n <Text allowFontScaling={allowFontScaling} style={styles.label}>\n {label}\n </Text>\n )}\n </TouchableOpacity>\n );\n});\nCheckbox.displayName = 'Checkbox';\n"
12
+ "content": "import React, { memo, useEffect, useRef } from 'react';\nimport { Animated, Text, TouchableOpacity } from 'react-native';\nimport { createStyles } from '../theme/createStyles';\nimport { useAllowFontScaling, useColors } from '../theme/UIProvider';\nimport { Icon } from '../icons/Icon';\n\nexport interface CheckboxProps {\n value: boolean;\n onValueChange: (value: boolean) => void;\n label?: string;\n color?: string;\n disabled?: boolean;\n testID?: string;\n}\n\nconst useStyles = createStyles((theme) => ({\n container: { flexDirection: 'row', alignItems: 'center', marginBottom: theme.tokens.spacing.sm },\n box: {\n width: 24,\n height: 24,\n borderRadius: theme.tokens.radius.sm,\n borderWidth: 2,\n justifyContent: 'center',\n alignItems: 'center',\n marginRight: theme.tokens.spacing.sm + 2,\n },\n label: { fontSize: theme.tokens.fontSize.md, color: theme.colors.text },\n}));\n\nexport const Checkbox = memo(({ value, onValueChange, label, color, disabled, testID }: CheckboxProps) => {\n const colors = useColors();\n const styles = useStyles();\n const allowFontScaling = useAllowFontScaling();\n const activeColor = color || colors.primary;\n // El check va encima del relleno: con `color` propio no sabemos qué tono\n // contrasta, así que ahí seguimos con blanco (decisión del consumidor).\n const checkColor = color ? colors.white : colors.onPrimary;\n const progress = useRef(new Animated.Value(value ? 1 : 0)).current;\n const checkScale = useRef(new Animated.Value(value ? 1 : 0)).current;\n\n useEffect(() => {\n Animated.timing(progress, { toValue: value ? 1 : 0, duration: 150, useNativeDriver: false }).start();\n Animated.spring(checkScale, {\n toValue: value ? 1 : 0,\n useNativeDriver: true,\n speed: 40,\n bounciness: 10,\n }).start();\n }, [value]);\n\n const backgroundColor = progress.interpolate({ inputRange: [0, 1], outputRange: [colors.surface, activeColor] });\n const borderColor = progress.interpolate({ inputRange: [0, 1], outputRange: [colors.border, activeColor] });\n\n return (\n <TouchableOpacity\n style={styles.container}\n activeOpacity={0.7}\n disabled={disabled}\n onPress={() => onValueChange(!value)}\n accessibilityRole=\"checkbox\"\n accessibilityState={{ checked: value, disabled: !!disabled }}\n accessibilityLabel={label}\n testID={testID}\n >\n <Animated.View style={[styles.box, { backgroundColor, borderColor, opacity: disabled ? 0.5 : 1 }]}>\n <Animated.View style={{ transform: [{ scale: checkScale }] }}>\n <Icon name=\"check\" size={16} color={checkColor} />\n </Animated.View>\n </Animated.View>\n {label && (\n <Text allowFontScaling={allowFontScaling} style={styles.label}>\n {label}\n </Text>\n )}\n </TouchableOpacity>\n );\n});\nCheckbox.displayName = 'Checkbox';\n"
13
13
  }
14
14
  ],
15
15
  "registryDependencies": [