webcake-ui-kit 1.0.28 → 1.0.29
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/package.json +3 -1
- package/src/components/avatar/avatar.css +1 -1
- package/src/components/avatar-stack/AvatarStack.vue +15 -9
- package/src/components/avatar-stack/avatar-stack.css +27 -25
- package/src/components/badge/badge.css +7 -2
- package/src/components/dialog/Dialog.vue +11 -6
- package/src/components/dropdown/Dropdown.vue +135 -0
- package/src/components/dropdown/DropdownMenuItems.vue +128 -0
- package/src/components/dropdown/dropdown-menu-items.css +116 -0
- package/src/components/dropdown/dropdown.css +3 -0
- package/src/components/menu/Menu.vue +218 -0
- package/src/components/menu/menu.css +64 -0
- package/src/components/select/Select.vue +62 -131
- package/src/components/select/select.css +9 -51
- package/src/components/select-option/SelectOption.vue +15 -19
- package/src/components/sidebar-item/sidebar_item.css +4 -3
- package/src/components/tooltip/Tooltip.vue +12 -3
- package/src/floating/click-outside-mixin.js +47 -0
- package/src/floating/compute-position.js +64 -0
- package/src/floating/focus-mixin.js +35 -0
- package/src/floating/keyboard-mixin.js +92 -0
- package/src/floating/layer-manager.js +22 -0
- package/src/floating/portal-mixin.js +45 -0
- package/src/floating/portal-root.js +44 -0
- package/src/floating/position-mixin.js +74 -0
- package/src/index.js +2 -0
- package/src/styles/color_general.css +90 -86
|
@@ -31,6 +31,8 @@
|
|
|
31
31
|
</template>
|
|
32
32
|
|
|
33
33
|
<script>
|
|
34
|
+
import { acquire, release } from '../../floating/portal-root.js'
|
|
35
|
+
|
|
34
36
|
const GAP = 4
|
|
35
37
|
|
|
36
38
|
export default {
|
|
@@ -83,7 +85,10 @@ export default {
|
|
|
83
85
|
},
|
|
84
86
|
mounted() {
|
|
85
87
|
if (typeof document !== 'undefined' && this.$refs.tooltip) {
|
|
86
|
-
|
|
88
|
+
this._tooltipPortalRoot = acquire()
|
|
89
|
+
if (this._tooltipPortalRoot) {
|
|
90
|
+
this._tooltipPortalRoot.appendChild(this.$refs.tooltip)
|
|
91
|
+
}
|
|
87
92
|
}
|
|
88
93
|
window.addEventListener('scroll', this.onScrollOrResize, true)
|
|
89
94
|
window.addEventListener('resize', this.onScrollOrResize)
|
|
@@ -138,8 +143,12 @@ export default {
|
|
|
138
143
|
cleanup() {
|
|
139
144
|
window.removeEventListener('scroll', this.onScrollOrResize, true)
|
|
140
145
|
window.removeEventListener('resize', this.onScrollOrResize)
|
|
141
|
-
if (this.$refs.tooltip && this.$refs.tooltip.parentNode
|
|
142
|
-
|
|
146
|
+
if (this.$refs.tooltip && this.$refs.tooltip.parentNode) {
|
|
147
|
+
this.$refs.tooltip.parentNode.removeChild(this.$refs.tooltip)
|
|
148
|
+
}
|
|
149
|
+
if (this._tooltipPortalRoot) {
|
|
150
|
+
release()
|
|
151
|
+
this._tooltipPortalRoot = null
|
|
143
152
|
}
|
|
144
153
|
}
|
|
145
154
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Listens for pointerdown anywhere in the document and calls the component's
|
|
2
|
+
// _coOnOutside() if the event target is not inside either the trigger or the
|
|
3
|
+
// floating element. Uses pointerdown (not mousedown) so it covers touch + pen
|
|
4
|
+
// + mouse and fires before focus moves.
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
data() {
|
|
8
|
+
return { coAttached: false }
|
|
9
|
+
},
|
|
10
|
+
methods: {
|
|
11
|
+
_coSetElements(trigger, floating) {
|
|
12
|
+
this._coTriggerEl = trigger
|
|
13
|
+
this._coFloatEl = floating
|
|
14
|
+
},
|
|
15
|
+
_coAttach() {
|
|
16
|
+
if (typeof document === 'undefined' || this.coAttached) return
|
|
17
|
+
this._coHandler = e => {
|
|
18
|
+
const trigger = this._coTriggerEl
|
|
19
|
+
const floating = this._coFloatEl
|
|
20
|
+
const path = typeof e.composedPath === 'function' ? e.composedPath() : []
|
|
21
|
+
const target = e.target
|
|
22
|
+
const insideTrigger =
|
|
23
|
+
trigger && (path.indexOf(trigger) !== -1 || (trigger.contains && trigger.contains(target)))
|
|
24
|
+
const insideFloat =
|
|
25
|
+
floating && (path.indexOf(floating) !== -1 || (floating.contains && floating.contains(target)))
|
|
26
|
+
if (!insideTrigger && !insideFloat) this._coOnOutside(e)
|
|
27
|
+
}
|
|
28
|
+
document.addEventListener('pointerdown', this._coHandler, true)
|
|
29
|
+
this.coAttached = true
|
|
30
|
+
},
|
|
31
|
+
_coDetach() {
|
|
32
|
+
if (typeof document === 'undefined' || !this.coAttached) return
|
|
33
|
+
document.removeEventListener('pointerdown', this._coHandler, true)
|
|
34
|
+
this._coHandler = null
|
|
35
|
+
this.coAttached = false
|
|
36
|
+
},
|
|
37
|
+
_coOnOutside() {
|
|
38
|
+
// Override in component.
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
beforeDestroy() {
|
|
42
|
+
this._coDetach()
|
|
43
|
+
},
|
|
44
|
+
beforeUnmount() {
|
|
45
|
+
this._coDetach()
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// Pure positioning function — no DOM mutation, no side effects.
|
|
2
|
+
// Inputs are DOMRect-shaped objects; output is { top, left, placement } in
|
|
3
|
+
// viewport coordinates suitable for `position: fixed`.
|
|
4
|
+
|
|
5
|
+
function parsePlacement(p) {
|
|
6
|
+
const parts = String(p || 'bottom-start').split('-')
|
|
7
|
+
const side = ['top', 'bottom', 'left', 'right'].indexOf(parts[0]) === -1 ? 'bottom' : parts[0]
|
|
8
|
+
const align = ['start', 'end'].indexOf(parts[1]) === -1 ? 'center' : parts[1]
|
|
9
|
+
return { side, align }
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function alignCross(refRect, floatRect, align, axis) {
|
|
13
|
+
if (axis === 'horizontal') {
|
|
14
|
+
if (align === 'start') return refRect.left
|
|
15
|
+
if (align === 'end') return refRect.right - floatRect.width
|
|
16
|
+
return refRect.left + (refRect.width - floatRect.width) / 2
|
|
17
|
+
}
|
|
18
|
+
if (align === 'start') return refRect.top
|
|
19
|
+
if (align === 'end') return refRect.bottom - floatRect.height
|
|
20
|
+
return refRect.top + (refRect.height - floatRect.height) / 2
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function computePosition(refRect, floatRect, placement, options) {
|
|
24
|
+
const opts = options || {}
|
|
25
|
+
const offset = typeof opts.offset === 'number' ? opts.offset : 4
|
|
26
|
+
const padding = typeof opts.padding === 'number' ? opts.padding : 8
|
|
27
|
+
const vw = opts.viewportWidth || (typeof window !== 'undefined' ? window.innerWidth : 1024)
|
|
28
|
+
const vh = opts.viewportHeight || (typeof window !== 'undefined' ? window.innerHeight : 768)
|
|
29
|
+
|
|
30
|
+
let { side, align } = parsePlacement(placement)
|
|
31
|
+
|
|
32
|
+
// Flip when the requested side has no room and the opposite does.
|
|
33
|
+
const fitsTop = refRect.top - floatRect.height - offset >= padding
|
|
34
|
+
const fitsBottom = refRect.bottom + floatRect.height + offset <= vh - padding
|
|
35
|
+
const fitsLeft = refRect.left - floatRect.width - offset >= padding
|
|
36
|
+
const fitsRight = refRect.right + floatRect.width + offset <= vw - padding
|
|
37
|
+
|
|
38
|
+
if (side === 'top' && !fitsTop && fitsBottom) side = 'bottom'
|
|
39
|
+
else if (side === 'bottom' && !fitsBottom && fitsTop) side = 'top'
|
|
40
|
+
else if (side === 'left' && !fitsLeft && fitsRight) side = 'right'
|
|
41
|
+
else if (side === 'right' && !fitsRight && fitsLeft) side = 'left'
|
|
42
|
+
|
|
43
|
+
let top = 0
|
|
44
|
+
let left = 0
|
|
45
|
+
|
|
46
|
+
if (side === 'top') {
|
|
47
|
+
top = refRect.top - floatRect.height - offset
|
|
48
|
+
left = alignCross(refRect, floatRect, align, 'horizontal')
|
|
49
|
+
} else if (side === 'bottom') {
|
|
50
|
+
top = refRect.bottom + offset
|
|
51
|
+
left = alignCross(refRect, floatRect, align, 'horizontal')
|
|
52
|
+
} else if (side === 'left') {
|
|
53
|
+
left = refRect.left - floatRect.width - offset
|
|
54
|
+
top = alignCross(refRect, floatRect, align, 'vertical')
|
|
55
|
+
} else {
|
|
56
|
+
left = refRect.right + offset
|
|
57
|
+
top = alignCross(refRect, floatRect, align, 'vertical')
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
left = Math.max(padding, Math.min(left, vw - floatRect.width - padding))
|
|
61
|
+
top = Math.max(padding, Math.min(top, vh - floatRect.height - padding))
|
|
62
|
+
|
|
63
|
+
return { top, left, placement: align === 'center' ? side : side + '-' + align }
|
|
64
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Save the currently focused element on open, restore it on close.
|
|
2
|
+
// This is RESTORE only — there is no focus trap. Menus and dropdowns should
|
|
3
|
+
// not trap focus (closing on Tab is the expected behavior). Dialogs trap;
|
|
4
|
+
// menus don't. Don't add a trap "for consistency" — they are different
|
|
5
|
+
// patterns by design.
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
data() {
|
|
9
|
+
return { fmRestoreTarget: null }
|
|
10
|
+
},
|
|
11
|
+
methods: {
|
|
12
|
+
_fmCapture() {
|
|
13
|
+
if (typeof document === 'undefined') return
|
|
14
|
+
this.fmRestoreTarget = document.activeElement
|
|
15
|
+
},
|
|
16
|
+
_fmFocus(el) {
|
|
17
|
+
if (el && typeof el.focus === 'function') {
|
|
18
|
+
el.focus()
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
_fmRestore() {
|
|
22
|
+
const t = this.fmRestoreTarget
|
|
23
|
+
this.fmRestoreTarget = null
|
|
24
|
+
if (
|
|
25
|
+
t &&
|
|
26
|
+
typeof t.focus === 'function' &&
|
|
27
|
+
typeof document !== 'undefined' &&
|
|
28
|
+
document.contains &&
|
|
29
|
+
document.contains(t)
|
|
30
|
+
) {
|
|
31
|
+
t.focus()
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// Keyboard navigation for menu-like floating layers. Items are discovered by
|
|
2
|
+
// querying the floating element's DOM for [data-wk-menu-item]:not([data-disabled])
|
|
3
|
+
// at the moment a key is pressed — this avoids registration-array bugs when
|
|
4
|
+
// items are added/removed via v-if. kmActiveIndex is clamped before every use.
|
|
5
|
+
|
|
6
|
+
const KEYS = {
|
|
7
|
+
ArrowDown: 'down',
|
|
8
|
+
Down: 'down',
|
|
9
|
+
ArrowUp: 'up',
|
|
10
|
+
Up: 'up',
|
|
11
|
+
Home: 'home',
|
|
12
|
+
End: 'end',
|
|
13
|
+
Escape: 'escape',
|
|
14
|
+
Esc: 'escape',
|
|
15
|
+
Enter: 'enter',
|
|
16
|
+
' ': 'space',
|
|
17
|
+
Spacebar: 'space',
|
|
18
|
+
Tab: 'tab'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default {
|
|
22
|
+
data() {
|
|
23
|
+
return { kmActiveIndex: -1 }
|
|
24
|
+
},
|
|
25
|
+
methods: {
|
|
26
|
+
_kmSetFloatEl(el) {
|
|
27
|
+
this._kmFloatEl = el
|
|
28
|
+
},
|
|
29
|
+
_kmListItems() {
|
|
30
|
+
const float = this._kmFloatEl
|
|
31
|
+
if (!float || typeof float.querySelectorAll !== 'function') return []
|
|
32
|
+
const nodes = float.querySelectorAll('[data-wk-menu-item]:not([data-disabled])')
|
|
33
|
+
const arr = []
|
|
34
|
+
for (let i = 0; i < nodes.length; i++) arr.push(nodes[i])
|
|
35
|
+
return arr
|
|
36
|
+
},
|
|
37
|
+
_kmReset() {
|
|
38
|
+
this.kmActiveIndex = -1
|
|
39
|
+
},
|
|
40
|
+
_kmOnKeydown(e) {
|
|
41
|
+
const k = KEYS[e.key]
|
|
42
|
+
if (!k) return
|
|
43
|
+
if (k === 'escape') {
|
|
44
|
+
e.preventDefault()
|
|
45
|
+
this._kmOnEscape(e)
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
if (k === 'tab') {
|
|
49
|
+
this._kmOnTab(e)
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
const items = this._kmListItems()
|
|
53
|
+
if (!items.length) return
|
|
54
|
+
let idx = this.kmActiveIndex
|
|
55
|
+
if (k === 'down') {
|
|
56
|
+
idx = idx < 0 ? 0 : (idx + 1) % items.length
|
|
57
|
+
e.preventDefault()
|
|
58
|
+
} else if (k === 'up') {
|
|
59
|
+
idx = idx < 0 ? items.length - 1 : (idx - 1 + items.length) % items.length
|
|
60
|
+
e.preventDefault()
|
|
61
|
+
} else if (k === 'home') {
|
|
62
|
+
idx = 0
|
|
63
|
+
e.preventDefault()
|
|
64
|
+
} else if (k === 'end') {
|
|
65
|
+
idx = items.length - 1
|
|
66
|
+
e.preventDefault()
|
|
67
|
+
} else if (k === 'enter' || k === 'space') {
|
|
68
|
+
if (idx >= 0 && idx < items.length) {
|
|
69
|
+
e.preventDefault()
|
|
70
|
+
items[idx].click()
|
|
71
|
+
}
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
idx = Math.max(0, Math.min(idx, items.length - 1))
|
|
75
|
+
this.kmActiveIndex = idx
|
|
76
|
+
this._kmFocusActive()
|
|
77
|
+
},
|
|
78
|
+
_kmFocusActive() {
|
|
79
|
+
const items = this._kmListItems()
|
|
80
|
+
const idx = this.kmActiveIndex
|
|
81
|
+
if (idx >= 0 && idx < items.length && items[idx] && typeof items[idx].focus === 'function') {
|
|
82
|
+
items[idx].focus()
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
_kmOnEscape() {
|
|
86
|
+
// Override in component.
|
|
87
|
+
},
|
|
88
|
+
_kmOnTab() {
|
|
89
|
+
// Override in component.
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Monotonic z-index counter shared across every floating component.
|
|
2
|
+
// Each opening of a Menu / Dropdown / Select calls nextZIndex() and applies
|
|
3
|
+
// the result inline. Closing does NOT decrement — keeping the counter
|
|
4
|
+
// monotonic avoids fights between concurrent opens (matches Radix UI's
|
|
5
|
+
// approach).
|
|
6
|
+
|
|
7
|
+
const BASE = 1000
|
|
8
|
+
let _counter = BASE
|
|
9
|
+
|
|
10
|
+
export function nextZIndex() {
|
|
11
|
+
_counter++
|
|
12
|
+
return _counter
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function currentZIndex() {
|
|
16
|
+
return _counter
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Test helper — reset between specs.
|
|
20
|
+
export function _reset() {
|
|
21
|
+
_counter = BASE
|
|
22
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Moves an element into the shared portal root on demand. Refcounts acquire/
|
|
2
|
+
// release so the portal root is cleaned up when nothing is using it. Used by
|
|
3
|
+
// Menu; reusable by future Popover/Tooltip/ContextMenu migrations.
|
|
4
|
+
//
|
|
5
|
+
// Non-reactive instance refs use leading underscore so they don't enter Vue's
|
|
6
|
+
// reactivity system. Reactive data is namespaced with the `pmHost` prefix to
|
|
7
|
+
// avoid collisions with other mixins.
|
|
8
|
+
|
|
9
|
+
import { acquire, release } from './portal-root.js'
|
|
10
|
+
|
|
11
|
+
export default {
|
|
12
|
+
data() {
|
|
13
|
+
return { pmHostMounted: false }
|
|
14
|
+
},
|
|
15
|
+
methods: {
|
|
16
|
+
_pmHostMount(el) {
|
|
17
|
+
if (!el || typeof document === 'undefined') return
|
|
18
|
+
const root = acquire()
|
|
19
|
+
if (!root) return
|
|
20
|
+
if (el.parentNode !== root) root.appendChild(el)
|
|
21
|
+
this._pmHostAcquired = true
|
|
22
|
+
this.pmHostMounted = true
|
|
23
|
+
},
|
|
24
|
+
_pmHostUnmount(el) {
|
|
25
|
+
if (el && el.parentNode) el.parentNode.removeChild(el)
|
|
26
|
+
if (this._pmHostAcquired) {
|
|
27
|
+
release()
|
|
28
|
+
this._pmHostAcquired = false
|
|
29
|
+
}
|
|
30
|
+
this.pmHostMounted = false
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
beforeDestroy() {
|
|
34
|
+
if (this._pmHostAcquired) {
|
|
35
|
+
release()
|
|
36
|
+
this._pmHostAcquired = false
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
beforeUnmount() {
|
|
40
|
+
if (this._pmHostAcquired) {
|
|
41
|
+
release()
|
|
42
|
+
this._pmHostAcquired = false
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Shared portal root for floating components (Menu, Dropdown, Select, and any
|
|
2
|
+
// future Popover/Tooltip/ContextMenu/Combobox). All access is lazy and inside
|
|
3
|
+
// functions — never at module top level — so SSR consumers don't blow up on
|
|
4
|
+
// import.
|
|
5
|
+
|
|
6
|
+
let _root = null
|
|
7
|
+
let _refCount = 0
|
|
8
|
+
|
|
9
|
+
export function ensureRoot() {
|
|
10
|
+
if (typeof document === 'undefined') return null
|
|
11
|
+
if (_root && document.body && document.body.contains(_root)) return _root
|
|
12
|
+
_root = document.createElement('div')
|
|
13
|
+
_root.id = 'wk-portal-root'
|
|
14
|
+
_root.setAttribute('data-wk-portal-root', '')
|
|
15
|
+
// The root itself takes no space; children position themselves via fixed.
|
|
16
|
+
_root.style.position = 'absolute'
|
|
17
|
+
_root.style.top = '0'
|
|
18
|
+
_root.style.left = '0'
|
|
19
|
+
_root.style.width = '0'
|
|
20
|
+
_root.style.height = '0'
|
|
21
|
+
document.body.appendChild(_root)
|
|
22
|
+
return _root
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function acquire() {
|
|
26
|
+
const root = ensureRoot()
|
|
27
|
+
if (root) _refCount++
|
|
28
|
+
return root
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function release() {
|
|
32
|
+
if (_refCount > 0) _refCount--
|
|
33
|
+
if (_refCount === 0 && _root && _root.parentNode) {
|
|
34
|
+
_root.parentNode.removeChild(_root)
|
|
35
|
+
_root = null
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Test helper — reset module state between specs.
|
|
40
|
+
export function _reset() {
|
|
41
|
+
if (_root && _root.parentNode) _root.parentNode.removeChild(_root)
|
|
42
|
+
_root = null
|
|
43
|
+
_refCount = 0
|
|
44
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Tracks a reference element and updates a floating element's position
|
|
2
|
+
// whenever either resizes or the viewport scrolls. Uses ResizeObserver +
|
|
3
|
+
// a single passive-capture scroll listener throttled via requestAnimationFrame
|
|
4
|
+
// so the work stays cheap even on long-scroll containers.
|
|
5
|
+
|
|
6
|
+
import { computePosition } from './compute-position.js'
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
data() {
|
|
10
|
+
return { pmTop: 0, pmLeft: 0, pmResolvedPlacement: '', pmAnchorWidth: 0 }
|
|
11
|
+
},
|
|
12
|
+
methods: {
|
|
13
|
+
_pmAttach(refEl, floatEl) {
|
|
14
|
+
if (typeof window === 'undefined') return
|
|
15
|
+
this._pmRefEl = refEl
|
|
16
|
+
this._pmFloatEl = floatEl
|
|
17
|
+
this._pmTick = () => {
|
|
18
|
+
if (this._pmRaf) return
|
|
19
|
+
this._pmRaf = window.requestAnimationFrame(() => {
|
|
20
|
+
this._pmRaf = null
|
|
21
|
+
this._pmUpdate()
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
window.addEventListener('scroll', this._pmTick, { passive: true, capture: true })
|
|
25
|
+
window.addEventListener('resize', this._pmTick)
|
|
26
|
+
if (typeof window.ResizeObserver === 'function') {
|
|
27
|
+
this._pmRO = new window.ResizeObserver(this._pmTick)
|
|
28
|
+
if (refEl) this._pmRO.observe(refEl)
|
|
29
|
+
if (floatEl) this._pmRO.observe(floatEl)
|
|
30
|
+
}
|
|
31
|
+
this._pmUpdate()
|
|
32
|
+
},
|
|
33
|
+
_pmDetach() {
|
|
34
|
+
if (typeof window === 'undefined') return
|
|
35
|
+
if (this._pmTick) {
|
|
36
|
+
window.removeEventListener('scroll', this._pmTick, true)
|
|
37
|
+
window.removeEventListener('resize', this._pmTick)
|
|
38
|
+
}
|
|
39
|
+
if (this._pmRaf) {
|
|
40
|
+
window.cancelAnimationFrame(this._pmRaf)
|
|
41
|
+
this._pmRaf = null
|
|
42
|
+
}
|
|
43
|
+
if (this._pmRO) {
|
|
44
|
+
this._pmRO.disconnect()
|
|
45
|
+
this._pmRO = null
|
|
46
|
+
}
|
|
47
|
+
this._pmRefEl = null
|
|
48
|
+
this._pmFloatEl = null
|
|
49
|
+
this._pmTick = null
|
|
50
|
+
},
|
|
51
|
+
_pmUpdate() {
|
|
52
|
+
const ref = this._pmRefEl
|
|
53
|
+
const float = this._pmFloatEl
|
|
54
|
+
if (!ref || !float || typeof ref.getBoundingClientRect !== 'function') return
|
|
55
|
+
const placement = this.placement || 'bottom-start'
|
|
56
|
+
const offset = typeof this.offset === 'number' ? this.offset : 4
|
|
57
|
+
const r = ref.getBoundingClientRect()
|
|
58
|
+
// Use offsetWidth/offsetHeight (pre-transform layout size) so that CSS enter/leave
|
|
59
|
+
// animations (e.g. scale(0.96)) don't skew the dimension used in top/left placement math.
|
|
60
|
+
const f = { width: float.offsetWidth, height: float.offsetHeight }
|
|
61
|
+
const result = computePosition(r, f, placement, { offset })
|
|
62
|
+
this.pmTop = result.top
|
|
63
|
+
this.pmLeft = result.left
|
|
64
|
+
this.pmResolvedPlacement = result.placement
|
|
65
|
+
this.pmAnchorWidth = r.width
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
beforeDestroy() {
|
|
69
|
+
this._pmDetach()
|
|
70
|
+
},
|
|
71
|
+
beforeUnmount() {
|
|
72
|
+
this._pmDetach()
|
|
73
|
+
}
|
|
74
|
+
}
|
package/src/index.js
CHANGED
|
@@ -11,10 +11,12 @@ export { default as WkCheckbox } from './components/checkbox/Checkbox.vue'
|
|
|
11
11
|
export { default as WkCheckboxGroup } from './components/checkbox-group/CheckboxGroup.vue'
|
|
12
12
|
export { default as WkDialog } from './components/dialog/Dialog.vue'
|
|
13
13
|
export { default as WkDivider } from './components/divider/Divider.vue'
|
|
14
|
+
export { default as WkDropdown } from './components/dropdown/Dropdown.vue'
|
|
14
15
|
export { default as WkEmpty } from './components/empty/Empty.vue'
|
|
15
16
|
export { default as WkEmptyIcon } from './components/empty-icon/EmptyIcon.vue'
|
|
16
17
|
export { default as WkField } from './components/field/Field.vue'
|
|
17
18
|
export { default as WkInput } from './components/input/Input.vue'
|
|
19
|
+
export { default as WkMenu } from './components/menu/Menu.vue'
|
|
18
20
|
export { default as WkPagination } from './components/pagination/Pagination.vue'
|
|
19
21
|
export { default as WkRadio } from './components/radio/Radio.vue'
|
|
20
22
|
export { default as WkRadioGroup } from './components/radio-group/RadioGroup.vue'
|