Skip to content

Commit 1b34f42

Browse files
committed
#112 Fix resize issues
1 parent dc604ab commit 1b34f42

File tree

1 file changed

+144
-10
lines changed

1 file changed

+144
-10
lines changed

src/lib/components/Table/TableContent.svelte

Lines changed: 144 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script lang="ts">
2-
import { createEventDispatcher } from 'svelte';
2+
import { createEventDispatcher, onMount } from 'svelte';
33
import { readable, writable } from 'svelte/store';
44
55
import Fa from 'svelte-fa';
@@ -33,7 +33,8 @@
3333
fixedWidth,
3434
normalizeFilters,
3535
resetResize,
36-
convertServerColumns
36+
convertServerColumns,
37+
minWidth
3738
} from './shared';
3839
import { Receive, Send } from '$models/Models';
3940
import type { TableConfig } from '$models/Models';
@@ -61,6 +62,8 @@
6162
6263
let searchValue = '';
6364
let isFetching = false;
65+
let tableRef: HTMLTableElement;
66+
6467
const serverSide = server !== undefined;
6568
const { baseUrl, entityId, versionId, sendModel = new Send() } = server ?? {};
6669
@@ -75,6 +78,16 @@
7578
const dispatch = createEventDispatcher();
7679
const actionDispatcher = (obj) => dispatch('action', obj);
7780
81+
// Stores to hold the width and height information for resizing
82+
const rowHeights = writable<
83+
{
84+
max: number;
85+
min: number;
86+
}[]
87+
>([]);
88+
const colWidths = writable<number[]>([]);
89+
90+
// Server-side variables
7891
const serverItems = serverSide ? writable<Number>(0) : undefined;
7992
const serverItemCount = serverSide
8093
? readable<Number>(0, (set) => {
@@ -375,10 +388,97 @@
375388
updateTable();
376389
};
377390
391+
const getMaxCellHeightInRow = () => {
392+
if (!tableRef || resizable === 'columns' || resizable === 'none') return;
393+
394+
// Initialize the rowHeights array if it is empty
395+
if ($rowHeights.length === 0) {
396+
$rowHeights = Array.from({ length: $pageRows.length }, () => ({ max: 44, min: 20 }));
397+
}
398+
399+
tableRef.querySelectorAll('tbody tr').forEach((row, index) => {
400+
const cells = row.querySelectorAll('td');
401+
402+
let maxHeight = optionsComponent ? 56 : 44;
403+
let minHeight = optionsComponent ? 56 : 44;
404+
405+
cells.forEach((cell) => {
406+
const cellHeight = cell.getBoundingClientRect().height;
407+
// + 2 pixels for rendering borders correctly
408+
if (cellHeight > maxHeight) {
409+
maxHeight = cellHeight + 2;
410+
}
411+
if (cellHeight < minHeight) {
412+
minHeight = cellHeight + 2;
413+
}
414+
});
415+
416+
rowHeights.update((rh) => {
417+
rh[index].max = maxHeight - 24;
418+
rh[index].min = Math.max(minHeight - 24, rowHeight ?? 20);
419+
return rh;
420+
});
421+
});
422+
};
423+
424+
const getMinCellWidthInColumn = () => {
425+
if (!tableRef || resizable === 'rows' || resizable === 'none') return;
426+
427+
// Initialize the colWidths array if it is empty
428+
if ($colWidths.length === 0) {
429+
$colWidths = Array.from({ length: $headerRows[0].cells.length }, () => 100);
430+
}
431+
432+
colWidths.update((cw) => {
433+
tableRef.querySelectorAll('thead tr th span').forEach((cell, index) => {
434+
// + 12 pixels for padding and + 32 pixels for filter icon
435+
// If the column width is 100, which means it has not been initialized, then calculate the width
436+
cw[index] = cw[index] === 100 ? cell.getBoundingClientRect().width + 12 + 32 : cw[index];
437+
});
438+
return cw;
439+
});
440+
};
441+
442+
const resizeObserver = new ResizeObserver(() => {
443+
getMaxCellHeightInRow();
444+
getMinCellWidthInColumn();
445+
});
446+
447+
const observeFirstCells = () => {
448+
if (!tableRef) return;
449+
450+
tableRef.querySelectorAll('tbody tr td:first-child').forEach((cell) => {
451+
resizeObserver.observe(cell);
452+
});
453+
};
454+
455+
const observeHeaderColumns = () => {
456+
if (!tableRef) return;
457+
458+
tableRef.querySelectorAll('thead tr th').forEach((cell) => {
459+
resizeObserver.observe(cell);
460+
});
461+
};
462+
463+
const getDimensions = () => {
464+
if (resizable === 'none') return;
465+
else if (resizable === 'columns') {
466+
observeHeaderColumns();
467+
} else if (resizable === 'rows') {
468+
observeFirstCells();
469+
} else {
470+
observeHeaderColumns();
471+
observeFirstCells();
472+
}
473+
};
474+
378475
$: sortKeys = pluginStates.sort.sortKeys;
379476
$: serverSide && updateTable();
380477
$: serverSide && sortServer($sortKeys[0]?.order, $sortKeys[0]?.id);
381478
$: $hiddenColumnIds = shownColumns.filter((col) => !col.visible).map((col) => col.id);
479+
$: tableRef && getDimensions();
480+
$: $headerRows.length > 0 && getMinCellWidthInColumn();
481+
$: $pageRows.length > 0 && getMaxCellHeightInRow();
382482
</script>
383483

384484
<div class="grid gap-2 overflow-auto" class:w-fit={!fitToScreen} class:w-full={fitToScreen}>
@@ -492,6 +592,7 @@
492592

493593
<div class="overflow-auto" style="height: {height}px">
494594
<table
595+
bind:this={tableRef}
495596
{...$tableAttrs}
496597
class="table table-auto table-compact bg-tertiary-500/30 dark:bg-tertiary-900/10 overflow-clip"
497598
id="{tableId}-table"
@@ -507,14 +608,25 @@
507608
let:rowProps
508609
>
509610
<tr {...rowAttrs} class="bg-primary-300 dark:bg-primary-800">
510-
{#each headerRow.cells as cell (cell.id)}
611+
{#each headerRow.cells as cell, index (cell.id)}
511612
<Subscribe attrs={cell.attrs()} props={cell.props()} let:props let:attrs>
512-
<th scope="col" class="!p-2" {...attrs} style={cellStyle(cell.id, columns)}>
613+
<th
614+
scope="col"
615+
class="!p-2"
616+
{...attrs}
617+
style={`
618+
width: ${cell.isData() ? 'auto' : '0'};
619+
${cellStyle(cell.id, columns)}
620+
`}
621+
>
513622
<div
514623
class="overflow-auto"
515624
class:resize-x={(resizable === 'columns' || resizable === 'both') &&
516625
!fixedWidth(cell.id, columns)}
517626
id="th-{tableId}-{cell.id}"
627+
style={`
628+
min-width: ${minWidth(cell.id, columns) ? minWidth(cell.id, columns) : $colWidths[index]}px;
629+
`}
518630
>
519631
<div class="flex justify-between items-center">
520632
<div class="flex gap-1 whitespace-pre-wrap">
@@ -563,20 +675,42 @@
563675
<tr {...rowAttrs} id="{tableId}-row-{row.id}" class="">
564676
{#each row.cells as cell, index (cell?.id)}
565677
<Subscribe attrs={cell.attrs()} let:attrs>
566-
<td {...attrs} class="!p-2">
678+
<td {...attrs} class="">
567679
<div
568-
class=" overflow-auto h-max {index === 0 &&
680+
class=" h-full {index === 0 &&
569681
(resizable === 'rows' || resizable === 'both')
570-
? 'resize-y'
682+
? 'resize-y overflow-auto'
571683
: ''}"
572684
id="{tableId}-{cell.id}-{row.id}"
685+
style={`
686+
min-height: ${$rowHeights && $rowHeights[row.id] ? `${$rowHeights[row.id].min}px` : 'auto'};
687+
max-height: ${
688+
index !== 0 && $rowHeights && $rowHeights[row.id]
689+
? `${$rowHeights[row.id].max}px`
690+
: 'auto'
691+
};
692+
height: ${$rowHeights && $rowHeights[+row.id] ? `${$rowHeights[+row.id].min}px` : 'auto'};
693+
`}
573694
>
574695
<!-- Adding config for initial rowHeight, if provided -->
575696
<div
576-
class="flex items-center overflow-auto"
577-
style="height: {rowHeight ? `${rowHeight}px` : 'auto'};"
697+
class="flex items-start overflow-auto"
698+
style={`
699+
max-height: ${$rowHeights && $rowHeights[row.id] ? `${$rowHeights[row.id].max}px` : 'auto'};
700+
`}
578701
>
579-
<div class="grow h-full"><Render of={cell.render()} /></div>
702+
<div
703+
class="grow overflow-auto"
704+
style={cell.isData()
705+
? `width: ${
706+
minWidth(cell.id, columns)
707+
? minWidth(cell.id, columns)
708+
: $colWidths[index]
709+
}px;`
710+
: 'max-width: min-content;'}
711+
>
712+
<Render of={cell.render()} />
713+
</div>
580714
</div>
581715
</div>
582716
</td>

0 commit comments

Comments
 (0)