libreria-astro-lefebvre 0.1.26 → 0.1.28
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/package.json +1 -1
- package/src/carbins/Mapa_2026_Girona.ts +111 -0
- package/src/carbins/Modal_2026_Almeria.ts +117 -0
- package/src/components/Astro/Mapa_2026_Girona.astro +130 -0
- package/src/components/Astro/Modal_2026_Almeria.astro +152 -0
- package/src/generated/componentRegistry.ts +4 -0
- package/src/index.ts +5 -1
package/package.json
CHANGED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { ComponentMetadata } from '../interfaces/types';
|
|
2
|
+
|
|
3
|
+
export const metadata: ComponentMetadata = {
|
|
4
|
+
component_name: 'Mapa_2026_Girona',
|
|
5
|
+
category: 'Contenido de Imagen',
|
|
6
|
+
name: 'Mapa de contacto Google 2026',
|
|
7
|
+
description: 'Sección de contacto con un mapa de Google Maps embebido (no requiere API key) junto a un panel con los datos de contacto: dirección, teléfono, email y horario. El mapa se construye automáticamente a partir de la dirección, o se puede pegar directamente el enlace de "Insertar un mapa" de Google. Incluye botón "Cómo llegar" y structured data schema.org (LocalBusiness + PostalAddress)',
|
|
8
|
+
framework: 'Astro',
|
|
9
|
+
priority: 1,
|
|
10
|
+
tags: ['mapa', 'google maps', 'contacto', 'ubicacion', 'direccion'],
|
|
11
|
+
fields: [
|
|
12
|
+
{
|
|
13
|
+
name: 'title',
|
|
14
|
+
type: 'text',
|
|
15
|
+
help: 'Título de la sección (opcional). Se muestra centrado sobre el mapa',
|
|
16
|
+
label: 'Título',
|
|
17
|
+
mandatory: false,
|
|
18
|
+
example_value: 'Dónde encontrarnos'
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: 'description',
|
|
22
|
+
type: 'textArea',
|
|
23
|
+
help: 'Texto introductorio opcional que se muestra bajo el título',
|
|
24
|
+
label: 'Descripción',
|
|
25
|
+
mandatory: false,
|
|
26
|
+
example_value: 'Ven a visitarnos o ponte en contacto con nosotros.'
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: 'address',
|
|
30
|
+
type: 'text',
|
|
31
|
+
help: 'Dirección completa del lugar. Se usa para generar el mapa, el enlace "Cómo llegar" y el structured data. Ejemplo: "Carrer de la Força, 8, 17004 Girona"',
|
|
32
|
+
label: 'Dirección',
|
|
33
|
+
mandatory: true,
|
|
34
|
+
example_value: 'Carrer de la Força, 8, 17004 Girona'
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'embedUrl',
|
|
38
|
+
type: 'textArea',
|
|
39
|
+
help: 'Opcional. Si quieres un mapa más preciso, pega aquí el enlace del campo "src" que aparece en Google Maps > Compartir > Insertar un mapa. Si lo rellenas, tiene prioridad sobre la dirección',
|
|
40
|
+
label: 'Enlace de inserción de Google Maps (opcional)',
|
|
41
|
+
mandatory: false,
|
|
42
|
+
example_value: ''
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'zoom',
|
|
46
|
+
type: 'select',
|
|
47
|
+
help: 'Nivel de zoom del mapa cuando se genera a partir de la dirección. Valores bajos alejan, valores altos acercan. Se ignora si se rellena el enlace de inserción',
|
|
48
|
+
label: 'Zoom del mapa',
|
|
49
|
+
options: ['10', '12', '14', '15', '16', '17', '18'],
|
|
50
|
+
options_labels: ['10 (ciudad)', '12', '14', '15 (barrio)', '16', '17', '18 (calle)'],
|
|
51
|
+
example_value: '15'
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'mapPosition',
|
|
55
|
+
type: 'select',
|
|
56
|
+
help: 'Posición del mapa respecto al panel de datos de contacto en escritorio',
|
|
57
|
+
label: 'Posición del mapa',
|
|
58
|
+
options: ['right', 'left'],
|
|
59
|
+
options_labels: ['Mapa a la derecha', 'Mapa a la izquierda'],
|
|
60
|
+
example_value: 'right'
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'backgroundColor',
|
|
64
|
+
type: 'select',
|
|
65
|
+
help: 'Color de fondo de la sección. "Transparente" hereda el fondo de la página. Si eliges un color, la sección añade padding y bordes redondeados',
|
|
66
|
+
label: 'Color de fondo',
|
|
67
|
+
options: ['transparent', '#F8F8F8', '#F2F2F2', '#DDE0EC', '#262626'],
|
|
68
|
+
options_labels: ['Transparente', 'Gris muy claro', 'Gris claro', 'Azul claro', 'Oscuro'],
|
|
69
|
+
example_value: 'transparent'
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'textColor',
|
|
73
|
+
type: 'text',
|
|
74
|
+
help: 'Color del texto y los iconos en formato hex. Usa un tono oscuro (ej. #262626) sobre fondos claros y uno claro (ej. #f2f3f8) sobre fondos oscuros. El panel de contacto se adapta automáticamente',
|
|
75
|
+
label: 'Color del texto',
|
|
76
|
+
mandatory: false,
|
|
77
|
+
example_value: '#262626'
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'showContactInfo',
|
|
81
|
+
type: 'boolean',
|
|
82
|
+
help: 'Muestra el panel lateral con los datos de contacto. Si se desactiva, el mapa ocupa todo el ancho',
|
|
83
|
+
label: 'Mostrar datos de contacto',
|
|
84
|
+
example_value: true
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: 'phone',
|
|
88
|
+
type: 'text',
|
|
89
|
+
help: 'Teléfono de contacto. Se muestra como enlace clicable (tel:)',
|
|
90
|
+
label: 'Teléfono',
|
|
91
|
+
mandatory: false,
|
|
92
|
+
example_value: '+34 972 00 00 00'
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: 'email',
|
|
96
|
+
type: 'text',
|
|
97
|
+
help: 'Email de contacto. Se muestra como enlace clicable (mailto:)',
|
|
98
|
+
label: 'Email',
|
|
99
|
+
mandatory: false,
|
|
100
|
+
example_value: 'contacto@ejemplo.com'
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: 'schedule',
|
|
104
|
+
type: 'textArea',
|
|
105
|
+
help: 'Horario de atención. Admite varias líneas',
|
|
106
|
+
label: 'Horario',
|
|
107
|
+
mandatory: false,
|
|
108
|
+
example_value: 'Lunes a Viernes: 9:00 - 18:00'
|
|
109
|
+
}
|
|
110
|
+
]
|
|
111
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { ComponentMetadata } from '../interfaces/types';
|
|
2
|
+
|
|
3
|
+
export const metadata: ComponentMetadata = {
|
|
4
|
+
component_name: 'Modal_2026_Almeria',
|
|
5
|
+
category: 'Formulario',
|
|
6
|
+
name: 'Modal con formulario LF2 (businessAction) disparable por botón propio o enlace externo 2026',
|
|
7
|
+
description: 'Mezcla de Modal_2025_Sagunto y Formulario_2025_Seul: modal con backdrop blur que contiene imagen lateral opcional, título h2, descripción HTML y formulario LF2 inyectado dinámicamente vía apiManager.pintarFormularioAutogestionado. A diferencia de Sagunto, soporta businessAction (accioncomercial) además de idebook/codprod/nomprod. Puede abrirse con su propio botón CTA (si se indica buttonText) y/o desde un enlace externo ya presente en la página (p. ej. el botón de la cabecera) indicando su hash en triggerHash. El formulario se pinta de forma diferida la primera vez que se abre el modal. Requiere LeadformApiManagerClass y apiManager disponibles globalmente en la página',
|
|
8
|
+
framework: 'Astro',
|
|
9
|
+
priority: 1,
|
|
10
|
+
tags: ['boton', 'cta', 'modal', 'formulario', 'lf2', 'popup', 'businessAction', 'trigger-externo'],
|
|
11
|
+
fields: [
|
|
12
|
+
{
|
|
13
|
+
name: 'buttonText',
|
|
14
|
+
type: 'text',
|
|
15
|
+
help: 'Texto visible del botón CTA propio que abre el modal. Si se deja vacío, NO se renderiza ningún botón y el modal solo podrá abrirse desde un disparador externo (triggerHash). No admite HTML',
|
|
16
|
+
label: 'Label del botón (opcional)',
|
|
17
|
+
mandatory: false,
|
|
18
|
+
example_value: 'Pruébalo ya'
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: 'frontColor',
|
|
22
|
+
type: 'text',
|
|
23
|
+
help: 'Color del texto del botón propio. Acepta nombre CSS (ej. "white") o hex (ej. "#2134F1"). No se invierte en hover. Solo aplica si hay buttonText',
|
|
24
|
+
label: 'Color del texto (frontal)',
|
|
25
|
+
mandatory: false,
|
|
26
|
+
example_value: '#ffffff'
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: 'backColor',
|
|
30
|
+
type: 'text',
|
|
31
|
+
help: 'Color de fondo del botón propio. Acepta nombre CSS o hex. Solo aplica si hay buttonText',
|
|
32
|
+
label: 'Color de fondo',
|
|
33
|
+
mandatory: false,
|
|
34
|
+
example_value: '#2134F1'
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'borderColor',
|
|
38
|
+
type: 'text',
|
|
39
|
+
help: 'Color del borde del botón propio (1px sólido). Acepta nombre CSS o hex. Solo aplica si hay buttonText',
|
|
40
|
+
label: 'Color del borde',
|
|
41
|
+
mandatory: false,
|
|
42
|
+
example_value: '#2134F1'
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'triggerHash',
|
|
46
|
+
type: 'text',
|
|
47
|
+
help: 'Disparador externo opcional: hash al que apunta un enlace <a href> ya existente en la página (p. ej. el botón de la cabecera). Se enlazan los clicks de esos enlaces para abrir el modal e impedir la navegación. Debe incluir la almohadilla. Ej.: "#formulario-genial"',
|
|
48
|
+
label: 'Hash del disparador externo (opcional)',
|
|
49
|
+
mandatory: false,
|
|
50
|
+
example_value: ''
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: 'imageModal',
|
|
54
|
+
type: 'image',
|
|
55
|
+
help: 'Imagen de la columna izquierda del modal. Si se deja vacía, el modal no incluye columna de imagen y el texto ocupa el ancho completo',
|
|
56
|
+
label: 'Imagen del modal',
|
|
57
|
+
mandatory: false,
|
|
58
|
+
example_value: 'https://assets.lefebvre.es/media/img/preview-comp/comp-w-4-3.png'
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: 'altModal',
|
|
62
|
+
type: 'text',
|
|
63
|
+
help: 'Texto alternativo de la imagen del modal para accesibilidad. Solo aplica si el modal tiene imagen',
|
|
64
|
+
label: 'Alt de la imagen del modal',
|
|
65
|
+
mandatory: false,
|
|
66
|
+
example_value: 'Captura del dashboard de GenIA-L con el asistente IA'
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: 'titleModal',
|
|
70
|
+
type: 'text',
|
|
71
|
+
help: 'Título del modal (h2). Si se deja vacío no se renderiza. No admite HTML',
|
|
72
|
+
label: 'Título del modal',
|
|
73
|
+
mandatory: false,
|
|
74
|
+
example_value: 'Aprende a utilizar GenIA-L'
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: 'descriptionModal',
|
|
78
|
+
type: 'text',
|
|
79
|
+
help: 'Descripción del modal que aparece debajo del título, antes del formulario. Si se deja vacía no se renderiza. Admite HTML',
|
|
80
|
+
label: 'Descripción del modal',
|
|
81
|
+
mandatory: false,
|
|
82
|
+
example_value: 'Solicita tu demo personalizada y comprueba por ti mismo la certeza jurídica de GenIA-L.'
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: 'lf2FormTitle',
|
|
86
|
+
type: 'text',
|
|
87
|
+
help: 'Nombre EXACTO del formulario tal como está dado de alta en LF2. Se pasa a apiManager.pintarFormularioAutogestionado() para cargar el formulario correcto. Si es erróneo, el formulario no se renderizará. Requiere LeadformApiManagerClass y apiManager globales',
|
|
88
|
+
label: 'Nombre del formulario en LF2',
|
|
89
|
+
mandatory: false,
|
|
90
|
+
example_value: '27925 Formulario Lead Web GenIA-L'
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: 'businessAction',
|
|
94
|
+
type: 'text',
|
|
95
|
+
help: 'Identificador de acción comercial de LF2 (accioncomercial) que se asociará al lead. Si se deja vacío, no se envía acción comercial (el formulario funciona pero sin tracking de acción específica)',
|
|
96
|
+
label: 'businessAction del formulario en LF2',
|
|
97
|
+
mandatory: false,
|
|
98
|
+
example_value: '27925'
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: 'idebook',
|
|
102
|
+
type: 'text',
|
|
103
|
+
help: 'ID del ebook, whitepaper o documento registrado en BI (formato EXXXXXXX) que se pasa como parámetro oculto (idebook/codprod) al formulario LF2. Solo para formularios que terminan en descarga',
|
|
104
|
+
label: 'ID de documento en BI (opcional)',
|
|
105
|
+
mandatory: false,
|
|
106
|
+
example_value: ''
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: 'nomprod',
|
|
110
|
+
type: 'text',
|
|
111
|
+
help: 'Nombre del producto (ebook, whitepaper o documento). Se envía como parámetro oculto junto a idebook. Solo aplica si se indica idebook',
|
|
112
|
+
label: 'Nombre del producto (opcional)',
|
|
113
|
+
mandatory: false,
|
|
114
|
+
example_value: ''
|
|
115
|
+
},
|
|
116
|
+
]
|
|
117
|
+
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
---
|
|
2
|
+
const {
|
|
3
|
+
title = '',
|
|
4
|
+
description = '',
|
|
5
|
+
address = '',
|
|
6
|
+
embedUrl = '',
|
|
7
|
+
zoom = '15',
|
|
8
|
+
mapPosition = 'right',
|
|
9
|
+
showContactInfo = true,
|
|
10
|
+
phone = '',
|
|
11
|
+
email = '',
|
|
12
|
+
schedule = '',
|
|
13
|
+
backgroundColor = 'transparent',
|
|
14
|
+
textColor = '#262626',
|
|
15
|
+
} = Astro.props;
|
|
16
|
+
|
|
17
|
+
// 'transparent' deja el fondo del contenedor padre.
|
|
18
|
+
const hasBackground = backgroundColor !== 'transparent';
|
|
19
|
+
|
|
20
|
+
// Si el usuario pega el src completo del iframe de "Insertar un mapa" de Google, se usa tal cual.
|
|
21
|
+
// Si no, se construye un embed a partir de la dirección (no requiere API key).
|
|
22
|
+
const mapSrc = embedUrl
|
|
23
|
+
? embedUrl
|
|
24
|
+
: address
|
|
25
|
+
? `https://maps.google.com/maps?q=${encodeURIComponent(address)}&z=${encodeURIComponent(zoom)}&output=embed`
|
|
26
|
+
: '';
|
|
27
|
+
|
|
28
|
+
// Enlace "Cómo llegar" que abre Google Maps con la dirección como destino.
|
|
29
|
+
const directionsUrl = address
|
|
30
|
+
? `https://www.google.com/maps/dir/?api=1&destination=${encodeURIComponent(address)}`
|
|
31
|
+
: '';
|
|
32
|
+
|
|
33
|
+
const hasInfo = showContactInfo && (address || phone || email || schedule);
|
|
34
|
+
|
|
35
|
+
const escapeJson = (s = '') => String(s).replace(/<[^>]*>/g, '').replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/</g, '\\u003c').replace(/>/g, '\\u003e').replace(/[\n\r\t]+/g, ' ');
|
|
36
|
+
|
|
37
|
+
const structuredData = address ? `<script type="application/ld+json">
|
|
38
|
+
{
|
|
39
|
+
"@context": "https://schema.org",
|
|
40
|
+
"@type": "LocalBusiness"${title ? `,
|
|
41
|
+
"name": "${escapeJson(title)}"` : ''},
|
|
42
|
+
"address": {
|
|
43
|
+
"@type": "PostalAddress",
|
|
44
|
+
"streetAddress": "${escapeJson(address)}"
|
|
45
|
+
}${phone ? `,
|
|
46
|
+
"telephone": "${escapeJson(phone)}"` : ''}${email ? `,
|
|
47
|
+
"email": "${escapeJson(email)}"` : ''}
|
|
48
|
+
}
|
|
49
|
+
</script>` : '';
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
<section class={`w-full max-w-[1200px] mx-auto py-10 ${hasBackground ? 'px-6 md:px-8 rounded-2xl' : 'px-6 md:px-0'}`} style={`color:${textColor}${hasBackground ? `;background-color:${backgroundColor}` : ''}`}>
|
|
53
|
+
{(title || description) && (
|
|
54
|
+
<div class="mb-8 text-center">
|
|
55
|
+
{title && (
|
|
56
|
+
<h2 class="font-poppins text-3xl md:text-4xl not-italic font-semibold leading-tight tracking-tight mb-4" set:html={title} />
|
|
57
|
+
)}
|
|
58
|
+
{description && (
|
|
59
|
+
<p class="font-inter font-normal text-base md:text-lg not-italic max-w-2xl mx-auto" set:html={description} />
|
|
60
|
+
)}
|
|
61
|
+
</div>
|
|
62
|
+
)}
|
|
63
|
+
|
|
64
|
+
<div class={`w-full flex flex-col gap-6 ${hasInfo ? (mapPosition === 'left' ? 'lg:flex-row' : 'lg:flex-row-reverse') : ''} items-stretch`}>
|
|
65
|
+
<div class={`w-full ${hasInfo ? 'lg:w-2/3' : ''}`}>
|
|
66
|
+
{mapSrc ? (
|
|
67
|
+
<iframe
|
|
68
|
+
src={mapSrc}
|
|
69
|
+
title={title || 'Mapa de ubicación'}
|
|
70
|
+
width="100%"
|
|
71
|
+
height="450"
|
|
72
|
+
style="border:0;"
|
|
73
|
+
loading="lazy"
|
|
74
|
+
referrerpolicy="no-referrer-when-downgrade"
|
|
75
|
+
allowfullscreen
|
|
76
|
+
class="w-full h-[350px] md:h-[450px] rounded-2xl shadow-sm"
|
|
77
|
+
></iframe>
|
|
78
|
+
) : (
|
|
79
|
+
<div class="w-full h-[350px] md:h-[450px] rounded-2xl bg-[#F2F2F2] flex items-center justify-center text-[#363942] font-inter text-sm">
|
|
80
|
+
Añade una dirección o un enlace de Google Maps para mostrar el mapa
|
|
81
|
+
</div>
|
|
82
|
+
)}
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
{hasInfo && (
|
|
86
|
+
<div class="w-full lg:w-1/3 flex flex-col justify-center gap-5 p-6 rounded-2xl" style="background-color:color-mix(in srgb, currentColor 6%, transparent);border:1px solid color-mix(in srgb, currentColor 15%, transparent)">
|
|
87
|
+
{address && (
|
|
88
|
+
<div class="flex items-start gap-3">
|
|
89
|
+
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 mt-0.5 shrink-0">
|
|
90
|
+
<path d="M12 21s7-5.686 7-11a7 7 0 1 0-14 0c0 5.314 7 11 7 11Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"></path>
|
|
91
|
+
<circle cx="12" cy="10" r="2.5" stroke="currentColor" stroke-width="1.5"></circle>
|
|
92
|
+
</svg>
|
|
93
|
+
<p class="font-inter text-base not-italic" set:html={address} />
|
|
94
|
+
</div>
|
|
95
|
+
)}
|
|
96
|
+
{phone && (
|
|
97
|
+
<div class="flex items-center gap-3">
|
|
98
|
+
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 shrink-0">
|
|
99
|
+
<path d="M3 5.5C3 4.67 3.67 4 4.5 4h2.1c.46 0 .86.31.97.76l.86 3.42a1 1 0 0 1-.27.95l-1.5 1.5a14.5 14.5 0 0 0 5.25 5.25l1.5-1.5a1 1 0 0 1 .95-.27l3.42.86c.45.11.76.51.76.97v2.1c0 .83-.67 1.5-1.5 1.5A16.5 16.5 0 0 1 3 5.5Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"></path>
|
|
100
|
+
</svg>
|
|
101
|
+
<a href={`tel:${String(phone).replace(/\s+/g, '')}`} class="font-inter text-base not-italic hover:text-[#2134F1] transition-colors duration-300">{phone}</a>
|
|
102
|
+
</div>
|
|
103
|
+
)}
|
|
104
|
+
{email && (
|
|
105
|
+
<div class="flex items-center gap-3">
|
|
106
|
+
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 shrink-0">
|
|
107
|
+
<rect x="3" y="5" width="18" height="14" rx="2" stroke="currentColor" stroke-width="1.5"></rect>
|
|
108
|
+
<path d="m4 7 8 5 8-5" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"></path>
|
|
109
|
+
</svg>
|
|
110
|
+
<a href={`mailto:${email}`} class="font-inter text-base not-italic hover:text-[#2134F1] transition-colors duration-300 break-all">{email}</a>
|
|
111
|
+
</div>
|
|
112
|
+
)}
|
|
113
|
+
{schedule && (
|
|
114
|
+
<div class="flex items-start gap-3">
|
|
115
|
+
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 mt-0.5 shrink-0">
|
|
116
|
+
<circle cx="12" cy="12" r="9" stroke="currentColor" stroke-width="1.5"></circle>
|
|
117
|
+
<path d="M12 7v5l3 2" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
118
|
+
</svg>
|
|
119
|
+
<div class="font-inter text-base not-italic" set:html={schedule} />
|
|
120
|
+
</div>
|
|
121
|
+
)}
|
|
122
|
+
{directionsUrl && (
|
|
123
|
+
<a href={directionsUrl} target="_blank" rel="noopener noreferrer" class="inline-block w-fit h-11 md:h-12 text-[#2134F1] font-inter font-semibold bg-[#ffffff] px-5 py-3 border border-[#2134F1] rounded-lg shadow-md hover:bg-[#DDE0EC] transition-all duration-300 text-sm md:text-base">Cómo llegar</a>
|
|
124
|
+
)}
|
|
125
|
+
</div>
|
|
126
|
+
)}
|
|
127
|
+
</div>
|
|
128
|
+
</section>
|
|
129
|
+
|
|
130
|
+
{structuredData && <Fragment set:html={structuredData} />}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { extractImageUrl } from '../../lib/functions.js';
|
|
3
|
+
|
|
4
|
+
// Mix de Modal_2025_Sagunto (modal con backdrop blur + formulario LF2 inyectado) y
|
|
5
|
+
// Formulario_2025_Seul (soporte de businessAction → accioncomercial). Además del botón
|
|
6
|
+
// propio opcional, puede dispararse desde un elemento externo de la página (p. ej. el
|
|
7
|
+
// botón "Pruébalo ya" de la cabecera) indicando su hash en triggerHash.
|
|
8
|
+
const {
|
|
9
|
+
// Botón propio (opcional). Si buttonText está vacío, no se renderiza botón y el modal
|
|
10
|
+
// solo se podrá abrir mediante un disparador externo (triggerHash).
|
|
11
|
+
buttonText = "",
|
|
12
|
+
frontColor = 'white',
|
|
13
|
+
backColor = '#2134F1',
|
|
14
|
+
borderColor = '#2134F1',
|
|
15
|
+
|
|
16
|
+
// Disparador externo (opcional): hash al que apunta un <a href> ya existente en la
|
|
17
|
+
// página. Se enlazan los clicks de esos enlaces para abrir el modal. Ej.: "#formulario-genial".
|
|
18
|
+
triggerHash = '',
|
|
19
|
+
|
|
20
|
+
imageModal = '',
|
|
21
|
+
altModal = '',
|
|
22
|
+
titleModal = '',
|
|
23
|
+
descriptionModal = '',
|
|
24
|
+
|
|
25
|
+
lf2FormTitle = '',
|
|
26
|
+
// Acción comercial de LF2 (accioncomercial). Vacío = no se envía (igual que Seul).
|
|
27
|
+
businessAction = '',
|
|
28
|
+
// Parámetros opcionales para formularios que terminan en descarga (igual que Sagunto).
|
|
29
|
+
idebook = '',
|
|
30
|
+
nomprod = '',
|
|
31
|
+
} = Astro.props;
|
|
32
|
+
|
|
33
|
+
const imageModalUrl = extractImageUrl(imageModal);
|
|
34
|
+
const uid = 'almeria-' + Math.random().toString(36).substring(2, 11);
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
{buttonText && (
|
|
38
|
+
<button
|
|
39
|
+
id={`js-open-${uid}`}
|
|
40
|
+
class="text-base font-semibold font-inter inline-block w-fit mt-6 p-3 px-5 h-12 rounded-xl transition-colors duration-300 cursor-pointer border"
|
|
41
|
+
style={`border-color: ${borderColor}; background-color: ${backColor}; color: ${frontColor};`}
|
|
42
|
+
>
|
|
43
|
+
{buttonText}
|
|
44
|
+
</button>
|
|
45
|
+
)}
|
|
46
|
+
|
|
47
|
+
<div id={`modal-${uid}`} class="fixed w-full h-full pt-12 top-0 left-0 z-9999 hidden bg-gray-100/60 backdrop-blur-sm flex items-center justify-center">
|
|
48
|
+
<div class="flex flex-col w-full md:w-4/5 max-w-[1000px] max-h-[calc(100vh-96px)] m-auto p-4 rounded-2xl bg-white shadow-lg border border-[#B6B7BB] overflow-y-auto modal-guia">
|
|
49
|
+
<div class="w-full flex justify-end">
|
|
50
|
+
<button id={`js-close-${uid}`} aria-label="Cerrar" class="js-close-popup w-auto transition-transform hover:scale-125 cursor-pointer">X</button>
|
|
51
|
+
</div>
|
|
52
|
+
<div class="w-full h-full flex flex-col lg:flex-row items-start justify-center gap-8">
|
|
53
|
+
{imageModalUrl && (
|
|
54
|
+
<div class="w-full lg:w-1/3 flex justify-center items-center p-0 lg:p-4 mt-8 lg:mt-0">
|
|
55
|
+
<img src={imageModalUrl} alt={altModal} width="300" height="400" class="w-fit" loading="lazy"/>
|
|
56
|
+
</div>
|
|
57
|
+
)}
|
|
58
|
+
<div class={imageModalUrl ? "w-full lg:w-2/3" : "w-full"}>
|
|
59
|
+
{titleModal && (
|
|
60
|
+
<h2 class="font-poppins text-[#262626] text-[28px] lg:text-[32px] font-normal text-left leading-10 mb-8">{titleModal}</h2>
|
|
61
|
+
)}
|
|
62
|
+
{descriptionModal && (
|
|
63
|
+
<div class="font-inter text-base font-normal leading-6 mb-8" set:html={descriptionModal} />
|
|
64
|
+
)}
|
|
65
|
+
<div id={`lf2-form-container-${uid}`} class="w-full flex flex-col mx-auto gap-3">
|
|
66
|
+
<!--FORMULARIO -->
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<script is:inline define:vars={{ uid, lf2FormTitle, businessAction, idebook, nomprod, triggerHash }}>
|
|
74
|
+
|
|
75
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
76
|
+
|
|
77
|
+
const modal = document.getElementById(`modal-${uid}`);
|
|
78
|
+
if (!modal) return;
|
|
79
|
+
|
|
80
|
+
const configLf2 = {
|
|
81
|
+
fake: true,
|
|
82
|
+
formulario: lf2FormTitle,
|
|
83
|
+
bootstrap: true,
|
|
84
|
+
target: '#' + `lf2-form-container-${uid}`,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const defaultparameters = {};
|
|
88
|
+
if (businessAction && businessAction !== '') {
|
|
89
|
+
defaultparameters.accioncomercial = businessAction;
|
|
90
|
+
}
|
|
91
|
+
if (idebook && idebook !== '') {
|
|
92
|
+
defaultparameters.idebook = idebook;
|
|
93
|
+
defaultparameters.codprod = idebook;
|
|
94
|
+
defaultparameters.nomprod = nomprod;
|
|
95
|
+
}
|
|
96
|
+
if (Object.keys(defaultparameters).length > 0) {
|
|
97
|
+
configLf2.defaultparameters = defaultparameters;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// El formulario se pinta una sola vez, de forma diferida al abrir el modal, para no
|
|
101
|
+
// duplicar el lead-form si la página ya renderiza ese mismo formulario inline.
|
|
102
|
+
let painted = false;
|
|
103
|
+
const paintForm = () => {
|
|
104
|
+
if (painted) return;
|
|
105
|
+
try {
|
|
106
|
+
apiManager.pintarFormularioAutogestionado(configLf2);
|
|
107
|
+
} catch (error) {
|
|
108
|
+
var apiManager = new LeadformApiManagerClass();
|
|
109
|
+
apiManager.pintarFormularioAutogestionado(configLf2);
|
|
110
|
+
}
|
|
111
|
+
painted = true;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const openModal = () => {
|
|
115
|
+
paintForm();
|
|
116
|
+
modal.classList.remove('hidden');
|
|
117
|
+
document.body.style.overflow = 'hidden';
|
|
118
|
+
};
|
|
119
|
+
const closeModal = () => {
|
|
120
|
+
modal.classList.add('hidden');
|
|
121
|
+
document.body.style.overflow = '';
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// Botón propio del componente.
|
|
125
|
+
const ownButton = document.getElementById(`js-open-${uid}`);
|
|
126
|
+
if (ownButton) {
|
|
127
|
+
ownButton.addEventListener('click', (e) => {
|
|
128
|
+
e.preventDefault();
|
|
129
|
+
openModal();
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Disparadores externos: enlaces de la página que apuntan al hash indicado.
|
|
134
|
+
if (triggerHash && triggerHash !== '') {
|
|
135
|
+
document.querySelectorAll(`a[href$="${triggerHash}"]`).forEach((trigger) => {
|
|
136
|
+
trigger.addEventListener('click', (e) => {
|
|
137
|
+
e.preventDefault();
|
|
138
|
+
openModal();
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Cierre: botón X, click en el backdrop y tecla Escape.
|
|
144
|
+
const closeBtn = modal.querySelector(`#js-close-${uid}`);
|
|
145
|
+
if (closeBtn) closeBtn.addEventListener('click', closeModal);
|
|
146
|
+
modal.addEventListener('click', (e) => { if (e.target === modal) closeModal(); });
|
|
147
|
+
document.addEventListener('keydown', (e) => {
|
|
148
|
+
if (e.key === 'Escape' && !modal.classList.contains('hidden')) closeModal();
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
</script>
|
|
@@ -52,7 +52,9 @@ import * as Imagen_2025_Bogota from '../carbins/Imagen_2025_Bogota.ts';
|
|
|
52
52
|
import * as Imagen_2025_Fukushima from '../carbins/Imagen_2025_Fukushima.ts';
|
|
53
53
|
import * as Imagen_2026_Algar from '../carbins/Imagen_2026_Algar.ts';
|
|
54
54
|
import * as Indice_2025_Taiwan from '../carbins/Indice_2025_Taiwan.ts';
|
|
55
|
+
import * as Mapa_2026_Girona from '../carbins/Mapa_2026_Girona.ts';
|
|
55
56
|
import * as Modal_2025_Sagunto from '../carbins/Modal_2025_Sagunto.ts';
|
|
57
|
+
import * as Modal_2026_Almeria from '../carbins/Modal_2026_Almeria.ts';
|
|
56
58
|
import * as Paginacion_2025_Paris from '../carbins/Paginacion_2025_Paris.ts';
|
|
57
59
|
import * as RRSS_2025_Pisa from '../carbins/RRSS_2025_Pisa.ts';
|
|
58
60
|
import * as ReactButton from '../carbins/ReactButton.ts';
|
|
@@ -139,7 +141,9 @@ export const components = [
|
|
|
139
141
|
{component: Imagen_2025_Fukushima},
|
|
140
142
|
{component: Imagen_2026_Algar},
|
|
141
143
|
{component: Indice_2025_Taiwan},
|
|
144
|
+
{component: Mapa_2026_Girona},
|
|
142
145
|
{component: Modal_2025_Sagunto},
|
|
146
|
+
{component: Modal_2026_Almeria},
|
|
143
147
|
{component: Paginacion_2025_Paris},
|
|
144
148
|
{component: RRSS_2025_Pisa},
|
|
145
149
|
{component: ReactButton},
|
package/src/index.ts
CHANGED
|
@@ -59,7 +59,9 @@ import Imagen_2025_Bogota from './components/Astro/Imagen_2025_Bogota.astro';
|
|
|
59
59
|
import Imagen_2025_Fukushima from './components/Astro/Imagen_2025_Fukushima.astro';
|
|
60
60
|
import Imagen_2026_Algar from './components/Astro/Imagen_2026_Algar.astro';
|
|
61
61
|
import Indice_2025_Taiwan from './components/Astro/Indice_2025_Taiwan.astro';
|
|
62
|
+
import Mapa_2026_Girona from './components/Astro/Mapa_2026_Girona.astro';
|
|
62
63
|
import Modal_2025_Sagunto from './components/Astro/Modal_2025_Sagunto.astro';
|
|
64
|
+
import Modal_2026_Almeria from './components/Astro/Modal_2026_Almeria.astro';
|
|
63
65
|
import Paginacion_2025_Paris from './components/Astro/Paginacion_2025_Paris.astro';
|
|
64
66
|
import RRSS_2025_Pisa from './components/Astro/RRSS_2025_Pisa.astro';
|
|
65
67
|
import SEO_Head_Section from './components/Astro/SEO_Head_Section.astro';
|
|
@@ -95,7 +97,7 @@ import ReactButton from './components/React/ReactButton.jsx';
|
|
|
95
97
|
// Exporta todos los componentes uno a uno para que puedan ser usados directamente.
|
|
96
98
|
|
|
97
99
|
|
|
98
|
-
export { VueButton, Author_2025_Algarve, Button, CTA_2025_Formentera, Cabecera_2025_Barcelona, Cabecera_2025_Madrid, Cabecera_2026_Bilbao, Cabecera_2026_Madrid, Card_2025_Malta, Contenido_2025_Alcorcon, Contenido_2025_Cordoba, Contenido_2025_Granada, Contenido_2025_Malaga, Contenido_2025_Montevideo, Contenido_2026_Cabra, Contenido_2026_Denia, Contenido_2026_Dubai, Contenido_2026_Estocolmo, Contenido_2026_Jaen, Contenido_2026_Leon, Contenido_2026_Mallorca, Contenido_2026_Marruecos, Contenido_2026_Menorca, Contenido_2026_Michigan, Contenido_2026_Moraira, Contenido_2026_Mostoles, Contenido_2026_Orcasitas, Contenido_2026_Oslo, Contenido_2026_Quito, Contenido_2026_Seattle, Contenido_2026_Sevilla, Contenido_2026_Tokyo, Contenido_2026_Ubeda, Contenido_2026_Yakarta, CorpFooter, CorpHero, CorpNavigation, Enlace_2025_Venecia, FAQ_2025_Hiroshima, Footer_2025_Napoles, Formulario_2025_Nara, Formulario_2025_Seul, Formulario_2025_Teruel, Formulario_2026_Carabanchel, Formulario_2026_Wichita, Galeria_2026_Segorbe, GeometricShape, GeometricShapeCard, HeaderCorporativo, Hero_2025_Benidorm, Hero_2026_Benidorm, ImageTextSimple, Imagen_2025_Bogota, Imagen_2025_Fukushima, Imagen_2026_Algar, Indice_2025_Taiwan, Modal_2025_Sagunto, Paginacion_2025_Paris, RRSS_2025_Pisa, SEO_Head_Section, SEO_Schema_Page, Separador_2025_Reinosa, Separador_2025_Toledo, Share_2025_Florencia, SpectrumSeparator, Sumario_2025_Beijing, Tabla_2025_Fuenlabrada, Tabla_2026_Cadiz, Tag_2025_Bolonia, TestHijo, TestPadre, Test_2026_Gaza, TextBox, TextImageBackground, TextImageBlock, TextImageCard, TextImageHeader, Texto_2025_Kyoto, Texto_2026_Alicante, Texto_2026_Castellon, Tiempo_2025_Londres, Titulo_2025_Algeciras, Titulo_2025_Santorini, VideoAutoplay, Video_2025_Polop, Video_2025_Valencia, Video_2026_Andujar, ReactButton };
|
|
100
|
+
export { VueButton, Author_2025_Algarve, Button, CTA_2025_Formentera, Cabecera_2025_Barcelona, Cabecera_2025_Madrid, Cabecera_2026_Bilbao, Cabecera_2026_Madrid, Card_2025_Malta, Contenido_2025_Alcorcon, Contenido_2025_Cordoba, Contenido_2025_Granada, Contenido_2025_Malaga, Contenido_2025_Montevideo, Contenido_2026_Cabra, Contenido_2026_Denia, Contenido_2026_Dubai, Contenido_2026_Estocolmo, Contenido_2026_Jaen, Contenido_2026_Leon, Contenido_2026_Mallorca, Contenido_2026_Marruecos, Contenido_2026_Menorca, Contenido_2026_Michigan, Contenido_2026_Moraira, Contenido_2026_Mostoles, Contenido_2026_Orcasitas, Contenido_2026_Oslo, Contenido_2026_Quito, Contenido_2026_Seattle, Contenido_2026_Sevilla, Contenido_2026_Tokyo, Contenido_2026_Ubeda, Contenido_2026_Yakarta, CorpFooter, CorpHero, CorpNavigation, Enlace_2025_Venecia, FAQ_2025_Hiroshima, Footer_2025_Napoles, Formulario_2025_Nara, Formulario_2025_Seul, Formulario_2025_Teruel, Formulario_2026_Carabanchel, Formulario_2026_Wichita, Galeria_2026_Segorbe, GeometricShape, GeometricShapeCard, HeaderCorporativo, Hero_2025_Benidorm, Hero_2026_Benidorm, ImageTextSimple, Imagen_2025_Bogota, Imagen_2025_Fukushima, Imagen_2026_Algar, Indice_2025_Taiwan, Mapa_2026_Girona, Modal_2025_Sagunto, Modal_2026_Almeria, Paginacion_2025_Paris, RRSS_2025_Pisa, SEO_Head_Section, SEO_Schema_Page, Separador_2025_Reinosa, Separador_2025_Toledo, Share_2025_Florencia, SpectrumSeparator, Sumario_2025_Beijing, Tabla_2025_Fuenlabrada, Tabla_2026_Cadiz, Tag_2025_Bolonia, TestHijo, TestPadre, Test_2026_Gaza, TextBox, TextImageBackground, TextImageBlock, TextImageCard, TextImageHeader, Texto_2025_Kyoto, Texto_2026_Alicante, Texto_2026_Castellon, Tiempo_2025_Londres, Titulo_2025_Algeciras, Titulo_2025_Santorini, VideoAutoplay, Video_2025_Polop, Video_2025_Valencia, Video_2026_Andujar, ReactButton };
|
|
99
101
|
|
|
100
102
|
|
|
101
103
|
// Exporta la función listComponents para que sea usado en el Pagebuilder en Vue.
|
|
@@ -166,7 +168,9 @@ export const components = {
|
|
|
166
168
|
Imagen_2025_Fukushima: Imagen_2025_Fukushima,
|
|
167
169
|
Imagen_2026_Algar: Imagen_2026_Algar,
|
|
168
170
|
Indice_2025_Taiwan: Indice_2025_Taiwan,
|
|
171
|
+
Mapa_2026_Girona: Mapa_2026_Girona,
|
|
169
172
|
Modal_2025_Sagunto: Modal_2025_Sagunto,
|
|
173
|
+
Modal_2026_Almeria: Modal_2026_Almeria,
|
|
170
174
|
Paginacion_2025_Paris: Paginacion_2025_Paris,
|
|
171
175
|
RRSS_2025_Pisa: RRSS_2025_Pisa,
|
|
172
176
|
SEO_Head_Section: SEO_Head_Section,
|