Remove last SelectLegacy (#6003)
* Replace SelectLegacy in AppPicker * Better DynSelect * remove select legacy * fix unknown values not displayed * undo tsconfig change * better dyn select search * index js remove * use bindable defaults
This commit is contained in:
@@ -1,26 +1,4 @@
|
||||
<script lang="ts">
|
||||
import { SELECT_INPUT_DEFAULT_STYLE } from '$lib/defaults'
|
||||
import type { Script } from '$lib/gen'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import SelectLegacy from './apps/svelte-select/lib/SelectLegacy.svelte'
|
||||
import DarkModeObserver from './DarkModeObserver.svelte'
|
||||
import ResultJobLoader from './ResultJobLoader.svelte'
|
||||
import Tooltip from './Tooltip.svelte'
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
import { stateSnapshot } from '$lib/svelte5Utils.svelte'
|
||||
|
||||
export let value: any = undefined
|
||||
export let helperScript:
|
||||
| { type: 'inline'; path?: string; lang: Script['language']; code: string }
|
||||
| { type: 'hash'; hash: string }
|
||||
| undefined = undefined
|
||||
export let entrypoint: string
|
||||
export let args: Record<string, any> = {}
|
||||
export let name: string
|
||||
|
||||
let darkMode: boolean = false
|
||||
|
||||
let rawCode = JSON.stringify(value, null, 2)
|
||||
<script lang="ts" module>
|
||||
function validSelectObject(x): string | undefined {
|
||||
if (typeof x != 'object') {
|
||||
return JSON.stringify(x) + ' is not an object'
|
||||
@@ -34,115 +12,105 @@
|
||||
}
|
||||
return
|
||||
}
|
||||
async function getItemsFromOptions(text: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import type { Script } from '$lib/gen'
|
||||
import { usePromise } from '$lib/svelte5Utils.svelte'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import ResultJobLoader from './ResultJobLoader.svelte'
|
||||
import Select from './select/Select.svelte'
|
||||
import Tooltip from './Tooltip.svelte'
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
import { untrack } from 'svelte'
|
||||
import { readFieldsRecursively } from '$lib/utils'
|
||||
|
||||
interface Props {
|
||||
value?: any
|
||||
helperScript?:
|
||||
| { type: 'inline'; path?: string; lang: Script['language']; code: string }
|
||||
| { type: 'hash'; hash: string }
|
||||
entrypoint: string
|
||||
args?: Record<string, any>
|
||||
name: string
|
||||
}
|
||||
|
||||
let { value = $bindable(), helperScript, entrypoint, args: _args, name }: Props = $props()
|
||||
|
||||
let args = $state(structuredClone($state.snapshot(_args)))
|
||||
$effect(() => {
|
||||
readFieldsRecursively(_args, { excludeField: [name] })
|
||||
untrack(() => !deepEqual(args, _args) && (args = $state.snapshot(_args)))
|
||||
})
|
||||
|
||||
async function getItemsFromOptions() {
|
||||
return new Promise<{ label: string; value: any }[]>((resolve, reject) => {
|
||||
let cb = {
|
||||
done(res) {
|
||||
if (!res || !Array.isArray(res)) {
|
||||
reject('Result was not an array')
|
||||
return
|
||||
}
|
||||
if (res.length == 0) {
|
||||
resolve([])
|
||||
}
|
||||
|
||||
if (res.length == 0) resolve([])
|
||||
if (res.every((x) => typeof x == 'string')) {
|
||||
res = res.map((x) => ({
|
||||
label: x,
|
||||
value: x
|
||||
}))
|
||||
res = res.map((x) => ({ label: x, value: x }))
|
||||
} else if (res.find((x) => validSelectObject(x) != undefined)) {
|
||||
reject(validSelectObject(res.find((x) => validSelectObject(x) != undefined)))
|
||||
} else {
|
||||
if (text != undefined && text != '') {
|
||||
res = res.filter((x) => x['label'].includes(text))
|
||||
}
|
||||
if (filterText != undefined && filterText != '')
|
||||
res = res.filter((x) => x['label'].includes(filterText))
|
||||
resolve(res)
|
||||
}
|
||||
},
|
||||
cancel() {
|
||||
reject()
|
||||
},
|
||||
error(err) {
|
||||
reject(err)
|
||||
}
|
||||
cancel: () => reject(),
|
||||
error: (err) => reject(err)
|
||||
}
|
||||
helperScript?.type == 'inline'
|
||||
? resultJobLoader?.runPreview(
|
||||
helperScript?.path ?? 'NO_PATH',
|
||||
helperScript.code,
|
||||
helperScript.lang,
|
||||
{ ...args, text, _ENTRYPOINT_OVERRIDE: entrypoint },
|
||||
{ ...args, filterText, _ENTRYPOINT_OVERRIDE: entrypoint },
|
||||
undefined,
|
||||
cb
|
||||
)
|
||||
: resultJobLoader?.runScriptByHash(
|
||||
helperScript?.hash ?? 'NO_HASH',
|
||||
{
|
||||
...args,
|
||||
text,
|
||||
_ENTRYPOINT_OVERRIDE: entrypoint
|
||||
},
|
||||
{ ...args, filterText, _ENTRYPOINT_OVERRIDE: entrypoint },
|
||||
cb
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
let lastArgs = structuredClone({ ...stateSnapshot(args), [name]: undefined })
|
||||
$: (entrypoint || helperScript) && refreshOptions()
|
||||
let _items = usePromise(getItemsFromOptions)
|
||||
let items = $derived(_items.value)
|
||||
$effect(() => {
|
||||
;[args, name, entrypoint, helperScript, filterText]
|
||||
untrack(() => _items.refresh())
|
||||
})
|
||||
|
||||
$: args && changeArgs()
|
||||
|
||||
let timeout: NodeJS.Timeout | undefined = undefined
|
||||
function changeArgs() {
|
||||
timeout && clearTimeout(timeout)
|
||||
timeout = setTimeout(() => {
|
||||
let argsWithoutSelf = { ...stateSnapshot(args), [name]: undefined }
|
||||
if (deepEqual(argsWithoutSelf, lastArgs)) {
|
||||
return
|
||||
}
|
||||
refreshOptions()
|
||||
lastArgs = structuredClone(argsWithoutSelf)
|
||||
timeout = undefined
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
function refreshOptions() {
|
||||
error = undefined
|
||||
renderCount += 1
|
||||
}
|
||||
|
||||
let error: string | undefined = undefined
|
||||
let resultJobLoader: ResultJobLoader
|
||||
let renderCount = 0
|
||||
let resultJobLoader: ResultJobLoader | undefined = $state()
|
||||
let filterText: string = $state('')
|
||||
let open: boolean = $state(false)
|
||||
</script>
|
||||
|
||||
{#if helperScript}
|
||||
<DarkModeObserver bind:darkMode />
|
||||
<ResultJobLoader bind:this={resultJobLoader} />
|
||||
|
||||
<div class="w-full flex-col flex">
|
||||
<div class="w-full">
|
||||
{#key renderCount}
|
||||
<SelectLegacy
|
||||
on:error={(e) => {
|
||||
error = e.detail.details
|
||||
}}
|
||||
on:change={(e) => {
|
||||
value = e.detail.value
|
||||
}}
|
||||
{value}
|
||||
computeOnClick={value == undefined}
|
||||
loadOptions={getItemsFromOptions}
|
||||
inputStyles={SELECT_INPUT_DEFAULT_STYLE.inputStyles}
|
||||
containerStyles={darkMode
|
||||
? SELECT_INPUT_DEFAULT_STYLE.containerStylesDark
|
||||
: SELECT_INPUT_DEFAULT_STYLE.containerStyles}
|
||||
/>
|
||||
{/key}
|
||||
</div>
|
||||
{#if error}
|
||||
<div class="text-red-400 text-2xs">error: <Tooltip>{JSON.stringify(error)}</Tooltip></div>
|
||||
<Select
|
||||
bind:value
|
||||
bind:open
|
||||
{items}
|
||||
bind:filterText
|
||||
loading={!open && _items.status === 'loading'}
|
||||
clearable
|
||||
noItemsMsg={_items.status === 'loading' ? 'Loading...' : 'No items found'}
|
||||
/>
|
||||
{#if _items.error}
|
||||
<div class="text-red-400 text-2xs">
|
||||
error: <Tooltip>{JSON.stringify(_items.error)}</Tooltip>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
@@ -153,7 +121,7 @@
|
||||
{#await import('$lib/components/JsonEditor.svelte')}
|
||||
<Loader2 class="animate-spin" />
|
||||
{:then Module}
|
||||
<Module.default code={rawCode} bind:value />
|
||||
<Module.default code={JSON.stringify(value, null, 2)} bind:value />
|
||||
{/await}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
export { default as default } from './SelectLegacy.svelte'
|
||||
@@ -74,7 +74,7 @@
|
||||
})
|
||||
|
||||
let valueEntry = $derived(
|
||||
value.map((v) => processedItems.find((item) => item.value === v)!).filter(Boolean)
|
||||
value.map((v) => processedItems.find((item) => item.value === v) ?? { value: v, label: v })
|
||||
)
|
||||
|
||||
function onAddValue(item: ProcessedItem<Value>) {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import CloseButton from '../common/CloseButton.svelte'
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
import { untrack } from 'svelte'
|
||||
import { processItems, type ProcessedItem } from './utils.svelte'
|
||||
import { getLabel, processItems, type ProcessedItem } from './utils.svelte'
|
||||
import SelectDropdown from './SelectDropdown.svelte'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
items,
|
||||
placeholder = 'Please select',
|
||||
value = $bindable(),
|
||||
filterText: _filterTextBind = $bindable(undefined),
|
||||
filterText = $bindable(''),
|
||||
class: className = '',
|
||||
clearable = false,
|
||||
listAutoWidth = true,
|
||||
@@ -27,6 +27,7 @@
|
||||
RightIcon,
|
||||
createText,
|
||||
noItemsMsg,
|
||||
open = $bindable(false),
|
||||
groupBy,
|
||||
sortBy,
|
||||
onFocus,
|
||||
@@ -50,6 +51,7 @@
|
||||
RightIcon?: any
|
||||
createText?: string
|
||||
noItemsMsg?: string
|
||||
open?: boolean
|
||||
groupBy?: (item: Item) => string
|
||||
sortBy?: (a: Item, b: Item) => number
|
||||
onFocus?: () => void
|
||||
@@ -60,8 +62,6 @@
|
||||
|
||||
let disabled = $derived(_disabled || loading)
|
||||
|
||||
let filterText = $state<string>('')
|
||||
let open = $state<boolean>(false)
|
||||
let inputEl: HTMLInputElement | undefined = $state()
|
||||
|
||||
let processedItems: ProcessedItem<Value>[] = $derived.by(() => {
|
||||
@@ -69,13 +69,6 @@
|
||||
return untrack(() => processItems(args))
|
||||
})
|
||||
|
||||
$effect(() => {
|
||||
if (_filterTextBind !== undefined) filterText = _filterTextBind
|
||||
})
|
||||
$effect(() => {
|
||||
if (_filterTextBind !== undefined) _filterTextBind = filterText
|
||||
})
|
||||
|
||||
$effect(() => {
|
||||
if (filterText) open = true
|
||||
})
|
||||
@@ -128,12 +121,12 @@
|
||||
{disabled}
|
||||
type="text"
|
||||
bind:value={() => filterText, (v) => (filterText = v)}
|
||||
placeholder={loading ? 'Loading...' : (valueEntry?.label ?? placeholder)}
|
||||
placeholder={loading ? 'Loading...' : (valueEntry?.label ?? getLabel({ value }) ?? placeholder)}
|
||||
style={containerStyle}
|
||||
class={twMerge(
|
||||
'!bg-surface text-ellipsis',
|
||||
open ? '' : 'cursor-pointer',
|
||||
valueEntry && !loading ? '!placeholder-primary' : '',
|
||||
value && !loading ? '!placeholder-primary' : '',
|
||||
(clearable || RightIcon) && !disabled && value ? '!pr-8' : '',
|
||||
inputClass ?? ''
|
||||
)}
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
onSelectValue(item)
|
||||
}}
|
||||
>
|
||||
{item.label}
|
||||
{item.label || '\xa0'}
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
<script lang="ts">
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import SelectLegacy from '../apps/svelte-select/lib/SelectLegacy.svelte'
|
||||
import { SELECT_INPUT_DEFAULT_STYLE } from '$lib/defaults'
|
||||
import DarkModeObserver from '../DarkModeObserver.svelte'
|
||||
import { userStore, workspaceStore } from '$lib/stores'
|
||||
import { AppService, type ListableApp } from '$lib/gen'
|
||||
import { canWrite } from '$lib/utils'
|
||||
import type { AppViewerContext } from '../apps/types'
|
||||
import Alert from '../common/alert/Alert.svelte'
|
||||
import Select from '../select/Select.svelte'
|
||||
|
||||
export let value = ''
|
||||
export let selecteValue = value
|
||||
let darkMode = false
|
||||
|
||||
const { appPath } = getContext<AppViewerContext>('AppViewerContext')
|
||||
@@ -33,38 +31,23 @@
|
||||
|
||||
onMount(() => {
|
||||
loadApps()
|
||||
|
||||
if (selecteValue === '') {
|
||||
selecteValue = $appPath
|
||||
value = $appPath
|
||||
}
|
||||
if (value === '') value = $appPath
|
||||
})
|
||||
</script>
|
||||
|
||||
<DarkModeObserver bind:darkMode />
|
||||
|
||||
<div class="flex flex-col gap-2 w-full">
|
||||
<SelectLegacy
|
||||
class="grow shrink max-w-full"
|
||||
on:change={(e) => {
|
||||
value = e.detail.value
|
||||
}}
|
||||
on:clear={() => {
|
||||
value = ''
|
||||
}}
|
||||
bind:value={selecteValue}
|
||||
items={apps.map((app) => {
|
||||
return {
|
||||
value: app.path,
|
||||
label: app.path === $appPath ? `${app.path} (current app)` : app.path
|
||||
}
|
||||
})}
|
||||
<Select
|
||||
clearable
|
||||
onClear={() => (value = '')}
|
||||
bind:value
|
||||
items={apps.map((app) => ({
|
||||
value: app.path,
|
||||
label: app.path === $appPath ? `${app.path} (current app)` : app.path
|
||||
}))}
|
||||
placeholder="Pick an app"
|
||||
inputStyles={SELECT_INPUT_DEFAULT_STYLE.inputStyles}
|
||||
containerStyles={darkMode
|
||||
? SELECT_INPUT_DEFAULT_STYLE.containerStylesDark
|
||||
: SELECT_INPUT_DEFAULT_STYLE.containerStyles}
|
||||
portal={false}
|
||||
disablePortal
|
||||
/>
|
||||
{#if !appPath}
|
||||
<Alert title="Current app not selectable" size="xs" type="warning" collapsible>
|
||||
|
||||
@@ -1378,7 +1378,11 @@ export type StateStore<T> = {
|
||||
val: T
|
||||
}
|
||||
|
||||
export function readFieldsRecursively(obj: any): void {
|
||||
export type ReadFieldsRecursivelyOptions = {
|
||||
excludeField?: string[]
|
||||
}
|
||||
|
||||
export function readFieldsRecursively(obj: any, options: ReadFieldsRecursivelyOptions = {}): void {
|
||||
if (Array.isArray(obj)) {
|
||||
// <= in case a new object is added. should read as undefined
|
||||
for (let i = 0; i <= obj.length; i++) {
|
||||
@@ -1387,7 +1391,9 @@ export function readFieldsRecursively(obj: any): void {
|
||||
}
|
||||
}
|
||||
} else if (obj !== null && typeof obj === 'object') {
|
||||
Object.keys(obj).forEach((key) => readFieldsRecursively(obj[key]))
|
||||
Object.keys(obj).forEach((key) => {
|
||||
if (!options.excludeField?.includes(key)) readFieldsRecursively(obj[key], options)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user