wave-ui 2.27.0 → 2.30.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 +457 -456
- package/dist/wave-ui.umd.js +1 -1
- package/package.json +10 -10
- package/src/wave-ui/components/w-accordion.vue +6 -4
- package/src/wave-ui/components/w-alert.vue +0 -1
- package/src/wave-ui/components/w-app.vue +1 -1
- package/src/wave-ui/components/w-confirm.vue +35 -10
- package/src/wave-ui/components/w-flex.vue +2 -0
- package/src/wave-ui/components/w-input.vue +21 -3
- package/src/wave-ui/components/w-menu.vue +75 -210
- package/src/wave-ui/components/w-select.vue +1 -1
- package/src/wave-ui/components/w-table.vue +17 -13
- package/src/wave-ui/components/w-tabs/index.vue +2 -1
- package/src/wave-ui/components/w-tag.vue +17 -6
- package/src/wave-ui/components/w-textarea.vue +1 -1
- package/src/wave-ui/components/w-tooltip.vue +136 -270
- package/src/wave-ui/mixins/detachable.js +189 -0
- package/src/wave-ui/scss/_base.scss +0 -21
- package/src/wave-ui/scss/_mixins.scss +18 -4
- package/src/wave-ui/scss/_variables.scss +5 -1
- package/src/wave-ui/scss/index.scss +0 -1
- package/src/wave-ui/utils/index.js +1 -1
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A detachable element is an element that can be appended to another DOM node
|
|
3
|
+
* (but keeping data-driven Vue DOM refreshes).
|
|
4
|
+
* This mixin is used by w-tooltip & w-menu.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { consoleWarn } from '../utils/console'
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
computed: {
|
|
11
|
+
// DOM element to attach tooltip/menu to.
|
|
12
|
+
// ! \ This computed uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
13
|
+
appendToTarget () {
|
|
14
|
+
const defaultTarget = '.w-app'
|
|
15
|
+
|
|
16
|
+
// Convert deprecated prop to renamed one.
|
|
17
|
+
if (this.detachTo && !this.appendTo) {
|
|
18
|
+
consoleWarn(`The ${this.$options.name} prop \`detach-to\` is deprecated. You can replace it with \`append-to\`.`, this)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let target = this.appendTo || this.detachTo || defaultTarget
|
|
22
|
+
if (target === true) target = defaultTarget
|
|
23
|
+
else if (this.appendTo === 'activator') target = this.$el.previousElementSibling
|
|
24
|
+
else if (target && !['object', 'string'].includes(typeof target)) target = defaultTarget
|
|
25
|
+
else if (typeof target === 'object' && !target.nodeType) {
|
|
26
|
+
target = defaultTarget
|
|
27
|
+
consoleWarn(`Invalid node provided in ${this.$options.name} \`append-to\`. Falling back to .w-app.`, this)
|
|
28
|
+
}
|
|
29
|
+
if (typeof target === 'string') target = document.querySelector(target)
|
|
30
|
+
|
|
31
|
+
if (!target) {
|
|
32
|
+
consoleWarn(`Unable to locate ${this.appendTo ? `target ${this.appendTo}` : defaultTarget}`, this)
|
|
33
|
+
target = document.querySelector(defaultTarget)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return target
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
// DOM element that will receive the tooltip/menu.
|
|
40
|
+
// ! \ This computed uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
41
|
+
detachableParentEl () {
|
|
42
|
+
return this.appendToTarget
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
methods: {
|
|
47
|
+
// ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
48
|
+
getActivatorCoordinates (e) {
|
|
49
|
+
// Get the activator coordinates relative to window.
|
|
50
|
+
const { top, left, width, height } = (e ? e.target : this.activatorEl).getBoundingClientRect()
|
|
51
|
+
let coords = { top, left, width, height }
|
|
52
|
+
|
|
53
|
+
// If absolute position, adjust top & left.
|
|
54
|
+
if (!this.fixed) {
|
|
55
|
+
const { top: targetTop, left: targetLeft } = this.detachableParentEl.getBoundingClientRect()
|
|
56
|
+
const computedStyles = window.getComputedStyle(this.detachableParentEl, null)
|
|
57
|
+
coords = {
|
|
58
|
+
...coords,
|
|
59
|
+
top: top - targetTop + this.detachableParentEl.scrollTop - parseInt(computedStyles.getPropertyValue('border-top-width')),
|
|
60
|
+
left: left - targetLeft + this.detachableParentEl.scrollLeft - parseInt(computedStyles.getPropertyValue('border-left-width'))
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return coords
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
// ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
68
|
+
computeDetachableCoords (e) {
|
|
69
|
+
// Get the activator coordinates.
|
|
70
|
+
let { top, left, width, height } = this.getActivatorCoordinates(e)
|
|
71
|
+
|
|
72
|
+
// 1. First display the menu but hide it (So we can get its dimension).
|
|
73
|
+
// --------------------------------------------------
|
|
74
|
+
this.detachableEl.style.visibility = 'hidden'
|
|
75
|
+
this.detachableEl.style.display = 'flex'
|
|
76
|
+
const computedStyles = window.getComputedStyle(this.detachableEl, null)
|
|
77
|
+
|
|
78
|
+
// 2. Position the menu top, left, right, bottom and apply chosen alignment.
|
|
79
|
+
// --------------------------------------------------
|
|
80
|
+
// Subtract half or full activator width or height and menu width or height according to the
|
|
81
|
+
// menu alignment.
|
|
82
|
+
// Note: the menu position relies on transform translate, the custom animation may override the
|
|
83
|
+
// css transform property so do without it i.e. no translateX(-50%), and recalculate top & left
|
|
84
|
+
// manually.
|
|
85
|
+
switch (this.position) {
|
|
86
|
+
case 'top': {
|
|
87
|
+
top -= this.detachableEl.offsetHeight
|
|
88
|
+
if (this.alignRight) {
|
|
89
|
+
// left: 100% of activator.
|
|
90
|
+
left += width - this.detachableEl.offsetWidth +
|
|
91
|
+
parseInt(computedStyles.getPropertyValue('border-right-width'))
|
|
92
|
+
}
|
|
93
|
+
else if (!this.alignLeft) left += (width - this.detachableEl.offsetWidth) / 2 // left: 50% of activator - half menu width.
|
|
94
|
+
break
|
|
95
|
+
}
|
|
96
|
+
case 'bottom': {
|
|
97
|
+
top += height
|
|
98
|
+
if (this.alignRight) {
|
|
99
|
+
// left: 100% of activator.
|
|
100
|
+
left += width - this.detachableEl.offsetWidth +
|
|
101
|
+
parseInt(computedStyles.getPropertyValue('border-right-width'))
|
|
102
|
+
}
|
|
103
|
+
else if (!this.alignLeft) left += (width - this.detachableEl.offsetWidth) / 2 // left: 50% of activator - half menu width.
|
|
104
|
+
break
|
|
105
|
+
}
|
|
106
|
+
case 'left': {
|
|
107
|
+
left -= this.detachableEl.offsetWidth
|
|
108
|
+
if (this.alignBottom) top += height - this.detachableEl.offsetHeight
|
|
109
|
+
else if (!this.alignTop) top += (height - this.detachableEl.offsetHeight) / 2 // top: 50% of activator - half menu height.
|
|
110
|
+
break
|
|
111
|
+
}
|
|
112
|
+
case 'right': {
|
|
113
|
+
left += width
|
|
114
|
+
if (this.alignBottom) {
|
|
115
|
+
top += height - this.detachableEl.offsetHeight +
|
|
116
|
+
parseInt(computedStyles.getPropertyValue('margin-top'))
|
|
117
|
+
}
|
|
118
|
+
else if (!this.alignTop) {
|
|
119
|
+
top += (height - this.detachableEl.offsetHeight) / 2 + // top: 50% of activator - half menu height.
|
|
120
|
+
parseInt(computedStyles.getPropertyValue('margin-top'))
|
|
121
|
+
}
|
|
122
|
+
break
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 3. Keep fully in viewport.
|
|
127
|
+
// @todo: do this.
|
|
128
|
+
// --------------------------------------------------
|
|
129
|
+
// if (this.position === 'top' && ((top - this.detachableEl.offsetHeight) < 0)) {
|
|
130
|
+
// const margin = - parseInt(computedStyles.getPropertyValue('margin-top'))
|
|
131
|
+
// top -= top - this.detachableEl.offsetHeight - margin - marginFromWindowSide
|
|
132
|
+
// }
|
|
133
|
+
// else if (this.position === 'left' && left - this.detachableEl.offsetWidth < 0) {
|
|
134
|
+
// const margin = - parseInt(computedStyles.getPropertyValue('margin-left'))
|
|
135
|
+
// left -= left - this.detachableEl.offsetWidth - margin - marginFromWindowSide
|
|
136
|
+
// }
|
|
137
|
+
// else if (this.position === 'right' && left + width + this.detachableEl.offsetWidth > window.innerWidth) {
|
|
138
|
+
// const margin = parseInt(computedStyles.getPropertyValue('margin-left'))
|
|
139
|
+
// left -= left + width + this.detachableEl.offsetWidth - window.innerWidth + margin + marginFromWindowSide
|
|
140
|
+
// }
|
|
141
|
+
// else if (this.position === 'bottom' && top + height + this.detachableEl.offsetHeight > window.innerHeight) {
|
|
142
|
+
// const margin = parseInt(computedStyles.getPropertyValue('margin-top'))
|
|
143
|
+
// top -= top + height + this.detachableEl.offsetHeight - window.innerHeight + margin + marginFromWindowSide
|
|
144
|
+
// }
|
|
145
|
+
|
|
146
|
+
// 4. Hide the menu again so the transition happens correctly.
|
|
147
|
+
// --------------------------------------------------
|
|
148
|
+
this.detachableEl.style.visibility = null
|
|
149
|
+
|
|
150
|
+
// The menu coordinates are also recalculated while resizing window with open menu: keep the menu visible.
|
|
151
|
+
if (!this.detachableVisible) this.detachableEl.style.display = 'none'
|
|
152
|
+
|
|
153
|
+
this.detachableCoords = { top, left }
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
onResize () {
|
|
157
|
+
if (this.minWidth === 'activator') this.activatorWidth = this.activatorEl.offsetWidth
|
|
158
|
+
this.computeDetachableCoords()
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
// ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
162
|
+
onOutsideMousedown (e) {
|
|
163
|
+
if (!this.detachableEl.contains(e.target) && !this.activatorEl.contains(e.target)) {
|
|
164
|
+
this.$emit('update:modelValue', (this.detachableVisible = false))
|
|
165
|
+
this.$emit('input', false)
|
|
166
|
+
this.$emit('close')
|
|
167
|
+
document.removeEventListener('mousedown', this.onOutsideMousedown)
|
|
168
|
+
window.removeEventListener('resize', this.onResize)
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
insertInDOM () {
|
|
173
|
+
return new Promise(resolve => {
|
|
174
|
+
this.$nextTick(() => {
|
|
175
|
+
this.detachableEl = this.$refs.detachable?.$el || this.$refs.detachable
|
|
176
|
+
|
|
177
|
+
// Move the tooltip/menu elsewhere in the DOM.
|
|
178
|
+
// wrapper.parentNode.insertBefore(this.detachableEl, wrapper)
|
|
179
|
+
this.appendToTarget.appendChild(this.detachableEl)
|
|
180
|
+
resolve()
|
|
181
|
+
})
|
|
182
|
+
})
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
removeFromDOM () {
|
|
186
|
+
if (this.detachableEl && this.detachableEl.parentNode) this.detachableEl.remove()
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -10,24 +10,3 @@ a {text-decoration: none;}
|
|
|
10
10
|
padding-left: 3 * $base-increment;
|
|
11
11
|
padding-right: 3 * $base-increment;
|
|
12
12
|
}
|
|
13
|
-
|
|
14
|
-
.nav-menu ~ .main-content {padding-left: 4em;}
|
|
15
|
-
|
|
16
|
-
footer {
|
|
17
|
-
margin-top: 5em;
|
|
18
|
-
|
|
19
|
-
.nav-drawer ~ & {padding-left: 12px;}
|
|
20
|
-
|
|
21
|
-
.heart:hover {animation: heartbeat 1s infinite;}
|
|
22
|
-
small {font-size: 10px;}
|
|
23
|
-
.caption {padding-top: 1px;}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
@keyframes heartbeat {
|
|
27
|
-
0%, 30%, 60%, 100% {transform: scale(1);}
|
|
28
|
-
15%, 45% {transform: scale(1.2);}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
@media screen and (max-width: 560px) {
|
|
32
|
-
button.go-top {display: none;}
|
|
33
|
-
}
|
|
@@ -54,10 +54,24 @@
|
|
|
54
54
|
margin-right: 0;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
&#{$selector}--align-top:before {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
&#{$selector}--align-top:before {
|
|
58
|
+
transform: none;
|
|
59
|
+
top: (2 * $base-increment) - 1px;
|
|
60
|
+
}
|
|
61
|
+
&#{$selector}--align-bottom:before {
|
|
62
|
+
transform: none;
|
|
63
|
+
top: auto;
|
|
64
|
+
bottom: (2 * $base-increment) - 1px;
|
|
65
|
+
}
|
|
66
|
+
&#{$selector}--align-left:before {
|
|
67
|
+
transform: none;
|
|
68
|
+
left: (2 * $base-increment) - 1px;
|
|
69
|
+
}
|
|
70
|
+
&#{$selector}--align-right:before {
|
|
71
|
+
transform: none;
|
|
72
|
+
left: auto;
|
|
73
|
+
right: (2 * $base-increment) - 1px;
|
|
74
|
+
}
|
|
61
75
|
}
|
|
62
76
|
|
|
63
77
|
// The colored triangle on top of `:before`.
|
|
@@ -11,7 +11,7 @@ $css-scope: '.w-app' !default; // Allows control on CSS rules priority.
|
|
|
11
11
|
// True by default. False allows you to use an external CSS library (like Tailwind).
|
|
12
12
|
$use-layout-classes: true !default;
|
|
13
13
|
|
|
14
|
-
$base-font-size: 14px !default;
|
|
14
|
+
$base-font-size: 14px !default; // Must be a px unit.
|
|
15
15
|
$base-increment: round(divide($base-font-size, 4)) !default;
|
|
16
16
|
$layout-padding: $base-increment * 4 !default; // Applied on the .content-wrap tag.
|
|
17
17
|
$border-radius: 3px !default;
|
|
@@ -57,3 +57,7 @@ $textarea-line-height: 1.2;
|
|
|
57
57
|
$tooltip-bg-color: #fff;
|
|
58
58
|
$tooltip-color: rgba(0, 0, 0, 0.7);
|
|
59
59
|
// --------------------------------------------------------
|
|
60
|
+
|
|
61
|
+
// Mixins.
|
|
62
|
+
// --------------------------------------------------------
|
|
63
|
+
@import './mixins';
|
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export const objectifyClasses = (classes = {}) => {
|
|
8
8
|
if (typeof classes === 'string') classes = { [classes]: true }
|
|
9
|
-
else if (
|
|
9
|
+
else if (Array.isArray(classes)) classes = { [classes.join(' ')]: true }
|
|
10
10
|
return classes
|
|
11
11
|
}
|