181 lines
5.2 KiB
JavaScript
181 lines
5.2 KiB
JavaScript
import {$$, addClass, Animation, assign, css, fastdom, hasClass, height, includes, isBoolean, isFunction, isVisible, noop, Promise, removeClass, toFloat, toggleClass, toNodes, Transition, trigger} from 'uikit-util';
|
|
|
|
export default {
|
|
|
|
props: {
|
|
cls: Boolean,
|
|
animation: 'list',
|
|
duration: Number,
|
|
origin: String,
|
|
transition: String
|
|
},
|
|
|
|
data: {
|
|
cls: false,
|
|
animation: [false],
|
|
duration: 200,
|
|
origin: false,
|
|
transition: 'linear',
|
|
clsEnter: 'uk-togglabe-enter',
|
|
clsLeave: 'uk-togglabe-leave',
|
|
|
|
initProps: {
|
|
overflow: '',
|
|
height: '',
|
|
paddingTop: '',
|
|
paddingBottom: '',
|
|
marginTop: '',
|
|
marginBottom: ''
|
|
},
|
|
|
|
hideProps: {
|
|
overflow: 'hidden',
|
|
height: 0,
|
|
paddingTop: 0,
|
|
paddingBottom: 0,
|
|
marginTop: 0,
|
|
marginBottom: 0
|
|
}
|
|
|
|
},
|
|
|
|
computed: {
|
|
|
|
hasAnimation({animation}) {
|
|
return !!animation[0];
|
|
},
|
|
|
|
hasTransition({animation}) {
|
|
return this.hasAnimation && animation[0] === true;
|
|
}
|
|
|
|
},
|
|
|
|
methods: {
|
|
|
|
toggleElement(targets, toggle, animate) {
|
|
return new Promise(resolve =>
|
|
Promise.all(toNodes(targets).map(el => {
|
|
|
|
const show = isBoolean(toggle) ? toggle : !this.isToggled(el);
|
|
|
|
if (!trigger(el, `before${show ? 'show' : 'hide'}`, [this])) {
|
|
return Promise.reject();
|
|
}
|
|
|
|
const promise = (
|
|
isFunction(animate)
|
|
? animate
|
|
: animate === false || !this.hasAnimation
|
|
? this._toggle
|
|
: this.hasTransition
|
|
? toggleHeight(this)
|
|
: toggleAnimation(this)
|
|
)(el, show);
|
|
|
|
const cls = show ? this.clsEnter : this.clsLeave;
|
|
|
|
addClass(el, cls);
|
|
|
|
trigger(el, show ? 'show' : 'hide', [this]);
|
|
|
|
const done = () => {
|
|
removeClass(el, cls);
|
|
trigger(el, show ? 'shown' : 'hidden', [this]);
|
|
this.$update(el);
|
|
};
|
|
|
|
return promise ? promise.then(done, () => {
|
|
removeClass(el, cls);
|
|
return Promise.reject();
|
|
}) : done();
|
|
|
|
})).then(resolve, noop)
|
|
);
|
|
},
|
|
|
|
isToggled(el = this.$el) {
|
|
[el] = toNodes(el);
|
|
return hasClass(el, this.clsEnter)
|
|
? true
|
|
: hasClass(el, this.clsLeave)
|
|
? false
|
|
: this.cls
|
|
? hasClass(el, this.cls.split(' ')[0])
|
|
: isVisible(el);
|
|
},
|
|
|
|
_toggle(el, toggled) {
|
|
|
|
if (!el) {
|
|
return;
|
|
}
|
|
|
|
toggled = Boolean(toggled);
|
|
|
|
let changed;
|
|
if (this.cls) {
|
|
changed = includes(this.cls, ' ') || toggled !== hasClass(el, this.cls);
|
|
changed && toggleClass(el, this.cls, includes(this.cls, ' ') ? undefined : toggled);
|
|
} else {
|
|
changed = toggled === el.hidden;
|
|
changed && (el.hidden = !toggled);
|
|
}
|
|
|
|
$$('[autofocus]', el).some(el => isVisible(el) ? el.focus() || true : el.blur());
|
|
|
|
if (changed) {
|
|
trigger(el, 'toggled', [toggled, this]);
|
|
this.$update(el);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
export function toggleHeight({isToggled, duration, initProps, hideProps, transition, _toggle}) {
|
|
return (el, show) => {
|
|
|
|
const inProgress = Transition.inProgress(el);
|
|
const inner = el.hasChildNodes ? toFloat(css(el.firstElementChild, 'marginTop')) + toFloat(css(el.lastElementChild, 'marginBottom')) : 0;
|
|
const currentHeight = isVisible(el) ? height(el) + (inProgress ? 0 : inner) : 0;
|
|
|
|
Transition.cancel(el);
|
|
|
|
if (!isToggled(el)) {
|
|
_toggle(el, true);
|
|
}
|
|
|
|
height(el, '');
|
|
|
|
// Update child components first
|
|
fastdom.flush();
|
|
|
|
const endHeight = height(el) + (inProgress ? 0 : inner);
|
|
height(el, currentHeight);
|
|
|
|
return (show
|
|
? Transition.start(el, assign({}, initProps, {overflow: 'hidden', height: endHeight}), Math.round(duration * (1 - currentHeight / endHeight)), transition)
|
|
: Transition.start(el, hideProps, Math.round(duration * (currentHeight / endHeight)), transition).then(() => _toggle(el, false))
|
|
).then(() => css(el, initProps));
|
|
|
|
};
|
|
}
|
|
|
|
function toggleAnimation(cmp) {
|
|
return (el, show) => {
|
|
|
|
Animation.cancel(el);
|
|
|
|
const {animation, duration, _toggle} = cmp;
|
|
|
|
if (show) {
|
|
_toggle(el, true);
|
|
return Animation.in(el, animation[0], duration, cmp.origin);
|
|
}
|
|
|
|
return Animation.out(el, animation[1] || animation[0], duration, cmp.origin).then(() => _toggle(el, false));
|
|
};
|
|
}
|