fix(schema-diff): keep maximized dialog fullscreen on legacy webviews

This commit is contained in:
知食分子
2026-09-29 17:29:43 +08:00
committed by GitHub
parent ebc9fcc098
commit a5a4d16992
4 changed files with 92 additions and 1 deletions
@@ -1283,7 +1283,7 @@ const targetConnectionInfo = computed(() => {
<template>
<Dialog v-model:open="open">
<DialogContent :class="['flex flex-col overflow-hidden', isMaximized ? 'min-w-0' : 'min-w-[800px] resize']" :portal-class="isMaximized ? 'p-0' : undefined" :style="dialogStyle" @interact-outside.prevent @escape-key-down="handleDialogEscape">
<DialogContent :class="['flex flex-col overflow-hidden dbx-schema-diff-dialog', isMaximized ? 'dbx-dialog-maximized min-w-0' : 'min-w-[800px] resize']" :portal-class="isMaximized ? 'p-0' : undefined" :style="dialogStyle" @interact-outside.prevent @escape-key-down="handleDialogEscape">
<Button variant="ghost" size="icon-sm" class="absolute top-2 right-10 z-10" @click="toggleMaximize">
<Maximize2 v-if="!isMaximized" class="w-4 h-4" />
<Minimize2 v-else class="w-4 h-4" />
@@ -0,0 +1,43 @@
// Source-contract guard for shared button press feedback.
// Overview: desktop click responsiveness depends on buttons giving immediate
// visual feedback while pressed. DOM `click` only fires on pointer-up, so a
// press with zero visual delta reads as "the click did nothing" and users
// retry or hold the button (~1s holds measured in click-probe sessions).
//
// Two guarantees keep that regression from re-appearing:
// 1. Every button variant styles an `active:` pressed state, so pressing
// always changes something on screen before pointer-up.
// 2. The pressed state must not move or scale the button (`scale-*` /
// `translate-*`). A moving hit target can land pointer-up outside the
// element it was pressed on, which genuinely drops the click.
// Keep these assertions updated alongside the variant class strings.
import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
const buttonVariantsSource = readFileSync(new URL("../index.ts", import.meta.url), "utf8");
const VARIANTS = ["default", "outline", "secondary", "ghost", "destructive", "link"] as const;
function variantClasses(variant: string): string {
const match = buttonVariantsSource.match(new RegExp(`${variant}:\\s*"([^"]+)"`));
expect(match, `variant "${variant}" exists in buttonVariants`).toBeTruthy();
return match![1];
}
describe("button press feedback guard", () => {
it.each(VARIANTS)("styles an active pressed state for the %s variant", (variant) => {
expect(variantClasses(variant)).toMatch(/(^|\s)active:/);
});
it.each(VARIANTS)("does not move the hit target while %s is pressed", (variant) => {
expect(variantClasses(variant)).not.toMatch(/active:(scale|translate|-translate)-/);
});
it("does not move the hit target from the shared base classes", () => {
const base = buttonVariantsSource.match(/cva\(\s*"([^"]+)"/);
expect(base).toBeTruthy();
expect(base![1]).not.toMatch(/active:(scale|translate|-translate)-/);
});
});
@@ -34,6 +34,7 @@ const mainSource = readFileSync(new URL("../../main.ts", import.meta.url), "utf8
const tabPresentationSource = readFileSync(new URL("../../lib/tabs/tabPresentation.ts", import.meta.url), "utf8");
const editorGroupTabBarSource = readFileSync(new URL("../../components/layout/EditorGroupTabBar.vue", import.meta.url), "utf8");
const appTabBarCssSource = readFileSync(new URL("../../components/layout/appTabBar.css", import.meta.url), "utf8");
const schemaDiffDialogSource = readFileSync(new URL("../../components/diff/SchemaDiffDialog.vue", import.meta.url), "utf8");
describe("legacy WebView CSS fallbacks", () => {
it("keeps globals.css balanced and free of min-width media wrappers", () => {
@@ -547,6 +548,34 @@ describe("legacy WebView CSS fallbacks", () => {
expect(globalsCss).toContain('html.dbx-legacy-webview input[type="number"]:not([class*="appearance-none"]):disabled::-webkit-inner-spin-button');
});
it("keeps the schema diff dialog inline sizing working in legacy WebViews", () => {
// The shared DialogContent base carries max-w-sm, whose legacy !important cap
// outranks the dialog's inline width/max-width styles: the normal state then
// renders at its min-w floor and the maximize toggle collapses to 24rem wide.
// The rescue rules must sit after the max-w-* caps to win by source order.
const fallbackStart = globalsCss.indexOf("html.dbx-legacy-webview .sm\\:block");
const splitpanesStart = globalsCss.indexOf("/* Splitpanes */");
const fallback = globalsCss.slice(fallbackStart, splitpanesStart);
const maxWsmCap = fallback.indexOf('html.dbx-legacy-webview [data-slot="dialog-content"][class~="max-w-sm"]');
const normalRule = fallback.indexOf('html.dbx-legacy-webview [data-slot="dialog-content"].dbx-schema-diff-dialog {');
const maximizedRule = fallback.indexOf('html.dbx-legacy-webview [data-slot="dialog-content"].dbx-schema-diff-dialog.dbx-dialog-maximized {');
expect(fallbackStart).toBeGreaterThan(-1);
expect(splitpanesStart).toBeGreaterThan(fallbackStart);
expect(maxWsmCap).toBeGreaterThan(-1);
expect(normalRule).toBeGreaterThan(maxWsmCap);
expect(maximizedRule).toBeGreaterThan(normalRule);
expect(fallback.slice(normalRule, maximizedRule)).toContain("max-width: calc(100vw - 2rem) !important;");
expect(fallback.slice(normalRule, maximizedRule)).toContain("max-height: calc(var(--dbx-viewport-height) - 2rem) !important;");
expect(fallback.slice(maximizedRule)).toContain("width: calc(100vw - 2rem) !important;");
expect(fallback.slice(maximizedRule)).toContain("height: calc(var(--dbx-viewport-height) - 2rem) !important;");
expect(fallback.slice(maximizedRule)).toContain("max-width: none !important;");
expect(fallback.slice(maximizedRule)).toContain("max-height: none !important;");
expect(fallback.slice(maximizedRule)).toContain("border-radius: 0 !important;");
expect(schemaDiffDialogSource).toContain("'flex flex-col overflow-hidden dbx-schema-diff-dialog'");
expect(schemaDiffDialogSource).toContain("isMaximized ? 'dbx-dialog-maximized min-w-0' : 'min-w-[800px] resize'");
});
it("keeps settings field stacks spaced in legacy WebViews", () => {
const fallbackStart = editorSettingsDialogSource.indexOf("html.dbx-legacy-webview .settings-layout");
const fallbackEnd = editorSettingsDialogSource.indexOf("@media (max-width: 760px)", fallbackStart);
+19
View File
@@ -1914,6 +1914,25 @@ html.dbx-legacy-webview [data-slot="dialog-content"][class*="max-w-[min(1080px"]
html.dbx-legacy-webview [data-slot="dialog-content"][class*="max-w-[1800px]"] {
max-width: 1800px !important;
}
/* The schema diff dialog sizes itself through inline width/height styles, which
* the max-w-sm !important cap above outranks in legacy WebViews — the dialog then
* renders at its min-w floor and the maximize toggle collapses to 24rem wide.
* Pin both states explicitly; these rules must stay after the max-w-* caps
* because they share specificity and win by source order. */
html.dbx-legacy-webview [data-slot="dialog-content"].dbx-schema-diff-dialog {
max-width: calc(100vw - 2rem) !important;
max-height: calc(var(--dbx-viewport-height) - 2rem) !important;
}
html.dbx-legacy-webview [data-slot="dialog-content"].dbx-schema-diff-dialog.dbx-dialog-maximized {
width: calc(100vw - 2rem) !important;
height: calc(var(--dbx-viewport-height) - 2rem) !important;
max-width: none !important;
max-height: none !important;
border-radius: 0 !important;
}
html.dbx-legacy-webview [data-slot="dialog-content"].dbx-form-dialog {
display: flex !important;
flex-direction: column !important;