feat: add theme support to editor dialogs and context menu
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 1m22s
CI / Lint & Test (pull_request) Successful in 23m1s
CI / Build Linux (pull_request) Successful in 21m11s
CI / Build Windows (cross-compile) (pull_request) Successful in 34m46s

Update ConfirmDialog, InputDialog, and FileContextMenu to use CSS
variables instead of hardcoded Tailwind colors, so they respect the
user's theme selection (dark/light/high-contrast).
This commit is contained in:
2026-01-28 16:54:51 -08:00
committed by Naomi Carrigan
parent 505e24cbd2
commit 38b591a084
3 changed files with 258 additions and 63 deletions
+89 -18
View File
@@ -31,30 +31,101 @@
<svelte:window onkeydown={handleKeydown} />
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/50" onclick={onCancel}>
<div class="dialog-overlay" onclick={onCancel}>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
class="mx-4 w-full max-w-md rounded-lg border border-gray-700 bg-gray-800 p-6 shadow-xl"
onclick={(e) => e.stopPropagation()}
>
<h2 class="mb-2 text-lg font-semibold text-gray-100">{title}</h2>
<p class="mb-6 text-sm text-gray-300">{message}</p>
<div class="dialog-content" onclick={(e) => e.stopPropagation()}>
<h2 class="dialog-title">{title}</h2>
<p class="dialog-message">{message}</p>
<div class="flex justify-end gap-3">
<button
class="rounded-md border border-gray-600 bg-gray-700 px-4 py-2 text-sm text-gray-200 hover:bg-gray-600"
onclick={onCancel}
>
<div class="dialog-actions">
<button class="btn-cancel" onclick={onCancel}>
{cancelText}
</button>
<button
class="rounded-md px-4 py-2 text-sm text-white {destructive
? 'bg-red-600 hover:bg-red-700'
: 'bg-pink-600 hover:bg-pink-700'}"
onclick={onConfirm}
>
<button class="btn-confirm" class:destructive onclick={onConfirm}>
{confirmText}
</button>
</div>
</div>
</div>
<style>
.dialog-overlay {
position: fixed;
inset: 0;
z-index: 50;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0.5);
}
.dialog-content {
margin: 0 1rem;
width: 100%;
max-width: 28rem;
border-radius: 0.5rem;
border: 1px solid var(--border-color);
background-color: var(--bg-secondary);
padding: 1.5rem;
box-shadow:
0 20px 25px -5px rgba(0, 0, 0, 0.1),
0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
.dialog-title {
margin-bottom: 0.5rem;
font-size: 1.125rem;
font-weight: 600;
color: var(--text-primary);
}
.dialog-message {
margin-bottom: 1.5rem;
font-size: 0.875rem;
color: var(--text-secondary);
}
.dialog-actions {
display: flex;
justify-content: flex-end;
gap: 0.75rem;
}
.btn-cancel {
border-radius: 0.375rem;
border: 1px solid var(--border-color);
background-color: var(--bg-primary);
padding: 0.5rem 1rem;
font-size: 0.875rem;
color: var(--text-primary);
cursor: pointer;
transition: background-color 0.15s ease;
}
.btn-cancel:hover {
background-color: var(--bg-secondary);
}
.btn-confirm {
border-radius: 0.375rem;
border: none;
background-color: var(--accent-primary);
padding: 0.5rem 1rem;
font-size: 0.875rem;
color: white;
cursor: pointer;
transition: background-color 0.15s ease;
}
.btn-confirm:hover {
background-color: var(--accent-secondary);
}
.btn-confirm.destructive {
background-color: #dc2626;
}
.btn-confirm.destructive:hover {
background-color: #b91c1c;
}
</style>
@@ -62,7 +62,7 @@
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
class="fixed inset-0 z-50"
class="menu-overlay"
onclick={onClose}
oncontextmenu={(e) => {
e.preventDefault();
@@ -70,16 +70,9 @@
}}
>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
class="absolute z-50 min-w-[160px] rounded-md border border-gray-700 bg-gray-800 py-1 shadow-lg"
style="left: {x}px; top: {y}px;"
onclick={(e) => e.stopPropagation()}
>
<button
class="flex w-full items-center gap-2 px-3 py-1.5 text-left text-sm text-gray-200 hover:bg-gray-700"
onclick={handleNewFile}
>
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<div class="menu-content" style="left: {x}px; top: {y}px;" onclick={(e) => e.stopPropagation()}>
<button class="menu-item" onclick={handleNewFile}>
<svg class="menu-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
@@ -90,11 +83,8 @@
New File
</button>
<button
class="flex w-full items-center gap-2 px-3 py-1.5 text-left text-sm text-gray-200 hover:bg-gray-700"
onclick={handleNewFolder}
>
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<button class="menu-item" onclick={handleNewFolder}>
<svg class="menu-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
@@ -106,13 +96,10 @@
</button>
{#if targetEntry}
<div class="my-1 border-t border-gray-700"></div>
<div class="menu-divider"></div>
<button
class="flex w-full items-center gap-2 px-3 py-1.5 text-left text-sm text-gray-200 hover:bg-gray-700"
onclick={handleRename}
>
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<button class="menu-item" onclick={handleRename}>
<svg class="menu-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
@@ -123,11 +110,8 @@
Rename
</button>
<button
class="flex w-full items-center gap-2 px-3 py-1.5 text-left text-sm text-red-400 hover:bg-gray-700"
onclick={handleDelete}
>
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<button class="menu-item destructive" onclick={handleDelete}>
<svg class="menu-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
@@ -140,3 +124,57 @@
{/if}
</div>
</div>
<style>
.menu-overlay {
position: fixed;
inset: 0;
z-index: 50;
}
.menu-content {
position: absolute;
z-index: 50;
min-width: 160px;
border-radius: 0.375rem;
border: 1px solid var(--border-color);
background-color: var(--bg-secondary);
padding: 0.25rem 0;
box-shadow:
0 10px 15px -3px rgba(0, 0, 0, 0.1),
0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
.menu-item {
display: flex;
width: 100%;
align-items: center;
gap: 0.5rem;
padding: 0.375rem 0.75rem;
text-align: left;
font-size: 0.875rem;
color: var(--text-primary);
background: transparent;
border: none;
cursor: pointer;
transition: background-color 0.15s ease;
}
.menu-item:hover {
background-color: var(--bg-primary);
}
.menu-item.destructive {
color: #ef4444;
}
.menu-divider {
margin: 0.25rem 0;
border-top: 1px solid var(--border-color);
}
.menu-icon {
width: 1rem;
height: 1rem;
}
</style>
+104 -18
View File
@@ -19,7 +19,6 @@
onCancel,
}: Props = $props();
// svelte-ignore state_referenced_locally - intentionally capture initial value once
let inputValue = $state(initialValue);
let inputElement: HTMLInputElement | undefined = $state();
@@ -52,36 +51,123 @@
<svelte:window onkeydown={handleKeydown} />
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/50" onclick={onCancel}>
<div class="dialog-overlay" onclick={onCancel}>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
class="mx-4 w-full max-w-md rounded-lg border border-gray-700 bg-gray-800 p-6 shadow-xl"
onclick={(e) => e.stopPropagation()}
>
<h2 class="mb-4 text-lg font-semibold text-gray-100">{title}</h2>
<div class="dialog-content" onclick={(e) => e.stopPropagation()}>
<h2 class="dialog-title">{title}</h2>
<input
bind:this={inputElement}
bind:value={inputValue}
type="text"
{placeholder}
class="mb-6 w-full rounded-md border border-gray-600 bg-gray-700 px-3 py-2 text-sm text-gray-100 placeholder-gray-400 focus:border-pink-500 focus:outline-none focus:ring-1 focus:ring-pink-500"
class="dialog-input"
/>
<div class="flex justify-end gap-3">
<button
class="rounded-md border border-gray-600 bg-gray-700 px-4 py-2 text-sm text-gray-200 hover:bg-gray-600"
onclick={onCancel}
>
<div class="dialog-actions">
<button class="btn-cancel" onclick={onCancel}>
{cancelText}
</button>
<button
class="rounded-md bg-pink-600 px-4 py-2 text-sm text-white hover:bg-pink-700 disabled:cursor-not-allowed disabled:opacity-50"
onclick={handleSubmit}
disabled={!inputValue.trim()}
>
<button class="btn-confirm" onclick={handleSubmit} disabled={!inputValue.trim()}>
{confirmText}
</button>
</div>
</div>
</div>
<style>
.dialog-overlay {
position: fixed;
inset: 0;
z-index: 50;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0.5);
}
.dialog-content {
margin: 0 1rem;
width: 100%;
max-width: 28rem;
border-radius: 0.5rem;
border: 1px solid var(--border-color);
background-color: var(--bg-secondary);
padding: 1.5rem;
box-shadow:
0 20px 25px -5px rgba(0, 0, 0, 0.1),
0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
.dialog-title {
margin-bottom: 1rem;
font-size: 1.125rem;
font-weight: 600;
color: var(--text-primary);
}
.dialog-input {
width: 100%;
margin-bottom: 1.5rem;
padding: 0.5rem 0.75rem;
border-radius: 0.375rem;
border: 1px solid var(--border-color);
background-color: var(--bg-primary);
font-size: 0.875rem;
color: var(--text-primary);
outline: none;
transition:
border-color 0.15s ease,
box-shadow 0.15s ease;
}
.dialog-input::placeholder {
color: var(--text-secondary);
}
.dialog-input:focus {
border-color: var(--accent-primary);
box-shadow: 0 0 0 1px var(--accent-primary);
}
.dialog-actions {
display: flex;
justify-content: flex-end;
gap: 0.75rem;
}
.btn-cancel {
border-radius: 0.375rem;
border: 1px solid var(--border-color);
background-color: var(--bg-primary);
padding: 0.5rem 1rem;
font-size: 0.875rem;
color: var(--text-primary);
cursor: pointer;
transition: background-color 0.15s ease;
}
.btn-cancel:hover {
background-color: var(--bg-secondary);
}
.btn-confirm {
border-radius: 0.375rem;
border: none;
background-color: var(--accent-primary);
padding: 0.5rem 1rem;
font-size: 0.875rem;
color: white;
cursor: pointer;
transition: background-color 0.15s ease;
}
.btn-confirm:hover {
background-color: var(--accent-secondary);
}
.btn-confirm:disabled {
cursor: not-allowed;
opacity: 0.5;
}
</style>