Stage 7 polish: app shell + nav + lobby + settings (Parts A/B/C)
- Screen.svelte shell: nav bar grows, ad+content+tabbar pinned bottom (mobile feel) - AdBanner.svelte + banner.ts rotator (params, mock long/short, linkify); Header CSS chevron + grow; Menu (bigger CSS hamburger); TabBar + HoldConfirm shared components; user-select:none - Lobby: hide-empty sections, tab order New/Tournaments/Stats, place-based result badges (result.ts) - Settings: Board style > Labels (beginner/classic/none) + prefs plumbing (boardlabels.ts); i18n keys + ru mirror
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
<script lang="ts">
|
||||
import type { Snippet } from 'svelte';
|
||||
// A press-and-hold control: a short tap opens a popover (the consumer renders its
|
||||
// buttons), a ~holdMs hold runs `onhold` immediately. Reused by MakeMove and the
|
||||
// game tab-bar confirmations. The popover snippet receives a `close` callback.
|
||||
let {
|
||||
onhold,
|
||||
holdMs = 700,
|
||||
disabled = false,
|
||||
triggerClass = '',
|
||||
trigger,
|
||||
popover,
|
||||
}: {
|
||||
onhold: () => void;
|
||||
holdMs?: number;
|
||||
disabled?: boolean;
|
||||
triggerClass?: string;
|
||||
trigger: Snippet;
|
||||
popover: Snippet<[() => void]>;
|
||||
} = $props();
|
||||
|
||||
let open = $state(false);
|
||||
let timer: ReturnType<typeof setTimeout> | null = null;
|
||||
let held = false;
|
||||
|
||||
function clear() {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
}
|
||||
}
|
||||
function down() {
|
||||
if (disabled) return;
|
||||
held = false;
|
||||
clear();
|
||||
timer = setTimeout(() => {
|
||||
held = true;
|
||||
open = false;
|
||||
onhold();
|
||||
}, holdMs);
|
||||
}
|
||||
function up() {
|
||||
clear();
|
||||
if (!held && !disabled) open = true;
|
||||
}
|
||||
function leave() {
|
||||
clear();
|
||||
}
|
||||
const close = () => (open = false);
|
||||
</script>
|
||||
|
||||
<div class="hc">
|
||||
<button
|
||||
class="trigger {triggerClass}"
|
||||
{disabled}
|
||||
onpointerdown={down}
|
||||
onpointerup={up}
|
||||
onpointerleave={leave}
|
||||
onpointercancel={leave}
|
||||
>
|
||||
{@render trigger()}
|
||||
</button>
|
||||
|
||||
{#if open}
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div class="backdrop" onclick={close}></div>
|
||||
<div class="popover">{@render popover(close)}</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.hc {
|
||||
position: relative;
|
||||
display: flex;
|
||||
}
|
||||
.trigger {
|
||||
width: 100%;
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
touch-action: none;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
.backdrop {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 18;
|
||||
}
|
||||
.popover {
|
||||
position: absolute;
|
||||
bottom: calc(100% + 6px);
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 19;
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user