8881214213
Mechanical, behaviour-preserving removal of Stage N / TODO-N / phase (RN) references from comments, doc-comments, service READMEs, the current-state docs (ARCHITECTURE, FUNCTIONAL+_ru, TESTING, UI_DESIGN), config-file comments, and the .fbs/.proto schema comments. PLAN.md / PRERELEASE.md / CLAUDE.md keep the stage history. - Rename the only stage-named identifiers: registerStage8 -> registerSocialOps, registerStage11 -> registerLinkOps (gateway transcode). - Split stage6_test.go: TestEmailLoginFlow -> email_test.go, TestGuestAutoMatchLeavesNoStats (+ provisionGuest) -> account_test.go. - Regenerated proto bindings (push.pb.go, telegram_grpc.pb.go) from the de-staged .proto comments; FB Go/TS bindings unchanged (flatc strips schema comments). go build/vet/gofmt clean across modules; integration typecheck and pnpm check green.
129 lines
4.0 KiB
Svelte
129 lines
4.0 KiB
Svelte
<script lang="ts">
|
|
import type { Snippet } from 'svelte';
|
|
|
|
let {
|
|
title = '',
|
|
onclose,
|
|
overlayKeyboard = false,
|
|
bottomSheet = false,
|
|
children,
|
|
}: {
|
|
title?: string;
|
|
onclose?: () => void;
|
|
overlayKeyboard?: boolean;
|
|
bottomSheet?: boolean;
|
|
children?: Snippet;
|
|
} = $props();
|
|
|
|
// Track the visual viewport so the backdrop covers only the area above an open
|
|
// mobile keyboard: dvh alone shrinks the sheet but the fixed, layout-viewport
|
|
// backdrop still centres it behind the keyboard. Sizing the backdrop to
|
|
// visualViewport keeps the sheet (and the start of a chat) fully on screen.
|
|
// overlayKeyboard opts out: the sheet is small and top-anchored, so the keyboard
|
|
// simply overlays the empty lower area — no resize, no relayout jank (e.g. check word).
|
|
// bottomSheet anchors a tall sheet (the chat) to the bottom and lifts it above the
|
|
// keyboard with a transform (kb), driven by the visual viewport — a compositor-only
|
|
// move, so neither the page behind nor the sheet relayouts as the keyboard animates
|
|
// The backdrop is not resized in this mode (no per-event reflow).
|
|
let vh = $state(0);
|
|
let top = $state(0);
|
|
let kb = $state(0);
|
|
$effect(() => {
|
|
const vv = typeof window !== 'undefined' ? window.visualViewport : null;
|
|
if (!vv || overlayKeyboard) return;
|
|
const update = () => {
|
|
vh = vv.height;
|
|
top = vv.offsetTop;
|
|
// Soft-keyboard height: the layout viewport minus the visible viewport.
|
|
kb = Math.max(0, window.innerHeight - vv.height - vv.offsetTop);
|
|
};
|
|
update();
|
|
vv.addEventListener('resize', update);
|
|
vv.addEventListener('scroll', update);
|
|
return () => {
|
|
vv.removeEventListener('resize', update);
|
|
vv.removeEventListener('scroll', update);
|
|
};
|
|
});
|
|
</script>
|
|
|
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
<div
|
|
class="backdrop"
|
|
class:overlay={overlayKeyboard}
|
|
class:bottom={bottomSheet}
|
|
style:height={!bottomSheet && vh ? `${vh}px` : null}
|
|
style:top={!bottomSheet && vh ? `${top}px` : null}
|
|
onclick={() => onclose?.()}
|
|
>
|
|
<div
|
|
class="sheet"
|
|
role="dialog"
|
|
aria-modal="true"
|
|
tabindex="-1"
|
|
style:--kb={bottomSheet ? `${kb}px` : null}
|
|
onclick={(e) => e.stopPropagation()}
|
|
>
|
|
{#if title}<h2>{title}</h2>{/if}
|
|
{@render children?.()}
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.backdrop {
|
|
position: fixed;
|
|
left: 0;
|
|
right: 0;
|
|
top: 0;
|
|
/* Base fallback; overridden inline to the visual-viewport height/top so the
|
|
backdrop (and the centred sheet) stay above an open mobile keyboard. */
|
|
height: 100dvh;
|
|
box-sizing: border-box;
|
|
background: rgba(0, 0, 0, 0.45);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 16px;
|
|
z-index: 40;
|
|
}
|
|
/* Overlay mode: top-anchor the (small) sheet and don't track the keyboard, so the
|
|
soft keyboard overlays the empty lower area without resizing/relaying out. */
|
|
.backdrop.overlay {
|
|
align-items: flex-start;
|
|
padding-top: 12vh;
|
|
}
|
|
/* Bottom-sheet mode (the chat): a wide sheet pinned to the bottom that lifts above the
|
|
soft keyboard via a transform (--kb) — compositor-only, so the page behind and the
|
|
sheet itself do not relayout as the keyboard animates. */
|
|
.backdrop.bottom {
|
|
align-items: flex-end;
|
|
padding: 0;
|
|
}
|
|
.backdrop.bottom .sheet {
|
|
width: 100%;
|
|
max-width: 640px;
|
|
border-radius: var(--radius) var(--radius) 0 0;
|
|
transform: translateY(calc(-1 * var(--kb, 0px)));
|
|
transition: transform 0.15s ease;
|
|
}
|
|
.sheet {
|
|
background: var(--surface);
|
|
color: var(--text);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius);
|
|
box-shadow: var(--shadow);
|
|
padding: var(--pad);
|
|
width: min(94vw, 420px);
|
|
/* dvh tracks the dynamic viewport, so the sheet shrinks above an open mobile
|
|
keyboard instead of being scrolled off the top (vh fallback first). */
|
|
max-height: 86vh;
|
|
max-height: 86dvh;
|
|
overflow: auto;
|
|
}
|
|
h2 {
|
|
margin: 0 0 10px;
|
|
font-size: 1.05rem;
|
|
}
|
|
</style>
|