diff --git a/src/lib/elements/element_ae_obj_field_editor_new.svelte b/src/lib/elements/element_ae_obj_field_editor_new.svelte index e9282797..9c8e203d 100644 --- a/src/lib/elements/element_ae_obj_field_editor_new.svelte +++ b/src/lib/elements/element_ae_obj_field_editor_new.svelte @@ -61,6 +61,9 @@ interface Props { edit_label?: string; display_block?: boolean; display_absolute_edit?: boolean; + display_modal?: boolean; + modal_blocking?: boolean; + modal_placement?: 'center' | 'above' | 'below' | 'left' | 'right'; placeholder?: string; class_li?: string; textarea_rows?: number; @@ -92,6 +95,9 @@ let { edit_label = 'Edit Field', display_block = false, display_absolute_edit = false, + display_modal = false, + modal_blocking = true, + modal_placement = 'center' as 'center' | 'above' | 'below' | 'left' | 'right', placeholder = 'Enter value...', class_li = '', textarea_rows = 4, @@ -159,6 +165,9 @@ let patch_status = $state<'idle' | 'processing' | 'success' | 'error'>('idle'); let error_message = $state(''); let draft_value = $state(to_input_value(current_value, field_type)); let input_ref = $state(null); +let dialog_ref = $state(null); +let trigger_ref = $state(null); +let dialog_style = $state(''); // Optimistic display state machine let has_optimistic = $state(false); @@ -190,6 +199,31 @@ $effect(() => { } }); +// Open/close the in modal mode +$effect(() => { + if (!display_modal || !dialog_ref) return; + if (is_editing) { + untrack(() => { + if (!dialog_ref!.open) { + if (modal_blocking) dialog_ref!.showModal(); + else dialog_ref!.show(); + } + }); + } else { + untrack(() => { if (dialog_ref!.open) dialog_ref!.close(); }); + } +}); + +// Non-modal: close when clicking outside the dialog (no backdrop to click) +$effect(() => { + if (!display_modal || modal_blocking || !is_editing) return; + function on_outside(e: PointerEvent) { + if (dialog_ref && !dialog_ref.contains(e.target as Node)) cancel_edit(); + } + document.addEventListener('pointerdown', on_outside); + return () => document.removeEventListener('pointerdown', on_outside); +}); + async function handle_patch() { if (log_lvl) console.log(`AE Field Editor (new): Patching ${object_type}.${field_name}...`); @@ -230,6 +264,8 @@ async function handle_patch() { } function cancel_edit() { + const value_changed = draft_value !== to_input_value(current_value, field_type); + if (value_changed && patch_status !== 'success' && !confirm('Discard unsaved changes?')) return; has_optimistic = false; draft_value = to_input_value(current_value, field_type); is_editing = false; @@ -237,11 +273,36 @@ function cancel_edit() { error_message = ''; } +function calc_dialog_pos() { + if (!trigger_ref) return; + const rect = trigger_ref.getBoundingClientRect(); + const gap = 8; + const cx = rect.left + rect.width / 2; + const cy = rect.top + rect.height / 2; + + let top: number, left: number, tx: string, ty: string; + switch (modal_placement) { + case 'above': + top = rect.top - gap; left = cx; tx = '-50%'; ty = '-100%'; break; + case 'below': + top = rect.bottom + gap; left = cx; tx = '-50%'; ty = '0'; break; + case 'left': + top = cy; left = rect.left - gap; tx = '-100%'; ty = '-50%'; break; + case 'right': + top = cy; left = rect.right + gap; tx = '0'; ty = '-50%'; break; + default: // center + top = cy; left = cx; tx = '-50%'; ty = '-50%'; break; + } + + dialog_style = `margin:0;top:${top}px;left:${left}px;transform:translate(${tx},${ty});`; +} + function toggle_edit() { if (is_editing) cancel_edit(); else { has_optimistic = false; draft_value = to_input_value(current_value, field_type); + if (display_modal) calc_dialog_pos(); is_editing = true; if (on_open) on_open(); } @@ -252,15 +313,160 @@ function handle_keydown(e: KeyboardEvent) { } +{#snippet edit_panel()} +
+ + {edit_label || field_name} + +
+ +
+
+ +
+ {#if field_type === 'textarea'} + + {:else if field_type === 'select'} +
+ + {#if Object.keys(select_options).length === 0} +
+ +
+ {/if} +
+ {:else if field_type === 'checkbox'} + + {:else if field_type === 'tiptap'} + + {:else if field_type === 'codemirror'} + + {:else if field_type === 'date'} + + {:else if field_type === 'datetime'} + + {:else} + e.key === 'Enter' && handle_patch()} /> + {/if} +
+ +
+
+ {#if patch_status === 'processing'} + + Saving... + + {:else if patch_status === 'success'} + + Saved + + {:else if patch_status === 'error'} +
+ + Error + + {error_message} +
+ {/if} +
+ +
+ {#if allow_null && draft_value !== null} + + {/if} + +
+
+{/snippet} +
- - -
+ + +
{#if children} {@render children()} @@ -281,6 +487,7 @@ function handle_keydown(e: KeyboardEvent) {
- - {#if is_editing} + + {#if is_editing && !display_modal}
- -
- - {edit_label || field_name} - -
- -
-
- -
- {#if field_type === 'textarea'} - - {:else if field_type === 'select'} -
- - {#if Object.keys(select_options).length === 0} -
- -
- {/if} -
- {:else if field_type === 'checkbox'} - - {:else if field_type === 'tiptap'} - - {:else if field_type === 'codemirror'} - - {:else if field_type === 'date'} - - {:else if field_type === 'datetime'} - - {:else} - e.key === 'Enter' && handle_patch()} /> - {/if} -
- - -
-
- {#if patch_status === 'processing'} - - Saving... - - {:else if patch_status === 'success'} - - Saved - - {:else if patch_status === 'error'} -
- - Error - - {error_message} -
- {/if} -
- -
- {#if allow_null && draft_value !== null} - - {/if} - -
-
+ {@render edit_panel()}
{/if}
+ +{#if display_modal} + { e.preventDefault(); cancel_edit(); }} + onclick={(e) => { if (e.target === dialog_ref) cancel_edit(); }} + onkeydown={(e) => { if (e.key === 'Escape') cancel_edit(); }}> + {@render edit_panel()} + +{/if} + diff --git a/src/routes/core/data_stores/+page.svelte b/src/routes/core/data_stores/+page.svelte index 20f2fb1a..f500cc8d 100644 --- a/src/routes/core/data_stores/+page.svelte +++ b/src/routes/core/data_stores/+page.svelte @@ -238,10 +238,10 @@ function content_preview(ds: ae_DataStore): string { } -
+
-
+
@@ -535,14 +535,17 @@ function content_preview(ds: ae_DataStore): string { {#each results as ds (ds.id ?? ds.data_store_id)} {@const ds_id = ds.id ?? ds.data_store_id} - + do_search(false)}> {ds.code} {#if !ds.enable}off{/if} @@ -554,8 +557,10 @@ function content_preview(ds: ae_DataStore): string { object_type="data_store" object_id={ds_id} field_name="name" + edit_label="Name" current_value={ds.name ?? ''} placeholder="Display name" + display_modal={true} on_success={() => do_search(false)} /> @@ -563,9 +568,11 @@ function content_preview(ds: ae_DataStore): string { object_type="data_store" object_id={ds_id} field_name="type" + edit_label="Type" current_value={ds.type ?? 'text'} field_type="select" select_options={ds_type_options} + display_modal={true} on_success={() => do_search(false)}> {ds.type ?? '?'}