wave-ui 3.27.2 → 4.0.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/types/types/$waveui.d.ts +21 -1
- package/dist/types/types/colors.d.ts +2 -0
- package/dist/types/types/components/WAccordion.d.ts +99 -6
- package/dist/types/types/components/WAutocomplete.d.ts +437 -0
- package/dist/types/types/components/WBreadcrumbs.d.ts +7 -0
- package/dist/types/types/components/WButton.d.ts +7 -0
- package/dist/types/types/components/WCheckbox.d.ts +34 -0
- package/dist/types/types/components/WCheckboxes.d.ts +30 -0
- package/dist/types/types/components/WInput.d.ts +34 -0
- package/dist/types/types/components/WList.d.ts +7 -0
- package/dist/types/types/components/WMenu.d.ts +12 -6
- package/dist/types/types/components/WRadio.d.ts +34 -0
- package/dist/types/types/components/WRadios.d.ts +30 -0
- package/dist/types/types/components/WScrollable.d.ts +143 -0
- package/dist/types/types/components/WScrollable.js +2 -0
- package/dist/types/types/components/WSelect.d.ts +34 -0
- package/dist/types/types/components/WSwitch.d.ts +34 -0
- package/dist/types/types/components/WTable.d.ts +7 -0
- package/dist/types/types/components/WTabs.d.ts +7 -0
- package/dist/types/types/components/WTag.d.ts +7 -0
- package/dist/types/types/components/WTooltip.d.ts +20 -7
- package/dist/types/types/components/WTransitions.d.ts +104 -0
- package/dist/types/types/components/WTransitions.js +2 -0
- package/dist/types/types/components/WTree.d.ts +7 -0
- package/dist/types/types/components/index.d.ts +3 -1
- package/dist/wave-ui.cjs.js +3 -3
- package/dist/wave-ui.css +1 -1
- package/dist/wave-ui.esm.js +2190 -1350
- package/dist/wave-ui.umd.js +3 -3
- package/package.json +6 -6
- package/src/wave-ui/components/index.js +0 -1
- package/src/wave-ui/components/transitions/w-transition-bounce.vue +2 -1
- package/src/wave-ui/components/transitions/w-transition-expand.vue +3 -2
- package/src/wave-ui/components/transitions/w-transition-fade.vue +2 -1
- package/src/wave-ui/components/transitions/w-transition-scale-fade.vue +2 -1
- package/src/wave-ui/components/transitions/w-transition-scale.vue +2 -1
- package/src/wave-ui/components/transitions/w-transition-slide-fade.vue +2 -1
- package/src/wave-ui/components/transitions/w-transition-slide.vue +2 -1
- package/src/wave-ui/components/transitions/w-transition-twist.vue +2 -1
- package/src/wave-ui/components/w-accordion/index.vue +15 -6
- package/src/wave-ui/components/w-accordion/item.vue +71 -26
- package/src/wave-ui/components/w-alert.vue +27 -29
- package/src/wave-ui/components/w-autocomplete.vue +626 -192
- package/src/wave-ui/components/w-badge.vue +54 -53
- package/src/wave-ui/components/w-breadcrumbs.vue +20 -11
- package/src/wave-ui/components/w-button/button.vue +36 -24
- package/src/wave-ui/components/w-button/index.vue +6 -5
- package/src/wave-ui/components/w-card.vue +8 -7
- package/src/wave-ui/components/w-checkbox.vue +31 -11
- package/src/wave-ui/components/w-checkboxes.vue +21 -3
- package/src/wave-ui/components/w-confirm.vue +22 -22
- package/src/wave-ui/components/w-dialog.vue +1 -1
- package/src/wave-ui/components/w-divider.vue +5 -5
- package/src/wave-ui/components/w-drawer.vue +3 -3
- package/src/wave-ui/components/w-form-element.vue +2 -2
- package/src/wave-ui/components/w-icon.vue +12 -14
- package/src/wave-ui/components/w-image.vue +1 -1
- package/src/wave-ui/components/w-input.vue +43 -25
- package/src/wave-ui/components/w-list.vue +23 -12
- package/src/wave-ui/components/w-menu.vue +57 -55
- package/src/wave-ui/components/w-notification.vue +4 -4
- package/src/wave-ui/components/w-progress.vue +6 -7
- package/src/wave-ui/components/w-radio.vue +32 -7
- package/src/wave-ui/components/w-radios.vue +28 -3
- package/src/wave-ui/components/w-rating.vue +7 -9
- package/src/wave-ui/components/w-scrollable.vue +670 -97
- package/src/wave-ui/components/w-select.vue +119 -101
- package/src/wave-ui/components/w-slider.vue +26 -26
- package/src/wave-ui/components/w-spinner.vue +5 -7
- package/src/wave-ui/components/w-switch.vue +71 -47
- package/src/wave-ui/components/w-table.vue +69 -36
- package/src/wave-ui/components/w-tabs/index.vue +31 -24
- package/src/wave-ui/components/w-tag.vue +49 -38
- package/src/wave-ui/components/w-textarea.vue +22 -22
- package/src/wave-ui/components/w-timeline.vue +6 -6
- package/src/wave-ui/components/w-toolbar.vue +8 -8
- package/src/wave-ui/components/w-tooltip.vue +30 -25
- package/src/wave-ui/components/w-tree.vue +35 -16
- package/src/wave-ui/core.js +11 -1
- package/src/wave-ui/mixins/detachable.js +98 -43
- package/src/wave-ui/mixins/ripple.js +39 -0
- package/src/wave-ui/scss/_base.scss +82 -17
- package/src/wave-ui/scss/_colors.scss +6 -75
- package/src/wave-ui/scss/_layout.scss +39 -47
- package/src/wave-ui/scss/_ripple.scss +37 -0
- package/src/wave-ui/scss/_transitions.scss +19 -19
- package/src/wave-ui/scss/_typography.scss +8 -9
- package/src/wave-ui/scss/index.scss +1 -0
- package/src/wave-ui/scss/variables/_mixins.scss +24 -25
- package/src/wave-ui/scss/variables/_variables.scss +4 -151
- package/src/wave-ui/utils/colors.js +7 -4
- package/src/wave-ui/utils/config.js +5 -4
- package/src/wave-ui/utils/dynamic-css.js +42 -20
- package/src/wave-ui/utils/ripple.js +72 -0
- package/src/wave-ui/utils/wave-ripple-directive.js +40 -0
- package/dist/types/types/components/WApp.d.ts +0 -83
- package/src/wave-ui/components/w-app.vue +0 -24
- /package/dist/types/types/components/{WApp.js → WAutocomplete.js} +0 -0
|
@@ -9,7 +9,9 @@ ul.w-tree(:class="classes")
|
|
|
9
9
|
:is="getTreeItemComponent(item)"
|
|
10
10
|
v-bind="item.route && { [!$router || hasExternalLink(item) ? 'href' : 'to']: item.route }"
|
|
11
11
|
@click="!disabled && !item.disabled && onLabelClick(item, $event)"
|
|
12
|
+
@pointerdown="onLabelPointerDown(item, $event)"
|
|
12
13
|
@keydown="!disabled && !item.disabled && onLabelKeydown(item, $event)"
|
|
14
|
+
:class="itemLabelClasses(item)"
|
|
13
15
|
:tabindex="getTreeItemTabindex(item)")
|
|
14
16
|
//- @click.stop to not follow link if item is a link.
|
|
15
17
|
w-button.w-tree__item-expand(
|
|
@@ -59,6 +61,7 @@ ul.w-tree(:class="classes")
|
|
|
59
61
|
</template>
|
|
60
62
|
|
|
61
63
|
<script>
|
|
64
|
+
import RippleMixin from '../mixins/ripple'
|
|
62
65
|
import { consoleWarn } from '../utils/console'
|
|
63
66
|
/**
|
|
64
67
|
* @todo:
|
|
@@ -67,6 +70,9 @@ import { consoleWarn } from '../utils/console'
|
|
|
67
70
|
|
|
68
71
|
export default {
|
|
69
72
|
name: 'w-tree',
|
|
73
|
+
|
|
74
|
+
mixins: [RippleMixin],
|
|
75
|
+
|
|
70
76
|
props: {
|
|
71
77
|
modelValue: { type: [Object, Array] },
|
|
72
78
|
data: { type: [Object, Array], required: true },
|
|
@@ -200,6 +206,16 @@ export default {
|
|
|
200
206
|
return true // Just to chain instructions.
|
|
201
207
|
},
|
|
202
208
|
|
|
209
|
+
isItemRippleActive (item) {
|
|
210
|
+
return this.rippleActive && this.selectable && !this.disabled && !item.disabled
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
itemLabelClasses (item) {
|
|
214
|
+
return {
|
|
215
|
+
'w-ripple': this.isItemRippleActive(item)
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
|
|
203
219
|
onLabelClick (item, e) {
|
|
204
220
|
const route = item[this.itemRouteKey]
|
|
205
221
|
if (route && this.$router && !this.hasExternalLink(item)) e.preventDefault()
|
|
@@ -212,6 +228,11 @@ export default {
|
|
|
212
228
|
this.emitItemSelection(item, e) // Always emitting on click, but different event for selection.
|
|
213
229
|
},
|
|
214
230
|
|
|
231
|
+
onLabelPointerDown (item, e) {
|
|
232
|
+
if (!this.isItemRippleActive(item) || e.target.closest?.('.w-tree__item-expand')) return
|
|
233
|
+
this.onRipple(e)
|
|
234
|
+
},
|
|
235
|
+
|
|
215
236
|
onLabelKeydown (item, e) {
|
|
216
237
|
// Keys: 13 enter, 32 space, 37 arrow left, 38 arrow up, 39 arrow right, 40 arrow down.
|
|
217
238
|
if (!(e.metaKey || e.ctrlKey || e.altKey || e.shiftKey) && [13, 32, 37, 38, 39, 40].includes(e.keyCode)) {
|
|
@@ -339,16 +360,14 @@ export default {
|
|
|
339
360
|
</script>
|
|
340
361
|
|
|
341
362
|
<style lang="scss">
|
|
342
|
-
$expand-icon-size: 20px;
|
|
343
|
-
|
|
344
363
|
.w-tree {
|
|
364
|
+
--w-tree-expand-icon-size: 20px;
|
|
345
365
|
margin: 0;
|
|
346
366
|
|
|
347
367
|
// Tree items.
|
|
348
368
|
// ------------------------------------------------------
|
|
349
369
|
&__item {list-style-type: none;}
|
|
350
|
-
&__item--
|
|
351
|
-
&__item--leaf {margin-left: $base-increment * 5 + 2px;}
|
|
370
|
+
&__item--leaf {margin-left: calc(var(--w-base-increment) * 5 + 2px);}
|
|
352
371
|
&--no-expand-button &__item--leaf {margin-left: 0;}
|
|
353
372
|
|
|
354
373
|
// Tree item label.
|
|
@@ -364,21 +383,21 @@ $expand-icon-size: 20px;
|
|
|
364
383
|
position: absolute;
|
|
365
384
|
top: -1px;
|
|
366
385
|
bottom: -1px;
|
|
367
|
-
left: -
|
|
368
|
-
right: -
|
|
369
|
-
border-radius:
|
|
386
|
+
left: calc(var(--w-base-increment) * -1 + 2px);
|
|
387
|
+
right: calc(var(--w-base-increment) * -1 - 2px);
|
|
388
|
+
border-radius: var(--w-border-radius);
|
|
370
389
|
}
|
|
371
|
-
&:hover:before {background-color:
|
|
372
|
-
&:focus-visible:before {background-color:
|
|
390
|
+
&:hover:before {background-color: var(--w-primary-color);opacity: 0.1;}
|
|
391
|
+
&:focus-visible:before {background-color: var(--w-primary-color);opacity: 0.15;}
|
|
373
392
|
}
|
|
374
393
|
&.w-tree--selectable &__item-label {cursor: pointer;}
|
|
375
394
|
&.w-tree--selectable &__item--disabled &__item-label {cursor: auto;}
|
|
376
395
|
&__item--leaf &__item-label:before {
|
|
377
|
-
left: -
|
|
378
|
-
right: -
|
|
396
|
+
left: calc(var(--w-base-increment) * -1);
|
|
397
|
+
right: calc(var(--w-base-increment) * -1);
|
|
379
398
|
}
|
|
380
399
|
&__item--selected > &__item-label:before {
|
|
381
|
-
background-color:
|
|
400
|
+
background-color: var(--w-primary-color);
|
|
382
401
|
opacity: 0.25;
|
|
383
402
|
}
|
|
384
403
|
&__item--disabled &__item-label {opacity: 0.5;}
|
|
@@ -388,21 +407,21 @@ $expand-icon-size: 20px;
|
|
|
388
407
|
|
|
389
408
|
&__item--branch > &__item-label {cursor: pointer;}
|
|
390
409
|
&__item--disabled > &__item-label {
|
|
391
|
-
color:
|
|
410
|
+
color: var(--w-disabled-color);
|
|
392
411
|
cursor: not-allowed;
|
|
393
412
|
-webkit-tap-highlight-color: transparent;
|
|
394
413
|
}
|
|
395
414
|
&__item--unexpandable > &__item-label {
|
|
396
|
-
margin-left:
|
|
415
|
+
margin-left: calc(var(--w-tree-expand-icon-size) + 2px);
|
|
397
416
|
cursor: auto;
|
|
398
417
|
}
|
|
399
418
|
&--disabled &__item-label {cursor: auto;}
|
|
400
419
|
&--disabled &__item--branch > &__item-label {opacity: 0.5;}
|
|
401
420
|
|
|
402
|
-
&__item-icon {margin-right:
|
|
421
|
+
&__item-icon {margin-right: var(--w-base-increment);}
|
|
403
422
|
|
|
404
423
|
// Recursive children.
|
|
405
424
|
// ------------------------------------------------------
|
|
406
|
-
.w-tree {margin-left:
|
|
425
|
+
.w-tree {margin-left: calc(var(--w-base-increment) * 5);}
|
|
407
426
|
}
|
|
408
427
|
</style>
|
package/src/wave-ui/core.js
CHANGED
|
@@ -4,6 +4,7 @@ import { consoleWarn } from './utils/console'
|
|
|
4
4
|
import { colorPalette, generateColorShades, flattenColors } from './utils/colors'
|
|
5
5
|
import { injectColorsCSSInDOM, injectCSSInDOM } from './utils/dynamic-css'
|
|
6
6
|
import { injectNotifManagerInDOM, NotificationManager } from './utils/notification-manager'
|
|
7
|
+
import { waveRippleDirective } from './utils/wave-ripple-directive'
|
|
7
8
|
import './scss/index.scss'
|
|
8
9
|
|
|
9
10
|
let mounted = false
|
|
@@ -90,7 +91,7 @@ export default class WaveUI {
|
|
|
90
91
|
document.documentElement.setAttribute('data-theme', theme)
|
|
91
92
|
document.head.querySelector('#wave-ui-colors')?.remove?.()
|
|
92
93
|
const themeColors = this.config.colors[this.theme]
|
|
93
|
-
injectColorsCSSInDOM(themeColors, this.config.css.colorShadeCssVariables)
|
|
94
|
+
injectColorsCSSInDOM(themeColors, colorPalette, this.config.css.colorShadeCssVariables)
|
|
94
95
|
this.colors = flattenColors(themeColors, colorPalette)
|
|
95
96
|
},
|
|
96
97
|
|
|
@@ -122,6 +123,7 @@ export default class WaveUI {
|
|
|
122
123
|
window.addEventListener('scroll', f)
|
|
123
124
|
}
|
|
124
125
|
})
|
|
126
|
+
app.directive('waveRipple', waveRippleDirective)
|
|
125
127
|
|
|
126
128
|
// Register a-la-carte components from the given list.
|
|
127
129
|
const { components = {} } = options || {}
|
|
@@ -145,6 +147,14 @@ export default class WaveUI {
|
|
|
145
147
|
const wApp = document.querySelector(config.on) || document.body
|
|
146
148
|
wApp.classList.add('w-app')
|
|
147
149
|
|
|
150
|
+
// Add any custom app classes from config.
|
|
151
|
+
if (config.css.appClasses) {
|
|
152
|
+
const classesToAdd = typeof config.css.appClasses === 'string'
|
|
153
|
+
? config.css.appClasses.split(' ').filter(c => c)
|
|
154
|
+
: config.css.appClasses
|
|
155
|
+
if (classesToAdd.length) wApp.classList.add(...classesToAdd)
|
|
156
|
+
}
|
|
157
|
+
|
|
148
158
|
if (config.theme === 'auto') detectOSDarkMode($waveui) // Also switches the theme.
|
|
149
159
|
else $waveui.switchTheme(config.theme, true)
|
|
150
160
|
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
* A detachable element is an element that can be appended to another DOM node
|
|
3
3
|
* (but keeping data-driven Vue DOM refreshes).
|
|
4
4
|
* This mixin is used by w-tooltip & w-menu.
|
|
5
|
+
*
|
|
6
|
+
* Vue Teleport handles moving the floating content to the right DOM node.
|
|
7
|
+
* Event listeners are auto-attached to the activator slot's root element so
|
|
8
|
+
* callers no longer need the `template(#activator="{ on }") v-on="on"` pattern.
|
|
5
9
|
*/
|
|
6
10
|
|
|
7
11
|
import { consoleWarn } from '../utils/console'
|
|
@@ -55,7 +59,11 @@ export default {
|
|
|
55
59
|
// Set to true by computeDetachableCoords after positioning, false until then.
|
|
56
60
|
// Components use this to bind visibility:hidden, so the element is never visible at the
|
|
57
61
|
// wrong position before its coordinates are calculated.
|
|
58
|
-
detachableReady: false
|
|
62
|
+
detachableReady: false,
|
|
63
|
+
// The Vue Teleport target. Stored as data (not computed) so it is resolved lazily at
|
|
64
|
+
// open()-time — after the DOM is committed — rather than during VNode creation where
|
|
65
|
+
// document.querySelector() may return null for elements that are part of the same render batch.
|
|
66
|
+
teleportTarget: null
|
|
59
67
|
}),
|
|
60
68
|
|
|
61
69
|
computed: {
|
|
@@ -74,7 +82,8 @@ export default {
|
|
|
74
82
|
|
|
75
83
|
let target = this.appendTo || defaultTarget
|
|
76
84
|
if (target === true) target = defaultTarget
|
|
77
|
-
|
|
85
|
+
// When appendTo is 'activator', teleport into the activator element itself.
|
|
86
|
+
else if (this.appendTo === 'activator') target = this.activatorEl
|
|
78
87
|
else if (target && !['object', 'string'].includes(typeof target)) target = defaultTarget
|
|
79
88
|
else if (typeof target === 'object' && !target.nodeType) {
|
|
80
89
|
target = defaultTarget
|
|
@@ -111,7 +120,9 @@ export default {
|
|
|
111
120
|
if (typeof document === 'undefined') return null
|
|
112
121
|
return document.querySelector(this.activator)
|
|
113
122
|
}
|
|
114
|
-
|
|
123
|
+
// For slot-based activators the component root ($el) is a comment fragment anchor in
|
|
124
|
+
// Vue 3 multi-root components; nextElementSibling is the activator slot's first real element.
|
|
125
|
+
return this.$el?.nextElementSibling || null
|
|
115
126
|
},
|
|
116
127
|
set () {}
|
|
117
128
|
},
|
|
@@ -154,6 +165,7 @@ export default {
|
|
|
154
165
|
// next open starts hidden. Done here rather than on close() to let the leave animation play.
|
|
155
166
|
onAfterLeave () {
|
|
156
167
|
this.detachableReady = false
|
|
168
|
+
this.detachableEl = null
|
|
157
169
|
},
|
|
158
170
|
|
|
159
171
|
unbindActivatorDocEvents () {
|
|
@@ -166,6 +178,61 @@ export default {
|
|
|
166
178
|
}
|
|
167
179
|
},
|
|
168
180
|
|
|
181
|
+
/**
|
|
182
|
+
* Single delegating handler for auto-attached slot-activator DOM events.
|
|
183
|
+
* Reads the current activatorEventHandlers computed each invocation so that changes to
|
|
184
|
+
* `disable`, `showOnHover`, etc. are always reflected without re-attaching.
|
|
185
|
+
*/
|
|
186
|
+
_handleActivatorEvent (e) {
|
|
187
|
+
const handler = this.activatorEventHandlers[e.type]
|
|
188
|
+
if (handler) handler(e)
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Attach DOM event listeners directly to the activator slot's root element.
|
|
193
|
+
* Called once from mounted(); a single delegating handler covers all event types so we never
|
|
194
|
+
* need to re-attach when props like `disable` or `showOnHover` change.
|
|
195
|
+
* ! \ This function uses the DOM - NO SSR.
|
|
196
|
+
*/
|
|
197
|
+
_attachActivatorListeners () {
|
|
198
|
+
if (typeof document === 'undefined') return
|
|
199
|
+
const el = this.activatorEl
|
|
200
|
+
if (!el) return
|
|
201
|
+
|
|
202
|
+
// Inspect the activator slot's first VNode for pre-declared event handlers.
|
|
203
|
+
// When the slot root already declares onClick / onMouseenter etc. — whether on a native
|
|
204
|
+
// element (w-select, w-autocomplete) or on a component (w-button @click="...") — the
|
|
205
|
+
// parent is managing that event itself. Skip auto-attaching the competing handler to avoid
|
|
206
|
+
// open/close races (both toggle() and the explicit handler firing on the same click).
|
|
207
|
+
// With the new API, the default slot is the activator (no #activator slot used).
|
|
208
|
+
let existingHandlers = {}
|
|
209
|
+
const activatorSlot = this.$slots.activator || this.$slots.default
|
|
210
|
+
if (activatorSlot) {
|
|
211
|
+
const vnodes = activatorSlot()
|
|
212
|
+
const firstVnode = vnodes?.[0]
|
|
213
|
+
existingHandlers = firstVnode?.props || {}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
this._activatorDomEl = el
|
|
217
|
+
this._activatorAttachedEvents = []
|
|
218
|
+
;['click', 'mouseenter', 'mouseleave', 'focus', 'blur'].forEach(evt => {
|
|
219
|
+
// Skip if the slot element already binds this event (camelCase Vue prop name: onClick etc.).
|
|
220
|
+
const vueProp = `on${evt.charAt(0).toUpperCase()}${evt.slice(1)}`
|
|
221
|
+
if (existingHandlers[vueProp]) return
|
|
222
|
+
el.addEventListener(evt, this._handleActivatorEvent)
|
|
223
|
+
this._activatorAttachedEvents.push(evt)
|
|
224
|
+
})
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
_detachActivatorListeners () {
|
|
228
|
+
if (!this._activatorDomEl) return
|
|
229
|
+
;(this._activatorAttachedEvents || []).forEach(evt => {
|
|
230
|
+
this._activatorDomEl.removeEventListener(evt, this._handleActivatorEvent)
|
|
231
|
+
})
|
|
232
|
+
this._activatorDomEl = null
|
|
233
|
+
this._activatorAttachedEvents = []
|
|
234
|
+
},
|
|
235
|
+
|
|
169
236
|
// ! \ This function uses the DOM - NO SSR (only trigger from beforeMount and later).
|
|
170
237
|
async open (e) {
|
|
171
238
|
if (this.disable) return
|
|
@@ -181,13 +248,22 @@ export default {
|
|
|
181
248
|
|
|
182
249
|
// Hide before entering the DOM; handles rapid re-opens where detachableReady is still true.
|
|
183
250
|
this.detachableReady = false
|
|
251
|
+
|
|
252
|
+
// Resolve the teleport target here, at open()-time, so the DOM is fully committed and
|
|
253
|
+
// detachableDefaultRoot() (for nested menus/tooltips) returns the correct element.
|
|
254
|
+
// Setting teleportTarget and detachableVisible in the same synchronous block lets Vue
|
|
255
|
+
// batch both into a single render — the content is never shown at the wrong location.
|
|
256
|
+
if (typeof document !== 'undefined') this.teleportTarget = this.appendToTarget
|
|
257
|
+
|
|
184
258
|
this.detachableVisible = true
|
|
185
259
|
|
|
186
260
|
// If the activator is external, there might be multiple,
|
|
187
261
|
// so on open, the activator will be set to the event target.
|
|
188
262
|
if (this.activator) this.activatorEl = e.target
|
|
189
263
|
|
|
190
|
-
|
|
264
|
+
// Wait for Vue Teleport to render the floating element into the target DOM node.
|
|
265
|
+
await this.$nextTick()
|
|
266
|
+
this.detachableEl = this.$refs.detachable?.$el || this.$refs.detachable
|
|
191
267
|
|
|
192
268
|
if (this.minWidth === 'activator' && this.activatorEl) {
|
|
193
269
|
this.activatorWidth = this.activatorEl.offsetWidth
|
|
@@ -361,32 +437,6 @@ export default {
|
|
|
361
437
|
}
|
|
362
438
|
},
|
|
363
439
|
|
|
364
|
-
insertInDOM () {
|
|
365
|
-
return new Promise(resolve => {
|
|
366
|
-
this.$nextTick(() => {
|
|
367
|
-
this.detachableEl = this.$refs.detachable?.$el || this.$refs.detachable
|
|
368
|
-
|
|
369
|
-
// Move the tooltip/menu elsewhere in the DOM.
|
|
370
|
-
if (this.detachableEl && this.appendToTarget) this.appendToTarget.appendChild(this.detachableEl)
|
|
371
|
-
resolve()
|
|
372
|
-
})
|
|
373
|
-
})
|
|
374
|
-
},
|
|
375
|
-
|
|
376
|
-
removeFromDOM () {
|
|
377
|
-
if (typeof document !== 'undefined') {
|
|
378
|
-
document.removeEventListener('mousedown', this.onOutsideMousedown)
|
|
379
|
-
}
|
|
380
|
-
if (typeof window !== 'undefined') {
|
|
381
|
-
window.removeEventListener('resize', this.onResize)
|
|
382
|
-
}
|
|
383
|
-
if (this.detachableEl?.parentNode) {
|
|
384
|
-
this.detachableVisible = false
|
|
385
|
-
this.detachableEl.remove()
|
|
386
|
-
this.detachableEl = null
|
|
387
|
-
}
|
|
388
|
-
},
|
|
389
|
-
|
|
390
440
|
// If the activator is external, add event listeners to the document and check the target is
|
|
391
441
|
// the activator when toggling.
|
|
392
442
|
// This way, the activator can be a future DOM element, that is not yet in the DOM.
|
|
@@ -401,8 +451,8 @@ export default {
|
|
|
401
451
|
eventName = eventName.replace('mouseenter', 'mouseover').replace('mouseleave', 'mouseout')
|
|
402
452
|
const handlerWrap = e => {
|
|
403
453
|
// The activator can be a DOM string selector a ref or a DOM node.
|
|
404
|
-
if (activatorIsString && e.target?.matches
|
|
405
|
-
else if (e.target === this.activatorEl || this.activatorEl
|
|
454
|
+
if (activatorIsString && e.target?.matches?.(this.activator)) handler(e)
|
|
455
|
+
else if (e.target === this.activatorEl || this.activatorEl?.contains(e.target)) handler(e)
|
|
406
456
|
}
|
|
407
457
|
document.addEventListener(eventName, handlerWrap)
|
|
408
458
|
// The event listeners handlers have to be removed the exact same way they have been attached.
|
|
@@ -414,14 +464,18 @@ export default {
|
|
|
414
464
|
},
|
|
415
465
|
|
|
416
466
|
mounted () {
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
// so check it on nextTick.
|
|
467
|
+
if (this.activator) {
|
|
468
|
+
// External activator: attach via document-level delegation.
|
|
469
|
+
this.bindActivatorEvents()
|
|
470
|
+
}
|
|
422
471
|
else {
|
|
472
|
+
// Slot-based activator: auto-attach DOM listeners to the slot's root element on next tick
|
|
473
|
+
// so the slot content is guaranteed to be in the DOM.
|
|
423
474
|
this.$nextTick(() => {
|
|
475
|
+
// Re-check activator prop (might have resolved from a Vue ref after the tick).
|
|
424
476
|
if (this.activator) this.bindActivatorEvents()
|
|
477
|
+
else this._attachActivatorListeners()
|
|
478
|
+
|
|
425
479
|
if (this.modelValue && !this.disable) this.open({ target: this.activatorEl })
|
|
426
480
|
})
|
|
427
481
|
}
|
|
@@ -438,10 +492,8 @@ export default {
|
|
|
438
492
|
unmounted () {
|
|
439
493
|
this.close()
|
|
440
494
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
// Remove the event listeners the exact same way they have been defined.
|
|
444
|
-
// Fixes issues on hot-reloading.
|
|
495
|
+
// Clean up slot-activator DOM listeners and external-activator document listeners.
|
|
496
|
+
this._detachActivatorListeners()
|
|
445
497
|
this.unbindActivatorDocEvents()
|
|
446
498
|
},
|
|
447
499
|
|
|
@@ -451,6 +503,8 @@ export default {
|
|
|
451
503
|
this.unbindActivatorDocEvents()
|
|
452
504
|
if (!disabled) this.bindActivatorEvents()
|
|
453
505
|
}
|
|
506
|
+
// For slot-based activators, _handleActivatorEvent always reads the current
|
|
507
|
+
// activatorEventHandlers computed which already respects `disable`, so no re-attach needed.
|
|
454
508
|
if (disabled) this.close()
|
|
455
509
|
else if (this.modelValue) this.open({ target: this.activatorEl })
|
|
456
510
|
},
|
|
@@ -461,9 +515,10 @@ export default {
|
|
|
461
515
|
else if (!bool) this.close()
|
|
462
516
|
}
|
|
463
517
|
},
|
|
518
|
+
|
|
519
|
+
// Keep teleportTarget in sync when the appendTo prop changes at runtime.
|
|
464
520
|
appendTo () {
|
|
465
|
-
this.
|
|
466
|
-
this.insertInDOM()
|
|
521
|
+
if (typeof document !== 'undefined') this.teleportTarget = this.appendToTarget
|
|
467
522
|
}
|
|
468
523
|
}
|
|
469
524
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { applyRipple, isRippleEnabled } from '../utils/ripple'
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
inject: {
|
|
5
|
+
$waveui: { from: '$waveui', default: () => ({ config: { ripple: true } }) }
|
|
6
|
+
},
|
|
7
|
+
|
|
8
|
+
props: {
|
|
9
|
+
/** Set to `true` to disable the ripple for this component only. */
|
|
10
|
+
noRipple: { type: Boolean, default: undefined }
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
computed: {
|
|
14
|
+
rippleActive () {
|
|
15
|
+
return isRippleEnabled(this.noRipple ? false : undefined, this.$waveui)
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
methods: {
|
|
20
|
+
/**
|
|
21
|
+
* Resolve host for applyRipple. Vue (and some browsers) can leave `event.currentTarget` null
|
|
22
|
+
* on delegated handlers, which would otherwise skip the ripple entirely.
|
|
23
|
+
*/
|
|
24
|
+
onRipple (e, hostEl, options) {
|
|
25
|
+
if (!this.rippleActive || !e) return
|
|
26
|
+
let host = hostEl ?? e.currentTarget
|
|
27
|
+
if (!host?.getBoundingClientRect) {
|
|
28
|
+
const raw = e.target
|
|
29
|
+
const el = raw?.nodeType === 1 ? raw : raw?.parentElement
|
|
30
|
+
host = el?.closest?.('.w-ripple') ?? null
|
|
31
|
+
}
|
|
32
|
+
if (!host?.getBoundingClientRect && this.$el instanceof Element && this.$el.classList?.contains('w-ripple')) {
|
|
33
|
+
host = this.$el
|
|
34
|
+
}
|
|
35
|
+
if (!host?.getBoundingClientRect) return
|
|
36
|
+
applyRipple(host, e, options)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -1,11 +1,43 @@
|
|
|
1
|
-
@use "sass:map";
|
|
2
1
|
@use "variables" as *;
|
|
3
2
|
|
|
4
3
|
// The CSS variables are used in the dynamic-css.js file in order to reuse the same SCSS
|
|
5
4
|
// variable presets.
|
|
6
5
|
:root {
|
|
7
|
-
--w-base-
|
|
6
|
+
--w-base-font-size: 14px;
|
|
7
|
+
// Keeps spacing in sync when only --w-base-font-size is overridden (matches former Sass rounding).
|
|
8
|
+
--w-base-increment: round(nearest, calc(var(--w-base-font-size) / 4), 1px);
|
|
9
|
+
// Sass-only: must match $css-scope in _layout.scss and what dynamic-css reads for injected rules.
|
|
8
10
|
--w-css-scope: #{$css-scope};
|
|
11
|
+
--w-layout-padding: 16px;
|
|
12
|
+
--w-border-radius: 4px;
|
|
13
|
+
--w-border-width: 1px;
|
|
14
|
+
--w-border-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 12%, transparent)};
|
|
15
|
+
--w-border: var(--w-border-width) solid var(--w-border-color);
|
|
16
|
+
--w-transition-duration: 0.25s;
|
|
17
|
+
--w-transition-duration-fast: 0.15s;
|
|
18
|
+
--w-transition-timing-fast-out-slow-in: cubic-bezier(0.4, 0, 0.2, 1);
|
|
19
|
+
--w-box-shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2),
|
|
20
|
+
0 2px 2px 0 rgba(0, 0, 0, 0.15),
|
|
21
|
+
0 1px 5px 0 rgba(0, 0, 0, 0.15);
|
|
22
|
+
--w-form-field-height: round(nearest, calc(2 * var(--w-base-font-size)), 1px);
|
|
23
|
+
// Even px step (same as former 2 * round(1.3 * $base-font-size / 2)).
|
|
24
|
+
--w-small-form-el-size: round(nearest, calc(1.3 * var(--w-base-font-size)), 2px);
|
|
25
|
+
--w-scrollbar-size: 8px;
|
|
26
|
+
|
|
27
|
+
@for $i from -6 through -1 {
|
|
28
|
+
--w-shadow-n#{-1 * $i}: 0 0 round(nearest, calc(#{$i} * var(--w-base-increment)), 1px) rgba(0, 0, 0, max(0.15, calc(0.15 * #{-$i} / 2))) inset;
|
|
29
|
+
}
|
|
30
|
+
--w-shadow-0: none;
|
|
31
|
+
@for $i from 1 through 6 {
|
|
32
|
+
--w-shadow-#{$i}: 0 0 1px rgba(0, 0, 0, 0.1),
|
|
33
|
+
round(nearest, calc(var(--w-base-increment) * #{$i} / 4), 1px) round(nearest, calc(var(--w-base-increment) * #{$i} / 4), 1px) round(nearest, calc(#{$i} * var(--w-base-increment)), 1px) rgba(0, 0, 0, max(0.15, calc(0.15 * #{$i} / 2)));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
--w-base-color-muted: #{color-mix(in srgb, var(--w-base-color) 70%, transparent)};
|
|
37
|
+
--w-overlay-scrim-color: #{color-mix(in srgb, #000 30%, transparent)};
|
|
38
|
+
--w-surface-hover-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 5%, transparent)};
|
|
39
|
+
--w-surface-active-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 8%, transparent)};
|
|
40
|
+
--w-surface-selected-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 15%, transparent)};
|
|
9
41
|
--w-contrast-bg-o025-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 2.5%, transparent)};
|
|
10
42
|
--w-contrast-bg-o05-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 5%, transparent)};
|
|
11
43
|
--w-contrast-bg-o1-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 10%, transparent)};
|
|
@@ -18,26 +50,59 @@
|
|
|
18
50
|
--w-contrast-bg-o8-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 80%, transparent)};
|
|
19
51
|
--w-contrast-bg-o9-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 90%, transparent)};
|
|
20
52
|
|
|
53
|
+
--w-detachable-bg-color: var(--w-base-bg-color);
|
|
54
|
+
--w-detachable-color: var(--w-base-color);
|
|
55
|
+
--w-confirm-bg-color: var(--w-detachable-bg-color);
|
|
56
|
+
--w-confirm-color: var(--w-detachable-color);
|
|
57
|
+
--w-dialog-bg-color: var(--w-base-bg-color);
|
|
58
|
+
--w-divider-color: var(--w-border-color);
|
|
59
|
+
--w-drawer-max-size: 380px;
|
|
60
|
+
--w-drawer-bg-color: var(--w-base-bg-color);
|
|
61
|
+
--w-menu-bg-color: var(--w-detachable-bg-color);
|
|
62
|
+
--w-menu-color: var(--w-detachable-color);
|
|
63
|
+
--w-progress-bg-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 15%, transparent)};
|
|
64
|
+
--w-rating-bg-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 25%, transparent)};
|
|
65
|
+
--w-slider-height: var(--w-base-increment);
|
|
66
|
+
--w-slider-track-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 15%, transparent)};
|
|
67
|
+
--w-slider-thumb-button-bg-color: var(--w-base-bg-color);
|
|
68
|
+
--w-slider-thumb-label-bg-color: var(--w-base-bg-color);
|
|
69
|
+
--w-slider-thumb-label-color: #{color-mix(in srgb, var(--w-base-color) 75%, transparent)};
|
|
70
|
+
--w-slider-step-label-bg-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 20%, transparent)};
|
|
71
|
+
--w-slider-step-label-color: #{color-mix(in srgb, var(--w-base-color) 50%, transparent)};
|
|
72
|
+
--w-switch-inactive-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 25%, transparent)};
|
|
73
|
+
--w-switch-thumb-color: var(--w-base-bg-color);
|
|
74
|
+
--w-table-tr-odd-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 2%, transparent)};
|
|
75
|
+
--w-table-tr-hover-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 5%, transparent)};
|
|
76
|
+
--w-table-color: #{color-mix(in srgb, var(--w-contrast-color) 70%, transparent)};
|
|
77
|
+
--w-textarea-line-height: 1.2;
|
|
78
|
+
--w-timeline-bullet-color: var(--w-base-bg-color);
|
|
79
|
+
--w-timeline-bg-color: #{color-mix(in srgb, var(--w-contrast-bg-color) 25%, transparent)};
|
|
80
|
+
--w-toolbar-max-size: 380px;
|
|
81
|
+
--w-toolbar-bg-color: var(--w-base-bg-color);
|
|
82
|
+
--w-tooltip-bg-color: var(--w-detachable-bg-color);
|
|
83
|
+
--w-tooltip-color: var(--w-detachable-color);
|
|
84
|
+
--w-tooltip-border-color: var(--w-border-color);
|
|
85
|
+
|
|
21
86
|
background-color: var(--w-base-bg-color);
|
|
22
87
|
color: var(--w-base-color);
|
|
23
88
|
}
|
|
24
89
|
|
|
25
90
|
:root[data-theme="light"] {
|
|
26
|
-
--w-base-bg-color: #
|
|
27
|
-
--w-base-color: #
|
|
28
|
-
--w-contrast-bg-color: #
|
|
29
|
-
--w-contrast-color: #
|
|
30
|
-
--w-caption-color: #
|
|
31
|
-
--w-disabled-color: #
|
|
91
|
+
--w-base-bg-color: #fff;
|
|
92
|
+
--w-base-color: #000;
|
|
93
|
+
--w-contrast-bg-color: #000;
|
|
94
|
+
--w-contrast-color: #fff;
|
|
95
|
+
--w-caption-color: #a0a0a0;
|
|
96
|
+
--w-disabled-color: #ccc;
|
|
32
97
|
}
|
|
33
98
|
|
|
34
99
|
:root[data-theme="dark"] {
|
|
35
|
-
--w-base-bg-color: #
|
|
36
|
-
--w-base-color: #
|
|
37
|
-
--w-contrast-bg-color: #
|
|
38
|
-
--w-contrast-color: #
|
|
39
|
-
--w-caption-color: #
|
|
40
|
-
--w-disabled-color: #
|
|
100
|
+
--w-base-bg-color: #222;
|
|
101
|
+
--w-base-color: #fff;
|
|
102
|
+
--w-contrast-bg-color: #fff;
|
|
103
|
+
--w-contrast-color: #000;
|
|
104
|
+
--w-caption-color: #6e6e6e;
|
|
105
|
+
--w-disabled-color: #4a4a4a;
|
|
41
106
|
}
|
|
42
107
|
|
|
43
108
|
* {
|
|
@@ -72,13 +137,13 @@ a {text-decoration: none;}
|
|
|
72
137
|
}
|
|
73
138
|
|
|
74
139
|
.w-main {
|
|
75
|
-
padding-left: 3 *
|
|
76
|
-
padding-right: 3 *
|
|
140
|
+
padding-left: calc(3 * var(--w-base-increment));
|
|
141
|
+
padding-right: calc(3 * var(--w-base-increment));
|
|
77
142
|
}
|
|
78
143
|
|
|
79
144
|
// Structure classes.
|
|
80
145
|
// ----------------------------------------------
|
|
81
146
|
.content-wrap {
|
|
82
147
|
position: relative;
|
|
83
|
-
padding:
|
|
148
|
+
padding: var(--w-layout-padding);
|
|
84
149
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
@use 'sass:color';
|
|
2
1
|
@use 'variables' as *;
|
|
3
2
|
|
|
4
3
|
#{$css-scope} {
|
|
@@ -29,82 +28,14 @@
|
|
|
29
28
|
// For each color, create a [color] and a [color]--bg associated classes,
|
|
30
29
|
// + 6 shades lighter and 6 shades darker.
|
|
31
30
|
@each $label, $color in $colors {
|
|
32
|
-
.#{$label}--bg {background-color: $color;}
|
|
33
|
-
.#{$label} {color: $color;}
|
|
31
|
+
.#{$label}--bg {background-color: var(--w-#{$label}-color);}
|
|
32
|
+
.#{$label} {color: var(--w-#{$label}-color);}
|
|
34
33
|
|
|
35
34
|
@for $i from 1 through 6 {
|
|
36
|
-
$light-
|
|
37
|
-
$light
|
|
38
|
-
$dark-
|
|
39
|
-
|
|
40
|
-
@if $label == 'deep-orange' {
|
|
41
|
-
$light-increment: 6.4;
|
|
42
|
-
}
|
|
43
|
-
@if $label == 'orange' {
|
|
44
|
-
}
|
|
45
|
-
@else if $label == 'green' {
|
|
46
|
-
$light-increment: 7.6;
|
|
47
|
-
$dark-increment: 5.7;
|
|
48
|
-
}
|
|
49
|
-
@else if $label == 'amber' {
|
|
50
|
-
}
|
|
51
|
-
@else if $label == 'pink' {
|
|
52
|
-
$light-increment: 6.7;
|
|
53
|
-
$light-offset: -4;
|
|
54
|
-
}
|
|
55
|
-
@else if $label == 'red' {
|
|
56
|
-
$light-increment: 6.5;
|
|
57
|
-
$light-offset: -1;
|
|
58
|
-
}
|
|
59
|
-
@else if $label == 'indigo' {
|
|
60
|
-
$light-increment: 8;
|
|
61
|
-
$dark-increment: 5.7;
|
|
62
|
-
}
|
|
63
|
-
@else if $label == 'deep-purple' {
|
|
64
|
-
$light-increment: 8;
|
|
65
|
-
$dark-increment: 5.7;
|
|
66
|
-
}
|
|
67
|
-
@else if $label == 'light-blue' {
|
|
68
|
-
$light-increment: 7.8;
|
|
69
|
-
}
|
|
70
|
-
@else if $label == 'light-green' {
|
|
71
|
-
$light-increment: 6;
|
|
72
|
-
$light-offset: -5;
|
|
73
|
-
}
|
|
74
|
-
@else if $label == 'lime' {
|
|
75
|
-
$light-increment: 6.2;
|
|
76
|
-
$light-offset: -6;
|
|
77
|
-
}
|
|
78
|
-
@else if $label == 'yellow' {
|
|
79
|
-
$light-increment: 5.5;
|
|
80
|
-
$light-offset: -8;
|
|
81
|
-
}
|
|
82
|
-
@else if $label == 'purple' {
|
|
83
|
-
$light-increment: 6.5;
|
|
84
|
-
$light-offset: -8.5;
|
|
85
|
-
}
|
|
86
|
-
@else if $label == 'cyan' {
|
|
87
|
-
$light-increment: 9.4;
|
|
88
|
-
$light-offset: 6.5;
|
|
89
|
-
$dark-increment: 5.7;
|
|
90
|
-
}
|
|
91
|
-
@else if $label == 'teal' {
|
|
92
|
-
$light-increment: 9.6;
|
|
93
|
-
$light-offset: 5;
|
|
94
|
-
$dark-increment: 5.4;
|
|
95
|
-
}
|
|
96
|
-
@else if $label == 'blue' {
|
|
97
|
-
$light-increment: 6.8;
|
|
98
|
-
$dark-increment: 6.8;
|
|
99
|
-
}
|
|
100
|
-
@else if $label == 'brown' {
|
|
101
|
-
$light-increment: 8.8;
|
|
102
|
-
$dark-increment: 5;
|
|
103
|
-
}
|
|
104
|
-
.#{$label}-light#{$i}--bg {background-color: color.adjust($color, $lightness: $light-increment * $i * 1% - $light-offset);}
|
|
105
|
-
.#{$label}-light#{$i} {color: color.adjust($color, $lightness: $light-increment * $i * 1% - $light-offset);}
|
|
106
|
-
.#{$label}-dark#{$i}--bg {background-color: color.adjust($color, $lightness: - $dark-increment * $i * 1%);}
|
|
107
|
-
.#{$label}-dark#{$i} {color: color.adjust($color, $lightness: - $dark-increment * $i * 1%);}
|
|
35
|
+
.#{$label}-light#{$i}--bg {background-color: var(--w-#{$label}-light#{$i}-color);}
|
|
36
|
+
.#{$label}-light#{$i} {color: var(--w-#{$label}-light#{$i}-color);}
|
|
37
|
+
.#{$label}-dark#{$i}--bg {background-color: var(--w-#{$label}-dark#{$i}-color);}
|
|
38
|
+
.#{$label}-dark#{$i} {color: var(--w-#{$label}-dark#{$i}-color);}
|
|
108
39
|
}
|
|
109
40
|
}
|
|
110
41
|
|