Skip to content

Commit

Permalink
Fixes #55: Chinese surname and first name are in the wrong position
Browse files Browse the repository at this point in the history
  • Loading branch information
magicsunday committed Mar 17, 2024
1 parent 5dbbd2c commit 6600127
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 73 deletions.
2 changes: 1 addition & 1 deletion resources/js/descendants-chart.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion resources/js/modules/custom/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,4 @@ import {Node} from "../lib/d3";
* @property {Boolean} isPreferred
* @property {Boolean} isLastName
* @property {Boolean} isNameRtl
*/
*/
177 changes: 106 additions & 71 deletions resources/js/modules/lib/tree/name.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* This file is part of the package magicsunday/webtrees-descendants-chart.
* This file is part of the package magicsunday/webtrees-descendant-chart.
*
* For the full copyright and license information, please read the
* LICENSE file distributed with this source code.
Expand All @@ -10,13 +10,14 @@ import OrientationTopBottom from "../chart/orientation/orientation-topBottom";
import OrientationBottomTop from "../chart/orientation/orientation-bottomTop";
import OrientationLeftRight from "../chart/orientation/orientation-leftRight";
import OrientationRightLeft from "../chart/orientation/orientation-rightLeft";
import * as d3 from "../d3.js";

/**
* The class handles the creation of the tree.
*
* @author Rico Sonntag <mail@ricosonntag.de>
* @license https://opensource.org/licenses/GPL-3.0 GNU General Public License v3.0
* @link https://github.com/magicsunday/webtrees-descendants-chart/
* @link https://github.com/magicsunday/webtrees-descendant-chart/
*/
export default class Name
{
Expand Down Expand Up @@ -53,43 +54,42 @@ export default class Name
if ((this._orientation instanceof OrientationTopBottom)
|| (this._orientation instanceof OrientationBottomTop)
) {
const that = this;

const enter = name.selectAll("text")
.data(datum => [
{
data: datum.data,
isRtl: datum.data.data.isNameRtl,
isAltRtl: datum.data.data.isAltRtl,
// Always arrange the text at the same position regardless if an image is displayed or not
withImage: true
}
])
.enter();

enter
.call((g) => {
const text = g.append("text")
.attr("class", "wt-chart-box-name")
.attr("text-anchor", "middle")
.attr("direction", d => d.isRtl ? "rtl" : "ltr")
.attr("alignment-baseline", "central")
.attr("y", this._text.y - 5);

this.addNameElements(
text,
datum => this.createNamesData(text, datum, true, false)
);
})
.call((g) => {
const text = g.append("text")
.attr("class", "wt-chart-box-name")
.attr("text-anchor", "middle")
.attr("direction", d => d.isRtl ? "rtl" : "ltr")
.attr("alignment-baseline", "central")
.attr("y", this._text.y + 15);

this.addNameElements(
text,
datum => this.createNamesData(text, datum, false, true)
);
.each(function (d) {
const element = d3.select(this);
const nameGroups = that.createNamesData(d);

nameGroups.forEach((nameGroup, index) => {
const text = element.append("text")
.attr("class", "wt-chart-box-name")
.attr("text-anchor", "middle")
.attr("direction", d => d.isRtl ? "rtl" : "ltr")
.attr("alignment-baseline", "central")
.attr("y", that._text.y - 5 + (index * 20));

that.addNameElements(
text,
that.truncateNamesData(
text,
nameGroup,
d.withImage
)
);
});
});

// Add alternative name if present
Expand Down Expand Up @@ -144,7 +144,20 @@ export default class Name

this.addNameElements(
text,
datum => this.createNamesData(text, datum, true, true)
(datum) => {
const nameGroups = this.createNamesData(datum);

return this.truncateNamesData(
text,
// Merge the firstname and lastname groups together,
// as we display the whole name in one line
[
...nameGroups[0],
...nameGroups[1],
],
datum.withImage
)
}
);
});

Expand Down Expand Up @@ -207,67 +220,89 @@ export default class Name
}

/**
* Creates the data array for the names.
* Creates the data array for the names in top/bottom layout.
*
* @param {Object} parent
* @param {NameElementData} datum
* @param {Boolean} addFirstNames
* @param {Boolean} addLastNames
*
* @return {LabelElementData[]}
* @return {LabelElementData[][]}
*
* @private
*/
createNamesData(parent, datum, addFirstNames, addLastNames)
createNamesData(datum)
{
/** @var {LabelElementData[]} names */
let names = [];
/** @var {LabelElementData[][]} names */
let names = {};
/** @var {LabelElementData[]} firstnames */
let firstnames = {};
/** @var {LabelElementData[]} lastnames */
let lastnames = {};
let minPosFirstnames = Number.MAX_SAFE_INTEGER;
let minPosLastnames = Number.MAX_SAFE_INTEGER;

// Iterate over the individual name components and determine their position in the overall
// name and insert the component at the corresponding position in the result object.
for (let i in datum.data.data.firstNames) {
const pos = datum.data.data.name.indexOf(datum.data.data.firstNames[i]);

if (pos !== -1) {
if (pos < minPosFirstnames) {
minPosFirstnames = pos;
}

if (addFirstNames === true) {
names = names.concat(
datum.data.data.firstNames.map((firstName) => {
return {
label: firstName,
isPreferred: firstName === datum.data.data.preferredName,
isLastName: false,
isNameRtl: datum.data.data.isNameRtl
}
})
);
firstnames[pos] = {
label: datum.data.data.firstNames[i],
isPreferred: datum.data.data.firstNames[i] === datum.data.data.preferredName,
isLastName: false,
isNameRtl: datum.data.data.isNameRtl
};
}
}

if (addLastNames === true) {
// Append the last names
names = names.concat(
datum.data.data.lastNames.map((lastName) => {
return {
label: lastName,
isPreferred: false,
isLastName: true,
isNameRtl: datum.data.data.isNameRtl
}
})
);
names[minPosFirstnames] = Object.values(firstnames);

for (let i in datum.data.data.lastNames) {
const pos = datum.data.data.name.indexOf(datum.data.data.lastNames[i]);

if (pos !== -1) {
if (pos < minPosLastnames) {
minPosLastnames = pos;
}

lastnames[pos] = {
label: datum.data.data.lastNames[i],
isPreferred: false,
isLastName: true,
isNameRtl: datum.data.data.isNameRtl
};
}
}

// // If both first and last names are empty, add the full name as an alternative
// if (!datum.data.data.firstNames.length
// && !datum.data.data.lastNames.length
// ) {
// names = names.concat([{
// label: datum.data.data.name,
// isPreferred: false,
// isLastName: false
// }]);
// }
names[minPosLastnames] = Object.values(lastnames);

// Extract the values (keys doesn't matter anymore)
return Object.values(names);
}

/**
* Creates the data array for the names.
*
* @param {Object} parent
* @param {LabelElementData[]} names
* @param {Boolean} withImage
*
* @return {LabelElementData[]}
*
* @private
*/
truncateNamesData(parent, names, withImage)
{
const fontSize = parent.style("font-size");
const fontWeight = parent.style("font-weight");

// The total available width that the text can occupy
let availableWidth = this._text.width;

if (datum.withImage) {
if (withImage) {
if ((this._orientation instanceof OrientationLeftRight)
|| (this._orientation instanceof OrientationRightLeft)
) {
Expand Down Expand Up @@ -358,7 +393,7 @@ export default class Name
return name;
})
.map((name) => {
// Afterward, the preferred ones if text takes still too much space
// Afterward, the preferred ones, if text takes still too much space
if (name.isPreferred === true) {
if (this.measureText(text, fontSize, fontWeight) > availableWidth) {
// Keep only the first letter
Expand Down

0 comments on commit 6600127

Please sign in to comment.