mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
feat(forms): wire keyboard hints and aria attributes across settings and admin forms
Bring the same `inputMode`, `enterKeyHint`, `autoComplete`, and
`spellCheck` defaults to the settings and admin surfaces:
- Account: profile email gets `autoComplete="email"` +
`enterKeyHint="next"`, the disabled username carries
`autoComplete="username"` so the password manager can still bind
the credential, and the height field hints the decimal keyboard.
- Telegram card: bot token and chat ID both opt out of autofill,
disable spell-check + autocapitalize, and lead the chained next →
done keyboard order.
- Integrations: Withings client-id / secret and the moodLog URL +
API key share the off-spell + no-capitalize secret-input contract
and chain into the test / save action via `enterKeyHint`.
- Notifications anchors: each card wrapper carries `scroll-mt-28`
so direct anchor navigation to `#telegram`, `#ntfy`, `#web-push`
lands the section header below the safe-area sticky header
instead of under it.
- Thresholds editor: min/max numeric inputs derive `inputMode`
("numeric" for integer ACTIVITY_STEPS, "decimal" elsewhere) and
chain next → done.
- Admin reminders: the late-window and missed-window minute inputs
pin `inputMode="numeric"` and the next → done keyboard order.
This commit is contained in:
1 parent
48261b6797
commit
192170ee5f
6 files changed
+55
-4
No files matched your search
@@ -132,6 +132,8 @@ export function RemindersSection() {
|
||||
<Input
|
||||
id="admin-reminder-late"
|
||||
type="number"
|
||||
inputMode="numeric"
|
||||
enterKeyHint="next"
|
||||
min={15}
|
||||
max={480}
|
||||
value={reminderLateDraft ?? settings?.reminderLateMinutes ?? 120}
|
||||
@@ -153,6 +155,8 @@ export function RemindersSection() {
|
||||
<Input
|
||||
id="admin-reminder-missed"
|
||||
type="number"
|
||||
inputMode="numeric"
|
||||
enterKeyHint="done"
|
||||
min={30}
|
||||
max={720}
|
||||
value={
|
||||
|
||||
@@ -355,7 +355,12 @@ export function AccountSection() {
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="username">{t("settings.username")}</Label>
|
||||
<Input id="username" value={user.username} disabled />
|
||||
<Input
|
||||
id="username"
|
||||
value={user.username}
|
||||
disabled
|
||||
autoComplete="username"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email">{t("auth.email")}</Label>
|
||||
@@ -366,6 +371,8 @@ export function AccountSection() {
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder={t("auth.emailPlaceholder")}
|
||||
maxLength={320}
|
||||
autoComplete="email"
|
||||
enterKeyHint="next"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -392,6 +399,8 @@ export function AccountSection() {
|
||||
<Input
|
||||
id="height"
|
||||
type="number"
|
||||
inputMode="decimal"
|
||||
enterKeyHint="next"
|
||||
value={heightCm}
|
||||
onChange={(e) => setHeightCm(e.target.value)}
|
||||
placeholder="175"
|
||||
|
||||
@@ -404,6 +404,11 @@ function WithingsCard({
|
||||
: t("settings.withingsClientId")
|
||||
}
|
||||
maxLength={200}
|
||||
autoComplete="off"
|
||||
inputMode="text"
|
||||
spellCheck={false}
|
||||
autoCapitalize="none"
|
||||
enterKeyHint="next"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
@@ -420,6 +425,11 @@ function WithingsCard({
|
||||
: t("settings.withingsClientSecret")
|
||||
}
|
||||
maxLength={200}
|
||||
autoComplete="off"
|
||||
inputMode="text"
|
||||
spellCheck={false}
|
||||
autoCapitalize="none"
|
||||
enterKeyHint="done"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
@@ -703,6 +713,10 @@ function MoodLogCard({
|
||||
placeholder={t("settings.moodLogUrlPlaceholder")}
|
||||
value={url}
|
||||
onChange={(e) => setUrl(e.target.value)}
|
||||
autoComplete="off"
|
||||
spellCheck={false}
|
||||
autoCapitalize="none"
|
||||
enterKeyHint="next"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
@@ -718,6 +732,11 @@ function MoodLogCard({
|
||||
}
|
||||
value={apiKey}
|
||||
onChange={(e) => setApiKey(e.target.value)}
|
||||
autoComplete="off"
|
||||
inputMode="text"
|
||||
spellCheck={false}
|
||||
autoCapitalize="none"
|
||||
enterKeyHint="done"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-start gap-2">
|
||||
|
||||
@@ -54,18 +54,23 @@ export function NotificationsSection() {
|
||||
|
||||
<NotificationStatusCard />
|
||||
|
||||
{/* v1.4.27 MB3 — `scroll-mt-28` keeps each notification card's
|
||||
anchor target below the safe-area sticky page header when the
|
||||
user lands on `/settings/notifications#telegram` or similar.
|
||||
Without the offset the section title pins under the header on
|
||||
mobile and the user has to scroll back up to find it. */}
|
||||
{showTelegram && (
|
||||
<div id="telegram">
|
||||
<div id="telegram" className="scroll-mt-28">
|
||||
<TelegramCard isAuthenticated={isAuthenticated} />
|
||||
</div>
|
||||
)}
|
||||
{showNtfy && (
|
||||
<div id="ntfy">
|
||||
<div id="ntfy" className="scroll-mt-28">
|
||||
<NtfyCard isAuthenticated={isAuthenticated} />
|
||||
</div>
|
||||
)}
|
||||
{showWebPush && (
|
||||
<div id="web-push">
|
||||
<div id="web-push" className="scroll-mt-28">
|
||||
<WebPushCard />
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -148,6 +148,11 @@ export function TelegramCard({
|
||||
: "123456:ABC-DEF..."
|
||||
}
|
||||
maxLength={100}
|
||||
autoComplete="off"
|
||||
inputMode="text"
|
||||
spellCheck={false}
|
||||
autoCapitalize="none"
|
||||
enterKeyHint="next"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
@@ -158,6 +163,11 @@ export function TelegramCard({
|
||||
onChange={(e) => setChatId(e.target.value)}
|
||||
placeholder="123456789"
|
||||
maxLength={50}
|
||||
autoComplete="off"
|
||||
inputMode="text"
|
||||
spellCheck={false}
|
||||
autoCapitalize="none"
|
||||
enterKeyHint="done"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -282,6 +282,8 @@ function MetricRow({
|
||||
<Input
|
||||
id={`min-${metric}`}
|
||||
type="number"
|
||||
inputMode={metric === "ACTIVITY_STEPS" ? "numeric" : "decimal"}
|
||||
enterKeyHint="next"
|
||||
step={metric === "ACTIVITY_STEPS" ? 100 : 0.1}
|
||||
min={bounds.min}
|
||||
max={bounds.max}
|
||||
@@ -298,6 +300,8 @@ function MetricRow({
|
||||
<Input
|
||||
id={`max-${metric}`}
|
||||
type="number"
|
||||
inputMode={metric === "ACTIVITY_STEPS" ? "numeric" : "decimal"}
|
||||
enterKeyHint="done"
|
||||
step={metric === "ACTIVITY_STEPS" ? 100 : 0.1}
|
||||
min={bounds.min}
|
||||
max={bounds.max}
|
||||
|
||||
Reference in new issue
Block a user