Add resizable rows
This commit is contained in:
parent
a0299d4c7c
commit
fcb8b9e9b1
|
@ -122,7 +122,7 @@
|
|||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
height: calc(var(--cell-height) - 12px);
|
||||
height: calc(var(--row-height) - 12px);
|
||||
padding: 0 8px;
|
||||
color: var(--spectrum-global-color-gray-800);
|
||||
border: 1px solid var(--spectrum-global-color-gray-300);
|
||||
|
@ -133,7 +133,7 @@
|
|||
user-select: none;
|
||||
}
|
||||
img {
|
||||
height: calc(var(--cell-height) - 12px);
|
||||
height: calc(var(--row-height) - 12px);
|
||||
max-width: 64px;
|
||||
}
|
||||
.dropzone {
|
||||
|
|
|
@ -92,7 +92,7 @@
|
|||
top: -1px;
|
||||
left: -1px;
|
||||
width: calc(100% + 100px);
|
||||
height: calc(5 * var(--cell-height) + 1px);
|
||||
height: calc(5 * var(--row-height) + 1px);
|
||||
border: var(--cell-border);
|
||||
box-shadow: inset 0 0 0 2px var(--spectrum-global-color-blue-400);
|
||||
}
|
||||
|
|
|
@ -192,7 +192,7 @@
|
|||
top: 0;
|
||||
}
|
||||
.option {
|
||||
flex: 0 0 var(--cell-height);
|
||||
flex: 0 0 var(--row-height);
|
||||
padding: 0 var(--cell-padding);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
@ -202,7 +202,7 @@
|
|||
background-color: var(--cell-background-hover);
|
||||
}
|
||||
.option:first-child {
|
||||
flex: 0 0 calc(var(--cell-height) - 1px);
|
||||
flex: 0 0 calc(var(--row-height) - 1px);
|
||||
}
|
||||
.option:hover,
|
||||
.option.focused {
|
||||
|
|
|
@ -316,7 +316,7 @@
|
|||
}
|
||||
.result {
|
||||
padding: 0 var(--cell-padding);
|
||||
flex: 0 0 var(--cell-height);
|
||||
flex: 0 0 var(--row-height);
|
||||
display: flex;
|
||||
gap: var(--cell-spacing);
|
||||
justify-content: space-between;
|
||||
|
@ -331,7 +331,7 @@
|
|||
}
|
||||
|
||||
.search {
|
||||
flex: 0 0 calc(var(--cell-height) - 1px);
|
||||
flex: 0 0 calc(var(--row-height) - 1px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0 var(--cell-padding);
|
||||
|
@ -349,7 +349,7 @@
|
|||
color: var(--spectrum-global-color-gray-600);
|
||||
font-size: 12px;
|
||||
padding: var(--cell-padding);
|
||||
flex: 0 0 var(--cell-height);
|
||||
flex: 0 0 var(--row-height);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
<style>
|
||||
/* Cells */
|
||||
.cell {
|
||||
height: var(--cell-height);
|
||||
height: var(--row-height);
|
||||
border-bottom: var(--cell-border);
|
||||
border-right: var(--cell-border);
|
||||
display: flex;
|
||||
|
@ -123,7 +123,7 @@
|
|||
right: 0;
|
||||
background: var(--spectrum-global-color-blue-400);
|
||||
width: 2px;
|
||||
height: calc(var(--cell-height) + 2px);
|
||||
height: calc(var(--row-height) + 2px);
|
||||
}
|
||||
|
||||
/* Other user email */
|
||||
|
|
|
@ -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>
|
|
@ -33,7 +33,7 @@
|
|||
border-bottom: var(--cell-border);
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
height: var(--cell-height);
|
||||
height: var(--row-height);
|
||||
}
|
||||
.row {
|
||||
display: flex;
|
||||
|
@ -42,7 +42,7 @@
|
|||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
height: var(--cell-height);
|
||||
height: var(--row-height);
|
||||
background: var(--spectrum-global-color-gray-100);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
|
|
|
@ -149,7 +149,7 @@
|
|||
.container {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: var(--cell-height);
|
||||
top: var(--row-height);
|
||||
transform: translateY(-100%);
|
||||
z-index: 1;
|
||||
transition: transform 130ms ease-out;
|
||||
|
|
|
@ -40,7 +40,6 @@
|
|||
export let allowEditRows = true
|
||||
|
||||
// Sheet constants
|
||||
const cellHeight = 36
|
||||
const gutterWidth = 72
|
||||
const rand = Math.random()
|
||||
|
||||
|
@ -60,7 +59,6 @@
|
|||
let context = {
|
||||
API: API || createAPIClient(),
|
||||
rand,
|
||||
cellHeight,
|
||||
gutterWidth,
|
||||
config,
|
||||
tableId: tableIdStore,
|
||||
|
@ -81,7 +79,7 @@
|
|||
context = { ...context, ...createWheelStores(context) }
|
||||
|
||||
// Reference some stores for local use
|
||||
const { isResizing, isReordering, ui, loaded } = context
|
||||
const { isResizing, isReordering, ui, loaded, rowHeight } = context
|
||||
|
||||
// Keep stores up to date
|
||||
$: tableIdStore.set(tableId)
|
||||
|
@ -109,7 +107,7 @@
|
|||
id="sheet-{rand}"
|
||||
class:is-resizing={$isResizing}
|
||||
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-left">
|
||||
|
|
|
@ -4,14 +4,13 @@
|
|||
import SheetRow from "./SheetRow.svelte"
|
||||
import { MaxCellRenderHeight } from "../lib/constants"
|
||||
|
||||
const { bounds, renderedRows, visualRowCapacity, cellHeight } =
|
||||
const { bounds, renderedRows, visualRowCapacity, rowHeight } =
|
||||
getContext("sheet")
|
||||
|
||||
let body
|
||||
|
||||
$: inversionIdx =
|
||||
$visualRowCapacity - Math.ceil(MaxCellRenderHeight / cellHeight) - 2
|
||||
$: console.log(inversionIdx)
|
||||
$visualRowCapacity - Math.ceil(MaxCellRenderHeight / $rowHeight) - 2
|
||||
|
||||
onMount(() => {
|
||||
// Observe and record the height of the body
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
import SortButton from "../controls/SortButton.svelte"
|
||||
import HideColumnsButton from "../controls/HideColumnsButton.svelte"
|
||||
import AddRowButton from "../controls/AddRowButton.svelte"
|
||||
import RowHeightButton from "../controls/RowHeightButton.svelte"
|
||||
</script>
|
||||
|
||||
<AddRowButton />
|
||||
<HideColumnsButton />
|
||||
<SortButton />
|
||||
<RowHeightButton />
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { getContext } from "svelte"
|
||||
|
||||
const {
|
||||
cellHeight,
|
||||
rowHeight,
|
||||
scroll,
|
||||
visibleColumns,
|
||||
renderedColumns,
|
||||
|
@ -15,11 +15,11 @@
|
|||
export let wheelInteractive = true
|
||||
|
||||
$: 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 offsetY = scrollVertically ? -1 * (scroll.top % cellHeight) : 0
|
||||
const offsetY = scrollVertically ? -1 * (scroll.top % rowHeight) : 0
|
||||
return `transform: translate3d(${offsetX}px, ${offsetY}px, 0);`
|
||||
}
|
||||
|
||||
|
|
|
@ -51,8 +51,8 @@
|
|||
.resize-slider {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
height: var(--cell-height);
|
||||
z-index: 2;
|
||||
height: var(--row-height);
|
||||
opacity: 0;
|
||||
padding: 0 8px;
|
||||
transform: translateX(-50%);
|
||||
|
@ -64,7 +64,7 @@
|
|||
opacity: 1;
|
||||
}
|
||||
.resize-slider.sticky {
|
||||
z-index: 2;
|
||||
z-index: 3;
|
||||
}
|
||||
.resize-indicator {
|
||||
margin-left: -1px;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
const {
|
||||
scroll,
|
||||
bounds,
|
||||
cellHeight,
|
||||
rowHeight,
|
||||
contentHeight,
|
||||
maxScrollTop,
|
||||
contentWidth,
|
||||
|
@ -35,7 +35,7 @@
|
|||
$: renderHeight = height - 2 * barOffset
|
||||
$: barHeight = Math.max(50, (height / $contentHeight) * renderHeight)
|
||||
$: availHeight = renderHeight - barHeight
|
||||
$: barTop = barOffset + cellHeight + availHeight * (scrollTop / $maxScrollTop)
|
||||
$: barTop = barOffset + $rowHeight + availHeight * (scrollTop / $maxScrollTop)
|
||||
|
||||
// Calculate H scrollbar size and offset
|
||||
$: renderWidth = $screenWidth - 2 * barOffset
|
||||
|
|
|
@ -6,7 +6,7 @@ export const createMaxScrollStores = context => {
|
|||
visibleColumns,
|
||||
stickyColumn,
|
||||
bounds,
|
||||
cellHeight,
|
||||
rowHeight,
|
||||
scroll,
|
||||
selectedCellRow,
|
||||
selectedCellId,
|
||||
|
@ -23,8 +23,8 @@ export const createMaxScrollStores = context => {
|
|||
const height = derived(bounds, $bounds => $bounds.height, 0)
|
||||
const width = derived(bounds, $bounds => $bounds.width, 0)
|
||||
const contentHeight = derived(
|
||||
rows,
|
||||
$rows => $rows.length * cellHeight + padding,
|
||||
[rows, rowHeight],
|
||||
([$rows, $rowHeight]) => $rows.length * $rowHeight + padding,
|
||||
0
|
||||
)
|
||||
const maxScrollTop = derived(
|
||||
|
@ -94,12 +94,13 @@ export const createMaxScrollStores = context => {
|
|||
}
|
||||
const $scroll = get(scroll)
|
||||
const $bounds = get(bounds)
|
||||
const $rowHeight = get(rowHeight)
|
||||
const scrollBarOffset = 16
|
||||
|
||||
// Ensure row is not below bottom of screen
|
||||
const rowYPos = row.__idx * cellHeight
|
||||
const rowYPos = row.__idx * $rowHeight
|
||||
const bottomCutoff =
|
||||
$scroll.top + $bounds.height - cellHeight - scrollBarOffset
|
||||
$scroll.top + $bounds.height - $rowHeight - scrollBarOffset
|
||||
let delta = rowYPos - bottomCutoff
|
||||
if (delta > 0) {
|
||||
scroll.update(state => ({
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { writable, get } from "svelte/store"
|
||||
|
||||
export const createMenuStores = context => {
|
||||
const { bounds, selectedCellId, stickyColumn, cellHeight } = context
|
||||
const { bounds, selectedCellId, stickyColumn, rowHeight } = context
|
||||
const menu = writable({
|
||||
x: 0,
|
||||
y: 0,
|
||||
|
@ -12,11 +12,12 @@ export const createMenuStores = context => {
|
|||
const open = (cellId, e) => {
|
||||
const $bounds = get(bounds)
|
||||
const $stickyColumn = get(stickyColumn)
|
||||
const $rowHeight = get(rowHeight)
|
||||
e.preventDefault()
|
||||
selectedCellId.set(cellId)
|
||||
menu.set({
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ export const createUIStores = context => {
|
|||
const selectedRows = writable({})
|
||||
const hoveredRowId = writable(null)
|
||||
const selectedCellAPI = writable(null)
|
||||
const rowHeight = writable(36)
|
||||
|
||||
// Derive the row that contains the selected cell.
|
||||
const selectedCellRow = derived(
|
||||
|
@ -92,6 +93,7 @@ export const createUIStores = context => {
|
|||
hoveredRowId,
|
||||
selectedCellRow,
|
||||
selectedCellAPI,
|
||||
rowHeight,
|
||||
ui: {
|
||||
actions: {
|
||||
blur,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { derived, get } from "svelte/store"
|
||||
|
||||
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 scrollLeft = derived(scroll, $scroll => $scroll.left, 0)
|
||||
|
||||
|
@ -13,16 +13,16 @@ export const createViewportStores = context => {
|
|||
// Split into multiple stores containing primitives to optimise invalidation
|
||||
// as much as possible
|
||||
const scrolledRowCount = derived(
|
||||
scrollTop,
|
||||
$scrollTop => {
|
||||
return Math.floor($scrollTop / cellHeight)
|
||||
[scrollTop, rowHeight],
|
||||
([$scrollTop, $rowHeight]) => {
|
||||
return Math.floor($scrollTop / $rowHeight)
|
||||
},
|
||||
0
|
||||
)
|
||||
const visualRowCapacity = derived(
|
||||
height,
|
||||
$height => {
|
||||
return Math.ceil($height / cellHeight) + 1
|
||||
[height, rowHeight],
|
||||
([$height, $rowHeight]) => {
|
||||
return Math.ceil($height / $rowHeight) + 1
|
||||
},
|
||||
0
|
||||
)
|
||||
|
|
|
@ -9,7 +9,7 @@ export const createWheelStores = context => {
|
|||
renderedRows,
|
||||
bounds,
|
||||
scroll,
|
||||
cellHeight,
|
||||
rowHeight,
|
||||
} = context
|
||||
|
||||
// Handles a wheel even and updates the scroll offsets
|
||||
|
@ -22,6 +22,7 @@ export const createWheelStores = context => {
|
|||
}
|
||||
const debouncedHandleWheel = domDebounce((deltaX, deltaY, clientY) => {
|
||||
const { top, left } = get(scroll)
|
||||
const $rowHeight = get(rowHeight)
|
||||
|
||||
// Calculate new scroll top
|
||||
let newScrollTop = top + deltaY
|
||||
|
@ -38,8 +39,8 @@ export const createWheelStores = context => {
|
|||
})
|
||||
|
||||
// Hover row under cursor
|
||||
const y = clientY - get(bounds).top + (newScrollTop % cellHeight)
|
||||
const hoveredRow = get(renderedRows)[Math.floor(y / cellHeight)]
|
||||
const y = clientY - get(bounds).top + (newScrollTop % $rowHeight)
|
||||
const hoveredRow = get(renderedRows)[Math.floor(y / $rowHeight)]
|
||||
hoveredRowId.set(hoveredRow?._id)
|
||||
})
|
||||
|
||||
|
|
Loading…
Reference in New Issue