uikit
Version:
UIkit is a lightweight and modular front-end framework for developing fast and powerful web interfaces.
209 lines (143 loc) • 5.16 kB
JavaScript
import Animate from '../mixin/animate';
import {$$, append, assign, css, data, each, fastdom, children as getChildren, hasClass, includes, isEmpty, isEqual, isUndefined, matches, parseOptions, Promise, toggleClass, trigger} from 'uikit-util';
export default {
mixins: [Animate],
args: 'target',
props: {
target: Boolean,
selActive: Boolean
},
data: {
target: null,
selActive: false,
attrItem: 'uk-filter-control',
cls: 'uk-active',
duration: 250
},
computed: {
toggles: {
get({attrItem}, $el) {
return $$(`[${attrItem}],[data-${attrItem}]`, $el);
},
watch() {
this.updateState();
if (this.selActive !== false) {
const actives = $$(this.selActive, this.$el);
this.toggles.forEach(el => toggleClass(el, this.cls, includes(actives, el)));
}
},
immediate: true
},
children: {
get({target}, $el) {
return $$(`${target} > *`, $el);
},
watch(list, old) {
if (old && !isEqualList(list, old)) {
this.updateState();
}
},
immediate: true
}
},
events: [
{
name: 'click',
delegate() {
return `[${this.attrItem}],[data-${this.attrItem}]`;
},
handler(e) {
e.preventDefault();
this.apply(e.current);
}
}
],
methods: {
apply(el) {
const prevState = this.getState();
const newState = mergeState(el, this.attrItem, this.getState());
if (!isEqualState(prevState, newState)) {
this.setState(newState);
}
},
getState() {
return this.toggles
.filter(item => hasClass(item, this.cls))
.reduce((state, el) => mergeState(el, this.attrItem, state), {filter: {'': ''}, sort: []});
},
setState(state, animate = true) {
state = assign({filter: {'': ''}, sort: []}, state);
trigger(this.$el, 'beforeFilter', [this, state]);
this.toggles.forEach(el => toggleClass(el, this.cls, !!matchFilter(el, this.attrItem, state)));
Promise.all($$(this.target, this.$el).map(target => {
const filterFn = () => {
applyState(state, target, getChildren(target));
this.$update(this.$el);
};
return animate ? this.animate(filterFn, target) : filterFn();
})).then(() => trigger(this.$el, 'afterFilter', [this]));
},
updateState() {
fastdom.write(() => this.setState(this.getState(), false));
}
}
};
function getFilter(el, attr) {
return parseOptions(data(el, attr), ['filter']);
}
function isEqualState(stateA, stateB) {
return ['filter', 'sort'].every(prop => isEqual(stateA[prop], stateB[prop]));
}
function applyState(state, target, children) {
const selector = getSelector(state);
children.forEach(el => css(el, 'display', selector && !matches(el, selector) ? 'none' : ''));
const [sort, order] = state.sort;
if (sort) {
const sorted = sortItems(children, sort, order);
if (!isEqual(sorted, children)) {
append(target, sorted);
}
}
}
function mergeState(el, attr, state) {
const filterBy = getFilter(el, attr);
const {filter, group, sort, order = 'asc'} = filterBy;
if (filter || isUndefined(sort)) {
if (group) {
if (filter) {
delete state.filter[''];
state.filter[group] = filter;
} else {
delete state.filter[group];
if (isEmpty(state.filter) || '' in state.filter) {
state.filter = {'': filter || ''};
}
}
} else {
state.filter = {'': filter || ''};
}
}
if (!isUndefined(sort)) {
state.sort = [sort, order];
}
return state;
}
function matchFilter(el, attr, {filter: stateFilter = {'': ''}, sort: [stateSort, stateOrder]}) {
const {filter = '', group = '', sort, order = 'asc'} = getFilter(el, attr);
return isUndefined(sort)
? group in stateFilter && filter === stateFilter[group]
|| !filter && group && !(group in stateFilter) && !stateFilter['']
: stateSort === sort && stateOrder === order;
}
function isEqualList(listA, listB) {
return listA.length === listB.length
&& listA.every(el => ~listB.indexOf(el));
}
function getSelector({filter}) {
let selector = '';
each(filter, value => selector += value || '');
return selector;
}
function sortItems(nodes, sort, order) {
return assign([], nodes).sort((a, b) => data(a, sort).localeCompare(data(b, sort), undefined, {numeric: true}) * (order === 'asc' || -1));
}