M9 S5 (frontend): shared BaseModal for the standard modals + BaseInput in Account
- BaseModal.vue (new): the backdrop + dialog-panel shell (dimmed fixed overlay, bordered rounded panel, role=dialog, close on Escape + backdrop mousedown). Caller sizes/pads/shadows the panel via `panelClass`, picks start/center `align`, and sets an `ariaLabel` for header-less panels. - LabelsModal, CommandPalette, and the AppShell keyboard-shortcuts overlay drop their hand-rolled backdrop+panel shells and slot their content into BaseModal (~12 lines of overlay boilerplate each → gone). - NoteEditor deliberately keeps its own shell: its backdrop mousedown is drag-guarded and its Esc/⌘-Enter handling is bespoke (unsaved-edit safety), so folding it in would risk regressing the app's core editing surface (rule 28). - AccountView's one device-name field now uses the shared BaseInput. SettingsView is intentionally NOT converted — its rows are a horizontal label+control pattern (checkbox/number/text, direct value mutation), a different shape than BaseInput's vertical form field. Frontend-only; CI vue-tsc is the type/template gate (no local typecheck, rule 10). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
<script setup lang="ts">
|
||||
// The backdrop + centered dialog panel every modal shares: a fixed dimmed overlay and
|
||||
// a bordered, rounded panel with role="dialog". Emits `close` on Escape (while focus is
|
||||
// inside the panel — matching the app's existing modals) and on a backdrop pointer-down
|
||||
// (mousedown outside the panel). The caller sizes/shadows/pads the panel via `panelClass`
|
||||
// and fills it (its own header + content) through the default slot.
|
||||
//
|
||||
// NoteEditor deliberately does NOT use this — its backdrop mousedown is drag-guarded and
|
||||
// its Escape/⌘-Enter handling is bespoke (unsaved-edit safety), so it keeps its own shell.
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
/** Panel size / shadow / padding classes (structure — rounded/border/bg — is fixed). */
|
||||
panelClass?: string;
|
||||
/** Vertical placement of the panel within the overlay. */
|
||||
align?: "start" | "center";
|
||||
/** aria-label for the panel when it has no visible heading (e.g. the command palette). */
|
||||
ariaLabel?: string;
|
||||
}>(),
|
||||
{ panelClass: "w-full max-w-sm shadow-xl", align: "start" },
|
||||
);
|
||||
const emit = defineEmits<{ (e: "close"): void }>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="fixed inset-0 z-50 flex justify-center overflow-y-auto bg-black/40 p-4"
|
||||
:class="align === 'center' ? 'items-center' : 'items-start pt-[12vh]'"
|
||||
@mousedown.self="emit('close')"
|
||||
>
|
||||
<div
|
||||
class="rounded-xl border border-neutral-200 bg-white dark:border-neutral-700 dark:bg-neutral-900"
|
||||
:class="panelClass"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
:aria-label="ariaLabel"
|
||||
@keydown.esc="emit('close')"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user