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

More grid improvements
This commit is contained in:
Andrew Kingston 2023-04-27 10:24:21 +01:00 committed by GitHub
commit 0769e332a6
11 changed files with 199 additions and 24 deletions

View File

@ -19,6 +19,7 @@
export let updateValue = rows.actions.updateValue
export let invertX = false
export let invertY = false
export let contentLines = 1
const emptyError = writable(null)
@ -84,5 +85,7 @@
{readonly}
{invertY}
{invertX}
{contentLines}
/>
<slot />
</GridCell>

View File

@ -117,6 +117,9 @@
.cell.error {
--cell-color: var(--spectrum-global-color-red-500);
}
.cell.readonly {
--cell-color: var(--spectrum-global-color-gray-600);
}
.cell:not(.focused) {
user-select: none;
}

View File

@ -37,6 +37,8 @@
$: sortedBy = column.name === $sort.column
$: canMoveLeft = orderable && idx > 0
$: canMoveRight = orderable && idx < $renderedColumns.length - 1
$: ascendingLabel = column.schema?.type === "number" ? "low-high" : "A-Z"
$: descendingLabel = column.schema?.type === "number" ? "high-low" : "Z-A"
const editColumn = () => {
dispatch("edit-column", column.schema)
@ -179,14 +181,14 @@
on:click={sortAscending}
disabled={column.name === $sort.column && $sort.order === "ascending"}
>
Sort A-Z
Sort {ascendingLabel}
</MenuItem>
<MenuItem
icon="SortOrderDown"
on:click={sortDescending}
disabled={column.name === $sort.column && $sort.order === "descending"}
>
Sort Z-A
Sort {descendingLabel}
</MenuItem>
<MenuItem disabled={!canMoveLeft} icon="ChevronLeft" on:click={moveLeft}>
Move left

View File

@ -12,6 +12,7 @@
export let api
export let invertX = false
export let invertY = false
export let contentLines = 1
let isOpen = false
let focusedOptionIdx = null
@ -86,7 +87,11 @@
class:open
on:click|self={editable ? open : null}
>
<div class="values" on:click={editable ? open : null}>
<div
class="values"
class:wrap={contentLines > 1}
on:click={editable ? open : null}
>
{#each values as val}
{@const color = getOptionColor(val)}
{#if color}
@ -160,6 +165,9 @@
grid-row-gap: var(--cell-padding);
overflow: hidden;
padding: var(--cell-padding);
flex-wrap: nowrap;
}
.values.wrap {
flex-wrap: wrap;
}
.text {

View File

@ -29,6 +29,7 @@
export let onChange
export let invertX = false
export let invertY = false
export let contentLines = 1
const { API, dispatch } = getContext("grid")
const color = getColor(0)
@ -243,7 +244,11 @@
<div class="wrapper" class:editable class:focused style="--color:{color};">
<div class="container">
<div class="values" on:wheel={e => (focused ? e.stopPropagation() : null)}>
<div
class="values"
class:wrap={editable || contentLines > 1}
on:wheel={e => (focused ? e.stopPropagation() : null)}
>
{#each value || [] as relationship, idx}
{#if relationship.primaryDisplay}
<div class="badge">
@ -376,6 +381,9 @@
grid-row-gap: var(--cell-padding);
overflow: hidden;
padding: var(--cell-padding);
flex-wrap: nowrap;
}
.values.wrap {
flex-wrap: wrap;
}
.count {

View File

@ -3,16 +3,13 @@
import { ActionButton, Popover, Select } from "@budibase/bbui"
const { sort, columns, stickyColumn } = getContext("grid")
const orderOptions = [
{ label: "A-Z", value: "ascending" },
{ label: "Z-A", value: "descending" },
]
let open = false
let anchor
$: columnOptions = getColumnOptions($stickyColumn, $columns)
$: checkValidSortColumn($sort.column, $stickyColumn, $columns)
$: orderOptions = getOrderOptions($sort.column, columnOptions)
const getColumnOptions = (stickyColumn, columns) => {
let options = []
@ -20,6 +17,7 @@
options.push({
label: stickyColumn.label || stickyColumn.name,
value: stickyColumn.name,
type: stickyColumn.schema?.type,
})
}
return [
@ -27,10 +25,25 @@
...columns.map(col => ({
label: col.label || col.name,
value: col.name,
type: col.schema?.type,
})),
]
}
const getOrderOptions = (column, columnOptions) => {
const type = columnOptions.find(col => col.value === column)?.type
return [
{
label: type === "number" ? "Low-high" : "A-Z",
value: "ascending",
},
{
label: type === "number" ? "High-low" : "Z-A",
value: "descending",
},
]
}
const updateSortColumn = e => {
sort.update(state => ({
...state,

View File

@ -15,6 +15,7 @@
selectedCellMap,
focusedRow,
columnHorizontalInversionIndex,
contentLines,
} = getContext("grid")
$: rowSelected = !!$selectedRows[row._id]
@ -44,6 +45,7 @@
focused={$focusedCellId === cellId}
selectedUser={$selectedCellMap[cellId]}
width={column.width}
contentLines={$contentLines}
/>
{/each}
</div>

View File

@ -0,0 +1,53 @@
<script>
export let keybind
export let padded = false
export let overlay = false
$: parsedKeys = parseKeys(keybind)
const parseKeys = keybind => {
return keybind?.split("+").map(key => {
if (key.toLowerCase() === "ctrl") {
return navigator.platform.startsWith("Mac") ? "⌘" : key
} else if (key.toLowerCase() === "enter") {
return "↵"
}
return key
})
}
</script>
<div class="keys" class:padded class:overlay>
{#each parsedKeys as key}
<div class="key">
{key}
</div>
{/each}
</div>
<style>
.keys {
display: flex;
flex-direction: row;
gap: 3px;
}
.keys.padded {
padding: var(--cell-padding);
}
.key {
padding: 2px 6px;
font-size: 12px;
font-weight: 600;
background-color: var(--spectrum-global-color-gray-200);
color: var(--spectrum-global-color-gray-700);
border-radius: 4px;
text-align: center;
display: grid;
place-items: center;
text-transform: uppercase;
}
.overlay .key {
background: rgba(255, 255, 255, 0.2);
color: #eee;
}
</style>

View File

@ -7,6 +7,7 @@
import { GutterWidth } from "../lib/constants"
import { NewRowID } from "../lib/constants"
import GutterCell from "../cells/GutterCell.svelte"
import KeyboardShortcut from "./KeyboardShortcut.svelte"
const {
hoveredRowId,
@ -27,13 +28,14 @@
columnHorizontalInversionIndex,
} = getContext("grid")
let visible = false
let isAdding = false
let newRow = {}
let offset = 0
$: firstColumn = $stickyColumn || $renderedColumns[0]
$: width = GutterWidth + ($stickyColumn?.width || 0)
$: $tableId, (isAdding = false)
$: $tableId, (visible = false)
$: invertY = shouldInvertY(offset, $rowVerticalInversionIndex, $renderedRows)
const shouldInvertY = (offset, inversionIndex, rows) => {
@ -45,7 +47,8 @@
const addRow = async () => {
// Blur the active cell and tick to let final value updates propagate
$focusedCellAPI?.blur()
isAdding = true
$focusedCellId = null
await tick()
// Create row
@ -60,17 +63,19 @@
$focusedCellId = `${savedRow._id}-${firstColumn.name}`
}
}
isAdding = false
}
const clear = () => {
isAdding = false
visible = false
$focusedCellId = null
$hoveredRowId = null
document.removeEventListener("keydown", handleKeyPress)
}
const startAdding = async () => {
if (isAdding) {
if (visible) {
return
}
@ -95,7 +100,7 @@
// Update state and select initial cell
newRow = {}
isAdding = true
visible = true
$hoveredRowId = NewRowID
if (firstColumn) {
$focusedCellId = `${NewRowID}-${firstColumn.name}`
@ -115,7 +120,7 @@
}
const handleKeyPress = e => {
if (!isAdding) {
if (!visible) {
return
}
if (e.key === "Escape") {
@ -137,7 +142,7 @@
</script>
<!-- Only show new row functionality if we have any columns -->
{#if isAdding}
{#if visible}
<div
class="container"
class:floating={offset > 0}
@ -148,6 +153,9 @@
<div class="sticky-column" transition:fade={{ duration: 130 }}>
<GutterCell 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 = `${NewRowID}-${$stickyColumn.name}`}
@ -161,7 +169,14 @@
{updateValue}
rowIdx={0}
{invertY}
/>
>
{#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 class="normal-columns" transition:fade={{ duration: 130 }}>
@ -181,15 +196,32 @@
rowIdx={0}
invertX={columnIdx >= $columnHorizontalInversionIndex}
{invertY}
/>
>
{#if column?.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>
{/key}
{/each}
</div>
</GridScrollWrapper>
</div>
<div class="buttons" transition:fade={{ duration: 130 }}>
<Button size="M" cta on:click={addRow}>Save</Button>
<Button size="M" secondary newStyles on:click={clear}>Cancel</Button>
<Button size="M" cta on:click={addRow} disabled={isAdding}>
<div class="button-with-keys">
Save
<KeyboardShortcut overlay keybind="Ctrl+Enter" />
</div>
</Button>
<Button size="M" secondary newStyles on:click={clear}>
<div class="button-with-keys">
Cancel
<KeyboardShortcut overlay keybind="Esc" />
</div>
</Button>
</div>
</div>
{/if}
@ -240,6 +272,14 @@
top: calc(var(--row-height) + var(--offset) + 24px);
left: var(--gutter-width);
}
.button-with-keys {
display: flex;
gap: 6px;
align-items: center;
}
.button-with-keys :global(> div) {
padding-top: 2px;
}
/* Sticky column styles */
.sticky-column {
@ -262,4 +302,33 @@
width: 0;
display: flex;
}
/* Readonly cell overlay */
.readonly-overlay {
position: absolute;
top: 0;
left: 0;
height: var(--row-height);
width: 100%;
padding: var(--cell-padding);
font-style: italic;
color: var(--spectrum-global-color-gray-600);
z-index: 1;
user-select: none;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
/* Overlay while row is being added */
.loading-overlay {
position: absolute;
top: 0;
left: 0;
height: var(--row-height);
width: 100%;
z-index: 1;
background: var(--spectrum-global-color-gray-400);
opacity: 0.25;
}
</style>

View File

@ -7,6 +7,7 @@
import HeaderCell from "../cells/HeaderCell.svelte"
import { GutterWidth, BlankRowID } from "../lib/constants"
import GutterCell from "../cells/GutterCell.svelte"
import KeyboardShortcut from "./KeyboardShortcut.svelte"
const {
rows,
@ -21,6 +22,7 @@
focusedRow,
scrollLeft,
dispatch,
contentLines,
} = getContext("grid")
$: rowCount = $rows.length
@ -85,6 +87,7 @@
selectedUser={$selectedCellMap[cellId]}
width={$stickyColumn.width}
column={$stickyColumn}
contentLines={$contentLines}
/>
{/if}
</div>
@ -103,7 +106,9 @@
<GridCell
width={$stickyColumn.width}
highlighted={$hoveredRowId === BlankRowID}
/>
>
<KeyboardShortcut padded keybind="Ctrl+Enter" />
</GridCell>
{/if}
</div>
{/if}

View File

@ -15,8 +15,22 @@
selectedRows,
} = getContext("grid")
const ignoredOriginSelectors = [
".spectrum-Modal",
"#builder-side-panel-container",
]
// Global key listener which intercepts all key events
const handleKeyDown = e => {
// Avoid processing events sourced from certain origins
if (e.target?.closest) {
for (let selector of ignoredOriginSelectors) {
if (e.target.closest(selector)) {
return
}
}
}
// If nothing selected avoid processing further key presses
if (!$focusedCellId) {
if (e.key === "Tab" || e.key?.startsWith("Arrow")) {
@ -60,11 +74,6 @@
return
}
}
// Avoid processing events sourced from modals
if (e.target?.closest?.(".spectrum-Modal")) {
return
}
e.preventDefault()
// Handle the key ourselves