@signal24/vue-foundation 3.3.3 → 3.7.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/.eslintrc.js +16 -0
- package/.prettierrc.json +7 -0
- package/CHANGES.md +6 -3
- package/package.json +26 -13
- package/postcss.config.js +1 -1
- package/src/app.js +2 -2
- package/src/components/ajax-select.vue +10 -6
- package/src/components/alert.vue +25 -27
- package/src/components/index.js +6 -11
- package/src/components/modal.vue +18 -10
- package/src/components/smart-select.vue +104 -59
- package/src/config.js +1 -1
- package/src/directives/autofocus.js +4 -3
- package/src/directives/confirm-button.js +2 -2
- package/src/directives/date-input.js +7 -9
- package/src/directives/datetime.js +11 -9
- package/src/directives/disabled.js +4 -5
- package/src/directives/duration.js +7 -8
- package/src/directives/index.js +1 -1
- package/src/directives/infinite-scroll.js +1 -1
- package/src/directives/readonly.js +5 -6
- package/src/directives/tooltip.js +58 -61
- package/src/directives/user-text.js +1 -1
- package/src/filters/index.js +9 -6
- package/src/helpers/array.js +13 -14
- package/src/helpers/context-menu.js +16 -18
- package/src/helpers/delay.js +1 -1
- package/src/helpers/error.js +6 -9
- package/src/helpers/http.js +27 -25
- package/src/helpers/index.js +1 -1
- package/src/helpers/mask.js +27 -20
- package/src/helpers/number.js +2 -2
- package/src/helpers/string.js +17 -17
- package/src/helpers/vue.js +2 -2
- package/src/index.js +5 -5
- package/src/plugins/index.js +6 -6
- package/src/plugins/infinite-scroll/hook.js +2 -2
- package/src/plugins/infinite-scroll.js +20 -10
- package/src/plugins/resize-watcher.js +2 -4
package/.eslintrc.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
root: true,
|
|
3
|
+
env: {
|
|
4
|
+
node: true
|
|
5
|
+
},
|
|
6
|
+
extends: ['plugin:vue/vue3-essential', 'eslint:recommended', '@vue/prettier'],
|
|
7
|
+
rules: {
|
|
8
|
+
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
|
9
|
+
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
|
10
|
+
'simple-import-sort/imports': 'error',
|
|
11
|
+
'simple-import-sort/exports': 'error',
|
|
12
|
+
'prettier/prettier': 'warn',
|
|
13
|
+
'no-unused-vars': 'warn'
|
|
14
|
+
},
|
|
15
|
+
plugins: ['simple-import-sort']
|
|
16
|
+
};
|
package/.prettierrc.json
ADDED
package/CHANGES.md
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
# Release notes
|
|
2
|
+
|
|
2
3
|
All notable changes to this project will be documented in this file.
|
|
3
4
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
|
4
5
|
|
|
5
6
|
## 1.0.1
|
|
6
|
-
|
|
7
|
-
-
|
|
7
|
+
|
|
8
|
+
- Update dependencies
|
|
9
|
+
- Fix $confirmDestroy
|
|
8
10
|
|
|
9
11
|
## 1.0.0
|
|
10
|
-
|
|
12
|
+
|
|
13
|
+
- Initial public release
|
package/package.json
CHANGED
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
2
|
+
"name": "@signal24/vue-foundation",
|
|
3
|
+
"version": "3.7.0",
|
|
4
|
+
"description": "Common components, directives, and helpers for Vue 3 apps",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"format": "prettier --write . && eslint src --fix"
|
|
8
|
+
},
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"axios": "^0.26.0",
|
|
12
|
+
"jquery": "^3.6.0",
|
|
13
|
+
"lodash": "^4.17.21",
|
|
14
|
+
"moment": "^2.29.1",
|
|
15
|
+
"vue-stash-nested": "fergusean/vue-stash-nested#vue-foundation-3"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@vue/eslint-config-prettier": "^7.0.0",
|
|
19
|
+
"eslint": "^8.10.0",
|
|
20
|
+
"eslint-plugin-prettier": "^4.0.0",
|
|
21
|
+
"eslint-plugin-simple-import-sort": "^7.0.0",
|
|
22
|
+
"eslint-plugin-vue": "^8.5.0",
|
|
23
|
+
"prettier": "^2.5.1",
|
|
24
|
+
"sass": "^1.49.9",
|
|
25
|
+
"sass-loader": "^12",
|
|
26
|
+
"vue": "^3.2.31"
|
|
27
|
+
}
|
|
15
28
|
}
|
package/postcss.config.js
CHANGED
package/src/app.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createApp, h } from 'vue'
|
|
1
|
+
import { createApp, h } from 'vue';
|
|
2
2
|
|
|
3
3
|
let rootComponent;
|
|
4
4
|
function setRootComponent(inComponent) {
|
|
@@ -17,7 +17,7 @@ const app = createApp({
|
|
|
17
17
|
},
|
|
18
18
|
|
|
19
19
|
render() {
|
|
20
|
-
return h(rootComponent)
|
|
20
|
+
return h(rootComponent);
|
|
21
21
|
}
|
|
22
22
|
});
|
|
23
23
|
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<select v-if="!options" disabled
|
|
2
|
+
<select v-if="!options" disabled>
|
|
3
|
+
<option>{{ loadingText || 'Loading...' }}</option>
|
|
4
|
+
</select>
|
|
3
5
|
<select v-else v-model="selectedItem">
|
|
4
6
|
<option v-if="nullText" :value="null">{{ nullText }}</option>
|
|
5
|
-
<option v-for="option in options" :key="option.id" :value="option">
|
|
7
|
+
<option v-for="option in options" :key="option.id" :value="option">
|
|
8
|
+
{{ textKey ? option[textKey] : option }}
|
|
9
|
+
</option>
|
|
6
10
|
</select>
|
|
7
11
|
</template>
|
|
8
12
|
|
|
@@ -14,7 +18,7 @@ export default {
|
|
|
14
18
|
return {
|
|
15
19
|
options: null,
|
|
16
20
|
selectedItem: null
|
|
17
|
-
}
|
|
21
|
+
};
|
|
18
22
|
},
|
|
19
23
|
|
|
20
24
|
watch: {
|
|
@@ -38,7 +42,7 @@ export default {
|
|
|
38
42
|
methods: {
|
|
39
43
|
async load() {
|
|
40
44
|
this.options = null;
|
|
41
|
-
let params = this.params ? { params: this.params } : undefined
|
|
45
|
+
let params = this.params ? { params: this.params } : undefined;
|
|
42
46
|
let result = await this.$http.get(this.url, params);
|
|
43
47
|
let options = this.itemsKey ? result.data[this.itemsKey] : result.data;
|
|
44
48
|
this.preprocessor && this.preprocessor(options);
|
|
@@ -46,5 +50,5 @@ export default {
|
|
|
46
50
|
this.selectedItem = this.modelValue;
|
|
47
51
|
}
|
|
48
52
|
}
|
|
49
|
-
}
|
|
50
|
-
</script>
|
|
53
|
+
};
|
|
54
|
+
</script>
|
package/src/components/alert.vue
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<Modal class="vf-alert" :class="classes">
|
|
3
3
|
<template v-if="!this.isBare" v-slot:header>
|
|
4
4
|
<h1>{{ title }}</h1>
|
|
5
5
|
</template>
|
|
@@ -14,13 +14,18 @@
|
|
|
14
14
|
</template>
|
|
15
15
|
<button v-else class="default" @click="ok" v-autofocus>OK</button>
|
|
16
16
|
</template>
|
|
17
|
-
</
|
|
17
|
+
</Modal>
|
|
18
18
|
</template>
|
|
19
19
|
|
|
20
20
|
<script>
|
|
21
21
|
import app from '../app';
|
|
22
|
+
import Modal from './modal';
|
|
22
23
|
|
|
23
24
|
const classDef = {
|
|
25
|
+
components: {
|
|
26
|
+
Modal
|
|
27
|
+
},
|
|
28
|
+
|
|
24
29
|
data() {
|
|
25
30
|
return {
|
|
26
31
|
isBare: false,
|
|
@@ -29,17 +34,15 @@ const classDef = {
|
|
|
29
34
|
title: null,
|
|
30
35
|
message: null,
|
|
31
36
|
shouldConfirm: false
|
|
32
|
-
}
|
|
37
|
+
};
|
|
33
38
|
},
|
|
34
39
|
|
|
35
40
|
created() {
|
|
36
|
-
if (typeof
|
|
41
|
+
if (typeof this.message == 'object') {
|
|
37
42
|
if (this.message.html) {
|
|
38
43
|
this.isHtml = true;
|
|
39
44
|
this.message = this.message.html;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
else if (this.message instanceof Error) {
|
|
45
|
+
} else if (this.message instanceof Error) {
|
|
43
46
|
let err = this.message;
|
|
44
47
|
err.handle();
|
|
45
48
|
|
|
@@ -60,51 +63,46 @@ export default classDef;
|
|
|
60
63
|
async function launchModal(context, classDef, ...args) {
|
|
61
64
|
classDef = { ...classDef, __modalId: Math.random().toString(36).substring(2, 10) };
|
|
62
65
|
return await context.$modal.apply(context, [classDef, ...args]);
|
|
63
|
-
}
|
|
66
|
+
}
|
|
64
67
|
|
|
65
|
-
app.config.globalProperties.$alert = async function(title, message) {
|
|
68
|
+
app.config.globalProperties.$alert = async function (title, message) {
|
|
66
69
|
return await launchModal(this, classDef, { title, message });
|
|
67
|
-
}
|
|
70
|
+
};
|
|
68
71
|
|
|
69
|
-
app.config.globalProperties.$confirm = async function(title, message, options) {
|
|
72
|
+
app.config.globalProperties.$confirm = async function (title, message, options) {
|
|
70
73
|
options = options || {};
|
|
71
74
|
const result = await launchModal(this, classDef, { title, message, shouldConfirm: true, ...options });
|
|
72
75
|
return !!result;
|
|
73
|
-
}
|
|
76
|
+
};
|
|
74
77
|
|
|
75
|
-
app.config.globalProperties.$confirmDestroy = function(title, message, options) {
|
|
78
|
+
app.config.globalProperties.$confirmDestroy = function (title, message, options) {
|
|
76
79
|
options = options || {};
|
|
77
80
|
options.classes = options.classes || [];
|
|
78
81
|
options.classes.push('destructive');
|
|
79
82
|
return this.$confirm(title, message, options);
|
|
80
|
-
}
|
|
83
|
+
};
|
|
81
84
|
|
|
82
|
-
app.config.globalProperties.$wait = function(title, message) {
|
|
85
|
+
app.config.globalProperties.$wait = function (title, message) {
|
|
83
86
|
if (title && !message) {
|
|
84
87
|
message = title;
|
|
85
88
|
title = undefined;
|
|
86
|
-
}
|
|
87
|
-
else if (!title && !message) {
|
|
89
|
+
} else if (!title && !message) {
|
|
88
90
|
message = 'Please wait...';
|
|
89
91
|
}
|
|
90
92
|
|
|
91
93
|
let resolved = null;
|
|
92
94
|
let promise = new Promise((resolve, reject) => {
|
|
93
95
|
launchModal(this, classDef, { title, message, isBare: true, classes: ['wait'] }, resolve);
|
|
94
|
-
})
|
|
95
|
-
.then(inResolved => resolved = inResolved);
|
|
96
|
+
}).then(inResolved => (resolved = inResolved));
|
|
96
97
|
|
|
97
98
|
this.$endWait = async () => {
|
|
98
99
|
delete this.$endWait;
|
|
99
|
-
if (resolved)
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
await promise.then(() => resolved.$dismiss());
|
|
103
|
-
}
|
|
100
|
+
if (resolved) await resolved.$dismiss();
|
|
101
|
+
else await promise.then(() => resolved.$dismiss());
|
|
102
|
+
};
|
|
104
103
|
|
|
105
104
|
return this.$endWait;
|
|
106
|
-
}
|
|
107
|
-
|
|
105
|
+
};
|
|
108
106
|
</script>
|
|
109
107
|
|
|
110
108
|
<style lang="scss">
|
|
@@ -129,4 +127,4 @@ app.config.globalProperties.$wait = function(title, message) {
|
|
|
129
127
|
}
|
|
130
128
|
}
|
|
131
129
|
}
|
|
132
|
-
</style>
|
|
130
|
+
</style>
|
package/src/components/index.js
CHANGED
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
import app from '../app';
|
|
2
|
-
|
|
3
|
-
import AjaxSelect from './ajax-select'
|
|
2
|
+
import AjaxSelect from './ajax-select';
|
|
4
3
|
app.component('AjaxSelect', AjaxSelect);
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
// the already-cached 'Alert' component instead of recompiling
|
|
9
|
-
// TODO: ^ is this for mutating Alert during dev, or...?
|
|
10
|
-
import Alert from './alert'
|
|
11
|
-
Alert;
|
|
5
|
+
import Alert from './alert';
|
|
6
|
+
app.component('Alert', Alert);
|
|
12
7
|
|
|
13
|
-
import Modal from './modal'
|
|
8
|
+
import Modal from './modal';
|
|
14
9
|
app.component('Modal', Modal);
|
|
15
10
|
|
|
16
|
-
import SmartSelect from './smart-select'
|
|
17
|
-
app.component('SmartSelect', SmartSelect);
|
|
11
|
+
import SmartSelect from './smart-select';
|
|
12
|
+
app.component('SmartSelect', SmartSelect);
|
package/src/components/modal.vue
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="vf-overlay vf-modal-wrap">
|
|
3
|
-
<form
|
|
3
|
+
<form
|
|
4
|
+
action="."
|
|
5
|
+
class="vf-modal"
|
|
6
|
+
:class="{ scrolls: $isPropTruthy(this.scrolls) }"
|
|
7
|
+
@submit.prevent="$emit('formSubmit')"
|
|
8
|
+
>
|
|
4
9
|
<div v-if="$slots.header" class="vf-modal-header">
|
|
5
10
|
<slot name="header" />
|
|
6
11
|
<i v-if="$isPropTruthy(this.closeX)" class="close" @click="$parent.$dismiss()"></i>
|
|
@@ -26,8 +31,7 @@ export default {
|
|
|
26
31
|
|
|
27
32
|
if (this.$isPropTruthy(this.closeOnMaskClick)) {
|
|
28
33
|
this.$el.addEventListener('click', e => {
|
|
29
|
-
if (e.target == this.$el)
|
|
30
|
-
this.$parent.$dismiss();
|
|
34
|
+
if (e.target == this.$el) this.$parent.$dismiss();
|
|
31
35
|
});
|
|
32
36
|
}
|
|
33
37
|
},
|
|
@@ -49,7 +53,7 @@ export default {
|
|
|
49
53
|
}
|
|
50
54
|
}
|
|
51
55
|
}
|
|
52
|
-
}
|
|
56
|
+
};
|
|
53
57
|
|
|
54
58
|
import app from '../app';
|
|
55
59
|
import { markRaw } from 'vue';
|
|
@@ -66,14 +70,18 @@ function bootModal(modalId) {
|
|
|
66
70
|
this.$options.storeParent = this.$modalOpener;
|
|
67
71
|
|
|
68
72
|
let originalDataFn = this.$options.data;
|
|
69
|
-
this.$options.data = function() {
|
|
73
|
+
this.$options.data = function () {
|
|
70
74
|
const injectedData = config.injectedData || {};
|
|
71
75
|
const keepOriginalKeys = this.$options.keepOriginal || [];
|
|
72
76
|
let data = originalDataFn ? originalDataFn.apply(this) : {};
|
|
73
77
|
|
|
74
78
|
for (let key in injectedData) {
|
|
75
79
|
if (!keepOriginalKeys.includes(key)) {
|
|
76
|
-
if (
|
|
80
|
+
if (
|
|
81
|
+
injectedData[key] !== null &&
|
|
82
|
+
typeof injectedData[key] == 'object' &&
|
|
83
|
+
injectedData[key].constructor === Object
|
|
84
|
+
) {
|
|
77
85
|
data[key] = cloneDeep(injectedData[key]);
|
|
78
86
|
} else {
|
|
79
87
|
data[key] = injectedData[key];
|
|
@@ -116,7 +124,7 @@ app.mixin({
|
|
|
116
124
|
}
|
|
117
125
|
});
|
|
118
126
|
|
|
119
|
-
app.config.globalProperties.$modal = function(classDef, injectedData, instanceCreationCallback) {
|
|
127
|
+
app.config.globalProperties.$modal = function (classDef, injectedData, instanceCreationCallback) {
|
|
120
128
|
return new Promise((resolve, reject) => {
|
|
121
129
|
const modalId = classDef.__modalId || classDef.__file || Math.random().toString(36).substring(2, 10);
|
|
122
130
|
classDef.__modalId = modalId;
|
|
@@ -132,7 +140,7 @@ app.config.globalProperties.$modal = function(classDef, injectedData, instanceCr
|
|
|
132
140
|
app.vm.store.rootInjections.push(markRaw(classDef));
|
|
133
141
|
});
|
|
134
142
|
});
|
|
135
|
-
}
|
|
143
|
+
};
|
|
136
144
|
|
|
137
145
|
// TODO: see about a injecting a root modal container & HMR inside it
|
|
138
146
|
// modals, on render, can hot move themselves to body end
|
|
@@ -163,7 +171,7 @@ app.config.globalProperties.$modal = function(classDef, injectedData, instanceCr
|
|
|
163
171
|
|
|
164
172
|
.vf-modal {
|
|
165
173
|
background: white;
|
|
166
|
-
box-shadow: 0px 3px 6px 0px rgba(0,0,0
|
|
174
|
+
box-shadow: 0px 3px 6px 0px rgba(0, 0, 0, 0.15);
|
|
167
175
|
min-width: 200px;
|
|
168
176
|
max-width: 95%;
|
|
169
177
|
max-height: 95%;
|
|
@@ -187,4 +195,4 @@ app.config.globalProperties.$modal = function(classDef, injectedData, instanceCr
|
|
|
187
195
|
flex-shrink: 1;
|
|
188
196
|
flex-basis: 0%;
|
|
189
197
|
}
|
|
190
|
-
</style>
|
|
198
|
+
</style>
|
|
@@ -41,14 +41,16 @@ import debounce from 'lodash/debounce';
|
|
|
41
41
|
const nullSymbol = Symbol(null);
|
|
42
42
|
const createSymbol = Symbol('create');
|
|
43
43
|
|
|
44
|
-
const VALID_KEYS = `\`1234567890-=[]
|
|
44
|
+
const VALID_KEYS = `\`1234567890-=[]\\;',./~!@#$%^&*()_+{}|:"<>?qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM`;
|
|
45
45
|
|
|
46
46
|
export default {
|
|
47
|
-
emits: ['optionsLoaded', 'createItem'],
|
|
47
|
+
emits: ['optionsLoaded', 'createItem', 'update:modelValue'],
|
|
48
48
|
|
|
49
49
|
props: [
|
|
50
50
|
'modelValue',
|
|
51
51
|
'options',
|
|
52
|
+
'prependOptions',
|
|
53
|
+
'appendOptions',
|
|
52
54
|
'preload',
|
|
53
55
|
'url',
|
|
54
56
|
'urlParams',
|
|
@@ -71,8 +73,7 @@ export default {
|
|
|
71
73
|
data() {
|
|
72
74
|
return {
|
|
73
75
|
isLoaded: false,
|
|
74
|
-
|
|
75
|
-
decoratedOptions: [],
|
|
76
|
+
loadedOptions: [],
|
|
76
77
|
isSearching: false,
|
|
77
78
|
searchText: '',
|
|
78
79
|
selectedOption: null,
|
|
@@ -84,8 +85,11 @@ export default {
|
|
|
84
85
|
},
|
|
85
86
|
|
|
86
87
|
computed: {
|
|
88
|
+
/**
|
|
89
|
+
* EFFECTIVE PROPS
|
|
90
|
+
*/
|
|
87
91
|
effectiveDisabled() {
|
|
88
|
-
return this.disabled || !this.
|
|
92
|
+
return this.disabled || !this.loadedOptions;
|
|
89
93
|
},
|
|
90
94
|
|
|
91
95
|
effectivePlaceholder() {
|
|
@@ -94,8 +98,73 @@ export default {
|
|
|
94
98
|
return this.placeholder || '';
|
|
95
99
|
},
|
|
96
100
|
|
|
101
|
+
effectiveIdKey() {
|
|
102
|
+
return this.idKey || 'id';
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
effectiveTitleKey() {
|
|
106
|
+
return this.titleKey || 'name';
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
effectiveValueKey() {
|
|
110
|
+
if (this.valueKey) return this.valueKey;
|
|
111
|
+
if (this.options && !Array.isArray(this.options)) return this.effectiveIdKey;
|
|
112
|
+
return undefined;
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
effectiveNoResultsText() {
|
|
116
|
+
return this.noResultsText || 'No options match your search.';
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* OPTIONS GENERATION
|
|
121
|
+
*/
|
|
122
|
+
|
|
123
|
+
loadedOptionsArray() {
|
|
124
|
+
return this.arrayifyOptions(this.loadedOptions);
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
prependOptionsArray() {
|
|
128
|
+
return this.prependOptions ? this.arrayifyOptions(this.prependOptions) : [];
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
appendOptionsArray() {
|
|
132
|
+
return this.appendOptions ? this.arrayifyOptions(this.appendOptions) : [];
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
fullOptionsArray() {
|
|
136
|
+
return [...this.prependOptionsArray, ...this.loadedOptionsArray, ...this.appendOptionsArray];
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
optionsDescriptors() {
|
|
140
|
+
return this.fullOptionsArray.map((option, index) => {
|
|
141
|
+
const title = this.getOptionTitle(option);
|
|
142
|
+
const subtitle = this.getOptionSubtitle(option);
|
|
143
|
+
const strippedTitle = title ? title.text.trim().toLowerCase() : '';
|
|
144
|
+
const strippedSubtitle = subtitle ? subtitle.text.trim().toLowerCase() : '';
|
|
145
|
+
|
|
146
|
+
let searchContent = [];
|
|
147
|
+
if (this.searchFields) {
|
|
148
|
+
this.searchFields.forEach(field => {
|
|
149
|
+
option[field] && searchContent.push(String(option[field]).toLowerCase());
|
|
150
|
+
});
|
|
151
|
+
} else {
|
|
152
|
+
searchContent.push(strippedTitle);
|
|
153
|
+
strippedSubtitle && searchContent.push(strippedSubtitle);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
key: typeof option == 'object' ? option[this.effectiveIdKey] || index : option,
|
|
158
|
+
titleHtml: title.html,
|
|
159
|
+
subtitleHtml: subtitle?.html,
|
|
160
|
+
searchContent: searchContent.join(''),
|
|
161
|
+
ref: option
|
|
162
|
+
};
|
|
163
|
+
});
|
|
164
|
+
},
|
|
165
|
+
|
|
97
166
|
effectiveOptions() {
|
|
98
|
-
let options = [...this.
|
|
167
|
+
let options = [...this.optionsDescriptors];
|
|
99
168
|
|
|
100
169
|
if (this.isSearching) {
|
|
101
170
|
const strippedSearchText = this.searchText.trim().toLowerCase();
|
|
@@ -115,7 +184,8 @@ export default {
|
|
|
115
184
|
});
|
|
116
185
|
|
|
117
186
|
if (this.shouldShowCreateOption) {
|
|
118
|
-
const hasExactMatch =
|
|
187
|
+
const hasExactMatch =
|
|
188
|
+
options.find(option => option.searchContent === strippedSearchText) !== undefined;
|
|
119
189
|
if (!hasExactMatch) {
|
|
120
190
|
options.push({
|
|
121
191
|
key: createSymbol,
|
|
@@ -124,9 +194,7 @@ export default {
|
|
|
124
194
|
}
|
|
125
195
|
}
|
|
126
196
|
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
else if (this.nullTitle) {
|
|
197
|
+
} else if (this.nullTitle) {
|
|
130
198
|
options.unshift({
|
|
131
199
|
key: nullSymbol,
|
|
132
200
|
titleHtml: this.nullTitle
|
|
@@ -134,18 +202,6 @@ export default {
|
|
|
134
202
|
}
|
|
135
203
|
|
|
136
204
|
return options;
|
|
137
|
-
},
|
|
138
|
-
|
|
139
|
-
effectiveIdKey() {
|
|
140
|
-
return this.idKey || 'id';
|
|
141
|
-
},
|
|
142
|
-
|
|
143
|
-
effectiveTitleKey() {
|
|
144
|
-
return this.titleKey || 'name';
|
|
145
|
-
},
|
|
146
|
-
|
|
147
|
-
effectiveNoResultsText() {
|
|
148
|
-
return this.noResultsText || 'No options match your search.';
|
|
149
205
|
}
|
|
150
206
|
},
|
|
151
207
|
|
|
@@ -157,7 +213,7 @@ export default {
|
|
|
157
213
|
},
|
|
158
214
|
|
|
159
215
|
options() {
|
|
160
|
-
this.
|
|
216
|
+
this.loadedOptions = this.options;
|
|
161
217
|
},
|
|
162
218
|
|
|
163
219
|
url() {
|
|
@@ -170,32 +226,7 @@ export default {
|
|
|
170
226
|
|
|
171
227
|
// data
|
|
172
228
|
|
|
173
|
-
|
|
174
|
-
this.decoratedOptions = this.resolvedOptions.map((option, index) => {
|
|
175
|
-
const title = this.getOptionTitle(option);
|
|
176
|
-
const subtitle = this.getOptionSubtitle(option);
|
|
177
|
-
const strippedTitle = title ? title.text.trim().toLowerCase() : '';
|
|
178
|
-
const strippedSubtitle = subtitle ? subtitle.text.trim().toLowerCase() : '';
|
|
179
|
-
|
|
180
|
-
let searchContent = [];
|
|
181
|
-
if (this.searchFields) {
|
|
182
|
-
this.searchFields.forEach(field => {
|
|
183
|
-
option[field] && searchContent.push(String(option[field]).toLowerCase());
|
|
184
|
-
});
|
|
185
|
-
} else {
|
|
186
|
-
searchContent.push(strippedTitle);
|
|
187
|
-
strippedSubtitle && searchContent.push(strippedSubtitle);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
return {
|
|
191
|
-
key: typeof option == 'object' ? option[this.effectiveIdKey] || index : option,
|
|
192
|
-
titleHtml: title.html,
|
|
193
|
-
subtitleHtml: subtitle?.html,
|
|
194
|
-
searchContent: searchContent.join(''),
|
|
195
|
-
ref: option
|
|
196
|
-
};
|
|
197
|
-
});
|
|
198
|
-
|
|
229
|
+
optionsDescriptors() {
|
|
199
230
|
this.shouldDisplayOptions && setTimeout(this.highlightInitialOption, 0);
|
|
200
231
|
},
|
|
201
232
|
|
|
@@ -226,7 +257,7 @@ export default {
|
|
|
226
257
|
this.shouldShowCreateOption = this.$attrs['onCreateItem'] !== undefined;
|
|
227
258
|
|
|
228
259
|
if (this.options) {
|
|
229
|
-
this.
|
|
260
|
+
this.loadedOptions = this.options;
|
|
230
261
|
this.isLoaded = true;
|
|
231
262
|
} else if (this.$isPropTruthy(this.preload)) {
|
|
232
263
|
this.performInitialLoad();
|
|
@@ -235,7 +266,10 @@ export default {
|
|
|
235
266
|
this.handleValueChanged();
|
|
236
267
|
|
|
237
268
|
this.$watch('selectedOption', () => {
|
|
238
|
-
const newValue =
|
|
269
|
+
const newValue =
|
|
270
|
+
this.selectedOption && this.effectiveValueKey
|
|
271
|
+
? this.selectedOption[this.effectiveValueKey]
|
|
272
|
+
: this.selectedOption;
|
|
239
273
|
newValue === this.modelValue || this.$emit('update:modelValue', newValue);
|
|
240
274
|
});
|
|
241
275
|
},
|
|
@@ -243,7 +277,7 @@ export default {
|
|
|
243
277
|
methods: {
|
|
244
278
|
async performInitialLoad() {
|
|
245
279
|
await this.reloadOptions();
|
|
246
|
-
this.$emit('optionsLoaded', this.
|
|
280
|
+
this.$emit('optionsLoaded', this.loadedOptions);
|
|
247
281
|
|
|
248
282
|
if (this.$isPropTruthy(this.remoteSearch)) {
|
|
249
283
|
this.$watch('searchText', debounce(this.reloadOptionsIfSearching, 250));
|
|
@@ -254,7 +288,7 @@ export default {
|
|
|
254
288
|
if (this.preload) return this.reloadOptions();
|
|
255
289
|
if (!this.isLoaded) return;
|
|
256
290
|
this.isLoaded = false;
|
|
257
|
-
this.
|
|
291
|
+
this.loadedOptions = [];
|
|
258
292
|
},
|
|
259
293
|
|
|
260
294
|
async reloadOptions() {
|
|
@@ -263,7 +297,7 @@ export default {
|
|
|
263
297
|
this.$isPropTruthy(this.remoteSearch) && this.searchText && (params.q = this.searchText);
|
|
264
298
|
|
|
265
299
|
const result = await this.$http.get(this.url, { params: params });
|
|
266
|
-
this.
|
|
300
|
+
this.loadedOptions = result.data;
|
|
267
301
|
this.isLoaded = true;
|
|
268
302
|
},
|
|
269
303
|
|
|
@@ -429,10 +463,10 @@ export default {
|
|
|
429
463
|
this.selectedOptionTitle = null;
|
|
430
464
|
this.$emit('createItem', createText);
|
|
431
465
|
} else {
|
|
432
|
-
const
|
|
466
|
+
const selectedDecoratedOption = this.optionsDescriptors.find(
|
|
433
467
|
decoratedOption => decoratedOption.key == option.key
|
|
434
468
|
);
|
|
435
|
-
const realOption =
|
|
469
|
+
const realOption = selectedDecoratedOption.ref;
|
|
436
470
|
this.selectedOption = realOption;
|
|
437
471
|
this.selectedOptionTitle = this.getOptionTitle(this.selectedOption).text;
|
|
438
472
|
this.searchText = this.selectedOptionTitle || '';
|
|
@@ -443,8 +477,10 @@ export default {
|
|
|
443
477
|
|
|
444
478
|
handleValueChanged() {
|
|
445
479
|
if (this.modelValue) {
|
|
446
|
-
if (this.
|
|
447
|
-
this.selectedOption = this.
|
|
480
|
+
if (this.effectiveValueKey) {
|
|
481
|
+
this.selectedOption = this.fullOptionsArray.find(
|
|
482
|
+
option => option[this.effectiveValueKey] === this.modelValue
|
|
483
|
+
);
|
|
448
484
|
} else {
|
|
449
485
|
this.selectedOption = this.modelValue;
|
|
450
486
|
}
|
|
@@ -507,7 +543,16 @@ export default {
|
|
|
507
543
|
},
|
|
508
544
|
|
|
509
545
|
addRemoteOption(option) {
|
|
510
|
-
this.
|
|
546
|
+
this.loadedOptions.push(option);
|
|
547
|
+
},
|
|
548
|
+
|
|
549
|
+
arrayifyOptions(options) {
|
|
550
|
+
return Array.isArray(options)
|
|
551
|
+
? options
|
|
552
|
+
: Object.entries(options).map(entry => ({
|
|
553
|
+
[this.effectiveIdKey]: entry[0],
|
|
554
|
+
[this.effectiveTitleKey]: entry[1]
|
|
555
|
+
}));
|
|
511
556
|
}
|
|
512
557
|
}
|
|
513
558
|
};
|
package/src/config.js
CHANGED