orn-ui 0.2.3 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/molecules/Steps.js +14 -6
- package/lib/commonjs/molecules/Steps.js.map +1 -1
- package/lib/commonjs/molecules/SymmetricGrid.js +84 -0
- package/lib/commonjs/molecules/SymmetricGrid.js.map +1 -0
- package/lib/commonjs/molecules/index.js +7 -0
- package/lib/commonjs/molecules/index.js.map +1 -1
- package/lib/commonjs/organisms/AlertProvider.js +12 -4
- package/lib/commonjs/organisms/AlertProvider.js.map +1 -1
- package/lib/commonjs/organisms/BottomSheet.js +58 -50
- package/lib/commonjs/organisms/BottomSheet.js.map +1 -1
- package/lib/commonjs/organisms/Modal.js +44 -42
- package/lib/commonjs/organisms/Modal.js.map +1 -1
- package/lib/commonjs/organisms/ReorderableList.js +143 -0
- package/lib/commonjs/organisms/ReorderableList.js.map +1 -0
- package/lib/commonjs/organisms/Screen.js +1 -2
- package/lib/commonjs/organisms/Screen.js.map +1 -1
- package/lib/commonjs/organisms/index.js +7 -0
- package/lib/commonjs/organisms/index.js.map +1 -1
- package/lib/commonjs/safe-area/SafeAreaUIProvider.js +38 -30
- package/lib/commonjs/safe-area/SafeAreaUIProvider.js.map +1 -1
- package/lib/commonjs/theme/UIProvider.js +35 -8
- package/lib/commonjs/theme/UIProvider.js.map +1 -1
- package/lib/module/molecules/Steps.js +14 -6
- package/lib/module/molecules/Steps.js.map +1 -1
- package/lib/module/molecules/SymmetricGrid.js +79 -0
- package/lib/module/molecules/SymmetricGrid.js.map +1 -0
- package/lib/module/molecules/index.js +1 -0
- package/lib/module/molecules/index.js.map +1 -1
- package/lib/module/organisms/AlertProvider.js +12 -4
- package/lib/module/organisms/AlertProvider.js.map +1 -1
- package/lib/module/organisms/BottomSheet.js +59 -51
- package/lib/module/organisms/BottomSheet.js.map +1 -1
- package/lib/module/organisms/Modal.js +45 -43
- package/lib/module/organisms/Modal.js.map +1 -1
- package/lib/module/organisms/ReorderableList.js +137 -0
- package/lib/module/organisms/ReorderableList.js.map +1 -0
- package/lib/module/organisms/Screen.js +1 -2
- package/lib/module/organisms/Screen.js.map +1 -1
- package/lib/module/organisms/index.js +1 -0
- package/lib/module/organisms/index.js.map +1 -1
- package/lib/module/safe-area/SafeAreaUIProvider.js +39 -31
- package/lib/module/safe-area/SafeAreaUIProvider.js.map +1 -1
- package/lib/module/theme/UIProvider.js +36 -10
- package/lib/module/theme/UIProvider.js.map +1 -1
- package/lib/typescript/molecules/Steps.d.ts.map +1 -1
- package/lib/typescript/molecules/SymmetricGrid.d.ts +26 -0
- package/lib/typescript/molecules/SymmetricGrid.d.ts.map +1 -0
- package/lib/typescript/molecules/index.d.ts +2 -0
- package/lib/typescript/molecules/index.d.ts.map +1 -1
- package/lib/typescript/organisms/AlertProvider.d.ts.map +1 -1
- package/lib/typescript/organisms/BottomSheet.d.ts.map +1 -1
- package/lib/typescript/organisms/Modal.d.ts.map +1 -1
- package/lib/typescript/organisms/ReorderableList.d.ts +29 -0
- package/lib/typescript/organisms/ReorderableList.d.ts.map +1 -0
- package/lib/typescript/organisms/Screen.d.ts.map +1 -1
- package/lib/typescript/organisms/index.d.ts +2 -0
- package/lib/typescript/organisms/index.d.ts.map +1 -1
- package/lib/typescript/safe-area/SafeAreaUIProvider.d.ts +0 -38
- package/lib/typescript/safe-area/SafeAreaUIProvider.d.ts.map +1 -1
- package/lib/typescript/theme/UIProvider.d.ts +21 -1
- package/lib/typescript/theme/UIProvider.d.ts.map +1 -1
- package/package.json +11 -1
- package/registry/alert-provider.json +1 -1
- package/registry/bottom-sheet.json +1 -1
- package/registry/core.json +1 -1
- package/registry/index.json +30 -6
- package/registry/manifest.json +14 -0
- package/registry/modal.json +1 -1
- package/registry/reorderable-list.json +18 -0
- package/registry/screen.json +1 -1
- package/registry/steps.json +1 -1
- package/registry/symmetric-grid.json +18 -0
- package/src/molecules/Steps.tsx +14 -8
- package/src/molecules/SymmetricGrid.tsx +82 -0
- package/src/molecules/index.ts +2 -0
- package/src/organisms/AlertProvider.tsx +13 -5
- package/src/organisms/BottomSheet.tsx +58 -44
- package/src/organisms/Modal.tsx +63 -55
- package/src/organisms/ReorderableList.tsx +151 -0
- package/src/organisms/Screen.tsx +1 -2
- package/src/organisms/index.ts +2 -0
- package/src/safe-area/SafeAreaUIProvider.tsx +46 -41
- 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 {
|
|
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
|
|
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
|
package/src/theme/UIProvider.tsx
CHANGED
|
@@ -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
|
-
() => ({
|
|
100
|
-
|
|
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 };
|