fix(telegram-widget): embed password reveal as in-input eye icon matching the main auth form so the toggle no longer overflows on native
This commit is contained in:
parent
6c052bbff9
commit
aca33f470d
2 changed files with 106 additions and 34 deletions
|
|
@ -110,6 +110,34 @@ const QrIcon = () => (
|
|||
</svg>
|
||||
);
|
||||
|
||||
// Eye + eye-with-slash for the password reveal toggle. SVG paths
|
||||
// copied verbatim from folds `Icons.Eye(false)` / `Icons.EyeBlind(false)`
|
||||
// — the unfilled variants Vojo's main auth uses via
|
||||
// `src/app/components/password-input/PasswordInput.tsx`. Importing folds
|
||||
// into the widget bundle would pull the whole component library, so we
|
||||
// inline the geometry. ViewBox 24×24 + `fill="currentColor"` matches
|
||||
// the folds Icon component output bit-for-bit; the only divergence vs
|
||||
// the host is the wrapper (folds `IconButton` vs our plain `<button>`).
|
||||
const EyeIcon = () => (
|
||||
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
||||
<path d="M15 12C15 13.6569 13.6569 15 12 15C10.3431 15 9 13.6569 9 12C9 10.3431 10.3431 9 12 9C13.6569 9 15 10.3431 15 12Z" />
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M1 12C1 12 5.92487 19 12 19C18.0751 19 23 12 23 12C23 12 18.0751 5 12 5C5.92487 5 1 12 1 12ZM2.90443 12C2.93793 12.0401 2.97258 12.0813 3.00836 12.1235C3.53083 12.7395 4.28523 13.5585 5.21221 14.3734C7.11461 16.0459 9.51515 17.5 12 17.5C14.4849 17.5 16.8854 16.0459 18.7878 14.3734C19.7148 13.5585 20.4692 12.7395 20.9916 12.1235C21.0274 12.0813 21.0621 12.0401 21.0956 12C21.0621 11.9599 21.0274 11.9187 20.9916 11.8765C20.4692 11.2605 19.7148 10.4415 18.7878 9.62656C16.8854 7.9541 14.4849 6.5 12 6.5C9.51515 6.5 7.11461 7.9541 5.21221 9.62656C4.28523 10.4415 3.53083 11.2605 3.00836 11.8765C2.97258 11.9187 2.93793 11.9599 2.90443 12Z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
const EyeBlindIcon = () => (
|
||||
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M4.75213 3.69141L3.69147 4.75207L6.02989 7.09049C3.00297 9.15318 1 12.0001 1 12.0001C1 12.0001 5.92487 19.0001 12 19.0001C13.663 19.0001 15.2399 18.4756 16.6531 17.7137L19.2478 20.3084L20.3085 19.2478L4.75213 3.69141ZM15.5394 16.6L13.5242 14.5848C13.0775 14.8488 12.5565 15.0003 12 15.0003C10.3431 15.0003 9 13.6572 9 12.0003C9 11.4439 9.1515 10.9228 9.4155 10.4761L7.11135 8.17195C6.4387 8.61141 5.80156 9.10856 5.21221 9.62667C4.28523 10.4416 3.53083 11.2607 3.00836 11.8766C2.97258 11.9188 2.93793 11.96 2.90443 12.0001C2.93793 12.0402 2.97258 12.0814 3.00836 12.1236C3.53083 12.7396 4.28523 13.5586 5.21221 14.3736C7.11461 16.046 9.51515 17.5001 12 17.5001C13.2162 17.5001 14.4122 17.1518 15.5394 16.6ZM18.5058 14.6167C18.6009 14.5363 18.6949 14.4552 18.7878 14.3736C19.7148 13.5586 20.4692 12.7396 20.9916 12.1236C21.0274 12.0814 21.0621 12.0402 21.0956 12.0001C21.0621 11.96 21.0274 11.9188 20.9916 11.8766C20.4692 11.2607 19.7148 10.4416 18.7878 9.62667C16.8854 7.95422 14.4849 6.50011 12 6.50011C11.5118 6.50011 11.0268 6.55625 10.5482 6.65915L9.32458 5.43554C10.181 5.16161 11.0772 5.00011 12 5.00011C18.0751 5.00011 23 12.0001 23 12.0001C23 12.0001 21.6825 13.8727 19.5699 15.6808L18.5058 14.6167Z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
// Sign-out arrow leaving an open box — leads the destructive logout
|
||||
// card. Open right side conveys «out of the session». Stays muted
|
||||
// inside `.command-card.danger` so the rose accent is reserved for
|
||||
|
|
@ -599,24 +627,37 @@ const PasswordForm = ({ state, t, dispatch, send, sendCancel }: FormProps) => {
|
|||
{t('auth-card.password.label')}
|
||||
</label>
|
||||
<div class="auth-card-row">
|
||||
<div class="password-row">
|
||||
<div class="auth-password-shell">
|
||||
<input
|
||||
id="auth-password-input"
|
||||
ref={inputRef}
|
||||
class="auth-input password"
|
||||
type={reveal ? 'text' : 'password'}
|
||||
autocomplete="current-password"
|
||||
// `size={1}` kills the HTML default `size=20` which, combined
|
||||
// with `.auth-input.password`'s 20 px font + 4 px
|
||||
// letter-spacing + 44 px right-padding, gives the input an
|
||||
// intrinsic min-content width near 460 px. Chromium does not
|
||||
// honour `min-width: 0` on a flex-item `<input>` against that
|
||||
// size-derived minimum, so the input refused to shrink and
|
||||
// pushed past the `.auth-card` border on narrow viewports.
|
||||
// Setting `size=1` drops the intrinsic floor so `flex: 1`
|
||||
// + `min-width: 0` actually shrink the box to the slot.
|
||||
size={1}
|
||||
value={value}
|
||||
onInput={(e) => setValue((e.currentTarget as HTMLInputElement).value)}
|
||||
disabled={submitting}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-icon"
|
||||
class="auth-password-eye"
|
||||
onClick={() => setReveal((v) => !v)}
|
||||
aria-label={reveal ? t('auth-card.password.hide') : t('auth-card.password.show')}
|
||||
aria-pressed={reveal}
|
||||
aria-controls="auth-password-input"
|
||||
disabled={submitting}
|
||||
>
|
||||
{reveal ? t('auth-card.password.hide') : t('auth-card.password.show')}
|
||||
{reveal ? <EyeIcon /> : <EyeBlindIcon />}
|
||||
</button>
|
||||
</div>
|
||||
<button type="submit" class="btn-primary" disabled={submitting || value === ''}>
|
||||
|
|
|
|||
|
|
@ -469,8 +469,7 @@ body {
|
|||
.command-card-confirm-yes,
|
||||
.command-card-confirm-no,
|
||||
.btn-primary,
|
||||
.btn-text,
|
||||
.btn-icon {
|
||||
.btn-text {
|
||||
font: inherit;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
@ -620,26 +619,73 @@ body {
|
|||
font-size: 20px;
|
||||
}
|
||||
|
||||
.password-row {
|
||||
/* Password-input shell — host for the reveal eye-button positioned over
|
||||
* the input's right padding. Mirrors `.auth-phone-shell` (left flag) and
|
||||
* follows the same UX as Vojo's main auth `PasswordInput` (eye toggle
|
||||
* embedded in the input chrome, no sibling pill that can overflow off
|
||||
* the right edge on narrow viewports). Replaces the previous
|
||||
* `.password-row` flex-pair that stacked into a full-width button on
|
||||
* mobile, see commit 8d8b39e8. */
|
||||
.auth-password-shell {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
gap: 6px;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
background: transparent;
|
||||
border: 1px solid var(--divider);
|
||||
border-radius: 8px;
|
||||
color: var(--muted);
|
||||
padding: 0 12px;
|
||||
font-size: 13px;
|
||||
flex-shrink: 0;
|
||||
.auth-password-shell .auth-input {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
/* Reserve room for the eye button so the password bullets don't run
|
||||
* under the glyph. Eye button ≈ 36 px wide + 6 px breathing room. */
|
||||
padding-right: 44px;
|
||||
}
|
||||
.btn-icon:hover {
|
||||
/* Suppress the legacy Edge / IE11 native reveal glyph (`::-ms-reveal`)
|
||||
* so it doesn't render on top of our own eye button. Chromium / WebKit
|
||||
* / Firefox ignore this pseudo — no-op on the platforms we actually
|
||||
* ship to, cheap defence for users who arrive on legacy Edge. */
|
||||
.auth-password-shell .auth-input::-ms-reveal {
|
||||
display: none;
|
||||
}
|
||||
.auth-password-eye {
|
||||
position: absolute;
|
||||
right: 6px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
color: var(--muted);
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
}
|
||||
.auth-password-eye:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
/* Hover / focus rings gated on `[data-input='mouse']` (set by main.tsx
|
||||
* from `pointerdown.pointerType`) because Capacitor Android WebView lies
|
||||
* about `(hover: hover)` on pure-touch devices — a media-query gate
|
||||
* would still let the WebView paint a stuck `:hover` on the tapped
|
||||
* button. Same reason `.command-card:hover` upstream is gated this way. */
|
||||
:root[data-input='mouse'] .auth-password-eye:hover:not(:disabled) {
|
||||
color: var(--text);
|
||||
border-color: var(--hairline);
|
||||
background: var(--hairline);
|
||||
}
|
||||
:root[data-input='mouse'] .auth-password-eye:focus-visible {
|
||||
outline: 2px solid var(--fleet);
|
||||
outline-offset: 1px;
|
||||
color: var(--text);
|
||||
}
|
||||
.auth-password-eye svg {
|
||||
/* 16 px matches folds `<Icon size="100">` used by the canonical
|
||||
* password input in `src/app/components/password-input`. */
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
|
|
@ -782,21 +828,6 @@ body {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
/* PasswordForm wraps its input + show/hide toggle in `.password-row`
|
||||
* so the toggle pill sits next to the input on desktop. On narrow
|
||||
* viewports that nested row stays row-direction with `flex-shrink: 0`
|
||||
* on `.btn-icon`, and the input's monospace `font-size: 20px` +
|
||||
* `letter-spacing: 4px` (see `.auth-input.password`) pushes the toggle
|
||||
* off-screen. Continue the same column-stack pattern the outer
|
||||
* `.auth-card-row` already uses so the toggle drops below the input
|
||||
* full-width — visually consistent with btn-primary / btn-text. */
|
||||
.password-row {
|
||||
flex-direction: column;
|
||||
}
|
||||
.password-row .btn-icon {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Compact .command-card on mobile — preserves the «two-row title +
|
||||
* chevron» structure but trims padding so a single login/logout card
|
||||
* doesn't dominate a phone-height viewport. */
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue