wave-ui 3.27.1 → 3.28.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 +6 -0
- package/dist/types/types/components/WAccordion.d.ts +7 -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/WList.d.ts +7 -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/WTabs.d.ts +7 -0
- package/dist/types/types/components/WTag.d.ts +7 -0
- package/dist/types/types/components/index.d.ts +1 -0
- package/dist/wave-ui.cjs.js +3 -3
- package/dist/wave-ui.css +1 -1
- package/dist/wave-ui.esm.js +1440 -939
- package/dist/wave-ui.umd.js +3 -3
- package/package.json +6 -6
- package/src/wave-ui/components/w-accordion/index.vue +5 -1
- package/src/wave-ui/components/w-accordion/item.vue +42 -12
- package/src/wave-ui/components/w-breadcrumbs.vue +13 -2
- package/src/wave-ui/components/w-button/button.vue +15 -1
- package/src/wave-ui/components/w-button/index.vue +2 -1
- package/src/wave-ui/components/w-checkbox.vue +5 -1
- package/src/wave-ui/components/w-checkboxes.vue +5 -1
- package/src/wave-ui/components/w-input.vue +5 -1
- package/src/wave-ui/components/w-list.vue +12 -0
- package/src/wave-ui/components/w-radio.vue +6 -1
- package/src/wave-ui/components/w-radios.vue +5 -1
- package/src/wave-ui/components/w-rating.vue +5 -1
- package/src/wave-ui/components/w-scrollable.vue +667 -94
- package/src/wave-ui/components/w-select.vue +11 -7
- package/src/wave-ui/components/w-slider.vue +5 -1
- package/src/wave-ui/components/w-switch.vue +5 -1
- package/src/wave-ui/components/w-tabs/index.vue +10 -0
- package/src/wave-ui/components/w-tag.vue +14 -0
- package/src/wave-ui/components/w-textarea.vue +5 -1
- package/src/wave-ui/core.js +2 -0
- package/src/wave-ui/mixins/form-elements.js +5 -8
- package/src/wave-ui/mixins/ripple.js +39 -0
- package/src/wave-ui/scss/_ripple.scss +37 -0
- package/src/wave-ui/scss/index.scss +1 -0
- package/src/wave-ui/scss/variables/_variables.scss +0 -2
- package/src/wave-ui/utils/config.js +2 -0
- package/src/wave-ui/utils/ripple.js +71 -0
- package/src/wave-ui/utils/wave-ripple-directive.js +40 -0
|
@@ -108,12 +108,21 @@ component(
|
|
|
108
108
|
* @todo Share the common parts between w-input, w-textarea & w-select.
|
|
109
109
|
**/
|
|
110
110
|
|
|
111
|
-
import
|
|
111
|
+
import { computed } from 'vue'
|
|
112
|
+
import FormElementMixin, { useWaveUiFormIds } from '../mixins/form-elements'
|
|
112
113
|
|
|
113
114
|
export default {
|
|
114
115
|
name: 'w-select',
|
|
115
116
|
mixins: [FormElementMixin],
|
|
116
117
|
|
|
118
|
+
setup (props) {
|
|
119
|
+
const { waveUiUseId } = useWaveUiFormIds()
|
|
120
|
+
const selectListId = computed(() =>
|
|
121
|
+
props.id ? `${props.id}__listbox` : `w-select-menu--${waveUiUseId}`
|
|
122
|
+
)
|
|
123
|
+
return { waveUiUseId, selectListId }
|
|
124
|
+
},
|
|
125
|
+
|
|
117
126
|
props: {
|
|
118
127
|
items: { type: Array, required: true },
|
|
119
128
|
modelValue: {}, // v-model on selected item if any.
|
|
@@ -147,7 +156,7 @@ export default {
|
|
|
147
156
|
light: { type: Boolean },
|
|
148
157
|
fitToContent: { type: Boolean }
|
|
149
158
|
// Props from mixin: id, name, disabled, readonly, required, tabindex, validators.
|
|
150
|
-
// Computed from mixin: inputId,
|
|
159
|
+
// Computed from mixin: inputId, inputName, isDisabled & isReadonly. `selectListId` from setup.
|
|
151
160
|
},
|
|
152
161
|
|
|
153
162
|
emits: ['input', 'update:modelValue', 'focus', 'blur', 'item-click', 'item-select', 'click:inner-icon-left', 'click:inner-icon-right'],
|
|
@@ -166,11 +175,6 @@ export default {
|
|
|
166
175
|
}),
|
|
167
176
|
|
|
168
177
|
computed: {
|
|
169
|
-
/** Prefix for w-list item ids (`id_item-N`) and ARIA listbox linkage; stable with SSR. */
|
|
170
|
-
selectListId () {
|
|
171
|
-
return this.id ? `${this.id}__listbox` : `w-select-menu--${this.waveUiUseId}`
|
|
172
|
-
},
|
|
173
|
-
|
|
174
178
|
// Check all the items and add a `value` if missing, containing either: value, label or index
|
|
175
179
|
// in this order.
|
|
176
180
|
selectItems () {
|
|
@@ -78,12 +78,16 @@ component(
|
|
|
78
78
|
</template>
|
|
79
79
|
|
|
80
80
|
<script>
|
|
81
|
-
import FormElementMixin from '../mixins/form-elements'
|
|
81
|
+
import FormElementMixin, { useWaveUiFormIds } from '../mixins/form-elements'
|
|
82
82
|
|
|
83
83
|
export default {
|
|
84
84
|
name: 'w-slider',
|
|
85
85
|
mixins: [FormElementMixin],
|
|
86
86
|
|
|
87
|
+
setup () {
|
|
88
|
+
return useWaveUiFormIds()
|
|
89
|
+
},
|
|
90
|
+
|
|
87
91
|
props: {
|
|
88
92
|
modelValue: { type: Number, default: 0 },
|
|
89
93
|
color: { type: String, default: 'primary' },
|
|
@@ -53,13 +53,17 @@ component(
|
|
|
53
53
|
</template>
|
|
54
54
|
|
|
55
55
|
<script>
|
|
56
|
-
import FormElementMixin from '../mixins/form-elements'
|
|
56
|
+
import FormElementMixin, { useWaveUiFormIds } from '../mixins/form-elements'
|
|
57
57
|
|
|
58
58
|
export default {
|
|
59
59
|
name: 'w-switch',
|
|
60
60
|
mixins: [FormElementMixin],
|
|
61
61
|
inheritAttrs: false, // The attrs should only be added to the input not the wrapper.
|
|
62
62
|
|
|
63
|
+
setup () {
|
|
64
|
+
return useWaveUiFormIds()
|
|
65
|
+
},
|
|
66
|
+
|
|
63
67
|
props: {
|
|
64
68
|
modelValue: { default: false }, // v-model.
|
|
65
69
|
label: { type: String, default: '' },
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
v-for="(item, i) in tabs"
|
|
6
6
|
:key="i"
|
|
7
7
|
:class="barItemClasses(item)"
|
|
8
|
+
@pointerdown="onTabsBarPointerDown($event, item)"
|
|
8
9
|
@click="!item._disabled && item._uid !== activeTabUid && openTab(item._uid)"
|
|
9
10
|
@focus="$emit('focus', getOriginalItem(item))"
|
|
10
11
|
:tabindex="!item._disabled && 0"
|
|
@@ -73,11 +74,14 @@
|
|
|
73
74
|
<script>
|
|
74
75
|
import { useId } from 'vue'
|
|
75
76
|
import { objectifyClasses } from '../../utils/index'
|
|
77
|
+
import RippleMixin from '../../mixins/ripple'
|
|
76
78
|
import TabContent from './tab-content.vue'
|
|
77
79
|
|
|
78
80
|
export default {
|
|
79
81
|
name: 'w-tabs',
|
|
80
82
|
|
|
83
|
+
mixins: [RippleMixin],
|
|
84
|
+
|
|
81
85
|
setup () {
|
|
82
86
|
return { tabsStableId: useId() }
|
|
83
87
|
},
|
|
@@ -235,6 +239,7 @@ export default {
|
|
|
235
239
|
const isActive = item._index === this.activeTabIndex
|
|
236
240
|
|
|
237
241
|
return {
|
|
242
|
+
'w-ripple': this.rippleActive,
|
|
238
243
|
[`${this.bgColor}--bg`]: this.bgColor,
|
|
239
244
|
[this.color]: this.color && !item._disabled && !(this.activeClass && isActive),
|
|
240
245
|
'w-tabs__bar-item--active': isActive,
|
|
@@ -244,6 +249,11 @@ export default {
|
|
|
244
249
|
}
|
|
245
250
|
},
|
|
246
251
|
|
|
252
|
+
onTabsBarPointerDown (e, item) {
|
|
253
|
+
if (item._disabled) return
|
|
254
|
+
this.onRipple(e)
|
|
255
|
+
},
|
|
256
|
+
|
|
247
257
|
// Switching tabs.
|
|
248
258
|
openTab (uid) {
|
|
249
259
|
this.prevTabIndex = this.activeTabIndex // To resolve the transition direction.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<template lang="pug">
|
|
2
2
|
span.w-tag(
|
|
3
3
|
@click="$emit('update:modelValue', !modelValue);$emit('input', !modelValue)"
|
|
4
|
+
@pointerdown="onTagPointerDown"
|
|
4
5
|
@keypress.enter="$emit('update:modelValue', !modelValue);$emit('input', !modelValue)"
|
|
5
6
|
:class="classes"
|
|
6
7
|
:role="modelValue !== -1 && 'button'"
|
|
@@ -17,9 +18,13 @@ span.w-tag(
|
|
|
17
18
|
</template>
|
|
18
19
|
|
|
19
20
|
<script>
|
|
21
|
+
import RippleMixin from '../mixins/ripple'
|
|
22
|
+
|
|
20
23
|
export default {
|
|
21
24
|
name: 'w-tag',
|
|
22
25
|
|
|
26
|
+
mixins: [RippleMixin],
|
|
27
|
+
|
|
23
28
|
props: {
|
|
24
29
|
modelValue: { type: [Boolean, Number], default: -1 },
|
|
25
30
|
color: { type: String },
|
|
@@ -58,6 +63,7 @@ export default {
|
|
|
58
63
|
[this.color]: this.color,
|
|
59
64
|
[`${this.bgColor}--bg`]: this.bgColor,
|
|
60
65
|
[`size--${this.presetSize}`]: true,
|
|
66
|
+
'w-ripple': this.rippleActive && this.modelValue !== -1,
|
|
61
67
|
'w-tag--dark': this.dark,
|
|
62
68
|
'w-tag--light': this.light,
|
|
63
69
|
'w-tag--clickable': this.modelValue !== -1,
|
|
@@ -74,6 +80,14 @@ export default {
|
|
|
74
80
|
height: (!isNaN(this.height) ? `${this.height}px` : this.height) || null
|
|
75
81
|
}
|
|
76
82
|
}
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
methods: {
|
|
86
|
+
onTagPointerDown (e) {
|
|
87
|
+
if (this.modelValue === -1) return
|
|
88
|
+
if (e.target.closest?.('.w-tag__closable')) return
|
|
89
|
+
this.onRipple(e)
|
|
90
|
+
}
|
|
77
91
|
}
|
|
78
92
|
}
|
|
79
93
|
</script>
|
|
@@ -67,13 +67,17 @@ component(
|
|
|
67
67
|
* @todo Share the common parts between w-input, w-textarea & w-select.
|
|
68
68
|
**/
|
|
69
69
|
|
|
70
|
-
import FormElementMixin from '../mixins/form-elements'
|
|
70
|
+
import FormElementMixin, { useWaveUiFormIds } from '../mixins/form-elements'
|
|
71
71
|
|
|
72
72
|
export default {
|
|
73
73
|
name: 'w-textarea',
|
|
74
74
|
mixins: [FormElementMixin],
|
|
75
75
|
inheritAttrs: false, // The attrs should only be added to the textarea not the wrapper.
|
|
76
76
|
|
|
77
|
+
setup () {
|
|
78
|
+
return useWaveUiFormIds()
|
|
79
|
+
},
|
|
80
|
+
|
|
77
81
|
props: {
|
|
78
82
|
modelValue: { default: '' },
|
|
79
83
|
label: { type: String },
|
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
|
|
@@ -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 || {}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { useId } from 'vue'
|
|
2
2
|
|
|
3
|
+
/** SSR-safe id suffix; call from each component's `setup()` (mixin `setup` is not reliably merged). */
|
|
4
|
+
export function useWaveUiFormIds () {
|
|
5
|
+
return { waveUiUseId: useId() }
|
|
6
|
+
}
|
|
7
|
+
|
|
3
8
|
export default {
|
|
4
9
|
inject: {
|
|
5
10
|
// Used in each form component to determine whether to use the w-form-element wrap or not.
|
|
@@ -9,14 +14,6 @@ export default {
|
|
|
9
14
|
formProps: { default: () => ({ disabled: false, readonly: false }) }
|
|
10
15
|
},
|
|
11
16
|
|
|
12
|
-
setup () {
|
|
13
|
-
return {
|
|
14
|
-
// SSR-safe unique suffix (Vue 3.5+). Must not start with `_`/`$` or Vue omits it from the
|
|
15
|
-
// render context and template/slot access can warn or fail (see exposeSetupStateOnRenderContext).
|
|
16
|
-
waveUiUseId: useId()
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
|
|
20
17
|
props: {
|
|
21
18
|
/** When set, used as the DOM `id` and for associated labels (`for`). SSR-safe when provided. */
|
|
22
19
|
id: { type: String },
|
|
@@ -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) {
|
|
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)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Pointer ripple (v-wave-ripple + components using applyRipple).
|
|
2
|
+
// Note: avoid overflow:hidden here so hosts like w-button keep focus rings that extend past the box.
|
|
3
|
+
.w-ripple {
|
|
4
|
+
position: relative;
|
|
5
|
+
|
|
6
|
+
.w-ripple__ink-container {
|
|
7
|
+
position: absolute;
|
|
8
|
+
inset: 0;
|
|
9
|
+
overflow: hidden;
|
|
10
|
+
border-radius: inherit;
|
|
11
|
+
pointer-events: none;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.w-ripple__ink {
|
|
16
|
+
position: absolute;
|
|
17
|
+
z-index: 2; // Above .w-button:before hover overlay (explicit z-index:0 on :before keeps ink visible).
|
|
18
|
+
border-radius: 50%;
|
|
19
|
+
pointer-events: none;
|
|
20
|
+
transform: scale(0);
|
|
21
|
+
opacity: 0.5;
|
|
22
|
+
// currentColor is often invisible on filled buttons (e.g. white on white); use a neutral ink.
|
|
23
|
+
background-color: rgba(#000, 0.22);
|
|
24
|
+
will-change: transform, opacity;
|
|
25
|
+
animation: w-ripple-ink-expand 0.65s ease-out forwards;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
html[data-theme='dark'] .w-ripple__ink {
|
|
29
|
+
background-color: rgba(#fff, 0.2);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@keyframes w-ripple-ink-expand {
|
|
33
|
+
to {
|
|
34
|
+
transform: scale(1);
|
|
35
|
+
opacity: 0;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -152,6 +152,4 @@ $tooltip-border-color: $border-color !default;
|
|
|
152
152
|
// w-scrollable.
|
|
153
153
|
// --------------------------------------------------------
|
|
154
154
|
$scrollbar-size: 8px !default;
|
|
155
|
-
$scrollbar-bg-color: color-mix(in srgb, var(--w-contrast-bg-color) 8%, var(--w-base-bg-color)) !default;
|
|
156
|
-
$scrollbar-thumb-color: color-mix(in srgb, var(--w-contrast-bg-color) 25%, var(--w-base-bg-color)) !default;
|
|
157
155
|
// --------------------------------------------------------
|
|
@@ -57,6 +57,8 @@ const config = reactive({
|
|
|
57
57
|
align: 'right',
|
|
58
58
|
transition: 'default' // Sliding from the side by default.
|
|
59
59
|
},
|
|
60
|
+
// Default ripple for v-wave-ripple and components (unless their `no-ripple` prop is set).
|
|
61
|
+
ripple: true,
|
|
60
62
|
presets: {} // User presets for each component.
|
|
61
63
|
})
|
|
62
64
|
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Material-style pointer ripple: appends a transient node at the pointer, removes after animation.
|
|
3
|
+
* SSR-safe: no-op when window/document are unavailable.
|
|
4
|
+
*
|
|
5
|
+
* @param {HTMLElement} hostEl
|
|
6
|
+
* @param {PointerEvent|MouseEvent|TouchEvent} event
|
|
7
|
+
* @param {{ disabled?: boolean }} [options]
|
|
8
|
+
*/
|
|
9
|
+
export function applyRipple (hostEl, event, options = {}) {
|
|
10
|
+
if (typeof window === 'undefined' || typeof document === 'undefined') return
|
|
11
|
+
if (!hostEl?.ownerDocument || options.disabled) return
|
|
12
|
+
if (window.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches) return
|
|
13
|
+
|
|
14
|
+
let clientX = typeof event.clientX === 'number' ? event.clientX : null
|
|
15
|
+
let clientY = typeof event.clientY === 'number' ? event.clientY : null
|
|
16
|
+
if ((clientX == null || clientY == null) && event.touches?.length) {
|
|
17
|
+
clientX = event.touches[0].clientX
|
|
18
|
+
clientY = event.touches[0].clientY
|
|
19
|
+
}
|
|
20
|
+
if (clientX == null || clientY == null) return
|
|
21
|
+
|
|
22
|
+
const rect = hostEl.getBoundingClientRect()
|
|
23
|
+
const maxSide = Math.max(rect.width, rect.height)
|
|
24
|
+
const size = Math.max(maxSide * 2.5, 48)
|
|
25
|
+
const x = clientX - rect.left - size / 2
|
|
26
|
+
const y = clientY - rect.top - size / 2
|
|
27
|
+
|
|
28
|
+
const inkContainer = document.createElement('span')
|
|
29
|
+
inkContainer.className = 'w-ripple__ink-container'
|
|
30
|
+
inkContainer.setAttribute('aria-hidden', 'true')
|
|
31
|
+
|
|
32
|
+
const ink = document.createElement('span')
|
|
33
|
+
ink.className = 'w-ripple__ink'
|
|
34
|
+
ink.style.width = `${size}px`
|
|
35
|
+
ink.style.height = `${size}px`
|
|
36
|
+
ink.style.left = `${x}px`
|
|
37
|
+
ink.style.top = `${y}px`
|
|
38
|
+
|
|
39
|
+
inkContainer.appendChild(ink)
|
|
40
|
+
hostEl.appendChild(inkContainer)
|
|
41
|
+
|
|
42
|
+
const done = () => {
|
|
43
|
+
ink.removeEventListener('animationend', done)
|
|
44
|
+
inkContainer.remove()
|
|
45
|
+
}
|
|
46
|
+
ink.addEventListener('animationend', done, { once: true })
|
|
47
|
+
// Fallback if animationend does not fire
|
|
48
|
+
window.setTimeout(() => {
|
|
49
|
+
if (inkContainer.parentNode === hostEl) inkContainer.remove()
|
|
50
|
+
}, 600)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @param {import('vue').ComponentPublicInstance | null | undefined} instance
|
|
55
|
+
* @returns {{ config: { ripple?: boolean } }}
|
|
56
|
+
*/
|
|
57
|
+
export function getWaveUiFromInstance (instance) {
|
|
58
|
+
const $waveui = instance?.$waveui
|
|
59
|
+
if ($waveui && typeof $waveui === 'object') return $waveui
|
|
60
|
+
return { config: { ripple: true } }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @param {boolean|undefined} propRipple
|
|
65
|
+
* @param {{ config?: { ripple?: boolean } }} $waveui
|
|
66
|
+
*/
|
|
67
|
+
export function isRippleEnabled (propRipple, $waveui) {
|
|
68
|
+
if (typeof propRipple === 'boolean') return propRipple
|
|
69
|
+
const g = $waveui?.config?.ripple
|
|
70
|
+
return g !== false
|
|
71
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { applyRipple, getWaveUiFromInstance, isRippleEnabled } from './ripple'
|
|
2
|
+
|
|
3
|
+
function bindingDisabled (binding) {
|
|
4
|
+
const v = binding.value
|
|
5
|
+
if (v === false) return true
|
|
6
|
+
if (v && typeof v === 'object' && v.disabled) return true
|
|
7
|
+
return false
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function rippleAllowed (binding, instance) {
|
|
11
|
+
if (bindingDisabled(binding)) return false
|
|
12
|
+
const $waveui = getWaveUiFromInstance(instance)
|
|
13
|
+
if (binding.value === true) return isRippleEnabled(true, $waveui)
|
|
14
|
+
return isRippleEnabled(undefined, $waveui)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const waveRippleDirective = {
|
|
18
|
+
mounted (el, binding) {
|
|
19
|
+
el.classList.add('w-ripple')
|
|
20
|
+
el.__waveRippleLastBinding = binding
|
|
21
|
+
const handler = e => {
|
|
22
|
+
if (typeof e.button === 'number' && e.button !== 0) return
|
|
23
|
+
const b = el.__waveRippleLastBinding
|
|
24
|
+
if (!rippleAllowed(b, b?.instance)) return
|
|
25
|
+
applyRipple(el, e)
|
|
26
|
+
}
|
|
27
|
+
el.__waveRippleHandler = handler
|
|
28
|
+
el.addEventListener('pointerdown', handler)
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
updated (el, binding) {
|
|
32
|
+
el.__waveRippleLastBinding = binding
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
unmounted (el) {
|
|
36
|
+
el.removeEventListener('pointerdown', el.__waveRippleHandler)
|
|
37
|
+
delete el.__waveRippleHandler
|
|
38
|
+
delete el.__waveRippleLastBinding
|
|
39
|
+
}
|
|
40
|
+
}
|