feat: improve inputs handling for large list on apps

This commit is contained in:
Ruben Fiszel
2023-11-01 11:30:35 +01:00
parent d97e99edb8
commit a450ac5301
10 changed files with 123 additions and 108 deletions
@@ -423,10 +423,29 @@
{#each actionButtons as actionButton, actionIndex (actionButton?.id)}
<!-- svelte-ignore a11y-no-static-element-interactions -->
<RowWrapper
bind:inputs
value={row.original}
index={rowIndex}
onInputsChange={() => {
on:set={(e) => {
const { id, value } = e.detail
if (!inputs[id]) {
inputs[id] = { [rowIndex]: value }
} else {
inputs[id] = { ...inputs[id], [rowIndex]: value }
}
outputs?.inputs.set(inputs, true)
}}
on:remove={(e) => {
const id = e.detail
if (inputs?.[id] == undefined) {
return
}
if (rowIndex == 0) {
delete inputs[id]
inputs = { ...inputs }
} else {
delete inputs[id][rowIndex]
inputs[id] = { ...inputs[id] }
}
outputs?.inputs.set(inputs, true)
}}
>
@@ -67,6 +67,7 @@
// }
const num = isNaN(+values[0]) ? null : +values[0]
outputs?.result.set(num)
if (iterContext && listInputs) {
listInputs.set(id, num)
}
@@ -22,7 +22,7 @@
export let render: boolean
export let initializing: boolean | undefined
const { app, focusedGrid, selectedComponent, worldStore, connectingInput, allIdsInPath } =
const { app, focusedGrid, selectedComponent, worldStore, connectingInput, allIdsInPath, mode } =
getContext<AppViewerContext>('AppViewerContext')
let page = 0
@@ -69,19 +69,17 @@
initialData: Array<any> | undefined = [],
page: number = 0
) {
const sanitizedData = Array.isArray(initialData) ? initialData : []
const l = initialData ? initialData.length : 0
if (mode === 'auto') {
const pageSize: number = configuration.auto.pageSize ?? 0
const data = sanitizedData?.slice(0 + page * pageSize, pageSize + page * pageSize) ?? []
const shouldDisplayPagination = pageSize < sanitizedData?.length ?? false
const total = Math.ceil(sanitizedData.length / pageSize ?? 0)
const shouldDisplayPagination = pageSize < l ?? false
const total = Math.ceil(l / pageSize ?? 0)
return {
data,
shouldDisplayPagination,
indexOffset: page * pageSize,
disableNext: pageSize > 0 && (page + 1) * pageSize >= sanitizedData?.length,
maxIndex: (page + 1) * pageSize - 1,
disableNext: pageSize > 0 && (page + 1) * pageSize >= l,
total: total
}
} else {
@@ -90,8 +88,8 @@
return {
shouldDisplayPagination: true,
data: sanitizedData ?? [],
indexOffset: 0,
maxIndex: l,
disableNext: page + 1 >= pageCount,
total: total
}
@@ -139,43 +137,61 @@
bind:loading
>
<div
class={twMerge('flex flex-col divide-y h-full', css?.container?.class, 'wm-list')}
class={twMerge('w-full h-full', css?.container?.class, 'wm-list')}
style={css?.container?.style}
>
<div
class="w-full flex flex-wrap {$allIdsInPath.includes(id)
class="w-full h-full shrink flex flex-wrap {$allIdsInPath.includes(id) && $mode == 'dnd'
? 'overflow-visible'
: 'overflow-auto'} {isCard
? 'h-full gap-2'
: resolvedConfig?.displayBorders
? 'divide-y max-h-full'
: 'max-h-full'}"
: 'overflow-auto'} {isCard ? 'gap-2' : resolvedConfig?.displayBorders ? 'divide-y' : ''}"
>
{#if $app.subgrids?.[`${id}-0`]}
{#if Array.isArray(pagination.data) && pagination.data.length > 0}
{#each pagination?.data ?? [] as value, index}
{#if Array.isArray(result) && result.length > 0}
{#each result ?? [] as value, index (index)}
{@const inRange = index <= pagination.maxIndex && index >= pagination.indexOffset}
<div
style={`${
isCard
? `min-width: ${resolvedConfig.width?.configuration?.card?.minWidthPx}px; `
: ''
} max-height: ${resolvedConfig.heightPx}px;`}
class="{$allIdsInPath.includes(id) ? 'overflow-visible' : 'overflow-auto'} {!isCard
? 'w-full'
: resolvedConfig?.displayBorders
? 'border'
: ''}"
style={inRange
? `${
isCard
? `min-width: ${resolvedConfig.width?.configuration?.card?.minWidthPx}px; `
: ''
} max-height: ${resolvedConfig.heightPx}px;`
: ''}
class={inRange
? `${$allIdsInPath.includes(id) ? 'overflow-visible' : 'overflow-auto'} ${
!isCard ? 'w-full' : resolvedConfig?.displayBorders ? 'border' : ''
}`
: 'h-0 float overflow-hidden invisible absolute'}
>
<ListWrapper
onInputsChange={() => {
on:set={(e) => {
const { id, value } = e.detail
if (!inputs[id]) {
inputs[id] = { [index]: value }
} else {
inputs[id] = { ...inputs[id], [index]: value }
}
outputs?.inputs.set(inputs, true)
}}
on:remove={(e) => {
const id = e.detail
if (inputs?.[id] == undefined) {
return
}
if (index == 0) {
delete inputs[id]
inputs = { ...inputs }
} else {
delete inputs[id][index]
inputs[id] = { ...inputs[id] }
}
outputs?.inputs.set(inputs, true)
}}
bind:inputs
{value}
index={index + pagination.indexOffset}
{index}
>
<SubGridEditor
visible={render}
visible={render && inRange}
{id}
subGridId={`${id}-0`}
containerHeight={resolvedConfig.heightPx}
@@ -190,7 +206,7 @@
</div>
{/each}
{:else}
<ListWrapper onInputsChange={() => {}} disabled value={undefined} index={0}>
<ListWrapper disabled value={undefined} index={0}>
<SubGridEditor visible={false} {id} subGridId={`${id}-0`} />
</ListWrapper>
{#if !Array.isArray(result)}
@@ -200,7 +216,9 @@
{/if}
</div>
{#if pagination.shouldDisplayPagination}
<div class="bg-surface-secondary h-8 flex flex-row gap-1 p-1 items-center wm-list-pagination">
<div
class="bg-surface-secondary z-20 h-8 flex flex-row gap-1 p-1 items-center wm-list-pagination absolute bottom-0 w-full"
>
<Button
size="xs2"
variant="border"
@@ -1,37 +1,24 @@
<script lang="ts">
import { setContext } from 'svelte'
import { createEventDispatcher, setContext } from 'svelte'
import type { ListInputs, ListContext } from '../../types'
import { writable } from 'svelte/store'
export let index: number
export let value: any
export let disabled = false
export let inputs: Record<string, Record<number, any>> = {}
export let onInputsChange: () => void
const ctx = writable({ index, value, disabled })
const dispatch = createEventDispatcher()
$: $ctx = { index, value, disabled }
setContext<ListContext>('ListWrapperContext', ctx)
setContext<ListInputs>('ListInputs', {
set: (id: string, value: any) => {
if (!inputs[id]) {
inputs[id] = { [index]: value }
} else {
inputs[id][index] = value
}
onInputsChange()
dispatch('set', { id, value })
},
remove(id) {
if (inputs?.[id] == undefined) {
return
}
if (index == 0) {
delete inputs[id]
} else {
delete inputs[id][index]
}
inputs = inputs
dispatch('remove', id)
}
})
</script>
@@ -1,39 +1,24 @@
<script lang="ts">
import { setContext } from 'svelte'
import { createEventDispatcher, setContext } from 'svelte'
import type { ListInputs, ListContext } from '../../types'
import { writable } from 'svelte/store'
export let index: number
export let value: any
export let disabled = false
export let inputs: Record<string, Record<number, any>> = {}
export let onInputsChange: () => void
const ctx = writable({ index, value, disabled })
$: $ctx = { index, value, disabled }
const dispatch = createEventDispatcher()
setContext<ListContext>('RowWrapperContext', ctx)
setContext<ListInputs>('RowInputs', {
set: (id: string, value: any) => {
if (!inputs[id]) {
inputs[id] = { [index]: value }
} else {
inputs[id][index] = value
}
inputs = inputs
onInputsChange()
dispatch('set', { id, value })
},
remove(id) {
if (inputs?.[id] == undefined) {
return
}
if (index == 0) {
delete inputs[id]
} else {
delete inputs[id][index]
}
inputs = inputs
onInputsChange()
dispatch('remove', id)
}
})
</script>
@@ -103,7 +103,6 @@
let:dataItem
rowHeight={36}
cols={columnConfiguration}
fastStart={true}
gap={[4, 2]}
>
<ComponentWrapper
@@ -76,7 +76,7 @@
>
<div
class={twMerge(
$allIdsInPath.includes(id) ? 'overflow-visible' : 'overflow-auto',
$allIdsInPath.includes(id) && $mode == 'dnd' ? 'overflow-visible' : 'overflow-auto',
noYPadding ? '' : 'py-2',
classes ?? '',
noPadding ? 'px-0' : 'px-2'
@@ -106,7 +106,6 @@
let:dataItem
rowHeight={36}
cols={columnConfiguration}
fastStart={true}
gap={[4, 2]}
scroller={container}
parentWidth={$parentWidth - 17}
-1
View File
@@ -169,7 +169,6 @@ export function settableOutput<T>(state: Writable<number>, previousValue: T): Ou
} else {
value = x
}
subscribers.forEach((x) => x.next(value!))
}
}
@@ -15,7 +15,6 @@
export let rowHeight: number
export let cols: [number, number][]
export let gap = [10, 10]
export let fastStart = false
export let throttleUpdate = 100
export let throttleResize = 100
export let selectedIds: string[] | undefined
@@ -171,8 +170,8 @@
</script>
<div class="svlt-grid-container" style="height: {containerHeight}px" bind:this={container}>
{#if xPerPx || !fastStart}
{#each sortedItems as item (item.id)}
{#each sortedItems as item (item.id)}
{#if item[getComputedCols] != undefined}
<MoveResize
on:initmove={handleInitMove}
on:move={handleMove}
@@ -183,9 +182,10 @@
id={item.id}
{xPerPx}
{yPerPx}
width={Math.min(getComputedCols, item[getComputedCols] && item[getComputedCols].w) *
xPerPx -
gapX * 2}
width={xPerPx == 0
? 0
: Math.min(getComputedCols, item[getComputedCols] && item[getComputedCols].w) * xPerPx -
gapX * 2}
height={(item[getComputedCols] && item[getComputedCols].h) * yPerPx - gapY * 2}
top={(item[getComputedCols] && item[getComputedCols].y) * yPerPx + gapY}
left={(item[getComputedCols] && item[getComputedCols].x) * xPerPx + gapX}
@@ -201,14 +201,8 @@
<slot dataItem={item} item={item[getComputedCols]} />
{/if}
</MoveResize>
{/each}
{:else if xPerPx === 0}
{#each sortedItems as item (item.id)}
<div class="w-0 h-0 overflow-hidden invisible">
<slot dataItem={item} item={item[getComputedCols]} />
</div>
{/each}
{/if}
{/if}
{/each}
</div>
<style>
@@ -1,6 +1,6 @@
<script lang="ts">
import { createEventDispatcher, getContext, onMount } from 'svelte'
import type { AppEditorContext } from '../types'
import type { AppEditorContext, AppViewerContext } from '../types'
import { writable } from 'svelte/store'
const dispatch = createEventDispatcher()
@@ -28,6 +28,8 @@
export let shadow: { x: number; y: number; w: number; h: number } | undefined = undefined
const ctx = getContext<AppEditorContext>('AppEditorContext')
const { mode } = getContext<AppViewerContext>('AppViewerContext')
const scale = ctx ? ctx.scale : writable(100)
const divId = `component-${id}`
@@ -50,7 +52,7 @@
let anima
onMount(() => {
if (ctx) {
if (ctx && $mode == 'dnd') {
ctx.dndItem.update((x) => {
x[id] = (moveX, moveY, topY) => {
ctx.componentActive.set(true)
@@ -204,16 +206,18 @@
}
const update = () => {
const boundX = capturePos.x + cordDiff.x
const _newScrollTop = (scrollElement?.scrollTop ?? 0) - (_scrollTop ?? 0)
const boundY = capturePos.y + (cordDiff.y + _newScrollTop)
if (xPerPx != 0) {
const boundX = capturePos.x + cordDiff.x
const _newScrollTop = (scrollElement?.scrollTop ?? 0) - (_scrollTop ?? 0)
const boundY = capturePos.y + (cordDiff.y + _newScrollTop)
let gridX = Math.round(boundX / xPerPx)
let gridY = Math.round(boundY / yPerPx)
let gridX = Math.round(boundX / xPerPx)
let gridY = Math.round(boundY / yPerPx)
if (shadow) {
shadow.x = Math.max(Math.min(gridX, cols - shadow.w), 0)
shadow.y = Math.max(gridY, 0)
if (shadow) {
shadow.x = Math.max(Math.min(gridX, cols - shadow.w), 0)
shadow.y = Math.max(gridY, 0)
}
}
}
@@ -321,9 +325,11 @@
// Limit bound
newSize.width = Math.min(newSize.width, maxWidth * xPerPx - gapX * 2)
// Limit col & row
shadow.w = Math.round(Math.max((newSize.width + gapX * 2) / xPerPx, 1))
shadow.h = Math.round(Math.max((newSize.height + gapY * 2) / yPerPx, 1))
if (xPerPx) {
// Limit col & row
shadow.w = Math.round(Math.max((newSize.width + gapX * 2) / xPerPx, 1))
shadow.h = Math.round(Math.max((newSize.height + gapY * 2) / yPerPx, 1))
}
repaint(false, false)
}
@@ -346,19 +352,27 @@
id={divId}
class="svlt-grid-item"
class:svlt-grid-active={active || (trans && rect)}
style="width: {active ? newSize.width : width}px; height:{active ? newSize.height : height}px;
style="width: {xPerPx == 0 ? 0 : active ? newSize.width : width}px; height:{xPerPx == 0
? 0
: active
? newSize.height
: height}px;
{xPerPx == 0 ? 'overflow: hidden;' : ''}
{onTop ? 'z-index: 1000;' : ''}
{active && rect
? `transform: translate(${cordDiff.x}px, ${cordDiff.y}px);top:${rect.top}px;left:${rect.left}px;z-index:10000;`
: trans
? `transform: translate(${cordDiff.x}px, ${cordDiff.y}px); position:absolute; transition: width 0.2s, height 0.2s;`
: `transition: transform 0.1s, opacity 0.1s; transform: translate(${left}px, ${top}px); `} "
: `${
xPerPx > 0 ? 'transition: transform 0.1s, opacity 0.1s;' : ''
} transform: translate(${left}px, ${top}px); `} "
>
<slot />
<div class="svlt-grid-resizer" on:pointerdown={resizePointerDown} />
</div>
{#if (active || trans) && shadow}
{#if xPerPx > 0 && (active || trans) && shadow}
<div
class="svlt-grid-shadow shadow-active"
style="width: {shadow.w * xPerPx - gapX * 2}px; height: {shadow.h * yPerPx -