Skip to content

Commit 81dd92b

Browse files
Merge branch 'match-column-widths' into dev
2 parents 2fe5d6b + 83babb6 commit 81dd92b

File tree

6 files changed

+128
-29
lines changed

6 files changed

+128
-29
lines changed

src/plugin/VDrilldownTable.vue

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
<component
33
:is="tableType"
44
v-if="tableType"
5-
v-model="loadedDrilldown.modelValue"
65
v-bind="$attrs"
6+
v-model="loadedDrilldown.modelValue"
77
:class="tableClasses"
8+
:data-vdt-id="tableId"
89
:density="loadedDrilldown.density"
910
:expand-on-click="loadedDrilldown.expandOnClick"
1011
:expanded="loadedDrilldown.expanded"
@@ -66,6 +67,7 @@
6667
<HeadersSlot
6768
:key="level"
6869
:colors="loadedDrilldown.colors"
70+
:column-widths="loadedDrilldown.columnWidths"
6971
:density="loadedDrilldown.density"
7072
:items="loadedDrilldown.items"
7173
:level="level"
@@ -78,14 +80,14 @@
7880
size: loadedDrilldown.loaderSize,
7981
skeltonType: loadedDrilldown.skeltonType,
8082
}"
83+
:match-column-widths="loadedDrilldown.matchColumnWidths"
8184
:select-strategy="loadedDrilldown.selectStrategy"
8285
:show-select="loadedDrilldown.showSelect"
8386
:slot-props="{ ...props }"
8487
:sort-asc-icon="loadedDrilldown.sortAscIcon"
8588
:sort-by="loadedDrilldown.sortBy"
8689
:table-model-value="loadedDrilldown.modelValue"
8790
>
88-
<!-- @click:selectAll="emitAllSelectedEvent($event)" -->
8991
<!-- Pass on all scoped slots -->
9092
<template
9193
v-for="(_, slot) in slots"
@@ -201,6 +203,7 @@
201203
<VDrilldownTable
202204
:key="item.raw"
203205
:colors="colors"
206+
:column-widths="loadedDrilldown.columnWidths"
204207
:density="loadedDrilldown.density"
205208
:drilldown="loadedDrilldown"
206209
:headers="item.raw[itemChildrenKey]?.headers"
@@ -215,9 +218,9 @@
215218
:loaderType="item.raw[itemChildrenKey]?.loaderType"
216219
:loading="item.raw[itemChildrenKey]?.loading"
217220
:loadingText="loadingText"
221+
:match-column-widths="loadedDrilldown.matchColumnWidths"
218222
:multi-sort="item.raw[itemChildrenKey]?.multiSort"
219223
:no-data-text="loadedDrilldown.noDataText"
220-
:parent-ref="parentTableRef"
221224
:server="item.raw[itemChildrenKey]?.server"
222225
:skeltonType="item.raw[itemChildrenKey]?.skeltonType"
223226
:sort-by="loadedDrilldown.sortBy"
@@ -331,7 +334,10 @@ import {
331334
} from './slots';
332335
import { useEmitUpdatedExpanded } from './composables/emits';
333336
import { useMergeDeep } from './composables/helpers';
334-
import { useSetLoadedDrilldown } from './composables/loadedDrilldown';
337+
import {
338+
useGetHeaderColumnWidths,
339+
useSetLoadedDrilldown,
340+
} from './composables/loadedDrilldown';
335341
import { useTableClasses } from './composables/classes';
336342
import { useTableStyles } from './composables/styles';
337343
import {
@@ -367,6 +373,7 @@ const emit = defineEmits([
367373
const props = withDefaults(defineProps<Props>(), { ...AllProps });
368374
369375
const slots = useSlots();
376+
const attrs = useAttrs();
370377
371378
const tableType = shallowRef<TableType>(null);
372379
@@ -394,7 +401,7 @@ const defaultDrilldownSettings = { ...props, ...loadedDrilldown };
394401
395402
396403
// -------------------------------------------------- Data //
397-
const parentTableRef = ref<string>('');
404+
const tableId = ref<string>(attrs['data-vdt-id'] as string ?? `v-drilldown-table-${Date.now()}`);
398405
const levelSearch = ref<string>('');
399406
const theme = useTheme();
400407
@@ -410,6 +417,7 @@ const hidingNoData = computed(() => {
410417
return loadedDrilldown.hideNoData;
411418
});
412419
420+
413421
// -------------------------------------------------- Watch //
414422
// eslint-disable-next-line @typescript-eslint/no-explicit-any
415423
watchOnce(props as any, () => {
@@ -420,13 +428,18 @@ watchOnce(props as any, () => {
420428
loadedDrilldown.itemsPerPage = props.itemsPerPage;
421429
}, { immediate: false });
422430
431+
423432
watch(() => props.items, () => {
424-
setLoadedDrilldown();
425-
});
433+
if (!props.loading) {
434+
setLoadedDrilldown();
435+
}
436+
}, { deep: true });
426437
427-
watch(() => props.loading, (value) => {
428-
if (value) {
429-
loadedDrilldown.loading = value;
438+
439+
watch(() => props.loading, () => {
440+
if (props.loading) {
441+
loadedDrilldown.loading = props.loading;
442+
return false;
430443
}
431444
432445
setLoadedDrilldown();
@@ -484,12 +497,17 @@ function setLoadedDrilldown(): void {
484497
level: props.level,
485498
levels: props.levels,
486499
loadedDrilldown,
500+
matchColumnWidths: props.matchColumnWidths,
487501
rawItem: props.item?.raw,
488502
});
489503
return;
490504
}
491505
492506
loadedDrilldown = useMergeDeep(loadedDrilldown, props) as Props;
507+
508+
if (props.matchColumnWidths && loadedDrilldown?.columnWidths?.length === 0) {
509+
loadedDrilldown.columnWidths = useGetHeaderColumnWidths({ tableId });
510+
}
493511
}
494512
495513
// -------------------------------------------------- Emit Events //

src/plugin/composables/loadedDrilldown.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,33 @@
11
import {
22
Props,
3+
UseGetHeaderColumnWidths,
34
UseSetLoadedDrilldown,
45
} from '@/types';
56
import { useMergeDeep } from './helpers';
67

78

9+
export const useGetHeaderColumnWidths: UseGetHeaderColumnWidths = (options) => {
10+
const { tableId } = options;
11+
const columnWidths = [] as number[];
12+
13+
const id = unref(tableId);
14+
15+
const domHeaders = document.querySelectorAll(`[data-vdt-id="${id}"] .v-drilldown-table--header-row-th-1`);
16+
17+
if (Object.keys(domHeaders).length > 0) {
18+
for (let i = 0; i < domHeaders.length; i += 1) {
19+
const column = domHeaders[i] as HTMLElement;
20+
21+
columnWidths.push(column.offsetWidth);
22+
}
23+
}
24+
25+
return columnWidths;
26+
};
27+
28+
829
export const useSetLoadedDrilldown: UseSetLoadedDrilldown = (options) => {
9-
const { loadedDrilldown, drilldown, rawItem, level, levels } = options;
30+
const { drilldown, rawItem, level, levels, loadedDrilldown } = options;
1031
let settings = loadedDrilldown;
1132

1233
settings = useMergeDeep(loadedDrilldown, drilldown) as Props;

src/plugin/slots/HeadersSlot.vue

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,15 +114,19 @@ const props = withDefaults(defineProps<HeaderSlotProps>(), {
114114
});
115115
116116
const allSelectable = ref();
117-
const columns = computed<Column[]>(() => props.slotProps.columns);
118117
const iconOptions = inject<IconOptions>(Symbol.for('vuetify:icons'));
119118
const isAllSelected = ref<boolean>(false);
120119
const items = ref(props.items);
120+
const matchColumnWidths = ref<boolean>(props.matchColumnWidths);
121+
const columnWidths = ref<number[]>(props.columnWidths || []);
121122
const sortAscIcon = ref(props.sortAscIcon);
122123
const tableModelValue = computed(() => props.tableModelValue);
123124
const theme = useTheme();
124125
125126
127+
const columns = computed<Column[]>(() => checkColumnWidthUsage());
128+
129+
126130
watch(() => props.items, (newItems) => {
127131
items.value = newItems;
128132
@@ -139,6 +143,35 @@ const headerRowClasses = computed<object>(() => {
139143
return useHeaderRowClasses({ level: props.level });
140144
});
141145
146+
function checkColumnWidthUsage(): Column[] {
147+
const cols = props.slotProps.columns;
148+
149+
if (props.level <= 1 || !matchColumnWidths.value) {
150+
return cols;
151+
}
152+
153+
if (columnWidths.value.length === 0) {
154+
throw new Error('VDrilldownTable (matchColumnWidths): There was an issue getting the parent tables widths.');
155+
}
156+
157+
// Attach the width to the column //
158+
Object.entries(cols).forEach(([index]) => {
159+
160+
// ? Do not add width to the last row
161+
// ? This can cause width issues if columns length differ
162+
if (Object.keys(cols).length === parseInt(index) + 1) {
163+
return;
164+
}
165+
166+
// Only set the width if the user has not already set it //
167+
if (typeof cols[index].width === 'undefined') {
168+
cols[index].width = columnWidths.value[parseInt(index)] as number;
169+
}
170+
});
171+
172+
return cols;
173+
}
174+
142175
143176
// -------------------------------------------------- Header Row Cells //
144177
const cellAlignClasses = (align: string): object => {

src/plugin/utils/props.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export const AllProps = {
3232
percentageChange: 25,
3333
percentageDirection: 'desc',
3434
}) as const,
35+
columnWidths: () => ([]),
3536
// customFilter: undefined, // ? Needs Testing
3637
// customKeyFilter: undefined, // ? Needs Testing
3738
density: 'default',
@@ -59,7 +60,7 @@ export const AllProps = {
5960
items: () => [],
6061
itemsLength: 0, // ? Not sure if this will be used
6162
itemsPerPage: 10,
62-
// itemsPerPageText: '$vuetify.dataFooter.itemsPerPageText', // * Works - Keep Commented Out
63+
// itemsPerPageText: ', // * Works - Keep Commented Out
6364
// itemsPerPageOptions: [ // * Works - Keep Commented Out
6465
// {
6566
// title: '10',
@@ -91,6 +92,7 @@ export const AllProps = {
9192
loaderType: 'linear',
9293
loading: false,
9394
loadingText: '$vuetify.dataIterator.loadingText',
95+
matchColumnWidths: true,
9496
multiSort: false, // ! Not sure if this is working correctly - Is binding prop
9597
// nextIcon: '', // * Works - Keep Commented Out
9698
noDataText: '$vuetify.noDataText',

src/stores/props.ts

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,25 @@ export const usePropsStore = defineStore('props', () => {
99
{
1010
default: '1px',
1111
desc: `Sets the height for the linear progress loader. See <a href="${links.vuetify}/en/api/v-progress-linear/#props-height" target="_blank"><code class="inline-code">VProgressLinear</code></a> for more information.`,
12-
name: 'loaderHeight',
12+
name: 'loader-height',
1313
type: 'VProgressLinear["$props"]["height"]',
1414
},
1515
{
1616
default: 'default',
1717
desc: `Sets the diameter of the circular progress loader circle in pixels. See <a href="${links.vuetify}/en/api/v-progress-circular/#props-size" target="_blank"><code class="inline-code">VProgressCircular</code></a> for more information.`,
18-
name: 'loaderSize',
18+
name: 'loader-size',
1919
type: 'VProgressCircular["$props"]["size"]',
2020
},
2121
{
2222
default: 'linear',
2323
desc: 'Sets the type of loader. Available types are <code class="inline-code">linear</code>, <code class="inline-code">circular</code>, <code class="inline-code">text</code> and <code class="inline-code">skelton</code>. You can also use multiple loaders by passing an array of types. The order of the array determines the order of the loaders. To use the <a href="#slots-supported-loader"><code class="inline-code">loader</code></a> slot, set this prop to <code class="inline-code">null</code> or <code class="inline-code">false</code>.',
24-
name: 'loaderType',
24+
name: 'loader-type',
2525
type: 'string | string[] | false | null',
2626
},
2727
{
2828
default: 'heading@1',
2929
desc: `The type of skelton loader to show. See <a href="${links.vuetify}/en/api/v-skeleton-loader/#props-type" target="_blank">Vuetify Skeleton Loader</a> for more information.`,
30-
name: 'skeltonType',
30+
name: 'skelton-type',
3131
type: 'string',
3232
},
3333
];
@@ -40,6 +40,12 @@ export const usePropsStore = defineStore('props', () => {
4040
name: 'colors',
4141
type: 'ColorsObject | null',
4242
},
43+
{
44+
default: () => ([]),
45+
desc: 'Internal use only. This holds the header cell widths when the <code class="inline-code">matchColumnWidths</code> prop is set.',
46+
name: 'column-widths',
47+
type: 'object',
48+
},
4349
{
4450
default: () => ({}),
4551
desc: 'Internal use only. This holds the drilldown tables data and settings.',
@@ -73,13 +79,13 @@ export const usePropsStore = defineStore('props', () => {
7379
{
7480
default: undefined,
7581
desc: 'Currently not supported',
76-
name: 'groupBy',
82+
name: 'group-by',
7783
type: 'VDataTable["$props"]["groupBy"]',
7884
},
7985
{
8086
default: false,
8187
desc: 'Internal use only',
82-
name: 'isDrilldown',
88+
name: 'is-drilldown',
8389
type: 'boolean',
8490
},
8591
{
@@ -91,13 +97,13 @@ export const usePropsStore = defineStore('props', () => {
9197
{
9298
default: 'child',
9399
desc: 'This is the key used to hold the drilldown data and settings in the item object.',
94-
name: 'itemChildrenKey',
100+
name: 'item-children-key',
95101
type: 'string',
96102
},
97103
{
98104
default: 0,
99105
desc: `<code class="inline-code">VDataTableServer</code> specific prop. See <a href="${links.vuetify}/en/api/v-data-table-server/#props-items-length" target="_blank">items-length</a> prop for more information.`,
100-
name: 'itemsLength',
106+
name: 'items-length',
101107
type: 'number',
102108
},
103109
{
@@ -118,10 +124,16 @@ export const usePropsStore = defineStore('props', () => {
118124
name: 'loading',
119125
type: 'VDataTable["$props"]["loading"]',
120126
},
127+
{
128+
default: false,
129+
desc: 'Adjusts the width of the drilldown table header cells to match the parent table\'s header cell widths. In case you specify a width for the headers, that specific column\'s width will be utilized instead. Ideally, the column count of both tables should align for optimal results. However, if they don\'t, the last column will be disregarded and automatically set to the default width used by Vuetify\'s <code class="inline-code">VDataTable</code>/<code class="inline-code">VDataTableServer</code>.',
130+
name: 'match-column-widths',
131+
type: 'boolean',
132+
},
121133
{
122134
default: true,
123135
desc: 'Missing description in Vuetify docs. This prop <u>needs</u> to be set to <code class="inline-code">true</code> for the server table to function properly. Do not set this to <code class="inline-code">false</code>.',
124-
name: 'returnObject',
136+
name: 'return-object',
125137
type: 'boolean | undefined',
126138
},
127139
{
@@ -133,19 +145,19 @@ export const usePropsStore = defineStore('props', () => {
133145
{
134146
default: 750,
135147
desc: 'The delay before the search filters the items',
136-
name: 'searchDebounce',
148+
name: 'search-debounce',
137149
type: 'number | undefined | null',
138150
},
139151
{
140152
default: 1000,
141153
desc: 'The maximum time to wait before the search filters the items',
142-
name: 'searchMaxWait',
154+
name: 'search-max-wait',
143155
type: 'number | undefined | null',
144156
},
145157
{
146158
default: 'tbd',
147159
desc: 'The <code class="inline-code">VTextField</code> props',
148-
name: 'searchProps',
160+
name: 'search-props',
149161
type: 'SearchProps',
150162
},
151163
{
@@ -163,25 +175,25 @@ export const usePropsStore = defineStore('props', () => {
163175
{
164176
default: true,
165177
desc: 'Determines if the table should show the drilldown when loading',
166-
name: 'showDrilldownWhenLoading',
178+
name: 'show-drilldown-when-loading',
167179
type: 'boolean',
168180
},
169181
{
170182
default: false,
171183
desc: 'Determines if the table should show the footer row, which by default shows the same values as the header row. To customize the footer row, use the <a href="#props-all-footers"><code class="inline-code">footers</code></a> prop.',
172-
name: 'showFooterRow',
184+
name: 'show-footer-row',
173185
type: 'boolean',
174186
},
175187
{
176188
default: false,
177189
desc: 'Determines if the table should show the <code class="inline-code">VTextField</code> in the <code class="inline-code">top</code> slot',
178-
name: 'showSearch',
190+
name: 'show-search',
179191
type: 'boolean',
180192
},
181193
{
182194
default: 'VDataTable',
183195
desc: 'Internal use only',
184-
name: 'tableType',
196+
name: 'table-type',
185197
type: 'TableType',
186198
},
187199
];

0 commit comments

Comments
 (0)