orn-ui 0.1.1 → 0.2.0
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/README.md +9 -2
- package/lib/commonjs/atoms/PressableScale.js +9 -10
- package/lib/commonjs/atoms/PressableScale.js.map +1 -1
- package/lib/commonjs/atoms/Skeleton.js +96 -0
- package/lib/commonjs/atoms/Skeleton.js.map +1 -0
- package/lib/commonjs/atoms/Transition.js +171 -0
- package/lib/commonjs/atoms/Transition.js.map +1 -0
- package/lib/commonjs/atoms/index.js +20 -0
- package/lib/commonjs/atoms/index.js.map +1 -1
- package/lib/commonjs/molecules/OptionCard.js +55 -22
- package/lib/commonjs/molecules/OptionCard.js.map +1 -1
- package/lib/commonjs/molecules/{ThemeToggle.js → SegmentedControl.js} +36 -36
- package/lib/commonjs/molecules/SegmentedControl.js.map +1 -0
- package/lib/commonjs/molecules/Stepper.js +124 -47
- package/lib/commonjs/molecules/Stepper.js.map +1 -1
- package/lib/commonjs/molecules/index.js +9 -3
- package/lib/commonjs/molecules/index.js.map +1 -1
- package/lib/commonjs/organisms/AlertProvider.js +26 -0
- package/lib/commonjs/organisms/AlertProvider.js.map +1 -1
- package/lib/commonjs/organisms/BottomSheet.js +118 -39
- package/lib/commonjs/organisms/BottomSheet.js.map +1 -1
- package/lib/commonjs/organisms/List.js +66 -44
- package/lib/commonjs/organisms/List.js.map +1 -1
- package/lib/commonjs/organisms/Modal.js +42 -87
- package/lib/commonjs/organisms/Modal.js.map +1 -1
- package/lib/commonjs/organisms/NavigationBar.js +205 -0
- package/lib/commonjs/organisms/NavigationBar.js.map +1 -0
- package/lib/commonjs/organisms/Screen.js +9 -2
- package/lib/commonjs/organisms/Screen.js.map +1 -1
- package/lib/commonjs/organisms/SearchList.js +97 -28
- package/lib/commonjs/organisms/SearchList.js.map +1 -1
- package/lib/commonjs/organisms/Select.js +222 -50
- package/lib/commonjs/organisms/Select.js.map +1 -1
- package/lib/commonjs/organisms/ThemeToggle.js +52 -0
- package/lib/commonjs/organisms/ThemeToggle.js.map +1 -0
- package/lib/commonjs/organisms/Toast.js +8 -22
- package/lib/commonjs/organisms/Toast.js.map +1 -1
- package/lib/commonjs/organisms/ToastProvider.js +24 -0
- package/lib/commonjs/organisms/ToastProvider.js.map +1 -1
- package/lib/commonjs/organisms/Wizard.js +22 -2
- package/lib/commonjs/organisms/Wizard.js.map +1 -1
- package/lib/commonjs/organisms/imperativeBridge.js +28 -0
- package/lib/commonjs/organisms/imperativeBridge.js.map +1 -0
- package/lib/commonjs/organisms/index.js +50 -0
- package/lib/commonjs/organisms/index.js.map +1 -1
- package/lib/commonjs/theme/colors.js +207 -0
- package/lib/commonjs/theme/colors.js.map +1 -0
- package/lib/commonjs/theme/createTheme.js +25 -2
- package/lib/commonjs/theme/createTheme.js.map +1 -1
- package/lib/commonjs/theme/index.js +25 -0
- package/lib/commonjs/theme/index.js.map +1 -1
- package/lib/commonjs/theme/palettes.js +5 -2
- package/lib/commonjs/theme/palettes.js.map +1 -1
- package/lib/module/atoms/PressableScale.js +9 -10
- package/lib/module/atoms/PressableScale.js.map +1 -1
- package/lib/module/atoms/Skeleton.js +91 -0
- package/lib/module/atoms/Skeleton.js.map +1 -0
- package/lib/module/atoms/Transition.js +165 -0
- package/lib/module/atoms/Transition.js.map +1 -0
- package/lib/module/atoms/index.js +2 -0
- package/lib/module/atoms/index.js.map +1 -1
- package/lib/module/molecules/OptionCard.js +56 -23
- package/lib/module/molecules/OptionCard.js.map +1 -1
- package/lib/module/molecules/{ThemeToggle.js → SegmentedControl.js} +36 -36
- package/lib/module/molecules/SegmentedControl.js.map +1 -0
- package/lib/module/molecules/Stepper.js +125 -49
- package/lib/module/molecules/Stepper.js.map +1 -1
- package/lib/module/molecules/index.js +2 -2
- package/lib/module/molecules/index.js.map +1 -1
- package/lib/module/organisms/AlertProvider.js +24 -1
- package/lib/module/organisms/AlertProvider.js.map +1 -1
- package/lib/module/organisms/BottomSheet.js +120 -41
- package/lib/module/organisms/BottomSheet.js.map +1 -1
- package/lib/module/organisms/List.js +68 -46
- package/lib/module/organisms/List.js.map +1 -1
- package/lib/module/organisms/Modal.js +44 -89
- package/lib/module/organisms/Modal.js.map +1 -1
- package/lib/module/organisms/NavigationBar.js +200 -0
- package/lib/module/organisms/NavigationBar.js.map +1 -0
- package/lib/module/organisms/Screen.js +9 -2
- package/lib/module/organisms/Screen.js.map +1 -1
- package/lib/module/organisms/SearchList.js +98 -29
- package/lib/module/organisms/SearchList.js.map +1 -1
- package/lib/module/organisms/Select.js +224 -52
- package/lib/module/organisms/Select.js.map +1 -1
- package/lib/module/organisms/ThemeToggle.js +47 -0
- package/lib/module/organisms/ThemeToggle.js.map +1 -0
- package/lib/module/organisms/Toast.js +10 -24
- package/lib/module/organisms/Toast.js.map +1 -1
- package/lib/module/organisms/ToastProvider.js +21 -0
- package/lib/module/organisms/ToastProvider.js.map +1 -1
- package/lib/module/organisms/Wizard.js +23 -3
- package/lib/module/organisms/Wizard.js.map +1 -1
- package/lib/module/organisms/imperativeBridge.js +24 -0
- package/lib/module/organisms/imperativeBridge.js.map +1 -0
- package/lib/module/organisms/index.js +4 -2
- package/lib/module/organisms/index.js.map +1 -1
- package/lib/module/theme/colors.js +194 -0
- package/lib/module/theme/colors.js.map +1 -0
- package/lib/module/theme/createTheme.js +25 -2
- package/lib/module/theme/createTheme.js.map +1 -1
- package/lib/module/theme/index.js +1 -0
- package/lib/module/theme/index.js.map +1 -1
- package/lib/module/theme/palettes.js +5 -2
- package/lib/module/theme/palettes.js.map +1 -1
- package/lib/typescript/atoms/PressableScale.d.ts +3 -2
- package/lib/typescript/atoms/PressableScale.d.ts.map +1 -1
- package/lib/typescript/atoms/Skeleton.d.ts +29 -0
- package/lib/typescript/atoms/Skeleton.d.ts.map +1 -0
- package/lib/typescript/atoms/Transition.d.ts +51 -0
- package/lib/typescript/atoms/Transition.d.ts.map +1 -0
- package/lib/typescript/atoms/index.d.ts +4 -0
- package/lib/typescript/atoms/index.d.ts.map +1 -1
- package/lib/typescript/molecules/OptionCard.d.ts +11 -6
- package/lib/typescript/molecules/OptionCard.d.ts.map +1 -1
- package/lib/typescript/molecules/SegmentedControl.d.ts +24 -0
- package/lib/typescript/molecules/SegmentedControl.d.ts.map +1 -0
- package/lib/typescript/molecules/Stepper.d.ts +35 -3
- package/lib/typescript/molecules/Stepper.d.ts.map +1 -1
- package/lib/typescript/molecules/index.d.ts +4 -4
- package/lib/typescript/molecules/index.d.ts.map +1 -1
- package/lib/typescript/organisms/AlertProvider.d.ts +9 -0
- package/lib/typescript/organisms/AlertProvider.d.ts.map +1 -1
- package/lib/typescript/organisms/BottomSheet.d.ts +26 -4
- package/lib/typescript/organisms/BottomSheet.d.ts.map +1 -1
- package/lib/typescript/organisms/List.d.ts +9 -2
- package/lib/typescript/organisms/List.d.ts.map +1 -1
- package/lib/typescript/organisms/Modal.d.ts +3 -5
- package/lib/typescript/organisms/Modal.d.ts.map +1 -1
- package/lib/typescript/organisms/NavigationBar.d.ts +32 -0
- package/lib/typescript/organisms/NavigationBar.d.ts.map +1 -0
- package/lib/typescript/organisms/Screen.d.ts +2 -1
- package/lib/typescript/organisms/Screen.d.ts.map +1 -1
- package/lib/typescript/organisms/SearchList.d.ts +21 -5
- package/lib/typescript/organisms/SearchList.d.ts.map +1 -1
- package/lib/typescript/organisms/Select.d.ts +19 -4
- package/lib/typescript/organisms/Select.d.ts.map +1 -1
- package/lib/typescript/{molecules → organisms}/ThemeToggle.d.ts +4 -4
- package/lib/typescript/organisms/ThemeToggle.d.ts.map +1 -0
- package/lib/typescript/organisms/Toast.d.ts.map +1 -1
- package/lib/typescript/organisms/ToastProvider.d.ts +8 -0
- package/lib/typescript/organisms/ToastProvider.d.ts.map +1 -1
- package/lib/typescript/organisms/Wizard.d.ts +7 -1
- package/lib/typescript/organisms/Wizard.d.ts.map +1 -1
- package/lib/typescript/organisms/imperativeBridge.d.ts +12 -0
- package/lib/typescript/organisms/imperativeBridge.d.ts.map +1 -0
- package/lib/typescript/organisms/index.d.ts +7 -3
- package/lib/typescript/organisms/index.d.ts.map +1 -1
- package/lib/typescript/theme/colors.d.ts +44 -0
- package/lib/typescript/theme/colors.d.ts.map +1 -0
- package/lib/typescript/theme/createTheme.d.ts +3 -0
- package/lib/typescript/theme/createTheme.d.ts.map +1 -1
- package/lib/typescript/theme/index.d.ts +3 -1
- package/lib/typescript/theme/index.d.ts.map +1 -1
- package/lib/typescript/theme/palettes.d.ts.map +1 -1
- package/lib/typescript/theme/types.d.ts +9 -0
- package/lib/typescript/theme/types.d.ts.map +1 -1
- package/package.json +67 -6
- package/registry/alert-provider.json +9 -2
- package/registry/bottom-sheet.json +6 -2
- package/registry/core.json +8 -4
- package/registry/date-field.json +1 -0
- package/registry/index.json +96 -27
- package/registry/list.json +2 -1
- package/registry/manifest.json +44 -6
- package/registry/modal.json +3 -2
- package/registry/navigation-bar.json +18 -0
- package/registry/option-card.json +3 -2
- package/registry/pressable-scale.json +1 -1
- package/registry/screen.json +1 -1
- package/registry/search-list.json +2 -1
- package/registry/segmented-control.json +18 -0
- package/registry/select.json +1 -1
- package/registry/skeleton.json +18 -0
- package/registry/stepper.json +3 -2
- package/registry/theme-toggle.json +5 -4
- package/registry/toast-provider.json +11 -3
- package/registry/toast.json +3 -2
- package/registry/transition.json +19 -0
- package/registry/wizard.json +3 -2
- package/src/atoms/PressableScale.tsx +16 -6
- package/src/atoms/Skeleton.tsx +114 -0
- package/src/atoms/Transition.tsx +189 -0
- package/src/atoms/index.ts +4 -0
- package/src/molecules/OptionCard.tsx +62 -25
- package/src/molecules/SegmentedControl.tsx +84 -0
- package/src/molecules/Stepper.tsx +147 -32
- package/src/molecules/index.ts +4 -4
- package/src/organisms/AlertProvider.tsx +26 -1
- package/src/organisms/BottomSheet.tsx +214 -62
- package/src/organisms/List.tsx +73 -41
- package/src/organisms/Modal.tsx +49 -61
- package/src/organisms/NavigationBar.tsx +223 -0
- package/src/organisms/Screen.tsx +11 -3
- package/src/organisms/SearchList.tsx +132 -36
- package/src/organisms/Select.tsx +223 -45
- package/src/organisms/ThemeToggle.tsx +30 -0
- package/src/organisms/Toast.tsx +8 -21
- package/src/organisms/ToastProvider.tsx +24 -0
- package/src/organisms/Wizard.tsx +36 -3
- package/src/organisms/imperativeBridge.ts +28 -0
- package/src/organisms/index.ts +7 -3
- package/src/theme/colors.ts +175 -0
- package/src/theme/createTheme.ts +38 -4
- package/src/theme/index.ts +3 -1
- package/src/theme/palettes.ts +5 -2
- package/src/theme/types.ts +10 -0
- package/lib/commonjs/molecules/ThemeToggle.js.map +0 -1
- package/lib/module/molecules/ThemeToggle.js.map +0 -1
- package/lib/typescript/molecules/ThemeToggle.d.ts.map +0 -1
- package/src/molecules/ThemeToggle.tsx +0 -67
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
export { Modal } from './Modal';
|
|
2
2
|
export type { ModalProps, ModalVariant } from './Modal';
|
|
3
3
|
export { BottomSheet } from './BottomSheet';
|
|
4
|
-
export type { BottomSheetProps } from './BottomSheet';
|
|
4
|
+
export type { BottomSheetProps, FooterPlacement } from './BottomSheet';
|
|
5
5
|
export { Select } from './Select';
|
|
6
6
|
export type { SelectProps, SelectOption } from './Select';
|
|
7
7
|
export { Alert } from './Alert';
|
|
8
8
|
export type { AlertProps, AlertButton, AlertType } from './Alert';
|
|
9
|
-
export { AlertProvider, useAlert } from './AlertProvider';
|
|
9
|
+
export { AlertProvider, useAlert, showAlert, showConfirm, hideAlert } from './AlertProvider';
|
|
10
10
|
export type { AlertOptions, ConfirmOptions } from './AlertProvider';
|
|
11
11
|
export { Screen } from './Screen';
|
|
12
12
|
export type { ScreenProps, ScreenEdge } from './Screen';
|
|
@@ -16,7 +16,7 @@ export { SearchList } from './SearchList';
|
|
|
16
16
|
export type { SearchListProps } from './SearchList';
|
|
17
17
|
export { Toast } from './Toast';
|
|
18
18
|
export type { ToastProps, ToastVariant } from './Toast';
|
|
19
|
-
export { ToastProvider, useToast } from './ToastProvider';
|
|
19
|
+
export { ToastProvider, useToast, showToast, hideToast, hideAllToasts } from './ToastProvider';
|
|
20
20
|
export type { ToastProviderProps, ToastOptions } from './ToastProvider';
|
|
21
21
|
export { DatePicker, buildMonthGrid, DEFAULT_MONTH_NAMES, DEFAULT_WEEKDAY_NAMES } from './DatePicker';
|
|
22
22
|
export type { DatePickerProps, DateRange } from './DatePicker';
|
|
@@ -24,4 +24,8 @@ export { DateField } from './DateField';
|
|
|
24
24
|
export type { DateFieldProps } from './DateField';
|
|
25
25
|
export { Wizard } from './Wizard';
|
|
26
26
|
export type { WizardProps, WizardStep } from './Wizard';
|
|
27
|
+
export { ThemeToggle } from './ThemeToggle';
|
|
28
|
+
export type { ThemeToggleProps } from './ThemeToggle';
|
|
29
|
+
export { NavigationBar } from './NavigationBar';
|
|
30
|
+
export type { NavigationBarProps, NavigationBarItem } from './NavigationBar';
|
|
27
31
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/organisms/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/organisms/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC1D,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC7F,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACpE,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACxD,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,YAAY,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC/F,YAAY,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACtG,YAAY,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilidades de color sin dependencias, usadas para derivar una familia de
|
|
3
|
+
* roles (relleno / encima / tenue / legible) a partir de un solo hex de marca.
|
|
4
|
+
*/
|
|
5
|
+
/** Contraste mínimo AA para texto normal. */
|
|
6
|
+
export declare const AA_CONTRAST = 4.5;
|
|
7
|
+
export interface Rgb {
|
|
8
|
+
r: number;
|
|
9
|
+
g: number;
|
|
10
|
+
b: number;
|
|
11
|
+
}
|
|
12
|
+
export interface Hsl {
|
|
13
|
+
h: number;
|
|
14
|
+
s: number;
|
|
15
|
+
l: number;
|
|
16
|
+
}
|
|
17
|
+
/** Roles que necesita un acento del theme, en un esquema concreto. */
|
|
18
|
+
export interface AccentRoles {
|
|
19
|
+
/** Relleno pleno: fondo de botón, círculo del paso actual. */
|
|
20
|
+
base: string;
|
|
21
|
+
/** Texto/ícono que va encima del relleno. */
|
|
22
|
+
on: string;
|
|
23
|
+
/** Fondo tenue: badges, pill del indicador activo. */
|
|
24
|
+
soft: string;
|
|
25
|
+
/** El acento legible sobre `surface`: labels, links. */
|
|
26
|
+
text: string;
|
|
27
|
+
}
|
|
28
|
+
export declare function hexToRgb(hex: string): Rgb;
|
|
29
|
+
export declare function rgbToHex({ r, g, b }: Rgb): string;
|
|
30
|
+
export declare function rgbToHsl({ r, g, b }: Rgb): Hsl;
|
|
31
|
+
export declare function hslToRgb({ h, s, l }: Hsl): Rgb;
|
|
32
|
+
export declare function toHsl(hex: string): Hsl;
|
|
33
|
+
export declare function toHex(hsl: Hsl): string;
|
|
34
|
+
/** Luminancia relativa WCAG de un hex. */
|
|
35
|
+
export declare function luminance(hex: string): number;
|
|
36
|
+
/** Ratio de contraste WCAG entre dos hex: 1 (igual) a 21 (blanco sobre negro). */
|
|
37
|
+
export declare function contrast(a: string, b: string): number;
|
|
38
|
+
/**
|
|
39
|
+
* Deriva los cuatro roles de un acento a partir de un hex, para un esquema.
|
|
40
|
+
* `surface` es el fondo sobre el que tiene que leerse el rol `text`, así que
|
|
41
|
+
* cada esquema pasa el suyo.
|
|
42
|
+
*/
|
|
43
|
+
export declare function deriveAccent(hex: string, scheme: 'light' | 'dark', surface: string): AccentRoles;
|
|
44
|
+
//# sourceMappingURL=colors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"colors.d.ts","sourceRoot":"","sources":["../../../src/theme/colors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,6CAA6C;AAC7C,eAAO,MAAM,WAAW,MAAM,CAAC;AAE/B,MAAM,WAAW,GAAG;IAClB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,GAAG;IAClB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,sEAAsE;AACtE,MAAM,WAAW,WAAW;IAC1B,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,EAAE,EAAE,MAAM,CAAC;IACX,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;CACd;AAID,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAYzC;AAID,wBAAgB,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,GAAG,MAAM,CAEjD;AAMD,wBAAgB,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,GAAG,GAAG,CAmB9C;AAED,wBAAgB,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,GAAG,GAAG,CAQ9C;AAED,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAEtC;AAED,wBAAgB,KAAK,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAEtC;AAOD,0CAA0C;AAC1C,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG7C;AAED,kFAAkF;AAClF,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAIrD;AAkCD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,WAAW,CAsBhG"}
|
|
@@ -3,6 +3,9 @@ import type { ThemeOverrides, ThemePair } from './types';
|
|
|
3
3
|
* Construye el par de themes light/dark que consume <UIProvider theme={...}>.
|
|
4
4
|
* Nunca muta los defaults: cada llamada produce objetos nuevos, seguros de
|
|
5
5
|
* compartir entre providers distintos (tests, multi-brand en el mismo árbol).
|
|
6
|
+
*
|
|
7
|
+
* `brand` se aplica antes que `colors`, así que un rol escrito a mano siempre
|
|
8
|
+
* gana sobre el derivado.
|
|
6
9
|
*/
|
|
7
10
|
export declare function createTheme(overrides?: ThemeOverrides): ThemePair;
|
|
8
11
|
export declare const defaultTheme: ThemePair;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createTheme.d.ts","sourceRoot":"","sources":["../../../src/theme/createTheme.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"createTheme.d.ts","sourceRoot":"","sources":["../../../src/theme/createTheme.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAsB,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AA+C7E;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,SAAS,CAAC,EAAE,cAAc,GAAG,SAAS,CAsBjE;AAED,eAAO,MAAM,YAAY,EAAE,SAAyB,CAAC"}
|
|
@@ -3,7 +3,9 @@ export type { Tokens } from './tokens';
|
|
|
3
3
|
export { lightPalette, darkPalette } from './palettes';
|
|
4
4
|
export type { ThemeColors } from './palettes';
|
|
5
5
|
export { createTheme, defaultTheme } from './createTheme';
|
|
6
|
-
export type { Theme, ThemePair, ThemeOverrides, DeepPartial } from './types';
|
|
6
|
+
export type { Theme, ThemePair, ThemeOverrides, BrandAccent, DeepPartial } from './types';
|
|
7
|
+
export { deriveAccent, contrast, luminance, AA_CONTRAST } from './colors';
|
|
8
|
+
export type { AccentRoles } from './colors';
|
|
7
9
|
export { defaultLabels } from './labels';
|
|
8
10
|
export type { Labels } from './labels';
|
|
9
11
|
export { zeroInsets } from './insets';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/theme/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvG,YAAY,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACvD,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC1D,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/theme/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvG,YAAY,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACvD,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC1D,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC1F,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC1E,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,YAAY,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EACL,UAAU,EACV,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,SAAS,EACT,SAAS,EACT,mBAAmB,GACpB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
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,
|
|
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,WAwC1B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,WAqCzB,CAAC"}
|
|
@@ -14,7 +14,16 @@ export interface ThemePair {
|
|
|
14
14
|
export type DeepPartial<T> = {
|
|
15
15
|
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
|
|
16
16
|
};
|
|
17
|
+
/** Acentos que se pueden definir con un solo hex vía `brand`. */
|
|
18
|
+
export type BrandAccent = 'primary' | 'secondary' | 'success' | 'error' | 'warning';
|
|
17
19
|
export interface ThemeOverrides {
|
|
20
|
+
/**
|
|
21
|
+
* Un hex por acento; de cada uno se derivan sus cuatro roles (relleno, lo
|
|
22
|
+
* que va encima, el fondo tenue y la variante legible sobre `surface`) para
|
|
23
|
+
* light y para dark, con el contraste ya resuelto. Un string suelto define
|
|
24
|
+
* `primary`. Lo que se pase en `colors` gana sobre lo derivado.
|
|
25
|
+
*/
|
|
26
|
+
brand?: string | Partial<Record<BrandAccent, string>>;
|
|
18
27
|
colors?: {
|
|
19
28
|
light?: Partial<ThemeColors>;
|
|
20
29
|
dark?: Partial<ThemeColors>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/theme/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,yEAAyE;AACzE,MAAM,WAAW,KAAK;IACpB,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC;CAC1B;AAED,+EAA+E;AAC/E,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,KAAK,CAAC;CACb;AAED,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;KAC1B,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAChE,CAAC;AAEF,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE;QACP,KAAK,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;QAC7B,IAAI,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;KAC7B,CAAC;IACF,MAAM,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAC9B"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/theme/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,yEAAyE;AACzE,MAAM,WAAW,KAAK;IACpB,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC;CAC1B;AAED,+EAA+E;AAC/E,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,KAAK,CAAC;CACb;AAED,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;KAC1B,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAChE,CAAC;AAEF,iEAAiE;AACjE,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;AAEpF,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD,MAAM,CAAC,EAAE;QACP,KAAK,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;QAC7B,IAAI,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;KAC7B,CAAC;IACF,MAAM,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CAC9B"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orn-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Fast, tree-shakeable, atomic-design component library for React Native. Zero runtime dependencies.",
|
|
5
|
+
"author": "David Trujillo <davot098@gmail.com>",
|
|
5
6
|
"main": "lib/commonjs/index.js",
|
|
6
7
|
"module": "lib/module/index.js",
|
|
7
8
|
"types": "lib/typescript/index.d.ts",
|
|
@@ -100,6 +101,21 @@
|
|
|
100
101
|
"react-native": "./src/atoms/Spinner.tsx",
|
|
101
102
|
"default": "./lib/module/atoms/Spinner.js"
|
|
102
103
|
},
|
|
104
|
+
"./skeleton": {
|
|
105
|
+
"types": "./lib/typescript/atoms/Skeleton.d.ts",
|
|
106
|
+
"react-native": "./src/atoms/Skeleton.tsx",
|
|
107
|
+
"default": "./lib/module/atoms/Skeleton.js"
|
|
108
|
+
},
|
|
109
|
+
"./transition": {
|
|
110
|
+
"types": "./lib/typescript/atoms/Transition.d.ts",
|
|
111
|
+
"react-native": "./src/atoms/Transition.tsx",
|
|
112
|
+
"default": "./lib/module/atoms/Transition.js"
|
|
113
|
+
},
|
|
114
|
+
"./use-reduce-motion": {
|
|
115
|
+
"types": "./lib/typescript/atoms/Transition.d.ts",
|
|
116
|
+
"react-native": "./src/atoms/Transition.tsx",
|
|
117
|
+
"default": "./lib/module/atoms/Transition.js"
|
|
118
|
+
},
|
|
103
119
|
"./empty-state": {
|
|
104
120
|
"types": "./lib/typescript/atoms/EmptyState.d.ts",
|
|
105
121
|
"react-native": "./src/atoms/EmptyState.tsx",
|
|
@@ -125,6 +141,11 @@
|
|
|
125
141
|
"react-native": "./src/molecules/Stepper.tsx",
|
|
126
142
|
"default": "./lib/module/molecules/Stepper.js"
|
|
127
143
|
},
|
|
144
|
+
"./sanitize-numeric": {
|
|
145
|
+
"types": "./lib/typescript/molecules/Stepper.d.ts",
|
|
146
|
+
"react-native": "./src/molecules/Stepper.tsx",
|
|
147
|
+
"default": "./lib/module/molecules/Stepper.js"
|
|
148
|
+
},
|
|
128
149
|
"./option-card": {
|
|
129
150
|
"types": "./lib/typescript/molecules/OptionCard.d.ts",
|
|
130
151
|
"react-native": "./src/molecules/OptionCard.tsx",
|
|
@@ -145,10 +166,10 @@
|
|
|
145
166
|
"react-native": "./src/molecules/AvatarHeader.tsx",
|
|
146
167
|
"default": "./lib/module/molecules/AvatarHeader.js"
|
|
147
168
|
},
|
|
148
|
-
"./
|
|
149
|
-
"types": "./lib/typescript/molecules/
|
|
150
|
-
"react-native": "./src/molecules/
|
|
151
|
-
"default": "./lib/module/molecules/
|
|
169
|
+
"./segmented-control": {
|
|
170
|
+
"types": "./lib/typescript/molecules/SegmentedControl.d.ts",
|
|
171
|
+
"react-native": "./src/molecules/SegmentedControl.tsx",
|
|
172
|
+
"default": "./lib/module/molecules/SegmentedControl.js"
|
|
152
173
|
},
|
|
153
174
|
"./steps": {
|
|
154
175
|
"types": "./lib/typescript/molecules/Steps.d.ts",
|
|
@@ -185,6 +206,21 @@
|
|
|
185
206
|
"react-native": "./src/organisms/AlertProvider.tsx",
|
|
186
207
|
"default": "./lib/module/organisms/AlertProvider.js"
|
|
187
208
|
},
|
|
209
|
+
"./show-alert": {
|
|
210
|
+
"types": "./lib/typescript/organisms/AlertProvider.d.ts",
|
|
211
|
+
"react-native": "./src/organisms/AlertProvider.tsx",
|
|
212
|
+
"default": "./lib/module/organisms/AlertProvider.js"
|
|
213
|
+
},
|
|
214
|
+
"./show-confirm": {
|
|
215
|
+
"types": "./lib/typescript/organisms/AlertProvider.d.ts",
|
|
216
|
+
"react-native": "./src/organisms/AlertProvider.tsx",
|
|
217
|
+
"default": "./lib/module/organisms/AlertProvider.js"
|
|
218
|
+
},
|
|
219
|
+
"./hide-alert": {
|
|
220
|
+
"types": "./lib/typescript/organisms/AlertProvider.d.ts",
|
|
221
|
+
"react-native": "./src/organisms/AlertProvider.tsx",
|
|
222
|
+
"default": "./lib/module/organisms/AlertProvider.js"
|
|
223
|
+
},
|
|
188
224
|
"./screen": {
|
|
189
225
|
"types": "./lib/typescript/organisms/Screen.d.ts",
|
|
190
226
|
"react-native": "./src/organisms/Screen.tsx",
|
|
@@ -215,6 +251,21 @@
|
|
|
215
251
|
"react-native": "./src/organisms/ToastProvider.tsx",
|
|
216
252
|
"default": "./lib/module/organisms/ToastProvider.js"
|
|
217
253
|
},
|
|
254
|
+
"./show-toast": {
|
|
255
|
+
"types": "./lib/typescript/organisms/ToastProvider.d.ts",
|
|
256
|
+
"react-native": "./src/organisms/ToastProvider.tsx",
|
|
257
|
+
"default": "./lib/module/organisms/ToastProvider.js"
|
|
258
|
+
},
|
|
259
|
+
"./hide-toast": {
|
|
260
|
+
"types": "./lib/typescript/organisms/ToastProvider.d.ts",
|
|
261
|
+
"react-native": "./src/organisms/ToastProvider.tsx",
|
|
262
|
+
"default": "./lib/module/organisms/ToastProvider.js"
|
|
263
|
+
},
|
|
264
|
+
"./hide-all-toasts": {
|
|
265
|
+
"types": "./lib/typescript/organisms/ToastProvider.d.ts",
|
|
266
|
+
"react-native": "./src/organisms/ToastProvider.tsx",
|
|
267
|
+
"default": "./lib/module/organisms/ToastProvider.js"
|
|
268
|
+
},
|
|
218
269
|
"./date-picker": {
|
|
219
270
|
"types": "./lib/typescript/organisms/DatePicker.d.ts",
|
|
220
271
|
"react-native": "./src/organisms/DatePicker.tsx",
|
|
@@ -245,6 +296,16 @@
|
|
|
245
296
|
"react-native": "./src/organisms/Wizard.tsx",
|
|
246
297
|
"default": "./lib/module/organisms/Wizard.js"
|
|
247
298
|
},
|
|
299
|
+
"./theme-toggle": {
|
|
300
|
+
"types": "./lib/typescript/organisms/ThemeToggle.d.ts",
|
|
301
|
+
"react-native": "./src/organisms/ThemeToggle.tsx",
|
|
302
|
+
"default": "./lib/module/organisms/ThemeToggle.js"
|
|
303
|
+
},
|
|
304
|
+
"./navigation-bar": {
|
|
305
|
+
"types": "./lib/typescript/organisms/NavigationBar.d.ts",
|
|
306
|
+
"react-native": "./src/organisms/NavigationBar.tsx",
|
|
307
|
+
"default": "./lib/module/organisms/NavigationBar.js"
|
|
308
|
+
},
|
|
248
309
|
"./theme": {
|
|
249
310
|
"types": "./lib/typescript/theme/index.d.ts",
|
|
250
311
|
"react-native": "./src/theme/index.ts",
|
|
@@ -309,7 +370,7 @@
|
|
|
309
370
|
"url": "git+https://github.com/DavidTrujillo123/orn-ui.git",
|
|
310
371
|
"directory": "packages/ui"
|
|
311
372
|
},
|
|
312
|
-
"homepage": "https://
|
|
373
|
+
"homepage": "https://orn-ui-docs.vercel.app/",
|
|
313
374
|
"bugs": {
|
|
314
375
|
"url": "https://github.com/DavidTrujillo123/orn-ui/issues"
|
|
315
376
|
},
|
|
@@ -5,12 +5,19 @@
|
|
|
5
5
|
"category": "organisms",
|
|
6
6
|
"exportedNames": [
|
|
7
7
|
"AlertProvider",
|
|
8
|
-
"useAlert"
|
|
8
|
+
"useAlert",
|
|
9
|
+
"showAlert",
|
|
10
|
+
"showConfirm",
|
|
11
|
+
"hideAlert"
|
|
9
12
|
],
|
|
10
13
|
"files": [
|
|
11
14
|
{
|
|
12
15
|
"path": "organisms/AlertProvider.tsx",
|
|
13
|
-
"content": "import React, { createContext, useCallback, useContext, useMemo, useRef, useState } from 'react';\nimport { Alert, type AlertButton, type AlertType } from './Alert';\n\nexport interface AlertOptions {\n title: string;\n message?: string;\n type?: AlertType;\n confirmText?: string;\n cancelText?: string;\n}\n\nexport interface ConfirmOptions extends AlertOptions {\n /** Botón de confirmar en rojo/destructivo (para \"eliminar\", etc). */\n destructive?: boolean;\n}\n\ninterface AlertContextValue {\n /** Muestra un alert de un solo botón. Resuelve cuando se cierra. */\n alert: (options: AlertOptions) => Promise<void>;\n /** Muestra confirmar/cancelar. Resuelve true si se confirmó, false si se canceló. */\n confirm: (options: ConfirmOptions) => Promise<boolean>;\n hide: () => void;\n}\n\nconst AlertContext = createContext<AlertContextValue | undefined>(undefined);\n\ninterface InternalState {\n visible: boolean;\n title: string;\n message?: string;\n type: AlertType;\n buttons: AlertButton[];\n}\n\nconst HIDDEN: InternalState = { visible: false, title: '', type: 'info', buttons: [] };\n\n/**\n * AlertProvider\n * Monta un único <Alert> controlado imperativamente. Colocar una vez cerca\n * de la raíz (dentro de UIProvider). Los handlers async pueden hacer\n * `const ok = await confirm({ title: 'Eliminar', destructive: true })`.\n */\nexport function AlertProvider({ children }: { children: React.ReactNode }) {\n const [state, setState] = useState<InternalState>(HIDDEN);\n const resolverRef = useRef<((value: any) => void) | null>(null);\n\n const hide = useCallback(() => {\n setState(HIDDEN);\n resolverRef.current?.(undefined);\n resolverRef.current = null;\n }, []);\n\n const alertFn = useCallback((options: AlertOptions): Promise<void> => {\n return new Promise((resolve) => {\n resolverRef.current = resolve;\n setState({\n visible: true,\n title: options.title,\n message: options.message,\n type: options.type ?? 'info',\n buttons: [\n {\n text: options.confirmText ?? 'OK',\n onPress: () => {\n setState(HIDDEN);\n resolve();\n resolverRef.current = null;\n },\n },\n ],\n });\n });\n }, []);\n\n const confirmFn = useCallback((options: ConfirmOptions): Promise<boolean> => {\n return new Promise((resolve) => {\n resolverRef.current = resolve;\n setState({\n visible: true,\n title: options.title,\n message: options.message,\n type: options.type ?? 'question',\n buttons: [\n {\n text: options.cancelText ?? 'Cancel',\n style: 'cancel',\n variant: 'outline',\n onPress: () => {\n setState(HIDDEN);\n resolve(false);\n resolverRef.current = null;\n },\n },\n {\n text: options.confirmText ?? 'OK',\n variant: options.destructive ? 'destructive' : 'primary',\n style: options.destructive ? 'destructive' : 'default',\n onPress: () => {\n setState(HIDDEN);\n resolve(true);\n resolverRef.current = null;\n },\n },\n ],\n });\n });\n }, []);\n\n const value = useMemo<AlertContextValue>(() => ({ alert: alertFn, confirm: confirmFn, hide }), [alertFn, confirmFn, hide]);\n\n return (\n <AlertContext.Provider value={value}>\n {children}\n <Alert visible={state.visible} title={state.title} message={state.message} type={state.type} buttons={state.buttons} onClose={hide} />\n </AlertContext.Provider>\n );\n}\n\nexport function useAlert(): AlertContextValue {\n const ctx = useContext(AlertContext);\n if (!ctx) {\n throw new Error('orn-ui: useAlert must be used within an <AlertProvider>.');\n }\n return ctx;\n}\n"
|
|
16
|
+
"content": "import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';\nimport { Alert, type AlertButton, type AlertType } from './Alert';\nimport { createImperativeBridge } from './imperativeBridge';\n\nexport interface AlertOptions {\n title: string;\n message?: string;\n type?: AlertType;\n confirmText?: string;\n cancelText?: string;\n}\n\nexport interface ConfirmOptions extends AlertOptions {\n /** Botón de confirmar en rojo/destructivo (para \"eliminar\", etc). */\n destructive?: boolean;\n}\n\ninterface AlertContextValue {\n /** Muestra un alert de un solo botón. Resuelve cuando se cierra. */\n alert: (options: AlertOptions) => Promise<void>;\n /** Muestra confirmar/cancelar. Resuelve true si se confirmó, false si se canceló. */\n confirm: (options: ConfirmOptions) => Promise<boolean>;\n hide: () => void;\n}\n\nconst AlertContext = createContext<AlertContextValue | undefined>(undefined);\nconst bridge = createImperativeBridge<AlertContextValue>('AlertProvider');\n\n/**\n * Abre un alert desde fuera del árbol de React —un servicio, un interceptor—\n * sin pasar por el hook. Sin <AlertProvider> montado resuelve de inmediato, así\n * que un `await` nunca queda colgado.\n */\nexport function showAlert(options: AlertOptions): Promise<void> {\n return bridge.get()?.alert(options) ?? Promise.resolve();\n}\n\n/** Igual que showAlert, pero resuelve true/false. Sin provider, false. */\nexport function showConfirm(options: ConfirmOptions): Promise<boolean> {\n return bridge.get()?.confirm(options) ?? Promise.resolve(false);\n}\n\nexport function hideAlert(): void {\n bridge.get()?.hide();\n}\n\ninterface InternalState {\n visible: boolean;\n title: string;\n message?: string;\n type: AlertType;\n buttons: AlertButton[];\n}\n\nconst HIDDEN: InternalState = { visible: false, title: '', type: 'info', buttons: [] };\n\n/**\n * AlertProvider\n * Monta un único <Alert> controlado imperativamente. Colocar una vez cerca\n * de la raíz (dentro de UIProvider). Los handlers async pueden hacer\n * `const ok = await confirm({ title: 'Eliminar', destructive: true })`.\n */\nexport function AlertProvider({ children }: { children: React.ReactNode }) {\n const [state, setState] = useState<InternalState>(HIDDEN);\n const resolverRef = useRef<((value: any) => void) | null>(null);\n\n const hide = useCallback(() => {\n setState(HIDDEN);\n resolverRef.current?.(undefined);\n resolverRef.current = null;\n }, []);\n\n const alertFn = useCallback((options: AlertOptions): Promise<void> => {\n return new Promise((resolve) => {\n resolverRef.current = resolve;\n setState({\n visible: true,\n title: options.title,\n message: options.message,\n type: options.type ?? 'info',\n buttons: [\n {\n text: options.confirmText ?? 'OK',\n onPress: () => {\n setState(HIDDEN);\n resolve();\n resolverRef.current = null;\n },\n },\n ],\n });\n });\n }, []);\n\n const confirmFn = useCallback((options: ConfirmOptions): Promise<boolean> => {\n return new Promise((resolve) => {\n resolverRef.current = resolve;\n setState({\n visible: true,\n title: options.title,\n message: options.message,\n type: options.type ?? 'question',\n buttons: [\n {\n text: options.cancelText ?? 'Cancel',\n style: 'cancel',\n variant: 'outline',\n onPress: () => {\n setState(HIDDEN);\n resolve(false);\n resolverRef.current = null;\n },\n },\n {\n text: options.confirmText ?? 'OK',\n variant: options.destructive ? 'destructive' : 'primary',\n style: options.destructive ? 'destructive' : 'default',\n onPress: () => {\n setState(HIDDEN);\n resolve(true);\n resolverRef.current = null;\n },\n },\n ],\n });\n });\n }, []);\n\n const value = useMemo<AlertContextValue>(() => ({ alert: alertFn, confirm: confirmFn, hide }), [alertFn, confirmFn, hide]);\n\n useEffect(() => {\n bridge.set(value);\n return () => bridge.set(null);\n }, [value]);\n\n return (\n <AlertContext.Provider value={value}>\n {children}\n <Alert visible={state.visible} title={state.title} message={state.message} type={state.type} buttons={state.buttons} onClose={hide} />\n </AlertContext.Provider>\n );\n}\n\nexport function useAlert(): AlertContextValue {\n const ctx = useContext(AlertContext);\n if (!ctx) {\n throw new Error('orn-ui: useAlert must be used within an <AlertProvider>.');\n }\n return ctx;\n}\n"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"path": "organisms/imperativeBridge.ts",
|
|
20
|
+
"content": "/**\n * Puente entre el proveedor montado y el código que no es un componente\n * (servicios, capa de API, interceptores). El proveedor se registra al montar y\n * se borra al desmontar, así que fuera del árbol nunca queda una referencia\n * viva a un contexto muerto.\n */\nexport interface ImperativeBridge<T> {\n set: (value: T | null) => void;\n get: () => T | null;\n}\n\nexport function createImperativeBridge<T>(providerName: string): ImperativeBridge<T> {\n let current: T | null = null;\n\n return {\n set: (value) => {\n current = value;\n },\n get: () => {\n if (!current && __DEV__) {\n console.warn(\n `orn-ui: se llamó a la API imperativa sin un <${providerName}> montado. La llamada se ignoró.`\n );\n }\n return current;\n },\n };\n}\n"
|
|
14
21
|
}
|
|
15
22
|
],
|
|
16
23
|
"registryDependencies": [
|
|
@@ -9,10 +9,14 @@
|
|
|
9
9
|
"files": [
|
|
10
10
|
{
|
|
11
11
|
"path": "organisms/BottomSheet.tsx",
|
|
12
|
-
"content": "import React, { memo, useEffect, useRef, useState } from 'react';\nimport {
|
|
12
|
+
"content": "import React, { memo, useEffect, useRef, useState } from 'react';\nimport {\n Animated,\n Dimensions,\n Keyboard,\n KeyboardAvoidingView,\n Modal,\n PanResponder,\n Platform,\n Pressable,\n ScrollView,\n View,\n type ViewStyle,\n} from 'react-native';\nimport { createStyles } from '../theme/createStyles';\nimport { useColors, useInsets } from '../theme/UIProvider';\nimport { Title } from '../atoms/Text';\nimport { IconButton } from '../atoms/IconButton';\nimport { Transition } from '../atoms/Transition';\n\nconst { height: SCREEN_HEIGHT } = Dimensions.get('window');\nconst DISMISS_THRESHOLD = 100;\n\nexport type FooterPlacement = 'fixed' | 'scroll' | 'hide-with-keyboard';\n\nexport interface BottomSheetProps {\n visible: boolean;\n onClose: () => void;\n children: React.ReactNode;\n /** Con título aparece el header, con su botón de cerrar. */\n title?: string;\n /** Acciones de la hoja. Dónde vive lo decide `footerPlacement`. */\n footer?: React.ReactNode;\n /**\n * 'fixed' lo ancla al pie de la hoja; 'scroll' lo deja al final del\n * contenido; 'hide-with-keyboard' lo esconde mientras se escribe, que en un\n * formulario largo le devuelve esa franja al campo enfocado. @default 'fixed'\n */\n footerPlacement?: FooterPlacement;\n /** El contenido scrollea cuando no entra. @default true */\n scrollable?: boolean;\n /** Arrastrar la hoja hacia abajo la cierra. @default true */\n draggable?: boolean;\n /** Fracción de la pantalla (0-1) o píxeles. @default 0.9 */\n maxHeight?: number;\n closeAccessibilityLabel?: string;\n containerStyle?: ViewStyle;\n contentStyle?: ViewStyle;\n testID?: string;\n}\n\nconst useStyles = createStyles((theme) => ({\n container: { flex: 1, justifyContent: 'flex-end' },\n backdrop: { backgroundColor: theme.colors.overlay },\n sheet: {\n backgroundColor: theme.colors.surface,\n borderTopLeftRadius: theme.tokens.radius.xxl,\n borderTopRightRadius: theme.tokens.radius.xxl,\n ...theme.tokens.shadow.lg,\n },\n handleArea: { paddingVertical: theme.tokens.spacing.md, alignItems: 'center' },\n handle: { width: 40, height: 5, borderRadius: 3, backgroundColor: theme.colors.border },\n header: {\n flexDirection: 'row',\n alignItems: 'center',\n gap: theme.tokens.spacing.md,\n paddingHorizontal: theme.tokens.spacing.xl,\n paddingBottom: theme.tokens.spacing.md,\n },\n headerSpaced: { paddingTop: theme.tokens.spacing.xl },\n title: { flex: 1, fontSize: theme.tokens.fontSize.xl, fontWeight: theme.tokens.fontWeight.bold },\n content: { paddingHorizontal: theme.tokens.spacing.xl },\n scroll: { flexShrink: 1 },\n footer: {\n paddingHorizontal: theme.tokens.spacing.xl,\n paddingTop: theme.tokens.spacing.md,\n borderTopWidth: 1,\n borderTopColor: theme.colors.border,\n },\n footerInline: { paddingTop: theme.tokens.spacing.xl },\n}));\n\nconst absoluteFill: ViewStyle = { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 };\n\n/**\n * BottomSheet\n * Hoja que entra desde abajo, con arrastre para cerrar (PanResponder, no un\n * gesture-handler externo: la librería es cero dependencias). Header, footer y\n * scroll son opcionales, así que cubre tanto la hoja desnuda como el formulario\n * completo.\n */\nexport const BottomSheet = memo(\n ({\n visible,\n onClose,\n children,\n title,\n footer,\n footerPlacement = 'fixed',\n scrollable = true,\n draggable = true,\n maxHeight = 0.9,\n closeAccessibilityLabel = 'Close',\n containerStyle,\n contentStyle,\n testID,\n }: BottomSheetProps) => {\n const styles = useStyles();\n const colors = useColors();\n const insets = useInsets();\n const [mounted, setMounted] = useState(visible);\n const [keyboardOpen, setKeyboardOpen] = useState(false);\n const progress = useRef(new Animated.Value(0)).current;\n\n useEffect(() => {\n if (visible) {\n setMounted(true);\n Animated.timing(progress, { toValue: 1, duration: 250, useNativeDriver: true }).start();\n } else {\n Animated.timing(progress, { toValue: 0, duration: 200, useNativeDriver: true }).start(({ finished }) => {\n if (finished) setMounted(false);\n });\n }\n }, [visible, progress]);\n\n useEffect(() => {\n if (footerPlacement !== 'hide-with-keyboard') return;\n const [show, hide] =\n Platform.OS === 'ios' ? ['keyboardWillShow', 'keyboardWillHide'] : ['keyboardDidShow', 'keyboardDidHide'];\n const subs = [\n Keyboard.addListener(show as 'keyboardDidShow', () => setKeyboardOpen(true)),\n Keyboard.addListener(hide as 'keyboardDidHide', () => setKeyboardOpen(false)),\n ];\n return () => subs.forEach((sub) => sub.remove());\n }, [footerPlacement]);\n\n const panResponder = useRef(\n PanResponder.create({\n onMoveShouldSetPanResponder: (_, gesture) => gesture.dy > 5 && Math.abs(gesture.dy) > Math.abs(gesture.dx),\n onPanResponderMove: (_, gesture) => {\n if (gesture.dy > 0) progress.setValue(1 - Math.min(gesture.dy, SCREEN_HEIGHT) / SCREEN_HEIGHT);\n },\n onPanResponderRelease: (_, gesture) => {\n if (gesture.dy > DISMISS_THRESHOLD) {\n onClose();\n } else {\n Animated.timing(progress, { toValue: 1, duration: 150, useNativeDriver: true }).start();\n }\n },\n })\n ).current;\n\n if (!mounted) return null;\n\n // Porcentaje y no píxeles: se mide contra el contenedor, que con el teclado\n // abierto es más bajo, así que la hoja entra siempre entera.\n const resolvedMaxHeight: number | `${number}%` = maxHeight <= 1 ? `${maxHeight * 100}%` : maxHeight;\n const hasHeader = !!title;\n\n const scrolledFooter = !!footer && footerPlacement === 'scroll' && (\n <View style={styles.footerInline}>{footer}</View>\n );\n const pinnedFooter =\n !!footer && footerPlacement !== 'scroll' && !(footerPlacement === 'hide-with-keyboard' && keyboardOpen) ? (\n <View style={styles.footer}>{footer}</View>\n ) : null;\n\n const body = scrollable ? (\n <ScrollView\n style={styles.scroll}\n contentContainerStyle={[styles.content, contentStyle]}\n keyboardShouldPersistTaps=\"handled\"\n showsVerticalScrollIndicator={false}\n >\n {children}\n {scrolledFooter}\n </ScrollView>\n ) : (\n <View style={[styles.content, contentStyle]}>\n {children}\n {scrolledFooter}\n </View>\n );\n\n return (\n <Modal visible transparent animationType=\"none\" statusBarTranslucent onRequestClose={onClose}>\n {/* La hoja está anclada abajo, así que el teclado la tapa entera: hay\n que empujarla, no ajustar insets adentro del scroll. Una sola\n compensación, la del contenedor. */}\n <KeyboardAvoidingView\n style={styles.container}\n behavior={Platform.OS === 'ios' ? 'padding' : 'height'}\n >\n <Transition value={progress} preset=\"fade\" style={[absoluteFill, styles.backdrop]}>\n {/* Con header, el botón de cerrar ya expone la acción: repetir el\n label acá deja dos elementos con el mismo nombre. */}\n <Pressable\n style={absoluteFill}\n onPress={onClose}\n testID=\"bottom-sheet-backdrop\"\n accessibilityRole={hasHeader ? undefined : 'button'}\n accessibilityLabel={hasHeader ? undefined : closeAccessibilityLabel}\n />\n </Transition>\n\n <Transition\n value={progress}\n preset=\"slide-up\"\n distance={SCREEN_HEIGHT}\n testID={testID}\n style={[\n styles.sheet,\n { maxHeight: resolvedMaxHeight, paddingBottom: Math.max(insets.bottom, 20) },\n containerStyle,\n ]}\n >\n {draggable && (\n <View style={styles.handleArea} testID=\"bottom-sheet-handle\" {...panResponder.panHandlers}>\n <View style={styles.handle} />\n </View>\n )}\n\n {hasHeader && (\n <View style={[styles.header, !draggable && styles.headerSpaced]}>\n <Title style={styles.title} numberOfLines={1}>\n {title}\n </Title>\n <IconButton\n iconName=\"close\"\n size={24}\n color={colors.text}\n onPress={onClose}\n accessibilityLabel={closeAccessibilityLabel}\n />\n </View>\n )}\n\n {body}\n\n {pinnedFooter}\n </Transition>\n </KeyboardAvoidingView>\n </Modal>\n );\n }\n);\nBottomSheet.displayName = 'BottomSheet';\n"
|
|
13
13
|
}
|
|
14
14
|
],
|
|
15
15
|
"registryDependencies": [
|
|
16
|
-
"core"
|
|
16
|
+
"core",
|
|
17
|
+
"title",
|
|
18
|
+
"icon-button",
|
|
19
|
+
"pressable-scale",
|
|
20
|
+
"transition"
|
|
17
21
|
]
|
|
18
22
|
}
|
package/registry/core.json
CHANGED
|
@@ -23,17 +23,21 @@
|
|
|
23
23
|
"path": "theme/UIProvider.tsx",
|
|
24
24
|
"content": "import React, { createContext, useContext, useMemo, useState, useCallback } from 'react';\nimport { Appearance, useColorScheme } from 'react-native';\nimport { defaultTheme } from './createTheme';\nimport { defaultLabels, type Labels } from './labels';\nimport { zeroInsets, type EdgeInsets } from './insets';\nimport type { Theme, ThemePair } from './types';\nimport { renderDefaultIcon } from '../icons/defaultIcons';\nimport type { IconRenderer } from '../icons/types';\n\nexport type ThemeMode = 'system' | 'light' | 'dark';\n\ninterface UIContextValue {\n theme: Theme;\n mode: ThemeMode;\n setMode: (mode: ThemeMode) => void;\n icons: IconRenderer;\n insets: EdgeInsets;\n labels: Labels;\n allowFontScaling: boolean;\n}\n\nconst UIContext = createContext<UIContextValue | undefined>(undefined);\n\n/** Una sola vez por sesión: en un re-render el aviso no aporta nada nuevo. */\nlet warnedAboutInsets = false;\n\nexport interface UIProviderProps {\n children: React.ReactNode;\n /** Par light/dark. Por defecto el theme de marca de la librería (créalo con createTheme()). */\n theme?: ThemePair;\n /** Modo inicial (no controlado) si no se pasa `mode`. @default 'system' */\n defaultMode?: ThemeMode;\n /** Modo controlado. Si se pasa junto a onModeChange, el provider no gestiona estado propio. */\n mode?: ThemeMode;\n onModeChange?: (mode: ThemeMode) => void;\n /** Renderer de íconos. Por defecto usa los glifos zero-dep incluidos. */\n icons?: IconRenderer;\n /** Insets de safe area (de useSafeAreaInsets()). Por defecto {0,0,0,0}. */\n insets?: EdgeInsets;\n labels?: Partial<Labels>;\n /** @default false, para paridad con el resto de la librería (accesibilidad primero). */\n allowFontScaling?: boolean;\n}\n\n/**\n * UIProvider\n * Único punto de entrada de la librería: resuelve el theme activo (sistema u\n * override manual), e inyecta íconos, insets y textos por defecto a todo el\n * árbol. No persiste el modo — si el consumidor quiere recordar la preferencia\n * entre sesiones, controla `mode`/`onModeChange` y persiste donde prefiera.\n */\nexport function UIProvider({\n children,\n theme = defaultTheme,\n defaultMode = 'system',\n mode: controlledMode,\n onModeChange,\n icons = renderDefaultIcon,\n insets = zeroInsets,\n labels: labelOverrides,\n allowFontScaling = false,\n}: UIProviderProps) {\n const systemScheme = useColorScheme();\n const [uncontrolledMode, setUncontrolledMode] = useState<ThemeMode>(defaultMode);\n const isControlled = controlledMode !== undefined;\n const mode = isControlled ? controlledMode : uncontrolledMode;\n\n const setMode = useCallback(\n (next: ThemeMode) => {\n if (!isControlled) setUncontrolledMode(next);\n onModeChange?.(next);\n },\n [isControlled, onModeChange]\n );\n\n // Olvidar `insets` no rompe nada visible al montar: el default de ceros\n // recién se nota cuando abrís un Modal `full` (se mete abajo del notch) o un\n // BottomSheet (queda pegado a la barra de gestos). El aviso llega antes.\n if (__DEV__ && insets === zeroInsets && !warnedAboutInsets) {\n warnedAboutInsets = true;\n console.warn(\n 'orn-ui: <UIProvider> sin `insets`, se usan {top:0,bottom:0,left:0,right:0}. ' +\n 'Los modales y el Screen quedan sin safe area. Pasá `insets={useSafeAreaInsets()}` ' +\n \"o usá <SafeAreaUIProvider> de 'orn-ui/safe-area', que lo cablea solo.\"\n );\n }\n\n const scheme: 'light' | 'dark' =\n mode === 'system' ? (systemScheme === 'dark' ? 'dark' : 'light') : mode;\n\n const resolvedTheme = theme[scheme];\n\n const labels = useMemo(\n () => (labelOverrides ? { ...defaultLabels, ...labelOverrides } : defaultLabels),\n [labelOverrides]\n );\n\n const value = useMemo<UIContextValue>(\n () => ({ theme: resolvedTheme, mode, setMode, icons, insets, labels, allowFontScaling }),\n [resolvedTheme, mode, setMode, icons, insets, labels, allowFontScaling]\n );\n\n return <UIContext.Provider value={value}>{children}</UIContext.Provider>;\n}\n\nfunction useUIContext(): UIContextValue {\n const ctx = useContext(UIContext);\n if (!ctx) {\n throw new Error('orn-ui: this hook must be used within a <UIProvider>.');\n }\n return ctx;\n}\n\nexport function useTheme(): Theme {\n return useUIContext().theme;\n}\n\nexport function useColors(): Theme['colors'] {\n return useUIContext().theme.colors;\n}\n\nexport function useThemeMode() {\n const { mode, setMode } = useUIContext();\n return { mode, setMode };\n}\n\nexport function useIcons(): IconRenderer {\n return useUIContext().icons;\n}\n\nexport function useInsets(): EdgeInsets {\n return useUIContext().insets;\n}\n\nexport function useLabels(): Labels {\n return useUIContext().labels;\n}\n\nexport function useAllowFontScaling(): boolean {\n return useUIContext().allowFontScaling;\n}\n\n/** Re-exportado para que tests/consumidores puedan simular cambios de sistema sin RN real. */\nexport const __testUtils = { Appearance };\n"
|
|
25
25
|
},
|
|
26
|
+
{
|
|
27
|
+
"path": "theme/colors.ts",
|
|
28
|
+
"content": "/**\n * Utilidades de color sin dependencias, usadas para derivar una familia de\n * roles (relleno / encima / tenue / legible) a partir de un solo hex de marca.\n */\n\n/** Contraste mínimo AA para texto normal. */\nexport const AA_CONTRAST = 4.5;\n\nexport interface Rgb {\n r: number;\n g: number;\n b: number;\n}\n\nexport interface Hsl {\n h: number;\n s: number;\n l: number;\n}\n\n/** Roles que necesita un acento del theme, en un esquema concreto. */\nexport interface AccentRoles {\n /** Relleno pleno: fondo de botón, círculo del paso actual. */\n base: string;\n /** Texto/ícono que va encima del relleno. */\n on: string;\n /** Fondo tenue: badges, pill del indicador activo. */\n soft: string;\n /** El acento legible sobre `surface`: labels, links. */\n text: string;\n}\n\nconst HEX = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i;\n\nexport function hexToRgb(hex: string): Rgb {\n const match = HEX.exec(hex.trim());\n if (!match) {\n throw new Error(`orn-ui: se esperaba un color hex como \"#7c3aed\", se recibió \"${hex}\".`);\n }\n const digits = match[1]!;\n const full = digits.length === 3 ? digits.replace(/./g, (c) => c + c) : digits;\n return {\n r: parseInt(full.slice(0, 2), 16),\n g: parseInt(full.slice(2, 4), 16),\n b: parseInt(full.slice(4, 6), 16),\n };\n}\n\nconst toHexPair = (value: number) => Math.round(clamp(value, 0, 255)).toString(16).padStart(2, '0');\n\nexport function rgbToHex({ r, g, b }: Rgb): string {\n return `#${toHexPair(r)}${toHexPair(g)}${toHexPair(b)}`;\n}\n\nfunction clamp(value: number, min: number, max: number) {\n return Math.min(max, Math.max(min, value));\n}\n\nexport function rgbToHsl({ r, g, b }: Rgb): Hsl {\n const rn = r / 255;\n const gn = g / 255;\n const bn = b / 255;\n const max = Math.max(rn, gn, bn);\n const min = Math.min(rn, gn, bn);\n const delta = max - min;\n const l = (max + min) / 2;\n\n if (delta === 0) return { h: 0, s: 0, l };\n\n const s = delta / (1 - Math.abs(2 * l - 1));\n let h: number;\n if (max === rn) h = ((gn - bn) / delta) % 6;\n else if (max === gn) h = (bn - rn) / delta + 2;\n else h = (rn - gn) / delta + 4;\n\n h *= 60;\n return { h: h < 0 ? h + 360 : h, s, l };\n}\n\nexport function hslToRgb({ h, s, l }: Hsl): Rgb {\n const c = (1 - Math.abs(2 * l - 1)) * s;\n const hp = (((h % 360) + 360) % 360) / 60;\n const x = c * (1 - Math.abs((hp % 2) - 1));\n const [r1, g1, b1] =\n hp < 1 ? [c, x, 0] : hp < 2 ? [x, c, 0] : hp < 3 ? [0, c, x] : hp < 4 ? [0, x, c] : hp < 5 ? [x, 0, c] : [c, 0, x];\n const m = l - c / 2;\n return { r: (r1 + m) * 255, g: (g1 + m) * 255, b: (b1 + m) * 255 };\n}\n\nexport function toHsl(hex: string): Hsl {\n return rgbToHsl(hexToRgb(hex));\n}\n\nexport function toHex(hsl: Hsl): string {\n return rgbToHex(hslToRgb({ ...hsl, s: clamp(hsl.s, 0, 1), l: clamp(hsl.l, 0, 1) }));\n}\n\nfunction channelLuminance(value: number) {\n const c = value / 255;\n return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;\n}\n\n/** Luminancia relativa WCAG de un hex. */\nexport function luminance(hex: string): number {\n const { r, g, b } = hexToRgb(hex);\n return 0.2126 * channelLuminance(r) + 0.7152 * channelLuminance(g) + 0.0722 * channelLuminance(b);\n}\n\n/** Ratio de contraste WCAG entre dos hex: 1 (igual) a 21 (blanco sobre negro). */\nexport function contrast(a: string, b: string): number {\n const la = luminance(a);\n const lb = luminance(b);\n return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);\n}\n\n/**\n * Blanco si alcanza el contraste AA —es lo que se espera encima de un relleno\n * de color— y si no, el extremo que más contraste dé.\n */\nfunction bestOnColor(background: string, light: string, dark: string): string {\n if (contrast(background, light) >= AA_CONTRAST) return light;\n return contrast(background, dark) >= contrast(background, light) ? dark : light;\n}\n\n/**\n * Mueve la luminosidad del tono hasta alcanzar el contraste pedido contra\n * `against`. Si ni el extremo llega —un amarillo puro sobre blanco, por\n * ejemplo— devuelve el más contrastado que encontró en el camino.\n */\nfunction reachContrast(hsl: Hsl, against: string, target: number, direction: 'darker' | 'lighter'): string {\n const step = direction === 'darker' ? -0.02 : 0.02;\n let best = toHex(hsl);\n let bestRatio = contrast(best, against);\n\n for (let l = hsl.l; l >= 0 && l <= 1; l += step) {\n const candidate = toHex({ ...hsl, l });\n const ratio = contrast(candidate, against);\n if (ratio > bestRatio) {\n best = candidate;\n bestRatio = ratio;\n }\n if (ratio >= target) return candidate;\n }\n\n return best;\n}\n\n/**\n * Deriva los cuatro roles de un acento a partir de un hex, para un esquema.\n * `surface` es el fondo sobre el que tiene que leerse el rol `text`, así que\n * cada esquema pasa el suyo.\n */\nexport function deriveAccent(hex: string, scheme: 'light' | 'dark', surface: string): AccentRoles {\n const hsl = toHsl(hex);\n\n if (scheme === 'light') {\n const base = toHex(hsl);\n return {\n base,\n on: bestOnColor(base, '#ffffff', '#1a1a1a'),\n soft: toHex({ h: hsl.h, s: Math.min(hsl.s, 0.85), l: 0.94 }),\n text: reachContrast({ ...hsl, l: Math.min(hsl.l, 0.38) }, surface, AA_CONTRAST, 'darker'),\n };\n }\n\n // En oscuro el acento sube de luminosidad y lo que va encima baja: un\n // #004cef sobre #1e1e1e no se distingue del fondo.\n const base = toHex({ ...hsl, l: 0.72 });\n return {\n base,\n on: bestOnColor(base, '#ffffff', toHex({ ...hsl, s: Math.min(hsl.s, 0.9), l: 0.16 })),\n soft: toHex({ h: hsl.h, s: Math.min(hsl.s, 0.35), l: 0.18 }),\n text: reachContrast({ ...hsl, l: Math.max(hsl.l, 0.74) }, surface, AA_CONTRAST, 'lighter'),\n };\n}\n"
|
|
29
|
+
},
|
|
26
30
|
{
|
|
27
31
|
"path": "theme/createStyles.ts",
|
|
28
32
|
"content": "import { StyleSheet } from 'react-native';\nimport { useTheme } from './UIProvider';\nimport type { Theme } from './types';\n\n/**\n * createStyles(factory)\n * Devuelve un hook useStyles() que memoiza el StyleSheet resultante por\n * theme (WeakMap<Theme, Styles>), no por instancia de componente. Todas las\n * filas de una lista comparten el mismo objeto de estilos: sin esto, N filas\n * ⇒ N StyleSheet.create() por render, que es lo que hacía cada componente\n * de la app original con `useMemo(() => createStyles(colors), [colors])`.\n *\n * La cache vive en el closure del factory (una WeakMap por llamada a\n * createStyles, es decir, una por componente), así que temas de distintos\n * <UIProvider> en el mismo árbol no colisionan entre sí.\n *\n * El tipo de `factory` sigue el patrón oficial de RN para StyleSheet\n * tipados (`T extends StyleSheet.NamedStyles<T> | StyleSheet.NamedStyles<any>`):\n * es lo que le da contexto a TS para inferir literales ('row', 'center', ...)\n * en vez de ensancharlos a `string` dentro del objeto devuelto.\n */\nexport function createStyles<T extends StyleSheet.NamedStyles<T> | StyleSheet.NamedStyles<any>>(\n factory: (theme: Theme) => T | StyleSheet.NamedStyles<T>\n): () => T {\n const cache = new WeakMap<Theme, T>();\n\n return function useStyles(): T {\n const theme = useTheme();\n let styles = cache.get(theme);\n if (!styles) {\n // El cast es seguro: StyleSheet.create<T>(...) con el mismo T del\n // factory siempre devuelve T; TS no puede probarlo por sí solo dentro\n // de un cuerpo genérico (T podría \"instanciarse\" con un subtipo no\n // relacionado desde su punto de vista, aunque en runtime es el mismo).\n styles = StyleSheet.create(factory(theme)) as T;\n cache.set(theme, styles);\n }\n return styles;\n };\n}\n"
|
|
29
33
|
},
|
|
30
34
|
{
|
|
31
35
|
"path": "theme/createTheme.ts",
|
|
32
|
-
"content": "import { lightPalette, darkPalette } from './palettes';\nimport { tokens as defaultTokens } from './tokens';\nimport type { Theme, ThemeOverrides, ThemePair } from './types';\n\nfunction mergeDeep<T extends Record<string, any>>(base: T, patch?: Partial<T>): T {\n if (!patch) return base;\n const out: Record<string, any> = { ...base };\n for (const key of Object.keys(patch)) {\n const patchVal = (patch as Record<string, any>)[key];\n const baseVal = (base as Record<string, any>)[key];\n if (\n patchVal &&\n baseVal &&\n typeof patchVal === 'object' &&\n typeof baseVal === 'object' &&\n !Array.isArray(patchVal)\n ) {\n out[key] = mergeDeep(baseVal, patchVal);\n } else if (patchVal !== undefined) {\n out[key] = patchVal;\n }\n }\n return out as T;\n}\n\n/**\n * Construye el par de themes light/dark que consume <UIProvider theme={...}>.\n * Nunca muta los defaults: cada llamada produce objetos nuevos, seguros de\n * compartir entre providers distintos (tests, multi-brand en el mismo árbol).\n */\nexport function createTheme(overrides?: ThemeOverrides): ThemePair {\n const tokens = mergeDeep(defaultTokens, overrides?.tokens as any);\n\n const light: Theme = {\n colors: mergeDeep(lightPalette, overrides?.colors?.light),\n tokens,\n scheme: 'light',\n };\n\n const dark: Theme = {\n colors: mergeDeep(darkPalette, overrides?.colors?.dark),\n tokens,\n scheme: 'dark',\n };\n\n return { light, dark };\n}\n\nexport const defaultTheme: ThemePair = createTheme();\n"
|
|
36
|
+
"content": "import { lightPalette, darkPalette, type ThemeColors } from './palettes';\nimport { tokens as defaultTokens } from './tokens';\nimport { deriveAccent } from './colors';\nimport type { BrandAccent, Theme, ThemeOverrides, ThemePair } from './types';\n\nfunction mergeDeep<T extends Record<string, any>>(base: T, patch?: Partial<T>): T {\n if (!patch) return base;\n const out: Record<string, any> = { ...base };\n for (const key of Object.keys(patch)) {\n const patchVal = (patch as Record<string, any>)[key];\n const baseVal = (base as Record<string, any>)[key];\n if (\n patchVal &&\n baseVal &&\n typeof patchVal === 'object' &&\n typeof baseVal === 'object' &&\n !Array.isArray(patchVal)\n ) {\n out[key] = mergeDeep(baseVal, patchVal);\n } else if (patchVal !== undefined) {\n out[key] = patchVal;\n }\n }\n return out as T;\n}\n\nconst capitalize = <T extends string>(name: T) => (name.charAt(0).toUpperCase() + name.slice(1)) as Capitalize<T>;\n\n/** Expande cada hex de `brand` a los cuatro roles del acento en un esquema. */\nfunction brandColors(\n brand: ThemeOverrides['brand'],\n scheme: 'light' | 'dark',\n palette: ThemeColors\n): Partial<ThemeColors> {\n if (!brand) return {};\n const accents = typeof brand === 'string' ? { primary: brand } : brand;\n const out: Partial<ThemeColors> = {};\n\n for (const [name, hex] of Object.entries(accents) as [BrandAccent, string | undefined][]) {\n if (!hex) continue;\n const roles = deriveAccent(hex, scheme, palette.surface);\n out[name] = roles.base;\n out[`on${capitalize(name)}`] = roles.on;\n out[`${name}Soft`] = roles.soft;\n out[`${name}Text`] = roles.text;\n }\n\n return out;\n}\n\n/**\n * Construye el par de themes light/dark que consume <UIProvider theme={...}>.\n * Nunca muta los defaults: cada llamada produce objetos nuevos, seguros de\n * compartir entre providers distintos (tests, multi-brand en el mismo árbol).\n *\n * `brand` se aplica antes que `colors`, así que un rol escrito a mano siempre\n * gana sobre el derivado.\n */\nexport function createTheme(overrides?: ThemeOverrides): ThemePair {\n const tokens = mergeDeep(defaultTokens, overrides?.tokens as any);\n\n const light: Theme = {\n colors: mergeDeep(\n mergeDeep(lightPalette, brandColors(overrides?.brand, 'light', lightPalette)),\n overrides?.colors?.light\n ),\n tokens,\n scheme: 'light',\n };\n\n const dark: Theme = {\n colors: mergeDeep(\n mergeDeep(darkPalette, brandColors(overrides?.brand, 'dark', darkPalette)),\n overrides?.colors?.dark\n ),\n tokens,\n scheme: 'dark',\n };\n\n return { light, dark };\n}\n\nexport const defaultTheme: ThemePair = createTheme();\n"
|
|
33
37
|
},
|
|
34
38
|
{
|
|
35
39
|
"path": "theme/index.ts",
|
|
36
|
-
"content": "export { spacing, radius, fontSize, fontWeight, lineHeight, duration, shadow, tokens } from './tokens';\nexport type { Tokens } from './tokens';\nexport { lightPalette, darkPalette } from './palettes';\nexport type { ThemeColors } from './palettes';\nexport { createTheme, defaultTheme } from './createTheme';\nexport type { Theme, ThemePair, ThemeOverrides, DeepPartial } from './types';\nexport { defaultLabels } from './labels';\nexport type { Labels } from './labels';\nexport { zeroInsets } from './insets';\nexport type { EdgeInsets } from './insets';\nexport { createStyles } from './createStyles';\nexport {\n UIProvider,\n useTheme,\n useColors,\n useThemeMode,\n useIcons,\n useInsets,\n useLabels,\n useAllowFontScaling,\n} from './UIProvider';\nexport type { UIProviderProps, ThemeMode } from './UIProvider';\n"
|
|
40
|
+
"content": "export { spacing, radius, fontSize, fontWeight, lineHeight, duration, shadow, tokens } from './tokens';\nexport type { Tokens } from './tokens';\nexport { lightPalette, darkPalette } from './palettes';\nexport type { ThemeColors } from './palettes';\nexport { createTheme, defaultTheme } from './createTheme';\nexport type { Theme, ThemePair, ThemeOverrides, BrandAccent, DeepPartial } from './types';\nexport { deriveAccent, contrast, luminance, AA_CONTRAST } from './colors';\nexport type { AccentRoles } from './colors';\nexport { defaultLabels } from './labels';\nexport type { Labels } from './labels';\nexport { zeroInsets } from './insets';\nexport type { EdgeInsets } from './insets';\nexport { createStyles } from './createStyles';\nexport {\n UIProvider,\n useTheme,\n useColors,\n useThemeMode,\n useIcons,\n useInsets,\n useLabels,\n useAllowFontScaling,\n} from './UIProvider';\nexport type { UIProviderProps, ThemeMode } from './UIProvider';\n"
|
|
37
41
|
},
|
|
38
42
|
{
|
|
39
43
|
"path": "theme/insets.ts",
|
|
@@ -45,7 +49,7 @@
|
|
|
45
49
|
},
|
|
46
50
|
{
|
|
47
51
|
"path": "theme/palettes.ts",
|
|
48
|
-
"content": "/**\n * Cada color es `string`, no un literal (`as const`): light y dark tienen\n * valores distintos para la misma clave, y createTheme() debe poder\n * aceptar cualquier hex/rgba en los overrides — literal typing lo impediría.\n *\n * Cada acento viene en cuatro roles, y no son intercambiables:\n *\n * `primary` relleno sólido (fondo de botón, día seleccionado, FAB).\n * `onPrimary` texto/ícono *encima* de ese relleno.\n * `primarySoft` fondo teñido para estados seleccionados y pills.\n * `primaryText` texto/ícono del acento sobre `surface`, `background`\n * o sobre su propio `primarySoft`.\n *\n * El motivo es el contraste: un acento que se lee bien como relleno no se\n * lee como texto. Antes los componentes usaban `colors.primary` para las dos\n * cosas y lo teñían concatenando alpha al hex (`colors.primary + '15'`), lo\n * que en dark daba 2.6:1 sobre la superficie — muy por debajo del 4.5:1 de\n * WCAG AA. Todos los pares de esta paleta pasan AA en ambos esquemas.\n */\nexport interface ThemeColors {\n primary: string;\n onPrimary: string;\n primarySoft: string;\n primaryText: string;\n\n secondary: string;\n onSecondary: string;\n secondarySoft: string;\n secondaryText: string;\n\n success: string;\n onSuccess: string;\n successSoft: string;\n successText: string;\n\n error: string;\n onError: string;\n errorSoft: string;\n errorText: string;\n\n warning: string;\n onWarning: string;\n warningSoft: string;\n warningText: string;\n\n white: string;\n black: string;\n background: string;\n surface: string;\n text: string;\n textLight: string;\n border: string;\n inputBackground: string;\n overlay: string;\n /** Velo semitransparente sobre contenido propio (footer de carga de List). */\n scrim: string;\n /** Fondo de placeholder mientras carga una imagen. */\n skeleton: string;\n}\n\nconst neutral = {\n white: '#ffffff',\n black: '#000000',\n};\n\nexport const lightPalette: ThemeColors = {\n ...neutral,\n\n primary: '#004cef',\n onPrimary: '#ffffff',\n primarySoft: '#e6edfe',\n primaryText: '#00369f',\n\n secondary: '#00cae1',\n onSecondary: '#00363d',\n secondarySoft: '#dcf6f9',\n secondaryText: '#00707d',\n\n success: '#34c759',\n onSuccess: '#00320f',\n successSoft: '#e3f6e9',\n successText: '#116a30',\n\n error: '#
|
|
52
|
+
"content": "/**\n * Cada color es `string`, no un literal (`as const`): light y dark tienen\n * valores distintos para la misma clave, y createTheme() debe poder\n * aceptar cualquier hex/rgba en los overrides — literal typing lo impediría.\n *\n * Cada acento viene en cuatro roles, y no son intercambiables:\n *\n * `primary` relleno sólido (fondo de botón, día seleccionado, FAB).\n * `onPrimary` texto/ícono *encima* de ese relleno.\n * `primarySoft` fondo teñido para estados seleccionados y pills.\n * `primaryText` texto/ícono del acento sobre `surface`, `background`\n * o sobre su propio `primarySoft`.\n *\n * El motivo es el contraste: un acento que se lee bien como relleno no se\n * lee como texto. Antes los componentes usaban `colors.primary` para las dos\n * cosas y lo teñían concatenando alpha al hex (`colors.primary + '15'`), lo\n * que en dark daba 2.6:1 sobre la superficie — muy por debajo del 4.5:1 de\n * WCAG AA. Todos los pares de esta paleta pasan AA en ambos esquemas.\n */\nexport interface ThemeColors {\n primary: string;\n onPrimary: string;\n primarySoft: string;\n primaryText: string;\n\n secondary: string;\n onSecondary: string;\n secondarySoft: string;\n secondaryText: string;\n\n success: string;\n onSuccess: string;\n successSoft: string;\n successText: string;\n\n error: string;\n onError: string;\n errorSoft: string;\n errorText: string;\n\n warning: string;\n onWarning: string;\n warningSoft: string;\n warningText: string;\n\n white: string;\n black: string;\n background: string;\n surface: string;\n text: string;\n textLight: string;\n border: string;\n inputBackground: string;\n overlay: string;\n /** Velo semitransparente sobre contenido propio (footer de carga de List). */\n scrim: string;\n /** Fondo de placeholder mientras carga una imagen. */\n skeleton: string;\n}\n\nconst neutral = {\n white: '#ffffff',\n black: '#000000',\n};\n\nexport const lightPalette: ThemeColors = {\n ...neutral,\n\n primary: '#004cef',\n onPrimary: '#ffffff',\n primarySoft: '#e6edfe',\n primaryText: '#00369f',\n\n secondary: '#00cae1',\n onSecondary: '#00363d',\n secondarySoft: '#dcf6f9',\n secondaryText: '#00707d',\n\n success: '#34c759',\n onSuccess: '#00320f',\n successSoft: '#e3f6e9',\n successText: '#116a30',\n\n // El rojo va con texto blanco, que es lo que la gente espera de un botón\n // destructivo. #ff3b30 no llega a 4.5:1 con blanco y obligaba a texto casi\n // negro sobre rojo; este tono sí, y sigue leyéndose como rojo de alerta.\n error: '#dc2626',\n onError: '#ffffff',\n errorSoft: '#fdeaea',\n errorText: '#c62222',\n\n warning: '#f59e0b',\n onWarning: '#3d2600',\n warningSoft: '#fdf1de',\n warningText: '#8a4b00',\n\n background: '#f8f9fa',\n surface: '#ffffff',\n text: '#333333',\n textLight: '#666666',\n border: '#e5e7eb',\n inputBackground: '#f5f7fa',\n overlay: 'rgba(0,0,0,0.5)',\n scrim: 'rgba(248,249,250,0.8)',\n skeleton: '#eceef1',\n};\n\n/**\n * Los acentos de dark NO son los de light: sobre `#1e1e1e` hay que subir la\n * luminosidad del acento y bajar la del texto que va encima (por eso\n * `onPrimary` es azul oscuro acá y blanco en light).\n */\nexport const darkPalette: ThemeColors = {\n ...neutral,\n\n primary: '#7ba4ff',\n onPrimary: '#00205c',\n primarySoft: '#1b2740',\n primaryText: '#a9c3ff',\n\n secondary: '#4dd8e8',\n onSecondary: '#00363d',\n secondarySoft: '#12333a',\n secondaryText: '#7fe6f2',\n\n success: '#5ddb84',\n onSuccess: '#003514',\n successSoft: '#13301e',\n successText: '#7fe6a1',\n\n error: '#ff7b72',\n onError: '#4d0500',\n errorSoft: '#3a1c1a',\n errorText: '#ffa9a3',\n\n warning: '#f0b458',\n onWarning: '#3d2600',\n warningSoft: '#33260f',\n warningText: '#f5c97e',\n\n background: '#121212',\n surface: '#1e1e1e',\n text: '#ecedee',\n textLight: '#9ba1a6',\n border: '#2a2a2e',\n inputBackground: '#232326',\n overlay: 'rgba(0,0,0,0.6)',\n scrim: 'rgba(18,18,18,0.8)',\n skeleton: '#26262a',\n};\n"
|
|
49
53
|
},
|
|
50
54
|
{
|
|
51
55
|
"path": "theme/tokens.ts",
|
|
@@ -53,7 +57,7 @@
|
|
|
53
57
|
},
|
|
54
58
|
{
|
|
55
59
|
"path": "theme/types.ts",
|
|
56
|
-
"content": "import type { Tokens } from './tokens';\nimport type { ThemeColors } from './palettes';\n\n/** Theme resuelto: paleta de un scheme concreto + tokens compartidos. */\nexport interface Theme {\n colors: ThemeColors;\n tokens: Tokens;\n scheme: 'light' | 'dark';\n}\n\n/** Par de themes light/dark que produce createTheme() y consume UIProvider. */\nexport interface ThemePair {\n light: Theme;\n dark: Theme;\n}\n\nexport type DeepPartial<T> = {\n [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];\n};\n\nexport interface ThemeOverrides {\n colors?: {\n light?: Partial<ThemeColors>;\n dark?: Partial<ThemeColors>;\n };\n tokens?: DeepPartial<Tokens>;\n}\n"
|
|
60
|
+
"content": "import type { Tokens } from './tokens';\nimport type { ThemeColors } from './palettes';\n\n/** Theme resuelto: paleta de un scheme concreto + tokens compartidos. */\nexport interface Theme {\n colors: ThemeColors;\n tokens: Tokens;\n scheme: 'light' | 'dark';\n}\n\n/** Par de themes light/dark que produce createTheme() y consume UIProvider. */\nexport interface ThemePair {\n light: Theme;\n dark: Theme;\n}\n\nexport type DeepPartial<T> = {\n [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];\n};\n\n/** Acentos que se pueden definir con un solo hex vía `brand`. */\nexport type BrandAccent = 'primary' | 'secondary' | 'success' | 'error' | 'warning';\n\nexport interface ThemeOverrides {\n /**\n * Un hex por acento; de cada uno se derivan sus cuatro roles (relleno, lo\n * que va encima, el fondo tenue y la variante legible sobre `surface`) para\n * light y para dark, con el contraste ya resuelto. Un string suelto define\n * `primary`. Lo que se pase en `colors` gana sobre lo derivado.\n */\n brand?: string | Partial<Record<BrandAccent, string>>;\n colors?: {\n light?: Partial<ThemeColors>;\n dark?: Partial<ThemeColors>;\n };\n tokens?: DeepPartial<Tokens>;\n}\n"
|
|
57
61
|
}
|
|
58
62
|
],
|
|
59
63
|
"registryDependencies": [],
|