Merge pull request #10491 from Budibase/more-grid-tweaks

Grid improvements and fixes
This commit is contained in:
Andrew Kingston 2023-05-05 11:38:24 +01:00 committed by GitHub
commit 687d3a9267
17 changed files with 143 additions and 87 deletions

View File

@ -1,4 +1,8 @@
const ignoredClasses = [".flatpickr-calendar", ".spectrum-Popover"] const ignoredClasses = [
".flatpickr-calendar",
".spectrum-Popover",
".download-js-link",
]
let clickHandlers = [] let clickHandlers = []
/** /**
@ -22,8 +26,8 @@ const handleClick = event => {
} }
// Ignore clicks for modals, unless the handler is registered from a modal // Ignore clicks for modals, unless the handler is registered from a modal
const sourceInModal = handler.anchor.closest(".spectrum-Modal") != null const sourceInModal = handler.anchor.closest(".spectrum-Underlay") != null
const clickInModal = event.target.closest(".spectrum-Modal") != null const clickInModal = event.target.closest(".spectrum-Underlay") != null
if (clickInModal && !sourceInModal) { if (clickInModal && !sourceInModal) {
return return
} }

View File

@ -39,7 +39,7 @@
{#if datasource} {#if datasource}
<div> <div>
<ActionButton icon="DataCorrelated" primary quiet on:click={modal.show}> <ActionButton icon="DataCorrelated" primary quiet on:click={modal.show}>
Define existing relationship Define relationship
</ActionButton> </ActionButton>
</div> </div>
<Modal bind:this={modal}> <Modal bind:this={modal}>

View File

@ -9,13 +9,21 @@
$: selectedRowArray = Object.keys($selectedRows).map(id => ({ _id: id })) $: selectedRowArray = Object.keys($selectedRows).map(id => ({ _id: id }))
</script> </script>
<ExportButton <span data-ignore-click-outside="true">
{disabled} <ExportButton
view={$tableId} {disabled}
filters={$filter} view={$tableId}
sorting={{ filters={$filter}
sortColumn: $sort.column, sorting={{
sortOrder: $sort.order, sortColumn: $sort.column,
}} sortOrder: $sort.order,
selectedRows={selectedRowArray} }}
/> selectedRows={selectedRowArray}
/>
</span>
<style>
span {
display: contents;
}
</style>

View File

@ -2,19 +2,19 @@
import TableFilterButton from "../TableFilterButton.svelte" import TableFilterButton from "../TableFilterButton.svelte"
import { getContext } from "svelte" import { getContext } from "svelte"
const { columns, config, filter, table } = getContext("grid") const { columns, tableId, filter, table } = getContext("grid")
const onFilter = e => { const onFilter = e => {
filter.set(e.detail || []) filter.set(e.detail || [])
} }
</script> </script>
{#key $config.tableId} {#key $tableId}
<TableFilterButton <TableFilterButton
schema={$table?.schema} schema={$table?.schema}
filters={$filter} filters={$filter}
on:change={onFilter} on:change={onFilter}
disabled={!$columns.length} disabled={!$columns.length}
tableId={$config.tableId} tableId={$tableId}
/> />
{/key} {/key}

View File

@ -2,7 +2,7 @@
import ManageAccessButton from "../ManageAccessButton.svelte" import ManageAccessButton from "../ManageAccessButton.svelte"
import { getContext } from "svelte" import { getContext } from "svelte"
const { config } = getContext("grid") const { tableId } = getContext("grid")
</script> </script>
<ManageAccessButton resourceId={$config.tableId} /> <ManageAccessButton resourceId={$tableId} />

View File

@ -70,7 +70,15 @@
</div> </div>
{/if} {/if}
{/if} {/if}
{#if $config.allowExpandRows} {#if rowSelected && $config.allowDeleteRows}
<div class="delete" on:click={() => dispatch("request-bulk-delete")}>
<Icon
name="Delete"
size="S"
color="var(--spectrum-global-color-red-400)"
/>
</div>
{:else if $config.allowExpandRows}
<div <div
class="expand" class="expand"
class:visible={!disableExpand && (rowFocused || rowHovered)} class:visible={!disableExpand && (rowFocused || rowHovered)}
@ -111,9 +119,12 @@
.number.visible { .number.visible {
display: flex; display: flex;
} }
.delete,
.expand {
margin-right: 4px;
}
.expand { .expand {
opacity: 0; opacity: 0;
margin-right: 4px;
} }
.expand :global(.spectrum-Icon) { .expand :global(.spectrum-Icon) {
pointer-events: none; pointer-events: none;
@ -124,4 +135,11 @@
.expand.visible :global(.spectrum-Icon) { .expand.visible :global(.spectrum-Icon) {
pointer-events: all; pointer-events: all;
} }
.delete:hover {
cursor: pointer;
}
.delete:hover :global(.spectrum-Icon) {
color: var(--spectrum-global-color-red-600) !important;
}
</style> </style>

View File

@ -1,8 +1,8 @@
<script> <script>
import { Modal, ModalContent, Button, notifications } from "@budibase/bbui" import { Modal, ModalContent, notifications } from "@budibase/bbui"
import { getContext, onMount } from "svelte" import { getContext, onMount } from "svelte"
const { selectedRows, rows, config, subscribe } = getContext("grid") const { selectedRows, rows, subscribe } = getContext("grid")
let modal let modal
@ -27,20 +27,6 @@
onMount(() => subscribe("request-bulk-delete", () => modal?.show())) onMount(() => subscribe("request-bulk-delete", () => modal?.show()))
</script> </script>
{#if selectedRowCount}
<div class="delete-button" data-ignore-click-outside="true">
<Button
icon="Delete"
size="M"
on:click={modal.show}
disabled={!$config.allowEditRows}
cta
>
Delete {selectedRowCount} row{selectedRowCount === 1 ? "" : "s"}
</Button>
</div>
{/if}
<Modal bind:this={modal}> <Modal bind:this={modal}>
<ModalContent <ModalContent
title="Delete rows" title="Delete rows"
@ -53,14 +39,3 @@
row{selectedRowCount === 1 ? "" : "s"}? row{selectedRowCount === 1 ? "" : "s"}?
</ModalContent> </ModalContent>
</Modal> </Modal>
<style>
.delete-button :global(.spectrum-Button:not(:disabled)) {
background-color: var(--spectrum-global-color-red-400);
border-color: var(--spectrum-global-color-red-400);
}
.delete-button :global(.spectrum-Button:not(:disabled):hover) {
background-color: var(--spectrum-global-color-red-500);
border-color: var(--spectrum-global-color-red-500);
}
</style>

View File

@ -3,7 +3,7 @@
import { ActionButton, Popover } from "@budibase/bbui" import { ActionButton, Popover } from "@budibase/bbui"
import { DefaultColumnWidth } from "../lib/constants" import { DefaultColumnWidth } from "../lib/constants"
const { stickyColumn, columns } = getContext("grid") const { stickyColumn, columns, compact } = getContext("grid")
const smallSize = 120 const smallSize = 120
const mediumSize = DefaultColumnWidth const mediumSize = DefaultColumnWidth
const largeSize = DefaultColumnWidth * 1.5 const largeSize = DefaultColumnWidth * 1.5
@ -59,12 +59,13 @@
on:click={() => (open = !open)} on:click={() => (open = !open)}
selected={open} selected={open}
disabled={!allCols.length} disabled={!allCols.length}
tooltip={$compact ? "Width" : null}
> >
Width {$compact ? "" : "Width"}
</ActionButton> </ActionButton>
</div> </div>
<Popover bind:open {anchor} align="left"> <Popover bind:open {anchor} align={$compact ? "right" : "left"}>
<div class="content"> <div class="content">
{#each sizeOptions as option} {#each sizeOptions as option}
<ActionButton <ActionButton

View File

@ -1,8 +1,9 @@
<script> <script>
import { getContext } from "svelte" import { getContext } from "svelte"
import { ActionButton, Popover, Toggle } from "@budibase/bbui" import { ActionButton, Popover, Toggle, Icon } from "@budibase/bbui"
import { getColumnIcon } from "../lib/utils"
const { columns, stickyColumn } = getContext("grid") const { columns, stickyColumn, compact } = getContext("grid")
let open = false let open = false
let anchor let anchor
@ -47,25 +48,32 @@
on:click={() => (open = !open)} on:click={() => (open = !open)}
selected={open || anyHidden} selected={open || anyHidden}
disabled={!$columns.length} disabled={!$columns.length}
tooltip={$compact ? "Columns" : ""}
> >
Columns {$compact ? "" : "Columns"}
</ActionButton> </ActionButton>
</div> </div>
<Popover bind:open {anchor} align="left"> <Popover bind:open {anchor} align={$compact ? "right" : "left"}>
<div class="content"> <div class="content">
<div class="columns"> <div class="columns">
{#if $stickyColumn} {#if $stickyColumn}
<div class="column">
<Icon size="S" name={getColumnIcon($stickyColumn)} />
{$stickyColumn.label}
</div>
<Toggle disabled size="S" value={true} /> <Toggle disabled size="S" value={true} />
<span>{$stickyColumn.label}</span>
{/if} {/if}
{#each $columns as column} {#each $columns as column}
<div class="column">
<Icon size="S" name={getColumnIcon(column)} />
{column.label}
</div>
<Toggle <Toggle
size="S" size="S"
value={column.visible} value={column.visible}
on:change={e => toggleVisibility(column, e.detail)} on:change={e => toggleVisibility(column, e.detail)}
/> />
<span>{column.label}</span>
{/each} {/each}
</div> </div>
<div class="buttons"> <div class="buttons">
@ -90,6 +98,13 @@
.columns { .columns {
display: grid; display: grid;
align-items: center; align-items: center;
grid-template-columns: auto 1fr; grid-template-columns: 1fr auto;
}
.columns :global(.spectrum-Switch) {
margin-right: 0;
}
.column {
display: flex;
gap: 8px;
} }
</style> </style>

View File

@ -7,7 +7,7 @@
SmallRowHeight, SmallRowHeight,
} from "../lib/constants" } from "../lib/constants"
const { rowHeight, columns, table } = getContext("grid") const { rowHeight, columns, table, compact } = getContext("grid")
const sizeOptions = [ const sizeOptions = [
{ {
label: "Small", label: "Small",
@ -41,12 +41,13 @@
size="M" size="M"
on:click={() => (open = !open)} on:click={() => (open = !open)}
selected={open} selected={open}
tooltip={$compact ? "Height" : null}
> >
Height {$compact ? "" : "Height"}
</ActionButton> </ActionButton>
</div> </div>
<Popover bind:open {anchor} align="left"> <Popover bind:open {anchor} align={$compact ? "right" : "left"}>
<div class="content"> <div class="content">
{#each sizeOptions as option} {#each sizeOptions as option}
<ActionButton <ActionButton

View File

@ -2,7 +2,7 @@
import { getContext } from "svelte" import { getContext } from "svelte"
import { ActionButton, Popover, Select } from "@budibase/bbui" import { ActionButton, Popover, Select } from "@budibase/bbui"
const { sort, columns, stickyColumn } = getContext("grid") const { sort, columns, stickyColumn, compact } = getContext("grid")
let open = false let open = false
let anchor let anchor
@ -90,12 +90,13 @@
on:click={() => (open = !open)} on:click={() => (open = !open)}
selected={open} selected={open}
disabled={!columnOptions.length} disabled={!columnOptions.length}
tooltip={$compact ? "Sort" : ""}
> >
Sort {$compact ? "" : "Sort"}
</ActionButton> </ActionButton>
</div> </div>
<Popover bind:open {anchor} align="left"> <Popover bind:open {anchor} align={$compact ? "right" : "left"}>
<div class="content"> <div class="content">
<Select <Select
placeholder={null} placeholder={null}

View File

@ -6,7 +6,7 @@
import { createEventManagers } from "../lib/events" import { createEventManagers } from "../lib/events"
import { createAPIClient } from "../../../api" import { createAPIClient } from "../../../api"
import { attachStores } from "../stores" import { attachStores } from "../stores"
import DeleteButton from "../controls/DeleteButton.svelte" import BulkDeleteHandler from "../controls/BulkDeleteHandler.svelte"
import BetaButton from "../controls/BetaButton.svelte" import BetaButton from "../controls/BetaButton.svelte"
import GridBody from "./GridBody.svelte" import GridBody from "./GridBody.svelte"
import ResizeOverlay from "../overlays/ResizeOverlay.svelte" import ResizeOverlay from "../overlays/ResizeOverlay.svelte"
@ -112,13 +112,12 @@
<AddRowButton /> <AddRowButton />
<AddColumnButton /> <AddColumnButton />
<slot name="controls" /> <slot name="controls" />
<SortButton />
<HideColumnsButton />
<ColumnWidthButton /> <ColumnWidthButton />
<RowHeightButton /> <RowHeightButton />
<HideColumnsButton />
<SortButton />
</div> </div>
<div class="controls-right"> <div class="controls-right">
<DeleteButton />
<UserAvatars /> <UserAvatars />
</div> </div>
</div> </div>
@ -131,7 +130,9 @@
<GridBody /> <GridBody />
</div> </div>
<BetaButton /> <BetaButton />
<NewRow /> {#if allowAddRows}
<NewRow />
{/if}
<div class="overlays"> <div class="overlays">
<ResizeOverlay /> <ResizeOverlay />
<ReorderOverlay /> <ReorderOverlay />
@ -146,6 +147,9 @@
<ProgressCircle /> <ProgressCircle />
</div> </div>
{/if} {/if}
{#if allowDeleteRows}
<BulkDeleteHandler />
{/if}
<KeyboardManager /> <KeyboardManager />
</div> </div>
@ -214,6 +218,7 @@
padding: var(--cell-padding); padding: var(--cell-padding);
gap: var(--cell-spacing); gap: var(--cell-spacing);
background: var(--background); background: var(--background);
z-index: 2;
} }
.controls-left, .controls-left,
.controls-right { .controls-right {
@ -239,7 +244,7 @@
height: 100%; height: 100%;
display: grid; display: grid;
place-items: center; place-items: center;
z-index: 10; z-index: 100;
} }
.grid-loading:before { .grid-loading:before {
content: ""; content: "";

View File

@ -4,8 +4,14 @@
import HeaderCell from "../cells/HeaderCell.svelte" import HeaderCell from "../cells/HeaderCell.svelte"
import { Icon } from "@budibase/bbui" import { Icon } from "@budibase/bbui"
const { renderedColumns, dispatch, scroll, hiddenColumnsWidth, width } = const {
getContext("grid") renderedColumns,
dispatch,
scroll,
hiddenColumnsWidth,
width,
config,
} = getContext("grid")
$: columnsWidth = $renderedColumns.reduce( $: columnsWidth = $renderedColumns.reduce(
(total, col) => (total += col.width), (total, col) => (total += col.width),
@ -23,13 +29,15 @@
{/each} {/each}
</div> </div>
</GridScrollWrapper> </GridScrollWrapper>
<div {#if $config.allowAddColumns}
class="add" <div
style="left:{left}px" class="add"
on:click={() => dispatch("add-column")} style="left:{left}px"
> on:click={() => dispatch("add-column")}
<Icon name="Add" /> >
</div> <Icon name="Add" />
</div>
{/if}
</div> </div>
<style> <style>
@ -38,7 +46,6 @@
border-bottom: var(--cell-border); border-bottom: var(--cell-border);
position: relative; position: relative;
height: var(--default-row-height); height: var(--default-row-height);
z-index: 1;
} }
.row { .row {
display: flex; display: flex;
@ -54,6 +61,7 @@
border-right: var(--cell-border); border-right: var(--cell-border);
border-bottom: var(--cell-border); border-bottom: var(--cell-border);
background: var(--spectrum-global-color-gray-100); background: var(--spectrum-global-color-gray-100);
z-index: 20;
} }
.add:hover { .add:hover {
background: var(--spectrum-global-color-gray-200); background: var(--spectrum-global-color-gray-200);

View File

@ -270,7 +270,7 @@
z-index: 3; z-index: 3;
position: absolute; position: absolute;
top: calc(var(--row-height) + var(--offset) + 24px); top: calc(var(--row-height) + var(--offset) + 24px);
left: var(--gutter-width); left: 18px;
} }
.button-with-keys { .button-with-keys {
display: flex; display: flex;

View File

@ -21,6 +21,9 @@ const TypeIconMap = {
} }
export const getColumnIcon = column => { export const getColumnIcon = column => {
if (column.schema.autocolumn) {
return "MagicWand"
}
const type = column.schema.type const type = column.schema.type
return TypeIconMap[type] || "Text" return TypeIconMap[type] || "Text"
} }

View File

@ -13,6 +13,7 @@
clipboard, clipboard,
dispatch, dispatch,
selectedRows, selectedRows,
config,
} = getContext("grid") } = getContext("grid")
const ignoredOriginSelectors = [ const ignoredOriginSelectors = [
@ -37,10 +38,12 @@
e.preventDefault() e.preventDefault()
focusFirstCell() focusFirstCell()
} else if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) { } else if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
e.preventDefault() if ($config.allowAddRows) {
dispatch("add-row-inline") e.preventDefault()
dispatch("add-row-inline")
}
} else if (e.key === "Delete" || e.key === "Backspace") { } else if (e.key === "Delete" || e.key === "Backspace") {
if (Object.keys($selectedRows).length) { if (Object.keys($selectedRows).length && $config.allowDeleteRows) {
dispatch("request-bulk-delete") dispatch("request-bulk-delete")
} }
} }
@ -88,7 +91,9 @@
} }
break break
case "Enter": case "Enter":
dispatch("add-row-inline") if ($config.allowAddRows) {
dispatch("add-row-inline")
}
} }
} else { } else {
switch (e.key) { switch (e.key) {
@ -106,7 +111,7 @@
break break
case "Delete": case "Delete":
case "Backspace": case "Backspace":
if (Object.keys($selectedRows).length) { if (Object.keys($selectedRows).length && $config.allowDeleteRows) {
dispatch("request-bulk-delete") dispatch("request-bulk-delete")
} else { } else {
deleteSelectedCell() deleteSelectedCell()
@ -117,7 +122,9 @@
break break
case " ": case " ":
case "Space": case "Space":
toggleSelectRow() if ($config.allowDeleteRows) {
toggleSelectRow()
}
break break
default: default:
startEnteringValue(e.key, e.which) startEnteringValue(e.key, e.which)

View File

@ -2,6 +2,7 @@ import { writable, get, derived } from "svelte/store"
import { tick } from "svelte" import { tick } from "svelte"
import { import {
DefaultRowHeight, DefaultRowHeight,
GutterWidth,
LargeRowHeight, LargeRowHeight,
MediumRowHeight, MediumRowHeight,
NewRowID, NewRowID,
@ -43,6 +44,8 @@ export const deriveStores = context => {
enrichedRows, enrichedRows,
rowLookupMap, rowLookupMap,
rowHeight, rowHeight,
stickyColumn,
width,
} = context } = context
// Derive the row that contains the selected cell // Derive the row that contains the selected cell
@ -70,6 +73,7 @@ export const deriveStores = context => {
hoveredRowId.set(null) hoveredRowId.set(null)
} }
// Derive the amount of content lines to show in cells depending on row height
const contentLines = derived(rowHeight, $rowHeight => { const contentLines = derived(rowHeight, $rowHeight => {
if ($rowHeight === LargeRowHeight) { if ($rowHeight === LargeRowHeight) {
return 3 return 3
@ -79,9 +83,15 @@ export const deriveStores = context => {
return 1 return 1
}) })
// Derive whether we should use the compact UI, depending on width
const compact = derived([stickyColumn, width], ([$stickyColumn, $width]) => {
return ($stickyColumn?.width || 0) + $width + GutterWidth < 1100
})
return { return {
focusedRow, focusedRow,
contentLines, contentLines,
compact,
ui: { ui: {
actions: { actions: {
blur, blur,