Fix some scroll issues and add shadow to sticky column
This commit is contained in:
parent
ca96a61cde
commit
15dffb0f40
|
@ -5,8 +5,7 @@
|
||||||
import { getIconForField } from "./utils"
|
import { getIconForField } from "./utils"
|
||||||
import SheetScrollWrapper from "./SheetScrollWrapper.svelte"
|
import SheetScrollWrapper from "./SheetScrollWrapper.svelte"
|
||||||
|
|
||||||
const { visibleColumns, reorder, selectedRows, rows } =
|
const { visibleColumns, reorder } = getContext("spreadsheet")
|
||||||
getContext("spreadsheet")
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
<script>
|
<script>
|
||||||
import SheetCell from "./SheetCell.svelte"
|
import SheetCell from "./SheetCell.svelte"
|
||||||
import { Icon } from "@budibase/bbui"
|
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
|
|
||||||
const { visibleColumns, cellHeight, rows, selectedCellId, reorder } =
|
const { visibleColumns, hoveredRowId, rows, selectedCellId, reorder } =
|
||||||
getContext("spreadsheet")
|
getContext("spreadsheet")
|
||||||
|
|
||||||
|
$: rowHovered = $hoveredRowId === "new"
|
||||||
|
|
||||||
const addRow = async field => {
|
const addRow = async field => {
|
||||||
const newRow = await rows.actions.addRow()
|
const newRow = await rows.actions.addRow()
|
||||||
if (newRow) {
|
if (newRow) {
|
||||||
|
@ -14,9 +15,10 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="row new">
|
<div class="row" on:mouseover={() => ($hoveredRowId = "new")}>
|
||||||
{#each $visibleColumns as column}
|
{#each $visibleColumns as column}
|
||||||
<SheetCell
|
<SheetCell
|
||||||
|
{rowHovered}
|
||||||
on:click={() => addRow(column)}
|
on:click={() => addRow(column)}
|
||||||
width={column.width}
|
width={column.width}
|
||||||
left={column.left}
|
left={column.left}
|
||||||
|
@ -30,8 +32,7 @@
|
||||||
.row {
|
.row {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
:global(.sheet:not(.is-resizing):not(.is-reordering) .row:hover .cell) {
|
.row:hover :global(.cell) {
|
||||||
background: var(--cell-background-hover);
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
const tableIdStore = writable()
|
const tableIdStore = writable()
|
||||||
const selectedCellId = writable()
|
const selectedCellId = writable()
|
||||||
const selectedRows = writable({})
|
const selectedRows = writable({})
|
||||||
|
const hoveredRowId = writable()
|
||||||
const scroll = writable({
|
const scroll = writable({
|
||||||
left: 0,
|
left: 0,
|
||||||
top: 0,
|
top: 0,
|
||||||
|
@ -51,6 +52,7 @@
|
||||||
cellHeight,
|
cellHeight,
|
||||||
bounds,
|
bounds,
|
||||||
scroll,
|
scroll,
|
||||||
|
hoveredRowId,
|
||||||
tableId: tableIdStore,
|
tableId: tableIdStore,
|
||||||
}
|
}
|
||||||
const { rows, schema } = createRowsStore(context)
|
const { rows, schema } = createRowsStore(context)
|
||||||
|
@ -114,7 +116,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-items: flex-start;
|
justify-items: flex-start;
|
||||||
align-items: stretch;
|
align-items: flex-start;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 0;
|
height: 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -124,5 +126,6 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
align-self: stretch;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,18 +1,11 @@
|
||||||
<script>
|
<script>
|
||||||
import { getContext, onMount } from "svelte"
|
import { getContext, onMount } from "svelte"
|
||||||
import { Utils } from "../../utils"
|
|
||||||
import SheetScrollWrapper from "./SheetScrollWrapper.svelte"
|
import SheetScrollWrapper from "./SheetScrollWrapper.svelte"
|
||||||
|
|
||||||
const { columns, selectedCellId, cellHeight, rows, bounds, scroll } =
|
const { selectedCellId, hoveredRowId, bounds } = getContext("spreadsheet")
|
||||||
getContext("spreadsheet")
|
|
||||||
|
|
||||||
const padding = 180
|
|
||||||
|
|
||||||
let ref
|
let ref
|
||||||
|
|
||||||
$: scrollLeft = $scroll.left
|
|
||||||
$: scrollTop = $scroll.top
|
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
// Observe and record the height of the body
|
// Observe and record the height of the body
|
||||||
const observer = new ResizeObserver(() => {
|
const observer = new ResizeObserver(() => {
|
||||||
|
@ -28,8 +21,8 @@
|
||||||
<div
|
<div
|
||||||
bind:this={ref}
|
bind:this={ref}
|
||||||
class="sheet-body"
|
class="sheet-body"
|
||||||
class:horizontally-scrolled={scrollLeft > 0}
|
|
||||||
on:click|self={() => ($selectedCellId = null)}
|
on:click|self={() => ($selectedCellId = null)}
|
||||||
|
on:mouseleave={() => ($hoveredRowId = null)}
|
||||||
>
|
>
|
||||||
<SheetScrollWrapper>
|
<SheetScrollWrapper>
|
||||||
<slot />
|
<slot />
|
||||||
|
@ -44,20 +37,4 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
.sheet-body::-webkit-scrollbar-track {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add shadow to sticky cells when horizontally scrolled */
|
|
||||||
.horizontally-scrolled :global(.cell.sticky) {
|
|
||||||
border-right-width: 1px;
|
|
||||||
}
|
|
||||||
.horizontally-scrolled :global(.cell.sticky:after) {
|
|
||||||
content: " ";
|
|
||||||
position: absolute;
|
|
||||||
width: 10px;
|
|
||||||
left: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: linear-gradient(to right, rgba(0, 0, 0, 0.08), transparent);
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
<svelte:options immutable={true} />
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export let header = false
|
export let header = false
|
||||||
export let label = false
|
export let label = false
|
||||||
export let rowSelected = false
|
export let rowSelected = false
|
||||||
|
export let rowHovered = false
|
||||||
export let sticky = false
|
export let sticky = false
|
||||||
export let selected = false
|
export let selected = false
|
||||||
export let reorderSource = false
|
export let reorderSource = false
|
||||||
|
@ -16,12 +15,12 @@
|
||||||
class:header
|
class:header
|
||||||
class:label
|
class:label
|
||||||
class:row-selected={rowSelected}
|
class:row-selected={rowSelected}
|
||||||
|
class:row-hovered={rowHovered}
|
||||||
class:sticky
|
class:sticky
|
||||||
class:selected
|
class:selected
|
||||||
class:reorder-source={reorderSource}
|
class:reorder-source={reorderSource}
|
||||||
class:reorder-target={reorderTarget}
|
class:reorder-target={reorderTarget}
|
||||||
on:focus
|
on:focus
|
||||||
on:mouseenter
|
|
||||||
on:click
|
on:click
|
||||||
on:mousedown
|
on:mousedown
|
||||||
style="--width:{width}px;"
|
style="--width:{width}px;"
|
||||||
|
@ -64,6 +63,9 @@
|
||||||
.cell.row-selected {
|
.cell.row-selected {
|
||||||
background-color: var(--spectrum-global-color-gray-100);
|
background-color: var(--spectrum-global-color-gray-100);
|
||||||
}
|
}
|
||||||
|
.cell.row-hovered {
|
||||||
|
background: var(--cell-background-hover);
|
||||||
|
}
|
||||||
|
|
||||||
/* Header cells */
|
/* Header cells */
|
||||||
.cell.header {
|
.cell.header {
|
||||||
|
|
|
@ -7,17 +7,25 @@
|
||||||
|
|
||||||
export let row
|
export let row
|
||||||
|
|
||||||
const { selectedCellId, reorder, selectedRows, rows, visibleColumns } =
|
const {
|
||||||
getContext("spreadsheet")
|
selectedCellId,
|
||||||
|
reorder,
|
||||||
|
selectedRows,
|
||||||
|
rows,
|
||||||
|
visibleColumns,
|
||||||
|
hoveredRowId,
|
||||||
|
} = getContext("spreadsheet")
|
||||||
|
|
||||||
$: rowSelected = !!$selectedRows[row._id]
|
$: rowSelected = !!$selectedRows[row._id]
|
||||||
|
$: rowHovered = $hoveredRowId === row._id
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row" on:mouseover={() => ($hoveredRowId = row._id)}>
|
||||||
{#each $visibleColumns as column (column.name)}
|
{#each $visibleColumns as column (column.name)}
|
||||||
{@const cellIdx = `${row._id}-${column.name}`}
|
{@const cellIdx = `${row._id}-${column.name}`}
|
||||||
<SheetCell
|
<SheetCell
|
||||||
{rowSelected}
|
{rowSelected}
|
||||||
|
{rowHovered}
|
||||||
selected={$selectedCellId === cellIdx}
|
selected={$selectedCellId === cellIdx}
|
||||||
reorderSource={$reorder.columnIdx === column.idx}
|
reorderSource={$reorder.columnIdx === column.idx}
|
||||||
reorderTarget={$reorder.swapColumnIdx === column.idx}
|
reorderTarget={$reorder.swapColumnIdx === column.idx}
|
||||||
|
@ -41,7 +49,4 @@
|
||||||
.row {
|
.row {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
:global(.sheet:not(.is-resizing):not(.is-reordering) .row:hover .cell) {
|
|
||||||
background: var(--cell-background-hover);
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -69,11 +69,11 @@
|
||||||
const offset = deltaY * step
|
const offset = deltaY * step
|
||||||
let newScrollTop = scrollTop
|
let newScrollTop = scrollTop
|
||||||
newScrollTop += offset
|
newScrollTop += offset
|
||||||
newScrollTop = Math.max(0, newScrollTop)
|
|
||||||
newScrollTop = Math.min(
|
newScrollTop = Math.min(
|
||||||
newScrollTop,
|
newScrollTop,
|
||||||
($rows.length + 1) * cellHeight - $bounds.height
|
($rows.length + 1) * cellHeight - $bounds.height
|
||||||
)
|
)
|
||||||
|
newScrollTop = Math.max(0, newScrollTop)
|
||||||
scroll.update(state => ({
|
scroll.update(state => ({
|
||||||
...state,
|
...state,
|
||||||
top: newScrollTop,
|
top: newScrollTop,
|
||||||
|
@ -91,6 +91,8 @@
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.scroll-wrapper {
|
.scroll-wrapper {
|
||||||
|
min-width: 100%;
|
||||||
|
min-height: 100%;
|
||||||
background: var(--background-alt);
|
background: var(--background-alt);
|
||||||
transform: translate3d(var(--offset-x), var(--offset-y), 0);
|
transform: translate3d(var(--offset-x), var(--offset-y), 0);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
|
@ -6,9 +6,17 @@
|
||||||
import { getCellComponent } from "./utils"
|
import { getCellComponent } from "./utils"
|
||||||
import SheetScrollWrapper from "./SheetScrollWrapper.svelte"
|
import SheetScrollWrapper from "./SheetScrollWrapper.svelte"
|
||||||
|
|
||||||
const { rows, selectedRows, stickyColumn, visibleRows, selectedCellId } =
|
const {
|
||||||
getContext("spreadsheet")
|
rows,
|
||||||
|
selectedRows,
|
||||||
|
stickyColumn,
|
||||||
|
visibleRows,
|
||||||
|
selectedCellId,
|
||||||
|
hoveredRowId,
|
||||||
|
scroll,
|
||||||
|
} = getContext("spreadsheet")
|
||||||
|
|
||||||
|
$: scrollLeft = $scroll.left
|
||||||
$: rowCount = $rows.length
|
$: rowCount = $rows.length
|
||||||
$: selectedRowCount = Object.values($selectedRows).filter(x => !!x).length
|
$: selectedRowCount = Object.values($selectedRows).filter(x => !!x).length
|
||||||
$: width = 40 + $stickyColumn?.width || 0
|
$: width = 40 + $stickyColumn?.width || 0
|
||||||
|
@ -41,7 +49,11 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="sticky-column" style="--width:{width}px;">
|
<div
|
||||||
|
class="sticky-column"
|
||||||
|
style="--width:{width}px;"
|
||||||
|
class:scrolled={scrollLeft > 0}
|
||||||
|
>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<!-- Field headers -->
|
<!-- Field headers -->
|
||||||
<SheetCell header label on:click={selectAll} width="40" left="0">
|
<SheetCell header label on:click={selectAll} width="40" left="0">
|
||||||
|
@ -67,70 +79,96 @@
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SheetScrollWrapper scrollHorizontally={false}>
|
<div on:mouseleave={() => ($hoveredRowId = null)}>
|
||||||
{#each $visibleRows as row}
|
<SheetScrollWrapper scrollHorizontally={false}>
|
||||||
{@const rowSelected = !!$selectedRows[row._id]}
|
{#each $visibleRows as row}
|
||||||
<div class="row">
|
{@const rowSelected = !!$selectedRows[row._id]}
|
||||||
|
{@const rowHovered = $hoveredRowId === row._id}
|
||||||
|
<div class="row" on:mouseenter={() => ($hoveredRowId = row._id)}>
|
||||||
|
<SheetCell
|
||||||
|
label
|
||||||
|
{rowSelected}
|
||||||
|
{rowHovered}
|
||||||
|
on:click={() => selectRow(row._id)}
|
||||||
|
width="40"
|
||||||
|
>
|
||||||
|
<div class="checkbox" class:visible={rowSelected || rowHovered}>
|
||||||
|
<Checkbox value={rowSelected} />
|
||||||
|
</div>
|
||||||
|
<div class="number" class:visible={!(rowSelected || rowHovered)}>
|
||||||
|
{row.__idx + 1}
|
||||||
|
</div>
|
||||||
|
</SheetCell>
|
||||||
|
|
||||||
|
{#if $stickyColumn}
|
||||||
|
{@const cellIdx = `${row._id}-${$stickyColumn.name}`}
|
||||||
|
<SheetCell
|
||||||
|
{rowSelected}
|
||||||
|
{rowHovered}
|
||||||
|
sticky
|
||||||
|
selected={$selectedCellId === cellIdx}
|
||||||
|
on:click={() => ($selectedCellId = cellIdx)}
|
||||||
|
width={$stickyColumn.width}
|
||||||
|
left="40"
|
||||||
|
>
|
||||||
|
<svelte:component
|
||||||
|
this={getCellComponent($stickyColumn)}
|
||||||
|
value={row[$stickyColumn.name]}
|
||||||
|
schema={$stickyColumn.schema}
|
||||||
|
selected={$selectedCellId === cellIdx}
|
||||||
|
onChange={val =>
|
||||||
|
rows.actions.updateRow(row._id, $stickyColumn, val)}
|
||||||
|
readonly={$stickyColumn.schema.autocolumn}
|
||||||
|
/>
|
||||||
|
</SheetCell>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
|
||||||
|
<div class="row new" on:mouseover={() => ($hoveredRowId = "new")}>
|
||||||
<SheetCell
|
<SheetCell
|
||||||
|
rowHovered={$hoveredRowId === "new"}
|
||||||
label
|
label
|
||||||
{rowSelected}
|
on:click={addRow}
|
||||||
on:click={() => selectRow(row._id)}
|
|
||||||
width="40"
|
width="40"
|
||||||
>
|
>
|
||||||
<div class="checkbox" class:visible={rowSelected}>
|
<Icon hoverable name="Add" size="S" />
|
||||||
<Checkbox value={rowSelected} />
|
|
||||||
</div>
|
|
||||||
<div class="number" class:visible={!rowSelected}>
|
|
||||||
{row.__idx + 1}
|
|
||||||
</div>
|
|
||||||
</SheetCell>
|
</SheetCell>
|
||||||
|
|
||||||
{#if $stickyColumn}
|
{#if $stickyColumn}
|
||||||
{@const cellIdx = `${row._id}-${$stickyColumn.name}`}
|
|
||||||
<SheetCell
|
<SheetCell
|
||||||
{rowSelected}
|
on:click={addRow}
|
||||||
sticky
|
|
||||||
selected={$selectedCellId === cellIdx}
|
|
||||||
on:click={() => ($selectedCellId = cellIdx)}
|
|
||||||
width={$stickyColumn.width}
|
width={$stickyColumn.width}
|
||||||
left="40"
|
rowHovered={$hoveredRowId === "new"}
|
||||||
>
|
/>
|
||||||
<svelte:component
|
|
||||||
this={getCellComponent($stickyColumn)}
|
|
||||||
value={row[$stickyColumn.name]}
|
|
||||||
schema={$stickyColumn.schema}
|
|
||||||
selected={$selectedCellId === cellIdx}
|
|
||||||
onChange={val =>
|
|
||||||
rows.actions.updateRow(row._id, $stickyColumn, val)}
|
|
||||||
readonly={$stickyColumn.schema.autocolumn}
|
|
||||||
/>
|
|
||||||
</SheetCell>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
</SheetScrollWrapper>
|
||||||
|
</div>
|
||||||
<div class="row">
|
|
||||||
<SheetCell label on:click={addRow} width="40">
|
|
||||||
<Icon hoverable name="Add" size="S" />
|
|
||||||
</SheetCell>
|
|
||||||
{#if $stickyColumn}
|
|
||||||
<SheetCell on:click={addRow} width={$stickyColumn.width} left="40" />
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</SheetScrollWrapper>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.sticky-column {
|
.sticky-column {
|
||||||
flex: 0 0 var(--width);
|
flex: 0 0 var(--width);
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
|
overflow: visible;
|
||||||
|
border-right: 1px solid var(--spectrum-global-color-gray-200);
|
||||||
}
|
}
|
||||||
|
.sticky-column.scrolled {
|
||||||
|
box-shadow: 1px -4px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
.sticky-column :global(.cell) {
|
||||||
|
border-right-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
|
.row.new:hover :global(.cell) {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
/* Styles for label cell */
|
/* Styles for label cell */
|
||||||
.checkbox {
|
.checkbox {
|
||||||
|
@ -140,12 +178,9 @@
|
||||||
display: none;
|
display: none;
|
||||||
color: var(--spectrum-global-color-gray-500);
|
color: var(--spectrum-global-color-gray-500);
|
||||||
}
|
}
|
||||||
.row:hover .checkbox,
|
.checkbox.visible,
|
||||||
.checkbox.visible {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
.number.visible {
|
.number.visible {
|
||||||
display: block;
|
display: flex;
|
||||||
}
|
}
|
||||||
.row:hover .number {
|
.row:hover .number {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
|
@ -6,6 +6,7 @@ export const createColumnsStores = context => {
|
||||||
const columns = writable([])
|
const columns = writable([])
|
||||||
const stickyColumn = writable(null)
|
const stickyColumn = writable(null)
|
||||||
|
|
||||||
|
// Merge new schema fields with existing schema in order to preserve widths
|
||||||
schema.subscribe($schema => {
|
schema.subscribe($schema => {
|
||||||
const currentColumns = get(columns)
|
const currentColumns = get(columns)
|
||||||
if (!$schema) {
|
if (!$schema) {
|
||||||
|
|
Loading…
Reference in New Issue