wave-ui 1.47.0 → 1.49.2
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/dist/wave-ui.cjs.js +1 -1
- package/dist/wave-ui.css +1 -1
- package/dist/wave-ui.es.js +192 -141
- package/dist/wave-ui.umd.js +1 -1
- package/package.json +16 -14
- package/src/wave-ui/components/w-input.vue +117 -34
- package/src/wave-ui/components/w-menu.vue +24 -83
- package/src/wave-ui/components/w-notification-manager.vue +9 -2
- package/src/wave-ui/components/w-select.vue +1 -1
- package/src/wave-ui/components/w-tooltip.vue +22 -75
- package/src/wave-ui/mixins/detachable.js +134 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wave-ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.49.2",
|
|
4
4
|
"description": "An emerging UI framework for Vue.js & Vue 3 with only the bright side. :sunny:",
|
|
5
5
|
"author": "Antoni Andre <antoniandre.web@gmail.com>",
|
|
6
6
|
"main": "./dist/wave-ui.umd.js",
|
|
@@ -43,37 +43,39 @@
|
|
|
43
43
|
"*.vue"
|
|
44
44
|
],
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@babel/core": "^7.
|
|
47
|
-
"@babel/eslint-parser": "^7.
|
|
48
|
-
"@babel/plugin-proposal-class-properties": "^7.16.
|
|
49
|
-
"@
|
|
50
|
-
"
|
|
51
|
-
"autoprefixer": "^10.4.0",
|
|
46
|
+
"@babel/core": "^7.17.0",
|
|
47
|
+
"@babel/eslint-parser": "^7.17.0",
|
|
48
|
+
"@babel/plugin-proposal-class-properties": "^7.16.7",
|
|
49
|
+
"@vitejs/plugin-vue": "^1.10.2",
|
|
50
|
+
"autoprefixer": "^10.4.2",
|
|
52
51
|
"axios": "^0.21.4",
|
|
53
|
-
"core-js": "^3.
|
|
52
|
+
"core-js": "^3.21.0",
|
|
54
53
|
"eslint": "^7.32.0",
|
|
55
54
|
"eslint-config-standard": "^16.0.3",
|
|
56
|
-
"eslint-plugin-import": "^2.25.
|
|
55
|
+
"eslint-plugin-import": "^2.25.4",
|
|
57
56
|
"eslint-plugin-node": "^11.1.0",
|
|
58
|
-
"eslint-plugin-promise": "^5.
|
|
57
|
+
"eslint-plugin-promise": "^5.2.0",
|
|
59
58
|
"eslint-plugin-vue": "^7.20.0",
|
|
60
59
|
"font-awesome": "^4.7.0",
|
|
61
60
|
"ghspa": "^1.0.0",
|
|
62
|
-
"gsap": "^3.
|
|
61
|
+
"gsap": "^3.9.1",
|
|
63
62
|
"ionicons": "^4.6.3",
|
|
64
63
|
"material-design-icons": "^3.0.1",
|
|
65
64
|
"rollup-plugin-delete": "^2.0.0",
|
|
66
|
-
"sass": "^1.
|
|
65
|
+
"sass": "^1.49.7",
|
|
67
66
|
"simple-syntax-highlighter": "^1.5.0",
|
|
68
67
|
"splitpanes": "^2.3.8",
|
|
69
|
-
"vite": "^2.
|
|
68
|
+
"vite": "^2.7.13",
|
|
70
69
|
"vite-plugin-pug": "^0.3.0",
|
|
71
|
-
"vite-plugin-vue2": "^1.9.
|
|
70
|
+
"vite-plugin-vue2": "^1.9.3",
|
|
72
71
|
"vue": "^2.6.14",
|
|
73
72
|
"vue-router": "^3.5.3",
|
|
74
73
|
"vue-svg-loader": "^0.16.0",
|
|
75
74
|
"vue-template-compiler": "^2.6.14",
|
|
76
75
|
"vueperslides": "^2.15.2",
|
|
77
76
|
"vuex": "^3.6.2"
|
|
77
|
+
},
|
|
78
|
+
"dependencies": {
|
|
79
|
+
"@mdi/font": "^6.5.95"
|
|
78
80
|
}
|
|
79
81
|
}
|
|
@@ -12,9 +12,16 @@ component(
|
|
|
12
12
|
template(v-else)
|
|
13
13
|
//- Left label.
|
|
14
14
|
template(v-if="labelPosition === 'left'")
|
|
15
|
-
label.w-input__label.w-input__label--left.w-form-el-shakable(
|
|
15
|
+
label.w-input__label.w-input__label--left.w-form-el-shakable(
|
|
16
|
+
v-if="$slots.default"
|
|
17
|
+
:for="`w-input--${_uid}`"
|
|
18
|
+
:class="validationClasses")
|
|
16
19
|
slot
|
|
17
|
-
label.w-input__label.w-input__label--left.w-form-el-shakable(
|
|
20
|
+
label.w-input__label.w-input__label--left.w-form-el-shakable(
|
|
21
|
+
v-else-if="label"
|
|
22
|
+
:for="`w-input--${_uid}`"
|
|
23
|
+
:class="validationClasses"
|
|
24
|
+
v-html="label")
|
|
18
25
|
|
|
19
26
|
//- Input wrapper.
|
|
20
27
|
.w-input__input-wrap(:class="inputWrapClasses")
|
|
@@ -58,47 +65,67 @@ component(
|
|
|
58
65
|
@blur="onBlur"
|
|
59
66
|
@change="onFileChange"
|
|
60
67
|
:multiple="multiple || null"
|
|
61
|
-
v-bind="attrs"
|
|
62
|
-
|
|
68
|
+
v-bind="attrs"
|
|
69
|
+
:data-progress="overallFilesProgress /* Needed to emit the overallProgress. */")
|
|
70
|
+
transition-group.w-input__input.w-input__input--file(
|
|
71
|
+
tag="label"
|
|
72
|
+
name="fade"
|
|
73
|
+
:for="`w-input--${_uid}`")
|
|
63
74
|
span.w-input__no-file(v-if="!inputFiles.length && isFocused" key="no-file")
|
|
64
75
|
slot(name="no-file")
|
|
65
76
|
template(v-if="$slots['no-file'] === undefined") No file
|
|
66
77
|
span(v-for="(file, i) in inputFiles" :key="file.lastModified")
|
|
67
78
|
| {{ i ? ', ': '' }}
|
|
68
79
|
span.filename(:key="`${i}b`") {{ file.base }}
|
|
69
|
-
| {{ file.extension }}
|
|
80
|
+
| {{ file.extension ? `.${file.extension}` : '' }}
|
|
70
81
|
|
|
71
82
|
template(v-if="labelPosition === 'inside' && showLabelInside")
|
|
72
83
|
label.w-input__label.w-input__label--inside.w-form-el-shakable(
|
|
73
84
|
v-if="$slots.default"
|
|
74
85
|
:for="`w-input--${_uid}`"
|
|
75
|
-
:class="
|
|
86
|
+
:class="validationClasses")
|
|
76
87
|
slot
|
|
77
88
|
label.w-input__label.w-input__label--inside.w-form-el-shakable(
|
|
78
89
|
v-else-if="label"
|
|
79
90
|
:for="`w-input--${_uid}`"
|
|
80
91
|
v-html="label"
|
|
81
|
-
:class="
|
|
92
|
+
:class="validationClasses")
|
|
82
93
|
w-icon.w-input__icon.w-input__icon--inner-right(
|
|
83
94
|
v-if="innerIconRight"
|
|
84
95
|
tag="label"
|
|
85
96
|
:for="`w-input--${_uid}`"
|
|
86
97
|
@click="$emit('click:inner-icon-right', $event)") {{ innerIconRight }}
|
|
87
98
|
|
|
99
|
+
w-progress.fill-width(
|
|
100
|
+
v-if="hasLoading || (showProgress && (uploadInProgress || uploadComplete))"
|
|
101
|
+
size="2"
|
|
102
|
+
:color="progressColor || color"
|
|
103
|
+
:value="showProgress ? (uploadInProgress || uploadComplete) && overallFilesProgress : loadingValue")
|
|
104
|
+
|
|
88
105
|
//- Files preview.
|
|
89
|
-
label.d-flex(v-if="type === 'file' && inputFiles.length" :for="`w-input--${_uid}`")
|
|
106
|
+
label.d-flex(v-if="type === 'file' && preview && inputFiles.length" :for="`w-input--${_uid}`")
|
|
90
107
|
template(v-for="(file, i) in inputFiles")
|
|
91
|
-
i.w-icon.wi-spinner.w-icon--spin.size--sm.w-input__file-preview.primary(
|
|
108
|
+
i.w-icon.wi-spinner.w-icon--spin.size--sm.w-input__file-preview.primary(
|
|
109
|
+
v-if="file.progress < 100"
|
|
110
|
+
:key="i")
|
|
92
111
|
img.w-input__file-preview(v-else-if="file.preview" :key="i" :src="file.preview" alt="")
|
|
93
|
-
i.w-icon.
|
|
112
|
+
i.w-icon.w-input__file-preview.primary.size--md(
|
|
113
|
+
v-else
|
|
114
|
+
:key="i"
|
|
115
|
+
:class="preview && typeof preview === 'string' ? preview : 'wi-file'")
|
|
94
116
|
|
|
95
117
|
//- Right label.
|
|
96
118
|
template(v-if="labelPosition === 'right'")
|
|
97
|
-
label.w-input__label.w-input__label--right.w-form-el-shakable(
|
|
119
|
+
label.w-input__label.w-input__label--right.w-form-el-shakable(
|
|
120
|
+
v-if="$slots.default"
|
|
121
|
+
:for="`w-input--${_uid}`"
|
|
122
|
+
:class="validationClasses")
|
|
98
123
|
slot
|
|
99
|
-
label.w-input__label.w-input__label--right.w-form-el-shakable(
|
|
100
|
-
|
|
101
|
-
|
|
124
|
+
label.w-input__label.w-input__label--right.w-form-el-shakable(
|
|
125
|
+
v-else-if="label"
|
|
126
|
+
:for="`w-input--${_uid}`"
|
|
127
|
+
:class="validationClasses"
|
|
128
|
+
v-html="label")
|
|
102
129
|
</template>
|
|
103
130
|
|
|
104
131
|
<script>
|
|
@@ -136,13 +163,17 @@ export default {
|
|
|
136
163
|
shadow: { type: Boolean },
|
|
137
164
|
tile: { type: Boolean },
|
|
138
165
|
multiple: { type: Boolean }, // Only for file uploads.
|
|
139
|
-
preview: { type: Boolean }, // Only for file uploads.
|
|
140
|
-
loading: { type: Boolean }
|
|
166
|
+
preview: { type: [Boolean, String], default: true }, // Only for file uploads.
|
|
167
|
+
loading: { type: [Boolean, Number], default: false }, // If a number is given, it will be the value of the progress.
|
|
168
|
+
showProgress: { type: [Boolean] }, // Only for file uploads.
|
|
169
|
+
// Allow syncing the files 1 way: prefilling a file is not possible.
|
|
170
|
+
// https://stackoverflow.com/questions/16365668/pre-populate-html-form-file-input
|
|
171
|
+
files: { type: Array }
|
|
141
172
|
// Props from mixin: name, disabled, readonly, required, tabindex, validators.
|
|
142
173
|
// Computed from mixin: inputName, isDisabled & isReadonly.
|
|
143
174
|
},
|
|
144
175
|
|
|
145
|
-
emits: ['input', 'update:modelValue', 'focus', 'blur', 'click:inner-icon-left', 'click:inner-icon-right'],
|
|
176
|
+
emits: ['input', 'update:modelValue', 'focus', 'blur', 'click:inner-icon-left', 'click:inner-icon-right', 'update:overallProgress'],
|
|
146
177
|
|
|
147
178
|
data () {
|
|
148
179
|
return {
|
|
@@ -173,23 +204,54 @@ export default {
|
|
|
173
204
|
},
|
|
174
205
|
|
|
175
206
|
hasValue () {
|
|
176
|
-
|
|
177
|
-
this.
|
|
178
|
-
this.inputValue === 0 ||
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
207
|
+
switch (this.type) {
|
|
208
|
+
case 'file': return !!this.inputFiles.length
|
|
209
|
+
case 'number': return this.inputValue || this.inputValue === 0 || this.inputNumberError
|
|
210
|
+
case 'date':
|
|
211
|
+
case 'time':
|
|
212
|
+
return true
|
|
213
|
+
default:
|
|
214
|
+
return this.inputValue || this.inputValue === 0
|
|
215
|
+
}
|
|
183
216
|
},
|
|
184
217
|
|
|
185
218
|
hasLabel () {
|
|
186
219
|
return this.label || this.$slots.default
|
|
187
220
|
},
|
|
188
221
|
|
|
222
|
+
hasLoading () {
|
|
223
|
+
return ![undefined, false].includes(this.loading)
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
loadingValue () {
|
|
227
|
+
let value
|
|
228
|
+
if (typeof this.loading === 'number') value = this.loading
|
|
229
|
+
else if (this.loading) {
|
|
230
|
+
value = this.type === 'file' && this.overallFilesProgress ? this.overallFilesProgress : undefined
|
|
231
|
+
}
|
|
232
|
+
return value
|
|
233
|
+
},
|
|
234
|
+
|
|
189
235
|
showLabelInside () {
|
|
190
236
|
return !this.staticLabel || (!this.hasValue && !this.placeholder)
|
|
191
237
|
},
|
|
192
238
|
|
|
239
|
+
overallFilesProgress () {
|
|
240
|
+
const progress = this.inputFiles.reduce((total, file) => total + file.progress, 0)
|
|
241
|
+
const total = progress / this.inputFiles.length
|
|
242
|
+
this.$emit('update:overallProgress', this.inputFiles.length ? total : undefined)
|
|
243
|
+
|
|
244
|
+
return total
|
|
245
|
+
},
|
|
246
|
+
|
|
247
|
+
uploadInProgress () {
|
|
248
|
+
return this.overallFilesProgress > 0 && this.overallFilesProgress < 100
|
|
249
|
+
},
|
|
250
|
+
|
|
251
|
+
uploadComplete () {
|
|
252
|
+
return this.overallFilesProgress === 100
|
|
253
|
+
},
|
|
254
|
+
|
|
193
255
|
classes () {
|
|
194
256
|
return {
|
|
195
257
|
'w-input': true,
|
|
@@ -207,6 +269,12 @@ export default {
|
|
|
207
269
|
}
|
|
208
270
|
},
|
|
209
271
|
|
|
272
|
+
validationClasses () {
|
|
273
|
+
return this.isFocused && {
|
|
274
|
+
[this.valid === false ? 'error' : this.color]: this.color || this.valid === false
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
|
|
210
278
|
inputWrapClasses () {
|
|
211
279
|
return {
|
|
212
280
|
[this.valid === false ? 'error' : this.color]: this.color || this.valid === false,
|
|
@@ -219,7 +287,8 @@ export default {
|
|
|
219
287
|
'w-input__input-wrap--underline': !this.outline,
|
|
220
288
|
'w-input__input-wrap--shadow': this.shadow,
|
|
221
289
|
'w-input__input-wrap--no-padding': !this.outline && !this.bgColor && !this.shadow && !this.round,
|
|
222
|
-
'w-input__input-wrap--loading': this.loading
|
|
290
|
+
'w-input__input-wrap--loading': this.loading || (this.showProgress && this.uploadInProgress),
|
|
291
|
+
'w-input__input-wrap--upload-complete': this.uploadComplete
|
|
223
292
|
}
|
|
224
293
|
}
|
|
225
294
|
},
|
|
@@ -244,37 +313,43 @@ export default {
|
|
|
244
313
|
// For file input.
|
|
245
314
|
onFileChange (e) {
|
|
246
315
|
this.$set(this, 'inputFiles', [...e.target.files].map(original => {
|
|
247
|
-
|
|
316
|
+
// `full` if there is no filename but only an extension.
|
|
317
|
+
const [, base = '', extension = '', full = ''] = original.name.match(/^(.*?)\.([^.]*)$|(.*)/)
|
|
248
318
|
const file = Object.assign({}, {
|
|
249
319
|
name: original.name,
|
|
250
|
-
base,
|
|
320
|
+
base: base || full,
|
|
251
321
|
extension,
|
|
252
322
|
type: original.type,
|
|
253
323
|
size: original.size,
|
|
254
324
|
lastModified: original.lastModified,
|
|
255
325
|
preview: null,
|
|
256
|
-
progress: 0
|
|
326
|
+
progress: 0,
|
|
327
|
+
file: original
|
|
257
328
|
})
|
|
258
329
|
|
|
259
|
-
this.
|
|
330
|
+
this.readFile(original, file)
|
|
260
331
|
|
|
261
332
|
return file
|
|
262
333
|
}))
|
|
263
334
|
this.$emit('update:modelValue', this.inputFiles)
|
|
264
335
|
this.$emit('input', this.inputFiles)
|
|
265
|
-
this.$emit('change', this.inputFiles)
|
|
266
336
|
},
|
|
267
337
|
|
|
268
338
|
// For file input.
|
|
269
|
-
|
|
339
|
+
readFile (original, file) {
|
|
270
340
|
const reader = new FileReader()
|
|
271
341
|
|
|
342
|
+
// If the preview prop is a string, the user is setting the preview to an icon and
|
|
343
|
+
// don't need the actual file preview.
|
|
344
|
+
const isPreviewAnIcon = typeof this.preview === 'string'
|
|
345
|
+
const isFileAnImage = original.type && original.type.startsWith('image/')
|
|
272
346
|
// Check if the file is an image and set a preview image.
|
|
273
|
-
if (
|
|
347
|
+
if (this.preview && !isPreviewAnIcon && isFileAnImage) {
|
|
274
348
|
reader.addEventListener('load', e => {
|
|
275
349
|
this.$set(file, 'preview', e.target.result)
|
|
276
350
|
})
|
|
277
351
|
}
|
|
352
|
+
else delete file.preview
|
|
278
353
|
|
|
279
354
|
// Used to display a spinner while the file is loading.
|
|
280
355
|
reader.addEventListener('progress', event => {
|
|
@@ -319,6 +394,8 @@ $inactive-color: #777;
|
|
|
319
394
|
&--file {
|
|
320
395
|
flex-wrap: nowrap;
|
|
321
396
|
align-items: flex-end;
|
|
397
|
+
|
|
398
|
+
span.fade-leave-to {position: absolute;}
|
|
322
399
|
}
|
|
323
400
|
|
|
324
401
|
// Input field wrapper.
|
|
@@ -348,7 +425,10 @@ $inactive-color: #777;
|
|
|
348
425
|
&--round {border-radius: 99em;}
|
|
349
426
|
&--tile {border-radius: initial;}
|
|
350
427
|
&--shadow {box-shadow: $box-shadow;}
|
|
351
|
-
&--loading
|
|
428
|
+
&--loading, &--upload-complete {
|
|
429
|
+
border-bottom-color: transparent;
|
|
430
|
+
flex-wrap: wrap;
|
|
431
|
+
}
|
|
352
432
|
&--loading ~ .w-progress {
|
|
353
433
|
height: 2px;
|
|
354
434
|
position: absolute;
|
|
@@ -357,7 +437,8 @@ $inactive-color: #777;
|
|
|
357
437
|
}
|
|
358
438
|
|
|
359
439
|
.w-input--focused & {border-color: currentColor;}
|
|
360
|
-
.w-input--focused &--loading
|
|
440
|
+
.w-input--focused &--loading,
|
|
441
|
+
.w-input--focused &--upload-complete {border-bottom-color: transparent;}
|
|
361
442
|
|
|
362
443
|
// Underline.
|
|
363
444
|
&--underline:after {
|
|
@@ -468,6 +549,8 @@ $inactive-color: #777;
|
|
|
468
549
|
margin-left: 4px;
|
|
469
550
|
max-height: 2em;
|
|
470
551
|
align-self: flex-end;
|
|
552
|
+
|
|
553
|
+
&.w-icon {margin-bottom: 4px;}
|
|
471
554
|
}
|
|
472
555
|
|
|
473
556
|
// Icons inside.
|
|
@@ -74,26 +74,16 @@ export default {
|
|
|
74
74
|
menuClass: { type: [String, Object, Array] },
|
|
75
75
|
titleClass: { type: [String, Object, Array] },
|
|
76
76
|
contentClass: { type: [String, Object, Array] },
|
|
77
|
-
// Position.
|
|
78
77
|
arrow: { type: Boolean }, // The small triangle pointing toward the activator.
|
|
79
|
-
detachTo: { type: [String, Boolean, Object], deprecated: true },
|
|
80
|
-
appendTo: { type: [String, Boolean, Object] },
|
|
81
|
-
fixed: { type: Boolean },
|
|
82
|
-
top: { type: Boolean },
|
|
83
|
-
bottom: { type: Boolean },
|
|
84
|
-
left: { type: Boolean },
|
|
85
|
-
right: { type: Boolean },
|
|
86
|
-
alignTop: { type: Boolean },
|
|
87
|
-
alignBottom: { type: Boolean },
|
|
88
|
-
alignLeft: { type: Boolean },
|
|
89
|
-
alignRight: { type: Boolean },
|
|
90
|
-
zIndex: { type: [Number, String, Boolean] },
|
|
91
78
|
minWidth: { type: [Number, String] }, // can be like: `40`, `5em`, `activator`.
|
|
92
79
|
overlay: { type: Boolean },
|
|
93
80
|
overlayClass: { type: [String, Object, Array] },
|
|
94
81
|
overlayProps: { type: Object }, // Allow passing down an object of props to the w-overlay component.
|
|
95
82
|
persistent: { type: Boolean },
|
|
96
|
-
|
|
83
|
+
delay: { type: Number }
|
|
84
|
+
// Other props in the detachable mixin:
|
|
85
|
+
// detachTo, appendTo, fixed, top, bottom, left, right, alignTop, alignBottom, alignLeft,
|
|
86
|
+
// alignRight, noPosition, zIndex, activator.
|
|
97
87
|
},
|
|
98
88
|
|
|
99
89
|
emits: ['input', 'update:modelValue', 'open', 'close'],
|
|
@@ -107,7 +97,6 @@ export default {
|
|
|
107
97
|
top: 0,
|
|
108
98
|
left: 0
|
|
109
99
|
},
|
|
110
|
-
activatorEl: null,
|
|
111
100
|
activatorWidth: 0,
|
|
112
101
|
detachableEl: null,
|
|
113
102
|
timeoutId: null
|
|
@@ -118,32 +107,15 @@ export default {
|
|
|
118
107
|
* Other computed in the detachable mixin:
|
|
119
108
|
* - `appendToTarget`
|
|
120
109
|
* - `detachableParentEl`
|
|
110
|
+
* - `activatorEl`
|
|
111
|
+
* - `position`
|
|
112
|
+
* - `alignment`
|
|
121
113
|
**/
|
|
122
114
|
|
|
123
115
|
transitionName () {
|
|
124
116
|
return this.transition || 'scale-fade'
|
|
125
117
|
},
|
|
126
118
|
|
|
127
|
-
position () {
|
|
128
|
-
return (
|
|
129
|
-
(this.top && 'top') ||
|
|
130
|
-
(this.bottom && 'bottom') ||
|
|
131
|
-
(this.left && 'left') ||
|
|
132
|
-
(this.right && 'right') ||
|
|
133
|
-
'bottom'
|
|
134
|
-
)
|
|
135
|
-
},
|
|
136
|
-
|
|
137
|
-
alignment () {
|
|
138
|
-
return (
|
|
139
|
-
(['top', 'bottom'].includes(this.position) && this.alignLeft && 'left') ||
|
|
140
|
-
(['top', 'bottom'].includes(this.position) && this.alignRight && 'right') ||
|
|
141
|
-
(['left', 'right'].includes(this.position) && this.alignTop && 'top') ||
|
|
142
|
-
(['left', 'right'].includes(this.position) && this.alignBottom && 'bottom') ||
|
|
143
|
-
''
|
|
144
|
-
)
|
|
145
|
-
},
|
|
146
|
-
|
|
147
119
|
menuMinWidth () {
|
|
148
120
|
if (this.minWidth === 'activator') return this.activatorWidth ? `${this.activatorWidth}px` : 0
|
|
149
121
|
else return isNaN(this.minWidth) ? this.minWidth : (this.minWidth ? `${this.minWidth}px` : 0)
|
|
@@ -197,8 +169,8 @@ export default {
|
|
|
197
169
|
|
|
198
170
|
if (this.showOnHover) {
|
|
199
171
|
handlers = {
|
|
200
|
-
focus: this.
|
|
201
|
-
blur: this.
|
|
172
|
+
focus: this.toggle,
|
|
173
|
+
blur: this.toggle,
|
|
202
174
|
mouseenter: e => {
|
|
203
175
|
this.hoveringActivator = true
|
|
204
176
|
this.open(e)
|
|
@@ -213,10 +185,10 @@ export default {
|
|
|
213
185
|
}
|
|
214
186
|
// Check the window exists: SSR-proof.
|
|
215
187
|
if (typeof window !== 'undefined' && 'ontouchstart' in window) {
|
|
216
|
-
handlers.click = this.
|
|
188
|
+
handlers.click = this.toggle
|
|
217
189
|
}
|
|
218
190
|
}
|
|
219
|
-
else handlers = { click: this.
|
|
191
|
+
else handlers = { click: this.toggle }
|
|
220
192
|
return handlers
|
|
221
193
|
}
|
|
222
194
|
},
|
|
@@ -233,7 +205,7 @@ export default {
|
|
|
233
205
|
**/
|
|
234
206
|
|
|
235
207
|
// ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
236
|
-
|
|
208
|
+
toggle (e) {
|
|
237
209
|
let shouldShowMenu = this.detachableVisible
|
|
238
210
|
if ('ontouchstart' in window && this.showOnHover && e.type === 'click') {
|
|
239
211
|
shouldShowMenu = !shouldShowMenu
|
|
@@ -250,19 +222,22 @@ export default {
|
|
|
250
222
|
|
|
251
223
|
this.timeoutId = clearTimeout(this.timeoutId)
|
|
252
224
|
|
|
253
|
-
if (shouldShowMenu)
|
|
254
|
-
this.$emit('update:modelValue', (this.detachableVisible = true))
|
|
255
|
-
this.$emit('input', true)
|
|
256
|
-
this.$emit('open')
|
|
257
|
-
|
|
258
|
-
this.open(e)
|
|
259
|
-
}
|
|
225
|
+
if (shouldShowMenu) this.open(e)
|
|
260
226
|
else this.close()
|
|
261
227
|
},
|
|
262
228
|
|
|
263
229
|
// ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
264
230
|
async open (e) {
|
|
231
|
+
// A tiny delay may help positioning the detachable correctly in case of multiple activators
|
|
232
|
+
// with different menu contents.
|
|
233
|
+
if (this.delay) await new Promise(resolve => setTimeout(resolve, this.delay))
|
|
234
|
+
|
|
265
235
|
this.detachableVisible = true
|
|
236
|
+
|
|
237
|
+
// If the activator is external, there might be multiple,
|
|
238
|
+
// so on open, the activator will be set to the event target.
|
|
239
|
+
if (this.activator) this.activatorEl = e.target
|
|
240
|
+
|
|
266
241
|
await this.insertInDOM()
|
|
267
242
|
|
|
268
243
|
if (this.minWidth === 'activator') this.activatorWidth = this.activatorEl.offsetWidth
|
|
@@ -310,43 +285,9 @@ export default {
|
|
|
310
285
|
document.removeEventListener('mousedown', this.onOutsideMousedown)
|
|
311
286
|
window.removeEventListener('resize', this.onResize)
|
|
312
287
|
}
|
|
313
|
-
},
|
|
314
|
-
|
|
315
|
-
mounted () {
|
|
316
|
-
const wrapper = this.$el
|
|
317
|
-
this.activatorEl = wrapper.firstElementChild
|
|
318
|
-
|
|
319
|
-
// Unwrap the activator element.
|
|
320
|
-
wrapper.parentNode.insertBefore(this.activatorEl, wrapper)
|
|
321
|
-
|
|
322
|
-
// Unwrap the overlay.
|
|
323
|
-
if (this.overlay) {
|
|
324
|
-
this.overlayEl = this.$refs.overlay?.$el
|
|
325
|
-
wrapper.parentNode.insertBefore(this.overlayEl, wrapper)
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
if (this.value) this.toggleMenu({ type: 'click', target: this.activatorEl })
|
|
329
|
-
},
|
|
330
|
-
|
|
331
|
-
beforeDestroy () {
|
|
332
|
-
this.removeFromDOM()
|
|
333
|
-
if (this.overlay && this.overlayEl.parentNode) this.overlayEl.remove()
|
|
334
|
-
if (this.activatorEl && this.activatorEl.parentNode) this.activatorEl.remove()
|
|
335
|
-
},
|
|
336
|
-
|
|
337
|
-
watch: {
|
|
338
|
-
value (bool) {
|
|
339
|
-
if (!!bool !== this.detachableVisible) this.toggleMenu({ type: 'click', target: this.activatorEl })
|
|
340
|
-
},
|
|
341
|
-
detachTo () {
|
|
342
|
-
this.removeFromDOM()
|
|
343
|
-
this.insertInDOM()
|
|
344
|
-
},
|
|
345
|
-
appendTo () {
|
|
346
|
-
this.removeFromDOM()
|
|
347
|
-
this.insertInDOM()
|
|
348
|
-
}
|
|
349
288
|
}
|
|
289
|
+
|
|
290
|
+
// watch, mounted & beforeDestroy hooks are set in the detachable.js mixin.
|
|
350
291
|
}
|
|
351
292
|
</script>
|
|
352
293
|
|
|
@@ -11,8 +11,8 @@ transition-group(
|
|
|
11
11
|
:key="notif._uid"
|
|
12
12
|
v-model="notif._value"
|
|
13
13
|
@close="notifManager.dismiss(notif._uid)"
|
|
14
|
-
v-bind="notif")
|
|
15
|
-
|
|
14
|
+
v-bind="notifProps(notif)")
|
|
15
|
+
div(v-html="notif.message")
|
|
16
16
|
</template>
|
|
17
17
|
|
|
18
18
|
<script>
|
|
@@ -42,6 +42,13 @@ export default {
|
|
|
42
42
|
}
|
|
43
43
|
},
|
|
44
44
|
|
|
45
|
+
methods: {
|
|
46
|
+
notifProps (notif) {
|
|
47
|
+
const { _value, _uid, message, timeout, ...props } = notif
|
|
48
|
+
return props
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
|
|
45
52
|
created () {
|
|
46
53
|
this.notifManager = new NotificationManager()
|
|
47
54
|
},
|
|
@@ -330,7 +330,7 @@ export default {
|
|
|
330
330
|
setTimeout(() => {
|
|
331
331
|
const itemIndex = this.inputValue.length ? this.inputValue[0].index : 0 // Real index starts at 0.
|
|
332
332
|
// User visible index starts at 1.
|
|
333
|
-
this.$refs['w-list'].$el.querySelector(`#w-select-menu--${this._uid}_item-${itemIndex + 1}`)
|
|
333
|
+
this.$refs['w-list'].$el.querySelector(`#w-select-menu--${this._uid}_item-${itemIndex + 1}`)?.focus()
|
|
334
334
|
}, 100)
|
|
335
335
|
},
|
|
336
336
|
|