mirror of
https://github.com/IliesAllali/parenthese.git
synced 2026-11-06 23:32:08 +00:00
Merge pull request #186 from IliesAllali/feat/noms-complets
Tree: optional last name under the first name, maiden name first (#168)
This commit is contained in:
5 files changed
+42
-7
No files matched your search
@@ -582,6 +582,9 @@ function App() {
|
||||
// Grandes belles-familles dans des onglets (réglage de ce navigateur, voir Paramètres de l'arbre)
|
||||
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.
|
||||
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
|
||||
const [photoStart, setPhotoStart] = useState({ visible: false, personsImported: null, refs: [], mediaRefs: [], faces: [], bundledFiles: [] })
|
||||
const photoStartDirtyRef = useRef(false)
|
||||
@@ -3485,6 +3488,7 @@ function App() {
|
||||
reorderEnabled={canReorderSiblings}
|
||||
onReorderSiblings={handleReorderSiblings}
|
||||
familyTabs={familyTabs}
|
||||
fullNames={fullNames}
|
||||
annotationViewRef={annotationViewRef}
|
||||
onAnnotationsAnchored={persistAnnotationAnchors}
|
||||
selfPersonId={linkedSelfPersonId || null}
|
||||
@@ -3659,6 +3663,8 @@ function App() {
|
||||
onClose={() => setAdminPanelVisible(false)}
|
||||
familyTabs={familyTabs}
|
||||
onFamilyTabsChange={setFamilyTabs}
|
||||
fullNames={fullNames}
|
||||
onFullNamesChange={setFullNames}
|
||||
/>
|
||||
<PhotoStartPanel
|
||||
visible={photoStart.visible}
|
||||
|
||||
@@ -41,6 +41,8 @@ const TREE_ICONS = ['🌳', '🌿', '🌻', '🏡', '❤️', '🧡', '⭐', '
|
||||
const AdminPanel = ({
|
||||
familyTabs = true,
|
||||
onFamilyTabsChange = null,
|
||||
fullNames = false,
|
||||
onFullNamesChange = null,
|
||||
visible,
|
||||
treeId,
|
||||
authToken,
|
||||
@@ -545,6 +547,18 @@ const AdminPanel = ({
|
||||
<small>{t("Une belle-famille plus large qu'un écran s'ouvre dans son propre onglet au lieu d'être dessinée dans l'arbre. Réglage de ce navigateur.")}</small>
|
||||
</span>
|
||||
</label>
|
||||
<label className={`ts-choice ${fullNames ? 'is-active' : ''}`}>
|
||||
<input
|
||||
type="checkbox"
|
||||
name="fullNames"
|
||||
checked={fullNames}
|
||||
onChange={(event) => onFullNamesChange?.(event.target.checked)}
|
||||
/>
|
||||
<span className="ts-choice-text">
|
||||
<strong>{t("Le nom de famille sous le prénom")}</strong>
|
||||
<small>{t("Le nom de naissance quand il est connu, sinon le nom d'usage. Réglage de ce navigateur.")}</small>
|
||||
</span>
|
||||
</label>
|
||||
</fieldset>
|
||||
</form>
|
||||
)}
|
||||
|
||||
@@ -47,7 +47,7 @@ const centerX = (node) => node.x + node.width / 2
|
||||
const easeOutQuint = (t) => 1 - (1 - t) ** 5
|
||||
const prefersReducedMotion = () => typeof window !== 'undefined' && window.matchMedia?.('(prefers-reduced-motion: reduce)').matches
|
||||
|
||||
const Galaxy = ({ onPersonSelect, onMediaSelect, selectedPersonId, searchHighlightIds = [], graphRevision = 0, annotationHandlers = null, transformReadRef = null, zoomApiRef = null, focusPersonId = null, assetRevision = 0, reorderEnabled = false, onReorderSiblings = null, familyTabs = true, annotationViewRef = null, onAnnotationsAnchored = null, selfPersonId = null, ghosts = null, initialFamilyKey = null, onFamilyChange = null }) => {
|
||||
const Galaxy = ({ onPersonSelect, onMediaSelect, selectedPersonId, searchHighlightIds = [], graphRevision = 0, annotationHandlers = null, transformReadRef = null, zoomApiRef = null, focusPersonId = null, assetRevision = 0, reorderEnabled = false, onReorderSiblings = null, familyTabs = true, fullNames = false, annotationViewRef = null, onAnnotationsAnchored = null, selfPersonId = null, ghosts = null, initialFamilyKey = null, onFamilyChange = null }) => {
|
||||
const canvasRef = useRef(null)
|
||||
const layoutDataRef = useRef(null)
|
||||
// Onglets de familles (réglage `familyTabs`) : la liste des familles de l'arbre, calculée une fois par
|
||||
@@ -79,6 +79,8 @@ const Galaxy = ({ onPersonSelect, onMediaSelect, selectedPersonId, searchHighlig
|
||||
useEffect(() => { familyTabsRef.current = familyTabs }, [familyTabs])
|
||||
// Propositions pas encore relues (utils/ghosts.js) : placées et dessinées en transparence, jamais dans les données
|
||||
const ghostsRef = useRef(ghosts)
|
||||
const fullNamesRef = useRef(fullNames)
|
||||
useEffect(() => { fullNamesRef.current = fullNames; needsRedrawRef.current = true }, [fullNames])
|
||||
useEffect(() => { ghostsRef.current = ghosts; needsRedrawRef.current = true }, [ghosts])
|
||||
const onAnnotationsAnchoredRef = useRef(onAnnotationsAnchored)
|
||||
useEffect(() => { onAnnotationsAnchoredRef.current = onAnnotationsAnchored }, [onAnnotationsAnchored])
|
||||
@@ -1084,6 +1086,7 @@ const Galaxy = ({ onPersonSelect, onMediaSelect, selectedPersonId, searchHighlig
|
||||
view,
|
||||
liftScales: liftScalesRef.current,
|
||||
ghostPatches: ghostsRef.current?.patches || null,
|
||||
fullNames: fullNamesRef.current,
|
||||
})
|
||||
drawRenvoiPills(ctx)
|
||||
// Annotations dessinées APRES les noeuds pour que stickers/texte soient au-dessus.
|
||||
|
||||
@@ -472,7 +472,7 @@ function measureTextCached(ctx, text, fontKey) {
|
||||
// ============================================================
|
||||
function drawPersonNode(ctx, node, pos, nodeProgress, {
|
||||
nodeRandomData, imageCache, hoverScales, selectedGlowScales, searchGlowScales,
|
||||
mediaHoverState, entrance, entranceActive, elapsed, view = null, liftScales = null,
|
||||
mediaHoverState, entrance, entranceActive, elapsed, view = null, liftScales = null, fullNames = false,
|
||||
}) {
|
||||
const { cx, cy } = pos
|
||||
const person = getPersonById(node._data.id) || node._data
|
||||
@@ -633,15 +633,20 @@ function drawPersonNode(ctx, node, pos, nodeProgress, {
|
||||
ctx.font = `${nameUnknown ? 'italic ' : ''}400 13px "Newsreader", Georgia, serif`
|
||||
ctx.textAlign = 'center'
|
||||
ctx.textBaseline = 'middle'
|
||||
// Réglage « nom de famille sous le prénom » (#168) : celui de naissance s'il est connu, coupé comme le surnom
|
||||
const familyName = fullNames && withDetail ? String(person.birthName || person.lastName || '').trim() : ''
|
||||
const familyLabel = familyName ? fitNickname(ctx, familyName, NICKNAME_MAX_W) : ''
|
||||
const lineShift = familyLabel ? 15 : 0
|
||||
if (nameLabel) {
|
||||
if (withDetail) {
|
||||
const nameMetrics = measureTextCached(ctx, nameLabel, nameUnknown ? 'name-unknown' : 'name')
|
||||
const nameBlockH = Math.max(
|
||||
18,
|
||||
(nameMetrics.actualBoundingBoxAscent || 7) + (nameMetrics.actualBoundingBoxDescent || 4) + 8,
|
||||
)
|
||||
const labelGlowY = 6
|
||||
const labelGlowRx = Math.max(26, Math.round(nameMetrics.width * 0.62))
|
||||
) + lineShift
|
||||
const labelGlowY = 6 + lineShift / 2
|
||||
const labelW = familyLabel ? Math.max(nameMetrics.width, ctx.measureText(familyLabel).width) : nameMetrics.width
|
||||
const labelGlowRx = Math.max(26, Math.round(labelW * 0.62))
|
||||
const labelGlowRy = Math.max(16, Math.round(nameBlockH * 1.15))
|
||||
const labelGlow = ctx.createRadialGradient(0, labelGlowY, 0, 0, labelGlowY, labelGlowRx)
|
||||
labelGlow.addColorStop(0, 'rgba(251, 250, 247, 0.98)')
|
||||
@@ -661,13 +666,18 @@ function drawPersonNode(ctx, node, pos, nodeProgress, {
|
||||
ctx.fillStyle = COLORS.text
|
||||
ctx.fillText(nameLabel, 0, 0)
|
||||
}
|
||||
if (familyLabel) {
|
||||
ctx.font = '400 13px "Newsreader", Georgia, serif'
|
||||
ctx.fillStyle = COLORS.text
|
||||
ctx.fillText(familyLabel, 0, 15)
|
||||
}
|
||||
|
||||
// Surnom sous le nom, en italique : les dates descendent pour lui faire place
|
||||
const hasNickname = withDetail && Boolean(person.nickname)
|
||||
if (hasNickname) {
|
||||
ctx.font = 'italic 300 13px "Newsreader", Georgia, serif'
|
||||
ctx.fillStyle = COLORS.textLight
|
||||
ctx.fillText(fitNickname(ctx, person.nickname, NICKNAME_MAX_W), 0, 15)
|
||||
ctx.fillText(fitNickname(ctx, person.nickname, NICKNAME_MAX_W), 0, 15 + lineShift)
|
||||
}
|
||||
|
||||
if (withDetail) {
|
||||
@@ -677,7 +687,7 @@ function drawPersonNode(ctx, node, pos, nodeProgress, {
|
||||
const years = person.deathYear
|
||||
? `${birthYearLabel}–${person.deathYear}`
|
||||
: person.isAlive === false ? `${birthYearLabel}–?` : `${birthYearLabel}`
|
||||
ctx.fillText(years, 0, hasNickname ? 31 : 15)
|
||||
ctx.fillText(years, 0, (hasNickname ? 31 : 15) + lineShift)
|
||||
}
|
||||
|
||||
ctx.restore()
|
||||
|
||||
@@ -6,6 +6,8 @@ export default {
|
||||
'Affichage': 'Display',
|
||||
'Les grandes familles par alliance dans des onglets': 'Large in-law families in tabs',
|
||||
"Une belle-famille plus large qu'un écran s'ouvre dans son propre onglet au lieu d'être dessinée dans l'arbre. Réglage de ce navigateur.": 'An in-law family wider than a screen opens in its own tab instead of being drawn in the tree. Saved in this browser.',
|
||||
'Le nom de famille sous le prénom': 'Last name under the first name',
|
||||
"Le nom de naissance quand il est connu, sinon le nom d'usage. Réglage de ce navigateur.": 'The maiden name when it is known, otherwise the current last name. Saved in this browser.',
|
||||
// Arbre sur canvas (components/galaxy/renderers.js)
|
||||
'Voir les parents': 'See parents',
|
||||
'Voir {firstName}': 'See {firstName}',
|
||||
|
||||
Reference in new issue
Block a user