Merge branch 'master' into revert-14020-revert-13993-fix/disallow-prohibited-columns

This commit is contained in:
Michael Drury 2024-06-27 15:00:34 +01:00 committed by GitHub
commit 3bfbea4b26
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 182 additions and 148 deletions

View File

@ -3,6 +3,7 @@
import { Button } from "@budibase/bbui"
import GridCell from "../cells/GridCell.svelte"
import GridScrollWrapper from "./GridScrollWrapper.svelte"
import { BlankRowID } from "../lib/constants"
const {
renderedRows,
@ -17,6 +18,7 @@
isDragging,
buttonColumnWidth,
showVScrollbar,
dispatch,
} = getContext("grid")
let container
@ -89,6 +91,17 @@
</GridCell>
</div>
{/each}
<div
class="row blank"
on:mouseenter={$isDragging ? null : () => ($hoveredRowId = BlankRowID)}
on:mouseleave={$isDragging ? null : () => ($hoveredRowId = null)}
>
<GridCell
width={$buttonColumnWidth}
highlighted={$hoveredRowId === BlankRowID}
on:click={() => dispatch("add-row-inline")}
/>
</div>
</GridScrollWrapper>
</div>
</div>
@ -129,8 +142,11 @@
align-items: center;
gap: 4px;
}
.blank :global(.cell:hover) {
cursor: pointer;
}
/* Add left cell border */
/* Add left cell border to all cells */
.button-column :global(.cell) {
border-left: var(--cell-border);
}

View File

@ -26,7 +26,7 @@
MaxCellRenderOverflow,
GutterWidth,
DefaultRowHeight,
Padding,
VPadding,
SmallRowHeight,
ControlsHeight,
ScrollBarSize,
@ -119,7 +119,7 @@
// Derive min height and make available in context
const minHeight = derived(rowHeight, $height => {
const heightForControls = showControls ? ControlsHeight : 0
return Padding + SmallRowHeight + $height + heightForControls
return VPadding + SmallRowHeight + $height + heightForControls
})
context = { ...context, minHeight }
@ -354,8 +354,13 @@
transition: none;
}
/* Overrides */
.grid.quiet :global(.grid-data-content .row > .cell:not(:last-child)) {
/* Overrides for quiet */
.grid.quiet :global(.grid-data-content .row > .cell:not(:last-child)),
.grid.quiet :global(.sticky-column .row > .cell),
.grid.quiet :global(.new-row .row > .cell:not(:last-child)) {
border-right: none;
}
.grid.quiet :global(.sticky-column:before) {
display: none;
}
</style>

View File

@ -2,6 +2,7 @@
import { getContext, onMount } from "svelte"
import GridScrollWrapper from "./GridScrollWrapper.svelte"
import GridRow from "./GridRow.svelte"
import GridCell from "../cells/GridCell.svelte"
import { BlankRowID } from "../lib/constants"
import ButtonColumn from "./ButtonColumn.svelte"
@ -46,7 +47,6 @@
</script>
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div bind:this={body} class="grid-body">
<GridScrollWrapper scrollHorizontally scrollVertically attachHandlers>
{#each $renderedRows as row, idx}
@ -54,13 +54,16 @@
{/each}
{#if $config.canAddRows}
<div
class="blank"
class:highlighted={$hoveredRowId === BlankRowID}
style="width:{columnsWidth}px"
class="row blank"
on:mouseenter={$isDragging ? null : () => ($hoveredRowId = BlankRowID)}
on:mouseleave={$isDragging ? null : () => ($hoveredRowId = null)}
on:click={() => dispatch("add-row-inline")}
/>
>
<GridCell
width={columnsWidth}
highlighted={$hoveredRowId === BlankRowID}
on:click={() => dispatch("add-row-inline")}
/>
</div>
{/if}
</GridScrollWrapper>
{#if $props.buttons?.length}
@ -76,15 +79,13 @@
overflow: hidden;
flex: 1 1 auto;
}
.blank {
height: var(--row-height);
background: var(--cell-background);
border-bottom: var(--cell-border);
border-right: var(--cell-border);
position: absolute;
.row {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: stretch;
}
.blank.highlighted {
background: var(--cell-background-hover);
.blank :global(.cell:hover) {
cursor: pointer;
}
</style>

View File

@ -31,6 +31,7 @@
filter,
inlineFilters,
columnRenderMap,
scrollTop,
} = getContext("grid")
let visible = false
@ -43,6 +44,21 @@
$: $datasource, (visible = false)
$: selectedRowCount = Object.values($selectedRows).length
$: hasNoRows = !$rows.length
$: renderedRowCount = $renderedRows.length
$: offset = getOffset($hasNextPage, renderedRowCount, $rowHeight, $scrollTop)
const getOffset = (hasNextPage, rowCount, rowHeight, scrollTop) => {
// If we have a next page of data then we aren't truly at the bottom, so we
// render the add row component at the top
if (hasNextPage) {
return 0
}
offset = rowCount * rowHeight - (scrollTop % rowHeight)
if (rowCount !== 0) {
offset -= 1
}
return offset
}
const addRow = async () => {
// Blur the active cell and tick to let final value updates propagate
@ -85,12 +101,6 @@
return
}
// If we have a next page of data then we aren't truly at the bottom, so we
// render the add row component at the top
if ($hasNextPage) {
offset = 0
}
// If we don't have a next page then we're at the bottom and can scroll to
// the max available offset
else {
@ -98,10 +108,6 @@
...state,
top: $maxScrollTop,
}))
offset = $renderedRows.length * $rowHeight - ($maxScrollTop % $rowHeight)
if ($renderedRows.length !== 0) {
offset -= 1
}
}
// Update state and select initial cell
@ -171,39 +177,41 @@
<!-- Only show new row functionality if we have any columns -->
{#if visible}
<div
class="container"
class="new-row"
class:floating={offset > 0}
style="--offset:{offset}px; --sticky-width:{width}px;"
>
<div class="underlay sticky" transition:fade|local={{ duration: 130 }} />
<div class="underlay" transition:fade|local={{ duration: 130 }} />
<div class="sticky-column" transition:fade|local={{ duration: 130 }}>
<GutterCell expandable on:expand={addViaModal} rowHovered>
<Icon name="Add" color="var(--spectrum-global-color-gray-500)" />
{#if isAdding}
<div in:fade={{ duration: 130 }} class="loading-overlay" />
{/if}
</GutterCell>
{#if $stickyColumn}
{@const cellId = getCellID(NewRowID, $stickyColumn.name)}
<DataCell
{cellId}
rowFocused
column={$stickyColumn}
row={newRow}
focused={$focusedCellId === cellId}
width={$stickyColumn.width}
{updateValue}
topRow={offset === 0}
>
{#if $stickyColumn?.schema?.autocolumn}
<div class="readonly-overlay">Can't edit auto column</div>
{/if}
<div class="row">
<GutterCell expandable on:expand={addViaModal} rowHovered>
<Icon name="Add" color="var(--spectrum-global-color-gray-500)" />
{#if isAdding}
<div in:fade={{ duration: 130 }} class="loading-overlay" />
{/if}
</DataCell>
{/if}
</GutterCell>
{#if $stickyColumn}
{@const cellId = getCellID(NewRowID, $stickyColumn.name)}
<DataCell
{cellId}
rowFocused
column={$stickyColumn}
row={newRow}
focused={$focusedCellId === cellId}
width={$stickyColumn.width}
{updateValue}
topRow={offset === 0}
>
{#if $stickyColumn?.schema?.autocolumn}
<div class="readonly-overlay">Can't edit auto column</div>
{/if}
{#if isAdding}
<div in:fade={{ duration: 130 }} class="loading-overlay" />
{/if}
</DataCell>
{/if}
</div>
</div>
<div class="normal-columns" transition:fade|local={{ duration: 130 }}>
<GridScrollWrapper scrollHorizontally attachHandlers>
@ -270,7 +278,7 @@
margin-left: -6px;
}
.container {
.new-row {
position: absolute;
top: var(--default-row-height);
left: 0;
@ -280,10 +288,10 @@
flex-direction: row;
align-items: stretch;
}
.container :global(.cell) {
.new-row :global(.cell) {
--cell-background: var(--spectrum-global-color-gray-75) !important;
}
.container.floating :global(.cell) {
.new-row.floating :global(.cell) {
height: calc(var(--row-height) + 1px);
border-top: var(--cell-border);
}
@ -312,8 +320,10 @@
pointer-events: all;
z-index: 3;
position: absolute;
top: calc(var(--row-height) + var(--offset) + 24px);
left: 18px;
top: calc(
var(--row-height) + var(--offset) + var(--default-row-height) / 2
);
left: calc(var(--default-row-height) / 2);
}
.button-with-keys {
display: flex;

View File

@ -66,62 +66,58 @@
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="content">
<GridScrollWrapper scrollVertically attachHandlers>
{#each $renderedRows as row, idx}
{@const rowSelected = !!$selectedRows[row._id]}
{@const rowHovered = $hoveredRowId === row._id}
{@const rowFocused = $focusedRow?._id === row._id}
{@const cellId = getCellID(row._id, $stickyColumn?.name)}
<div
class="row"
on:mouseenter={$isDragging ? null : () => ($hoveredRowId = row._id)}
on:mouseleave={$isDragging ? null : () => ($hoveredRowId = null)}
on:click={() => dispatch("rowclick", rows.actions.cleanRow(row))}
>
<GutterCell {row} {rowFocused} {rowHovered} {rowSelected} />
{#if $stickyColumn}
<DataCell
{row}
{cellId}
{rowFocused}
selected={rowSelected}
highlighted={rowHovered || rowFocused}
rowIdx={row.__idx}
topRow={idx === 0}
focused={$focusedCellId === cellId}
selectedUser={$selectedCellMap[cellId]}
width={$stickyColumn.width}
column={$stickyColumn}
contentLines={$contentLines}
/>
{/if}
</div>
{/each}
{#if $config.canAddRows}
<div
class="row new"
on:mouseenter={$isDragging
? null
: () => ($hoveredRowId = BlankRowID)}
on:mouseleave={$isDragging ? null : () => ($hoveredRowId = null)}
on:click={() => dispatch("add-row-inline")}
>
<GutterCell rowHovered={$hoveredRowId === BlankRowID}>
<Icon name="Add" color="var(--spectrum-global-color-gray-500)" />
</GutterCell>
{#if $stickyColumn}
<GridCell
width={$stickyColumn.width}
highlighted={$hoveredRowId === BlankRowID}
>
<KeyboardShortcut padded keybind="Ctrl+Enter" />
</GridCell>
{/if}
</div>
{/if}
</GridScrollWrapper>
</div>
<GridScrollWrapper scrollVertically attachHandlers>
{#each $renderedRows as row, idx}
{@const rowSelected = !!$selectedRows[row._id]}
{@const rowHovered = $hoveredRowId === row._id}
{@const rowFocused = $focusedRow?._id === row._id}
{@const cellId = getCellID(row._id, $stickyColumn?.name)}
<div
class="row"
on:mouseenter={$isDragging ? null : () => ($hoveredRowId = row._id)}
on:mouseleave={$isDragging ? null : () => ($hoveredRowId = null)}
on:click={() => dispatch("rowclick", rows.actions.cleanRow(row))}
>
<GutterCell {row} {rowFocused} {rowHovered} {rowSelected} />
{#if $stickyColumn}
<DataCell
{row}
{cellId}
{rowFocused}
selected={rowSelected}
highlighted={rowHovered || rowFocused}
rowIdx={row.__idx}
topRow={idx === 0}
focused={$focusedCellId === cellId}
selectedUser={$selectedCellMap[cellId]}
width={$stickyColumn.width}
column={$stickyColumn}
contentLines={$contentLines}
/>
{/if}
</div>
{/each}
{#if $config.canAddRows}
<div
class="row blank"
on:mouseenter={$isDragging ? null : () => ($hoveredRowId = BlankRowID)}
on:mouseleave={$isDragging ? null : () => ($hoveredRowId = null)}
on:click={() => dispatch("add-row-inline")}
>
<GutterCell rowHovered={$hoveredRowId === BlankRowID}>
<Icon name="Add" color="var(--spectrum-global-color-gray-500)" />
</GutterCell>
{#if $stickyColumn}
<GridCell
width={$stickyColumn.width}
highlighted={$hoveredRowId === BlankRowID}
>
<KeyboardShortcut padded keybind="Ctrl+Enter" />
</GridCell>
{/if}
</div>
{/if}
</GridScrollWrapper>
</div>
<style>
@ -174,11 +170,7 @@
justify-content: flex-start;
align-items: stretch;
}
.content {
position: relative;
flex: 1 1 auto;
}
.row.new :global(*:hover) {
.blank :global(.cell:hover) {
cursor: pointer;
}
</style>

View File

@ -1,12 +1,13 @@
export const Padding = 100
export const ScrollBarSize = 8
export const GutterWidth = 72
export const DefaultColumnWidth = 200
export const MinColumnWidth = 80
export const SmallRowHeight = 36
export const MediumRowHeight = 64
export const LargeRowHeight = 92
export const DefaultRowHeight = SmallRowHeight
export const VPadding = SmallRowHeight * 2
export const HPadding = 40
export const ScrollBarSize = 8
export const GutterWidth = 72
export const DefaultColumnWidth = 200
export const MinColumnWidth = 80
export const NewRowID = "new"
export const BlankRowID = "blank"
export const RowPageSize = 100

View File

@ -1,6 +1,12 @@
import { writable, derived, get } from "svelte/store"
import { tick } from "svelte"
import { Padding, GutterWidth, FocusedCellMinOffset } from "../lib/constants"
import {
GutterWidth,
FocusedCellMinOffset,
ScrollBarSize,
HPadding,
VPadding,
} from "../lib/constants"
import { parseCellID } from "../lib/utils"
export const createStores = () => {
@ -34,28 +40,15 @@ export const deriveStores = context => {
// Memoize store primitives
const stickyColumnWidth = derived(stickyColumn, $col => $col?.width || 0, 0)
// Derive vertical limits
const contentHeight = derived(
[rows, rowHeight],
([$rows, $rowHeight]) => ($rows.length + 1) * $rowHeight + Padding,
0
)
const maxScrollTop = derived(
[height, contentHeight],
([$height, $contentHeight]) => Math.max($contentHeight - $height, 0),
0
)
// Derive horizontal limits
const contentWidth = derived(
[visibleColumns, stickyColumnWidth, buttonColumnWidth],
([$visibleColumns, $stickyColumnWidth, $buttonColumnWidth]) => {
const space = Math.max(Padding, $buttonColumnWidth - 1)
let width = GutterWidth + space + $stickyColumnWidth
let width = GutterWidth + $buttonColumnWidth + $stickyColumnWidth
$visibleColumns.forEach(col => {
width += col.width
})
return width
return width + HPadding
},
0
)
@ -71,14 +64,6 @@ export const deriveStores = context => {
},
0
)
// Derive whether to show scrollbars or not
const showVScrollbar = derived(
[contentHeight, height],
([$contentHeight, $height]) => {
return $contentHeight > $height
}
)
const showHScrollbar = derived(
[contentWidth, screenWidth],
([$contentWidth, $screenWidth]) => {
@ -86,6 +71,30 @@ export const deriveStores = context => {
}
)
// Derive vertical limits
const contentHeight = derived(
[rows, rowHeight, showHScrollbar],
([$rows, $rowHeight, $showHScrollbar]) => {
let height = ($rows.length + 1) * $rowHeight + VPadding
if ($showHScrollbar) {
height += ScrollBarSize * 2
}
return height
},
0
)
const maxScrollTop = derived(
[height, contentHeight],
([$height, $contentHeight]) => Math.max($contentHeight - $height, 0),
0
)
const showVScrollbar = derived(
[contentHeight, height],
([$contentHeight, $height]) => {
return $contentHeight > $height
}
)
return {
contentHeight,
contentWidth,