mirror of
https://github.com/gramps-project/gramps-web.git
synced 2026-10-16 21:01:38 +08:00
Allow changing genealogical symbols (#1382)
* Mudanças para resolução da issue #507, além de outros erros * Alteração dos ícones de eventos nas páginas pessoais * Contribution for the resolution of issue #507 - providing different genealogical ssymbols * Fix package-lock.json out of sync with package.json * Restore package-lock.json to match upstream, fixing missing yaml@2.9.0 * Restore package-lock.json to match upstream, fixing missing yaml@2.9.0 * Implementing recommended changes * Addressing the latest comments by Copilot * Resolving ESLint test fail * Resolving issues (cards, etc.) post refactoring of the tree views * Resolving failing unit test issue: objectDetail undefined in 'util.test.js' * Resolving ESLint error * Changes made due to recent review * Retouching the translation of abbreviations * Discarding out-of-scope changes to objectRender.js and timeline.test.js * Revert tabindex changes
This commit is contained in:
@@ -63,6 +63,9 @@
|
||||
"Recently changed objects": "Recently changed objects",
|
||||
"Select language": "Select language",
|
||||
"Select theme": "Select theme",
|
||||
"Genealogical symbols": "Genealogical symbols",
|
||||
"Unicode symbols (∗ † ⚭ ⚮)": "Unicode symbols (∗ † ⚭ ⚮)",
|
||||
"Text abbreviations (b. d. m. div.)": "Text abbreviations (b. d. m. div.)",
|
||||
"Theme": "Theme",
|
||||
"System": "System",
|
||||
"Light": "Light",
|
||||
|
||||
@@ -196,6 +196,12 @@ export const sharedStyles = css`
|
||||
fill: var(--grampsjs-body-font-color-40);
|
||||
}
|
||||
|
||||
.event i {
|
||||
color: var(--grampsjs-body-font-color-40);
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.event i svg {
|
||||
height: 16px;
|
||||
width: auto;
|
||||
|
||||
@@ -128,6 +128,8 @@ export class ChartCanvas {
|
||||
drawChangedCards(people, {
|
||||
getImageUrl: settings.getImageUrl,
|
||||
nameDisplayFormat,
|
||||
birthSymbol: settings.birthSymbol,
|
||||
deathSymbol: settings.deathSymbol,
|
||||
palette,
|
||||
boxWidth,
|
||||
boxHeight,
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
colorAddPersonButtons,
|
||||
} from './addPersonButton.js'
|
||||
import {chartPalette} from './palette.js'
|
||||
import {SYMBOL_SETS, SYMBOL_SET_DEFAULT} from '../symbols.js'
|
||||
|
||||
// Pattern ids must be unique in the document, and the same person can be
|
||||
// shown in several cards
|
||||
@@ -43,6 +44,8 @@ export function appendPersonCard(
|
||||
boxHeight = 90,
|
||||
imgPadding = 10,
|
||||
nameDisplayFormat = chartNameDisplayFormat.surnameThenGiven,
|
||||
birthSymbol = SYMBOL_SETS[SYMBOL_SET_DEFAULT].birthSymbol,
|
||||
deathSymbol = SYMBOL_SETS[SYMBOL_SET_DEFAULT].deathSymbol,
|
||||
palette = chartPalette,
|
||||
}
|
||||
) {
|
||||
@@ -87,8 +90,16 @@ export function appendPersonCard(
|
||||
text: p => (surnameFirst ? p.name_given || '…' : p.name_surname || '…'),
|
||||
weight: 500,
|
||||
},
|
||||
{show: p => p?.birth?.date, text: p => `*${p.birth.date}`, weight: 350},
|
||||
{show: p => p?.death?.date, text: p => `†${p.death.date}`, weight: 350},
|
||||
{
|
||||
show: p => p?.birth?.date,
|
||||
text: p => `${birthSymbol} ${p.birth.date}`,
|
||||
weight: 350,
|
||||
},
|
||||
{
|
||||
show: p => p?.death?.date,
|
||||
text: p => `${deathSymbol} ${p.death.date}`,
|
||||
weight: 350,
|
||||
},
|
||||
]
|
||||
lines.forEach((line, i) => {
|
||||
nodes
|
||||
@@ -195,6 +206,8 @@ export function drawChangedCards(
|
||||
{
|
||||
getImageUrl = () => '',
|
||||
nameDisplayFormat,
|
||||
birthSymbol,
|
||||
deathSymbol,
|
||||
palette = chartPalette,
|
||||
boxWidth,
|
||||
boxHeight,
|
||||
@@ -206,6 +219,8 @@ export function drawChangedCards(
|
||||
person: d.person,
|
||||
imageUrl: getImageUrl(d),
|
||||
nameDisplayFormat,
|
||||
birthSymbol,
|
||||
deathSymbol,
|
||||
palette,
|
||||
}
|
||||
const previous = cardInputs.get(this)
|
||||
@@ -229,6 +244,8 @@ export function drawChangedCards(
|
||||
boxWidth,
|
||||
boxHeight,
|
||||
nameDisplayFormat,
|
||||
birthSymbol,
|
||||
deathSymbol,
|
||||
palette,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import {classMap} from 'lit/directives/class-map.js'
|
||||
import {fireEvent} from '../util.js'
|
||||
import {renderPersonAvatar, renderPersonDates} from './personListUtils.js'
|
||||
import {GrampsjsEditableList} from './GrampsjsEditableList.js'
|
||||
import {getSymbols} from '../symbols.js'
|
||||
import './GrampsjsFormChildRef.js'
|
||||
import './GrampsjsFormNewChild.js'
|
||||
|
||||
@@ -69,7 +70,11 @@ export class GrampsjsChildren extends GrampsjsEditableList {
|
||||
}
|
||||
}}"
|
||||
>
|
||||
${p.name_given || ''} ${p.name_surname || ''} ${renderPersonDates(p)}
|
||||
${p.name_given || ''} ${p.name_surname || ''}
|
||||
${renderPersonDates(
|
||||
p,
|
||||
getSymbols(this.appState.settings, s => this._(s))
|
||||
)}
|
||||
${hasNonBirthRel
|
||||
? html`<span slot="supporting-text">${relText}</span>`
|
||||
: ''}
|
||||
|
||||
@@ -4,6 +4,7 @@ import {GrampsjsConnectedComponent} from './GrampsjsConnectedComponent.js'
|
||||
import {fireEvent} from '../util.js'
|
||||
import {renderPersonAvatar, renderPersonDates} from './personListUtils.js'
|
||||
import {personListItemStyles} from '../SharedStyles.js'
|
||||
import {getSymbols} from '../symbols.js'
|
||||
import '@material/web/list/list.js'
|
||||
import '@material/web/list/list-item.js'
|
||||
|
||||
@@ -85,7 +86,10 @@ export class GrampsjsConnectedParents extends GrampsjsConnectedComponent {
|
||||
})}"
|
||||
>
|
||||
${personProfile.name_given || ''} ${personProfile.name_surname || ''}
|
||||
${renderPersonDates(personProfile, {showAge: false})}
|
||||
${renderPersonDates(personProfile, {
|
||||
showAge: false,
|
||||
...getSymbols(this.appState.settings, s => this._(s)),
|
||||
})}
|
||||
${renderPersonAvatar(extPerson, personProfile.sex)}
|
||||
</md-list-item>
|
||||
`
|
||||
|
||||
@@ -11,6 +11,7 @@ import './GrampsjsFormEditFamily.js'
|
||||
import './GrampsjsFormNewPerson.js'
|
||||
import './GrampsjsFormPersonRef.js'
|
||||
import {GrampsjsObject} from './GrampsjsObject.js'
|
||||
import {getSymbols} from '../symbols.js'
|
||||
|
||||
export class GrampsjsFamily extends GrampsjsObject {
|
||||
static get styles() {
|
||||
@@ -97,6 +98,10 @@ export class GrampsjsFamily extends GrampsjsObject {
|
||||
const divorce = this.data?.profile?.divorce
|
||||
const hasMarriage = marriage?.date || marriage?.place
|
||||
const hasDivorce = divorce && Object.keys(divorce).length > 0
|
||||
const {marriageSymbol, divorceSymbol} = getSymbols(
|
||||
this.appState.settings,
|
||||
s => this._(s)
|
||||
)
|
||||
if (!relType && !hasMarriage && !hasDivorce && !this.edit) {
|
||||
return ''
|
||||
}
|
||||
@@ -109,7 +114,7 @@ export class GrampsjsFamily extends GrampsjsObject {
|
||||
${relType || ''}
|
||||
${hasMarriage
|
||||
? html`<span class="parent-dates">
|
||||
<span class="sym">⚭</span>
|
||||
<span class="sym">${marriageSymbol}</span>
|
||||
${marriage.date || ''}
|
||||
${marriage.place
|
||||
? `${this._('in')} ${
|
||||
@@ -120,7 +125,7 @@ export class GrampsjsFamily extends GrampsjsObject {
|
||||
: ''}
|
||||
${hasDivorce
|
||||
? html`<span class="parent-dates">
|
||||
<span class="sym">⚮</span>
|
||||
<span class="sym">${divorceSymbol}</span>
|
||||
${divorce.date || ''}
|
||||
${divorce.place ? `${this._('in')} ${divorce.place}` : ''}
|
||||
</span>`
|
||||
@@ -153,6 +158,9 @@ export class GrampsjsFamily extends GrampsjsObject {
|
||||
const hasProfile = Object.keys(profile ?? {}).length > 0
|
||||
const birthDate = profile?.birth?.date || ''
|
||||
const deathDate = profile?.death?.date || ''
|
||||
const {birthSymbol, deathSymbol} = getSymbols(this.appState.settings, s =>
|
||||
this._(s)
|
||||
)
|
||||
|
||||
return html`
|
||||
<dl>
|
||||
@@ -170,11 +178,13 @@ export class GrampsjsFamily extends GrampsjsObject {
|
||||
${birthDate || deathDate
|
||||
? html`<span class="parent-dates">
|
||||
${birthDate
|
||||
? html`<span class="sym">∗</span> ${birthDate}`
|
||||
? html`<span class="sym">${birthSymbol}</span>
|
||||
${birthDate}`
|
||||
: ''}
|
||||
${birthDate && deathDate ? ' ' : ''}
|
||||
${deathDate
|
||||
? html`<span class="sym">†</span> ${deathDate}`
|
||||
? html`<span class="sym">${deathSymbol}</span>
|
||||
${deathDate}`
|
||||
: ''}
|
||||
</span>`
|
||||
: ''}`
|
||||
|
||||
@@ -525,7 +525,8 @@ class GrampsjsMapSearchbox extends GrampsjsAppStateMixin(LitElement) {
|
||||
supportingText: objectDetail(
|
||||
TYPE_PERSON,
|
||||
obj.object,
|
||||
this.appState.i18n.strings
|
||||
this.appState.i18n.strings,
|
||||
this.appState.settings
|
||||
).trim(),
|
||||
iconPath: mdiAccount,
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
mdiMap,
|
||||
} from '@mdi/js'
|
||||
import {GrampsjsObject} from './GrampsjsObject.js'
|
||||
import {asteriskIcon, crossIcon} from '../icons.js'
|
||||
import {getSymbols} from '../symbols.js'
|
||||
import './GrampsjsImg.js'
|
||||
import './GrampsjsEditGender.js'
|
||||
import './GrampsjsPersonRelationship.js'
|
||||
@@ -124,9 +124,10 @@ export class GrampsjsPerson extends GrampsjsObject {
|
||||
if (obj === undefined || Object.keys(obj).length === 0) {
|
||||
return ''
|
||||
}
|
||||
const {birthSymbol} = getSymbols(this.appState.settings, s => this._(s))
|
||||
return html`
|
||||
<span class="event">
|
||||
<i>${asteriskIcon}</i>
|
||||
<i>${birthSymbol}</i>
|
||||
${obj.date || ''} ${obj.place ? this._('in') : ''}
|
||||
${obj.place_name || obj.place || ''}
|
||||
</span>
|
||||
@@ -138,9 +139,10 @@ export class GrampsjsPerson extends GrampsjsObject {
|
||||
if (obj === undefined || Object.keys(obj).length === 0) {
|
||||
return ''
|
||||
}
|
||||
const {deathSymbol} = getSymbols(this.appState.settings, s => this._(s))
|
||||
return html`
|
||||
<span class="event">
|
||||
<i>${crossIcon}</i>
|
||||
<i>${deathSymbol}</i>
|
||||
${obj.date || ''} ${obj.place ? this._('in') : ''}
|
||||
${obj.place_name || obj.place || ''}
|
||||
</span>
|
||||
|
||||
@@ -7,6 +7,7 @@ import './GrampsjsEvents.js'
|
||||
import {GrampsjsConnectedComponent} from './GrampsjsConnectedComponent.js'
|
||||
import {fireEvent, personProfileDisplayName} from '../util.js'
|
||||
import {sharedStyles} from '../SharedStyles.js'
|
||||
import {getSymbols} from '../symbols.js'
|
||||
|
||||
export class GrampsjsPersonBox extends GrampsjsConnectedComponent {
|
||||
static get styles() {
|
||||
@@ -94,17 +95,22 @@ export class GrampsjsPersonBox extends GrampsjsConnectedComponent {
|
||||
const birthPlace = person.profile?.birth?.place_name || ''
|
||||
const deathDate = person.profile?.death?.date || ''
|
||||
const deathPlace = person.profile?.death?.place_name || ''
|
||||
const {birthSymbol, deathSymbol} = getSymbols(this.appState.settings, s =>
|
||||
this._(s)
|
||||
)
|
||||
return html`
|
||||
<h2>${fullName || this.name || this._('Person')}</h2>
|
||||
<div class="dates">
|
||||
${birthDate || birthPlace
|
||||
? html`<p>
|
||||
∗ ${[birthDate, birthPlace].filter(Boolean).join(' · ')}
|
||||
${birthSymbol}
|
||||
${[birthDate, birthPlace].filter(Boolean).join(' · ')}
|
||||
</p>`
|
||||
: ''}
|
||||
${deathDate || deathPlace
|
||||
? html`<p>
|
||||
† ${[deathDate, deathPlace].filter(Boolean).join(' · ')}
|
||||
${deathSymbol}
|
||||
${[deathDate, deathPlace].filter(Boolean).join(' · ')}
|
||||
</p>`
|
||||
: ''}
|
||||
</div>
|
||||
|
||||
@@ -48,7 +48,8 @@ export class GrampsjsReferences extends GrampsjsEditableList {
|
||||
const detail = objectDetail(
|
||||
type,
|
||||
merged,
|
||||
this.appState.i18n.strings
|
||||
this.appState.i18n.strings,
|
||||
this.appState.settings
|
||||
).trim()
|
||||
return html`
|
||||
<md-list-item
|
||||
|
||||
@@ -5,6 +5,7 @@ import {RelationshipChart} from '../charts/RelationshipChart.js'
|
||||
import {layoutRelationships} from '../charts/layout/relationshipLayout.js'
|
||||
import {chartTransitionDuration, getImageUrl} from '../charts/util.js'
|
||||
import {fireEvent} from '../util.js'
|
||||
import {getSymbols} from '../symbols.js'
|
||||
|
||||
class GrampsjsRelationshipChart extends GrampsjsChartBase {
|
||||
static get properties() {
|
||||
@@ -57,6 +58,7 @@ class GrampsjsRelationshipChart extends GrampsjsChartBase {
|
||||
this._chart.update(this._layout, {
|
||||
getImageUrl: node => getImageUrl(node.person, 100),
|
||||
nameDisplayFormat: this.nameDisplayFormat,
|
||||
...getSymbols(this.appState.settings, s => this._(s)),
|
||||
canEdit: this.canEdit,
|
||||
duration: chartTransitionDuration(),
|
||||
bboxWidth: this.containerWidth,
|
||||
|
||||
@@ -169,7 +169,8 @@ export class GrampsjsSearchResultList extends GrampsjsAppStateMixin(
|
||||
const detail = objectDetail(
|
||||
obj.object_type,
|
||||
obj.object,
|
||||
this.appState.i18n.strings
|
||||
this.appState.i18n.strings,
|
||||
this.appState.settings
|
||||
).trim()
|
||||
if (detail?.length) {
|
||||
return detail
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
getImageUrl,
|
||||
} from '../charts/util.js'
|
||||
import {fireEvent, menuSelectionHandler} from '../util.js'
|
||||
import {getSymbols} from '../symbols.js'
|
||||
import {personListItemStyles} from '../SharedStyles.js'
|
||||
import {renderPersonAvatar, renderPersonDates} from './personListUtils.js'
|
||||
|
||||
@@ -139,6 +140,7 @@ class GrampsjsTreeChart extends GrampsjsChartBase {
|
||||
bboxWidth: this.containerWidth,
|
||||
bboxHeight: this.containerHeight,
|
||||
nameDisplayFormat: this.nameDisplayFormat,
|
||||
...getSymbols(this.appState.settings, s => this._(s)),
|
||||
canEdit: this.canEdit,
|
||||
duration: chartTransitionDuration(),
|
||||
})
|
||||
@@ -169,6 +171,7 @@ class GrampsjsTreeChart extends GrampsjsChartBase {
|
||||
if (relatives.length === 0) {
|
||||
return ''
|
||||
}
|
||||
const symbols = getSymbols(this.appState.settings, s => this._(s))
|
||||
return html`
|
||||
<md-menu
|
||||
id="relatives-menu"
|
||||
@@ -184,7 +187,7 @@ class GrampsjsTreeChart extends GrampsjsChartBase {
|
||||
<div slot="headline">
|
||||
${formatChartName(person.profile, this.nameDisplayFormat)}
|
||||
</div>
|
||||
${renderPersonDates(person.profile)}
|
||||
${renderPersonDates(person.profile, symbols)}
|
||||
</md-menu-item>
|
||||
`
|
||||
)}
|
||||
|
||||
@@ -2,6 +2,7 @@ import {html} from 'lit'
|
||||
import {mdiAccount} from '@mdi/js'
|
||||
|
||||
import {objectIconPath, sexColor} from '../util.js'
|
||||
import {SYMBOL_SETS, SYMBOL_SET_DEFAULT} from '../symbols.js'
|
||||
import './GrampsjsImg.js'
|
||||
import './GrampsjsIcon.js'
|
||||
|
||||
@@ -32,7 +33,14 @@ export function renderPersonAvatar(extPerson, sex) {
|
||||
></grampsjs-icon>`
|
||||
}
|
||||
|
||||
export function renderPersonDates(profile, {showAge = true} = {}) {
|
||||
export function renderPersonDates(
|
||||
profile,
|
||||
{
|
||||
showAge = true,
|
||||
birthSymbol = SYMBOL_SETS[SYMBOL_SET_DEFAULT].birthSymbol,
|
||||
deathSymbol = SYMBOL_SETS[SYMBOL_SET_DEFAULT].deathSymbol,
|
||||
} = {}
|
||||
) {
|
||||
const birthStr = profile?.birth?.date || ''
|
||||
const deathStr = profile?.death?.date || ''
|
||||
const ageStr =
|
||||
@@ -41,9 +49,12 @@ export function renderPersonDates(profile, {showAge = true} = {}) {
|
||||
: ''
|
||||
if (!birthStr && !deathStr && !ageStr) return ''
|
||||
return html`<span slot="supporting-text"
|
||||
><span class="date-col">${birthStr ? `∗ ${birthStr}` : ''}</span
|
||||
><span class="date-col"
|
||||
>${deathStr ? `† ${deathStr}` : ''}${ageStr ? ` ${ageStr}` : ''}</span
|
||||
>${birthStr ? `${birthSymbol} ${birthStr}` : ''}</span
|
||||
><span class="date-col"
|
||||
>${deathStr ? `${deathSymbol} ${deathStr}` : ''}${ageStr
|
||||
? ` ${ageStr}`
|
||||
: ''}</span
|
||||
></span
|
||||
>`
|
||||
}
|
||||
|
||||
@@ -151,6 +151,7 @@ export const grampsStrings = [
|
||||
'Birth year until which to include people',
|
||||
'Birth year',
|
||||
'Birth',
|
||||
'birth abbreviation\u0004b.',
|
||||
'Birthday and Anniversary Report',
|
||||
'Blessing',
|
||||
'Bold',
|
||||
@@ -253,6 +254,7 @@ export const grampsStrings = [
|
||||
'Database overview',
|
||||
'Database Summary Report',
|
||||
'Date',
|
||||
'death abbreviation\u0004d.',
|
||||
'de Villiers/Pama numbering',
|
||||
'Death Date',
|
||||
'Death Place',
|
||||
@@ -289,6 +291,7 @@ export const grampsStrings = [
|
||||
'District',
|
||||
'Divorce Filing',
|
||||
'Divorce',
|
||||
'Divorce abbreviation\u0004div.',
|
||||
'Do not include a title',
|
||||
'Do not include any dates or places',
|
||||
'Do not include any occupation',
|
||||
@@ -510,6 +513,7 @@ export const grampsStrings = [
|
||||
'Marriage place',
|
||||
'Marriage Settlement',
|
||||
'Marriage',
|
||||
'marriage abbreviation\u0004m.',
|
||||
'Married Name',
|
||||
'Married',
|
||||
'Maternal',
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
// src/symbols.js
|
||||
export const SYMBOL_SET_DEFAULT = 'default'
|
||||
export const SYMBOL_SET_TEXT = 'text'
|
||||
|
||||
export const SYMBOL_SETS = {
|
||||
[SYMBOL_SET_DEFAULT]: {
|
||||
birthSymbol: '∗',
|
||||
deathSymbol: '†',
|
||||
marriageSymbol: '⚭',
|
||||
divorceSymbol: '⚮',
|
||||
},
|
||||
[SYMBOL_SET_TEXT]: {
|
||||
birthSymbol: 'b.',
|
||||
deathSymbol: 'd.',
|
||||
marriageSymbol: 'm.',
|
||||
divorceSymbol: 'div.',
|
||||
},
|
||||
}
|
||||
|
||||
// Used to populate the <select> in user settings.
|
||||
// `label` is a plain English string - passed through `this._()`
|
||||
// in the component, following the same pattern as `_treeViewLabel()`
|
||||
// in GrampsjsViewSettingsUser.js
|
||||
export const SYMBOL_SET_OPTIONS = [
|
||||
{value: SYMBOL_SET_DEFAULT, label: 'Unicode symbols (∗ † ⚭ ⚮)'},
|
||||
{value: SYMBOL_SET_TEXT, label: 'Text abbreviations (b. d. m. div.)'},
|
||||
]
|
||||
|
||||
const SYMBOL_TRANSLATION_CONTEXTS = {
|
||||
birthSymbol: 'birth abbreviation',
|
||||
deathSymbol: 'death abbreviation',
|
||||
marriageSymbol: 'marriage abbreviation',
|
||||
divorceSymbol: 'Divorce abbreviation',
|
||||
}
|
||||
|
||||
export function getSymbols(settings, translate = value => value) {
|
||||
const symbols =
|
||||
SYMBOL_SETS[settings?.symbolSet] ?? SYMBOL_SETS[SYMBOL_SET_DEFAULT]
|
||||
return Object.fromEntries(
|
||||
Object.entries(symbols).map(([key, value]) => {
|
||||
const context =
|
||||
settings?.symbolSet === SYMBOL_SET_TEXT
|
||||
? SYMBOL_TRANSLATION_CONTEXTS[key]
|
||||
: undefined
|
||||
if (!context) return [key, value]
|
||||
|
||||
const translationKey = `${context}\u0004${value}`
|
||||
const translated = translate(translationKey)
|
||||
return [
|
||||
key,
|
||||
translated == null || translated === translationKey
|
||||
? value
|
||||
: translated,
|
||||
]
|
||||
})
|
||||
)
|
||||
}
|
||||
+10
-4
@@ -52,6 +52,7 @@ import {
|
||||
import {renderIconSvg, ringsIconPath} from './icons.js'
|
||||
import './components/GrampsjsIcon.js'
|
||||
import {frontendLanguages} from './strings.js'
|
||||
import {getSymbols} from './symbols.js'
|
||||
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
@@ -437,13 +438,18 @@ export function objectDescription(type, obj, strings) {
|
||||
}
|
||||
}
|
||||
|
||||
export function objectDetail(type, obj, strings) {
|
||||
export function objectDetail(type, obj, strings, settings = {}) {
|
||||
const symbols = getSymbols(settings, value => translate(strings, value))
|
||||
switch (type) {
|
||||
case 'person':
|
||||
return `
|
||||
${obj?.profile?.birth?.date ? `∗ ${obj.profile.birth.date}` : ''}${
|
||||
obj?.profile?.birth?.place && obj?.profile?.birth?.date ? ', ' : ''
|
||||
}${obj?.profile?.birth?.place_name || obj?.profile?.birth?.place || ''}
|
||||
${
|
||||
obj?.profile?.birth?.date
|
||||
? `${symbols.birthSymbol} ${obj.profile.birth.date}`
|
||||
: ''
|
||||
}${obj?.profile?.birth?.place && obj?.profile?.birth?.date ? ', ' : ''}${
|
||||
obj?.profile?.birth?.place_name || obj?.profile?.birth?.place || ''
|
||||
}
|
||||
`
|
||||
// case 'family':
|
||||
// return ''
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
import {fireEvent} from '../util.js'
|
||||
import {applyScheme, DEFAULT_PRIMARY, DEFAULT_SECONDARY} from '../theme.js'
|
||||
import {DEFAULT_TREE_VIEW, TREE_VIEWS} from '../treeDefaults.js'
|
||||
import {SYMBOL_SET_DEFAULT, SYMBOL_SET_OPTIONS} from '../symbols.js'
|
||||
|
||||
export class GrampsjsViewSettingsUser extends GrampsjsView {
|
||||
static get styles() {
|
||||
@@ -143,6 +144,8 @@ export class GrampsjsViewSettingsUser extends GrampsjsView {
|
||||
${this.renderLangSelect()}
|
||||
<h3>${this._('Select theme')}</h3>
|
||||
${this.renderThemeSelect()}
|
||||
<h3>${this._('Genealogical symbols')}</h3>
|
||||
${this.renderSymbolsSelect()}
|
||||
<h3>${this._('Family tree preferences')}</h3>
|
||||
${this.renderTreePreferences()}
|
||||
</grampsjs-collapsible-section>
|
||||
@@ -256,6 +259,33 @@ export class GrampsjsViewSettingsUser extends GrampsjsView {
|
||||
}
|
||||
}
|
||||
|
||||
renderSymbolsSelect() {
|
||||
const symbolSet = this.appState.settings.symbolSet ?? SYMBOL_SET_DEFAULT
|
||||
return html`
|
||||
<md-filled-select
|
||||
id="select-symbols"
|
||||
label="${this._('Genealogical symbols')}"
|
||||
@change="${this._handleSymbolsSelected}"
|
||||
>
|
||||
${SYMBOL_SET_OPTIONS.map(
|
||||
({value, label}) => html`
|
||||
<md-select-option value="${value}" ?selected=${value === symbolSet}>
|
||||
${this._(label)}
|
||||
</md-select-option>
|
||||
`
|
||||
)}
|
||||
</md-filled-select>
|
||||
`
|
||||
}
|
||||
|
||||
_handleSymbolsSelected(event) {
|
||||
const candidate = event.target.value || SYMBOL_SET_DEFAULT
|
||||
const symbolSet = SYMBOL_SET_OPTIONS.some(({value}) => value === candidate)
|
||||
? candidate
|
||||
: SYMBOL_SET_DEFAULT
|
||||
this.appState.updateSettings({symbolSet})
|
||||
}
|
||||
|
||||
renderTreePreferences() {
|
||||
const defaultView =
|
||||
this.appState.settings.treeDefaultView ?? DEFAULT_TREE_VIEW
|
||||
|
||||
@@ -3,6 +3,7 @@ import {create} from 'd3-selection'
|
||||
import {
|
||||
appendPersonCard,
|
||||
clearPersonCardInteraction,
|
||||
drawChangedCards,
|
||||
setPersonCardInteraction,
|
||||
} from '../../src/charts/personCard.js'
|
||||
import {chartPalette} from '../../src/charts/palette.js'
|
||||
@@ -91,7 +92,7 @@ const click = node =>
|
||||
describe('appendPersonCard', () => {
|
||||
it('shows the surname first with placeholders for missing names', () => {
|
||||
const cards = renderCards()
|
||||
expect(texts(cards.full)).toEqual(['Berg,', 'Anna', '*1900', '†1980'])
|
||||
expect(texts(cards.full)).toEqual(['Berg,', 'Anna', '∗ 1900', '† 1980'])
|
||||
expect(texts(cards.noSurname)).toEqual(['…,', 'Carl'])
|
||||
expect(texts(cards.noGiven)).toEqual(['Doe,', '…'])
|
||||
expect(texts(cards.notFetched)).toEqual([])
|
||||
@@ -101,10 +102,36 @@ describe('appendPersonCard', () => {
|
||||
const cards = renderCards({
|
||||
nameDisplayFormat: chartNameDisplayFormat.givenThenSurname,
|
||||
})
|
||||
expect(texts(cards.full)).toEqual(['Anna', 'Berg', '*1900', '†1980'])
|
||||
expect(texts(cards.full)).toEqual(['Anna', 'Berg', '∗ 1900', '† 1980'])
|
||||
expect(texts(cards.noSurname)).toEqual(['Carl', '…'])
|
||||
})
|
||||
|
||||
it('uses configured birth and death symbols', () => {
|
||||
expect(
|
||||
texts(renderCards({birthSymbol: 'b.', deathSymbol: 'd.'}).full)
|
||||
).toEqual(['Berg,', 'Anna', 'b. 1900', 'd. 1980'])
|
||||
})
|
||||
|
||||
it('redraws cards when symbols change', () => {
|
||||
const svg = create('svg')
|
||||
const nodes = svg
|
||||
.selectAll('g')
|
||||
.data([{person: people.full}])
|
||||
.join('g')
|
||||
nodes.append('g').attr('class', 'person-card')
|
||||
|
||||
const draw = symbols =>
|
||||
drawChangedCards(nodes, {
|
||||
...symbols,
|
||||
nameDisplayFormat: chartNameDisplayFormat.surnameThenGiven,
|
||||
})
|
||||
|
||||
draw({birthSymbol: '*', deathSymbol: '†'})
|
||||
draw({birthSymbol: 'b.', deathSymbol: 'd.'})
|
||||
|
||||
expect(texts(nodes.node())).toEqual(['Berg,', 'Anna', 'b. 1900', 'd. 1980'])
|
||||
})
|
||||
|
||||
it('shortens names that do not fit', () => {
|
||||
const [surname] = texts(renderCards().longName)
|
||||
expect(surname.endsWith('…')).toBe(true)
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import {describe, it, expect} from 'vitest'
|
||||
import {renderPersonDates} from '../../src/components/personListUtils.js'
|
||||
|
||||
describe('renderPersonDates', () => {
|
||||
it('uses the default symbols when none are passed', () => {
|
||||
const profile = {birth: {date: '1990'}, death: {date: '2020'}}
|
||||
const result = renderPersonDates(profile)
|
||||
expect(result.values).toContain('∗ 1990')
|
||||
expect(result.values[1]).toContain('† 2020')
|
||||
})
|
||||
|
||||
it('uses custom symbols when passed', () => {
|
||||
const profile = {birth: {date: '1990'}, death: {date: '2020'}}
|
||||
const result = renderPersonDates(profile, {
|
||||
birthSymbol: 'b.',
|
||||
deathSymbol: 'd.',
|
||||
})
|
||||
expect(result.values).toContain('b. 1990')
|
||||
expect(result.values[1]).toContain('d. 2020')
|
||||
})
|
||||
})
|
||||
@@ -1,5 +1,6 @@
|
||||
import {describe, it, expect, vi} from 'vitest'
|
||||
import {GrampsjsViewSettingsUser} from '../../src/views/GrampsjsViewSettingsUser.js'
|
||||
import {SYMBOL_SET_DEFAULT, SYMBOL_SET_TEXT} from '../../src/symbols.js'
|
||||
|
||||
const makeElement = ({
|
||||
username,
|
||||
@@ -127,3 +128,25 @@ describe('user profile settings', () => {
|
||||
expect(element._fetchOwnUserDetails).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('genealogical symbols setting', () => {
|
||||
it('saves the selected symbol set', () => {
|
||||
const element = new GrampsjsViewSettingsUser()
|
||||
const updateSettings = vi.fn()
|
||||
element.appState = {settings: {}, updateSettings}
|
||||
|
||||
element._handleSymbolsSelected({target: {value: SYMBOL_SET_TEXT}})
|
||||
|
||||
expect(updateSettings).toHaveBeenCalledWith({symbolSet: SYMBOL_SET_TEXT})
|
||||
})
|
||||
|
||||
it('falls back to the default set for an invalid value', () => {
|
||||
const element = new GrampsjsViewSettingsUser()
|
||||
const updateSettings = vi.fn()
|
||||
element.appState = {settings: {}, updateSettings}
|
||||
|
||||
element._handleSymbolsSelected({target: {value: 'nonsense'}})
|
||||
|
||||
expect(updateSettings).toHaveBeenCalledWith({symbolSet: SYMBOL_SET_DEFAULT})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import {describe, it, expect} from 'vitest'
|
||||
import {
|
||||
getSymbols,
|
||||
SYMBOL_SET_DEFAULT,
|
||||
SYMBOL_SET_TEXT,
|
||||
SYMBOL_SET_OPTIONS,
|
||||
} from '../../src/symbols.js'
|
||||
|
||||
describe('getSymbols', () => {
|
||||
it('returns the default unicode symbols when settings is undefined', () => {
|
||||
expect(getSymbols(undefined)).toEqual({
|
||||
birthSymbol: '∗',
|
||||
deathSymbol: '†',
|
||||
marriageSymbol: '⚭',
|
||||
divorceSymbol: '⚮',
|
||||
})
|
||||
})
|
||||
|
||||
it('returns the default unicode symbols when symbolSet is not set', () => {
|
||||
expect(getSymbols({})).toEqual(getSymbols({symbolSet: SYMBOL_SET_DEFAULT}))
|
||||
})
|
||||
|
||||
it('returns text abbreviations for the "text" set', () => {
|
||||
expect(getSymbols({symbolSet: SYMBOL_SET_TEXT})).toEqual({
|
||||
birthSymbol: 'b.',
|
||||
deathSymbol: 'd.',
|
||||
marriageSymbol: 'm.',
|
||||
divorceSymbol: 'div.',
|
||||
})
|
||||
})
|
||||
|
||||
it('translates text abbreviations when a translator is provided', () => {
|
||||
const translations = {
|
||||
'birth abbreviation\u0004b.': 'geb.',
|
||||
'death abbreviation\u0004d.': 'gest.',
|
||||
'marriage abbreviation\u0004m.': 'verh.',
|
||||
'Divorce abbreviation\u0004div.': 'gesch.',
|
||||
}
|
||||
expect(
|
||||
getSymbols({symbolSet: SYMBOL_SET_TEXT}, value => translations[value])
|
||||
).toEqual({
|
||||
birthSymbol: 'geb.',
|
||||
deathSymbol: 'gest.',
|
||||
marriageSymbol: 'verh.',
|
||||
divorceSymbol: 'gesch.',
|
||||
})
|
||||
})
|
||||
|
||||
it('falls back to visible abbreviations when contextual translations are missing', () => {
|
||||
expect(getSymbols({symbolSet: SYMBOL_SET_TEXT}, () => undefined)).toEqual({
|
||||
birthSymbol: 'b.',
|
||||
deathSymbol: 'd.',
|
||||
marriageSymbol: 'm.',
|
||||
divorceSymbol: 'div.',
|
||||
})
|
||||
})
|
||||
|
||||
it('falls back to the default set for an unknown symbolSet', () => {
|
||||
expect(getSymbols({symbolSet: 'nonsense'})).toEqual(
|
||||
getSymbols({symbolSet: SYMBOL_SET_DEFAULT})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('SYMBOL_SET_OPTIONS', () => {
|
||||
it('has one option per symbol set, with a matching getSymbols() result', () => {
|
||||
SYMBOL_SET_OPTIONS.forEach(({value}) => {
|
||||
expect(getSymbols({symbolSet: value})).toBeTruthy()
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -2,6 +2,7 @@ import {describe, it, expect} from 'vitest'
|
||||
import {render, html} from 'lit'
|
||||
import {
|
||||
translate,
|
||||
objectDetail,
|
||||
personTitleFromProfile,
|
||||
personDisplayName,
|
||||
reportSelectItemLabel,
|
||||
@@ -46,6 +47,17 @@ describe('translate', () => {
|
||||
expect(translate(strings, 'Hello')).to.equal('Hola')
|
||||
})
|
||||
|
||||
it('uses the configured text birth symbol in object detail', () => {
|
||||
const result = objectDetail(
|
||||
'person',
|
||||
{profile: {birth: {date: '1990', place: 'Paris'}}},
|
||||
{},
|
||||
{symbolSet: 'text'}
|
||||
)
|
||||
expect(result).to.include('b. 1990')
|
||||
expect(result).not.to.include('∗ 1990')
|
||||
})
|
||||
|
||||
it('strips leading underscore', () => {
|
||||
expect(translate(strings, '_Save')).to.equal('Guardar')
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user