feat(oxibadge): add onboarding flow with i18n and reader preferences
Check, format and test / build (push) Successful in 9m59s
Check, format and test / build (push) Successful in 9m59s
Adds the first real screen: a setup form for the device's host/port, tested against the real device (3 retries) and persisted to preferences.toml before showing a placeholder "done" screen. - Rust: test_connection/save_preferences/load_preferences commands; preferences stored as [[reader]] in TOML so more readers can be added later without a format change. - Svelte: split the monolithic page into reusable components (Screen, Column, Field, Button, ErrorBanner, Hint) under src/lib/components, sharing design tokens from app.css. - Automatic it/en i18n via svelte-i18n, locale detected from navigator.language. Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
<script lang="ts">
|
||||
let {
|
||||
label,
|
||||
type = 'button',
|
||||
faded = false,
|
||||
busy = false,
|
||||
disabled = false,
|
||||
onclick
|
||||
}: {
|
||||
label: string;
|
||||
type?: 'button' | 'submit';
|
||||
faded?: boolean;
|
||||
busy?: boolean;
|
||||
disabled?: boolean;
|
||||
onclick?: () => void;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<button {type} class="btn" class:faded class:busy {disabled} {onclick}>
|
||||
<span>{label}</span>
|
||||
{#if busy}
|
||||
<span class="spinner"></span>
|
||||
{:else}
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" stroke-width="2" aria-hidden="true"
|
||||
><path d="M5 12h14M13 6l6 6-6 6" /></svg
|
||||
>
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
<style>
|
||||
.btn {
|
||||
all: unset;
|
||||
box-sizing: border-box;
|
||||
height: var(--ox-control-h);
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
border-radius: var(--ox-radius);
|
||||
background: var(--ox-accent);
|
||||
color: var(--ox-on-accent);
|
||||
font-family: var(--ox-font-body);
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
filter 0.15s,
|
||||
transform 0.1s,
|
||||
opacity 0.2s;
|
||||
}
|
||||
|
||||
.btn:not(:disabled):hover {
|
||||
filter: brightness(1.08);
|
||||
}
|
||||
|
||||
.btn:not(:disabled):active {
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
.btn:focus-visible {
|
||||
outline: 2px solid #ffffff;
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
.btn:disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.btn.faded {
|
||||
opacity: 0.35;
|
||||
}
|
||||
|
||||
.btn.busy {
|
||||
opacity: 0.7;
|
||||
cursor: progress;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 2px solid rgba(26, 7, 0, 0.35);
|
||||
border-top-color: var(--ox-on-accent);
|
||||
border-radius: 50%;
|
||||
animation: spin 0.7s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user