limbo-component 3.5.0 → 3.5.2

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 CHANGED
@@ -1,25 +1,36 @@
1
- # limbo-component
1
+ # Limbo Component
2
2
 
3
- > Componente React altamente configurable para gestión de imágenes en portales web. Incluye galería, subida, recortador, y servicios de IA/Stock.
3
+ > Componente React embebible para gestión completa de activos digitales (DAM). Soporta imágenes, vídeos, audio, documentos PDF, Office y texto. Incluye galería, subida, recortador, visualizadores, generación con IA, búsqueda en Stock y gestión de carpetas.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/limbo-component.svg)](https://www.npmjs.com/package/limbo-component)
6
- [![License: Propietary](https://img.shields.io/badge/License-propietary-blue.svg)](https://opensource.org/licenses/ISC)
7
-
8
- ## 🌟 Características
9
-
10
- - 🖼️ **Galería completa**: Navega y selecciona imágenes del portal o portales externos
11
- - 📤 **Subida de imágenes**: Con preview, validación y persistencia de estado
12
- - ✂️ **Recortador avanzado**: Basado en Cropper.js 2.0 con presets y recortes obligatorios
13
- - 🤖 **Integración IA/Stock**: Generación y búsqueda de imágenes con Atenea
14
- - 🎭 **Múltiples modos**: embed, modal, button, gallery-only, crop-only, upload-only, ia-only
15
- - 🎯 **Altamente configurable**: Por portal, instancia, dataset HTML o JS
16
- - 🔐 **Seguro**: Autenticación JWT con 3 modos (session, manual, jwt)
17
- - 📦 **Multi-formato**: ESM, CJS, UMD (con React 19 incluido)
18
- - 🌍 **Internacionalización**: ES/EN incluidos
19
- - **Accesible**: ARIA, teclado, focus trap
20
- - 📱 **Responsive**: Mobile-friendly con container queries
21
-
22
- ## 📦 Instalación
6
+ [![License: Proprietary](https://img.shields.io/badge/License-Proprietary-blue.svg)](https://en.wikipedia.org/wiki/Proprietary_software)
7
+
8
+ ---
9
+
10
+ ## Características
11
+
12
+ - **Galería multi-tipo** Navega imágenes, vídeos, audio, documentos y texto del portal
13
+ - **Subida de archivos** Drag & drop, validación por tipo/tamaño, preview por formato
14
+ - **Recortador avanzado** Cropper.js 2.0 con presets, recortes obligatorios y libres
15
+ - **Visualizadores nativos** ImageViewer (zoom/pan), VideoViewer, AudioViewer, PdfViewer, DocumentViewer, TextViewer
16
+ - **Generación con IA** DALL-E, Midjourney y más a través de Atenea
17
+ - **Stock de imágenes** Shutterstock y otros proveedores
18
+ - **Portales externos** Acceso a activos de otros portales Limbo
19
+ - **Sistema de carpetas** Organización, filtrado y movimiento de activos entre carpetas
20
+ - **Filtro `accept`** Control granular de tipos de archivo permitidos por instancia
21
+ - **GIF inteligente** — Preserva animación original, hover-to-animate en galería
22
+ - **Múltiples modos** — `embed`, `modal`, `button` · `full`, `gallery-only`, `upload-only`, `crop-only`, `ia-only`
23
+ - **AutoInputs** — Integración automática con formularios HTML existentes
24
+ - **Métodos prefab** — `openGallery()`, `openUploader()`, `openCropper()` y más
25
+ - **Autenticación JWT** — 3 modos: session, manual, tokenProvider
26
+ - **Multi-formato** — ESM, CJS, UMD (con React 19 incluido en UMD)
27
+ - **CSS aislado** — Prefijo `lb:` evita colisiones con estilos del portal
28
+ - **Accesible** — ARIA, navegación por teclado, focus trap, screen readers
29
+ - **Responsive** — Mobile-friendly con container queries
30
+
31
+ ---
32
+
33
+ ## Instalación
23
34
 
24
35
  ### NPM / Yarn / PNPM
25
36
 
@@ -31,50 +42,56 @@ yarn add limbo-component
31
42
  pnpm add limbo-component
32
43
  ```
33
44
 
34
- ### CDN (UMD)
45
+ ### CDN (UMD — React incluido)
35
46
 
36
47
  ```html
37
48
  <!-- CSS -->
38
- <link rel="stylesheet" href="https://limbo.lefebvre.es/cdn/component-limbo/latest/limbo.css" />
49
+ <link
50
+ rel="stylesheet"
51
+ href="https://limbo.lefebvre.es/cdn/component-limbo/latest/limbo.css"
52
+ />
39
53
 
40
- <!-- JS (React 19 incluido) -->
54
+ <!-- JS -->
41
55
  <script src="https://limbo.lefebvre.es/cdn/component-limbo/latest/limbo.min.js"></script>
42
56
  ```
43
57
 
44
- ## 🔐 Modos de Autenticación
58
+ ---
59
+
60
+ ## Autenticación
45
61
 
46
62
  El componente soporta 3 modos de autenticación:
47
63
 
48
64
  ```javascript
49
- // MODO 1: Session (producción con cookies)
50
- // El token se obtiene automáticamente usando la sesión del usuario
65
+ // MODO 1: JWT con tokenProvider (RECOMENDADO para producción)
66
+ // Tu backend genera el token y lo expone en un endpoint propio
51
67
  Limbo.configure({
52
68
  publicKey: "pk_tu_clave_publica",
53
- authMode: "session",
69
+ authMode: "jwt",
70
+ tokenProvider: async () => {
71
+ const res = await fetch("/api/limbo-token");
72
+ return (await res.json()).token;
73
+ },
54
74
  });
55
75
 
56
76
  // MODO 2: Manual (token proporcionado directamente)
57
- // Útil cuando el backend ya generó el token
58
77
  Limbo.configure({
59
78
  publicKey: "pk_tu_clave_publica",
60
- token: "eyJ0eXAiOiJKV1Q...",
61
79
  authMode: "manual",
80
+ token: "eyJ0eXAiOiJKV1Q...",
62
81
  });
63
82
 
64
- // MODO 3: JWT con tokenProvider (recomendado)
65
- // Proporciona una función async para renovación automática
83
+ // MODO 3: Session (usa cookies de sesión)
66
84
  Limbo.configure({
67
85
  publicKey: "pk_tu_clave_publica",
68
- authMode: "jwt",
69
- tokenProvider: async () => {
70
- const response = await fetch("/api/limbo-token");
71
- const data = await response.json();
72
- return data.token;
73
- },
86
+ authMode: "session",
74
87
  });
75
88
  ```
76
89
 
77
- ## 🚀 Uso Rápido
90
+ > **Seguridad**: En producción, usa siempre `tokenProvider` o `manual`. Nunca expongas la API Key (`sk_...`) en código cliente.
91
+
92
+ ---
93
+
94
+ ## Uso Rápido
78
95
 
79
96
  ### Modo Embed (ESM)
80
97
 
@@ -82,9 +99,9 @@ Limbo.configure({
82
99
  import Limbo from "limbo-component";
83
100
  import "limbo-component/css";
84
101
 
85
- // Configuración global
86
102
  Limbo.configure({
87
103
  publicKey: "pk_tu_clave_publica",
104
+ prod: true,
88
105
  authMode: "jwt",
89
106
  tokenProvider: async () => {
90
107
  const res = await fetch("/api/limbo-token");
@@ -92,14 +109,15 @@ Limbo.configure({
92
109
  },
93
110
  });
94
111
 
95
- // Crear instancia embebida
112
+ // Selector completo: galería + subida + recortador
96
113
  Limbo.create({
97
114
  container: "#limbo-container",
98
115
  mode: "embed",
99
- modeUI: "full", // 'full' | 'gallery-only' | 'upload-only' | 'crop-only' | 'ia-only'
116
+ modeUI: "full",
100
117
  callbacks: {
101
118
  onSelect: (data) => console.log("Seleccionado:", data),
102
119
  onUpload: (data) => console.log("Subido:", data),
120
+ onCropsSaved: (data) => console.log("Recortes:", data),
103
121
  },
104
122
  });
105
123
  ```
@@ -110,20 +128,22 @@ Limbo.create({
110
128
  <!DOCTYPE html>
111
129
  <html>
112
130
  <head>
113
- <link rel="stylesheet" href="https://limbo.lefebvre.es/cdn/component-limbo/latest/limbo.css" />
131
+ <link
132
+ rel="stylesheet"
133
+ href="https://limbo.lefebvre.es/cdn/component-limbo/latest/limbo.css"
134
+ />
114
135
  </head>
115
136
  <body>
116
137
  <div id="limbo-app"></div>
117
138
 
118
- <script src="https://limbo.lefebvre.es/cdn/component-limbo/latest/limbo.umd.js"></script>
139
+ <script src="https://limbo.lefebvre.es/cdn/component-limbo/latest/limbo.min.js"></script>
119
140
  <script>
120
- // Configurar
121
141
  Limbo.configure({
122
142
  publicKey: "pk_tu_clave_publica",
143
+ prod: true,
123
144
  authMode: "session",
124
145
  });
125
146
 
126
- // Crear instancia
127
147
  Limbo.create({
128
148
  container: "#limbo-app",
129
149
  mode: "embed",
@@ -134,153 +154,664 @@ Limbo.create({
134
154
  </html>
135
155
  ```
136
156
 
137
- ### AutoInputs (Integración automática con formularios)
157
+ ---
158
+
159
+ ## Filtro `accept` — Control de tipos de archivo
160
+
161
+ El parámetro `accept` permite controlar qué tipos de archivo puede seleccionar o subir el usuario. Usa la misma sintaxis que el atributo HTML `<input accept>`:
162
+
163
+ | Valor | Descripción |
164
+ | -------------------- | --------------------------------------------------- |
165
+ | `"image/*"` | Todas las imágenes (JPEG, PNG, GIF, WebP, SVG) |
166
+ | `"video/*"` | Todos los vídeos (MP4, WebM, MOV, AVI) |
167
+ | `"audio/*"` | Todo el audio (MP3, WAV, OGG, AAC, FLAC) |
168
+ | `"document/*"` | Todos los documentos (PDF, DOC, XLS, PPT, TXT, CSV) |
169
+ | `".pdf"` | Solo archivos PDF |
170
+ | `".pdf,.docx,.xlsx"` | Solo esos formatos específicos |
171
+ | `"image/*,.pdf"` | Imágenes + PDFs |
172
+ | `null` / omitido | Todo permitido (por defecto) |
173
+
174
+ ### Adaptación automática de la UI
175
+
176
+ Cuando las imágenes están excluidas del `accept`, el componente adapta la interfaz automáticamente:
177
+
178
+ | Elemento | Con imágenes | Sin imágenes |
179
+ | --------------------- | ------------ | --------------------------------- |
180
+ | Tab "Generar con IA" | Visible | **Oculta** (solo genera imágenes) |
181
+ | Tab "Buscar en Stock" | Visible | **Oculta** (solo busca imágenes) |
182
+ | Tab "Otros portales" | Visible | **Oculta** |
183
+ | Recortador (Cropper) | Activo | **Desactivado** |
184
+ | Tab "Subir archivo" | Visible | Visible (filtrado por accept) |
185
+ | Galería | Muestra todo | Filtrada por tipos permitidos |
186
+
187
+ ### Ejemplos
188
+
189
+ ```javascript
190
+ // Selector de imágenes — UI completa (IA, Stock, Portales, Cropper)
191
+ Limbo.openGallery({
192
+ accept: "image/*",
193
+ onSelect: (img) => console.log("Imagen:", img),
194
+ });
195
+
196
+ // Selector de PDFs — Solo galería + subida, sin Cropper/IA/Stock
197
+ Limbo.openGallery({
198
+ accept: ".pdf",
199
+ onSelect: (doc) => console.log("PDF:", doc),
200
+ });
201
+
202
+ // Selector de documentos Office
203
+ Limbo.openUploader({
204
+ accept: ".pdf,.docx,.xlsx,.pptx",
205
+ onUpload: (doc) => console.log("Documento:", doc),
206
+ });
207
+
208
+ // Selector mixto: imágenes + PDFs
209
+ Limbo.createFullSelector("#file-picker", {
210
+ accept: "image/*,.pdf",
211
+ onSelect: (asset) => console.log("Archivo:", asset),
212
+ });
213
+
214
+ // Todo permitido (por defecto)
215
+ Limbo.openGallery({
216
+ onSelect: (asset) => console.log("Cualquier tipo:", asset),
217
+ });
218
+ ```
219
+
220
+ ### Con AutoInputs
138
221
 
139
222
  ```html
140
- <script>
141
- // Configuración global
142
- Limbo.configure({
143
- publicKey: "pk_tu_clave_publica",
144
- authMode: "session",
145
- });
146
-
147
- // Configurar AutoInputs
148
- Limbo.configureAutoInputs({
149
- selector: ".js-limbo", // Selector CSS para inputs
150
- return: "url", // 'url' | 'json' | 'assetId' | 'base64'
151
- mode: "modal",
152
- modeUI: "full",
153
- buttonText: "Seleccionar imagen",
154
- showPreview: true,
155
- });
156
- </script>
157
-
158
- <!-- Inputs se transforman automáticamente -->
159
- <input type="text" class="js-limbo" name="avatar" />
160
-
161
- <!-- Con recortes obligatorios via data attribute -->
223
+ <!-- Selector de imágenes -->
224
+ <input data-limbo-input-file data-accept="image/*" name="avatar" />
225
+
226
+ <!-- Solo PDFs -->
162
227
  <input
163
- type="text"
164
- class="js-limbo"
165
- name="banner"
166
- data-mandatorycrops='[{"label":"Desktop","width":1920,"height":400},{"label":"Mobile","width":640,"height":300}]'
228
+ data-limbo-input-file
229
+ data-accept=".pdf"
230
+ data-returntype="url"
231
+ name="contrato"
167
232
  />
168
233
 
169
- <!-- Solo galería -->
170
- <input type="text" class="js-limbo" name="imagen" data-modeui="gallery-only" />
234
+ <!-- Imágenes + PDFs -->
235
+ <input data-limbo-input-file data-accept="image/*,.pdf" name="adjunto" />
236
+
237
+ <!-- Vídeos solamente -->
238
+ <input data-limbo-input-file data-accept="video/*" name="video_promo" />
239
+
240
+ <!-- Todo permitido (sin data-accept) -->
241
+ <input data-limbo-input-file name="archivo_generico" />
242
+ ```
243
+
244
+ ### En la API
245
+
246
+ La galería envía el filtro automáticamente al backend:
247
+
248
+ ```txt
249
+ GET /api/assets?accept=image/*,.pdf
171
250
  ```
172
251
 
173
- ## ⚙️ Configuración Completa
252
+ El backend resuelve las extensiones y categorías y filtra por `originalMime`.
253
+
254
+ ---
255
+
256
+ ## AutoInputs — Integración automática con formularios
174
257
 
175
- ### Limbo.configure() - Opciones Globales
258
+ El sistema AutoInputs transforma inputs HTML existentes en selectores de archivos Limbo. Detecta automáticamente inputs con el atributo configurado, oculta el input original y añade un botón estilizado.
259
+
260
+ ### Configuración global
176
261
 
177
262
  ```javascript
178
263
  Limbo.configure({
179
- // Autenticación
180
- publicKey: "pk_...", // Clave pública del portal (requerida)
181
- token: null, // Token JWT (solo para authMode: 'manual')
182
- authMode: "session", // 'session' | 'manual' | 'jwt'
183
- tokenProvider: null, // Función async para obtener token
264
+ publicKey: "pk_tu_clave_publica",
265
+ prod: true,
266
+ authMode: "session",
267
+ });
184
268
 
185
- // URLs
186
- prod: false, // true para producción (limbo.lefebvre.es)
269
+ Limbo.configureAutoInputs({
270
+ // Selector puede ser dataset o selector CSS
271
+ dataset: "data-limbo-input-file", // por defecto
272
+ // selector: ".js-limbo", // alternativa: selector CSS directo
187
273
 
188
- // Callbacks globales
189
- callbacks: {
190
- onUpload: (data) => {},
191
- onSelect: (data) => {},
192
- onDelete: (assetId) => {},
193
- onCropsSaved: (data) => {},
194
- onError: (error) => {},
274
+ // Formato de retorno del valor al input
275
+ return: "url", // "url" | "json" | "assetId" | "base64" | "object" | "fileName"
276
+
277
+ // Modo y UI
278
+ mode: "modal",
279
+ modeUI: "full",
280
+ modalSize: "fullscreen",
281
+
282
+ // Botón
283
+ buttonText: "Seleccionar archivo",
284
+ buttonStyle: "primary", // "primary" | "secondary" | "outline" | "ghost"
285
+ buttonClass: "limbo-auto-button",
286
+
287
+ // Comportamiento
288
+ showPreview: true,
289
+ autoAssign: true, // Asignar valor automáticamente al input
290
+ });
291
+ ```
292
+
293
+ ### Configuración por input via `data-*`
294
+
295
+ Cada input puede sobreescribir la configuración global usando atributos:
296
+
297
+ ```html
298
+ <!-- Ejemplo básico -->
299
+ <input
300
+ type="hidden"
301
+ name="avatar"
302
+ data-limbo-input-file
303
+ data-returntype="url"
304
+ />
305
+
306
+ <!-- Configuración completa por input -->
307
+ <input
308
+ type="hidden"
309
+ name="banner"
310
+ data-limbo-input-file
311
+ data-returntype="json"
312
+ data-accept="image/*"
313
+ data-modeui="full"
314
+ data-crop="16:9"
315
+ data-quality="0.85"
316
+ data-format="webp"
317
+ data-limbo-max-size="5MB"
318
+ data-limbo-button-text="Elegir banner"
319
+ data-limbo-button-style="outline"
320
+ data-mandatorycrops='[
321
+ {"label":"Desktop","width":1920,"height":400,"required":true},
322
+ {"label":"Mobile","width":640,"height":300,"required":true}
323
+ ]'
324
+ />
325
+
326
+ <!-- Solo galería, devuelve ID del asset -->
327
+ <input
328
+ type="hidden"
329
+ name="imagen_existente"
330
+ data-limbo-input-file
331
+ data-modeui="gallery-only"
332
+ data-returntype="assetId"
333
+ />
334
+
335
+ <!-- Solo PDFs, devuelve URL -->
336
+ <input
337
+ type="hidden"
338
+ name="contrato_pdf"
339
+ data-limbo-input-file
340
+ data-accept=".pdf"
341
+ data-returntype="url"
342
+ data-limbo-button-text="Seleccionar PDF"
343
+ />
344
+
345
+ <!-- Varios inputs diferentes en la misma página -->
346
+ <input data-limbo-input-file data-accept="image/*" name="hero" />
347
+ <input data-limbo-input-file data-accept=".pdf" name="terms" />
348
+ <input data-limbo-input-file data-accept="video/*" name="promo" />
349
+ ```
350
+
351
+ ### Atributos `data-*` soportados
352
+
353
+ | Atributo | Valores | Defecto | Descripción |
354
+ | ----------------------------------- | -------------------------------------------------------- | ------------------------ | ---------------------------------------------- |
355
+ | `data-returntype` | `url`, `json`, `assetId`, `base64`, `object`, `fileName` | `json` | Formato del valor devuelto al input |
356
+ | `data-accept` | Sintaxis HTML accept | `null` (todo) | Filtro de tipos de archivo |
357
+ | `data-modeui` | `full`, `gallery-only`, `upload-only`, `crop-only` | `full` | Modo funcional de la UI |
358
+ | `data-mode` | `modal`, `embed` | `modal` | Modo de renderizado |
359
+ | `data-features` | `gallery,upload,cropper` | `gallery,upload,cropper` | Features habilitadas (separadas por coma) |
360
+ | `data-crop` | `free`, `1:1`, `16:9`, `4:3`, `none` | `free` | Ratio de recorte |
361
+ | `data-quality` | `0.0` - `1.0` | `0.9` | Calidad de salida |
362
+ | `data-format` | `webp`, `jpg`, `png` | `webp` | Formato de salida |
363
+ | `data-modalsize` | `small`, `medium`, `large`, `fullscreen` | `fullscreen` | Tamaño del modal |
364
+ | `data-theme` | `light`, `dark` | `light` | Tema visual |
365
+ | `data-compact` | `true`, `false` | `false` | Modo compacto |
366
+ | `data-limbo-max-size` | `5MB`, `1024KB` | `10MB` | Tamaño máximo de archivo |
367
+ | `data-limbo-formats` | `jpg,png,webp` | `jpg,jpeg,png,webp` | Formatos permitidos (legacy) |
368
+ | `data-limbo-min-width` | Píxeles | `null` | Ancho mínimo de imagen |
369
+ | `data-limbo-min-height` | Píxeles | `null` | Alto mínimo de imagen |
370
+ | `data-limbo-max-width` | Píxeles | `null` | Ancho máximo de imagen |
371
+ | `data-limbo-max-height` | Píxeles | `null` | Alto máximo de imagen |
372
+ | `data-limbo-button-text` | Texto | `Seleccionar archivo` | Texto del botón |
373
+ | `data-limbo-button-style` | `primary`, `secondary`, `outline`, `ghost` | `primary` | Estilo del botón |
374
+ | `data-limbo-button-class` | Clase CSS | `limbo-auto-button` | Clase CSS adicional del botón |
375
+ | `data-limbo-show-preview` | `true`, `false` | `true` | Mostrar preview al seleccionar |
376
+ | `data-limbo-auto-assign` | `true`, `false` | `true` | Asignar valor al input automáticamente |
377
+ | `data-limbo-required` | `true`, `false` | `false` | Campo requerido |
378
+ | `data-limbo-disabled` | `true`, `false` | `false` | Input deshabilitado |
379
+ | `data-mandatorycrops` | JSON array | `null` | Recortes obligatorios |
380
+ | `data-limbo-allow-additional-crops` | `true`, `false` | `true` | Permitir recortes adicionales |
381
+ | `data-limbo-max-crops` | Número | `null` | Máximo de recortes seleccionables |
382
+ | `data-limbo-allow-select-new` | `true`, `false` | `true` | Permitir seleccionar nueva imagen en crop-only |
383
+
384
+ ### Eventos DOM de AutoInputs
385
+
386
+ ```javascript
387
+ // Resultado listo — se dispara en el input cuando se completa la selección
388
+ document.addEventListener("limbo:resultReady", (e) => {
389
+ const { input, value, imageData, config } = e.detail;
390
+ console.log(`Input "${input.name}" recibió:`, value);
391
+ });
392
+
393
+ // Compatibilidad con evento anterior
394
+ document.addEventListener("limbo:imageSelected", (e) => {
395
+ const { input, value, imageData } = e.detail;
396
+ });
397
+
398
+ // Errores
399
+ document.addEventListener("limbo:error", (e) => {
400
+ console.error("Error en input:", e.detail.error);
401
+ });
402
+ ```
403
+
404
+ ### Re-escaneo dinámico
405
+
406
+ Si añades inputs al DOM dinámicamente (SPA, AJAX), el MutationObserver los detecta automáticamente. También puedes forzar un re-escaneo:
407
+
408
+ ```javascript
409
+ // Re-escanear todo el documento
410
+ Limbo.rescan();
411
+
412
+ // Re-escanear un contenedor específico
413
+ Limbo.rescan(document.getElementById("formulario-dinamico"));
414
+
415
+ // Escanear y activar con selector personalizado
416
+ Limbo.scanAndActivate(".js-limbo-new");
417
+ ```
418
+
419
+ ---
420
+
421
+ ## Métodos Prefab
422
+
423
+ Métodos de conveniencia para los casos de uso más comunes. Todos aceptan el parámetro `accept` para filtrar tipos.
424
+
425
+ ### Modales
426
+
427
+ #### `Limbo.openGallery(options)`
428
+
429
+ Abre la galería en modal para seleccionar un archivo existente.
430
+
431
+ ```javascript
432
+ // Galería de imágenes
433
+ Limbo.openGallery({
434
+ accept: "image/*",
435
+ onSelect: (data) => {
436
+ document.getElementById("preview").src = data.url;
437
+ },
438
+ });
439
+
440
+ // Galería de PDFs
441
+ Limbo.openGallery({
442
+ accept: ".pdf",
443
+ title: "Seleccionar documento",
444
+ onSelect: (data) => {
445
+ document.getElementById("pdf-link").href = data.url;
446
+ },
447
+ });
448
+
449
+ // Galería de cualquier tipo
450
+ Limbo.openGallery({
451
+ onSelect: (data) => console.log("Seleccionado:", data),
452
+ autoClose: true, // Cerrar modal al seleccionar (por defecto: true)
453
+ size: "large", // "small" | "medium" | "large" | "fullscreen"
454
+ });
455
+ ```
456
+
457
+ #### `Limbo.openUploader(options)`
458
+
459
+ Abre el uploader en modal para subir un archivo nuevo.
460
+
461
+ ```javascript
462
+ // Subir imagen y navegar a galería tras subida
463
+ Limbo.openUploader({
464
+ accept: "image/*",
465
+ redirectToGallery: true, // Muestra galería tras subida (por defecto: true)
466
+ onUpload: (data) => console.log("Subido:", data),
467
+ });
468
+
469
+ // Subir documento y cerrar inmediatamente
470
+ Limbo.openUploader({
471
+ accept: ".pdf,.docx",
472
+ redirectToGallery: false,
473
+ onUpload: (data) => {
474
+ console.log("Documento subido:", data);
195
475
  },
196
476
  });
197
477
  ```
198
478
 
199
- ### Limbo.create() - Opciones de Instancia
479
+ #### `Limbo.openCropper(imageUrl, options)`
480
+
481
+ Abre el recortador con una imagen externa (URL) para crear recortes.
482
+
483
+ ```javascript
484
+ // Recortar imagen con dimensiones obligatorias
485
+ Limbo.openCropper("https://example.com/foto.jpg", {
486
+ mandatoryCrops: [
487
+ { label: "Desktop", width: 1920, height: 400, required: true },
488
+ { label: "Mobile", width: 640, height: 300, required: true },
489
+ { label: "Thumbnail", width: 300, height: 300, required: true },
490
+ ],
491
+ onComplete: (data) => {
492
+ console.log("Recortes creados:", data.crops);
493
+ data.crops.forEach((crop) => {
494
+ console.log(`${crop.name}: ${crop.url} (${crop.width}x${crop.height})`);
495
+ });
496
+ },
497
+ onCancelled: () => console.log("Cancelado por el usuario"),
498
+ onError: (err) => console.error("Error:", err),
499
+ });
500
+
501
+ // Recorte libre
502
+ Limbo.openCropper("https://example.com/banner.jpg", {
503
+ allowCustomCrops: true,
504
+ onComplete: (data) => console.log("Recortes:", data.crops),
505
+ });
506
+ ```
507
+
508
+ ### Embebidos
509
+
510
+ #### `Limbo.createInlineGallery(container, options)`
511
+
512
+ Galería permanente embebida en un contenedor.
513
+
514
+ ```javascript
515
+ // Galería embebida de imágenes
516
+ const gallery = Limbo.createInlineGallery("#gallery-container", {
517
+ accept: "image/*",
518
+ onSelect: (data) => {
519
+ document.getElementById("selected-image").src = data.url;
520
+ },
521
+ onDelete: (assetId) => console.log("Eliminado:", assetId),
522
+ });
523
+ ```
524
+
525
+ #### `Limbo.createInlineUploader(container, options)`
526
+
527
+ Formulario de subida permanente embebido.
528
+
529
+ ```javascript
530
+ // Uploader embebido solo para PDFs
531
+ const uploader = Limbo.createInlineUploader("#upload-area", {
532
+ accept: ".pdf",
533
+ onUpload: (data) => console.log("PDF subido:", data),
534
+ });
535
+ ```
536
+
537
+ #### `Limbo.createStandaloneCropper(container, imageUrl, options)`
538
+
539
+ Recortador embebido para una imagen específica.
540
+
541
+ ```javascript
542
+ // Recortador embebido con recortes obligatorios
543
+ const cropper = Limbo.createStandaloneCropper(
544
+ "#cropper-container",
545
+ "https://example.com/imagen.jpg",
546
+ {
547
+ mandatoryCrops: [
548
+ { label: "Avatar", width: 200, height: 200, required: true },
549
+ ],
550
+ autoHideOnComplete: true,
551
+ onComplete: (data) => {
552
+ fetch("/api/save-avatar", {
553
+ method: "POST",
554
+ body: JSON.stringify({ url: data.crops[0].url }),
555
+ });
556
+ },
557
+ },
558
+ );
559
+ ```
560
+
561
+ #### `Limbo.createFullSelector(container, options)`
562
+
563
+ Selector completo con galería + subida + recortador.
564
+
565
+ ```javascript
566
+ // Selector completo embebido
567
+ const selector = Limbo.createFullSelector("#limbo-full", {
568
+ accept: "image/*,.pdf",
569
+ onSelect: (data) => console.log("Seleccionado:", data),
570
+ onUpload: (data) => console.log("Subido:", data),
571
+ onCropsSaved: (data) => console.log("Recortes:", data),
572
+ });
573
+ ```
574
+
575
+ ---
576
+
577
+ ## Configuración de Instancias
578
+
579
+ ### `Limbo.create(options)` — Referencia completa
200
580
 
201
581
  ```javascript
202
582
  Limbo.create({
203
- // Contenedor (requerido)
583
+ // ── Contenedor (requerido para modo embed) ──
204
584
  container: "#limbo-gallery", // Selector CSS o elemento DOM
205
585
 
206
- // Modo de renderizado
207
- mode: "embed", // 'embed' | 'modal' | 'button'
586
+ // ── Modo de renderizado ──
587
+ mode: "embed", // "embed" | "modal" | "button"
208
588
 
209
- // Modo funcional
210
- modeUI: "full", // 'full' | 'gallery-only' | 'upload-only' | 'crop-only' | 'ia-only'
589
+ // ── Modo funcional ──
590
+ modeUI: "full", // "full" | "gallery-only" | "upload-only" | "crop-only" | "ia-only"
211
591
 
212
- // Features habilitadas
592
+ // ── Features habilitadas ──
213
593
  features: ["gallery", "upload", "cropper"], // Tabs visibles
214
594
 
215
- // Configuración UI
595
+ // ── Validación de archivos ──
596
+ validation: {
597
+ accept: null, // Filtro accept: "image/*", ".pdf", etc. (null = todo)
598
+ maxSize: "10MB", // Tamaño máximo por archivo
599
+ allowedCategories: null, // (legacy) ["image", "video", "document", "audio"]
600
+ },
601
+
602
+ // ── Configuración de galería ──
603
+ gallery: {
604
+ filters: {
605
+ showNameFilter: true,
606
+ showDateFilter: true,
607
+ showTypeFilter: false, // Dropdown de tipo de archivo
608
+ showUploadedByFilter: false,
609
+ },
610
+ loading: {
611
+ showPlaceholders: true,
612
+ placeholderCount: 10,
613
+ },
614
+ pagination: {
615
+ itemsPerPage: 20,
616
+ },
617
+ },
618
+
619
+ // ── Configuración de carpetas ──
620
+ folders: {
621
+ showFolderFilter: true, // Filtro de carpeta en galería
622
+ showFolderSelector: true, // Selector de carpeta en subida
623
+ showFolderInfo: true, // Badge de carpeta en tarjetas
624
+ allowMoveFolder: true, // Permitir mover archivos entre carpetas
625
+ allowCreateFolder: false, // Permitir crear carpetas nuevas
626
+ include: [], // Whitelist de slugs de carpeta
627
+ exclude: [], // Blacklist de slugs de carpeta
628
+ defaultFolder: null, // Carpeta por defecto para subidas
629
+ },
630
+
631
+ // ── Acciones permitidas ──
632
+ allowedActions: {
633
+ select: true,
634
+ download: true,
635
+ copy: true,
636
+ delete: true,
637
+ crop: true,
638
+ variants: true,
639
+ },
640
+
641
+ // ── Recortador ──
642
+ cropper: {
643
+ mandatoryCrops: [],
644
+ allowCustomCrops: true,
645
+ maxCrops: null,
646
+ quality: 0.9,
647
+ format: "webp",
648
+ },
649
+
650
+ // ── Modal (solo para mode: "modal") ──
651
+ modal: {
652
+ size: "fullscreen", // "small" | "medium" | "large" | "xlarge" | "fullscreen"
653
+ title: "Limbo - Gestor de Archivos",
654
+ closeOnEscape: true,
655
+ closeOnBackdrop: true,
656
+ },
657
+
658
+ // ── UI ──
216
659
  ui: {
217
- showActions: ["select", "download", "copy", "delete", "crop", "variants"],
218
- hideActions: [],
219
- theme: "light", // 'light' | 'dark'
660
+ theme: "light", // "light" | "dark"
220
661
  language: "es",
221
662
  compactMode: false,
222
- showTabs: true,
223
663
  },
224
664
 
225
- // Recortes obligatorios
226
- cropperConfig: {
227
- mandatoryCrops: [
228
- { label: "Desktop", width: 1920, height: 400, required: true },
229
- { label: "Mobile", width: 640, height: 300, required: true },
230
- ],
231
- allowCustomCrops: false,
665
+ // ── Callbacks ──
666
+ callbacks: {
667
+ onSelect: (data) => {}, // Selección de archivo desde galería
668
+ onUpload: (data) => {}, // Archivo subido
669
+ onCropsSaved: (data) => {}, // Recortes guardados
670
+ onCropperComplete: (data) => {}, // Cropper standalone completado
671
+ onCropperCancelled: () => {}, // Cropper cancelado
672
+ onCropperError: (error) => {}, // Error en cropper
673
+ onDelete: (assetId) => {}, // Archivo eliminado
674
+ onClose: () => {}, // Modal cerrado
675
+ onError: (error) => {}, // Error general
232
676
  },
677
+ });
678
+ ```
233
679
 
234
- // Modal
235
- modalSize: "fullscreen", // 'small' | 'medium' | 'large' | 'fullscreen'
236
- buttonText: "Seleccionar imagen",
680
+ ---
237
681
 
238
- // Paginación
239
- itemsPerPage: 20,
682
+ ## Sistema de Carpetas
240
683
 
241
- // Callbacks
242
- callbacks: {
243
- onSelect: (data) => {
244
- // data: { assetId, url, fileName, mime, width, height, images, original }
245
- },
246
- onUpload: (data) => {},
247
- onCropsSaved: (data) => {},
248
- onClose: () => {},
684
+ El componente permite organizar archivos en carpetas configurables por portal.
685
+
686
+ ### En la galería
687
+
688
+ ```javascript
689
+ Limbo.create({
690
+ container: "#limbo",
691
+ mode: "embed",
692
+ modeUI: "full",
693
+ folders: {
694
+ showFolderFilter: true, // Dropdown de carpeta en filtros de galería
695
+ showFolderInfo: true, // Badge con nombre de carpeta en cada tarjeta
696
+ allowMoveFolder: true, // Acción de mover archivo a otra carpeta
697
+ allowCreateFolder: true, // Permitir crear carpetas al mover
698
+ include: ["productos", "marketing"], // Solo mostrar estas carpetas
699
+ // exclude: ["privado"], // O excluir estas
249
700
  },
250
701
  });
251
702
  ```
252
703
 
253
- ## 📡 Eventos DOM
704
+ ### En la subida
254
705
 
255
706
  ```javascript
256
- // Resultado listo para el input
257
- document.addEventListener("limbo:resultReady", (e) => {
258
- const { input, value } = e.detail;
259
- console.log(`Input ${input.name} recibió:`, value);
707
+ Limbo.create({
708
+ container: "#uploader",
709
+ mode: "embed",
710
+ modeUI: "upload-only",
711
+ folders: {
712
+ showFolderSelector: true,
713
+ allowCreateFolder: true,
714
+ defaultFolder: "marketing",
715
+ },
260
716
  });
717
+ ```
718
+
719
+ ---
720
+
721
+ ## Visualizadores
722
+
723
+ El componente incluye visualizadores nativos para cada tipo de archivo. Se abren automáticamente al hacer clic en una tarjeta de la galería.
261
724
 
262
- // Imagen seleccionada
725
+ | Tipo | Visualizador | Características |
726
+ | ----------------- | ---------------- | -------------------------------------------------------------------- |
727
+ | Imágenes | `ImageViewer` | Zoom con rueda, arrastrar para mover, doble clic para zoom, descarga |
728
+ | Vídeos | `VideoViewer` | Reproductor HTML5, play/pause con espacio, descarga |
729
+ | Audio | `AudioViewer` | Reproductor con timeline, progreso visual, tiempo transcurrido |
730
+ | PDF | `PdfViewer` | Iframe nativo del navegador con controles de zoom/página |
731
+ | Documentos Office | `DocumentViewer` | Información del archivo (tipo, tamaño) con botón de descarga |
732
+ | Texto / CSV | `TextViewer` | Renderizado inline del contenido con scroll |
733
+
734
+ Todos los visualizadores comparten el componente `ViewerShell`:
735
+
736
+ - Overlay oscuro a pantalla completa
737
+ - Cierre con tecla `ESC` o clic en el fondo
738
+ - Bloqueo de scroll del body
739
+ - Cabecera con nombre de archivo y acciones
740
+ - Botón de descarga directa
741
+
742
+ ---
743
+
744
+ ## Tipos de archivo soportados
745
+
746
+ | Categoría | Extensiones | MIME Types |
747
+ | -------------- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
748
+ | **Imágenes** | jpg, jpeg, png, gif, webp, svg | `image/jpeg`, `image/png`, `image/gif`, `image/webp`, `image/svg+xml` |
749
+ | **Vídeos** | mp4, webm, mov, avi, mkv | `video/mp4`, `video/webm`, `video/quicktime`, `video/x-msvideo`, `video/x-matroska` |
750
+ | **Documentos** | pdf, doc, docx, xls, xlsx, ppt, pptx, txt, csv | `application/pdf`, `application/msword`, `application/vnd.openxmlformats-*`, `text/plain`, `text/csv` |
751
+ | **Audio** | mp3, wav, ogg, aac, flac | `audio/mpeg`, `audio/wav`, `audio/ogg`, `audio/aac`, `audio/flac` |
752
+
753
+ ### GIFs
754
+
755
+ Los GIFs reciben un tratamiento especial:
756
+
757
+ - **Backend**: Se almacena el GIF original sin conversión a WebP (preserva la animación)
758
+ - **Galería**: Muestra thumbnail estático por defecto, anima al pasar el ratón (hover-to-animate)
759
+ - **Visualizador**: Reproduce la animación completa en ImageViewer
760
+ - **Cropper**: Deshabilitado para GIFs (no tiene sentido recortar animaciones)
761
+
762
+ ---
763
+
764
+ ## Eventos DOM
765
+
766
+ ```javascript
767
+ // ── Eventos globales ──
768
+
769
+ // Archivo seleccionado desde galería
263
770
  document.addEventListener("limbo:select", (e) => {
264
- console.log("Seleccionada:", e.detail);
771
+ const { assetId, url, fileName, mime, width, height } = e.detail;
772
+ });
773
+
774
+ // Archivo subido
775
+ document.addEventListener("limbo:upload", (e) => {
776
+ const { assetId, url, fileName } = e.detail;
265
777
  });
266
778
 
267
779
  // Recortes guardados
268
780
  document.addEventListener("limbo:cropsSaved", (e) => {
269
- console.log("Recortes:", e.detail.crops);
781
+ const { crops, assetId, instanceId } = e.detail;
782
+ crops.forEach((crop) =>
783
+ console.log(crop.name, crop.url, crop.width, crop.height),
784
+ );
270
785
  });
271
786
 
272
- // Imagen subida
273
- document.addEventListener("limbo:upload", (e) => {
274
- console.log("Subida:", e.detail);
787
+ // Cropper completado (modo crop-only)
788
+ document.addEventListener("limbo:cropperComplete", (e) => {
789
+ const { crops, instanceId } = e.detail;
790
+ });
791
+
792
+ // Cropper cancelado
793
+ document.addEventListener("limbo:cropperCancelled", (e) => {
794
+ console.log("Cancelado:", e.detail);
275
795
  });
276
796
 
277
797
  // Error
278
798
  document.addEventListener("limbo:error", (e) => {
279
799
  console.error("Error:", e.detail);
280
800
  });
801
+
802
+ // ── Eventos de AutoInputs (se disparan en el input) ──
803
+
804
+ // Resultado asignado al input
805
+ myInput.addEventListener("limbo:resultReady", (e) => {
806
+ const { value, imageData, config } = e.detail;
807
+ });
281
808
  ```
282
809
 
283
- ## 🎨 Personalización de Tema
810
+ ---
811
+
812
+ ## Personalización de Tema
813
+
814
+ ### Via JavaScript
284
815
 
285
816
  ```javascript
286
817
  Limbo.setTheme({
@@ -294,7 +825,7 @@ Limbo.setTheme({
294
825
  });
295
826
  ```
296
827
 
297
- O mediante CSS:
828
+ ### Via CSS
298
829
 
299
830
  ```css
300
831
  :root {
@@ -308,11 +839,73 @@ O mediante CSS:
308
839
  }
309
840
  ```
310
841
 
311
- ## 📄 Licencia
842
+ ---
843
+
844
+ ## Gestión de Instancias
845
+
846
+ ```javascript
847
+ // Crear instancia
848
+ const instance = Limbo.create({ container: "#limbo", mode: "modal" });
849
+
850
+ // Controlar instancia
851
+ instance.open(); // Abrir modal
852
+ instance.close(); // Cerrar modal
853
+ instance.mount(); // Montar componente (embed)
854
+ instance.unmount(); // Desmontar componente
855
+ instance.destroy(); // Destruir instancia y limpiar recursos
856
+
857
+ // Obtener todas las instancias activas
858
+ const instances = Limbo.getInstances();
859
+
860
+ // Destruir todo
861
+ Limbo.destroy();
862
+ ```
863
+
864
+ ---
865
+
866
+ ## Arquitectura de Subida Diferida (v2.0+)
867
+
868
+ Las imágenes **no se suben inmediatamente** al seleccionarlas. Permanecen en memoria hasta que el usuario confirma en el recortador:
869
+
870
+ 1. **Selección** (Upload, IA, Stock, Portales) → Imagen en memoria como `File`/`Blob`
871
+ 2. **Preview** → Vista previa unificada con edición de nombre, descarte, descarga
872
+ 3. **Recortador** → Al clic en "Guardar" → se sube la imagen y se crean los recortes
873
+ 4. **Cancelar** → Descarta sin tocar el backend
874
+
875
+ **Beneficios:**
876
+
877
+ - No se desperdician subidas para imágenes descartadas
878
+ - UX más rápida (sin espera antes del recorte)
879
+ - Menor carga en el backend
880
+
881
+ ---
882
+
883
+ ## Compatibilidad
884
+
885
+ | Requisito | Versión mínima |
886
+ | ----------- | --------------------------------------------- |
887
+ | React | 19.x (incluido en UMD) |
888
+ | Navegadores | Chrome 90+, Firefox 90+, Safari 15+, Edge 90+ |
889
+ | Node.js | 18+ (para bundlers) |
890
+
891
+ ## Formatos de distribución
892
+
893
+ | Formato | Archivo | Uso |
894
+ | --------- | ------------------------------- | ------------------------------------------------ |
895
+ | **ESM** | `limbo.es.js` | Bundlers modernos (Vite, Webpack 5+) |
896
+ | **CJS** | `limbo.cjs.js` | Node.js, Webpack legacy |
897
+ | **UMD** | `limbo.umd.js` / `limbo.min.js` | `<script>` directo en navegador (React incluido) |
898
+ | **CSS** | `limbo.css` | Estilos del componente (requerido) |
899
+ | **Types** | `index.d.ts` | Definiciones TypeScript |
900
+
901
+ ---
902
+
903
+ ## Licencia
312
904
 
313
- Proprietary © Lefebvre El Derecho S.A.
905
+ Proprietary &copy; Lefebvre El Derecho S.A.
314
906
 
315
- ## 🔗 Links
907
+ ## Links
316
908
 
317
909
  - [Changelog](./CHANGELOG.md)
318
910
  - [Documentación completa](https://limbo.lefebvre.es/docs)
911
+ - [NPM](https://www.npmjs.com/package/limbo-component)