diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 571ec68..2f315c6 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -585,9 +585,9 @@ function App() { const [familyTabs, setFamilyTabsState] = useState(() => { try { return localStorage.getItem('parenthese.familyTabs') !== 'off' } catch { return true } }) const setFamilyTabs = useCallback((on) => { setFamilyTabsState(on); try { localStorage.setItem('parenthese.familyTabs', on ? 'on' : 'off') } catch { /* navigateur sans stockage */ } }, []) // Nom de famille sous le prénom dans l'arbre, celui de naissance s'il est connu (#168). Réglage de ce navigateur. - // Proches des autres familles dessinés, estompés, autour d'un onglet : 0 (ma ligne seulement) à 3 liens - const [tabKinDepth, setTabKinDepthState] = useState(() => { try { const v = localStorage.getItem('parenthese.tabKinDepth'); return ['0', '1', '2', '3'].includes(v) ? Number(v) : 1 } catch { return 1 } }) - const setTabKinDepth = useCallback((d) => { setTabKinDepthState(d); try { localStorage.setItem('parenthese.tabKinDepth', String(d)) } catch { /* navigateur sans stockage */ } }, []) + // Onglets par nom : nombre de porteurs d'un nom à partir duquel il a son onglet (8 par défaut, Patch, 07/11) + const [tabMinCount, setTabMinCountState] = useState(() => { try { const v = Number(localStorage.getItem('parenthese.tabMinCount')); return v >= 2 && v <= 30 ? v : 8 } catch { return 8 } }) + const setTabMinCount = useCallback((n) => { setTabMinCountState(n); try { localStorage.setItem('parenthese.tabMinCount', String(n)) } catch { /* navigateur sans stockage */ } }, []) const [fullNames, setFullNamesState] = useState(() => { try { return localStorage.getItem('parenthese.fullNames') === 'on' } catch { return false } }) const setFullNames = useCallback((on) => { setFullNamesState(on); try { localStorage.setItem('parenthese.fullNames', on ? 'on' : 'off') } catch { /* navigateur sans stockage */ } }, []) // Premiers visages : fenêtre ouverte après un import GEDCOM, ou depuis les paramètres de l'arbre @@ -3574,7 +3574,7 @@ function App() { reorderEnabled={canReorderSiblings} onReorderSiblings={handleReorderSiblings} familyTabs={familyTabs} - tabKinDepth={tabKinDepth} + tabMinCount={tabMinCount} fullNames={fullNames} annotationViewRef={annotationViewRef} onAnnotationsAnchored={persistAnnotationAnchors} @@ -3756,8 +3756,8 @@ function App() { onClose={() => setAdminPanelVisible(false)} familyTabs={familyTabs} onFamilyTabsChange={setFamilyTabs} - tabKinDepth={tabKinDepth} - onTabKinDepthChange={setTabKinDepth} + tabMinCount={tabMinCount} + onTabMinCountChange={setTabMinCount} fullNames={fullNames} onFullNamesChange={setFullNames} /> diff --git a/frontend/src/components/AdminPanel.jsx b/frontend/src/components/AdminPanel.jsx index d224b5d..fa0ece3 100644 --- a/frontend/src/components/AdminPanel.jsx +++ b/frontend/src/components/AdminPanel.jsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from 'react' +import { useEffect, useMemo, useRef, useState } from 'react' import { Download, ImagePlus, Trash2, Unplug, X } from 'lucide-react' import './SettingsPanels.css' import { @@ -25,6 +25,7 @@ import { t } from '../i18n/index.js' import { readGedcomFile } from '../utils/gedcomText.js' import { readGedcomBundle, zipErrorMessage } from '../utils/gedcomBundle.js' import Skeleton from './Skeleton.jsx' +import { nameTabsOf } from './galaxy/nameTabs.js' function normalizeSlug(value) { return String(value || '') @@ -41,8 +42,8 @@ const TREE_ICONS = ['🌳', '🌿', '🌻', '🏡', '❤️', '🧡', '⭐', ' const AdminPanel = ({ familyTabs = true, onFamilyTabsChange = null, - tabKinDepth = 1, - onTabKinDepthChange = null, + tabMinCount = 8, + onTabMinCountChange = null, fullNames = false, onFullNamesChange = null, visible, @@ -62,6 +63,16 @@ const AdminPanel = ({ onOpenPhotoStart, }) => { const [section, setSection] = useState('tree') + // Seuil des onglets : le curseur montre tout de suite les onglets qu'il crée ; l'arbre ne se recalcule qu'une + // fois le curseur posé + const [tabDraft, setTabDraft] = useState(tabMinCount) + useEffect(() => { setTabDraft(tabMinCount) }, [tabMinCount]) + useEffect(() => { + if (tabDraft === tabMinCount) return undefined + const timer = setTimeout(() => onTabMinCountChange?.(tabDraft), 350) + return () => clearTimeout(timer) + }, [tabDraft, tabMinCount, onTabMinCountChange]) + const tabPreview = useMemo(() => (visible && familyTabs ? nameTabsOf(null, tabDraft).slice(1) : []), [visible, familyTabs, tabDraft]) const [loading, setLoading] = useState(false) const [saving, setSaving] = useState(false) const [errorMessage, setErrorMessage] = useState('') @@ -540,27 +551,47 @@ const AdminPanel = ({

{t('Réglages de ce navigateur.')}

-
- {t("Dans un onglet, montrer aussi les proches de l'autre côté")} -
- {[[0, t('Ma ligne seulement')], [1, t('1 lien')], [2, t('2 liens')], [3, t('3 liens')]].map(([d, text]) => ( - - ))} + {familyTabs && ( +
+ + setTabDraft(Number(event.target.value))} + /> +

+ {tabPreview.length + ? tabPreview.map((tb) => {tb.label} {tb.count}) + : t("Aucun onglet à ce seuil : tout l'arbre sur une seule page.")} +

-

{t('Un lien : un parent, un enfant ou un conjoint. Ils apparaissent en plus clair ; votre propre ligne reste toujours visible.')}

-
+ )} +