| import { useI18n } from '../i18n' |
| import type { MessageKey } from '../i18n/en' |
| import { isWebAuthnSupported } from '../auth/passkey' |
| import { Callout } from './ui' |
|
|
| type T = (key: MessageKey, vars?: Record<string, string | number>) => string |
|
|
| |
| export function inEmbeddedFrame(): boolean { |
| try { |
| return window.self !== window.top |
| } catch { |
| return true |
| } |
| } |
|
|
| export function statusLabel(t: T, status: string): string { |
| const key = `d2.status.${status}` as MessageKey |
| const label = t(key) |
| return label === key ? status : label |
| } |
|
|
| export function roleLabel(t: T, role: string): string { |
| const key = `d2.role.${role}` as MessageKey |
| const label = t(key) |
| return label === key ? role : label |
| } |
|
|
| |
| export function PasskeyEnvWarnings() { |
| const { t } = useI18n() |
| return ( |
| <> |
| {!isWebAuthnSupported() && <Callout tone="warn">{t('d2.unsupported')}</Callout>} |
| {inEmbeddedFrame() && <Callout tone="warn">{t('d2.iframeWarn')}</Callout>} |
| </> |
| ) |
| } |
|
|
| |
| |
| |
| |
| |
| export function OpenInNewTabBanner() { |
| const { t } = useI18n() |
| if (!inEmbeddedFrame()) return null |
| const href = typeof window !== 'undefined' ? window.location.href : '#' |
| return ( |
| <div className="callout tone-warn" role="note"> |
| <strong>{t('d2.openNewTab.title')}</strong> |
| <p className="hint">{t('d2.openNewTab.body')}</p> |
| <a className="btn" href={href} target="_blank" rel="noopener noreferrer"> |
| {t('d2.openNewTab.button')} ↗ |
| </a> |
| </div> |
| ) |
| } |
|
|
| |
| export function ConfirmDialog({ |
| message, confirmLabel, danger = true, busy = false, onConfirm, onCancel, |
| }: { |
| message: string |
| confirmLabel: string |
| danger?: boolean |
| busy?: boolean |
| onConfirm: () => void |
| onCancel: () => void |
| }) { |
| const { t } = useI18n() |
| return ( |
| <div className="d2-modal-backdrop" onClick={onCancel}> |
| <div |
| className="d2-modal card" |
| role="alertdialog" |
| aria-modal="true" |
| aria-label={message} |
| onClick={(e) => e.stopPropagation()} |
| onKeyDown={(e) => { |
| if (e.key === 'Escape') onCancel() |
| }} |
| > |
| <p>{message}</p> |
| <div className="row"> |
| <button type="button" className="btn" autoFocus onClick={onCancel} disabled={busy}> |
| {t('d2.cancel')} |
| </button> |
| <button |
| type="button" |
| className={danger ? 'btn danger' : 'btn'} |
| onClick={onConfirm} |
| disabled={busy} |
| > |
| {confirmLabel} |
| </button> |
| </div> |
| </div> |
| </div> |
| ) |
| } |
|
|