More upgrades to grids to support new popovers and use popovers for options cells

This commit is contained in:
Andrew Kingston 2024-04-24 16:28:44 +01:00
parent 795991438f
commit 443be4cdab
7 changed files with 56 additions and 84 deletions

View File

@ -101,7 +101,7 @@
<style> <style>
.wrapper { .wrapper {
flex: 1 1 auto; flex: 0 0 400px;
margin: -28px -40px -40px -40px; margin: -28px -40px -40px -40px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@ -1,10 +1,5 @@
<script> <script>
import { import { CoreDatePickerPopoverContents, Icon, Helpers } from "@budibase/bbui"
CoreDatePickerPopoverContents,
Icon,
Helpers,
clickOutside,
} from "@budibase/bbui"
import { onMount } from "svelte" import { onMount } from "svelte"
import dayjs from "dayjs" import dayjs from "dayjs"
import GridPopover from "../overlays/GridPopover.svelte" import GridPopover from "../overlays/GridPopover.svelte"
@ -16,7 +11,6 @@
export let readonly = false export let readonly = false
export let api export let api
export let invertX = false export let invertX = false
export let invertY = false
export let rand export let rand
let isOpen let isOpen
@ -118,17 +112,15 @@
</div> </div>
{#if isOpen} {#if isOpen}
<GridPopover bind:open={isOpen} {anchor} {invertX} {rand}> <GridPopover open={isOpen} {anchor} {invertX} {rand} on:close={close}>
<div class="picker" use:clickOutside={close}> <CoreDatePickerPopoverContents
<CoreDatePickerPopoverContents value={parsedValue}
value={parsedValue} useKeyboardShortcuts={false}
on:change={e => (value = e.detail)} on:change={e => (value = e.detail)}
{enableTime} {enableTime}
{timeOnly} {timeOnly}
{ignoreTimezones} {ignoreTimezones}
useKeyboardShortcuts={false} />
/>
</div>
</GridPopover> </GridPopover>
{/if} {/if}
@ -155,9 +147,4 @@
line-height: 20px; line-height: 20px;
height: 20px; height: 20px;
} }
.picker {
background: var(--grid-background-alt);
border: var(--cell-border);
border-radius: 2px;
}
</style> </style>

View File

@ -1,7 +1,8 @@
<script> <script>
import { Icon, clickOutside } from "@budibase/bbui" import { Icon } from "@budibase/bbui"
import { getColor } from "../lib/utils" import { getColor } from "../lib/utils"
import { onMount } from "svelte" import { onMount } from "svelte"
import GridPopover from "../overlays/GridPopover.svelte"
export let value export let value
export let schema export let schema
@ -10,12 +11,13 @@
export let multi = false export let multi = false
export let readonly = false export let readonly = false
export let api export let api
export let invertX = false export let invertX
export let invertY = false
export let contentLines = 1 export let contentLines = 1
export let rand
let isOpen = false let isOpen = false
let focusedOptionIdx = null let focusedOptionIdx = null
let anchor
$: options = schema?.constraints?.inclusion || [] $: options = schema?.constraints?.inclusion || []
$: optionColors = schema?.optionColors || {} $: optionColors = schema?.optionColors || {}
@ -89,6 +91,7 @@
class:editable class:editable
class:open class:open
on:click|self={editable ? open : null} on:click|self={editable ? open : null}
bind:this={anchor}
> >
<div <div
class="values" class="values"
@ -115,16 +118,15 @@
<Icon name="ChevronDown" /> <Icon name="ChevronDown" />
</div> </div>
{/if} {/if}
{#if isOpen} </div>
<div
class="options" {#if isOpen}
class:invertX <GridPopover open={isOpen} {anchor} {rand} {invertX} on:close={close}>
class:invertY <div class="options" on:wheel={e => e.stopPropagation()}>
on:wheel={e => e.stopPropagation()}
use:clickOutside={close}
>
{#each options as option, idx} {#each options as option, idx}
{@const color = optionColors[option] || getOptionColor(option)} {@const color = optionColors[option] || getOptionColor(option)}
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div <div
class="option" class="option"
on:click={() => toggleOption(option)} on:click={() => toggleOption(option)}
@ -132,7 +134,9 @@
on:mouseenter={() => (focusedOptionIdx = idx)} on:mouseenter={() => (focusedOptionIdx = idx)}
> >
<div class="badge text" style="--color: {color}"> <div class="badge text" style="--color: {color}">
{option} <span>
{option}
</span>
</div> </div>
{#if values.includes(option)} {#if values.includes(option)}
<Icon name="Checkmark" color="var(--accent-color)" /> <Icon name="Checkmark" color="var(--accent-color)" />
@ -140,8 +144,8 @@
</div> </div>
{/each} {/each}
</div> </div>
{/if} </GridPopover>
</div> {/if}
<style> <style>
.container { .container {
@ -211,28 +215,14 @@
); );
} }
.options { .options {
min-width: calc(100% + 2px);
position: absolute;
top: 100%;
left: -1px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: flex-start; justify-content: flex-start;
align-items: stretch; align-items: stretch;
max-height: var(--max-cell-render-overflow); max-height: var(--max-cell-render-overflow);
overflow-y: auto; overflow-y: auto;
border: var(--cell-border); min-width: 200px;
box-shadow: 0 0 20px -4px rgba(0, 0, 0, 0.15); max-width: 400px;
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
}
.options.invertX {
left: auto;
right: 0;
}
.options.invertY {
transform: translateY(-100%);
top: 0;
} }
.option { .option {
flex: 0 0 var(--default-row-height); flex: 0 0 var(--default-row-height);
@ -247,5 +237,6 @@
.option:hover, .option:hover,
.option.focused { .option.focused {
background-color: var(--spectrum-global-color-gray-200); background-color: var(--spectrum-global-color-gray-200);
cursor: pointer;
} }
</style> </style>

View File

@ -12,6 +12,7 @@
bounds, bounds,
hoveredRowId, hoveredRowId,
menu, menu,
focusedCellAPI,
} = getContext("grid") } = getContext("grid")
export let scrollVertically = false export let scrollVertically = false
@ -35,6 +36,9 @@
e.preventDefault() e.preventDefault()
updateScroll(e.deltaX, e.deltaY, e.clientY) updateScroll(e.deltaX, e.deltaY, e.clientY)
// Close any open popovers when scrolling
$focusedCellAPI?.blur()
// If a context menu was visible, hide it // If a context menu was visible, hide it
if ($menu.visible) { if ($menu.visible) {
menu.actions.close() menu.actions.close()

View File

@ -1,40 +1,30 @@
<script> <script>
import { Popover } from "@budibase/bbui" import { Popover, clickOutside } from "@budibase/bbui"
import { getContext } from "svelte" import { createEventDispatcher } from "svelte"
export let rand export let rand
export let invertX export let invertX
export let open export let open
export let anchor export let anchor
const { rowHeight, scroll } = getContext("grid") const dispatch = createEventDispatcher()
let initialOffsetX = 0
let initialOffsetY = 0
$: updateInitialOffsets(open)
$: offsetX = initialOffsetX - $scroll.left
$: offsetY = initialOffsetY - ($scroll.top % $rowHeight)
$: style = `transform: translateX(${offsetX}px) translateY(${offsetY}px);`
$: markup = `<style>.grid-popover-container .spectrum-Popover { ${style} }</style>`
const updateInitialOffsets = open => {
if (!open) {
return
}
initialOffsetX = $scroll.left
initialOffsetY = $scroll.top % $rowHeight
}
</script> </script>
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
{@html markup}
<Popover <Popover
bind:open bind:open
{anchor} {anchor}
align={invertX ? "right" : "left"} align={invertX ? "right" : "left"}
portalTarget="#grid-{rand} .grid-popover-container" portalTarget="#grid-{rand} .grid-popover-container"
offset={0} offset={1}
> >
<slot /> <div use:clickOutside={() => dispatch("close")}>
<slot />
</div>
</Popover> </Popover>
<style>
:global(.grid-popover-container .spectrum-Popover) {
background: var(--grid-background-alt);
border: var(--cell-border);
}
</style>

View File

@ -17,6 +17,7 @@
height, height,
isDragging, isDragging,
menu, menu,
focusedCellAPI,
} = getContext("grid") } = getContext("grid")
// State for dragging bars // State for dragging bars
@ -47,10 +48,11 @@
$: barLeft = ScrollBarSize + availWidth * ($scrollLeft / $maxScrollLeft) $: barLeft = ScrollBarSize + availWidth * ($scrollLeft / $maxScrollLeft)
// Helper to close the context menu if it's open // Helper to close the context menu if it's open
const closeMenu = () => { const closePopovers = () => {
if ($menu.visible) { if ($menu.visible) {
menu.actions.close() menu.actions.close()
} }
$focusedCellAPI?.blur()
} }
const getLocation = e => { const getLocation = e => {
@ -70,7 +72,7 @@
document.addEventListener("mouseup", stopVDragging) document.addEventListener("mouseup", stopVDragging)
document.addEventListener("touchend", stopVDragging) document.addEventListener("touchend", stopVDragging)
isDraggingV = true isDraggingV = true
closeMenu() closePopovers()
} }
const moveVDragging = domDebounce(e => { const moveVDragging = domDebounce(e => {
const delta = getLocation(e).y - initialMouse const delta = getLocation(e).y - initialMouse
@ -99,7 +101,7 @@
document.addEventListener("mouseup", stopHDragging) document.addEventListener("mouseup", stopHDragging)
document.addEventListener("touchend", stopHDragging) document.addEventListener("touchend", stopHDragging)
isDraggingH = true isDraggingH = true
closeMenu() closePopovers()
} }
const moveHDragging = domDebounce(e => { const moveHDragging = domDebounce(e => {
const delta = getLocation(e).x - initialMouse const delta = getLocation(e).x - initialMouse
@ -127,7 +129,6 @@
on:mousedown={startVDragging} on:mousedown={startVDragging}
on:touchstart={startVDragging} on:touchstart={startVDragging}
class:dragging={isDraggingV} class:dragging={isDraggingV}
data-ignore-click-outside="true"
/> />
{/if} {/if}
{#if $showHScrollbar} {#if $showHScrollbar}
@ -138,7 +139,6 @@
on:mousedown={startHDragging} on:mousedown={startHDragging}
on:touchstart={startHDragging} on:touchstart={startHDragging}
class:dragging={isDraggingH} class:dragging={isDraggingH}
data-ignore-click-outside="true"
/> />
{/if} {/if}

View File

@ -87,7 +87,7 @@ export const createActions = context => {
// Check if we need to start auto-scrolling // Check if we need to start auto-scrolling
const $reorder = get(reorder) const $reorder = get(reorder)
const proximityCutoff = 140 const proximityCutoff = 140
const speedFactor = 8 const speedFactor = 16
const rightProximity = Math.max(0, $reorder.gridLeft + $reorder.width - x) const rightProximity = Math.max(0, $reorder.gridLeft + $reorder.width - x)
const leftProximity = Math.max(0, x - $reorder.gridLeft) const leftProximity = Math.max(0, x - $reorder.gridLeft)
if (rightProximity < proximityCutoff) { if (rightProximity < proximityCutoff) {