mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2025-01-18 12:34:25 +08:00
refactor(select): using new scrollbar
This commit is contained in:
parent
f1cea3cf14
commit
2d7ff80d14
@ -73,6 +73,7 @@
|
||||
</div>
|
||||
<div
|
||||
ref="contentWrapper"
|
||||
v-clickoutside="handleClickOutsideMenu"
|
||||
class="n-select-menu__content-wrapper"
|
||||
>
|
||||
<div
|
||||
@ -90,26 +91,33 @@
|
||||
:class="{[`n-select-menu--${size}-size`]: true}"
|
||||
@mouseleave="hideLightBar"
|
||||
>
|
||||
<transition name="n-select-menu__light-bar--transition">
|
||||
<div
|
||||
v-if="showLightBar"
|
||||
class="n-select-menu__light-bar"
|
||||
:style="{ top: `${lightBarTop}px` }"
|
||||
/>
|
||||
</transition>
|
||||
<div
|
||||
v-for="item in items"
|
||||
:key="item.value"
|
||||
class="n-select-menu__item"
|
||||
:class="{
|
||||
'n-select-menu__item--selected':
|
||||
isSelected(item)
|
||||
}"
|
||||
@click.stop="toggleItemInMultipleSelect(item)"
|
||||
@mouseenter="showLightBarTop"
|
||||
<scrollbar
|
||||
@scrollstart="handleMenuScrollStart"
|
||||
@scrollend="handleMenuScrollEnd"
|
||||
>
|
||||
{{ item.label }}
|
||||
</div>
|
||||
<div class="n-select-menu__item-wrapper">
|
||||
<transition name="n-select-menu__light-bar--transition">
|
||||
<div
|
||||
v-if="showLightBar"
|
||||
class="n-select-menu__light-bar"
|
||||
:style="{ top: `${lightBarTop}px` }"
|
||||
/>
|
||||
</transition>
|
||||
<div
|
||||
v-for="item in items"
|
||||
:key="item.value"
|
||||
class="n-select-menu__item"
|
||||
:class="{
|
||||
'n-select-menu__item--selected':
|
||||
isSelected(item)
|
||||
}"
|
||||
@click.stop="toggleItemInMultipleSelect(item)"
|
||||
@mouseenter="showLightBarTop"
|
||||
>
|
||||
{{ item.label }}
|
||||
</div>
|
||||
</div>
|
||||
</scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
@ -124,11 +132,17 @@ import detachable from '../../../mixins/detachable'
|
||||
import placeable from '../../../mixins/placeable'
|
||||
import toggleable from '../../../mixins/toggleable'
|
||||
import zindexable from '../../../mixins/zindexable'
|
||||
import Scrollbar from '../../Scrollbar'
|
||||
import clickoutside from '../../../directives/clickoutside'
|
||||
|
||||
export default {
|
||||
name: 'NMultipleSelect',
|
||||
components: {
|
||||
NIcon
|
||||
NIcon,
|
||||
Scrollbar
|
||||
},
|
||||
directives: {
|
||||
clickoutside
|
||||
},
|
||||
mixins: [detachable, toggleable, placeable, zindexable],
|
||||
model: {
|
||||
@ -168,7 +182,6 @@ export default {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
@ -179,7 +192,7 @@ export default {
|
||||
lightBarTop: null,
|
||||
showLightBar: false,
|
||||
label: '',
|
||||
labelPlaceholder: 'Please Select'
|
||||
scrolling: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -214,26 +227,8 @@ export default {
|
||||
} else {
|
||||
this.label = ''
|
||||
}
|
||||
},
|
||||
active (newValue) {
|
||||
if (newValue === true) {
|
||||
this.$nextTick().then(
|
||||
() => {
|
||||
document.addEventListener('click', this.nativeCloseMenu)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
this.$nextTick().then(
|
||||
() => {
|
||||
document.removeEventListener('click', this.nativeCloseMenu)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy () {
|
||||
document.removeEventListener('click', this.nativeCloseMenu)
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* @param {string} value
|
||||
@ -263,8 +258,8 @@ export default {
|
||||
if (!Array.isArray(this.selectedValue)) return false
|
||||
return 1 + this.selectedValue.findIndex(value => value === item.value)
|
||||
},
|
||||
nativeCloseMenu (e) {
|
||||
if (!this.$refs.select.contains(e.target)) {
|
||||
handleClickOutsideMenu (e) {
|
||||
if (!this.$refs.activator.contains(e.target) && !this.scrolling) {
|
||||
this.deactivate()
|
||||
}
|
||||
},
|
||||
@ -292,6 +287,14 @@ export default {
|
||||
}
|
||||
this.$emit('input', newSelectedValues)
|
||||
this.$nextTick().then(this.updatePosition)
|
||||
},
|
||||
handleMenuScrollStart () {
|
||||
this.scrolling = true
|
||||
},
|
||||
handleMenuScrollEnd () {
|
||||
window.setTimeout(() => {
|
||||
this.scrolling = false
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,7 @@
|
||||
<transition name="n-select-menu--transition">
|
||||
<div
|
||||
v-if="active"
|
||||
v-clickoutside="handleClickOutsideMenu"
|
||||
class="n-select-menu-wrapper"
|
||||
>
|
||||
<div
|
||||
@ -46,40 +47,48 @@
|
||||
:class="{[`n-select-menu--${size}-size`]: true}"
|
||||
@mouseleave="hideLightBar"
|
||||
>
|
||||
<transition name="n-select-menu__light-bar--transition">
|
||||
<div
|
||||
v-if="showLightBar"
|
||||
class="n-select-menu__light-bar"
|
||||
:style="{ top: `${lightBarTop}px` }"
|
||||
/>
|
||||
</transition>
|
||||
<div
|
||||
v-for="item in filteredItems"
|
||||
:key="item.value"
|
||||
class="n-select-menu__item"
|
||||
:class="{
|
||||
'n-select-menu__item--selected':
|
||||
selectedValue ===
|
||||
item.value
|
||||
}"
|
||||
@click.stop="toggleItemInSingleSelect(item)"
|
||||
@mouseenter="showLightBarTop"
|
||||
<scrollbar
|
||||
ref="scrollbar"
|
||||
@scrollstart="handleMenuScrollStart"
|
||||
@scrollend="handleMenuScrollEnd"
|
||||
>
|
||||
{{ item.label }}
|
||||
</div>
|
||||
<div
|
||||
v-if="label.length && !filteredItems.length"
|
||||
class="n-select-menu__item n-select-menu__item--not-found"
|
||||
>
|
||||
{{
|
||||
/**
|
||||
* This method to activate hideLightBar is ridiculous, however using
|
||||
* event handler still has some problem.
|
||||
*/
|
||||
hideLightBar()
|
||||
}}
|
||||
none result matched
|
||||
</div>
|
||||
<div class="n-select-menu__item-wrapper">
|
||||
<transition name="n-select-menu__light-bar--transition">
|
||||
<div
|
||||
v-if="showLightBar"
|
||||
class="n-select-menu__light-bar"
|
||||
:style="{ top: `${lightBarTop}px` }"
|
||||
/>
|
||||
</transition>
|
||||
<div
|
||||
v-for="item in filteredItems"
|
||||
:key="item.value"
|
||||
class="n-select-menu__item"
|
||||
:class="{
|
||||
'n-select-menu__item--selected':
|
||||
selectedValue ===
|
||||
item.value
|
||||
}"
|
||||
@click.stop="toggleItemInSingleSelect(item)"
|
||||
@mouseenter="showLightBarTop"
|
||||
>
|
||||
{{ item.label }}
|
||||
</div>
|
||||
<div
|
||||
v-if="label.length && !filteredItems.length"
|
||||
class="n-select-menu__item n-select-menu__item--not-found"
|
||||
>
|
||||
{{
|
||||
/**
|
||||
* This method to activate hideLightBar is ridiculous, however using
|
||||
* event handler still has some problem.
|
||||
*/
|
||||
hideLightBar()
|
||||
}}
|
||||
none result matched
|
||||
</div>
|
||||
</div>
|
||||
</scrollbar>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
@ -94,9 +103,17 @@ import detachable from '../../../mixins/detachable'
|
||||
import placeable from '../../../mixins/placeable'
|
||||
import toggleable from '../../../mixins/toggleable'
|
||||
import zindexable from '../../../mixins/zindexable'
|
||||
import Scrollbar from '../../Scrollbar'
|
||||
import clickoutside from '../../../directives/clickoutside'
|
||||
|
||||
export default {
|
||||
name: 'NSingleSelect',
|
||||
components: {
|
||||
Scrollbar
|
||||
},
|
||||
directives: {
|
||||
clickoutside
|
||||
},
|
||||
mixins: [detachable, toggleable, placeable, zindexable, Emitter],
|
||||
model: {
|
||||
prop: 'selectedValue',
|
||||
@ -146,7 +163,8 @@ export default {
|
||||
lightBarTop: null,
|
||||
showLightBar: false,
|
||||
label: '',
|
||||
labelPlaceholder: 'Please Select'
|
||||
labelPlaceholder: 'Please Select',
|
||||
scrolling: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -170,6 +188,14 @@ export default {
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
label () {
|
||||
this.$nextTick().then(() => {
|
||||
this.updatePosition()
|
||||
if (this.$refs.scrollbar) {
|
||||
this.$refs.scrollbar.updateParameters()
|
||||
}
|
||||
})
|
||||
},
|
||||
selectedItem (n, o) {
|
||||
if (this.selectedItem !== null) {
|
||||
this.label = this.selectedItem.label
|
||||
@ -186,11 +212,6 @@ export default {
|
||||
this.labelPlaceholder = this.selectedItem.label
|
||||
this.label = ''
|
||||
}
|
||||
this.$nextTick().then(
|
||||
() => {
|
||||
document.addEventListener('click', this.handleClickOutsideMenu)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
this.$refs.singleSelectInput.blur()
|
||||
if (this.selectedItem) {
|
||||
@ -199,11 +220,6 @@ export default {
|
||||
this.label = ''
|
||||
this.labelPlaceholder = this.placeholder
|
||||
}
|
||||
this.$nextTick().then(
|
||||
() => {
|
||||
document.removeEventListener('click', this.handleClickOutsideMenu)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -213,9 +229,6 @@ export default {
|
||||
this.label = this.selectedItem.label
|
||||
}
|
||||
},
|
||||
beforeDestroy () {
|
||||
document.removeEventListener('click', this.handleClickOutsideMenu)
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* @param {string} value
|
||||
@ -245,7 +258,7 @@ export default {
|
||||
return item.value === this.selectedValue
|
||||
},
|
||||
handleClickOutsideMenu (e) {
|
||||
if (!this.$refs.select.contains(e.target)) {
|
||||
if (!this.$refs.activator.contains(e.target) && !this.scrolling) {
|
||||
this.deactivate()
|
||||
}
|
||||
},
|
||||
@ -266,6 +279,14 @@ export default {
|
||||
this.$emit('input', item.value)
|
||||
this.emitChangeEvent(item, true)
|
||||
this.closeMenu()
|
||||
},
|
||||
handleMenuScrollStart () {
|
||||
this.scrolling = true
|
||||
},
|
||||
handleMenuScrollEnd () {
|
||||
window.setTimeout(() => {
|
||||
this.scrolling = false
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -260,16 +260,18 @@
|
||||
border-radius: 6px;
|
||||
margin-top: 4px;
|
||||
margin-bottom: 4px;
|
||||
@include fade-in-transition(select-menu);
|
||||
@include fade-in-scale-up-transition(select-menu);
|
||||
.n-select-menu {
|
||||
z-index: 0;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
overflow: hidden;
|
||||
border-radius: 6px;
|
||||
background-color: rgba(75, 81, 106, 1);
|
||||
box-shadow: 0px 2px 20px 0px rgba(0,0,0,0.16);
|
||||
@include scrollbar;
|
||||
.n-select-menu__item-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
.n-select-menu__item {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
@ -292,14 +294,18 @@
|
||||
transition: top .15s $default-cubic-bezier;
|
||||
}
|
||||
&.n-select-menu--small-size {
|
||||
max-height: $small-height * 8;
|
||||
.n-scrollbar-container {
|
||||
max-height: $small-height * 8;
|
||||
}
|
||||
.n-select-menu__item, .n-select-menu__light-bar {
|
||||
height: $small-height;
|
||||
line-height: $small-height;
|
||||
}
|
||||
}
|
||||
&.n-select-menu--default-size, &.n-select-menu--medium-size {
|
||||
max-height: $default-height * 8;
|
||||
.n-scrollbar-container {
|
||||
max-height: $default-height * 8;
|
||||
}
|
||||
.n-select-menu__item, .n-select-menu__light-bar {
|
||||
height: $default-height;
|
||||
line-height: $default-height;
|
||||
@ -310,7 +316,9 @@
|
||||
height: $large-height;
|
||||
line-height: $large-height;
|
||||
}
|
||||
max-height: $large-height * 8;
|
||||
.n-scrollbar-container {
|
||||
max-height: $large-height * 8;
|
||||
}
|
||||
}
|
||||
&.n-select-menu--multiple {
|
||||
.n-select-menu__item {
|
||||
|
@ -23,6 +23,7 @@
|
||||
@import './Radio.scss';
|
||||
@import './Form.scss';
|
||||
@import './TimePicker.scss';
|
||||
@import './Scrollbar.scss';
|
||||
|
||||
@import "./NimbusServiceLayout.scss";
|
||||
@import "./Popover.scss";
|
||||
|
@ -122,6 +122,9 @@ $default-cubic-bezier: cubic-bezier(.4, 0, .2, 1);
|
||||
|
||||
$default-font-family: 'Lato';
|
||||
|
||||
$scrollbar-color: rgba(255,255,255,0.2);
|
||||
$scrollbar-color--hover: rgba(255,255,255,0.3);
|
||||
|
||||
@mixin scrollbar {
|
||||
&::-webkit-scrollbar {
|
||||
width: 5px;
|
||||
@ -164,7 +167,7 @@ $default-font-family: 'Lato';
|
||||
}
|
||||
}
|
||||
|
||||
@mixin fade-in-transition($block, $origin: inherit) {
|
||||
@mixin fade-in-scale-up-transition($block, $origin: inherit) {
|
||||
&.#{$namespace}-#{$block}--transition-enter-active,
|
||||
&.#{$namespace}-#{$block}--transition-leave-active {
|
||||
transform: scale(1);
|
||||
@ -182,4 +185,20 @@ $default-font-family: 'Lato';
|
||||
&.#{$namespace}-#{$block}--transition-leave-to {
|
||||
transition: opacity .2s $slow-out-cubic-bezier, transform .2s $slow-out-cubic-bezier;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin fade-in-transition($block) {
|
||||
&.#{$namespace}-#{$block}--transition-enter-active,
|
||||
&.#{$namespace}-#{$block}--transition-leave-active {
|
||||
opacity: 1;
|
||||
}
|
||||
&.#{$namespace}-#{$block}--transition-enter-active {
|
||||
transition: opacity .2s $fast-in-cubic-bezier;
|
||||
}
|
||||
&.#{$namespace}-#{$block}--transition-enter, &.#{$namespace}-#{$block}--transition-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
&.#{$namespace}-#{$block}--transition-leave-to {
|
||||
transition: opacity .2s $slow-out-cubic-bezier;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user