1
0
Fork 0
mirror of synced 2024-08-14 09:31:49 +12:00

Move new row component to top, automatically invert cell renderers when required

This commit is contained in:
Andrew Kingston 2023-03-31 17:37:59 +01:00
parent 7050c6713a
commit a0299d4c7c
17 changed files with 331 additions and 62 deletions

View file

@ -355,6 +355,9 @@
input[type="file"] { input[type="file"] {
display: none; display: none;
} }
.compact .spectrum-Dropzone {
padding: 6px 0 !important;
}
.gallery { .gallery {
display: flex; display: flex;
@ -381,7 +384,14 @@
} }
.compact .placeholder, .compact .placeholder,
.compact img { .compact img {
margin: 12px 16px; margin: 10px 16px;
}
.compact img {
height: 90px;
}
.compact .gallery {
padding: 6px 10px;
margin-bottom: 8px;
} }
.title { .title {
display: flex; display: flex;

View file

@ -125,7 +125,7 @@
<div class="sort-indicator"> <div class="sort-indicator">
<Icon <Icon
size="S" size="S"
name={$sort.order === "descending" ? "ChevronDown" : "ChevronUp"} name={$sort.order === "descending" ? "SortOrderDown" : "SortOrderUp"}
color="var(--spectrum-global-color-gray-600)" color="var(--spectrum-global-color-gray-600)"
/> />
</div> </div>

View file

@ -158,10 +158,11 @@
user-select: none; user-select: none;
} }
.arrow { .arrow {
border-right: 2px solid var(--spectrum-global-color-blue-400); /*border-right: 2px solid var(--spectrum-global-color-blue-400);*/
position: absolute; position: absolute;
right: 0; right: 0;
top: 2px; top: 0;
height: 100%;
bottom: 2px; bottom: 2px;
padding: 0 6px 0 16px; padding: 0 6px 0 16px;
display: grid; display: grid;
@ -182,7 +183,7 @@
box-shadow: 0 0 8px 4px rgba(0, 0, 0, 0.15); box-shadow: 0 0 8px 4px rgba(0, 0, 0, 0.15);
justify-content: flex-start; justify-content: flex-start;
align-items: stretch; align-items: stretch;
max-height: calc(5 * var(--cell-height) + 1px); max-height: var(--max-cell-render-height);
overflow-y: auto; overflow-y: auto;
border: var(--cell-border); border: var(--cell-border);
} }

View file

@ -293,7 +293,7 @@
left: 0; left: 0;
min-width: 100%; min-width: 100%;
max-width: calc(100% + 240px); max-width: calc(100% + 240px);
max-height: calc(var(--cell-height) + 240px); max-height: var(--max-cell-render-height);
background: var(--cell-background); background: var(--cell-background);
border: var(--cell-border); border: var(--cell-border);
box-shadow: 0 0 8px 4px rgba(0, 0, 0, 0.15); box-shadow: 0 0 8px 4px rgba(0, 0, 0, 0.15);

View file

@ -68,16 +68,34 @@
position: relative; position: relative;
width: 0; width: 0;
} }
.cell.selected:after,
.cell.error:after,
.cell.selected-other:not(.selected):after {
content: " ";
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
border: 2px solid transparent;
pointer-events: none;
border-radius: 2px;
box-sizing: border-box;
}
.cell.selected { .cell.selected {
box-shadow: inset 0 0 0 2px var(--spectrum-global-color-blue-400);
z-index: 2; z-index: 2;
} }
.cell.error { .cell.selected:after {
box-shadow: inset 0 0 0 2px var(--spectrum-global-color-red-400); border-color: var(--spectrum-global-color-blue-400);
}
.cell.error:after {
border-color: var(--spectrum-global-color-red-400);
} }
.cell.selected-other:not(.selected) { .cell.selected-other:not(.selected) {
z-index: 1; z-index: 1;
box-shadow: inset 0 0 0 2px var(--user-color); }
.cell.selected-other:not(.selected):after {
border-color: var(--spectrum-global-color-red-400);
} }
.cell:not(.selected) { .cell:not(.selected) {
user-select: none; user-select: none;
@ -112,7 +130,8 @@
.label { .label {
position: absolute; position: absolute;
bottom: 100%; bottom: 100%;
padding: 1px 4px; margin: 0 0 -2px 0;
padding: 1px 4px 3px 4px;
background: var(--user-color); background: var(--user-color);
border-radius: 2px 2px 0 0; border-radius: 2px 2px 0 0;
display: none; display: none;
@ -129,7 +148,8 @@
bottom: auto; bottom: auto;
top: 100%; top: 100%;
border-radius: 0 0 2px 2px; border-radius: 0 0 2px 2px;
padding: 0 4px 2px 4px; padding: 2px 4px 2px 4px;
margin: -2px 0 0 0;
} }
.cell:hover .label { .cell:hover .label {
display: block; display: block;

View file

@ -1,38 +1,16 @@
<script> <script>
import { Icon } from "@budibase/bbui" import { ActionButton } from "@budibase/bbui"
import { getContext } from "svelte" import { getContext } from "svelte"
const { dispatch, columns, ui } = getContext("sheet") const { dispatch, columns, stickyColumn } = getContext("sheet")
const addRow = () => {
ui.actions.blur()
dispatch("add-row")
}
</script> </script>
{#if $columns.length} <ActionButton
<div class="add-component" on:click={addRow}> icon="Add"
<Icon size="XL" name="Add" /> quiet
</div> size="M"
{/if} on:click={() => dispatch("add-row-inline")}
disabled={!$columns.length && !$stickyColumn}
<style> >
.add-component { Create row
position: absolute; </ActionButton>
bottom: calc(20px + var(--spacing-l));
right: calc(20px + var(--spacing-xl));
width: 60px;
height: 60px;
border-radius: 50%;
background: var(--spectrum-global-color-blue-500);
display: grid;
place-items: center;
color: white;
box-shadow: 1px 3px 8px 0 rgba(0, 0, 0, 0.3);
cursor: pointer;
transition: transform ease-out 300ms, background ease-out 130ms;
}
.add-component:hover {
background: var(--spectrum-global-color-blue-600);
}
</style>

View file

@ -32,7 +32,7 @@
background: var(--background); background: var(--background);
border-bottom: var(--cell-border); border-bottom: var(--cell-border);
position: relative; position: relative;
z-index: 1; z-index: 2;
height: var(--cell-height); height: var(--cell-height);
} }
.row { .row {

View file

@ -0,0 +1,242 @@
<script>
import SheetCell from "../cells/SheetCell.svelte"
import { getContext, onMount } from "svelte"
import { Icon, Button } from "@budibase/bbui"
import SheetScrollWrapper from "./SheetScrollWrapper.svelte"
import DataCell from "../cells/DataCell.svelte"
const {
renderedColumns,
hoveredRowId,
selectedCellId,
stickyColumn,
gutterWidth,
scroll,
config,
dispatch,
visibleColumns,
rows,
wheel,
showHScrollbar,
tableId,
subscribe,
selectedCellAPI,
} = getContext("sheet")
let isAdding = false
let newRow = {}
let touched = false
$: firstColumn = $stickyColumn || $visibleColumns[0]
$: rowHovered = $hoveredRowId === "new"
$: containsSelectedCell = $selectedCellId?.startsWith("new-")
$: width = gutterWidth + ($stickyColumn?.width || 0)
$: scrollLeft = $scroll.left
$: $tableId, (isAdding = false)
const addRow = async () => {
const savedRow = await rows.actions.addRow(newRow, 0)
if (savedRow && firstColumn) {
$selectedCellId = `${savedRow._id}-${firstColumn.name}`
isAdding = false
}
}
const cancel = () => {
isAdding = false
}
const startAdding = () => {
newRow = {}
isAdding = true
if (firstColumn) {
$selectedCellId = `new-${firstColumn.name}`
setTimeout(() => {
$selectedCellAPI?.focus()
}, 100)
}
}
const updateRow = (rowId, columnName, val) => {
touched = true
newRow[columnName] = val
}
const addViaModal = () => {
isAdding = false
dispatch("add-row")
}
onMount(() => subscribe("add-row-inline", startAdding))
</script>
<!-- Only show new row functionality if we have any columns -->
{#if firstColumn}
<div
class="container"
class:visible={isAdding}
on:wheel={wheel.actions.handleWheel}
>
<div class="content" class:above-scrollbar={$showHScrollbar}>
<div
class="new-row"
on:mouseenter={() => ($hoveredRowId = "new")}
on:mouseleave={() => ($hoveredRowId = null)}
>
<div
class="sticky-column"
style="flex: 0 0 {width}px"
class:scrolled={scrollLeft > 0}
>
<SheetCell
width={gutterWidth}
{rowHovered}
rowSelected={containsSelectedCell}
>
<div class="gutter">
<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}
selected={$selectedCellId === cellId}
rowSelected={containsSelectedCell}
width={$stickyColumn.width}
{updateRow}
/>
{/if}
</div>
<SheetScrollWrapper scrollVertically={false}>
<div class="row">
{#each $renderedColumns as column}
{@const cellId = `new-${column.name}`}
<DataCell
{cellId}
{column}
row={newRow}
{rowHovered}
selected={$selectedCellId === cellId}
rowSelected={containsSelectedCell}
width={column.width}
{updateRow}
/>
{/each}
</div>
</SheetScrollWrapper>
</div>
</div>
<div class="buttons">
<Button size="M" cta on:click={addRow}>Save</Button>
<Button size="M" secondary newStyles on:click={cancel}>Cancel</Button>
</div>
</div>
{/if}
<style>
.container {
pointer-events: none;
position: absolute;
top: var(--cell-height);
transform: translateY(-100%);
z-index: 1;
transition: transform 130ms ease-out;
background: linear-gradient(
to bottom,
var(--cell-background) 20%,
transparent 100%
);
width: 100%;
padding-bottom: 64px;
display: flex;
flex-direction: column;
align-items: stretch;
}
.container.visible {
transform: translateY(0);
}
.content {
pointer-events: all;
background: var(--background);
border-bottom: var(--cell-border);
}
.new-row {
display: flex;
bottom: 0;
left: 0;
width: 100%;
transition: margin-bottom 130ms ease-out;
}
.new-row.visible {
margin-bottom: 0;
}
.new-row :global(.cell) {
--cell-background: var(--background) !important;
border-bottom: none;
}
.sticky-column {
display: flex;
z-index: 1;
}
/* Don't show borders between cells in the sticky column */
.sticky-column :global(.cell:not(:last-child)) {
border-right: none;
}
.row {
width: 0;
display: flex;
}
/* Add shadow when scrolled */
.sticky.scrolled :global(.cell:last-child:after) {
content: " ";
position: absolute;
width: 10px;
height: 100%;
left: 100%;
background: linear-gradient(to right, rgba(0, 0, 0, 0.08), transparent);
}
/* Styles for gutter */
.gutter {
flex: 1 1 auto;
display: grid;
align-items: center;
padding: var(--cell-padding);
grid-template-columns: 1fr auto;
gap: var(--cell-spacing);
}
/* Floating buttons */
.buttons {
display: flex;
flex-direction: row;
gap: 8px;
margin: 16px 0 0 16px;
pointer-events: all;
align-self: flex-start;
}
.number {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
color: var(--spectrum-global-color-gray-500);
}
</style>

View file

@ -27,7 +27,8 @@
import KeyboardManager from "../overlays/KeyboardManager.svelte" import KeyboardManager from "../overlays/KeyboardManager.svelte"
import { clickOutside } from "@budibase/bbui" import { clickOutside } from "@budibase/bbui"
import SheetControls from "./SheetControls.svelte" import SheetControls from "./SheetControls.svelte"
import NewRow from "./NewRow.svelte" import NewRowTop from "./NewRowTop.svelte"
import { MaxCellRenderHeight } from "../lib/constants"
export let API export let API
export let tableId export let tableId
@ -108,12 +109,12 @@
id="sheet-{rand}" id="sheet-{rand}"
class:is-resizing={$isResizing} class:is-resizing={$isResizing}
class:is-reordering={$isReordering} class:is-reordering={$isReordering}
style="--cell-height:{cellHeight}px; --gutter-width:{gutterWidth}px;" style="--cell-height:{cellHeight}px; --gutter-width:{gutterWidth}px; --max-cell-render-height:{MaxCellRenderHeight}px;"
> >
<div class="controls"> <div class="controls">
<div class="controls-left"> <div class="controls-left">
<slot name="controls" />
<SheetControls /> <SheetControls />
<slot name="controls" />
</div> </div>
<div class="controls-right"> <div class="controls-right">
<DeleteButton /> <DeleteButton />
@ -129,7 +130,8 @@
<SheetBody /> <SheetBody />
</div> </div>
{#if $config.allowAddRows} {#if $config.allowAddRows}
<NewRow /> <!-- <NewRow />-->
<NewRowTop />
{/if} {/if}
<ResizeOverlay /> <ResizeOverlay />
<ScrollOverlay /> <ScrollOverlay />

View file

@ -2,11 +2,17 @@
import { getContext, onMount } from "svelte" import { getContext, onMount } from "svelte"
import SheetScrollWrapper from "./SheetScrollWrapper.svelte" import SheetScrollWrapper from "./SheetScrollWrapper.svelte"
import SheetRow from "./SheetRow.svelte" import SheetRow from "./SheetRow.svelte"
import { MaxCellRenderHeight } from "../lib/constants"
const { bounds, renderedRows } = getContext("sheet") const { bounds, renderedRows, visualRowCapacity, cellHeight } =
getContext("sheet")
let body let body
$: inversionIdx =
$visualRowCapacity - Math.ceil(MaxCellRenderHeight / cellHeight) - 2
$: console.log(inversionIdx)
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(() => {
@ -22,7 +28,7 @@
<div bind:this={body} class="sheet-body"> <div bind:this={body} class="sheet-body">
<SheetScrollWrapper> <SheetScrollWrapper>
{#each $renderedRows as row, idx} {#each $renderedRows as row, idx}
<SheetRow {row} {idx} /> <SheetRow {row} {idx} invert={idx >= inversionIdx} />
{/each} {/each}
</SheetScrollWrapper> </SheetScrollWrapper>
</div> </div>

View file

@ -1,7 +1,9 @@
<script> <script>
import SortButton from "../controls/SortButton.svelte" import SortButton from "../controls/SortButton.svelte"
import HideColumnsButton from "../controls/HideColumnsButton.svelte" import HideColumnsButton from "../controls/HideColumnsButton.svelte"
import AddRowButton from "../controls/AddRowButton.svelte"
</script> </script>
<AddRowButton />
<HideColumnsButton /> <HideColumnsButton />
<SortButton /> <SortButton />

View file

@ -4,6 +4,7 @@
export let row export let row
export let idx export let idx
export let invert = false
const { const {
selectedCellId, selectedCellId,
@ -40,6 +41,7 @@
{cellId} {cellId}
{column} {column}
{row} {row}
{invert}
/> />
{/each} {/each}
</div> </div>

View file

@ -164,12 +164,12 @@
/* Add shadow when scrolled */ /* Add shadow when scrolled */
.sticky-column.scrolled :global(.cell:last-child:after) { .sticky-column.scrolled :global(.cell:last-child:after) {
content: " "; /*content: " ";*/
position: absolute; /*position: absolute;*/
width: 10px; /*width: 10px;*/
height: 100%; /*height: 100%;*/
left: 100%; /*left: 100%;*/
background: linear-gradient(to right, rgba(0, 0, 0, 0.08), transparent); /*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 */
@ -179,7 +179,7 @@
.header { .header {
position: relative; position: relative;
z-index: 2; z-index: 3;
} }
.header :global(.cell) { .header :global(.cell) {
background: var(--spectrum-global-color-gray-100); background: var(--spectrum-global-color-gray-100);

View file

@ -0,0 +1 @@
export const MaxCellRenderHeight = 216

View file

@ -12,7 +12,7 @@ export const createMaxScrollStores = context => {
selectedCellId, selectedCellId,
gutterWidth, gutterWidth,
} = context } = context
const padding = 255 const padding = 264
// Memoize store primitives // Memoize store primitives
const scrollTop = derived(scroll, $scroll => $scroll.top, 0) const scrollTop = derived(scroll, $scroll => $scroll.top, 0)

View file

@ -136,7 +136,7 @@ export const createRowsStore = context => {
} else { } else {
handleNewRows([newRow]) handleNewRows([newRow])
} }
notifications.success("Row added successfully") notifications.success("Row created successfully")
return newRow return newRow
} catch (error) { } catch (error) {
notifications.error(`Error adding row: ${error?.message}`) notifications.error(`Error adding row: ${error?.message}`)

View file

@ -77,5 +77,10 @@ export const createViewportStores = context => {
[] []
) )
return { scrolledRowCount, visualRowCapacity, renderedRows, renderedColumns } return {
scrolledRowCount,
visualRowCapacity,
renderedRows,
renderedColumns,
}
} }