feat(frontend): app static and user resource picker default values (#6179)

This commit is contained in:
HugoCasa
2025-07-15 09:52:54 +00:00
committed by GitHub
parent ee528b4f68
commit eb1b4466bc
4 changed files with 84 additions and 63 deletions
@@ -1,40 +1,37 @@
<script lang="ts">
import { ResourceService } from '$lib/gen'
import { workspaceStore } from '$lib/stores'
import { createEventDispatcher, getContext } from 'svelte'
import { getContext, untrack } from 'svelte'
import DarkModeObserver from './DarkModeObserver.svelte'
import { Button, Drawer, DrawerContent } from './common'
import { Plus, Loader2, Link2Off } from 'lucide-svelte'
import type { AppViewerContext } from './apps/types'
import { sendUserToast } from '$lib/toast'
import { createDispatcherIfMounted } from '$lib/createDispatcherIfMounted'
import Select from './select/Select.svelte'
const dispatch = createEventDispatcher()
const dispatchIfMounted = createDispatcherIfMounted(dispatch)
interface Props {
value: string | undefined
resourceType?: string | undefined
disablePortal?: boolean
expressOAuthSetup?: boolean
disabled?: boolean
}
export let initialValue: string | undefined = undefined
export let value: string | undefined = initialValue
export let resourceType: string | undefined = undefined
export let disablePortal = false
export let expressOAuthSetup = false
export let disabled = false
let {
value = $bindable(),
resourceType = undefined,
disablePortal = false,
expressOAuthSetup = false,
disabled = false
}: Props = $props()
let open = false
let refreshCount = 0
let open = $state(false)
let refreshCount = $state(0)
const appViewerContext = getContext<AppViewerContext>('AppViewerContext')
let valueSelect =
initialValue || value
? {
value: value ?? initialValue,
label: value ?? initialValue
}
: undefined
let collection = $state(value ? [{ value, label: value }] : [])
let collection = valueSelect ? [valueSelect] : []
let loading = true
let loading = $state(true)
async function loadResources(resourceType: string | undefined) {
loading = true
try {
@@ -49,26 +46,25 @@
}))
// TODO check if this is needed
if (!nc.find((x) => x.value == value) && (initialValue || value)) {
nc.push({ value: value ?? initialValue!, label: value ?? initialValue! })
if (!nc.find((x) => x.value == value) && value) {
nc.push({ value: value, label: value })
}
collection = nc
if (expressOAuthSetup && nc.length > 0) {
value = nc[0].value
valueSelect = nc[0]
}
} finally {
loading = false
}
}
$: $workspaceStore && loadResources(resourceType)
$effect(() => {
$workspaceStore && resourceType && untrack(() => loadResources(resourceType))
})
$: dispatchIfMounted('change', value)
let darkMode: boolean = $state(false)
let darkMode: boolean = false
let drawer: Drawer | undefined = undefined
let drawer: Drawer | undefined = $state(undefined)
export function askNewResource() {
refreshCount += 1
@@ -93,7 +89,6 @@
on:refresh={async (e) => {
await loadResources(resourceType)
value = e.detail
valueSelect = { value, label: value }
open = false
}}
/>
@@ -121,7 +116,6 @@
on:refresh={async (e) => {
await loadResources(resourceType)
value = e.detail
valueSelect = { value, label: value }
drawer?.closeDrawer?.()
open = false
}}
@@ -157,7 +151,6 @@
const item = collection[0]
if (item) {
value = item.value
valueSelect = item
}
}}>Use existing</Button
>
@@ -167,15 +160,13 @@
{disabled}
{disablePortal}
bind:value={
() => valueSelect?.value,
() => value,
(v) => {
value = v
valueSelect = collection.find((x) => x.value === v)
}
}
onClear={() => {
value = undefined
valueSelect = undefined
}}
clearable
items={collection}
@@ -184,7 +175,7 @@
/>
{/if}
<div class="flex gap-1 items-center">
{#if valueSelect && expressOAuthSetup}
{#if value && expressOAuthSetup}
<Button
{disabled}
color="light"
@@ -193,15 +184,13 @@
btnClasses="w-8 px-0.5 py-1.5"
iconOnly
on:click={async () => {
if (valueSelect && valueSelect.label) {
value = undefined
if (value) {
await ResourceService.deleteResource({
workspace: appViewerContext?.workspace ?? $workspaceStore,
path: valueSelect.label
path: value
})
await loadResources(resourceType)
value = undefined
valueSelect = undefined
await loadResources(resourceType)
}
}}
startIcon={{ icon: Link2Off }}
@@ -1,5 +1,5 @@
<script lang="ts">
import { getContext, onDestroy } from 'svelte'
import { getContext, onDestroy, untrack } from 'svelte'
import { initConfig, initOutput } from '../../editor/appUtils'
import type {
AppViewerContext,
@@ -33,7 +33,8 @@
render
}: Props = $props()
const { app, worldStore, componentControl } = getContext<AppViewerContext>('AppViewerContext')
const { app, worldStore, componentControl, isEditor, mode } =
getContext<AppViewerContext>('AppViewerContext')
let resolvedConfig = $state(
initConfig(components['userresourcecomponent'].initialData.configuration, configuration)
@@ -57,9 +58,34 @@
)
)
let value: string | undefined = $state(outputs.result.peak()?.replace('$res:', ''))
function getDefaultValue(): string | undefined {
if (resolvedConfig.defaultValue && typeof resolvedConfig.defaultValue === 'string') {
const nval = resolvedConfig.defaultValue as string
return nval.replace('$res:', '')
}
return undefined
}
value && assignValue(outputs.result.peak())
let value: string | undefined = $state(
outputs.result.peak()?.replace('$res:', '') ?? getDefaultValue()
)
$effect(() => {
value
untrack(() => assignValue(value))
})
let lastDefaultValue = $state(getDefaultValue())
$effect(() => {
// when in dnd mode, we react to the default value being changed for better UX
if (isEditor && $mode === 'dnd') {
const currentDefaultValue = getDefaultValue() // reactive
if (lastDefaultValue !== currentDefaultValue) {
value = currentDefaultValue
lastDefaultValue = currentDefaultValue
}
}
})
onDestroy(() => {
listInputs?.remove(id)
@@ -74,7 +100,7 @@
}
}
function assignValue(value: string) {
function assignValue(value: string | undefined) {
let nval
if (!value || value === '') {
nval = undefined
@@ -119,10 +145,7 @@
<LightweightResourcePicker
expressOAuthSetup={resolvedConfig.expressOauthSetup}
bind:this={resourcePicker}
{value}
on:change={(e) => {
assignValue(e.detail)
}}
bind:value
disabled={resolvedConfig.disabled}
resourceType={resolvedConfig.resourceType}
/>
@@ -1648,8 +1648,8 @@ export const components = {
backgroundColor: ['#FF6384', '#4BC0C0', '#FFCE56']
}
]
},
},
}
}
}
},
chartjscomponentv2: {
@@ -1687,9 +1687,8 @@ export const components = {
backgroundColor: ['#FF6384', '#4BC0C0', '#FFCE56']
}
]
},
},
}
}
}
},
barchartcomponent: {
@@ -1859,8 +1858,7 @@ This is a paragraph.
width: 2.5
}
}
},
}
},
configuration: {
layout: {
@@ -1893,7 +1891,7 @@ This is a paragraph.
width: 2.5
}
}
},
}
},
configuration: {
layout: {
@@ -1904,8 +1902,7 @@ This is a paragraph.
'Layout options for the plot. See https://plotly.com/javascript/reference/layout/ for more information'
}
}
},
}
},
timeseriescomponent: {
name: 'Timeseries',
@@ -2431,6 +2428,12 @@ This is a paragraph.
allowTypeChange: false,
value: []
} as StaticAppInput,
defaultValue: {
type: 'static',
fieldType: 'text',
value: undefined,
tooltip: 'Format: $res:path/to/resource'
},
placeholder: {
type: 'static',
fieldType: 'text',
@@ -2468,6 +2471,12 @@ This is a paragraph.
fieldType: 'text',
value: 'postgresql'
},
defaultValue: {
type: 'static',
fieldType: 'text',
value: undefined,
tooltip: 'Format: $res:path/to/resource'
},
expressOauthSetup: {
type: 'static',
fieldType: 'boolean',
@@ -156,8 +156,8 @@
<div class="h-screen">
{#key value}
<AppEditor
on:savedNewAppPath={(event) => {
goto(`/apps/edit/${event.detail}`)
onSavedNewAppPath={(path) => {
goto(`/apps/edit/${path}`)
}}
{summary}
app={value}