orn-ui 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/lib/commonjs/molecules/Steps.js +14 -6
  2. package/lib/commonjs/molecules/Steps.js.map +1 -1
  3. package/lib/commonjs/molecules/SymmetricGrid.js +84 -0
  4. package/lib/commonjs/molecules/SymmetricGrid.js.map +1 -0
  5. package/lib/commonjs/molecules/index.js +7 -0
  6. package/lib/commonjs/molecules/index.js.map +1 -1
  7. package/lib/commonjs/organisms/AlertProvider.js +12 -4
  8. package/lib/commonjs/organisms/AlertProvider.js.map +1 -1
  9. package/lib/commonjs/organisms/BottomSheet.js +58 -50
  10. package/lib/commonjs/organisms/BottomSheet.js.map +1 -1
  11. package/lib/commonjs/organisms/Modal.js +44 -42
  12. package/lib/commonjs/organisms/Modal.js.map +1 -1
  13. package/lib/commonjs/organisms/ReorderableList.js +165 -0
  14. package/lib/commonjs/organisms/ReorderableList.js.map +1 -0
  15. package/lib/commonjs/organisms/Screen.js +1 -2
  16. package/lib/commonjs/organisms/Screen.js.map +1 -1
  17. package/lib/commonjs/organisms/index.js +7 -0
  18. package/lib/commonjs/organisms/index.js.map +1 -1
  19. package/lib/commonjs/safe-area/SafeAreaUIProvider.js +38 -30
  20. package/lib/commonjs/safe-area/SafeAreaUIProvider.js.map +1 -1
  21. package/lib/commonjs/theme/UIProvider.js +35 -8
  22. package/lib/commonjs/theme/UIProvider.js.map +1 -1
  23. package/lib/module/molecules/Steps.js +14 -6
  24. package/lib/module/molecules/Steps.js.map +1 -1
  25. package/lib/module/molecules/SymmetricGrid.js +79 -0
  26. package/lib/module/molecules/SymmetricGrid.js.map +1 -0
  27. package/lib/module/molecules/index.js +1 -0
  28. package/lib/module/molecules/index.js.map +1 -1
  29. package/lib/module/organisms/AlertProvider.js +12 -4
  30. package/lib/module/organisms/AlertProvider.js.map +1 -1
  31. package/lib/module/organisms/BottomSheet.js +59 -51
  32. package/lib/module/organisms/BottomSheet.js.map +1 -1
  33. package/lib/module/organisms/Modal.js +45 -43
  34. package/lib/module/organisms/Modal.js.map +1 -1
  35. package/lib/module/organisms/ReorderableList.js +159 -0
  36. package/lib/module/organisms/ReorderableList.js.map +1 -0
  37. package/lib/module/organisms/Screen.js +1 -2
  38. package/lib/module/organisms/Screen.js.map +1 -1
  39. package/lib/module/organisms/index.js +1 -0
  40. package/lib/module/organisms/index.js.map +1 -1
  41. package/lib/module/safe-area/SafeAreaUIProvider.js +39 -31
  42. package/lib/module/safe-area/SafeAreaUIProvider.js.map +1 -1
  43. package/lib/module/theme/UIProvider.js +36 -10
  44. package/lib/module/theme/UIProvider.js.map +1 -1
  45. package/lib/typescript/molecules/Steps.d.ts.map +1 -1
  46. package/lib/typescript/molecules/SymmetricGrid.d.ts +26 -0
  47. package/lib/typescript/molecules/SymmetricGrid.d.ts.map +1 -0
  48. package/lib/typescript/molecules/index.d.ts +2 -0
  49. package/lib/typescript/molecules/index.d.ts.map +1 -1
  50. package/lib/typescript/organisms/AlertProvider.d.ts.map +1 -1
  51. package/lib/typescript/organisms/BottomSheet.d.ts.map +1 -1
  52. package/lib/typescript/organisms/Modal.d.ts.map +1 -1
  53. package/lib/typescript/organisms/ReorderableList.d.ts +48 -0
  54. package/lib/typescript/organisms/ReorderableList.d.ts.map +1 -0
  55. package/lib/typescript/organisms/Screen.d.ts.map +1 -1
  56. package/lib/typescript/organisms/index.d.ts +2 -0
  57. package/lib/typescript/organisms/index.d.ts.map +1 -1
  58. package/lib/typescript/safe-area/SafeAreaUIProvider.d.ts +0 -38
  59. package/lib/typescript/safe-area/SafeAreaUIProvider.d.ts.map +1 -1
  60. package/lib/typescript/theme/UIProvider.d.ts +21 -1
  61. package/lib/typescript/theme/UIProvider.d.ts.map +1 -1
  62. package/package.json +11 -1
  63. package/registry/alert-provider.json +1 -1
  64. package/registry/bottom-sheet.json +1 -1
  65. package/registry/core.json +1 -1
  66. package/registry/index.json +30 -6
  67. package/registry/manifest.json +14 -0
  68. package/registry/modal.json +1 -1
  69. package/registry/reorderable-list.json +18 -0
  70. package/registry/screen.json +1 -1
  71. package/registry/steps.json +1 -1
  72. package/registry/symmetric-grid.json +18 -0
  73. package/src/molecules/Steps.tsx +14 -8
  74. package/src/molecules/SymmetricGrid.tsx +82 -0
  75. package/src/molecules/index.ts +2 -0
  76. package/src/organisms/AlertProvider.tsx +13 -5
  77. package/src/organisms/BottomSheet.tsx +58 -44
  78. package/src/organisms/Modal.tsx +63 -55
  79. package/src/organisms/ReorderableList.tsx +183 -0
  80. package/src/organisms/Screen.tsx +1 -2
  81. package/src/organisms/index.ts +2 -0
  82. package/src/safe-area/SafeAreaUIProvider.tsx +46 -41
  83. package/src/theme/UIProvider.tsx +50 -8
@@ -1,59 +1,64 @@
1
- import React from 'react';
1
+ import React, { useEffect, useState } from 'react';
2
+ import type { ViewStyle } from 'react-native';
2
3
  import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context';
3
- import { UIProvider, type UIProviderProps } from '../theme/UIProvider';
4
+ import {
5
+ UIProvider,
6
+ useInsets,
7
+ type ModalSafeAreaBoundaryProps,
8
+ type UIProviderProps,
9
+ } from '../theme/UIProvider';
10
+ import type { EdgeInsets } from '../theme/insets';
4
11
 
5
12
  export interface SafeAreaUIProviderProps extends Omit<UIProviderProps, 'insets'> {
6
- /**
7
- * Insets explícitos. Ganan sobre los medidos — útil para tests, storybook o
8
- * una pantalla embebida que no ocupa la ventana entera.
9
- */
10
13
  insets?: UIProviderProps['insets'];
11
- /**
12
- * Montar también el `<SafeAreaProvider>`. Ponelo en false si tu app ya tiene
13
- * uno arriba (React Navigation lo monta solo en algunas plantillas):
14
- * un provider anidado mide el frame de *su* View, no el de la ventana, así
15
- * que ahí adentro los insets darían cero.
16
- * @default true
17
- */
18
14
  mountSafeAreaProvider?: boolean;
19
15
  }
20
16
 
17
+ const probeStyle: ViewStyle = { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 };
18
+
19
+ function sameInsets(a: EdgeInsets, b: EdgeInsets) {
20
+ return a.top === b.top && a.right === b.right && a.bottom === b.bottom && a.left === b.left;
21
+ }
22
+
23
+ function InsetsProbe({ onMeasure }: { onMeasure: (insets: EdgeInsets) => void }) {
24
+ const insets = useSafeAreaInsets();
25
+ useEffect(() => {
26
+ onMeasure(insets);
27
+ }, [insets, onMeasure]);
28
+ return null;
29
+ }
30
+
31
+ function RemeasuringModalSafeAreaBoundary({ children }: ModalSafeAreaBoundaryProps) {
32
+ const ambient = useInsets();
33
+ const [measured, setMeasured] = useState<EdgeInsets | null>(null);
34
+ const onMeasure = React.useCallback(
35
+ (next: EdgeInsets) => setMeasured((prev) => (prev && sameInsets(prev, next) ? prev : next)),
36
+ []
37
+ );
38
+
39
+ return (
40
+ <>
41
+ <SafeAreaProvider style={probeStyle} pointerEvents="none">
42
+ <InsetsProbe onMeasure={onMeasure} />
43
+ </SafeAreaProvider>
44
+ {children(measured ?? ambient)}
45
+ </>
46
+ );
47
+ }
48
+
21
49
  function Measured({ insets: override, children, ...rest }: SafeAreaUIProviderProps) {
22
50
  const measured = useSafeAreaInsets();
23
51
  return (
24
- <UIProvider insets={override ?? measured} {...rest}>
52
+ <UIProvider
53
+ insets={override ?? measured}
54
+ ModalSafeAreaBoundary={RemeasuringModalSafeAreaBoundary}
55
+ {...rest}
56
+ >
25
57
  {children}
26
58
  </UIProvider>
27
59
  );
28
60
  }
29
61
 
30
- /**
31
- * SafeAreaUIProvider
32
- * `UIProvider` con los insets ya cableados desde `react-native-safe-area-context`.
33
- *
34
- * Vive en su propio subpath (`orn-ui/safe-area`) a propósito: es el único
35
- * archivo de la librería que importa un paquete de terceros. Nada del entry
36
- * principal lo referencia, así que quien hace `import { Button } from 'orn-ui'`
37
- * nunca lo resuelve y el bundle sigue sin dependencias de runtime.
38
- *
39
- * ```tsx
40
- * import { SafeAreaUIProvider } from 'orn-ui/safe-area';
41
- *
42
- * export default function App() {
43
- * return (
44
- * <SafeAreaUIProvider defaultMode="system">
45
- * <RootNavigator />
46
- * </SafeAreaUIProvider>
47
- * );
48
- * }
49
- * ```
50
- *
51
- * El equivalente a mano — pasarle `insets={useSafeAreaInsets()}` a `UIProvider`
52
- * desde un componente que ya esté dentro de `<SafeAreaProvider>` — sigue
53
- * siendo válido. Esto sólo evita el error silencioso de olvidarlo: `insets`
54
- * por defecto es `{0,0,0,0}`, y con ceros el bottom sheet queda pegado contra
55
- * la barra de gestos y el modal `full` se mete abajo del notch.
56
- */
57
62
  export function SafeAreaUIProvider({
58
63
  mountSafeAreaProvider = true,
59
64
  ...props
@@ -1,4 +1,4 @@
1
- import React, { createContext, useContext, useMemo, useState, useCallback } from 'react';
1
+ import React, { createContext, useContext, useMemo, useState, useCallback, useRef } from 'react';
2
2
  import { Appearance, useColorScheme } from 'react-native';
3
3
  import { defaultTheme } from './createTheme';
4
4
  import { defaultLabels, type Labels } from './labels';
@@ -9,6 +9,24 @@ import type { IconRenderer } from '../icons/types';
9
9
 
10
10
  export type ThemeMode = 'system' | 'light' | 'dark';
11
11
 
12
+ /**
13
+ * Un `<Modal>`/`<BottomSheet>` nativo abre su propia ventana (en Android
14
+ * siempre; en iOS según `presentationStyle`), así que los insets medidos en
15
+ * la raíz de la app pueden no valer ahí adentro. Este boundary es el punto de
16
+ * inyección: el default sólo repite los insets ambientes (comportamiento de
17
+ * siempre, cero deps), pero `SafeAreaUIProvider` lo reemplaza por uno que
18
+ * monta un `<SafeAreaProvider>` nuevo y remide dentro de esa ventana.
19
+ */
20
+ export interface ModalSafeAreaBoundaryProps {
21
+ children: (insets: EdgeInsets) => React.ReactNode;
22
+ }
23
+ export type ModalSafeAreaBoundaryComponent = React.ComponentType<ModalSafeAreaBoundaryProps>;
24
+
25
+ function DefaultModalSafeAreaBoundary({ children }: ModalSafeAreaBoundaryProps) {
26
+ const insets = useInsets();
27
+ return <>{children(insets)}</>;
28
+ }
29
+
12
30
  interface UIContextValue {
13
31
  theme: Theme;
14
32
  mode: ThemeMode;
@@ -17,13 +35,11 @@ interface UIContextValue {
17
35
  insets: EdgeInsets;
18
36
  labels: Labels;
19
37
  allowFontScaling: boolean;
38
+ ModalSafeAreaBoundary: ModalSafeAreaBoundaryComponent;
20
39
  }
21
40
 
22
41
  const UIContext = createContext<UIContextValue | undefined>(undefined);
23
42
 
24
- /** Una sola vez por sesión: en un re-render el aviso no aporta nada nuevo. */
25
- let warnedAboutInsets = false;
26
-
27
43
  export interface UIProviderProps {
28
44
  children: React.ReactNode;
29
45
  /** Par light/dark. Por defecto el theme de marca de la librería (créalo con createTheme()). */
@@ -40,6 +56,12 @@ export interface UIProviderProps {
40
56
  labels?: Partial<Labels>;
41
57
  /** @default false, para paridad con el resto de la librería (accesibilidad primero). */
42
58
  allowFontScaling?: boolean;
59
+ /**
60
+ * Fuente de insets que usan `Modal`/`BottomSheet` para su propia ventana
61
+ * nativa. No la pases a mano — `SafeAreaUIProvider` la cablea sola.
62
+ * @default repite los insets ambientes (comportamiento sin safe-area-context)
63
+ */
64
+ ModalSafeAreaBoundary?: ModalSafeAreaBoundaryComponent;
43
65
  }
44
66
 
45
67
  /**
@@ -59,6 +81,7 @@ export function UIProvider({
59
81
  insets = zeroInsets,
60
82
  labels: labelOverrides,
61
83
  allowFontScaling = false,
84
+ ModalSafeAreaBoundary = DefaultModalSafeAreaBoundary,
62
85
  }: UIProviderProps) {
63
86
  const systemScheme = useColorScheme();
64
87
  const [uncontrolledMode, setUncontrolledMode] = useState<ThemeMode>(defaultMode);
@@ -73,11 +96,16 @@ export function UIProvider({
73
96
  [isControlled, onModeChange]
74
97
  );
75
98
 
99
+ // Por instancia, no por módulo: dos `<UIProvider>` en el mismo árbol (tests,
100
+ // un modal con su propio provider) avisan cada uno si le faltan insets, en
101
+ // vez de que el segundo se calle porque el primero ya avisó una vez.
102
+ const warnedAboutInsets = useRef(false);
103
+
76
104
  // Olvidar `insets` no rompe nada visible al montar: el default de ceros
77
105
  // recién se nota cuando abrís un Modal `full` (se mete abajo del notch) o un
78
106
  // BottomSheet (queda pegado a la barra de gestos). El aviso llega antes.
79
- if (__DEV__ && insets === zeroInsets && !warnedAboutInsets) {
80
- warnedAboutInsets = true;
107
+ if (__DEV__ && insets === zeroInsets && !warnedAboutInsets.current) {
108
+ warnedAboutInsets.current = true;
81
109
  console.warn(
82
110
  'orn-ui: <UIProvider> sin `insets`, se usan {top:0,bottom:0,left:0,right:0}. ' +
83
111
  'Los modales y el Screen quedan sin safe area. Pasá `insets={useSafeAreaInsets()}` ' +
@@ -96,8 +124,17 @@ export function UIProvider({
96
124
  );
97
125
 
98
126
  const value = useMemo<UIContextValue>(
99
- () => ({ theme: resolvedTheme, mode, setMode, icons, insets, labels, allowFontScaling }),
100
- [resolvedTheme, mode, setMode, icons, insets, labels, allowFontScaling]
127
+ () => ({
128
+ theme: resolvedTheme,
129
+ mode,
130
+ setMode,
131
+ icons,
132
+ insets,
133
+ labels,
134
+ allowFontScaling,
135
+ ModalSafeAreaBoundary,
136
+ }),
137
+ [resolvedTheme, mode, setMode, icons, insets, labels, allowFontScaling, ModalSafeAreaBoundary]
101
138
  );
102
139
 
103
140
  return <UIContext.Provider value={value}>{children}</UIContext.Provider>;
@@ -140,5 +177,10 @@ export function useAllowFontScaling(): boolean {
140
177
  return useUIContext().allowFontScaling;
141
178
  }
142
179
 
180
+ /** Interno: lo consumen `Modal`/`BottomSheet` para remedir insets dentro de su propia ventana nativa. */
181
+ export function useModalSafeAreaBoundary(): ModalSafeAreaBoundaryComponent {
182
+ return useUIContext().ModalSafeAreaBoundary;
183
+ }
184
+
143
185
  /** Re-exportado para que tests/consumidores puedan simular cambios de sistema sin RN real. */
144
186
  export const __testUtils = { Appearance };