wave-ui 2.30.0 → 2.31.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/dist/wave-ui.cjs.js +1 -1
- package/dist/wave-ui.css +1 -1
- package/dist/wave-ui.es.js +224 -165
- package/dist/wave-ui.umd.js +1 -1
- package/package.json +12 -9
- package/src/wave-ui/components/w-confirm.vue +1 -1
- package/src/wave-ui/components/w-input.vue +111 -32
- 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-rating.vue +1 -1
- package/src/wave-ui/components/w-select.vue +1 -1
- package/src/wave-ui/components/w-table.vue +2 -2
- 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": "2.
|
|
3
|
+
"version": "2.31.0",
|
|
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,29 +43,32 @@
|
|
|
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.
|
|
46
|
+
"@babel/core": "^7.17.0",
|
|
47
|
+
"@babel/eslint-parser": "^7.17.0",
|
|
48
|
+
"@babel/plugin-proposal-class-properties": "^7.16.7",
|
|
49
49
|
"@mdi/font": "^5.9.55",
|
|
50
50
|
"@vitejs/plugin-vue": "^1.10.2",
|
|
51
51
|
"@vue/compiler-sfc": "3.1.5",
|
|
52
|
-
"autoprefixer": "^10.4.
|
|
53
|
-
"axios": "^0.
|
|
52
|
+
"autoprefixer": "^10.4.2",
|
|
53
|
+
"axios": "^0.25.0",
|
|
54
54
|
"eslint": "^7.32.0",
|
|
55
55
|
"font-awesome": "^4.7.0",
|
|
56
56
|
"gsap": "^3.9.1",
|
|
57
57
|
"ionicons": "^4.6.3",
|
|
58
58
|
"material-design-icons": "^3.0.1",
|
|
59
59
|
"rollup-plugin-delete": "^2.0.0",
|
|
60
|
-
"sass": "^1.
|
|
60
|
+
"sass": "^1.49.7",
|
|
61
61
|
"simple-syntax-highlighter": "^2.2.0",
|
|
62
62
|
"splitpanes": "^3.0.6",
|
|
63
|
-
"vite": "^2.7.
|
|
63
|
+
"vite": "^2.7.13",
|
|
64
64
|
"vite-plugin-pug": "^0.3.0",
|
|
65
|
-
"vue": "^3.2.
|
|
65
|
+
"vue": "^3.2.29",
|
|
66
66
|
"vue-cal": "^4.2.0",
|
|
67
67
|
"vue-router": "^4.0.12",
|
|
68
68
|
"vueperslides": "^3.3.2",
|
|
69
69
|
"vuex": "^4.0.2"
|
|
70
|
+
},
|
|
71
|
+
"dependencies": {
|
|
72
|
+
"postcss": "^8.4.6"
|
|
70
73
|
}
|
|
71
74
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
.w-confirm
|
|
3
3
|
w-menu(v-model="showPopup" v-bind="wMenuProps")
|
|
4
4
|
template(#activator="{ on }")
|
|
5
|
-
w-button.w-confirm__button(v-
|
|
5
|
+
w-button.w-confirm__button(v-bind="{ ...$attrs, ...buttonProps, ...on }")
|
|
6
6
|
slot
|
|
7
7
|
w-flex(:column="!inline" align-center)
|
|
8
8
|
div
|
|
@@ -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,35 +65,45 @@ 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
|
+
:model-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
108
|
i.w-icon.wi-spinner.w-icon--spin.size--sm.w-input__file-preview.primary(
|
|
92
109
|
v-if="file.progress < 100"
|
|
@@ -96,20 +113,23 @@ component(
|
|
|
96
113
|
:key="`${i}b`"
|
|
97
114
|
:src="file.preview"
|
|
98
115
|
alt="")
|
|
99
|
-
i.w-icon.
|
|
116
|
+
i.w-icon.w-input__file-preview.primary.size--md(
|
|
117
|
+
v-else
|
|
118
|
+
:key="`${i}c`"
|
|
119
|
+
:class="preview && typeof preview === 'string' ? preview : 'wi-file'")
|
|
100
120
|
|
|
101
121
|
//- Right label.
|
|
102
122
|
template(v-if="labelPosition === 'right'")
|
|
103
123
|
label.w-input__label.w-input__label--right.w-form-el-shakable(
|
|
104
124
|
v-if="$slots.default"
|
|
105
|
-
:for="`w-input--${_.uid}`"
|
|
125
|
+
:for="`w-input--${_.uid}`"
|
|
126
|
+
:class="validationClasses")
|
|
106
127
|
slot
|
|
107
128
|
label.w-input__label.w-input__label--right.w-form-el-shakable(
|
|
108
129
|
v-else-if="label"
|
|
109
130
|
:for="`w-input--${_.uid}`"
|
|
131
|
+
:class="validationClasses"
|
|
110
132
|
v-html="label")
|
|
111
|
-
|
|
112
|
-
w-progress.fill-width(v-if="loading" size="2" :color="progressColor || color")
|
|
113
133
|
</template>
|
|
114
134
|
|
|
115
135
|
<script>
|
|
@@ -148,13 +168,17 @@ export default {
|
|
|
148
168
|
shadow: { type: Boolean },
|
|
149
169
|
tile: { type: Boolean },
|
|
150
170
|
multiple: { type: Boolean }, // Only for file uploads.
|
|
151
|
-
preview: { type: Boolean }, // Only for file uploads.
|
|
152
|
-
loading: { type: Boolean }
|
|
171
|
+
preview: { type: [Boolean, String], default: true }, // Only for file uploads.
|
|
172
|
+
loading: { type: [Boolean, Number], default: false }, // If a number is given, it will be the value of the progress.
|
|
173
|
+
showProgress: { type: [Boolean] }, // Only for file uploads.
|
|
174
|
+
// Allow syncing the files 1 way: prefilling a file is not possible.
|
|
175
|
+
// https://stackoverflow.com/questions/16365668/pre-populate-html-form-file-input
|
|
176
|
+
files: { type: Array }
|
|
153
177
|
// Props from mixin: name, disabled, readonly, required, tabindex, validators.
|
|
154
178
|
// Computed from mixin: inputName, isDisabled & isReadonly.
|
|
155
179
|
},
|
|
156
180
|
|
|
157
|
-
emits: ['input', 'update:modelValue', 'focus', 'blur', 'click:inner-icon-left', 'click:inner-icon-right'],
|
|
181
|
+
emits: ['input', 'update:modelValue', 'focus', 'blur', 'click:inner-icon-left', 'click:inner-icon-right', 'update:overallProgress'],
|
|
158
182
|
|
|
159
183
|
data () {
|
|
160
184
|
return {
|
|
@@ -183,29 +207,62 @@ export default {
|
|
|
183
207
|
const { input, focus, blur, ...listeners } = this.$attrs
|
|
184
208
|
return listeners
|
|
185
209
|
},
|
|
210
|
+
|
|
186
211
|
attrs () {
|
|
187
212
|
// eslint-disable-next-line no-unused-vars
|
|
188
213
|
const { class: Class, ...htmlAttrs } = this.$attrs
|
|
189
214
|
return htmlAttrs
|
|
190
215
|
},
|
|
216
|
+
|
|
191
217
|
hasValue () {
|
|
192
|
-
|
|
193
|
-
this.
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
218
|
+
switch (this.type) {
|
|
219
|
+
case 'file': return !!this.inputFiles.length
|
|
220
|
+
case 'number': return this.inputNumberError
|
|
221
|
+
case 'date':
|
|
222
|
+
case 'time':
|
|
223
|
+
return true
|
|
224
|
+
default:
|
|
225
|
+
return this.inputValue || this.inputValue === 0
|
|
226
|
+
}
|
|
199
227
|
},
|
|
200
228
|
|
|
201
229
|
hasLabel () {
|
|
202
230
|
return this.label || this.$slots.default
|
|
203
231
|
},
|
|
204
232
|
|
|
233
|
+
hasLoading () {
|
|
234
|
+
return ![undefined, false].includes(this.loading)
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
loadingValue () {
|
|
238
|
+
let value
|
|
239
|
+
if (typeof this.loading === 'number') value = this.loading
|
|
240
|
+
else if (this.loading) {
|
|
241
|
+
value = this.type === 'file' && this.overallFilesProgress ? this.overallFilesProgress : undefined
|
|
242
|
+
}
|
|
243
|
+
return value
|
|
244
|
+
},
|
|
245
|
+
|
|
205
246
|
showLabelInside () {
|
|
206
247
|
return !this.staticLabel || (!this.hasValue && !this.placeholder)
|
|
207
248
|
},
|
|
208
249
|
|
|
250
|
+
overallFilesProgress () {
|
|
251
|
+
const progress = this.inputFiles.reduce((total, file) => total + file.progress, 0)
|
|
252
|
+
const total = progress / this.inputFiles.length
|
|
253
|
+
this.$emit('update:overallProgress', this.inputFiles.length ? total : undefined)
|
|
254
|
+
|
|
255
|
+
return total
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
uploadInProgress () {
|
|
259
|
+
return this.overallFilesProgress > 0 && this.overallFilesProgress < 100
|
|
260
|
+
},
|
|
261
|
+
|
|
262
|
+
uploadComplete () {
|
|
263
|
+
return this.overallFilesProgress === 100
|
|
264
|
+
},
|
|
265
|
+
|
|
209
266
|
classes () {
|
|
210
267
|
return {
|
|
211
268
|
'w-input': true,
|
|
@@ -223,6 +280,12 @@ export default {
|
|
|
223
280
|
}
|
|
224
281
|
},
|
|
225
282
|
|
|
283
|
+
validationClasses () {
|
|
284
|
+
return this.isFocused && {
|
|
285
|
+
[this.valid === false ? 'error' : this.color]: this.color || this.valid === false
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
|
|
226
289
|
inputWrapClasses () {
|
|
227
290
|
return {
|
|
228
291
|
[this.valid === false ? 'error' : this.color]: this.color || this.valid === false,
|
|
@@ -235,7 +298,8 @@ export default {
|
|
|
235
298
|
'w-input__input-wrap--underline': !this.outline,
|
|
236
299
|
'w-input__input-wrap--shadow': this.shadow,
|
|
237
300
|
'w-input__input-wrap--no-padding': !this.outline && !this.bgColor && !this.shadow && !this.round,
|
|
238
|
-
'w-input__input-wrap--loading': this.loading
|
|
301
|
+
'w-input__input-wrap--loading': this.loading || (this.showProgress && this.uploadInProgress),
|
|
302
|
+
'w-input__input-wrap--upload-complete': this.uploadComplete
|
|
239
303
|
}
|
|
240
304
|
}
|
|
241
305
|
},
|
|
@@ -260,37 +324,44 @@ export default {
|
|
|
260
324
|
// For file input.
|
|
261
325
|
onFileChange (e) {
|
|
262
326
|
this.inputFiles = [...e.target.files].map(original => {
|
|
263
|
-
|
|
327
|
+
// `full` if there is no filename but only an extension.
|
|
328
|
+
const [, base = '', extension = '', full = ''] = original.name.match(/^(.*?)\.([^.]*)$|(.*)/)
|
|
329
|
+
|
|
264
330
|
const file = reactive({
|
|
265
331
|
name: original.name,
|
|
266
|
-
base,
|
|
332
|
+
base: base || full,
|
|
267
333
|
extension,
|
|
268
334
|
type: original.type,
|
|
269
335
|
size: original.size,
|
|
270
336
|
lastModified: original.lastModified,
|
|
271
337
|
preview: null,
|
|
272
|
-
progress: 0
|
|
338
|
+
progress: 0,
|
|
339
|
+
file: original
|
|
273
340
|
})
|
|
274
341
|
|
|
275
|
-
this.
|
|
342
|
+
this.readFile(original, file)
|
|
276
343
|
|
|
277
344
|
return file
|
|
278
345
|
})
|
|
279
346
|
this.$emit('update:modelValue', this.inputFiles)
|
|
280
347
|
this.$emit('input', this.inputFiles)
|
|
281
|
-
this.$emit('change', this.inputFiles)
|
|
282
348
|
},
|
|
283
349
|
|
|
284
350
|
// For file input.
|
|
285
|
-
|
|
351
|
+
readFile (original, file) {
|
|
286
352
|
const reader = new FileReader()
|
|
287
353
|
|
|
354
|
+
// If the preview prop is a string, the user is setting the preview to an icon and
|
|
355
|
+
// don't need the actual file preview.
|
|
356
|
+
const isPreviewAnIcon = typeof this.preview === 'string'
|
|
357
|
+
const isFileAnImage = original.type && original.type.startsWith('image/')
|
|
288
358
|
// Check if the file is an image and set a preview image.
|
|
289
|
-
if (
|
|
359
|
+
if (this.preview && !isPreviewAnIcon && isFileAnImage) {
|
|
290
360
|
reader.addEventListener('load', e => {
|
|
291
361
|
file.preview = e.target.result
|
|
292
362
|
})
|
|
293
363
|
}
|
|
364
|
+
else delete file.preview
|
|
294
365
|
|
|
295
366
|
// Used to display a spinner while the file is loading.
|
|
296
367
|
reader.addEventListener('progress', e => {
|
|
@@ -333,6 +404,8 @@ $inactive-color: #777;
|
|
|
333
404
|
&--file {
|
|
334
405
|
flex-wrap: nowrap;
|
|
335
406
|
align-items: flex-end;
|
|
407
|
+
|
|
408
|
+
span.fade-leave-to {position: absolute;}
|
|
336
409
|
}
|
|
337
410
|
|
|
338
411
|
// Input field wrapper.
|
|
@@ -362,7 +435,10 @@ $inactive-color: #777;
|
|
|
362
435
|
&--round {border-radius: 99em;}
|
|
363
436
|
&--tile {border-radius: initial;}
|
|
364
437
|
&--shadow {box-shadow: $box-shadow;}
|
|
365
|
-
&--loading
|
|
438
|
+
&--loading, &--upload-complete {
|
|
439
|
+
border-bottom-color: transparent;
|
|
440
|
+
flex-wrap: wrap;
|
|
441
|
+
}
|
|
366
442
|
&--loading ~ .w-progress {
|
|
367
443
|
height: 2px;
|
|
368
444
|
position: absolute;
|
|
@@ -371,7 +447,8 @@ $inactive-color: #777;
|
|
|
371
447
|
}
|
|
372
448
|
|
|
373
449
|
.w-input--focused & {border-color: currentColor;}
|
|
374
|
-
.w-input--focused &--loading
|
|
450
|
+
.w-input--focused &--loading,
|
|
451
|
+
.w-input--focused &--upload-complete {border-bottom-color: transparent;}
|
|
375
452
|
|
|
376
453
|
// Underline.
|
|
377
454
|
&--underline:after {
|
|
@@ -482,6 +559,8 @@ $inactive-color: #777;
|
|
|
482
559
|
margin-left: 4px;
|
|
483
560
|
max-height: 2em;
|
|
484
561
|
align-self: flex-end;
|
|
562
|
+
|
|
563
|
+
&.w-icon {margin-bottom: 4px;}
|
|
485
564
|
}
|
|
486
565
|
|
|
487
566
|
// 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.modelValue) this.toggleMenu({ type: 'click', target: this.activatorEl })
|
|
329
|
-
},
|
|
330
|
-
|
|
331
|
-
beforeUnmount () {
|
|
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
|
-
modelValue (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
|
},
|
|
@@ -3,7 +3,7 @@ component(
|
|
|
3
3
|
ref="formEl"
|
|
4
4
|
:is="formRegister ? 'w-form-element' : 'div'"
|
|
5
5
|
v-bind="formRegister && { validators, inputValue: rating, disabled: isDisabled, readonly: isReadonly }"
|
|
6
|
-
:valid
|
|
6
|
+
v-model:valid="valid"
|
|
7
7
|
@reset="$emit('update:modelValue', rating = null);$emit('input', null)"
|
|
8
8
|
:class="classes")
|
|
9
9
|
input(:id="inputName" :name="inputName" type="hidden" :value="rating")
|
|
@@ -329,7 +329,7 @@ export default {
|
|
|
329
329
|
setTimeout(() => {
|
|
330
330
|
const itemIndex = this.inputValue.length ? this.inputValue[0].index : 0 // Real index starts at 0.
|
|
331
331
|
// User visible index starts at 1.
|
|
332
|
-
this.$refs['w-list'].$el.querySelector(`#w-select-menu--${this._.uid}_item-${itemIndex + 1}`)
|
|
332
|
+
this.$refs['w-list'].$el.querySelector(`#w-select-menu--${this._.uid}_item-${itemIndex + 1}`)?.focus()
|
|
333
333
|
}, 100)
|
|
334
334
|
},
|
|
335
335
|
|
|
@@ -313,7 +313,7 @@ export default {
|
|
|
313
313
|
}
|
|
314
314
|
)
|
|
315
315
|
|
|
316
|
-
// Keep external `expanded-rows.sync` updated.
|
|
316
|
+
// Keep external `expanded-rows.sync` (Vue 2) or v-model:expanded-rows (Vue 3) updated.
|
|
317
317
|
this.$emit('update:expanded-rows', this.expandedRowsInternal)
|
|
318
318
|
}
|
|
319
319
|
|
|
@@ -342,7 +342,7 @@ export default {
|
|
|
342
342
|
}
|
|
343
343
|
)
|
|
344
344
|
|
|
345
|
-
// Keep external `selected-rows.sync` updated.
|
|
345
|
+
// Keep external `selected-rows.sync` (Vue 2) or v-model:selected-rows (Vue 3) updated.
|
|
346
346
|
this.$emit('update:selected-rows', this.selectedRowsInternal)
|
|
347
347
|
}
|
|
348
348
|
}
|