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.
@@ -0,0 +1,229 @@
1
+ <!-- LauEcomInputSearchDesktop.vue -->
2
+ <script lang="ts" setup>
3
+ import { ref, computed } from 'vue';
4
+ import {
5
+ LauEcomUpcIconSearch,
6
+ LauEcomCoreIconNavClose as LauEcomUpcIconClose,
7
+ } from "../LauEcomIcon";
8
+
9
+ interface Props {
10
+ placeholder?: string;
11
+ isDisabled?: boolean;
12
+ modelValue?: string;
13
+ buttonColorClass?: string;
14
+ buttonTextColorClass?: string;
15
+ containerClass?: string;
16
+ expandedWidth?: string;
17
+ expandedBackgroundClass?: string;
18
+ overlayClass?: string;
19
+ inputClass?: string;
20
+ }
21
+
22
+ const props = withDefaults(defineProps<Props>(), {
23
+ placeholder: "Buscar...",
24
+ isDisabled: false,
25
+ modelValue: "",
26
+ buttonColorClass: "dsEcom-bg-primary-60 hover:dsEcom-bg-primary-70",
27
+ buttonTextColorClass: "dsEcom-text-white",
28
+ containerClass: "",
29
+ expandedWidth: "50vw",
30
+ expandedBackgroundClass: "dsEcom-bg-white",
31
+ overlayClass: "dsEcom-bg-black dsEcom-opacity-50",
32
+ inputClass: "dsEcom-h-10 dsEcom-rounded-lg"
33
+ });
34
+
35
+ const emit = defineEmits({
36
+ "update:modelValue": (value: string) => true,
37
+ "search": (value: string) => true,
38
+ "click:search-icon": () => true
39
+ });
40
+
41
+ const searchQuery = ref(props.modelValue);
42
+ const isExpanded = ref(false);
43
+ const showOverlay = ref(false);
44
+
45
+ const containerClasses = computed(() => {
46
+ return [
47
+ "dsEcom-transition-transform dsEcom-duration-300 dsEcom-ease-in-out dsEcom-relative",
48
+ {
49
+ "dsEcom-w-[250px] md:dsEcom-w-[350px]": !isExpanded.value
50
+ }
51
+ ];
52
+ });
53
+
54
+ const overlayClasses = computed(() => [
55
+ "dsEcom-fixed dsEcom-inset-0 dsEcom-transition-opacity dsEcom-duration-300 dsEcom-ease-in-out dsEcom-z-40",
56
+ props.overlayClass,
57
+ {
58
+ "dsEcom-opacity-100": showOverlay.value,
59
+ "dsEcom-opacity-0 dsEcom-pointer-events-none": !showOverlay.value
60
+ }
61
+ ]);
62
+
63
+ const handleSearch = () => {
64
+ if (searchQuery.value && searchQuery.value.trim()) {
65
+ emit("search", searchQuery.value);
66
+ emit("update:modelValue", searchQuery.value);
67
+ emit("click:search-icon");
68
+ closeSearch();
69
+ }
70
+ };
71
+
72
+ const handleInput = () => {
73
+ emit("update:modelValue", searchQuery.value);
74
+ };
75
+
76
+ const clearSearch = () => {
77
+ searchQuery.value = "";
78
+ emit("update:modelValue", "");
79
+ };
80
+
81
+ const closeSearch = () => {
82
+ showOverlay.value = false;
83
+ setTimeout(() => {
84
+ isExpanded.value = false;
85
+ }, 300);
86
+ };
87
+
88
+ const handleFocus = () => {
89
+ isExpanded.value = true;
90
+ showOverlay.value = true;
91
+ };
92
+ </script>
93
+
94
+ <template>
95
+ <div class="dsEcom-relative dsEcom-transform-gpu">
96
+ <div
97
+ :class="[containerClasses, containerClass]"
98
+ ref="originalContainer"
99
+ >
100
+ <div class="dsEcom-relative" :class="{ 'dsEcom-invisible': isExpanded }">
101
+ <input
102
+ v-model="searchQuery"
103
+ type="text"
104
+ :placeholder="placeholder"
105
+ :disabled="isDisabled"
106
+ :class="[
107
+ '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 dsEcom-transition-all dsEcom-duration-300',
108
+ inputClass,
109
+ { 'dsEcom-opacity-0': isExpanded }
110
+ ]"
111
+ @focus="handleFocus"
112
+ @input="handleInput"
113
+ @keyup.enter="handleSearch"
114
+ />
115
+ <div class="dsEcom-absolute dsEcom-right-0 dsEcom-inset-y-0 dsEcom-flex dsEcom-items-stretch">
116
+ <button
117
+ v-show="searchQuery.length >= 3"
118
+ @click="clearSearch"
119
+ :class="[
120
+ 'dsEcom-flex dsEcom-items-center dsEcom-px-1.5 dsEcom-text-neutral-100 hover:dsEcom-text-neutral-80 dsEcom-transition-all dsEcom-duration-300',
121
+ inputClass
122
+ ]"
123
+ >
124
+ <LauEcomUpcIconClose width="16" height="16" />
125
+ </button>
126
+
127
+ <button
128
+ @click="handleSearch"
129
+ :class="[
130
+ 'dsEcom-flex dsEcom-items-center dsEcom-px-3 dsEcom-transition-all dsEcom-duration-300',
131
+ inputClass?.includes('rounded') ? inputClass : 'dsEcom-rounded-r-lg',
132
+ buttonColorClass,
133
+ buttonTextColorClass,
134
+ 'dsEcom-border-0'
135
+ ]"
136
+ style="margin: 1px; height: calc(100% - 2px);"
137
+ >
138
+ <LauEcomUpcIconSearch width="20" height="20" color="currentColor" />
139
+ </button>
140
+ </div>
141
+ </div>
142
+ </div>
143
+
144
+ <!-- Overlay -->
145
+ <div
146
+ v-show="isExpanded"
147
+ :class="[
148
+ overlayClasses,
149
+ 'dsEcom-transition-opacity dsEcom-duration-300 dsEcom-ease-in-out'
150
+ ]"
151
+ @click="closeSearch"
152
+ ></div>
153
+
154
+ <!-- Versión expandida -->
155
+ <div
156
+ v-show="isExpanded"
157
+ class="dsEcom-fixed dsEcom-z-50 dsEcom-shadow-lg dsEcom-overflow-hidden dsEcom-transition-all dsEcom-duration-300"
158
+ :class="[
159
+ inputClass?.includes('rounded') ? inputClass : 'dsEcom-rounded-lg',
160
+ expandedBackgroundClass
161
+ ]"
162
+ :style="{
163
+ width: expandedWidth,
164
+ maxWidth: '90vw'
165
+ }"
166
+ ref="expandedContainer"
167
+ >
168
+ <div class="dsEcom-relative">
169
+ <input
170
+ v-model="searchQuery"
171
+ type="text"
172
+ :placeholder="placeholder"
173
+ :disabled="isDisabled"
174
+ :class="[
175
+ '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 dsEcom-transition-all dsEcom-duration-300',
176
+ inputClass
177
+ ]"
178
+ @input="handleInput"
179
+ @keyup.enter="handleSearch"
180
+ autofocus
181
+ />
182
+ <div class="dsEcom-absolute dsEcom-right-0 dsEcom-inset-y-0 dsEcom-flex dsEcom-items-stretch">
183
+ <button
184
+ v-show="searchQuery.length >= 3"
185
+ @click="clearSearch"
186
+ :class="[
187
+ 'dsEcom-flex dsEcom-items-center dsEcom-px-1.5 dsEcom-text-neutral-100 hover:dsEcom-text-neutral-80 dsEcom-transition-all dsEcom-duration-300',
188
+ inputClass
189
+ ]"
190
+ >
191
+ <LauEcomUpcIconClose width="16" height="16" />
192
+ </button>
193
+
194
+ <button
195
+ @click="handleSearch"
196
+ :class="[
197
+ 'dsEcom-flex dsEcom-items-center dsEcom-px-3 dsEcom-transition-all dsEcom-duration-300',
198
+ { 'dsEcom-rounded-r-lg': !inputClass?.includes('rounded') },
199
+ buttonColorClass,
200
+ buttonTextColorClass,
201
+ 'dsEcom-border-0'
202
+ ]"
203
+ style="margin: 1px; height: calc(100% - 2px);"
204
+ >
205
+ <LauEcomUpcIconSearch width="20" height="20" color="currentColor" />
206
+ </button>
207
+ </div>
208
+ </div>
209
+ </div>
210
+ </div>
211
+ </template>
212
+
213
+ <style scoped>
214
+ .lau-ecom-input {
215
+ transition: all 0.3s ease-in-out;
216
+ backface-visibility: hidden;
217
+ transform: translateZ(0);
218
+ -webkit-font-smoothing: antialiased;
219
+ -moz-osx-font-smoothing: grayscale;
220
+ will-change: transform, opacity;
221
+ }
222
+
223
+ .dsEcom-transform-gpu {
224
+ transform: translateZ(0);
225
+ backface-visibility: hidden;
226
+ perspective: 1000px;
227
+ will-change: transform, opacity;
228
+ }
229
+ </style>
@@ -0,0 +1,187 @@
1
+ <!-- LauEcomInputSearchMobile.vue -->
2
+ <script lang="ts" setup>
3
+ import { ref } from 'vue';
4
+ import {
5
+ LauEcomUpcIconSearch,
6
+ LauEcomCoreIconNavClose as LauEcomUpcIconClose,
7
+ LauEcomUpcIconNavArrow,
8
+ } from "../LauEcomIcon";
9
+
10
+ interface Props {
11
+ placeholder?: string;
12
+ isDisabled?: boolean;
13
+ modelValue?: string;
14
+ buttonColorClass?: string;
15
+ buttonTextColorClass?: string;
16
+ arrowColorClass?: string;
17
+ arrowButtonClass?: string;
18
+ mobileInputWidth?: string;
19
+ mobileInputHeight?: string;
20
+ mobilePanelClass?: string;
21
+ }
22
+
23
+ const props = withDefaults(defineProps<Props>(), {
24
+ placeholder: "Buscar...",
25
+ isDisabled: false,
26
+ modelValue: "",
27
+ buttonColorClass: "dsEcom-bg-primary-60",
28
+ buttonTextColorClass: "dsEcom-text-white",
29
+ arrowColorClass: "dsEcom-text-neutral-100",
30
+ arrowButtonClass: "",
31
+ mobileInputWidth: "240px",
32
+ mobileInputHeight: "40px",
33
+ mobilePanelClass: "dsEcom-bg-white"
34
+ });
35
+
36
+ const emit = defineEmits({
37
+ "update:modelValue": (value: string) => true,
38
+ "search": (value: string) => true,
39
+ "click:search-icon": () => true
40
+ });
41
+
42
+ const searchQuery = ref(props.modelValue);
43
+ const isMobileSearchOpen = ref(false);
44
+
45
+ const handleMobileSearchClick = () => {
46
+ isMobileSearchOpen.value = true;
47
+ };
48
+
49
+ const handleMobileSearchClose = () => {
50
+ const panel = document.querySelector('.dsEcom-fixed') as HTMLElement;
51
+ if (panel) {
52
+ panel.style.opacity = '0';
53
+ panel.style.transform = 'translateY(100%)';
54
+ }
55
+
56
+ setTimeout(() => {
57
+ isMobileSearchOpen.value = false;
58
+ searchQuery.value = '';
59
+ emit('update:modelValue', '');
60
+ }, 300);
61
+ };
62
+
63
+ const handleMobileSearch = () => {
64
+ if (searchQuery.value && searchQuery.value.trim()) {
65
+ emit("search", searchQuery.value);
66
+ emit("update:modelValue", searchQuery.value);
67
+ emit("click:search-icon");
68
+ handleMobileSearchClose();
69
+ }
70
+ };
71
+
72
+ const handleInput = () => {
73
+ emit("update:modelValue", searchQuery.value);
74
+ };
75
+
76
+ const handleMobileClear = () => {
77
+ searchQuery.value = "";
78
+ emit("update:modelValue", "");
79
+ };
80
+ </script>
81
+
82
+ <template>
83
+ <div class="dsEcom-relative">
84
+ <!-- Botón de búsqueda -->
85
+ <button
86
+ v-show="!isMobileSearchOpen"
87
+ @click="handleMobileSearchClick"
88
+ class="dsEcom-p-2 dsEcom-rounded-lg dsEcom-transition-all dsEcom-duration-300 dsEcom-transform-gpu"
89
+ :class="[buttonColorClass]"
90
+ >
91
+ <LauEcomUpcIconSearch
92
+ width="24"
93
+ height="24"
94
+ :class="[buttonTextColorClass]"
95
+ />
96
+ </button>
97
+
98
+ <!-- Panel de búsqueda -->
99
+ <div
100
+ v-show="isMobileSearchOpen"
101
+ class="dsEcom-fixed dsEcom-inset-0 dsEcom-z-50 dsEcom-transition-all dsEcom-duration-300 dsEcom-transform-gpu"
102
+ :class="[mobilePanelClass]"
103
+ :style="{
104
+ opacity: isMobileSearchOpen ? '1' : '0',
105
+ transform: isMobileSearchOpen ? 'translateY(0)' : 'translateY(100%)'
106
+ }"
107
+ >
108
+ <div class="dsEcom-flex dsEcom-items-center dsEcom-gap-2 dsEcom-p-4">
109
+ <div class="dsEcom-flex dsEcom-items-center dsEcom-gap-2 dsEcom-flex-1 dsEcom-transform-gpu">
110
+ <button
111
+ @click="handleMobileSearchClose"
112
+ class="dsEcom-p-2 dsEcom-rounded-lg dsEcom-transition-all dsEcom-duration-300 dsEcom-transform-gpu"
113
+ :class="[arrowButtonClass]"
114
+ >
115
+ <LauEcomUpcIconNavArrow
116
+ width="24"
117
+ height="24"
118
+ class="dsEcom-transform dsEcom-rotate-[-90deg] dsEcom-transition-all dsEcom-duration-300 dsEcom-transform-gpu"
119
+ :class="[arrowColorClass]"
120
+ color="currentColor"
121
+ />
122
+ </button>
123
+
124
+ <div class="dsEcom-flex dsEcom-items-center dsEcom-gap-0 dsEcom-flex-1 dsEcom-transform-gpu">
125
+ <input
126
+ v-model="searchQuery"
127
+ type="text"
128
+ :placeholder="placeholder"
129
+ :disabled="isDisabled"
130
+ :style="{
131
+ width: mobileInputWidth,
132
+ height: mobileInputHeight
133
+ }"
134
+ 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 dsEcom-transition-all dsEcom-duration-300 dsEcom-transform-gpu"
135
+ @input="handleInput"
136
+ @keyup.enter="handleMobileSearch"
137
+ />
138
+ <button
139
+ @click="handleMobileSearch"
140
+ :style="{ height: mobileInputHeight }"
141
+ :class="[
142
+ 'dsEcom-px-3 dsEcom-rounded-r-lg dsEcom-border-0 dsEcom-transition-all dsEcom-duration-300 dsEcom-transform-gpu',
143
+ buttonColorClass,
144
+ buttonTextColorClass
145
+ ]"
146
+ >
147
+ <LauEcomUpcIconSearch width="20" height="20" color="currentColor" />
148
+ </button>
149
+ </div>
150
+
151
+ <button
152
+ v-show="searchQuery.length >= 3"
153
+ @click="handleMobileClear"
154
+ class="dsEcom-p-2 dsEcom-text-neutral-100 hover:dsEcom-text-neutral-80 dsEcom-transition-all dsEcom-duration-300 dsEcom-transform-gpu"
155
+ >
156
+ <LauEcomUpcIconClose width="16" height="16" />
157
+ </button>
158
+ </div>
159
+ </div>
160
+ </div>
161
+ </div>
162
+ </template>
163
+
164
+ <style scoped>
165
+ .lau-ecom-input {
166
+ transition: all 0.3s ease-in-out;
167
+ backface-visibility: hidden;
168
+ transform: translateZ(0);
169
+ -webkit-font-smoothing: antialiased;
170
+ -moz-osx-font-smoothing: grayscale;
171
+ will-change: transform, opacity;
172
+ }
173
+
174
+ .dsEcom-transform-gpu {
175
+ transform: translateZ(0);
176
+ backface-visibility: hidden;
177
+ perspective: 1000px;
178
+ will-change: transform, opacity;
179
+ }
180
+
181
+ @media (max-width: 768px) {
182
+ .dsEcom-fixed {
183
+ transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out;
184
+ will-change: transform, opacity;
185
+ }
186
+ }
187
+ </style>