phonograph/svelte/src/expression-editor.webc.svelte
2025-12-19 20:19:35 +00:00

105 lines
3.1 KiB
Svelte

<svelte:options
customElement={{
props: {
identifier_hints: { attribute: "identifier-hints", type: "Array" },
value: { reflect: true, type: "Object" },
},
shadow: "none",
tag: "expression-editor",
}}
/>
<script lang="ts">
import DatumEditor from "./datum-editor.svelte";
import ExpressionSelector from "./expression-selector.svelte";
import { type PgExpressionAny } from "./expression.svelte";
import ExpressionEditor from "./expression-editor.webc.svelte";
import { type FieldInfo } from "./field.svelte";
import { RFC_3339_S, type Presentation } from "./presentation.svelte";
import type { Datum } from "./datum.svelte";
const ASSIGNABLE_PRESENTATIONS: Presentation[] = [
{ t: "Text", c: { input_mode: { t: "MultiLine", c: {} } } },
{ t: "Timestamp", c: { format: RFC_3339_S } },
{ t: "Uuid", c: {} },
];
const ASSIGNABLE_FIELDS: FieldInfo[] = ASSIGNABLE_PRESENTATIONS.map(
(presentation) => ({
field: {
id: "",
name: "",
ordinality: -1,
presentation,
table_label: "",
table_width_px: -1,
},
not_null: true,
has_default: false,
}),
);
type Props = {
identifier_hints?: string[];
value?: PgExpressionAny;
};
let { identifier_hints = [], value = $bindable() }: Props = $props();
// Dynamic state to bind to datum editor.
let editor_value = $state<Datum | undefined>();
let editor_field_info = $state<FieldInfo>(ASSIGNABLE_FIELDS[0]);
$effect(() => {
editor_value = value?.t === "Literal" ? value.c : undefined;
});
function handle_identifier_selector_change(
ev: Event & { currentTarget: HTMLSelectElement },
) {
if (value?.t === "Identifier") {
value.c.parts_raw = [ev.currentTarget.value];
}
}
function handle_editor_change(datum_value: Datum) {
if (value?.t === "Literal") {
value.c = datum_value;
}
}
</script>
<div class="expression-editor__container">
<div class="expression-editor__sidebar">
<ExpressionSelector bind:value />
</div>
{#if value !== undefined}
<div class="expression-editor__main">
<div class="expression-editor__params">
{#if value.t === "Comparison"}
{#if value.c.t === "Infix"}
<ExpressionEditor bind:value={value.c.c.lhs} {identifier_hints} />
<ExpressionEditor bind:value={value.c.c.rhs} {identifier_hints} />
{:else if value.c.t === "IsNull" || value.c.t === "IsNotNull"}
<ExpressionEditor bind:value={value.c.c.lhs} {identifier_hints} />
{/if}
{:else if value.t === "Identifier"}
<select
onchange={handle_identifier_selector_change}
value={value.c.parts_raw[0]}
>
{#each identifier_hints as hint}
<option value={hint}>{hint}</option>
{/each}
</select>
{:else if value.t === "Literal"}
<DatumEditor
bind:field_info={editor_field_info}
bind:value={editor_value}
assignable_fields={ASSIGNABLE_FIELDS}
on_change={handle_editor_change}
/>
{/if}
</div>
</div>
{/if}
</div>