diff --git a/frontend/src/components/Galaxy.css b/frontend/src/components/Galaxy.css index 36f53a6..56ba59e 100644 --- a/frontend/src/components/Galaxy.css +++ b/frontend/src/components/Galaxy.css @@ -61,6 +61,10 @@ padding: 4px; } .family-tabs::-webkit-scrollbar { display: none; } +/* Bords coupés : un fondu de chaque côté qui défile encore */ +.family-tabs.has-left { mask-image: linear-gradient(to right, transparent, #000 36px); -webkit-mask-image: linear-gradient(to right, transparent, #000 36px); } +.family-tabs.has-right { mask-image: linear-gradient(to left, transparent, #000 36px); -webkit-mask-image: linear-gradient(to left, transparent, #000 36px); } +.family-tabs.has-left.has-right { mask-image: linear-gradient(to right, transparent, #000 36px, #000 calc(100% - 36px), transparent); -webkit-mask-image: linear-gradient(to right, transparent, #000 36px, #000 calc(100% - 36px), transparent); } .family-tab { flex: 0 0 auto; diff --git a/frontend/src/components/Galaxy.jsx b/frontend/src/components/Galaxy.jsx index 8a5307a..c77972d 100644 --- a/frontend/src/components/Galaxy.jsx +++ b/frontend/src/components/Galaxy.jsx @@ -58,8 +58,22 @@ const Galaxy = ({ onPersonSelect, onMediaSelect, selectedPersonId, searchHighlig useEffect(() => { startRefRef.current = focusPersonId; setActiveKey(focusPersonId != null ? String(focusPersonId) : null) }, [focusPersonId]) // Lu au calcul du cadrage et par le réordonnancement : la référence de l'arbre affiché const focusPersonIdRef = useRef(focusPersonId) - // Sur téléphone, la barre défile : l'onglet ouvert par un badge se met en vue, sans animation - useEffect(() => { document.querySelector('.family-tab.is-active')?.scrollIntoView({ behavior: 'auto', inline: 'nearest', block: 'nearest' }) }, [activeKey, tabs]) + // La barre défile quand les onglets débordent : l'onglet ouvert se met en vue (sans animation) et les bords + // coupés se signalent par un fondu + const tabsBarRef = useRef(null) + const [tabsEdges, setTabsEdges] = useState('') + const updateTabsEdges = useCallback(() => { + const el = tabsBarRef.current + if (!el) return + const left = el.scrollLeft > 2, right = el.scrollLeft + el.clientWidth < el.scrollWidth - 2 + setTabsEdges(`${left ? ' has-left' : ''}${right ? ' has-right' : ''}`) + }, []) + useEffect(() => { + document.querySelector('.family-tab.is-active')?.scrollIntoView({ behavior: 'auto', inline: 'nearest', block: 'nearest' }) + updateTabsEdges() + window.addEventListener('resize', updateTabsEdges) + return () => window.removeEventListener('resize', updateTabsEdges) + }, [activeKey, tabs, updateTabsEdges]) // Clic sur un badge : l'onglet de cette famille (ou, depuis un onglet, celui d'où l'on vient) const openFamily = useCallback((tabKey) => { if (tabKey != null) setActiveKey(String(tabKey)) }, []) // Propriétaire : un portrait vide survolé montre « + photo » @@ -1076,7 +1090,7 @@ const Galaxy = ({ onPersonSelect, onMediaSelect, selectedPersonId, searchHighlig