1
0
Fork 0
mirror of synced 2024-08-14 17:42:01 +12:00

Add resizable rows

This commit is contained in:
Andrew Kingston 2023-03-31 20:33:08 +01:00
parent a0299d4c7c
commit fcb8b9e9b1
19 changed files with 108 additions and 46 deletions

View file

@ -122,7 +122,7 @@
flex-direction: row; flex-direction: row;
justify-content: flex-start; justify-content: flex-start;
align-items: center; align-items: center;
height: calc(var(--cell-height) - 12px); height: calc(var(--row-height) - 12px);
padding: 0 8px; padding: 0 8px;
color: var(--spectrum-global-color-gray-800); color: var(--spectrum-global-color-gray-800);
border: 1px solid var(--spectrum-global-color-gray-300); border: 1px solid var(--spectrum-global-color-gray-300);
@ -133,7 +133,7 @@
user-select: none; user-select: none;
} }
img { img {
height: calc(var(--cell-height) - 12px); height: calc(var(--row-height) - 12px);
max-width: 64px; max-width: 64px;
} }
.dropzone { .dropzone {

View file

@ -92,7 +92,7 @@
top: -1px; top: -1px;
left: -1px; left: -1px;
width: calc(100% + 100px); width: calc(100% + 100px);
height: calc(5 * var(--cell-height) + 1px); height: calc(5 * var(--row-height) + 1px);
border: var(--cell-border); border: var(--cell-border);
box-shadow: inset 0 0 0 2px var(--spectrum-global-color-blue-400); box-shadow: inset 0 0 0 2px var(--spectrum-global-color-blue-400);
} }

View file

@ -192,7 +192,7 @@
top: 0; top: 0;
} }
.option { .option {
flex: 0 0 var(--cell-height); flex: 0 0 var(--row-height);
padding: 0 var(--cell-padding); padding: 0 var(--cell-padding);
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -202,7 +202,7 @@
background-color: var(--cell-background-hover); background-color: var(--cell-background-hover);
} }
.option:first-child { .option:first-child {
flex: 0 0 calc(var(--cell-height) - 1px); flex: 0 0 calc(var(--row-height) - 1px);
} }
.option:hover, .option:hover,
.option.focused { .option.focused {

View file

@ -316,7 +316,7 @@
} }
.result { .result {
padding: 0 var(--cell-padding); padding: 0 var(--cell-padding);
flex: 0 0 var(--cell-height); flex: 0 0 var(--row-height);
display: flex; display: flex;
gap: var(--cell-spacing); gap: var(--cell-spacing);
justify-content: space-between; justify-content: space-between;
@ -331,7 +331,7 @@
} }
.search { .search {
flex: 0 0 calc(var(--cell-height) - 1px); flex: 0 0 calc(var(--row-height) - 1px);
display: flex; display: flex;
align-items: center; align-items: center;
margin: 0 var(--cell-padding); margin: 0 var(--cell-padding);
@ -349,7 +349,7 @@
color: var(--spectrum-global-color-gray-600); color: var(--spectrum-global-color-gray-600);
font-size: 12px; font-size: 12px;
padding: var(--cell-padding); padding: var(--cell-padding);
flex: 0 0 var(--cell-height); flex: 0 0 var(--row-height);
display: flex; display: flex;
align-items: center; align-items: center;
white-space: nowrap; white-space: nowrap;

View file

@ -54,7 +54,7 @@
<style> <style>
/* Cells */ /* Cells */
.cell { .cell {
height: var(--cell-height); height: var(--row-height);
border-bottom: var(--cell-border); border-bottom: var(--cell-border);
border-right: var(--cell-border); border-right: var(--cell-border);
display: flex; display: flex;
@ -123,7 +123,7 @@
right: 0; right: 0;
background: var(--spectrum-global-color-blue-400); background: var(--spectrum-global-color-blue-400);
width: 2px; width: 2px;
height: calc(var(--cell-height) + 2px); height: calc(var(--row-height) + 2px);
} }
/* Other user email */ /* Other user email */

View file

@ -0,0 +1,58 @@
<script>
import { getContext } from "svelte"
import { ActionButton, Popover } from "@budibase/bbui"
const { rowHeight } = getContext("sheet")
const sizeOptions = [
{
label: "Small",
size: 36,
},
{
label: "Medium",
size: 54,
},
{
label: "Large",
size: 72,
},
]
let open = false
let anchor
</script>
<div bind:this={anchor}>
<ActionButton
icon="LineHeight"
quiet
size="M"
on:click={() => (open = !open)}
selected={open}
>
Row height
</ActionButton>
</div>
<Popover bind:open {anchor} align="left">
<div class="content">
{#each sizeOptions as option}
<ActionButton
quiet
selected={$rowHeight === option.size}
on:click={() => rowHeight.set(option.size)}
>
{option.label}
</ActionButton>
{/each}
</div>
</Popover>
<style>
.content {
padding: 12px;
display: flex;
align-items: center;
gap: 8px;
}
</style>

View file

@ -33,7 +33,7 @@
border-bottom: var(--cell-border); border-bottom: var(--cell-border);
position: relative; position: relative;
z-index: 2; z-index: 2;
height: var(--cell-height); height: var(--row-height);
} }
.row { .row {
display: flex; display: flex;
@ -42,7 +42,7 @@
position: absolute; position: absolute;
right: 0; right: 0;
top: 0; top: 0;
height: var(--cell-height); height: var(--row-height);
background: var(--spectrum-global-color-gray-100); background: var(--spectrum-global-color-gray-100);
display: grid; display: grid;
place-items: center; place-items: center;

View file

@ -149,7 +149,7 @@
.container { .container {
pointer-events: none; pointer-events: none;
position: absolute; position: absolute;
top: var(--cell-height); top: var(--row-height);
transform: translateY(-100%); transform: translateY(-100%);
z-index: 1; z-index: 1;
transition: transform 130ms ease-out; transition: transform 130ms ease-out;

View file

@ -40,7 +40,6 @@
export let allowEditRows = true export let allowEditRows = true
// Sheet constants // Sheet constants
const cellHeight = 36
const gutterWidth = 72 const gutterWidth = 72
const rand = Math.random() const rand = Math.random()
@ -60,7 +59,6 @@
let context = { let context = {
API: API || createAPIClient(), API: API || createAPIClient(),
rand, rand,
cellHeight,
gutterWidth, gutterWidth,
config, config,
tableId: tableIdStore, tableId: tableIdStore,
@ -81,7 +79,7 @@
context = { ...context, ...createWheelStores(context) } context = { ...context, ...createWheelStores(context) }
// Reference some stores for local use // Reference some stores for local use
const { isResizing, isReordering, ui, loaded } = context const { isResizing, isReordering, ui, loaded, rowHeight } = context
// Keep stores up to date // Keep stores up to date
$: tableIdStore.set(tableId) $: tableIdStore.set(tableId)
@ -109,7 +107,7 @@
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; --max-cell-render-height:{MaxCellRenderHeight}px;" style="--row-height:{$rowHeight}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">

View file

@ -4,14 +4,13 @@
import SheetRow from "./SheetRow.svelte" import SheetRow from "./SheetRow.svelte"
import { MaxCellRenderHeight } from "../lib/constants" import { MaxCellRenderHeight } from "../lib/constants"
const { bounds, renderedRows, visualRowCapacity, cellHeight } = const { bounds, renderedRows, visualRowCapacity, rowHeight } =
getContext("sheet") getContext("sheet")
let body let body
$: inversionIdx = $: inversionIdx =
$visualRowCapacity - Math.ceil(MaxCellRenderHeight / cellHeight) - 2 $visualRowCapacity - Math.ceil(MaxCellRenderHeight / $rowHeight) - 2
$: console.log(inversionIdx)
onMount(() => { onMount(() => {
// Observe and record the height of the body // Observe and record the height of the body

View file

@ -2,8 +2,10 @@
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" import AddRowButton from "../controls/AddRowButton.svelte"
import RowHeightButton from "../controls/RowHeightButton.svelte"
</script> </script>
<AddRowButton /> <AddRowButton />
<HideColumnsButton /> <HideColumnsButton />
<SortButton /> <SortButton />
<RowHeightButton />

View file

@ -2,7 +2,7 @@
import { getContext } from "svelte" import { getContext } from "svelte"
const { const {
cellHeight, rowHeight,
scroll, scroll,
visibleColumns, visibleColumns,
renderedColumns, renderedColumns,
@ -15,11 +15,11 @@
export let wheelInteractive = true export let wheelInteractive = true
$: hiddenWidths = calculateHiddenWidths($renderedColumns) $: hiddenWidths = calculateHiddenWidths($renderedColumns)
$: style = generateStyle($scroll, hiddenWidths) $: style = generateStyle($scroll, $rowHeight, hiddenWidths)
const generateStyle = (scroll, hiddenWidths) => { const generateStyle = (scroll, rowHeight, hiddenWidths) => {
const offsetX = scrollHorizontally ? -1 * scroll.left + hiddenWidths : 0 const offsetX = scrollHorizontally ? -1 * scroll.left + hiddenWidths : 0
const offsetY = scrollVertically ? -1 * (scroll.top % cellHeight) : 0 const offsetY = scrollVertically ? -1 * (scroll.top % rowHeight) : 0
return `transform: translate3d(${offsetX}px, ${offsetY}px, 0);` return `transform: translate3d(${offsetX}px, ${offsetY}px, 0);`
} }

View file

@ -51,8 +51,8 @@
.resize-slider { .resize-slider {
position: absolute; position: absolute;
top: 0; top: 0;
z-index: 1; z-index: 2;
height: var(--cell-height); height: var(--row-height);
opacity: 0; opacity: 0;
padding: 0 8px; padding: 0 8px;
transform: translateX(-50%); transform: translateX(-50%);
@ -64,7 +64,7 @@
opacity: 1; opacity: 1;
} }
.resize-slider.sticky { .resize-slider.sticky {
z-index: 2; z-index: 3;
} }
.resize-indicator { .resize-indicator {
margin-left: -1px; margin-left: -1px;

View file

@ -5,7 +5,7 @@
const { const {
scroll, scroll,
bounds, bounds,
cellHeight, rowHeight,
contentHeight, contentHeight,
maxScrollTop, maxScrollTop,
contentWidth, contentWidth,
@ -35,7 +35,7 @@
$: renderHeight = height - 2 * barOffset $: renderHeight = height - 2 * barOffset
$: barHeight = Math.max(50, (height / $contentHeight) * renderHeight) $: barHeight = Math.max(50, (height / $contentHeight) * renderHeight)
$: availHeight = renderHeight - barHeight $: availHeight = renderHeight - barHeight
$: barTop = barOffset + cellHeight + availHeight * (scrollTop / $maxScrollTop) $: barTop = barOffset + $rowHeight + availHeight * (scrollTop / $maxScrollTop)
// Calculate H scrollbar size and offset // Calculate H scrollbar size and offset
$: renderWidth = $screenWidth - 2 * barOffset $: renderWidth = $screenWidth - 2 * barOffset

View file

@ -6,7 +6,7 @@ export const createMaxScrollStores = context => {
visibleColumns, visibleColumns,
stickyColumn, stickyColumn,
bounds, bounds,
cellHeight, rowHeight,
scroll, scroll,
selectedCellRow, selectedCellRow,
selectedCellId, selectedCellId,
@ -23,8 +23,8 @@ export const createMaxScrollStores = context => {
const height = derived(bounds, $bounds => $bounds.height, 0) const height = derived(bounds, $bounds => $bounds.height, 0)
const width = derived(bounds, $bounds => $bounds.width, 0) const width = derived(bounds, $bounds => $bounds.width, 0)
const contentHeight = derived( const contentHeight = derived(
rows, [rows, rowHeight],
$rows => $rows.length * cellHeight + padding, ([$rows, $rowHeight]) => $rows.length * $rowHeight + padding,
0 0
) )
const maxScrollTop = derived( const maxScrollTop = derived(
@ -94,12 +94,13 @@ export const createMaxScrollStores = context => {
} }
const $scroll = get(scroll) const $scroll = get(scroll)
const $bounds = get(bounds) const $bounds = get(bounds)
const $rowHeight = get(rowHeight)
const scrollBarOffset = 16 const scrollBarOffset = 16
// Ensure row is not below bottom of screen // Ensure row is not below bottom of screen
const rowYPos = row.__idx * cellHeight const rowYPos = row.__idx * $rowHeight
const bottomCutoff = const bottomCutoff =
$scroll.top + $bounds.height - cellHeight - scrollBarOffset $scroll.top + $bounds.height - $rowHeight - scrollBarOffset
let delta = rowYPos - bottomCutoff let delta = rowYPos - bottomCutoff
if (delta > 0) { if (delta > 0) {
scroll.update(state => ({ scroll.update(state => ({

View file

@ -1,7 +1,7 @@
import { writable, get } from "svelte/store" import { writable, get } from "svelte/store"
export const createMenuStores = context => { export const createMenuStores = context => {
const { bounds, selectedCellId, stickyColumn, cellHeight } = context const { bounds, selectedCellId, stickyColumn, rowHeight } = context
const menu = writable({ const menu = writable({
x: 0, x: 0,
y: 0, y: 0,
@ -12,11 +12,12 @@ export const createMenuStores = context => {
const open = (cellId, e) => { const open = (cellId, e) => {
const $bounds = get(bounds) const $bounds = get(bounds)
const $stickyColumn = get(stickyColumn) const $stickyColumn = get(stickyColumn)
const $rowHeight = get(rowHeight)
e.preventDefault() e.preventDefault()
selectedCellId.set(cellId) selectedCellId.set(cellId)
menu.set({ menu.set({
left: e.clientX - $bounds.left + 44 + ($stickyColumn?.width || 0), left: e.clientX - $bounds.left + 44 + ($stickyColumn?.width || 0),
top: e.clientY - $bounds.top + cellHeight + 4, top: e.clientY - $bounds.top + $rowHeight + 4,
visible: true, visible: true,
}) })
} }

View file

@ -6,6 +6,7 @@ export const createUIStores = context => {
const selectedRows = writable({}) const selectedRows = writable({})
const hoveredRowId = writable(null) const hoveredRowId = writable(null)
const selectedCellAPI = writable(null) const selectedCellAPI = writable(null)
const rowHeight = writable(36)
// Derive the row that contains the selected cell. // Derive the row that contains the selected cell.
const selectedCellRow = derived( const selectedCellRow = derived(
@ -92,6 +93,7 @@ export const createUIStores = context => {
hoveredRowId, hoveredRowId,
selectedCellRow, selectedCellRow,
selectedCellAPI, selectedCellAPI,
rowHeight,
ui: { ui: {
actions: { actions: {
blur, blur,

View file

@ -1,7 +1,7 @@
import { derived, get } from "svelte/store" import { derived, get } from "svelte/store"
export const createViewportStores = context => { export const createViewportStores = context => {
const { cellHeight, visibleColumns, rows, scroll, bounds } = context const { rowHeight, visibleColumns, rows, scroll, bounds } = context
const scrollTop = derived(scroll, $scroll => $scroll.top, 0) const scrollTop = derived(scroll, $scroll => $scroll.top, 0)
const scrollLeft = derived(scroll, $scroll => $scroll.left, 0) const scrollLeft = derived(scroll, $scroll => $scroll.left, 0)
@ -13,16 +13,16 @@ export const createViewportStores = context => {
// Split into multiple stores containing primitives to optimise invalidation // Split into multiple stores containing primitives to optimise invalidation
// as much as possible // as much as possible
const scrolledRowCount = derived( const scrolledRowCount = derived(
scrollTop, [scrollTop, rowHeight],
$scrollTop => { ([$scrollTop, $rowHeight]) => {
return Math.floor($scrollTop / cellHeight) return Math.floor($scrollTop / $rowHeight)
}, },
0 0
) )
const visualRowCapacity = derived( const visualRowCapacity = derived(
height, [height, rowHeight],
$height => { ([$height, $rowHeight]) => {
return Math.ceil($height / cellHeight) + 1 return Math.ceil($height / $rowHeight) + 1
}, },
0 0
) )

View file

@ -9,7 +9,7 @@ export const createWheelStores = context => {
renderedRows, renderedRows,
bounds, bounds,
scroll, scroll,
cellHeight, rowHeight,
} = context } = context
// Handles a wheel even and updates the scroll offsets // Handles a wheel even and updates the scroll offsets
@ -22,6 +22,7 @@ export const createWheelStores = context => {
} }
const debouncedHandleWheel = domDebounce((deltaX, deltaY, clientY) => { const debouncedHandleWheel = domDebounce((deltaX, deltaY, clientY) => {
const { top, left } = get(scroll) const { top, left } = get(scroll)
const $rowHeight = get(rowHeight)
// Calculate new scroll top // Calculate new scroll top
let newScrollTop = top + deltaY let newScrollTop = top + deltaY
@ -38,8 +39,8 @@ export const createWheelStores = context => {
}) })
// Hover row under cursor // Hover row under cursor
const y = clientY - get(bounds).top + (newScrollTop % cellHeight) const y = clientY - get(bounds).top + (newScrollTop % $rowHeight)
const hoveredRow = get(renderedRows)[Math.floor(y / cellHeight)] const hoveredRow = get(renderedRows)[Math.floor(y / $rowHeight)]
hoveredRowId.set(hoveredRow?._id) hoveredRowId.set(hoveredRow?._id)
}) })