Stage 8 polish: keyboard-aware modals, consistent select pickers, required game type
Tests · UI / test (push) Successful in 17s
Tests · UI / test (push) Successful in 17s
Third owner-review pass (iPhone): - Modals (and the chat) size their backdrop to window.visualViewport, so they stay fully above the software keyboard (dvh alone left the sheet partly behind it). - On the owner's call, every profile / new-game picker is a native <select> for consistent cross-platform behaviour: the away window returns to hour + 10-minute selects (which also avoids the iOS time-wheel "clear" button), alongside the offset timezone and the game-type / move-time / hints selects. Native time/wheel inputs render differently per OS and cannot be forced to match. - New-game "play with friends" has no preselected game type — an explicit, required pick (empty placeholder); Send invitation stays disabled until both a type and a friend are chosen. A smart default (from play history / language) is TODO-6.
This commit is contained in:
@@ -62,7 +62,9 @@
|
||||
let friends = $state<AccountRef[]>([]);
|
||||
let selected = $state<string[]>([]);
|
||||
let friendFilter = $state('');
|
||||
let inviteVariant = $state<Variant>('english');
|
||||
// No default game type yet — the player must pick one (a smarter default from play
|
||||
// history / language is TODO-6). '' renders the disabled placeholder option.
|
||||
let inviteVariant = $state<Variant | ''>('');
|
||||
let timeoutSecs = $state(86400);
|
||||
let hints = $state(1);
|
||||
|
||||
@@ -86,7 +88,7 @@
|
||||
}
|
||||
|
||||
async function sendInvite() {
|
||||
if (selected.length === 0 || selected.length > 3) return;
|
||||
if (selected.length === 0 || selected.length > 3 || !inviteVariant) return;
|
||||
try {
|
||||
await gateway.invitationCreate(selected, {
|
||||
variant: inviteVariant,
|
||||
@@ -148,7 +150,8 @@
|
||||
<div class="settings-row">
|
||||
<label class="field">
|
||||
<span>{t('new.gameType')}</span>
|
||||
<select bind:value={inviteVariant}>
|
||||
<select bind:value={inviteVariant} class:placeholder={!inviteVariant}>
|
||||
<option value="" disabled>—</option>
|
||||
{#each variants as v (v.id)}<option value={v.id}>{t(v.label)}</option>{/each}
|
||||
</select>
|
||||
</label>
|
||||
@@ -165,7 +168,7 @@
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<button class="invite" disabled={selected.length === 0} onclick={sendInvite}>{t('new.invite')}</button>
|
||||
<button class="invite" disabled={selected.length === 0 || !inviteVariant} onclick={sendInvite}>{t('new.invite')}</button>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
@@ -289,6 +292,9 @@
|
||||
color: var(--text);
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
.field select.placeholder {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.muted {
|
||||
color: var(--text-muted);
|
||||
margin: 0;
|
||||
|
||||
Reference in New Issue
Block a user