1+ <template >
2+ <div class =" size-selector" >
3+ <div class =" label" >Sizes</div >
4+
5+ <div class =" size-row" >
6+ <div v-for =" size in selectedSizes" :key =" size" class =" pill small" >{{ size }}</div >
7+
8+ <div class =" add-button-container" @click =" toggleDropdown" >
9+ <div class =" pill small add-circle" >
10+ <svg class =" plus-icon red" xmlns =" http://www.w3.org/2000/svg" width =" 15" height =" 14" viewBox =" 0 0 15 14" fill =" none" >
11+ <path d =" M13.0714 7.85714H8.42857V12.1429C8.42857 12.3702 8.33074 12.5882 8.1566 12.7489C7.98246 12.9097 7.74627 13 7.5 13C7.25373 13 7.01754 12.9097 6.8434 12.7489C6.66926 12.5882 6.57143 12.3702 6.57143 12.1429V7.85714H1.92857C1.6823 7.85714 1.44611 7.76684 1.27197 7.60609C1.09783 7.44535 1 7.22733 1 7C1 6.77267 1.09783 6.55465 1.27197 6.39391C1.44611 6.23316 1.6823 6.14286 1.92857 6.14286H6.57143V1.85714C6.57143 1.62981 6.66926 1.4118 6.8434 1.25105C7.01754 1.09031 7.25373 1 7.5 1C7.74627 1 7.98246 1.09031 8.1566 1.25105C8.33074 1.4118 8.42857 1.62981 8.42857 1.85714V6.14286H13.0714C13.3177 6.14286 13.5539 6.23316 13.728 6.39391C13.9022 6.55465 14 6.77267 14 7C14 7.22733 13.9022 7.44535 13.728 7.60609C13.5539 7.76684 13.3177 7.85714 13.0714 7.85714Z" fill =" red" stroke =" red" />
12+ </svg >
13+ </div >
14+ <span class =" add-label" >Add</span >
15+ </div >
16+ </div >
17+
18+ <div v-if =" dropdownOpen" class =" dropdown-wrapper" >
19+ <svg class =" dropdown-border" xmlns =" http://www.w3.org/2000/svg" viewBox =" 0 0 400 244" fill =" none" >
20+ <g filter =" url(#filter0_d_1343_914)" >
21+ <path d =" M10 10H390V214C390 225.046 381.046 234 370 234H30C18.9543 234 10 225.046 10 214V10Z" fill =" white" />
22+ <path d =" M389.5 10.5V214C389.5 224.77 380.77 233.5 370 233.5H30C19.2304 233.5 10.5 224.77 10.5 214V10.5H389.5Z" stroke =" black" />
23+ </g >
24+ <defs >
25+ <filter id =" filter0_d_1343_914" x =" 0" y =" 0" width =" 400" height =" 244" filterUnits =" userSpaceOnUse" color-interpolation-filters =" sRGB" >
26+ <feFlood flood-opacity =" 0" result =" BackgroundImageFix" />
27+ <feColorMatrix in =" SourceAlpha" type =" matrix" values =" 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result =" hardAlpha" />
28+ <feOffset />
29+ <feGaussianBlur stdDeviation =" 5" />
30+ <feComposite in2 =" hardAlpha" operator =" out" />
31+ <feColorMatrix type =" matrix" values =" 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0" />
32+ <feBlend mode =" normal" in2 =" BackgroundImageFix" result =" effect1_dropShadow_1343_914" />
33+ <feBlend mode =" normal" in =" SourceGraphic" in2 =" effect1_dropShadow_1343_914" result =" shape" />
34+ </filter >
35+ </defs >
36+ </svg >
37+
38+ <div class =" dropdown" >
39+ <div class =" size-grid" >
40+ <div
41+ v-for =" size in availableSizes"
42+ :key =" size"
43+ :class =" ['pill', 'small', getPillClass(size)]"
44+ @click =" handleSelect(size)"
45+ >
46+ <span v-if =" !props.selectedSizes.includes(size)" >{{ size }}</span >
47+ <div v-else class =" selected-pill-content" >
48+ <span class =" size-text" >{{ size }}</span >
49+ <svg
50+ class =" cross-icon"
51+ xmlns =" http://www.w3.org/2000/svg"
52+ width =" 10"
53+ height =" 10"
54+ viewBox =" 0 0 10 10"
55+ fill =" none"
56+ @click.stop =" removeSize(size)"
57+ >
58+ <path d =" M7.5 2.5L2.5 7.5M2.5 2.5L7.5 7.5" stroke =" #ea454c" stroke-width =" 1.5" stroke-linecap =" round" stroke-linejoin =" round" />
59+ </svg >
60+ </div >
61+ </div >
62+ </div >
63+
64+ <button class =" add-btn" @click =" addSizes" >
65+ Add
66+ <svg class =" plus-icon inverse" xmlns =" http://www.w3.org/2000/svg" width =" 15" height =" 14" viewBox =" 0 0 15 14" fill =" none" >
67+ <path d =" M13.0714 7.85714H8.42857V12.1429C8.42857 12.3702 8.33074 12.5882 8.1566 12.7489C7.98246 12.9097 7.74627 13 7.5 13C7.25373 13 7.01754 12.9097 6.8434 12.7489C6.66926 12.5882 6.57143 12.3702 6.57143 12.1429V7.85714H1.92857C1.6823 7.85714 1.44611 7.76684 1.27197 7.60609C1.09783 7.44535 1 7.22733 1 7C1 6.77267 1.09783 6.55465 1.27197 6.39391C1.44611 6.23316 1.6823 6.14286 1.92857 6.14286H6.57143V1.85714C6.57143 1.62981 6.66926 1.4118 6.8434 1.25105C7.01754 1.09031 7.25373 1 7.5 1C7.74627 1 7.98246 1.09031 8.1566 1.25105C8.33074 1.4118 8.42857 1.62981 8.42857 1.85714V6.14286H13.0714C13.3177 6.14286 13.5539 6.23316 13.728 6.39391C13.9022 6.55465 14 6.77267 14 7C14 7.22733 13.9022 7.44535 13.728 7.60609C13.5539 7.76684 13.3177 7.85714 13.0714 7.85714Z" fill =" white" stroke =" white" />
68+ </svg >
69+ </button >
70+ </div >
71+ </div >
72+ </div >
73+ </template >
74+
75+ <script setup>
76+ import { ref } from ' vue'
77+
78+ const props = defineProps ({
79+ availableSizes: {
80+ type: Array ,
81+ default : () => [' XXS' , ' XS' , ' S' , ' M' , ' L' , ' XL' , ' XXL' , ' XXXL' ]
82+ },
83+ selectedSizes: {
84+ type: Array ,
85+ default : () => []
86+ }
87+ })
88+
89+ const emit = defineEmits ([' update:selectedSizes' ])
90+
91+ const dropdownOpen = ref (false )
92+ const tempSelected = ref ([])
93+
94+ const toggleDropdown = () => {
95+ dropdownOpen .value = ! dropdownOpen .value
96+ tempSelected .value = []
97+ }
98+
99+ const handleSelect = (size ) => {
100+ if (props .selectedSizes .includes (size)) return
101+ if (tempSelected .value .includes (size)) {
102+ tempSelected .value = tempSelected .value .filter (s => s !== size)
103+ } else {
104+ tempSelected .value .push (size)
105+ }
106+ }
107+
108+ const addSizes = () => {
109+ const newSizes = [... props .selectedSizes , ... tempSelected .value ]
110+ emit (' update:selectedSizes' , newSizes)
111+ dropdownOpen .value = false
112+ tempSelected .value = []
113+ }
114+
115+ const removeSize = (size ) => {
116+ const newSizes = props .selectedSizes .filter (s => s !== size)
117+ emit (' update:selectedSizes' , newSizes)
118+ }
119+
120+ const getPillClass = (size ) => {
121+ if (props .selectedSizes .includes (size)) return ' disabled'
122+ if (tempSelected .value .includes (size)) return ' active'
123+ return ' '
124+ }
125+ </script >
126+
127+ <style scoped>
128+ .size-selector {
129+ width : 100% ;
130+ max-width : 380px ;
131+ font-family : ' Poppins' , sans-serif ;
132+ position : relative ;
133+ padding : 1rem ;
134+ box-sizing : border-box ;
135+ }
136+
137+ .label {
138+ font-size : 1.5rem ;
139+ color : #6c6c6c ;
140+ margin-bottom : 0.75rem ;
141+ }
142+
143+ .size-row {
144+ display : flex ;
145+ flex-wrap : wrap ;
146+ gap : 0.75rem ;
147+ align-items : flex-start ;
148+ margin-bottom : 0.5rem ;
149+ }
150+
151+ .pill {
152+ background : white ;
153+ border-radius : 50% ;
154+ box-shadow : 0 0 8px rgba (0 , 0 , 0 , 0.1 );
155+ font-weight : 500 ;
156+ color : #6c6c6c ;
157+ display : flex ;
158+ align-items : center ;
159+ justify-content : center ;
160+ cursor : pointer ;
161+ transition : all 0.2s ease ;
162+ }
163+
164+ .pill.small {
165+ width : 38px ;
166+ height : 38px ;
167+ font-size : 0.875rem ;
168+ }
169+
170+ .add-button-container {
171+ display : flex ;
172+ flex-direction : column ;
173+ align-items : center ;
174+ cursor : pointer ;
175+ }
176+
177+ .pill.add-circle {
178+ background : white ;
179+ border-radius : 50% ;
180+ width : 38px ;
181+ height : 38px ;
182+ display : flex ;
183+ align-items : center ;
184+ justify-content : center ;
185+ box-shadow : 0 0 8px rgba (0 , 0 , 0 , 0.1 );
186+ }
187+
188+ .add-label {
189+ font-size : 0.75rem ;
190+ color : #6c6c6c ;
191+ margin-top : 0.25rem ;
192+ text-align : center ;
193+ }
194+
195+ .dropdown-wrapper {
196+ position : relative ;
197+ width : 100% ;
198+ }
199+
200+ .dropdown-border {
201+ width : 100% ;
202+ height : auto ;
203+ display : block ;
204+ }
205+
206+ .dropdown {
207+ position : absolute ;
208+ top : 10px ;
209+ left : 0 ;
210+ width : 100% ;
211+ padding : 1.25rem ;
212+ box-sizing : border-box ;
213+ }
214+
215+ .size-grid {
216+ display : grid ;
217+ grid-template-columns : repeat (auto-fit , minmax (38px , 1fr ));
218+ gap : 0.75rem ;
219+ justify-items : center ;
220+ margin-bottom : 1.25rem ;
221+ }
222+
223+ .pill.active {
224+ border : 2px solid #ea454c ;
225+ }
226+
227+ .pill.disabled {
228+ opacity : 0.3 ;
229+ cursor : default ;
230+ }
231+
232+ .selected-pill-content {
233+ display : flex ;
234+ align-items : center ;
235+ justify-content : center ;
236+ gap : 2px ;
237+ width : 100% ;
238+ height : 100% ;
239+ }
240+
241+ .size-text {
242+ font-size : 0.75rem ;
243+ }
244+
245+ .cross-icon {
246+ cursor : pointer ;
247+ opacity : 0.7 ;
248+ transition : opacity 0.2s ease ;
249+ }
250+
251+ .cross-icon :hover {
252+ opacity : 1 ;
253+ }
254+
255+ .add-btn {
256+ background : #ea454c ;
257+ color : white ;
258+ border : none ;
259+ padding : 0.5rem 1.25rem ;
260+ border-radius : 5px ;
261+ font-size : 1rem ;
262+ font-weight : 700 ;
263+ display : flex ;
264+ align-items : center ;
265+ justify-content : center ;
266+ margin : auto ;
267+ cursor : pointer ;
268+ }
269+
270+ .add-btn .plus-icon.inverse {
271+ margin-left : 0.5rem ;
272+ color : white ;
273+ }
274+
275+ @media (max-width : 480px ) {
276+ .size-selector {
277+ padding : 0.75rem ;
278+ }
279+ .pill.small {
280+ width : 34px ;
281+ height : 34px ;
282+ font-size : 0.75rem ;
283+ }
284+ .label {
285+ font-size : 1.25rem ;
286+ }
287+ .add-btn {
288+ width : 100% ;
289+ }
290+ .pill.add-circle {
291+ width : 34px ;
292+ height : 34px ;
293+ }
294+ }
295+
296+ .plus-icon {
297+ width : 15px ;
298+ height : 14px ;
299+ display : inline-block ;
300+ }
301+
302+ .plus-icon.red {
303+ fill : red ;
304+ stroke : red ;
305+ }
306+
307+ .plus-icon.inverse {
308+ fill : white ;
309+ stroke : white ;
310+ }
311+
312+ .cross-icon {
313+ width : 10px ;
314+ height : 10px ;
315+ display : inline-block ;
316+ }
317+
318+ .pill.disabled .cross-icon {
319+ opacity : 1 !important ;
320+ cursor : pointer !important ;
321+ }
322+
323+ .pill.disabled .cross-icon path {
324+ stroke : #ea454c !important ;
325+ }
326+
327+ .pill.disabled .cross-icon :hover {
328+ transform : scale (1.1 );
329+ }
330+
331+ </style >
0 commit comments