mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2025-01-18 12:34:25 +08:00
feat(transfer): animation
This commit is contained in:
parent
97d16c86c2
commit
7d5d0383bf
@ -38,7 +38,8 @@ function genOptions () {
|
||||
prefix = Math.random().toString(36).slice(2, 5)
|
||||
return Array.apply(null, { length: 20 }).map((v, i) => ({
|
||||
label: prefix + 'Option' + i,
|
||||
value: prefix + i
|
||||
value: prefix + i,
|
||||
disabled: i % 3 === 0
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -1,16 +1,25 @@
|
||||
<template>
|
||||
<li
|
||||
class="n-transfer-list-item"
|
||||
@click="handleClick"
|
||||
<transition
|
||||
:name="transitionName"
|
||||
>
|
||||
<div class="n-transfer-list-item__checkbox">
|
||||
<n-checkbox
|
||||
:value="checked"
|
||||
@input="handleInput"
|
||||
/>
|
||||
</div>
|
||||
<slot />
|
||||
</li>
|
||||
<li
|
||||
v-show="show"
|
||||
class="n-transfer-list-item"
|
||||
:class="{
|
||||
'n-transfer-list-item--disabled': disabled
|
||||
}"
|
||||
@click="handleClick"
|
||||
>
|
||||
<div class="n-transfer-list-item__checkbox">
|
||||
<n-checkbox
|
||||
:disabled="disabled"
|
||||
:value="checked"
|
||||
@input="handleInput"
|
||||
/>
|
||||
</div>
|
||||
<slot />
|
||||
</li>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -31,11 +40,22 @@ export default {
|
||||
return true
|
||||
},
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
show: false
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
source: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
target: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -43,15 +63,16 @@ export default {
|
||||
return this.source ? 'n-transfer-list-item-source--transition' : 'n-transfer-list-item-target--transition'
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.show = true
|
||||
},
|
||||
methods: {
|
||||
handleClick () {
|
||||
this.$emit('click')
|
||||
if (!this.disabled) {
|
||||
this.$emit('click')
|
||||
}
|
||||
},
|
||||
handleInput (checked) {
|
||||
this.$emit('input', checked, this.value)
|
||||
if (!this.disabled) {
|
||||
this.$emit('input', checked, this.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,23 +19,21 @@
|
||||
<div class="n-transfer-list-body">
|
||||
<n-scrollbar ref="leftScrollbar">
|
||||
<ul class="n-transfer-list-body-content">
|
||||
<transition
|
||||
v-for="option in memorizedOptions"
|
||||
<n-transfer-list-item
|
||||
v-for="option in memorizedSourceOptions"
|
||||
:key="option.value"
|
||||
name="n-transfer-list-item-source--transition"
|
||||
:show="sourceValueSet.has(option.value)"
|
||||
:value="option.value"
|
||||
:checked="sourceCheckedValueSet.has(option.value)"
|
||||
:disabled="option.disabled"
|
||||
source
|
||||
@click="handleSourceCheckboxInput(
|
||||
!sourceCheckedValueSet.has(option.value),
|
||||
option.value
|
||||
)"
|
||||
>
|
||||
<n-transfer-list-item
|
||||
v-if="sourceValueSet.has(option.value)"
|
||||
:value="option.value"
|
||||
:checked="sourceCheckedValueSet.has(option.value)"
|
||||
@click="handleSourceCheckboxInput(
|
||||
!sourceCheckedValueSet.has(option.value),
|
||||
option.value
|
||||
)"
|
||||
>
|
||||
{{ option.label }}
|
||||
</n-transfer-list-item>
|
||||
</transition>
|
||||
{{ option.label }}
|
||||
</n-transfer-list-item>
|
||||
</ul>
|
||||
</n-scrollbar>
|
||||
</div>
|
||||
@ -67,23 +65,21 @@
|
||||
<div class="n-transfer-list-body">
|
||||
<n-scrollbar ref="rightScrollbar">
|
||||
<ul class="n-transfer-list-body-content">
|
||||
<transition
|
||||
v-for="option in memorizedOptions"
|
||||
<n-transfer-list-item
|
||||
v-for="option in memorizedTargetOptions"
|
||||
:key="option.value"
|
||||
name="n-transfer-list-item-target--transition"
|
||||
:show="targetValueSet.has(option.value)"
|
||||
:value="option.value"
|
||||
:checked="targetCheckedValueSet.has(option.value)"
|
||||
:disabled="option.disabled"
|
||||
target
|
||||
@click="handleTargetCheckboxInput(
|
||||
!targetCheckedValueSet.has(option.value),
|
||||
option.value
|
||||
)"
|
||||
>
|
||||
<n-transfer-list-item
|
||||
v-if="targetValueSet.has(option.value)"
|
||||
:value="option.value"
|
||||
:checked="targetCheckedValueSet.has(option.value)"
|
||||
@click="handleTargetCheckboxInput(
|
||||
!targetCheckedValueSet.has(option.value),
|
||||
option.value
|
||||
)"
|
||||
>
|
||||
{{ option.label }}
|
||||
</n-transfer-list-item>
|
||||
</transition>
|
||||
{{ option.label }}
|
||||
</n-transfer-list-item>
|
||||
</ul>
|
||||
</n-scrollbar>
|
||||
</div>
|
||||
@ -95,6 +91,7 @@
|
||||
import NCheckbox from '../../Checkbox'
|
||||
import NScrollbar from '../../Scrollbar'
|
||||
import NTransferListItem from './TransferListItem'
|
||||
import cloneDeep from 'lodash/cloneDeep'
|
||||
|
||||
export default {
|
||||
name: 'NTransfer',
|
||||
@ -111,13 +108,18 @@ export default {
|
||||
options: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
sourceCheckedValues: [],
|
||||
targetCheckedValues: [],
|
||||
memorizedOptions: null,
|
||||
memorizedSourceOptions: null,
|
||||
memorizedTargetOptions: null,
|
||||
init: true
|
||||
}
|
||||
},
|
||||
@ -130,7 +132,7 @@ export default {
|
||||
},
|
||||
valueToOptionMap () {
|
||||
const map = new Map()
|
||||
this.memorizedOptions.forEach(option => {
|
||||
this.options.forEach(option => {
|
||||
map.set(option.value, option)
|
||||
})
|
||||
return map
|
||||
@ -155,11 +157,12 @@ export default {
|
||||
},
|
||||
sourceOptions () {
|
||||
const valueSet = Array.isArray(this.value) ? new Set(this.value) : new Set()
|
||||
return this.memorizedOptions.filter(option => !valueSet.has(option.value))
|
||||
return this.memorizedSourceOptions.filter(option => !valueSet.has(option.value))
|
||||
},
|
||||
targetOptions () {
|
||||
const value = Array.isArray(this.value) ? this.value : []
|
||||
return value.filter(v => this.valueToOptionMap.has(v)).map(v => this.valueToOptionMap.get(v))
|
||||
const valueSet = Array.isArray(this.value) ? new Set(this.value) : new Set()
|
||||
return this.memorizedTargetOptions.filter(option => valueSet.has(option.value))
|
||||
// value.filter(v => this.valueToOptionMap.has(v)).map(v => this.valueToOptionMap.get(v))
|
||||
},
|
||||
orderedOptions () {
|
||||
return this.sourceOptions.concat(this.targetOptions)
|
||||
@ -169,7 +172,8 @@ export default {
|
||||
options (newOptions) {
|
||||
this.init = true
|
||||
this.$nextTick().then(() => {
|
||||
this.memorizedOptions = newOptions
|
||||
this.memorizedSourceOptions = cloneDeep(newOptions)
|
||||
this.memorizedTargetOptions = cloneDeep(newOptions)
|
||||
this.sourceCheckedValues = []
|
||||
this.targetCheckedValues = []
|
||||
return this.$nextTick()
|
||||
@ -184,7 +188,8 @@ export default {
|
||||
})
|
||||
},
|
||||
created () {
|
||||
this.memorizedOptions = this.options
|
||||
this.memorizedSourceOptions = cloneDeep(this.options)
|
||||
this.memorizedTargetOptions = cloneDeep(this.options)
|
||||
},
|
||||
methods: {
|
||||
emitInputEvent (value) {
|
||||
@ -201,7 +206,8 @@ export default {
|
||||
return
|
||||
}
|
||||
if (value) {
|
||||
this.sourceCheckedValues = this.sourceOptions.map(option => option.value)
|
||||
const newValues = this.sourceOptions.filter(option => !option.disabled).map(option => option.value).concat(this.sourceCheckedValues)
|
||||
this.sourceCheckedValues = Array.from(new Set(newValues))
|
||||
} else {
|
||||
this.sourceCheckedValues = []
|
||||
}
|
||||
@ -212,7 +218,8 @@ export default {
|
||||
return
|
||||
}
|
||||
if (value) {
|
||||
this.targetCheckedValues = this.targetOptions.map(option => option.value)
|
||||
const newValues = this.targetOptions.filter(option => !option.disabled).map(option => option.value).concat(this.targetCheckedValues)
|
||||
this.targetCheckedValues = Array.from(new Set(newValues))
|
||||
} else {
|
||||
this.targetCheckedValues = []
|
||||
}
|
||||
@ -236,20 +243,33 @@ export default {
|
||||
handleToTargetClick () {
|
||||
let newValue = Array.isArray(this.value) ? this.value : []
|
||||
newValue = this.sourceCheckedValues.concat(newValue)
|
||||
this.emitInputEvent(newValue)
|
||||
const headTargetOptions = this.sourceCheckedValues.map(value => this.valueToOptionMap.get(value)).map(option => ({
|
||||
...option
|
||||
}))
|
||||
const tailTargetOptions = this.memorizedTargetOptions.filter(option => !this.sourceCheckedValueSet.has(option.value)).map(option => ({
|
||||
...option
|
||||
}))
|
||||
const reorderedTargetOptions = headTargetOptions.concat(tailTargetOptions)
|
||||
this.memorizedTargetOptions = reorderedTargetOptions
|
||||
this.$nextTick().then(() => {
|
||||
this.emitInputEvent(newValue)
|
||||
})
|
||||
this.sourceCheckedValues = []
|
||||
},
|
||||
handleToSourceClick () {
|
||||
let newValue = Array.isArray(this.value) ? this.value : []
|
||||
newValue = newValue.filter(value => this.valueToOptionMap.has(value))
|
||||
const filteredValues = newValue.filter(value => this.targetCheckedValueSet.has(value))
|
||||
const stayedValues = newValue.filter(value => !this.targetCheckedValueSet.has(value))
|
||||
newValue = stayedValues
|
||||
const reorderedOptionsFirstPart = filteredValues.concat(stayedValues).map(value => this.valueToOptionMap.get(value))
|
||||
const reorderedOptionsSecondPart = this.memorizedOptions.filter(option => !filteredValues.includes(option.value) && !stayedValues.includes(option.value))
|
||||
const reorderedOptions = reorderedOptionsFirstPart.concat(reorderedOptionsSecondPart)
|
||||
this.memorizedOptions = reorderedOptions
|
||||
this.emitInputEvent(stayedValues)
|
||||
newValue = newValue.filter(value => !this.targetCheckedValueSet.has(value))
|
||||
const headSourceOptions = this.targetCheckedValues.map(value => this.valueToOptionMap.get(value)).map(option => ({
|
||||
...option
|
||||
}))
|
||||
const tailSourceOptions = this.memorizedSourceOptions.filter(option => !this.targetCheckedValueSet.has(option.value)).map(option => ({
|
||||
...option
|
||||
}))
|
||||
const reorderedSourceOptions = headSourceOptions.concat(tailSourceOptions)
|
||||
this.memorizedSourceOptions = reorderedSourceOptions
|
||||
this.$nextTick().then(() => {
|
||||
this.emitInputEvent(newValue)
|
||||
})
|
||||
this.targetCheckedValues = []
|
||||
}
|
||||
}
|
||||
|
@ -89,15 +89,15 @@
|
||||
&.n-checkbox--disabled {
|
||||
.n-checkbox__checkbox {
|
||||
cursor: not-allowed;
|
||||
background-color: rgba(255,255,255,.4);
|
||||
box-shadow: inset 0 0 0 1px $checkbox-border-color;
|
||||
background-color: rgba(255,255,255,.16);
|
||||
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .25);
|
||||
&::after {
|
||||
border: 1.5px solid rgba(255,255,255,.4);
|
||||
border: 1.5px solid rgba(255,255,255,.16);
|
||||
border-left: 0;
|
||||
border-top: 0;
|
||||
}
|
||||
&::before {
|
||||
border-bottom: 1.5px solid rgba(255,255,255,.4);
|
||||
border-bottom: 1.5px solid rgba(255,255,255,.16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,8 +45,8 @@
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
@include b(transfer-list-item) {
|
||||
@include slide-right-transition(transfer-list-item-source, 2s);
|
||||
@include slide-left-transition(transfer-list-item-target, 2s);
|
||||
@include slide-right-transition(transfer-list-item-source);
|
||||
@include slide-left-transition(transfer-list-item-target);
|
||||
cursor: pointer;
|
||||
max-height: 34px;
|
||||
display: flex;
|
||||
|
@ -220,7 +220,7 @@ $scrollbar-color--hover: rgba(255,255,255,0.3);
|
||||
}
|
||||
}
|
||||
|
||||
@mixin slide-right-transition($block, $duration: .2s, $delay: .2s) {
|
||||
@mixin slide-right-transition($block, $duration: .3s, $delay: .3s) {
|
||||
&.#{$namespace}-#{$block}--transition-leave-active {
|
||||
transition: transform $duration $slow-out-cubic-bezier, max-height $duration $default-cubic-bezier $delay;
|
||||
}
|
||||
@ -231,19 +231,19 @@ $scrollbar-color--hover: rgba(255,255,255,0.3);
|
||||
transform: translateX(0);
|
||||
}
|
||||
&.#{$namespace}-#{$block}--transition-enter {
|
||||
transform: translateX(150%);
|
||||
transform: translateX(120%);
|
||||
max-height: 0;
|
||||
}
|
||||
&.#{$namespace}-#{$block}--transition-leave {
|
||||
transform: translateX(0);
|
||||
}
|
||||
&.#{$namespace}-#{$block}--transition-leave-to {
|
||||
transform: translateX(150%);
|
||||
transform: translateX(120%);
|
||||
max-height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin slide-left-transition($block, $duration: .2s, $delay: .2s) {
|
||||
@mixin slide-left-transition($block, $duration: .3s, $delay: .3s) {
|
||||
&.#{$namespace}-#{$block}--transition-leave-active {
|
||||
transition: transform $duration $slow-out-cubic-bezier, max-height $duration $default-cubic-bezier $delay;
|
||||
}
|
||||
@ -254,14 +254,14 @@ $scrollbar-color--hover: rgba(255,255,255,0.3);
|
||||
transform: translateX(0);
|
||||
}
|
||||
&.#{$namespace}-#{$block}--transition-enter {
|
||||
transform: translateX(-150%);
|
||||
transform: translateX(-120%);
|
||||
max-height: 0;
|
||||
}
|
||||
&.#{$namespace}-#{$block}--transition-leave {
|
||||
transform: translateX(0);
|
||||
}
|
||||
&.#{$namespace}-#{$block}--transition-leave-to {
|
||||
transform: translateX(-150%);
|
||||
transform: translateX(-120%);
|
||||
max-height: 0;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user