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
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="ui-menu-host">
|
|
3
|
+
<slot name="trigger" v-bind="triggerSlotProps"></slot>
|
|
4
|
+
<div ref="portal" class="ui-menu-portal">
|
|
5
|
+
<transition name="ui-menu" @after-leave="onAfterLeave">
|
|
6
|
+
<div
|
|
7
|
+
v-if="isVisible"
|
|
8
|
+
ref="floating"
|
|
9
|
+
:id="menuUid"
|
|
10
|
+
class="ui-menu"
|
|
11
|
+
:class="floatingClasses"
|
|
12
|
+
:style="floatingStyle"
|
|
13
|
+
tabindex="-1"
|
|
14
|
+
role="menu"
|
|
15
|
+
@keydown="_kmOnKeydown"
|
|
16
|
+
>
|
|
17
|
+
<div class="ui-menu__content">
|
|
18
|
+
<slot></slot>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
</transition>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<script>
|
|
27
|
+
import { acquire, release } from '../../floating/portal-root.js'
|
|
28
|
+
import { nextZIndex } from '../../floating/layer-manager.js'
|
|
29
|
+
import positionMixin from '../../floating/position-mixin.js'
|
|
30
|
+
import clickOutsideMixin from '../../floating/click-outside-mixin.js'
|
|
31
|
+
import keyboardMixin from '../../floating/keyboard-mixin.js'
|
|
32
|
+
import focusMixin from '../../floating/focus-mixin.js'
|
|
33
|
+
|
|
34
|
+
let _uid = 0
|
|
35
|
+
const PLACEMENTS = [
|
|
36
|
+
'top',
|
|
37
|
+
'top-start',
|
|
38
|
+
'top-end',
|
|
39
|
+
'bottom',
|
|
40
|
+
'bottom-start',
|
|
41
|
+
'bottom-end',
|
|
42
|
+
'left',
|
|
43
|
+
'left-start',
|
|
44
|
+
'left-end',
|
|
45
|
+
'right',
|
|
46
|
+
'right-start',
|
|
47
|
+
'right-end'
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
export default {
|
|
51
|
+
name: 'Menu',
|
|
52
|
+
mixins: [keyboardMixin, focusMixin, clickOutsideMixin, positionMixin],
|
|
53
|
+
model: { prop: 'open', event: 'change' },
|
|
54
|
+
provide() {
|
|
55
|
+
return { menu: this }
|
|
56
|
+
},
|
|
57
|
+
props: {
|
|
58
|
+
open: { type: Boolean, default: false },
|
|
59
|
+
modelValue: { type: Boolean, default: undefined },
|
|
60
|
+
placement: {
|
|
61
|
+
type: String,
|
|
62
|
+
default: 'bottom-start',
|
|
63
|
+
validator: v => PLACEMENTS.indexOf(v) !== -1
|
|
64
|
+
},
|
|
65
|
+
offset: { type: Number, default: 4 },
|
|
66
|
+
closeOnSelect: { type: Boolean, default: true },
|
|
67
|
+
closeOnEsc: { type: Boolean, default: true },
|
|
68
|
+
disabled: { type: Boolean, default: false },
|
|
69
|
+
anchorWidth: { type: Boolean, default: false },
|
|
70
|
+
width: { type: [String, Number], default: null }
|
|
71
|
+
},
|
|
72
|
+
emits: ['change', 'update:modelValue', 'open', 'close', 'select'],
|
|
73
|
+
data() {
|
|
74
|
+
return {
|
|
75
|
+
menuUid: 'wk-menu-' + ++_uid,
|
|
76
|
+
isVisible: false,
|
|
77
|
+
zIndex: 0
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
computed: {
|
|
81
|
+
isOpen() {
|
|
82
|
+
return this.modelValue !== undefined ? this.modelValue : this.open
|
|
83
|
+
},
|
|
84
|
+
triggerSlotProps() {
|
|
85
|
+
return {
|
|
86
|
+
toggle: this.toggle,
|
|
87
|
+
open: this.openMenu,
|
|
88
|
+
close: this.closeMenu,
|
|
89
|
+
isOpen: this.isOpen,
|
|
90
|
+
attrs: this.triggerAttrs,
|
|
91
|
+
triggerRef: this._setTriggerEl
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
triggerAttrs() {
|
|
95
|
+
return {
|
|
96
|
+
'aria-haspopup': 'menu',
|
|
97
|
+
'aria-expanded': this.isOpen ? 'true' : 'false',
|
|
98
|
+
'aria-controls': this.isOpen ? this.menuUid : null
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
floatingClasses() {
|
|
102
|
+
return [this.pmResolvedPlacement && 'ui-menu--' + this.pmResolvedPlacement]
|
|
103
|
+
},
|
|
104
|
+
floatingStyle() {
|
|
105
|
+
const style = {
|
|
106
|
+
top: this.pmTop + 'px',
|
|
107
|
+
left: this.pmLeft + 'px',
|
|
108
|
+
zIndex: this.zIndex || null
|
|
109
|
+
}
|
|
110
|
+
if (this.anchorWidth && this.pmAnchorWidth) {
|
|
111
|
+
style.minWidth = this.pmAnchorWidth + 'px'
|
|
112
|
+
} else if (this.width) {
|
|
113
|
+
style.minWidth = typeof this.width === 'number' ? `${this.width}px` : this.width
|
|
114
|
+
}
|
|
115
|
+
return style
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
watch: {
|
|
119
|
+
isOpen(v) {
|
|
120
|
+
if (v) this.handleOpen()
|
|
121
|
+
else this.handleClose()
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
mounted() {
|
|
125
|
+
if (typeof document !== 'undefined' && this.$refs.portal) {
|
|
126
|
+
const root = acquire()
|
|
127
|
+
if (root) {
|
|
128
|
+
root.appendChild(this.$refs.portal)
|
|
129
|
+
this._portalAcquired = true
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (typeof this.$on === 'function') {
|
|
133
|
+
// eslint-disable-next-line vue/no-deprecated-events-api
|
|
134
|
+
this.$on('hook:beforeDestroy', this._cleanupAll)
|
|
135
|
+
}
|
|
136
|
+
if (this.isOpen) this.handleOpen()
|
|
137
|
+
},
|
|
138
|
+
beforeUnmount() {
|
|
139
|
+
this._cleanupAll()
|
|
140
|
+
},
|
|
141
|
+
methods: {
|
|
142
|
+
setOpenState(v) {
|
|
143
|
+
this.$emit('change', v)
|
|
144
|
+
this.$emit('update:modelValue', v)
|
|
145
|
+
},
|
|
146
|
+
toggle() {
|
|
147
|
+
if (this.disabled) return
|
|
148
|
+
this.setOpenState(!this.isOpen)
|
|
149
|
+
},
|
|
150
|
+
openMenu() {
|
|
151
|
+
if (this.disabled || this.isOpen) return
|
|
152
|
+
this.setOpenState(true)
|
|
153
|
+
},
|
|
154
|
+
closeMenu() {
|
|
155
|
+
if (!this.isOpen) return
|
|
156
|
+
this.setOpenState(false)
|
|
157
|
+
},
|
|
158
|
+
selectItem(value) {
|
|
159
|
+
this.$emit('select', value)
|
|
160
|
+
if (this.closeOnSelect) this.closeMenu()
|
|
161
|
+
},
|
|
162
|
+
setActiveItem(el) {
|
|
163
|
+
const items = this._kmListItems()
|
|
164
|
+
const idx = items.indexOf(el)
|
|
165
|
+
if (idx !== -1) this.kmActiveIndex = idx
|
|
166
|
+
},
|
|
167
|
+
_setTriggerEl(el) {
|
|
168
|
+
this._triggerEl = el || null
|
|
169
|
+
},
|
|
170
|
+
handleOpen() {
|
|
171
|
+
this.isVisible = true
|
|
172
|
+
this.zIndex = nextZIndex()
|
|
173
|
+
this._fmCapture()
|
|
174
|
+
this._kmReset()
|
|
175
|
+
this.$emit('open')
|
|
176
|
+
this.$nextTick(() => {
|
|
177
|
+
const float = this.$refs.floating
|
|
178
|
+
if (!float) return
|
|
179
|
+
this._coSetElements(this._triggerEl, float)
|
|
180
|
+
this._kmSetFloatEl(float)
|
|
181
|
+
this._pmAttach(this._triggerEl, float)
|
|
182
|
+
this._coAttach()
|
|
183
|
+
this._fmFocus(float)
|
|
184
|
+
})
|
|
185
|
+
},
|
|
186
|
+
handleClose() {
|
|
187
|
+
this._pmDetach()
|
|
188
|
+
this._coDetach()
|
|
189
|
+
this.isVisible = false
|
|
190
|
+
this.$emit('close')
|
|
191
|
+
},
|
|
192
|
+
onAfterLeave() {
|
|
193
|
+
this._fmRestore()
|
|
194
|
+
},
|
|
195
|
+
_kmOnEscape() {
|
|
196
|
+
if (this.closeOnEsc) this.closeMenu()
|
|
197
|
+
},
|
|
198
|
+
_kmOnTab() {
|
|
199
|
+
this.closeMenu()
|
|
200
|
+
},
|
|
201
|
+
_coOnOutside() {
|
|
202
|
+
this.closeMenu()
|
|
203
|
+
},
|
|
204
|
+
_cleanupAll() {
|
|
205
|
+
this._pmDetach()
|
|
206
|
+
this._coDetach()
|
|
207
|
+
const portal = this.$refs.portal
|
|
208
|
+
if (portal && portal.parentNode) portal.parentNode.removeChild(portal)
|
|
209
|
+
if (this._portalAcquired) {
|
|
210
|
+
release()
|
|
211
|
+
this._portalAcquired = false
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
</script>
|
|
217
|
+
|
|
218
|
+
<style src="./menu.css" scoped></style>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
.ui-menu-host {
|
|
2
|
+
display: contents;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.ui-menu-portal {
|
|
6
|
+
position: absolute;
|
|
7
|
+
top: 0;
|
|
8
|
+
left: 0;
|
|
9
|
+
width: 0;
|
|
10
|
+
height: 0;
|
|
11
|
+
pointer-events: none;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.ui-menu {
|
|
15
|
+
position: fixed;
|
|
16
|
+
background: var(--card);
|
|
17
|
+
border: 1px solid var(--border-primary);
|
|
18
|
+
border-radius: var(--rounded-xl);
|
|
19
|
+
box-shadow: var(--shadow-md);
|
|
20
|
+
display: flex;
|
|
21
|
+
flex-direction: column;
|
|
22
|
+
min-width: 176px;
|
|
23
|
+
max-width: 320px;
|
|
24
|
+
outline: none;
|
|
25
|
+
pointer-events: auto;
|
|
26
|
+
box-sizing: border-box;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.ui-menu__content {
|
|
30
|
+
display: flex;
|
|
31
|
+
flex-direction: column;
|
|
32
|
+
gap: var(--spacing-2xs);
|
|
33
|
+
max-height: 320px;
|
|
34
|
+
overflow-y: auto;
|
|
35
|
+
border-radius: var(--rounded-lg);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.ui-menu__content::-webkit-scrollbar {
|
|
39
|
+
width: 6px;
|
|
40
|
+
}
|
|
41
|
+
.ui-menu__content::-webkit-scrollbar-thumb {
|
|
42
|
+
background: var(--border-primary);
|
|
43
|
+
border-radius: 3px;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Open/close animation — class names cover both Vue 2 and Vue 3 transition naming. */
|
|
47
|
+
.ui-menu-enter-active,
|
|
48
|
+
.ui-menu-leave-active {
|
|
49
|
+
transition:
|
|
50
|
+
opacity 0.12s ease,
|
|
51
|
+
transform 0.12s ease;
|
|
52
|
+
}
|
|
53
|
+
.ui-menu-enter,
|
|
54
|
+
.ui-menu-enter-from,
|
|
55
|
+
.ui-menu-leave-to {
|
|
56
|
+
opacity: 0;
|
|
57
|
+
transform: scale(0.96);
|
|
58
|
+
}
|
|
59
|
+
.ui-menu-enter-to,
|
|
60
|
+
.ui-menu-leave,
|
|
61
|
+
.ui-menu-leave-from {
|
|
62
|
+
opacity: 1;
|
|
63
|
+
transform: scale(1);
|
|
64
|
+
}
|
|
@@ -1,73 +1,72 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
:
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
]"
|
|
13
|
-
tabindex="0"
|
|
14
|
-
@click="toggle"
|
|
15
|
-
@keydown.esc="close"
|
|
16
|
-
@keydown.enter.prevent="toggle"
|
|
17
|
-
@keydown.space.prevent="toggle"
|
|
2
|
+
<Menu
|
|
3
|
+
:open="isOpen"
|
|
4
|
+
:disabled="disabled || loading"
|
|
5
|
+
placement="bottom-start"
|
|
6
|
+
:offset="4"
|
|
7
|
+
:anchor-width="true"
|
|
8
|
+
:close-on-select="false"
|
|
9
|
+
@change="onMenuChange"
|
|
10
|
+
@close="onMenuClose"
|
|
18
11
|
>
|
|
19
|
-
<
|
|
20
|
-
<span v-if="hasIconSlot" class="ui-select__icon">
|
|
21
|
-
<slot name="icon" />
|
|
22
|
-
</span>
|
|
23
|
-
<span v-if="prepend" class="ui-select__prepend">{{ prepend }}</span>
|
|
24
|
-
<span class="ui-select__value" :class="{ 'ui-select__value--placeholder': !selectedLabel }">
|
|
25
|
-
{{ selectedLabel || placeholder }}
|
|
26
|
-
</span>
|
|
27
|
-
</span>
|
|
28
|
-
|
|
29
|
-
<spinner v-if="loading" type="mirrored" size="sm" />
|
|
30
|
-
<span v-else class="ui-select__chevron">
|
|
31
|
-
<ChevronDownIcon :size="16" />
|
|
32
|
-
</span>
|
|
33
|
-
|
|
34
|
-
<div ref="dropdown" v-show="isOpen" class="ui-select__dropdown" :style="dropdownStyle" @click.stop>
|
|
35
|
-
<div v-if="canScrollUp" class="ui-select__scroll-indicator ui-select__scroll-indicator--up">
|
|
36
|
-
<ChevronUpIcon :size="16" />
|
|
37
|
-
</div>
|
|
12
|
+
<template #trigger="{ toggle, isOpen: open, attrs, triggerRef }">
|
|
38
13
|
<div
|
|
39
|
-
ref="
|
|
40
|
-
class="ui-
|
|
41
|
-
:
|
|
42
|
-
|
|
14
|
+
:ref="triggerRef"
|
|
15
|
+
class="ui-select"
|
|
16
|
+
:class="[
|
|
17
|
+
`ui-select--${size}`,
|
|
18
|
+
{
|
|
19
|
+
'ui-select--open': open,
|
|
20
|
+
'ui-select--disabled': disabled,
|
|
21
|
+
'ui-select--error': error,
|
|
22
|
+
'ui-select--loading': loading
|
|
23
|
+
}
|
|
24
|
+
]"
|
|
25
|
+
tabindex="0"
|
|
26
|
+
v-bind="attrs"
|
|
27
|
+
@click="toggle"
|
|
28
|
+
@keydown.enter.prevent="toggle"
|
|
29
|
+
@keydown.space.prevent="toggle"
|
|
43
30
|
>
|
|
44
|
-
<
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
</
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
31
|
+
<span class="ui-select__left">
|
|
32
|
+
<span v-if="hasIconSlot" class="ui-select__icon">
|
|
33
|
+
<slot name="icon" />
|
|
34
|
+
</span>
|
|
35
|
+
<span v-if="prepend" class="ui-select__prepend">{{ prepend }}</span>
|
|
36
|
+
<span class="ui-select__value" :class="{ 'ui-select__value--placeholder': !selectedLabel }">
|
|
37
|
+
{{ selectedLabel || placeholder }}
|
|
38
|
+
</span>
|
|
39
|
+
</span>
|
|
40
|
+
<Spinner v-if="loading" type="mirrored" size="sm" />
|
|
41
|
+
<span v-else class="ui-select__chevron">
|
|
42
|
+
<ChevronDownIcon :size="16" />
|
|
43
|
+
</span>
|
|
56
44
|
</div>
|
|
45
|
+
</template>
|
|
46
|
+
<div class="ui-select__list" :style="listStyle">
|
|
47
|
+
<slot>
|
|
48
|
+
<SelectOption
|
|
49
|
+
v-for="opt in normalizedOptions"
|
|
50
|
+
:key="opt.value"
|
|
51
|
+
:value="opt.value"
|
|
52
|
+
:label="opt.label"
|
|
53
|
+
:disabled="opt.disabled"
|
|
54
|
+
/>
|
|
55
|
+
</slot>
|
|
57
56
|
</div>
|
|
58
|
-
</
|
|
57
|
+
</Menu>
|
|
59
58
|
</template>
|
|
60
59
|
|
|
61
60
|
<script>
|
|
62
61
|
import ChevronDownIcon from '../../icons/ChevronDownIcon.vue'
|
|
63
|
-
import
|
|
62
|
+
import Menu from '../menu/Menu.vue'
|
|
64
63
|
import SelectOption from '../select-option/SelectOption.vue'
|
|
65
64
|
import Spinner from '../spinner/Spinner.vue'
|
|
66
65
|
|
|
67
66
|
export default {
|
|
68
67
|
name: 'Select',
|
|
69
68
|
|
|
70
|
-
components: { SelectOption, Spinner, ChevronDownIcon
|
|
69
|
+
components: { Menu, SelectOption, Spinner, ChevronDownIcon },
|
|
71
70
|
|
|
72
71
|
provide() {
|
|
73
72
|
return { select: this }
|
|
@@ -83,7 +82,7 @@ export default {
|
|
|
83
82
|
type: String,
|
|
84
83
|
default: 'default',
|
|
85
84
|
validator: function (v) {
|
|
86
|
-
return ['xs', 'sm', 'default', 'lg'].
|
|
85
|
+
return ['xs', 'sm', 'default', 'lg'].indexOf(v) !== -1
|
|
87
86
|
}
|
|
88
87
|
},
|
|
89
88
|
value: {
|
|
@@ -125,24 +124,7 @@ export default {
|
|
|
125
124
|
data() {
|
|
126
125
|
return {
|
|
127
126
|
isOpen: false,
|
|
128
|
-
|
|
129
|
-
canScrollDown: false,
|
|
130
|
-
labelCache: {},
|
|
131
|
-
dropdownStyle: {}
|
|
132
|
-
}
|
|
133
|
-
},
|
|
134
|
-
|
|
135
|
-
watch: {
|
|
136
|
-
isOpen(val) {
|
|
137
|
-
if (val) {
|
|
138
|
-
this.$nextTick(() => {
|
|
139
|
-
this.positionDropdown()
|
|
140
|
-
this.updateScrollIndicators()
|
|
141
|
-
})
|
|
142
|
-
} else {
|
|
143
|
-
this.canScrollUp = false
|
|
144
|
-
this.canScrollDown = false
|
|
145
|
-
}
|
|
127
|
+
labelCache: {}
|
|
146
128
|
}
|
|
147
129
|
},
|
|
148
130
|
|
|
@@ -162,76 +144,25 @@ export default {
|
|
|
162
144
|
},
|
|
163
145
|
hasIconSlot() {
|
|
164
146
|
return !!((this.$scopedSlots && this.$scopedSlots['icon']) || this.$slots['icon'])
|
|
147
|
+
},
|
|
148
|
+
listStyle() {
|
|
149
|
+
return { maxHeight: this.listHeight + 'px' }
|
|
165
150
|
}
|
|
166
151
|
},
|
|
167
152
|
|
|
168
|
-
mounted() {
|
|
169
|
-
document.addEventListener('click', this.handleOutsideClick)
|
|
170
|
-
window.addEventListener('scroll', this.handleScroll, true)
|
|
171
|
-
window.addEventListener('resize', this.handleResize)
|
|
172
|
-
if (typeof document !== 'undefined' && this.$refs.dropdown) {
|
|
173
|
-
document.body.appendChild(this.$refs.dropdown)
|
|
174
|
-
}
|
|
175
|
-
if (typeof this.$on === 'function') {
|
|
176
|
-
// eslint-disable-next-line vue/no-deprecated-events-api
|
|
177
|
-
this.$on('hook:beforeDestroy', this.cleanup)
|
|
178
|
-
}
|
|
179
|
-
},
|
|
180
|
-
|
|
181
|
-
beforeUnmount() {
|
|
182
|
-
this.cleanup()
|
|
183
|
-
},
|
|
184
|
-
|
|
185
153
|
methods: {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
this.isOpen = !this.isOpen
|
|
154
|
+
onMenuChange(v) {
|
|
155
|
+
this.isOpen = v
|
|
189
156
|
},
|
|
190
|
-
|
|
157
|
+
onMenuClose() {
|
|
191
158
|
this.isOpen = false
|
|
192
159
|
},
|
|
193
160
|
select(val) {
|
|
194
161
|
this.$emit('change', val)
|
|
195
|
-
this.
|
|
162
|
+
this.isOpen = false
|
|
196
163
|
},
|
|
197
164
|
registerOption(value, label) {
|
|
198
165
|
this.labelCache = Object.assign({}, this.labelCache, { [value]: label })
|
|
199
|
-
},
|
|
200
|
-
positionDropdown() {
|
|
201
|
-
if (!this.$el || !this.$refs.dropdown) return
|
|
202
|
-
var rect = this.$el.getBoundingClientRect()
|
|
203
|
-
this.dropdownStyle = {
|
|
204
|
-
position: 'fixed',
|
|
205
|
-
top: rect.bottom + 4 + 'px',
|
|
206
|
-
left: rect.left + 'px',
|
|
207
|
-
width: rect.width + 'px',
|
|
208
|
-
zIndex: 1050
|
|
209
|
-
}
|
|
210
|
-
},
|
|
211
|
-
updateScrollIndicators() {
|
|
212
|
-
const el = this.$refs.options
|
|
213
|
-
if (!el) return
|
|
214
|
-
this.canScrollUp = el.scrollTop > 0
|
|
215
|
-
this.canScrollDown = el.scrollTop + el.clientHeight < el.scrollHeight
|
|
216
|
-
},
|
|
217
|
-
handleOutsideClick(e) {
|
|
218
|
-
if (!this.$el.contains(e.target) && !(this.$refs.dropdown && this.$refs.dropdown.contains(e.target))) {
|
|
219
|
-
this.close()
|
|
220
|
-
}
|
|
221
|
-
},
|
|
222
|
-
handleScroll() {
|
|
223
|
-
if (this.isOpen) this.positionDropdown()
|
|
224
|
-
},
|
|
225
|
-
handleResize() {
|
|
226
|
-
if (this.isOpen) this.positionDropdown()
|
|
227
|
-
},
|
|
228
|
-
cleanup() {
|
|
229
|
-
document.removeEventListener('click', this.handleOutsideClick)
|
|
230
|
-
window.removeEventListener('scroll', this.handleScroll, true)
|
|
231
|
-
window.removeEventListener('resize', this.handleResize)
|
|
232
|
-
if (this.$refs.dropdown && this.$refs.dropdown.parentNode === document.body) {
|
|
233
|
-
document.body.removeChild(this.$refs.dropdown)
|
|
234
|
-
}
|
|
235
166
|
}
|
|
236
167
|
}
|
|
237
168
|
}
|
|
@@ -110,23 +110,6 @@
|
|
|
110
110
|
transform: rotate(180deg);
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
.ui-select__spinner {
|
|
114
|
-
display: flex;
|
|
115
|
-
align-items: center;
|
|
116
|
-
justify-content: center;
|
|
117
|
-
width: 16px;
|
|
118
|
-
height: 16px;
|
|
119
|
-
flex-shrink: 0;
|
|
120
|
-
color: var(--muted-fg);
|
|
121
|
-
animation: ui-select-spin 0.75s linear infinite;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
@keyframes ui-select-spin {
|
|
125
|
-
to {
|
|
126
|
-
transform: rotate(360deg);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
113
|
/* — sizes — */
|
|
131
114
|
.ui-select--lg {
|
|
132
115
|
min-height: 40px;
|
|
@@ -163,45 +146,20 @@
|
|
|
163
146
|
line-height: var(--paragraph-mini-line-height);
|
|
164
147
|
}
|
|
165
148
|
|
|
166
|
-
/*
|
|
167
|
-
.
|
|
168
|
-
|
|
169
|
-
border: 1px solid var(--border-primary);
|
|
170
|
-
border-radius: var(--radius-10);
|
|
171
|
-
box-shadow: var(--shadow-md);
|
|
172
|
-
overflow: hidden;
|
|
149
|
+
/* List wrapper inside the portal'd Menu. Width is anchored to trigger via the
|
|
150
|
+
Menu component's anchorWidth prop (computes min-width). */
|
|
151
|
+
.ui-select__list {
|
|
173
152
|
display: flex;
|
|
174
153
|
flex-direction: column;
|
|
175
|
-
scrollbar-width: none;
|
|
176
|
-
-ms-overflow-style: none;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
.ui-select__dropdown .ui-select__options::-webkit-scrollbar {
|
|
180
|
-
display: none;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
.ui-select__options {
|
|
184
154
|
overflow-y: auto;
|
|
185
155
|
padding: var(--spacing-2xs);
|
|
156
|
+
scrollbar-width: thin;
|
|
186
157
|
}
|
|
187
158
|
|
|
188
|
-
.ui-
|
|
189
|
-
|
|
190
|
-
left: 0;
|
|
191
|
-
right: 0;
|
|
192
|
-
display: flex;
|
|
193
|
-
align-items: center;
|
|
194
|
-
justify-content: center;
|
|
195
|
-
flex-shrink: 0;
|
|
196
|
-
color: var(--muted-fg);
|
|
197
|
-
background: var(--input);
|
|
198
|
-
padding: var(--spacing-2xs) 0;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
.ui-select__scroll-indicator--up {
|
|
202
|
-
top: 0;
|
|
159
|
+
.ui-select__list::-webkit-scrollbar {
|
|
160
|
+
width: 6px;
|
|
203
161
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
162
|
+
.ui-select__list::-webkit-scrollbar-thumb {
|
|
163
|
+
background: var(--border-primary);
|
|
164
|
+
border-radius: 3px;
|
|
207
165
|
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
3
|
class="ui-select-option"
|
|
4
|
-
:class="
|
|
5
|
-
'ui-select-option--
|
|
6
|
-
'ui-select-option--
|
|
7
|
-
'ui-select-option--
|
|
8
|
-
'ui-select-option--
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
:class="[
|
|
5
|
+
size === 'large' ? 'ui-select-option--large' : null,
|
|
6
|
+
variant === 'destructive' ? 'ui-select-option--destructive' : null,
|
|
7
|
+
isDisabled ? 'ui-select-option--disabled' : null,
|
|
8
|
+
isSelected ? 'ui-select-option--selected' : null
|
|
9
|
+
]"
|
|
10
|
+
role="option"
|
|
11
|
+
:aria-selected="isSelected ? 'true' : 'false'"
|
|
12
|
+
:aria-disabled="isDisabled ? 'true' : null"
|
|
11
13
|
@click="handleClick"
|
|
12
|
-
@keydown.enter="handleClick"
|
|
13
|
-
@keydown.space.prevent="handleClick"
|
|
14
14
|
>
|
|
15
15
|
<slot>{{ label || value }}</slot>
|
|
16
16
|
</div>
|
|
@@ -55,25 +55,21 @@ export default {
|
|
|
55
55
|
|
|
56
56
|
emits: [],
|
|
57
57
|
|
|
58
|
-
mounted() {
|
|
59
|
-
if (this.select && this.select.registerOption) {
|
|
60
|
-
this.select.registerOption(this.value, this.label || this.value)
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
|
|
64
58
|
computed: {
|
|
65
59
|
isSelected() {
|
|
66
|
-
return this.select
|
|
60
|
+
return !!(this.select && this.select.value !== undefined && this.select.value === this.value)
|
|
67
61
|
},
|
|
68
62
|
isDisabled() {
|
|
69
|
-
return this.disabled || (this.select
|
|
63
|
+
return this.disabled || !!(this.select && this.select.disabled)
|
|
70
64
|
}
|
|
71
65
|
},
|
|
72
66
|
|
|
73
67
|
methods: {
|
|
74
68
|
handleClick() {
|
|
75
|
-
if (this.isDisabled
|
|
76
|
-
this.select.select
|
|
69
|
+
if (this.isDisabled) return
|
|
70
|
+
if (this.select && typeof this.select.select === 'function') {
|
|
71
|
+
this.select.select(this.value)
|
|
72
|
+
}
|
|
77
73
|
}
|
|
78
74
|
}
|
|
79
75
|
}
|
|
@@ -21,12 +21,13 @@
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
.ui-sidebar-item:hover {
|
|
24
|
-
background-color: var(--
|
|
24
|
+
background-color: var(--sidebar-hover);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
.ui-sidebar-item:focus-visible {
|
|
28
28
|
outline: none;
|
|
29
|
-
box-shadow: 0 0 0
|
|
29
|
+
box-shadow: 0 0 0 3px var(--sidebar-ring);
|
|
30
|
+
background-color: var(--ghost);
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
.ui-sidebar-item:active {
|
|
@@ -35,7 +36,7 @@
|
|
|
35
36
|
|
|
36
37
|
/* Active (selected page) */
|
|
37
38
|
.ui-sidebar-item--active {
|
|
38
|
-
background-color: var(--
|
|
39
|
+
background-color: var(--sidebar-accent);
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
/* Disabled */
|