feat: list component for apps (#1740)

* all

* feat: list component for apps
This commit is contained in:
Ruben Fiszel
2023-06-19 20:47:04 +02:00
committed by GitHub
parent ad170c10bd
commit 12def5ccca
14 changed files with 104 additions and 54 deletions
@@ -147,8 +147,8 @@
{#if resolvedConfig.beforeIcon && beforeIconComponent}
<svelte:component this={beforeIconComponent} size={14} />
{/if}
{#if resolvedConfig.label && resolvedConfig.label?.length > 0}
<div>{resolvedConfig.label}</div>
{#if resolvedConfig.label?.toString() && resolvedConfig.label?.toString()?.length > 0}
<div>{resolvedConfig.label.toString()}</div>
{/if}
{#if resolvedConfig.afterIcon && afterIconComponent}
<svelte:component this={afterIconComponent} size={14} />
@@ -2,7 +2,7 @@
import { isCodeInjection } from '$lib/components/flows/utils'
import { createEventDispatcher, getContext, onDestroy, tick } from 'svelte'
import type { AppInput, EvalAppInput, UploadAppInput } from '../../inputType'
import type { AppViewerContext, RichConfiguration } from '../../types'
import type { AppViewerContext, ListContext, RichConfiguration } from '../../types'
import { accessPropertyByPath } from '../../utils'
import { computeGlobalContext, eval_like } from './eval'
import deepEqualWithOrderedArray from './deepEqualWithOrderedArray'
@@ -19,6 +19,9 @@
const { componentControl, runnableComponents } = getContext<AppViewerContext>('AppViewerContext')
const iterContext = getContext<ListContext>('ListWrapperContext')
$: fullContext = iterContext ? { ...extraContext, iter: $iterContext } : extraContext
const dispatch = createEventDispatcher()
if (input == undefined) {
@@ -116,7 +119,7 @@
try {
const r = await eval_like(
input.expr,
computeGlobalContext($worldStore, extraContext),
computeGlobalContext($worldStore, fullContext),
true,
$state,
$mode == 'dnd',
@@ -138,7 +141,7 @@
try {
const r = await eval_like(
'`' + input.eval + '`',
computeGlobalContext($worldStore, extraContext),
computeGlobalContext($worldStore, fullContext),
true,
$state,
$mode == 'dnd',
@@ -10,7 +10,7 @@
import { createEventDispatcher, getContext, onDestroy, onMount } from 'svelte'
import type { AppInputs, Runnable } from '../../inputType'
import type { Output } from '../../rx'
import type { AppViewerContext, CancelablePromise, InlineScript } from '../../types'
import type { AppViewerContext, CancelablePromise, InlineScript, ListContext } from '../../types'
import { computeGlobalContext, eval_like } from './eval'
import InputValue from './InputValue.svelte'
import RefreshButton from './RefreshButton.svelte'
@@ -57,6 +57,7 @@
app,
connectingInput
} = getContext<AppViewerContext>('AppViewerContext')
const iterContext = getContext<ListContext>('ListWrapperContext')
const dispatch = createEventDispatcher()
@@ -165,7 +166,7 @@
try {
const r = await eval_like(
runnable.inlineScript?.content,
computeGlobalContext($worldStore, {}),
computeGlobalContext($worldStore, iterContext ? { iter: $iterContext } : {}),
false,
$state,
$mode == 'dnd',
@@ -12,7 +12,6 @@
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
export let id: string
export let componentContainerHeight: number
export let componentInput: AppInput | undefined
export let configuration: RichConfigurations
export let customCss: ComponentCustomCSS<'containercomponent'> | undefined = undefined
@@ -41,6 +40,8 @@
$: css = concatCustomCss($app.css?.containercomponent, customCss)
let result: any[] | undefined = undefined
$: isCard = resolvedConfig.width?.selected == 'card'
</script>
{#each Object.keys(components['listcomponent'].initialData.configuration) as key (key)}
@@ -53,33 +54,59 @@
{/each}
<InitializeComponent {id} />
<RunnableWrapper {outputs} {render} autoRefresh {componentInput} {id} bind:initializing bind:result>
<div class="w-full h-full flex flex-wrap overflow-auto gap-2">
<div
class="w-full flex flex-wrap overflow-auto {isCard ? 'h-full gap-2' : 'divide-y max-h-full'}"
>
{#if $app.subgrids?.[`${id}-0`]}
{#each result ?? [] as value, index}
<div
style={`min-width: ${resolvedConfig.minWidthPx}px; max-height: ${resolvedConfig.maxHeightPx}px;`}
class="border overflow-auto"
>
<ListWrapper {value} {index}>
<SubGridEditor
visible={render}
{id}
class={css?.container?.class}
style={css?.container?.style}
subGridId={`${id}-0`}
containerHeight={componentContainerHeight}
forceView={index != 0}
on:focus={() => {
if (!$connectingInput.opened) {
$selectedComponent = [id]
}
onFocus()
}}
/>
</ListWrapper>
</div>
{/each}
{#if Array.isArray(result)}
{#each result ?? [] as value, index}
<div
style={`${
isCard
? `min-width: ${resolvedConfig.width?.configuration?.card?.minWidthPx}px; `
: ''
} max-height: ${resolvedConfig.heightPx}px;`}
class="overflow-auto {!isCard ? 'w-full' : 'border'}"
>
<ListWrapper {value} {index}>
<SubGridEditor
visible={render}
{id}
class={css?.container?.class}
style={css?.container?.style}
subGridId={`${id}-0`}
containerHeight={resolvedConfig.heightPx}
on:focus={() => {
if (!$connectingInput.opened) {
$selectedComponent = [id]
}
onFocus()
}}
/>
</ListWrapper>
</div>
{/each}
{:else}
<ListWrapper value={undefined} index={0}>
<SubGridEditor
visible={false}
{id}
class={css?.container?.class}
style={css?.container?.style}
subGridId={`${id}-0`}
containerHeight={resolvedConfig.heightPx}
on:focus={() => {
if (!$connectingInput.opened) {
$selectedComponent = [id]
}
onFocus()
}}
/>
</ListWrapper>
<div class="text-center text-gray-500">Input data is not an array</div>
{/if}
{/if}
</div>
</RunnableWrapper>
@@ -1,11 +1,15 @@
<script lang="ts">
import { setContext } from 'svelte'
import type { ListContext } from '../../types'
import { writable } from 'svelte/store'
export let index: number
export let value: any
setContext<ListContext>('ListWrapperContext', { index, value })
const ctx = writable({ index, value })
$: $ctx = { index, value }
setContext<ListContext>('ListWrapperContext', ctx)
</script>
<slot />
@@ -150,7 +150,7 @@
if (befSelected) {
if (!['ctx', 'state'].includes(befSelected) && !befSelected?.startsWith(BG_PREFIX)) {
let item = findGridItem($appStore, befSelected)
if (item?.data.type === 'containercomponent') {
if (item?.data.type === 'containercomponent' || item?.data.type === 'listcomponent') {
$focusedGrid = {
parentComponentId: befSelected,
subGridIndex: 0
@@ -22,7 +22,6 @@
export let visible: boolean = true
export let id: string
export let shouldHighlight: boolean = true
export let forceView = false
const dispatch = createEventDispatcher()
@@ -76,9 +75,9 @@
classes ?? '',
noPadding ? 'px-0' : 'px-2'
)}
style="{containerHeight ? `height: ${containerHeight}px;` : ''} {style ?? ''}"
style="{containerHeight ? `height: ${containerHeight - 2}px;` : ''} {style ?? ''}"
>
{#if $mode !== 'preview' || forceView}
{#if $mode !== 'preview'}
<div
class={highlight ? 'border-gray-600 animate-border border-dashed border-2 min-h-full' : ''}
>
@@ -460,7 +460,6 @@
customCss={component.customCss}
configuration={component.configuration}
componentInput={component.componentInput}
{componentContainerHeight}
{render}
bind:initializing
/>
@@ -475,24 +475,37 @@ export const components = {
listcomponent: {
name: 'List',
icon: ListIcon,
dims: '2:8-6:8' as AppComponentDimensions,
dims: '3:8-12:8' as AppComponentDimensions,
customCss: {
container: { class: '', style: '' }
},
initialData: {
configuration: {
minWidthPx: {
width: {
type: 'oneOf',
selected: 'card',
labels: {
card: 'Card',
row: 'Full Row'
},
configuration: {
card: {
minWidthPx: {
type: 'static',
fieldType: 'number',
value: 300,
tooltip: 'Min Width in pixels'
}
},
row: {}
}
} as const,
heightPx: {
type: 'static',
fieldType: 'number',
value: 300,
tooltip: 'Min Width in pixels'
},
maxHeightPx: {
type: 'static',
fieldType: 'number',
value: 300,
tooltip: 'Max Height in pixels'
value: 280,
tooltip: 'Height in pixels'
}
},
componentInput: {
@@ -10,7 +10,7 @@ const layout: ComponentSet = {
title: 'Layout',
components: [
'containercomponent',
// 'listcomponent',
'listcomponent',
'horizontaldividercomponent',
'verticaldividercomponent',
'drawercomponent',
@@ -659,6 +659,9 @@ export const quickStyleProperties: Record<
containercomponent: {
container: containerDefaultProps
},
listcomponent: {
container: containerDefaultProps
},
dateinputcomponent: {
input: inputDefaultProps
},
@@ -16,8 +16,8 @@
<ScriptSettingsSection title="Triggers">
<div class="flex flex-col">
{#if autoRefresh !== undefined && canConfigureRunOnStart}
<div class="flex items-center justify-between w-full">
<div class="flex flex-row items-center gap-2 text-xs">
<div class="flex items-center justify-between w-full gap-1">
<div class="flex flex-row items-center gap-2 text-xs mb-0.5">
Run on start and app refresh
<Tooltip>
You may want to disable this so that the background runnable is only triggered by
+2 -2
View File
@@ -150,10 +150,10 @@ export interface CancelablePromise<T> extends Promise<T> {
cancel: () => void
}
export type ListContext = {
export type ListContext = Writable<{
index: number
value: any
}
}>
export type AppViewerContext = {
worldStore: Writable<World>
@@ -222,6 +222,7 @@ declare function setSelectedIndex(id: string, index: number): void;
: ''
}
declare const state: ${JSON.stringify(state)};
declare const iter: {index: number, value: any};
`
}