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.')}
-
+ )}
+