Fix table loading states and remove virtual table rendering
This commit is contained in:
parent
268d06a120
commit
5e98b01d4e
|
@ -4,6 +4,7 @@
|
||||||
import CellRenderer from "./CellRenderer.svelte"
|
import CellRenderer from "./CellRenderer.svelte"
|
||||||
import SelectEditRenderer from "./SelectEditRenderer.svelte"
|
import SelectEditRenderer from "./SelectEditRenderer.svelte"
|
||||||
import { cloneDeep, deepGet } from "../helpers"
|
import { cloneDeep, deepGet } from "../helpers"
|
||||||
|
import ProgressCircle from "../ProgressCircle/ProgressCircle.svelte"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The expected schema is our normal couch schemas for our tables.
|
* The expected schema is our normal couch schemas for our tables.
|
||||||
|
@ -14,6 +15,7 @@
|
||||||
* sortable: Set to false to disable sorting data by a certain column
|
* sortable: Set to false to disable sorting data by a certain column
|
||||||
* editable: Set to false to disable editing a certain column if the
|
* editable: Set to false to disable editing a certain column if the
|
||||||
* allowEditColumns prop is true
|
* allowEditColumns prop is true
|
||||||
|
* width: the width of the column
|
||||||
*/
|
*/
|
||||||
export let data = []
|
export let data = []
|
||||||
export let schema = {}
|
export let schema = {}
|
||||||
|
@ -31,14 +33,11 @@
|
||||||
export let autoSortColumns = true
|
export let autoSortColumns = true
|
||||||
export let compact = false
|
export let compact = false
|
||||||
|
|
||||||
rowCount = 5
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
// Config
|
// Config
|
||||||
$: rowHeight = compact ? 46 : 55
|
$: rowHeight = compact ? 46 : 55
|
||||||
const headerHeight = 36
|
const headerHeight = 36
|
||||||
const rowPreload = 5
|
|
||||||
|
|
||||||
// Sorting state
|
// Sorting state
|
||||||
let sortColumn
|
let sortColumn
|
||||||
|
@ -57,26 +56,6 @@
|
||||||
$: gridStyle = getGridStyle(fields, schema, showEditColumn)
|
$: gridStyle = getGridStyle(fields, schema, showEditColumn)
|
||||||
$: showEditColumn = allowEditRows || allowSelectRows
|
$: showEditColumn = allowEditRows || allowSelectRows
|
||||||
|
|
||||||
// Scrolling state
|
|
||||||
let timeout
|
|
||||||
let nextScrollTop = 0
|
|
||||||
let scrollTop = 0
|
|
||||||
$: firstVisibleRow = calculateFirstVisibleRow(scrollTop)
|
|
||||||
$: lastVisibleRow = calculateLastVisibleRow(
|
|
||||||
firstVisibleRow,
|
|
||||||
visibleRowCount,
|
|
||||||
rows.length
|
|
||||||
)
|
|
||||||
|
|
||||||
// Reset state when data changes
|
|
||||||
$: rows.length, reset()
|
|
||||||
const reset = () => {
|
|
||||||
nextScrollTop = 0
|
|
||||||
scrollTop = 0
|
|
||||||
clearTimeout(timeout)
|
|
||||||
timeout = null
|
|
||||||
}
|
|
||||||
|
|
||||||
const fixSchema = schema => {
|
const fixSchema = schema => {
|
||||||
let fixedSchema = {}
|
let fixedSchema = {}
|
||||||
Object.entries(schema || {}).forEach(([fieldName, fieldSchema]) => {
|
Object.entries(schema || {}).forEach(([fieldName, fieldSchema]) => {
|
||||||
|
@ -193,28 +172,6 @@
|
||||||
.map(column => column.name)
|
.map(column => column.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onScroll = event => {
|
|
||||||
nextScrollTop = event.target.scrollTop
|
|
||||||
if (timeout) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
timeout = setTimeout(() => {
|
|
||||||
scrollTop = nextScrollTop
|
|
||||||
timeout = null
|
|
||||||
}, 50)
|
|
||||||
}
|
|
||||||
|
|
||||||
const calculateFirstVisibleRow = scrollTop => {
|
|
||||||
return Math.max(Math.floor(scrollTop / (rowHeight + 1)) - rowPreload, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
const calculateLastVisibleRow = (firstRow, visibleRowCount, allRowCount) => {
|
|
||||||
if (visibleRowCount === 0) {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
return Math.min(firstRow + visibleRowCount + 2 * rowPreload, allRowCount)
|
|
||||||
}
|
|
||||||
|
|
||||||
const editColumn = (e, field) => {
|
const editColumn = (e, field) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
dispatch("editcolumn", field)
|
dispatch("editcolumn", field)
|
||||||
|
@ -245,16 +202,16 @@
|
||||||
style={`--row-height: ${rowHeight}px; --header-height: ${headerHeight}px;`}
|
style={`--row-height: ${rowHeight}px; --header-height: ${headerHeight}px;`}
|
||||||
>
|
>
|
||||||
{#if !loaded}
|
{#if !loaded}
|
||||||
<div class="loading" style={contentStyle} />
|
<div class="loading" style={contentStyle}>
|
||||||
|
<ProgressCircle />
|
||||||
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div
|
<div class="spectrum-Table" style={`${contentStyle}${gridStyle}`}>
|
||||||
on:scroll={onScroll}
|
|
||||||
class="spectrum-Table"
|
|
||||||
style={`${contentStyle}${gridStyle}`}
|
|
||||||
>
|
|
||||||
{#if fields.length}
|
{#if fields.length}
|
||||||
{#if showEditColumn}
|
{#if showEditColumn}
|
||||||
<div class="spectrum-Table-headCell spectrum-Table-headCell--divider">
|
<div
|
||||||
|
class="spectrum-Table-headCell spectrum-Table-headCell--divider spectrum-Table-headCell--edit"
|
||||||
|
>
|
||||||
{editColumnTitle || ""}
|
{editColumnTitle || ""}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -307,38 +264,36 @@
|
||||||
on:click={() => dispatch("click", row)}
|
on:click={() => dispatch("click", row)}
|
||||||
on:click={() => toggleSelectRow(row)}
|
on:click={() => toggleSelectRow(row)}
|
||||||
>
|
>
|
||||||
{#if idx >= firstVisibleRow && idx <= lastVisibleRow}
|
{#if showEditColumn}
|
||||||
{#if showEditColumn}
|
<div
|
||||||
<div class="spectrum-Table-cell spectrum-Table-cell--divider">
|
class="spectrum-Table-cell spectrum-Table-cell--divider spectrum-Table-cell--edit"
|
||||||
<SelectEditRenderer
|
>
|
||||||
data={row}
|
<SelectEditRenderer
|
||||||
selected={selectedRows.includes(row)}
|
data={row}
|
||||||
onToggleSelection={() => toggleSelectRow(row)}
|
selected={selectedRows.includes(row)}
|
||||||
onEdit={e => editRow(e, row)}
|
onToggleSelection={() => toggleSelectRow(row)}
|
||||||
{allowSelectRows}
|
onEdit={e => editRow(e, row)}
|
||||||
{allowEditRows}
|
{allowSelectRows}
|
||||||
/>
|
{allowEditRows}
|
||||||
</div>
|
/>
|
||||||
{/if}
|
</div>
|
||||||
{#each fields as field}
|
|
||||||
<div
|
|
||||||
class="spectrum-Table-cell"
|
|
||||||
class:spectrum-Table-cell--divider={!!schema[field].divider}
|
|
||||||
>
|
|
||||||
<CellRenderer
|
|
||||||
{customRenderers}
|
|
||||||
{row}
|
|
||||||
schema={schema[field]}
|
|
||||||
value={deepGet(row, field)}
|
|
||||||
on:clickrelationship
|
|
||||||
>
|
|
||||||
<slot />
|
|
||||||
</CellRenderer>
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
{:else}
|
|
||||||
<div class="spectrum-Table-cell spectrum-Table-cell--empty" />
|
|
||||||
{/if}
|
{/if}
|
||||||
|
{#each fields as field}
|
||||||
|
<div
|
||||||
|
class="spectrum-Table-cell"
|
||||||
|
class:spectrum-Table-cell--divider={!!schema[field].divider}
|
||||||
|
>
|
||||||
|
<CellRenderer
|
||||||
|
{customRenderers}
|
||||||
|
{row}
|
||||||
|
schema={schema[field]}
|
||||||
|
value={deepGet(row, field)}
|
||||||
|
on:clickrelationship
|
||||||
|
>
|
||||||
|
<slot />
|
||||||
|
</CellRenderer>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
{:else}
|
{:else}
|
||||||
|
@ -361,13 +316,14 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
background-color: var(--table-bg);
|
|
||||||
--table-bg: var(--spectrum-global-color-gray-50);
|
--table-bg: var(--spectrum-global-color-gray-50);
|
||||||
--table-border: 1px solid var(--spectrum-alias-border-color-mid);
|
--table-border: 1px solid var(--spectrum-alias-border-color-mid);
|
||||||
--cell-padding: 20px;
|
--cell-padding: 20px;
|
||||||
|
--hover-bg: var(--spectrum-global-color-gray-100);
|
||||||
}
|
}
|
||||||
.wrapper--quiet {
|
.wrapper--quiet {
|
||||||
--table-bg: var(--spectrum-alias-background-color-transparent);
|
--table-bg: var(--spectrum-alias-background-color-transparent);
|
||||||
|
--hover-bg: var(--spectrum-global-color-gray-200);
|
||||||
}
|
}
|
||||||
.wrapper--compact {
|
.wrapper--compact {
|
||||||
/*--spectrum-table-header-padding-x: 5px;*/
|
/*--spectrum-table-header-padding-x: 5px;*/
|
||||||
|
@ -375,6 +331,13 @@
|
||||||
--cell-padding: 12px;
|
--cell-padding: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Loading */
|
||||||
|
.loading {
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
min-height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Table */
|
/* Table */
|
||||||
.spectrum-Table {
|
.spectrum-Table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -383,15 +346,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Header */
|
/* Header */
|
||||||
.spectrum-Table-head {
|
|
||||||
display: flex;
|
|
||||||
position: sticky;
|
|
||||||
top: 0;
|
|
||||||
width: fit-content;
|
|
||||||
border-bottom: var(--table-border);
|
|
||||||
border-right: 2px solid transparent;
|
|
||||||
min-width: calc(100% - 2px);
|
|
||||||
}
|
|
||||||
.spectrum-Table-headCell {
|
.spectrum-Table-headCell {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
height: var(--header-height);
|
height: var(--header-height);
|
||||||
|
@ -408,6 +362,11 @@
|
||||||
.spectrum-Table-headCell--divider {
|
.spectrum-Table-headCell--divider {
|
||||||
padding-right: var(--cell-padding);
|
padding-right: var(--cell-padding);
|
||||||
}
|
}
|
||||||
|
.spectrum-Table-headCell--edit {
|
||||||
|
position: sticky;
|
||||||
|
left: 0;
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
.spectrum-Table-headCell-content {
|
.spectrum-Table-headCell-content {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
@ -447,7 +406,7 @@
|
||||||
display: contents;
|
display: contents;
|
||||||
}
|
}
|
||||||
.spectrum-Table-row:hover .spectrum-Table-cell {
|
.spectrum-Table-row:hover .spectrum-Table-cell {
|
||||||
background-color: var(--spectrum-alias-highlight-hover);
|
background-color: var(--hover-bg);
|
||||||
}
|
}
|
||||||
.wrapper--quiet .spectrum-Table-row {
|
.wrapper--quiet .spectrum-Table-row {
|
||||||
border-left: none;
|
border-left: none;
|
||||||
|
@ -482,12 +441,16 @@
|
||||||
border-bottom: 1px solid var(--spectrum-alias-border-color-mid);
|
border-bottom: 1px solid var(--spectrum-alias-border-color-mid);
|
||||||
padding-left: var(--cell-padding);
|
padding-left: var(--cell-padding);
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
|
background-color: var(--table-bg);
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
.spectrum-Table-cell--divider {
|
.spectrum-Table-cell--divider {
|
||||||
padding-right: var(--cell-padding);
|
padding-right: var(--cell-padding);
|
||||||
}
|
}
|
||||||
.spectrum-Table-cell--empty {
|
.spectrum-Table-cell--edit {
|
||||||
grid-column: 1 / -1;
|
position: sticky;
|
||||||
|
left: 0;
|
||||||
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Placeholder */
|
/* Placeholder */
|
||||||
|
@ -499,6 +462,7 @@
|
||||||
border: var(--table-border);
|
border: var(--table-border);
|
||||||
border-top: none;
|
border-top: none;
|
||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
|
background-color: var(--table-bg);
|
||||||
}
|
}
|
||||||
.placeholder--no-fields {
|
.placeholder--no-fields {
|
||||||
border-top: var(--table-border);
|
border-top: var(--table-border);
|
||||||
|
|
|
@ -98,7 +98,7 @@
|
||||||
tableId={id}
|
tableId={id}
|
||||||
data={$fetch.rows}
|
data={$fetch.rows}
|
||||||
bind:hideAutocolumns
|
bind:hideAutocolumns
|
||||||
loading={$fetch.loading}
|
loading={!$fetch.loaded}
|
||||||
on:sort={onSort}
|
on:sort={onSort}
|
||||||
allowEditing
|
allowEditing
|
||||||
disableSorting
|
disableSorting
|
||||||
|
|
|
@ -136,7 +136,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#key tableId}
|
{#key tableId}
|
||||||
<div class="table-wrapper" in:fade={{ delay: 200, duration: 100 }}>
|
<div class="table-wrapper">
|
||||||
<Table
|
<Table
|
||||||
{data}
|
{data}
|
||||||
{schema}
|
{schema}
|
||||||
|
|
Loading…
Reference in New Issue