@satellatickets/core 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client-DpHlKasm.d.ts +300 -0
- package/dist/client.d.ts +3 -0
- package/dist/client.js +158 -0
- package/dist/index.d.ts +4 -298
- package/dist/index.js +5 -190
- package/dist/resolve-theme-DbIlzBBD.js +36 -0
- package/package.json +3 -3
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
|
|
2
|
+
import { BrandName, Theme, ThemeName } from "@satellatickets/tokens";
|
|
3
|
+
import { ReactNode } from "react";
|
|
4
|
+
//#region src/form-field/context.d.ts
|
|
5
|
+
/** Lo que `FormField` publica para el control que envuelve (ADR-037). */
|
|
6
|
+
interface FormFieldValue {
|
|
7
|
+
/** `id` del control. `FormField` lo usa en el `htmlFor` de su etiqueta (web). */
|
|
8
|
+
controlId: string;
|
|
9
|
+
/** `id` del texto de ayuda, si lo hay (web). */
|
|
10
|
+
helpId: string | undefined;
|
|
11
|
+
/** `id` del mensaje de error, si lo hay (web). */
|
|
12
|
+
errorId: string | undefined;
|
|
13
|
+
label: string;
|
|
14
|
+
help: string | undefined;
|
|
15
|
+
error: string | undefined;
|
|
16
|
+
disabled: boolean;
|
|
17
|
+
required: boolean;
|
|
18
|
+
}
|
|
19
|
+
interface FormFieldValueOptions {
|
|
20
|
+
label: string;
|
|
21
|
+
help?: string | undefined;
|
|
22
|
+
error?: string | undefined;
|
|
23
|
+
disabled?: boolean | undefined;
|
|
24
|
+
required?: boolean | undefined;
|
|
25
|
+
}
|
|
26
|
+
/** Construye el valor del contexto a partir de un id base (el de `useId()`). */
|
|
27
|
+
declare function createFormFieldValue(baseId: string, { label, help, error, disabled, required }: FormFieldValueOptions): FormFieldValue;
|
|
28
|
+
/** `null` fuera de un `FormField`. */
|
|
29
|
+
declare const FormFieldContext: import("react").Context<FormFieldValue | null>;
|
|
30
|
+
/** Las props propias de un control que se combinan con las de su `FormField`. */
|
|
31
|
+
interface FormFieldControlOptions {
|
|
32
|
+
id?: string | undefined;
|
|
33
|
+
invalid?: boolean | undefined;
|
|
34
|
+
disabled?: boolean | undefined;
|
|
35
|
+
required?: boolean | undefined;
|
|
36
|
+
accessibilityLabel?: string | undefined;
|
|
37
|
+
}
|
|
38
|
+
/** Lo que un control necesita para enlazarse con su `FormField`, en web y en nativo. */
|
|
39
|
+
interface FormFieldControl {
|
|
40
|
+
/** `id` del control (web): el propio o el que espera la etiqueta del campo. */
|
|
41
|
+
id: string | undefined;
|
|
42
|
+
/** Ids de la ayuda y el error para `aria-describedby` (web). */
|
|
43
|
+
describedBy: string | undefined;
|
|
44
|
+
/** Nombre accesible (nativo): el propio o la etiqueta del campo. */
|
|
45
|
+
accessibilityLabel: string | undefined;
|
|
46
|
+
/** El error y la ayuda, uno tras otro, para `accessibilityHint` (nativo). */
|
|
47
|
+
accessibilityHint: string | undefined;
|
|
48
|
+
invalid: boolean;
|
|
49
|
+
disabled: boolean;
|
|
50
|
+
required: boolean;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Combina las props de un control con las de su `FormField`. Las del control
|
|
54
|
+
* mandan en `id` y nombre accesible; `invalid`, `disabled` y `required` se suman:
|
|
55
|
+
* basta con que lo diga uno de los dos.
|
|
56
|
+
*/
|
|
57
|
+
declare function resolveFormFieldControl(field: FormFieldValue | null, { id, invalid, disabled, required, accessibilityLabel }?: FormFieldControlOptions): FormFieldControl;
|
|
58
|
+
/** Enlaza un control con el `FormField` que lo contiene, si lo hay. */
|
|
59
|
+
declare function useFormFieldControl(options?: FormFieldControlOptions): FormFieldControl;
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/hooks/use-button.d.ts
|
|
62
|
+
interface UseButtonOptions {
|
|
63
|
+
disabled?: boolean | undefined;
|
|
64
|
+
loading?: boolean | undefined;
|
|
65
|
+
onPress?: (() => void) | undefined;
|
|
66
|
+
}
|
|
67
|
+
interface UseButtonResult {
|
|
68
|
+
disabled: boolean;
|
|
69
|
+
loading: boolean;
|
|
70
|
+
/** `false` si está deshabilitado o cargando: el botón no responde a la pulsación. */
|
|
71
|
+
interactive: boolean;
|
|
72
|
+
/** Llama a `onPress` solo si el botón es interactivo. */
|
|
73
|
+
press: () => void;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Comportamiento de `Button` común a web y nativo: un botón deshabilitado o que
|
|
77
|
+
* está cargando no dispara `onPress`. Cada vista añade su accesibilidad
|
|
78
|
+
* (`disabled`/`aria-busy` en web, `accessibilityState` en nativo).
|
|
79
|
+
*/
|
|
80
|
+
declare function useButton({ disabled, loading, onPress }: UseButtonOptions): UseButtonResult;
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region src/hooks/use-controllable-state.d.ts
|
|
83
|
+
interface UseControllableStateOptions<T> {
|
|
84
|
+
/** Valor controlado por la app. Si existe, manda sobre el estado interno. */
|
|
85
|
+
value?: T | undefined;
|
|
86
|
+
/** Valor inicial cuando el componente guarda su propio estado. */
|
|
87
|
+
defaultValue: T;
|
|
88
|
+
/** Se llama con el valor nuevo cuando cambia, en los dos modos. */
|
|
89
|
+
onChange?: ((value: T) => void) | undefined;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Estado controlado o no, a elección de la app (ADR-037). Con `value`, el componente
|
|
93
|
+
* solo avisa del cambio y espera a que la app le pase el valor nuevo; sin él, guarda
|
|
94
|
+
* el estado a partir de `defaultValue`. No avisa si el valor no cambia.
|
|
95
|
+
*/
|
|
96
|
+
declare function useControllableState<T>({ value, defaultValue, onChange }: UseControllableStateOptions<T>): [T, (next: T) => void];
|
|
97
|
+
//#endregion
|
|
98
|
+
//#region src/types/text.d.ts
|
|
99
|
+
declare const textVariants: readonly ["hero", "display", "title", "heading", "subheading", "body", "bodySmall", "label", "caption", "code"];
|
|
100
|
+
type TextVariant = (typeof textVariants)[number];
|
|
101
|
+
type TextColorToken = keyof Theme['color']['text'];
|
|
102
|
+
declare const textColors: TextColorToken[];
|
|
103
|
+
declare const textAligns: readonly ["left", "center", "right"];
|
|
104
|
+
type TextAlign = (typeof textAligns)[number];
|
|
105
|
+
/** Tokens que componen una variante tipográfica; ambas vistas los leen del tema. */
|
|
106
|
+
interface TextVariantStyle {
|
|
107
|
+
family: keyof Theme['font']['family'];
|
|
108
|
+
/** Tamaño; `font.lineHeight` comparte las mismas claves (lo comprueba el typecheck de las vistas). */
|
|
109
|
+
size: keyof Theme['font']['size'];
|
|
110
|
+
weight: keyof Theme['font']['weight'];
|
|
111
|
+
uppercase?: boolean;
|
|
112
|
+
/** Espaciado entre letras en px. */
|
|
113
|
+
letterSpacing?: number;
|
|
114
|
+
/** Nivel de encabezado semántico (h1–h4) si la variante es un título. */
|
|
115
|
+
headingLevel?: 1 | 2 | 3 | 4;
|
|
116
|
+
}
|
|
117
|
+
/** Escala tipográfica de Satella: Unbounded para titulares, Hanken Grotesk para texto, IBM Plex Mono para etiquetas. */
|
|
118
|
+
declare const textVariantStyles: Readonly<Record<TextVariant, TextVariantStyle>>;
|
|
119
|
+
interface TextProps {
|
|
120
|
+
variant?: TextVariant | undefined;
|
|
121
|
+
color?: TextColorToken | undefined;
|
|
122
|
+
align?: TextAlign | undefined;
|
|
123
|
+
/** Una sola línea con puntos suspensivos. */
|
|
124
|
+
truncate?: boolean | undefined;
|
|
125
|
+
testID?: string | undefined;
|
|
126
|
+
children?: ReactNode;
|
|
127
|
+
}
|
|
128
|
+
//#endregion
|
|
129
|
+
//#region src/types/link.d.ts
|
|
130
|
+
/**
|
|
131
|
+
* Cuándo se subraya un `Link` (ADR-009). `always` es lo correcto dentro de un párrafo:
|
|
132
|
+
* el color solo no basta para distinguir un enlace. `hover` lo subraya al pasar el
|
|
133
|
+
* puntero o enfocarlo en web, y mientras se pulsa en nativo.
|
|
134
|
+
*/
|
|
135
|
+
declare const linkUnderlines: readonly ["always", "hover"];
|
|
136
|
+
type LinkUnderline = (typeof linkUnderlines)[number];
|
|
137
|
+
/** Lo que recibe `onPress` de un `Link`: permite cancelar la navegación por defecto. */
|
|
138
|
+
interface LinkPressEvent {
|
|
139
|
+
/** Cancela la navegación por defecto, por ejemplo para hacerla con el router de la app. */
|
|
140
|
+
preventDefault: () => void;
|
|
141
|
+
readonly defaultPrevented: boolean;
|
|
142
|
+
}
|
|
143
|
+
interface LinkProps {
|
|
144
|
+
/** Destino. En web es el `href` del `<a>`; en nativo se abre con `Linking`. */
|
|
145
|
+
href: string;
|
|
146
|
+
/**
|
|
147
|
+
* Tipografía, con las mismas variantes que `Text`. Sin ella hereda la del texto
|
|
148
|
+
* que lo contiene; fuera de un `Text`, usa `body`.
|
|
149
|
+
*/
|
|
150
|
+
variant?: TextVariant | undefined;
|
|
151
|
+
/** Color del texto (por defecto `link`). */
|
|
152
|
+
color?: TextColorToken | undefined;
|
|
153
|
+
underline?: LinkUnderline | undefined;
|
|
154
|
+
/**
|
|
155
|
+
* Se llama antes de navegar. Con `event.preventDefault()` la librería no navega y
|
|
156
|
+
* la app decide qué hacer. En web no se llama si el clic lleva un modificador
|
|
157
|
+
* (Ctrl, Cmd, Mayús, Alt) o no es del botón principal: ahí manda el navegador.
|
|
158
|
+
*/
|
|
159
|
+
onPress?: ((event: LinkPressEvent) => void) | undefined;
|
|
160
|
+
/** Nombre accesible cuando el texto visible no basta para describir el destino. */
|
|
161
|
+
accessibilityLabel?: string | undefined;
|
|
162
|
+
/** `data-testid` en web, `testID` en nativo. */
|
|
163
|
+
testID?: string | undefined;
|
|
164
|
+
children: ReactNode;
|
|
165
|
+
}
|
|
166
|
+
//#endregion
|
|
167
|
+
//#region src/hooks/use-link.d.ts
|
|
168
|
+
interface UseLinkOptions {
|
|
169
|
+
onPress?: ((event: LinkPressEvent) => void) | undefined;
|
|
170
|
+
}
|
|
171
|
+
interface UseLinkResult {
|
|
172
|
+
/**
|
|
173
|
+
* Llama a `onPress` y devuelve `true` si la navegación por defecto debe seguir,
|
|
174
|
+
* es decir, si nadie llamó a `preventDefault()`.
|
|
175
|
+
*/
|
|
176
|
+
press: () => boolean;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Comportamiento de `Link` común a web y nativo: `onPress` se llama antes de navegar
|
|
180
|
+
* y puede cancelar la navegación por defecto. Cada vista aporta esa navegación
|
|
181
|
+
* (el `<a>` en web, `Linking` en nativo).
|
|
182
|
+
*/
|
|
183
|
+
declare function useLink({ onPress }: UseLinkOptions): UseLinkResult;
|
|
184
|
+
//#endregion
|
|
185
|
+
//#region src/types/ui-provider.d.ts
|
|
186
|
+
/** Modo de tema que acepta `UIProvider` (ADR-010). */
|
|
187
|
+
declare const themeModes: readonly ["light", "dark", "system"];
|
|
188
|
+
type ThemeMode = (typeof themeModes)[number];
|
|
189
|
+
/** Esquema resuelto: siempre uno de los temas definidos en tokens. */
|
|
190
|
+
type ColorScheme = ThemeName;
|
|
191
|
+
interface UIProviderProps {
|
|
192
|
+
/** Tema claro, oscuro o el del sistema (por defecto `system`). */
|
|
193
|
+
theme?: ThemeMode | undefined;
|
|
194
|
+
/** Marca registrada en tokens. Sin marca se usa la identidad por defecto. */
|
|
195
|
+
brand?: BrandName | undefined;
|
|
196
|
+
children: ReactNode;
|
|
197
|
+
}
|
|
198
|
+
//#endregion
|
|
199
|
+
//#region src/theme/context.d.ts
|
|
200
|
+
interface UIContextValue {
|
|
201
|
+
theme: Theme;
|
|
202
|
+
colorScheme: ColorScheme;
|
|
203
|
+
mode: ThemeMode;
|
|
204
|
+
brand: BrandName | undefined;
|
|
205
|
+
}
|
|
206
|
+
declare function createUIContextValue(mode: ThemeMode, systemScheme: ColorScheme, brand?: BrandName): UIContextValue;
|
|
207
|
+
/** Sin `UIProvider` se usa el tema claro por defecto, para que los componentes funcionen aislados. */
|
|
208
|
+
declare const UIContext: import("react").Context<UIContextValue>;
|
|
209
|
+
declare function useUIContext(): UIContextValue;
|
|
210
|
+
/** Tema resuelto (tokens con la marca aplicada). Imprescindible en las vistas nativas (ADR-008). */
|
|
211
|
+
declare function useTheme(): Theme;
|
|
212
|
+
/** `'light' | 'dark'` ya resuelto, también con `theme="system"`. */
|
|
213
|
+
declare function useColorScheme(): ColorScheme;
|
|
214
|
+
declare function useBrand(): BrandName | undefined;
|
|
215
|
+
//#endregion
|
|
216
|
+
//#region src/types/feedback.d.ts
|
|
217
|
+
/**
|
|
218
|
+
* Los cuatro significados de `color.feedback` en los tokens: éxito, aviso, error e
|
|
219
|
+
* información. Los comparten los componentes que comunican un estado.
|
|
220
|
+
*/
|
|
221
|
+
declare const feedbackTones: readonly ["success", "warning", "danger", "info"];
|
|
222
|
+
type FeedbackTone = (typeof feedbackTones)[number];
|
|
223
|
+
declare function isFeedbackTone(value: string): value is FeedbackTone;
|
|
224
|
+
//#endregion
|
|
225
|
+
//#region src/types/toast.d.ts
|
|
226
|
+
/** Acción opcional de un toast, por ejemplo "Deshacer". */
|
|
227
|
+
interface ToastAction {
|
|
228
|
+
label: string;
|
|
229
|
+
onPress: () => void;
|
|
230
|
+
}
|
|
231
|
+
/** Lo que recibe `show()` de `useToast` (ADR-039). */
|
|
232
|
+
interface ToastOptions {
|
|
233
|
+
/** Qué comunica (por defecto `info`). Fija el icono y la urgencia con que se anuncia. */
|
|
234
|
+
tone?: FeedbackTone | undefined;
|
|
235
|
+
/** El mensaje, en una línea. */
|
|
236
|
+
title: string;
|
|
237
|
+
/** Detalle opcional. */
|
|
238
|
+
description?: string | undefined;
|
|
239
|
+
/**
|
|
240
|
+
* Milisegundos hasta que se cierra solo (por defecto 5000). Con `0` no se cierra
|
|
241
|
+
* solo: necesita `closeLabel`, una `action` o una llamada a `dismiss`.
|
|
242
|
+
*/
|
|
243
|
+
duration?: number | undefined;
|
|
244
|
+
/** Botón de acción. Al pulsarlo se llama a `onPress` y el toast se cierra. */
|
|
245
|
+
action?: ToastAction | undefined;
|
|
246
|
+
/**
|
|
247
|
+
* Si existe, el toast muestra un botón de cierre con este nombre accesible
|
|
248
|
+
* ("Cerrar aviso"). La librería no trae textos propios.
|
|
249
|
+
*/
|
|
250
|
+
closeLabel?: string | undefined;
|
|
251
|
+
}
|
|
252
|
+
/** Un toast ya en la cola: sus opciones resueltas y su identificador. */
|
|
253
|
+
interface ToastItem {
|
|
254
|
+
id: string;
|
|
255
|
+
tone: FeedbackTone;
|
|
256
|
+
title: string;
|
|
257
|
+
description: string | undefined;
|
|
258
|
+
duration: number;
|
|
259
|
+
action: ToastAction | undefined;
|
|
260
|
+
closeLabel: string | undefined;
|
|
261
|
+
}
|
|
262
|
+
/** Lo que devuelve `useToast`. */
|
|
263
|
+
interface ToastApi {
|
|
264
|
+
/** Muestra un toast y devuelve su `id`. */
|
|
265
|
+
show: (options: ToastOptions) => string;
|
|
266
|
+
/** Cierra el toast con ese `id`; sin `id`, los cierra todos. */
|
|
267
|
+
dismiss: (id?: string) => void;
|
|
268
|
+
}
|
|
269
|
+
//#endregion
|
|
270
|
+
//#region src/toast/store.d.ts
|
|
271
|
+
/** Milisegundos que un toast permanece visible si no se indica otra cosa. */
|
|
272
|
+
declare const TOAST_DEFAULT_DURATION = 5000;
|
|
273
|
+
/** Toasts visibles a la vez. Al llegar uno más, se retira el más antiguo. */
|
|
274
|
+
declare const TOAST_MAX_VISIBLE = 3;
|
|
275
|
+
interface ToastStore extends ToastApi {
|
|
276
|
+
/** Para `useSyncExternalStore`: avisa de cada cambio y devuelve cómo dejar de escuchar. */
|
|
277
|
+
subscribe: (listener: () => void) => () => void;
|
|
278
|
+
/** Los toasts visibles, del más antiguo al más reciente. Cambia de identidad con cada cambio. */
|
|
279
|
+
getSnapshot: () => readonly ToastItem[];
|
|
280
|
+
/** Detiene el cierre automático, por ejemplo mientras el puntero está sobre los toasts. */
|
|
281
|
+
pause: () => void;
|
|
282
|
+
/** Reanuda el cierre automático con el tiempo que le quedaba a cada toast. */
|
|
283
|
+
resume: () => void;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* La cola de toasts de un `UIProvider` (ADR-039): alta, baja, cierre automático, pausa
|
|
287
|
+
* y límite de visibles. No depende de React ni de la plataforma.
|
|
288
|
+
*/
|
|
289
|
+
declare function createToastStore(): ToastStore;
|
|
290
|
+
//#endregion
|
|
291
|
+
//#region src/toast/context.d.ts
|
|
292
|
+
/** La cola de toasts del `UIProvider` más cercano; `null` fuera de uno. */
|
|
293
|
+
declare const ToastContext: import("react").Context<ToastStore | null>;
|
|
294
|
+
/**
|
|
295
|
+
* Muestra y cierra toasts (ADR-039). Necesita un `UIProvider` por encima: es él quien
|
|
296
|
+
* guarda la cola y los pinta.
|
|
297
|
+
*/
|
|
298
|
+
declare function useToast(): ToastApi;
|
|
299
|
+
//#endregion
|
|
300
|
+
export { useFormFieldControl as $, LinkUnderline as A, textVariants as B, UIProviderProps as C, useLink as D, UseLinkResult as E, TextVariant as F, useButton as G, useControllableState as H, TextVariantStyle as I, FormFieldControlOptions as J, FormFieldContext as K, textAligns as L, TextAlign as M, TextColorToken as N, LinkPressEvent as O, TextProps as P, resolveFormFieldControl as Q, textColors as R, ThemeMode as S, UseLinkOptions as T, UseButtonOptions as U, UseControllableStateOptions as V, UseButtonResult as W, FormFieldValueOptions as X, FormFieldValue as Y, createFormFieldValue as Z, useBrand as _, ToastStore as a, useUIContext as b, ToastApi as c, FeedbackTone as d, feedbackTones as f, createUIContextValue as g, UIContextValue as h, TOAST_MAX_VISIBLE as i, linkUnderlines as j, LinkProps as k, ToastItem as l, UIContext as m, useToast as n, createToastStore as o, isFeedbackTone as p, FormFieldControl as q, TOAST_DEFAULT_DURATION as r, ToastAction as s, ToastContext as t, ToastOptions as u, useColorScheme as v, themeModes as w, ColorScheme as x, useTheme as y, textVariantStyles as z };
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
|
|
2
|
+
import { $ as useFormFieldControl, D as useLink, E as UseLinkResult, G as useButton, H as useControllableState, J as FormFieldControlOptions, K as FormFieldContext, Q as resolveFormFieldControl, T as UseLinkOptions, U as UseButtonOptions, V as UseControllableStateOptions, W as UseButtonResult, X as FormFieldValueOptions, Y as FormFieldValue, Z as createFormFieldValue, _ as useBrand, b as useUIContext, g as createUIContextValue, h as UIContextValue, m as UIContext, n as useToast, q as FormFieldControl, t as ToastContext, v as useColorScheme, y as useTheme } from "./client-DpHlKasm.js";
|
|
3
|
+
export { FormFieldContext, FormFieldControl, FormFieldControlOptions, FormFieldValue, FormFieldValueOptions, ToastContext, UIContext, type UIContextValue, UseButtonOptions, UseButtonResult, UseControllableStateOptions, UseLinkOptions, UseLinkResult, createFormFieldValue, createUIContextValue, resolveFormFieldControl, useBrand, useButton, useColorScheme, useControllableState, useFormFieldControl, useLink, useTheme, useToast, useUIContext };
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { n as resolveColorScheme, r as resolveTheme } from "./resolve-theme-DbIlzBBD.js";
|
|
3
|
+
import { createContext, useCallback, useContext, useState } from "react";
|
|
4
|
+
//#region src/form-field/context.ts
|
|
5
|
+
/** Un texto vacío cuenta como ausente: `error={errores.email}` suele llegar así. */
|
|
6
|
+
function present(text) {
|
|
7
|
+
return text === void 0 || text === "" ? void 0 : text;
|
|
8
|
+
}
|
|
9
|
+
/** Construye el valor del contexto a partir de un id base (el de `useId()`). */
|
|
10
|
+
function createFormFieldValue(baseId, { label, help, error, disabled = false, required = false }) {
|
|
11
|
+
const helpText = present(help);
|
|
12
|
+
const errorText = present(error);
|
|
13
|
+
return {
|
|
14
|
+
controlId: `${baseId}-control`,
|
|
15
|
+
helpId: helpText === void 0 ? void 0 : `${baseId}-help`,
|
|
16
|
+
errorId: errorText === void 0 ? void 0 : `${baseId}-error`,
|
|
17
|
+
label,
|
|
18
|
+
help: helpText,
|
|
19
|
+
error: errorText,
|
|
20
|
+
disabled,
|
|
21
|
+
required
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/** `null` fuera de un `FormField`. */
|
|
25
|
+
const FormFieldContext = createContext(null);
|
|
26
|
+
/**
|
|
27
|
+
* Combina las props de un control con las de su `FormField`. Las del control
|
|
28
|
+
* mandan en `id` y nombre accesible; `invalid`, `disabled` y `required` se suman:
|
|
29
|
+
* basta con que lo diga uno de los dos.
|
|
30
|
+
*/
|
|
31
|
+
function resolveFormFieldControl(field, { id, invalid = false, disabled = false, required = false, accessibilityLabel } = {}) {
|
|
32
|
+
if (field === null) return {
|
|
33
|
+
id,
|
|
34
|
+
describedBy: void 0,
|
|
35
|
+
accessibilityLabel,
|
|
36
|
+
accessibilityHint: void 0,
|
|
37
|
+
invalid,
|
|
38
|
+
disabled,
|
|
39
|
+
required
|
|
40
|
+
};
|
|
41
|
+
const describedBy = [field.helpId, field.errorId].filter((part) => part !== void 0).join(" ");
|
|
42
|
+
const hint = [field.error, field.help].filter((part) => part !== void 0).join(" ");
|
|
43
|
+
return {
|
|
44
|
+
id: id ?? field.controlId,
|
|
45
|
+
describedBy: describedBy === "" ? void 0 : describedBy,
|
|
46
|
+
accessibilityLabel: accessibilityLabel ?? field.label,
|
|
47
|
+
accessibilityHint: hint === "" ? void 0 : hint,
|
|
48
|
+
invalid: invalid || field.error !== void 0,
|
|
49
|
+
disabled: disabled || field.disabled,
|
|
50
|
+
required: required || field.required
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/** Enlaza un control con el `FormField` que lo contiene, si lo hay. */
|
|
54
|
+
function useFormFieldControl(options) {
|
|
55
|
+
return resolveFormFieldControl(useContext(FormFieldContext), options);
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/hooks/use-button.ts
|
|
59
|
+
/**
|
|
60
|
+
* Comportamiento de `Button` común a web y nativo: un botón deshabilitado o que
|
|
61
|
+
* está cargando no dispara `onPress`. Cada vista añade su accesibilidad
|
|
62
|
+
* (`disabled`/`aria-busy` en web, `accessibilityState` en nativo).
|
|
63
|
+
*/
|
|
64
|
+
function useButton({ disabled = false, loading = false, onPress }) {
|
|
65
|
+
const interactive = !disabled && !loading;
|
|
66
|
+
return {
|
|
67
|
+
disabled,
|
|
68
|
+
loading,
|
|
69
|
+
interactive,
|
|
70
|
+
press: useCallback(() => {
|
|
71
|
+
if (interactive) onPress?.();
|
|
72
|
+
}, [interactive, onPress])
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
//#region src/hooks/use-controllable-state.ts
|
|
77
|
+
/**
|
|
78
|
+
* Estado controlado o no, a elección de la app (ADR-037). Con `value`, el componente
|
|
79
|
+
* solo avisa del cambio y espera a que la app le pase el valor nuevo; sin él, guarda
|
|
80
|
+
* el estado a partir de `defaultValue`. No avisa si el valor no cambia.
|
|
81
|
+
*/
|
|
82
|
+
function useControllableState({ value, defaultValue, onChange }) {
|
|
83
|
+
const [internal, setInternal] = useState(defaultValue);
|
|
84
|
+
const controlled = value !== void 0;
|
|
85
|
+
const current = controlled ? value : internal;
|
|
86
|
+
return [current, useCallback((next) => {
|
|
87
|
+
if (Object.is(next, current)) return;
|
|
88
|
+
if (!controlled) setInternal(next);
|
|
89
|
+
onChange?.(next);
|
|
90
|
+
}, [
|
|
91
|
+
controlled,
|
|
92
|
+
current,
|
|
93
|
+
onChange
|
|
94
|
+
])];
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
97
|
+
//#region src/hooks/use-link.ts
|
|
98
|
+
/**
|
|
99
|
+
* Comportamiento de `Link` común a web y nativo: `onPress` se llama antes de navegar
|
|
100
|
+
* y puede cancelar la navegación por defecto. Cada vista aporta esa navegación
|
|
101
|
+
* (el `<a>` en web, `Linking` en nativo).
|
|
102
|
+
*/
|
|
103
|
+
function useLink({ onPress }) {
|
|
104
|
+
return { press: useCallback(() => {
|
|
105
|
+
let prevented = false;
|
|
106
|
+
onPress?.({
|
|
107
|
+
preventDefault: () => {
|
|
108
|
+
prevented = true;
|
|
109
|
+
},
|
|
110
|
+
get defaultPrevented() {
|
|
111
|
+
return prevented;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
return !prevented;
|
|
115
|
+
}, [onPress]) };
|
|
116
|
+
}
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region src/theme/context.ts
|
|
119
|
+
function createUIContextValue(mode, systemScheme, brand) {
|
|
120
|
+
const colorScheme = resolveColorScheme(mode, systemScheme);
|
|
121
|
+
return {
|
|
122
|
+
theme: resolveTheme(colorScheme, brand),
|
|
123
|
+
colorScheme,
|
|
124
|
+
mode,
|
|
125
|
+
brand
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/** Sin `UIProvider` se usa el tema claro por defecto, para que los componentes funcionen aislados. */
|
|
129
|
+
const UIContext = createContext(createUIContextValue("light", "light"));
|
|
130
|
+
function useUIContext() {
|
|
131
|
+
return useContext(UIContext);
|
|
132
|
+
}
|
|
133
|
+
/** Tema resuelto (tokens con la marca aplicada). Imprescindible en las vistas nativas (ADR-008). */
|
|
134
|
+
function useTheme() {
|
|
135
|
+
return useContext(UIContext).theme;
|
|
136
|
+
}
|
|
137
|
+
/** `'light' | 'dark'` ya resuelto, también con `theme="system"`. */
|
|
138
|
+
function useColorScheme() {
|
|
139
|
+
return useContext(UIContext).colorScheme;
|
|
140
|
+
}
|
|
141
|
+
function useBrand() {
|
|
142
|
+
return useContext(UIContext).brand;
|
|
143
|
+
}
|
|
144
|
+
//#endregion
|
|
145
|
+
//#region src/toast/context.ts
|
|
146
|
+
/** La cola de toasts del `UIProvider` más cercano; `null` fuera de uno. */
|
|
147
|
+
const ToastContext = createContext(null);
|
|
148
|
+
/**
|
|
149
|
+
* Muestra y cierra toasts (ADR-039). Necesita un `UIProvider` por encima: es él quien
|
|
150
|
+
* guarda la cola y los pinta.
|
|
151
|
+
*/
|
|
152
|
+
function useToast() {
|
|
153
|
+
const store = useContext(ToastContext);
|
|
154
|
+
if (store === null) throw new Error("useToast necesita un <UIProvider> por encima: es él quien pinta los toasts.");
|
|
155
|
+
return store;
|
|
156
|
+
}
|
|
157
|
+
//#endregion
|
|
158
|
+
export { FormFieldContext, ToastContext, UIContext, createFormFieldValue, createUIContextValue, resolveFormFieldControl, useBrand, useButton, useColorScheme, useControllableState, useFormFieldControl, useLink, useTheme, useToast, useUIContext };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,186 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
|
+
import { $ as useFormFieldControl, A as LinkUnderline, B as textVariants, C as UIProviderProps, D as useLink, E as UseLinkResult, F as TextVariant, G as useButton, H as useControllableState, I as TextVariantStyle, J as FormFieldControlOptions, K as FormFieldContext, L as textAligns, M as TextAlign, N as TextColorToken, O as LinkPressEvent, P as TextProps, Q as resolveFormFieldControl, R as textColors, S as ThemeMode, T as UseLinkOptions, U as UseButtonOptions, V as UseControllableStateOptions, W as UseButtonResult, X as FormFieldValueOptions, Y as FormFieldValue, Z as createFormFieldValue, _ as useBrand, a as ToastStore, b as useUIContext, c as ToastApi, d as FeedbackTone, f as feedbackTones, g as createUIContextValue, h as UIContextValue, i as TOAST_MAX_VISIBLE, j as linkUnderlines, k as LinkProps, l as ToastItem, m as UIContext, n as useToast, o as createToastStore, p as isFeedbackTone, q as FormFieldControl, r as TOAST_DEFAULT_DURATION, s as ToastAction, t as ToastContext, u as ToastOptions, v as useColorScheme, w as themeModes, x as ColorScheme, y as useTheme, z as textVariantStyles } from "./client-DpHlKasm.js";
|
|
2
3
|
import { BrandName, Theme, ThemeName, ThemeOverrides } from "@satellatickets/tokens";
|
|
3
|
-
|
|
4
|
-
/** Lo que `FormField` publica para el control que envuelve (ADR-037). */
|
|
5
|
-
export interface FormFieldValue {
|
|
6
|
-
/** `id` del control. `FormField` lo usa en el `htmlFor` de su etiqueta (web). */
|
|
7
|
-
controlId: string;
|
|
8
|
-
/** `id` del texto de ayuda, si lo hay (web). */
|
|
9
|
-
helpId: string | undefined;
|
|
10
|
-
/** `id` del mensaje de error, si lo hay (web). */
|
|
11
|
-
errorId: string | undefined;
|
|
12
|
-
label: string;
|
|
13
|
-
help: string | undefined;
|
|
14
|
-
error: string | undefined;
|
|
15
|
-
disabled: boolean;
|
|
16
|
-
required: boolean;
|
|
17
|
-
}
|
|
18
|
-
export interface FormFieldValueOptions {
|
|
19
|
-
label: string;
|
|
20
|
-
help?: string | undefined;
|
|
21
|
-
error?: string | undefined;
|
|
22
|
-
disabled?: boolean | undefined;
|
|
23
|
-
required?: boolean | undefined;
|
|
24
|
-
}
|
|
25
|
-
/** Construye el valor del contexto a partir de un id base (el de `useId()`). */
|
|
26
|
-
export declare function createFormFieldValue(baseId: string, { label, help, error, disabled, required }: FormFieldValueOptions): FormFieldValue;
|
|
27
|
-
/** `null` fuera de un `FormField`. */
|
|
28
|
-
export declare const FormFieldContext: import("react").Context<FormFieldValue | null>;
|
|
29
|
-
/** Las props propias de un control que se combinan con las de su `FormField`. */
|
|
30
|
-
export interface FormFieldControlOptions {
|
|
31
|
-
id?: string | undefined;
|
|
32
|
-
invalid?: boolean | undefined;
|
|
33
|
-
disabled?: boolean | undefined;
|
|
34
|
-
required?: boolean | undefined;
|
|
35
|
-
accessibilityLabel?: string | undefined;
|
|
36
|
-
}
|
|
37
|
-
/** Lo que un control necesita para enlazarse con su `FormField`, en web y en nativo. */
|
|
38
|
-
export interface FormFieldControl {
|
|
39
|
-
/** `id` del control (web): el propio o el que espera la etiqueta del campo. */
|
|
40
|
-
id: string | undefined;
|
|
41
|
-
/** Ids de la ayuda y el error para `aria-describedby` (web). */
|
|
42
|
-
describedBy: string | undefined;
|
|
43
|
-
/** Nombre accesible (nativo): el propio o la etiqueta del campo. */
|
|
44
|
-
accessibilityLabel: string | undefined;
|
|
45
|
-
/** El error y la ayuda, uno tras otro, para `accessibilityHint` (nativo). */
|
|
46
|
-
accessibilityHint: string | undefined;
|
|
47
|
-
invalid: boolean;
|
|
48
|
-
disabled: boolean;
|
|
49
|
-
required: boolean;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Combina las props de un control con las de su `FormField`. Las del control
|
|
53
|
-
* mandan en `id` y nombre accesible; `invalid`, `disabled` y `required` se suman:
|
|
54
|
-
* basta con que lo diga uno de los dos.
|
|
55
|
-
*/
|
|
56
|
-
export declare function resolveFormFieldControl(field: FormFieldValue | null, { id, invalid, disabled, required, accessibilityLabel }?: FormFieldControlOptions): FormFieldControl;
|
|
57
|
-
/** Enlaza un control con el `FormField` que lo contiene, si lo hay. */
|
|
58
|
-
export declare function useFormFieldControl(options?: FormFieldControlOptions): FormFieldControl;
|
|
59
|
-
//#endregion
|
|
60
|
-
//#region src/hooks/use-button.d.ts
|
|
61
|
-
export interface UseButtonOptions {
|
|
62
|
-
disabled?: boolean | undefined;
|
|
63
|
-
loading?: boolean | undefined;
|
|
64
|
-
onPress?: (() => void) | undefined;
|
|
65
|
-
}
|
|
66
|
-
export interface UseButtonResult {
|
|
67
|
-
disabled: boolean;
|
|
68
|
-
loading: boolean;
|
|
69
|
-
/** `false` si está deshabilitado o cargando: el botón no responde a la pulsación. */
|
|
70
|
-
interactive: boolean;
|
|
71
|
-
/** Llama a `onPress` solo si el botón es interactivo. */
|
|
72
|
-
press: () => void;
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Comportamiento de `Button` común a web y nativo: un botón deshabilitado o que
|
|
76
|
-
* está cargando no dispara `onPress`. Cada vista añade su accesibilidad
|
|
77
|
-
* (`disabled`/`aria-busy` en web, `accessibilityState` en nativo).
|
|
78
|
-
*/
|
|
79
|
-
export declare function useButton({ disabled, loading, onPress }: UseButtonOptions): UseButtonResult;
|
|
80
|
-
//#endregion
|
|
81
|
-
//#region src/hooks/use-controllable-state.d.ts
|
|
82
|
-
export interface UseControllableStateOptions<T> {
|
|
83
|
-
/** Valor controlado por la app. Si existe, manda sobre el estado interno. */
|
|
84
|
-
value?: T | undefined;
|
|
85
|
-
/** Valor inicial cuando el componente guarda su propio estado. */
|
|
86
|
-
defaultValue: T;
|
|
87
|
-
/** Se llama con el valor nuevo cuando cambia, en los dos modos. */
|
|
88
|
-
onChange?: ((value: T) => void) | undefined;
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Estado controlado o no, a elección de la app (ADR-037). Con `value`, el componente
|
|
92
|
-
* solo avisa del cambio y espera a que la app le pase el valor nuevo; sin él, guarda
|
|
93
|
-
* el estado a partir de `defaultValue`. No avisa si el valor no cambia.
|
|
94
|
-
*/
|
|
95
|
-
export declare function useControllableState<T>({ value, defaultValue, onChange }: UseControllableStateOptions<T>): [T, (next: T) => void];
|
|
96
|
-
//#endregion
|
|
97
|
-
//#region src/types/text.d.ts
|
|
98
|
-
export declare const textVariants: readonly ["hero", "display", "title", "heading", "subheading", "body", "bodySmall", "label", "caption", "code"];
|
|
99
|
-
export type TextVariant = (typeof textVariants)[number];
|
|
100
|
-
export type TextColorToken = keyof Theme['color']['text'];
|
|
101
|
-
export declare const textColors: TextColorToken[];
|
|
102
|
-
export declare const textAligns: readonly ["left", "center", "right"];
|
|
103
|
-
export type TextAlign = (typeof textAligns)[number];
|
|
104
|
-
/** Tokens que componen una variante tipográfica; ambas vistas los leen del tema. */
|
|
105
|
-
export interface TextVariantStyle {
|
|
106
|
-
family: keyof Theme['font']['family'];
|
|
107
|
-
/** Tamaño; `font.lineHeight` comparte las mismas claves (lo comprueba el typecheck de las vistas). */
|
|
108
|
-
size: keyof Theme['font']['size'];
|
|
109
|
-
weight: keyof Theme['font']['weight'];
|
|
110
|
-
uppercase?: boolean;
|
|
111
|
-
/** Espaciado entre letras en px. */
|
|
112
|
-
letterSpacing?: number;
|
|
113
|
-
/** Nivel de encabezado semántico (h1–h4) si la variante es un título. */
|
|
114
|
-
headingLevel?: 1 | 2 | 3 | 4;
|
|
115
|
-
}
|
|
116
|
-
/** Escala tipográfica de Satella: Unbounded para titulares, Hanken Grotesk para texto, IBM Plex Mono para etiquetas. */
|
|
117
|
-
export declare const textVariantStyles: Readonly<Record<TextVariant, TextVariantStyle>>;
|
|
118
|
-
export interface TextProps {
|
|
119
|
-
variant?: TextVariant | undefined;
|
|
120
|
-
color?: TextColorToken | undefined;
|
|
121
|
-
align?: TextAlign | undefined;
|
|
122
|
-
/** Una sola línea con puntos suspensivos. */
|
|
123
|
-
truncate?: boolean | undefined;
|
|
124
|
-
testID?: string | undefined;
|
|
125
|
-
children?: ReactNode;
|
|
126
|
-
}
|
|
127
|
-
//#endregion
|
|
128
|
-
//#region src/types/link.d.ts
|
|
129
|
-
/**
|
|
130
|
-
* Cuándo se subraya un `Link` (ADR-009). `always` es lo correcto dentro de un párrafo:
|
|
131
|
-
* el color solo no basta para distinguir un enlace. `hover` lo subraya al pasar el
|
|
132
|
-
* puntero o enfocarlo en web, y mientras se pulsa en nativo.
|
|
133
|
-
*/
|
|
134
|
-
export declare const linkUnderlines: readonly ["always", "hover"];
|
|
135
|
-
export type LinkUnderline = (typeof linkUnderlines)[number];
|
|
136
|
-
/** Lo que recibe `onPress` de un `Link`: permite cancelar la navegación por defecto. */
|
|
137
|
-
export interface LinkPressEvent {
|
|
138
|
-
/** Cancela la navegación por defecto, por ejemplo para hacerla con el router de la app. */
|
|
139
|
-
preventDefault: () => void;
|
|
140
|
-
readonly defaultPrevented: boolean;
|
|
141
|
-
}
|
|
142
|
-
export interface LinkProps {
|
|
143
|
-
/** Destino. En web es el `href` del `<a>`; en nativo se abre con `Linking`. */
|
|
144
|
-
href: string;
|
|
145
|
-
/**
|
|
146
|
-
* Tipografía, con las mismas variantes que `Text`. Sin ella hereda la del texto
|
|
147
|
-
* que lo contiene; fuera de un `Text`, usa `body`.
|
|
148
|
-
*/
|
|
149
|
-
variant?: TextVariant | undefined;
|
|
150
|
-
/** Color del texto (por defecto `link`). */
|
|
151
|
-
color?: TextColorToken | undefined;
|
|
152
|
-
underline?: LinkUnderline | undefined;
|
|
153
|
-
/**
|
|
154
|
-
* Se llama antes de navegar. Con `event.preventDefault()` la librería no navega y
|
|
155
|
-
* la app decide qué hacer. En web no se llama si el clic lleva un modificador
|
|
156
|
-
* (Ctrl, Cmd, Mayús, Alt) o no es del botón principal: ahí manda el navegador.
|
|
157
|
-
*/
|
|
158
|
-
onPress?: ((event: LinkPressEvent) => void) | undefined;
|
|
159
|
-
/** Nombre accesible cuando el texto visible no basta para describir el destino. */
|
|
160
|
-
accessibilityLabel?: string | undefined;
|
|
161
|
-
/** `data-testid` en web, `testID` en nativo. */
|
|
162
|
-
testID?: string | undefined;
|
|
163
|
-
children: ReactNode;
|
|
164
|
-
}
|
|
165
|
-
//#endregion
|
|
166
|
-
//#region src/hooks/use-link.d.ts
|
|
167
|
-
export interface UseLinkOptions {
|
|
168
|
-
onPress?: ((event: LinkPressEvent) => void) | undefined;
|
|
169
|
-
}
|
|
170
|
-
export interface UseLinkResult {
|
|
171
|
-
/**
|
|
172
|
-
* Llama a `onPress` y devuelve `true` si la navegación por defecto debe seguir,
|
|
173
|
-
* es decir, si nadie llamó a `preventDefault()`.
|
|
174
|
-
*/
|
|
175
|
-
press: () => boolean;
|
|
176
|
-
}
|
|
177
|
-
/**
|
|
178
|
-
* Comportamiento de `Link` común a web y nativo: `onPress` se llama antes de navegar
|
|
179
|
-
* y puede cancelar la navegación por defecto. Cada vista aporta esa navegación
|
|
180
|
-
* (el `<a>` en web, `Linking` en nativo).
|
|
181
|
-
*/
|
|
182
|
-
export declare function useLink({ onPress }: UseLinkOptions): UseLinkResult;
|
|
183
|
-
//#endregion
|
|
4
|
+
import { ReactNode } from "react";
|
|
184
5
|
//#region src/tabs/navigation.d.ts
|
|
185
6
|
/** Lo mínimo que hace falta saber de una pestaña para moverse entre ellas. */
|
|
186
7
|
export interface NavigableTab {
|
|
@@ -199,37 +20,6 @@ export declare function firstEnabledTab(items: readonly NavigableTab[]): string
|
|
|
199
20
|
*/
|
|
200
21
|
export declare function getTabInDirection(items: readonly NavigableTab[], current: string, direction: TabDirection): string;
|
|
201
22
|
//#endregion
|
|
202
|
-
//#region src/types/ui-provider.d.ts
|
|
203
|
-
/** Modo de tema que acepta `UIProvider` (ADR-010). */
|
|
204
|
-
export declare const themeModes: readonly ["light", "dark", "system"];
|
|
205
|
-
export type ThemeMode = (typeof themeModes)[number];
|
|
206
|
-
/** Esquema resuelto: siempre uno de los temas definidos en tokens. */
|
|
207
|
-
export type ColorScheme = ThemeName;
|
|
208
|
-
export interface UIProviderProps {
|
|
209
|
-
/** Tema claro, oscuro o el del sistema (por defecto `system`). */
|
|
210
|
-
theme?: ThemeMode | undefined;
|
|
211
|
-
/** Marca registrada en tokens. Sin marca se usa la identidad por defecto. */
|
|
212
|
-
brand?: BrandName | undefined;
|
|
213
|
-
children: ReactNode;
|
|
214
|
-
}
|
|
215
|
-
//#endregion
|
|
216
|
-
//#region src/theme/context.d.ts
|
|
217
|
-
interface UIContextValue {
|
|
218
|
-
theme: Theme;
|
|
219
|
-
colorScheme: ColorScheme;
|
|
220
|
-
mode: ThemeMode;
|
|
221
|
-
brand: BrandName | undefined;
|
|
222
|
-
}
|
|
223
|
-
export declare function createUIContextValue(mode: ThemeMode, systemScheme: ColorScheme, brand?: BrandName): UIContextValue;
|
|
224
|
-
/** Sin `UIProvider` se usa el tema claro por defecto, para que los componentes funcionen aislados. */
|
|
225
|
-
export declare const UIContext: import("react").Context<UIContextValue>;
|
|
226
|
-
export declare function useUIContext(): UIContextValue;
|
|
227
|
-
/** Tema resuelto (tokens con la marca aplicada). Imprescindible en las vistas nativas (ADR-008). */
|
|
228
|
-
export declare function useTheme(): Theme;
|
|
229
|
-
/** `'light' | 'dark'` ya resuelto, también con `theme="system"`. */
|
|
230
|
-
export declare function useColorScheme(): ColorScheme;
|
|
231
|
-
export declare function useBrand(): BrandName | undefined;
|
|
232
|
-
//#endregion
|
|
233
23
|
//#region src/theme/resolve-theme.d.ts
|
|
234
24
|
/** Combina los overrides de una marca sobre un tema (ADR-010). Los arrays (sombras) se sustituyen enteros. */
|
|
235
25
|
export declare function mergeTheme(base: Theme, overrides: ThemeOverrides): Theme;
|
|
@@ -237,90 +27,6 @@ export declare function mergeTheme(base: Theme, overrides: ThemeOverrides): Them
|
|
|
237
27
|
export declare function resolveTheme(scheme: ThemeName, brand?: BrandName): Theme;
|
|
238
28
|
export declare function resolveColorScheme(mode: ThemeMode, systemScheme: ColorScheme): ColorScheme;
|
|
239
29
|
//#endregion
|
|
240
|
-
//#region src/types/feedback.d.ts
|
|
241
|
-
/**
|
|
242
|
-
* Los cuatro significados de `color.feedback` en los tokens: éxito, aviso, error e
|
|
243
|
-
* información. Los comparten los componentes que comunican un estado.
|
|
244
|
-
*/
|
|
245
|
-
export declare const feedbackTones: readonly ["success", "warning", "danger", "info"];
|
|
246
|
-
export type FeedbackTone = (typeof feedbackTones)[number];
|
|
247
|
-
export declare function isFeedbackTone(value: string): value is FeedbackTone;
|
|
248
|
-
//#endregion
|
|
249
|
-
//#region src/types/toast.d.ts
|
|
250
|
-
/** Acción opcional de un toast, por ejemplo "Deshacer". */
|
|
251
|
-
export interface ToastAction {
|
|
252
|
-
label: string;
|
|
253
|
-
onPress: () => void;
|
|
254
|
-
}
|
|
255
|
-
/** Lo que recibe `show()` de `useToast` (ADR-039). */
|
|
256
|
-
export interface ToastOptions {
|
|
257
|
-
/** Qué comunica (por defecto `info`). Fija el icono y la urgencia con que se anuncia. */
|
|
258
|
-
tone?: FeedbackTone | undefined;
|
|
259
|
-
/** El mensaje, en una línea. */
|
|
260
|
-
title: string;
|
|
261
|
-
/** Detalle opcional. */
|
|
262
|
-
description?: string | undefined;
|
|
263
|
-
/**
|
|
264
|
-
* Milisegundos hasta que se cierra solo (por defecto 5000). Con `0` no se cierra
|
|
265
|
-
* solo: necesita `closeLabel`, una `action` o una llamada a `dismiss`.
|
|
266
|
-
*/
|
|
267
|
-
duration?: number | undefined;
|
|
268
|
-
/** Botón de acción. Al pulsarlo se llama a `onPress` y el toast se cierra. */
|
|
269
|
-
action?: ToastAction | undefined;
|
|
270
|
-
/**
|
|
271
|
-
* Si existe, el toast muestra un botón de cierre con este nombre accesible
|
|
272
|
-
* ("Cerrar aviso"). La librería no trae textos propios.
|
|
273
|
-
*/
|
|
274
|
-
closeLabel?: string | undefined;
|
|
275
|
-
}
|
|
276
|
-
/** Un toast ya en la cola: sus opciones resueltas y su identificador. */
|
|
277
|
-
export interface ToastItem {
|
|
278
|
-
id: string;
|
|
279
|
-
tone: FeedbackTone;
|
|
280
|
-
title: string;
|
|
281
|
-
description: string | undefined;
|
|
282
|
-
duration: number;
|
|
283
|
-
action: ToastAction | undefined;
|
|
284
|
-
closeLabel: string | undefined;
|
|
285
|
-
}
|
|
286
|
-
/** Lo que devuelve `useToast`. */
|
|
287
|
-
export interface ToastApi {
|
|
288
|
-
/** Muestra un toast y devuelve su `id`. */
|
|
289
|
-
show: (options: ToastOptions) => string;
|
|
290
|
-
/** Cierra el toast con ese `id`; sin `id`, los cierra todos. */
|
|
291
|
-
dismiss: (id?: string) => void;
|
|
292
|
-
}
|
|
293
|
-
//#endregion
|
|
294
|
-
//#region src/toast/store.d.ts
|
|
295
|
-
/** Milisegundos que un toast permanece visible si no se indica otra cosa. */
|
|
296
|
-
export declare const TOAST_DEFAULT_DURATION = 5000;
|
|
297
|
-
/** Toasts visibles a la vez. Al llegar uno más, se retira el más antiguo. */
|
|
298
|
-
export declare const TOAST_MAX_VISIBLE = 3;
|
|
299
|
-
interface ToastStore extends ToastApi {
|
|
300
|
-
/** Para `useSyncExternalStore`: avisa de cada cambio y devuelve cómo dejar de escuchar. */
|
|
301
|
-
subscribe: (listener: () => void) => () => void;
|
|
302
|
-
/** Los toasts visibles, del más antiguo al más reciente. Cambia de identidad con cada cambio. */
|
|
303
|
-
getSnapshot: () => readonly ToastItem[];
|
|
304
|
-
/** Detiene el cierre automático, por ejemplo mientras el puntero está sobre los toasts. */
|
|
305
|
-
pause: () => void;
|
|
306
|
-
/** Reanuda el cierre automático con el tiempo que le quedaba a cada toast. */
|
|
307
|
-
resume: () => void;
|
|
308
|
-
}
|
|
309
|
-
/**
|
|
310
|
-
* La cola de toasts de un `UIProvider` (ADR-039): alta, baja, cierre automático, pausa
|
|
311
|
-
* y límite de visibles. No depende de React ni de la plataforma.
|
|
312
|
-
*/
|
|
313
|
-
export declare function createToastStore(): ToastStore;
|
|
314
|
-
//#endregion
|
|
315
|
-
//#region src/toast/context.d.ts
|
|
316
|
-
/** La cola de toasts del `UIProvider` más cercano; `null` fuera de uno. */
|
|
317
|
-
export declare const ToastContext: import("react").Context<ToastStore | null>;
|
|
318
|
-
/**
|
|
319
|
-
* Muestra y cierra toasts (ADR-039). Necesita un `UIProvider` por encima: es él quien
|
|
320
|
-
* guarda la cola y los pinta.
|
|
321
|
-
*/
|
|
322
|
-
export declare function useToast(): ToastApi;
|
|
323
|
-
//#endregion
|
|
324
30
|
//#region src/types/alert.d.ts
|
|
325
31
|
interface AlertBaseProps {
|
|
326
32
|
/** Qué comunica (por defecto `info`). Fija el color y el icono. */
|
|
@@ -884,4 +590,4 @@ export interface TextAreaProps {
|
|
|
884
590
|
testID?: string | undefined;
|
|
885
591
|
}
|
|
886
592
|
//#endregion
|
|
887
|
-
export type
|
|
593
|
+
export { ColorScheme, FeedbackTone, FormFieldContext, FormFieldControl, FormFieldControlOptions, FormFieldValue, FormFieldValueOptions, LinkPressEvent, LinkProps, LinkUnderline, TOAST_DEFAULT_DURATION, TOAST_MAX_VISIBLE, TextAlign, TextColorToken, TextProps, TextVariant, TextVariantStyle, ThemeMode, ToastAction, ToastApi, ToastContext, ToastItem, ToastOptions, type ToastStore, UIContext, type UIContextValue, UIProviderProps, UseButtonOptions, UseButtonResult, UseControllableStateOptions, UseLinkOptions, UseLinkResult, createFormFieldValue, createToastStore, createUIContextValue, feedbackTones, isFeedbackTone, linkUnderlines, resolveFormFieldControl, textAligns, textColors, textVariantStyles, textVariants, themeModes, useBrand, useButton, useColorScheme, useControllableState, useFormFieldControl, useLink, useTheme, useToast, useUIContext };
|
package/dist/index.js
CHANGED
|
@@ -1,119 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
function present(text) {
|
|
6
|
-
return text === void 0 || text === "" ? void 0 : text;
|
|
7
|
-
}
|
|
8
|
-
/** Construye el valor del contexto a partir de un id base (el de `useId()`). */
|
|
9
|
-
function createFormFieldValue(baseId, { label, help, error, disabled = false, required = false }) {
|
|
10
|
-
const helpText = present(help);
|
|
11
|
-
const errorText = present(error);
|
|
12
|
-
return {
|
|
13
|
-
controlId: `${baseId}-control`,
|
|
14
|
-
helpId: helpText === void 0 ? void 0 : `${baseId}-help`,
|
|
15
|
-
errorId: errorText === void 0 ? void 0 : `${baseId}-error`,
|
|
16
|
-
label,
|
|
17
|
-
help: helpText,
|
|
18
|
-
error: errorText,
|
|
19
|
-
disabled,
|
|
20
|
-
required
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
/** `null` fuera de un `FormField`. */
|
|
24
|
-
const FormFieldContext = createContext(null);
|
|
25
|
-
/**
|
|
26
|
-
* Combina las props de un control con las de su `FormField`. Las del control
|
|
27
|
-
* mandan en `id` y nombre accesible; `invalid`, `disabled` y `required` se suman:
|
|
28
|
-
* basta con que lo diga uno de los dos.
|
|
29
|
-
*/
|
|
30
|
-
function resolveFormFieldControl(field, { id, invalid = false, disabled = false, required = false, accessibilityLabel } = {}) {
|
|
31
|
-
if (field === null) return {
|
|
32
|
-
id,
|
|
33
|
-
describedBy: void 0,
|
|
34
|
-
accessibilityLabel,
|
|
35
|
-
accessibilityHint: void 0,
|
|
36
|
-
invalid,
|
|
37
|
-
disabled,
|
|
38
|
-
required
|
|
39
|
-
};
|
|
40
|
-
const describedBy = [field.helpId, field.errorId].filter((part) => part !== void 0).join(" ");
|
|
41
|
-
const hint = [field.error, field.help].filter((part) => part !== void 0).join(" ");
|
|
42
|
-
return {
|
|
43
|
-
id: id ?? field.controlId,
|
|
44
|
-
describedBy: describedBy === "" ? void 0 : describedBy,
|
|
45
|
-
accessibilityLabel: accessibilityLabel ?? field.label,
|
|
46
|
-
accessibilityHint: hint === "" ? void 0 : hint,
|
|
47
|
-
invalid: invalid || field.error !== void 0,
|
|
48
|
-
disabled: disabled || field.disabled,
|
|
49
|
-
required: required || field.required
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
/** Enlaza un control con el `FormField` que lo contiene, si lo hay. */
|
|
53
|
-
function useFormFieldControl(options) {
|
|
54
|
-
return resolveFormFieldControl(useContext(FormFieldContext), options);
|
|
55
|
-
}
|
|
56
|
-
//#endregion
|
|
57
|
-
//#region src/hooks/use-button.ts
|
|
58
|
-
/**
|
|
59
|
-
* Comportamiento de `Button` común a web y nativo: un botón deshabilitado o que
|
|
60
|
-
* está cargando no dispara `onPress`. Cada vista añade su accesibilidad
|
|
61
|
-
* (`disabled`/`aria-busy` en web, `accessibilityState` en nativo).
|
|
62
|
-
*/
|
|
63
|
-
function useButton({ disabled = false, loading = false, onPress }) {
|
|
64
|
-
const interactive = !disabled && !loading;
|
|
65
|
-
return {
|
|
66
|
-
disabled,
|
|
67
|
-
loading,
|
|
68
|
-
interactive,
|
|
69
|
-
press: useCallback(() => {
|
|
70
|
-
if (interactive) onPress?.();
|
|
71
|
-
}, [interactive, onPress])
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
//#endregion
|
|
75
|
-
//#region src/hooks/use-controllable-state.ts
|
|
76
|
-
/**
|
|
77
|
-
* Estado controlado o no, a elección de la app (ADR-037). Con `value`, el componente
|
|
78
|
-
* solo avisa del cambio y espera a que la app le pase el valor nuevo; sin él, guarda
|
|
79
|
-
* el estado a partir de `defaultValue`. No avisa si el valor no cambia.
|
|
80
|
-
*/
|
|
81
|
-
function useControllableState({ value, defaultValue, onChange }) {
|
|
82
|
-
const [internal, setInternal] = useState(defaultValue);
|
|
83
|
-
const controlled = value !== void 0;
|
|
84
|
-
const current = controlled ? value : internal;
|
|
85
|
-
return [current, useCallback((next) => {
|
|
86
|
-
if (Object.is(next, current)) return;
|
|
87
|
-
if (!controlled) setInternal(next);
|
|
88
|
-
onChange?.(next);
|
|
89
|
-
}, [
|
|
90
|
-
controlled,
|
|
91
|
-
current,
|
|
92
|
-
onChange
|
|
93
|
-
])];
|
|
94
|
-
}
|
|
95
|
-
//#endregion
|
|
96
|
-
//#region src/hooks/use-link.ts
|
|
97
|
-
/**
|
|
98
|
-
* Comportamiento de `Link` común a web y nativo: `onPress` se llama antes de navegar
|
|
99
|
-
* y puede cancelar la navegación por defecto. Cada vista aporta esa navegación
|
|
100
|
-
* (el `<a>` en web, `Linking` en nativo).
|
|
101
|
-
*/
|
|
102
|
-
function useLink({ onPress }) {
|
|
103
|
-
return { press: useCallback(() => {
|
|
104
|
-
let prevented = false;
|
|
105
|
-
onPress?.({
|
|
106
|
-
preventDefault: () => {
|
|
107
|
-
prevented = true;
|
|
108
|
-
},
|
|
109
|
-
get defaultPrevented() {
|
|
110
|
-
return prevented;
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
return !prevented;
|
|
114
|
-
}, [onPress]) };
|
|
115
|
-
}
|
|
116
|
-
//#endregion
|
|
1
|
+
|
|
2
|
+
import { n as resolveColorScheme, r as resolveTheme, t as mergeTheme } from "./resolve-theme-DbIlzBBD.js";
|
|
3
|
+
import { themes } from "@satellatickets/tokens";
|
|
4
|
+
export * from "./client.js";
|
|
117
5
|
//#region src/tabs/navigation.ts
|
|
118
6
|
/** Hacia dónde mueve el foco una tecla: flechas, Inicio y Fin. */
|
|
119
7
|
const tabDirections = [
|
|
@@ -144,79 +32,6 @@ function getTabInDirection(items, current, direction) {
|
|
|
144
32
|
return enabled[(index + (direction === "next" ? 1 : -1) + enabled.length) % enabled.length] ?? current;
|
|
145
33
|
}
|
|
146
34
|
//#endregion
|
|
147
|
-
//#region src/theme/resolve-theme.ts
|
|
148
|
-
function isPlainObject(value) {
|
|
149
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
150
|
-
}
|
|
151
|
-
function merge(base, overrides) {
|
|
152
|
-
const result = { ...base };
|
|
153
|
-
for (const [key, value] of Object.entries(overrides)) {
|
|
154
|
-
if (value === void 0) continue;
|
|
155
|
-
const current = base[key];
|
|
156
|
-
result[key] = isPlainObject(current) && isPlainObject(value) ? merge(current, value) : value;
|
|
157
|
-
}
|
|
158
|
-
return result;
|
|
159
|
-
}
|
|
160
|
-
/** Combina los overrides de una marca sobre un tema (ADR-010). Los arrays (sombras) se sustituyen enteros. */
|
|
161
|
-
function mergeTheme(base, overrides) {
|
|
162
|
-
return merge(base, overrides);
|
|
163
|
-
}
|
|
164
|
-
const cache = /* @__PURE__ */ new Map();
|
|
165
|
-
/** Tema resuelto para un esquema y una marca; memoizado para que la identidad sea estable. */
|
|
166
|
-
function resolveTheme(scheme, brand) {
|
|
167
|
-
const key = `${scheme}/${brand ?? ""}`;
|
|
168
|
-
const cached = cache.get(key);
|
|
169
|
-
if (cached) return cached;
|
|
170
|
-
const base = themes[scheme];
|
|
171
|
-
const overrides = brand === void 0 ? void 0 : brands[brand]?.[scheme];
|
|
172
|
-
const resolved = overrides === void 0 ? base : mergeTheme(base, overrides);
|
|
173
|
-
cache.set(key, resolved);
|
|
174
|
-
return resolved;
|
|
175
|
-
}
|
|
176
|
-
function resolveColorScheme(mode, systemScheme) {
|
|
177
|
-
return mode === "system" ? systemScheme : mode;
|
|
178
|
-
}
|
|
179
|
-
//#endregion
|
|
180
|
-
//#region src/theme/context.ts
|
|
181
|
-
function createUIContextValue(mode, systemScheme, brand) {
|
|
182
|
-
const colorScheme = resolveColorScheme(mode, systemScheme);
|
|
183
|
-
return {
|
|
184
|
-
theme: resolveTheme(colorScheme, brand),
|
|
185
|
-
colorScheme,
|
|
186
|
-
mode,
|
|
187
|
-
brand
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
/** Sin `UIProvider` se usa el tema claro por defecto, para que los componentes funcionen aislados. */
|
|
191
|
-
const UIContext = createContext(createUIContextValue("light", "light"));
|
|
192
|
-
function useUIContext() {
|
|
193
|
-
return useContext(UIContext);
|
|
194
|
-
}
|
|
195
|
-
/** Tema resuelto (tokens con la marca aplicada). Imprescindible en las vistas nativas (ADR-008). */
|
|
196
|
-
function useTheme() {
|
|
197
|
-
return useContext(UIContext).theme;
|
|
198
|
-
}
|
|
199
|
-
/** `'light' | 'dark'` ya resuelto, también con `theme="system"`. */
|
|
200
|
-
function useColorScheme() {
|
|
201
|
-
return useContext(UIContext).colorScheme;
|
|
202
|
-
}
|
|
203
|
-
function useBrand() {
|
|
204
|
-
return useContext(UIContext).brand;
|
|
205
|
-
}
|
|
206
|
-
//#endregion
|
|
207
|
-
//#region src/toast/context.ts
|
|
208
|
-
/** La cola de toasts del `UIProvider` más cercano; `null` fuera de uno. */
|
|
209
|
-
const ToastContext = createContext(null);
|
|
210
|
-
/**
|
|
211
|
-
* Muestra y cierra toasts (ADR-039). Necesita un `UIProvider` por encima: es él quien
|
|
212
|
-
* guarda la cola y los pinta.
|
|
213
|
-
*/
|
|
214
|
-
function useToast() {
|
|
215
|
-
const store = useContext(ToastContext);
|
|
216
|
-
if (store === null) throw new Error("useToast necesita un <UIProvider> por encima: es él quien pinta los toasts.");
|
|
217
|
-
return store;
|
|
218
|
-
}
|
|
219
|
-
//#endregion
|
|
220
35
|
//#region src/toast/store.ts
|
|
221
36
|
/** Milisegundos que un toast permanece visible si no se indica otra cosa. */
|
|
222
37
|
const TOAST_DEFAULT_DURATION = 5e3;
|
|
@@ -573,4 +388,4 @@ const themeModes = [
|
|
|
573
388
|
"system"
|
|
574
389
|
];
|
|
575
390
|
//#endregion
|
|
576
|
-
export {
|
|
391
|
+
export { TOAST_DEFAULT_DURATION, TOAST_MAX_VISIBLE, backgroundTokens, badgeVariants, borderColorTokens, buttonIconSize, buttonVariants, cardVariants, controlSizes, createToastStore, dividerOrientations, feedbackTones, firstEnabledTab, getTabInDirection, iconButtonIconSize, iconSizePx, iconSizes, inputIconSize, inputTypes, isFeedbackTone, linkUnderlines, mergeTheme, modalPresentations, radiusTokens, resolveColorScheme, resolveTheme, shadowTokens, skeletonShapes, spaceTokens, spinnerSizePx, spinnerSizes, stackAligns, stackDirections, stackJustifies, tabDirections, textAligns, textColors, textVariantStyles, textVariants, themeModes };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
|
|
2
|
+
import { brands, themes } from "@satellatickets/tokens";
|
|
3
|
+
//#region src/theme/resolve-theme.ts
|
|
4
|
+
function isPlainObject(value) {
|
|
5
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6
|
+
}
|
|
7
|
+
function merge(base, overrides) {
|
|
8
|
+
const result = { ...base };
|
|
9
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
10
|
+
if (value === void 0) continue;
|
|
11
|
+
const current = base[key];
|
|
12
|
+
result[key] = isPlainObject(current) && isPlainObject(value) ? merge(current, value) : value;
|
|
13
|
+
}
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
16
|
+
/** Combina los overrides de una marca sobre un tema (ADR-010). Los arrays (sombras) se sustituyen enteros. */
|
|
17
|
+
function mergeTheme(base, overrides) {
|
|
18
|
+
return merge(base, overrides);
|
|
19
|
+
}
|
|
20
|
+
const cache = /* @__PURE__ */ new Map();
|
|
21
|
+
/** Tema resuelto para un esquema y una marca; memoizado para que la identidad sea estable. */
|
|
22
|
+
function resolveTheme(scheme, brand) {
|
|
23
|
+
const key = `${scheme}/${brand ?? ""}`;
|
|
24
|
+
const cached = cache.get(key);
|
|
25
|
+
if (cached) return cached;
|
|
26
|
+
const base = themes[scheme];
|
|
27
|
+
const overrides = brand === void 0 ? void 0 : brands[brand]?.[scheme];
|
|
28
|
+
const resolved = overrides === void 0 ? base : mergeTheme(base, overrides);
|
|
29
|
+
cache.set(key, resolved);
|
|
30
|
+
return resolved;
|
|
31
|
+
}
|
|
32
|
+
function resolveColorScheme(mode, systemScheme) {
|
|
33
|
+
return mode === "system" ? systemScheme : mode;
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
export { resolveColorScheme as n, resolveTheme as r, mergeTheme as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@satellatickets/core",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Lógica headless de satella-ui: contratos de props, constantes de variantes, contexto de tema y hooks compartidos por React y React Native",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"tsdown": "^0.23.0",
|
|
41
41
|
"typescript": "^5.9.3",
|
|
42
42
|
"vitest": "^5.0.3",
|
|
43
|
-
"@satellatickets/
|
|
44
|
-
"@satellatickets/
|
|
43
|
+
"@satellatickets/eslint-config": "0.0.0",
|
|
44
|
+
"@satellatickets/tsconfig": "0.0.0"
|
|
45
45
|
},
|
|
46
46
|
"scripts": {
|
|
47
47
|
"build": "tsdown",
|