login: "eye" button on password field
Massimo Melina committed
Dec 27, 2025 at 17:24 UTC
fc9e8260634222da8e74949c0585ac5c8aeed8a1
6 files changed
+59
-11
frontend/fontello-config.json
+12
@@ -263,6 +263,18 @@
263
"css": "info",
264
"code": 59419,
265
"src": "elusive"
266
+ },
267
+ {
268
+ "uid": "44e04715aecbca7f266a17d5a7863c68",
269
+ "css": "plus",
270
+ "code": 59420,
271
+ "src": "fontawesome"
272
+ },
273
+ {
274
+ "uid": "c5fd349cbd3d23e4ade333789c29c729",
275
+ "css": "eye",
276
+ "code": 59421,
277
+ "src": "fontawesome"
278
}
279
]
280
}
\ No newline at end of file
frontend/public/fontello.css
+3
-1
@@ -1,6 +1,6 @@
1
@font-face {
2
font-family: 'fontello';
3
- src: url('fontello.woff2?74614144') format('woff2');
3
+ src: url('fontello.woff2?70687998') format('woff2');
4
font-weight: normal;
5
font-style: normal;
6
}
@@ -67,6 +67,8 @@
67
.fa-cut:before { content: '\e819'; } /* '' */
68
.fa-shuffle:before { content: '\e81a'; } /* '' */
69
.fa-info:before { content: '\e81b'; } /* '' */
70
+.fa-plus:before { content: '\e81c'; } /* '' */
71
+.fa-eye:before { content: '\e81d'; } /* '' */
72
.fa-spin6:before { content: '\e839'; } /* '' */
73
.fa-crown:before { content: '\e844'; } /* '' */
74
.fa-download:before { content: '\f02e'; } /* '' */
frontend/public/fontello.woff2
Binary files a/frontend/public/fontello.woff2 and b/frontend/public/fontello.woff2 differ
frontend/src/index.scss
+16
@@ -519,6 +519,22 @@ button .icon + .label {
519
margin-bottom: .5em;
520
margin-left: .1em;
521
}
522
+ .password-field {
523
+ position: relative;
524
+ input { padding-right: 2.2em; }
525
+ }
526
+ .password-eye {
527
+ position: absolute;
528
+ right: .5em;
529
+ top: 50%;
530
+ transform: translateY(-50%);
531
+ border: 0;
532
+ background: none;
533
+ outline: none;
534
+ padding: 0;
535
+ cursor: pointer;
536
+ line-height: 1;
537
+ }
538
.submit { text-align: right; }
539
}
540
#search-dialog form {
frontend/src/login.ts
+27
-10
@@ -7,7 +7,7 @@ import {
7
getHFS, hIcon, makeSessionRefresher, srpClientSequence, working, fallbackToBasicAuth, hfsEvent,
8
HTTP_CONFLICT, HTTP_UNAUTHORIZED, HTTP_METHOD_NOT_ALLOWED, ALLOW_SESSION_IP_CHANGE,
9
} from './misc'
10
-import { createElement as h, Fragment, useEffect, useRef } from 'react'
10
+import { createElement as h, Fragment, useEffect, useRef, useState } from 'react'
11
import { reloadList } from './useFetchList'
12
import { Checkbox, CustomCode } from './components'
13
import { changePassword } from './UserPanel'
@@ -71,6 +71,7 @@ export async function loginDialog(closable=true, reloadAfter=true) {
71
const usrRef = useRef<HTMLInputElement>()
72
const pwdRef = useRef<HTMLInputElement>()
73
const ipRef = useRef<HTMLInputElement>()
74
+ const [showPassword, setShowPassword] = useState(false)
75
useEffect(() => {
76
setTimeout(() => usrRef.current?.focus()) // setTimeout workarounds problem due to double-mount while in dev
77
}, [])
@@ -95,15 +96,31 @@ export async function loginDialog(closable=true, reloadAfter=true) {
96
)),
97
h(CustomCode, { name: 'loginPasswordField' }, h('div', { className: 'field' },
98
h('label', { htmlFor: 'login_password' }, t`Password`),
98
- h('input', {
99
- ref: pwdRef,
100
- id: 'login_password',
101
- name: 'password',
102
- type: 'password',
103
- autoComplete: 'current-password',
104
- required: true,
105
- onKeyDown
106
- }),
99
+ h('div', { className: 'password-field' },
100
+ h('input', {
101
+ ref: pwdRef,
102
+ id: 'login_password',
103
+ name: 'password',
104
+ type: showPassword ? 'text' : 'password',
105
+ autoComplete: 'current-password',
106
+ required: true,
107
+ onKeyDown
108
+ }),
109
+ h('button', {
110
+ type: 'button',
111
+ className: 'password-eye',
112
+ 'aria-hidden': true,
113
+ tabIndex: -1,
114
+ onPointerDown(ev: PointerEvent) {
115
+ ev.preventDefault()
116
+ setShowPassword(true)
117
+ },
118
+ onPointerUp: () => setShowPassword(false),
119
+ onPointerEnter: () => setShowPassword(true),
120
+ onPointerLeave: () => setShowPassword(false),
121
+ onPointerCancel: () => setShowPassword(false),
122
+ }, hIcon('eye')),
123
+ ),
124
)),
125
h(CustomCode, { name: 'beforeLoginSubmit' }),
126
h('div', { className: 'submit' },
frontend/src/sysIcons.ts
+1
@@ -46,4 +46,5 @@ export const SYS_ICONS: Record<string, [string] | [string, string | false]> = {
46
image: ['📸'],
47
cancel: ['❌','cancel'],
48
total: ['➕', 'spin6'],
49
+ eye: ['👁️'],
50
}