feat(frontend): App initial loading animations (#1176)

* feat(frontend): App initial loading animations

* fix(frontend): Optional prop
This commit is contained in:
Ádám Kovács
2023-02-02 17:55:11 +01:00
committed by GitHub
parent 42691bc1bd
commit 3305481d5d
16 changed files with 89 additions and 69 deletions
@@ -109,12 +109,10 @@
console.error(err)
}
}
console.debug('cancelled')
}
export async function clearCurrentJob() {
if (currentId) {
console.debug('clear')
job = undefined
await cancelJob()
}
@@ -180,7 +178,6 @@
async function syncer(id: string): Promise<void> {
if (currentId != id) {
console.debug('stop')
return
}
syncIteration++
@@ -7,6 +7,7 @@
export let id: string
export let componentInput: AppInput | undefined
export let initializing
const requireHtmlApproval = getContext<boolean | undefined>(IS_APP_PUBLIC_CONTEXT_KEY)
let result: any = undefined
@@ -14,7 +15,7 @@
export const staticOutputs: string[] = ['result', 'loading']
</script>
<RunnableWrapper flexWrap bind:result bind:componentInput {id}>
<RunnableWrapper flexWrap bind:componentInput {id} bind:initializing bind:result>
<div class="w-full border-b px-2 text-xs p-1 font-semibold bg-gray-500 text-white rounded-t-sm">
Results
</div>
@@ -1,6 +1,5 @@
<script lang="ts">
import { Bar, Line } from 'svelte-chartjs'
import {
Chart as ChartJS,
Title,
@@ -12,7 +11,6 @@
CategoryScale,
BarElement
} from 'chart.js'
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
import type { AppInput } from '../../inputType'
import InputValue from '../helpers/InputValue.svelte'
@@ -20,6 +18,7 @@
export let id: string
export let componentInput: AppInput | undefined
export let configuration: Record<string, AppInput>
export let initializing
export const staticOutputs: string[] = ['loading', 'result']
@@ -71,7 +70,7 @@
<InputValue {id} input={configuration.theme} bind:value={theme} />
<InputValue {id} input={configuration.line} bind:value={lineChart} />
<RunnableWrapper flexWrap autoRefresh bind:componentInput {id} bind:result>
<RunnableWrapper flexWrap autoRefresh bind:componentInput {id} bind:initializing bind:result>
{#if result}
{#if lineChart}
<Line {data} {options} />
@@ -4,6 +4,7 @@
export let id: string
export let componentInput: AppInput | undefined
export let initializing
export const staticOutputs: string[] = ['result', 'loading']
@@ -20,7 +21,7 @@
bind:clientHeight={h}
bind:clientWidth={w}
>
<RunnableWrapper autoRefresh flexWrap bind:componentInput {id} bind:result>
<RunnableWrapper autoRefresh flexWrap bind:componentInput {id} bind:initializing bind:result>
{#key result}
<iframe
frameborder="0"
@@ -1,6 +1,5 @@
<script lang="ts">
import { Doughnut, Pie } from 'svelte-chartjs'
import {
Chart as ChartJS,
Title,
@@ -19,6 +18,7 @@
export let id: string
export let componentInput: AppInput | undefined
export let configuration: Record<string, AppInput>
export let initializing
export const staticOutputs: string[] = ['loading', 'result']
@@ -65,7 +65,7 @@
<InputValue {id} input={configuration.theme} bind:value={theme} />
<InputValue {id} input={configuration.doughnutStyle} bind:value={doughnut} />
<RunnableWrapper flexWrap autoRefresh bind:componentInput {id} bind:result>
<RunnableWrapper flexWrap autoRefresh bind:componentInput {id} bind:initializing bind:result>
{#if result}
{#if doughnut}
<Doughnut {data} {options} />
@@ -1,6 +1,5 @@
<script lang="ts">
import zoomPlugin from 'chartjs-plugin-zoom'
import {
Chart as ChartJS,
Title,
@@ -12,7 +11,6 @@
CategoryScale,
BarElement
} from 'chart.js'
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
import type { AppInput } from '../../inputType'
import Scatter from 'svelte-chartjs/Scatter.svelte'
@@ -21,6 +19,7 @@
export let id: string
export let componentInput: AppInput | undefined
export let configuration: Record<string, AppInput>
export let initializing
let zoomable = false
let pannable = false
@@ -70,7 +69,7 @@
<InputValue {id} input={configuration.zoomable} bind:value={zoomable} />
<InputValue {id} input={configuration.pannable} bind:value={pannable} />
<RunnableWrapper flexWrap autoRefresh bind:componentInput {id} bind:result>
<RunnableWrapper flexWrap autoRefresh bind:componentInput {id} bind:initializing bind:result>
{#if result}
<Scatter {data} {options} />
{/if}
@@ -1,6 +1,6 @@
<script lang="ts">
import { Clipboard } from 'lucide-svelte'
import { copyToClipboard } from '../../../../utils'
import { copyToClipboard } from '../../../../utils'
import Button from '../../../common/button/Button.svelte'
import Popover from '../../../Popover.svelte'
import type { AppInput } from '../../inputType'
@@ -13,6 +13,7 @@ import { copyToClipboard } from '../../../../utils'
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = 'left'
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
export let configuration: Record<string, AppInput>
export let initializing
export const staticOutputs: string[] = ['result', 'loading']
@@ -59,7 +60,7 @@ import { copyToClipboard } from '../../../../utils'
<InputValue {id} input={configuration.style} bind:value={style} />
<InputValue {id} input={configuration.copyButton} bind:value={copyButton} />
<RunnableWrapper flexWrap bind:componentInput {id} bind:result>
<RunnableWrapper flexWrap bind:componentInput {id} bind:initializing bind:result>
<AlignWrapper {horizontalAlignment} {verticalAlignment}>
{#if !result || result === ''}
<div class="text-gray-400 bg-gray-100 flex justify-center items-center h-full w-full">
@@ -1,7 +1,6 @@
<script lang="ts">
import zoomPlugin from 'chartjs-plugin-zoom'
import 'chartjs-adapter-date-fns'
import {
Chart as ChartJS,
Title,
@@ -15,7 +14,6 @@
TimeScale,
LogarithmicScale
} from 'chart.js'
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
import type { AppInput } from '../../inputType'
import Scatter from 'svelte-chartjs/Scatter.svelte'
@@ -24,6 +22,7 @@
export let id: string
export let componentInput: AppInput | undefined
export let configuration: Record<string, AppInput>
export let initializing
export const staticOutputs: string[] = ['loading', 'result']
@@ -85,7 +84,7 @@
<InputValue {id} input={configuration.zoomable} bind:value={zoomable} />
<InputValue {id} input={configuration.pannable} bind:value={pannable} />
<RunnableWrapper flexWrap autoRefresh bind:componentInput {id} bind:result>
<RunnableWrapper flexWrap autoRefresh bind:componentInput {id} bind:initializing bind:result>
{#if result}
<Scatter {data} {options} />
{/if}
@@ -2,12 +2,12 @@
import { Loader2 } from 'lucide-svelte'
import { onMount } from 'svelte'
import type { AppInput } from '../../inputType'
import InputValue from '../helpers/InputValue.svelte'
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
export let id: string
export let componentInput: AppInput | undefined
export let configuration: Record<string, AppInput>
export let initializing
export const staticOutputs: string[] = ['result', 'loading']
@@ -41,7 +41,7 @@
</script>
<div class="w-full h-full" bind:clientHeight={h} bind:clientWidth={w}>
<RunnableWrapper flexWrap bind:componentInput {id} bind:result>
<RunnableWrapper flexWrap bind:componentInput {id} bind:initializing bind:result>
{#if !Plotly}
<div class="p-2">
<Loader2 class="animate-spin" />
@@ -8,6 +8,7 @@
export let id: string
export let componentInput: AppInput | undefined
export let configuration: Record<string, AppInput>
export let initializing
export const staticOutputs: string[] = ['result', 'loading']
@@ -52,7 +53,7 @@
<InputValue {id} input={configuration.canvas} bind:value={canvas} />
<div class="w-full h-full" bind:clientHeight={h} bind:clientWidth={w}>
<RunnableWrapper flexWrap bind:componentInput {id} bind:result>
<RunnableWrapper flexWrap bind:componentInput {id} bind:initializing bind:result>
{#if !vegaEmbed}
<div class="p-2">
<Loader2 class="animate-spin" />
@@ -24,6 +24,7 @@
export let defaultUserInput = false
export let flexWrap = false
export let wrapperClass = ''
export let initializing: boolean | undefined = undefined
const {
worldStore,
@@ -305,7 +306,7 @@
/>
<div class="h-full flex relative flex-row flex-wrap {wrapperClass}">
{#if autoRefresh === true}
{#if !initializing && autoRefresh === true}
<div class="flex absolute top-1 right-1">
<RefreshButton componentId={id} />
</div>
@@ -9,6 +9,7 @@
export let componentInput: AppInput | undefined
export let id: string
export let result: any = undefined
export let initializing = true
export let extraQueryParams: Record<string, any> = {}
export let autoRefresh: boolean = true
@@ -19,6 +20,10 @@
export let runnableClass = ''
const { staticExporter, noBackend } = getContext<AppEditorContext>('AppEditorContext')
$: if(initializing && result) {
initializing = false
}
if (noBackend && componentInput?.type == 'runnable') {
result = componentInput?.['value']
@@ -46,6 +51,7 @@
{id}
{extraQueryParams}
{forceSchemaDisplay}
{initializing}
wrapperClass={runnableClass}
>
<slot />
@@ -18,6 +18,7 @@
export let componentInput: AppInput | undefined
export let configuration: Record<string, AppInput>
export let actionButtons: (BaseAppComponent & ButtonComponent)[]
export let initializing
export const staticOutputs: string[] = [
'selectedRow',
@@ -135,7 +136,7 @@
<InputValue {id} input={configuration.search} bind:value={search} />
<RunnableWrapper flexWrap bind:componentInput {id} bind:result>
<RunnableWrapper flexWrap bind:componentInput {id} bind:initializing bind:result>
{#if Array.isArray(result) && result.every(isObject)}
<div class="border border-gray-300 shadow-sm divide-y divide-gray-300 flex flex-col h-full">
{#if search !== 'Disabled'}
@@ -1,11 +1,13 @@
<script lang="ts">
import { classNames } from '$lib/utils'
import { getContext } from 'svelte'
import { fade } from 'svelte/transition';
import { classNames } from '$lib/utils'
import type { AppComponent, AppEditorContext } from '../types'
import { Loader2 } from 'lucide-svelte'
import BarChartComponent from '../components/dataDisplay/AppBarChart.svelte'
import DisplayComponent from '../components/DisplayComponent.svelte'
import TableComponent from '../components/table/AppTable.svelte'
import TextComponent from '../components/dataDisplay/AppText.svelte'
import type { AppComponent, AppEditorContext } from '../types'
import ButtonComponent from '../components/buttons/AppButton.svelte'
import PieChartComponent from '../components/dataDisplay/AppPieChart.svelte'
import SelectComponent from '../components/selectInputs/AppSelect.svelte'
@@ -28,8 +30,9 @@
export let locked: boolean = false
export let pointerdown: boolean = false
let hover = false
const { staticOutputs, mode, connectingInput } = getContext<AppEditorContext>('AppEditorContext')
let hover = false
let initializing
</script>
<div
@@ -64,54 +67,63 @@
{#if component.type === 'displaycomponent'}
<DisplayComponent
{...component}
bind:initializing
bind:componentInput={component.componentInput}
bind:staticOutputs={$staticOutputs[component.id]}
/>
{:else if component.type === 'barchartcomponent'}
<BarChartComponent
{...component}
bind:initializing
bind:componentInput={component.componentInput}
bind:staticOutputs={$staticOutputs[component.id]}
/>
{:else if component.type === 'timeseriescomponent'}
<AppTimeseries
{...component}
bind:initializing
bind:componentInput={component.componentInput}
bind:staticOutputs={$staticOutputs[component.id]}
/>
{:else if component.type === 'htmlcomponent'}
<AppHtml
{...component}
bind:initializing
bind:componentInput={component.componentInput}
bind:staticOutputs={$staticOutputs[component.id]}
/>
{:else if component.type === 'vegalitecomponent'}
<VegaLiteHtml
{...component}
bind:initializing
bind:componentInput={component.componentInput}
bind:staticOutputs={$staticOutputs[component.id]}
/>
{:else if component.type === 'plotlycomponent'}
<PlotlyHtml
{...component}
bind:initializing
bind:componentInput={component.componentInput}
bind:staticOutputs={$staticOutputs[component.id]}
/>
{:else if component.type === 'scatterchartcomponent'}
<AppScatterChart
{...component}
bind:initializing
bind:componentInput={component.componentInput}
bind:staticOutputs={$staticOutputs[component.id]}
/>
{:else if component.type === 'piechartcomponent'}
<PieChartComponent
{...component}
bind:initializing
bind:staticOutputs={$staticOutputs[component.id]}
bind:componentInput={component.componentInput}
/>
{:else if component.type === 'tablecomponent'}
<TableComponent
{...component}
bind:initializing
bind:staticOutputs={$staticOutputs[component.id]}
bind:componentInput={component.componentInput}
bind:actionButtons={component.actionButtons}
@@ -119,6 +131,7 @@
{:else if component.type === 'textcomponent'}
<TextComponent
{...component}
bind:initializing
bind:componentInput={component.componentInput}
bind:staticOutputs={$staticOutputs[component.id]}
/>
@@ -165,3 +178,12 @@
{/if}
</div>
</div>
{#if initializing}
<div
out:fade={{ duration: 200 }}
class="absolute inset-0 center-center flex-col bg-white text-gray-600 border"
>
<Loader2 class="animate-spin" size={16} />
<span class="text-xs mt-1">Loading</span>
</div>
{/if}
+30 -38
View File
@@ -110,49 +110,44 @@ const DEFAULT_CODES: Partial<Record<AppComponent['type'], Record<'deno' | 'pytho
},
vegalitecomponent: {
deno:
`
export async function main() {
return {
data: {
values: [
{ a: "A", b: 28 },
{ a: "B", b: 55 },
{ a: "C", b: 43 },
{ a: "D", b: 91 },
],
},
mark: "bar",
encoding: {
x: { field: "a", type: "ordinal" },
y: { field: "b", type: "quantitative" },
},
};
}
`,
`export async function main() {
return {
data: {
values: [
{ a: "A", b: 28 },
{ a: "B", b: 55 },
{ a: "C", b: 43 },
{ a: "D", b: 91 },
],
},
mark: "bar",
encoding: {
x: { field: "a", type: "ordinal" },
y: { field: "b", type: "quantitative" },
},
}
}`,
python3:
`
def main():
`def main():
return {
"data": {
"values": [
{ "a": "A", "b": 28 },
{ "a": "B", "b": 55 },
{ "a": "C", "b": 43 },
{ "a": "D", "b": 91 },
],
"values": [
{ "a": "A", "b": 28 },
{ "a": "B", "b": 55 },
{ "a": "C", "b": 43 },
{ "a": "D", "b": 91 },
],
},
"mark": "bar",
"mark": "bar",
"encoding": {
"x": { "field": "a", "type": "ordinal" },
"y": { "field": "b", "type": "quantitative" },
},
}
`
}`
},
plotlycomponent: {
deno:
`
export async function main() {
`export async function main() {
return {
type: 'bar',
x: [1, 2, 3, 4],
@@ -164,11 +159,9 @@ export async function main() {
}
}
};
}
`,
}`,
python3:
`
def main():
`def main():
return {
"type": "bar",
"x": [1, 2, 3, 4],
@@ -179,8 +172,7 @@ def main():
"width": 2.5
}
}
}
`
}`
},
piechartcomponent: {
deno:
+5 -5
View File
@@ -115,7 +115,7 @@ export const displayData: Record<AppComponent['type'], { name: string; icon: any
icon: BarChart4
},
htmlcomponent: {
name: 'Html',
name: 'HTML',
icon: Code2
},
vegalitecomponent: {
@@ -143,7 +143,7 @@ export const displayData: Record<AppComponent['type'], { name: string; icon: any
icon: ToggleLeft
},
textinputcomponent: {
name: 'Text input',
name: 'Text Input',
icon: TextCursorInput
},
imagecomponent: {
@@ -155,11 +155,11 @@ export const displayData: Record<AppComponent['type'], { name: string; icon: any
icon: FormInput
},
radiocomponent: {
name: 'Radio button',
name: 'Radio Button',
icon: CircleDot
},
runformcomponent: {
name: 'Run form',
name: 'Run Form',
icon: Play
},
selectcomponent: {
@@ -179,7 +179,7 @@ export const displayData: Record<AppComponent['type'], { name: string; icon: any
icon: Lock
},
dateinputcomponent: {
name: 'Date input',
name: 'Date Input',
icon: Calendar
}
}