265 lines
7.6 KiB
JavaScript
265 lines
7.6 KiB
JavaScript
// Utilities
|
|
import { capitalize, inject, provide, ref, watchEffect } from 'vue';
|
|
import { consoleError, propsFactory } from "../../../util/index.mjs"; // Types
|
|
export const makeDataTableHeaderProps = propsFactory({
|
|
headers: Array
|
|
}, 'DataTable-header');
|
|
export const VDataTableHeadersSymbol = Symbol.for('vuetify:data-table-headers');
|
|
const defaultHeader = {
|
|
title: '',
|
|
sortable: false
|
|
};
|
|
const defaultActionHeader = {
|
|
...defaultHeader,
|
|
width: 48
|
|
};
|
|
function priorityQueue() {
|
|
let arr = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
|
|
const queue = arr.map(element => ({
|
|
element,
|
|
priority: 0
|
|
}));
|
|
return {
|
|
enqueue: (element, priority) => {
|
|
let added = false;
|
|
for (let i = 0; i < queue.length; i++) {
|
|
const item = queue[i];
|
|
if (item.priority > priority) {
|
|
queue.splice(i, 0, {
|
|
element,
|
|
priority
|
|
});
|
|
added = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!added) queue.push({
|
|
element,
|
|
priority
|
|
});
|
|
},
|
|
size: () => queue.length,
|
|
count: () => {
|
|
let count = 0;
|
|
if (!queue.length) return 0;
|
|
const whole = Math.floor(queue[0].priority);
|
|
for (let i = 0; i < queue.length; i++) {
|
|
if (Math.floor(queue[i].priority) === whole) count += 1;
|
|
}
|
|
return count;
|
|
},
|
|
dequeue: () => {
|
|
return queue.shift();
|
|
}
|
|
};
|
|
}
|
|
function extractLeaves(item) {
|
|
let columns = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
|
|
if (!item.children) {
|
|
columns.push(item);
|
|
} else {
|
|
for (const child of item.children) {
|
|
extractLeaves(child, columns);
|
|
}
|
|
}
|
|
return columns;
|
|
}
|
|
function extractKeys(headers) {
|
|
let keys = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Set();
|
|
for (const item of headers) {
|
|
if (item.key) keys.add(item.key);
|
|
if (item.children) {
|
|
extractKeys(item.children, keys);
|
|
}
|
|
}
|
|
return keys;
|
|
}
|
|
function getDefaultItem(item) {
|
|
if (!item.key) return undefined;
|
|
if (item.key === 'data-table-group') return defaultHeader;
|
|
if (['data-table-expand', 'data-table-select'].includes(item.key)) return defaultActionHeader;
|
|
return undefined;
|
|
}
|
|
function getDepth(item) {
|
|
let depth = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
|
|
if (!item.children) return depth;
|
|
return Math.max(depth, ...item.children.map(child => getDepth(child, depth + 1)));
|
|
}
|
|
function parseFixedColumns(items) {
|
|
let seenFixed = false;
|
|
function setFixed(item) {
|
|
let parentFixed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
if (!item) return;
|
|
if (parentFixed) {
|
|
item.fixed = true;
|
|
}
|
|
if (item.fixed) {
|
|
if (item.children) {
|
|
for (let i = item.children.length - 1; i >= 0; i--) {
|
|
setFixed(item.children[i], true);
|
|
}
|
|
} else {
|
|
if (!seenFixed) {
|
|
item.lastFixed = true;
|
|
} else if (isNaN(+item.width)) {
|
|
consoleError(`Multiple fixed columns should have a static width (key: ${item.key})`);
|
|
}
|
|
seenFixed = true;
|
|
}
|
|
} else {
|
|
if (item.children) {
|
|
for (let i = item.children.length - 1; i >= 0; i--) {
|
|
setFixed(item.children[i]);
|
|
}
|
|
} else {
|
|
seenFixed = false;
|
|
}
|
|
}
|
|
}
|
|
for (let i = items.length - 1; i >= 0; i--) {
|
|
setFixed(items[i]);
|
|
}
|
|
function setFixedOffset(item) {
|
|
let fixedOffset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
|
|
if (!item) return fixedOffset;
|
|
if (item.children) {
|
|
item.fixedOffset = fixedOffset;
|
|
for (const child of item.children) {
|
|
fixedOffset = setFixedOffset(child, fixedOffset);
|
|
}
|
|
} else if (item.fixed) {
|
|
item.fixedOffset = fixedOffset;
|
|
fixedOffset += parseFloat(item.width || '0') || 0;
|
|
}
|
|
return fixedOffset;
|
|
}
|
|
let fixedOffset = 0;
|
|
for (const item of items) {
|
|
fixedOffset = setFixedOffset(item, fixedOffset);
|
|
}
|
|
}
|
|
function parse(items, maxDepth) {
|
|
const headers = [];
|
|
let currentDepth = 0;
|
|
const queue = priorityQueue(items);
|
|
while (queue.size() > 0) {
|
|
let rowSize = queue.count();
|
|
const row = [];
|
|
let fraction = 1;
|
|
while (rowSize > 0) {
|
|
const {
|
|
element: item,
|
|
priority
|
|
} = queue.dequeue();
|
|
const diff = maxDepth - currentDepth - getDepth(item);
|
|
row.push({
|
|
...item,
|
|
rowspan: diff ?? 1,
|
|
colspan: item.children ? extractLeaves(item).length : 1
|
|
});
|
|
if (item.children) {
|
|
for (const child of item.children) {
|
|
// This internally sorts items that are on the same priority "row"
|
|
const sort = priority % 1 + fraction / Math.pow(10, currentDepth + 2);
|
|
queue.enqueue(child, currentDepth + diff + sort);
|
|
}
|
|
}
|
|
fraction += 1;
|
|
rowSize -= 1;
|
|
}
|
|
currentDepth += 1;
|
|
headers.push(row);
|
|
}
|
|
const columns = items.map(item => extractLeaves(item)).flat();
|
|
return {
|
|
columns,
|
|
headers
|
|
};
|
|
}
|
|
function convertToInternalHeaders(items) {
|
|
const internalHeaders = [];
|
|
for (const item of items) {
|
|
const defaultItem = {
|
|
...getDefaultItem(item),
|
|
...item
|
|
};
|
|
const key = defaultItem.key ?? (typeof defaultItem.value === 'string' ? defaultItem.value : null);
|
|
const value = defaultItem.value ?? key ?? null;
|
|
const internalItem = {
|
|
...defaultItem,
|
|
key,
|
|
value,
|
|
sortable: defaultItem.sortable ?? (defaultItem.key != null || !!defaultItem.sort),
|
|
children: defaultItem.children ? convertToInternalHeaders(defaultItem.children) : undefined
|
|
};
|
|
internalHeaders.push(internalItem);
|
|
}
|
|
return internalHeaders;
|
|
}
|
|
export function createHeaders(props, options) {
|
|
const headers = ref([]);
|
|
const columns = ref([]);
|
|
const sortFunctions = ref({});
|
|
const sortRawFunctions = ref({});
|
|
const filterFunctions = ref({});
|
|
watchEffect(() => {
|
|
const _headers = props.headers || Object.keys(props.items[0] ?? {}).map(key => ({
|
|
key,
|
|
title: capitalize(key)
|
|
}));
|
|
const items = _headers.slice();
|
|
const keys = extractKeys(items);
|
|
if (options?.groupBy?.value.length && !keys.has('data-table-group')) {
|
|
items.unshift({
|
|
key: 'data-table-group',
|
|
title: 'Group'
|
|
});
|
|
}
|
|
if (options?.showSelect?.value && !keys.has('data-table-select')) {
|
|
items.unshift({
|
|
key: 'data-table-select'
|
|
});
|
|
}
|
|
if (options?.showExpand?.value && !keys.has('data-table-expand')) {
|
|
items.push({
|
|
key: 'data-table-expand'
|
|
});
|
|
}
|
|
const internalHeaders = convertToInternalHeaders(items);
|
|
parseFixedColumns(internalHeaders);
|
|
const maxDepth = Math.max(...internalHeaders.map(item => getDepth(item))) + 1;
|
|
const parsed = parse(internalHeaders, maxDepth);
|
|
headers.value = parsed.headers;
|
|
columns.value = parsed.columns;
|
|
const flatHeaders = parsed.headers.flat(1);
|
|
for (const header of flatHeaders) {
|
|
if (!header.key) continue;
|
|
if (header.sortable) {
|
|
if (header.sort) {
|
|
sortFunctions.value[header.key] = header.sort;
|
|
}
|
|
if (header.sortRaw) {
|
|
sortRawFunctions.value[header.key] = header.sortRaw;
|
|
}
|
|
}
|
|
if (header.filter) {
|
|
filterFunctions.value[header.key] = header.filter;
|
|
}
|
|
}
|
|
});
|
|
const data = {
|
|
headers,
|
|
columns,
|
|
sortFunctions,
|
|
sortRawFunctions,
|
|
filterFunctions
|
|
};
|
|
provide(VDataTableHeadersSymbol, data);
|
|
return data;
|
|
}
|
|
export function useHeaders() {
|
|
const data = inject(VDataTableHeadersSymbol);
|
|
if (!data) throw new Error('Missing headers!');
|
|
return data;
|
|
}
|
|
//# sourceMappingURL=headers.mjs.map
|