diff --git a/frontend/src/components/AdminPanel.jsx b/frontend/src/components/AdminPanel.jsx index 866f919..b0b58c6 100644 --- a/frontend/src/components/AdminPanel.jsx +++ b/frontend/src/components/AdminPanel.jsx @@ -24,6 +24,7 @@ import { removeManager } from '../api/managersApi.js' import { t } from '../i18n/index.js' import { readGedcomFile } from '../utils/gedcomText.js' import { readGedcomBundle, zipErrorMessage } from '../utils/gedcomBundle.js' +import Skeleton from './Skeleton.jsx' function normalizeSlug(value) { return String(value || '') @@ -442,7 +443,7 @@ const AdminPanel = ({
{t('Chargement…')}
+{notice}
+ // Premier chargement : la place de la frise, au lieu d'un « pas encore de date » qui serait faux une seconde après + if (loading && !life) return{t('Où chercher')}
- {sources === null &&{t('Chargement…')}
} + {sources === null &&{t("Ce serveur n'interroge aucune source : aucun nom ne quitte l'instance. Les liens plus bas ouvrent les sites dans votre navigateur.")}
)} diff --git a/frontend/src/components/Skeleton.css b/frontend/src/components/Skeleton.css new file mode 100644 index 0000000..c9bd481 --- /dev/null +++ b/frontend/src/components/Skeleton.css @@ -0,0 +1,14 @@ +/* Blocs gris de chargement (Skeleton.jsx) : immobiles, couleur des séparateurs */ +.pz-skel { display: grid; gap: 14px; padding: 4px 0; } +.pz-skel-row { display: flex; align-items: center; gap: 12px; } +.pz-skel-text { display: grid; gap: 7px; flex: 1; min-width: 0; } +.pz-skel-bar { display: block; height: 12px; border-radius: 6px; background: var(--pz-line); } +.pz-skel-bar--thin { height: 9px; background: var(--pz-line-soft); } +.pz-skel-dot { flex: none; width: 32px; height: 32px; border-radius: 50%; background: var(--pz-line); } + +/* Frise : année, point sur le rail, texte */ +.pz-skel--timeline { gap: 22px; padding: 8px 0 4px; } +.pz-skel--timeline .pz-skel-year { flex: none; width: 34px; height: 11px; } +.pz-skel--timeline .pz-skel-dot { width: 10px; height: 10px; } + +.pz-skel--lines .pz-skel-bar { height: 10px; } diff --git a/frontend/src/components/Skeleton.jsx b/frontend/src/components/Skeleton.jsx new file mode 100644 index 0000000..5d4008b --- /dev/null +++ b/frontend/src/components/Skeleton.jsx @@ -0,0 +1,34 @@ +import { useEffect, useState } from 'react' +import { t } from '../i18n/index.js' +import './Skeleton.css' + +// Blocs gris à la forme de ce qui arrive, à la place d'un « Chargement… ». Rien avant SHOW_AFTER : une réponse +// rapide ne fait pas clignoter l'écran. Aucun mouvement (pas de reflet qui passe), juste la place réservée. +const SHOW_AFTER = 300 + +// variant : rows (avatar et deux lignes : personnes, visites, propositions), timeline (frise de l'onglet Vie), +// lines (paragraphes) +export default function Skeleton({ variant = 'rows', count = 3, label = '' }) { + const [shown, setShown] = useState(false) + useEffect(() => { + const timer = setTimeout(() => setShown(true), SHOW_AFTER) + return () => clearTimeout(timer) + }, []) + + return ( +{t('Chargement…')}
+ if (state.loading) return{t('Chargement…')}
+ if (state.loading) return