* data tables settings ui * install runed * zod 4 fixes * use new toJSONSchema * Migrate ducklake catalogs to more generic custom instance databases * fix compilation * Safety conversion for old duckdb ffi * data tables settings * ts client basis * inline run works * datatables work * Revert "datatables work" This reverts commit6e1588d59e. * datatables work (without leaking pg credentials) * println * separate sqlUtils.ts * nit * Separate custom instance db Select and Wizard components * nit * nit wording * add tags to custom instance dbs * error when trying to use ducklake as datatable or opposite * show status in dropdown * data table instance setup works * sqk function for ducklake * factorize logic * fix temp reactivity * Data table assetexplore * Migrate S3 permissions to modal * Revert "Migrate S3 permissions to modal" This reverts commit0631d03cb0. * nit query -> fetch * Custom instance setup new look * run_language_executor separate fn * run_inline param * nit wording * Better typed client * Data tables display as assets in frontend * asset db icon * nit * cleaner errors * nit * Fix sed calls in mac * run_inline_script_preview in python client * basic python datatable client * datatable and datalake parser in python * ducklake client python * nit fix * Fix migration producing NULL instead of {} when no custom databases * merge conflict fail * python ducklake client arg fix * parse or infer sql types in ts client * ts asset parser, detect datatable & ducklake R/W * fix sql repl for other read ops than select * export type SqlTemplateFunction * rename list_custom_instance_pg_databases * typecheck datatable and ducklake name in Typescript * Fix typecheck datatable and ducklake in TS * declare module overriding instead of extending * infer_sql_type in python client * SqlQuery object in python * fix merge conflicts * update const_format * CI fix * factor out to var_identifiers * sqlx prepare * unnecessary security (admin is required) * clearer comment * ee repo ref * nit snake case * claude step 1: detect var declarations * move detect_sql_access_type to common mod * claude step 2: detect when saved vars are queried * Revert "claude step 2: detect when saved vars are queried" This reverts commit1e1f930568. * Revert "claude step 1: detect var declarations" This reverts commitf866f4819d. * remove ducklake/datatable and default * detect data table assigns in var_identifiers * Python parser successfully infers R/W/RW from ducklake / datatable * still register ducklake/datatable if not used as unknown R/W * Go to settings button in Assets Dropdown on not found * nit * sqlx prepare fail * manual fix, somehow sqlx prepare won't do it * fix frontend ci * ee repo ref * ducklake_user doesnt exist in unit tests * nit fix * ui nit * nit * nit missing clone * fork ducklakes and datatables * fix surface hover bug * stupid mistake * better deeply reactive mutable derived * Ducklake picker * Editor bar data tables * DuckDB supports datatables * datatable in duckdb asset parser * duckdb asset parser var_identifiers * Revert "duckdb asset parser var_identifiers" This reverts commit88068b1a77. * sqlx prepare * Box pin in test_workflow_as_code to fix stack overflow * stash * sql asset parser parses most s3 literals * nit * Detect attach + handle returning RW * detect assets used with dot notation * detect implicit access with USE dl; syntax * Add assets as unknown if var was never used * Support default ducklake/datatable main in parser * ignore asset parsing errors in frontend (avoid flow layout shift) * super weird duplication (merge conflict ?) * nits * fix duckdb parser detecting too much as asset when RW ctx is unknown * fix transparent assets btn * missing arg * nit styling * asset parser specific table parsing * fix resource specific table parsing * More concise asset display in flows + better icons * fix assets page filtering out resources with added table * Fix frontend to support specific table assets * Open DB Manager to specific table * Specific table parser in Python and TS + unit tests * Fix UPDATE setting access to None * fix flow edge rendering on top of output picker * python parser fix var override bug * add ts test * fix compilation * sqlx prepare * update parsers version * fix missing schema key onDelete * Grant permission to create schemas in custom instance databases * Update pg query to return empty schemas * Create schema * Select nits * support schemas in sql parser * ts parser handle schema with sql parser result * detect .schema() syntax * detect schema syntax in python * support .schema() in ts and py SDK * open db manager to specific schema * support reassignment in ts parser * nit better unitest * : syntax in ts * datatable:schema syntax in python * fix client py * nit select dropdown darkmode * object | null fetchOne * ts client nits * parse_sql_client_name fn * getImportWmillTsStatement refactor in EditorBar * text to json() in python client * update parser versions * pkg lock
205 lines
5.2 KiB
Svelte
205 lines
5.2 KiB
Svelte
<script
|
|
lang="ts"
|
|
generics="Item extends { label?: string; value: any; subtitle?: string; disabled?: boolean }"
|
|
>
|
|
import { clickOutside } from '$lib/utils'
|
|
import { twMerge } from 'tailwind-merge'
|
|
import CloseButton from '../common/CloseButton.svelte'
|
|
import { Loader2 } from 'lucide-svelte'
|
|
import { untrack, type Snippet } from 'svelte'
|
|
import { getLabel, processItems, type ProcessedItem } from './utils.svelte'
|
|
import SelectDropdown from './SelectDropdown.svelte'
|
|
import { deepEqual } from 'fast-equals'
|
|
import {
|
|
inputBaseClass,
|
|
inputBorderClass,
|
|
inputSizeClasses
|
|
} from '../text_input/TextInput.svelte'
|
|
import { ButtonType } from '../common/button/model'
|
|
|
|
type Value = Item['value']
|
|
|
|
let {
|
|
items,
|
|
placeholder = 'Please select',
|
|
value = $bindable(),
|
|
filterText = $bindable(''),
|
|
class: className = '',
|
|
clearable = false,
|
|
listAutoWidth = true,
|
|
disabled: _disabled = false,
|
|
containerStyle = '',
|
|
inputClass = '',
|
|
disablePortal = false,
|
|
loading = false,
|
|
error = false,
|
|
autofocus,
|
|
RightIcon,
|
|
createText,
|
|
noItemsMsg,
|
|
open = $bindable(false),
|
|
id,
|
|
itemLabelWrapperClasses,
|
|
itemButtonWrapperClasses,
|
|
size = 'md',
|
|
showPlaceholderOnOpen = false,
|
|
transformInputSelectedText,
|
|
groupBy,
|
|
sortBy,
|
|
onFocus,
|
|
onBlur,
|
|
onClear,
|
|
onCreateItem,
|
|
startSnippet,
|
|
endSnippet,
|
|
bottomSnippet
|
|
}: {
|
|
items?: Item[]
|
|
value: Value | undefined
|
|
placeholder?: string
|
|
class?: string
|
|
clearable?: boolean
|
|
filterText?: string
|
|
disabled?: boolean
|
|
listAutoWidth?: boolean
|
|
containerStyle?: string
|
|
inputClass?: string
|
|
disablePortal?: boolean
|
|
loading?: boolean
|
|
error?: boolean
|
|
autofocus?: boolean
|
|
RightIcon?: any
|
|
createText?: string
|
|
noItemsMsg?: string
|
|
open?: boolean
|
|
id?: string
|
|
itemLabelWrapperClasses?: string
|
|
itemButtonWrapperClasses?: string
|
|
size?: 'sm' | 'md' | 'lg'
|
|
showPlaceholderOnOpen?: boolean
|
|
transformInputSelectedText?: (text: string) => string
|
|
groupBy?: (item: Item) => string
|
|
sortBy?: (a: Item, b: Item) => number
|
|
onFocus?: () => void
|
|
onBlur?: () => void
|
|
onClear?: () => void
|
|
onCreateItem?: (value: string) => void
|
|
startSnippet?: Snippet<[{ item: ProcessedItem<Value>; close: () => void }]>
|
|
endSnippet?: Snippet<[{ item: ProcessedItem<Value>; close: () => void }]>
|
|
bottomSnippet?: Snippet<[{ close: () => void }]>
|
|
} = $props()
|
|
|
|
let disabled = $derived(_disabled || (loading && !value))
|
|
let iconSize = $derived(ButtonType.UnifiedIconSizes[size])
|
|
|
|
let inputEl: HTMLInputElement | undefined = $state()
|
|
|
|
let processedItems: ProcessedItem<Value>[] = $derived.by(() => {
|
|
let args = { items, createText, filterText, groupBy, onCreateItem, sortBy }
|
|
return untrack(() => processItems(args))
|
|
})
|
|
|
|
$effect(() => {
|
|
if (filterText) open = true
|
|
})
|
|
$effect(() => {
|
|
if (!open) filterText = ''
|
|
})
|
|
|
|
let valueEntry = $derived(value && processedItems?.find((item) => deepEqual(item.value, value)))
|
|
|
|
function setValue(item: ProcessedItem<Value>) {
|
|
if (item.__is_create && onCreateItem) {
|
|
onCreateItem(item.value)
|
|
} else {
|
|
value = item.value
|
|
}
|
|
filterText = ''
|
|
open = false
|
|
}
|
|
|
|
function clearValue() {
|
|
filterText = ''
|
|
if (onClear) onClear()
|
|
else value = undefined
|
|
}
|
|
|
|
let inputText = $derived.by(() => {
|
|
let text = valueEntry?.label ?? getLabel({ value }) ?? ''
|
|
return transformInputSelectedText?.(text) ?? text
|
|
})
|
|
</script>
|
|
|
|
<div
|
|
class={`relative ${className}`}
|
|
use:clickOutside={{ onClickOutside: () => (open = false) }}
|
|
onpointerdown={() => onFocus?.()}
|
|
onfocus={() => onFocus?.()}
|
|
onblur={() => onBlur?.()}
|
|
>
|
|
{#if loading}
|
|
<div class="absolute z-10 right-2 h-full flex items-center">
|
|
<Loader2 size={iconSize} class="animate-spin" />
|
|
</div>
|
|
{:else if clearable && !disabled && value}
|
|
<div class="absolute z-10 right-2 h-full flex items-center">
|
|
<CloseButton
|
|
class="bg-transparent text-secondary hover:text-primary"
|
|
noBg
|
|
small
|
|
on:close={clearValue}
|
|
/>
|
|
</div>
|
|
{:else if RightIcon}
|
|
<div class="absolute z-10 right-2 h-full flex items-center">
|
|
<RightIcon size={iconSize} class="text-secondary" />
|
|
</div>
|
|
{/if}
|
|
<!-- svelte-ignore a11y_autofocus -->
|
|
<input
|
|
{autofocus}
|
|
{disabled}
|
|
type="text"
|
|
bind:value={() => (open ? filterText : inputText), (v) => open && (filterText = v)}
|
|
placeholder={loading && !value
|
|
? 'Loading...'
|
|
: value && !showPlaceholderOnOpen
|
|
? inputText
|
|
: placeholder}
|
|
style={containerStyle}
|
|
class={twMerge(
|
|
inputBaseClass,
|
|
inputSizeClasses[size],
|
|
ButtonType.UnifiedHeightClasses[size],
|
|
inputBorderClass({ error, forceFocus: open }),
|
|
'w-full',
|
|
open ? '' : 'cursor-pointer',
|
|
// Show value as placeholder when opening the dropdown and the search is empty
|
|
!value ? 'placeholder-hint' : '!placeholder-primary',
|
|
(clearable || RightIcon) && !disabled && value ? 'pr-8' : '',
|
|
inputClass ?? ''
|
|
)}
|
|
autocomplete="off"
|
|
onpointerdown={() => (open = true)}
|
|
bind:this={inputEl}
|
|
{id}
|
|
/>
|
|
<SelectDropdown
|
|
{disablePortal}
|
|
onSelectValue={setValue}
|
|
{open}
|
|
{processedItems}
|
|
{value}
|
|
{disabled}
|
|
{filterText}
|
|
getInputRect={inputEl && (() => inputEl!.getBoundingClientRect())}
|
|
{listAutoWidth}
|
|
{noItemsMsg}
|
|
{itemLabelWrapperClasses}
|
|
{itemButtonWrapperClasses}
|
|
{startSnippet}
|
|
{endSnippet}
|
|
{bottomSnippet}
|
|
/>
|
|
</div>
|