@gerege-systems/eid-mongolia-sdk 0.15.1 → 0.15.3
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/README.md +35 -3
- package/dist/button.cjs +70 -20
- package/dist/button.d.cts +2 -0
- package/dist/button.d.ts +2 -0
- package/dist/button.js +70 -20
- package/dist/chunk-52O3T7DZ.js +1043 -0
- package/dist/index.cjs +837 -68
- package/dist/index.d.cts +113 -1
- package/dist/index.d.ts +113 -1
- package/dist/index.js +265 -28
- package/dist/pades.cjs +16 -3
- package/dist/pades.d.cts +5 -0
- package/dist/pades.d.ts +5 -0
- package/dist/pades.js +61 -1028
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ Smart-ID-тэй RP-үүдийн хэв маяг: хөтөчид **нэг тов
|
|
|
35
35
|
|
|
36
36
|
| Хаана | Юу болно |
|
|
37
37
|
|---|---|
|
|
38
|
-
| Desktop хөтөч | Цонх: QR (секунд тутам шинэчлэгдэнэ) +
|
|
38
|
+
| Desktop хөтөч | Цонх: QR (секунд тутам шинэчлэгдэнэ) + баталгаажуулах код + «Хүсэлтийн хугацаа m:ss» (сүүлийн 30с анхааруулах өнгө) + «Регистрийн дугаараар нэвтрэх» товч (ижил цонхонд доош дэлгэгдэх push хэсэг), шууд төлөв |
|
|
39
39
|
| Утасны хөтөч | Товч → `start?mode=web2app` → сервер 302 → eID апп (Web2App). Апп баталгаажуулаад `/auth/eid/callback` руу буцаана |
|
|
40
40
|
| Backend | session эхлүүлэх, QR угсрах (`sessionSecret` серверт), poll, `validateAuth` (гинж + ACSP_V2 + `flowType` + Web2App-д `userChallengeVerifier`) → таны `onSuccess` |
|
|
41
41
|
|
|
@@ -74,6 +74,8 @@ export const eidLogin = createEidLogin({
|
|
|
74
74
|
// store: redisStore, // олон instance / serverless бол (анхдагч — процессын санах ой)
|
|
75
75
|
// rateLimit: { start: (key) => redisLimiter.allow(key) }, // анхдагчтай нэгтгэгдэнэ (push 3/мин хэвээр) — доор «Хязгаарлалт»
|
|
76
76
|
trustProxy: 1, // handle(Request)-д клиент IP: nginx/LB нэг hop бол 1 (доор «Клиент IP»)
|
|
77
|
+
onEvent: ({ kind, sessionId, meta }) => audit.record(`auth.eid_${kind}`, sessionId, meta), // таны аудит
|
|
78
|
+
// auditKey: process.env.EID_AUDIT_KEY, // pushKey-ийг instance/restart хооронд тогтвортой болгох
|
|
77
79
|
});
|
|
78
80
|
```
|
|
79
81
|
|
|
@@ -197,7 +199,31 @@ RP талд нэмэлт шалгалт хэрэггүй. Эх сурвалж: e
|
|
|
197
199
|
- `OK` нь зөвхөн `validateAuth` амжилттай үед: QR → `flowType=QR`, push → `Notification`, утас → `Web2App` + callback-ийн
|
|
198
200
|
`userChallengeVerifier`. Web2App session-ийг poll дуусгахгүй (`WAIT_CALLBACK`). `onSuccess` нэг session-д нэг л удаа
|
|
199
201
|
(store-ийн `delete` атомар байх ёстой — Redis `DEL`).
|
|
200
|
-
- `onSuccess`-ийн `data` хөтөчид очно — нууц бүү оруул.
|
|
202
|
+
- `onSuccess`-ийн `data` хөтөчид очно — нууц бүү оруул.
|
|
203
|
+
- **Регистрийн oracle хаалт (decoy + доод хугацаа, анхдагчаар асаалттай).** eID push-ийг дугаарын шалтгаанаар татгалзвал
|
|
204
|
+
(eID-ийн өөрийн JSON 4xx — танихгүй / ашиглах боломжгүй дугаар) бодит push-тэй **ижил** хариу: `{mode, vc (санамсаргүй
|
|
205
|
+
5 оронтой — eID-тэй ижил хэлбэр), expiresIn}`, ижил cookie; `status` нь eID-ээс асуулгүй `pollMs`-ийн хугацаанд барьж
|
|
206
|
+
`PENDING`, TTL-ийн дараа `EXPIRED` — утсаа аваагүй иргэнтэй ялгаагүй (бодит push-ийн CA `TIMEOUT` ч `EXPIRED` болно). Push start-ийн хариу бүр (бодит, decoy, 502)
|
|
207
|
+
`pushFloorMs`-оос (анхдагч 1000) хурдан гарахгүй — decoy-г цагаар ялгахгүйн тулд (`handle(Request)`-д хүсэлт тасарвал
|
|
208
|
+
хүлээхгүй; `handleRequest`-д `signal` өг). Хязгаар (`push:<idCode>`, `start:<ip>`) decoy-д ч үйлчилнэ; 429 ба 400
|
|
209
|
+
(`invalid_id` — хэлбэрийн алдаа, бүртгэлээс хамааралгүй) нь floor-гүй. eID-ийн 5xx, холболтын алдаа, JSON бус 4xx (eID-ийн
|
|
210
|
+
өмнөх proxy, ж: mTLS татгалзал), 401/403 (RP-ийн нууц/эрх) ба 429 нь **decoy биш** — `502 eid_unavailable` / `429` (eID
|
|
211
|
+
порталын зан: тасалдлыг decoy-оор нуувал иргэн дуусахгүй spinner харна; эдгээр нь дугаараас хамаардаггүй тул oracle
|
|
212
|
+
биш). Унтраах: `decoy: false`, `pushFloorMs: 0` — **⚠️** тэгвэл `/start` нь «энэ регистр eID-д бүртгэлтэй эсэх»-ийг
|
|
213
|
+
нэвтрээгүй хэнд ч хэлнэ; зөвхөн дотоод/тестийн орчинд.
|
|
214
|
+
- **Гэрчилгээний цуцлалт (OCSP, анхдагчаар асаалттай).** `validateAuth`-ийн дараа, `onSuccess`-ийн өмнө иргэний гэрчилгээг
|
|
215
|
+
түүний AIA-д бичигдсэн OCSP responder-оос асууна (`revocation`, анхдагч `ocspRevocationChecker()`; хариу олгогч эсвэл
|
|
216
|
+
түүний OCSPSigning responder-ийн гарын үсэгтэй, энэ serial-ийн, thisUpdate ≤ 48 цаг, good-ийг 5 мин кэшлэнэ). `revoked`,
|
|
217
|
+
мөн `unknown` (олгогч энэ гэрчилгээг олгоогүй) → `FAILED` / `VALIDATION` (500 биш), `onSuccess` дуудагдахгүй. Хариугүй
|
|
218
|
+
(timeout 2 с, 5xx, AIA алга, шалгалт унасан хариу): `revocationMode` анхдагч `"soft-fail"` — зөвшөөрч `onError`-оор
|
|
219
|
+
анхааруулна, `identity.revocation = "unchecked"` (eID порталын `EID_OCSP=soft`: CA-ийн responder унавал бүх иргэн
|
|
220
|
+
түгжигдэхгүй; утас session-ийг батлахад CA секундын өмнө хүрэх боломжтой байсан); `"hard-fail"` → `FAILED` /
|
|
221
|
+
`VALIDATION`. `revocation: false` — **⚠️** цуцалсан (хулгайлагдсан утасны) гэрчилгээгээр нэвтэрнэ.
|
|
222
|
+
- **Аудит — `onEvent({ kind, sessionId, meta })`** (await хийгдэнэ, алдаа нь `onError` руу): `push_sent` (meta `decoy` —
|
|
223
|
+
зөвхөн таны аудитад, хөтөч рүү хэзээ ч), `push_limited`, `refused` (иргэн татгалзсан), `expired` (TIMEOUT / eID-д
|
|
224
|
+
олдохгүй), `unverified` (`reason`: `validation` | `revoked` | `revocation_unavailable`), `flow_refused` (Web2App callback
|
|
225
|
+
өөр хөтчид), `success` (`flow`, `revocation`), `failed` (бусад `endResult`). meta-д `mode`; токен/нууц, регистрийн дугаар
|
|
226
|
+
орохгүй — push-д `pushKey` = HMAC-SHA256(`auditKey`, дугаар)-ын 32 hex (`auditKey` алга бол процесс бүрт санамсаргүй).
|
|
201
227
|
- Олон instance / serverless (Vercel г.м.) дээр анхдагч санах ойн store ажиллахгүй — `EidLoginStore` (`get`/`set`/`delete`)-ийг Redis-ээр.
|
|
202
228
|
- `onSuccess` дуудагдахаас ӨМНӨ tx store-оос атомар устгагдана (нэг удаагийн баталгаа) — `onSuccess` дотор store-оос бүү
|
|
203
229
|
унш; хэрэгтэй бүхэн `identity`-д (`sessionId`, `documentNumber`, `certificateDerB64`, …).
|
|
@@ -482,6 +508,11 @@ const reg = await eid.registerSignature(registrationFromCompleted(req.body, {
|
|
|
482
508
|
reply({ verifyUrl: reg.verifyUrl });
|
|
483
509
|
```
|
|
484
510
|
|
|
511
|
+
- `reg.created` — `true` 201 (шинэ), `false` 200 (ижил биеийн давталт). Өмнөх гарын үсэг CA-ийн угсарсан (горим A) бол
|
|
512
|
+
`complete().priorRevisionSha256`-ийг (`sha256(doc[0 : ByteRange[2]+ByteRange[3]])`) `registrationFromCompleted`
|
|
513
|
+
`priorSignatures` болгож дамжуулна; `registerSignature` 409 `PRIOR_MISMATCH`-ийн дараа өөрөө revision-оор тааруулна
|
|
514
|
+
(`priorRevisionSha256`-г гараар өгвөл оролдохгүй). Хөтчөөс JSON-оор дамжуулахдаа `priorRevisionSha256`-ийг ч явуул.
|
|
515
|
+
|
|
485
516
|
### Зөвхөн Node (backend бүгдийг хийнэ)
|
|
486
517
|
|
|
487
518
|
```ts
|
|
@@ -626,7 +657,8 @@ OK ирж болзошгүй. Иймд `ResponseValidator`:
|
|
|
626
657
|
хүссэн session-д `ecdsa-with-SHA256` хариу ирвэл хуурамч гэж татгалзана.
|
|
627
658
|
|
|
628
659
|
5. Цуцлалт — `trust.revocation` (OCSP/CRL hook) → `checkRevocation`, эсвэл `checkRevocationStatus` (`"good"` /
|
|
629
|
-
`"unchecked"` (soft-fail) / `"skipped"` (hook алга)).
|
|
660
|
+
`"unchecked"` (soft-fail) / `"skipped"` (hook алга)). Бэлэн hook: `ocspRevocationChecker()` — гэрчилгээний AIA OCSP
|
|
661
|
+
(revoked/unknown → `"revoked"`; хариугүй → `"unknown"`, горим шийднэ). Өөр checker/горимоор нэг удаа: `checkRevocationWith`.
|
|
630
662
|
6. Нэмэлт (сонголттой): `allowedIssuerSkis: [CITIZEN_ISSUING_CA_SKI]` — зөвхөн иргэний Issuing CA (04)-ийн гэрчилгээ;
|
|
631
663
|
`checkKeyUsage: true` — auth → clientAuth, sign → nonRepudiation (CA-ийн профайл); `schemeName` — CA-ийн
|
|
632
664
|
`acsp_scheme_name` (анхдагч `smart-id`).
|
package/dist/button.cjs
CHANGED
|
@@ -397,8 +397,9 @@ var T = {
|
|
|
397
397
|
loading: "\u0410\u0447\u0430\u0430\u043B\u0436 \u0431\u0430\u0439\u043D\u0430",
|
|
398
398
|
qrAlt: "\u041D\u044D\u0432\u0442\u0440\u044D\u0445 QR \u043A\u043E\u0434 \u2014 \u0443\u0442\u0441\u0430\u043D \u0434\u044D\u044D\u0440\u0445 eID Mongolia \u0430\u043F\u043F\u0430\u0430\u0440 \u0443\u043D\u0448\u0443\u0443\u043B\u043D\u0430",
|
|
399
399
|
steps: ["\u0423\u0442\u0441\u0430\u043D \u0434\u044D\u044D\u0440\u0445 eID Mongolia \u0430\u043F\u043F\u044B\u0433 \u043D\u044D\u044D\u043D\u044D", "QR \u043A\u043E\u0434\u044B\u0433 \u0443\u043D\u0448\u0443\u0443\u043B\u043D\u0430", "PIN1 \u043A\u043E\u0434\u043E\u043E \u043E\u0440\u0443\u0443\u043B\u043D\u0430"],
|
|
400
|
-
|
|
400
|
+
vcLabel: "\u0411\u0430\u0442\u0430\u043B\u0433\u0430\u0430\u0436\u0443\u0443\u043B\u0430\u0445 \u043A\u043E\u0434",
|
|
401
401
|
or: "\u044D\u0441\u0432\u044D\u043B",
|
|
402
|
+
usePush: "\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0439\u043D \u0434\u0443\u0433\u0430\u0430\u0440\u0430\u0430\u0440 \u043D\u044D\u0432\u0442\u0440\u044D\u0445",
|
|
402
403
|
idLabel: "\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0439\u043D \u0434\u0443\u0433\u0430\u0430\u0440",
|
|
403
404
|
idHint: "\u0423\u0442\u0441\u0430\u043D\u0434 \u0442\u0430\u043D\u044C \u043C\u044D\u0434\u044D\u0433\u0434\u044D\u043B \u043E\u0447\u043D\u043E. \u0418\u0440\u0433\u044D\u043D\u0438\u0439 12 \u043E\u0440\u043E\u043D\u0442\u043E\u0439 \u0434\u0443\u0433\u0430\u0430\u0440\u0430\u0430\u0440 \u0447 \u0431\u043E\u043B\u043D\u043E.",
|
|
404
405
|
idEmpty: "\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0439\u043D \u0434\u0443\u0433\u0430\u0430\u0440 \u043E\u0440\u0443\u0443\u043B\u043D\u0430 \u0443\u0443",
|
|
@@ -411,7 +412,7 @@ var T = {
|
|
|
411
412
|
waitText: "\u0423\u0442\u0441\u0430\u043D \u0434\u044D\u044D\u0440\u0445 \u043A\u043E\u0434 \u0438\u0436\u0438\u043B \u0431\u043E\u043B PIN1 \u043E\u0440\u0443\u0443\u043B\u043D\u0430 \u0443\u0443. \u0418\u0436\u0438\u043B \u0431\u0438\u0448 \u0431\u043E\u043B \u0442\u0430\u0442\u0433\u0430\u043B\u0437\u0430\u043D\u0430 \u0443\u0443.",
|
|
412
413
|
waiting: "\u0425\u04AF\u043B\u044D\u044D\u0436 \u0431\u0430\u0439\u043D\u0430",
|
|
413
414
|
back: "\u0411\u0443\u0446\u0430\u0445",
|
|
414
|
-
left: "\
|
|
415
|
+
left: "\u0425\u04AF\u0441\u044D\u043B\u0442\u0438\u0439\u043D \u0445\u0443\u0433\u0430\u0446\u0430\u0430:",
|
|
415
416
|
stale: "QR \u043A\u043E\u0434 \u0445\u0443\u0443\u0447\u0438\u0440\u0441\u0430\u043D",
|
|
416
417
|
refresh: "\u0428\u0438\u043D\u044D\u0447\u043B\u044D\u0445",
|
|
417
418
|
offline: "\u0425\u043E\u043B\u0431\u043E\u043B\u0442 \u0442\u0430\u0441\u0430\u0440\u0441\u0430\u043D. \u0418\u043D\u0442\u0435\u0440\u043D\u044D\u0442\u044D\u044D \u0448\u0430\u043B\u0433\u0430\u043D\u0430 \u0443\u0443 \u2014 \u0434\u0430\u0445\u0438\u043D \u0445\u043E\u043B\u0431\u043E\u0433\u0434\u043E\u0445\u044B\u0433 \u043E\u0440\u043E\u043B\u0434\u043E\u0436 \u0431\u0430\u0439\u043D\u0430.",
|
|
@@ -425,7 +426,7 @@ var T = {
|
|
|
425
426
|
retry: "\u0414\u0430\u0445\u0438\u043D \u043E\u0440\u043E\u043B\u0434\u043E\u0445",
|
|
426
427
|
success: "\u0410\u043C\u0436\u0438\u043B\u0442\u0442\u0430\u0439 \u043D\u044D\u0432\u0442\u044D\u0440\u043B\u044D\u044D",
|
|
427
428
|
sent: (vc) => `\u041C\u044D\u0434\u044D\u0433\u0434\u044D\u043B \u0438\u043B\u0433\u044D\u044D\u043B\u044D\u044D. \u0423\u0442\u0441\u0430\u043D \u0434\u044D\u044D\u0440\u0445 \u043A\u043E\u0434 ${vc.split("").join(" ")}`,
|
|
428
|
-
|
|
429
|
+
minutes: (n) => `${n} \u043C\u0438\u043D\u0443\u0442 \u04AF\u043B\u0434\u043B\u044D\u044D`
|
|
429
430
|
},
|
|
430
431
|
en: {
|
|
431
432
|
button: "Log in with eID",
|
|
@@ -434,8 +435,9 @@ var T = {
|
|
|
434
435
|
loading: "Loading",
|
|
435
436
|
qrAlt: "Login QR code \u2014 scan it with the eID Mongolia app on your phone",
|
|
436
437
|
steps: ["Open the eID Mongolia app on your phone", "Scan the QR code", "Enter your PIN1"],
|
|
437
|
-
|
|
438
|
+
vcLabel: "Verification code",
|
|
438
439
|
or: "or",
|
|
440
|
+
usePush: "Log in with registration number",
|
|
439
441
|
idLabel: "Registration number",
|
|
440
442
|
idHint: "A notification will be sent to your phone. The 12-digit civil ID also works.",
|
|
441
443
|
idEmpty: "Enter your registration number",
|
|
@@ -448,7 +450,7 @@ var T = {
|
|
|
448
450
|
waitText: "If the code on your phone matches, enter PIN1. Otherwise decline.",
|
|
449
451
|
waiting: "Waiting",
|
|
450
452
|
back: "Back",
|
|
451
|
-
left: "
|
|
453
|
+
left: "Request expires in:",
|
|
452
454
|
stale: "QR code expired",
|
|
453
455
|
refresh: "Refresh",
|
|
454
456
|
offline: "Connection lost. Check your internet \u2014 reconnecting.",
|
|
@@ -462,7 +464,7 @@ var T = {
|
|
|
462
464
|
retry: "Try again",
|
|
463
465
|
success: "Logged in",
|
|
464
466
|
sent: (vc) => `Notification sent. Code on your phone ${vc.split("").join(" ")}`,
|
|
465
|
-
minute: "
|
|
467
|
+
minutes: (n) => `${n} minute${n === 1 ? "" : "s"} left`
|
|
466
468
|
}
|
|
467
469
|
};
|
|
468
470
|
var EID_COLOR_TOKENS = [
|
|
@@ -528,7 +530,8 @@ h2, p, ol { margin: 0; }
|
|
|
528
530
|
.qr.stale svg { opacity: 0.12; }
|
|
529
531
|
.qr-stale { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px;
|
|
530
532
|
color: hsl(222 47% 11%); font-weight: 500; }
|
|
531
|
-
.qr-vc { margin-top:
|
|
533
|
+
.qr-vc { margin-top: 12px; text-align: center; }
|
|
534
|
+
.vc-label { font-size: 0.8125rem; color: var(--_eid-fg-subtle); }
|
|
532
535
|
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-variant-numeric: tabular-nums; letter-spacing: 0.08em; color: var(--_eid-fg-muted); }
|
|
533
536
|
.steps { margin-top: 16px; padding: 0; list-style: none; counter-reset: st; display: grid; gap: 8px; }
|
|
534
537
|
.steps li { counter-increment: st; display: flex; align-items: center; gap: 12px; }
|
|
@@ -536,6 +539,13 @@ h2, p, ol { margin: 0; }
|
|
|
536
539
|
color: var(--_eid-accent-subtle-fg); display: flex; align-items: center; justify-content: center; font-size: 0.75rem; font-weight: 600; }
|
|
537
540
|
.or { display: flex; align-items: center; gap: 12px; margin: 24px 0; color: var(--_eid-fg-subtle); font-size: 0.8125rem; }
|
|
538
541
|
.or::before, .or::after { content: ""; flex: 1; height: 1px; background: var(--_eid-border); }
|
|
542
|
+
.disc-btn .chev { width: 16px; height: 16px; flex: none; transition: transform 200ms cubic-bezier(0,0,0.2,1); }
|
|
543
|
+
.disc-btn[aria-expanded="true"] .chev { transform: rotate(180deg); }
|
|
544
|
+
.disc { margin: 0 -4px; display: grid; grid-template-rows: 0fr; transition: grid-template-rows 200ms cubic-bezier(0,0,0.2,1); }
|
|
545
|
+
.disc.open { grid-template-rows: 1fr; }
|
|
546
|
+
.disc > div { min-height: 0; overflow: hidden; visibility: hidden; transition: visibility 0s linear 200ms; }
|
|
547
|
+
.disc.open > div { visibility: visible; transition-delay: 0s; }
|
|
548
|
+
.disc form { padding: 16px 4px 4px; }
|
|
539
549
|
.field { display: grid; gap: 8px; }
|
|
540
550
|
.field label { font-weight: 500; }
|
|
541
551
|
.field input { width: 100%; height: 44px; padding: 0 12px; border: 1px solid var(--_eid-border-strong); border-radius: var(--_eid-radius);
|
|
@@ -546,7 +556,8 @@ h2, p, ol { margin: 0; }
|
|
|
546
556
|
.err { font-size: 0.8125rem; color: var(--_eid-danger-fg); font-weight: 500; }
|
|
547
557
|
.push { margin-top: 16px; }
|
|
548
558
|
.center { text-align: center; }
|
|
549
|
-
.vc { font:
|
|
559
|
+
.vc { font: 700 2rem/2.5rem ui-monospace, SFMono-Regular, Menlo, monospace; font-variant-numeric: tabular-nums; letter-spacing: 0.16em;
|
|
560
|
+
padding-left: 0.16em; color: var(--_eid-fg); }
|
|
550
561
|
.vc:focus, h2:focus { outline: none; }
|
|
551
562
|
.text { margin-top: 12px; color: var(--_eid-fg-muted); }
|
|
552
563
|
.pending { margin-top: 16px; display: inline-flex; align-items: center; gap: 8px; color: var(--_eid-fg-muted); }
|
|
@@ -555,7 +566,8 @@ h2, p, ol { margin: 0; }
|
|
|
555
566
|
@keyframes spin { to { transform: rotate(360deg); } }
|
|
556
567
|
.netwarn { margin-top: 16px; padding: 8px 12px; border: 1px solid var(--_eid-warning-border); border-radius: var(--_eid-radius); background: var(--_eid-warning-bg);
|
|
557
568
|
color: var(--_eid-warning-fg); font-size: 0.8125rem; }
|
|
558
|
-
.meta { margin-top:
|
|
569
|
+
.meta { margin-top: 4px; text-align: center; font-size: 0.8125rem; color: var(--_eid-fg-subtle); }
|
|
570
|
+
.meta.low, .meta.low .mono { color: var(--_eid-warning-fg); }
|
|
559
571
|
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { transition: none !important; }
|
|
560
572
|
.spinner { animation: none; border-right-color: currentColor; opacity: 0.6; } }
|
|
561
573
|
`;
|
|
@@ -578,6 +590,24 @@ function el(tag, attrs = {}, text) {
|
|
|
578
590
|
if (text != null) e.textContent = text;
|
|
579
591
|
return e;
|
|
580
592
|
}
|
|
593
|
+
function chevron() {
|
|
594
|
+
const NS = "http://www.w3.org/2000/svg";
|
|
595
|
+
const svg = document.createElementNS(NS, "svg");
|
|
596
|
+
for (const [k, v] of Object.entries({
|
|
597
|
+
class: "chev",
|
|
598
|
+
viewBox: "0 0 24 24",
|
|
599
|
+
fill: "none",
|
|
600
|
+
stroke: "currentColor",
|
|
601
|
+
"stroke-width": "2",
|
|
602
|
+
"stroke-linecap": "round",
|
|
603
|
+
"stroke-linejoin": "round",
|
|
604
|
+
"aria-hidden": "true"
|
|
605
|
+
})) svg.setAttribute(k, v);
|
|
606
|
+
const path = document.createElementNS(NS, "path");
|
|
607
|
+
path.setAttribute("d", "m6 9 6 6 6-6");
|
|
608
|
+
svg.append(path);
|
|
609
|
+
return svg;
|
|
610
|
+
}
|
|
581
611
|
var Base = typeof HTMLElement === "undefined" ? class {
|
|
582
612
|
} : HTMLElement;
|
|
583
613
|
var EidLoginButton = class extends Base {
|
|
@@ -710,7 +740,7 @@ var EidLoginButton = class extends Base {
|
|
|
710
740
|
return;
|
|
711
741
|
}
|
|
712
742
|
if (e.key !== "Tab" || !this.backdrop) return;
|
|
713
|
-
const items = Array.from(this.backdrop.querySelectorAll(FOCUSABLE)).filter((n) => !n.closest("[hidden]"));
|
|
743
|
+
const items = Array.from(this.backdrop.querySelectorAll(FOCUSABLE)).filter((n) => !n.closest("[hidden], [inert]"));
|
|
714
744
|
if (items.length === 0) return;
|
|
715
745
|
const first = items[0];
|
|
716
746
|
const last = items[items.length - 1];
|
|
@@ -792,21 +822,22 @@ var EidLoginButton = class extends Base {
|
|
|
792
822
|
if (s.qr) this.drawQr(s.qr);
|
|
793
823
|
const nodes = [this.qrBox];
|
|
794
824
|
if (s.vc) {
|
|
795
|
-
const vc = el("
|
|
796
|
-
vc.append(el("
|
|
825
|
+
const vc = el("div", { class: "qr-vc" });
|
|
826
|
+
vc.append(el("p", { class: "vc-label", id: "eid-vc-label" }, t.vcLabel), el("p", { class: "vc" }, s.vc));
|
|
797
827
|
nodes.push(vc);
|
|
798
828
|
}
|
|
829
|
+
nodes.push(this.meta());
|
|
799
830
|
const steps = el("ol", { class: "steps" });
|
|
800
831
|
for (const st of t.steps) steps.append(el("li", {}, st));
|
|
801
|
-
nodes.push(steps, el("div", { class: "or", role: "separator", "aria-label": t.or }, t.or), this.
|
|
832
|
+
nodes.push(steps, el("div", { class: "or", role: "separator", "aria-label": t.or }, t.or), ...this.pushToggle());
|
|
802
833
|
this.netwarn = el("p", { class: "netwarn", role: "status", hidden: "" }, t.offline);
|
|
803
|
-
nodes.push(this.netwarn
|
|
834
|
+
nodes.push(this.netwarn);
|
|
804
835
|
this.view(...nodes);
|
|
805
836
|
}
|
|
806
837
|
leftEl = null;
|
|
807
838
|
meta() {
|
|
808
839
|
const p = el("p", { class: "meta" }, this.t.left + " ");
|
|
809
|
-
this.leftEl = el("span", { class: "mono", role: "timer", "aria-live": "off" }, "
|
|
840
|
+
this.leftEl = el("span", { class: "mono", role: "timer", "aria-live": "off" }, "-:--");
|
|
810
841
|
p.append(this.leftEl);
|
|
811
842
|
if (this.left < 0) p.hidden = true;
|
|
812
843
|
return p;
|
|
@@ -819,8 +850,9 @@ var EidLoginButton = class extends Base {
|
|
|
819
850
|
this.leftEl.parentElement.hidden = false;
|
|
820
851
|
const m = Math.floor(this.left / 60);
|
|
821
852
|
const s = this.left % 60;
|
|
822
|
-
this.leftEl.textContent = `${m
|
|
823
|
-
|
|
853
|
+
this.leftEl.textContent = `${m}:${s < 10 ? "0" : ""}${s}`;
|
|
854
|
+
this.leftEl.parentElement.classList.toggle("low", this.left <= 30);
|
|
855
|
+
if (this.left > 0 && s === 0 && this.left !== seconds) this.say(this.t.minutes(m));
|
|
824
856
|
};
|
|
825
857
|
render();
|
|
826
858
|
if (seconds < 0) return;
|
|
@@ -830,6 +862,24 @@ var EidLoginButton = class extends Base {
|
|
|
830
862
|
if (this.left <= 0) this.end("expired");
|
|
831
863
|
}, 1e3);
|
|
832
864
|
}
|
|
865
|
+
/** «Регистрийн дугаараар нэвтрэх» товч + ижил цонхонд доошоо дэлгэгдэх push хэсэг (QR, код харагдсаар; QR poll үргэлжилнэ). */
|
|
866
|
+
pushToggle() {
|
|
867
|
+
const toggle = el("button", { type: "button", class: "btn btn-outline disc-btn", "aria-expanded": "false", "aria-controls": "eid-push" });
|
|
868
|
+
toggle.append(el("span", {}, this.t.usePush), chevron());
|
|
869
|
+
const disc = el("div", { class: "disc", id: "eid-push" });
|
|
870
|
+
const inner = el("div", { inert: "" });
|
|
871
|
+
const form = this.pushForm();
|
|
872
|
+
inner.append(form);
|
|
873
|
+
disc.append(inner);
|
|
874
|
+
toggle.addEventListener("click", () => {
|
|
875
|
+
const open = toggle.getAttribute("aria-expanded") !== "true";
|
|
876
|
+
toggle.setAttribute("aria-expanded", String(open));
|
|
877
|
+
disc.classList.toggle("open", open);
|
|
878
|
+
inner.toggleAttribute("inert", !open);
|
|
879
|
+
if (open) form.querySelector("input")?.focus();
|
|
880
|
+
});
|
|
881
|
+
return [toggle, disc];
|
|
882
|
+
}
|
|
833
883
|
pushForm() {
|
|
834
884
|
const t = this.t;
|
|
835
885
|
const form = el("form", { novalidate: "" });
|
|
@@ -898,14 +948,14 @@ var EidLoginButton = class extends Base {
|
|
|
898
948
|
showWait(s) {
|
|
899
949
|
const t = this.t;
|
|
900
950
|
const c = el("div", { class: "center" });
|
|
901
|
-
c.append(el("h2", { class: "sr-only" }, t.waiting));
|
|
902
|
-
const vc = el("p", { class: "vc", tabindex: "-1", "aria-describedby": "eid-wait" }, s.vc ?? "");
|
|
951
|
+
c.append(el("h2", { class: "sr-only" }, t.waiting), el("p", { class: "vc-label", id: "eid-vc-label" }, t.vcLabel));
|
|
952
|
+
const vc = el("p", { class: "vc", tabindex: "-1", "aria-labelledby": "eid-vc-label", "aria-describedby": "eid-wait" }, s.vc ?? "");
|
|
903
953
|
const pending = el("p", { class: "pending" });
|
|
904
954
|
pending.append(el("span", { class: "spinner", "aria-hidden": "true" }), el("span", {}, t.waiting));
|
|
905
955
|
const back = el("button", { type: "button", class: "btn btn-outline" }, t.back);
|
|
906
956
|
back.addEventListener("click", () => this.open());
|
|
907
957
|
this.netwarn = el("p", { class: "netwarn", role: "status", hidden: "" }, t.offline);
|
|
908
|
-
c.append(vc, el("p", { class: "text", id: "eid-wait" }, t.waitText), pending, back, this.netwarn
|
|
958
|
+
c.append(vc, this.meta(), el("p", { class: "text", id: "eid-wait" }, t.waitText), pending, back, this.netwarn);
|
|
909
959
|
this.view(c);
|
|
910
960
|
this.startTimer(s.expiresIn ?? -1);
|
|
911
961
|
vc.focus();
|
package/dist/button.d.cts
CHANGED
|
@@ -78,6 +78,8 @@ declare class EidLoginButton extends Base {
|
|
|
78
78
|
private leftEl;
|
|
79
79
|
private meta;
|
|
80
80
|
private startTimer;
|
|
81
|
+
/** «Регистрийн дугаараар нэвтрэх» товч + ижил цонхонд доошоо дэлгэгдэх push хэсэг (QR, код харагдсаар; QR poll үргэлжилнэ). */
|
|
82
|
+
private pushToggle;
|
|
81
83
|
private pushForm;
|
|
82
84
|
private showWait;
|
|
83
85
|
private schedule;
|
package/dist/button.d.ts
CHANGED
|
@@ -78,6 +78,8 @@ declare class EidLoginButton extends Base {
|
|
|
78
78
|
private leftEl;
|
|
79
79
|
private meta;
|
|
80
80
|
private startTimer;
|
|
81
|
+
/** «Регистрийн дугаараар нэвтрэх» товч + ижил цонхонд доошоо дэлгэгдэх push хэсэг (QR, код харагдсаар; QR poll үргэлжилнэ). */
|
|
82
|
+
private pushToggle;
|
|
81
83
|
private pushForm;
|
|
82
84
|
private showWait;
|
|
83
85
|
private schedule;
|
package/dist/button.js
CHANGED
|
@@ -368,8 +368,9 @@ var T = {
|
|
|
368
368
|
loading: "\u0410\u0447\u0430\u0430\u043B\u0436 \u0431\u0430\u0439\u043D\u0430",
|
|
369
369
|
qrAlt: "\u041D\u044D\u0432\u0442\u0440\u044D\u0445 QR \u043A\u043E\u0434 \u2014 \u0443\u0442\u0441\u0430\u043D \u0434\u044D\u044D\u0440\u0445 eID Mongolia \u0430\u043F\u043F\u0430\u0430\u0440 \u0443\u043D\u0448\u0443\u0443\u043B\u043D\u0430",
|
|
370
370
|
steps: ["\u0423\u0442\u0441\u0430\u043D \u0434\u044D\u044D\u0440\u0445 eID Mongolia \u0430\u043F\u043F\u044B\u0433 \u043D\u044D\u044D\u043D\u044D", "QR \u043A\u043E\u0434\u044B\u0433 \u0443\u043D\u0448\u0443\u0443\u043B\u043D\u0430", "PIN1 \u043A\u043E\u0434\u043E\u043E \u043E\u0440\u0443\u0443\u043B\u043D\u0430"],
|
|
371
|
-
|
|
371
|
+
vcLabel: "\u0411\u0430\u0442\u0430\u043B\u0433\u0430\u0430\u0436\u0443\u0443\u043B\u0430\u0445 \u043A\u043E\u0434",
|
|
372
372
|
or: "\u044D\u0441\u0432\u044D\u043B",
|
|
373
|
+
usePush: "\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0439\u043D \u0434\u0443\u0433\u0430\u0430\u0440\u0430\u0430\u0440 \u043D\u044D\u0432\u0442\u0440\u044D\u0445",
|
|
373
374
|
idLabel: "\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0439\u043D \u0434\u0443\u0433\u0430\u0430\u0440",
|
|
374
375
|
idHint: "\u0423\u0442\u0441\u0430\u043D\u0434 \u0442\u0430\u043D\u044C \u043C\u044D\u0434\u044D\u0433\u0434\u044D\u043B \u043E\u0447\u043D\u043E. \u0418\u0440\u0433\u044D\u043D\u0438\u0439 12 \u043E\u0440\u043E\u043D\u0442\u043E\u0439 \u0434\u0443\u0433\u0430\u0430\u0440\u0430\u0430\u0440 \u0447 \u0431\u043E\u043B\u043D\u043E.",
|
|
375
376
|
idEmpty: "\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0439\u043D \u0434\u0443\u0433\u0430\u0430\u0440 \u043E\u0440\u0443\u0443\u043B\u043D\u0430 \u0443\u0443",
|
|
@@ -382,7 +383,7 @@ var T = {
|
|
|
382
383
|
waitText: "\u0423\u0442\u0441\u0430\u043D \u0434\u044D\u044D\u0440\u0445 \u043A\u043E\u0434 \u0438\u0436\u0438\u043B \u0431\u043E\u043B PIN1 \u043E\u0440\u0443\u0443\u043B\u043D\u0430 \u0443\u0443. \u0418\u0436\u0438\u043B \u0431\u0438\u0448 \u0431\u043E\u043B \u0442\u0430\u0442\u0433\u0430\u043B\u0437\u0430\u043D\u0430 \u0443\u0443.",
|
|
383
384
|
waiting: "\u0425\u04AF\u043B\u044D\u044D\u0436 \u0431\u0430\u0439\u043D\u0430",
|
|
384
385
|
back: "\u0411\u0443\u0446\u0430\u0445",
|
|
385
|
-
left: "\
|
|
386
|
+
left: "\u0425\u04AF\u0441\u044D\u043B\u0442\u0438\u0439\u043D \u0445\u0443\u0433\u0430\u0446\u0430\u0430:",
|
|
386
387
|
stale: "QR \u043A\u043E\u0434 \u0445\u0443\u0443\u0447\u0438\u0440\u0441\u0430\u043D",
|
|
387
388
|
refresh: "\u0428\u0438\u043D\u044D\u0447\u043B\u044D\u0445",
|
|
388
389
|
offline: "\u0425\u043E\u043B\u0431\u043E\u043B\u0442 \u0442\u0430\u0441\u0430\u0440\u0441\u0430\u043D. \u0418\u043D\u0442\u0435\u0440\u043D\u044D\u0442\u044D\u044D \u0448\u0430\u043B\u0433\u0430\u043D\u0430 \u0443\u0443 \u2014 \u0434\u0430\u0445\u0438\u043D \u0445\u043E\u043B\u0431\u043E\u0433\u0434\u043E\u0445\u044B\u0433 \u043E\u0440\u043E\u043B\u0434\u043E\u0436 \u0431\u0430\u0439\u043D\u0430.",
|
|
@@ -396,7 +397,7 @@ var T = {
|
|
|
396
397
|
retry: "\u0414\u0430\u0445\u0438\u043D \u043E\u0440\u043E\u043B\u0434\u043E\u0445",
|
|
397
398
|
success: "\u0410\u043C\u0436\u0438\u043B\u0442\u0442\u0430\u0439 \u043D\u044D\u0432\u0442\u044D\u0440\u043B\u044D\u044D",
|
|
398
399
|
sent: (vc) => `\u041C\u044D\u0434\u044D\u0433\u0434\u044D\u043B \u0438\u043B\u0433\u044D\u044D\u043B\u044D\u044D. \u0423\u0442\u0441\u0430\u043D \u0434\u044D\u044D\u0440\u0445 \u043A\u043E\u0434 ${vc.split("").join(" ")}`,
|
|
399
|
-
|
|
400
|
+
minutes: (n) => `${n} \u043C\u0438\u043D\u0443\u0442 \u04AF\u043B\u0434\u043B\u044D\u044D`
|
|
400
401
|
},
|
|
401
402
|
en: {
|
|
402
403
|
button: "Log in with eID",
|
|
@@ -405,8 +406,9 @@ var T = {
|
|
|
405
406
|
loading: "Loading",
|
|
406
407
|
qrAlt: "Login QR code \u2014 scan it with the eID Mongolia app on your phone",
|
|
407
408
|
steps: ["Open the eID Mongolia app on your phone", "Scan the QR code", "Enter your PIN1"],
|
|
408
|
-
|
|
409
|
+
vcLabel: "Verification code",
|
|
409
410
|
or: "or",
|
|
411
|
+
usePush: "Log in with registration number",
|
|
410
412
|
idLabel: "Registration number",
|
|
411
413
|
idHint: "A notification will be sent to your phone. The 12-digit civil ID also works.",
|
|
412
414
|
idEmpty: "Enter your registration number",
|
|
@@ -419,7 +421,7 @@ var T = {
|
|
|
419
421
|
waitText: "If the code on your phone matches, enter PIN1. Otherwise decline.",
|
|
420
422
|
waiting: "Waiting",
|
|
421
423
|
back: "Back",
|
|
422
|
-
left: "
|
|
424
|
+
left: "Request expires in:",
|
|
423
425
|
stale: "QR code expired",
|
|
424
426
|
refresh: "Refresh",
|
|
425
427
|
offline: "Connection lost. Check your internet \u2014 reconnecting.",
|
|
@@ -433,7 +435,7 @@ var T = {
|
|
|
433
435
|
retry: "Try again",
|
|
434
436
|
success: "Logged in",
|
|
435
437
|
sent: (vc) => `Notification sent. Code on your phone ${vc.split("").join(" ")}`,
|
|
436
|
-
minute: "
|
|
438
|
+
minutes: (n) => `${n} minute${n === 1 ? "" : "s"} left`
|
|
437
439
|
}
|
|
438
440
|
};
|
|
439
441
|
var EID_COLOR_TOKENS = [
|
|
@@ -499,7 +501,8 @@ h2, p, ol { margin: 0; }
|
|
|
499
501
|
.qr.stale svg { opacity: 0.12; }
|
|
500
502
|
.qr-stale { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px;
|
|
501
503
|
color: hsl(222 47% 11%); font-weight: 500; }
|
|
502
|
-
.qr-vc { margin-top:
|
|
504
|
+
.qr-vc { margin-top: 12px; text-align: center; }
|
|
505
|
+
.vc-label { font-size: 0.8125rem; color: var(--_eid-fg-subtle); }
|
|
503
506
|
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-variant-numeric: tabular-nums; letter-spacing: 0.08em; color: var(--_eid-fg-muted); }
|
|
504
507
|
.steps { margin-top: 16px; padding: 0; list-style: none; counter-reset: st; display: grid; gap: 8px; }
|
|
505
508
|
.steps li { counter-increment: st; display: flex; align-items: center; gap: 12px; }
|
|
@@ -507,6 +510,13 @@ h2, p, ol { margin: 0; }
|
|
|
507
510
|
color: var(--_eid-accent-subtle-fg); display: flex; align-items: center; justify-content: center; font-size: 0.75rem; font-weight: 600; }
|
|
508
511
|
.or { display: flex; align-items: center; gap: 12px; margin: 24px 0; color: var(--_eid-fg-subtle); font-size: 0.8125rem; }
|
|
509
512
|
.or::before, .or::after { content: ""; flex: 1; height: 1px; background: var(--_eid-border); }
|
|
513
|
+
.disc-btn .chev { width: 16px; height: 16px; flex: none; transition: transform 200ms cubic-bezier(0,0,0.2,1); }
|
|
514
|
+
.disc-btn[aria-expanded="true"] .chev { transform: rotate(180deg); }
|
|
515
|
+
.disc { margin: 0 -4px; display: grid; grid-template-rows: 0fr; transition: grid-template-rows 200ms cubic-bezier(0,0,0.2,1); }
|
|
516
|
+
.disc.open { grid-template-rows: 1fr; }
|
|
517
|
+
.disc > div { min-height: 0; overflow: hidden; visibility: hidden; transition: visibility 0s linear 200ms; }
|
|
518
|
+
.disc.open > div { visibility: visible; transition-delay: 0s; }
|
|
519
|
+
.disc form { padding: 16px 4px 4px; }
|
|
510
520
|
.field { display: grid; gap: 8px; }
|
|
511
521
|
.field label { font-weight: 500; }
|
|
512
522
|
.field input { width: 100%; height: 44px; padding: 0 12px; border: 1px solid var(--_eid-border-strong); border-radius: var(--_eid-radius);
|
|
@@ -517,7 +527,8 @@ h2, p, ol { margin: 0; }
|
|
|
517
527
|
.err { font-size: 0.8125rem; color: var(--_eid-danger-fg); font-weight: 500; }
|
|
518
528
|
.push { margin-top: 16px; }
|
|
519
529
|
.center { text-align: center; }
|
|
520
|
-
.vc { font:
|
|
530
|
+
.vc { font: 700 2rem/2.5rem ui-monospace, SFMono-Regular, Menlo, monospace; font-variant-numeric: tabular-nums; letter-spacing: 0.16em;
|
|
531
|
+
padding-left: 0.16em; color: var(--_eid-fg); }
|
|
521
532
|
.vc:focus, h2:focus { outline: none; }
|
|
522
533
|
.text { margin-top: 12px; color: var(--_eid-fg-muted); }
|
|
523
534
|
.pending { margin-top: 16px; display: inline-flex; align-items: center; gap: 8px; color: var(--_eid-fg-muted); }
|
|
@@ -526,7 +537,8 @@ h2, p, ol { margin: 0; }
|
|
|
526
537
|
@keyframes spin { to { transform: rotate(360deg); } }
|
|
527
538
|
.netwarn { margin-top: 16px; padding: 8px 12px; border: 1px solid var(--_eid-warning-border); border-radius: var(--_eid-radius); background: var(--_eid-warning-bg);
|
|
528
539
|
color: var(--_eid-warning-fg); font-size: 0.8125rem; }
|
|
529
|
-
.meta { margin-top:
|
|
540
|
+
.meta { margin-top: 4px; text-align: center; font-size: 0.8125rem; color: var(--_eid-fg-subtle); }
|
|
541
|
+
.meta.low, .meta.low .mono { color: var(--_eid-warning-fg); }
|
|
530
542
|
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { transition: none !important; }
|
|
531
543
|
.spinner { animation: none; border-right-color: currentColor; opacity: 0.6; } }
|
|
532
544
|
`;
|
|
@@ -549,6 +561,24 @@ function el(tag, attrs = {}, text) {
|
|
|
549
561
|
if (text != null) e.textContent = text;
|
|
550
562
|
return e;
|
|
551
563
|
}
|
|
564
|
+
function chevron() {
|
|
565
|
+
const NS = "http://www.w3.org/2000/svg";
|
|
566
|
+
const svg = document.createElementNS(NS, "svg");
|
|
567
|
+
for (const [k, v] of Object.entries({
|
|
568
|
+
class: "chev",
|
|
569
|
+
viewBox: "0 0 24 24",
|
|
570
|
+
fill: "none",
|
|
571
|
+
stroke: "currentColor",
|
|
572
|
+
"stroke-width": "2",
|
|
573
|
+
"stroke-linecap": "round",
|
|
574
|
+
"stroke-linejoin": "round",
|
|
575
|
+
"aria-hidden": "true"
|
|
576
|
+
})) svg.setAttribute(k, v);
|
|
577
|
+
const path = document.createElementNS(NS, "path");
|
|
578
|
+
path.setAttribute("d", "m6 9 6 6 6-6");
|
|
579
|
+
svg.append(path);
|
|
580
|
+
return svg;
|
|
581
|
+
}
|
|
552
582
|
var Base = typeof HTMLElement === "undefined" ? class {
|
|
553
583
|
} : HTMLElement;
|
|
554
584
|
var EidLoginButton = class extends Base {
|
|
@@ -681,7 +711,7 @@ var EidLoginButton = class extends Base {
|
|
|
681
711
|
return;
|
|
682
712
|
}
|
|
683
713
|
if (e.key !== "Tab" || !this.backdrop) return;
|
|
684
|
-
const items = Array.from(this.backdrop.querySelectorAll(FOCUSABLE)).filter((n) => !n.closest("[hidden]"));
|
|
714
|
+
const items = Array.from(this.backdrop.querySelectorAll(FOCUSABLE)).filter((n) => !n.closest("[hidden], [inert]"));
|
|
685
715
|
if (items.length === 0) return;
|
|
686
716
|
const first = items[0];
|
|
687
717
|
const last = items[items.length - 1];
|
|
@@ -763,21 +793,22 @@ var EidLoginButton = class extends Base {
|
|
|
763
793
|
if (s.qr) this.drawQr(s.qr);
|
|
764
794
|
const nodes = [this.qrBox];
|
|
765
795
|
if (s.vc) {
|
|
766
|
-
const vc = el("
|
|
767
|
-
vc.append(el("
|
|
796
|
+
const vc = el("div", { class: "qr-vc" });
|
|
797
|
+
vc.append(el("p", { class: "vc-label", id: "eid-vc-label" }, t.vcLabel), el("p", { class: "vc" }, s.vc));
|
|
768
798
|
nodes.push(vc);
|
|
769
799
|
}
|
|
800
|
+
nodes.push(this.meta());
|
|
770
801
|
const steps = el("ol", { class: "steps" });
|
|
771
802
|
for (const st of t.steps) steps.append(el("li", {}, st));
|
|
772
|
-
nodes.push(steps, el("div", { class: "or", role: "separator", "aria-label": t.or }, t.or), this.
|
|
803
|
+
nodes.push(steps, el("div", { class: "or", role: "separator", "aria-label": t.or }, t.or), ...this.pushToggle());
|
|
773
804
|
this.netwarn = el("p", { class: "netwarn", role: "status", hidden: "" }, t.offline);
|
|
774
|
-
nodes.push(this.netwarn
|
|
805
|
+
nodes.push(this.netwarn);
|
|
775
806
|
this.view(...nodes);
|
|
776
807
|
}
|
|
777
808
|
leftEl = null;
|
|
778
809
|
meta() {
|
|
779
810
|
const p = el("p", { class: "meta" }, this.t.left + " ");
|
|
780
|
-
this.leftEl = el("span", { class: "mono", role: "timer", "aria-live": "off" }, "
|
|
811
|
+
this.leftEl = el("span", { class: "mono", role: "timer", "aria-live": "off" }, "-:--");
|
|
781
812
|
p.append(this.leftEl);
|
|
782
813
|
if (this.left < 0) p.hidden = true;
|
|
783
814
|
return p;
|
|
@@ -790,8 +821,9 @@ var EidLoginButton = class extends Base {
|
|
|
790
821
|
this.leftEl.parentElement.hidden = false;
|
|
791
822
|
const m = Math.floor(this.left / 60);
|
|
792
823
|
const s = this.left % 60;
|
|
793
|
-
this.leftEl.textContent = `${m
|
|
794
|
-
|
|
824
|
+
this.leftEl.textContent = `${m}:${s < 10 ? "0" : ""}${s}`;
|
|
825
|
+
this.leftEl.parentElement.classList.toggle("low", this.left <= 30);
|
|
826
|
+
if (this.left > 0 && s === 0 && this.left !== seconds) this.say(this.t.minutes(m));
|
|
795
827
|
};
|
|
796
828
|
render();
|
|
797
829
|
if (seconds < 0) return;
|
|
@@ -801,6 +833,24 @@ var EidLoginButton = class extends Base {
|
|
|
801
833
|
if (this.left <= 0) this.end("expired");
|
|
802
834
|
}, 1e3);
|
|
803
835
|
}
|
|
836
|
+
/** «Регистрийн дугаараар нэвтрэх» товч + ижил цонхонд доошоо дэлгэгдэх push хэсэг (QR, код харагдсаар; QR poll үргэлжилнэ). */
|
|
837
|
+
pushToggle() {
|
|
838
|
+
const toggle = el("button", { type: "button", class: "btn btn-outline disc-btn", "aria-expanded": "false", "aria-controls": "eid-push" });
|
|
839
|
+
toggle.append(el("span", {}, this.t.usePush), chevron());
|
|
840
|
+
const disc = el("div", { class: "disc", id: "eid-push" });
|
|
841
|
+
const inner = el("div", { inert: "" });
|
|
842
|
+
const form = this.pushForm();
|
|
843
|
+
inner.append(form);
|
|
844
|
+
disc.append(inner);
|
|
845
|
+
toggle.addEventListener("click", () => {
|
|
846
|
+
const open = toggle.getAttribute("aria-expanded") !== "true";
|
|
847
|
+
toggle.setAttribute("aria-expanded", String(open));
|
|
848
|
+
disc.classList.toggle("open", open);
|
|
849
|
+
inner.toggleAttribute("inert", !open);
|
|
850
|
+
if (open) form.querySelector("input")?.focus();
|
|
851
|
+
});
|
|
852
|
+
return [toggle, disc];
|
|
853
|
+
}
|
|
804
854
|
pushForm() {
|
|
805
855
|
const t = this.t;
|
|
806
856
|
const form = el("form", { novalidate: "" });
|
|
@@ -869,14 +919,14 @@ var EidLoginButton = class extends Base {
|
|
|
869
919
|
showWait(s) {
|
|
870
920
|
const t = this.t;
|
|
871
921
|
const c = el("div", { class: "center" });
|
|
872
|
-
c.append(el("h2", { class: "sr-only" }, t.waiting));
|
|
873
|
-
const vc = el("p", { class: "vc", tabindex: "-1", "aria-describedby": "eid-wait" }, s.vc ?? "");
|
|
922
|
+
c.append(el("h2", { class: "sr-only" }, t.waiting), el("p", { class: "vc-label", id: "eid-vc-label" }, t.vcLabel));
|
|
923
|
+
const vc = el("p", { class: "vc", tabindex: "-1", "aria-labelledby": "eid-vc-label", "aria-describedby": "eid-wait" }, s.vc ?? "");
|
|
874
924
|
const pending = el("p", { class: "pending" });
|
|
875
925
|
pending.append(el("span", { class: "spinner", "aria-hidden": "true" }), el("span", {}, t.waiting));
|
|
876
926
|
const back = el("button", { type: "button", class: "btn btn-outline" }, t.back);
|
|
877
927
|
back.addEventListener("click", () => this.open());
|
|
878
928
|
this.netwarn = el("p", { class: "netwarn", role: "status", hidden: "" }, t.offline);
|
|
879
|
-
c.append(vc, el("p", { class: "text", id: "eid-wait" }, t.waitText), pending, back, this.netwarn
|
|
929
|
+
c.append(vc, this.meta(), el("p", { class: "text", id: "eid-wait" }, t.waitText), pending, back, this.netwarn);
|
|
880
930
|
this.view(c);
|
|
881
931
|
this.startTimer(s.expiresIn ?? -1);
|
|
882
932
|
vc.focus();
|