Add sheet button to control column visibilty, improve sorting, improve disabled states
This commit is contained in:
parent
c573955998
commit
57c82c4a5d
17 changed files with 206 additions and 115 deletions
|
@ -1467,11 +1467,6 @@
|
|||
dependencies:
|
||||
"@sinonjs/commons" "^1.7.0"
|
||||
|
||||
"@socket.io/component-emitter@~3.1.0":
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553"
|
||||
integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==
|
||||
|
||||
"@spectrum-css/accordion@^3.0.24":
|
||||
version "3.0.30"
|
||||
resolved "https://registry.yarnpkg.com/@spectrum-css/accordion/-/accordion-3.0.30.tgz#0893a6db28bab984bf5adaf7e1ba194e741db615"
|
||||
|
@ -2586,7 +2581,7 @@ dayjs@^1.10.4, dayjs@^1.11.2:
|
|||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2"
|
||||
integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==
|
||||
|
||||
debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2:
|
||||
debug@4, debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4:
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
||||
|
@ -2779,22 +2774,6 @@ end-of-stream@^1.1.0:
|
|||
dependencies:
|
||||
once "^1.4.0"
|
||||
|
||||
engine.io-client@~6.4.0:
|
||||
version "6.4.0"
|
||||
resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.4.0.tgz#88cd3082609ca86d7d3c12f0e746d12db4f47c91"
|
||||
integrity sha512-GyKPDyoEha+XZ7iEqam49vz6auPnNJ9ZBfy89f+rMMas8AuiMWOZ9PVzu8xb9ZC6rafUqiGHSCfu22ih66E+1g==
|
||||
dependencies:
|
||||
"@socket.io/component-emitter" "~3.1.0"
|
||||
debug "~4.3.1"
|
||||
engine.io-parser "~5.0.3"
|
||||
ws "~8.11.0"
|
||||
xmlhttprequest-ssl "~2.0.0"
|
||||
|
||||
engine.io-parser@~5.0.3:
|
||||
version "5.0.6"
|
||||
resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.6.tgz#7811244af173e157295dec9b2718dfe42a64ef45"
|
||||
integrity sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==
|
||||
|
||||
enquirer@^2.3.6:
|
||||
version "2.3.6"
|
||||
resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d"
|
||||
|
@ -5917,24 +5896,6 @@ snapdragon@^0.8.1:
|
|||
source-map-resolve "^0.5.0"
|
||||
use "^3.1.0"
|
||||
|
||||
socket.io-client@^4.6.1:
|
||||
version "4.6.1"
|
||||
resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.6.1.tgz#80d97d5eb0feca448a0fb6d69a7b222d3d547eab"
|
||||
integrity sha512-5UswCV6hpaRsNg5kkEHVcbBIXEYoVbMQaHJBXJCyEQ+CiFPV1NIOY0XOFWG4XR4GZcB8Kn6AsRs/9cy9TbqVMQ==
|
||||
dependencies:
|
||||
"@socket.io/component-emitter" "~3.1.0"
|
||||
debug "~4.3.2"
|
||||
engine.io-client "~6.4.0"
|
||||
socket.io-parser "~4.2.1"
|
||||
|
||||
socket.io-parser@~4.2.1:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.2.tgz#1dd384019e25b7a3d374877f492ab34f2ad0d206"
|
||||
integrity sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==
|
||||
dependencies:
|
||||
"@socket.io/component-emitter" "~3.1.0"
|
||||
debug "~4.3.1"
|
||||
|
||||
source-map-js@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
|
||||
|
@ -6752,11 +6713,6 @@ ws@^7.4.6:
|
|||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591"
|
||||
integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==
|
||||
|
||||
ws@~8.11.0:
|
||||
version "8.11.0"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143"
|
||||
integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==
|
||||
|
||||
xml-name-validator@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
|
||||
|
@ -6767,11 +6723,6 @@ xmlchars@^2.2.0:
|
|||
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
|
||||
integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
|
||||
|
||||
xmlhttprequest-ssl@~2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67"
|
||||
integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==
|
||||
|
||||
y18n@^4.0.0:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"
|
||||
|
|
|
@ -2,12 +2,14 @@
|
|||
import { Icon } from "@budibase/bbui"
|
||||
import { getContext } from "svelte"
|
||||
|
||||
const { dispatch } = getContext("sheet")
|
||||
const { dispatch, columns } = getContext("sheet")
|
||||
</script>
|
||||
|
||||
<div class="add-component" on:click={() => dispatch("add-row")}>
|
||||
{#if $columns.length}
|
||||
<div class="add-component" on:click={() => dispatch("add-row")}>
|
||||
<Icon size="XL" name="Add" />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.add-component {
|
||||
|
|
|
@ -4,14 +4,14 @@
|
|||
import HeaderCell from "./cells/HeaderCell.svelte"
|
||||
import { Icon } from "@budibase/bbui"
|
||||
|
||||
const { visibleColumns, dispatch, config } = getContext("sheet")
|
||||
const { renderedColumns, dispatch, config } = getContext("sheet")
|
||||
</script>
|
||||
|
||||
<div class="header">
|
||||
<SheetScrollWrapper scrollVertically={false} wheelInteractive={false}>
|
||||
<div class="row">
|
||||
{#each $visibleColumns as column}
|
||||
<HeaderCell {column} />
|
||||
{#each $renderedColumns as column, idx}
|
||||
<HeaderCell {column} {idx} />
|
||||
{/each}
|
||||
</div>
|
||||
</SheetScrollWrapper>
|
||||
|
@ -28,6 +28,7 @@
|
|||
border-bottom: var(--cell-border);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
height: var(--cell-height);
|
||||
}
|
||||
.row {
|
||||
display: flex;
|
||||
|
@ -42,6 +43,7 @@
|
|||
place-items: center;
|
||||
width: 46px;
|
||||
border-left: var(--cell-border);
|
||||
border-bottom: var(--cell-border);
|
||||
}
|
||||
.new-column:hover {
|
||||
cursor: pointer;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import { getCellRenderer } from "./renderers"
|
||||
|
||||
const {
|
||||
visibleColumns,
|
||||
renderedColumns,
|
||||
hoveredRowId,
|
||||
rows,
|
||||
selectedCellId,
|
||||
|
@ -71,7 +71,7 @@
|
|||
</div>
|
||||
<SheetScrollWrapper scrollVertically={false}>
|
||||
<div class="row">
|
||||
{#each $visibleColumns as column}
|
||||
{#each $renderedColumns as column}
|
||||
{@const cellId = `new-${column.name}`}
|
||||
<SheetCell
|
||||
width={column.width}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
columns,
|
||||
resize,
|
||||
scroll,
|
||||
visibleColumns,
|
||||
renderedColumns,
|
||||
stickyColumn,
|
||||
isReordering,
|
||||
} = getContext("sheet")
|
||||
|
@ -13,7 +13,7 @@
|
|||
$: scrollLeft = $scroll.left
|
||||
$: cutoff = scrollLeft + 40 + ($columns[0]?.width || 0)
|
||||
$: offset = 40 + ($stickyColumn?.width || 0)
|
||||
$: columnIdx = $resize.columnIdx
|
||||
$: column = $resize.column
|
||||
|
||||
const getStyle = (column, offset, scrollLeft) => {
|
||||
const left = offset + column.left + column.width - scrollLeft
|
||||
|
@ -25,17 +25,17 @@
|
|||
{#if $stickyColumn}
|
||||
<div
|
||||
class="resize-slider sticky"
|
||||
class:visible={columnIdx === "sticky"}
|
||||
class:visible={column === $stickyColumn.name}
|
||||
on:mousedown={e => resize.actions.startResizing($stickyColumn, e)}
|
||||
style="left:{40 + $stickyColumn.width}px;"
|
||||
>
|
||||
<div class="resize-indicator" />
|
||||
</div>
|
||||
{/if}
|
||||
{#each $visibleColumns as column}
|
||||
{#each $renderedColumns as column}
|
||||
<div
|
||||
class="resize-slider"
|
||||
class:visible={columnIdx === column.idx}
|
||||
class:visible={column === column.name}
|
||||
on:mousedown={e => resize.actions.startResizing(column, e)}
|
||||
style={getStyle(column, offset, scrollLeft)}
|
||||
>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import SheetScrollWrapper from "./SheetScrollWrapper.svelte"
|
||||
import SheetRow from "./SheetRow.svelte"
|
||||
|
||||
const { bounds, visibleRows } = getContext("sheet")
|
||||
const { bounds, renderedRows } = getContext("sheet")
|
||||
|
||||
let body
|
||||
|
||||
|
@ -21,7 +21,7 @@
|
|||
|
||||
<div bind:this={body} class="sheet-body">
|
||||
<SheetScrollWrapper>
|
||||
{#each $visibleRows as row, idx}
|
||||
{#each $renderedRows as row, idx}
|
||||
<SheetRow {row} {idx} />
|
||||
{/each}
|
||||
</SheetScrollWrapper>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { ActionButton } from "@budibase/bbui"
|
||||
import SortButton from "./controls/SortButton.svelte"
|
||||
import HideColumnsButton from "./controls/HideColumnsButton.svelte"
|
||||
</script>
|
||||
|
||||
<ActionButton icon="VisibilityOff" quiet size="M">Hide fields</ActionButton>
|
||||
<HideColumnsButton />
|
||||
<SortButton />
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
reorder,
|
||||
selectedRows,
|
||||
rows,
|
||||
visibleColumns,
|
||||
renderedColumns,
|
||||
hoveredRowId,
|
||||
selectedCellMap,
|
||||
selectedCellRow,
|
||||
|
@ -29,7 +29,7 @@
|
|||
on:mouseover={() => ($hoveredRowId = row._id)}
|
||||
on:mouseleave={() => ($hoveredRowId = null)}
|
||||
>
|
||||
{#each $visibleColumns as column (column.name)}
|
||||
{#each $renderedColumns as column (column.name)}
|
||||
{@const cellId = `${row._id}-${column.name}`}
|
||||
<SheetCell
|
||||
rowSelected={rowSelected || containsSelectedCell}
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
cellHeight,
|
||||
scroll,
|
||||
bounds,
|
||||
columns,
|
||||
visibleRows,
|
||||
visibleColumns,
|
||||
renderedRows,
|
||||
renderedColumns,
|
||||
hoveredRowId,
|
||||
maxScrollTop,
|
||||
maxScrollLeft,
|
||||
|
@ -19,7 +19,7 @@
|
|||
export let scrollHorizontally = true
|
||||
export let wheelInteractive = true
|
||||
|
||||
$: hiddenWidths = calculateHiddenWidths($visibleColumns)
|
||||
$: hiddenWidths = calculateHiddenWidths($renderedColumns)
|
||||
$: scrollLeft = $scroll.left
|
||||
$: scrollTop = $scroll.top
|
||||
$: style = generateStyle($scroll, hiddenWidths)
|
||||
|
@ -31,12 +31,14 @@
|
|||
}
|
||||
|
||||
// Calculates with total width of all columns currently not rendered
|
||||
const calculateHiddenWidths = visibleColumns => {
|
||||
const idx = visibleColumns[0]?.idx
|
||||
const calculateHiddenWidths = renderedColumns => {
|
||||
const idx = $visibleColumns.findIndex(
|
||||
col => col.name === renderedColumns[0]?.name
|
||||
)
|
||||
let width = 0
|
||||
if (idx > 0) {
|
||||
for (let i = 0; i < idx; i++) {
|
||||
width += $columns[i].width
|
||||
width += $visibleColumns[i].width
|
||||
}
|
||||
}
|
||||
return width
|
||||
|
@ -67,7 +69,7 @@
|
|||
|
||||
// Hover row under cursor
|
||||
const y = clientY - $bounds.top + (newScrollTop % cellHeight)
|
||||
const hoveredRow = $visibleRows[Math.floor(y / cellHeight)]
|
||||
const hoveredRow = $renderedRows[Math.floor(y / cellHeight)]
|
||||
$hoveredRowId = hoveredRow?._id
|
||||
})
|
||||
</script>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
rows,
|
||||
selectedRows,
|
||||
stickyColumn,
|
||||
visibleRows,
|
||||
renderedRows,
|
||||
selectedCellId,
|
||||
hoveredRowId,
|
||||
scroll,
|
||||
|
@ -77,7 +77,7 @@
|
|||
|
||||
<div class="content" on:mouseleave={() => ($hoveredRowId = null)}>
|
||||
<SheetScrollWrapper scrollHorizontally={false}>
|
||||
{#each $visibleRows as row, idx}
|
||||
{#each $renderedRows as row, idx}
|
||||
{@const rowSelected = !!$selectedRows[row._id]}
|
||||
{@const rowHovered = $hoveredRowId === row._id}
|
||||
{@const containsSelectedRow = $selectedCellRow?._id === row._id}
|
||||
|
@ -161,13 +161,11 @@
|
|||
}
|
||||
|
||||
.header {
|
||||
border-bottom: var(--cell-border);
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
.header :global(.cell) {
|
||||
background: var(--spectrum-global-color-gray-100);
|
||||
border-bottom: none;
|
||||
}
|
||||
.row {
|
||||
display: flex;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
import { getIconForField } from "../utils"
|
||||
|
||||
export let column
|
||||
export let idx
|
||||
export let orderable = true
|
||||
|
||||
const {
|
||||
|
@ -13,7 +14,7 @@
|
|||
isResizing,
|
||||
rand,
|
||||
sort,
|
||||
columns,
|
||||
renderedColumns,
|
||||
dispatch,
|
||||
config,
|
||||
} = getContext("sheet")
|
||||
|
@ -23,8 +24,8 @@
|
|||
let timeout
|
||||
|
||||
$: sortedBy = column.name === $sort.column
|
||||
$: canMoveLeft = orderable && column.idx > 0
|
||||
$: canMoveRight = orderable && column.idx < $columns.length - 1
|
||||
$: canMoveLeft = orderable && idx > 0
|
||||
$: canMoveRight = orderable && idx < $renderedColumns.length - 1
|
||||
|
||||
const editColumn = () => {
|
||||
dispatch("edit-column", column.schema)
|
||||
|
@ -147,7 +148,6 @@
|
|||
background: var(--background);
|
||||
padding: 0 var(--cell-padding);
|
||||
gap: calc(2 * var(--cell-spacing));
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.name {
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
<script>
|
||||
import { getContext } from "svelte"
|
||||
import { ActionButton, Popover, Toggle } from "@budibase/bbui"
|
||||
|
||||
const { columns } = getContext("sheet")
|
||||
|
||||
let open = false
|
||||
let anchor
|
||||
|
||||
$: anyHidden = $columns.some(col => !col.visible)
|
||||
|
||||
const toggleVisibility = (column, visible) => {
|
||||
columns.update(state => {
|
||||
const index = state.findIndex(col => col.name === column.name)
|
||||
state[index].visible = visible
|
||||
return state.slice()
|
||||
})
|
||||
}
|
||||
|
||||
const showAll = () => {
|
||||
columns.update(state => {
|
||||
return state.map(col => ({
|
||||
...col,
|
||||
visible: true,
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
const hideAll = () => {
|
||||
columns.update(state => {
|
||||
return state.map(col => ({
|
||||
...col,
|
||||
visible: false,
|
||||
}))
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<div bind:this={anchor}>
|
||||
<ActionButton
|
||||
icon="VisibilityOff"
|
||||
quiet
|
||||
size="M"
|
||||
on:click={() => (open = !open)}
|
||||
selected={open || anyHidden}
|
||||
disabled={!$columns.length}
|
||||
>
|
||||
Hide columns
|
||||
</ActionButton>
|
||||
</div>
|
||||
|
||||
<Popover bind:open {anchor} align="left">
|
||||
<div class="content">
|
||||
<div class="columns">
|
||||
{#each $columns as column}
|
||||
<Toggle
|
||||
size="S"
|
||||
value={column.visible}
|
||||
on:change={e => toggleVisibility(column, e.detail)}
|
||||
/>
|
||||
<span>{column.name}</span>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<ActionButton on:click={showAll}>Show all</ActionButton>
|
||||
<ActionButton on:click={hideAll}>Hide all</ActionButton>
|
||||
</div>
|
||||
</div>
|
||||
</Popover>
|
||||
|
||||
<style>
|
||||
.content {
|
||||
padding: 12px 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
.buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
}
|
||||
.columns {
|
||||
display: grid;
|
||||
align-items: center;
|
||||
grid-template-columns: auto 1fr;
|
||||
}
|
||||
</style>
|
|
@ -2,7 +2,7 @@
|
|||
import { getContext } from "svelte"
|
||||
import { ActionButton, Popover, Select } from "@budibase/bbui"
|
||||
|
||||
const { sort, columns, stickyColumn } = getContext("sheet")
|
||||
const { sort, visibleColumns, stickyColumn } = getContext("sheet")
|
||||
const orderOptions = [
|
||||
{ label: "A-Z", value: "ascending" },
|
||||
{ label: "Z-A", value: "descending" },
|
||||
|
@ -11,7 +11,8 @@
|
|||
let open = false
|
||||
let anchor
|
||||
|
||||
$: columnOptions = getColumnOptions($stickyColumn, $columns)
|
||||
$: columnOptions = getColumnOptions($stickyColumn, $visibleColumns)
|
||||
$: checkValidSortColumn($sort.column, $stickyColumn, $visibleColumns)
|
||||
|
||||
const getColumnOptions = (stickyColumn, columns) => {
|
||||
let options = []
|
||||
|
@ -20,6 +21,29 @@
|
|||
}
|
||||
return [...options, ...columns.map(col => col.name)]
|
||||
}
|
||||
|
||||
// Ensure we never have a sort column selected that is not visible
|
||||
const checkValidSortColumn = (sortColumn, stickyColumn, visibleColumns) => {
|
||||
if (!sortColumn) {
|
||||
return
|
||||
}
|
||||
if (
|
||||
sortColumn !== stickyColumn?.name &&
|
||||
!visibleColumns.some(col => col.name === sortColumn)
|
||||
) {
|
||||
if (stickyColumn) {
|
||||
sort.update(state => ({
|
||||
...state,
|
||||
column: stickyColumn.name,
|
||||
}))
|
||||
} else {
|
||||
sort.update(state => ({
|
||||
...state,
|
||||
column: visibleColumns[0]?.name,
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div bind:this={anchor}>
|
||||
|
@ -28,7 +52,8 @@
|
|||
quiet
|
||||
size="M"
|
||||
on:click={() => (open = !open)}
|
||||
selected={!!$sort.order}
|
||||
selected={!!$sort.column}
|
||||
disabled={!$visibleColumns.length}
|
||||
>
|
||||
Sort
|
||||
</ActionButton>
|
||||
|
|
|
@ -10,17 +10,23 @@ export const createColumnsStores = context => {
|
|||
// automatically calculated
|
||||
const enrichedColumns = derived(columns, $columns => {
|
||||
let offset = 0
|
||||
return $columns.map((column, idx) => {
|
||||
return $columns.map(column => {
|
||||
const enriched = {
|
||||
...column,
|
||||
idx,
|
||||
left: offset,
|
||||
}
|
||||
if (column.visible) {
|
||||
offset += column.width
|
||||
}
|
||||
return enriched
|
||||
})
|
||||
})
|
||||
|
||||
// Derived list of columns which have not been explicitly hidden
|
||||
const visibleColumns = derived(enrichedColumns, $columns => {
|
||||
return $columns.filter(col => col.visible)
|
||||
})
|
||||
|
||||
// Merge new schema fields with existing schema in order to preserve widths
|
||||
schema.subscribe($schema => {
|
||||
const currentColumns = get(columns)
|
||||
|
@ -45,6 +51,7 @@ export const createColumnsStores = context => {
|
|||
name: field,
|
||||
width: existing?.width || defaultWidth,
|
||||
schema: $schema[field],
|
||||
visible: existing?.visible ?? true,
|
||||
}
|
||||
})
|
||||
)
|
||||
|
@ -75,5 +82,6 @@ export const createColumnsStores = context => {
|
|||
subscribe: enrichedColumns.subscribe,
|
||||
},
|
||||
stickyColumn,
|
||||
visibleColumns,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,12 +5,13 @@ export const createResizeStores = context => {
|
|||
const initialState = {
|
||||
initialMouseX: null,
|
||||
initialWidth: null,
|
||||
column: null,
|
||||
columnIdx: null,
|
||||
width: 0,
|
||||
left: 0,
|
||||
}
|
||||
const resize = writable(initialState)
|
||||
const isResizing = derived(resize, $resize => $resize.columnIdx != null)
|
||||
const isResizing = derived(resize, $resize => $resize.column != null)
|
||||
const MinColumnWidth = 100
|
||||
|
||||
// Starts resizing a certain column
|
||||
|
@ -18,12 +19,20 @@ export const createResizeStores = context => {
|
|||
// Prevent propagation to stop reordering triggering
|
||||
e.stopPropagation()
|
||||
|
||||
// Find and cache index
|
||||
let columnIdx = get(columns).findIndex(col => col.name === column.name)
|
||||
if (columnIdx === -1) {
|
||||
columnIdx = "sticky"
|
||||
}
|
||||
|
||||
// Set initial store state
|
||||
resize.set({
|
||||
width: column.width,
|
||||
left: column.left,
|
||||
initialWidth: column.width,
|
||||
initialMouseX: e.clientX,
|
||||
columnIdx: column.idx,
|
||||
column: column.name,
|
||||
columnIdx,
|
||||
})
|
||||
|
||||
// Add mouse event listeners to handle resizing
|
||||
|
|
|
@ -17,7 +17,7 @@ export const createScrollStores = context => {
|
|||
const width = derived(bounds, $bounds => $bounds.width, 0)
|
||||
const contentHeight = derived(
|
||||
rows,
|
||||
$rows => ($rows.length + 1) * cellHeight + padding,
|
||||
$rows => $rows.length * cellHeight + padding,
|
||||
0
|
||||
)
|
||||
const maxScrollTop = derived(
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { derived, get } from "svelte/store"
|
||||
|
||||
export const createViewportStores = context => {
|
||||
const { cellHeight, columns, rows, scroll, bounds } = context
|
||||
const { cellHeight, visibleColumns, rows, scroll, bounds } = context
|
||||
const scrollTop = derived(scroll, $scroll => $scroll.top, 0)
|
||||
const scrollLeft = derived(scroll, $scroll => $scroll.left, 0)
|
||||
|
||||
|
@ -15,53 +15,59 @@ export const createViewportStores = context => {
|
|||
const firstRowIdx = derived(scrollTop, $scrollTop => {
|
||||
return Math.floor($scrollTop / cellHeight)
|
||||
})
|
||||
const visibleRowCount = derived(height, $height => {
|
||||
const renderedRowCount = derived(height, $height => {
|
||||
return Math.ceil($height / cellHeight)
|
||||
})
|
||||
const visibleRows = derived(
|
||||
[rows, firstRowIdx, visibleRowCount],
|
||||
const renderedRows = derived(
|
||||
[rows, firstRowIdx, renderedRowCount],
|
||||
([$rows, $firstRowIdx, $visibleRowCount]) => {
|
||||
return $rows.slice($firstRowIdx, $firstRowIdx + $visibleRowCount)
|
||||
}
|
||||
)
|
||||
|
||||
// Derive visible columns
|
||||
const visibleColumns = derived(
|
||||
[columns, scrollLeft, width],
|
||||
([$columns, $scrollLeft, $width]) => {
|
||||
if (!$columns.length) {
|
||||
const renderedColumns = derived(
|
||||
[visibleColumns, scrollLeft, width],
|
||||
([$visibleColumns, $scrollLeft, $width]) => {
|
||||
if (!$visibleColumns.length) {
|
||||
return []
|
||||
}
|
||||
let startColIdx = 0
|
||||
let rightEdge = $columns[0].width
|
||||
while (rightEdge < $scrollLeft && startColIdx < $columns.length - 1) {
|
||||
let rightEdge = $visibleColumns[0].width
|
||||
while (
|
||||
rightEdge < $scrollLeft &&
|
||||
startColIdx < $visibleColumns.length - 1
|
||||
) {
|
||||
startColIdx++
|
||||
rightEdge += $columns[startColIdx].width
|
||||
rightEdge += $visibleColumns[startColIdx].width
|
||||
}
|
||||
let endColIdx = startColIdx + 1
|
||||
let leftEdge = rightEdge
|
||||
while (leftEdge < $width + $scrollLeft && endColIdx < $columns.length) {
|
||||
leftEdge += $columns[endColIdx].width
|
||||
while (
|
||||
leftEdge < $width + $scrollLeft &&
|
||||
endColIdx < $visibleColumns.length
|
||||
) {
|
||||
leftEdge += $visibleColumns[endColIdx].width
|
||||
endColIdx++
|
||||
}
|
||||
const nextVisibleColumns = $columns.slice(startColIdx, endColIdx)
|
||||
const nextRenderedColumns = $visibleColumns.slice(startColIdx, endColIdx)
|
||||
|
||||
// Cautiously shrink the number of rendered columns.
|
||||
// This is to avoid rapidly shrinking and growing the visible column count
|
||||
// which results in remounting cells
|
||||
const currentCount = get(visibleColumns).length
|
||||
if (currentCount === nextVisibleColumns.length + 1) {
|
||||
return $columns.slice(startColIdx, endColIdx + 1)
|
||||
const currentCount = get(renderedColumns).length
|
||||
if (currentCount === nextRenderedColumns.length + 1) {
|
||||
return $visibleColumns.slice(startColIdx, endColIdx + 1)
|
||||
} else {
|
||||
return nextVisibleColumns
|
||||
return nextRenderedColumns
|
||||
}
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
// Fetch next page when approaching end of data
|
||||
visibleRows.subscribe($visibleRows => {
|
||||
const lastVisible = $visibleRows[$visibleRows.length - 1]
|
||||
renderedRows.subscribe($renderedRows => {
|
||||
const lastVisible = $renderedRows[$renderedRows.length - 1]
|
||||
const $rows = get(rows)
|
||||
const lastRow = $rows[$rows.length - 1]
|
||||
if (lastVisible && lastRow && lastVisible._id === lastRow._id) {
|
||||
|
@ -69,5 +75,5 @@ export const createViewportStores = context => {
|
|||
}
|
||||
})
|
||||
|
||||
return { visibleRows, visibleColumns }
|
||||
return { renderedRows, renderedColumns }
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue