453ddc5e94
- plain Svelte 5 + TS + Vite (no SvelteKit); CSS-token design system (Telegram-ready), hash router, IndexedDB session - pure libs: domain model, premium/value maps ported from solver, board replay, placement state machine, i18n en/ru - in-memory mock transport + seed data; pnpm start runs lobby->active game->board with no backend - board: pointer-drag + tap placement, MakeMove (popup / 1s-hold commit), two-state zoom, blank chooser, exchange, hint, word-check, chat - Playwright smoke (mock) green; svelte-check clean; mock bundle ~37 KB gzip
123 lines
2.5 KiB
Svelte
123 lines
2.5 KiB
Svelte
<script lang="ts">
|
|
// The contextual commit control: appears when tiles are pending. A short tap opens a
|
|
// minimalist popup (Make move / Reset); a press-and-hold (~1s) commits immediately.
|
|
let {
|
|
label,
|
|
resetLabel,
|
|
onmake,
|
|
onreset,
|
|
}: { label: string; resetLabel: string; onmake: () => void; onreset: () => void } = $props();
|
|
|
|
let popup = $state(false);
|
|
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
let held = false;
|
|
|
|
function clear() {
|
|
if (timer) {
|
|
clearTimeout(timer);
|
|
timer = null;
|
|
}
|
|
}
|
|
function down() {
|
|
held = false;
|
|
clear();
|
|
timer = setTimeout(() => {
|
|
held = true;
|
|
popup = false;
|
|
onmake();
|
|
}, 1000);
|
|
}
|
|
function up() {
|
|
clear();
|
|
if (!held) popup = true;
|
|
}
|
|
function leave() {
|
|
clear();
|
|
}
|
|
</script>
|
|
|
|
<div class="wrap">
|
|
<button
|
|
class="make"
|
|
onpointerdown={down}
|
|
onpointerup={up}
|
|
onpointerleave={leave}
|
|
onpointercancel={leave}
|
|
>
|
|
{label}
|
|
</button>
|
|
|
|
{#if popup}
|
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
<div class="backdrop" onclick={() => (popup = false)}></div>
|
|
<div class="popup">
|
|
<button
|
|
class="go"
|
|
onclick={() => {
|
|
popup = false;
|
|
onmake();
|
|
}}>{label}</button
|
|
>
|
|
<button
|
|
class="rs"
|
|
onclick={() => {
|
|
popup = false;
|
|
onreset();
|
|
}}>{resetLabel}</button
|
|
>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
|
|
<style>
|
|
.wrap {
|
|
position: relative;
|
|
}
|
|
.make {
|
|
height: 100%;
|
|
min-width: 64px;
|
|
padding: 0 14px;
|
|
border: 1px solid var(--accent);
|
|
background: var(--accent);
|
|
color: var(--accent-text);
|
|
border-radius: var(--radius-sm);
|
|
font-weight: 700;
|
|
touch-action: none;
|
|
user-select: none;
|
|
}
|
|
.backdrop {
|
|
position: fixed;
|
|
inset: 0;
|
|
z-index: 18;
|
|
}
|
|
.popup {
|
|
position: absolute;
|
|
right: 0;
|
|
bottom: calc(100% + 6px);
|
|
z-index: 19;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
background: var(--surface);
|
|
border: 1px solid var(--border);
|
|
border-radius: var(--radius-sm);
|
|
box-shadow: var(--shadow);
|
|
padding: 6px;
|
|
min-width: 140px;
|
|
}
|
|
.popup button {
|
|
padding: 10px;
|
|
border-radius: var(--radius-sm);
|
|
border: 1px solid var(--border);
|
|
background: var(--surface);
|
|
color: var(--text);
|
|
font-weight: 600;
|
|
}
|
|
.popup .go {
|
|
background: var(--accent);
|
|
color: var(--accent-text);
|
|
border-color: var(--accent);
|
|
}
|
|
</style>
|