From ac937c39e3c184054df806b88ceb7880dfe422ef Mon Sep 17 00:00:00 2001 From: Thomas Cazade Date: Tue, 30 Apr 2024 09:49:24 +0200 Subject: [PATCH 1/2] chore: add recommended vscode settings --- .gitignore | 1 + .vscode/settings.json | 46 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.gitignore b/.gitignore index 8053325..a2a27ff 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ coverage # Editor directories and files .vscode/* !.vscode/extensions.json +!.vscode/settings.json .idea *.suo *.ntvs* diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..4cb9519 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,46 @@ +{ + // Enable the ESlint flat config support. + "eslint.experimental.useFlatConfig": true, + + // Disable the default formatter, use ESLint instead. + "prettier.enable": false, + "editor.formatOnSave": false, + + // Auto fix ESLint errors on save. + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit", + "source.organizeImports": "never" + }, + + // Silent the stylistic rules in you IDE, but still auto fix them. + "eslint.rules.customizations": [ + { "rule": "style/*", "severity": "off" }, + { "rule": "format/*", "severity": "off" }, + { "rule": "*-indent", "severity": "off" }, + { "rule": "*-spacing", "severity": "off" }, + { "rule": "*-spaces", "severity": "off" }, + { "rule": "*-order", "severity": "off" }, + { "rule": "*-dangle", "severity": "off" }, + { "rule": "*-newline", "severity": "off" }, + { "rule": "*quotes", "severity": "off" }, + { "rule": "*semi", "severity": "off" } + ], + + // Enable eslint for all supported languages. + "eslint.validate": [ + "javascript", + "javascriptreact", + "typescript", + "typescriptreact", + "vue", + "html", + "markdown", + "json", + "jsonc", + "yaml", + "toml" + ], + + // Vue Official extension should not have the hybrid mode. + "vue.server.hybridMode": false, +} \ No newline at end of file From 9136819b380dae6cfe1385d01a46081f6951912b Mon Sep 17 00:00:00 2001 From: Thomas Cazade Date: Tue, 30 Apr 2024 09:50:08 +0200 Subject: [PATCH 2/2] feat(select): add optionSelected and optionDeselected events --- docs/events.md | 44 ++++++++++++++++++++++++++++++++++++++++++-- src/Select.vue | 28 +++++++++++++++++++--------- 2 files changed, 61 insertions(+), 11 deletions(-) diff --git a/docs/events.md b/docs/events.md index faeb924..2b803d1 100644 --- a/docs/events.md +++ b/docs/events.md @@ -4,6 +4,46 @@ title: 'Events' # Events -Currently, Vue 3 Select Component doesn't emit any custom events, except for the native `v-model` one. - 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. + +## `@option-selected` + +Emitted when an option is selected, in the same tick where the `v-model` is updated. + +```vue + +``` + +**Note**: this is emitted on the same tick as the v-model is updated, before a DOM re-render. + +::: info +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)). + +```ts +const options = [{ label: "France", value: "FR" }, { label: "Spain", value: "ES" }]; +const activeValue = ref(); +const selectedOption = computed(() => options.find((option) => option.value === activeValue.value)); +``` +::: + +## `@option-deselected` + +Emitted when an option is deselected, in the same tick where the `v-model` is updated. + +```vue + +``` + +**Note**: this is emitted on the same tick as the v-model is updated, before a DOM re-render. diff --git a/src/Select.vue b/src/Select.vue index 86b3163..cf6091e 100644 --- a/src/Select.vue +++ b/src/Select.vue @@ -101,6 +101,11 @@ const props = withDefaults( }, ); +const emit = defineEmits<{ + (e: "optionSelected", option: GenericOption): void; + (e: "optionDeselected", option: GenericOption | null): void; +}>(); + /** * The value of the selected option. When `isMulti` prop is set to `true`, this * should be an array of `OptionValue`. @@ -164,14 +169,16 @@ const closeMenu = () => { search.value = ""; }; -const setOption = (value: OptionValue) => { +const setOption = (option: GenericOption) => { if (props.isMulti) { - (selected.value as OptionValue[]).push(value); + (selected.value as OptionValue[]).push(option.value); } else { - selected.value = value; + selected.value = option.value; } + emit("optionSelected", option); + search.value = ""; if (props.closeOnSelect) { @@ -183,18 +190,21 @@ const setOption = (value: OptionValue) => { } }; -const removeOption = (value: OptionValue) => { +const removeOption = (option: GenericOption) => { if (props.isMulti) { - selected.value = (selected.value as OptionValue[]).filter((v) => v !== value); + selected.value = (selected.value as OptionValue[]).filter((value) => value !== option.value); + emit("optionDeselected", option); } }; const clear = () => { if (props.isMulti) { selected.value = []; + emit("optionDeselected", null); } else { selected.value = undefined as OptionValue; + emit("optionDeselected", selectedOptions.value[0]); } menuOpen.value = false; @@ -219,13 +229,13 @@ const handleNavigation = (e: KeyboardEvent) => { if (e.key === "Enter") { e.preventDefault(); - setOption(filteredOptions.value[focusedOption.value].value); + setOption(filteredOptions.value[focusedOption.value]); } // When pressing space with menu open but no search, select the focused option. if (e.code === "Space" && search.value.length === 0) { e.preventDefault(); - setOption(filteredOptions.value[focusedOption.value].value); + setOption(filteredOptions.value[focusedOption.value]); } if (e.key === "Escape") { @@ -342,7 +352,7 @@ onBeforeUnmount(() => { :key="i" type="button" class="multi-value" - @click="removeOption(option.value)" + @click="removeOption(option)" > {{ getMultiValueLabel(option) }} @@ -421,7 +431,7 @@ onBeforeUnmount(() => { :index="i" :is-focused="focusedOption === i" :is-selected="option.value === selected" - @select="setOption(option.value)" + @select="setOption(option)" > {{ getOptionLabel(option) }}