Files
DistrictCentral/imports/ui/FlexTable.svelte

182 lines
4.9 KiB
Svelte

<script>
export let rows;
export let columns;
export let rowKey;
export let edited;
// Setup a width for each column.
columns.forEach(column => {
let min = column.minWidth ? Math.max(10, column.minWidth) : 10;
let weight = column.weight ? Math.max(1, column.weight) : 1;
column.width = 'minmax(' + min + 'px, ' + weight + 'fr)';
});
let gridTemplateColumns = columns.map(({width}) => width).join(' ');
let headerBeingResized = null;
let horizontalScrollOffset = 0;
const initResize = ({target}) => {
headerBeingResized = target.parentNode;
window.addEventListener('mousemove', onMouseMove);
window.addEventListener('mouseup', completeResize);
headerBeingResized.classList.add('header--being-resized');
};
const completeResize = () => {
window.removeEventListener('mousemove', onMouseMove);
window.removeEventListener('mouseup', completeResize);
headerBeingResized.classList.remove('header--being-resized');
headerBeingResized = null;
};
const onMouseMove = e => {
try {
// Calculate the desired width.
horizontalScrollOffset = document.documentElement.scrollLeft;
let parentX = Math.round(headerBeingResized.getBoundingClientRect().x);
const width = horizontalScrollOffset + (e.clientX - parentX);
// Update the column object with the new size value.
const column = columns.find(({element}) => element === headerBeingResized);
column.width = Math.max(column.minWidth, width) + "px";
// Ensure all the column widths are converted to fixed sizes.
columns.forEach((column, index) => {
if((index < columns.length - 1) && (column.width.startsWith('minmax'))) {
column.width = parseInt(column.element.clientWidth, 10) + 'px';
}
});
// Render the new column sizes.
gridTemplateColumns = columns.map(({width}) => width).join(' ');
} catch(e) {console.log(e);}
}
let selectedRowElement = null;
const selectRow = (e, row) => {
let element = e.target;
while(element && element.nodeName !== "TR") element = element.parentNode;
if(selectedRowElement) {
selectedRowElement.classList.remove('selected');
}
selectedRowElement = element;
element.classList.add('selected');
}
let editorContainer;
const editRow = (e, row) => {
let element = e.target;
while(element && element.nodeName !== "TR") element = element.parentNode;
let editor = element.querySelector('.editor');
// Save the edited row so the editor has access to it.
$edited = row;
if(editor) {
editor.appendChild(editorContainer);
}
editorContainer.classList.remove('hidden');
}
</script>
<div bind:this={editorContainer} class="hidden"><slot>Slot</slot></div>
<table style="--grid-template-columns: {gridTemplateColumns}">
<thead>
<tr>
{#each columns as column}
<th bind:this={column.element}>{column.title} <span class="resize-handle" on:mousedown={initResize}></span></th>
{/each}
</tr>
</thead>
<tbody>
{#each $rows as row (rowKey(row))}
<!-- data-key="{rowKey(row)}"-->
<tr class:hidden={row === $edited} on:mousedown={(e) => selectRow(e, row)} on:dblclick={(e) => editRow(e, row)}>
{#each columns as column}
<td>{column.value(row)}</td>
{/each}
<td class="editor"></td>
</tr>
{/each}
</tbody>
</table>
<button on:click={() => {$edited = null}} type="button">Stop Editing</button>
<style>
table {
width: auto;
-webkit-box-flex: 1;
flex: 1;
display: grid;
border-collapse: collapse;
grid-template-columns: var(--grid-template-columns);
}
thead, tbody, tr {
display: contents;
}
th, td {
padding: 15px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
user-select: none;
}
th {
position: -webkit-sticky;
position: sticky;
top: 0;
background: #5cb85c;
text-align: left;
font-weight: normal;
font-size: 1.1rem;
color: white;
position: relative;
}
th:last-child {
border: 0;
}
.resize-handle {
position: absolute;
top: 0;
right: 0;
bottom: 0;
background: black;
opacity: 0;
width: 3px;
cursor: col-resize;
}
th:last-child .resize-handle {
display: none;
}
.resize-handle:hover, .header--being-resized .resize-handle {
opacity: 0.5;
}
th:hover .resize-handle {
opacity: 0.3;
}
td {
padding-top: 10px;
padding-bottom: 10px;
color: #808080;
}
tr:nth-child(even) {
background: #f8f6ff;
}
:global(.selected), :global(.selected) > td {
background-color: yellow;
}
.editor {
grid-column: 1 / 4;
display: none;
}
/*:global(td.hidden) {*/
/* display: none;*/
/*}*/
:global(tr.hidden) > td:not(.editor) {
display: none !important;
}
:global(tr.hidden) > td.editor {
display: block !important;
}
:global(div.hidden) {
display: none !important;
}
</style>