Skip to content

Commit e539f2c

Browse files
Merge pull request #227 from ShirshenduR/Fix#214
Fix #214: feat: Made a size selection component with parent child communication with props
2 parents 8be1217 + 3b4c4f0 commit e539f2c

File tree

1 file changed

+331
-0
lines changed

1 file changed

+331
-0
lines changed
Lines changed: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
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

Comments
 (0)