libreria-astro-lefebvre 0.0.50 → 0.0.51
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 +68 -0
- package/package.json +12 -4
- package/src/carbins/AstroButton.ts +2 -14
- package/src/carbins/Contenido_2025_Alcorcon.ts +1 -1
- package/src/carbins/Contenido_2025_Malaga.ts +1 -1
- package/src/carbins/Contenido_2025_Montevideo.ts +1 -1
- package/src/carbins/Formulario_2025_Seul.ts +1 -1
- package/src/carbins/Formulario_2025_Teruel.ts +2 -2
- package/src/carbins/GeometricShapeCard.ts +1 -1
- package/src/carbins/ImageTextSimple.ts +1 -1
- package/src/carbins/Imagen_2025_Bogota.ts +1 -1
- package/src/carbins/Imagen_2025_Fukushima.ts +1 -1
- package/src/carbins/TextImageBackground.ts +1 -1
- package/src/carbins/TextImageBlock.ts +1 -1
- package/src/carbins/TextImageCard.ts +1 -1
- package/src/carbins/TextImageHeader.ts +1 -1
- package/src/components/Astro/Contenido_2025_Alcorcon.astro +5 -3
- package/src/components/Astro/Contenido_2025_Malaga.astro +4 -2
- package/src/components/Astro/Contenido_2025_Montevideo.astro +3 -3
- package/src/components/Astro/Formulario_2025_Seul.astro +3 -1
- package/src/components/Astro/Formulario_2025_Teruel.astro +5 -2
- package/src/components/Astro/GeometricShape.astro +4 -1
- package/src/components/Astro/GeometricShapeCard.astro +4 -1
- package/src/components/Astro/ImageTextSimple.astro +5 -1
- package/src/components/Astro/Imagen_2025_Bogota.astro +30 -16
- package/src/components/Astro/Imagen_2025_Fukushima.astro +5 -2
- package/src/components/Astro/TextImageBackground.astro +6 -1
- package/src/components/Astro/TextImageBlock.astro +5 -1
- package/src/components/Astro/TextImageCard.astro +5 -1
- package/src/components/Astro/TextImageHeader.astro +5 -1
- package/src/components/LimboImage.astro +89 -0
- package/src/index.ts +2 -0
- package/src/lib/functions.js +171 -0
- package/src/limbo/LimboProvider.astro +17 -1
- package/src/limbo/init.ts +138 -17
package/README.md
CHANGED
|
@@ -8,6 +8,74 @@ Instrucciones:
|
|
|
8
8
|
npm i libreria-astro-lefebvre
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
## 🖼️ Imágenes de Limbo
|
|
12
|
+
|
|
13
|
+
La librería incluye utilidades para trabajar con imágenes de Limbo de forma sencilla.
|
|
14
|
+
|
|
15
|
+
### Opción 1: Componente LimboImage (Recomendado para maquetadores)
|
|
16
|
+
|
|
17
|
+
El componente `<LimboImage>` extrae automáticamente la URL del JSON de Limbo:
|
|
18
|
+
|
|
19
|
+
```astro
|
|
20
|
+
---
|
|
21
|
+
import LimboImage from 'libreria-astro-lefebvre/components/LimboImage.astro';
|
|
22
|
+
|
|
23
|
+
const { imagen } = Astro.props; // Puede ser URL directa o JSON de Limbo
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
<!-- Uso básico -->
|
|
27
|
+
<LimboImage src={imagen} alt="Descripción de la imagen" />
|
|
28
|
+
|
|
29
|
+
<!-- Con estilos -->
|
|
30
|
+
<LimboImage
|
|
31
|
+
src={imagen}
|
|
32
|
+
alt="Hero"
|
|
33
|
+
class="w-full h-auto rounded-lg"
|
|
34
|
+
/>
|
|
35
|
+
|
|
36
|
+
<!-- Preferir imagen original en lugar del crop -->
|
|
37
|
+
<LimboImage src={imagen} prefer="original" alt="Original" />
|
|
38
|
+
|
|
39
|
+
<!-- Con fallback si no hay imagen -->
|
|
40
|
+
<LimboImage src={imagen} fallback="/default.jpg" alt="Con fallback" />
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Opción 2: Función extractImageUrl
|
|
44
|
+
|
|
45
|
+
Para casos donde necesitas más control:
|
|
46
|
+
|
|
47
|
+
```astro
|
|
48
|
+
---
|
|
49
|
+
import { extractImageUrl } from 'libreria-astro-lefebvre/lib/functions';
|
|
50
|
+
|
|
51
|
+
const { imagen } = Astro.props;
|
|
52
|
+
|
|
53
|
+
// Extraer URL (preferir crop por defecto)
|
|
54
|
+
const srcUrl = extractImageUrl(imagen);
|
|
55
|
+
|
|
56
|
+
// Preferir imagen original
|
|
57
|
+
const originalUrl = extractImageUrl(imagen, { prefer: 'original' });
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
<img src={srcUrl} alt="Mi imagen" />
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Funciones disponibles
|
|
64
|
+
|
|
65
|
+
```javascript
|
|
66
|
+
import {
|
|
67
|
+
extractImageUrl, // Extrae URL de JSON Limbo
|
|
68
|
+
parseImageData, // Obtiene datos completos (original + crops)
|
|
69
|
+
resolveUrl, // Convierte /files/... a URL absoluta
|
|
70
|
+
isValidImageUrl, // Verifica si URL es válida (no blob)
|
|
71
|
+
LIMBO_BASE_URL // { DEV: '...', PROD: '...' }
|
|
72
|
+
} from 'libreria-astro-lefebvre/lib/functions';
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Instalación y desarrollo
|
|
78
|
+
|
|
11
79
|
|
|
12
80
|
Hacer un link en librería local:
|
|
13
81
|
``` npm link ```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "libreria-astro-lefebvre",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.51",
|
|
4
4
|
"description": "Librería de componentes Astro, React y Vue para Lefebvre",
|
|
5
5
|
"author": "Equipo web desarrollo Lefebvre",
|
|
6
6
|
"type": "module",
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
},
|
|
13
13
|
"./list": "./src/list.ts",
|
|
14
14
|
"./limbo": "./src/limbo/index.ts",
|
|
15
|
-
"./limbo/LimboProvider.astro": "./src/limbo/LimboProvider.astro"
|
|
15
|
+
"./limbo/LimboProvider.astro": "./src/limbo/LimboProvider.astro",
|
|
16
|
+
"./components/LimboImage.astro": "./src/components/LimboImage.astro",
|
|
17
|
+
"./lib/functions": "./src/lib/functions.js"
|
|
16
18
|
},
|
|
17
19
|
"files": [
|
|
18
20
|
"src"
|
|
@@ -26,12 +28,18 @@
|
|
|
26
28
|
],
|
|
27
29
|
"peerDependencies": {
|
|
28
30
|
"astro": "^5.11.0",
|
|
29
|
-
"limbo-component": "latest"
|
|
31
|
+
"limbo-component": "latest",
|
|
32
|
+
"vue": "^3.3.0",
|
|
33
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
34
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
30
35
|
},
|
|
31
36
|
"scripts": {
|
|
32
37
|
"generate:registry": "node scripts/generateRegistry.js"
|
|
33
38
|
},
|
|
34
39
|
"devDependencies": {
|
|
35
|
-
"typescript": "^5.8.3"
|
|
40
|
+
"typescript": "^5.8.3",
|
|
41
|
+
"vue": "^3.5.0",
|
|
42
|
+
"react": "^19.0.0",
|
|
43
|
+
"react-dom": "^19.0.0"
|
|
36
44
|
}
|
|
37
45
|
}
|
|
@@ -27,20 +27,8 @@ export const metadata: ComponentMetadata = {
|
|
|
27
27
|
name: 'imagen',
|
|
28
28
|
type: 'image',
|
|
29
29
|
label: 'Imagen de prueba',
|
|
30
|
-
mandatory:
|
|
31
|
-
example_value: ''
|
|
32
|
-
image_cuts: [
|
|
33
|
-
{
|
|
34
|
-
label: "mobile",
|
|
35
|
-
width: "300",
|
|
36
|
-
height: "100"
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
label: "desktop",
|
|
40
|
-
width: "400",
|
|
41
|
-
height: "400"
|
|
42
|
-
},
|
|
43
|
-
]
|
|
30
|
+
mandatory: false,
|
|
31
|
+
example_value: ''
|
|
44
32
|
},
|
|
45
33
|
{
|
|
46
34
|
name: 'target',
|
|
@@ -97,7 +97,7 @@ export const metadata: ComponentMetadata = {
|
|
|
97
97
|
},
|
|
98
98
|
{
|
|
99
99
|
name: 'imageModal',
|
|
100
|
-
type: '
|
|
100
|
+
type: 'image',
|
|
101
101
|
label: 'Src de la imagen modal',
|
|
102
102
|
mandatory: false,
|
|
103
103
|
example_value: ''
|
|
@@ -118,7 +118,7 @@ export const metadata: ComponentMetadata = {
|
|
|
118
118
|
},
|
|
119
119
|
{
|
|
120
120
|
name: 'imageSrc',
|
|
121
|
-
type: '
|
|
121
|
+
type: 'image',
|
|
122
122
|
label: 'Src de la imagen',
|
|
123
123
|
mandatory: false,
|
|
124
124
|
example_value: 'https://assets.lefebvre.es/media/img/preview-comp/comp-16-9.png'
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
+
import { extractImageUrl } from '../../lib/functions.js';
|
|
3
|
+
|
|
2
4
|
const {
|
|
3
5
|
title,
|
|
4
6
|
subtitle,
|
|
@@ -11,7 +13,7 @@ const {
|
|
|
11
13
|
showBtn = true
|
|
12
14
|
} = Astro.props;
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
const srcUrl = extractImageUrl(src);
|
|
15
17
|
---
|
|
16
18
|
|
|
17
19
|
<section class="w-full flex items-center justify-center pb-[36px]">
|
|
@@ -20,8 +22,8 @@ const {
|
|
|
20
22
|
|
|
21
23
|
<div class="max-w-7xl w-full flex justify-center items-center relative md:w-4/5">
|
|
22
24
|
<div class="relative w-full max-w-7xl min-h-[400px] md:min-h-[500px] lg:min-h-[auto]">
|
|
23
|
-
{
|
|
24
|
-
<img src={
|
|
25
|
+
{srcUrl && srcUrl !== "" ? (
|
|
26
|
+
<img src={srcUrl} alt={alt} title={title} class="object-cover object-center w-full h-full lg:max-h-[446px] min-h-[400px] md:min-h-[500px] lg:min-h-[auto] rounded-2xl">
|
|
25
27
|
) : (
|
|
26
28
|
<video autoplay loop muted playsinline class="object-cover object-center w-full h-full lg:max-h-[446px] min-h-[400px] md:min-h-[500px] lg:min-h-[auto] rounded-2xl">
|
|
27
29
|
<source src={iframeSrc} type="video/mp4" />
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
+
import { extractImageUrl } from '../../lib/functions.js';
|
|
3
|
+
|
|
2
4
|
const {
|
|
3
5
|
title,
|
|
4
6
|
description,
|
|
@@ -17,7 +19,7 @@ const {
|
|
|
17
19
|
alignItems = 'start',
|
|
18
20
|
} = Astro.props;
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
const imageUrl = extractImageUrl(image);
|
|
21
23
|
---
|
|
22
24
|
|
|
23
25
|
<section class="w-full flex items-center justify-center">
|
|
@@ -94,7 +96,7 @@ const {
|
|
|
94
96
|
)}
|
|
95
97
|
|
|
96
98
|
<div class={`w-full lg:w-1/2 p-4 flex items-center ${orientation === 'left' ? 'justify-end' : 'justify-start'} aspect-square`}>
|
|
97
|
-
<img src={
|
|
99
|
+
<img src={imageUrl} alt={title} class="w-[100%] h-[100%] object-cover rounded-lg" />
|
|
98
100
|
</div>
|
|
99
101
|
</div>
|
|
100
102
|
</article>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
2
|
import Titulo_2025_Santorini from './Titulo_2025_Santorini.astro';
|
|
3
|
+
import { extractImageUrl } from '../../lib/functions.js';
|
|
4
4
|
|
|
5
5
|
const {
|
|
6
6
|
link='#',
|
|
@@ -11,14 +11,14 @@ const {
|
|
|
11
11
|
description=''
|
|
12
12
|
} = Astro.props;
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
const imageUrl = extractImageUrl(image);
|
|
15
15
|
---
|
|
16
16
|
|
|
17
17
|
<a href={link} class="w-full flex flex-col cursor-pointer px-4 md:px-0">
|
|
18
18
|
<article class="w-full h-full flex flex-col rounded-lg border border-gray-200 items-center shadow-article hover:bg-gradient-to-r from-[#001978] via-[#2134F1] to-[#F81BBD] transition-all duration-300 p-[1px]">
|
|
19
19
|
<div class="bg-white mt-[1px] rounded-tl-[7px] rounded-tr-[7px] h-full">
|
|
20
20
|
<div class="w-full">
|
|
21
|
-
<img
|
|
21
|
+
<img class="cursor-pointer rounded-tr-lg rounded-tl-lg mx-auto h-full" src={imageUrl} alt={altImage} />
|
|
22
22
|
</div>
|
|
23
23
|
<div class="p-6 flex-grow flex flex-col">
|
|
24
24
|
{tag && tag !== '' && <Titulo_2025_Santorini description={tag} />}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
+
import { extractImageUrl } from '../../lib/functions.js';
|
|
2
3
|
|
|
3
4
|
const {
|
|
4
5
|
title,
|
|
@@ -10,6 +11,7 @@ const {
|
|
|
10
11
|
businessAction = false
|
|
11
12
|
} = Astro.props;
|
|
12
13
|
|
|
14
|
+
const imageSrcUrl = extractImageUrl(imageSrc);
|
|
13
15
|
const idTargetLf2 = 'lf2-form-' + Math.random().toString(36).substring(2, 15);
|
|
14
16
|
|
|
15
17
|
const structuredData = `<script type="application/ld+json">
|
|
@@ -44,7 +46,7 @@ const structuredData = `<script type="application/ld+json">
|
|
|
44
46
|
<div id={idTargetLf2} class="w-full flex flex-col mx-auto gap-3"></div>
|
|
45
47
|
</div>
|
|
46
48
|
<div class={`w-full p-0 md:p-4 flex items-center ${orientation === 'left' ? 'justify-end' : 'justify-start'}`}>
|
|
47
|
-
<img src={
|
|
49
|
+
<img src={imageSrcUrl} alt={title} class="w-full md:w-4/5 min-h-auto md:min-h-[650px] object-cover rounded-lg" />
|
|
48
50
|
</div>
|
|
49
51
|
</div>
|
|
50
52
|
</article>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
+
import { extractImageUrl } from '../../lib/functions.js';
|
|
2
3
|
|
|
3
4
|
const {
|
|
4
5
|
bottomBorder = false,
|
|
@@ -22,6 +23,8 @@ const {
|
|
|
22
23
|
|
|
23
24
|
} = Astro.props;
|
|
24
25
|
|
|
26
|
+
const imageSrcUrl = extractImageUrl(imageSrc);
|
|
27
|
+
const imageModalUrl = extractImageUrl(imageModal);
|
|
25
28
|
const randomId = Math.floor(Math.random() * 1000);
|
|
26
29
|
|
|
27
30
|
|
|
@@ -53,7 +56,7 @@ const randomId = Math.floor(Math.random() * 1000);
|
|
|
53
56
|
)}
|
|
54
57
|
</div>
|
|
55
58
|
<div class={`w-full md:w-1/2 p-4 flex items-center ${orientation === 'left' ? 'justify-end' : 'justify-start'}`}>
|
|
56
|
-
<img src={
|
|
59
|
+
<img src={imageSrcUrl} alt={title} class="w-full md:w-1/2 rounded-2xl" />
|
|
57
60
|
</div>
|
|
58
61
|
</div>
|
|
59
62
|
|
|
@@ -66,7 +69,7 @@ const randomId = Math.floor(Math.random() * 1000);
|
|
|
66
69
|
</div>
|
|
67
70
|
<div class="w-full h-full flex flex-col lg:flex-row items-start justify-center gap-8">
|
|
68
71
|
<div class="w-full lg:w-1/3 flex justify-center items-center p-0 lg:p-4 mt-[32px] lg:mt-0">
|
|
69
|
-
<img src={
|
|
72
|
+
<img src={imageModalUrl} alt={altModal} class="w-fit" />
|
|
70
73
|
</div>
|
|
71
74
|
<div class="w-full lg:w-2/3">
|
|
72
75
|
<h2 class="font-poppins text-[#262626] text-[28px] lg:text-[32px] font-normal text-left leading-[40px] mb-[32px]">{titleModal}</h2>
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
---
|
|
2
|
+
import { extractImageUrl } from '../../lib/functions.js';
|
|
3
|
+
|
|
2
4
|
const { imageSrc, orientation, figure = 'Trapecio', gradiantIndex = 0 } = Astro.props;
|
|
5
|
+
const imageSrcUrl = extractImageUrl(imageSrc);
|
|
3
6
|
const polygons = [
|
|
4
7
|
{
|
|
5
8
|
name: 'Trapeze',
|
|
@@ -36,5 +39,5 @@ const selectedPolygon = polygons.find(p => p.name === figure) || polygons[0];
|
|
|
36
39
|
---
|
|
37
40
|
<div class={`w-[400px] h-[400px] relative ${orientation === 'right' ? '' : '-scale-x-100'}`}>
|
|
38
41
|
<div class={`absolute bottom-0 right-0 w-full h-full bg-cover mask-no-repeat mask-bottom-right ${gradiants[gradiantIndex]}`} style={`mask-image: url(${selectedPolygon.polygon});`}></div>
|
|
39
|
-
<img src={`${
|
|
42
|
+
<img src={`${imageSrcUrl}`} loading="lazy" class="absolute bottom-0 right-0 max-h-[400px] h-auto z-10 mask-no-repeat mask-bottom-right" style={`mask-image: url(${selectedPolygon.full});`} alt="Figura Geométrica" />
|
|
40
43
|
</div>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
import GeometricShape from './GeometricShape.astro';
|
|
3
|
+
import { extractImageUrl } from '../../lib/functions.js';
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
const {
|
|
@@ -12,13 +13,15 @@ const {
|
|
|
12
13
|
gradiantIndex = 0
|
|
13
14
|
} = Astro.props;
|
|
14
15
|
|
|
16
|
+
const imageSrcUrl = extractImageUrl(imageSrc);
|
|
17
|
+
|
|
15
18
|
const randomId = `geo-shape-card-`+ Math.random().toString(36).substring(2, 15);
|
|
16
19
|
|
|
17
20
|
---
|
|
18
21
|
|
|
19
22
|
<section class={`flex my-8 items-center justify-center relative min-h-[500px]`}>
|
|
20
23
|
<div class={`absolute top-0 ${orientation === 'right' ? 'right-0' : 'left-0'}`}>
|
|
21
|
-
<GeometricShape imageSrc={
|
|
24
|
+
<GeometricShape imageSrc={imageSrcUrl} orientation={orientation} figure={figure} gradiantIndex={gradiantIndex} />
|
|
22
25
|
</div>
|
|
23
26
|
<div class={`flex text-gray-800 flex-col w-3/5 mx-auto ${orientation === 'right' ? 'text-left items-start' : 'text-right items-end'}`}>
|
|
24
27
|
<h2 class="text-4xl font-bold mb-5 max-w-3xl">{title}</h2>
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
---
|
|
2
|
+
import { extractImageUrl } from '../../lib/functions.js';
|
|
3
|
+
|
|
2
4
|
const {
|
|
3
5
|
image,
|
|
4
6
|
title,
|
|
5
7
|
video ,
|
|
6
8
|
description,
|
|
7
9
|
} = Astro.props;
|
|
10
|
+
|
|
11
|
+
const imageUrl = extractImageUrl(image);
|
|
8
12
|
---
|
|
9
13
|
|
|
10
14
|
<!-- Componente que sirve para las páginas soluciones-profesionales y conocenos entre otras. Si se le pasa por parámetro una imagen, la muestra a la izquierda y el texto a la derecha. Si se le pasa un vídeo, muestra el vídeo a la izquierda y el texto a la derecha. -->
|
|
@@ -15,7 +19,7 @@ const {
|
|
|
15
19
|
|
|
16
20
|
{image
|
|
17
21
|
? (
|
|
18
|
-
<div class="w-full lg:w-1/2 lg:mx-4 mask-add mask-[url(https://assets.lefebvre.es/media/logos-2/svg/lefebvre-iso.svg)] mask-no-repeat mask-center bg-cover" style={`background-image: url('${
|
|
22
|
+
<div class="w-full lg:w-1/2 lg:mx-4 mask-add mask-[url(https://assets.lefebvre.es/media/logos-2/svg/lefebvre-iso.svg)] mask-no-repeat mask-center bg-cover" style={`background-image: url('${imageUrl}')`} id="rotating-image"></div>
|
|
19
23
|
)
|
|
20
24
|
: video
|
|
21
25
|
? (
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
---
|
|
2
|
+
/**
|
|
3
|
+
* 🎯 Imagen_2025_Bogota
|
|
4
|
+
*
|
|
5
|
+
* Componente de imagen con overlay de texto.
|
|
6
|
+
* Usa LimboImage para simplificar el manejo de imágenes de Limbo.
|
|
7
|
+
*/
|
|
8
|
+
import LimboImage from '../LimboImage.astro';
|
|
9
|
+
|
|
2
10
|
const {
|
|
3
11
|
title,
|
|
4
12
|
description,
|
|
@@ -10,19 +18,19 @@ const {
|
|
|
10
18
|
showBtn = true,
|
|
11
19
|
showDescription = true,
|
|
12
20
|
color = '#ffffff',
|
|
13
|
-
|
|
21
|
+
} = Astro.props;
|
|
14
22
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
const structuredData = `<script type="application/ld+json">
|
|
24
|
+
{
|
|
25
|
+
"@context": "https://schema.org",
|
|
26
|
+
"@type": "ImageObject",
|
|
27
|
+
"name": "${title || ''}",
|
|
28
|
+
"description": "${description ? description.replace(/<[^>]*>/g, '').replace(/"/g, '\\"') : ''}",
|
|
29
|
+
"url": "${src || iframeSrc || ''}",
|
|
30
|
+
"contentUrl": "${src || iframeSrc || ''}",
|
|
31
|
+
${src ? `"encodingFormat": "image"` : `"encodingFormat": "video/mp4"`}
|
|
32
|
+
}
|
|
33
|
+
</script>`;
|
|
26
34
|
---
|
|
27
35
|
|
|
28
36
|
<section class="w-full flex items-center justify-center">
|
|
@@ -32,13 +40,19 @@ const {
|
|
|
32
40
|
<div class="max-w-full lg:max-w-7xl h-full flex justify-center items-center aspect-auto relative md:w-4/5 lg:aspect-video">
|
|
33
41
|
<div class="absolute inset-0">
|
|
34
42
|
|
|
35
|
-
{src
|
|
36
|
-
<
|
|
37
|
-
|
|
43
|
+
{src ? (
|
|
44
|
+
<LimboImage
|
|
45
|
+
src={src}
|
|
46
|
+
alt={alt || title || ''}
|
|
47
|
+
title={title}
|
|
48
|
+
class="object-cover object-center w-full h-full rounded-2xl shadow-lg"
|
|
49
|
+
fallback={iframeSrc}
|
|
50
|
+
/>
|
|
51
|
+
) : iframeSrc ? (
|
|
38
52
|
<video autoplay loop muted playsinline class="object-cover object-center w-full h-full rounded-2xl shadow-lg">
|
|
39
53
|
<source src={iframeSrc} type="video/mp4" />
|
|
40
54
|
</video>
|
|
41
|
-
)}
|
|
55
|
+
) : null}
|
|
42
56
|
</div>
|
|
43
57
|
|
|
44
58
|
<div class="w-4/5 relative z-10 flex flex-col justify-center items-center h-full text-center p-0 py-8 md:p-6">
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
+
import { extractImageUrl } from '../../lib/functions.js';
|
|
3
|
+
|
|
2
4
|
const {
|
|
3
5
|
link = false,
|
|
4
6
|
image,
|
|
@@ -6,6 +8,7 @@ const {
|
|
|
6
8
|
descriptionImage
|
|
7
9
|
} = Astro.props;
|
|
8
10
|
|
|
11
|
+
const imageUrl = extractImageUrl(image);
|
|
9
12
|
---
|
|
10
13
|
|
|
11
14
|
|
|
@@ -14,12 +17,12 @@ const {
|
|
|
14
17
|
<div class="w-full">
|
|
15
18
|
{(link && link !== '') ? (
|
|
16
19
|
<a href={link} class="w-full flex flex-col flex-1 justify-center items-center">
|
|
17
|
-
<img class="max-w-[80px] w-fit mb-4" src={
|
|
20
|
+
<img class="max-w-[80px] w-fit mb-4" src={imageUrl} alt={altImage} />
|
|
18
21
|
<p class="text-[14px] text-[#363942]" set:html={descriptionImage}></p>
|
|
19
22
|
</a>
|
|
20
23
|
) : (
|
|
21
24
|
<div class="w-full flex flex-col flex-1 justify-center items-center max-h-[94px]">
|
|
22
|
-
<img class="max-w-[80px] w-fit mb-4" src={
|
|
25
|
+
<img class="max-w-[80px] w-fit mb-4" src={imageUrl} alt={altImage} />
|
|
23
26
|
</div>
|
|
24
27
|
<div class="w-full flex flex-col flex-1 justify-center items-center">
|
|
25
28
|
<p class="text-[14px] text-[#363942]" set:html={descriptionImage}></p>
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
---
|
|
2
|
+
import { extractImageUrl } from '../../lib/functions.js';
|
|
3
|
+
|
|
2
4
|
const {
|
|
3
5
|
title,
|
|
4
6
|
description,
|
|
5
7
|
image,
|
|
6
8
|
orientation = 'left',
|
|
7
9
|
} = Astro.props;
|
|
10
|
+
|
|
11
|
+
// Extraer URL de la imagen (puede venir como JSON de Limbo o URL directa)
|
|
12
|
+
const imageUrl = extractImageUrl(image);
|
|
8
13
|
---
|
|
9
14
|
|
|
10
|
-
<section class={`w-full flex items-center justify-center bg-white bg-no-repeat ${orientation === 'left' ? 'bg-right' : 'bg-left'} bg-contain `} style={`background-image: url('${
|
|
15
|
+
<section class={`w-full flex items-center justify-center bg-white bg-no-repeat ${orientation === 'left' ? 'bg-right' : 'bg-left'} bg-contain `} style={`background-image: url('${imageUrl}');`}>
|
|
11
16
|
<article class={`flex ${orientation === 'left' ? 'flex-row' : 'flex-row-reverse'} py-12 w-4/5 center mx-auto `}>
|
|
12
17
|
<div class="basis-1/2">
|
|
13
18
|
<h3 class="font-semibold mb-4 text-[2.313em]" style="line-height:47px;" set:html={title}/>
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
+
import { extractImageUrl } from '../../lib/functions.js';
|
|
3
|
+
|
|
2
4
|
const {
|
|
3
5
|
title,
|
|
4
6
|
description,
|
|
@@ -7,6 +9,8 @@ const {
|
|
|
7
9
|
image,
|
|
8
10
|
orientation = 'left',
|
|
9
11
|
} = Astro.props;
|
|
12
|
+
|
|
13
|
+
const imageUrl = extractImageUrl(image);
|
|
10
14
|
---
|
|
11
15
|
<section class="w-full flex items-center justify-center bg-gradient-to-r from-slate-50 to-slate-100 border-t border-b border-gray-300">
|
|
12
16
|
<article class="w-3/5 flex flex-col items-center justify-center p-12">
|
|
@@ -22,7 +26,7 @@ const {
|
|
|
22
26
|
</div>
|
|
23
27
|
</div>
|
|
24
28
|
<div class={`w-1/2 p-4 flex items-center ${orientation === 'left' ? 'justify-end' : 'justify-start'}`}>
|
|
25
|
-
<img src={
|
|
29
|
+
<img src={imageUrl} alt={title} class="w-4/5 h-auto rounded" />
|
|
26
30
|
</div>
|
|
27
31
|
</div>
|
|
28
32
|
</article>
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
---
|
|
2
|
+
import { extractImageUrl } from '../../lib/functions.js';
|
|
3
|
+
|
|
2
4
|
const {
|
|
3
5
|
title,
|
|
4
6
|
description,
|
|
5
7
|
image,
|
|
6
8
|
orientation = 'left',
|
|
7
9
|
} = Astro.props;
|
|
10
|
+
|
|
11
|
+
const imageUrl = extractImageUrl(image);
|
|
8
12
|
---
|
|
9
13
|
<article class={`flex ${orientation === 'left' ? 'flex-row' : 'flex-row-reverse'} items-center gap-4 w-full rounded-xl overflow-hidden border border-slate-300 shadow-lg bg-white`}>
|
|
10
14
|
<div class="w-2/3 p-8 flex flex-col flex-wrap justify-center items-start">
|
|
@@ -17,7 +21,7 @@ const {
|
|
|
17
21
|
<div class={`w-1/3 aspect-square flex items-center ${orientation === 'left' ? 'justify-end' : 'justify-start'}`}>
|
|
18
22
|
<div
|
|
19
23
|
class="w-full h-full bg-center bg-cover"
|
|
20
|
-
style={`background-image: url('${
|
|
24
|
+
style={`background-image: url('${imageUrl}');`}
|
|
21
25
|
aria-label={title}
|
|
22
26
|
role="img"
|
|
23
27
|
></div>
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
+
import { extractImageUrl } from '../../lib/functions.js';
|
|
3
|
+
|
|
2
4
|
const {
|
|
3
5
|
image,
|
|
4
6
|
pretitle,
|
|
@@ -8,9 +10,11 @@ const {
|
|
|
8
10
|
buttonText,
|
|
9
11
|
buttonUrl,
|
|
10
12
|
} = Astro.props;
|
|
13
|
+
|
|
14
|
+
const imageUrl = extractImageUrl(image);
|
|
11
15
|
---
|
|
12
16
|
|
|
13
|
-
<div class={`bg-[100%_0] bg-cover h-[570px] w-full ${orientation === "right" ? "rotate-y-180" : ""} `} style={`background-image: url('${
|
|
17
|
+
<div class={`bg-[100%_0] bg-cover h-[570px] w-full ${orientation === "right" ? "rotate-y-180" : ""} `} style={`background-image: url('${imageUrl}');`} >
|
|
14
18
|
<div class="w-full lg:w-[70%] h-full transition duration-900 ease-in-out bg-gradient-to-r from-indigo-900 via-blue-600 to-pink-500 hover:from-pink-500 hover:via-blue-600 hover:to-indigo-900 lg:[clip-path:polygon(0_0,100%_0,88%_86%,68%_100%,0_100%)]">
|
|
15
19
|
<div class="flex flex-row w-4/5 center mx-auto pt-20 sm:pt-30">
|
|
16
20
|
<div class={`w-full ${orientation === "right" ? "rotate-y-180" : ""}`}>
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* 🎯 LimboImage - Componente wrapper para imágenes de Limbo
|
|
4
|
+
*
|
|
5
|
+
* Este componente simplifica el uso de imágenes de Limbo en componentes Astro.
|
|
6
|
+
* Extrae automáticamente la URL del JSON de Limbo y maneja URLs relativas.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* // Uso simple - el src puede ser URL directa o JSON de Limbo
|
|
10
|
+
* <LimboImage src={imagenDeLimbo} alt="Descripción" />
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* // Con clases personalizadas
|
|
14
|
+
* <LimboImage
|
|
15
|
+
* src={imagenDeLimbo}
|
|
16
|
+
* alt="Hero image"
|
|
17
|
+
* class="w-full h-auto rounded-lg"
|
|
18
|
+
* />
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* // Preferir imagen original en lugar del crop
|
|
22
|
+
* <LimboImage src={imagenDeLimbo} prefer="original" />
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* // Con fallback si no hay imagen
|
|
26
|
+
* <LimboImage src={imagenDeLimbo} fallback="/default.jpg" />
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { extractImageUrl } from '../lib/functions.js';
|
|
30
|
+
|
|
31
|
+
interface Props {
|
|
32
|
+
/** Valor de imagen - puede ser URL directa o JSON de Limbo */
|
|
33
|
+
src: string;
|
|
34
|
+
/** Texto alternativo (requerido para accesibilidad) */
|
|
35
|
+
alt: string;
|
|
36
|
+
/** Título opcional para tooltip */
|
|
37
|
+
title?: string;
|
|
38
|
+
/** Clases CSS adicionales */
|
|
39
|
+
class?: string;
|
|
40
|
+
/** Preferir 'crop' (recorte) u 'original' */
|
|
41
|
+
prefer?: 'crop' | 'original';
|
|
42
|
+
/** Imagen fallback si src está vacío o es inválido */
|
|
43
|
+
fallback?: string;
|
|
44
|
+
/** Carga diferida (por defecto: lazy) */
|
|
45
|
+
loading?: 'lazy' | 'eager';
|
|
46
|
+
/** Decodificación (por defecto: async) */
|
|
47
|
+
decoding?: 'async' | 'sync' | 'auto';
|
|
48
|
+
/** Ancho explícito (para CLS) */
|
|
49
|
+
width?: number | string;
|
|
50
|
+
/** Alto explícito (para CLS) */
|
|
51
|
+
height?: number | string;
|
|
52
|
+
/** Usar entorno de producción para URLs relativas */
|
|
53
|
+
isProd?: boolean;
|
|
54
|
+
/** Atributos HTML adicionales */
|
|
55
|
+
[key: string]: any;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const {
|
|
59
|
+
src,
|
|
60
|
+
alt,
|
|
61
|
+
title,
|
|
62
|
+
class: className,
|
|
63
|
+
prefer = 'crop',
|
|
64
|
+
fallback = '',
|
|
65
|
+
loading = 'lazy',
|
|
66
|
+
decoding = 'async',
|
|
67
|
+
width,
|
|
68
|
+
height,
|
|
69
|
+
isProd = false,
|
|
70
|
+
...restProps
|
|
71
|
+
} = Astro.props;
|
|
72
|
+
|
|
73
|
+
// Extraer URL usando la función centralizada
|
|
74
|
+
const imageUrl = extractImageUrl(src, { prefer, isProd }) || fallback;
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
{imageUrl && (
|
|
78
|
+
<img
|
|
79
|
+
src={imageUrl}
|
|
80
|
+
alt={alt}
|
|
81
|
+
title={title}
|
|
82
|
+
class={className}
|
|
83
|
+
loading={loading}
|
|
84
|
+
decoding={decoding}
|
|
85
|
+
width={width}
|
|
86
|
+
height={height}
|
|
87
|
+
{...restProps}
|
|
88
|
+
/>
|
|
89
|
+
)}
|
package/src/index.ts
CHANGED
package/src/lib/functions.js
CHANGED
|
@@ -1,5 +1,176 @@
|
|
|
1
1
|
import { components } from '../generated/componentRegistry.ts';
|
|
2
2
|
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// 🎯 Utilidades de Imagen para Limbo
|
|
5
|
+
// ============================================================================
|
|
6
|
+
// NOTA: Estas funciones están copiadas de component-limbo/src/utils/helpers.js
|
|
7
|
+
// para evitar problemas de dependencias en entornos sin acceso directo a limbo-component.
|
|
8
|
+
// Si se actualizan en helpers.js, sincronizar aquí también.
|
|
9
|
+
// ============================================================================
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* URLs base de Limbo según entorno
|
|
13
|
+
*/
|
|
14
|
+
export const LIMBO_BASE_URL = {
|
|
15
|
+
DEV: 'https://led-dev-limbo-dev.eu.els.local',
|
|
16
|
+
PROD: 'https://limbo.lefebvre.es'
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Convierte URLs relativas de Limbo a absolutas
|
|
21
|
+
*/
|
|
22
|
+
export function resolveUrl(url, isProd = false) {
|
|
23
|
+
if (!url) return '';
|
|
24
|
+
if (url.startsWith('/files/')) {
|
|
25
|
+
const baseUrl = isProd ? LIMBO_BASE_URL.PROD : LIMBO_BASE_URL.DEV;
|
|
26
|
+
return baseUrl + url;
|
|
27
|
+
}
|
|
28
|
+
return url;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Decodifica entidades HTML comunes en un string
|
|
33
|
+
*/
|
|
34
|
+
export function decodeHtmlEntities(value) {
|
|
35
|
+
if (typeof value !== 'string') return value;
|
|
36
|
+
return value
|
|
37
|
+
.replace(/"/g, '"')
|
|
38
|
+
.replace(/&/g, '&')
|
|
39
|
+
.replace(/</g, '<')
|
|
40
|
+
.replace(/>/g, '>')
|
|
41
|
+
.replace(/'/g, "'")
|
|
42
|
+
.replace(/'/g, "'");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Verifica si una URL es válida y usable (no blob, no vacía)
|
|
47
|
+
*/
|
|
48
|
+
export function isValidImageUrl(url) {
|
|
49
|
+
if (!url || typeof url !== 'string') return false;
|
|
50
|
+
if (url.startsWith('blob:')) return false;
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 🎯 FUNCIÓN PRINCIPAL - Extrae URL de imagen de cualquier formato
|
|
56
|
+
*
|
|
57
|
+
* @param {string} value - URL directa o JSON de Limbo
|
|
58
|
+
* @param {Object} options - { prefer: 'crop'|'original', isProd: boolean }
|
|
59
|
+
* @returns {string} URL lista para usar en <img src="">
|
|
60
|
+
*/
|
|
61
|
+
export function extractImageUrl(value, options = {}) {
|
|
62
|
+
if (!value) return '';
|
|
63
|
+
|
|
64
|
+
const { prefer = 'crop', isProd = false } = options;
|
|
65
|
+
const normalizedValue = decodeHtmlEntities(value);
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
const data = JSON.parse(normalizedValue);
|
|
69
|
+
|
|
70
|
+
const findValidCropUrl = () => {
|
|
71
|
+
if (data.images && Array.isArray(data.images)) {
|
|
72
|
+
for (const img of data.images) {
|
|
73
|
+
if (img && isValidImageUrl(img.url)) {
|
|
74
|
+
return img.url;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const originalUrl = data.original?.url;
|
|
82
|
+
const cropUrl = findValidCropUrl();
|
|
83
|
+
|
|
84
|
+
if (prefer === 'crop') {
|
|
85
|
+
if (cropUrl) return resolveUrl(cropUrl, isProd);
|
|
86
|
+
if (isValidImageUrl(originalUrl)) return resolveUrl(originalUrl, isProd);
|
|
87
|
+
} else {
|
|
88
|
+
if (isValidImageUrl(originalUrl)) return resolveUrl(originalUrl, isProd);
|
|
89
|
+
if (cropUrl) return resolveUrl(cropUrl, isProd);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (isValidImageUrl(data.url)) {
|
|
93
|
+
return resolveUrl(data.url, isProd);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return '';
|
|
97
|
+
|
|
98
|
+
} catch {
|
|
99
|
+
if (typeof normalizedValue === 'string') {
|
|
100
|
+
if (normalizedValue.startsWith('blob:')) return '';
|
|
101
|
+
if (normalizedValue.startsWith('/files/')) return resolveUrl(normalizedValue, isProd);
|
|
102
|
+
if (normalizedValue.startsWith('http') || normalizedValue.startsWith('/')) return normalizedValue;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (typeof value === 'string' && (value.startsWith('{') || value.startsWith('['))) {
|
|
107
|
+
return '';
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return value;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Obtiene datos completos de imagen (original + recortes)
|
|
115
|
+
*/
|
|
116
|
+
export function parseImageData(value, options = {}) {
|
|
117
|
+
const { isProd = false } = options;
|
|
118
|
+
|
|
119
|
+
if (!value) return { original: null, images: [], url: '' };
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
const data = JSON.parse(decodeHtmlEntities(value));
|
|
123
|
+
|
|
124
|
+
const resolvedOriginal = data.original ? {
|
|
125
|
+
...data.original,
|
|
126
|
+
url: resolveUrl(data.original.url, isProd)
|
|
127
|
+
} : null;
|
|
128
|
+
|
|
129
|
+
const resolvedImages = (data.images || []).map(img => ({
|
|
130
|
+
...img,
|
|
131
|
+
url: resolveUrl(img.url, isProd)
|
|
132
|
+
}));
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
original: resolvedOriginal,
|
|
136
|
+
images: resolvedImages,
|
|
137
|
+
url: resolvedImages[0]?.url || resolvedOriginal?.url || resolveUrl(data.url, isProd) || ''
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
} catch {
|
|
141
|
+
return {
|
|
142
|
+
original: null,
|
|
143
|
+
images: [],
|
|
144
|
+
url: resolveUrl(value, isProd)
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Preprocesa campos de imagen para enviar a servidor de preview
|
|
151
|
+
*/
|
|
152
|
+
export function prepareImageFieldsForPreview(obj, options = {}) {
|
|
153
|
+
const { isProd = false } = options;
|
|
154
|
+
|
|
155
|
+
if (!obj?.fields) return obj;
|
|
156
|
+
|
|
157
|
+
const processedFields = obj.fields.map(field => {
|
|
158
|
+
if (field.type === 'image' && typeof field.example_value === 'string') {
|
|
159
|
+
const extractedUrl = extractImageUrl(field.example_value, { isProd });
|
|
160
|
+
return {
|
|
161
|
+
...field,
|
|
162
|
+
example_value: extractedUrl || field.example_value
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
return field;
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
...obj,
|
|
170
|
+
fields: processedFields
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
3
174
|
export function listComponents() {
|
|
4
175
|
const metadatas = components
|
|
5
176
|
.map(c => c.component.metadata)
|
|
@@ -30,8 +30,16 @@ interface Props {
|
|
|
30
30
|
selector?: string;
|
|
31
31
|
/** Tipo de retorno por defecto */
|
|
32
32
|
defaultReturnType?: 'url' | 'assetId' | 'object' | 'json' | 'base64';
|
|
33
|
+
/** Modo UI por defecto */
|
|
34
|
+
defaultModeUI?: 'full' | 'gallery-only' | 'upload-only' | 'crop-only';
|
|
33
35
|
/** Tamaño del modal (default: 'xlarge') */
|
|
34
36
|
sizeModal?: 'small' | 'medium' | 'large' | 'xlarge' | 'fullscreen';
|
|
37
|
+
/** Texto del botón por defecto */
|
|
38
|
+
defaultButtonText?: string;
|
|
39
|
+
/** Permitir crops adicionales además de los obligatorios */
|
|
40
|
+
allowAdditionalCrops?: boolean;
|
|
41
|
+
/** Máximo número de crops permitidos */
|
|
42
|
+
maxCrops?: number;
|
|
35
43
|
/** Versión de limbo-component a usar (default: 'latest') */
|
|
36
44
|
version?: string;
|
|
37
45
|
/** Usar assets locales en vez de CDN (para desarrollo con npm link) */
|
|
@@ -43,7 +51,11 @@ const {
|
|
|
43
51
|
prod = false,
|
|
44
52
|
selector = '.js-limbo',
|
|
45
53
|
defaultReturnType = 'json',
|
|
54
|
+
defaultModeUI = 'full',
|
|
46
55
|
sizeModal = 'xlarge',
|
|
56
|
+
defaultButtonText = 'Seleccionar imagen',
|
|
57
|
+
allowAdditionalCrops = true,
|
|
58
|
+
maxCrops = 10,
|
|
47
59
|
version = 'latest',
|
|
48
60
|
useLocalAssets = !prod
|
|
49
61
|
} = Astro.props;
|
|
@@ -57,7 +69,11 @@ const initScript = generateLimboInitScript({
|
|
|
57
69
|
prod,
|
|
58
70
|
selector,
|
|
59
71
|
defaultReturnType,
|
|
60
|
-
|
|
72
|
+
defaultModeUI,
|
|
73
|
+
sizeModal,
|
|
74
|
+
defaultButtonText,
|
|
75
|
+
allowAdditionalCrops,
|
|
76
|
+
maxCrops
|
|
61
77
|
});
|
|
62
78
|
---
|
|
63
79
|
|
package/src/limbo/init.ts
CHANGED
|
@@ -1,16 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Limbo Integration Module for libreria-astro-lefebvre
|
|
3
3
|
*
|
|
4
|
+
* v2.0 - Actualizado para limbo-component v2.0
|
|
5
|
+
*
|
|
4
6
|
* Este módulo gestiona la inicialización y configuración de limbo-component
|
|
5
7
|
* para ser usado en cualquier portal que consuma esta librería.
|
|
8
|
+
*
|
|
9
|
+
* Soporta configuración via data attributes en inputs HTML:
|
|
10
|
+
* - data-mandatorycrops: JSON array de crops obligatorios
|
|
11
|
+
* - data-modeui: full | gallery-only | upload-only | crop-only
|
|
12
|
+
* - data-returntype: url | assetId | object | json | base64
|
|
13
|
+
* - data-allowadditionalcrops: true | false
|
|
14
|
+
* - data-maxcrops: number
|
|
15
|
+
* - data-buttontext: texto personalizado del botón
|
|
6
16
|
*/
|
|
7
17
|
|
|
18
|
+
export interface MandatoryCrop {
|
|
19
|
+
label: string;
|
|
20
|
+
width?: number;
|
|
21
|
+
height?: number;
|
|
22
|
+
required?: boolean;
|
|
23
|
+
preset_aspect?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
8
26
|
export interface LimboConfig {
|
|
9
27
|
publicKey: string;
|
|
10
28
|
prod?: boolean;
|
|
11
29
|
selector?: string;
|
|
12
|
-
defaultReturnType?:
|
|
13
|
-
|
|
30
|
+
defaultReturnType?: "url" | "assetId" | "object" | "json" | "base64";
|
|
31
|
+
defaultModeUI?: "full" | "gallery-only" | "upload-only" | "crop-only";
|
|
32
|
+
sizeModal?: "small" | "medium" | "large" | "xlarge" | "fullscreen";
|
|
33
|
+
defaultButtonText?: string;
|
|
34
|
+
allowAdditionalCrops?: boolean;
|
|
35
|
+
maxCrops?: number;
|
|
14
36
|
onSelect?: (data: any) => void;
|
|
15
37
|
onError?: (error: Error) => void;
|
|
16
38
|
}
|
|
@@ -27,14 +49,18 @@ export function generateLimboInitScript(config: LimboConfig): string {
|
|
|
27
49
|
const {
|
|
28
50
|
publicKey,
|
|
29
51
|
prod = false,
|
|
30
|
-
selector =
|
|
31
|
-
defaultReturnType =
|
|
32
|
-
|
|
52
|
+
selector = ".js-limbo",
|
|
53
|
+
defaultReturnType = "json",
|
|
54
|
+
defaultModeUI = "full",
|
|
55
|
+
sizeModal = "xlarge",
|
|
56
|
+
defaultButtonText = "Seleccionar imagen",
|
|
57
|
+
allowAdditionalCrops = true,
|
|
58
|
+
maxCrops = 10,
|
|
33
59
|
} = config;
|
|
34
60
|
|
|
35
61
|
// URL del proxy para evitar CORS (usa endpoint local del portal)
|
|
36
62
|
// El proxy está en /api/limbo-token del portal que consume la librería
|
|
37
|
-
const tokenProxyUrl =
|
|
63
|
+
const tokenProxyUrl = "/api/limbo-token";
|
|
38
64
|
|
|
39
65
|
return `
|
|
40
66
|
(function() {
|
|
@@ -79,11 +105,41 @@ export function generateLimboInitScript(config: LimboConfig): string {
|
|
|
79
105
|
}
|
|
80
106
|
}
|
|
81
107
|
|
|
108
|
+
// Estado de inicialización
|
|
109
|
+
var initRetrys = 0;
|
|
110
|
+
var maxRetrys = 20;
|
|
111
|
+
|
|
112
|
+
// Función global para re-escanear inputs (útil después de hydration de Vue/React)
|
|
113
|
+
window.LimboRescan = function(selector) {
|
|
114
|
+
var targetSelector = selector || '${selector}';
|
|
115
|
+
var inputs = document.querySelectorAll(targetSelector);
|
|
116
|
+
var processed = 0;
|
|
117
|
+
console.log('[Limbo] Rescan manual: buscando inputs con selector', targetSelector);
|
|
118
|
+
inputs.forEach(function(input) {
|
|
119
|
+
if (input.tagName === 'INPUT' && !input.dataset.limboProcessed) {
|
|
120
|
+
try {
|
|
121
|
+
Limbo.autoInputs._processInput(input);
|
|
122
|
+
input.dataset.limboProcessed = 'true';
|
|
123
|
+
processed++;
|
|
124
|
+
} catch (e) {
|
|
125
|
+
console.error('[Limbo] Error procesando input:', e);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
console.log('[Limbo] Rescan completado:', processed, 'inputs procesados de', inputs.length, 'encontrados');
|
|
130
|
+
return processed;
|
|
131
|
+
};
|
|
132
|
+
|
|
82
133
|
// Esperamos a que Limbo esté disponible
|
|
83
134
|
function initLimbo() {
|
|
84
135
|
if (typeof Limbo === 'undefined') {
|
|
85
|
-
|
|
86
|
-
|
|
136
|
+
initRetrys++;
|
|
137
|
+
if (initRetrys <= maxRetrys) {
|
|
138
|
+
console.warn('[Limbo] Limbo no está cargado. Reintentando... (' + initRetrys + '/' + maxRetrys + ')');
|
|
139
|
+
setTimeout(initLimbo, 100);
|
|
140
|
+
} else {
|
|
141
|
+
console.error('[Limbo] No se pudo cargar Limbo después de ' + maxRetrys + ' intentos.');
|
|
142
|
+
}
|
|
87
143
|
return;
|
|
88
144
|
}
|
|
89
145
|
|
|
@@ -104,10 +160,17 @@ export function generateLimboInitScript(config: LimboConfig): string {
|
|
|
104
160
|
// Configurar auto-inputs para detectar elementos con el selector
|
|
105
161
|
Limbo.configureAutoInputs({
|
|
106
162
|
selector: '${selector}',
|
|
107
|
-
buttonText: '
|
|
163
|
+
buttonText: '${defaultButtonText}',
|
|
108
164
|
returnType: '${defaultReturnType}',
|
|
165
|
+
modeUI: '${defaultModeUI}',
|
|
166
|
+
allowAdditionalCrops: ${allowAdditionalCrops},
|
|
167
|
+
maxCrops: ${maxCrops},
|
|
168
|
+
// 🔧 Los recortes se guardan en Limbo para garantizar persistencia
|
|
169
|
+
// Los recortes del page-builder se pueden identificar por su nombre (contienen dimensiones)
|
|
170
|
+
localCropsOnly: false,
|
|
109
171
|
|
|
110
172
|
// Parsear configuración del input desde datasets
|
|
173
|
+
// Los valores del dataset tienen prioridad sobre los defaults
|
|
111
174
|
parseInputConfig: function(input) {
|
|
112
175
|
var config = {};
|
|
113
176
|
|
|
@@ -130,11 +193,68 @@ export function generateLimboInitScript(config: LimboConfig): string {
|
|
|
130
193
|
config.modeUI = input.dataset.modeui;
|
|
131
194
|
}
|
|
132
195
|
|
|
196
|
+
// Parsear allowAdditionalCrops
|
|
197
|
+
if (input.dataset.allowadditionalcrops !== undefined) {
|
|
198
|
+
config.allowAdditionalCrops = input.dataset.allowadditionalcrops === 'true';
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Parsear maxCrops
|
|
202
|
+
if (input.dataset.maxcrops) {
|
|
203
|
+
config.maxCrops = parseInt(input.dataset.maxcrops, 10);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Parsear buttonText personalizado
|
|
207
|
+
if (input.dataset.buttontext) {
|
|
208
|
+
config.buttonText = input.dataset.buttontext;
|
|
209
|
+
}
|
|
210
|
+
|
|
133
211
|
return config;
|
|
134
212
|
}
|
|
135
213
|
});
|
|
136
214
|
|
|
137
215
|
console.log('[Limbo] Integración configurada. Buscando inputs con selector: ${selector}');
|
|
216
|
+
|
|
217
|
+
// Debug: Verificar que el observer esté activo
|
|
218
|
+
console.log('[Limbo] AutoInputs stats:', Limbo.autoInputs?.getStats?.() || 'N/A');
|
|
219
|
+
|
|
220
|
+
// Debug: Escanear manualmente después de un delay para Vue/React hydration
|
|
221
|
+
setTimeout(function() {
|
|
222
|
+
console.log('[Limbo] Re-escaneando inputs después de hydration...');
|
|
223
|
+
var inputs = document.querySelectorAll('${selector}');
|
|
224
|
+
console.log('[Limbo] Encontrados ' + inputs.length + ' inputs con selector ${selector}');
|
|
225
|
+
inputs.forEach(function(input, idx) {
|
|
226
|
+
console.log('[Limbo] Input ' + idx + ':', input.tagName, input.className, input.id || input.name);
|
|
227
|
+
// Forzar procesamiento si es un input válido
|
|
228
|
+
if (input.tagName === 'INPUT' && !input.dataset.limboProcessed) {
|
|
229
|
+
try {
|
|
230
|
+
Limbo.autoInputs._processInput(input);
|
|
231
|
+
input.dataset.limboProcessed = 'true';
|
|
232
|
+
console.log('[Limbo] Input procesado manualmente:', input.id || input.name);
|
|
233
|
+
} catch (e) {
|
|
234
|
+
console.error('[Limbo] Error procesando input:', e);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
}, 1000); // Esperar 1 segundo para Vue hydration
|
|
239
|
+
|
|
240
|
+
// Segunda pasada después de 2.5 segundos para componentes Vue más lentos
|
|
241
|
+
setTimeout(function() {
|
|
242
|
+
var inputs = document.querySelectorAll('${selector}');
|
|
243
|
+
var pendingInputs = Array.from(inputs).filter(function(input) {
|
|
244
|
+
return input.tagName === 'INPUT' && !input.dataset.limboProcessed;
|
|
245
|
+
});
|
|
246
|
+
if (pendingInputs.length > 0) {
|
|
247
|
+
console.log('[Limbo] Segunda pasada: procesando ' + pendingInputs.length + ' inputs pendientes');
|
|
248
|
+
pendingInputs.forEach(function(input) {
|
|
249
|
+
try {
|
|
250
|
+
Limbo.autoInputs._processInput(input);
|
|
251
|
+
input.dataset.limboProcessed = 'true';
|
|
252
|
+
} catch (e) {
|
|
253
|
+
console.error('[Limbo] Error procesando input:', e);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}, 2500);
|
|
138
258
|
}
|
|
139
259
|
|
|
140
260
|
// Iniciar cuando el DOM esté listo
|
|
@@ -150,25 +270,26 @@ export function generateLimboInitScript(config: LimboConfig): string {
|
|
|
150
270
|
/**
|
|
151
271
|
* Genera las URLs de los assets de Limbo (CSS y JS)
|
|
152
272
|
*/
|
|
153
|
-
export function getLimboAssetUrls(
|
|
154
|
-
|
|
155
|
-
|
|
273
|
+
export function getLimboAssetUrls(
|
|
274
|
+
options: { prod?: boolean; version?: string } = {}
|
|
275
|
+
) {
|
|
276
|
+
const { prod = false, version = "latest" } = options;
|
|
156
277
|
// En producción, usar CDN de npm o URL de producción
|
|
157
278
|
if (prod) {
|
|
158
279
|
return {
|
|
159
280
|
css: `https://unpkg.com/limbo-component@${version}/dist/limbo.css`,
|
|
160
|
-
js: `https://unpkg.com/limbo-component@${version}/dist/limbo.min.js
|
|
281
|
+
js: `https://unpkg.com/limbo-component@${version}/dist/limbo.min.js`,
|
|
161
282
|
};
|
|
162
283
|
}
|
|
163
284
|
|
|
164
|
-
// En desarrollo, usar el build local
|
|
285
|
+
// En desarrollo, usar el build local desde public/limbo
|
|
165
286
|
return {
|
|
166
|
-
css:
|
|
167
|
-
js:
|
|
287
|
+
css: "/limbo/limbo.css",
|
|
288
|
+
js: "/limbo/limbo.min.js",
|
|
168
289
|
};
|
|
169
290
|
}
|
|
170
291
|
|
|
171
292
|
export default {
|
|
172
293
|
generateLimboInitScript,
|
|
173
|
-
getLimboAssetUrls
|
|
294
|
+
getLimboAssetUrls,
|
|
174
295
|
};
|