feat(transfer): animation

This commit is contained in:
07akioni 2019-09-03 23:41:01 +08:00
parent 97d16c86c2
commit 7d5d0383bf
6 changed files with 125 additions and 83 deletions

View File

@ -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
}))
}

View File

@ -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)
}
}
}
}

View File

@ -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 = []
}
}

View File

@ -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);
}
}
}

View File

@ -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;

View File

@ -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;
}
}