mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-21 00:02:23 +00:00
feat: improve large apps performances (#5265)
This commit is contained in:
@@ -89,75 +89,79 @@
|
||||
configuration={configuration[key]}
|
||||
/>
|
||||
{/each}
|
||||
<AlwaysMountedModal {css} title={resolvedConfig.modalTitle ?? ''} bind:this={modal}>
|
||||
<div class="flex flex-col gap-2 px-4 w-full pt-2">
|
||||
<RunnableWrapper
|
||||
bind:this={runnableWrapper}
|
||||
bind:loading
|
||||
{recomputeIds}
|
||||
{render}
|
||||
bind:runnableComponent
|
||||
{componentInput}
|
||||
{id}
|
||||
{extraQueryParams}
|
||||
autoRefresh={false}
|
||||
forceSchemaDisplay={true}
|
||||
runnableClass="!block"
|
||||
{outputs}
|
||||
doOnSuccess={resolvedConfig.onSuccess}
|
||||
doOnError={resolvedConfig.onError}
|
||||
{errorHandledByComponent}
|
||||
>
|
||||
{#if noInputs}
|
||||
<div class="text-secondary italic text-sm my-4">
|
||||
Run forms are associated with a runnable that has user inputs.
|
||||
<br />
|
||||
Once a script or flow is chosen, set some <strong>Runnable Inputs</strong> to
|
||||
<strong>
|
||||
User Input
|
||||
<User size={20} class="rounded-sm bg-gray-200 p-1 ml-0.5" />
|
||||
</strong>
|
||||
{#if render}
|
||||
<AlwaysMountedModal {css} title={resolvedConfig.modalTitle ?? ''} bind:this={modal}>
|
||||
<div class="flex flex-col gap-2 px-4 w-full pt-2">
|
||||
<RunnableWrapper
|
||||
bind:this={runnableWrapper}
|
||||
bind:loading
|
||||
{recomputeIds}
|
||||
{render}
|
||||
bind:runnableComponent
|
||||
{componentInput}
|
||||
{id}
|
||||
{extraQueryParams}
|
||||
autoRefresh={false}
|
||||
forceSchemaDisplay={true}
|
||||
runnableClass="!block"
|
||||
{outputs}
|
||||
doOnSuccess={resolvedConfig.onSuccess}
|
||||
doOnError={resolvedConfig.onError}
|
||||
{errorHandledByComponent}
|
||||
>
|
||||
{#if noInputs}
|
||||
<div class="text-secondary italic text-sm my-4">
|
||||
Run forms are associated with a runnable that has user inputs.
|
||||
<br />
|
||||
Once a script or flow is chosen, set some <strong>Runnable Inputs</strong> to
|
||||
<strong>
|
||||
User Input
|
||||
<User size={20} class="rounded-sm bg-gray-200 p-1 ml-0.5" />
|
||||
</strong>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex justify-end gap-3 p-2">
|
||||
<Button
|
||||
{loading}
|
||||
btnClasses="my-1"
|
||||
on:pointerdown={(e) => {
|
||||
e?.stopPropagation()
|
||||
}}
|
||||
on:click={async () => {
|
||||
if (!runnableComponent) {
|
||||
runnableWrapper?.handleSideEffect(true)
|
||||
} else {
|
||||
await runnableComponent?.runComponent()
|
||||
}
|
||||
modal?.close()
|
||||
}}
|
||||
size="xs"
|
||||
color="dark"
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex justify-end gap-3 p-2">
|
||||
<Button
|
||||
{loading}
|
||||
btnClasses="my-1"
|
||||
on:pointerdown={(e) => {
|
||||
e?.stopPropagation()
|
||||
}}
|
||||
on:click={async () => {
|
||||
if (!runnableComponent) {
|
||||
runnableWrapper?.handleSideEffect(true)
|
||||
} else {
|
||||
await runnableComponent?.runComponent()
|
||||
}
|
||||
modal?.close()
|
||||
}}
|
||||
size="xs"
|
||||
color="dark"
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
</RunnableWrapper>
|
||||
</div>
|
||||
</AlwaysMountedModal>
|
||||
</RunnableWrapper>
|
||||
</div>
|
||||
</AlwaysMountedModal>
|
||||
|
||||
<AlignWrapper {render} {horizontalAlignment} {verticalAlignment}>
|
||||
{#if errorsMessage}
|
||||
<div class="text-red-500 text-xs">{errorsMessage}</div>
|
||||
{/if}
|
||||
<Button
|
||||
disabled={resolvedConfig.disabled ?? false}
|
||||
size={resolvedConfig.size ?? 'md'}
|
||||
color={resolvedConfig.color}
|
||||
btnClasses={twMerge(css?.button?.class, 'wm-button', 'wm-modal-form-button')}
|
||||
style={css?.button?.style ?? ''}
|
||||
on:click={(e) => {
|
||||
modal?.open()
|
||||
}}
|
||||
>
|
||||
{resolvedConfig.label}
|
||||
</Button>
|
||||
</AlignWrapper>
|
||||
<AlignWrapper {render} {horizontalAlignment} {verticalAlignment}>
|
||||
{#if errorsMessage}
|
||||
<div class="text-red-500 text-xs">{errorsMessage}</div>
|
||||
{/if}
|
||||
<Button
|
||||
disabled={resolvedConfig.disabled ?? false}
|
||||
size={resolvedConfig.size ?? 'md'}
|
||||
color={resolvedConfig.color}
|
||||
btnClasses={twMerge(css?.button?.class, 'wm-button', 'wm-modal-form-button')}
|
||||
style={css?.button?.style ?? ''}
|
||||
on:click={(e) => {
|
||||
modal?.open()
|
||||
}}
|
||||
>
|
||||
{resolvedConfig.label}
|
||||
</Button>
|
||||
</AlignWrapper>
|
||||
{:else}
|
||||
<RunnableWrapper {outputs} {render} {componentInput} {id} />
|
||||
{/if}
|
||||
|
||||
@@ -18,17 +18,21 @@
|
||||
export let initializing: boolean | undefined
|
||||
export let componentContainerHeight: number
|
||||
|
||||
type AccordionListValue = { header: string; [key: string]: any };
|
||||
type AccordionListValue = { header: string; [key: string]: any }
|
||||
|
||||
type InternalAccordionListInput = AppInput & {
|
||||
value: AccordionListValue[];
|
||||
};
|
||||
value: AccordionListValue[]
|
||||
}
|
||||
|
||||
$: accordionInput = componentInput as InternalAccordionListInput;
|
||||
$: accordionInput = componentInput as InternalAccordionListInput
|
||||
|
||||
const { app, focusedGrid, selectedComponent, worldStore, connectingInput } =
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let everRender = render
|
||||
|
||||
$: render && !everRender && (everRender = true)
|
||||
|
||||
let activeIndex: number = 0
|
||||
|
||||
const outputs = initOutput($worldStore, id, {
|
||||
@@ -61,7 +65,6 @@
|
||||
activeIndex = activeIndex === index ? -1 : index
|
||||
outputs.activeIndex.set(activeIndex)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
{#each Object.keys(css ?? {}) as key (key)}
|
||||
@@ -85,27 +88,27 @@
|
||||
bind:initializing
|
||||
bind:result
|
||||
>
|
||||
<div class="w-full flex flex-col overflow-auto max-h-full">
|
||||
{#if $app.subgrids?.[`${id}-0`]}
|
||||
{#if Array.isArray(result) && result.length > 0}
|
||||
{#each result ?? [] as value, index}
|
||||
<div class="border-b">
|
||||
<button
|
||||
on:pointerdown|stopPropagation
|
||||
on:click={() => toggleAccordion(index)}
|
||||
class={twMerge(
|
||||
'w-full text-left bg-surface !truncate text-sm hover:text-primary px-1 py-2',
|
||||
'wm-tabs-alltabs',
|
||||
activeIndex === index
|
||||
? twMerge('bg-surface text-primary ', 'wm-tabs-selectedTab')
|
||||
: 'text-secondary'
|
||||
)}
|
||||
>
|
||||
<span class="mr-2 w-8 font-mono">{activeIndex === index ? '-' : '+'}</span>
|
||||
{result[index]?.header || `Header ${index}`}
|
||||
</button>
|
||||
{#if activeIndex === index}
|
||||
<div class="p-2 overflow-auto w-full">
|
||||
{#if everRender}
|
||||
<div class="w-full flex flex-col overflow-auto max-h-full">
|
||||
{#if $app.subgrids?.[`${id}-0`]}
|
||||
{#if Array.isArray(result) && result.length > 0}
|
||||
{#each result ?? [] as value, index}
|
||||
<div class="border-b">
|
||||
<button
|
||||
on:pointerdown|stopPropagation
|
||||
on:click={() => toggleAccordion(index)}
|
||||
class={twMerge(
|
||||
'w-full text-left bg-surface !truncate text-sm hover:text-primary px-1 py-2',
|
||||
'wm-tabs-alltabs',
|
||||
activeIndex === index
|
||||
? twMerge('bg-surface text-primary ', 'wm-tabs-selectedTab')
|
||||
: 'text-secondary'
|
||||
)}
|
||||
>
|
||||
<span class="mr-2 w-8 font-mono">{activeIndex === index ? '-' : '+'}</span>
|
||||
{result[index]?.header || `Header ${index}`}
|
||||
</button>
|
||||
<div class="overflow-auto w-full">
|
||||
<ListWrapper
|
||||
onSet={(id, value) => {
|
||||
if (!inputs[id]) {
|
||||
@@ -133,8 +136,8 @@
|
||||
>
|
||||
<SubGridEditor
|
||||
{id}
|
||||
visible={render}
|
||||
class={twMerge(css?.container?.class, 'wm-accordion')}
|
||||
visible={render && index === activeIndex}
|
||||
class={twMerge(css?.container?.class, 'wm-accordion p-2')}
|
||||
style={css?.container?.style}
|
||||
subGridId={`${id}-0`}
|
||||
containerHeight={componentContainerHeight -
|
||||
@@ -148,17 +151,21 @@
|
||||
/>
|
||||
</ListWrapper>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
{:else}
|
||||
<ListWrapper disabled value={undefined} index={0}>
|
||||
<SubGridEditor visible={false} {id} subGridId={`${id}-0`} />
|
||||
</ListWrapper>
|
||||
{#if !Array.isArray(result)}
|
||||
<div class="text-center text-tertiary">Input data is not an array</div>
|
||||
</div>
|
||||
{/each}
|
||||
{:else}
|
||||
<ListWrapper disabled value={undefined} index={0}>
|
||||
<SubGridEditor visible={false} {id} subGridId={`${id}-0`} />
|
||||
</ListWrapper>
|
||||
{#if !Array.isArray(result)}
|
||||
<div class="text-center text-tertiary">Input data is not an array</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{:else if $app.subgrids}
|
||||
<ListWrapper disabled value={undefined} index={0}>
|
||||
<SubGridEditor visible={false} {id} subGridId={`${id}-0`} />
|
||||
</ListWrapper>
|
||||
{/if}
|
||||
</RunnableWrapper>
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
const { app, focusedGrid, selectedComponent, worldStore, connectingInput } =
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let everRender = render
|
||||
$: render && !everRender && (everRender = true)
|
||||
|
||||
const outputs = initOutput($worldStore, id, {
|
||||
result: undefined,
|
||||
loading: false,
|
||||
@@ -90,117 +93,123 @@
|
||||
bind:initializing
|
||||
bind:result
|
||||
>
|
||||
<div class="w-full flex flex-wrap overflow-auto divide-y max-h-full">
|
||||
{#if $app.subgrids?.[`${id}-0`]}
|
||||
{#if Array.isArray(result) && result.length > 0}
|
||||
{#key result}
|
||||
<Carousel
|
||||
particlesToShow={1}
|
||||
particlesToScroll={1}
|
||||
autoplay={false}
|
||||
autoplayProgressVisible={false}
|
||||
timingFunction={resolvedConfig.timingFunction}
|
||||
dots={true}
|
||||
arrows={true}
|
||||
swiping={false}
|
||||
bind:this={carousel}
|
||||
on:pageChange={(event) => {
|
||||
currentPageIndex = event.detail
|
||||
$focusedGrid = {
|
||||
parentComponentId: id,
|
||||
subGridIndex: event.detail
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div slot="prev" class="h-full flex justify-center flex-col p-2">
|
||||
<div>
|
||||
<Button
|
||||
color="light"
|
||||
on:click={() => {
|
||||
const pagesCount = result?.length ?? 0
|
||||
{#if everRender}
|
||||
<div class="w-full flex flex-wrap overflow-auto divide-y max-h-full">
|
||||
{#if $app.subgrids?.[`${id}-0`]}
|
||||
{#if Array.isArray(result) && result.length > 0}
|
||||
{#key result}
|
||||
<Carousel
|
||||
particlesToShow={1}
|
||||
particlesToScroll={1}
|
||||
autoplay={false}
|
||||
autoplayProgressVisible={false}
|
||||
timingFunction={resolvedConfig.timingFunction}
|
||||
dots={true}
|
||||
arrows={true}
|
||||
swiping={false}
|
||||
bind:this={carousel}
|
||||
on:pageChange={(event) => {
|
||||
currentPageIndex = event.detail
|
||||
$focusedGrid = {
|
||||
parentComponentId: id,
|
||||
subGridIndex: event.detail
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div slot="prev" class="h-full flex justify-center flex-col p-2">
|
||||
<div>
|
||||
<Button
|
||||
color="light"
|
||||
on:click={() => {
|
||||
const pagesCount = result?.length ?? 0
|
||||
|
||||
if (currentPageIndex > 0) {
|
||||
carousel.goTo(currentPageIndex - 1)
|
||||
} else {
|
||||
carousel.goTo(pagesCount - 1)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ArrowLeftCircle size={16} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="next" class="h-full flex justify-center flex-col p-2">
|
||||
<div>
|
||||
<Button
|
||||
color="light"
|
||||
on:click={() => {
|
||||
const pagesCount = result?.length ?? 0
|
||||
if (currentPageIndex < pagesCount - 1) {
|
||||
carousel.goTo(currentPageIndex + 1)
|
||||
} else {
|
||||
carousel.goTo(0)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ArrowRightCircle size={16} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{#each result ?? [] as value, index}
|
||||
<div class="overflow-auto w-full">
|
||||
<ListWrapper
|
||||
onSet={(id, value) => {
|
||||
if (!inputs[id]) {
|
||||
inputs[id] = { [index]: value }
|
||||
} else {
|
||||
inputs[id] = { ...inputs[id], [index]: value }
|
||||
}
|
||||
outputs?.inputs.set(inputs, true)
|
||||
}}
|
||||
onRemove={(id) => {
|
||||
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)
|
||||
}}
|
||||
{value}
|
||||
{index}
|
||||
>
|
||||
<SubGridEditor
|
||||
{id}
|
||||
visible={render}
|
||||
class={twMerge(css?.container?.class, 'wm-carousel')}
|
||||
style={css?.container?.style}
|
||||
subGridId={`${id}-0`}
|
||||
containerHeight={componentContainerHeight - 40}
|
||||
on:focus={() => {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = [id]
|
||||
if (currentPageIndex > 0) {
|
||||
carousel.goTo(currentPageIndex - 1)
|
||||
} else {
|
||||
carousel.goTo(pagesCount - 1)
|
||||
}
|
||||
onFocus()
|
||||
}}
|
||||
/>
|
||||
</ListWrapper>
|
||||
>
|
||||
<ArrowLeftCircle size={16} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</Carousel>
|
||||
{/key}
|
||||
{:else}
|
||||
<ListWrapper disabled value={undefined} index={0}>
|
||||
<SubGridEditor visible={false} {id} subGridId={`${id}-0`} />
|
||||
</ListWrapper>
|
||||
{#if !Array.isArray(result)}
|
||||
<div class="text-center text-tertiary">Input data is not an array</div>
|
||||
<div slot="next" class="h-full flex justify-center flex-col p-2">
|
||||
<div>
|
||||
<Button
|
||||
color="light"
|
||||
on:click={() => {
|
||||
const pagesCount = result?.length ?? 0
|
||||
if (currentPageIndex < pagesCount - 1) {
|
||||
carousel.goTo(currentPageIndex + 1)
|
||||
} else {
|
||||
carousel.goTo(0)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ArrowRightCircle size={16} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{#each result ?? [] as value, index}
|
||||
<div class="overflow-auto w-full">
|
||||
<ListWrapper
|
||||
onSet={(id, value) => {
|
||||
if (!inputs[id]) {
|
||||
inputs[id] = { [index]: value }
|
||||
} else {
|
||||
inputs[id] = { ...inputs[id], [index]: value }
|
||||
}
|
||||
outputs?.inputs.set(inputs, true)
|
||||
}}
|
||||
onRemove={(id) => {
|
||||
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)
|
||||
}}
|
||||
{value}
|
||||
{index}
|
||||
>
|
||||
<SubGridEditor
|
||||
{id}
|
||||
visible={render}
|
||||
class={twMerge(css?.container?.class, 'wm-carousel')}
|
||||
style={css?.container?.style}
|
||||
subGridId={`${id}-0`}
|
||||
containerHeight={componentContainerHeight - 40}
|
||||
on:focus={() => {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = [id]
|
||||
}
|
||||
onFocus()
|
||||
}}
|
||||
/>
|
||||
</ListWrapper>
|
||||
</div>
|
||||
{/each}
|
||||
</Carousel>
|
||||
{/key}
|
||||
{:else}
|
||||
<ListWrapper disabled value={undefined} index={0}>
|
||||
<SubGridEditor visible={false} {id} subGridId={`${id}-0`} />
|
||||
</ListWrapper>
|
||||
{#if !Array.isArray(result)}
|
||||
<div class="text-center text-tertiary">Input data is not an array</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{:else if $app.subgrids}
|
||||
<ListWrapper disabled value={undefined} index={0}>
|
||||
<SubGridEditor visible={false} {id} subGridId={`${id}-0`} />
|
||||
</ListWrapper>
|
||||
{/if}
|
||||
</RunnableWrapper>
|
||||
|
||||
@@ -119,4 +119,6 @@
|
||||
<div id={divId} />
|
||||
</RunnableWrapper>
|
||||
</div>
|
||||
{:else}
|
||||
<RunnableWrapper {outputs} {render} autoRefresh {componentInput} {id} />
|
||||
{/if}
|
||||
|
||||
@@ -35,25 +35,29 @@
|
||||
/>
|
||||
{/each}
|
||||
|
||||
<div
|
||||
on:pointerdown={(e) => {
|
||||
e?.preventDefault()
|
||||
}}
|
||||
class="h-full w-full"
|
||||
>
|
||||
<RunnableWrapper
|
||||
{outputs}
|
||||
{render}
|
||||
autoRefresh
|
||||
{componentInput}
|
||||
{id}
|
||||
bind:initializing
|
||||
bind:result
|
||||
{#if render}
|
||||
<div
|
||||
on:pointerdown={(e) => {
|
||||
e?.preventDefault()
|
||||
}}
|
||||
class="h-full w-full"
|
||||
>
|
||||
<div class="w-full h-full overflow-auto">
|
||||
{#key result}
|
||||
{@html result}
|
||||
{/key}
|
||||
</div>
|
||||
</RunnableWrapper>
|
||||
</div>
|
||||
<RunnableWrapper
|
||||
{outputs}
|
||||
{render}
|
||||
autoRefresh
|
||||
{componentInput}
|
||||
{id}
|
||||
bind:initializing
|
||||
bind:result
|
||||
>
|
||||
<div class="w-full h-full overflow-auto">
|
||||
{#key result}
|
||||
{@html result}
|
||||
{/key}
|
||||
</div>
|
||||
</RunnableWrapper>
|
||||
</div>
|
||||
{:else}
|
||||
<RunnableWrapper {outputs} render={false} autoRefresh {componentInput} {id} />
|
||||
{/if}
|
||||
|
||||
@@ -62,32 +62,36 @@
|
||||
/>
|
||||
{/each}
|
||||
|
||||
<div
|
||||
on:pointerdown={(e) => {
|
||||
e?.preventDefault()
|
||||
}}
|
||||
class={classNames(
|
||||
'h-full w-full overflow-y-auto prose max-w-full',
|
||||
resolvedConfig?.size ? proseMapping[resolvedConfig.size] : '',
|
||||
css?.container?.class,
|
||||
' dark:prose-invert',
|
||||
'wm-markdown'
|
||||
)}
|
||||
style={css?.container?.style}
|
||||
>
|
||||
<RunnableWrapper
|
||||
{outputs}
|
||||
{render}
|
||||
autoRefresh
|
||||
{componentInput}
|
||||
{id}
|
||||
bind:initializing
|
||||
bind:result
|
||||
{#if render}
|
||||
<div
|
||||
on:pointerdown={(e) => {
|
||||
e?.preventDefault()
|
||||
}}
|
||||
class={classNames(
|
||||
'h-full w-full overflow-y-auto prose max-w-full',
|
||||
resolvedConfig?.size ? proseMapping[resolvedConfig.size] : '',
|
||||
css?.container?.class,
|
||||
' dark:prose-invert',
|
||||
'wm-markdown'
|
||||
)}
|
||||
style={css?.container?.style}
|
||||
>
|
||||
{#if result}
|
||||
{#key result}
|
||||
<Markdown md={result} plugins={[gfmPlugin()]} />
|
||||
{/key}
|
||||
{/if}
|
||||
</RunnableWrapper>
|
||||
</div>
|
||||
<RunnableWrapper
|
||||
{outputs}
|
||||
render={true}
|
||||
autoRefresh
|
||||
{componentInput}
|
||||
{id}
|
||||
bind:initializing
|
||||
bind:result
|
||||
>
|
||||
{#if result}
|
||||
{#key result}
|
||||
<Markdown md={result} plugins={[gfmPlugin()]} />
|
||||
{/key}
|
||||
{/if}
|
||||
</RunnableWrapper>
|
||||
</div>
|
||||
{:else}
|
||||
<RunnableWrapper {outputs} render={false} autoRefresh {componentInput} {id} />
|
||||
{/if}
|
||||
|
||||
@@ -217,7 +217,7 @@
|
||||
{:else}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class="flex flex-wrap gap-2 pb-0.5 w-full {$mode === 'dnd' &&
|
||||
class="flex flex-wrap gap-0.5 pb-0.5 w-full {$mode === 'dnd' &&
|
||||
(componentInput?.type == 'template' || componentInput?.type == 'templatev2')
|
||||
? 'cursor-text'
|
||||
: 'overflow-auto'}"
|
||||
@@ -225,7 +225,7 @@
|
||||
<svelte:element
|
||||
this={component}
|
||||
class={twMerge(
|
||||
'whitespace-pre-wrap w-full',
|
||||
'whitespace-pre-wrap grow',
|
||||
|
||||
css?.text?.class,
|
||||
classes,
|
||||
@@ -244,7 +244,7 @@
|
||||
</svelte:element>
|
||||
|
||||
{#if resolvedConfig.copyButton && result}
|
||||
<div class="flex">
|
||||
<div class="flex items-center">
|
||||
<Button
|
||||
title="Copy to clipboard"
|
||||
variant="border"
|
||||
|
||||
@@ -103,15 +103,19 @@
|
||||
/>
|
||||
{/each}
|
||||
|
||||
<div class="w-full h-full" bind:clientHeight={h} bind:clientWidth={w}>
|
||||
<RunnableWrapper {outputs} {render} {componentInput} {id} bind:initializing bind:result>
|
||||
{#if error != ''}
|
||||
<div class="flex flex-col h-full w-full overflow-auto">
|
||||
<Alert title="Plotly error" type="error" size="xs" class="h-full w-full ">
|
||||
<pre class="w-full bg-surface p-2 rounded-md whitespace-pre-wrap">{error}</pre>
|
||||
</Alert>
|
||||
</div>
|
||||
{/if}
|
||||
<div on:pointerdown bind:this={divEl} />
|
||||
</RunnableWrapper>
|
||||
</div>
|
||||
{#if render}
|
||||
<div class="w-full h-full" bind:clientHeight={h} bind:clientWidth={w}>
|
||||
<RunnableWrapper {outputs} {render} {componentInput} {id} bind:initializing bind:result>
|
||||
{#if error != ''}
|
||||
<div class="flex flex-col h-full w-full overflow-auto">
|
||||
<Alert title="Plotly error" type="error" size="xs" class="h-full w-full ">
|
||||
<pre class="w-full bg-surface p-2 rounded-md whitespace-pre-wrap">{error}</pre>
|
||||
</Alert>
|
||||
</div>
|
||||
{/if}
|
||||
<div on:pointerdown bind:this={divEl} />
|
||||
</RunnableWrapper>
|
||||
</div>
|
||||
{:else}
|
||||
<RunnableWrapper {outputs} {render} {componentInput} {id} />
|
||||
{/if}
|
||||
|
||||
@@ -169,15 +169,19 @@
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
<div class="w-full h-full" bind:clientHeight={h} bind:clientWidth={w}>
|
||||
<RunnableWrapper {outputs} {render} {componentInput} {id} bind:initializing bind:result>
|
||||
{#if error != ''}
|
||||
<div class="flex flex-col h-full w-full overflow-auto">
|
||||
<Alert title="Plotly error" type="error" size="xs" class="h-full w-full ">
|
||||
<pre class="w-full bg-surface p-2 rounded-md whitespace-pre-wrap">{error}</pre>
|
||||
</Alert>
|
||||
</div>
|
||||
{/if}
|
||||
<div on:pointerdown bind:this={divEl} />
|
||||
</RunnableWrapper>
|
||||
</div>
|
||||
{#if render}
|
||||
<div class="w-full h-full" bind:clientHeight={h} bind:clientWidth={w}>
|
||||
<RunnableWrapper {outputs} {render} {componentInput} {id} bind:initializing bind:result>
|
||||
{#if error != ''}
|
||||
<div class="flex flex-col h-full w-full overflow-auto">
|
||||
<Alert title="Plotly error" type="error" size="xs" class="h-full w-full ">
|
||||
<pre class="w-full bg-surface p-2 rounded-md whitespace-pre-wrap">{error}</pre>
|
||||
</Alert>
|
||||
</div>
|
||||
{/if}
|
||||
<div on:pointerdown bind:this={divEl} />
|
||||
</RunnableWrapper>
|
||||
</div>
|
||||
{:else}
|
||||
<RunnableWrapper {outputs} {render} {componentInput} {id} />
|
||||
{/if}
|
||||
|
||||
@@ -67,8 +67,12 @@
|
||||
|
||||
<InputValue key={'canvas'} {id} input={configuration.canvas} bind:value={canvas} />
|
||||
|
||||
<div class="w-full h-full" bind:clientHeight={h} bind:clientWidth={w}>
|
||||
<RunnableWrapper {outputs} {render} {componentInput} {id} bind:initializing bind:result>
|
||||
<div on:pointerdown bind:this={divEl} />
|
||||
</RunnableWrapper>
|
||||
</div>
|
||||
{#if render}
|
||||
<div class="w-full h-full" bind:clientHeight={h} bind:clientWidth={w}>
|
||||
<RunnableWrapper {outputs} {render} {componentInput} {id} bind:initializing bind:result>
|
||||
<div on:pointerdown bind:this={divEl} />
|
||||
</RunnableWrapper>
|
||||
</div>
|
||||
{:else}
|
||||
<RunnableWrapper {outputs} {render} {componentInput} {id} />
|
||||
{/if}
|
||||
|
||||
@@ -695,6 +695,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
</RunnableWrapper>
|
||||
|
||||
<Portal name="db-explorer">
|
||||
<Drawer bind:this={insertDrawer} size="800px">
|
||||
<DrawerContent title="Insert row" on:close={insertDrawer.closeDrawer}>
|
||||
|
||||
@@ -84,6 +84,4 @@
|
||||
<RefreshIndicator {loading} />
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="w-full h-full" />
|
||||
{/if}
|
||||
|
||||
@@ -31,6 +31,9 @@
|
||||
selectedTabIndex: 0
|
||||
})
|
||||
|
||||
let everRender = render
|
||||
$: render && !everRender && (everRender = true)
|
||||
|
||||
function onFocus() {
|
||||
$focusedGrid = {
|
||||
parentComponentId: id,
|
||||
@@ -97,23 +100,29 @@
|
||||
|
||||
<InitializeComponent {id} />
|
||||
|
||||
<div class="w-full h-full">
|
||||
{#if $app.subgrids}
|
||||
{#each resolvedConditions ?? [] as _res, i}
|
||||
<SubGridEditor
|
||||
visible={render && i == selectedConditionIndex}
|
||||
{id}
|
||||
class={twMerge(css?.container?.class, 'wm-conditional-tabs')}
|
||||
style={css?.container?.style}
|
||||
subGridId={`${id}-${i}`}
|
||||
containerHeight={componentContainerHeight}
|
||||
on:focus={(e) => {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = [id]
|
||||
}
|
||||
onFocus()
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
{#if everRender}
|
||||
<div class="w-full h-full">
|
||||
{#if $app.subgrids}
|
||||
{#each resolvedConditions ?? [] as _res, i}
|
||||
<SubGridEditor
|
||||
visible={render && i == selectedConditionIndex}
|
||||
{id}
|
||||
class={twMerge(css?.container?.class, 'wm-conditional-tabs')}
|
||||
style={css?.container?.style}
|
||||
subGridId={`${id}-${i}`}
|
||||
containerHeight={componentContainerHeight}
|
||||
on:focus={(e) => {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = [id]
|
||||
}
|
||||
onFocus()
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
{:else if $app.subgrids}
|
||||
{#each resolvedConditions ?? [] as _res, i}
|
||||
<SubGridEditor visible={false} {id} subGridId={`${id}-${i}`} />
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
const { app, focusedGrid, selectedComponent, worldStore, connectingInput, componentControl } =
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let everRender = render
|
||||
$: render && !everRender && (everRender = true)
|
||||
|
||||
let groupContext = writable({})
|
||||
|
||||
let outputs = initOutput($worldStore, id, { group: $groupContext })
|
||||
@@ -66,23 +69,29 @@
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
<div class="w-full h-full">
|
||||
{#if $app.subgrids?.[`${id}-0`]}
|
||||
<GroupWrapper {id} context={groupContext}>
|
||||
<SubGridEditor
|
||||
visible={render}
|
||||
{id}
|
||||
class={twMerge(css?.container?.class, 'wm-container')}
|
||||
style={css?.container?.style}
|
||||
subGridId={`${id}-0`}
|
||||
containerHeight={componentContainerHeight}
|
||||
on:focus={() => {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = [id]
|
||||
}
|
||||
onFocus()
|
||||
}}
|
||||
/>
|
||||
</GroupWrapper>
|
||||
{/if}
|
||||
</div>
|
||||
{#if everRender}
|
||||
<div class="w-full h-full">
|
||||
{#if $app.subgrids?.[`${id}-0`]}
|
||||
<GroupWrapper {id} context={groupContext}>
|
||||
<SubGridEditor
|
||||
visible={render}
|
||||
{id}
|
||||
class={twMerge(css?.container?.class, 'wm-container')}
|
||||
style={css?.container?.style}
|
||||
subGridId={`${id}-0`}
|
||||
containerHeight={componentContainerHeight}
|
||||
on:focus={() => {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = [id]
|
||||
}
|
||||
onFocus()
|
||||
}}
|
||||
/>
|
||||
</GroupWrapper>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if $app.subgrids?.[`${id}-0`]}
|
||||
<GroupWrapper {id} context={groupContext}>
|
||||
<SubGridEditor visible={false} {id} subGridId={`${id}-0`} />
|
||||
</GroupWrapper>
|
||||
{/if}
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
export let render: boolean
|
||||
export let nodes: DecisionTreeNode[]
|
||||
|
||||
let everRender = render
|
||||
$: render && !everRender && (everRender = true)
|
||||
|
||||
const {
|
||||
app,
|
||||
focusedGrid,
|
||||
@@ -182,55 +185,63 @@
|
||||
|
||||
<InitializeComponent {id} />
|
||||
|
||||
<div class="w-full overflow-auto">
|
||||
<div class="w-full">
|
||||
{#if $app.subgrids}
|
||||
{#each Object.values(nodes) ?? [] as node, i}
|
||||
<SubGridEditor
|
||||
visible={render && node.id === currentNodeId}
|
||||
{id}
|
||||
class={twMerge(css?.container?.class, 'wm-decision-tree')}
|
||||
style={css?.container?.style}
|
||||
subGridId={`${id}-${i}`}
|
||||
containerHeight={componentContainerHeight - 40}
|
||||
on:focus={() => {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = [id]
|
||||
}
|
||||
onFocus(i)
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
{#if everRender}
|
||||
<div class="w-full overflow-auto">
|
||||
<div class="w-full">
|
||||
{#if $app.subgrids}
|
||||
{#each Object.values(nodes) ?? [] as node, i}
|
||||
<SubGridEditor
|
||||
visible={render && node.id === currentNodeId}
|
||||
{id}
|
||||
class={twMerge(css?.container?.class, 'wm-decision-tree')}
|
||||
style={css?.container?.style}
|
||||
subGridId={`${id}-${i}`}
|
||||
containerHeight={componentContainerHeight - 40}
|
||||
on:focus={() => {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = [id]
|
||||
}
|
||||
onFocus(i)
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="h-8 flex flex-row gap-2 justify-end items-center px-2 bg-surface-primary z-50">
|
||||
{#if isDebugging($debuggingComponents, id)}
|
||||
<Badge color="red" size="xs2">
|
||||
{`Debugging. Actions are disabled.`}
|
||||
</Badge>
|
||||
{#if render}
|
||||
<div class="h-8 flex flex-row gap-2 justify-end items-center px-2 bg-surface-primary z-50">
|
||||
{#if isDebugging($debuggingComponents, id)}
|
||||
<Badge color="red" size="xs2">
|
||||
{`Debugging. Actions are disabled.`}
|
||||
</Badge>
|
||||
{/if}
|
||||
{#if getFirstNode(nodes)?.id !== currentNodeId}
|
||||
<Button
|
||||
on:click={prev}
|
||||
size="xs2"
|
||||
color="light"
|
||||
startIcon={{ icon: ArrowLeft }}
|
||||
disabled={isDebugging($debuggingComponents, id)}
|
||||
>
|
||||
Prev
|
||||
</Button>
|
||||
{/if}
|
||||
<Button
|
||||
on:click={next}
|
||||
size="xs2"
|
||||
color="dark"
|
||||
endIcon={{ icon: ArrowRight }}
|
||||
disabled={isNextDisabled ||
|
||||
currentNodeId === lastNodeId ||
|
||||
isDebugging($debuggingComponents, id)}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
{#if getFirstNode(nodes)?.id !== currentNodeId}
|
||||
<Button
|
||||
on:click={prev}
|
||||
size="xs2"
|
||||
color="light"
|
||||
startIcon={{ icon: ArrowLeft }}
|
||||
disabled={isDebugging($debuggingComponents, id)}
|
||||
>
|
||||
Prev
|
||||
</Button>
|
||||
{/if}
|
||||
<Button
|
||||
on:click={next}
|
||||
size="xs2"
|
||||
color="dark"
|
||||
endIcon={{ icon: ArrowRight }}
|
||||
disabled={isNextDisabled ||
|
||||
currentNodeId === lastNodeId ||
|
||||
isDebugging($debuggingComponents, id)}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
{:else if $app.subgrids}
|
||||
{#each Object.values(nodes) ?? [] as _node, i}
|
||||
<SubGridEditor visible={false} {id} subGridId={`${id}-${i}`} />
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
export let onOpenRecomputeIds: string[] | undefined = undefined
|
||||
export let onCloseRecomputeIds: string[] | undefined = undefined
|
||||
|
||||
let everRender = render
|
||||
$: render && !everRender && (everRender = true)
|
||||
|
||||
const {
|
||||
app,
|
||||
focusedGrid,
|
||||
@@ -77,98 +80,103 @@
|
||||
{/each}
|
||||
|
||||
<InitializeComponent {id} />
|
||||
|
||||
<div class="h-full w-full">
|
||||
<AlignWrapper {noWFull} {horizontalAlignment} {verticalAlignment}>
|
||||
<Button
|
||||
btnClasses={twMerge(css?.button?.class, 'wm-drawer-button')}
|
||||
wrapperClasses={twMerge(
|
||||
css?.container?.class,
|
||||
'wm-drawer-button-container',
|
||||
resolvedConfig?.fillContainer ? 'w-full h-full' : '',
|
||||
resolvedConfig?.hideButtonOnView && $mode == 'preview'
|
||||
? 'invisible h-0 overflow-hidden'
|
||||
: ''
|
||||
)}
|
||||
wrapperStyle={css?.container?.style}
|
||||
disabled={resolvedConfig?.disabled}
|
||||
on:pointerdown={(e) => {
|
||||
e?.stopPropagation()
|
||||
}}
|
||||
on:click={async (e) => {
|
||||
$focusedGrid = {
|
||||
parentComponentId: id,
|
||||
subGridIndex: 0
|
||||
}
|
||||
appDrawer.toggleDrawer()
|
||||
}}
|
||||
size={resolvedConfig.size}
|
||||
color={resolvedConfig.color}
|
||||
style={css?.button?.style}
|
||||
>
|
||||
{#if resolvedConfig.label && resolvedConfig.label?.length > 0}
|
||||
<div>{resolvedConfig.label}</div>
|
||||
{/if}
|
||||
</Button>
|
||||
</AlignWrapper>
|
||||
</div>
|
||||
|
||||
<Portal target="#app-editor-top-level-drawer" name="app-drawer">
|
||||
<Drawer
|
||||
let:open
|
||||
bind:this={appDrawer}
|
||||
size="800px"
|
||||
alwaysOpen
|
||||
positionClass={$mode == 'dnd' ? '!absolute' : '!fixed'}
|
||||
shouldUsePortal={false}
|
||||
on:open={() => {
|
||||
outputs?.open.set(true)
|
||||
onOpenRecomputeIds?.forEach((id) => $runnableComponents?.[id]?.cb?.map((cb) => cb?.()))
|
||||
}}
|
||||
on:close={() => {
|
||||
outputs?.open.set(false)
|
||||
onCloseRecomputeIds?.forEach((id) => $runnableComponents?.[id]?.cb?.map((cb) => cb?.()))
|
||||
}}
|
||||
>
|
||||
<DrawerContent
|
||||
title={resolvedConfig.drawerTitle}
|
||||
on:close={() => {
|
||||
appDrawer?.toggleDrawer()
|
||||
$focusedGrid = undefined
|
||||
}}
|
||||
fullScreen={$mode !== 'dnd'}
|
||||
>
|
||||
<div
|
||||
class={twMerge('h-full', css?.drawer?.class, 'wm-drawer')}
|
||||
style={css?.drawer?.style}
|
||||
{#if render}
|
||||
<div class="h-full w-full">
|
||||
<AlignWrapper {noWFull} {horizontalAlignment} {verticalAlignment}>
|
||||
<Button
|
||||
btnClasses={twMerge(css?.button?.class, 'wm-drawer-button')}
|
||||
wrapperClasses={twMerge(
|
||||
css?.container?.class,
|
||||
'wm-drawer-button-container',
|
||||
resolvedConfig?.fillContainer ? 'w-full h-full' : '',
|
||||
resolvedConfig?.hideButtonOnView && $mode == 'preview'
|
||||
? 'invisible h-0 overflow-hidden'
|
||||
: ''
|
||||
)}
|
||||
wrapperStyle={css?.container?.style}
|
||||
disabled={resolvedConfig?.disabled}
|
||||
on:pointerdown={(e) => {
|
||||
e?.stopPropagation()
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = [id]
|
||||
$focusedGrid = {
|
||||
parentComponentId: id,
|
||||
subGridIndex: 0
|
||||
}
|
||||
}
|
||||
}}
|
||||
on:click={async (e) => {
|
||||
$focusedGrid = {
|
||||
parentComponentId: id,
|
||||
subGridIndex: 0
|
||||
}
|
||||
appDrawer.toggleDrawer()
|
||||
}}
|
||||
size={resolvedConfig.size}
|
||||
color={resolvedConfig.color}
|
||||
style={css?.button?.style}
|
||||
>
|
||||
{#if $app.subgrids?.[`${id}-0`]}
|
||||
<SubGridEditor
|
||||
visible={open && render}
|
||||
{id}
|
||||
subGridId={`${id}-0`}
|
||||
on:focus={() => {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = [id]
|
||||
$focusedGrid = {
|
||||
parentComponentId: id,
|
||||
subGridIndex: 0
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{#if resolvedConfig.label && resolvedConfig.label?.length > 0}
|
||||
<div>{resolvedConfig.label}</div>
|
||||
{/if}
|
||||
</div>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
</Portal>
|
||||
</Button>
|
||||
</AlignWrapper>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if everRender}
|
||||
<Portal target="#app-editor-top-level-drawer" name="app-drawer">
|
||||
<Drawer
|
||||
let:open
|
||||
bind:this={appDrawer}
|
||||
size="800px"
|
||||
alwaysOpen
|
||||
positionClass={$mode == 'dnd' ? '!absolute' : '!fixed'}
|
||||
shouldUsePortal={false}
|
||||
on:open={() => {
|
||||
outputs?.open.set(true)
|
||||
onOpenRecomputeIds?.forEach((id) => $runnableComponents?.[id]?.cb?.map((cb) => cb?.()))
|
||||
}}
|
||||
on:close={() => {
|
||||
outputs?.open.set(false)
|
||||
onCloseRecomputeIds?.forEach((id) => $runnableComponents?.[id]?.cb?.map((cb) => cb?.()))
|
||||
}}
|
||||
>
|
||||
<DrawerContent
|
||||
title={resolvedConfig.drawerTitle}
|
||||
on:close={() => {
|
||||
appDrawer?.toggleDrawer()
|
||||
$focusedGrid = undefined
|
||||
}}
|
||||
fullScreen={$mode !== 'dnd'}
|
||||
>
|
||||
<div
|
||||
class={twMerge('h-full', css?.drawer?.class, 'wm-drawer')}
|
||||
style={css?.drawer?.style}
|
||||
on:pointerdown={(e) => {
|
||||
e?.stopPropagation()
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = [id]
|
||||
$focusedGrid = {
|
||||
parentComponentId: id,
|
||||
subGridIndex: 0
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#if $app.subgrids?.[`${id}-0`]}
|
||||
<SubGridEditor
|
||||
visible={open && render}
|
||||
{id}
|
||||
subGridId={`${id}-0`}
|
||||
on:focus={() => {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = [id]
|
||||
$focusedGrid = {
|
||||
parentComponentId: id,
|
||||
subGridIndex: 0
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
</Portal>
|
||||
{:else if $app.subgrids?.[`${id}-0`]}
|
||||
<SubGridEditor visible={false} {id} subGridId={`${id}-0`} />
|
||||
{/if}
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
let page = 0
|
||||
|
||||
let everRender = render
|
||||
$: render && !everRender && (everRender = true)
|
||||
|
||||
const outputs = initOutput($worldStore, id, {
|
||||
result: undefined,
|
||||
loading: false,
|
||||
@@ -72,8 +75,8 @@
|
||||
const l = initialData ? initialData.length : 0
|
||||
if (mode === 'auto') {
|
||||
const pageSize: number = configuration.auto.pageSize ?? 0
|
||||
const shouldDisplayPagination = pageSize < l ?? false
|
||||
const total = Math.ceil(l / pageSize ?? 0)
|
||||
const shouldDisplayPagination = (pageSize ?? 0) < l
|
||||
const total = Math.ceil(l / (pageSize ?? 0))
|
||||
|
||||
return {
|
||||
shouldDisplayPagination,
|
||||
@@ -136,136 +139,144 @@
|
||||
bind:result
|
||||
bind:loading
|
||||
>
|
||||
<div
|
||||
class={twMerge('w-full h-full', css?.container?.class, 'wm-list')}
|
||||
style={css?.container?.style}
|
||||
>
|
||||
{#if everRender}
|
||||
<div
|
||||
class="w-full h-full shrink flex {$allIdsInPath.includes(id) && $mode == 'dnd'
|
||||
? 'overflow-visible'
|
||||
: 'overflow-auto'} {isCard
|
||||
? 'gap-2 flex-wrap'
|
||||
: resolvedConfig?.displayBorders
|
||||
? 'divide-y flex-col'
|
||||
: 'flex-col'}"
|
||||
class={twMerge('w-full h-full', css?.container?.class, 'wm-list')}
|
||||
style={css?.container?.style}
|
||||
>
|
||||
{#if $app.subgrids?.[`${id}-0`]}
|
||||
{#if Array.isArray(result) && result.length > 0}
|
||||
{#each result ?? [] as value, index (index)}
|
||||
{@const inRange = index <= pagination.maxIndex && index >= pagination.indexOffset}
|
||||
<div
|
||||
style={inRange
|
||||
? `${
|
||||
isCard
|
||||
? `min-width: ${resolvedConfig.width?.configuration?.card?.minWidthPx}px; `
|
||||
: ''
|
||||
} max-height: ${resolvedConfig.heightPx}px;`
|
||||
: ''}
|
||||
class={inRange
|
||||
? `${
|
||||
$allIdsInPath.includes(id)
|
||||
? 'overflow-visible'
|
||||
: resolvedConfig.heightPx
|
||||
? 'overflow-auto'
|
||||
: ''
|
||||
} ${!isCard ? 'w-full' : resolvedConfig?.displayBorders ? 'border' : ''}`
|
||||
: 'h-0 float overflow-hidden invisible absolute'}
|
||||
>
|
||||
<ListWrapper
|
||||
onSet={(id, value) => {
|
||||
if (!inputs[id]) {
|
||||
inputs[id] = { [index]: value }
|
||||
} else {
|
||||
inputs[id] = { ...inputs[id], [index]: value }
|
||||
}
|
||||
outputs?.inputs.set(inputs, true)
|
||||
}}
|
||||
onRemove={(id) => {
|
||||
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)
|
||||
}}
|
||||
{value}
|
||||
{index}
|
||||
<div
|
||||
class="w-full h-full shrink flex {$allIdsInPath.includes(id) && $mode == 'dnd'
|
||||
? 'overflow-visible'
|
||||
: 'overflow-auto'} {isCard
|
||||
? 'gap-2 flex-wrap'
|
||||
: resolvedConfig?.displayBorders
|
||||
? 'divide-y flex-col'
|
||||
: 'flex-col'}"
|
||||
>
|
||||
{#if $app.subgrids?.[`${id}-0`]}
|
||||
{#if Array.isArray(result) && result.length > 0}
|
||||
{#each result ?? [] as value, index (index)}
|
||||
{@const inRange = index <= pagination.maxIndex && index >= pagination.indexOffset}
|
||||
<div
|
||||
style={inRange
|
||||
? `${
|
||||
isCard
|
||||
? `min-width: ${resolvedConfig.width?.configuration?.card?.minWidthPx}px; `
|
||||
: ''
|
||||
} max-height: ${resolvedConfig.heightPx}px;`
|
||||
: ''}
|
||||
class={inRange
|
||||
? `${
|
||||
$allIdsInPath.includes(id)
|
||||
? 'overflow-visible'
|
||||
: resolvedConfig.heightPx
|
||||
? 'overflow-auto'
|
||||
: ''
|
||||
} ${!isCard ? 'w-full' : resolvedConfig?.displayBorders ? 'border' : ''}`
|
||||
: 'h-0 float overflow-hidden invisible absolute'}
|
||||
>
|
||||
<SubGridEditor
|
||||
visible={render && inRange}
|
||||
{id}
|
||||
subGridId={`${id}-0`}
|
||||
containerHeight={resolvedConfig.heightPx}
|
||||
on:focus={() => {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = [id]
|
||||
<ListWrapper
|
||||
onSet={(id, value) => {
|
||||
if (!inputs[id]) {
|
||||
inputs[id] = { [index]: value }
|
||||
} else {
|
||||
inputs[id] = { ...inputs[id], [index]: value }
|
||||
}
|
||||
onFocus()
|
||||
outputs?.inputs.set(inputs, true)
|
||||
}}
|
||||
/>
|
||||
</ListWrapper>
|
||||
</div>
|
||||
{/each}
|
||||
{:else}
|
||||
<ListWrapper disabled value={undefined} index={0}>
|
||||
<SubGridEditor visible={false} {id} subGridId={`${id}-0`} />
|
||||
</ListWrapper>
|
||||
{#if !Array.isArray(result)}
|
||||
<div class="text-center text-tertiary">Input data is not an array</div>
|
||||
onRemove={(id) => {
|
||||
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)
|
||||
}}
|
||||
{value}
|
||||
{index}
|
||||
>
|
||||
<SubGridEditor
|
||||
visible={render && inRange}
|
||||
{id}
|
||||
subGridId={`${id}-0`}
|
||||
containerHeight={resolvedConfig.heightPx}
|
||||
on:focus={() => {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = [id]
|
||||
}
|
||||
onFocus()
|
||||
}}
|
||||
/>
|
||||
</ListWrapper>
|
||||
</div>
|
||||
{/each}
|
||||
{:else}
|
||||
<ListWrapper disabled value={undefined} index={0}>
|
||||
<SubGridEditor visible={false} {id} subGridId={`${id}-0`} />
|
||||
</ListWrapper>
|
||||
{#if !Array.isArray(result)}
|
||||
<div class="text-center text-tertiary">Input data is not an array</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
{#if pagination.shouldDisplayPagination}
|
||||
<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"
|
||||
color="light"
|
||||
btnClasses="flex flex-row gap-1 items-center wm-list-pagination-buttons"
|
||||
on:click={() => {
|
||||
isPreviousLoading = true
|
||||
page = page - 1
|
||||
outputs?.page.set(page, true)
|
||||
}}
|
||||
disabled={page === 0}
|
||||
>
|
||||
{#if isPreviousLoading && loading}
|
||||
<Loader2 size={14} class="animate-spin" />
|
||||
{:else}
|
||||
<ChevronLeft size={14} />
|
||||
{/if}
|
||||
Previous
|
||||
</Button>
|
||||
<Button
|
||||
size="xs2"
|
||||
variant="border"
|
||||
color="light"
|
||||
btnClasses="flex flex-row gap-1 items-center wm-list-pagination-buttons"
|
||||
on:click={() => {
|
||||
isNextLoading = true
|
||||
page = page + 1
|
||||
outputs?.page.set(page, true)
|
||||
}}
|
||||
disabled={pagination.disableNext && pagination.total > 0}
|
||||
>
|
||||
Next
|
||||
|
||||
{#if isNextLoading && loading}
|
||||
<Loader2 size={14} class="animate-spin" />
|
||||
{:else}
|
||||
<ChevronRight size={14} />
|
||||
{/if}
|
||||
</Button>
|
||||
<div class="text-xs"
|
||||
>{page + 1} {pagination.total > 0 ? `of ${pagination.total}` : ''}</div
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if pagination.shouldDisplayPagination}
|
||||
<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"
|
||||
color="light"
|
||||
btnClasses="flex flex-row gap-1 items-center wm-list-pagination-buttons"
|
||||
on:click={() => {
|
||||
isPreviousLoading = true
|
||||
page = page - 1
|
||||
outputs?.page.set(page, true)
|
||||
}}
|
||||
disabled={page === 0}
|
||||
>
|
||||
{#if isPreviousLoading && loading}
|
||||
<Loader2 size={14} class="animate-spin" />
|
||||
{:else}
|
||||
<ChevronLeft size={14} />
|
||||
{/if}
|
||||
Previous
|
||||
</Button>
|
||||
<Button
|
||||
size="xs2"
|
||||
variant="border"
|
||||
color="light"
|
||||
btnClasses="flex flex-row gap-1 items-center wm-list-pagination-buttons"
|
||||
on:click={() => {
|
||||
isNextLoading = true
|
||||
page = page + 1
|
||||
outputs?.page.set(page, true)
|
||||
}}
|
||||
disabled={pagination.disableNext && pagination.total > 0}
|
||||
>
|
||||
Next
|
||||
|
||||
{#if isNextLoading && loading}
|
||||
<Loader2 size={14} class="animate-spin" />
|
||||
{:else}
|
||||
<ChevronRight size={14} />
|
||||
{/if}
|
||||
</Button>
|
||||
<div class="text-xs">{page + 1} {pagination.total > 0 ? `of ${pagination.total}` : ''}</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if $app.subgrids}
|
||||
<ListWrapper disabled value={undefined} index={0}>
|
||||
<SubGridEditor visible={false} {id} subGridId={`${id}-0`} />
|
||||
</ListWrapper>
|
||||
{/if}
|
||||
</RunnableWrapper>
|
||||
|
||||
@@ -40,6 +40,9 @@
|
||||
breakpoint
|
||||
} = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let everRender = render
|
||||
$: render && !everRender && (everRender = true)
|
||||
|
||||
//used so that we can count number of outputs setup for first refresh
|
||||
const outputs = initOutput($worldStore, id, {
|
||||
open: false
|
||||
@@ -110,7 +113,7 @@
|
||||
/>
|
||||
{/each}
|
||||
|
||||
{#if render}
|
||||
{#if everRender}
|
||||
<div class="h-full w-full">
|
||||
<AlignWrapper {noWFull} {horizontalAlignment} {verticalAlignment}>
|
||||
<Button
|
||||
@@ -144,92 +147,94 @@
|
||||
</Button>
|
||||
</AlignWrapper>
|
||||
</div>
|
||||
{/if}
|
||||
<Portal target="#app-editor-top-level-drawer" name="app-modal">
|
||||
<Disposable
|
||||
{id}
|
||||
let:handleClickAway
|
||||
let:zIndex
|
||||
let:open
|
||||
bind:this={disposable}
|
||||
on:open={() => {
|
||||
outputs?.open.set(true)
|
||||
onOpenRecomputeIds?.forEach((id) => $runnableComponents?.[id]?.cb?.map((cb) => cb?.()))
|
||||
}}
|
||||
on:close={() => {
|
||||
outputs?.open.set(false)
|
||||
onCloseRecomputeIds?.forEach((id) => $runnableComponents?.[id]?.cb?.map((cb) => cb?.()))
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class={twMerge(
|
||||
`${
|
||||
$mode == 'dnd' ? 'absolute' : 'fixed'
|
||||
} top-0 bottom-0 left-0 right-0 transition-all duration-50`,
|
||||
open ? ' bg-black bg-opacity-60' : 'h-0 overflow-hidden invisible'
|
||||
)}
|
||||
style="z-index: {zIndex}"
|
||||
bind:clientHeight={wrapperHeight}
|
||||
<Portal target="#app-editor-top-level-drawer" name="app-modal">
|
||||
<Disposable
|
||||
{id}
|
||||
let:handleClickAway
|
||||
let:zIndex
|
||||
let:open
|
||||
bind:this={disposable}
|
||||
on:open={() => {
|
||||
outputs?.open.set(true)
|
||||
onOpenRecomputeIds?.forEach((id) => $runnableComponents?.[id]?.cb?.map((cb) => cb?.()))
|
||||
}}
|
||||
on:close={() => {
|
||||
outputs?.open.set(false)
|
||||
onCloseRecomputeIds?.forEach((id) => $runnableComponents?.[id]?.cb?.map((cb) => cb?.()))
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={css?.popup?.style}
|
||||
class={twMerge('mx-24 mt-8 bg-surface rounded-lg relative', css?.popup?.class)}
|
||||
use:clickOutside={false}
|
||||
on:click_outside={(e) => {
|
||||
if ($mode !== 'dnd' && !unclickableOutside) {
|
||||
handleClickAway(e)
|
||||
}
|
||||
}}
|
||||
class={twMerge(
|
||||
`${
|
||||
$mode == 'dnd' ? 'absolute' : 'fixed'
|
||||
} top-0 bottom-0 left-0 right-0 transition-all duration-50`,
|
||||
open ? ' bg-black bg-opacity-60' : 'h-0 overflow-hidden invisible'
|
||||
)}
|
||||
style="z-index: {zIndex}"
|
||||
bind:clientHeight={wrapperHeight}
|
||||
>
|
||||
<div
|
||||
class="px-4 py-2 border-b flex justify-between items-center"
|
||||
bind:clientHeight={headerHeight}
|
||||
>
|
||||
<div>{resolvedConfig.modalTitle}</div>
|
||||
<div class="w-8">
|
||||
<button
|
||||
on:click|stopPropagation={() => {
|
||||
disposable?.closeDrawer()
|
||||
}}
|
||||
class="hover:bg-surface-hover bg-surface-secondary rounded-full w-8 h-8 flex items-center justify-center transition-all"
|
||||
>
|
||||
<X class="text-tertiary" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class={twMerge('wm-modal h-full', 'overflow-y-auto')}
|
||||
on:pointerdown={(e) => {
|
||||
e?.stopPropagation()
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = [id]
|
||||
$focusedGrid = {
|
||||
parentComponentId: id,
|
||||
subGridIndex: 0
|
||||
}
|
||||
style={css?.popup?.style}
|
||||
class={twMerge('mx-24 mt-8 bg-surface rounded-lg relative', css?.popup?.class)}
|
||||
use:clickOutside={false}
|
||||
on:click_outside={(e) => {
|
||||
if ($mode !== 'dnd' && !unclickableOutside) {
|
||||
handleClickAway(e)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#if $app.subgrids?.[`${id}-0`]}
|
||||
<SubGridEditor
|
||||
visible={open && render}
|
||||
{id}
|
||||
{containerHeight}
|
||||
subGridId={`${id}-0`}
|
||||
on:focus={() => {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = [id]
|
||||
$focusedGrid = {
|
||||
parentComponentId: id,
|
||||
subGridIndex: 0
|
||||
}
|
||||
<div
|
||||
class="px-4 py-2 border-b flex justify-between items-center"
|
||||
bind:clientHeight={headerHeight}
|
||||
>
|
||||
<div>{resolvedConfig.modalTitle}</div>
|
||||
<div class="w-8">
|
||||
<button
|
||||
on:click|stopPropagation={() => {
|
||||
disposable?.closeDrawer()
|
||||
}}
|
||||
class="hover:bg-surface-hover bg-surface-secondary rounded-full w-8 h-8 flex items-center justify-center transition-all"
|
||||
>
|
||||
<X class="text-tertiary" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class={twMerge('wm-modal h-full', 'overflow-y-auto')}
|
||||
on:pointerdown={(e) => {
|
||||
e?.stopPropagation()
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = [id]
|
||||
$focusedGrid = {
|
||||
parentComponentId: id,
|
||||
subGridIndex: 0
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#if $app.subgrids?.[`${id}-0`]}
|
||||
<SubGridEditor
|
||||
visible={open && render}
|
||||
{id}
|
||||
{containerHeight}
|
||||
subGridId={`${id}-0`}
|
||||
on:focus={() => {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = [id]
|
||||
$focusedGrid = {
|
||||
parentComponentId: id,
|
||||
subGridIndex: 0
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Disposable>
|
||||
</Portal>
|
||||
</Disposable>
|
||||
</Portal>
|
||||
{:else if $app.subgrids?.[`${id}-0`]}
|
||||
<SubGridEditor visible={false} {id} subGridId={`${id}-0`} />
|
||||
{/if}
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
//used so that we can count number of outputs setup for first refresh
|
||||
initOutput($worldStore, id, {})
|
||||
|
||||
let everRender = render
|
||||
|
||||
$: render && !everRender && (everRender = true)
|
||||
|
||||
function onFocus() {
|
||||
$focusedGrid = {
|
||||
parentComponentId: id,
|
||||
@@ -82,47 +86,53 @@
|
||||
|
||||
<InitializeComponent {id} />
|
||||
|
||||
<div class="h-full w-full border" on:pointerdown={onFocus}>
|
||||
{#key sumedup}
|
||||
<Splitpanes {horizontal}>
|
||||
{#each sumedup as paneSize, index (index)}
|
||||
<Pane size={paneSize} minSize={20}>
|
||||
<div
|
||||
class="w-full h-full"
|
||||
on:pointerdown|stopPropagation={() => {
|
||||
$selectedComponent = [id]
|
||||
$focusedGrid = {
|
||||
parentComponentId: id,
|
||||
subGridIndex: index
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#if $app.subgrids?.[`${id}-${index}`]}
|
||||
<SubGridEditor
|
||||
visible={render}
|
||||
{id}
|
||||
shouldHighlight={$focusedGrid?.subGridIndex === index}
|
||||
class={twMerge(
|
||||
css?.container?.class,
|
||||
horizontal ? 'wm-horizontal-split-panes' : 'wm-vertical-split-panes'
|
||||
)}
|
||||
style={css?.container?.style}
|
||||
subGridId={`${id}-${index}`}
|
||||
containerHeight={horizontal ? undefined : componentContainerHeight - 8}
|
||||
on:focus={() => {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = [id]
|
||||
$focusedGrid = {
|
||||
parentComponentId: id,
|
||||
subGridIndex: index
|
||||
{#if everRender}
|
||||
<div class="h-full w-full border" on:pointerdown={onFocus}>
|
||||
{#key sumedup}
|
||||
<Splitpanes {horizontal}>
|
||||
{#each sumedup as paneSize, index (index)}
|
||||
<Pane size={paneSize} minSize={20}>
|
||||
<div
|
||||
class="w-full h-full"
|
||||
on:pointerdown|stopPropagation={() => {
|
||||
$selectedComponent = [id]
|
||||
$focusedGrid = {
|
||||
parentComponentId: id,
|
||||
subGridIndex: index
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#if $app.subgrids?.[`${id}-${index}`]}
|
||||
<SubGridEditor
|
||||
visible={render}
|
||||
{id}
|
||||
shouldHighlight={$focusedGrid?.subGridIndex === index}
|
||||
class={twMerge(
|
||||
css?.container?.class,
|
||||
horizontal ? 'wm-horizontal-split-panes' : 'wm-vertical-split-panes'
|
||||
)}
|
||||
style={css?.container?.style}
|
||||
subGridId={`${id}-${index}`}
|
||||
containerHeight={horizontal ? undefined : componentContainerHeight - 8}
|
||||
on:focus={() => {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = [id]
|
||||
$focusedGrid = {
|
||||
parentComponentId: id,
|
||||
subGridIndex: index
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</Pane>
|
||||
{/each}
|
||||
</Splitpanes>
|
||||
{/key}
|
||||
</div>
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</Pane>
|
||||
{/each}
|
||||
</Splitpanes>
|
||||
{/key}
|
||||
</div>
|
||||
{:else}
|
||||
{#each sumedup as _paneSize, index (index)}
|
||||
<SubGridEditor visible={false} {id} subGridId={`${id}-${index}`} />
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
runnableComponents
|
||||
} = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let everRender = render
|
||||
$: render && !everRender && (everRender = true)
|
||||
|
||||
let selected = tabs[0]
|
||||
let tabHeight: number = 0
|
||||
let footerHeight: number = 0
|
||||
@@ -155,97 +158,109 @@
|
||||
bind:result
|
||||
errorHandledByComponent={true}
|
||||
>
|
||||
<div class="w-full overflow-auto">
|
||||
<div bind:clientHeight={tabHeight}>
|
||||
<Stepper
|
||||
on:click={(e) => {
|
||||
const index = e.detail.index
|
||||
if (index <= maxReachedIndex || $mode === 'dnd') {
|
||||
runStep(index)
|
||||
}
|
||||
}}
|
||||
{tabs}
|
||||
{selectedIndex}
|
||||
{maxReachedIndex}
|
||||
{statusByStep}
|
||||
hasValidations={Boolean(runnableComponent)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="w-full">
|
||||
{#if $app.subgrids}
|
||||
{#each tabs ?? [] as _res, i}
|
||||
<SubGridEditor
|
||||
{id}
|
||||
visible={render && i === selectedIndex}
|
||||
subGridId={`${id}-${i}`}
|
||||
class={twMerge(css?.container?.class, 'wm-stepper')}
|
||||
style={css?.container?.style}
|
||||
containerHeight={componentContainerHeight - tabHeight - footerHeight}
|
||||
on:focus={() => {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = [id]
|
||||
handleTabSelection()
|
||||
{#if everRender}
|
||||
<div class="w-full overflow-auto">
|
||||
{#if render}
|
||||
<div bind:clientHeight={tabHeight}>
|
||||
<Stepper
|
||||
on:click={(e) => {
|
||||
const index = e.detail.index
|
||||
if (index <= maxReachedIndex || $mode === 'dnd') {
|
||||
runStep(index)
|
||||
}
|
||||
}}
|
||||
{tabs}
|
||||
{selectedIndex}
|
||||
{maxReachedIndex}
|
||||
{statusByStep}
|
||||
hasValidations={Boolean(runnableComponent)}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="w-full">
|
||||
{#if $app.subgrids}
|
||||
{#each tabs ?? [] as _res, i}
|
||||
<SubGridEditor
|
||||
{id}
|
||||
visible={render && i === selectedIndex}
|
||||
subGridId={`${id}-${i}`}
|
||||
class={twMerge(css?.container?.class, 'wm-stepper')}
|
||||
style={css?.container?.style}
|
||||
containerHeight={componentContainerHeight - tabHeight - footerHeight}
|
||||
on:focus={() => {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = [id]
|
||||
handleTabSelection()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if render}
|
||||
<div bind:clientHeight={footerHeight}>
|
||||
<div class="flex justify-between h-10 p-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-sm font-medium text-tertiary">
|
||||
Step {selectedIndex + 1} of {tabs.length}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
variant="contained"
|
||||
disabled={selectedIndex === 0}
|
||||
on:click={(e) => {
|
||||
e.preventDefault()
|
||||
directionClicked = 'left'
|
||||
onPrevious?.forEach((id) =>
|
||||
$runnableComponents?.[id]?.cb?.forEach((cb) => cb?.())
|
||||
)
|
||||
runStep(selectedIndex - 1)
|
||||
}}
|
||||
>
|
||||
<div class="flex flex-row gap-2">
|
||||
{#if statusByStep[selectedIndex] === 'pending' && directionClicked === 'left'}
|
||||
<Loader2 class="w-4 h-4 animate-spin" />
|
||||
{:else}
|
||||
<ArrowLeftIcon class="w-4 h-4" />
|
||||
{/if}
|
||||
Previous
|
||||
</div>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
size="xs"
|
||||
color="dark"
|
||||
variant="contained"
|
||||
disabled={lastStep}
|
||||
on:click={(e) => {
|
||||
e.preventDefault()
|
||||
directionClicked = 'right'
|
||||
onNext?.forEach((id) => $runnableComponents?.[id]?.cb?.forEach((cb) => cb?.()))
|
||||
runStep(selectedIndex + 1)
|
||||
}}
|
||||
>
|
||||
<div class="flex flex-row gap-2">
|
||||
Next
|
||||
{#if statusByStep[selectedIndex] === 'pending' && directionClicked === 'right'}
|
||||
<Loader2 class="w-4 h-4 animate-spin" />
|
||||
{:else}
|
||||
<ArrowRightIcon class="w-4 h-4" />
|
||||
{/if}
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div bind:clientHeight={footerHeight}>
|
||||
<div class="flex justify-between h-10 p-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-sm font-medium text-tertiary">
|
||||
Step {selectedIndex + 1} of {tabs.length}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
variant="contained"
|
||||
disabled={selectedIndex === 0}
|
||||
on:click={(e) => {
|
||||
e.preventDefault()
|
||||
directionClicked = 'left'
|
||||
onPrevious?.forEach((id) => $runnableComponents?.[id]?.cb?.forEach((cb) => cb?.()))
|
||||
runStep(selectedIndex - 1)
|
||||
}}
|
||||
>
|
||||
<div class="flex flex-row gap-2">
|
||||
{#if statusByStep[selectedIndex] === 'pending' && directionClicked === 'left'}
|
||||
<Loader2 class="w-4 h-4 animate-spin" />
|
||||
{:else}
|
||||
<ArrowLeftIcon class="w-4 h-4" />
|
||||
{/if}
|
||||
Previous
|
||||
</div>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
size="xs"
|
||||
color="dark"
|
||||
variant="contained"
|
||||
disabled={lastStep}
|
||||
on:click={(e) => {
|
||||
e.preventDefault()
|
||||
directionClicked = 'right'
|
||||
onNext?.forEach((id) => $runnableComponents?.[id]?.cb?.forEach((cb) => cb?.()))
|
||||
runStep(selectedIndex + 1)
|
||||
}}
|
||||
>
|
||||
<div class="flex flex-row gap-2">
|
||||
Next
|
||||
{#if statusByStep[selectedIndex] === 'pending' && directionClicked === 'right'}
|
||||
<Loader2 class="w-4 h-4 animate-spin" />
|
||||
{:else}
|
||||
<ArrowRightIcon class="w-4 h-4" />
|
||||
{/if}
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else if $app.subgrids}
|
||||
{#each tabs ?? [] as _res, i}
|
||||
<SubGridEditor {id} visible={false} subGridId={`${id}-${i}`} />
|
||||
{/each}
|
||||
{/if}
|
||||
</RunnableWrapper>
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
components['tabscomponent'].initialData.configuration,
|
||||
configuration
|
||||
)
|
||||
let everRender = render
|
||||
$: render && !everRender && (everRender = true)
|
||||
|
||||
const {
|
||||
app,
|
||||
@@ -117,128 +119,134 @@
|
||||
/>
|
||||
{/each}
|
||||
|
||||
<div class={resolvedConfig.tabsKind == 'sidebar' ? 'flex gap-4 w-full h-full' : 'w-full'}>
|
||||
{#if !resolvedConfig.tabsKind || resolvedConfig.tabsKind == 'tabs' || (resolvedConfig.tabsKind == 'invisibleOnView' && $mode == 'dnd')}
|
||||
<div bind:clientHeight={tabHeight}>
|
||||
<Tabs
|
||||
bind:selected
|
||||
class={twMerge(css?.tabRow?.class, 'wm-tabs-tabRow')}
|
||||
{#if everRender}
|
||||
<div class={resolvedConfig.tabsKind == 'sidebar' ? 'flex gap-4 w-full h-full' : 'w-full'}>
|
||||
{#if !resolvedConfig.tabsKind || resolvedConfig.tabsKind == 'tabs' || (resolvedConfig.tabsKind == 'invisibleOnView' && $mode == 'dnd')}
|
||||
<div bind:clientHeight={tabHeight}>
|
||||
<Tabs
|
||||
bind:selected
|
||||
class={twMerge(css?.tabRow?.class, 'wm-tabs-tabRow')}
|
||||
style={css?.tabRow?.style}
|
||||
>
|
||||
{#each tabs ?? [] as res, index}
|
||||
<Tab
|
||||
value={res}
|
||||
class={twMerge(css?.allTabs?.class, 'wm-tabs-alltabs')}
|
||||
style={css?.allTabs?.style}
|
||||
selectedClass={twMerge(css?.selectedTab?.class, 'wm-tabs-selectedTab')}
|
||||
selectedStyle={css?.selectedTab?.style}
|
||||
disabled={resolvedDisabledTabs[index]}
|
||||
>
|
||||
<span class="font-semibold">{res}</span>
|
||||
</Tab>
|
||||
{/each}
|
||||
</Tabs>
|
||||
</div>
|
||||
{:else if resolvedConfig.tabsKind == 'sidebar'}
|
||||
<div
|
||||
class={twMerge(
|
||||
'flex gap-y-2 flex-col w-1/6 max-w-[160px] bg-surface text-secondary opacity-80 px-4 pt-4 border-r ',
|
||||
css?.tabRow?.class,
|
||||
'wm-tabs-tabRow'
|
||||
)}
|
||||
style={css?.tabRow?.style}
|
||||
>
|
||||
{#each tabs ?? [] as res, index}
|
||||
<Tab
|
||||
value={res}
|
||||
class={twMerge(css?.allTabs?.class, 'wm-tabs-alltabs')}
|
||||
style={css?.allTabs?.style}
|
||||
selectedClass={twMerge(css?.selectedTab?.class, 'wm-tabs-selectedTab')}
|
||||
selectedStyle={css?.selectedTab?.style}
|
||||
disabled={resolvedDisabledTabs[index]}
|
||||
>
|
||||
<span class="font-semibold">{res}</span>
|
||||
</Tab>
|
||||
{/each}
|
||||
</Tabs>
|
||||
</div>
|
||||
{:else if resolvedConfig.tabsKind == 'sidebar'}
|
||||
<div
|
||||
class={twMerge(
|
||||
'flex gap-y-2 flex-col w-1/6 max-w-[160px] bg-surface text-secondary opacity-80 px-4 pt-4 border-r ',
|
||||
css?.tabRow?.class,
|
||||
'wm-tabs-tabRow'
|
||||
)}
|
||||
style={css?.tabRow?.style}
|
||||
>
|
||||
{#each tabs ?? [] as res}
|
||||
<button
|
||||
on:pointerdown|stopPropagation
|
||||
on:click={() => (selected = res)}
|
||||
class={twMerge(
|
||||
'rounded-sm !truncate text-sm hover:text-primary px-1 py-2',
|
||||
css?.allTabs?.class,
|
||||
'wm-tabs-alltabs',
|
||||
selected == res
|
||||
? twMerge(
|
||||
'border-r border-primary border-l bg-surface text-primary',
|
||||
css?.selectedTab?.class,
|
||||
'wm-tabs-selectedTab'
|
||||
)
|
||||
: ''
|
||||
)}
|
||||
style={selected == res
|
||||
? [css?.allTabs?.style, css?.selectedTab?.style].filter(Boolean).join(';')
|
||||
: css?.allTabs?.style}
|
||||
>
|
||||
{res}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{#if resolvedConfig.tabsKind == 'accordion'}
|
||||
<div class="flex flex-col w-full">
|
||||
{#each tabs ?? [] as res, index}
|
||||
<div class="border-b">
|
||||
{#each tabs ?? [] as res}
|
||||
<button
|
||||
on:pointerdown|stopPropagation
|
||||
on:click={() => (selected = res)}
|
||||
class={twMerge(
|
||||
'w-full text-left bg-surface !truncate text-sm hover:text-primary px-1 py-2',
|
||||
'rounded-sm !truncate text-sm hover:text-primary px-1 py-2',
|
||||
css?.allTabs?.class,
|
||||
'wm-tabs-alltabs',
|
||||
selected == res
|
||||
? twMerge(
|
||||
'bg-surface text-primary ',
|
||||
'border-r border-primary border-l bg-surface text-primary',
|
||||
css?.selectedTab?.class,
|
||||
'wm-tabs-selectedTab'
|
||||
)
|
||||
: 'text-secondary'
|
||||
: ''
|
||||
)}
|
||||
style={selected == res
|
||||
? [css?.allTabs?.style, css?.selectedTab?.style].filter(Boolean).join(';')
|
||||
: css?.allTabs?.style}
|
||||
>
|
||||
<span class="mr-2 w-8 font-mono">{selected == res ? '-' : '+'}</span>
|
||||
{res}
|
||||
</button>
|
||||
{#if selected == res}
|
||||
<div class="p-2 border-t">
|
||||
<SubGridEditor
|
||||
{id}
|
||||
visible={render && index === selectedIndex}
|
||||
subGridId={`${id}-${index}`}
|
||||
class={twMerge(css?.container?.class, 'wm-tabs-container')}
|
||||
style={css?.container?.style}
|
||||
containerHeight={componentContainerHeight - (titleBarHeight * tabs.length + 40)}
|
||||
on:focus={() => {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = [id]
|
||||
handleTabSelection()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="w-full">
|
||||
{#if $app.subgrids}
|
||||
{#each tabs ?? [] as _res, i}
|
||||
<SubGridEditor
|
||||
{id}
|
||||
visible={render && i === selectedIndex}
|
||||
subGridId={`${id}-${i}`}
|
||||
class={twMerge(css?.container?.class, 'wm-tabs-container')}
|
||||
style={css?.container?.style}
|
||||
containerHeight={resolvedConfig.tabsKind !== 'sidebar' && $mode !== 'preview'
|
||||
? componentContainerHeight - tabHeight
|
||||
: componentContainerHeight}
|
||||
on:focus={() => {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = [id]
|
||||
handleTabSelection()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if resolvedConfig.tabsKind == 'accordion'}
|
||||
<div class="flex flex-col w-full">
|
||||
{#each tabs ?? [] as res, index}
|
||||
<div class="border-b">
|
||||
<button
|
||||
on:pointerdown|stopPropagation
|
||||
on:click={() => (selected = res)}
|
||||
class={twMerge(
|
||||
'w-full text-left bg-surface !truncate text-sm hover:text-primary px-1 py-2',
|
||||
css?.allTabs?.class,
|
||||
'wm-tabs-alltabs',
|
||||
selected == res
|
||||
? twMerge(
|
||||
'bg-surface text-primary ',
|
||||
css?.selectedTab?.class,
|
||||
'wm-tabs-selectedTab'
|
||||
)
|
||||
: 'text-secondary'
|
||||
)}
|
||||
>
|
||||
<span class="mr-2 w-8 font-mono">{selected == res ? '-' : '+'}</span>
|
||||
{res}
|
||||
</button>
|
||||
{#if selected == res}
|
||||
<div class="p-2 border-t">
|
||||
<SubGridEditor
|
||||
{id}
|
||||
visible={render && index === selectedIndex}
|
||||
subGridId={`${id}-${index}`}
|
||||
class={twMerge(css?.container?.class, 'wm-tabs-container')}
|
||||
style={css?.container?.style}
|
||||
containerHeight={componentContainerHeight - (titleBarHeight * tabs.length + 40)}
|
||||
on:focus={() => {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = [id]
|
||||
handleTabSelection()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="w-full">
|
||||
{#if $app.subgrids}
|
||||
{#each tabs ?? [] as _res, i}
|
||||
<SubGridEditor
|
||||
{id}
|
||||
visible={render && i === selectedIndex}
|
||||
subGridId={`${id}-${i}`}
|
||||
class={twMerge(css?.container?.class, 'wm-tabs-container')}
|
||||
style={css?.container?.style}
|
||||
containerHeight={resolvedConfig.tabsKind !== 'sidebar' && $mode !== 'preview'
|
||||
? componentContainerHeight - tabHeight
|
||||
: componentContainerHeight}
|
||||
on:focus={() => {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = [id]
|
||||
handleTabSelection()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if $app.subgrids}
|
||||
{#each tabs ?? [] as _res, i}
|
||||
<SubGridEditor {id} visible={false} subGridId={`${id}-${i}`} />
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
import GridViewer from './GridViewer.svelte'
|
||||
import Component from './component/Component.svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { columnConfiguration } from '../gridUtils'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import { dfs, maxHeight } from './appUtils'
|
||||
import { BG_PREFIX, migrateApp } from '../utils'
|
||||
@@ -292,8 +291,6 @@
|
||||
allIdsInPath={$allIdsInPath}
|
||||
items={app.grid}
|
||||
let:dataItem
|
||||
let:hidden
|
||||
cols={columnConfiguration}
|
||||
{maxRow}
|
||||
breakpoint={$breakpoint}
|
||||
>
|
||||
@@ -308,7 +305,6 @@
|
||||
selected={false}
|
||||
locked={true}
|
||||
fullHeight={dataItem?.[$breakpoint === 'sm' ? 3 : 12]?.fullHeight}
|
||||
{hidden}
|
||||
/>
|
||||
</div>
|
||||
</GridViewer>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppEditorContext, AppViewerContext } from '../types'
|
||||
import { columnConfiguration, gridColumns, isFixed, toggleFixed } from '../gridUtils'
|
||||
import { gridColumns, isFixed, toggleFixed } from '../gridUtils'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
import HiddenComponent from '../components/helpers/HiddenComponent.svelte'
|
||||
@@ -195,11 +195,9 @@
|
||||
}}
|
||||
root
|
||||
let:dataItem
|
||||
let:hidden
|
||||
let:overlapped
|
||||
let:moveMode
|
||||
let:componentDraggedId
|
||||
cols={columnConfiguration}
|
||||
on:dropped={(e) => {
|
||||
const { id, overlapped, x, y } = e.detail
|
||||
|
||||
@@ -252,7 +250,6 @@
|
||||
fullHeight={dataItem?.[$breakpoint === 'sm' ? 3 : 12]?.fullHeight}
|
||||
>
|
||||
<Component
|
||||
{hidden}
|
||||
render={true}
|
||||
component={dataItem.data}
|
||||
selected={Boolean($selectedComponent?.includes(dataItem.id))}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { columnConfiguration, WIDE_GRID_COLUMNS } from '../gridUtils'
|
||||
|
||||
import { ROW_GAP_X, ROW_GAP_Y, ROW_HEIGHT } from './appUtils'
|
||||
|
||||
import type { EditorBreakpoint } from '../types'
|
||||
import type { AppViewerContext, EditorBreakpoint } from '../types'
|
||||
|
||||
import { onMount, createEventDispatcher } from 'svelte'
|
||||
import { onMount, createEventDispatcher, getContext } from 'svelte'
|
||||
|
||||
import type { FilledItem } from '../svelte-grid/types'
|
||||
import { getColumn, throttle } from '../svelte-grid/utils/other'
|
||||
@@ -14,9 +16,10 @@
|
||||
|
||||
type T = $$Generic
|
||||
|
||||
const { app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
export let items: FilledItem<T>[]
|
||||
export let rowHeight: number = ROW_HEIGHT
|
||||
export let cols: [number, number][]
|
||||
export let gap = [ROW_GAP_X, ROW_GAP_Y]
|
||||
export let throttleUpdate = 100
|
||||
export let maxRow: number
|
||||
@@ -27,12 +30,19 @@
|
||||
|
||||
export let parentWidth: number | undefined = undefined
|
||||
|
||||
let getComputedCols
|
||||
const cols = columnConfiguration
|
||||
|
||||
let showSkeleton = false
|
||||
|
||||
let getComputedCols: 3 | 12 | undefined =
|
||||
$app.mobileViewOnSmallerScreens == false ? WIDE_GRID_COLUMNS : undefined
|
||||
|
||||
let container
|
||||
|
||||
$: [gapX, gapY] = gap
|
||||
|
||||
let xPerPx = 0
|
||||
|
||||
let yPerPx = rowHeight
|
||||
|
||||
$: containerHeight = getContainerHeight(items, yPerPx, getComputedCols)
|
||||
@@ -48,15 +58,19 @@
|
||||
}, throttleUpdate)
|
||||
|
||||
onMount(() => {
|
||||
setTimeout(() => {
|
||||
showSkeleton = true
|
||||
}, 100)
|
||||
const sizeObserver = new ResizeObserver((entries) => {
|
||||
requestAnimationFrame(() => {
|
||||
let width = entries[0].contentRect.width
|
||||
|
||||
if (width === containerWidth) return
|
||||
if ($app.mobileViewOnSmallerScreens != false || !getComputedCols) {
|
||||
getComputedCols = getColumn(parentWidth ?? width, cols)
|
||||
}
|
||||
|
||||
getComputedCols = getColumn(parentWidth ?? width, cols)
|
||||
|
||||
xPerPx = width / getComputedCols
|
||||
xPerPx = width / getComputedCols!
|
||||
|
||||
if (!containerWidth) {
|
||||
items = specifyUndefinedColumns(items, getComputedCols, cols)
|
||||
@@ -81,7 +95,7 @@
|
||||
</script>
|
||||
|
||||
<div class="svlt-grid-container" style="height: {containerHeight}px" bind:this={container}>
|
||||
{#if xPerPx}
|
||||
{#if xPerPx && getComputedCols}
|
||||
{#each items as item (item.id)}
|
||||
{@const onTop = allIdsInPath?.includes(item.id)}
|
||||
{@const width =
|
||||
@@ -103,10 +117,14 @@
|
||||
: ''} top: {top}px; left: {left}px;"
|
||||
>
|
||||
{#if item[getComputedCols]}
|
||||
<slot dataItem={item} item={item[getComputedCols]} hidden={false} />
|
||||
<slot dataItem={item} item={item[getComputedCols]} />
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
{:else if showSkeleton}
|
||||
<div
|
||||
class="h-full w-full flex-col animate-skeleton dark:bg-frost-900/50 [animation-delay:1000ms]"
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { classNames } from '$lib/utils'
|
||||
import { createEventDispatcher, getContext, onDestroy } from 'svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { columnConfiguration, gridColumns, isFixed, toggleFixed } from '../gridUtils'
|
||||
import { gridColumns, isFixed, toggleFixed } from '../gridUtils'
|
||||
import Grid from '../svelte-grid/Grid.svelte'
|
||||
import type { AppEditorContext, AppViewerContext, GridItem } from '../types'
|
||||
import {
|
||||
@@ -53,6 +53,10 @@
|
||||
let isActive = false
|
||||
let sber = editorContext?.componentActive?.subscribe((x) => (isActive = x))
|
||||
|
||||
let everVisible = visible
|
||||
|
||||
$: visible && !everVisible && (everVisible = true)
|
||||
|
||||
onDestroy(() => {
|
||||
sber?.()
|
||||
})
|
||||
@@ -173,160 +177,156 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="translate-x-0 translate-y-0 w-full subgrid {visible
|
||||
? 'visible'
|
||||
: 'invisible h-0 overflow-hidden'}"
|
||||
bind:this={container}
|
||||
on:pointerdown={onpointerdown}
|
||||
>
|
||||
{#if everVisible}
|
||||
<div
|
||||
class={twMerge(
|
||||
$allIdsInPath.includes(id) && $mode == 'dnd' ? 'overflow-visible' : 'overflow-auto',
|
||||
noYPadding ? '' : 'py-2',
|
||||
classes ?? '',
|
||||
noPadding ? 'px-0' : 'px-2'
|
||||
)}
|
||||
style="{containerHeight ? `height: ${containerHeight - 2}px;` : ''} {style ?? ''}"
|
||||
class="translate-x-0 translate-y-0 w-full subgrid {visible
|
||||
? 'visible'
|
||||
: 'invisible h-0 overflow-hidden'}"
|
||||
bind:this={container}
|
||||
on:pointerdown={onpointerdown}
|
||||
>
|
||||
{#if $mode !== 'preview'}
|
||||
<div
|
||||
class={highlight
|
||||
? `animate-border border-dashed border-2 min-h-full ${
|
||||
isActive && !$selectedComponent?.includes(id)
|
||||
? 'border-orange-600'
|
||||
: 'dark:border-gray-600 border-gray-400'
|
||||
}`
|
||||
: ''}
|
||||
>
|
||||
<Grid
|
||||
<div
|
||||
class={twMerge(
|
||||
$allIdsInPath.includes(id) && $mode == 'dnd' ? 'overflow-visible' : 'overflow-auto',
|
||||
noYPadding ? '' : 'py-2',
|
||||
classes ?? '',
|
||||
noPadding ? 'px-0' : 'px-2'
|
||||
)}
|
||||
style="{containerHeight ? `height: ${containerHeight - 2}px;` : ''} {style ?? ''}"
|
||||
>
|
||||
{#if $mode !== 'preview'}
|
||||
<div
|
||||
class={highlight
|
||||
? `outline !outline-dashed outline-2 min-h-full ${
|
||||
isActive && !$selectedComponent?.includes(id)
|
||||
? 'outline-orange-600'
|
||||
: 'outline-gray-400 dark:outline-gray-600'
|
||||
}`
|
||||
: ''}
|
||||
>
|
||||
<Grid
|
||||
allIdsInPath={$allIdsInPath}
|
||||
items={$app.subgrids?.[subGridId] ?? []}
|
||||
on:redraw={(e) => {
|
||||
push(editorContext?.history, $app)
|
||||
if ($app.subgrids) {
|
||||
$app.subgrids[subGridId] = e.detail
|
||||
}
|
||||
}}
|
||||
selectedIds={$selectedComponent}
|
||||
let:dataItem
|
||||
let:overlapped
|
||||
let:moveMode
|
||||
let:componentDraggedId
|
||||
scroller={container}
|
||||
parentWidth={$parentWidth - 17}
|
||||
{containerWidth}
|
||||
on:dropped={(e) => {
|
||||
const { id, overlapped, x, y } = e.detail
|
||||
|
||||
if (!overlapped) {
|
||||
moveToRoot(id, { x, y })
|
||||
} else {
|
||||
const overlappedComponent = findGridItem($app, overlapped)
|
||||
|
||||
if (overlappedComponent && !isContainer(overlappedComponent.data.type)) {
|
||||
return
|
||||
}
|
||||
if (!overlapped) {
|
||||
return
|
||||
}
|
||||
|
||||
if (id === overlapped) {
|
||||
return
|
||||
}
|
||||
|
||||
moveComponentBetweenSubgrids(
|
||||
id,
|
||||
overlapped,
|
||||
subGridIndexKey(overlappedComponent?.data?.type, overlapped, $worldStore),
|
||||
{ x, y }
|
||||
)
|
||||
}
|
||||
}}
|
||||
disableMove={!!$connectingInput.opened}
|
||||
>
|
||||
<ComponentWrapper
|
||||
id={dataItem.id}
|
||||
type={dataItem.data.type}
|
||||
class={classNames(
|
||||
'h-full w-full center-center',
|
||||
$selectedComponent?.includes(dataItem.id) ? 'active-grid-item' : '',
|
||||
'top-0'
|
||||
)}
|
||||
>
|
||||
<GridEditorMenu id={dataItem.id}>
|
||||
<Component
|
||||
{overlapped}
|
||||
fullHeight={dataItem?.[$breakpoint === 'sm' ? 3 : 12]?.fullHeight}
|
||||
render={visible}
|
||||
component={dataItem.data}
|
||||
selected={Boolean($selectedComponent?.includes(dataItem.id))}
|
||||
locked={isFixed(dataItem)}
|
||||
on:lock={() => lock(dataItem)}
|
||||
on:expand={() => {
|
||||
const parentGridItem = findGridItem($app, id)
|
||||
|
||||
if (!parentGridItem) {
|
||||
return
|
||||
}
|
||||
|
||||
$selectedComponent = [dataItem.id]
|
||||
push(editorContext?.history, $app)
|
||||
|
||||
expandGriditem(
|
||||
$app.subgrids?.[subGridId] ?? [],
|
||||
dataItem.id,
|
||||
$breakpoint,
|
||||
parentGridItem
|
||||
)
|
||||
$app = $app
|
||||
}}
|
||||
on:fillHeight={() => {
|
||||
const gridItem = findGridItem($app, dataItem.id)
|
||||
const b = $breakpoint === 'sm' ? 3 : 12
|
||||
|
||||
if (gridItem?.[b]) {
|
||||
gridItem[b].fullHeight = !gridItem[b].fullHeight
|
||||
}
|
||||
$app = $app
|
||||
}}
|
||||
{moveMode}
|
||||
{componentDraggedId}
|
||||
/>
|
||||
</GridEditorMenu>
|
||||
</ComponentWrapper>
|
||||
</Grid>
|
||||
</div>
|
||||
{:else}
|
||||
<GridViewer
|
||||
allIdsInPath={$allIdsInPath}
|
||||
items={$app.subgrids?.[subGridId] ?? []}
|
||||
on:redraw={(e) => {
|
||||
push(editorContext?.history, $app)
|
||||
if ($app.subgrids) {
|
||||
$app.subgrids[subGridId] = e.detail
|
||||
}
|
||||
}}
|
||||
selectedIds={$selectedComponent}
|
||||
let:dataItem
|
||||
let:hidden
|
||||
let:overlapped
|
||||
let:moveMode
|
||||
let:componentDraggedId
|
||||
cols={columnConfiguration}
|
||||
scroller={container}
|
||||
breakpoint={$breakpoint}
|
||||
parentWidth={$parentWidth - 17}
|
||||
{containerWidth}
|
||||
on:dropped={(e) => {
|
||||
const { id, overlapped, x, y } = e.detail
|
||||
|
||||
if (!overlapped) {
|
||||
moveToRoot(id, { x, y })
|
||||
} else {
|
||||
const overlappedComponent = findGridItem($app, overlapped)
|
||||
|
||||
if (overlappedComponent && !isContainer(overlappedComponent.data.type)) {
|
||||
return
|
||||
}
|
||||
if (!overlapped) {
|
||||
return
|
||||
}
|
||||
|
||||
if (id === overlapped) {
|
||||
return
|
||||
}
|
||||
|
||||
moveComponentBetweenSubgrids(
|
||||
id,
|
||||
overlapped,
|
||||
subGridIndexKey(overlappedComponent?.data?.type, overlapped, $worldStore),
|
||||
{ x, y }
|
||||
)
|
||||
}
|
||||
}}
|
||||
disableMove={!!$connectingInput.opened}
|
||||
{maxRow}
|
||||
>
|
||||
<ComponentWrapper
|
||||
id={dataItem.id}
|
||||
type={dataItem.data.type}
|
||||
class={classNames(
|
||||
'h-full w-full center-center',
|
||||
$selectedComponent?.includes(dataItem.id) ? 'active-grid-item' : '',
|
||||
'top-0'
|
||||
)}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
on:pointerdown|stopPropagation={(e) => selectComponent(e, dataItem.id)}
|
||||
class={classNames('h-full w-full center-center', 'top-0')}
|
||||
>
|
||||
<GridEditorMenu id={dataItem.id}>
|
||||
<Component
|
||||
{hidden}
|
||||
{overlapped}
|
||||
fullHeight={dataItem?.[$breakpoint === 'sm' ? 3 : 12]?.fullHeight}
|
||||
render={visible}
|
||||
component={dataItem.data}
|
||||
selected={Boolean($selectedComponent?.includes(dataItem.id))}
|
||||
locked={isFixed(dataItem)}
|
||||
on:lock={() => lock(dataItem)}
|
||||
on:expand={() => {
|
||||
const parentGridItem = findGridItem($app, id)
|
||||
|
||||
if (!parentGridItem) {
|
||||
return
|
||||
}
|
||||
|
||||
$selectedComponent = [dataItem.id]
|
||||
push(editorContext?.history, $app)
|
||||
|
||||
expandGriditem(
|
||||
$app.subgrids?.[subGridId] ?? [],
|
||||
dataItem.id,
|
||||
$breakpoint,
|
||||
parentGridItem
|
||||
)
|
||||
$app = $app
|
||||
}}
|
||||
on:fillHeight={() => {
|
||||
const gridItem = findGridItem($app, dataItem.id)
|
||||
const b = $breakpoint === 'sm' ? 3 : 12
|
||||
|
||||
if (gridItem?.[b]) {
|
||||
gridItem[b].fullHeight = !gridItem[b].fullHeight
|
||||
}
|
||||
$app = $app
|
||||
}}
|
||||
{moveMode}
|
||||
{componentDraggedId}
|
||||
/>
|
||||
</GridEditorMenu>
|
||||
</ComponentWrapper>
|
||||
</Grid>
|
||||
</div>
|
||||
{:else}
|
||||
<GridViewer
|
||||
allIdsInPath={$allIdsInPath}
|
||||
items={$app.subgrids?.[subGridId] ?? []}
|
||||
let:dataItem
|
||||
let:hidden
|
||||
cols={columnConfiguration}
|
||||
breakpoint={$breakpoint}
|
||||
parentWidth={$parentWidth - 17}
|
||||
{containerWidth}
|
||||
{maxRow}
|
||||
>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
on:pointerdown|stopPropagation={(e) => selectComponent(e, dataItem.id)}
|
||||
class={classNames('h-full w-full center-center', 'top-0')}
|
||||
>
|
||||
<Component
|
||||
fullHeight={dataItem?.[$breakpoint === 'sm' ? 3 : 12]?.fullHeight}
|
||||
render={visible}
|
||||
component={dataItem.data}
|
||||
selected={Boolean($selectedComponent?.includes(dataItem.id))}
|
||||
locked={isFixed(dataItem)}
|
||||
{hidden}
|
||||
/>
|
||||
</div>
|
||||
</GridViewer>
|
||||
{/if}
|
||||
<Component
|
||||
fullHeight={dataItem?.[$breakpoint === 'sm' ? 3 : 12]?.fullHeight}
|
||||
render={true}
|
||||
component={dataItem.data}
|
||||
selected={Boolean($selectedComponent?.includes(dataItem.id))}
|
||||
locked={isFixed(dataItem)}
|
||||
/>
|
||||
</div>
|
||||
</GridViewer>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -1,969 +1,39 @@
|
||||
<script lang="ts" context="module">
|
||||
let outTimeout: NodeJS.Timeout | undefined = undefined
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { AppEditorContext, AppViewerContext } from '../../types'
|
||||
import ComponentHeader from '../ComponentHeader.svelte'
|
||||
import ComponentInner from './ComponentInner.svelte'
|
||||
import ComponentRendered from './ComponentRendered.svelte'
|
||||
import type { AppComponent } from './components'
|
||||
|
||||
import AppMultiSelect from '../../components/inputs/AppMultiSelect.svelte'
|
||||
import AppMultiSelectV2 from '../../components/inputs/AppMultiSelectV2.svelte'
|
||||
import AppModal from '../../components/layout/AppModal.svelte'
|
||||
import AppSchemaForm from '../../components/buttons/AppSchemaForm.svelte'
|
||||
import AppStepper from '../../components/layout/AppStepper.svelte'
|
||||
import AppSelectTab from '../../components/inputs/AppSelectTab.svelte'
|
||||
import AppConditionalWrapper from '../../components/layout/AppConditionalWrapper.svelte'
|
||||
import AppSelectStep from '../../components/inputs/AppSelectStep.svelte'
|
||||
import AppDownload from '../../components/display/AppDownload.svelte'
|
||||
import AppLogsComponent from '../../components/display/AppLogsComponent.svelte'
|
||||
import AppFlowStatusComponent from '../../components/display/AppFlowStatusComponent.svelte'
|
||||
import AppChartJs from '../../components/display/AppChartJs.svelte'
|
||||
import AppChartJsV2 from '../../components/display/AppChartJsV2.svelte'
|
||||
import AppQuillEditor from '../../components/inputs/AppQuillEditor.svelte'
|
||||
import AppList from '../../components/layout/AppList.svelte'
|
||||
import AppJobIdLogComponent from '../../components/display/AppJobIdLogComponent.svelte'
|
||||
import AppJobIdFlowStatus from '../../components/display/AppJobIdFlowStatus.svelte'
|
||||
import AppCarouselList from '../../components/display/AppCarouselList.svelte'
|
||||
import AppAccordionList from '../../components/display/AppAccordionList.svelte'
|
||||
import AppAggridTableEe from '../../components/display/table/AppAggridTableEe.svelte'
|
||||
import AppCustomComponent from '../../components/display/AppCustomComponent.svelte'
|
||||
import AppStatCard from '../../components/display/AppStatCard.svelte'
|
||||
import AppMenu from '../../components/display/AppMenu.svelte'
|
||||
import AppDecisionTree from '../../components/layout/AppDecisionTree.svelte'
|
||||
import AppAgCharts from '../../components/display/charts/AppAgCharts.svelte'
|
||||
import AppDbExplorer from '../../components/display/dbtable/AppDbExplorer.svelte'
|
||||
import AppS3FileInput from '../../components/inputs/AppS3FileInput.svelte'
|
||||
import AppAlert from '../../components/display/AppAlert.svelte'
|
||||
import AppDateSliderInput from '../../components/inputs/AppDateSliderInput.svelte'
|
||||
import AppTimeInput from '../../components/inputs/AppTimeInput.svelte'
|
||||
import AppDateTimeInput from '../../components/inputs/AppDateTimeInput.svelte'
|
||||
import AppAggridInfiniteTable from '../../components/display/table/AppAggridInfiniteTable.svelte'
|
||||
import AppAggridInfiniteTableEe from '../../components/display/table/AppAggridInfiniteTableEe.svelte'
|
||||
import AppDisplayComponent from '../../components/display/AppDisplayComponent.svelte'
|
||||
import AppTimeseries from '../../components/display/AppTimeseries.svelte'
|
||||
import AppHtml from '../../components/display/AppHtml.svelte'
|
||||
import AppMarkdown from '../../components/display/AppMarkdown.svelte'
|
||||
import VegaLiteHtml from '../../components/display/VegaLiteHtml.svelte'
|
||||
import PlotlyHtml from '../../components/display/PlotlyHtml.svelte'
|
||||
import PlotlyHtmlV2 from '../../components/display/PlotlyHtmlV2.svelte'
|
||||
import AppScatterChart from '../../components/display/AppScatterChart.svelte'
|
||||
import AppPieChart from '../../components/display/AppPieChart.svelte'
|
||||
import AppTable from '../../components/display/table/AppTable.svelte'
|
||||
import AppAggridTable from '../../components/display/table/AppAggridTable.svelte'
|
||||
import AppText from '../../components/display/AppText.svelte'
|
||||
import AppButton from '../../components/buttons/AppButton.svelte'
|
||||
import AppForm from '../../components/buttons/AppForm.svelte'
|
||||
import AppFormButton from '../../components/buttons/AppFormButton.svelte'
|
||||
import AppCheckbox from '../../components/inputs/AppCheckbox.svelte'
|
||||
import AppTextInput from '../../components/inputs/AppTextInput.svelte'
|
||||
import AppDateInput from '../../components/inputs/AppDateInput.svelte'
|
||||
import AppSelect from '../../components/inputs/AppSelect.svelte'
|
||||
import AppBarChart from '../../components/display/AppBarChart.svelte'
|
||||
import AppDivider from '../../components/layout/AppDivider.svelte'
|
||||
import AppRangeInput from '../../components/inputs/AppRangeInput.svelte'
|
||||
import AppTabs from '../../components/layout/AppTabs.svelte'
|
||||
import AppContainer from '../../components/layout/AppContainer.svelte'
|
||||
import AppSplitpanes from '../../components/layout/AppSplitpanes.svelte'
|
||||
import AppIcon from '../../components/display/AppIcon.svelte'
|
||||
import AppFileInput from '../../components/inputs/AppFileInput.svelte'
|
||||
import AppImage from '../../components/display/AppImage.svelte'
|
||||
import AppDrawer from '../../components/layout/AppDrawer.svelte'
|
||||
import AppMap from '../../components/display/AppMap.svelte'
|
||||
import AppPdf from '../../components/display/AppPdf.svelte'
|
||||
import AppCurrencyInput from '../../components/inputs/currency/AppCurrencyInput.svelte'
|
||||
import AppSliderInputs from '../../components/inputs/AppSliderInputs.svelte'
|
||||
import AppNumberInput from '../../components/inputs/AppNumberInput.svelte'
|
||||
import AppNavbar from '../../components/display/AppNavbar.svelte'
|
||||
import AppDateSelect from '../../components/inputs/AppDateSelect.svelte'
|
||||
import AppDisplayComponentByJobId from '../../components/display/AppRecomputeAll.svelte'
|
||||
import AppRecomputeAll from '../../components/display/AppRecomputeAll.svelte'
|
||||
import AppUserResource from '../../components/inputs/AppUserResource.svelte'
|
||||
import { Anchor } from 'lucide-svelte'
|
||||
import { findGridItemParentGrid, isContainer } from '../appUtils'
|
||||
|
||||
export let component: AppComponent
|
||||
export let selected: boolean
|
||||
export let locked: boolean = false
|
||||
export let render: boolean
|
||||
export let hidden: boolean
|
||||
export let fullHeight: boolean
|
||||
export let overlapped: string | undefined = undefined
|
||||
export let moveMode: string | undefined = undefined
|
||||
export let componentDraggedId: string | undefined = undefined
|
||||
const { mode, app, hoverStore, connectingInput } =
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
const editorContext = getContext<AppEditorContext>('AppEditorContext')
|
||||
const componentActive = editorContext?.componentActive
|
||||
export let moveMode: string | undefined = undefined
|
||||
let everRender = render
|
||||
|
||||
const movingcomponents = editorContext?.movingcomponents
|
||||
$: ismoving =
|
||||
movingcomponents != undefined && $mode == 'dnd' && $movingcomponents?.includes(component.id)
|
||||
|
||||
let initializing: boolean | undefined = undefined
|
||||
let errorHandledByComponent: boolean = false
|
||||
let componentContainerHeight: number = 0
|
||||
let componentContainerWidth: number = 0
|
||||
|
||||
let inlineEditorOpened: boolean = false
|
||||
|
||||
function mouseOut() {
|
||||
outTimeout && clearTimeout(outTimeout)
|
||||
outTimeout = setTimeout(() => {
|
||||
if ($hoverStore !== undefined) {
|
||||
// In order to avoid flickering when hovering over table actions,
|
||||
// we leave the actions to manage the hover state
|
||||
if ($hoverStore.startsWith(`${component.id}_`)) {
|
||||
return
|
||||
}
|
||||
|
||||
$hoverStore = undefined
|
||||
}
|
||||
}, 50)
|
||||
}
|
||||
|
||||
function componentDraggedIsNotChild(componentDraggedId: string, componentId: string) {
|
||||
let parentGrid = findGridItemParentGrid($app, componentDraggedId)
|
||||
|
||||
return !parentGrid?.startsWith(`${componentId}-`)
|
||||
}
|
||||
|
||||
function areOnTheSameSubgrid(componentDraggedId: string, componentId: string) {
|
||||
return (
|
||||
findGridItemParentGrid($app, componentDraggedId) === findGridItemParentGrid($app, componentId)
|
||||
)
|
||||
}
|
||||
|
||||
let cachedComponentDraggedIsNotChild: boolean | undefined
|
||||
let cachedAreOnTheSameSubgrid: boolean | undefined
|
||||
|
||||
function updateCache(componentDraggedId: string | undefined) {
|
||||
if (componentDraggedId) {
|
||||
cachedComponentDraggedIsNotChild = componentDraggedIsNotChild(
|
||||
componentDraggedId,
|
||||
component.id
|
||||
)
|
||||
cachedAreOnTheSameSubgrid = areOnTheSameSubgrid(componentDraggedId, component.id)
|
||||
} else {
|
||||
cachedComponentDraggedIsNotChild = undefined
|
||||
cachedAreOnTheSameSubgrid = undefined
|
||||
}
|
||||
}
|
||||
|
||||
$: updateCache(componentDraggedId)
|
||||
$: render && !everRender && (everRender = true)
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
on:mouseover|stopPropagation={() => {
|
||||
outTimeout && clearTimeout(outTimeout)
|
||||
if (component.id !== $hoverStore) {
|
||||
$hoverStore = component.id
|
||||
}
|
||||
}}
|
||||
on:mouseout|stopPropagation={mouseOut}
|
||||
class={twMerge(
|
||||
'h-full flex flex-col w-full component relative',
|
||||
initializing ? 'overflow-hidden h-0' : '',
|
||||
hidden && $mode === 'preview' ? 'hidden' : ''
|
||||
)}
|
||||
data-connection-button
|
||||
>
|
||||
{#if locked && componentActive && $componentActive && moveMode === 'move' && componentDraggedId && componentDraggedId !== component.id && cachedAreOnTheSameSubgrid}
|
||||
<div
|
||||
class={twMerge('absolute inset-0 bg-locked center-center flex-col z-50', 'bg-locked-hover')}
|
||||
>
|
||||
<div class="bg-surface p-2 shadow-sm rounded-md flex center-center flex-col gap-2">
|
||||
<Anchor size={24} class="text-primary " />
|
||||
<div class="text-xs"> Anchored: The component cannot be moved. </div>
|
||||
</div>
|
||||
</div>
|
||||
{:else if moveMode === 'insert' && isContainer(component.type) && componentDraggedId && componentDraggedId !== component.id && cachedComponentDraggedIsNotChild}
|
||||
<div
|
||||
class={twMerge(
|
||||
'absolute inset-0 flex-col rounded-md bg-blue-100 dark:bg-gray-800 bg-opacity-50',
|
||||
'outline-dashed outline-offset-2 outline-2 outline-blue-300 dark:outline-blue-700',
|
||||
overlapped === component?.id ? 'bg-draggedover dark:bg-draggedover-dark' : ''
|
||||
)}
|
||||
/>
|
||||
{/if}
|
||||
{#if $mode !== 'preview'}
|
||||
<ComponentHeader
|
||||
on:mouseover={() => {
|
||||
outTimeout && clearTimeout(outTimeout)
|
||||
|
||||
if (component.id !== $hoverStore) {
|
||||
$hoverStore = component.id
|
||||
}
|
||||
}}
|
||||
hover={$hoverStore === component.id}
|
||||
{component}
|
||||
{selected}
|
||||
{fullHeight}
|
||||
connecting={$connectingInput.opened}
|
||||
on:lock
|
||||
on:expand
|
||||
on:fillHeight
|
||||
{locked}
|
||||
{inlineEditorOpened}
|
||||
hasInlineEditor={component.type === 'textcomponent' &&
|
||||
component.componentInput &&
|
||||
component.componentInput.type !== 'connected'}
|
||||
on:triggerInlineEditor={() => {
|
||||
inlineEditorOpened = !inlineEditorOpened
|
||||
}}
|
||||
{errorHandledByComponent}
|
||||
{componentContainerWidth}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if ismoving}
|
||||
<div class="absolute -top-8 w-40">
|
||||
<button
|
||||
class="border p-0.5 text-xs"
|
||||
on:click={() => {
|
||||
$movingcomponents = undefined
|
||||
}}
|
||||
>
|
||||
Cancel move
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
<div
|
||||
class={twMerge(
|
||||
'h-full outline-1',
|
||||
$mode === 'dnd' ? 'bg-surface/40' : '',
|
||||
$hoverStore === component.id && $mode !== 'preview'
|
||||
? $connectingInput.opened
|
||||
? 'outline outline-[#f8aa4b]'
|
||||
: 'outline outline-blue-400'
|
||||
: '',
|
||||
selected && $mode !== 'preview' ? 'outline outline-blue-600' : '',
|
||||
$mode != 'preview' ? 'cursor-pointer' : '',
|
||||
'relative z-auto',
|
||||
$app.css?.['app']?.['component']?.class,
|
||||
'wm-app-component',
|
||||
ismoving ? 'animate-pulse' : ''
|
||||
)}
|
||||
style={$app.css?.['app']?.['component']?.style}
|
||||
bind:clientHeight={componentContainerHeight}
|
||||
bind:clientWidth={componentContainerWidth}
|
||||
>
|
||||
{#if component.type === 'displaycomponent'}
|
||||
<AppDisplayComponent
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
configuration={component.configuration}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'logcomponent'}
|
||||
<AppLogsComponent />
|
||||
{:else if component.type === 'jobidlogcomponent'}
|
||||
<AppJobIdLogComponent
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
configuration={component.configuration}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'flowstatuscomponent'}
|
||||
<AppFlowStatusComponent />
|
||||
{:else if component.type === 'jobidflowstatuscomponent'}
|
||||
<AppJobIdFlowStatus
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
configuration={component.configuration}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'barchartcomponent'}
|
||||
<AppBarChart
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'timeseriescomponent'}
|
||||
<AppTimeseries
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
configuration={component.configuration}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'htmlcomponent'}
|
||||
<AppHtml
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'customcomponent'}
|
||||
<AppCustomComponent
|
||||
customComponent={component.customComponent}
|
||||
id={component.id}
|
||||
componentInput={component.componentInput}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'mardowncomponent'}
|
||||
<AppMarkdown
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
configuration={component.configuration}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'vegalitecomponent'}
|
||||
<VegaLiteHtml
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'plotlycomponent'}
|
||||
<PlotlyHtml
|
||||
id={component.id}
|
||||
configuration={component.configuration}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'plotlycomponentv2'}
|
||||
<PlotlyHtmlV2
|
||||
id={component.id}
|
||||
configuration={component.configuration}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
datasets={component.datasets}
|
||||
xData={component.xData}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'scatterchartcomponent'}
|
||||
<AppScatterChart
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'piechartcomponent'}
|
||||
<AppPieChart
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'agchartscomponent'}
|
||||
<AppAgCharts
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
datasets={component.datasets}
|
||||
xData={component.xData}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'agchartscomponentee'}
|
||||
<AppAgCharts
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
datasets={component.datasets}
|
||||
xData={component.xData}
|
||||
license={component.license}
|
||||
ee={true}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'tablecomponent'}
|
||||
<AppTable
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
actionButtons={component.actionButtons}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'dbexplorercomponent'}
|
||||
<AppDbExplorer
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
actions={component.actions ?? []}
|
||||
bind:initializing
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'aggridcomponent'}
|
||||
<AppAggridTable
|
||||
id={component.id}
|
||||
configuration={component.configuration}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
customCss={component.customCss}
|
||||
actions={component.actions ?? []}
|
||||
actionsOrder={component.actionsOrder ?? undefined}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'aggridcomponentee'}
|
||||
<AppAggridTableEe
|
||||
license={component.license}
|
||||
id={component.id}
|
||||
configuration={component.configuration}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
customCss={component.customCss}
|
||||
actions={component.actions ?? []}
|
||||
actionsOrder={component.actionsOrder ?? undefined}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'aggridinfinitecomponent'}
|
||||
<AppAggridInfiniteTable
|
||||
id={component.id}
|
||||
configuration={component.configuration}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
customCss={component.customCss}
|
||||
actions={component.actions ?? []}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'aggridinfinitecomponentee'}
|
||||
<AppAggridInfiniteTableEe
|
||||
license={component.license}
|
||||
id={component.id}
|
||||
configuration={component.configuration}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
customCss={component.customCss}
|
||||
actions={component.actions ?? []}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'textcomponent'}
|
||||
<AppText
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
bind:editorMode={inlineEditorOpened}
|
||||
componentInput={component.componentInput}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'buttoncomponent'}
|
||||
<AppButton
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
componentInput={component.componentInput}
|
||||
recomputeIds={component.recomputeIds}
|
||||
bind:errorHandledByComponent
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'downloadcomponent'}
|
||||
<AppDownload
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'selectcomponent' || component.type === 'resourceselectcomponent'}
|
||||
<AppSelect
|
||||
recomputeIds={component.recomputeIds}
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
onSelect={component.onSelect}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'userresourcecomponent'}
|
||||
<AppUserResource
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'multiselectcomponent'}
|
||||
<AppMultiSelect
|
||||
id={component.id}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'multiselectcomponentv2'}
|
||||
<AppMultiSelectV2
|
||||
id={component.id}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'formcomponent'}
|
||||
<AppForm
|
||||
id={component.id}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
componentInput={component.componentInput}
|
||||
recomputeIds={component.recomputeIds}
|
||||
bind:errorHandledByComponent
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'formbuttoncomponent'}
|
||||
<AppFormButton
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
componentInput={component.componentInput}
|
||||
recomputeIds={component.recomputeIds}
|
||||
bind:errorHandledByComponent
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'checkboxcomponent'}
|
||||
<AppCheckbox
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
recomputeIds={component.recomputeIds}
|
||||
onToggle={component.onToggle}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'textinputcomponent'}
|
||||
<AppTextInput
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'quillcomponent'}
|
||||
<AppQuillEditor id={component.id} configuration={component.configuration} {render} />
|
||||
{:else if component.type === 'textareainputcomponent'}
|
||||
<AppTextInput
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
inputType="textarea"
|
||||
appCssKey="textareainputcomponent"
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'emailinputcomponent'}
|
||||
<AppTextInput
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
inputType="email"
|
||||
appCssKey="emailinputcomponent"
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'passwordinputcomponent'}
|
||||
<AppTextInput
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
inputType="password"
|
||||
appCssKey="passwordinputcomponent"
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'dateinputcomponent'}
|
||||
<AppDateInput
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
inputType="date"
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'timeinputcomponent'}
|
||||
<AppTimeInput
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'datetimeinputcomponent'}
|
||||
<AppDateTimeInput
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
inputType="date"
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'numberinputcomponent'}
|
||||
<AppNumberInput
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'currencycomponent'}
|
||||
<AppCurrencyInput
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'slidercomponent'}
|
||||
<AppSliderInputs
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'dateslidercomponent'}
|
||||
<AppDateSliderInput
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'horizontaldividercomponent'}
|
||||
<AppDivider
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
position="horizontal"
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'verticaldividercomponent'}
|
||||
<AppDivider
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
position="vertical"
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'rangecomponent'}
|
||||
<AppRangeInput
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'tabscomponent' && component.tabs}
|
||||
<AppTabs
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
tabs={component.tabs}
|
||||
disabledTabs={component.disabledTabs}
|
||||
onTabChange={component.onTabChange}
|
||||
customCss={component.customCss}
|
||||
{componentContainerHeight}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'steppercomponent' && component.tabs}
|
||||
<AppStepper
|
||||
id={component.id}
|
||||
tabs={component.tabs}
|
||||
customCss={component.customCss}
|
||||
{componentContainerHeight}
|
||||
componentInput={component.componentInput}
|
||||
onNext={component.onNext}
|
||||
onPrevious={component.onPrevious}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'conditionalwrapper' && component.conditions}
|
||||
<AppConditionalWrapper
|
||||
id={component.id}
|
||||
conditions={component.conditions}
|
||||
customCss={component.customCss}
|
||||
onTabChange={component.onTabChange}
|
||||
{componentContainerHeight}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'containercomponent'}
|
||||
<AppContainer
|
||||
groupFields={component.groupFields}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{componentContainerHeight}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'listcomponent'}
|
||||
<AppList
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
configuration={component.configuration}
|
||||
componentInput={component.componentInput}
|
||||
{render}
|
||||
bind:initializing
|
||||
/>
|
||||
{:else if component.type === 'verticalsplitpanescomponent'}
|
||||
<AppSplitpanes
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
panes={component.panes}
|
||||
{componentContainerHeight}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'horizontalsplitpanescomponent'}
|
||||
<AppSplitpanes
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
panes={component.panes}
|
||||
{componentContainerHeight}
|
||||
horizontal={true}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'iconcomponent'}
|
||||
<AppIcon
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'fileinputcomponent'}
|
||||
<AppFileInput
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
onFileChange={component.onFileChange}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 's3fileinputcomponent'}
|
||||
<AppS3FileInput
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
onFileChange={component.onFileChange}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'imagecomponent'}
|
||||
<AppImage
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'drawercomponent'}
|
||||
<AppDrawer
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
onOpenRecomputeIds={component.onOpenRecomputeIds}
|
||||
onCloseRecomputeIds={component.onCloseRecomputeIds}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'mapcomponent'}
|
||||
<AppMap
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'pdfcomponent'}
|
||||
<AppPdf
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'modalcomponent'}
|
||||
<AppModal
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
onOpenRecomputeIds={component.onOpenRecomputeIds}
|
||||
onCloseRecomputeIds={component.onCloseRecomputeIds}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'schemaformcomponent'}
|
||||
<AppSchemaForm
|
||||
id={component.id}
|
||||
componentInput={component.componentInput}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
{initializing}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'selecttabcomponent'}
|
||||
<AppSelectTab
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'selectstepcomponent'}
|
||||
<AppSelectStep
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'chartjscomponent'}
|
||||
<AppChartJs
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'chartjscomponentv2'}
|
||||
<AppChartJsV2
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
datasets={component.datasets}
|
||||
xData={component.xData}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'carousellistcomponent'}
|
||||
<AppCarouselList
|
||||
id={component.id}
|
||||
configuration={component.configuration}
|
||||
componentInput={component.componentInput}
|
||||
customCss={component.customCss}
|
||||
{componentContainerHeight}
|
||||
{render}
|
||||
bind:initializing
|
||||
/>
|
||||
{:else if component.type === 'accordionlistcomponent'}
|
||||
<AppAccordionList
|
||||
id={component.id}
|
||||
componentInput={component.componentInput}
|
||||
customCss={component.customCss}
|
||||
{componentContainerHeight}
|
||||
{render}
|
||||
bind:initializing
|
||||
/>
|
||||
{:else if component.type === 'statcomponent'}
|
||||
<AppStatCard
|
||||
id={component.id}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'menucomponent'}
|
||||
<AppMenu
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
menuItems={component.menuItems}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'decisiontreecomponent' && component.nodes}
|
||||
<AppDecisionTree
|
||||
id={component.id}
|
||||
nodes={component.nodes}
|
||||
customCss={component.customCss}
|
||||
{componentContainerHeight}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'alertcomponent'}
|
||||
<AppAlert
|
||||
id={component.id}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'navbarcomponent'}
|
||||
<AppNavbar
|
||||
id={component.id}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
navbarItems={component.navbarItems}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'dateselectcomponent'}
|
||||
<AppDateSelect
|
||||
id={component.id}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'jobiddisplaycomponent'}
|
||||
<AppDisplayComponentByJobId
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
configuration={component.configuration}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'recomputeallcomponent'}
|
||||
<AppRecomputeAll
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
configuration={component.configuration}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
{render}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{#if initializing}
|
||||
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
on:mouseover|stopPropagation={() => {
|
||||
if (component.id !== $hoverStore) {
|
||||
$hoverStore = component.id
|
||||
}
|
||||
}}
|
||||
on:mouseout|stopPropagation={() => {
|
||||
if ($hoverStore !== undefined) {
|
||||
$hoverStore = undefined
|
||||
}
|
||||
}}
|
||||
class="absolute inset-0 center-center flex-col bg- border animate-skeleton"
|
||||
{#if everRender}
|
||||
<ComponentRendered
|
||||
{moveMode}
|
||||
{componentDraggedId}
|
||||
{overlapped}
|
||||
{render}
|
||||
{component}
|
||||
{selected}
|
||||
{locked}
|
||||
{fullHeight}
|
||||
/>
|
||||
{:else}
|
||||
<ComponentInner
|
||||
{component}
|
||||
render={false}
|
||||
componentContainerHeight={0}
|
||||
errorHandledByComponent={false}
|
||||
inlineEditorOpened={false}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -0,0 +1,778 @@
|
||||
<script lang="ts">
|
||||
import AppMultiSelect from '../../components/inputs/AppMultiSelect.svelte'
|
||||
import AppMultiSelectV2 from '../../components/inputs/AppMultiSelectV2.svelte'
|
||||
import AppModal from '../../components/layout/AppModal.svelte'
|
||||
import AppSchemaForm from '../../components/buttons/AppSchemaForm.svelte'
|
||||
import AppStepper from '../../components/layout/AppStepper.svelte'
|
||||
import AppSelectTab from '../../components/inputs/AppSelectTab.svelte'
|
||||
import AppConditionalWrapper from '../../components/layout/AppConditionalWrapper.svelte'
|
||||
import AppSelectStep from '../../components/inputs/AppSelectStep.svelte'
|
||||
import AppDownload from '../../components/display/AppDownload.svelte'
|
||||
import AppLogsComponent from '../../components/display/AppLogsComponent.svelte'
|
||||
import AppFlowStatusComponent from '../../components/display/AppFlowStatusComponent.svelte'
|
||||
import AppChartJs from '../../components/display/AppChartJs.svelte'
|
||||
import AppChartJsV2 from '../../components/display/AppChartJsV2.svelte'
|
||||
import AppQuillEditor from '../../components/inputs/AppQuillEditor.svelte'
|
||||
import AppList from '../../components/layout/AppList.svelte'
|
||||
import AppJobIdLogComponent from '../../components/display/AppJobIdLogComponent.svelte'
|
||||
import AppJobIdFlowStatus from '../../components/display/AppJobIdFlowStatus.svelte'
|
||||
import AppCarouselList from '../../components/display/AppCarouselList.svelte'
|
||||
import AppAccordionList from '../../components/display/AppAccordionList.svelte'
|
||||
import AppAggridTableEe from '../../components/display/table/AppAggridTableEe.svelte'
|
||||
import AppCustomComponent from '../../components/display/AppCustomComponent.svelte'
|
||||
import AppStatCard from '../../components/display/AppStatCard.svelte'
|
||||
import AppMenu from '../../components/display/AppMenu.svelte'
|
||||
import AppDecisionTree from '../../components/layout/AppDecisionTree.svelte'
|
||||
import AppAgCharts from '../../components/display/charts/AppAgCharts.svelte'
|
||||
import AppDbExplorer from '../../components/display/dbtable/AppDbExplorer.svelte'
|
||||
import AppS3FileInput from '../../components/inputs/AppS3FileInput.svelte'
|
||||
import AppAlert from '../../components/display/AppAlert.svelte'
|
||||
import AppDateSliderInput from '../../components/inputs/AppDateSliderInput.svelte'
|
||||
import AppTimeInput from '../../components/inputs/AppTimeInput.svelte'
|
||||
import AppDateTimeInput from '../../components/inputs/AppDateTimeInput.svelte'
|
||||
import AppAggridInfiniteTable from '../../components/display/table/AppAggridInfiniteTable.svelte'
|
||||
import AppAggridInfiniteTableEe from '../../components/display/table/AppAggridInfiniteTableEe.svelte'
|
||||
import AppDisplayComponent from '../../components/display/AppDisplayComponent.svelte'
|
||||
import AppTimeseries from '../../components/display/AppTimeseries.svelte'
|
||||
import AppHtml from '../../components/display/AppHtml.svelte'
|
||||
import AppMarkdown from '../../components/display/AppMarkdown.svelte'
|
||||
import VegaLiteHtml from '../../components/display/VegaLiteHtml.svelte'
|
||||
import PlotlyHtml from '../../components/display/PlotlyHtml.svelte'
|
||||
import PlotlyHtmlV2 from '../../components/display/PlotlyHtmlV2.svelte'
|
||||
import AppScatterChart from '../../components/display/AppScatterChart.svelte'
|
||||
import AppPieChart from '../../components/display/AppPieChart.svelte'
|
||||
import AppTable from '../../components/display/table/AppTable.svelte'
|
||||
import AppAggridTable from '../../components/display/table/AppAggridTable.svelte'
|
||||
import AppText from '../../components/display/AppText.svelte'
|
||||
import AppButton from '../../components/buttons/AppButton.svelte'
|
||||
import AppForm from '../../components/buttons/AppForm.svelte'
|
||||
import AppFormButton from '../../components/buttons/AppFormButton.svelte'
|
||||
import AppCheckbox from '../../components/inputs/AppCheckbox.svelte'
|
||||
import AppTextInput from '../../components/inputs/AppTextInput.svelte'
|
||||
import AppDateInput from '../../components/inputs/AppDateInput.svelte'
|
||||
import AppSelect from '../../components/inputs/AppSelect.svelte'
|
||||
import AppBarChart from '../../components/display/AppBarChart.svelte'
|
||||
import AppDivider from '../../components/layout/AppDivider.svelte'
|
||||
import AppRangeInput from '../../components/inputs/AppRangeInput.svelte'
|
||||
import AppTabs from '../../components/layout/AppTabs.svelte'
|
||||
import AppContainer from '../../components/layout/AppContainer.svelte'
|
||||
import AppSplitpanes from '../../components/layout/AppSplitpanes.svelte'
|
||||
import AppIcon from '../../components/display/AppIcon.svelte'
|
||||
import AppFileInput from '../../components/inputs/AppFileInput.svelte'
|
||||
import AppImage from '../../components/display/AppImage.svelte'
|
||||
import AppDrawer from '../../components/layout/AppDrawer.svelte'
|
||||
import AppMap from '../../components/display/AppMap.svelte'
|
||||
import AppPdf from '../../components/display/AppPdf.svelte'
|
||||
import AppCurrencyInput from '../../components/inputs/currency/AppCurrencyInput.svelte'
|
||||
import AppSliderInputs from '../../components/inputs/AppSliderInputs.svelte'
|
||||
import AppNumberInput from '../../components/inputs/AppNumberInput.svelte'
|
||||
import AppNavbar from '../../components/display/AppNavbar.svelte'
|
||||
import AppDateSelect from '../../components/inputs/AppDateSelect.svelte'
|
||||
import AppDisplayComponentByJobId from '../../components/display/AppRecomputeAll.svelte'
|
||||
import AppRecomputeAll from '../../components/display/AppRecomputeAll.svelte'
|
||||
import AppUserResource from '../../components/inputs/AppUserResource.svelte'
|
||||
import type { AppComponent } from './components'
|
||||
|
||||
export let component: AppComponent
|
||||
export let render: boolean
|
||||
export let componentContainerHeight: number
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let errorHandledByComponent: boolean
|
||||
export let inlineEditorOpened: boolean
|
||||
</script>
|
||||
|
||||
{#if component.type === 'displaycomponent'}
|
||||
<AppDisplayComponent
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
configuration={component.configuration}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'logcomponent'}
|
||||
<AppLogsComponent />
|
||||
{:else if component.type === 'jobidlogcomponent'}
|
||||
<AppJobIdLogComponent
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
configuration={component.configuration}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'flowstatuscomponent'}
|
||||
<AppFlowStatusComponent />
|
||||
{:else if component.type === 'jobidflowstatuscomponent'}
|
||||
<AppJobIdFlowStatus
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
configuration={component.configuration}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'barchartcomponent'}
|
||||
<AppBarChart
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'timeseriescomponent'}
|
||||
<AppTimeseries
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
configuration={component.configuration}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'htmlcomponent'}
|
||||
<AppHtml
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'customcomponent'}
|
||||
<AppCustomComponent
|
||||
customComponent={component.customComponent}
|
||||
id={component.id}
|
||||
componentInput={component.componentInput}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'mardowncomponent'}
|
||||
<AppMarkdown
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
configuration={component.configuration}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'vegalitecomponent'}
|
||||
<VegaLiteHtml
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'plotlycomponent'}
|
||||
<PlotlyHtml
|
||||
id={component.id}
|
||||
configuration={component.configuration}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'plotlycomponentv2'}
|
||||
<PlotlyHtmlV2
|
||||
id={component.id}
|
||||
configuration={component.configuration}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
datasets={component.datasets}
|
||||
xData={component.xData}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'scatterchartcomponent'}
|
||||
<AppScatterChart
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'piechartcomponent'}
|
||||
<AppPieChart
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'agchartscomponent'}
|
||||
<AppAgCharts
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
datasets={component.datasets}
|
||||
xData={component.xData}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'agchartscomponentee'}
|
||||
<AppAgCharts
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
datasets={component.datasets}
|
||||
xData={component.xData}
|
||||
license={component.license}
|
||||
ee={true}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'tablecomponent'}
|
||||
<AppTable
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
actionButtons={component.actionButtons}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'dbexplorercomponent'}
|
||||
<AppDbExplorer
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
actions={component.actions ?? []}
|
||||
bind:initializing
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'aggridcomponent'}
|
||||
<AppAggridTable
|
||||
id={component.id}
|
||||
configuration={component.configuration}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
customCss={component.customCss}
|
||||
actions={component.actions ?? []}
|
||||
actionsOrder={component.actionsOrder ?? undefined}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'aggridcomponentee'}
|
||||
<AppAggridTableEe
|
||||
license={component.license}
|
||||
id={component.id}
|
||||
configuration={component.configuration}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
customCss={component.customCss}
|
||||
actions={component.actions ?? []}
|
||||
actionsOrder={component.actionsOrder ?? undefined}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'aggridinfinitecomponent'}
|
||||
<AppAggridInfiniteTable
|
||||
id={component.id}
|
||||
configuration={component.configuration}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
customCss={component.customCss}
|
||||
actions={component.actions ?? []}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'aggridinfinitecomponentee'}
|
||||
<AppAggridInfiniteTableEe
|
||||
license={component.license}
|
||||
id={component.id}
|
||||
configuration={component.configuration}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
customCss={component.customCss}
|
||||
actions={component.actions ?? []}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'textcomponent'}
|
||||
<AppText
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
bind:editorMode={inlineEditorOpened}
|
||||
componentInput={component.componentInput}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'buttoncomponent'}
|
||||
<AppButton
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
componentInput={component.componentInput}
|
||||
recomputeIds={component.recomputeIds}
|
||||
bind:errorHandledByComponent
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'downloadcomponent'}
|
||||
<AppDownload
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'selectcomponent' || component.type === 'resourceselectcomponent'}
|
||||
<AppSelect
|
||||
recomputeIds={component.recomputeIds}
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
onSelect={component.onSelect}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'userresourcecomponent'}
|
||||
<AppUserResource
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'multiselectcomponent'}
|
||||
<AppMultiSelect
|
||||
id={component.id}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'multiselectcomponentv2'}
|
||||
<AppMultiSelectV2
|
||||
id={component.id}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'formcomponent'}
|
||||
<AppForm
|
||||
id={component.id}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
componentInput={component.componentInput}
|
||||
recomputeIds={component.recomputeIds}
|
||||
bind:errorHandledByComponent
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'formbuttoncomponent'}
|
||||
<AppFormButton
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
componentInput={component.componentInput}
|
||||
recomputeIds={component.recomputeIds}
|
||||
bind:errorHandledByComponent
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'checkboxcomponent'}
|
||||
<AppCheckbox
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
recomputeIds={component.recomputeIds}
|
||||
onToggle={component.onToggle}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'textinputcomponent'}
|
||||
<AppTextInput
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'quillcomponent'}
|
||||
<AppQuillEditor id={component.id} configuration={component.configuration} {render} />
|
||||
{:else if component.type === 'textareainputcomponent'}
|
||||
<AppTextInput
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
inputType="textarea"
|
||||
appCssKey="textareainputcomponent"
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'emailinputcomponent'}
|
||||
<AppTextInput
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
inputType="email"
|
||||
appCssKey="emailinputcomponent"
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'passwordinputcomponent'}
|
||||
<AppTextInput
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
inputType="password"
|
||||
appCssKey="passwordinputcomponent"
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'dateinputcomponent'}
|
||||
<AppDateInput
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
inputType="date"
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'timeinputcomponent'}
|
||||
<AppTimeInput
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'datetimeinputcomponent'}
|
||||
<AppDateTimeInput
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
inputType="date"
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'numberinputcomponent'}
|
||||
<AppNumberInput
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'currencycomponent'}
|
||||
<AppCurrencyInput
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'slidercomponent'}
|
||||
<AppSliderInputs
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'dateslidercomponent'}
|
||||
<AppDateSliderInput
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'horizontaldividercomponent'}
|
||||
<AppDivider
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
position="horizontal"
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'verticaldividercomponent'}
|
||||
<AppDivider
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
position="vertical"
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'rangecomponent'}
|
||||
<AppRangeInput
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'tabscomponent' && component.tabs}
|
||||
<AppTabs
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
tabs={component.tabs}
|
||||
disabledTabs={component.disabledTabs}
|
||||
onTabChange={component.onTabChange}
|
||||
customCss={component.customCss}
|
||||
{componentContainerHeight}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'steppercomponent' && component.tabs}
|
||||
<AppStepper
|
||||
id={component.id}
|
||||
tabs={component.tabs}
|
||||
customCss={component.customCss}
|
||||
{componentContainerHeight}
|
||||
componentInput={component.componentInput}
|
||||
onNext={component.onNext}
|
||||
onPrevious={component.onPrevious}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'conditionalwrapper' && component.conditions}
|
||||
<AppConditionalWrapper
|
||||
id={component.id}
|
||||
conditions={component.conditions}
|
||||
customCss={component.customCss}
|
||||
onTabChange={component.onTabChange}
|
||||
{componentContainerHeight}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'containercomponent'}
|
||||
<AppContainer
|
||||
groupFields={component.groupFields}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{componentContainerHeight}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'listcomponent'}
|
||||
<AppList
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
configuration={component.configuration}
|
||||
componentInput={component.componentInput}
|
||||
{render}
|
||||
bind:initializing
|
||||
/>
|
||||
{:else if component.type === 'verticalsplitpanescomponent'}
|
||||
<AppSplitpanes
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
panes={component.panes}
|
||||
{componentContainerHeight}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'horizontalsplitpanescomponent'}
|
||||
<AppSplitpanes
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
panes={component.panes}
|
||||
{componentContainerHeight}
|
||||
horizontal={true}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'iconcomponent'}
|
||||
<AppIcon
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'fileinputcomponent'}
|
||||
<AppFileInput
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
onFileChange={component.onFileChange}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 's3fileinputcomponent'}
|
||||
<AppS3FileInput
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
onFileChange={component.onFileChange}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'imagecomponent'}
|
||||
<AppImage
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'drawercomponent'}
|
||||
<AppDrawer
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
onOpenRecomputeIds={component.onOpenRecomputeIds}
|
||||
onCloseRecomputeIds={component.onCloseRecomputeIds}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'mapcomponent'}
|
||||
<AppMap
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'pdfcomponent'}
|
||||
<AppPdf
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'modalcomponent'}
|
||||
<AppModal
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
onOpenRecomputeIds={component.onOpenRecomputeIds}
|
||||
onCloseRecomputeIds={component.onCloseRecomputeIds}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'schemaformcomponent'}
|
||||
<AppSchemaForm
|
||||
id={component.id}
|
||||
componentInput={component.componentInput}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
{initializing}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'selecttabcomponent'}
|
||||
<AppSelectTab
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'selectstepcomponent'}
|
||||
<AppSelectStep
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'chartjscomponent'}
|
||||
<AppChartJs
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'chartjscomponentv2'}
|
||||
<AppChartJsV2
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
componentInput={component.componentInput}
|
||||
datasets={component.datasets}
|
||||
xData={component.xData}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'carousellistcomponent'}
|
||||
<AppCarouselList
|
||||
id={component.id}
|
||||
configuration={component.configuration}
|
||||
componentInput={component.componentInput}
|
||||
customCss={component.customCss}
|
||||
{componentContainerHeight}
|
||||
{render}
|
||||
bind:initializing
|
||||
/>
|
||||
{:else if component.type === 'accordionlistcomponent'}
|
||||
<AppAccordionList
|
||||
id={component.id}
|
||||
componentInput={component.componentInput}
|
||||
customCss={component.customCss}
|
||||
{componentContainerHeight}
|
||||
{render}
|
||||
bind:initializing
|
||||
/>
|
||||
{:else if component.type === 'statcomponent'}
|
||||
<AppStatCard
|
||||
id={component.id}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'menucomponent'}
|
||||
<AppMenu
|
||||
id={component.id}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
menuItems={component.menuItems}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'decisiontreecomponent' && component.nodes}
|
||||
<AppDecisionTree
|
||||
id={component.id}
|
||||
nodes={component.nodes}
|
||||
customCss={component.customCss}
|
||||
{componentContainerHeight}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'alertcomponent'}
|
||||
<AppAlert
|
||||
id={component.id}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'navbarcomponent'}
|
||||
<AppNavbar
|
||||
id={component.id}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
navbarItems={component.navbarItems}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'dateselectcomponent'}
|
||||
<AppDateSelect
|
||||
id={component.id}
|
||||
configuration={component.configuration}
|
||||
customCss={component.customCss}
|
||||
verticalAlignment={component.verticalAlignment}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'jobiddisplaycomponent'}
|
||||
<AppDisplayComponentByJobId
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
configuration={component.configuration}
|
||||
{render}
|
||||
/>
|
||||
{:else if component.type === 'recomputeallcomponent'}
|
||||
<AppRecomputeAll
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
bind:initializing
|
||||
configuration={component.configuration}
|
||||
horizontalAlignment={component.horizontalAlignment}
|
||||
{render}
|
||||
/>
|
||||
{/if}
|
||||
@@ -0,0 +1,219 @@
|
||||
<script lang="ts" context="module">
|
||||
let outTimeout: NodeJS.Timeout | undefined = undefined
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { AppEditorContext, AppViewerContext } from '../../types'
|
||||
import ComponentHeader from '../ComponentHeader.svelte'
|
||||
import type { AppComponent } from './components'
|
||||
|
||||
import { Anchor } from 'lucide-svelte'
|
||||
import { findGridItemParentGrid, isContainer } from '../appUtils'
|
||||
import ComponentInner from './ComponentInner.svelte'
|
||||
|
||||
export let component: AppComponent
|
||||
export let selected: boolean
|
||||
export let locked: boolean = false
|
||||
export let fullHeight: boolean
|
||||
export let overlapped: string | undefined = undefined
|
||||
export let moveMode: string | undefined = undefined
|
||||
export let componentDraggedId: string | undefined = undefined
|
||||
export let render: boolean = false
|
||||
const { mode, app, hoverStore, connectingInput } =
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
const editorContext = getContext<AppEditorContext>('AppEditorContext')
|
||||
const componentActive = editorContext?.componentActive
|
||||
|
||||
const movingcomponents = editorContext?.movingcomponents
|
||||
$: ismoving =
|
||||
movingcomponents != undefined && $mode == 'dnd' && $movingcomponents?.includes(component.id)
|
||||
|
||||
let initializing: boolean | undefined = undefined
|
||||
|
||||
let errorHandledByComponent: boolean = false
|
||||
let componentContainerHeight: number = 0
|
||||
let componentContainerWidth: number = 0
|
||||
|
||||
let inlineEditorOpened: boolean = false
|
||||
let showSkeleton = false
|
||||
|
||||
onMount(() => {
|
||||
setTimeout(() => {
|
||||
showSkeleton = true
|
||||
}, 100)
|
||||
})
|
||||
|
||||
function mouseOut() {
|
||||
outTimeout && clearTimeout(outTimeout)
|
||||
outTimeout = setTimeout(() => {
|
||||
if ($hoverStore !== undefined) {
|
||||
// In order to avoid flickering when hovering over table actions,
|
||||
// we leave the actions to manage the hover state
|
||||
if ($hoverStore.startsWith(`${component.id}_`)) {
|
||||
return
|
||||
}
|
||||
|
||||
$hoverStore = undefined
|
||||
}
|
||||
}, 50)
|
||||
}
|
||||
|
||||
function componentDraggedIsNotChild(componentDraggedId: string, componentId: string) {
|
||||
let parentGrid = findGridItemParentGrid($app, componentDraggedId)
|
||||
|
||||
return !parentGrid?.startsWith(`${componentId}-`)
|
||||
}
|
||||
|
||||
function areOnTheSameSubgrid(componentDraggedId: string, componentId: string) {
|
||||
return (
|
||||
findGridItemParentGrid($app, componentDraggedId) === findGridItemParentGrid($app, componentId)
|
||||
)
|
||||
}
|
||||
|
||||
let cachedComponentDraggedIsNotChild: boolean | undefined
|
||||
let cachedAreOnTheSameSubgrid: boolean | undefined
|
||||
|
||||
function updateCache(componentDraggedId: string | undefined) {
|
||||
if (componentDraggedId) {
|
||||
cachedComponentDraggedIsNotChild = componentDraggedIsNotChild(
|
||||
componentDraggedId,
|
||||
component.id
|
||||
)
|
||||
cachedAreOnTheSameSubgrid = areOnTheSameSubgrid(componentDraggedId, component.id)
|
||||
} else {
|
||||
cachedComponentDraggedIsNotChild = undefined
|
||||
cachedAreOnTheSameSubgrid = undefined
|
||||
}
|
||||
}
|
||||
|
||||
$: updateCache(componentDraggedId)
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
on:mouseover|stopPropagation={() => {
|
||||
outTimeout && clearTimeout(outTimeout)
|
||||
if (component.id !== $hoverStore) {
|
||||
$hoverStore = component.id
|
||||
}
|
||||
}}
|
||||
on:mouseout|stopPropagation={mouseOut}
|
||||
class={twMerge(
|
||||
'h-full flex flex-col w-full component relative',
|
||||
initializing ? 'overflow-hidden h-0' : ''
|
||||
)}
|
||||
data-connection-button
|
||||
>
|
||||
{#if render}
|
||||
{#if locked && componentActive && $componentActive && moveMode === 'move' && componentDraggedId && componentDraggedId !== component.id && cachedAreOnTheSameSubgrid}
|
||||
<div
|
||||
class={twMerge('absolute inset-0 bg-locked center-center flex-col z-50', 'bg-locked-hover')}
|
||||
>
|
||||
<div class="bg-surface p-2 shadow-sm rounded-md flex center-center flex-col gap-2">
|
||||
<Anchor size={24} class="text-primary " />
|
||||
<div class="text-xs"> Anchored: The component cannot be moved. </div>
|
||||
</div>
|
||||
</div>
|
||||
{:else if moveMode === 'insert' && isContainer(component.type) && componentDraggedId && componentDraggedId !== component.id && cachedComponentDraggedIsNotChild}
|
||||
<div
|
||||
class={twMerge(
|
||||
'absolute inset-0 flex-col rounded-md bg-blue-100 dark:bg-gray-800 bg-opacity-50',
|
||||
'outline-dashed outline-offset-2 outline-2 outline-blue-300 dark:outline-blue-700',
|
||||
overlapped === component?.id ? 'bg-draggedover dark:bg-draggedover-dark' : ''
|
||||
)}
|
||||
/>
|
||||
{/if}
|
||||
{#if $mode !== 'preview'}
|
||||
<ComponentHeader
|
||||
on:mouseover={() => {
|
||||
outTimeout && clearTimeout(outTimeout)
|
||||
|
||||
if (component.id !== $hoverStore) {
|
||||
$hoverStore = component.id
|
||||
}
|
||||
}}
|
||||
hover={$hoverStore === component.id}
|
||||
{component}
|
||||
{selected}
|
||||
{fullHeight}
|
||||
connecting={$connectingInput.opened}
|
||||
on:lock
|
||||
on:expand
|
||||
on:fillHeight
|
||||
{locked}
|
||||
{inlineEditorOpened}
|
||||
hasInlineEditor={component.type === 'textcomponent' &&
|
||||
component.componentInput &&
|
||||
component.componentInput.type !== 'connected'}
|
||||
on:triggerInlineEditor={() => {
|
||||
inlineEditorOpened = !inlineEditorOpened
|
||||
}}
|
||||
{errorHandledByComponent}
|
||||
{componentContainerWidth}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if ismoving}
|
||||
<div class="absolute -top-8 w-40">
|
||||
<button
|
||||
class="border p-0.5 text-xs"
|
||||
on:click={() => {
|
||||
$movingcomponents = undefined
|
||||
}}
|
||||
>
|
||||
Cancel move
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
<div
|
||||
class={twMerge(
|
||||
render ? 'h-full outline-1' : 'h-0 overflow-hidden',
|
||||
$mode === 'dnd' ? 'bg-surface/40' : '',
|
||||
$hoverStore === component.id && $mode !== 'preview'
|
||||
? $connectingInput.opened
|
||||
? 'outline outline-[#f8aa4b]'
|
||||
: 'outline outline-blue-400'
|
||||
: '',
|
||||
selected && $mode !== 'preview' ? 'outline outline-blue-600' : '',
|
||||
$mode != 'preview' ? 'cursor-pointer' : '',
|
||||
'relative z-auto',
|
||||
$app.css?.['app']?.['component']?.class,
|
||||
'wm-app-component',
|
||||
ismoving ? 'animate-pulse' : ''
|
||||
)}
|
||||
style={$app.css?.['app']?.['component']?.style}
|
||||
bind:clientHeight={componentContainerHeight}
|
||||
bind:clientWidth={componentContainerWidth}
|
||||
>
|
||||
<ComponentInner
|
||||
{component}
|
||||
{render}
|
||||
{componentContainerHeight}
|
||||
bind:initializing
|
||||
bind:errorHandledByComponent
|
||||
{inlineEditorOpened}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{#if initializing && render && showSkeleton}
|
||||
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
on:mouseover|stopPropagation={() => {
|
||||
if (component.id !== $hoverStore) {
|
||||
$hoverStore = component.id
|
||||
}
|
||||
}}
|
||||
on:mouseout|stopPropagation={() => {
|
||||
if ($hoverStore !== undefined) {
|
||||
$hoverStore = undefined
|
||||
}
|
||||
}}
|
||||
class="absolute inset-0 center-center flex-col border animate-skeleton dark:bg-frost-900/50 [animation-delay:1000ms]"
|
||||
/>
|
||||
{/if}
|
||||
@@ -9,6 +9,8 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { columnConfiguration, WIDE_GRID_COLUMNS } from '../gridUtils'
|
||||
|
||||
import gridHelp from './utils/helper'
|
||||
import type { AppViewerContext, GridItem } from '../types'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
@@ -41,7 +43,7 @@
|
||||
|
||||
export let items: FilledItem<T>[]
|
||||
export let rowHeight: number = ROW_HEIGHT
|
||||
export let cols: [number, number][]
|
||||
|
||||
export let gap = [ROW_GAP_X, ROW_GAP_Y]
|
||||
export let throttleUpdate = 100
|
||||
export let throttleResize = 100
|
||||
@@ -53,7 +55,10 @@
|
||||
export let root: boolean = false
|
||||
export let parentWidth: number | undefined = undefined
|
||||
export let disableMove: boolean = false
|
||||
let getComputedCols
|
||||
const cols = columnConfiguration
|
||||
|
||||
let getComputedCols: 3 | 12 | undefined =
|
||||
$app.mobileViewOnSmallerScreens == false ? WIDE_GRID_COLUMNS : undefined
|
||||
let container
|
||||
|
||||
$: [gapX, gapY] = gap
|
||||
@@ -73,16 +78,22 @@
|
||||
})
|
||||
}, throttleUpdate)
|
||||
|
||||
let mounted = false
|
||||
|
||||
onMount(() => {
|
||||
const sizeObserver = new ResizeObserver((entries) => {
|
||||
requestAnimationFrame(() => {
|
||||
let width = entries[0].contentRect.width
|
||||
|
||||
if (width === containerWidth) return
|
||||
if (width === containerWidth) {
|
||||
mounted = true
|
||||
return
|
||||
}
|
||||
|
||||
getComputedCols = getColumn(parentWidth ?? width, cols)
|
||||
|
||||
xPerPx = width / getComputedCols
|
||||
if ($app.mobileViewOnSmallerScreens != false || !getComputedCols) {
|
||||
getComputedCols = getColumn(parentWidth ?? width, cols)
|
||||
}
|
||||
xPerPx = width / getComputedCols!
|
||||
|
||||
if (!containerWidth) {
|
||||
items = specifyUndefinedColumns(items, getComputedCols, cols)
|
||||
@@ -97,6 +108,7 @@
|
||||
}
|
||||
|
||||
containerWidth = width
|
||||
mounted = true
|
||||
})
|
||||
})
|
||||
|
||||
@@ -106,7 +118,7 @@
|
||||
})
|
||||
|
||||
let sortedItems: FilledItem<T>[] = []
|
||||
$: sortedItems = JSON.parse(JSON.stringify(items)).sort((a, b) => a.id.localeCompare(b.id))
|
||||
$: sortedItems = smartCopy(items).sort((a, b) => a.id.localeCompare(b.id))
|
||||
|
||||
let resizing: boolean = false
|
||||
|
||||
@@ -123,21 +135,31 @@
|
||||
|
||||
let initItems: FilledItem<T>[] | undefined = undefined
|
||||
|
||||
function smartCopy(items: FilledItem<T>[]) {
|
||||
return getComputedCols != undefined
|
||||
? items.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
[getComputedCols as number]: structuredClone(item[getComputedCols as number])
|
||||
}
|
||||
})
|
||||
: []
|
||||
}
|
||||
const updateMatrix = ({ detail }) => {
|
||||
let isPointerUp = detail.isPointerUp
|
||||
let citems: FilledItem<T>[]
|
||||
if (isPointerUp) {
|
||||
try {
|
||||
citems = JSON.parse(JSON.stringify(initItems))
|
||||
} catch (e) {
|
||||
citems = JSON.parse(JSON.stringify(sortedItems))
|
||||
if (initItems == undefined) {
|
||||
citems = smartCopy(sortedItems)
|
||||
} else {
|
||||
citems = smartCopy(initItems)
|
||||
}
|
||||
initItems = undefined
|
||||
} else {
|
||||
if (initItems == undefined) {
|
||||
initItems = JSON.parse(JSON.stringify(sortedItems))
|
||||
initItems = smartCopy(sortedItems)
|
||||
}
|
||||
citems = JSON.parse(JSON.stringify(initItems))
|
||||
citems = smartCopy(initItems)
|
||||
}
|
||||
let nselectedIds = selectedIds ?? []
|
||||
if (detail.id && !selectedIds?.includes(detail.id)) {
|
||||
@@ -147,7 +169,7 @@
|
||||
for (let id of nselectedIds) {
|
||||
let activeItem = getItemById(id, sortedItems)
|
||||
|
||||
if (activeItem) {
|
||||
if (activeItem && getComputedCols) {
|
||||
activeItem = {
|
||||
...activeItem,
|
||||
[getComputedCols]: {
|
||||
@@ -202,11 +224,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
if (isPointerUp) {
|
||||
dispatch(
|
||||
'redraw',
|
||||
sortGridItemsPosition(JSON.parse(JSON.stringify(sortedItems)), getComputedCols)
|
||||
)
|
||||
if (isPointerUp && getComputedCols) {
|
||||
dispatch('redraw', sortGridItemsPosition(smartCopy(sortedItems), getComputedCols))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,7 +273,7 @@
|
||||
export function handleMove({ detail }) {
|
||||
Object.entries(moveResizes).forEach(([id, moveResize]) => {
|
||||
if (selectedIds?.includes(id)) {
|
||||
moveResize?.updateMove(JSON.parse(JSON.stringify(detail.cordDiff)), detail.eventY)
|
||||
moveResize?.updateMove(structuredClone(detail.cordDiff), detail.eventY)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -275,7 +294,7 @@
|
||||
) {
|
||||
const draggedItem = sortedItems.find((item) => item.id === $componentDraggedIdStore)
|
||||
|
||||
if (draggedItem) {
|
||||
if (draggedItem && getComputedCols) {
|
||||
draggedItem[getComputedCols].x = detail.shadow.x
|
||||
draggedItem[getComputedCols].y = detail.shadow.y
|
||||
}
|
||||
@@ -391,33 +410,33 @@
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if xPerPx > 0 && getComputedCols}
|
||||
{#each sortedItems as item (item.id)}
|
||||
{#if item[getComputedCols] != undefined}
|
||||
{#if $isCtrlOrMetaPressedStore && item.id === $overlappedStore && $componentDraggedIdStore && $componentDraggedParentIdStore !== item.id && $fakeShadowStore}
|
||||
{@const columnGap = gapX}
|
||||
<!-- gap between the columns in px -->
|
||||
{@const containerBorder = 0.5 * 16}
|
||||
<!-- 0.5rem converted to px (1rem = 16px) -->
|
||||
{@const gridTotalWidth = containerWidth ? containerWidth - 2 * containerBorder : 0}
|
||||
<!-- subtract borders -->
|
||||
{@const availableWidth = gridTotalWidth - 11 * columnGap}
|
||||
<!-- subtract gaps between the 12 columns (11 gaps) -->
|
||||
{@const columnWidthPx = availableWidth / 12}
|
||||
<!-- divide by the number of columns -->
|
||||
{@const maxX = Math.floor(availableWidth / columnWidthPx) - $fakeShadowStore.w}
|
||||
|
||||
{#each sortedItems as item (item.id)}
|
||||
{#if item[getComputedCols] != undefined}
|
||||
{#if $isCtrlOrMetaPressedStore && item.id === $overlappedStore && $componentDraggedIdStore && $componentDraggedParentIdStore !== item.id && $fakeShadowStore}
|
||||
{@const columnGap = gapX}
|
||||
<!-- gap between the columns in px -->
|
||||
{@const containerBorder = 0.5 * 16}
|
||||
<!-- 0.5rem converted to px (1rem = 16px) -->
|
||||
{@const gridTotalWidth = containerWidth ? containerWidth - 2 * containerBorder : 0}
|
||||
<!-- subtract borders -->
|
||||
{@const availableWidth = gridTotalWidth - 11 * columnGap}
|
||||
<!-- subtract gaps between the 12 columns (11 gaps) -->
|
||||
{@const columnWidthPx = availableWidth / 12}
|
||||
<!-- divide by the number of columns -->
|
||||
{@const maxX = Math.floor(availableWidth / columnWidthPx) - $fakeShadowStore.w}
|
||||
|
||||
<div
|
||||
class="absolute"
|
||||
style={`
|
||||
<div
|
||||
class="absolute"
|
||||
style={`
|
||||
left: ${item[getComputedCols].x * xPerPx + gapX}px;
|
||||
top: ${item[getComputedCols].y * yPerPx + gapY}px;
|
||||
`}
|
||||
>
|
||||
<div class="relative h-full w-full">
|
||||
<div
|
||||
class={twMerge('absolute transition-all duration-[50ms] bg-blue-300')}
|
||||
style={`
|
||||
>
|
||||
<div class="relative h-full w-full">
|
||||
<div
|
||||
class={twMerge('absolute transition-all duration-[50ms] bg-blue-300')}
|
||||
style={`
|
||||
left: calc(${
|
||||
Math.min($fakeShadowStore.x, maxX) * $fakeShadowStore.xPerPx + gapX
|
||||
}px + 0.5rem + ${getDeltaXByComponent(item.data['type'])});
|
||||
@@ -427,68 +446,73 @@
|
||||
width: ${$fakeShadowStore.w * $fakeShadowStore.xPerPx - gapX * 2}px;
|
||||
height: ${$fakeShadowStore.h * $fakeShadowStore.yPerPx - gapY * 2}px;
|
||||
`}
|
||||
/>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<MoveResize
|
||||
on:initmove={() => handleInitMove(item.id)}
|
||||
on:move={handleMove}
|
||||
bind:shadow={shadows[item.id]}
|
||||
bind:this={moveResizes[item.id]}
|
||||
on:repaint={handleRepaint}
|
||||
on:resizeStart={() => (resizing = true)}
|
||||
on:resizeEnd={() => (resizing = false)}
|
||||
onTop={Boolean(allIdsInPath?.includes(item.id))}
|
||||
id={item.id}
|
||||
{xPerPx}
|
||||
{yPerPx}
|
||||
fakeShadow={$fakeShadowStore}
|
||||
on:dropped={(e) => {
|
||||
$componentDraggedIdStore = undefined
|
||||
$componentDraggedParentIdStore = undefined
|
||||
$overlappedStore = undefined
|
||||
$fakeShadowStore = undefined
|
||||
lastDetail = undefined
|
||||
|
||||
if (!$isCtrlOrMetaPressedStore) {
|
||||
return
|
||||
}
|
||||
|
||||
dispatch('dropped', e.detail)
|
||||
}}
|
||||
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}
|
||||
item={item[getComputedCols]}
|
||||
cols={getComputedCols}
|
||||
{gapX}
|
||||
{gapY}
|
||||
{sensor}
|
||||
container={scroller}
|
||||
nativeContainer={container}
|
||||
overlapped={$overlappedStore}
|
||||
moveMode={$isCtrlOrMetaPressedStore ? 'insert' : 'move'}
|
||||
type={item.data['type']}
|
||||
{disableMove}
|
||||
>
|
||||
{#if item[getComputedCols]}
|
||||
<slot
|
||||
dataItem={item}
|
||||
hidden={false}
|
||||
overlapped={$overlappedStore}
|
||||
moveMode={$isCtrlOrMetaPressedStore ? 'insert' : 'move'}
|
||||
componentDraggedId={$componentDraggedIdStore}
|
||||
/>
|
||||
{/if}
|
||||
</MoveResize>
|
||||
{/if}
|
||||
{/each}
|
||||
<MoveResize
|
||||
{mounted}
|
||||
on:initmove={() => handleInitMove(item.id)}
|
||||
on:move={handleMove}
|
||||
bind:shadow={shadows[item.id]}
|
||||
bind:this={moveResizes[item.id]}
|
||||
on:repaint={handleRepaint}
|
||||
on:resizeStart={() => (resizing = true)}
|
||||
on:resizeEnd={() => (resizing = false)}
|
||||
onTop={Boolean(allIdsInPath?.includes(item.id))}
|
||||
id={item.id}
|
||||
{xPerPx}
|
||||
{yPerPx}
|
||||
fakeShadow={$fakeShadowStore}
|
||||
on:dropped={(e) => {
|
||||
$componentDraggedIdStore = undefined
|
||||
$componentDraggedParentIdStore = undefined
|
||||
$overlappedStore = undefined
|
||||
$fakeShadowStore = undefined
|
||||
lastDetail = undefined
|
||||
|
||||
if (!$isCtrlOrMetaPressedStore) {
|
||||
return
|
||||
}
|
||||
|
||||
dispatch('dropped', e.detail)
|
||||
}}
|
||||
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}
|
||||
item={item[getComputedCols]}
|
||||
cols={getComputedCols}
|
||||
{gapX}
|
||||
{gapY}
|
||||
{sensor}
|
||||
container={scroller}
|
||||
nativeContainer={container}
|
||||
overlapped={$overlappedStore}
|
||||
moveMode={$isCtrlOrMetaPressedStore ? 'insert' : 'move'}
|
||||
type={item.data['type']}
|
||||
{disableMove}
|
||||
>
|
||||
{#if item[getComputedCols]}
|
||||
<slot
|
||||
dataItem={item}
|
||||
hidden={false}
|
||||
overlapped={$overlappedStore}
|
||||
moveMode={$isCtrlOrMetaPressedStore ? 'insert' : 'move'}
|
||||
componentDraggedId={$componentDraggedIdStore}
|
||||
/>
|
||||
{/if}
|
||||
</MoveResize>
|
||||
{/if}
|
||||
{/each}
|
||||
{:else if root}
|
||||
<div
|
||||
class="h-full w-full flex-col animate-skeleton dark:bg-frost-900/50 [animation-delay:1000ms]"
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
export let type: string | undefined = undefined
|
||||
export let fakeShadow: GridShadow | undefined = undefined
|
||||
export let disableMove: boolean = true
|
||||
export let mounted: boolean = false
|
||||
|
||||
const ctx = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { mode, app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
@@ -504,7 +505,7 @@
|
||||
: trans
|
||||
? `transform: translate(${cordDiff.x}px, ${cordDiff.y}px); position:absolute; transition: width 0.2s, height 0.2s;`
|
||||
: `${
|
||||
xPerPx > 0 ? 'transition: transform 0.1s, opacity 0.1s;' : ''
|
||||
xPerPx > 0 && mounted ? 'transition: transform 0.1s, opacity 0.1s;' : ''
|
||||
} transform: translate(${left}px, ${top}px); `} "
|
||||
>
|
||||
<slot />
|
||||
|
||||
Reference in New Issue
Block a user