phonograph/svelte/src/datum-editor.svelte

147 lines
4.2 KiB
Svelte
Raw Normal View History

2025-08-24 23:24:01 -07:00
<script lang="ts">
2025-10-16 06:40:11 +00:00
import icon_cube_transparent from "../assets/heroicons/20/solid/cube-transparent.svg?raw";
import icon_cube from "../assets/heroicons/20/solid/cube.svg?raw";
import type { Datum } from "./datum.svelte";
import {
datum_from_editor_state,
editor_state_from_datum,
type EditorState,
} from "./editor-state.svelte";
2025-08-24 23:24:01 -07:00
import { type FieldInfo } from "./field.svelte";
type Props = {
2025-10-16 06:40:11 +00:00
/**
* For use cases in which the user may select between multiple datum types,
* such as when embedded in an expression editor, this array represents the
* permissible set of field parameters.
*/
2025-08-24 23:24:01 -07:00
assignable_fields?: ReadonlyArray<FieldInfo>;
2025-10-16 06:40:11 +00:00
2025-08-24 23:24:01 -07:00
field_info: FieldInfo;
2025-10-16 06:40:11 +00:00
on_change?(value?: Datum): void;
value?: Datum;
2025-08-24 23:24:01 -07:00
};
let {
assignable_fields = [],
field_info = $bindable(),
2025-10-16 06:40:11 +00:00
on_change,
value = $bindable(),
2025-08-24 23:24:01 -07:00
}: Props = $props();
2025-10-16 06:40:11 +00:00
let editor_state = $state<EditorState | undefined>();
2025-08-24 23:24:01 -07:00
let type_selector_menu_button_element = $state<
HTMLButtonElement | undefined
>();
let type_selector_popover_element = $state<HTMLDivElement | undefined>();
2025-10-16 06:40:11 +00:00
let text_input_element = $state<HTMLInputElement | undefined>();
$effect(() => {
if (value) {
editor_state = editor_state_from_datum(value);
}
});
export function focus() {
text_input_element?.focus();
}
function handle_input() {
if (!editor_state) {
console.warn("preconditions for handle_input() not met");
return;
}
value = datum_from_editor_state(
editor_state,
field_info.field.presentation,
);
on_change?.(value);
}
2025-08-24 23:24:01 -07:00
function handle_type_selector_menu_button_click() {
type_selector_popover_element?.togglePopover();
}
function handle_type_selector_field_button_click(value: FieldInfo) {
field_info = value;
type_selector_popover_element?.hidePopover();
type_selector_menu_button_element?.focus();
}
</script>
2025-10-16 06:40:11 +00:00
<div
class="datum-editor__container"
class:datum-editor__container--incomplete={!value}
>
{#if editor_state}
{#if assignable_fields?.length > 0}
<div class="datum-editor__type-selector">
<button
bind:this={type_selector_menu_button_element}
class="datum-editor__type-selector-menu-button"
onclick={handle_type_selector_menu_button_click}
type="button"
>
{field_info.field.presentation.t}
</button>
<div
bind:this={type_selector_popover_element}
class="datum-editor__type-selector-popover"
popover="auto"
>
{#each assignable_fields as assignable_field_info}
<button
onclick={() =>
handle_type_selector_field_button_click(assignable_field_info)}
type="button"
>
{assignable_field_info.field.presentation.t}
</button>
{/each}
</div>
2025-08-24 23:24:01 -07:00
</div>
2025-10-16 06:40:11 +00:00
{/if}
<button
type="button"
class="datum-editor__null-control"
class:datum-editor__null-control--disabled={editor_state.text_value !==
""}
disabled={editor_state.text_value !== ""}
onclick={() => {
if (!editor_state) {
console.warn("null control onclick() preconditions not met");
return;
}
editor_state.is_null = !editor_state.is_null;
handle_input();
}}
>
{#if editor_state.is_null}
{@html icon_cube_transparent}
{:else}
{@html icon_cube}
{/if}
</button>
2025-09-08 15:56:57 -07:00
{#if field_info.field.presentation.t === "Text" || field_info.field.presentation.t === "Uuid"}
2025-10-16 06:40:11 +00:00
<input
bind:this={text_input_element}
value={editor_state.text_value}
oninput={({ currentTarget: { value } }) => {
if (editor_state) {
editor_state.text_value = value;
handle_input();
}
}}
class="datum-editor__text-input"
type="text"
/>
2025-09-08 15:56:57 -07:00
{:else if field_info.field.presentation.t === "Timestamp"}
2025-10-16 06:40:11 +00:00
<input value={editor_state.date_value} type="date" />
<input value={editor_state.time_value} type="time" />
2025-08-24 23:24:01 -07:00
{/if}
2025-10-16 06:40:11 +00:00
<div class="datum-editor__helpers"></div>
{/if}
2025-08-24 23:24:01 -07:00
</div>