@satellatickets/core 0.1.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/README.md +6 -6
- 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 +432 -81
- package/dist/index.js +216 -74
- package/dist/resolve-theme-DbIlzBBD.js +36 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,82 +1,127 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
|
|
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";
|
|
5
|
+
//#region src/tabs/navigation.ts
|
|
6
|
+
/** Hacia dónde mueve el foco una tecla: flechas, Inicio y Fin. */
|
|
7
|
+
const tabDirections = [
|
|
8
|
+
"next",
|
|
9
|
+
"previous",
|
|
10
|
+
"first",
|
|
11
|
+
"last"
|
|
12
|
+
];
|
|
13
|
+
/** La primera pestaña habilitada: la elegida por defecto. `undefined` si no hay ninguna. */
|
|
14
|
+
function firstEnabledTab(items) {
|
|
15
|
+
return items.find((item) => item.disabled !== true)?.value;
|
|
16
|
+
}
|
|
4
17
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
18
|
+
* La pestaña a la que lleva una tecla desde la actual, saltando las deshabilitadas.
|
|
19
|
+
* `next` y `previous` dan la vuelta al llegar al final. Si no hay otra a la que ir,
|
|
20
|
+
* devuelve la actual.
|
|
8
21
|
*/
|
|
9
|
-
function
|
|
10
|
-
const
|
|
11
|
-
return
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
22
|
+
function getTabInDirection(items, current, direction) {
|
|
23
|
+
const enabled = items.filter((item) => item.disabled !== true).map((item) => item.value);
|
|
24
|
+
if (enabled.length === 0) return current;
|
|
25
|
+
const [first] = enabled;
|
|
26
|
+
const last = enabled[enabled.length - 1];
|
|
27
|
+
if (first === void 0 || last === void 0) return current;
|
|
28
|
+
if (direction === "first") return first;
|
|
29
|
+
if (direction === "last") return last;
|
|
30
|
+
const index = enabled.indexOf(current);
|
|
31
|
+
if (index === -1) return direction === "next" ? first : last;
|
|
32
|
+
return enabled[(index + (direction === "next" ? 1 : -1) + enabled.length) % enabled.length] ?? current;
|
|
19
33
|
}
|
|
20
34
|
//#endregion
|
|
21
|
-
//#region src/
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
35
|
+
//#region src/toast/store.ts
|
|
36
|
+
/** Milisegundos que un toast permanece visible si no se indica otra cosa. */
|
|
37
|
+
const TOAST_DEFAULT_DURATION = 5e3;
|
|
38
|
+
/** Toasts visibles a la vez. Al llegar uno más, se retira el más antiguo. */
|
|
39
|
+
const TOAST_MAX_VISIBLE = 3;
|
|
40
|
+
/**
|
|
41
|
+
* La cola de toasts de un `UIProvider` (ADR-039): alta, baja, cierre automático, pausa
|
|
42
|
+
* y límite de visibles. No depende de React ni de la plataforma.
|
|
43
|
+
*/
|
|
44
|
+
function createToastStore() {
|
|
45
|
+
let items = [];
|
|
46
|
+
let paused = false;
|
|
47
|
+
let nextId = 0;
|
|
48
|
+
const timers = /* @__PURE__ */ new Map();
|
|
49
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
50
|
+
function emit(next) {
|
|
51
|
+
items = next;
|
|
52
|
+
for (const listener of listeners) listener();
|
|
53
|
+
}
|
|
54
|
+
function stop(id) {
|
|
55
|
+
const timer = timers.get(id);
|
|
56
|
+
if (timer?.handle !== void 0) clearTimeout(timer.handle);
|
|
57
|
+
timers.delete(id);
|
|
58
|
+
}
|
|
59
|
+
function start(id, remaining) {
|
|
60
|
+
const timer = {
|
|
61
|
+
handle: void 0,
|
|
62
|
+
startedAt: Date.now(),
|
|
63
|
+
remaining
|
|
64
|
+
};
|
|
65
|
+
if (!paused) timer.handle = setTimeout(() => dismiss(id), remaining);
|
|
66
|
+
timers.set(id, timer);
|
|
67
|
+
}
|
|
68
|
+
function dismiss(id) {
|
|
69
|
+
if (id === void 0) {
|
|
70
|
+
if (items.length === 0) return;
|
|
71
|
+
for (const item of items) stop(item.id);
|
|
72
|
+
emit([]);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (!items.some((item) => item.id === id)) return;
|
|
76
|
+
stop(id);
|
|
77
|
+
emit(items.filter((item) => item.id !== id));
|
|
78
|
+
}
|
|
79
|
+
function show(options) {
|
|
80
|
+
nextId += 1;
|
|
81
|
+
const item = {
|
|
82
|
+
id: `toast-${nextId}`,
|
|
83
|
+
tone: options.tone ?? "info",
|
|
84
|
+
title: options.title,
|
|
85
|
+
description: options.description,
|
|
86
|
+
duration: options.duration ?? 5e3,
|
|
87
|
+
action: options.action,
|
|
88
|
+
closeLabel: options.closeLabel
|
|
89
|
+
};
|
|
90
|
+
const next = [...items, item];
|
|
91
|
+
for (const old of next.splice(0, Math.max(0, next.length - 3))) stop(old.id);
|
|
92
|
+
if (item.duration > 0) start(item.id, item.duration);
|
|
93
|
+
emit(next);
|
|
94
|
+
return item.id;
|
|
95
|
+
}
|
|
96
|
+
function pause() {
|
|
97
|
+
if (paused) return;
|
|
98
|
+
paused = true;
|
|
99
|
+
const now = Date.now();
|
|
100
|
+
for (const timer of timers.values()) {
|
|
101
|
+
if (timer.handle !== void 0) clearTimeout(timer.handle);
|
|
102
|
+
timer.handle = void 0;
|
|
103
|
+
timer.remaining = Math.max(0, timer.remaining - (now - timer.startedAt));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function resume() {
|
|
107
|
+
if (!paused) return;
|
|
108
|
+
paused = false;
|
|
109
|
+
for (const [id, timer] of timers) start(id, timer.remaining);
|
|
31
110
|
}
|
|
32
|
-
return result;
|
|
33
|
-
}
|
|
34
|
-
/** Combina los overrides de una marca sobre un tema (ADR-010). Los arrays (sombras) se sustituyen enteros. */
|
|
35
|
-
function mergeTheme(base, overrides) {
|
|
36
|
-
return merge(base, overrides);
|
|
37
|
-
}
|
|
38
|
-
const cache = /* @__PURE__ */ new Map();
|
|
39
|
-
/** Tema resuelto para un esquema y una marca; memoizado para que la identidad sea estable. */
|
|
40
|
-
function resolveTheme(scheme, brand) {
|
|
41
|
-
const key = `${scheme}/${brand ?? ""}`;
|
|
42
|
-
const cached = cache.get(key);
|
|
43
|
-
if (cached) return cached;
|
|
44
|
-
const base = themes[scheme];
|
|
45
|
-
const overrides = brand === void 0 ? void 0 : brands[brand]?.[scheme];
|
|
46
|
-
const resolved = overrides === void 0 ? base : mergeTheme(base, overrides);
|
|
47
|
-
cache.set(key, resolved);
|
|
48
|
-
return resolved;
|
|
49
|
-
}
|
|
50
|
-
function resolveColorScheme(mode, systemScheme) {
|
|
51
|
-
return mode === "system" ? systemScheme : mode;
|
|
52
|
-
}
|
|
53
|
-
//#endregion
|
|
54
|
-
//#region src/theme/context.ts
|
|
55
|
-
function createUIContextValue(mode, systemScheme, brand) {
|
|
56
|
-
const colorScheme = resolveColorScheme(mode, systemScheme);
|
|
57
111
|
return {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
112
|
+
subscribe(listener) {
|
|
113
|
+
listeners.add(listener);
|
|
114
|
+
return () => {
|
|
115
|
+
listeners.delete(listener);
|
|
116
|
+
};
|
|
117
|
+
},
|
|
118
|
+
getSnapshot: () => items,
|
|
119
|
+
show,
|
|
120
|
+
dismiss,
|
|
121
|
+
pause,
|
|
122
|
+
resume
|
|
62
123
|
};
|
|
63
124
|
}
|
|
64
|
-
/** Sin `UIProvider` se usa el tema claro por defecto, para que los componentes funcionen aislados. */
|
|
65
|
-
const UIContext = createContext(createUIContextValue("light", "light"));
|
|
66
|
-
function useUIContext() {
|
|
67
|
-
return useContext(UIContext);
|
|
68
|
-
}
|
|
69
|
-
/** Tema resuelto (tokens con la marca aplicada). Imprescindible en las vistas nativas (ADR-008). */
|
|
70
|
-
function useTheme() {
|
|
71
|
-
return useContext(UIContext).theme;
|
|
72
|
-
}
|
|
73
|
-
/** `'light' | 'dark'` ya resuelto, también con `theme="system"`. */
|
|
74
|
-
function useColorScheme() {
|
|
75
|
-
return useContext(UIContext).colorScheme;
|
|
76
|
-
}
|
|
77
|
-
function useBrand() {
|
|
78
|
-
return useContext(UIContext).brand;
|
|
79
|
-
}
|
|
80
125
|
//#endregion
|
|
81
126
|
//#region src/types/badge.ts
|
|
82
127
|
/**
|
|
@@ -120,23 +165,120 @@ const buttonIconSize = {
|
|
|
120
165
|
lg: "md"
|
|
121
166
|
};
|
|
122
167
|
//#endregion
|
|
168
|
+
//#region src/types/card.ts
|
|
169
|
+
/**
|
|
170
|
+
* Variantes de `Card` (ADR-009): `outlined` se apoya en el borde; `elevated` añade
|
|
171
|
+
* sombra para separarse de lo que tiene debajo.
|
|
172
|
+
*/
|
|
173
|
+
const cardVariants = ["outlined", "elevated"];
|
|
174
|
+
//#endregion
|
|
175
|
+
//#region src/types/control.ts
|
|
176
|
+
/**
|
|
177
|
+
* Alturas de los controles interactivos de una línea (`Input`, `Select`). Coinciden
|
|
178
|
+
* con `size.control` de los tokens y con los tamaños de `Button`, para que un campo y
|
|
179
|
+
* un botón del mismo tamaño queden alineados en una fila.
|
|
180
|
+
*/
|
|
181
|
+
const controlSizes = [
|
|
182
|
+
"sm",
|
|
183
|
+
"md",
|
|
184
|
+
"lg"
|
|
185
|
+
];
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region src/types/divider.ts
|
|
188
|
+
/** Orientación de `Divider` (ADR-009). */
|
|
189
|
+
const dividerOrientations = ["horizontal", "vertical"];
|
|
190
|
+
//#endregion
|
|
191
|
+
//#region src/types/feedback.ts
|
|
192
|
+
/**
|
|
193
|
+
* Los cuatro significados de `color.feedback` en los tokens: éxito, aviso, error e
|
|
194
|
+
* información. Los comparten los componentes que comunican un estado.
|
|
195
|
+
*/
|
|
196
|
+
const feedbackTones = [
|
|
197
|
+
"success",
|
|
198
|
+
"warning",
|
|
199
|
+
"danger",
|
|
200
|
+
"info"
|
|
201
|
+
];
|
|
202
|
+
function isFeedbackTone(value) {
|
|
203
|
+
return feedbackTones.includes(value);
|
|
204
|
+
}
|
|
205
|
+
//#endregion
|
|
123
206
|
//#region src/types/icon.ts
|
|
124
207
|
const iconSizes = [
|
|
125
208
|
"sm",
|
|
126
209
|
"md",
|
|
127
|
-
"lg"
|
|
210
|
+
"lg",
|
|
211
|
+
"xl"
|
|
128
212
|
];
|
|
129
213
|
/** Tamaño en px/puntos de cada icono. */
|
|
130
214
|
const iconSizePx = {
|
|
131
215
|
sm: 16,
|
|
132
216
|
md: 20,
|
|
133
|
-
lg: 24
|
|
217
|
+
lg: 24,
|
|
218
|
+
xl: 32
|
|
219
|
+
};
|
|
220
|
+
//#endregion
|
|
221
|
+
//#region src/types/icon-button.ts
|
|
222
|
+
/**
|
|
223
|
+
* Tamaño del icono para cada tamaño de `IconButton`. Es un paso mayor que en
|
|
224
|
+
* `Button`, porque aquí el icono es todo el contenido.
|
|
225
|
+
*/
|
|
226
|
+
const iconButtonIconSize = {
|
|
227
|
+
sm: "sm",
|
|
228
|
+
md: "md",
|
|
229
|
+
lg: "lg"
|
|
134
230
|
};
|
|
135
231
|
//#endregion
|
|
232
|
+
//#region src/types/input.ts
|
|
233
|
+
/**
|
|
234
|
+
* Qué se escribe en un `Input` (ADR-009). Cada vista lo traduce a su plataforma:
|
|
235
|
+
* el atributo `type` y el teclado en web, y el teclado, las mayúsculas automáticas
|
|
236
|
+
* y el autocompletado en nativo.
|
|
237
|
+
*/
|
|
238
|
+
const inputTypes = [
|
|
239
|
+
"text",
|
|
240
|
+
"email",
|
|
241
|
+
"password",
|
|
242
|
+
"search",
|
|
243
|
+
"tel",
|
|
244
|
+
"url",
|
|
245
|
+
"number"
|
|
246
|
+
];
|
|
247
|
+
/** Tamaño de los iconos de un `Input` para cada tamaño de campo. */
|
|
248
|
+
const inputIconSize = {
|
|
249
|
+
sm: "sm",
|
|
250
|
+
md: "md",
|
|
251
|
+
lg: "md"
|
|
252
|
+
};
|
|
253
|
+
//#endregion
|
|
254
|
+
//#region src/types/link.ts
|
|
255
|
+
/**
|
|
256
|
+
* Cuándo se subraya un `Link` (ADR-009). `always` es lo correcto dentro de un párrafo:
|
|
257
|
+
* el color solo no basta para distinguir un enlace. `hover` lo subraya al pasar el
|
|
258
|
+
* puntero o enfocarlo en web, y mientras se pulsa en nativo.
|
|
259
|
+
*/
|
|
260
|
+
const linkUnderlines = ["always", "hover"];
|
|
261
|
+
//#endregion
|
|
262
|
+
//#region src/types/modal.ts
|
|
263
|
+
/** Las dos presentaciones de un diálogo. */
|
|
264
|
+
const modalPresentations = ["dialog", "sheet"];
|
|
265
|
+
//#endregion
|
|
266
|
+
//#region src/types/skeleton.ts
|
|
267
|
+
/**
|
|
268
|
+
* Formas de `Skeleton` (ADR-009): `text` ocupa el hueco de una línea de `Text`,
|
|
269
|
+
* `rectangle` el de una imagen o un control, y `circle` el de un avatar.
|
|
270
|
+
*/
|
|
271
|
+
const skeletonShapes = [
|
|
272
|
+
"text",
|
|
273
|
+
"rectangle",
|
|
274
|
+
"circle"
|
|
275
|
+
];
|
|
276
|
+
//#endregion
|
|
136
277
|
//#region src/types/spinner.ts
|
|
137
278
|
/**
|
|
138
279
|
* `Spinner` comparte la escala de `Icon`: así puede ocupar el sitio de un icono
|
|
139
280
|
* (por ejemplo, dentro de un botón que carga) sin mover el resto del contenido.
|
|
281
|
+
* `xl` es para la carga de una página o de una sección entera.
|
|
140
282
|
*/
|
|
141
283
|
const spinnerSizes = iconSizes;
|
|
142
284
|
/** Tamaño en px/puntos de cada spinner. */
|
|
@@ -246,4 +388,4 @@ const themeModes = [
|
|
|
246
388
|
"system"
|
|
247
389
|
];
|
|
248
390
|
//#endregion
|
|
249
|
-
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.1
|
|
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": {
|