Skip to content

Commit bf11bf5

Browse files
authored
Merge pull request #1 from howard-tzw/fix-1273-close-dropdown-when-clicking-selection
feat: close dropdown when clicking selection
2 parents 4942cb2 + d886d78 commit bf11bf5

File tree

5 files changed

+136
-116
lines changed

5 files changed

+136
-116
lines changed

package.json

Lines changed: 100 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,104 @@
11
{
2-
"name": "vue-select",
3-
"version": "4.0.0-beta.6",
4-
"description": "Everything you wish the HTML <select> element could do, wrapped up into a lightweight, extensible Vue component.",
5-
"author": "Jeff Sagal <sagalbot@gmail.com>",
6-
"homepage": "https://vue-select.org",
7-
"directories": {
8-
"doc": "docs",
9-
"test": "tests"
2+
"name": "vue3-select",
3+
"version": "0.0.1",
4+
"description": "Everything you wish the HTML <select> element could do, wrapped up into a lightweight, extensible Vue component.",
5+
"author": "Howard Chen <howard.tzw@gmail.com>, Jeff Sagal <sagalbot@gmail.com>",
6+
"homepage": "https://vue-select.org",
7+
"directories": {
8+
"doc": "docs",
9+
"test": "tests"
10+
},
11+
"files": [
12+
"dist"
13+
],
14+
"main": "./dist/vue-select.umd.js",
15+
"module": "./dist/vue-select.es.js",
16+
"exports": {
17+
".": {
18+
"import": "./dist/vue-select.es.js",
19+
"require": "./dist/vue-select.umd.js"
1020
},
11-
"files": [
12-
"dist"
13-
],
14-
"main": "./dist/vue-select.umd.js",
15-
"module": "./dist/vue-select.es.js",
16-
"exports": {
17-
".": {
18-
"import": "./dist/vue-select.es.js",
19-
"require": "./dist/vue-select.umd.js"
20-
},
21-
"./dist/vue-select.css": {
22-
"import": "./dist/vue-select.css",
23-
"require": "./dist/vue-select.css",
24-
"style": "./dist/vue-select.css"
25-
}
26-
},
27-
"private": false,
28-
"license": "MIT",
29-
"prepare": "npm run build",
30-
"scripts": {
31-
"dev:docs": "cd docs && yarn serve",
32-
"build:docs": "cd docs && yarn build",
33-
"semantic-release": "semantic-release",
34-
"commit": "git-cz",
35-
"dev": "vite",
36-
"build": "vue-tsc --noEmit && vite build",
37-
"preview": "vite preview --port 5050",
38-
"test": "vitest --environment jsdom",
39-
"coverage": "vitest --run --coverage --environment jsdom --silent",
40-
"typecheck": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false",
41-
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
42-
},
43-
"repository": {
44-
"type": "git",
45-
"url": "https://github.com/sagalbot/vue-select.git"
46-
},
47-
"peerDependencies": {
48-
"vue": "3.x"
49-
},
50-
"devDependencies": {
51-
"@rushstack/eslint-patch": "^1.1.4",
52-
"@semantic-release/git": "^10.0.1",
53-
"@semantic-release/github": "^8.0.5",
54-
"@types/jsdom": "^16.2.14",
55-
"@types/node": "^18.0.5",
56-
"@vitejs/plugin-vue": "^3.0.0",
57-
"@vue/eslint-config-prettier": "^7.0.0",
58-
"@vue/eslint-config-typescript": "^11.0.0",
59-
"@vue/test-utils": "^2.0.2",
60-
"@vue/tsconfig": "^0.1.3",
61-
"autoprefixer": "^10.4.7",
62-
"bundlewatch": "^0.3.3",
63-
"c8": "^7.11.3",
64-
"commitizen": "^4.2.5",
65-
"coveralls": "^3.1.1",
66-
"cross-env": "^7.0.3",
67-
"cz-conventional-changelog": "3.3.0",
68-
"eslint": "^8.20.0",
69-
"eslint-plugin-vue": "^9.2.0",
70-
"jsdom": "^20.0.0",
71-
"postcss-nested": "^5.0.6",
72-
"prettier": "^2.7.1",
73-
"semantic-release": "^19.0.3",
74-
"typescript": "^4.7.4",
75-
"vite": "^3.0.0",
76-
"vitest": "^0.18.1",
77-
"vue": "^3.2.37",
78-
"vue-tsc": "^0.38.8"
79-
},
80-
"config": {
81-
"commitizen": {
82-
"path": "./node_modules/cz-conventional-changelog"
83-
}
84-
},
85-
"bundlewatch": {
86-
"files": [
87-
{
88-
"path": "./dist/vue-select.es.js",
89-
"compression": "gzip",
90-
"maxSize": "8 KB"
91-
},
92-
{
93-
"path": "./dist/vue-select.umd.js",
94-
"compression": "gzip",
95-
"maxSize": "7 KB"
96-
},
97-
{
98-
"path": "./dist/vue-select.css",
99-
"compression": "gzip",
100-
"maxSize": "2 KB"
101-
}
102-
]
21+
"./dist/vue-select.css": {
22+
"import": "./dist/vue-select.css",
23+
"require": "./dist/vue-select.css",
24+
"style": "./dist/vue-select.css"
10325
}
26+
},
27+
"private": false,
28+
"license": "MIT",
29+
"prepare": "npm run build",
30+
"scripts": {
31+
"dev:docs": "cd docs && yarn serve",
32+
"build:docs": "cd docs && yarn build",
33+
"semantic-release": "semantic-release",
34+
"commit": "git-cz",
35+
"dev": "vite",
36+
"build": "vue-tsc --noEmit && vite build",
37+
"preview": "vite preview --port 5050",
38+
"test": "vitest --environment jsdom",
39+
"coverage": "vitest --run --coverage --environment jsdom --silent",
40+
"typecheck": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false",
41+
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
42+
},
43+
"repository": {
44+
"type": "git",
45+
"url": "https://github.com/sagalbot/vue-select.git"
46+
},
47+
"peerDependencies": {
48+
"vue": "3.x"
49+
},
50+
"devDependencies": {
51+
"@rushstack/eslint-patch": "^1.1.4",
52+
"@semantic-release/git": "^10.0.1",
53+
"@semantic-release/github": "^8.0.5",
54+
"@types/jsdom": "^16.2.14",
55+
"@types/node": "^18.0.5",
56+
"@vitejs/plugin-vue": "^3.0.0",
57+
"@vue/eslint-config-prettier": "^7.0.0",
58+
"@vue/eslint-config-typescript": "^11.0.0",
59+
"@vue/test-utils": "^2.0.2",
60+
"@vue/tsconfig": "^0.1.3",
61+
"autoprefixer": "^10.4.7",
62+
"bundlewatch": "^0.3.3",
63+
"c8": "^7.11.3",
64+
"commitizen": "^4.2.5",
65+
"coveralls": "^3.1.1",
66+
"cross-env": "^7.0.3",
67+
"cz-conventional-changelog": "3.3.0",
68+
"eslint": "^8.20.0",
69+
"eslint-plugin-vue": "^9.2.0",
70+
"jsdom": "^20.0.0",
71+
"postcss-nested": "^5.0.6",
72+
"prettier": "^2.7.1",
73+
"semantic-release": "^19.0.3",
74+
"typescript": "^4.7.4",
75+
"vite": "^3.0.0",
76+
"vitest": "^0.18.1",
77+
"vue": "^3.2.37",
78+
"vue-tsc": "^0.38.8"
79+
},
80+
"config": {
81+
"commitizen": {
82+
"path": "./node_modules/cz-conventional-changelog"
83+
}
84+
},
85+
"bundlewatch": {
86+
"files": [
87+
{
88+
"path": "./dist/vue-select.es.js",
89+
"compression": "gzip",
90+
"maxSize": "8 KB"
91+
},
92+
{
93+
"path": "./dist/vue-select.umd.js",
94+
"compression": "gzip",
95+
"maxSize": "7 KB"
96+
},
97+
{
98+
"path": "./dist/vue-select.css",
99+
"compression": "gzip",
100+
"maxSize": "2 KB"
101+
}
102+
]
103+
}
104104
}

src/components/Select.vue

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
:aria-expanded="dropdownOpen.toString()"
1414
:aria-owns="`vs${uid}__listbox`"
1515
aria-label="Search for option"
16+
v-click-outside="clickOutside"
1617
@mousedown="toggleDropdown($event)"
1718
>
1819
<div ref="selectedOptions" class="vs__selected-options">
@@ -141,6 +142,7 @@ import typeAheadPointer from '@/mixins/typeAheadPointer.js'
141142
import ajax from '@/mixins/ajax.js'
142143
import childComponents from '@/components/childComponents.js'
143144
import appendToBody from '@/directives/appendToBody.js'
145+
import clickOutside from '@/directives/clickOutside.js'
144146
import sortAndStringify from '@/utility/sortAndStringify.js'
145147
import uniqueId from '@/utility/uniqueId.js'
146148
@@ -150,10 +152,10 @@ import uniqueId from '@/utility/uniqueId.js'
150152
export default {
151153
components: { ...childComponents },
152154
153-
directives: { appendToBody },
155+
directives: { appendToBody, clickOutside },
154156
155157
mixins: [pointerScroll, typeAheadPointer, ajax],
156-
158+
157159
compatConfig: {
158160
MODE: 3,
159161
},
@@ -984,6 +986,11 @@ export default {
984986
},
985987
986988
methods: {
989+
clickOutside() {
990+
if (this.open) {
991+
this.open = false
992+
}
993+
},
987994
/**
988995
* Make sure tracked value is
989996
* one option if possible.
@@ -1123,9 +1130,10 @@ export default {
11231130
if (this.open && targetIsNotSearch) {
11241131
this.searchEl.blur()
11251132
} else if (!this.disabled) {
1126-
this.open = true
11271133
this.searchEl.focus()
11281134
}
1135+
1136+
this.open = !this.open
11291137
},
11301138
11311139
/**
@@ -1188,16 +1196,6 @@ export default {
11881196
)
11891197
},
11901198
1191-
/**
1192-
* 'Private' function to close the search options
1193-
* @emits {search:blur}
1194-
* @returns {void}
1195-
*/
1196-
closeSearchOptions() {
1197-
this.open = false
1198-
this.$emit('search:blur')
1199-
},
1200-
12011199
/**
12021200
* Delete the value on Delete keypress when there is no
12031201
* text in the search input, & there's tags to delete
@@ -1280,12 +1278,12 @@ export default {
12801278
if (this.clearSearchOnBlur({ clearSearchOnSelect, multiple })) {
12811279
this.search = ''
12821280
}
1283-
this.closeSearchOptions()
1281+
this.$emit('search:blur')
12841282
return
12851283
}
12861284
// Fixed bug where no-options message could not be closed
12871285
if (this.search.length === 0 && this.options.length === 0) {
1288-
this.closeSearchOptions()
1286+
this.$emit('search:blur')
12891287
return
12901288
}
12911289
},
@@ -1296,7 +1294,6 @@ export default {
12961294
* @return {void}
12971295
*/
12981296
onSearchFocus() {
1299-
this.open = true
13001297
this.$emit('search:focus')
13011298
},
13021299

src/directives/clickOutside.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
export default {
2+
beforeMount: (el, binding) => {
3+
el.clickOutsideEvent = (event) => {
4+
event.stopPropagation()
5+
6+
if (event.target !== el && !el.contains(event.target)) {
7+
binding.value(event)
8+
}
9+
}
10+
const clickHandler =
11+
'ontouchstart' in document.documentElement ? 'touchstart' : 'click'
12+
setTimeout(() => {
13+
document.addEventListener(clickHandler, el.clickOutsideEvent)
14+
}, 0)
15+
},
16+
unmounted: (el) => {
17+
const clickOutsideEvent = el.clickOutsideEvent
18+
delete el.clickOutsideEvent
19+
const clickHandler =
20+
'ontouchstart' in document.documentElement ? 'touchstart' : 'click'
21+
document.removeEventListener(clickHandler, clickOutsideEvent)
22+
},
23+
}

0 commit comments

Comments
 (0)