fill-box web component

This commit is contained in:
Azgaar 2022-01-22 17:47:56 +03:00
parent 002558785a
commit 8184b416b9
13 changed files with 226 additions and 138 deletions

74
components/fill-box.js Normal file
View file

@ -0,0 +1,74 @@
// fill-box cannot use Shadow DOM as it needs access to svg hatches
// append stylesheet
{
const style = `
fill-box:not([disabled]) {
cursor: pointer;
}
fill-box > svg {
aspect-ratio: 1;
vertical-align: middle;
pointer-events: none;
}
fill-box > svg > rect {
stroke: #666666;
stroke-width: 2;
}`;
const styleElement = document.createElement("style");
styleElement.setAttribute("type", "text/css");
styleElement.innerHTML = style;
document.head.appendChild(styleElement);
}
{
const template = document.createElement("template");
template.innerHTML = `
<svg>
<rect x="0" y="0" width="100%" height="100%">
</svg>
`;
class FillBox extends HTMLElement {
constructor() {
super();
this.appendChild(template.content.cloneNode(true));
this.querySelector("rect")?.setAttribute("fill", this.fill);
this.querySelector("svg")?.setAttribute("width", this.size);
}
static showTip() {
tip(this.tip);
}
connectedCallback() {
this.addEventListener("mousemove", this.constructor.showTip);
}
disconnectedCallback() {
this.removeEventListener("mousemove", this.constructor.showTip);
}
get fill() {
return this.getAttribute("fill") || "#333";
}
set fill(newFill) {
this.setAttribute("fill", newFill);
this.querySelector("rect")?.setAttribute("fill", newFill);
}
get size() {
return this.getAttribute("size") || "1em";
}
get tip() {
return this.dataset.tip || "Fill style. Click to change";
}
}
customElements.define("fill-box", FillBox);
}

View file

@ -1531,6 +1531,11 @@ div.states > .riverType {
cursor: pointer;
}
.changeRelations > * {
pointer-events: none;
cursor: pointer;
}
#diplomacySelect {
width: 5em;
margin: 0.1em 0 0 -0.3em;
@ -1668,11 +1673,6 @@ div.states > div.biomeArea {
width: 5em;
}
rect.fillRect {
stroke: #666666;
stroke-width: 2;
}
#militaryHeader > div,
#regimentsHeader > div {
width: 5.2em;

View file

@ -4443,5 +4443,8 @@
<script defer src="libs/jquery.ui.touch-punch.min.js"></script>
<script defer src="libs/pell.min.js"></script>
<script defer src="libs/jszip.min.js"></script>
<!-- Web Components -->
<script defer src="components/fill-box.js"></script>
</body>
</html>

View file

@ -141,8 +141,8 @@ class Battle {
const state = pack.states[regiment.state];
const distance = (Math.hypot(this.y - regiment.by, this.x - regiment.bx) * distanceScaleInput.value) | 0; // distance between regiment and its base
const color = state.color[0] === "#" ? state.color : "#999";
const icon = `<svg width="1.4em" height="1.4em" style="margin-bottom: -.6em">
<rect x="0" y="0" width="100%" height="100%" fill="${color}" class="fillRect"></rect>
const icon = `<svg width="1.4em" height="1.4em" style="margin-bottom: -.6em; stroke: #333">
<rect x="0" y="0" width="100%" height="100%" fill="${color}"></rect>
<text x="0" y="1.04em" style="">${regiment.icon}</text></svg>`;
const body = `<tbody id="battle${state.i}-${regiment.i}">`;
@ -183,7 +183,7 @@ class Battle {
dist = added ? "0 " + distanceUnitInput.value : distance(r);
return `<div ${added ? "class='inactive'" : ""} data-s=${s.i} data-i=${r.i} data-state=${s.name} data-regiment=${r.name}
data-total=${r.a} data-distance=${dist} data-tip="Click to select regiment">
<svg width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${s.color}" class="fillRect"></svg>
<svg width=".9em" height=".9em" style="margin-bottom:-1px; stroke: #333"><rect x="0" y="0" width="100%" height="100%" fill="${s.color}" ></svg>
<div style="width:6em">${s.name.slice(0, 11)}</div>
<div style="width:1.2em">${r.icon}</div>
<div style="width:13em">${r.name.slice(0, 24)}</div>

View file

@ -37,9 +37,9 @@ function editBiomes() {
document.getElementById("biomesExport").addEventListener("click", downloadBiomesData);
body.addEventListener("click", function (ev) {
const el = ev.target,
cl = el.classList;
if (cl.contains("fillRect")) biomeChangeColor(el);
const el = ev.target;
const cl = el.classList;
if (el.tagName === "FILL-BOX") biomeChangeColor(el);
else if (cl.contains("icon-info-circled")) openWiki(el);
else if (cl.contains("icon-trash-empty")) removeCustomBiome(el);
if (customization === 6) selectBiomeOnLineClick(el);
@ -94,9 +94,7 @@ function editBiomes() {
lines += `<div class="states biomes" data-id="${i}" data-name="${b.name[i]}" data-habitability="${b.habitability[i]}"
data-cells=${b.cells[i]} data-area=${area} data-population=${population} data-color=${b.color[i]}>
<svg data-tip="Biomes fill style. Click to change" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${
b.color[i]
}" class="fillRect pointer"></svg>
<fill-box fill="${b.color[i]}"></fill-box>
<input data-tip="Biome name. Click and type to change" class="biomeName" value="${b.name[i]}" autocorrect="off" spellcheck="false">
<span data-tip="Biome habitability percent" class="hide">%</span>
<input data-tip="Biome habitability percent. Click and set new value to change" type="number" min=0 max=9999 class="biomeHabitability hide" value=${
@ -158,15 +156,15 @@ function editBiomes() {
function biomeChangeColor(el) {
const currentFill = el.getAttribute("fill");
const biome = +el.parentNode.parentNode.dataset.id;
const biome = +el.parentNode.dataset.id;
const callback = function (fill) {
el.setAttribute("fill", fill);
biomesData.color[biome] = fill;
const callback = newFill => {
el.fill = newFill;
biomesData.color[biome] = newFill;
biomes
.select("#biome" + biome)
.attr("fill", fill)
.attr("stroke", fill);
.attr("fill", newFill)
.attr("stroke", newFill);
};
openPicker(currentFill, callback);
@ -270,7 +268,7 @@ function editBiomes() {
const unit = areaUnit.value === "square" ? " " + distanceUnitInput.value + "²" : " " + areaUnit.value;
const line = `<div class="states biomes" data-id="${i}" data-name="${b.name[i]}" data-habitability=${b.habitability[i]} data-cells=0 data-area=0 data-population=0 data-color=${b.color[i]}>
<svg data-tip="Biomes fill style. Click to change" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${b.color[i]}" class="fillRect pointer"></svg>
<fill-box fill="${b.color[i]}"></fill-box>
<input data-tip="Biome name. Click and type to change" class="biomeName" value="${b.name[i]}" autocorrect="off" spellcheck="false">
<span data-tip="Biome habitability percent" class="hide">%</span>
<input data-tip="Biome habitability percent. Click and set new value to change" type="number" min=0 max=9999 step=1 class="biomeHabitability hide" value=${b.habitability[i]}>

View file

@ -108,9 +108,7 @@ function editCultures() {
lines += `<div class="states cultures" data-id=${c.i} data-name="${c.name}" data-color="${c.color}" data-cells=${c.cells}
data-area=${area} data-population=${population} data-base=${c.base} data-type=${c.type} data-expansionism=${c.expansionism} data-emblems="${c.shield}">
<svg data-tip="Culture fill style. Click to change" width=".9em" height=".9em" style="margin-bottom:-1px">
<rect x="0" y="0" width="100%" height="100%" fill="${c.color}" class="fillRect pointer">
</svg>
<fill-box fill="${c.color}"></fill-box>
<input data-tip="Culture name. Click and type to change" class="cultureName" value="${c.name}" autocorrect="off" spellcheck="false">
<span data-tip="Regenerate culture name" class="icon-cw hiddenIcon" style="visibility: hidden"></span>
<span data-tip="Cells count" class="icon-check-empty hide"></span>
@ -148,7 +146,7 @@ function editCultures() {
body.querySelectorAll("div.cultures").forEach(el => el.addEventListener("mouseenter", ev => cultureHighlightOn(ev)));
body.querySelectorAll("div.cultures").forEach(el => el.addEventListener("mouseleave", ev => cultureHighlightOff(ev)));
body.querySelectorAll("div.states").forEach(el => el.addEventListener("click", selectCultureOnLineClick));
body.querySelectorAll("rect.fillRect").forEach(el => el.addEventListener("click", cultureChangeColor));
body.querySelectorAll("fill-box").forEach(el => el.addEventListener("click", cultureChangeColor));
body.querySelectorAll("div > input.cultureName").forEach(el => el.addEventListener("input", cultureChangeName));
body.querySelectorAll("div > span.icon-cw").forEach(el => el.addEventListener("click", cultureRegenerateName));
body.querySelectorAll("div > input.statePower").forEach(el => el.addEventListener("input", cultureChangeExpansionism));
@ -248,16 +246,16 @@ function editCultures() {
function cultureChangeColor() {
const el = this;
const currentFill = el.getAttribute("fill");
const culture = +el.parentNode.parentNode.dataset.id;
const culture = +el.parentNode.dataset.id;
const callback = function (fill) {
el.setAttribute("fill", fill);
pack.cultures[culture].color = fill;
const callback = newFill => {
el.fill = newFill;
pack.cultures[culture].color = newFill;
cults
.select("#culture" + culture)
.attr("fill", fill)
.attr("stroke", fill);
debug.select("#cultureCenter" + culture).attr("fill", fill);
.attr("fill", newFill)
.attr("stroke", newFill);
debug.select("#cultureCenter" + culture).attr("fill", newFill);
};
openPicker(currentFill, callback);

View file

@ -101,10 +101,8 @@ function editDiplomacy() {
lines += `<div class="states" data-id=${state.i} data-name="${name}" data-relations="${relation}">
<svg data-tip="${tipSelect}" class="coaIcon" viewBox="0 0 200 200"><use href="#stateCOA${state.i}"></use></svg>
<div data-tip="${tipSelect}" style="width: 12em">${name}</div>
<div data-tip="${tipChange}" class="changeRelations pointer" style="width: 6em">
<svg width=".9em" height=".9em" style="margin-bottom:-1px">
<rect x="0" y="0" width="100%" height="100%" fill="${color}" class="fillRect"></rect>
</svg>
<div data-tip="${tipChange}" class="changeRelations" style="width: 6em">
<fill-box fill="${color}" size=".9em"></fill-box>
${relation}
</div>
</div>`;
@ -195,9 +193,7 @@ function editDiplomacy() {
([relation, {color, inText, tip}]) =>
`<div style="margin-block: 0.2em" data-tip="${tip}"><label class="pointer">
<input type="radio" name="relationSelect" value="${relation}" ${currentRelation === relation && "checked"} >
<svg width=".9em" height=".9em" style="margin-bottom:-1px">
<rect x="0" y="0" width="100%" height="100%" fill="${color}" class="fillRect" />
</svg>
<fill-box fill="${color}" size=".8em"></fill-box>
${inText}
</label></div>`
)

View file

@ -75,12 +75,9 @@ function overviewMilitary() {
const sortData = options.military.map(u => `data-${u.name}="${getForces(u)}"`).join(" ");
const lineData = options.military.map(u => `<div data-type="${u.name}" data-tip="State ${u.name} units number">${getForces(u)}</div>`).join(" ");
lines += `<div class="states" data-id=${s.i} data-state="${
s.name
}" ${sortData} data-total="${total}" data-population="${population}" data-rate="${rate}" data-alert="${s.alert}">
<svg data-tip="${s.fullName}" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${
s.color
}" class="fillRect"></svg>
lines += `<div class="states" data-id=${s.i} data-state="${s.name}" ${sortData} data-total="${total}"
data-population="${population}" data-rate="${rate}" data-alert="${s.alert}">
<fill-box data-tip="${s.fullName}" fill="${s.color}" disabled></fill-box>
<input data-tip="${s.fullName}" style="width:6em" value="${s.name}" readonly>
${lineData}
<div data-type="total" data-tip="Total state military personnel (considering crew)" style="font-weight: bold">${si(total)}</div>

View file

@ -44,7 +44,8 @@ function editProvinces() {
cl = el.classList,
line = el.parentNode,
p = +line.dataset.id;
if (cl.contains("fillRect")) changeFill(el);
if (el.tagName === "FILL-BOX") changeFill(el);
else if (cl.contains("name")) editProvinceName(p);
else if (cl.contains("coaIcon")) editEmblem("province", "provinceCOA" + p, pack.provinces[p]);
else if (cl.contains("icon-star-empty")) capitalZoomIn(p);
@ -133,9 +134,7 @@ function editProvinces() {
lines += `<div class="states" data-id=${p.i} data-name="${p.name}" data-form="${p.formName}" data-color="${
p.color
}" data-capital="${capital}" data-state="${stateName}" data-area=${area} data-population=${population}>
<svg data-tip="Province fill style. Click to change" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${
p.color
}" class="fillRect pointer"></svg>
<fill-box fill="${p.color}"></fill-box>
<input data-tip="Province name. Click to change" class="name pointer" value="${p.name}" readonly>
<svg data-tip="Click to show and edit province emblem" class="coaIcon pointer hide" viewBox="0 0 200 200"><use href="#provinceCOA${p.i}"></use></svg>
<input data-tip="Province form name. Click to change" class="name pointer hide" value="${p.formName}" readonly>
@ -215,14 +214,14 @@ function editProvinces() {
function changeFill(el) {
const currentFill = el.getAttribute("fill");
const p = +el.parentNode.parentNode.dataset.id;
const p = +el.parentNode.dataset.id;
const callback = function (fill) {
el.setAttribute("fill", fill);
pack.provinces[p].color = fill;
const callback = newFill => {
el.fill = newFill;
pack.provinces[p].color = newFill;
const g = provs.select("#provincesBody");
g.select("#province" + p).attr("fill", fill);
g.select("#province-gap" + p).attr("stroke", fill);
g.select("#province" + p).attr("fill", newFill);
g.select("#province-gap" + p).attr("stroke", newFill);
};
openPicker(currentFill, callback);

View file

@ -14,7 +14,9 @@ function overviewRegiments(state) {
updateHeaders();
$("#regimentsOverview").dialog({
title: "Regiments Overview", resizable: false, width: fitContent(),
title: "Regiments Overview",
resizable: false,
width: fitContent(),
position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}
});
@ -31,11 +33,13 @@ function overviewRegiments(state) {
header.querySelectorAll(".removable").forEach(el => el.remove());
const insert = html => document.getElementById("regimentsTotal").insertAdjacentHTML("beforebegin", html);
for (const u of options.military) {
const label = capitalize(u.name.replace(/_/g, ' '));
const label = capitalize(u.name.replace(/_/g, " "));
insert(`<div data-tip="Regiment ${u.name} units number. Click to sort" class="sortable removable" data-sortby="${u.name}">${label}&nbsp;</div>`);
}
header.querySelectorAll(".removable").forEach(function(e) {
e.addEventListener("click", function() {sortLines(this);});
header.querySelectorAll(".removable").forEach(function (e) {
e.addEventListener("click", function () {
sortLines(this);
});
});
}
@ -51,11 +55,13 @@ function overviewRegiments(state) {
if (state !== -1 && s.i !== state) continue; // specific state is selected
for (const r of s.military) {
const sortData = options.military.map(u => `data-${u.name}=${r.u[u.name]||0}`).join(" ");
const lineData = options.military.map(u => `<div data-type="${u.name}" data-tip="${capitalize(u.name)} units number">${r.u[u.name]||0}</div>`).join(" ");
const sortData = options.military.map(u => `data-${u.name}=${r.u[u.name] || 0}`).join(" ");
const lineData = options.military
.map(u => `<div data-type="${u.name}" data-tip="${capitalize(u.name)} units number">${r.u[u.name] || 0}</div>`)
.join(" ");
lines += `<div class="states" data-id=${r.i} data-s="${s.i}" data-state="${s.name}" data-name="${r.name}" ${sortData} data-total="${r.a}">
<svg data-tip="${s.fullName}" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${s.color}" class="fillRect"></svg>
<fill-box data-tip="${s.fullName}" fill="${s.color}" disabled></fill-box>
<input data-tip="${s.fullName}" style="width:6em" value="${s.name}" readonly>
<span data-tip="Regiment's emblem" style="width:1em">${r.icon}</span>
<input data-tip="Regiment's name" style="width:13em" value="${r.name}" readonly>
@ -70,12 +76,15 @@ function overviewRegiments(state) {
lines += `<div id="regimentsTotalLine" class="totalLine" data-tip="Total of all displayed regiments">
<div style="width: 21em; margin-left: 1em">Regiments: ${regiments.length}</div>
${options.military.map(u => `<div style="width:5em">${si(d3.sum(regiments.map(r => r.u[u.name]||0)))}</div>`).join(" ")}
${options.military.map(u => `<div style="width:5em">${si(d3.sum(regiments.map(r => r.u[u.name] || 0)))}</div>`).join(" ")}
<div style="width:5em">${si(d3.sum(regiments.map(r => r.a)))}</div>
</div>`;
body.insertAdjacentHTML("beforeend", lines);
if (body.dataset.type === "percentage") {body.dataset.type = "absolute"; togglePercentageMode();}
if (body.dataset.type === "percentage") {
body.dataset.type = "absolute";
togglePercentageMode();
}
applySorting(regimentsHeader);
// add listeners
@ -87,7 +96,7 @@ function overviewRegiments(state) {
const filter = document.getElementById("regimentsFilter");
filter.options.length = 0; // remove all options
filter.options.add(new Option(`all`, -1, false, state === -1));
const statesSorted = pack.states.filter(s => s.i && !s.removed).sort((a, b) => (a.name > b.name) ? 1 : -1);
const statesSorted = pack.states.filter(s => s.i && !s.removed).sort((a, b) => (a.name > b.name ? 1 : -1));
statesSorted.forEach(s => filter.options.add(new Option(s.name, s.i, false, s.i == state)));
}
@ -108,19 +117,20 @@ function overviewRegiments(state) {
if (body.dataset.type === "absolute") {
body.dataset.type = "percentage";
const lines = body.querySelectorAll(":scope > div:not(.totalLine)");
const array = Array.from(lines), cache = [];
const array = Array.from(lines),
cache = [];
const total = function(type) {
const total = function (type) {
if (cache[type]) cache[type];
cache[type] = d3.sum(array.map(el => +el.dataset[type]));
return cache[type];
}
};
lines.forEach(function(el) {
el.querySelectorAll("div").forEach(function(div) {
lines.forEach(function (el) {
el.querySelectorAll("div").forEach(function (div) {
const type = div.dataset.type;
if (type === "rate") return;
div.textContent = total(type) ? rn(+el.dataset[type] / total(type) * 100) + "%" : "0%";
div.textContent = total(type) ? rn((+el.dataset[type] / total(type)) * 100) + "%" : "0%";
});
});
} else {
@ -145,15 +155,19 @@ function overviewRegiments(state) {
function addRegimentOnClick() {
const state = +regimentsFilter.value;
if (state === -1) {tip("Please select state from the list", false, "error"); return;}
if (state === -1) {
tip("Please select state from the list", false, "error");
return;
}
const point = d3.mouse(this);
const cell = findCell(point[0], point[1]);
const x = pack.cells.p[cell][0], y = pack.cells.p[cell][1];
const x = pack.cells.p[cell][0],
y = pack.cells.p[cell][1];
const military = pack.states[state].military;
const i = military.length ? last(military).i + 1 : 0;
const n = +(pack.cells.h[cell] < 20); // naval or land
const reg = {a:0, cell, i, n, u:{}, x, y, bx:x, by:y, state, icon:"🛡️"};
const reg = {a: 0, cell, i, n, u: {}, x, y, bx: x, by: y, state, icon: "🛡️"};
reg.name = Military.getName(reg, military);
military.push(reg);
Military.generateNote(reg, pack.states[state]); // add legend
@ -163,9 +177,9 @@ function overviewRegiments(state) {
function downloadRegimentsData() {
const units = options.military.map(u => u.name);
let data = "State,Id,Name,"+units.map(u => capitalize(u)).join(",")+",Total\n"; // headers
let data = "State,Id,Name," + units.map(u => capitalize(u)).join(",") + ",Total\n"; // headers
body.querySelectorAll(":scope > div:not(.totalLine)").forEach(function(el) {
body.querySelectorAll(":scope > div:not(.totalLine)").forEach(function (el) {
data += el.dataset.state + ",";
data += el.dataset.id + ",";
data += el.dataset.name + ",";
@ -176,5 +190,4 @@ function overviewRegiments(state) {
const name = getFileName("Regiments") + ".csv";
downloadFile(data, name);
}
}

View file

@ -79,7 +79,7 @@ function editReligions() {
if (r.i) {
lines += `<div class="states religions" data-id=${r.i} data-name="${r.name}" data-color="${r.color}" data-area=${area}
data-population=${population} data-type=${r.type} data-form=${r.form} data-deity="${r.deity ? r.deity : ""}" data-expansionism=${r.expansionism}>
<svg data-tip="Religion fill style. Click to change" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${r.color}" class="fillRect pointer"></svg>
<fill-box fill="${r.color}"></fill-box>
<input data-tip="Religion name. Click and type to change" class="religionName" value="${r.name}" autocorrect="off" spellcheck="false">
<select data-tip="Religion type" class="religionType">${getTypeOptions(r.type)}</select>
<input data-tip="Religion form" class="religionForm hide" value="${r.form}" autocorrect="off" spellcheck="false">
@ -93,7 +93,9 @@ function editReligions() {
</div>`;
} else {
// No religion (neutral) line
lines += `<div class="states" data-id=${r.i} data-name="${r.name}" data-color="" data-area=${area} data-population=${population} data-type="" data-form="" data-deity="" data-expansionism="">
lines += `<div class="states" data-id=${r.i} data-name="${
r.name
}" data-color="" data-area=${area} data-population=${population} data-type="" data-form="" data-deity="" data-expansionism="">
<svg width="9" height="9" class="placeholder"></svg>
<input data-tip="Religion name. Click and type to change" class="religionName italic" value="${r.name}" autocorrect="off" spellcheck="false">
<select data-tip="Religion type" class="religionType placeholder">${getTypeOptions(r.type)}</select>
@ -124,7 +126,7 @@ function editReligions() {
body.querySelectorAll("div.religions").forEach(el => el.addEventListener("mouseenter", ev => religionHighlightOn(ev)));
body.querySelectorAll("div.religions").forEach(el => el.addEventListener("mouseleave", ev => religionHighlightOff(ev)));
body.querySelectorAll("div.states").forEach(el => el.addEventListener("click", selectReligionOnLineClick));
body.querySelectorAll("rect.fillRect").forEach(el => el.addEventListener("click", religionChangeColor));
body.querySelectorAll("fill-box").forEach(el => el.addEventListener("click", religionChangeColor));
body.querySelectorAll("div > input.religionName").forEach(el => el.addEventListener("input", religionChangeName));
body.querySelectorAll("div > select.religionType").forEach(el => el.addEventListener("change", religionChangeType));
body.querySelectorAll("div > input.religionForm").forEach(el => el.addEventListener("input", religionChangeForm));
@ -215,13 +217,13 @@ function editReligions() {
function religionChangeColor() {
const el = this;
const currentFill = el.getAttribute("fill");
const religion = +el.parentNode.parentNode.dataset.id;
const religion = +el.parentNode.dataset.id;
const callback = function (fill) {
el.setAttribute("fill", fill);
pack.religions[religion].color = fill;
relig.select("#religion" + religion).attr("fill", fill);
debug.select("#religionsCenter" + religion).attr("fill", fill);
const callback = newFill => {
el.fill = newFill;
pack.religions[religion].color = newFill;
relig.select("#religion" + religion).attr("fill", newFill);
debug.select("#religionsCenter" + religion).attr("fill", newFill);
};
openPicker(currentFill, callback);
@ -459,7 +461,13 @@ function editReligions() {
// prepare svg
alertMessage.innerHTML = "<div id='religionInfo' class='chartInfo'>&#8205;</div>";
const svg = d3.select("#alertMessage").insert("svg", "#religionInfo").attr("id", "hierarchy").attr("width", width).attr("height", height).style("text-anchor", "middle");
const svg = d3
.select("#alertMessage")
.insert("svg", "#religionInfo")
.attr("id", "hierarchy")
.attr("width", width)
.attr("height", height)
.style("text-anchor", "middle");
const graph = svg.append("g").attr("transform", `translate(10, -45)`);
const links = graph.append("g").attr("fill", "none").attr("stroke", "#aaaaaa");
const nodes = graph.append("g");
@ -473,7 +481,24 @@ function editReligions() {
.enter()
.append("path")
.attr("d", d => {
return "M" + d.source.x + "," + d.source.y + "C" + d.source.x + "," + (d.source.y * 3 + d.target.y) / 4 + " " + d.target.x + "," + (d.source.y * 2 + d.target.y) / 3 + " " + d.target.x + "," + d.target.y;
return (
"M" +
d.source.x +
"," +
d.source.y +
"C" +
d.source.x +
"," +
(d.source.y * 3 + d.target.y) / 4 +
" " +
d.target.x +
"," +
(d.source.y * 2 + d.target.y) / 3 +
" " +
d.target.x +
"," +
d.target.y
);
});
const node = nodes
@ -578,7 +603,11 @@ function editReligions() {
$("#religionsEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg"}});
tip("Click on religion to select, drag the circle to change religion", true);
viewbox.style("cursor", "crosshair").on("click", selectReligionOnMapClick).call(d3.drag().on("start", dragReligionBrush)).on("touchmove mousemove", moveReligionBrush);
viewbox
.style("cursor", "crosshair")
.on("click", selectReligionOnMapClick)
.call(d3.drag().on("start", dragReligionBrush))
.on("touchmove mousemove", moveReligionBrush);
body.querySelector("div").classList.add("selected");
}

View file

@ -45,7 +45,7 @@ function editStates() {
cl = el.classList,
line = el.parentNode,
state = +line.dataset.id;
if (cl.contains("fillRect")) stateChangeFill(el);
if (el.tagName === "FILL-BOX") stateChangeFill(el);
else if (cl.contains("name")) editStateName(state);
else if (cl.contains("coaIcon")) editEmblem("state", "stateCOA" + state, pack.states[state]);
else if (cl.contains("icon-star-empty")) stateCapitalZoomIn(state);
@ -102,7 +102,7 @@ function editStates() {
// Neutral line
lines += `<div class="states" data-id=${s.i} data-name="${s.name}" data-cells=${s.cells} data-area=${area}
data-population=${population} data-burgs=${s.burgs} data-color="" data-form="" data-capital="" data-culture="" data-type="" data-expansionism="">
<svg width="9" height="9" class="placeholder"></svg>
<svg width="1em" height="1em" class="placeholder"></svg>
<input data-tip="Neutral lands name. Click to change" class="stateName name pointer italic" value="${s.name}" readonly>
<svg class="coaIcon placeholder hide"></svg>
<input class="stateForm placeholder" value="none">
@ -126,15 +126,10 @@ function editStates() {
const capital = pack.burgs[s.capital].name;
COArenderer.trigger("stateCOA" + s.i, s.coa);
lines += `<div class="states" data-id=${s.i} data-name="${s.name}" data-form="${s.formName}" data-capital="${capital}" data-color="${
s.color
}" data-cells=${s.cells}
data-area=${area} data-population=${population} data-burgs=${s.burgs} data-culture=${pack.cultures[s.culture].name} data-type=${
s.type
} data-expansionism=${s.expansionism}>
<svg data-tip="State fill style. Click to change" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${
s.color
}" class="fillRect pointer"></svg>
lines += `<div class="states" data-id=${s.i} data-name="${s.name}" data-form="${s.formName}" data-capital="${capital}"
data-color="${s.color}" data-cells=${s.cells} data-area=${area} data-population=${population} data-burgs=${s.burgs}
data-culture=${pack.cultures[s.culture].name} data-type=${s.type} data-expansionism=${s.expansionism}>
<fill-box fill="${s.color}"></fill-box>
<input data-tip="State name. Click to change" class="stateName name pointer" value="${s.name}" readonly>
<svg data-tip="Click to show and edit state emblem" class="coaIcon pointer hide" viewBox="0 0 200 200"><use href="#stateCOA${s.i}"></use></svg>
<input data-tip="State form name. Click to change" class="stateForm name pointer" value="${s.formName}" readonly>
@ -149,9 +144,8 @@ function editStates() {
<div data-tip="${populationTip}" class="culturePopulation hide">${si(population)}</div>
<select data-tip="State type. Defines growth model. Click to change" class="cultureType ${hidden} show hide">${getTypeOptions(s.type)}</select>
<span data-tip="State expansionism" class="icon-resize-full ${hidden} show hide"></span>
<input data-tip="Expansionism (defines competitive size). Change to re-calculate states based on new value" class="statePower ${hidden} show hide" type="number" min=0 max=99 step=.1 value=${
s.expansionism
}>
<input data-tip="Expansionism (defines competitive size). Change to re-calculate states based on new value"
class="statePower ${hidden} show hide" type="number" min=0 max=99 step=.1 value=${s.expansionism}>
<span data-tip="Cells count" class="icon-check-empty ${hidden} show hide"></span>
<div data-tip="Cells count" class="stateCells ${hidden} show hide">${s.cells}</div>
<span data-tip="Toggle state focus" class="icon-pin ${focused ? "" : " inactive"} hide"></span>
@ -237,18 +231,18 @@ function editStates() {
function stateChangeFill(el) {
const currentFill = el.getAttribute("fill");
const state = +el.parentNode.parentNode.dataset.id;
const state = +el.parentNode.dataset.id;
const callback = function (fill) {
el.setAttribute("fill", fill);
pack.states[state].color = fill;
statesBody.select("#state" + state).attr("fill", fill);
statesBody.select("#state-gap" + state).attr("stroke", fill);
const halo = d3.color(fill) ? d3.color(fill).darker().hex() : "#666666";
const callback = function (newFill) {
el.fill = newFill;
pack.states[state].color = newFill;
statesBody.select("#state" + state).attr("fill", newFill);
statesBody.select("#state-gap" + state).attr("stroke", newFill);
const halo = d3.color(newFill) ? d3.color(newFill).darker().hex() : "#666666";
statesHalo.select("#state-border" + state).attr("stroke", halo);
// recolor regiments
const solidColor = fill[0] === "#" ? fill : "#999";
const solidColor = newFill[0] === "#" ? newFill : "#999";
const darkerColor = d3.color(solidColor).darker().hex();
armies.select("#army" + state).attr("fill", solidColor);
armies

View file

@ -33,26 +33,12 @@ function editZones() {
const el = ev.target,
cl = el.classList,
zone = el.parentNode.dataset.id;
if (cl.contains("culturePopulation")) {
changePopulation(zone);
return;
}
if (cl.contains("icon-trash-empty")) {
zoneRemove(zone);
return;
}
if (cl.contains("icon-eye")) {
toggleVisibility(el);
return;
}
if (cl.contains("icon-pin")) {
toggleFog(zone, cl);
return;
}
if (cl.contains("fillRect")) {
changeFill(el);
return;
}
if (el.tagName === "FILL-BOX") changeFill(el);
else if (cl.contains("culturePopulation")) changePopulation(zone);
else if (cl.contains("icon-trash-empty")) zoneRemove(zone);
else if (cl.contains("icon-eye")) toggleVisibility(el);
else if (cl.contains("icon-pin")) toggleFog(zone, cl);
if (customization) selectZone(el);
});
@ -79,8 +65,9 @@ function editZones() {
const inactive = this.style.display === "none";
const focused = defs.select("#fog #focus" + this.id).size();
lines += `<div class="states" data-id="${this.id}" data-fill="${fill}" data-description="${description}" data-cells=${c.length} data-area=${area} data-population=${population}>
<svg data-tip="Zone fill style. Click to change" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${fill}" class="fillRect pointer"></svg>
lines += `<div class="states" data-id="${this.id}" data-fill="${fill}" data-description="${description}"
data-cells=${c.length} data-area=${area} data-population=${population}>
<fill-box fill="${fill}"></fill-box>
<input data-tip="Zone description. Click and type to change" class="religionName" value="${description}" autocorrect="off" spellcheck="false">
<span data-tip="Cells count" class="icon-check-empty hide"></span>
<div data-tip="Cells count" class="stateCells hide">${c.length}</div>
@ -275,9 +262,9 @@ function editZones() {
function changeFill(el) {
const fill = el.getAttribute("fill");
const callback = function (fill) {
el.setAttribute("fill", fill);
document.getElementById(el.parentNode.parentNode.dataset.id).setAttribute("fill", fill);
const callback = newFill => {
el.fill = newFill;
document.getElementById(el.parentNode.dataset.id).setAttribute("fill", newFill);
};
openPicker(fill, callback);
@ -349,7 +336,7 @@ function editZones() {
const unit = areaUnit.value === "square" ? " " + distanceUnitInput.value + "²" : " " + areaUnit.value;
const line = `<div class="states" data-id="${id}" data-fill="${fill}" data-description="${description}" data-cells=0 data-area=0 data-population=0>
<svg data-tip="Zone fill style. Click to change" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${fill}" class="fillRect pointer"></svg>
<fill-box fill="${fill}"></fill-box>
<input data-tip="Zone description. Click and type to change" class="religionName" value="${description}" autocorrect="off" spellcheck="false">
<span data-tip="Cells count" class="icon-check-empty hide"></span>
<div data-tip="Cells count" class="stateCells hide">0</div>