lau-ecom-design-system 1.0.22 → 1.0.23

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.
@@ -1,323 +1,511 @@
1
- <script lang="ts" setup>
2
- import { ref, computed, onMounted, onBeforeUnmount, watch, nextTick } from "vue";
3
- import {
4
- LauEcomUpcIconSearch,
5
- LauEcomCoreIconNavClose as LauEcomUpcIconClose,
6
- } from "../LauEcomIcon";
7
-
8
- // Log de versión del componente
9
- console.log('🔍 InputSearch Version: 1.0.20 - Debug Mode');
10
-
11
- interface Props {
12
- placeholder?: string;
13
- isDisabled?: boolean;
14
- modelValue?: string;
15
- forceClose?: boolean;
16
- buttonColorClass?: string;
17
- buttonTextColorClass?: string;
18
- buttonClass?: string;
19
- inputClass?: string;
20
- containerClass?: string;
21
- }
22
-
23
- const props = withDefaults(defineProps<Props>(), {
24
- placeholder: "Quiero aprender...",
25
- isDisabled: false,
26
- modelValue: "",
27
- forceClose: false,
28
- buttonColorClass: "dsEcom-bg-primary-60 hover:dsEcom-bg-primary-70",
29
- buttonTextColorClass: "dsEcom-text-white",
30
- buttonClass: "",
31
- inputClass: "dsEcom-h-10 dsEcom-rounded-lg",
32
- containerClass: ""
33
- });
34
-
35
- // Log de props iniciales
36
- console.log('🔍 InputSearch Props:', {
37
- placeholder: props.placeholder,
38
- isDisabled: props.isDisabled,
39
- modelValue: props.modelValue,
40
- forceClose: props.forceClose
41
- });
42
-
43
- const emit = defineEmits({
44
- "update:modelValue": (value: string) => true,
45
- "search": (value: string) => true,
46
- "click:search-icon": () => true
47
- });
48
-
49
- const searchQuery = ref(props.modelValue);
50
- const isExpanded = ref(false);
51
- const showOverlay = ref(false);
52
-
53
- const handleSearch = () => {
54
- console.log('🔍 Search triggered:', {
55
- query: searchQuery.value,
56
- isExpanded: isExpanded.value,
57
- showOverlay: showOverlay.value
58
- });
59
-
60
- if (searchQuery.value && searchQuery.value.trim()) {
61
- emit("search", searchQuery.value);
62
- emit("update:modelValue", searchQuery.value);
63
- emit("click:search-icon");
64
- closeSearch();
65
- }
66
- };
67
-
68
- const handleInput = () => {
69
- console.log('🔍 Input changed:', {
70
- value: searchQuery.value,
71
- length: searchQuery.value.length,
72
- isExpanded: isExpanded.value
73
- });
74
- emit("update:modelValue", searchQuery.value);
75
- };
76
-
77
- const clearSearch = () => {
78
- console.log('🔍 Clear search triggered');
79
- searchQuery.value = "";
80
- emit("update:modelValue", "");
81
- };
82
-
83
- const closeSearch = () => {
84
- console.log('🔍 Close search triggered');
85
- showOverlay.value = false;
86
- setTimeout(() => {
87
- console.log('🔍 Closing search after timeout');
88
- isExpanded.value = false;
89
- }, 300);
90
- };
91
-
92
- const handleFocus = () => {
93
- console.log('🔍 Focus triggered:', {
94
- currentValue: searchQuery.value,
95
- wasExpanded: isExpanded.value
96
- });
97
- isExpanded.value = true;
98
- showOverlay.value = true;
99
- };
100
-
101
- const containerClasses = computed(() => {
102
- const classes = [
103
- "dsEcom-transition-transform dsEcom-duration-300 dsEcom-ease-in-out dsEcom-relative",
104
- {
105
- "dsEcom-w-[250px] md:dsEcom-w-[350px]": !isExpanded.value
106
- }
107
- ];
108
- console.log('🔍 Container classes updated:', { isExpanded: isExpanded.value });
109
- return classes;
110
- });
111
-
112
- const overlayClasses = computed(() => [
113
- "dsEcom-fixed dsEcom-inset-0 dsEcom-bg-black dsEcom-transition-opacity dsEcom-duration-300 dsEcom-ease-in-out dsEcom-z-40",
114
- {
115
- "dsEcom-opacity-50": showOverlay.value,
116
- "dsEcom-opacity-0 dsEcom-pointer-events-none": !showOverlay.value
117
- }
118
- ]);
119
-
120
- const originalContainer = ref<HTMLElement | null>(null);
121
- const expandedContainer = ref<HTMLElement | null>(null);
122
-
123
- const updateExpandedPosition = () => {
124
- if (originalContainer.value && expandedContainer.value) {
125
- const rect = originalContainer.value.getBoundingClientRect();
126
- const viewportHeight = window.innerHeight;
127
-
128
- // Si el input original está fuera de la vista (arriba o abajo), posicionamos el expandido en una posición visible
129
- let topPosition;
130
- if (rect.top < 0) {
131
- topPosition = '20px'; // Si está arriba de la vista, lo ponemos cerca del tope
132
- } else if (rect.top > viewportHeight) {
133
- topPosition = '20px'; // Si está abajo de la vista, también lo ponemos cerca del tope
134
- } else {
135
- topPosition = `${rect.top}px`; // Si está visible, mantenemos su posición actual
136
- }
137
-
138
- expandedContainer.value.style.position = 'fixed';
139
- expandedContainer.value.style.top = topPosition;
140
- expandedContainer.value.style.left = '50%';
141
- expandedContainer.value.style.transform = 'translateX(-50%)';
142
- expandedContainer.value.style.width = '656px';
143
- expandedContainer.value.style.maxWidth = '90vw';
144
- }
145
- };
146
-
147
- onMounted(() => {
148
- console.log('🔍 Component mounted');
149
- window.addEventListener('resize', updateExpandedPosition);
150
- });
151
-
152
- onBeforeUnmount(() => {
153
- console.log('🔍 Component will unmount');
154
- window.removeEventListener('resize', updateExpandedPosition);
155
- });
156
-
157
- watch(isExpanded, (newValue) => {
158
- console.log('🔍 isExpanded changed:', {
159
- newValue,
160
- searchQuery: searchQuery.value,
161
- showOverlay: showOverlay.value
162
- });
163
- if (newValue) {
164
- nextTick(updateExpandedPosition);
165
- }
166
- });
167
-
168
- watch(() => props.forceClose, (newValue) => {
169
- console.log('🔍 forceClose prop changed:', newValue);
170
- if (newValue) {
171
- closeSearch();
172
- }
173
- });
174
-
175
- watch(() => props.modelValue, (newValue) => {
176
- console.log('🔍 modelValue prop changed:', {
177
- newValue,
178
- currentSearchQuery: searchQuery.value
179
- });
180
- if (newValue !== searchQuery.value) {
181
- searchQuery.value = newValue;
182
- }
183
- });
184
-
185
- const buttonClasses = computed(() => {
186
- const defaultClasses = "dsEcom-absolute dsEcom-right-0 dsEcom-top-1/2 -dsEcom-translate-y-1/2 dsEcom-p-2 dsEcom-rounded-r-lg dsEcom-transition-all dsEcom-duration-300";
187
-
188
- // Si hay buttonClass, usamos esas clases directamente
189
- if (props.buttonClass) {
190
- return [defaultClasses, props.buttonClass];
191
- }
192
-
193
- // Si no, usamos las clases de color tradicionales
194
- return [
195
- defaultClasses,
196
- props.buttonColorClass,
197
- props.buttonTextColorClass
198
- ];
199
- });
200
- </script>
201
-
202
- <template>
203
- <div class="dsEcom-relative">
204
- <div
205
- :class="[containerClasses, props.containerClass]"
206
- ref="originalContainer"
207
- >
208
- <div class="dsEcom-relative" :class="{ 'dsEcom-invisible': isExpanded }">
209
- <input
210
- v-model="searchQuery"
211
- type="text"
212
- :placeholder="placeholder"
213
- :disabled="isDisabled"
214
- :class="[
215
- 'lau-ecom-input dsEcom-border dsEcom-border-neutral-80 dsEcom-pl-4 dsEcom-pr-24 dsEcom-w-full dsEcom-focus:outline-none dsEcom-focus:ring-2 dsEcom-focus:ring-primary-60',
216
- props.inputClass,
217
- { 'dsEcom-opacity-0': isExpanded }
218
- ]"
219
- @focus="handleFocus"
220
- @input="handleInput"
221
- @keyup.enter="handleSearch"
222
- />
223
- <div class="dsEcom-absolute dsEcom-right-0 dsEcom-inset-y-0 dsEcom-flex dsEcom-items-stretch">
224
- <button
225
- v-if="searchQuery.length >= 3"
226
- @click="clearSearch"
227
- :class="[
228
- 'dsEcom-flex dsEcom-items-center dsEcom-px-1.5 dsEcom-text-neutral-100 hover:dsEcom-text-neutral-80 dsEcom-transition-all dsEcom-duration-300',
229
- props.inputClass
230
- ]"
231
- >
232
- <LauEcomUpcIconClose width="16" height="16" />
233
- </button>
234
-
235
- <button
236
- @click="handleSearch"
237
- :class="[
238
- 'dsEcom-flex dsEcom-items-center dsEcom-px-3 dsEcom-transition-all dsEcom-duration-300',
239
- props.inputClass?.includes('rounded') ? props.inputClass : 'dsEcom-rounded-r-lg',
240
- props.buttonColorClass,
241
- props.buttonTextColorClass,
242
- 'dsEcom-border-0'
243
- ]"
244
- style="margin: 1px; height: calc(100% - 2px);"
245
- >
246
- <LauEcomUpcIconSearch width="20" height="20" color="currentColor" />
247
- </button>
248
- </div>
249
- </div>
250
- </div>
251
-
252
- <!-- Overlay -->
253
- <div
254
- v-show="isExpanded"
255
- :class="overlayClasses"
256
- @click="closeSearch"
257
- ></div>
258
-
259
- <!-- Versión expandida -->
260
- <div
261
- v-show="isExpanded"
262
- class="dsEcom-fixed dsEcom-z-50 dsEcom-bg-white dsEcom-shadow-lg dsEcom-overflow-hidden"
263
- :class="[props.inputClass?.includes('rounded') ? props.inputClass : 'dsEcom-rounded-lg']"
264
- ref="expandedContainer"
265
- >
266
- <div class="dsEcom-relative">
267
- <input
268
- v-model="searchQuery"
269
- type="text"
270
- :placeholder="placeholder"
271
- :disabled="isDisabled"
272
- :class="[
273
- 'lau-ecom-input dsEcom-border dsEcom-border-neutral-80 dsEcom-pl-4 dsEcom-pr-24 dsEcom-w-full dsEcom-focus:outline-none dsEcom-focus:ring-2 dsEcom-focus:ring-primary-60',
274
- props.inputClass
275
- ]"
276
- @input="handleInput"
277
- @keyup.enter="handleSearch"
278
- autofocus
279
- />
280
- <div class="dsEcom-absolute dsEcom-right-0 dsEcom-inset-y-0 dsEcom-flex dsEcom-items-stretch">
281
- <button
282
- v-if="searchQuery.length >= 3"
283
- @click="clearSearch"
284
- :class="[
285
- 'dsEcom-flex dsEcom-items-center dsEcom-px-1.5 dsEcom-text-neutral-100 hover:dsEcom-text-neutral-80 dsEcom-transition-all dsEcom-duration-300',
286
- props.inputClass
287
- ]"
288
- >
289
- <LauEcomUpcIconClose width="16" height="16" />
290
- </button>
291
-
292
- <button
293
- @click="handleSearch"
294
- :class="[
295
- 'dsEcom-flex dsEcom-items-center dsEcom-px-3 dsEcom-transition-all dsEcom-duration-300',
296
- { 'dsEcom-rounded-r-lg': !props.inputClass?.includes('rounded') },
297
- props.buttonColorClass,
298
- props.buttonTextColorClass,
299
- 'dsEcom-border-0'
300
- ]"
301
- style="margin: 1px; height: calc(100% - 2px);"
302
- >
303
- <LauEcomUpcIconSearch width="20" height="20" color="currentColor" />
304
- </button>
305
- </div>
306
- </div>
307
- </div>
308
- </div>
309
- </template>
310
-
311
- <style scoped>
312
- .lau-ecom-input {
313
- transition: all 0.3s ease-in-out;
314
- }
315
-
316
- .dsEcom-transform-gpu {
317
- transform: translateZ(0);
318
- backface-visibility: hidden;
319
- perspective: 1000px;
320
- }
321
- </style>
322
-
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
+ // Log de versión del componente
10
+ console.log('🔍 InputSearch Version: 1.0.25 - Debug Mode');
11
+
12
+ interface Props {
13
+ placeholder?: string;
14
+ isDisabled?: boolean;
15
+ modelValue?: string;
16
+ forceClose?: boolean;
17
+ buttonColorClass?: string;
18
+ buttonTextColorClass?: string;
19
+ buttonClass?: string;
20
+ inputClass?: string;
21
+ containerClass?: string;
22
+ expandedWidth?: string;
23
+ isMobileSearch?: boolean;
24
+ arrowColorClass?: string;
25
+ arrowButtonClass?: string;
26
+ mobileInputWidth?: string;
27
+ mobileInputHeight?: string;
28
+ mobilePanelClass?: string;
29
+ expandedBackgroundClass?: string;
30
+ overlayClass?: string;
31
+ }
32
+
33
+ const props = withDefaults(defineProps<Props>(), {
34
+ placeholder: "Quiero aprender...",
35
+ isDisabled: false,
36
+ modelValue: "",
37
+ forceClose: false,
38
+ buttonColorClass: "dsEcom-bg-primary-60 hover:dsEcom-bg-primary-70",
39
+ buttonTextColorClass: "dsEcom-text-white",
40
+ buttonClass: "",
41
+ inputClass: "dsEcom-h-10 dsEcom-rounded-lg",
42
+ containerClass: "",
43
+ expandedWidth: "50vw",
44
+ isMobileSearch: false,
45
+ arrowColorClass: "dsEcom-text-neutral-100",
46
+ arrowButtonClass: "",
47
+ mobileInputWidth: "240px",
48
+ mobileInputHeight: "40px",
49
+ mobilePanelClass: "dsEcom-bg-white",
50
+ expandedBackgroundClass: "dsEcom-bg-white",
51
+ overlayClass: "dsEcom-bg-black dsEcom-opacity-50"
52
+ });
53
+
54
+ // Log de props iniciales
55
+ console.log('🔍 InputSearch Props:', {
56
+ placeholder: props.placeholder,
57
+ isDisabled: props.isDisabled,
58
+ modelValue: props.modelValue,
59
+ forceClose: props.forceClose
60
+ });
61
+
62
+ const emit = defineEmits({
63
+ "update:modelValue": (value: string) => true,
64
+ "search": (value: string) => true,
65
+ "click:search-icon": () => true
66
+ });
67
+
68
+ const searchQuery = ref(props.modelValue);
69
+ const isExpanded = ref(false);
70
+ const showOverlay = ref(false);
71
+
72
+ const isMobileView = ref(false);
73
+ const isMobileSearchOpen = ref(false);
74
+
75
+ const handleSearch = () => {
76
+ console.log('🔍 Search triggered:', {
77
+ query: searchQuery.value,
78
+ isExpanded: isExpanded.value,
79
+ showOverlay: showOverlay.value
80
+ });
81
+
82
+ if (searchQuery.value && searchQuery.value.trim()) {
83
+ emit("search", searchQuery.value);
84
+ emit("update:modelValue", searchQuery.value);
85
+ emit("click:search-icon");
86
+ closeSearch();
87
+ }
88
+ };
89
+
90
+ const handleInput = () => {
91
+ console.log('🔍 Input changed:', {
92
+ value: searchQuery.value,
93
+ length: searchQuery.value.length,
94
+ isExpanded: isExpanded.value
95
+ });
96
+ emit("update:modelValue", searchQuery.value);
97
+ };
98
+
99
+ const clearSearch = () => {
100
+ console.log('🔍 Clear search triggered');
101
+ searchQuery.value = "";
102
+ emit("update:modelValue", "");
103
+ };
104
+
105
+ const closeSearch = () => {
106
+ console.log('🔍 Close search triggered');
107
+ showOverlay.value = false;
108
+ setTimeout(() => {
109
+ console.log('🔍 Closing search after timeout');
110
+ isExpanded.value = false;
111
+ }, 300);
112
+ };
113
+
114
+ const handleFocus = () => {
115
+ console.log('🔍 Focus triggered:', {
116
+ currentValue: searchQuery.value,
117
+ wasExpanded: isExpanded.value
118
+ });
119
+ isExpanded.value = true;
120
+ showOverlay.value = true;
121
+ };
122
+
123
+ const containerClasses = computed(() => {
124
+ const classes = [
125
+ "dsEcom-transition-transform dsEcom-duration-300 dsEcom-ease-in-out dsEcom-relative",
126
+ {
127
+ "dsEcom-w-[250px] md:dsEcom-w-[350px]": !isExpanded.value
128
+ }
129
+ ];
130
+ console.log('🔍 Container classes updated:', { isExpanded: isExpanded.value });
131
+ return classes;
132
+ });
133
+
134
+ const overlayClasses = computed(() => [
135
+ "dsEcom-fixed dsEcom-inset-0 dsEcom-transition-opacity dsEcom-duration-300 dsEcom-ease-in-out dsEcom-z-40",
136
+ props.overlayClass,
137
+ {
138
+ "dsEcom-opacity-100": showOverlay.value,
139
+ "dsEcom-opacity-0 dsEcom-pointer-events-none": !showOverlay.value
140
+ }
141
+ ]);
142
+
143
+ const originalContainer = ref<HTMLElement | null>(null);
144
+ const expandedContainer = ref<HTMLElement | null>(null);
145
+
146
+ const updateExpandedPosition = () => {
147
+ if (originalContainer.value && expandedContainer.value) {
148
+ const rect = originalContainer.value.getBoundingClientRect();
149
+ const viewportHeight = window.innerHeight;
150
+ const viewportWidth = window.innerWidth;
151
+
152
+ let topPosition;
153
+ if (rect.top < 0) {
154
+ topPosition = '20px';
155
+ } else if (rect.top > viewportHeight) {
156
+ topPosition = '20px';
157
+ } else {
158
+ topPosition = `${rect.top}px`;
159
+ }
160
+
161
+ // Mantenemos la posición original del input
162
+ const leftPosition = `${rect.left}px`;
163
+
164
+ expandedContainer.value.style.position = 'fixed';
165
+ expandedContainer.value.style.top = topPosition;
166
+ expandedContainer.value.style.left = leftPosition;
167
+ expandedContainer.value.style.transform = 'none';
168
+ expandedContainer.value.style.width = props.expandedWidth;
169
+ expandedContainer.value.style.maxWidth = '90vw';
170
+
171
+ // Ajustamos si se sale por la derecha
172
+ const expandedWidth = parseInt(props.expandedWidth);
173
+ if (rect.left + expandedWidth > viewportWidth) {
174
+ // Si se sale por la derecha, ajustamos el ancho para que quepa
175
+ const availableWidth = viewportWidth - rect.left - 32; // 32px de margen
176
+ expandedContainer.value.style.width = `${availableWidth}px`;
177
+ }
178
+
179
+ // Log para debugging
180
+ console.log('🔍 Expanded position updated:', {
181
+ top: topPosition,
182
+ left: leftPosition,
183
+ width: expandedContainer.value.style.width,
184
+ originalLeft: rect.left,
185
+ viewportWidth,
186
+ originalWidth: rect.width
187
+ });
188
+ }
189
+ };
190
+
191
+ onMounted(() => {
192
+ console.log('🔍 Component mounted with props:', {
193
+ buttonColorClass: props.buttonColorClass,
194
+ buttonTextColorClass: props.buttonTextColorClass,
195
+ arrowColorClass: props.arrowColorClass,
196
+ isMobileSearch: props.isMobileSearch,
197
+ mobileInputWidth: props.mobileInputWidth,
198
+ mobileInputHeight: props.mobileInputHeight
199
+ });
200
+ checkMobileView();
201
+ window.addEventListener('resize', updateExpandedPosition);
202
+ window.addEventListener('resize', checkMobileView);
203
+ });
204
+
205
+ onBeforeUnmount(() => {
206
+ console.log('🔍 Component will unmount');
207
+ window.removeEventListener('resize', updateExpandedPosition);
208
+ window.removeEventListener('resize', checkMobileView);
209
+ });
210
+
211
+ watch(isExpanded, (newValue) => {
212
+ console.log('🔍 isExpanded changed:', {
213
+ newValue,
214
+ searchQuery: searchQuery.value,
215
+ showOverlay: showOverlay.value
216
+ });
217
+ if (newValue) {
218
+ nextTick(updateExpandedPosition);
219
+ }
220
+ });
221
+
222
+ watch(() => props.forceClose, (newValue) => {
223
+ console.log('🔍 forceClose prop changed:', newValue);
224
+ if (newValue) {
225
+ closeSearch();
226
+ }
227
+ });
228
+
229
+ watch(() => props.modelValue, (newValue) => {
230
+ console.log('🔍 modelValue prop changed:', {
231
+ newValue,
232
+ currentSearchQuery: searchQuery.value
233
+ });
234
+ if (newValue !== searchQuery.value) {
235
+ searchQuery.value = newValue;
236
+ }
237
+ });
238
+
239
+ const buttonClasses = computed(() => {
240
+ const defaultClasses = "dsEcom-absolute dsEcom-right-0 dsEcom-top-1/2 -dsEcom-translate-y-1/2 dsEcom-p-2 dsEcom-rounded-r-lg dsEcom-transition-all dsEcom-duration-300";
241
+
242
+ // Si hay buttonClass, usamos esas clases directamente
243
+ if (props.buttonClass) {
244
+ return [defaultClasses, props.buttonClass];
245
+ }
246
+
247
+ // Si no, usamos las clases de color tradicionales
248
+ return [
249
+ defaultClasses,
250
+ props.buttonColorClass,
251
+ props.buttonTextColorClass
252
+ ];
253
+ });
254
+
255
+ const checkMobileView = () => {
256
+ const wasMobile = isMobileView.value;
257
+ isMobileView.value = window.innerWidth < 768;
258
+ if (wasMobile !== isMobileView.value) {
259
+ console.log('🔍 Mobile view changed:', {
260
+ isMobileView: isMobileView.value,
261
+ width: window.innerWidth,
262
+ buttonColorClass: props.buttonColorClass,
263
+ buttonTextColorClass: props.buttonTextColorClass
264
+ });
265
+ }
266
+ };
267
+
268
+ const handleMobileSearchClick = () => {
269
+ console.log('🔍 Mobile Search Click:', {
270
+ buttonColorClass: props.buttonColorClass,
271
+ buttonTextColorClass: props.buttonTextColorClass,
272
+ arrowColorClass: props.arrowColorClass,
273
+ isMobileView: isMobileView.value
274
+ });
275
+ isMobileSearchOpen.value = true;
276
+ nextTick(() => {
277
+ if (expandedContainer.value) {
278
+ const input = expandedContainer.value.querySelector('input');
279
+ input?.focus();
280
+ }
281
+ });
282
+ };
283
+
284
+ const handleMobileSearchClose = () => {
285
+ console.log('🔍 Mobile Search Close:', {
286
+ buttonColorClass: props.buttonColorClass,
287
+ buttonTextColorClass: props.buttonTextColorClass,
288
+ arrowColorClass: props.arrowColorClass
289
+ });
290
+ isMobileSearchOpen.value = false;
291
+ searchQuery.value = '';
292
+ emit('update:modelValue', '');
293
+ };
294
+
295
+ const handleMobileSearch = () => {
296
+ if (searchQuery.value && searchQuery.value.trim()) {
297
+ emit("search", searchQuery.value);
298
+ emit("update:modelValue", searchQuery.value);
299
+ emit("click:search-icon");
300
+ handleMobileSearchClose();
301
+ }
302
+ };
303
+
304
+ const handleMobileClear = () => {
305
+ clearSearch();
306
+ };
307
+ </script>
308
+
309
+ <template>
310
+ <div class="dsEcom-relative">
311
+ <!-- Versión móvil - Solo botón de búsqueda -->
312
+ <button
313
+ v-if="isMobileView && !isMobileSearchOpen"
314
+ @click="handleMobileSearchClick"
315
+ class="dsEcom-p-2 dsEcom-rounded-lg"
316
+ :class="[buttonColorClass]"
317
+ >
318
+ <LauEcomUpcIconSearch
319
+ width="24"
320
+ height="24"
321
+ :class="[buttonTextColorClass]"
322
+ />
323
+ </button>
324
+
325
+ <!-- Capa blanca móvil -->
326
+ <div
327
+ v-if="isMobileView && isMobileSearchOpen"
328
+ class="dsEcom-fixed dsEcom-inset-0 dsEcom-z-50"
329
+ :class="[mobilePanelClass]"
330
+ >
331
+ <div class="dsEcom-flex dsEcom-items-center dsEcom-gap-2 dsEcom-p-4">
332
+ <!-- Input y botones -->
333
+ <div class="dsEcom-flex dsEcom-items-center dsEcom-gap-2 dsEcom-flex-1">
334
+ <button
335
+ @click="handleMobileSearchClose"
336
+ class="dsEcom-p-2 dsEcom-rounded-lg"
337
+ :class="[arrowButtonClass]"
338
+ >
339
+ <LauEcomUpcIconNavArrow
340
+ width="24"
341
+ height="24"
342
+ class="dsEcom-transform dsEcom-rotate-[-90deg]"
343
+ :class="[arrowColorClass]"
344
+ color="currentColor"
345
+ />
346
+ </button>
347
+
348
+ <div class="dsEcom-flex dsEcom-items-center dsEcom-gap-0 dsEcom-flex-1">
349
+ <input
350
+ v-model="searchQuery"
351
+ type="text"
352
+ :placeholder="placeholder"
353
+ :disabled="isDisabled"
354
+ :style="{
355
+ width: props.mobileInputWidth,
356
+ height: props.mobileInputHeight
357
+ }"
358
+ class="lau-ecom-input dsEcom-pl-4 dsEcom-pr-4 dsEcom-border dsEcom-border-neutral-80 dsEcom-rounded-l-lg dsEcom-focus:outline-none dsEcom-focus:ring-2 dsEcom-focus:ring-primary-60 dsEcom-flex-1"
359
+ @input="handleInput"
360
+ @keyup.enter="handleMobileSearch"
361
+ />
362
+ <button
363
+ @click="handleMobileSearch"
364
+ :style="{ height: props.mobileInputHeight }"
365
+ :class="[
366
+ 'dsEcom-px-3 dsEcom-rounded-r-lg dsEcom-border-0',
367
+ buttonColorClass,
368
+ buttonTextColorClass
369
+ ]"
370
+ >
371
+ <LauEcomUpcIconSearch width="20" height="20" color="currentColor" />
372
+ </button>
373
+ </div>
374
+
375
+ <button
376
+ v-if="searchQuery.length >= 3"
377
+ @click="handleMobileClear"
378
+ class="dsEcom-p-2 dsEcom-text-neutral-100 hover:dsEcom-text-neutral-80"
379
+ >
380
+ <LauEcomUpcIconClose width="16" height="16" />
381
+ </button>
382
+ </div>
383
+ </div>
384
+ </div>
385
+
386
+ <!-- Versión desktop -->
387
+ <div v-if="!isMobileView">
388
+ <div
389
+ :class="[containerClasses, props.containerClass]"
390
+ ref="originalContainer"
391
+ >
392
+ <div class="dsEcom-relative" :class="{ 'dsEcom-invisible': isExpanded }">
393
+ <input
394
+ v-model="searchQuery"
395
+ type="text"
396
+ :placeholder="placeholder"
397
+ :disabled="isDisabled"
398
+ :class="[
399
+ 'lau-ecom-input dsEcom-border dsEcom-border-neutral-80 dsEcom-pl-4 dsEcom-pr-24 dsEcom-w-full dsEcom-focus:outline-none dsEcom-focus:ring-2 dsEcom-focus:ring-primary-60',
400
+ props.inputClass,
401
+ { 'dsEcom-opacity-0': isExpanded }
402
+ ]"
403
+ @focus="handleFocus"
404
+ @input="handleInput"
405
+ @keyup.enter="handleSearch"
406
+ />
407
+ <div class="dsEcom-absolute dsEcom-right-0 dsEcom-inset-y-0 dsEcom-flex dsEcom-items-stretch">
408
+ <button
409
+ v-if="searchQuery.length >= 3"
410
+ @click="clearSearch"
411
+ :class="[
412
+ 'dsEcom-flex dsEcom-items-center dsEcom-px-1.5 dsEcom-text-neutral-100 hover:dsEcom-text-neutral-80 dsEcom-transition-all dsEcom-duration-300',
413
+ props.inputClass
414
+ ]"
415
+ >
416
+ <LauEcomUpcIconClose width="16" height="16" />
417
+ </button>
418
+
419
+ <button
420
+ @click="handleSearch"
421
+ :class="[
422
+ 'dsEcom-flex dsEcom-items-center dsEcom-px-3 dsEcom-transition-all dsEcom-duration-300',
423
+ props.inputClass?.includes('rounded') ? props.inputClass : 'dsEcom-rounded-r-lg',
424
+ props.buttonColorClass,
425
+ props.buttonTextColorClass,
426
+ 'dsEcom-border-0'
427
+ ]"
428
+ style="margin: 1px; height: calc(100% - 2px);"
429
+ >
430
+ <LauEcomUpcIconSearch width="20" height="20" color="currentColor" />
431
+ </button>
432
+ </div>
433
+ </div>
434
+ </div>
435
+
436
+ <!-- Overlay -->
437
+ <div
438
+ v-show="isExpanded"
439
+ :class="overlayClasses"
440
+ @click="closeSearch"
441
+ ></div>
442
+
443
+ <!-- Versión expandida -->
444
+ <div
445
+ v-show="isExpanded"
446
+ class="dsEcom-fixed dsEcom-z-50 dsEcom-shadow-lg dsEcom-overflow-hidden"
447
+ :class="[
448
+ props.inputClass?.includes('rounded') ? props.inputClass : 'dsEcom-rounded-lg',
449
+ expandedBackgroundClass
450
+ ]"
451
+ ref="expandedContainer"
452
+ >
453
+ <div class="dsEcom-relative">
454
+ <input
455
+ v-model="searchQuery"
456
+ type="text"
457
+ :placeholder="placeholder"
458
+ :disabled="isDisabled"
459
+ :class="[
460
+ 'lau-ecom-input dsEcom-border dsEcom-border-neutral-80 dsEcom-pl-4 dsEcom-pr-24 dsEcom-w-full dsEcom-focus:outline-none dsEcom-focus:ring-2 dsEcom-focus:ring-primary-60',
461
+ props.inputClass
462
+ ]"
463
+ @input="handleInput"
464
+ @keyup.enter="handleSearch"
465
+ autofocus
466
+ />
467
+ <div class="dsEcom-absolute dsEcom-right-0 dsEcom-inset-y-0 dsEcom-flex dsEcom-items-stretch">
468
+ <button
469
+ v-if="searchQuery.length >= 3"
470
+ @click="clearSearch"
471
+ :class="[
472
+ 'dsEcom-flex dsEcom-items-center dsEcom-px-1.5 dsEcom-text-neutral-100 hover:dsEcom-text-neutral-80 dsEcom-transition-all dsEcom-duration-300',
473
+ props.inputClass
474
+ ]"
475
+ >
476
+ <LauEcomUpcIconClose width="16" height="16" />
477
+ </button>
478
+
479
+ <button
480
+ @click="handleSearch"
481
+ :class="[
482
+ 'dsEcom-flex dsEcom-items-center dsEcom-px-3 dsEcom-transition-all dsEcom-duration-300',
483
+ { 'dsEcom-rounded-r-lg': !props.inputClass?.includes('rounded') },
484
+ props.buttonColorClass,
485
+ props.buttonTextColorClass,
486
+ 'dsEcom-border-0'
487
+ ]"
488
+ style="margin: 1px; height: calc(100% - 2px);"
489
+ >
490
+ <LauEcomUpcIconSearch width="20" height="20" color="currentColor" />
491
+ </button>
492
+ </div>
493
+ </div>
494
+ </div>
495
+ </div>
496
+ </div>
497
+ </template>
498
+
499
+ <style scoped>
500
+ .lau-ecom-input {
501
+ transition: all 0.3s ease-in-out;
502
+ }
503
+
504
+ .dsEcom-transform-gpu {
505
+ transform: translateZ(0);
506
+ backface-visibility: hidden;
507
+ perspective: 1000px;
508
+ }
509
+ </style>
510
+
323
511