vuetify
Version:
Vue Material Component Framework
116 lines (115 loc) • 4.26 kB
JavaScript
/* eslint-disable max-statements */
/* eslint-disable no-labels */
// Utilities
import { computed, ref, unref, watchEffect } from 'vue';
import { getPropertyFromItem, propsFactory, wrapInArray } from "../util/index.mjs"; // Types
/**
* - match without highlight
* - single match (index), length already known
* - single match (start, end)
* - multiple matches (start, end), probably shouldn't overlap
*/
// Composables
export const defaultFilter = (value, query, item) => {
if (value == null || query == null) return -1;
return value.toString().toLocaleLowerCase().indexOf(query.toString().toLocaleLowerCase());
};
export const makeFilterProps = propsFactory({
customFilter: Function,
customKeyFilter: Object,
filterKeys: [Array, String],
filterMode: {
type: String,
default: 'intersection'
},
noFilter: Boolean
}, 'filter');
export function filterItems(items, query, options) {
const array = [];
// always ensure we fall back to a functioning filter
const filter = options?.default ?? defaultFilter;
const keys = options?.filterKeys ? wrapInArray(options.filterKeys) : false;
const customFiltersLength = Object.keys(options?.customKeyFilter ?? {}).length;
if (!items?.length) return array;
loop: for (let i = 0; i < items.length; i++) {
const [item, transformed = item] = wrapInArray(items[i]);
const customMatches = {};
const defaultMatches = {};
let match = -1;
if (query && !options?.noFilter) {
if (typeof item === 'object') {
const filterKeys = keys || Object.keys(transformed);
for (const key of filterKeys) {
const value = getPropertyFromItem(transformed, key, transformed);
const keyFilter = options?.customKeyFilter?.[key];
match = keyFilter ? keyFilter(value, query, item) : filter(value, query, item);
if (match !== -1 && match !== false) {
if (keyFilter) customMatches[key] = match;else defaultMatches[key] = match;
} else if (options?.filterMode === 'every') {
continue loop;
}
}
} else {
match = filter(item, query, item);
if (match !== -1 && match !== false) {
defaultMatches.title = match;
}
}
const defaultMatchesLength = Object.keys(defaultMatches).length;
const customMatchesLength = Object.keys(customMatches).length;
if (!defaultMatchesLength && !customMatchesLength) continue;
if (options?.filterMode === 'union' && customMatchesLength !== customFiltersLength && !defaultMatchesLength) continue;
if (options?.filterMode === 'intersection' && (customMatchesLength !== customFiltersLength || !defaultMatchesLength)) continue;
}
array.push({
index: i,
matches: {
...defaultMatches,
...customMatches
}
});
}
return array;
}
export function useFilter(props, items, query, options) {
const filteredItems = ref([]);
const filteredMatches = ref(new Map());
const transformedItems = computed(() => options?.transform ? unref(items).map(item => [item, options.transform(item)]) : unref(items));
watchEffect(() => {
const _query = typeof query === 'function' ? query() : unref(query);
const strQuery = typeof _query !== 'string' && typeof _query !== 'number' ? '' : String(_query);
const results = filterItems(transformedItems.value, strQuery, {
customKeyFilter: {
...props.customKeyFilter,
...unref(options?.customKeyFilter)
},
default: props.customFilter,
filterKeys: props.filterKeys,
filterMode: props.filterMode,
noFilter: props.noFilter
});
const originalItems = unref(items);
const _filteredItems = [];
const _filteredMatches = new Map();
results.forEach(_ref => {
let {
index,
matches
} = _ref;
const item = originalItems[index];
_filteredItems.push(item);
_filteredMatches.set(item.value, matches);
});
filteredItems.value = _filteredItems;
filteredMatches.value = _filteredMatches;
});
function getMatches(item) {
return filteredMatches.value.get(item.value);
}
return {
filteredItems,
filteredMatches,
getMatches
};
}
//# sourceMappingURL=filter.mjs.map