@samitouri / QOSami-HFS / commits / fc9e8260

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 }