fix(frontend): fix recompute all (#4161)

* fix(frontend): wip

* fix(frontend): wip

* fix(frontend): fix recompute all

* fix(frontend): fix recompute all

* fix(frontend): clean up

* fix(frontend): fix how the onClick callback is called
This commit is contained in:
Faton Ramadani
2024-07-31 17:27:38 +02:00
committed by GitHub
parent 78c115c68c
commit f9324a2e89
7 changed files with 194 additions and 135 deletions
@@ -168,7 +168,13 @@
previewTheme,
debuggingComponents: writable({}),
replaceStateFn: (path) => replaceState(path, $page.state),
policy: policy
policy: policy,
recomputeAllContext: writable({
loading: false,
componentNumber: 0,
refreshing: [],
progress: 100
})
})
let scale = writable(100)
@@ -118,7 +118,13 @@
debuggingComponents: writable({}),
replaceStateFn,
gotoFn,
policy
policy,
recomputeAllContext: writable({
loading: false,
componentNumber: 0,
refreshing: [],
progress: 100
})
})
let previousSelectedIds: string[] | undefined = undefined
@@ -200,11 +206,13 @@
: 'max-w-7xl'} mx-auto"
id="app-content"
>
{#if $appStore.grid && $appStore.hideLegacyTopBar !== true}
{#if $appStore.grid}
<div
class={twMerge(
'mx-auto',
hideRefreshBar || $appStore?.norefreshbar ? 'invisible h-0 overflow-hidden' : ''
hideRefreshBar || $appStore?.norefreshbar || $appStore.hideLegacyTopBar === true
? 'invisible h-0 overflow-hidden'
: ''
)}
>
<div
@@ -48,7 +48,7 @@
</script>
<div class="w-full z-[1000] overflow-visible h-full">
{#if $app.hideLegacyTopBar !== true}
<div class={$app.hideLegacyTopBar ? 'hidden' : ''}>
<div
class="w-full sticky top-0 flex justify-between border-b {$componentActive
? 'invisible'
@@ -94,7 +94,7 @@
</div>
</div>
</div>
{/if}
</div>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
style={$app.css?.['app']?.['grid']?.style}
@@ -0,0 +1,113 @@
<script lang="ts">
import { Loader2, RefreshCw, TimerReset } from 'lucide-svelte'
import Button from '../../common/button/Button.svelte'
import ButtonDropdown from '$lib/components/common/button/ButtonDropdown.svelte'
import { MenuItem } from '@rgossiaux/svelte-headlessui'
import { classNames } from '$lib/utils'
import { twMerge } from 'tailwind-merge'
import Badge from '$lib/components/common/badge/Badge.svelte'
import { createEventDispatcher } from 'svelte'
export let componentNumber: number = 0
export let interval: number | undefined = undefined
export let refreshing: string[] = []
export let progress: number = 100
export let loading: boolean | undefined = false
const dispatch = createEventDispatcher()
const items = [
{
displayName: 'Once',
action: () => dispatch('setInter', undefined)
},
...[1, 2, 3, 4, 5, 6].map((i) => ({
displayName: `Every ${i * 5} seconds`,
action: () => dispatch('setInter', i * 5000)
}))
]
</script>
<div class="border rounded-md overflow-hidden">
<div class={twMerge('flex items-center')}>
<Button
disabled={componentNumber == 0}
on:click
color="light"
size="xs"
variant="border"
btnClasses={twMerge(
'!rounded-none text-tertiary !text-2xs !border-r border-y-0 border-l-0 group'
)}
title="Refresh {componentNumber} component{componentNumber > 1 ? 's' : ''} {interval
? `every ${interval / 1000} seconds`
: 'Once'} {refreshing?.length > 0 ? `(live: ${refreshing?.join(', ')}))` : ''}"
>
<div class="z-10 flex flex-row items-center gap-2">
{#if !loading}
<RefreshCw size={14} />
{:else}
<Loader2 class="animate-spin text-blue-500" size={14} />
{/if}
({componentNumber})
</div>
</Button>
<ButtonDropdown hasPadding={false}>
<slot:fragment slot="buttonReplacement">
<div class="flex flex-row gap-2 text-xs hover:bg-surface-hover px-2 items-center h-7">
{#if interval}
<Badge color="blue" small>
{interval ? `Every ${interval / 1000}s` : 'Once'}
</Badge>
{/if}
<div class="flex justify-center items-center">
<TimerReset size={14} />
</div>
</div>
</slot:fragment>
<svelte:fragment slot="label">
<span
class={twMerge('text-xs min-w-[2rem] ', interval ? 'text-blue-500' : 'text-tertiary')}
>
{interval ? `${interval / 1000}s` : 'Once'}
</span>
</svelte:fragment>
<svelte:fragment slot="items">
{#each items ?? [] as { }, index}
<MenuItem
on:click={() => {
if (index === 0) {
dispatch('setInter', undefined)
} else {
dispatch('setInter', index * 5000)
}
}}
>
<div
class={classNames(
'!text-tertiary text-left px-4 py-2 gap-2 cursor-pointer hover:bg-surface-hover !text-xs font-semibold'
)}
>
{#if index === 0}
Once
{:else}
{`Every ${index * 5} seconds`}
{/if}
</div>
</MenuItem>
{/each}
</svelte:fragment>
</ButtonDropdown>
</div>
{#if interval}
<div class="w-full bg-gray-200 rounded-full h-0.5 dark:bg-gray-700">
<div
class="bg-blue-300 h-0.5 rounded-full dark:bg-blue-500 transition-all"
style="width: {progress}%"
/>
</div>
{/if}
</div>
@@ -1,22 +1,11 @@
<script lang="ts" context="module">
let loading: Writable<boolean> = writable(false)
let progress: Writable<number> = writable(100)
</script>
<script lang="ts">
import { Loader2, RefreshCw, TimerReset } from 'lucide-svelte'
import { getContext, onMount } from 'svelte'
import Button from '../../common/button/Button.svelte'
import type { AppEditorContext, AppViewerContext } from '../types'
import { allItems } from '../utils'
import ButtonDropdown from '$lib/components/common/button/ButtonDropdown.svelte'
import { MenuItem } from '@rgossiaux/svelte-headlessui'
import { classNames } from '$lib/utils'
import { twMerge } from 'tailwind-merge'
import { writable, type Writable } from 'svelte/store'
import Badge from '$lib/components/common/badge/Badge.svelte'
import RecomputeAllButton from './RecomputeAllButton.svelte'
const { runnableComponents, app, initialized } = getContext<AppViewerContext>('AppViewerContext')
const { runnableComponents, app, initialized, recomputeAllContext } =
getContext<AppViewerContext>('AppViewerContext')
const appEditorContext = getContext<AppEditorContext>('AppEditorContext')
let timeout: NodeJS.Timeout | undefined = undefined
@@ -29,7 +18,9 @@
$initialized.initializedComponents?.length ==
allItems($app.grid, $app.subgrids).length + ($app.hiddenInlineScripts?.length ?? 0) &&
refresh()
$: componentNumber = Object.values($runnableComponents).filter((x) => x.autoRefresh).length
$: $recomputeAllContext.componentNumber =
Object.values($runnableComponents).filter((x) => x.autoRefresh).length ?? 0
onMount(() => {
if (appEditorContext) {
@@ -56,6 +47,7 @@
if (stopAfterClear) return
}
refresh()
if (interval) {
shouldRefresh = true
timeout = setInterval(refresh, interval)
@@ -64,21 +56,23 @@
}
function startProgress() {
progress.set(100)
$recomputeAllContext.progress = 100
if (progressTimer) clearInterval(progressTimer)
progressTimer = setInterval(() => {
progress.update((n) => {
const newProgress = n - 100 / ((interval ?? 1000) / 100)
if ($recomputeAllContext.progress) {
const newProgress = $recomputeAllContext.progress - 100 / ((interval ?? 1000) / 100)
if (newProgress <= 0) {
return 0
}
return newProgress
})
$recomputeAllContext.progress = newProgress
}
}, 100)
}
function setInter(inter: number | undefined) {
interval = inter
$recomputeAllContext.interval = inter
onClick(!inter)
}
@@ -90,8 +84,8 @@
firstLoad = true
isFirstLoad = true
}
$loading = true
$progress = 100
$recomputeAllContext.loading = true
$recomputeAllContext.progress = 100
console.log('refresh all')
refreshing = []
@@ -118,13 +112,16 @@
console.error('refresh error', id)
refreshing = refreshing.filter((x) => x !== id)
})
.finally(() => {
$recomputeAllContext.refreshing = refreshing
})
)
}
})
.filter(Boolean)
Promise.all(promises).finally(() => {
$loading = false
$recomputeAllContext.loading = false
})
}
@@ -141,108 +138,23 @@
}
}
let items = [
{
displayName: 'Once',
action: () => setInter(undefined)
},
...[1, 2, 3, 4, 5, 6].map((i) => ({
displayName: `Every ${i * 5} seconds`,
action: () => setInter(i * 5000)
}))
]
onMount(() => {
$recomputeAllContext = {
onClick,
setInter
}
})
</script>
<!-- {$initialized.initializedComponents?.join(', ')}
{allItems($app.grid, $app.subgrids).length + $app.hiddenInlineScripts.length} -->
<!-- {$initialized.initializedComponents} -->
<!-- {allItems($app.grid, $app.subgrids)
.map((x) => x.id)
.filter((x) => !$initialized.initializedComponents?.includes(x))
.sort()
.join(', ')} -->
<!-- {allItems($app.grid, $app.subgrids).map((x) => x.id)} -->
<div class=" border rounded-md overflow-hidden">
<div class={twMerge('flex items-center')}>
<Button
disabled={componentNumber == 0}
on:click={() => onClick()}
color="light"
size="xs"
variant="border"
btnClasses={twMerge(
'!rounded-none text-tertiary !text-2xs !border-r border-y-0 border-l-0 group'
)}
title="Refresh {componentNumber} component{componentNumber > 1 ? 's' : ''} {interval
? `every ${interval / 1000} seconds`
: 'Once'} {refreshing.length > 0 ? `(live: ${refreshing.join(', ')}))` : ''}"
>
<div class="z-10 flex flex-row items-center gap-2">
{#if !$loading}
<RefreshCw size={14} />
{:else}
<Loader2 class="animate-spin text-blue-500" size={14} />
{/if}
({componentNumber})
</div>
</Button>
<ButtonDropdown hasPadding={false}>
<slot:fragment slot="buttonReplacement">
<div class="flex flex-row gap-2 text-xs hover:bg-surface-hover px-2 items-center h-7">
{#if interval}
<Badge color="blue" small>
{interval ? `Every ${interval / 1000}s` : 'Once'}
</Badge>
{/if}
<div class="flex justify-center items-center">
<TimerReset size={14} />
</div>
</div>
</slot:fragment>
<svelte:fragment slot="label">
<span
class={twMerge('text-xs min-w-[2rem] ', interval ? 'text-blue-500' : 'text-tertiary')}
>
{interval ? `${interval / 1000}s` : 'Once'}
</span>
</svelte:fragment>
<svelte:fragment slot="items">
{#each items ?? [] as { }, index}
<MenuItem
on:click={() => {
if (index === 0) {
setInter(undefined)
} else {
setInter(index * 5000)
}
}}
>
<div
class={classNames(
'!text-tertiary text-left px-4 py-2 gap-2 cursor-pointer hover:bg-surface-hover !text-xs font-semibold'
)}
>
{#if index === 0}
Once
{:else}
{`Every ${index * 5} seconds`}
{/if}
</div>
</MenuItem>
{/each}
</svelte:fragment>
</ButtonDropdown>
</div>
{#if interval}
<div class="w-full bg-gray-200 rounded-full h-0.5 dark:bg-gray-700">
<div
class="bg-blue-300 h-0.5 rounded-full dark:bg-blue-500 transition-all"
style="width: {$progress}%"
/>
</div>
{/if}
</div>
<RecomputeAllButton
on:click={() => onClick()}
{interval}
{refreshing}
componentNumber={$recomputeAllContext.componentNumber ?? 0}
loading={$recomputeAllContext.loading}
progress={$recomputeAllContext.progress}
on:setInter={(e) => {
setInter(e.detail)
onClick(false)
}}
/>
@@ -1,17 +1,17 @@
<script lang="ts">
import { getContext } from 'svelte'
import type { AppViewerContext } from '../types'
import RecomputeAllComponents from './RecomputeAllComponents.svelte'
import { dfs } from './appUtils'
import { deepEqual } from 'fast-equals'
import { Loader2 } from 'lucide-svelte'
import Popover from '$lib/components/Popover.svelte'
import { twMerge } from 'tailwind-merge'
import RecomputeAllButton from './RecomputeAllButton.svelte'
export let containerClass: string | undefined = undefined
export let containerStyle: string | undefined = undefined
const { selectedComponent, app, connectingInput, allIdsInPath, bgRuns } =
const { selectedComponent, app, connectingInput, allIdsInPath, bgRuns, recomputeAllContext } =
getContext<AppViewerContext>('AppViewerContext')
let previousSelectedIds: string[] | undefined = undefined
@@ -47,7 +47,17 @@
</div>
<div>
{#if !$connectingInput.opened}
<RecomputeAllComponents />
<RecomputeAllButton
interval={$recomputeAllContext.interval}
componentNumber={$recomputeAllContext.componentNumber ?? 0}
on:click={() => $recomputeAllContext.onClick?.()}
on:setInter={(e) => {
$recomputeAllContext.setInter?.(e.detail)
}}
refreshing={$recomputeAllContext.refreshing}
progress={$recomputeAllContext.progress}
loading={$recomputeAllContext.loading}
/>
{/if}
</div>
</div>
+10
View File
@@ -274,6 +274,16 @@ export type AppViewerContext = {
replaceStateFn?: ((url: string) => void) | undefined
gotoFn?: ((url: string, opt?: Record<string, any> | undefined) => void) | undefined
policy: Policy
recomputeAllContext: Writable<{
onClick?: () => void
componentNumber?: number | undefined
interval?: number | undefined
refreshing?: string[] | undefined
setInter?: (interval: number) => void | undefined
progress?: number | undefined
loading?: boolean | undefined
}>
}
export type AppEditorContext = {