wave-ui 1.68.0 → 1.69.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 +125 -50
- package/dist/wave-ui.umd.js +1 -1
- package/package.json +14 -14
- package/src/wave-ui/components/w-button/button.vue +4 -1
- package/src/wave-ui/components/w-button/index.vue +2 -0
- package/src/wave-ui/components/w-checkbox.vue +11 -8
- package/src/wave-ui/components/w-checkboxes.vue +4 -1
- package/src/wave-ui/components/w-input.vue +2 -0
- package/src/wave-ui/components/w-list.vue +7 -7
- package/src/wave-ui/components/w-menu.vue +12 -1
- package/src/wave-ui/components/w-select.vue +30 -16
- package/src/wave-ui/components/w-table.vue +1 -1
- package/src/wave-ui/components/w-textarea.vue +1 -0
- package/src/wave-ui/components/w-toolbar.vue +1 -2
- package/src/wave-ui/components/w-tooltip.vue +44 -10
- package/src/wave-ui/core.js +36 -2
- package/src/wave-ui/mixins/detachable.js +27 -6
- package/src/wave-ui/scss/_base.scss +18 -0
- package/src/wave-ui/scss/_variables.scss +102 -8
- package/src/wave-ui/scss/index.scss +0 -1
- package/src/wave-ui/utils/dynamic-css.js +19 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wave-ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.69.0",
|
|
4
4
|
"description": "An emerging UI framework for Vue.js (2 & 3) with only the bright side. :sunny:",
|
|
5
5
|
"author": "Antoni Andre <antoniandre.web@gmail.com>",
|
|
6
6
|
"homepage": "https://antoniandre.github.io/wave-ui",
|
|
@@ -50,38 +50,38 @@
|
|
|
50
50
|
"publish-doc": "npm run build && npm run build-bundle && git add . && git commit -m 'Publish documentation on Github.' && git push && git push --tag"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@babel/core": "^7.
|
|
54
|
-
"@babel/eslint-parser": "^7.22.
|
|
53
|
+
"@babel/core": "^7.23.2",
|
|
54
|
+
"@babel/eslint-parser": "^7.22.15",
|
|
55
55
|
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
|
56
|
-
"@faker-js/faker": "^8.0
|
|
56
|
+
"@faker-js/faker": "^8.2.0",
|
|
57
57
|
"@mdi/font": "^6.9.96",
|
|
58
58
|
"@vitejs/plugin-vue": "^1.10.2",
|
|
59
|
-
"autoprefixer": "^10.4.
|
|
59
|
+
"autoprefixer": "^10.4.16",
|
|
60
60
|
"axios": "^0.21.4",
|
|
61
|
-
"core-js": "^3.
|
|
61
|
+
"core-js": "^3.33.2",
|
|
62
62
|
"eslint": "^7.32.0",
|
|
63
63
|
"eslint-config-standard": "^16.0.3",
|
|
64
|
-
"eslint-plugin-import": "^2.
|
|
64
|
+
"eslint-plugin-import": "^2.29.0",
|
|
65
65
|
"eslint-plugin-node": "^11.1.0",
|
|
66
66
|
"eslint-plugin-promise": "^5.2.0",
|
|
67
67
|
"eslint-plugin-vue": "^7.20.0",
|
|
68
68
|
"font-awesome": "^4.7.0",
|
|
69
69
|
"ghspa": "^1.0.0",
|
|
70
|
-
"gsap": "^3.
|
|
70
|
+
"gsap": "^3.12.2",
|
|
71
71
|
"ionicons": "^4.6.3",
|
|
72
72
|
"material-design-icons": "^3.0.1",
|
|
73
|
-
"postcss": "^8.4.
|
|
73
|
+
"postcss": "^8.4.31",
|
|
74
74
|
"pug": "^3.0.2",
|
|
75
75
|
"rollup-plugin-delete": "^2.0.0",
|
|
76
|
-
"sass": "^1.
|
|
77
|
-
"simple-syntax-highlighter": "^1.
|
|
76
|
+
"sass": "^1.69.5",
|
|
77
|
+
"simple-syntax-highlighter": "^1.6.2",
|
|
78
78
|
"splitpanes": "^2.4.1",
|
|
79
|
-
"vite": "^2.9.
|
|
79
|
+
"vite": "^2.9.16",
|
|
80
80
|
"vite-plugin-vue2": "^1.9.3",
|
|
81
|
-
"vue": "^2.7.
|
|
81
|
+
"vue": "^2.7.15",
|
|
82
82
|
"vue-router": "^3.6.5",
|
|
83
83
|
"vue-svg-loader": "^0.16.0",
|
|
84
|
-
"vue-template-compiler": "^2.7.
|
|
84
|
+
"vue-template-compiler": "^2.7.15",
|
|
85
85
|
"vueperslides": "^2.16.0",
|
|
86
86
|
"vuex": "^3.6.2"
|
|
87
87
|
}
|
|
@@ -281,7 +281,10 @@ $spinner-size: 40;
|
|
|
281
281
|
|
|
282
282
|
// Active state.
|
|
283
283
|
&:active {transform: scale(1.02);}
|
|
284
|
-
&:active:before {
|
|
284
|
+
&:active:before {
|
|
285
|
+
opacity: 0.3;
|
|
286
|
+
@include default-transition($fast-transition-duration);
|
|
287
|
+
}
|
|
285
288
|
&--dark:active:before, &.primary--bg:active:before {opacity: 0.35;}
|
|
286
289
|
|
|
287
290
|
// Disable visual feedback on loading and disabled buttons.
|
|
@@ -105,8 +105,9 @@ export default {
|
|
|
105
105
|
methods: {
|
|
106
106
|
onInput () {
|
|
107
107
|
this.isChecked = !this.isChecked
|
|
108
|
-
this
|
|
109
|
-
this.$emit('
|
|
108
|
+
const returnValue = this.isChecked && this.returnValue !== undefined ? this.returnValue : this.isChecked
|
|
109
|
+
this.$emit('update:modelValue', returnValue)
|
|
110
|
+
this.$emit('input', returnValue)
|
|
110
111
|
|
|
111
112
|
if (!this.noRipple) {
|
|
112
113
|
if (this.isChecked) {
|
|
@@ -143,11 +144,8 @@ $inactive-color: #666;
|
|
|
143
144
|
vertical-align: middle;
|
|
144
145
|
// Contain the hidden radio button, so browser doesn't pan to it when outside of the screen.
|
|
145
146
|
position: relative;
|
|
146
|
-
cursor: pointer;
|
|
147
147
|
-webkit-tap-highlight-color: transparent;
|
|
148
148
|
|
|
149
|
-
&--disabled {cursor: not-allowed;}
|
|
150
|
-
|
|
151
149
|
// The hidden real checkbox.
|
|
152
150
|
input[type="checkbox"] {
|
|
153
151
|
position: absolute;
|
|
@@ -165,8 +163,10 @@ $inactive-color: #666;
|
|
|
165
163
|
flex: 0 0 auto; // Prevent stretching width or height.
|
|
166
164
|
align-items: center;
|
|
167
165
|
justify-content: center;
|
|
168
|
-
cursor:
|
|
166
|
+
cursor: pointer;
|
|
169
167
|
z-index: 0;
|
|
168
|
+
|
|
169
|
+
.w-checkbox--disabled & {cursor: not-allowed;}
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
// The checkmark - visible when checked.
|
|
@@ -260,10 +260,13 @@ $inactive-color: #666;
|
|
|
260
260
|
&__label {
|
|
261
261
|
display: flex;
|
|
262
262
|
align-items: center;
|
|
263
|
-
cursor:
|
|
263
|
+
cursor: pointer;
|
|
264
264
|
user-select: none;
|
|
265
265
|
|
|
266
|
-
.w-checkbox--disabled & {
|
|
266
|
+
.w-checkbox--disabled & {
|
|
267
|
+
cursor: not-allowed;
|
|
268
|
+
opacity: 0.7;
|
|
269
|
+
}
|
|
267
270
|
}
|
|
268
271
|
}
|
|
269
272
|
|
|
@@ -38,6 +38,9 @@ export default {
|
|
|
38
38
|
props: {
|
|
39
39
|
items: { type: Array, required: true }, // All the possible options.
|
|
40
40
|
value: { type: Array }, // v-model on selected option.
|
|
41
|
+
// If true, the returnValue set on each w-checkboxes item will be returned once the checkbox is
|
|
42
|
+
// checked. If false & by default, the return value of the w-checkboxes is an array of booleans.
|
|
43
|
+
returnValues: { type: Boolean },
|
|
41
44
|
labelOnLeft: { type: Boolean },
|
|
42
45
|
itemLabelKey: { type: String, default: 'label' },
|
|
43
46
|
itemValueKey: { type: String, default: 'value' },
|
|
@@ -92,7 +95,7 @@ export default {
|
|
|
92
95
|
|
|
93
96
|
toggleCheck (checkbox, isChecked) {
|
|
94
97
|
checkbox._isChecked = isChecked
|
|
95
|
-
const selection = this.checkboxItems.filter(item => item._isChecked).map(item => item.value)
|
|
98
|
+
const selection = this.checkboxItems.filter(item => item._isChecked).map(item => this.returnValues ? item.returnValue : item.value)
|
|
96
99
|
|
|
97
100
|
this.$emit('update:modelValue', selection)
|
|
98
101
|
this.$emit('input', selection)
|
|
@@ -61,6 +61,7 @@ component(
|
|
|
61
61
|
@change="onFileChange"
|
|
62
62
|
:multiple="multiple || null"
|
|
63
63
|
v-bind="attrs"
|
|
64
|
+
:disabled="isDisabled || null"
|
|
64
65
|
:data-progress="overallFilesProgress /* Needed to emit the overallProgress. */")
|
|
65
66
|
transition-group.w-input__input.w-input__input--file(
|
|
66
67
|
tag="label"
|
|
@@ -558,6 +559,7 @@ $inactive-color: #777;
|
|
|
558
559
|
&__label {
|
|
559
560
|
transition: color $transition-duration;
|
|
560
561
|
cursor: pointer;
|
|
562
|
+
user-select: none;
|
|
561
563
|
|
|
562
564
|
&--left {margin-right: 2 * $base-increment;}
|
|
563
565
|
&--right {margin-left: 2 * $base-increment;}
|
|
@@ -229,8 +229,8 @@ export default {
|
|
|
229
229
|
'w-list__item-label--focused': item._focused,
|
|
230
230
|
'w-list__item-label--hoverable': this.hover,
|
|
231
231
|
'w-list__item-label--selectable': this.isSelectable,
|
|
232
|
-
[item.
|
|
233
|
-
[this.SelectionColor]: item._selected && !item.
|
|
232
|
+
[item[this.itemColorKey]]: !!item[this.itemColorKey],
|
|
233
|
+
[this.SelectionColor]: item._selected && !item[this.itemColorKey] && this.SelectionColor,
|
|
234
234
|
[item[this.itemClassKey] || this.itemClass]: item[this.itemClassKey] || this.itemClass
|
|
235
235
|
}
|
|
236
236
|
},
|
|
@@ -406,11 +406,11 @@ export default {
|
|
|
406
406
|
// Reset the selections when single selection allowed for w-select.
|
|
407
407
|
if (!this.isMultipleSelect) this.listItems.forEach(item => (item._selected = false))
|
|
408
408
|
|
|
409
|
-
this.checkSelection(selection) // Create an array with the selected values.
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
409
|
+
const selectedItems = this.checkSelection(selection) // Create an array with the selected values.
|
|
410
|
+
// Update which items are selected or not.
|
|
411
|
+
this.listItems.forEach(item => {
|
|
412
|
+
item._selected = selectedItems.find(val => item._value === val) !== undefined
|
|
413
|
+
})
|
|
414
414
|
}
|
|
415
415
|
},
|
|
416
416
|
|
|
@@ -79,6 +79,7 @@ export default {
|
|
|
79
79
|
contentClass: { type: [String, Object, Array] },
|
|
80
80
|
arrow: { type: Boolean }, // The small triangle pointing toward the activator.
|
|
81
81
|
minWidth: { type: [Number, String] }, // can be like: `40`, `5em`, `activator`.
|
|
82
|
+
maxWidth: { type: [Number, String] }, // can be like: `40`, `5em`, `activator`.
|
|
82
83
|
overlay: { type: Boolean },
|
|
83
84
|
overlayClass: { type: [String, Object, Array] },
|
|
84
85
|
overlayProps: { type: Object }, // Allow passing down an object of props to the w-overlay component.
|
|
@@ -132,6 +133,11 @@ export default {
|
|
|
132
133
|
else return isNaN(this.minWidth) ? this.minWidth : (this.minWidth ? `${this.minWidth}px` : 0)
|
|
133
134
|
},
|
|
134
135
|
|
|
136
|
+
menuMaxWidth () {
|
|
137
|
+
if (this.maxWidth === 'activator') return this.activatorWidth ? `${this.activatorWidth}px` : 0
|
|
138
|
+
else return isNaN(this.maxWidth) ? this.maxWidth : (this.maxWidth ? `${this.maxWidth}px` : 0)
|
|
139
|
+
},
|
|
140
|
+
|
|
135
141
|
menuClasses () {
|
|
136
142
|
return objectifyClasses(this.menuClass)
|
|
137
143
|
},
|
|
@@ -174,7 +180,8 @@ export default {
|
|
|
174
180
|
top: (this.detachableCoords.top && `${~~this.detachableCoords.top}px`) || null,
|
|
175
181
|
left: (this.detachableCoords.left && `${~~this.detachableCoords.left}px`) || null,
|
|
176
182
|
minWidth: (this.minWidth && this.menuMinWidth) || null,
|
|
177
|
-
|
|
183
|
+
maxWidth: (this.maxWidth && this.menuMaxWidth) || null,
|
|
184
|
+
'--w-menu-bg-color': this.arrow && (this.$waveui.colors[this.bgColor] || 'rgb(var(--w-base-bg-color-rgb))')
|
|
178
185
|
}
|
|
179
186
|
},
|
|
180
187
|
|
|
@@ -254,6 +261,10 @@ export default {
|
|
|
254
261
|
* even while hovering the menu.
|
|
255
262
|
*/
|
|
256
263
|
async close (force = false) {
|
|
264
|
+
// The user may open and close the detachable so fast (like when toggling on hover) that it
|
|
265
|
+
// should not show up at all. This cancels the opening timer (if there is a set delay prop).
|
|
266
|
+
this.openTimeout = clearTimeout(this.openTimeout)
|
|
267
|
+
|
|
257
268
|
// Might be already closed.
|
|
258
269
|
// E.g. showOnHover & hideOnMenuClick: on click, force hide then mouseleave is also firing.
|
|
259
270
|
if (!this.detachableVisible) return
|
|
@@ -10,13 +10,13 @@ component(
|
|
|
10
10
|
template(v-if="labelPosition === 'left'")
|
|
11
11
|
label.w-select__label.w-select__label--left.w-form-el-shakable(
|
|
12
12
|
v-if="$slots.default || label"
|
|
13
|
-
|
|
13
|
+
@click="$refs['selection-input'].click()"
|
|
14
14
|
:class="labelClasses")
|
|
15
15
|
slot {{ label }}
|
|
16
16
|
|
|
17
17
|
w-menu(
|
|
18
18
|
v-model="showMenu"
|
|
19
|
-
@close="
|
|
19
|
+
@close="closeMenu"
|
|
20
20
|
:menu-class="`w-select__menu ${menuClass || ''}`"
|
|
21
21
|
transition="slide-fade-down"
|
|
22
22
|
:append-to="(menuProps || {}).appendTo !== undefined ? (menuProps || {}).appendTo : undefined"
|
|
@@ -47,7 +47,6 @@ component(
|
|
|
47
47
|
@focus="!isDisabled && !isReadonly && onFocus($event)"
|
|
48
48
|
@blur="onBlur"
|
|
49
49
|
@keydown="!isDisabled && !isReadonly && onKeydown($event)"
|
|
50
|
-
:id="`w-select--${_uid}`"
|
|
51
50
|
:class="{ 'w-select__selection--placeholder': !$scopedSlots.selection && !selectionString && placeholder }"
|
|
52
51
|
:disabled="isDisabled || null"
|
|
53
52
|
readonly
|
|
@@ -64,7 +63,6 @@ component(
|
|
|
64
63
|
template(v-if="labelPosition === 'inside' && showLabelInside")
|
|
65
64
|
label.w-select__label.w-select__label--inside.w-form-el-shakable(
|
|
66
65
|
v-if="$slots.default || label"
|
|
67
|
-
:for="`w-select--${_uid}`"
|
|
68
66
|
:class="labelClasses")
|
|
69
67
|
slot {{ label }}
|
|
70
68
|
w-icon.w-select__icon.w-select__icon--inner-right(
|
|
@@ -77,7 +75,7 @@ component(
|
|
|
77
75
|
@item-click="$emit('item-click', $event)"
|
|
78
76
|
@item-select="onListItemSelect"
|
|
79
77
|
@keydown:enter="noUnselect && !multiple && closeMenu()"
|
|
80
|
-
@keydown:escape="showMenu && (
|
|
78
|
+
@keydown:escape="showMenu && (showMenu = false) /* Will call closeMenu() from w-menu(@close). */"
|
|
81
79
|
:value="inputValue"
|
|
82
80
|
:items="selectItems"
|
|
83
81
|
:multiple="multiple"
|
|
@@ -103,7 +101,7 @@ component(
|
|
|
103
101
|
template(v-if="labelPosition === 'right'")
|
|
104
102
|
label.w-select__label.w-select__label--right.w-form-el-shakable(
|
|
105
103
|
v-if="$slots.default || label"
|
|
106
|
-
|
|
104
|
+
@click="$refs['selection-input'].click()"
|
|
107
105
|
:class="labelClasses")
|
|
108
106
|
slot {{ label }}
|
|
109
107
|
</template>
|
|
@@ -252,7 +250,7 @@ export default {
|
|
|
252
250
|
if (!e.metaKey && !e.ctrlKey && e.keyCode !== 9) e.preventDefault()
|
|
253
251
|
|
|
254
252
|
if (e.keyCode === 27 && this.showMenu) this.closeMenu() // Escape.
|
|
255
|
-
else if (e.keyCode
|
|
253
|
+
else if ([13, 32].includes(e.keyCode)) this.openMenu() // Enter or Space.
|
|
256
254
|
|
|
257
255
|
// Up & down arrows.
|
|
258
256
|
else if ([38, 40].includes(e.keyCode)) {
|
|
@@ -272,7 +270,25 @@ export default {
|
|
|
272
270
|
index = (index + items.length + direction) % items.length
|
|
273
271
|
}
|
|
274
272
|
|
|
275
|
-
|
|
273
|
+
// If the current item is disabled, find the next one enabled (forward or backward).
|
|
274
|
+
let allItemsAreDisabled = false
|
|
275
|
+
if (items[index].disabled) {
|
|
276
|
+
const direction = e.keyCode === 38 ? -1 : 1 // Prev or next.
|
|
277
|
+
|
|
278
|
+
// Modulo to prevent out of range; + items.length to also work with negative values.
|
|
279
|
+
let newIndex = (index + direction + items.length) % items.length
|
|
280
|
+
const itemsCount = items.length
|
|
281
|
+
let loop = 0 // While-safety: will always end at least after 1 full array cycle.
|
|
282
|
+
while (loop < itemsCount && items[newIndex].disabled) {
|
|
283
|
+
// Circle through the array of items forward or backward, and reloop when out of range.
|
|
284
|
+
newIndex = (newIndex + items.length + direction) % items.length
|
|
285
|
+
loop++
|
|
286
|
+
}
|
|
287
|
+
if (loop >= itemsCount) allItemsAreDisabled = true
|
|
288
|
+
index = newIndex
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (!allItemsAreDisabled) this.onInput(items[index])
|
|
276
292
|
}
|
|
277
293
|
}
|
|
278
294
|
},
|
|
@@ -321,7 +337,7 @@ export default {
|
|
|
321
337
|
|
|
322
338
|
return items.map(item => {
|
|
323
339
|
let value = item
|
|
324
|
-
if (typeof item === 'object') {
|
|
340
|
+
if (item && typeof item === 'object') { // `null` is also an object!
|
|
325
341
|
value = item[this.itemValueKey] !== undefined ? item[this.itemValueKey] : (item[this.itemLabelKey] !== undefined ? item[this.itemLabelKey] : item)
|
|
326
342
|
}
|
|
327
343
|
|
|
@@ -508,8 +524,8 @@ export default {
|
|
|
508
524
|
-webkit-tap-highlight-color: transparent;
|
|
509
525
|
}
|
|
510
526
|
|
|
511
|
-
&--inner-left {left:
|
|
512
|
-
&--inner-right {right:
|
|
527
|
+
&--inner-left {left: $base-increment;}
|
|
528
|
+
&--inner-right {right: $base-increment;}
|
|
513
529
|
.w-select--no-padding &--inner-left {left: 1px;}
|
|
514
530
|
.w-select--no-padding &--inner-right {right: 1px;}
|
|
515
531
|
|
|
@@ -526,6 +542,7 @@ export default {
|
|
|
526
542
|
align-items: center;
|
|
527
543
|
transition: color $transition-duration;
|
|
528
544
|
cursor: pointer;
|
|
545
|
+
user-select: none;
|
|
529
546
|
|
|
530
547
|
&--left {margin-right: 2 * $base-increment;}
|
|
531
548
|
&--right {margin-left: 2 * $base-increment;}
|
|
@@ -553,7 +570,7 @@ export default {
|
|
|
553
570
|
transform: translateY(-50%);
|
|
554
571
|
pointer-events: none;
|
|
555
572
|
|
|
556
|
-
.w-select--inner-icon-right & {padding-right:
|
|
573
|
+
.w-select--inner-icon-right & {padding-right: 26px;}
|
|
557
574
|
|
|
558
575
|
.w-select--no-padding & {
|
|
559
576
|
left: 0;
|
|
@@ -570,8 +587,7 @@ export default {
|
|
|
570
587
|
transition: $transition-duration ease;
|
|
571
588
|
}
|
|
572
589
|
|
|
573
|
-
//
|
|
574
|
-
.w-select--focused.w-select--floating-label &,
|
|
590
|
+
// Move label with underline style.
|
|
575
591
|
.w-select--open.w-select--floating-label &,
|
|
576
592
|
.w-select--filled.w-select--floating-label &,
|
|
577
593
|
.w-select--has-placeholder.w-select--floating-label & {
|
|
@@ -582,13 +598,11 @@ export default {
|
|
|
582
598
|
transform: translateY(-160%) scale(0.85);
|
|
583
599
|
}
|
|
584
600
|
// Move label with outline style or with shadow.
|
|
585
|
-
.w-select--focused.w-select--floating-label .w-select__selection-wrap--box &,
|
|
586
601
|
.w-select--open.w-select--floating-label .w-select__selection-wrap--box &,
|
|
587
602
|
.w-select--filled.w-select--floating-label .w-select__selection-wrap--box &,
|
|
588
603
|
.w-select--has-placeholder.w-select--floating-label .w-select__selection-wrap--box & {
|
|
589
604
|
transform: translateY(-180%) scale(0.85);
|
|
590
605
|
}
|
|
591
|
-
.w-select--focused.w-select--floating-label.w-select--inner-icon-left &,
|
|
592
606
|
.w-select--open.w-select--floating-label.w-select--inner-icon-left &,
|
|
593
607
|
.w-select--filled.w-select--floating-label.w-select--inner-icon-left & {left: 0;}
|
|
594
608
|
// Chrome & Safari - Must remain in a separated rule as Firefox discard the whole rule seeing -webkit-.
|
|
@@ -63,7 +63,7 @@ export default {
|
|
|
63
63
|
.w-toolbar {
|
|
64
64
|
position: relative;
|
|
65
65
|
display: flex;
|
|
66
|
-
flex:
|
|
66
|
+
flex: 0 1 auto; // No grow, so it doesn't stretch vertically in flex column.
|
|
67
67
|
align-items: center;
|
|
68
68
|
padding: (2 * $base-increment) (3 * $base-increment);
|
|
69
69
|
background-color: #fff;
|
|
@@ -88,7 +88,6 @@ export default {
|
|
|
88
88
|
&--vertical {
|
|
89
89
|
padding: (2 * $base-increment);
|
|
90
90
|
flex-direction: column;
|
|
91
|
-
flex-grow: 0;
|
|
92
91
|
flex-shrink: 0;
|
|
93
92
|
}
|
|
94
93
|
|
|
@@ -42,7 +42,14 @@ export default {
|
|
|
42
42
|
transition: { type: String },
|
|
43
43
|
tooltipClass: { type: [String, Object, Array] },
|
|
44
44
|
persistent: { type: Boolean },
|
|
45
|
-
delay: { type: Number }
|
|
45
|
+
delay: { type: Number },
|
|
46
|
+
caption: { type: Boolean }, // Apply the caption class and style (grey, italic, small).
|
|
47
|
+
xs: { type: Boolean },
|
|
48
|
+
sm: { type: Boolean },
|
|
49
|
+
md: { type: Boolean },
|
|
50
|
+
lg: { type: Boolean },
|
|
51
|
+
xl: { type: Boolean },
|
|
52
|
+
enableTouch: { type: Boolean }
|
|
46
53
|
// Other props in the detachable mixin:
|
|
47
54
|
// appendTo, fixed, top, bottom, left, right, alignTop, alignBottom, alignLeft,
|
|
48
55
|
// alignRight, noPosition, zIndex, activator.
|
|
@@ -81,6 +88,17 @@ export default {
|
|
|
81
88
|
return this.transition || `w-tooltip-slide-fade-${direction}`
|
|
82
89
|
},
|
|
83
90
|
|
|
91
|
+
size () {
|
|
92
|
+
return (
|
|
93
|
+
(this.xs && 'xs') ||
|
|
94
|
+
(this.sm && 'sm') ||
|
|
95
|
+
(this.sm && 'md') ||
|
|
96
|
+
(this.lg && 'lg') ||
|
|
97
|
+
(this.xl && 'xl') ||
|
|
98
|
+
(this.caption ? 'sm' : 'md') // if no size is set put md by default, or sm if caption is on.
|
|
99
|
+
)
|
|
100
|
+
},
|
|
101
|
+
|
|
84
102
|
classes () {
|
|
85
103
|
return {
|
|
86
104
|
[this.color]: this.color,
|
|
@@ -90,6 +108,8 @@ export default {
|
|
|
90
108
|
[`w-tooltip--align-${this.alignment}`]: !this.noPosition && this.alignment,
|
|
91
109
|
'w-tooltip--tile': this.tile,
|
|
92
110
|
'w-tooltip--round': this.round,
|
|
111
|
+
caption: this.caption,
|
|
112
|
+
[`size--${this.size}`]: true,
|
|
93
113
|
'w-tooltip--shadow': this.shadow,
|
|
94
114
|
'w-tooltip--fixed': this.fixed,
|
|
95
115
|
'w-tooltip--no-border': this.noBorder || this.bgColor,
|
|
@@ -103,17 +123,21 @@ export default {
|
|
|
103
123
|
zIndex: this.zIndex || this.zIndex === 0 || null,
|
|
104
124
|
top: (this.detachableCoords.top && `${~~this.detachableCoords.top}px`) || null,
|
|
105
125
|
left: (this.detachableCoords.left && `${~~this.detachableCoords.left}px`) || null,
|
|
106
|
-
'--w-tooltip-bg-color': this.$waveui.colors[this.bgColor || '
|
|
126
|
+
'--w-tooltip-bg-color': this.$waveui.colors[this.bgColor] || 'rgb(var(--w-base-bg-color-rgb))'
|
|
107
127
|
}
|
|
108
128
|
},
|
|
109
129
|
|
|
110
130
|
activatorEventHandlers () {
|
|
111
131
|
let handlers = {}
|
|
112
|
-
|
|
113
|
-
|
|
132
|
+
|
|
133
|
+
// Check the window exists: SSR-proof.
|
|
134
|
+
const isTouchDevice = typeof window !== 'undefined' && 'ontouchstart' in window
|
|
135
|
+
|
|
136
|
+
// Toggling tooltip on mouseenter/mouseout (by default), and also show on focus, hide on blur.
|
|
137
|
+
if (!this.showOnClick && !isTouchDevice) {
|
|
114
138
|
handlers = {
|
|
115
|
-
focus: this.
|
|
116
|
-
blur: this.
|
|
139
|
+
focus: this.open,
|
|
140
|
+
blur: this.close,
|
|
117
141
|
mouseenter: e => {
|
|
118
142
|
this.hoveringActivator = true
|
|
119
143
|
this.open(e)
|
|
@@ -123,10 +147,10 @@ export default {
|
|
|
123
147
|
this.close()
|
|
124
148
|
}
|
|
125
149
|
}
|
|
126
|
-
|
|
127
|
-
// Check the window exists: SSR-proof.
|
|
128
|
-
if (typeof window !== 'undefined' && 'ontouchstart' in window) handlers.click = this.toggle
|
|
129
150
|
}
|
|
151
|
+
// Only bind a click event on mobile, or if showOnClick is set.
|
|
152
|
+
else if (this.enableTouch || this.showOnClick) handlers = { click: this.toggle }
|
|
153
|
+
|
|
130
154
|
return handlers
|
|
131
155
|
}
|
|
132
156
|
},
|
|
@@ -146,8 +170,12 @@ export default {
|
|
|
146
170
|
// ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
147
171
|
toggle (e) {
|
|
148
172
|
let shouldShowTooltip = this.detachableVisible
|
|
173
|
+
|
|
174
|
+
// For touch devices.
|
|
149
175
|
if (typeof window !== 'undefined' && 'ontouchstart' in window) {
|
|
150
|
-
|
|
176
|
+
// disable tooltip opening for mouseenter activation.
|
|
177
|
+
if (!this.enableTouch && !this.showOnClick) shouldShowTooltip = false
|
|
178
|
+
else shouldShowTooltip = !shouldShowTooltip
|
|
151
179
|
}
|
|
152
180
|
else if (e.type === 'click' && this.showOnClick) shouldShowTooltip = !shouldShowTooltip
|
|
153
181
|
else if (['mouseenter', 'focus'].includes(e.type) && !this.showOnClick) shouldShowTooltip = true
|
|
@@ -226,6 +254,12 @@ export default {
|
|
|
226
254
|
&--left {margin-left: -3 * $base-increment;}
|
|
227
255
|
&--right {margin-left: 3 * $base-increment;}
|
|
228
256
|
|
|
257
|
+
&.size--xs {font-size: 0.75rem;}
|
|
258
|
+
&.size--sm {font-size: 0.83rem;}
|
|
259
|
+
&.size--md {font-size: 0.9rem;}
|
|
260
|
+
&.size--lg {font-size: 1rem;}
|
|
261
|
+
&.size--xl {font-size: 1.1rem;}
|
|
262
|
+
|
|
229
263
|
&--custom-transition {transform: none;}
|
|
230
264
|
|
|
231
265
|
// Tooltip without border.
|
package/src/wave-ui/core.js
CHANGED
|
@@ -1,17 +1,51 @@
|
|
|
1
|
+
import { consoleWarn } from './utils/console'
|
|
1
2
|
import { mergeConfig } from './utils/config'
|
|
2
3
|
import NotificationManager from './utils/notification-manager'
|
|
3
4
|
import { colorPalette, generateColorShades, flattenColors } from './utils/colors'
|
|
4
|
-
|
|
5
|
+
|
|
6
|
+
// const detectOSDarkMode = $waveui => {
|
|
7
|
+
// const matchMedia = window.matchMedia('(prefers-color-scheme: dark)')
|
|
8
|
+
// $waveui.preferredTheme = matchMedia.matches ? 'dark' : 'light'
|
|
9
|
+
// $waveui.switchTheme($waveui.preferredTheme)
|
|
10
|
+
|
|
11
|
+
// matchMedia.addEventListener('change', event => {
|
|
12
|
+
// $waveui.preferredTheme = event.matches ? 'dark' : 'light'
|
|
13
|
+
// $waveui.switchTheme($waveui.preferredTheme)
|
|
14
|
+
// })
|
|
15
|
+
// }
|
|
5
16
|
|
|
6
17
|
/**
|
|
7
18
|
* Inject presets into a Vue component props defaults before its registration into the app.
|
|
19
|
+
* If a preset is not found in the given component props, try to find it in its mixins, if any.
|
|
8
20
|
*
|
|
21
|
+
* @todo remove mixins-related code when stopping support for Vue 2.
|
|
9
22
|
* @param {Object} component the Vue component to inject presets into.
|
|
10
23
|
* @param {Object} presets the presets to inject. E.g. `{ bgColor: 'green' }`.
|
|
11
24
|
*/
|
|
12
25
|
const injectPresets = (component, presets) => {
|
|
13
26
|
for (const preset in presets) {
|
|
14
|
-
|
|
27
|
+
// If we don't have the prop output a warning and continue.
|
|
28
|
+
if (!component.props?.[preset]) {
|
|
29
|
+
let foundProp = false
|
|
30
|
+
// Check to see if the prop exists on a mixin when it doesn't exist on the component.
|
|
31
|
+
// @todo: remove this check when there is no more Vue 2 and mixins: mixins are now deprecated.
|
|
32
|
+
if (Array.isArray(component.mixins) && component.mixins.length) {
|
|
33
|
+
// Loop through the array of mixin, and if we find the prop in one, update its default value.
|
|
34
|
+
for (const mixin of component.mixins) {
|
|
35
|
+
if (mixin?.props?.[preset]) {
|
|
36
|
+
mixin.props[preset].default = presets[preset]
|
|
37
|
+
foundProp = true
|
|
38
|
+
break
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// If the given prop (= preset) is still not found in the mixins props raise warning.
|
|
43
|
+
if (!foundProp) consoleWarn(`Attempting to set a preset on a prop that doesn't exist: \`${component.name}.${preset}\`.`)
|
|
44
|
+
continue // Continue to the next preset.
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
else component.props[preset].default = presets[preset]
|
|
15
49
|
}
|
|
16
50
|
}
|
|
17
51
|
|
|
@@ -22,7 +22,8 @@ export default {
|
|
|
22
22
|
noPosition: { type: Boolean },
|
|
23
23
|
zIndex: { type: [Number, String, Boolean] },
|
|
24
24
|
// Optionally designate an external activator.
|
|
25
|
-
|
|
25
|
+
// The activator can be a DOM string selector, a ref or a DOM node.
|
|
26
|
+
activator: { type: [String, Object] }
|
|
26
27
|
},
|
|
27
28
|
|
|
28
29
|
inject: {
|
|
@@ -35,7 +36,11 @@ export default {
|
|
|
35
36
|
// as is in an array so we can delete them on destroy.
|
|
36
37
|
// This only applies to the activatorEventHandlers, the other events listeners can be removed
|
|
37
38
|
// normally.
|
|
38
|
-
docEventListenersHandlers: []
|
|
39
|
+
docEventListenersHandlers: [],
|
|
40
|
+
// The user may open and close the detachable so fast (like when toggling on hover) that it
|
|
41
|
+
// should not show up at all. Keep the ability to cancel the opening timer (if there is a set
|
|
42
|
+
// delay prop).
|
|
43
|
+
openTimeout: null
|
|
39
44
|
}),
|
|
40
45
|
|
|
41
46
|
computed: {
|
|
@@ -111,6 +116,13 @@ export default {
|
|
|
111
116
|
(['left', 'right'].includes(this.position) && this.alignBottom && 'bottom') ||
|
|
112
117
|
''
|
|
113
118
|
)
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
shouldShowOnClick () {
|
|
122
|
+
// For props simplicity, the w-tooltip component has the `showOnHover` prop,
|
|
123
|
+
// whereas the w-menu has `showOnClick`.
|
|
124
|
+
return (this.$options.props.showOnHover && !this.showOnHover) ||
|
|
125
|
+
(this.$options.props.showOnClick && this.showOnClick)
|
|
114
126
|
}
|
|
115
127
|
},
|
|
116
128
|
|
|
@@ -119,7 +131,10 @@ export default {
|
|
|
119
131
|
async open (e) {
|
|
120
132
|
// A tiny delay may help positioning the detachable correctly in case of multiple activators
|
|
121
133
|
// with different menu contents.
|
|
122
|
-
if (this.delay) await new Promise(resolve => setTimeout(resolve, this.delay))
|
|
134
|
+
if (this.delay) await new Promise(resolve => (this.openTimeout = setTimeout(resolve, this.delay)))
|
|
135
|
+
|
|
136
|
+
// Cancel opening if the timeout has been cancelled by blur event (when going fast).
|
|
137
|
+
if (this.delay && !this.openTimeout) return
|
|
123
138
|
|
|
124
139
|
this.detachableVisible = true
|
|
125
140
|
|
|
@@ -336,7 +351,7 @@ export default {
|
|
|
336
351
|
else {
|
|
337
352
|
this.$nextTick(() => {
|
|
338
353
|
if (this.activator) this.bindActivatorEvents()
|
|
339
|
-
if (this.value) this.
|
|
354
|
+
if (this.value) this.open({ target: this.activatorEl })
|
|
340
355
|
})
|
|
341
356
|
}
|
|
342
357
|
|
|
@@ -346,7 +361,10 @@ export default {
|
|
|
346
361
|
wrapper.parentNode.insertBefore(this.overlayEl, wrapper)
|
|
347
362
|
}
|
|
348
363
|
|
|
349
|
-
if (this.value && this.activator)
|
|
364
|
+
if (this.value && this.activator) {
|
|
365
|
+
this.toggle({ type: this.shouldShowOnClick ? 'click' : 'mouseenter', target: this.activatorEl })
|
|
366
|
+
}
|
|
367
|
+
else if (this.value) this.open({ target: this.activatorEl })
|
|
350
368
|
},
|
|
351
369
|
|
|
352
370
|
beforeDestroy () {
|
|
@@ -368,7 +386,10 @@ export default {
|
|
|
368
386
|
|
|
369
387
|
watch: {
|
|
370
388
|
value (bool) {
|
|
371
|
-
if (!!bool !== this.detachableVisible)
|
|
389
|
+
if (!!bool !== this.detachableVisible) {
|
|
390
|
+
if (bool) this.open({ target: this.activatorEl })
|
|
391
|
+
else this.close()
|
|
392
|
+
}
|
|
372
393
|
},
|
|
373
394
|
appendTo () {
|
|
374
395
|
this.removeFromDOM()
|