Update new row component, fix z-index issues, improve UX
This commit is contained in:
parent
5ab0652c87
commit
da2023974e
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext } from "svelte"
|
import { getContext, onMount } from "svelte"
|
||||||
import SheetCell from "./SheetCell.svelte"
|
import SheetCell from "./SheetCell.svelte"
|
||||||
import { getCellRenderer } from "../lib/renderers"
|
import { getCellRenderer } from "../lib/renderers"
|
||||||
|
|
||||||
|
@ -17,30 +17,36 @@
|
||||||
export let row
|
export let row
|
||||||
export let cellId
|
export let cellId
|
||||||
export let updateRow = rows.actions.updateRow
|
export let updateRow = rows.actions.updateRow
|
||||||
export let showPlaceholder = false
|
|
||||||
export let invert = false
|
export let invert = false
|
||||||
|
|
||||||
let api
|
let api
|
||||||
let error
|
let error
|
||||||
|
|
||||||
// Determine if the cell is editable
|
const cellAPI = {
|
||||||
$: readonly = column.schema.autocolumn || (!$config.allowEditRows && row._id)
|
focus: () => api?.focus(),
|
||||||
|
blur: () => api?.blur(),
|
||||||
// Build cell API
|
onKeyDown: (...params) => api?.onKeyDown(...params),
|
||||||
$: cellAPI = {
|
|
||||||
...api,
|
|
||||||
isReadonly: () => readonly,
|
isReadonly: () => readonly,
|
||||||
isRequired: () => !!column.schema.constraints?.presence,
|
isRequired: () => !!column.schema.constraints?.presence,
|
||||||
|
validate: value => {
|
||||||
|
if (value === undefined) {
|
||||||
|
value = row[column.name]
|
||||||
|
}
|
||||||
|
if (cellAPI.isReadonly() && !(value == null || value === "")) {
|
||||||
|
// Ensure cell isn't readonly
|
||||||
|
error = "Auto columns can't be edited"
|
||||||
|
} else if (cellAPI.isRequired() && (value == null || value === "")) {
|
||||||
|
// Sanity check required fields
|
||||||
|
error = "Required field"
|
||||||
|
} else {
|
||||||
|
error = null
|
||||||
|
}
|
||||||
|
return error
|
||||||
|
},
|
||||||
updateValue: value => {
|
updateValue: value => {
|
||||||
error = null
|
|
||||||
try {
|
try {
|
||||||
if (cellAPI.isReadonly()) {
|
cellAPI.validate(value)
|
||||||
// Ensure cell isn't readonly
|
if (!error) {
|
||||||
error = "Auto columns can't be edited"
|
|
||||||
} else if (cellAPI.isRequired() && (value == null || value === "")) {
|
|
||||||
// Sanity check required fields
|
|
||||||
error = "Required field"
|
|
||||||
} else {
|
|
||||||
updateRow(row._id, column.name, value)
|
updateRow(row._id, column.name, value)
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -49,34 +55,33 @@
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determine if the cell is editable
|
||||||
|
$: readonly = column.schema.autocolumn || (!$config.allowEditRows && row._id)
|
||||||
|
|
||||||
// Update selected cell API if selected
|
// Update selected cell API if selected
|
||||||
$: {
|
$: {
|
||||||
if (selected) {
|
if (selected) {
|
||||||
selectedCellAPI.set(cellAPI)
|
selectedCellAPI.set(cellAPI)
|
||||||
} else {
|
} else if (error) {
|
||||||
error = null
|
// error = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SheetCell
|
{#key error}
|
||||||
{rowSelected}
|
<SheetCell
|
||||||
{rowHovered}
|
{rowSelected}
|
||||||
{rowIdx}
|
{rowHovered}
|
||||||
{selected}
|
{rowIdx}
|
||||||
{selectedUser}
|
{selected}
|
||||||
{reorderSource}
|
{selectedUser}
|
||||||
{reorderTarget}
|
{reorderSource}
|
||||||
{error}
|
{reorderTarget}
|
||||||
on:click={() => selectedCellId.set(cellId)}
|
{error}
|
||||||
on:contextmenu={e => menu.actions.open(cellId, e)}
|
on:click={() => selectedCellId.set(cellId)}
|
||||||
width={column.width}
|
on:contextmenu={e => menu.actions.open(cellId, e)}
|
||||||
>
|
width={column.width}
|
||||||
{#if !selected && showPlaceholder && (row[column.name] == null || row[column.name] === "")}
|
>
|
||||||
<div class="placeholder">
|
|
||||||
{column.name}
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<svelte:component
|
<svelte:component
|
||||||
this={getCellRenderer(column)}
|
this={getCellRenderer(column)}
|
||||||
bind:api
|
bind:api
|
||||||
|
@ -86,9 +91,10 @@
|
||||||
onChange={cellAPI.updateValue}
|
onChange={cellAPI.updateValue}
|
||||||
{readonly}
|
{readonly}
|
||||||
{invert}
|
{invert}
|
||||||
|
placeholder="error"
|
||||||
/>
|
/>
|
||||||
{/if}
|
</SheetCell>
|
||||||
</SheetCell>
|
{/key}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.placeholder {
|
.placeholder {
|
||||||
|
|
|
@ -179,7 +179,6 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
.header-cell :global(.cell) {
|
.header-cell :global(.cell) {
|
||||||
background: var(--background);
|
|
||||||
padding: 0 var(--cell-padding);
|
padding: 0 var(--cell-padding);
|
||||||
gap: calc(2 * var(--cell-spacing));
|
gap: calc(2 * var(--cell-spacing));
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,7 +92,7 @@
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{#if editable}
|
{#if editable}
|
||||||
<div class="arrow">
|
<div class="arrow" on:click={open}>
|
||||||
<Icon name="ChevronDown" />
|
<Icon name="ChevronDown" />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -40,11 +40,7 @@
|
||||||
data-row={rowIdx}
|
data-row={rowIdx}
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
{#if selected && error}
|
{#if selectedUser && !selected}
|
||||||
<div class="label">
|
|
||||||
{error}
|
|
||||||
</div>
|
|
||||||
{:else if selectedUser && !selected}
|
|
||||||
<div class="label">
|
<div class="label">
|
||||||
{selectedUser.label}
|
{selectedUser.label}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -113,7 +113,6 @@
|
||||||
rowSelected={containsSelectedCell}
|
rowSelected={containsSelectedCell}
|
||||||
width={$stickyColumn.width}
|
width={$stickyColumn.width}
|
||||||
{updateRow}
|
{updateRow}
|
||||||
showPlaceholder
|
|
||||||
invert
|
invert
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -131,7 +130,6 @@
|
||||||
rowSelected={containsSelectedCell}
|
rowSelected={containsSelectedCell}
|
||||||
width={column.width}
|
width={column.width}
|
||||||
{updateRow}
|
{updateRow}
|
||||||
showPlaceholder
|
|
||||||
invert
|
invert
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import SheetCell from "../cells/SheetCell.svelte"
|
import SheetCell from "../cells/SheetCell.svelte"
|
||||||
import { getContext, onMount } from "svelte"
|
import { getContext, onMount, tick } from "svelte"
|
||||||
import { Icon, Button } from "@budibase/bbui"
|
import { Icon, Button } from "@budibase/bbui"
|
||||||
import SheetScrollWrapper from "./SheetScrollWrapper.svelte"
|
import SheetScrollWrapper from "./SheetScrollWrapper.svelte"
|
||||||
import DataCell from "../cells/DataCell.svelte"
|
import DataCell from "../cells/DataCell.svelte"
|
||||||
|
@ -16,7 +16,6 @@
|
||||||
dispatch,
|
dispatch,
|
||||||
visibleColumns,
|
visibleColumns,
|
||||||
rows,
|
rows,
|
||||||
wheel,
|
|
||||||
showHScrollbar,
|
showHScrollbar,
|
||||||
tableId,
|
tableId,
|
||||||
subscribe,
|
subscribe,
|
||||||
|
@ -34,12 +33,36 @@
|
||||||
$: scrollLeft = $scroll.left
|
$: scrollLeft = $scroll.left
|
||||||
$: $tableId, (isAdding = false)
|
$: $tableId, (isAdding = false)
|
||||||
|
|
||||||
|
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
|
||||||
|
|
||||||
const addRow = async () => {
|
const addRow = async () => {
|
||||||
|
// Validate new row
|
||||||
|
let allColumns = []
|
||||||
|
if ($stickyColumn) {
|
||||||
|
allColumns.push($stickyColumn)
|
||||||
|
}
|
||||||
|
allColumns = allColumns.concat($visibleColumns)
|
||||||
|
for (let col of allColumns) {
|
||||||
|
$selectedCellId = `new-${col.name}`
|
||||||
|
await tick()
|
||||||
|
const error = $selectedCellAPI.validate()
|
||||||
|
if (error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create row
|
||||||
const savedRow = await rows.actions.addRow(newRow, 0)
|
const savedRow = await rows.actions.addRow(newRow, 0)
|
||||||
if (savedRow && firstColumn) {
|
if (savedRow && firstColumn) {
|
||||||
$selectedCellId = `${savedRow._id}-${firstColumn.name}`
|
$selectedCellId = `${savedRow._id}-${firstColumn.name}`
|
||||||
isAdding = false
|
isAdding = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset scroll
|
||||||
|
scroll.set({
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const cancel = () => {
|
const cancel = () => {
|
||||||
|
@ -47,13 +70,14 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const startAdding = () => {
|
const startAdding = () => {
|
||||||
|
scroll.set({
|
||||||
|
left: 0,
|
||||||
|
top: 0,
|
||||||
|
})
|
||||||
newRow = {}
|
newRow = {}
|
||||||
isAdding = true
|
isAdding = true
|
||||||
if (firstColumn) {
|
if (firstColumn) {
|
||||||
$selectedCellId = `new-${firstColumn.name}`
|
$selectedCellId = `new-${firstColumn.name}`
|
||||||
setTimeout(() => {
|
|
||||||
$selectedCellAPI?.focus()
|
|
||||||
}, 100)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,59 +103,61 @@
|
||||||
on:mouseenter={() => ($hoveredRowId = "new")}
|
on:mouseenter={() => ($hoveredRowId = "new")}
|
||||||
on:mouseleave={() => ($hoveredRowId = null)}
|
on:mouseleave={() => ($hoveredRowId = null)}
|
||||||
>
|
>
|
||||||
<SheetScrollWrapper scrollHorizontally={false} scrollVertically={false}>
|
<div
|
||||||
<div
|
class="sticky-column"
|
||||||
class="sticky-column"
|
style="flex: 0 0 {width}px"
|
||||||
style="flex: 0 0 {width}px"
|
class:scrolled={scrollLeft > 0}
|
||||||
class:scrolled={scrollLeft > 0}
|
>
|
||||||
|
<SheetCell
|
||||||
|
width={gutterWidth}
|
||||||
|
{rowHovered}
|
||||||
|
rowSelected={containsSelectedCell}
|
||||||
>
|
>
|
||||||
<SheetCell
|
<div class="gutter">
|
||||||
width={gutterWidth}
|
<div class="number">1</div>
|
||||||
|
{#if $config.allowExpandRows}
|
||||||
|
<Icon
|
||||||
|
name="Maximize"
|
||||||
|
size="S"
|
||||||
|
hoverable
|
||||||
|
on:click={addViaModal}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</SheetCell>
|
||||||
|
{#if $stickyColumn}
|
||||||
|
{@const cellId = `new-${$stickyColumn.name}`}
|
||||||
|
<DataCell
|
||||||
|
{cellId}
|
||||||
|
column={$stickyColumn}
|
||||||
|
row={newRow}
|
||||||
{rowHovered}
|
{rowHovered}
|
||||||
|
selected={$selectedCellId === cellId}
|
||||||
rowSelected={containsSelectedCell}
|
rowSelected={containsSelectedCell}
|
||||||
>
|
width={$stickyColumn.width}
|
||||||
<div class="gutter">
|
{updateRow}
|
||||||
<div class="number">1</div>
|
rowIdx={0}
|
||||||
{#if $config.allowExpandRows}
|
/>
|
||||||
<Icon
|
{/if}
|
||||||
name="Maximize"
|
</div>
|
||||||
size="S"
|
|
||||||
hoverable
|
|
||||||
on:click={addViaModal}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</SheetCell>
|
|
||||||
{#if $stickyColumn}
|
|
||||||
{@const cellId = `new-${$stickyColumn.name}`}
|
|
||||||
<DataCell
|
|
||||||
{cellId}
|
|
||||||
column={$stickyColumn}
|
|
||||||
row={newRow}
|
|
||||||
{rowHovered}
|
|
||||||
selected={$selectedCellId === cellId}
|
|
||||||
rowSelected={containsSelectedCell}
|
|
||||||
width={$stickyColumn.width}
|
|
||||||
{updateRow}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</SheetScrollWrapper>
|
|
||||||
|
|
||||||
<SheetScrollWrapper scrollVertically={false}>
|
<SheetScrollWrapper scrollVertically={false} foo>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{#each $renderedColumns as column}
|
{#each $visibleColumns as column}
|
||||||
{@const cellId = `new-${column.name}`}
|
{@const cellId = `new-${column.name}`}
|
||||||
<DataCell
|
{#key cellId}
|
||||||
{cellId}
|
<DataCell
|
||||||
{column}
|
{cellId}
|
||||||
row={newRow}
|
{column}
|
||||||
{rowHovered}
|
row={newRow}
|
||||||
selected={$selectedCellId === cellId}
|
{rowHovered}
|
||||||
rowSelected={containsSelectedCell}
|
selected={$selectedCellId === cellId}
|
||||||
width={column.width}
|
rowSelected={containsSelectedCell}
|
||||||
{updateRow}
|
width={column.width}
|
||||||
/>
|
{updateRow}
|
||||||
|
rowIdx={0}
|
||||||
|
/>
|
||||||
|
{/key}
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</SheetScrollWrapper>
|
</SheetScrollWrapper>
|
||||||
|
@ -149,8 +175,8 @@
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: var(--row-height);
|
top: var(--row-height);
|
||||||
|
left: 0;
|
||||||
transform: translateY(-100%);
|
transform: translateY(-100%);
|
||||||
z-index: 1;
|
|
||||||
transition: transform 130ms ease-out;
|
transition: transform 130ms ease-out;
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
to bottom,
|
to bottom,
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
context = { ...context, ...createUserStores(context) }
|
context = { ...context, ...createUserStores(context) }
|
||||||
context = { ...context, ...createMenuStores(context) }
|
context = { ...context, ...createMenuStores(context) }
|
||||||
context = { ...context, ...createPaginationStores(context) }
|
context = { ...context, ...createPaginationStores(context) }
|
||||||
|
context = { ...context, ...context }
|
||||||
|
|
||||||
// Reference some stores for local use
|
// Reference some stores for local use
|
||||||
const { isResizing, isReordering, ui, loaded, rowHeight } = context
|
const { isResizing, isReordering, ui, loaded, rowHeight } = context
|
||||||
|
@ -125,13 +126,14 @@
|
||||||
<HeaderRow />
|
<HeaderRow />
|
||||||
<SheetBody />
|
<SheetBody />
|
||||||
</div>
|
</div>
|
||||||
{#if $config.allowAddRows}
|
<div class="overlays">
|
||||||
<!-- <NewRow />-->
|
{#if $config.allowAddRows}
|
||||||
<NewRowTop />
|
<NewRowTop />
|
||||||
{/if}
|
{/if}
|
||||||
<ResizeOverlay />
|
<ResizeOverlay />
|
||||||
<ScrollOverlay />
|
<ScrollOverlay />
|
||||||
<MenuOverlay />
|
<MenuOverlay />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -215,4 +217,9 @@
|
||||||
.controls-right {
|
.controls-right {
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Overlays */
|
||||||
|
.overlays {
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -18,13 +18,20 @@
|
||||||
export let scrollVertically = true
|
export let scrollVertically = true
|
||||||
export let scrollHorizontally = true
|
export let scrollHorizontally = true
|
||||||
export let wheelInteractive = true
|
export let wheelInteractive = true
|
||||||
|
export let foo = false
|
||||||
|
|
||||||
$: hiddenWidths = calculateHiddenWidths($renderedColumns)
|
$: hiddenWidths = calculateHiddenWidths($renderedColumns)
|
||||||
$: style = generateStyle($scroll, $rowHeight, hiddenWidths)
|
$: style = generateStyle($scroll, $rowHeight, hiddenWidths, foo)
|
||||||
|
|
||||||
const generateStyle = (scroll, rowHeight, hiddenWidths) => {
|
const generateStyle = (scroll, rowHeight, hiddenWidths, foo) => {
|
||||||
const offsetX = scrollHorizontally ? -1 * scroll.left + hiddenWidths : 0
|
let offsetX, offsetY
|
||||||
const offsetY = scrollVertically ? -1 * (scroll.top % rowHeight) : 0
|
if (!foo) {
|
||||||
|
offsetX = scrollHorizontally ? -1 * scroll.left + hiddenWidths : 0
|
||||||
|
offsetY = scrollVertically ? -1 * (scroll.top % rowHeight) : 0
|
||||||
|
} else {
|
||||||
|
offsetX = scrollHorizontally ? -1 * scroll.left : 0
|
||||||
|
offsetY = scrollVertically ? -1 * scroll.top : 0
|
||||||
|
}
|
||||||
return `transform: translate3d(${offsetX}px, ${offsetY}px, 0);`
|
return `transform: translate3d(${offsetX}px, ${offsetY}px, 0);`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,8 +70,8 @@
|
||||||
|
|
||||||
// Update state
|
// Update state
|
||||||
scroll.set({
|
scroll.set({
|
||||||
left: newScrollLeft,
|
left: scrollHorizontally ? newScrollLeft : left,
|
||||||
top: newScrollTop,
|
top: scrollVertically ? newScrollTop : top,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Hover row under cursor
|
// Hover row under cursor
|
||||||
|
|
|
@ -160,16 +160,14 @@
|
||||||
.sticky-column {
|
.sticky-column {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
border-right: var(--cell-border);
|
||||||
|
z-index: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add shadow when scrolled */
|
/* Add shadow when scrolled */
|
||||||
.sticky-column.scrolled :global(.cell:last-child:after) {
|
.sticky-column.scrolled {
|
||||||
/*content: " ";*/
|
box-shadow: 0 0 8px -2px rgba(0, 0, 0, 0.2);
|
||||||
/*position: absolute;*/
|
|
||||||
/*width: 10px;*/
|
|
||||||
/*height: 100%;*/
|
|
||||||
/*left: 100%;*/
|
|
||||||
/*background: linear-gradient(to right, rgba(0, 0, 0, 0.08), transparent);*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Don't show borders between cells in the sticky column */
|
/* Don't show borders between cells in the sticky column */
|
||||||
|
@ -178,8 +176,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
position: relative;
|
z-index: 1;
|
||||||
z-index: 3;
|
|
||||||
}
|
}
|
||||||
.header :global(.cell) {
|
.header :global(.cell) {
|
||||||
background: var(--spectrum-global-color-gray-100);
|
background: var(--spectrum-global-color-gray-100);
|
||||||
|
@ -192,7 +189,6 @@
|
||||||
}
|
}
|
||||||
.content {
|
.content {
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,6 @@
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.menu {
|
.menu {
|
||||||
z-index: 1;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background: var(--cell-background);
|
background: var(--cell-background);
|
||||||
border: var(--cell-border);
|
border: var(--cell-border);
|
||||||
|
@ -65,5 +64,6 @@
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
box-shadow: 0 0 32px 0 rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -51,7 +51,6 @@
|
||||||
.resize-slider {
|
.resize-slider {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 2;
|
|
||||||
height: var(--row-height);
|
height: var(--row-height);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
|
|
|
@ -109,7 +109,6 @@
|
||||||
background: var(--spectrum-global-color-gray-500);
|
background: var(--spectrum-global-color-gray-500);
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
z-index: 1;
|
|
||||||
transition: opacity 130ms ease-out;
|
transition: opacity 130ms ease-out;
|
||||||
}
|
}
|
||||||
div:hover {
|
div:hover {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { derived, get } from "svelte/store"
|
import { derived, get } from "svelte/store"
|
||||||
|
import { tick } from "svelte"
|
||||||
|
|
||||||
export const createMaxScrollStores = context => {
|
export const createMaxScrollStores = context => {
|
||||||
const {
|
const {
|
||||||
|
@ -88,65 +89,68 @@ export const createMaxScrollStores = context => {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Ensure the selected cell is visible
|
// Ensure the selected cell is visible
|
||||||
selectedCellRow.subscribe(row => {
|
selectedCellId.subscribe(async $selectedCellId => {
|
||||||
if (!row) {
|
await tick()
|
||||||
return
|
const $selectedCellRow = get(selectedCellRow)
|
||||||
}
|
|
||||||
const $scroll = get(scroll)
|
const $scroll = get(scroll)
|
||||||
const $bounds = get(bounds)
|
const $bounds = get(bounds)
|
||||||
const $rowHeight = get(rowHeight)
|
const $rowHeight = get(rowHeight)
|
||||||
const scrollBarOffset = 16
|
const verticalOffset = $rowHeight * 1.5
|
||||||
|
|
||||||
// Ensure row is not below bottom of screen
|
// Ensure vertical position is viewable
|
||||||
const rowYPos = row.__idx * $rowHeight
|
if ($selectedCellRow) {
|
||||||
const bottomCutoff =
|
// Ensure row is not below bottom of screen
|
||||||
$scroll.top + $bounds.height - $rowHeight - scrollBarOffset
|
const rowYPos = $selectedCellRow.__idx * $rowHeight
|
||||||
let delta = rowYPos - bottomCutoff
|
const bottomCutoff =
|
||||||
if (delta > 0) {
|
$scroll.top + $bounds.height - $rowHeight - verticalOffset
|
||||||
scroll.update(state => ({
|
let delta = rowYPos - bottomCutoff
|
||||||
...state,
|
|
||||||
top: state.top + delta,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure row is not above top of screen
|
|
||||||
else {
|
|
||||||
delta = $scroll.top - rowYPos
|
|
||||||
if (delta > 0) {
|
if (delta > 0) {
|
||||||
scroll.update(state => ({
|
scroll.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
top: Math.max(0, state.top - delta),
|
top: state.top + delta,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure row is not above top of screen
|
||||||
|
else {
|
||||||
|
const delta = $scroll.top - rowYPos + verticalOffset
|
||||||
|
if (delta > 0) {
|
||||||
|
scroll.update(state => ({
|
||||||
|
...state,
|
||||||
|
top: Math.max(0, state.top - delta),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure horizontal position is viewable
|
||||||
// Check horizontal position of columns next
|
// Check horizontal position of columns next
|
||||||
const $selectedCellId = get(selectedCellId)
|
|
||||||
const $visibleColumns = get(visibleColumns)
|
const $visibleColumns = get(visibleColumns)
|
||||||
const columnName = $selectedCellId?.split("-")[1]
|
const columnName = $selectedCellId?.split("-")[1]
|
||||||
const column = $visibleColumns.find(col => col.name === columnName)
|
const column = $visibleColumns.find(col => col.name === columnName)
|
||||||
|
const horizontalOffset = 24
|
||||||
if (!column) {
|
if (!column) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure column is not cutoff on left edge
|
// Ensure column is not cutoff on left edge
|
||||||
delta = $scroll.left - column.left
|
let delta = $scroll.left - column.left + horizontalOffset
|
||||||
if (delta > 0) {
|
if (delta > 0) {
|
||||||
scroll.update(state => ({
|
scroll.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
left: state.left - delta,
|
left: Math.max(0, state.left - delta),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure column is not cutoff on right edge
|
// Ensure column is not cutoff on right edge
|
||||||
else {
|
else {
|
||||||
const rightEdge = column.left + column.width
|
const rightEdge = column.left + column.width
|
||||||
const rightBound = $bounds.width + $scroll.left
|
const rightBound = $bounds.width + $scroll.left - horizontalOffset
|
||||||
delta = rightEdge - rightBound
|
delta = rightEdge - rightBound
|
||||||
if (delta > 0) {
|
if (delta > 0) {
|
||||||
scroll.update(state => ({
|
scroll.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
left: state.left + delta + scrollBarOffset,
|
left: state.left + delta,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ export const createUIStores = context => {
|
||||||
const selectedCellAPI = writable(null)
|
const selectedCellAPI = writable(null)
|
||||||
const rowHeight = writable(36)
|
const rowHeight = writable(36)
|
||||||
|
|
||||||
// Derive the row that contains the selected cell.
|
// Derive the row that contains the selected cell
|
||||||
const selectedCellRow = derived(
|
const selectedCellRow = derived(
|
||||||
[selectedCellId, rowLookupMap, rows],
|
[selectedCellId, rowLookupMap, rows],
|
||||||
([$selectedCellId, $rowLookupMap, $rows]) => {
|
([$selectedCellId, $rowLookupMap, $rows]) => {
|
||||||
|
|
Loading…
Reference in New Issue