More upgrades to grids to support new popovers and use popovers for options cells
This commit is contained in:
parent
795991438f
commit
443be4cdab
|
@ -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;
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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}
|
||||||
|
</div>
|
||||||
|
|
||||||
{#if isOpen}
|
{#if isOpen}
|
||||||
<div
|
<GridPopover open={isOpen} {anchor} {rand} {invertX} on:close={close}>
|
||||||
class="options"
|
<div class="options" on:wheel={e => e.stopPropagation()}>
|
||||||
class:invertX
|
|
||||||
class:invertY
|
|
||||||
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}">
|
||||||
|
<span>
|
||||||
{option}
|
{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>
|
||||||
|
</GridPopover>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
|
||||||
|
|
||||||
<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>
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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}
|
||||||
>
|
>
|
||||||
|
<div use:clickOutside={() => dispatch("close")}>
|
||||||
<slot />
|
<slot />
|
||||||
|
</div>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
:global(.grid-popover-container .spectrum-Popover) {
|
||||||
|
background: var(--grid-background-alt);
|
||||||
|
border: var(--cell-border);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -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}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue