Skip to content

Commit 9136819

Browse files
committed
feat(select): add optionSelected and optionDeselected events
1 parent ac937c3 commit 9136819

File tree

2 files changed

+61
-11
lines changed

2 files changed

+61
-11
lines changed

docs/events.md

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,46 @@ title: 'Events'
44

55
# Events
66

7-
Currently, Vue 3 Select Component doesn't emit any custom events, except for the native `v-model` one.
8-
97
If you have the need for custom events, please open an issue on the [GitHub repository](https://github.com/TotomInc/vue3-select-component) with your use case and we will be happy to investigate it.
8+
9+
## `@option-selected`
10+
11+
Emitted when an option is selected, in the same tick where the `v-model` is updated.
12+
13+
```vue
14+
<template>
15+
<VueSelect
16+
v-model="selectedValue"
17+
:options="options"
18+
@option-selected="(option) => console.log(option.label, option.value)"
19+
/>
20+
</template>
21+
```
22+
23+
**Note**: this is emitted on the same tick as the v-model is updated, before a DOM re-render.
24+
25+
::: info
26+
If you want to keep track of the selected option, it is recommended to use a `computed` combined with the `v-model`, instead of this event ([see this issue comment](https://github.com/TotomInc/vue3-select-component/issues/7#issuecomment-2083422621)).
27+
28+
```ts
29+
const options = [{ label: "France", value: "FR" }, { label: "Spain", value: "ES" }];
30+
const activeValue = ref<string>();
31+
const selectedOption = computed(() => options.find((option) => option.value === activeValue.value));
32+
```
33+
:::
34+
35+
## `@option-deselected`
36+
37+
Emitted when an option is deselected, in the same tick where the `v-model` is updated.
38+
39+
```vue
40+
<template>
41+
<VueSelect
42+
v-model="selectedValue"
43+
:options="options"
44+
@option-deselected="(option) => console.log(option.label, option.value)"
45+
/>
46+
</template>
47+
```
48+
49+
**Note**: this is emitted on the same tick as the v-model is updated, before a DOM re-render.

src/Select.vue

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ const props = withDefaults(
101101
},
102102
);
103103
104+
const emit = defineEmits<{
105+
(e: "optionSelected", option: GenericOption): void;
106+
(e: "optionDeselected", option: GenericOption | null): void;
107+
}>();
108+
104109
/**
105110
* The value of the selected option. When `isMulti` prop is set to `true`, this
106111
* should be an array of `OptionValue`.
@@ -164,14 +169,16 @@ const closeMenu = () => {
164169
search.value = "";
165170
};
166171
167-
const setOption = (value: OptionValue) => {
172+
const setOption = (option: GenericOption) => {
168173
if (props.isMulti) {
169-
(selected.value as OptionValue[]).push(value);
174+
(selected.value as OptionValue[]).push(option.value);
170175
}
171176
else {
172-
selected.value = value;
177+
selected.value = option.value;
173178
}
174179
180+
emit("optionSelected", option);
181+
175182
search.value = "";
176183
177184
if (props.closeOnSelect) {
@@ -183,18 +190,21 @@ const setOption = (value: OptionValue) => {
183190
}
184191
};
185192
186-
const removeOption = (value: OptionValue) => {
193+
const removeOption = (option: GenericOption) => {
187194
if (props.isMulti) {
188-
selected.value = (selected.value as OptionValue[]).filter((v) => v !== value);
195+
selected.value = (selected.value as OptionValue[]).filter((value) => value !== option.value);
196+
emit("optionDeselected", option);
189197
}
190198
};
191199
192200
const clear = () => {
193201
if (props.isMulti) {
194202
selected.value = [];
203+
emit("optionDeselected", null);
195204
}
196205
else {
197206
selected.value = undefined as OptionValue;
207+
emit("optionDeselected", selectedOptions.value[0]);
198208
}
199209
200210
menuOpen.value = false;
@@ -219,13 +229,13 @@ const handleNavigation = (e: KeyboardEvent) => {
219229
220230
if (e.key === "Enter") {
221231
e.preventDefault();
222-
setOption(filteredOptions.value[focusedOption.value].value);
232+
setOption(filteredOptions.value[focusedOption.value]);
223233
}
224234
225235
// When pressing space with menu open but no search, select the focused option.
226236
if (e.code === "Space" && search.value.length === 0) {
227237
e.preventDefault();
228-
setOption(filteredOptions.value[focusedOption.value].value);
238+
setOption(filteredOptions.value[focusedOption.value]);
229239
}
230240
231241
if (e.key === "Escape") {
@@ -342,7 +352,7 @@ onBeforeUnmount(() => {
342352
:key="i"
343353
type="button"
344354
class="multi-value"
345-
@click="removeOption(option.value)"
355+
@click="removeOption(option)"
346356
>
347357
{{ getMultiValueLabel(option) }}
348358
<XMarkIcon />
@@ -421,7 +431,7 @@ onBeforeUnmount(() => {
421431
:index="i"
422432
:is-focused="focusedOption === i"
423433
:is-selected="option.value === selected"
424-
@select="setOption(option.value)"
434+
@select="setOption(option)"
425435
>
426436
<slot name="option" :option="option">
427437
{{ getOptionLabel(option) }}

0 commit comments

Comments
 (0)