From a1f7250e909e5713be7895f15fb9a073aefcf6dd Mon Sep 17 00:00:00 2001 From: shuaiplus <2327005759@qq.com> Date: Sat, 25 Apr 2026 03:19:06 +0800 Subject: [PATCH] feat: update mobile layout query to 1180px and enhance icon loading experience --- webapp/src/App.tsx | 2 +- webapp/src/components/SendsPage.tsx | 2 +- webapp/src/components/TotpCodesPage.tsx | 30 ++++++++++------- .../components/vault/vault-page-helpers.tsx | 32 ++++++++++++------- webapp/src/styles/responsive.css | 10 ++++-- webapp/src/styles/tokens.css | 2 +- webapp/src/styles/vault.css | 32 +++++++++++++++++-- 7 files changed, 78 insertions(+), 32 deletions(-) diff --git a/webapp/src/App.tsx b/webapp/src/App.tsx index 905fa08..f20f06e 100644 --- a/webapp/src/App.tsx +++ b/webapp/src/App.tsx @@ -217,7 +217,7 @@ export default function App() { useEffect(() => { if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return; - const media = window.matchMedia('(max-width: 900px)'); + const media = window.matchMedia('(max-width: 1180px)'); const sync = () => setMobileLayout(media.matches); sync(); if (typeof media.addEventListener === 'function') { diff --git a/webapp/src/components/SendsPage.tsx b/webapp/src/components/SendsPage.tsx index d76199b..08c1885 100644 --- a/webapp/src/components/SendsPage.tsx +++ b/webapp/src/components/SendsPage.tsx @@ -20,7 +20,7 @@ interface SendsPageProps { type SendTypeFilter = 'all' | 'text' | 'file'; const AUTO_COPY_KEY = 'nodewarden.send.auto_copy_link.v1'; -const MOBILE_LAYOUT_QUERY = '(max-width: 900px)'; +const MOBILE_LAYOUT_QUERY = '(max-width: 1180px)'; function daysFromNow(iso: string | null | undefined, fallback: number): string { if (!iso) return String(fallback); diff --git a/webapp/src/components/TotpCodesPage.tsx b/webapp/src/components/TotpCodesPage.tsx index 64844c3..603b9be 100644 --- a/webapp/src/components/TotpCodesPage.tsx +++ b/webapp/src/components/TotpCodesPage.tsx @@ -73,23 +73,31 @@ function TotpListIcon({ cipher }: { cipher: Cipher }) { const uri = firstCipherUri(cipher); const host = hostFromUri(uri); const [errored, setErrored] = useState(() => (host ? failedIconHosts.has(host) : false)); + const [loaded, setLoaded] = useState(false); useEffect(() => { setErrored(host ? failedIconHosts.has(host) : false); + setLoaded(false); }, [host]); if (host && !errored) { return ( - { - failedIconHosts.add(host); - setErrored(true); - }} - /> + + + + + setLoaded(true)} + onError={() => { + failedIconHosts.add(host); + setErrored(true); + }} + /> + ); } return ( diff --git a/webapp/src/components/vault/vault-page-helpers.tsx b/webapp/src/components/vault/vault-page-helpers.tsx index b9cda6d..f4e3e62 100644 --- a/webapp/src/components/vault/vault-page-helpers.tsx +++ b/webapp/src/components/vault/vault-page-helpers.tsx @@ -36,7 +36,7 @@ export const CREATE_TYPE_OPTIONS: TypeOption[] = [ ]; export const VAULT_SORT_STORAGE_KEY = 'nodewarden.vault.sort.v1'; -export const MOBILE_LAYOUT_QUERY = '(max-width: 900px)'; +export const MOBILE_LAYOUT_QUERY = '(max-width: 1180px)'; export const VAULT_LIST_ROW_HEIGHT = 74; export const VAULT_LIST_OVERSCAN = 10; export const VAULT_SORT_OPTIONS: Array<{ value: VaultSortMode; label: string }> = [ @@ -433,23 +433,31 @@ export function VaultListIcon({ cipher }: { cipher: Cipher }) { const uri = firstCipherUri(cipher); const host = hostFromUri(uri); const [errored, setErrored] = useState(() => (host ? failedIconHosts.has(host) : false)); + const [loaded, setLoaded] = useState(false); useEffect(() => { setErrored(host ? failedIconHosts.has(host) : false); + setLoaded(false); }, [host]); if (host && !errored) { return ( - { - failedIconHosts.add(host); - setErrored(true); - }} - /> + + + + + setLoaded(true)} + onError={() => { + failedIconHosts.add(host); + setErrored(true); + }} + /> + ); } return ( diff --git a/webapp/src/styles/responsive.css b/webapp/src/styles/responsive.css index cd62a5c..0b425b7 100644 --- a/webapp/src/styles/responsive.css +++ b/webapp/src/styles/responsive.css @@ -59,7 +59,7 @@ } } -@media (max-width: 900px) { +@media (max-width: 1180px) { .auth-page { @apply items-start p-3.5; } @@ -114,6 +114,10 @@ @apply h-[34px] w-[34px]; } + .brand-wordmark { + @apply hidden; + } + .mobile-page-title { @apply inline; } @@ -410,7 +414,7 @@ .detail-actions { flex-direction: column; align-items: stretch; - gap: 10px; + gap: 5px; } .detail-actions .actions { @@ -624,7 +628,7 @@ } } -@media (max-width: 900px) { +@media (max-width: 1180px) { .backup-grid { grid-template-columns: 1fr; } diff --git a/webapp/src/styles/tokens.css b/webapp/src/styles/tokens.css index 5d30614..3be4098 100644 --- a/webapp/src/styles/tokens.css +++ b/webapp/src/styles/tokens.css @@ -29,7 +29,7 @@ --dur-fast: 180ms; --dur-medium: 240ms; --dur-panel: 280ms; - --actions-gap: clamp(0px, calc((100vw - 520px) * 1), 10px); + --actions-gap: clamp(5px, calc((100vw - 520px) * 1), 10px); } :root[data-theme='dark'] { diff --git a/webapp/src/styles/vault.css b/webapp/src/styles/vault.css index d839e87..29539f6 100644 --- a/webapp/src/styles/vault.css +++ b/webapp/src/styles/vault.css @@ -1,6 +1,6 @@ .vault-grid { @apply grid h-full min-h-0 gap-3 p-0.5; - grid-template-columns: 240px minmax(420px, 46%) minmax(575px, 1fr); + grid-template-columns: 270px minmax(400px, 36%) minmax(400px, 1fr); } .sidebar, @@ -355,12 +355,38 @@ .list-icon { @apply h-6 w-6 rounded-md; + opacity: 1; + transition: opacity var(--dur-fast) var(--ease-smooth); +} + +.list-icon-stack { + @apply grid h-6 w-6 place-items-center; +} + +.list-icon-stack > .list-icon, +.list-icon-stack > .list-icon-fallback { + grid-area: 1 / 1; +} + +.list-icon-stack > .list-icon { + opacity: 0; +} + +.list-icon-stack > .list-icon.loaded { + opacity: 1; +} + +.list-icon-fallback.hidden { + opacity: 0; } .list-icon-fallback { @apply grid place-items-center; color: #64748b; - transition: color var(--dur-fast) var(--ease-smooth), transform 240ms var(--ease-out-soft); + transition: + color var(--dur-fast) var(--ease-smooth), + opacity var(--dur-fast) var(--ease-smooth), + transform 240ms var(--ease-out-soft); } .list-icon-fallback svg { @@ -615,7 +641,7 @@ .totp-codes-list { @apply grid w-full items-start gap-2.5; - grid-template-columns: repeat(var(--totp-columns, 1), minmax(320px, 1fr)); + grid-template-columns: repeat(var(--totp-columns, 1), minmax(300px, 1fr)); } .totp-code-row {