lau-ecom-design-system 1.0.23 → 1.0.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lau-ecom-design-system.esm.css +6 -1
- package/dist/lau-ecom-design-system.esm.js +1614 -398
- package/dist/lau-ecom-design-system.min.css +6 -1
- package/dist/lau-ecom-design-system.min.js +1 -1
- package/dist/lau-ecom-design-system.ssr.css +6 -1
- package/dist/lau-ecom-design-system.ssr.js +1619 -340
- package/dist/style.css +158 -214
- package/package.json +1 -2
- package/src/components/LauEcomIcon/LauEcomUpcIconClose.vue +29 -0
- package/src/components/LauEcomIcon/LauEcomUpcIconNavArrow.vue +1 -1
- package/src/components/LauEcomIcon/LauEcomUpcIconSearch.vue +6 -16
- package/src/components/LauEcomInputSearch/LauEcomInputSearch.vue +85 -150
- package/src/components/LauEcomInputSearch/LauEcomInputSearchDesktop.vue +229 -0
- package/src/components/LauEcomInputSearch/LauEcomInputSearchMobile.vue +187 -0
- package/src/components/LauEcomInputSearchHeader/LauEcomInputSearchHeader.vue +432 -0
- package/src/components/LauEcomInputSearchHeader/LauEcomInputSearchHeaderDesktop.vue +324 -0
- package/src/components/LauEcomInputSearchHeader/LauEcomInputSearchHeaderMobile.vue +279 -0
@@ -0,0 +1,432 @@
|
|
1
|
+
<script lang="ts" setup>
|
2
|
+
import { ref, computed, onMounted, onBeforeUnmount, watch, nextTick } from "vue";
|
3
|
+
import {
|
4
|
+
LauEcomUpcIconSearch,
|
5
|
+
LauEcomCoreIconNavClose as LauEcomUpcIconClose,
|
6
|
+
LauEcomUpcIconNavArrow,
|
7
|
+
} from "../LauEcomIcon";
|
8
|
+
|
9
|
+
interface Props {
|
10
|
+
// Input props
|
11
|
+
placeholder?: string;
|
12
|
+
isDisabled?: boolean;
|
13
|
+
modelValue?: string;
|
14
|
+
inputWidth?: string;
|
15
|
+
inputHeight?: string;
|
16
|
+
inputClass?: string;
|
17
|
+
inputBorderClass?: string;
|
18
|
+
inputBgClass?: string;
|
19
|
+
inputTextClass?: string;
|
20
|
+
|
21
|
+
// Botón de búsqueda props
|
22
|
+
searchButtonClass?: string;
|
23
|
+
searchButtonBgClass?: string;
|
24
|
+
searchButtonTextClass?: string;
|
25
|
+
searchIconSize?: {
|
26
|
+
width: string;
|
27
|
+
height: string;
|
28
|
+
};
|
29
|
+
searchIconClass?: string;
|
30
|
+
|
31
|
+
// Props para versión móvil
|
32
|
+
mobileButtonClass?: string;
|
33
|
+
mobileButtonBgClass?: string;
|
34
|
+
mobileButtonTextClass?: string;
|
35
|
+
mobileIconSize?: {
|
36
|
+
width: string;
|
37
|
+
height: string;
|
38
|
+
};
|
39
|
+
|
40
|
+
// Props para flecha de retorno
|
41
|
+
arrowButtonClass?: string;
|
42
|
+
arrowButtonBgClass?: string;
|
43
|
+
arrowIconClass?: string;
|
44
|
+
arrowIconSize?: {
|
45
|
+
width: string;
|
46
|
+
height: string;
|
47
|
+
};
|
48
|
+
|
49
|
+
// Props para panel expandido
|
50
|
+
expandedWidth?: string;
|
51
|
+
expandedBgClass?: string;
|
52
|
+
expandedShadowClass?: string;
|
53
|
+
|
54
|
+
// Props para overlay y panel móvil
|
55
|
+
overlayBgClass?: string;
|
56
|
+
overlayOpacityClass?: string;
|
57
|
+
mobilePanelBgClass?: string;
|
58
|
+
mobilePanelClass?: string;
|
59
|
+
}
|
60
|
+
|
61
|
+
const props = withDefaults(defineProps<Props>(), {
|
62
|
+
placeholder: "Quiero aprender...",
|
63
|
+
isDisabled: false,
|
64
|
+
modelValue: "",
|
65
|
+
inputWidth: "350px",
|
66
|
+
inputHeight: "40px",
|
67
|
+
inputClass: "dsEcom-rounded-lg",
|
68
|
+
inputBorderClass: "dsEcom-border dsEcom-border-neutral-80",
|
69
|
+
inputBgClass: "dsEcom-bg-white",
|
70
|
+
inputTextClass: "dsEcom-text-neutral-900",
|
71
|
+
|
72
|
+
searchButtonClass: "dsEcom-rounded-r-lg",
|
73
|
+
searchButtonBgClass: "bg-base-color-primary-20",
|
74
|
+
searchButtonTextClass: "text-base-color-primary-60",
|
75
|
+
searchIconSize: () => ({ width: "20", height: "20" }),
|
76
|
+
searchIconClass: "",
|
77
|
+
|
78
|
+
mobileButtonClass: "dsEcom-rounded-lg",
|
79
|
+
mobileButtonBgClass: "bg-base-color-primary-20",
|
80
|
+
mobileButtonTextClass: "text-base-color-primary-60",
|
81
|
+
mobileIconSize: () => ({ width: "24", height: "24" }),
|
82
|
+
|
83
|
+
arrowButtonClass: "dsEcom-rounded-lg",
|
84
|
+
arrowButtonBgClass: "bg-base-color-neutral-10",
|
85
|
+
arrowIconClass: "text-[#00A08C]",
|
86
|
+
arrowIconSize: () => ({ width: "24", height: "24" }),
|
87
|
+
|
88
|
+
expandedWidth: "800px",
|
89
|
+
expandedBgClass: "dsEcom-bg-white",
|
90
|
+
expandedShadowClass: "dsEcom-shadow-lg",
|
91
|
+
|
92
|
+
// Defaults para overlay y panel móvil
|
93
|
+
overlayBgClass: "dsEcom-bg-black",
|
94
|
+
overlayOpacityClass: "dsEcom-bg-opacity-50",
|
95
|
+
mobilePanelBgClass: "dsEcom-bg-white",
|
96
|
+
mobilePanelClass: "dsEcom-p-4"
|
97
|
+
});
|
98
|
+
|
99
|
+
const emit = defineEmits({
|
100
|
+
"update:modelValue": (value: string) => true,
|
101
|
+
"search": (value: string) => true,
|
102
|
+
"click:search-icon": () => true
|
103
|
+
});
|
104
|
+
|
105
|
+
const searchQuery = ref(props.modelValue);
|
106
|
+
const isExpanded = ref(false);
|
107
|
+
const showOverlay = ref(false);
|
108
|
+
const isMobileView = ref(false);
|
109
|
+
const isMobileSearchOpen = ref(false);
|
110
|
+
|
111
|
+
// Añadir ref para la posición expandida
|
112
|
+
const originalContainer = ref<HTMLElement | null>(null);
|
113
|
+
const expandedPosition = ref({ top: 0, left: 0 });
|
114
|
+
|
115
|
+
// Clases computadas
|
116
|
+
const inputClasses = computed(() => [
|
117
|
+
'dsEcom-w-full dsEcom-transition-all dsEcom-duration-300',
|
118
|
+
props.inputClass,
|
119
|
+
props.inputBorderClass,
|
120
|
+
props.inputBgClass,
|
121
|
+
props.inputTextClass
|
122
|
+
]);
|
123
|
+
|
124
|
+
const searchButtonClasses = computed(() => [
|
125
|
+
'dsEcom-flex dsEcom-items-center dsEcom-px-3 dsEcom-transition-all dsEcom-duration-300',
|
126
|
+
props.searchButtonClass,
|
127
|
+
props.searchButtonBgClass,
|
128
|
+
props.searchButtonTextClass
|
129
|
+
]);
|
130
|
+
|
131
|
+
const expandedContainerClasses = computed(() => [
|
132
|
+
'dsEcom-fixed dsEcom-z-50 dsEcom-overflow-hidden dsEcom-transition-all dsEcom-duration-300',
|
133
|
+
props.expandedBgClass,
|
134
|
+
props.expandedShadowClass
|
135
|
+
]);
|
136
|
+
|
137
|
+
// Manejo de posición del input expandido
|
138
|
+
const updateExpandedPosition = () => {
|
139
|
+
if (originalContainer.value) {
|
140
|
+
const rect = originalContainer.value.getBoundingClientRect();
|
141
|
+
expandedPosition.value = {
|
142
|
+
top: rect.top,
|
143
|
+
left: rect.left
|
144
|
+
};
|
145
|
+
}
|
146
|
+
};
|
147
|
+
|
148
|
+
// Handlers
|
149
|
+
const handleSearch = () => {
|
150
|
+
if (searchQuery.value?.trim()) {
|
151
|
+
emit("search", searchQuery.value);
|
152
|
+
emit("update:modelValue", searchQuery.value);
|
153
|
+
emit("click:search-icon");
|
154
|
+
if (isMobileView.value) {
|
155
|
+
handleMobileSearchClose();
|
156
|
+
} else {
|
157
|
+
closeSearch();
|
158
|
+
}
|
159
|
+
}
|
160
|
+
};
|
161
|
+
|
162
|
+
const handleInput = () => {
|
163
|
+
emit("update:modelValue", searchQuery.value);
|
164
|
+
};
|
165
|
+
|
166
|
+
// Modificar handleFocus
|
167
|
+
const handleFocus = () => {
|
168
|
+
if (!isMobileView.value) {
|
169
|
+
updateExpandedPosition();
|
170
|
+
showOverlay.value = true;
|
171
|
+
isExpanded.value = true;
|
172
|
+
}
|
173
|
+
};
|
174
|
+
|
175
|
+
// Modificar closeSearch
|
176
|
+
const closeSearch = () => {
|
177
|
+
showOverlay.value = false;
|
178
|
+
isExpanded.value = false;
|
179
|
+
};
|
180
|
+
|
181
|
+
const handleMobileSearchClick = () => {
|
182
|
+
isMobileSearchOpen.value = true;
|
183
|
+
};
|
184
|
+
|
185
|
+
const handleMobileSearchClose = () => {
|
186
|
+
const panel = document.querySelector('.mobile-search-panel') as HTMLElement;
|
187
|
+
if (panel) {
|
188
|
+
panel.style.transform = 'translateY(100%)';
|
189
|
+
panel.style.opacity = '0';
|
190
|
+
}
|
191
|
+
|
192
|
+
setTimeout(() => {
|
193
|
+
isMobileSearchOpen.value = false;
|
194
|
+
searchQuery.value = '';
|
195
|
+
emit('update:modelValue', '');
|
196
|
+
}, 300);
|
197
|
+
};
|
198
|
+
|
199
|
+
// Lifecycle hooks
|
200
|
+
onMounted(() => {
|
201
|
+
checkMobileView();
|
202
|
+
window.addEventListener('resize', checkMobileView);
|
203
|
+
window.addEventListener('resize', updateExpandedPosition);
|
204
|
+
});
|
205
|
+
|
206
|
+
onBeforeUnmount(() => {
|
207
|
+
window.removeEventListener('resize', checkMobileView);
|
208
|
+
window.removeEventListener('resize', updateExpandedPosition);
|
209
|
+
});
|
210
|
+
|
211
|
+
const checkMobileView = () => {
|
212
|
+
isMobileView.value = window.innerWidth < 768;
|
213
|
+
};
|
214
|
+
|
215
|
+
watch(() => props.modelValue, (newValue) => {
|
216
|
+
if (newValue !== searchQuery.value) {
|
217
|
+
searchQuery.value = newValue;
|
218
|
+
}
|
219
|
+
});
|
220
|
+
</script>
|
221
|
+
|
222
|
+
<template>
|
223
|
+
<div class="dsEcom-relative">
|
224
|
+
<!-- Versión Desktop -->
|
225
|
+
<div v-show="!isMobileView" class="dsEcom-transform-gpu">
|
226
|
+
<div
|
227
|
+
ref="originalContainer"
|
228
|
+
:class="[
|
229
|
+
'dsEcom-inline-flex dsEcom-items-center dsEcom-transition-all dsEcom-duration-300',
|
230
|
+
isExpanded ? [
|
231
|
+
'dsEcom-fixed dsEcom-z-[60]',
|
232
|
+
expandedBgClass,
|
233
|
+
expandedShadowClass
|
234
|
+
] : ''
|
235
|
+
]"
|
236
|
+
:style="{
|
237
|
+
width: isExpanded ? props.expandedWidth : inputWidth,
|
238
|
+
maxWidth: isExpanded ? '90vw' : 'none',
|
239
|
+
top: isExpanded ? `${expandedPosition.top}px` : 'auto',
|
240
|
+
left: isExpanded ? `${expandedPosition.left}px` : 'auto'
|
241
|
+
}"
|
242
|
+
>
|
243
|
+
<div class="dsEcom-relative dsEcom-w-full">
|
244
|
+
<div class="dsEcom-flex dsEcom-items-center">
|
245
|
+
<input
|
246
|
+
v-model="searchQuery"
|
247
|
+
type="text"
|
248
|
+
:placeholder="placeholder"
|
249
|
+
:disabled="isDisabled"
|
250
|
+
:style="{ height: inputHeight }"
|
251
|
+
:class="[inputClasses, 'dsEcom-w-full dsEcom-pr-12']"
|
252
|
+
@focus="handleFocus"
|
253
|
+
@input="handleInput"
|
254
|
+
@keyup.enter="handleSearch"
|
255
|
+
/>
|
256
|
+
<button
|
257
|
+
@click="handleSearch"
|
258
|
+
:class="[
|
259
|
+
searchButtonClasses,
|
260
|
+
'dsEcom-absolute dsEcom-right-0 dsEcom-top-1/2 dsEcom-transform dsEcom--translate-y-1/2 dsEcom-px-3'
|
261
|
+
]"
|
262
|
+
>
|
263
|
+
<LauEcomUpcIconSearch
|
264
|
+
:width="searchIconSize.width"
|
265
|
+
:height="searchIconSize.height"
|
266
|
+
:class="searchIconClass"
|
267
|
+
/>
|
268
|
+
</button>
|
269
|
+
</div>
|
270
|
+
</div>
|
271
|
+
</div>
|
272
|
+
</div>
|
273
|
+
|
274
|
+
<!-- Versión Mobile -->
|
275
|
+
<div v-show="isMobileView" class="dsEcom-transform-gpu">
|
276
|
+
<!-- Botón de búsqueda móvil -->
|
277
|
+
<button
|
278
|
+
v-show="!isMobileSearchOpen"
|
279
|
+
@click="handleMobileSearchClick"
|
280
|
+
:class="[
|
281
|
+
'dsEcom-p-2 dsEcom-rounded-lg dsEcom-transition-all dsEcom-duration-300',
|
282
|
+
mobileButtonClass,
|
283
|
+
mobileButtonBgClass,
|
284
|
+
mobileButtonTextClass
|
285
|
+
]"
|
286
|
+
>
|
287
|
+
<LauEcomUpcIconSearch
|
288
|
+
:width="mobileIconSize.width"
|
289
|
+
:height="mobileIconSize.height"
|
290
|
+
/>
|
291
|
+
</button>
|
292
|
+
|
293
|
+
<!-- Panel de búsqueda móvil -->
|
294
|
+
<div
|
295
|
+
v-show="isMobileSearchOpen"
|
296
|
+
:class="[
|
297
|
+
'mobile-search-panel dsEcom-fixed dsEcom-inset-0 dsEcom-z-50',
|
298
|
+
mobilePanelBgClass,
|
299
|
+
mobilePanelClass
|
300
|
+
]"
|
301
|
+
>
|
302
|
+
<div class="dsEcom-flex dsEcom-items-center dsEcom-gap-2 dsEcom-p-4">
|
303
|
+
<button
|
304
|
+
@click="handleMobileSearchClose"
|
305
|
+
:class="[
|
306
|
+
'dsEcom-p-2 dsEcom-rounded-lg dsEcom-transition-all dsEcom-duration-300',
|
307
|
+
arrowButtonClass,
|
308
|
+
arrowButtonBgClass
|
309
|
+
]"
|
310
|
+
>
|
311
|
+
<LauEcomUpcIconNavArrow
|
312
|
+
:width="arrowIconSize.width"
|
313
|
+
:height="arrowIconSize.height"
|
314
|
+
class="dsEcom-transform dsEcom-rotate-90"
|
315
|
+
:class="arrowIconClass"
|
316
|
+
/>
|
317
|
+
</button>
|
318
|
+
|
319
|
+
<div class="dsEcom-flex-1">
|
320
|
+
<div class="dsEcom-relative">
|
321
|
+
<input
|
322
|
+
v-model="searchQuery"
|
323
|
+
type="text"
|
324
|
+
:placeholder="placeholder"
|
325
|
+
:disabled="isDisabled"
|
326
|
+
:style="{ height: inputHeight }"
|
327
|
+
:class="[
|
328
|
+
inputClasses,
|
329
|
+
'dsEcom-w-full dsEcom-pr-12 dsEcom-pl-4',
|
330
|
+
'dsEcom-border dsEcom-border-neutral-200 dsEcom-rounded-lg'
|
331
|
+
]"
|
332
|
+
@input="handleInput"
|
333
|
+
@keyup.enter="handleSearch"
|
334
|
+
autofocus
|
335
|
+
/>
|
336
|
+
<button
|
337
|
+
@click="handleSearch"
|
338
|
+
:class="[
|
339
|
+
searchButtonClasses,
|
340
|
+
'dsEcom-absolute dsEcom-right-0 dsEcom-top-1/2 dsEcom-transform dsEcom--translate-y-1/2 dsEcom-px-3'
|
341
|
+
]"
|
342
|
+
>
|
343
|
+
<LauEcomUpcIconSearch
|
344
|
+
:width="searchIconSize.width"
|
345
|
+
:height="searchIconSize.height"
|
346
|
+
:class="searchIconClass"
|
347
|
+
/>
|
348
|
+
</button>
|
349
|
+
</div>
|
350
|
+
</div>
|
351
|
+
</div>
|
352
|
+
</div>
|
353
|
+
</div>
|
354
|
+
</div>
|
355
|
+
|
356
|
+
<!-- Overlay (fuera del componente principal) -->
|
357
|
+
<Teleport to="body">
|
358
|
+
<Transition name="fade">
|
359
|
+
<div
|
360
|
+
v-if="showOverlay"
|
361
|
+
:class="[
|
362
|
+
'dsEcom-fixed dsEcom-inset-0 dsEcom-z-50 dsEcom-transition-opacity dsEcom-duration-300',
|
363
|
+
overlayBgClass,
|
364
|
+
overlayOpacityClass
|
365
|
+
]"
|
366
|
+
@click="closeSearch"
|
367
|
+
></div>
|
368
|
+
</Transition>
|
369
|
+
</Teleport>
|
370
|
+
</template>
|
371
|
+
|
372
|
+
<style scoped>
|
373
|
+
.mobile-search-panel {
|
374
|
+
transform-origin: top center;
|
375
|
+
will-change: transform, opacity;
|
376
|
+
animation: slideIn 0.3s ease-out;
|
377
|
+
}
|
378
|
+
|
379
|
+
@keyframes slideIn {
|
380
|
+
from {
|
381
|
+
transform: translateY(-100%);
|
382
|
+
opacity: 0;
|
383
|
+
}
|
384
|
+
to {
|
385
|
+
transform: translateY(0);
|
386
|
+
opacity: 1;
|
387
|
+
}
|
388
|
+
}
|
389
|
+
|
390
|
+
.dsEcom-transform-gpu {
|
391
|
+
transform: translateZ(0);
|
392
|
+
backface-visibility: hidden;
|
393
|
+
perspective: 1000px;
|
394
|
+
will-change: transform, opacity;
|
395
|
+
}
|
396
|
+
|
397
|
+
/* Asegurarse de que el overlay cubra toda la pantalla */
|
398
|
+
.dsEcom-fixed.dsEcom-inset-0 {
|
399
|
+
position: fixed;
|
400
|
+
top: 0;
|
401
|
+
right: 0;
|
402
|
+
bottom: 0;
|
403
|
+
left: 0;
|
404
|
+
width: 100vw;
|
405
|
+
height: 100vh;
|
406
|
+
}
|
407
|
+
|
408
|
+
/* Ajustar z-index para asegurar que el overlay esté por debajo del input expandido */
|
409
|
+
.dsEcom-z-50 {
|
410
|
+
z-index: 50;
|
411
|
+
}
|
412
|
+
|
413
|
+
.dsEcom-z-\[60\] {
|
414
|
+
z-index: 60;
|
415
|
+
}
|
416
|
+
|
417
|
+
/* Transiciones suaves */
|
418
|
+
.dsEcom-transition-all {
|
419
|
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
420
|
+
}
|
421
|
+
|
422
|
+
/* Transición para el overlay */
|
423
|
+
.fade-enter-active,
|
424
|
+
.fade-leave-active {
|
425
|
+
transition: opacity 0.3s ease;
|
426
|
+
}
|
427
|
+
|
428
|
+
.fade-enter-from,
|
429
|
+
.fade-leave-to {
|
430
|
+
opacity: 0;
|
431
|
+
}
|
432
|
+
</style>
|