@reviewpush/rp-treeselect 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +172 -0
- package/dist/rp-treeselect.cjs.js +3656 -0
- package/dist/rp-treeselect.cjs.js.map +1 -0
- package/dist/rp-treeselect.cjs.min.js +2 -0
- package/dist/rp-treeselect.cjs.min.js.map +1 -0
- package/dist/rp-treeselect.css +947 -0
- package/dist/rp-treeselect.css.map +1 -0
- package/dist/rp-treeselect.min.css +1 -0
- package/dist/rp-treeselect.umd.js +4837 -0
- package/dist/rp-treeselect.umd.js.map +1 -0
- package/dist/rp-treeselect.umd.min.js +2 -0
- package/dist/rp-treeselect.umd.min.js.map +1 -0
- package/package.json +140 -0
- package/src/assets/checkbox-checked-disabled.png +0 -0
- package/src/assets/checkbox-checked-disabled@2x.png +0 -0
- package/src/assets/checkbox-checked-disabled@3x.png +0 -0
- package/src/assets/checkbox-checked.png +0 -0
- package/src/assets/checkbox-checked@2x.png +0 -0
- package/src/assets/checkbox-checked@3x.png +0 -0
- package/src/assets/checkbox-indeterminate-disabled.png +0 -0
- package/src/assets/checkbox-indeterminate-disabled@2x.png +0 -0
- package/src/assets/checkbox-indeterminate-disabled@3x.png +0 -0
- package/src/assets/checkbox-indeterminate.png +0 -0
- package/src/assets/checkbox-indeterminate@2x.png +0 -0
- package/src/assets/checkbox-indeterminate@3x.png +0 -0
- package/src/components/Control.vue +153 -0
- package/src/components/HiddenFields.vue +37 -0
- package/src/components/Input.vue +295 -0
- package/src/components/Menu.vue +313 -0
- package/src/components/MenuPortal.vue +179 -0
- package/src/components/MultiValue.vue +56 -0
- package/src/components/MultiValueItem.vue +45 -0
- package/src/components/Option.vue +300 -0
- package/src/components/Placeholder.vue +21 -0
- package/src/components/SingleValue.vue +34 -0
- package/src/components/Tip.vue +32 -0
- package/src/components/Treeselect.vue +42 -0
- package/src/components/icons/Arrow.vue +11 -0
- package/src/components/icons/Delete.vue +11 -0
- package/src/constants.js +50 -0
- package/src/index.js +14 -0
- package/src/mixins/treeselectMixin.js +1949 -0
- package/src/style.less +1147 -0
- package/src/utils/.eslintrc.js +6 -0
- package/src/utils/constant.js +1 -0
- package/src/utils/createMap.js +1 -0
- package/src/utils/debounce.js +1 -0
- package/src/utils/deepExtend.js +25 -0
- package/src/utils/find.js +6 -0
- package/src/utils/identity.js +1 -0
- package/src/utils/includes.js +3 -0
- package/src/utils/index.js +38 -0
- package/src/utils/isNaN.js +3 -0
- package/src/utils/isPromise.js +1 -0
- package/src/utils/last.js +1 -0
- package/src/utils/noop.js +1 -0
- package/src/utils/onLeftClick.js +7 -0
- package/src/utils/once.js +1 -0
- package/src/utils/quickDiff.js +9 -0
- package/src/utils/removeFromArray.js +4 -0
- package/src/utils/scrollIntoView.js +15 -0
- package/src/utils/setupResizeAndScrollEventListeners.js +34 -0
- package/src/utils/warning.js +11 -0
- package/src/utils/watchSize.js +67 -0
package/package.json
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@reviewpush/rp-treeselect",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"repository": "reviewpush/rp-treeselect",
|
|
5
|
+
"main": "dist/rp-treeselect.cjs.js",
|
|
6
|
+
"unpkg": "dist/rp-treeselect.umd.min.js",
|
|
7
|
+
"css": "dist/rp-treeselect.min.css",
|
|
8
|
+
"files": [
|
|
9
|
+
"src",
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"dev": "node build/dev-server.js",
|
|
14
|
+
"build-library": "node build/build-library.js",
|
|
15
|
+
"build-docs": "rimraf gh-pages && mkdir gh-pages && node build/build-docs.js",
|
|
16
|
+
"preview-docs": "http-server gh-pages",
|
|
17
|
+
"gh-pages": "npm run build-docs && gh-pages --dist gh-pages --branch gh-pages --dotfiles",
|
|
18
|
+
"cleanup-cov": "rimraf test/unit/coverage",
|
|
19
|
+
"unit": "npm run cleanup-cov && karma start test/unit/karma.conf.js --watch",
|
|
20
|
+
"testonly": "npm run cleanup-cov && karma start test/unit/karma.config.js --single-run",
|
|
21
|
+
"test": "npm run testonly",
|
|
22
|
+
"pretest": "npm run lint",
|
|
23
|
+
"lint:js": "eslint --ext .js --ext .vue --cache --cache-location node_modules/.cache/eslint --rule 'no-console: 2' .",
|
|
24
|
+
"lint:css": "stylelint '**/*.less'",
|
|
25
|
+
"lint": "npm run lint:js && npm run lint:css",
|
|
26
|
+
"verify-builds": "size-limit && node build/verify-builds.js",
|
|
27
|
+
"finish": "npm test && npm run build-library && npm run verify-builds"
|
|
28
|
+
},
|
|
29
|
+
"pre-commit": "lint",
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@babel/runtime": "^7.3.1",
|
|
32
|
+
"babel-helper-vue-jsx-merge-props": "^2.0.3",
|
|
33
|
+
"easings-css": "^1.0.0",
|
|
34
|
+
"fuzzysearch": "^1.0.3",
|
|
35
|
+
"is-promise": "^2.1.0",
|
|
36
|
+
"lodash": "^4.0.0",
|
|
37
|
+
"material-colors": "^1.2.6",
|
|
38
|
+
"watch-size": "^2.0.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@babel/core": "^7.0.0",
|
|
42
|
+
"@babel/plugin-syntax-jsx": "^7.0.0",
|
|
43
|
+
"@babel/plugin-transform-runtime": "^7.0.0",
|
|
44
|
+
"@babel/preset-env": "^7.3.1",
|
|
45
|
+
"@size-limit/preset-big-lib": "^2.0.2",
|
|
46
|
+
"@vue/test-utils": "1.0.0-beta.16",
|
|
47
|
+
"autoprefixer": "^9.4.6",
|
|
48
|
+
"babel-eslint": "^10.0.1",
|
|
49
|
+
"babel-loader": "^8.0.0",
|
|
50
|
+
"babel-plugin-istanbul": "^5.0.1",
|
|
51
|
+
"babel-plugin-transform-vue-jsx": "^4.0.1",
|
|
52
|
+
"cache-loader": "^4.0.1",
|
|
53
|
+
"chalk": "^2.3.2",
|
|
54
|
+
"codecov": "^3.0.0",
|
|
55
|
+
"connect-history-api-fallback": "^1.5.0",
|
|
56
|
+
"copy-webpack-plugin": "^5.0.3",
|
|
57
|
+
"css-loader": "^3.0.0",
|
|
58
|
+
"entities": "^2.0.0",
|
|
59
|
+
"eslint": "^6.1.0",
|
|
60
|
+
"eslint-config-riophae": "^0.10.0",
|
|
61
|
+
"eslint-friendly-formatter": "^4.0.0",
|
|
62
|
+
"eslint-import-resolver-webpack": "^0.11.0",
|
|
63
|
+
"eslint-loader": "^3.0.0",
|
|
64
|
+
"eslint-plugin-import": "^2.15.0",
|
|
65
|
+
"eslint-plugin-react": "^7.10.0",
|
|
66
|
+
"eslint-plugin-unicorn": "^12.1.0",
|
|
67
|
+
"eslint-plugin-vue": "^6.0.0",
|
|
68
|
+
"eventsource-polyfill": "^0.9.6",
|
|
69
|
+
"express": "^4.16.3",
|
|
70
|
+
"friendly-errors-webpack-plugin": "^1.7.0",
|
|
71
|
+
"gh-pages": "^2.0.0",
|
|
72
|
+
"html-webpack-plugin": "^3.1.0",
|
|
73
|
+
"http-server": "^0.11.1",
|
|
74
|
+
"jasmine-core": "^3.1.0",
|
|
75
|
+
"jstransformer-markdown-it": "^2.0.0",
|
|
76
|
+
"karma": "^4.0.0",
|
|
77
|
+
"karma-chrome-launcher": "^3.0.0",
|
|
78
|
+
"karma-coverage": "^2.0.1",
|
|
79
|
+
"karma-jasmine": "^2.0.0",
|
|
80
|
+
"karma-jasmine-matchers": "^4.0.1",
|
|
81
|
+
"karma-sourcemap-loader": "^0.3.7",
|
|
82
|
+
"karma-spec-reporter": "0.0.32",
|
|
83
|
+
"karma-webpack": "^4.0.2",
|
|
84
|
+
"less": "^3.0.1",
|
|
85
|
+
"less-loader": "^5.0.0",
|
|
86
|
+
"mini-css-extract-plugin": "^0.8.0",
|
|
87
|
+
"open": "^7.0.0",
|
|
88
|
+
"optimize-css-assets-webpack-plugin": "^5.0.0",
|
|
89
|
+
"ora": "^4.0.1",
|
|
90
|
+
"postcss-loader": "^3.0.0",
|
|
91
|
+
"pre-commit": "^1.2.2",
|
|
92
|
+
"pug": "^2.0.0",
|
|
93
|
+
"pug-loader": "^2.4.0",
|
|
94
|
+
"puppeteer": "^2.0.0",
|
|
95
|
+
"raw-loader": "^3.0.0",
|
|
96
|
+
"regenerator-runtime": "^0.13.1",
|
|
97
|
+
"rimraf": "^3.0.0",
|
|
98
|
+
"run-series": "^1.1.8",
|
|
99
|
+
"script-loader": "^0.7.2",
|
|
100
|
+
"shallow-equal": "^1.0.0",
|
|
101
|
+
"shelljs": "^0.8.1",
|
|
102
|
+
"string.prototype.repeat": "^0.2.0",
|
|
103
|
+
"strip-indent": "^3.0.0",
|
|
104
|
+
"stylelint": "^11.0.0",
|
|
105
|
+
"stylelint-config-xo-space": "^0.13.0",
|
|
106
|
+
"terser-webpack-plugin": "^2.1.0",
|
|
107
|
+
"url-loader": "^2.0.1",
|
|
108
|
+
"vodal": "^2.3.3",
|
|
109
|
+
"vue": "^2.2.0",
|
|
110
|
+
"vue-loader": "^15.6.0",
|
|
111
|
+
"vue-style-loader": "^4.0.2",
|
|
112
|
+
"vue-template-compiler": "^2.4.4",
|
|
113
|
+
"vuex": "^3.0.1",
|
|
114
|
+
"webpack": "^4.6.0",
|
|
115
|
+
"webpack-bundle-analyzer": "^3.0.2",
|
|
116
|
+
"webpack-cdn-plugin": "^3.1.4",
|
|
117
|
+
"webpack-dev-middleware": "^3.1.0",
|
|
118
|
+
"webpack-hot-middleware": "^2.18.0",
|
|
119
|
+
"webpack-merge": "^4.1.0",
|
|
120
|
+
"webpack-node-externals": "^1.7.2",
|
|
121
|
+
"yaku": "^0.19.3"
|
|
122
|
+
},
|
|
123
|
+
"peerDependencies": {
|
|
124
|
+
"vue": "^2.2.0"
|
|
125
|
+
},
|
|
126
|
+
"keywords": [
|
|
127
|
+
"vue",
|
|
128
|
+
"component",
|
|
129
|
+
"tree",
|
|
130
|
+
"treeview",
|
|
131
|
+
"select",
|
|
132
|
+
"dropdown",
|
|
133
|
+
"treeselect",
|
|
134
|
+
"multiselect",
|
|
135
|
+
"form",
|
|
136
|
+
"control",
|
|
137
|
+
"input",
|
|
138
|
+
"ui"
|
|
139
|
+
]
|
|
140
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { onLeftClick, isPromise } from '../utils'
|
|
3
|
+
import SingleValue from './SingleValue'
|
|
4
|
+
import MultiValue from './MultiValue'
|
|
5
|
+
import DeleteIcon from './icons/Delete'
|
|
6
|
+
import ArrowIcon from './icons/Arrow'
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
name: 'rp-treeselect--control',
|
|
10
|
+
inject: [ 'instance' ],
|
|
11
|
+
|
|
12
|
+
computed: {
|
|
13
|
+
/* eslint-disable valid-jsdoc */
|
|
14
|
+
/**
|
|
15
|
+
* Should show the "×" button that resets value?
|
|
16
|
+
* @return {boolean}
|
|
17
|
+
*/
|
|
18
|
+
shouldShowX() {
|
|
19
|
+
const { instance } = this
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
instance.clearable &&
|
|
23
|
+
!instance.disabled &&
|
|
24
|
+
instance.hasValue &&
|
|
25
|
+
(this.hasUndisabledValue || instance.allowClearingDisabled)
|
|
26
|
+
)
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Should show the arrow button that toggles menu?
|
|
31
|
+
* @return {boolean}
|
|
32
|
+
*/
|
|
33
|
+
shouldShowArrow() {
|
|
34
|
+
const { instance } = this
|
|
35
|
+
|
|
36
|
+
if (!instance.alwaysOpen) return true
|
|
37
|
+
// Even with `alwaysOpen: true`, sometimes the menu is still closed,
|
|
38
|
+
// e.g. when the control is disabled.
|
|
39
|
+
return !instance.menu.isOpen
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Has any undisabled option been selected?
|
|
44
|
+
* @type {boolean}
|
|
45
|
+
*/
|
|
46
|
+
hasUndisabledValue() {
|
|
47
|
+
const { instance } = this
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
instance.hasValue &&
|
|
51
|
+
instance.internalValue.some(id => !instance.getNode(id).isDisabled)
|
|
52
|
+
)
|
|
53
|
+
},
|
|
54
|
+
/* eslint-enable valid-jsdoc */
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
methods: {
|
|
58
|
+
renderX() {
|
|
59
|
+
const { instance } = this
|
|
60
|
+
const title = instance.multiple ? instance.clearAllText : instance.clearValueText
|
|
61
|
+
|
|
62
|
+
if (!this.shouldShowX) return null
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<div class="rp-treeselect__x-container" title={title} onMousedown={this.handleMouseDownOnX}>
|
|
66
|
+
<DeleteIcon class="rp-treeselect__x" />
|
|
67
|
+
</div>
|
|
68
|
+
)
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
renderArrow() {
|
|
72
|
+
const { instance } = this
|
|
73
|
+
const arrowClass = {
|
|
74
|
+
'rp-treeselect__control-arrow': true,
|
|
75
|
+
'rp-treeselect__control-arrow--rotated': instance.menu.isOpen,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (!this.shouldShowArrow) return null
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<div class="rp-treeselect__control-arrow-container" onMousedown={this.handleMouseDownOnArrow}>
|
|
82
|
+
<ArrowIcon class={arrowClass} />
|
|
83
|
+
</div>
|
|
84
|
+
)
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
handleMouseDownOnX: onLeftClick(function handleMouseDownOnX(evt) {
|
|
88
|
+
/**
|
|
89
|
+
* We don't use async/await here because we don't want
|
|
90
|
+
* to rely on Babel polyfill or regenerator runtime.
|
|
91
|
+
* See: https://babeljs.io/docs/plugins/transform-regenerator/
|
|
92
|
+
* We also don't want to assume there is a global `Promise`
|
|
93
|
+
* class, since we are targeting to support IE9 without the
|
|
94
|
+
* need of any polyfill.
|
|
95
|
+
*/
|
|
96
|
+
|
|
97
|
+
evt.stopPropagation()
|
|
98
|
+
evt.preventDefault()
|
|
99
|
+
|
|
100
|
+
const { instance } = this
|
|
101
|
+
const result = instance.beforeClearAll()
|
|
102
|
+
const handler = shouldClear => {
|
|
103
|
+
if (shouldClear) instance.clear()
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (isPromise(result)) {
|
|
107
|
+
// The handler will be called async.
|
|
108
|
+
result.then(handler)
|
|
109
|
+
} else {
|
|
110
|
+
// Keep the same behavior here.
|
|
111
|
+
setTimeout(() => handler(result), 0)
|
|
112
|
+
// Also, note that IE9 requires:
|
|
113
|
+
// setTimeout(() => fn(...args), delay)
|
|
114
|
+
// Instead of:
|
|
115
|
+
// setTimeout(fn, delay, ...args)
|
|
116
|
+
}
|
|
117
|
+
}),
|
|
118
|
+
|
|
119
|
+
handleMouseDownOnArrow: onLeftClick(function handleMouseDownOnArrow(evt) {
|
|
120
|
+
evt.preventDefault()
|
|
121
|
+
evt.stopPropagation()
|
|
122
|
+
|
|
123
|
+
const { instance } = this
|
|
124
|
+
|
|
125
|
+
// Focus the input or prevent blurring.
|
|
126
|
+
instance.focusInput()
|
|
127
|
+
instance.toggleMenu()
|
|
128
|
+
}),
|
|
129
|
+
|
|
130
|
+
// This is meant to be called by child `<Value />` component.
|
|
131
|
+
renderValueContainer(children) {
|
|
132
|
+
return (
|
|
133
|
+
<div class="rp-treeselect__value-container">
|
|
134
|
+
{children}
|
|
135
|
+
</div>
|
|
136
|
+
)
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
render() {
|
|
141
|
+
const { instance } = this
|
|
142
|
+
const ValueContainer = instance.single ? SingleValue : MultiValue
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<div class="rp-treeselect__control" onMousedown={instance.handleMouseDown}>
|
|
146
|
+
<ValueContainer ref="value-container" />
|
|
147
|
+
{this.renderX()}
|
|
148
|
+
{this.renderArrow()}
|
|
149
|
+
</div>
|
|
150
|
+
)
|
|
151
|
+
},
|
|
152
|
+
}
|
|
153
|
+
</script>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { isNaN } from '../utils'
|
|
3
|
+
|
|
4
|
+
function stringifyValue(value) {
|
|
5
|
+
if (typeof value === 'string') return value
|
|
6
|
+
// istanbul ignore else
|
|
7
|
+
if (value != null && !isNaN(value)) return JSON.stringify(value)
|
|
8
|
+
// istanbul ignore next
|
|
9
|
+
return ''
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
name: 'rp-treeselect--hidden-fields',
|
|
14
|
+
inject: [ 'instance' ],
|
|
15
|
+
functional: true,
|
|
16
|
+
|
|
17
|
+
render(_, context) {
|
|
18
|
+
const { instance } = context.injections
|
|
19
|
+
|
|
20
|
+
if (!instance.name || instance.disabled || !instance.hasValue) return null
|
|
21
|
+
|
|
22
|
+
let stringifiedValues = instance.internalValue.map(stringifyValue)
|
|
23
|
+
|
|
24
|
+
if (instance.multiple && instance.joinValues) stringifiedValues = [
|
|
25
|
+
stringifiedValues.join(instance.delimiter),
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
return stringifiedValues.map((stringifiedValue, i) => (
|
|
29
|
+
<input type="hidden"
|
|
30
|
+
name={instance.name}
|
|
31
|
+
value={stringifiedValue}
|
|
32
|
+
key={'hidden-field-' + i}
|
|
33
|
+
/>
|
|
34
|
+
))
|
|
35
|
+
},
|
|
36
|
+
}
|
|
37
|
+
</script>
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { debounce, deepExtend, includes } from '../utils'
|
|
3
|
+
import { MIN_INPUT_WIDTH, KEY_CODES, INPUT_DEBOUNCE_DELAY } from '../constants'
|
|
4
|
+
|
|
5
|
+
const keysThatRequireMenuBeingOpen = [
|
|
6
|
+
KEY_CODES.ENTER,
|
|
7
|
+
KEY_CODES.END,
|
|
8
|
+
KEY_CODES.HOME,
|
|
9
|
+
KEY_CODES.ARROW_LEFT,
|
|
10
|
+
KEY_CODES.ARROW_UP,
|
|
11
|
+
KEY_CODES.ARROW_RIGHT,
|
|
12
|
+
KEY_CODES.ARROW_DOWN,
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
export default {
|
|
16
|
+
name: 'rp-treeselect--input',
|
|
17
|
+
inject: [ 'instance' ],
|
|
18
|
+
|
|
19
|
+
data: () => ({
|
|
20
|
+
inputWidth: MIN_INPUT_WIDTH,
|
|
21
|
+
value: '',
|
|
22
|
+
}),
|
|
23
|
+
|
|
24
|
+
computed: {
|
|
25
|
+
needAutoSize() {
|
|
26
|
+
const { instance } = this
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
instance.searchable &&
|
|
30
|
+
!instance.disabled &&
|
|
31
|
+
instance.multiple
|
|
32
|
+
)
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
inputStyle() {
|
|
36
|
+
return {
|
|
37
|
+
width: this.needAutoSize ? `${this.inputWidth}px` : null,
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
watch: {
|
|
43
|
+
'instance.trigger.searchQuery'(newValue) {
|
|
44
|
+
this.value = newValue
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
value() {
|
|
48
|
+
// istanbul ignore else
|
|
49
|
+
if (this.needAutoSize) this.$nextTick(this.updateInputWidth)
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
created() {
|
|
54
|
+
this.debouncedCallback = debounce(
|
|
55
|
+
this.updateSearchQuery,
|
|
56
|
+
INPUT_DEBOUNCE_DELAY,
|
|
57
|
+
{ leading: true, trailing: true },
|
|
58
|
+
)
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
methods: {
|
|
62
|
+
clear() {
|
|
63
|
+
this.onInput({
|
|
64
|
+
target: { value: '' },
|
|
65
|
+
})
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
focus() {
|
|
69
|
+
const { instance } = this
|
|
70
|
+
|
|
71
|
+
if (!instance.disabled) {
|
|
72
|
+
this.$refs.input && this.$refs.input.focus()
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
blur() {
|
|
77
|
+
this.$refs.input && this.$refs.input.blur()
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
onFocus() {
|
|
81
|
+
const { instance } = this
|
|
82
|
+
|
|
83
|
+
instance.trigger.isFocused = true
|
|
84
|
+
// istanbul ignore else
|
|
85
|
+
if (instance.openOnFocus) instance.openMenu()
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
onBlur() {
|
|
89
|
+
const { instance } = this
|
|
90
|
+
const menu = instance.getMenu()
|
|
91
|
+
|
|
92
|
+
// #15
|
|
93
|
+
// istanbul ignore next
|
|
94
|
+
if (menu && document.activeElement === menu) {
|
|
95
|
+
return this.focus()
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
instance.trigger.isFocused = false
|
|
99
|
+
instance.closeMenu()
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
onInput(evt) {
|
|
103
|
+
const { value } = evt.target
|
|
104
|
+
|
|
105
|
+
this.value = value
|
|
106
|
+
|
|
107
|
+
if (value) {
|
|
108
|
+
this.debouncedCallback()
|
|
109
|
+
} else {
|
|
110
|
+
this.debouncedCallback.cancel()
|
|
111
|
+
this.updateSearchQuery()
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
// 用 keyUp 事件存在一个问题,删除输入框最后一个字符也会导致取消选中最后一项
|
|
116
|
+
onKeyDown(evt) {
|
|
117
|
+
const { instance } = this
|
|
118
|
+
// https://css-tricks.com/snippets/javascript/javascript-keycodes/
|
|
119
|
+
// https://stackoverflow.com/questions/4471582/javascript-keycode-vs-which
|
|
120
|
+
const key = 'which' in evt ? evt.which : /* istanbul ignore next */ evt.keyCode
|
|
121
|
+
|
|
122
|
+
if (evt.ctrlKey || evt.shiftKey || evt.altKey || evt.metaKey)
|
|
123
|
+
return
|
|
124
|
+
|
|
125
|
+
if (!instance.menu.isOpen && includes(keysThatRequireMenuBeingOpen, key)) {
|
|
126
|
+
evt.preventDefault()
|
|
127
|
+
return instance.openMenu()
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
switch (key) {
|
|
131
|
+
case KEY_CODES.BACKSPACE: {
|
|
132
|
+
if (instance.backspaceRemoves && !this.value.length) {
|
|
133
|
+
instance.removeLastValue()
|
|
134
|
+
}
|
|
135
|
+
break
|
|
136
|
+
}
|
|
137
|
+
case KEY_CODES.ENTER: {
|
|
138
|
+
evt.preventDefault()
|
|
139
|
+
if (instance.menu.current === null) return
|
|
140
|
+
const current = instance.getNode(instance.menu.current)
|
|
141
|
+
if (current.isBranch && instance.disableBranchNodes) return
|
|
142
|
+
instance.select(current)
|
|
143
|
+
break
|
|
144
|
+
}
|
|
145
|
+
case KEY_CODES.ESCAPE: {
|
|
146
|
+
if (this.value.length) {
|
|
147
|
+
this.clear()
|
|
148
|
+
} else if (instance.menu.isOpen) {
|
|
149
|
+
instance.closeMenu()
|
|
150
|
+
}
|
|
151
|
+
break
|
|
152
|
+
}
|
|
153
|
+
case KEY_CODES.END: {
|
|
154
|
+
evt.preventDefault()
|
|
155
|
+
instance.highlightLastOption()
|
|
156
|
+
break
|
|
157
|
+
}
|
|
158
|
+
case KEY_CODES.HOME: {
|
|
159
|
+
evt.preventDefault()
|
|
160
|
+
instance.highlightFirstOption()
|
|
161
|
+
break
|
|
162
|
+
}
|
|
163
|
+
case KEY_CODES.ARROW_LEFT: {
|
|
164
|
+
const current = instance.getNode(instance.menu.current)
|
|
165
|
+
if (current.isBranch && instance.shouldExpand(current)) {
|
|
166
|
+
evt.preventDefault()
|
|
167
|
+
instance.toggleExpanded(current)
|
|
168
|
+
} else if (!current.isRootNode && (current.isLeaf || (current.isBranch && !(instance.shouldExpand(current))))) {
|
|
169
|
+
evt.preventDefault()
|
|
170
|
+
instance.setCurrentHighlightedOption(current.parentNode)
|
|
171
|
+
}
|
|
172
|
+
break
|
|
173
|
+
}
|
|
174
|
+
case KEY_CODES.ARROW_UP: {
|
|
175
|
+
evt.preventDefault()
|
|
176
|
+
instance.highlightPrevOption()
|
|
177
|
+
break
|
|
178
|
+
}
|
|
179
|
+
case KEY_CODES.ARROW_RIGHT: {
|
|
180
|
+
const current = instance.getNode(instance.menu.current)
|
|
181
|
+
if (current.isBranch && !instance.shouldExpand(current)) {
|
|
182
|
+
evt.preventDefault()
|
|
183
|
+
instance.toggleExpanded(current)
|
|
184
|
+
}
|
|
185
|
+
break
|
|
186
|
+
}
|
|
187
|
+
case KEY_CODES.ARROW_DOWN: {
|
|
188
|
+
evt.preventDefault()
|
|
189
|
+
instance.highlightNextOption()
|
|
190
|
+
break
|
|
191
|
+
}
|
|
192
|
+
case KEY_CODES.DELETE: {
|
|
193
|
+
if (instance.deleteRemoves && !this.value.length) {
|
|
194
|
+
instance.removeLastValue()
|
|
195
|
+
}
|
|
196
|
+
break
|
|
197
|
+
}
|
|
198
|
+
default: {
|
|
199
|
+
// istanbul ignore else
|
|
200
|
+
instance.openMenu()
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
|
|
205
|
+
onMouseDown(evt) {
|
|
206
|
+
// istanbul ignore next
|
|
207
|
+
if (this.value.length) {
|
|
208
|
+
// Prevent it from bubbling to the top level and triggering `preventDefault()`
|
|
209
|
+
// to make the textbox unselectable
|
|
210
|
+
evt.stopPropagation()
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
renderInputContainer() {
|
|
215
|
+
const { instance } = this
|
|
216
|
+
const props = {}
|
|
217
|
+
const children = []
|
|
218
|
+
|
|
219
|
+
if (instance.searchable && !instance.disabled) {
|
|
220
|
+
children.push(this.renderInput())
|
|
221
|
+
if (this.needAutoSize) children.push(this.renderSizer())
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (!instance.searchable) {
|
|
225
|
+
deepExtend(props, {
|
|
226
|
+
on: {
|
|
227
|
+
focus: this.onFocus,
|
|
228
|
+
blur: this.onBlur,
|
|
229
|
+
keydown: this.onKeyDown,
|
|
230
|
+
},
|
|
231
|
+
ref: 'input',
|
|
232
|
+
})
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (!instance.searchable && !instance.disabled) {
|
|
236
|
+
deepExtend(props, {
|
|
237
|
+
attrs: {
|
|
238
|
+
tabIndex: instance.tabIndex,
|
|
239
|
+
},
|
|
240
|
+
})
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return (
|
|
244
|
+
<div class="rp-treeselect__input-container" {...props}>
|
|
245
|
+
{children}
|
|
246
|
+
</div>
|
|
247
|
+
)
|
|
248
|
+
},
|
|
249
|
+
|
|
250
|
+
renderInput() {
|
|
251
|
+
const { instance } = this
|
|
252
|
+
|
|
253
|
+
return (
|
|
254
|
+
<input ref="input"
|
|
255
|
+
class="rp-treeselect__input"
|
|
256
|
+
type="text"
|
|
257
|
+
autocomplete="off"
|
|
258
|
+
tabIndex={instance.tabIndex}
|
|
259
|
+
required={instance.required && !instance.hasValue}
|
|
260
|
+
value={this.value}
|
|
261
|
+
style={this.inputStyle}
|
|
262
|
+
onFocus={this.onFocus}
|
|
263
|
+
onInput={this.onInput}
|
|
264
|
+
onBlur={this.onBlur}
|
|
265
|
+
onKeydown={this.onKeyDown}
|
|
266
|
+
onMousedown={this.onMouseDown}
|
|
267
|
+
/>
|
|
268
|
+
)
|
|
269
|
+
},
|
|
270
|
+
|
|
271
|
+
renderSizer() {
|
|
272
|
+
return (
|
|
273
|
+
<div ref="sizer" class="rp-treeselect__sizer">{this.value}</div>
|
|
274
|
+
)
|
|
275
|
+
},
|
|
276
|
+
|
|
277
|
+
updateInputWidth() {
|
|
278
|
+
this.inputWidth = Math.max(
|
|
279
|
+
MIN_INPUT_WIDTH,
|
|
280
|
+
this.$refs.sizer.scrollWidth + 15,
|
|
281
|
+
)
|
|
282
|
+
},
|
|
283
|
+
|
|
284
|
+
updateSearchQuery() {
|
|
285
|
+
const { instance } = this
|
|
286
|
+
|
|
287
|
+
instance.trigger.searchQuery = this.value
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
|
|
291
|
+
render() {
|
|
292
|
+
return this.renderInputContainer()
|
|
293
|
+
},
|
|
294
|
+
}
|
|
295
|
+
</script>
|