chordia-ui 3.2.1 → 3.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs2.js +2 -2
- package/dist/index.cjs2.js.map +1 -1
- package/dist/index.es2.js +717 -705
- package/dist/index.es2.js.map +1 -1
- package/package.json +1 -1
- package/src/components/login/LoginPage.jsx +42 -14
package/package.json
CHANGED
|
@@ -190,16 +190,40 @@ export default function LoginPage({
|
|
|
190
190
|
// Sync React state with browser autofill (autofill doesn't fire onChange)
|
|
191
191
|
const emailRef = useRef(null);
|
|
192
192
|
const passwordRef = useRef(null);
|
|
193
|
+
const [autofilled, setAutofilled] = useState(false);
|
|
193
194
|
useEffect(() => {
|
|
194
195
|
const sync = () => {
|
|
195
196
|
const e = emailRef.current;
|
|
196
197
|
const p = passwordRef.current;
|
|
197
|
-
if (e && e.value
|
|
198
|
-
if (p && p.value
|
|
198
|
+
if (e && e.value) setEmail(e.value);
|
|
199
|
+
if (p && p.value) setPassword(p.value);
|
|
200
|
+
};
|
|
201
|
+
// Poll for autofill values at multiple intervals
|
|
202
|
+
const timers = [100, 300, 600, 1000, 2000].map(ms => setTimeout(sync, ms));
|
|
203
|
+
|
|
204
|
+
// Detect Chrome/Safari autofill via the :-webkit-autofill animation
|
|
205
|
+
const handleAnimationStart = (e) => {
|
|
206
|
+
if (e.animationName === 'onAutoFillStart' || e.target.matches(':-webkit-autofill')) {
|
|
207
|
+
setAutofilled(true);
|
|
208
|
+
setTimeout(sync, 50);
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
const eEl = emailRef.current;
|
|
212
|
+
const pEl = passwordRef.current;
|
|
213
|
+
eEl?.addEventListener('animationstart', handleAnimationStart);
|
|
214
|
+
pEl?.addEventListener('animationstart', handleAnimationStart);
|
|
215
|
+
// Also listen for input/change events that some password managers fire
|
|
216
|
+
const onInput = () => setTimeout(sync, 0);
|
|
217
|
+
eEl?.addEventListener('input', onInput);
|
|
218
|
+
pEl?.addEventListener('input', onInput);
|
|
219
|
+
|
|
220
|
+
return () => {
|
|
221
|
+
timers.forEach(clearTimeout);
|
|
222
|
+
eEl?.removeEventListener('animationstart', handleAnimationStart);
|
|
223
|
+
pEl?.removeEventListener('animationstart', handleAnimationStart);
|
|
224
|
+
eEl?.removeEventListener('input', onInput);
|
|
225
|
+
pEl?.removeEventListener('input', onInput);
|
|
199
226
|
};
|
|
200
|
-
// Check multiple times as autofill timing varies across browsers
|
|
201
|
-
const timers = [100, 500, 1000, 2000].map(ms => setTimeout(sync, ms));
|
|
202
|
-
return () => timers.forEach(clearTimeout);
|
|
203
227
|
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
204
228
|
|
|
205
229
|
// Switch to full-name screen when host app signals unregistered email
|
|
@@ -212,7 +236,7 @@ export default function LoginPage({
|
|
|
212
236
|
|
|
213
237
|
const loading = externalLoading ?? internalLoading;
|
|
214
238
|
const error = externalError ?? internalError;
|
|
215
|
-
const canSubmit = email && password && !loading;
|
|
239
|
+
const canSubmit = (email && password && !loading) || (autofilled && !loading);
|
|
216
240
|
|
|
217
241
|
// Error-aware focus handlers for sign-in fields
|
|
218
242
|
const focusErr = (e) => {
|
|
@@ -241,10 +265,14 @@ export default function LoginPage({
|
|
|
241
265
|
const handleSubmit = async (e) => {
|
|
242
266
|
e.preventDefault();
|
|
243
267
|
if (!canSubmit) return;
|
|
268
|
+
// Read directly from DOM if React state missed autofill
|
|
269
|
+
const submitEmail = email || emailRef.current?.value || '';
|
|
270
|
+
const submitPassword = password || passwordRef.current?.value || '';
|
|
271
|
+
if (!submitEmail || !submitPassword) return;
|
|
244
272
|
setInternalError(null);
|
|
245
273
|
setInternalLoading(true);
|
|
246
274
|
try {
|
|
247
|
-
await onLogin?.(
|
|
275
|
+
await onLogin?.(submitEmail, submitPassword);
|
|
248
276
|
} catch (err) {
|
|
249
277
|
setInternalError(err?.message || 'Something went wrong. Please try again.');
|
|
250
278
|
} finally {
|
|
@@ -487,7 +515,7 @@ export default function LoginPage({
|
|
|
487
515
|
|
|
488
516
|
<div style={{ display: 'flex', flexDirection: 'column', width: '100%', gap: 16 }}>
|
|
489
517
|
<Field label="Enter one-time code" gap={12}>
|
|
490
|
-
<div style={{ display: 'flex', gap:
|
|
518
|
+
<div style={{ display: 'flex', gap: 8 }}>
|
|
491
519
|
{otpDigits.map((digit, i) => (
|
|
492
520
|
<input key={i} ref={(el) => { otpRefs.current[i] = el; }}
|
|
493
521
|
type="text" inputMode="numeric" maxLength={1} value={digit}
|
|
@@ -495,9 +523,9 @@ export default function LoginPage({
|
|
|
495
523
|
onKeyDown={(e) => handleOtpKeyDown(i, e)}
|
|
496
524
|
autoFocus={i === 0}
|
|
497
525
|
style={{
|
|
498
|
-
|
|
526
|
+
flex: 1, minWidth: 0, height: 40, textAlign: 'center', fontSize: 16, fontWeight: 600,
|
|
499
527
|
fontFamily: FF, color: 'var(--color-text)', border: '1px solid var(--color-input-border)',
|
|
500
|
-
borderRadius:
|
|
528
|
+
borderRadius: 4, outline: 'none', background: 'white', boxSizing: 'border-box',
|
|
501
529
|
transition: 'border-color 0.15s, box-shadow 0.15s', caretColor: GREEN,
|
|
502
530
|
}}
|
|
503
531
|
onFocus={focusGreen} onBlur={blurGray}
|
|
@@ -554,7 +582,7 @@ export default function LoginPage({
|
|
|
554
582
|
</Field>
|
|
555
583
|
|
|
556
584
|
<Field label="Enter one-time code" gap={12}>
|
|
557
|
-
<div style={{ display: 'flex', gap:
|
|
585
|
+
<div style={{ display: 'flex', gap: 8 }}>
|
|
558
586
|
{otpDigits.map((digit, i) => (
|
|
559
587
|
<input key={i} ref={(el) => { otpRefs.current[i] = el; }}
|
|
560
588
|
type="text" inputMode="numeric" maxLength={1} value={digit}
|
|
@@ -562,9 +590,9 @@ export default function LoginPage({
|
|
|
562
590
|
onKeyDown={(e) => handleOtpKeyDown(i, e)}
|
|
563
591
|
autoFocus={i === 0}
|
|
564
592
|
style={{
|
|
565
|
-
|
|
593
|
+
flex: 1, minWidth: 0, height: 40, textAlign: 'center', fontSize: 16, fontWeight: 600,
|
|
566
594
|
fontFamily: FF, color: 'var(--color-text)', border: '1px solid var(--color-input-border)',
|
|
567
|
-
borderRadius:
|
|
595
|
+
borderRadius: 4, outline: 'none', background: 'white', boxSizing: 'border-box',
|
|
568
596
|
transition: 'border-color 0.15s, box-shadow 0.15s', caretColor: GREEN,
|
|
569
597
|
}}
|
|
570
598
|
onFocus={focusGreen} onBlur={blurGray}
|
|
@@ -660,7 +688,7 @@ export default function LoginPage({
|
|
|
660
688
|
</div>
|
|
661
689
|
|
|
662
690
|
<NavRow text="Not a member yet?" linkText="Sign Up" onClick={() => { setView('signup'); onSignUp?.(); }} />
|
|
663
|
-
<TermsFooter onTerms={onTerms} onPrivacyPolicy={onPrivacyPolicy} paddingTop={
|
|
691
|
+
<TermsFooter onTerms={onTerms} onPrivacyPolicy={onPrivacyPolicy} paddingTop={16} />
|
|
664
692
|
</div>
|
|
665
693
|
</div>
|
|
666
694
|
)}
|