Skip to content

Commit b9e1ed3

Browse files
authored
feat(collapse): added collapse and chevron up for tests (#90)
1 parent e19a7a0 commit b9e1ed3

File tree

2 files changed

+316
-0
lines changed

2 files changed

+316
-0
lines changed
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
<script setup lang="ts">
2+
import { ref } from "vue";
3+
import chevronUp from "../../assets/icons_svg/Chevron-up.svg";
4+
5+
/**
6+
* Custom collapse input field.
7+
* @displayName input-collapse
8+
*/
9+
10+
const props = defineProps({
11+
/**
12+
* Text contained within the top of the collapse.
13+
*/
14+
labelText: {
15+
type: String,
16+
required: true,
17+
},
18+
/**
19+
* Chosen placeholder value, not necessarily part of the valueGroup.
20+
*/
21+
placeHolderValue: {
22+
type: String,
23+
default: "",
24+
},
25+
/**
26+
* Group of values to be selected within the collapse.
27+
*/
28+
valueGroup: {
29+
type: Array<any>,
30+
required: true,
31+
},
32+
/**
33+
* Group of names to serve as placeholders for valueGroup, if its length does not match the
34+
* valueGroup's the value will be swapped for the valueGroup's values capitalized.
35+
*/
36+
valueNames: {
37+
type: Array<String>,
38+
default: [],
39+
},
40+
/**
41+
* Value selected to be displayed in the collapse's field, the actual value being given through
42+
* methods
43+
*/
44+
selectedValue: {
45+
type: String,
46+
default: "",
47+
},
48+
/**
49+
* Additional function to be called whenever an option is selected
50+
*/
51+
onSelect: {
52+
type: Function,
53+
},
54+
/**
55+
* Indicates whether the choice made is erroneous
56+
*/
57+
isError: {
58+
type: Boolean,
59+
default: false,
60+
},
61+
/**
62+
* Controls whether the collapse is open by default
63+
*/
64+
isOpen: {
65+
type: Boolean,
66+
default: false,
67+
},
68+
});
69+
70+
// Setting ref values
71+
let isCollapseOpen = ref(props.isOpen);
72+
let selectedValue = ref(props.placeHolderValue);
73+
let selectedValueIndex = ref(props.valueGroup.indexOf(props.selectedValue));
74+
let hasBeenOpenedViaClick = ref(false);
75+
76+
// Providing the displayed strings using either the valueNames provided or the capitalized-string version of the valueGroup's contents
77+
let defaultValueNames: Array<string> =
78+
props.valueNames.length > 0 && props.valueGroup.length
79+
? props.valueNames
80+
: props.valueGroup.map((x) => x.toString()[0].toUpperCase() + x.toString().slice(1));
81+
82+
// Emitter, allows to pass the selected option's value to parent components
83+
const emit = defineEmits<{ emittedSelectedInput: [value: any] }>();
84+
85+
// Called upon selecting a value, changes the value emitted and calls the onSelect function if it exists
86+
const onSelectValue = (value: String) => {
87+
if (props.onSelect) {
88+
props.onSelect();
89+
}
90+
selectedValue.value = value as string;
91+
emit("emittedSelectedInput", value);
92+
defineSelectedElementIndex(value as string);
93+
};
94+
95+
// Opens or closes the collapse
96+
const onToggleOpen = () => {
97+
isCollapseOpen.value = !isCollapseOpen.value;
98+
hasBeenOpenedViaClick.value = !hasBeenOpenedViaClick.value;
99+
};
100+
101+
// Opens or closes the collapse if the user clicks outside of the component
102+
const onClickOutside = () => {
103+
if (hasBeenOpenedViaClick.value) {
104+
hasBeenOpenedViaClick.value = !hasBeenOpenedViaClick.value;
105+
isCollapseOpen.value = !isCollapseOpen.value;
106+
}
107+
};
108+
109+
// Changes the selected element's index to help with display
110+
const defineSelectedElementIndex = (value: string) => {
111+
selectedValueIndex.value = props.valueGroup.indexOf(value);
112+
};
113+
114+
const classList = [
115+
"input__border-background",
116+
"input__container-flex",
117+
"input__label-style",
118+
"collapse",
119+
];
120+
121+
const isBrowserMozilla = new RegExp("Firefox").test(navigator.userAgent.toString());
122+
123+
</script>
124+
125+
<template>
126+
<section
127+
@click="onToggleOpen"
128+
@keypress.enter="onToggleOpen"
129+
v-click-outside="onClickOutside"
130+
:class="[classList, props.isError ? 'input__error' : '']"
131+
tabindex="0"
132+
>
133+
<div :class="'collapse_container'">
134+
<input
135+
:value="defaultValueNames[props.valueGroup.indexOf(selectedValue)]"
136+
:placeholder="props.placeHolderValue"
137+
class="input__text input__no-border-background input_button"
138+
type="text"
139+
id="collapse-text"
140+
readonly
141+
/>
142+
<figure>
143+
<img
144+
:src="chevronUp"
145+
:class="isCollapseOpen ? 'opened' : 'closed'"
146+
alt="collapse_content_visibility_option"
147+
/>
148+
</figure>
149+
</div>
150+
151+
<label
152+
class="collapse-label"
153+
for="collapse-text"
154+
>{{ props.labelText }}</label
155+
>
156+
157+
<Transition>
158+
<div
159+
:class="isBrowserMozilla ? 'collapse-list-firefox' : 'collapse-list-others'"
160+
v-if="isCollapseOpen"
161+
>
162+
<div
163+
class="collapse-element"
164+
tabindex="0"
165+
v-for="(item, index) in props.valueGroup"
166+
@keypress.enter="onSelectValue(item)"
167+
:key="index"
168+
>
169+
<span @click="onSelectValue(item)">{{ defaultValueNames[index] }}</span>
170+
</div>
171+
</div>
172+
</Transition>
173+
</section>
174+
</template>
175+
176+
<style scoped lang="scss">
177+
.input__error {
178+
border: 1px solid red;
179+
}
180+
181+
.input__error:after {
182+
content: "Please choose a valid option.";
183+
position: absolute;
184+
bottom: 0%;
185+
left: 30%;
186+
font-size: 0.8rem;
187+
font-weight: 900;
188+
color: red;
189+
}
190+
191+
.input_button {
192+
flex: 1;
193+
cursor: pointer;
194+
}
195+
196+
.opened {
197+
transform: rotate(0deg);
198+
transition-duration: 0.5s;
199+
}
200+
201+
.closed {
202+
transform: rotate(180deg);
203+
transition-duration: 0.5s;
204+
}
205+
206+
.collapse-label {
207+
margin-top: -1rem;
208+
font-size: 0.8rem;
209+
}
210+
211+
.collapse-list-others {
212+
background-color: white;
213+
position: absolute;
214+
width: 85%;
215+
max-height: 8.5rem;
216+
overflow-y: auto;
217+
z-index: 1;
218+
margin-top: 10px;
219+
padding: 0 1rem 1rem 1rem;
220+
border-radius: 0 0 2rem 2rem;
221+
box-shadow: $dark-color 0px 25px 20px -10px;
222+
223+
&::-webkit-scrollbar {
224+
width: 20px;
225+
}
226+
227+
&::-webkit-scrollbar-thumb {
228+
padding: 0 5px;
229+
border-right: 5px solid transparent;
230+
border-left: 5px solid transparent;
231+
background-clip: padding-box;
232+
border-radius: 0.6rem;
233+
background-color: #d8d8d8;
234+
}
235+
236+
&::-webkit-scrollbar-thumb:hover {
237+
background-color: $background-color-theme-secondary;
238+
}
239+
240+
&::-webkit-scrollbar-track-piece:end {
241+
background: transparent;
242+
margin-bottom: 25px;
243+
}
244+
}
245+
246+
.collapse-list-firefox {
247+
background-color: white;
248+
position: absolute;
249+
width: 85%;
250+
max-height: 8.5rem;
251+
overflow-y: auto;
252+
z-index: 1;
253+
margin-top: 10px;
254+
padding: 0 1rem 1rem 1rem;
255+
border-radius: 0 0 2rem 2rem;
256+
box-shadow: $dark-color 0px 25px 20px -10px;
257+
258+
scrollbar-width: 8px;
259+
scrollbar-color: $background-color-theme-secondary transparent;
260+
}
261+
262+
.collapse-element {
263+
padding: 0.3rem;
264+
border-top: 1px solid $option-separator;
265+
display: block;
266+
font-family: "SF-compact-Rounded";
267+
}
268+
269+
.collapse-element:hover {
270+
cursor: pointer;
271+
}
272+
273+
.collapse-element:hover span {
274+
background-color: $option-hover-color-passive;
275+
}
276+
277+
.collapse-element:focus span {
278+
background-color: $option-hover-color-active;
279+
}
280+
281+
.collapse-element:focus {
282+
border: 1px solid red;
283+
}
284+
285+
.collapse-element span {
286+
display: block;
287+
width: 90%;
288+
padding: 0.7rem;
289+
border-radius: 0.3rem;
290+
}
291+
292+
.v-enter-active,
293+
.v-leave-active {
294+
max-height: 8.5rem;
295+
transition: 0.2s ease-out;
296+
}
297+
298+
.v-enter-from,
299+
.v-leave-to {
300+
max-height: 0rem;
301+
opacity: 0;
302+
}
303+
304+
.collapse {
305+
display: block;
306+
margin-bottom: 1rem;
307+
cursor: pointer;
308+
}
309+
310+
.collapse_container {
311+
display: flex;
312+
}
313+
</style>

0 commit comments

Comments
 (0)