evcc-io/assets/js/components/Helper/CustomSelect.vue

91 lines
1.9 KiB
Vue

<template>
<label
class="root position-relative keyboard-focus-ring"
:class="[
inline ? 'd-inline-block align-baseline' : 'd-block',
{ 'focus-ring-visible': showFocusRing },
]"
:for="id"
role="button"
@mousedown="mouseDown = true"
>
<select
:id="id"
:value="selected"
:aria-label="ariaLabel"
class="custom-select"
tabindex="0"
@change="change"
@focus="handleFocus"
@keydown="showFocusRing = true"
@blur="handleBlur"
>
<option
v-for="{ name, value, count, disabled } in options"
:key="value"
:value="value"
:disabled="count === 0 || disabled"
>
{{ text(name, count) }}
</option>
</select>
<slot></slot>
</label>
</template>
<script lang="ts">
import type { SelectOption } from "@/types/evcc";
import { defineComponent, type PropType } from "vue";
export default defineComponent({
name: "CustomSelect",
props: {
options: { type: Array as PropType<SelectOption<number | string>[]> },
selected: { type: [String, Number] },
id: { type: String },
inline: Boolean,
ariaLabel: { type: String },
},
emits: ["change"],
data() {
return {
// selects match :focus-visible even on mouse interaction, track modality manually
mouseDown: false,
showFocusRing: false,
};
},
methods: {
text(name: string, count?: number) {
if (count === undefined) {
return name;
}
return `${name} (${count})`;
},
change(event: Event) {
this.$emit("change", event);
},
handleFocus() {
this.showFocusRing = !this.mouseDown;
this.mouseDown = false;
},
handleBlur() {
this.showFocusRing = false;
this.mouseDown = false;
},
},
});
</script>
<style scoped>
.custom-select {
left: 0;
top: 0;
bottom: 0;
width: 100%;
cursor: pointer;
position: absolute;
opacity: 0;
-webkit-appearance: menulist-button;
/* WebKit propagates the select's intrinsic option width as overflow, causing page scroll */
contain: paint;
}
</style>