refactor(es modules): continue migration

This commit is contained in:
Azgaar 2022-06-26 00:57:53 +03:00
parent 4a04a8622d
commit 922c6e2431
39 changed files with 551 additions and 589 deletions

View file

@ -1,74 +0,0 @@
{
const style = /* css */ `
fill-box:not([disabled]) {
cursor: pointer;
}
fill-box > svg {
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 = /* html */ `
<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);
this.querySelector("svg")?.setAttribute("height", 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";
}
}
// cannot use Shadow DOM here as need an access to svg hatches
customElements.define("fill-box", FillBox);
}

View file

@ -3248,7 +3248,11 @@
<button id="burgToggleMFCGMap" data-tip="Toggle MFCG map" class="icon-map"></button> <button id="burgToggleMFCGMap" data-tip="Toggle MFCG map" class="icon-map"></button>
<button id="burgRelocate" data-tip="Relocate burg" class="icon-target"></button> <button id="burgRelocate" data-tip="Relocate burg" class="icon-target"></button>
<button id="burglLegend" data-tip="Edit free text notes (legend) for this burg" class="icon-edit"></button> <button id="burglLegend" data-tip="Edit free text notes (legend) for this burg" class="icon-edit"></button>
<button id="burgLock" class="icon-lock-open" onmouseover="showElementLockTip(event)"></button> <button
id="burgLock"
data-tip="Toggle element lock. Lock will prevent it from regeneration"
class="icon-lock-open"
></button>
<button <button
id="burgRemove" id="burgRemove"
data-tip="Remove non-capital burg" data-tip="Remove non-capital burg"
@ -3328,7 +3332,11 @@
<div id="markerBottom"> <div id="markerBottom">
<button id="markerNotes" data-tip="Edit place legend (notes)" class="icon-edit"></button> <button id="markerNotes" data-tip="Edit place legend (notes)" class="icon-edit"></button>
<button id="markerLock" class="icon-lock-open" onmouseover="showElementLockTip(event)"></button> <button
id="markerLock"
data-tip="Toggle element lock. Lock will prevent it from regeneration"
class="icon-lock-open"
></button>
<button id="markerAdd" data-tip="Add additional marker of that type" class="icon-plus"></button> <button id="markerAdd" data-tip="Add additional marker of that type" class="icon-plus"></button>
<button <button
id="markerRemove" id="markerRemove"
@ -3810,44 +3818,14 @@
<div id="brushesSliders" style="display: none"> <div id="brushesSliders" style="display: none">
<div data-tip="Change brush size" data-shortcut="+ (increase), (decrease)" style="padding-bottom: 1px"> <div data-tip="Change brush size" data-shortcut="+ (increase), (decrease)" style="padding-bottom: 1px">
<div style="width: 3.2em; display: inline-block"><i>Radius:</i></div> <div style="width: 3.2em; display: inline-block"><i>Radius:</i></div>
<input <input id="brushRadius" type="range" min="1" max="99" value="25" />
id="brushRadius" <input id="brushRadiusNumber" type="number" min="1" max="99" value="25" style="border: 1px solid #d4d4d4" />
oninput="tip('Brush radius: '+this.value); brushRadiusNumber.value = this.value"
type="range"
min="1"
max="99"
value="25"
/>
<input
id="brushRadiusNumber"
oninput="tip('Brush radius: '+this.value); brushRadius.value = this.value"
type="number"
min="1"
max="99"
value="25"
style="border: 1px solid #d4d4d4"
/>
</div> </div>
<div data-tip="Set the brush power"> <div data-tip="Set the brush power">
<div style="width: 3.2em; display: inline-block"><i>Power:</i></div> <div style="width: 3.2em; display: inline-block"><i>Power:</i></div>
<input <input id="brushPower" type="range" min="1" max="10" value="5" />
id="brushPower" <input id="brushPowerNumber" type="number" min="1" max="10" value="5" style="border: 1px solid #d4d4d4" />
oninput="tip('Brush power: '+this.value); brushPowerNumber.value = this.value"
type="range"
min="1"
max="10"
value="5"
/>
<input
id="brushPowerNumber"
oninput="tip('Brush power: '+this.value); brushPower.value = this.value"
type="number"
min="1"
max="10"
value="5"
style="border: 1px solid #d4d4d4"
/>
</div> </div>
</div> </div>
@ -4108,23 +4086,8 @@
<div id="biomesManuallyButtons" style="display: none"> <div id="biomesManuallyButtons" style="display: none">
<label data-tip="Change brush size" data-shortcut="+ (increase), (decrease)" class="italic" <label data-tip="Change brush size" data-shortcut="+ (increase), (decrease)" class="italic"
>Brush radius: >Brush radius:
<input <input id="biomesManuallyBrush" type="range" min="5" max="99" value="15" style="width: 4em" />
id="biomesManuallyBrush" <input id="biomesManuallyBrushNumber" type="number" min="5" max="99" value="15" /> </label
oninput="tip('Brush radius: '+this.value); biomesManuallyBrushNumber.value = this.value"
type="range"
min="5"
max="99"
value="15"
style="width: 4em"
/>
<input
id="biomesManuallyBrushNumber"
oninput="tip('Brush radius: '+this.value); biomesManuallyBrush.value = this.value"
type="number"
min="5"
max="99"
value="15"
/> </label
><br /> ><br />
<button id="biomesManuallyApply" data-tip="Apply current assignment" class="icon-check"></button> <button id="biomesManuallyApply" data-tip="Apply current assignment" class="icon-check"></button>
<button id="biomesManuallyCancel" data-tip="Cancel assignment" class="icon-cancel"></button> <button id="biomesManuallyCancel" data-tip="Cancel assignment" class="icon-cancel"></button>
@ -4346,23 +4309,8 @@
<div id="provincesManuallyButtons" style="display: none"> <div id="provincesManuallyButtons" style="display: none">
<label data-tip="Change brush size" data-shortcut="+ (increase), (decrease)" class="italic" <label data-tip="Change brush size" data-shortcut="+ (increase), (decrease)" class="italic"
>Brush size: >Brush size:
<input <input id="provincesManuallyBrush" type="range" min="5" max="99" value="8" style="width: 5em" />
id="provincesManuallyBrush" <input id="provincesManuallyBrushNumber" type="number" min="5" max="99" value="8" /> </label
oninput="tip('Brush size: '+this.value); provincesManuallyBrushNumber.value = this.value"
type="range"
min="5"
max="99"
value="8"
style="width: 5em"
/>
<input
id="provincesManuallyBrushNumber"
oninput="tip('Brush size: '+this.value); provincesManuallyBrush.value = this.value"
type="number"
min="5"
max="99"
value="8"
/> </label
><br /> ><br />
<button id="provincesManuallyApply" data-tip="Apply assignment" class="icon-check"></button> <button id="provincesManuallyApply" data-tip="Apply assignment" class="icon-check"></button>
<button id="provincesManuallyCancel" data-tip="Cancel assignment" class="icon-cancel"></button> <button id="provincesManuallyCancel" data-tip="Cancel assignment" class="icon-cancel"></button>
@ -4646,23 +4594,8 @@
<div id="zonesManuallyButtons" style="display: none"> <div id="zonesManuallyButtons" style="display: none">
<label data-tip="Change brush size" data-shortcut="+ (increase), (decrease)" class="italic" <label data-tip="Change brush size" data-shortcut="+ (increase), (decrease)" class="italic"
>Brush: >Brush:
<input <input id="zonesBrush" type="range" min="5" max="50" value="7" style="width: 9em" />
id="zonesBrush" <input id="zonesBrushNumber" type="number" min="5" max="50" value="7" /> </label
oninput="tip('Brush size: '+this.value); zonesBrushNumber.value = this.value"
type="range"
min="5"
max="50"
value="7"
style="width: 9em"
/>
<input
id="zonesBrushNumber"
oninput="tip('Brush size: '+this.value); zonesBrush.value = this.value"
type="number"
min="5"
max="50"
value="7"
/> </label
><br /> ><br />
<button id="zonesManuallyApply" data-tip="Apply assignment" class="icon-check"></button> <button id="zonesManuallyApply" data-tip="Apply assignment" class="icon-check"></button>
<button id="zonesManuallyCancel" data-tip="Cancel assignment" class="icon-cancel"></button> <button id="zonesManuallyCancel" data-tip="Cancel assignment" class="icon-cancel"></button>
@ -7828,9 +7761,5 @@
<script type="module" src="modules/io/cloud.js"></script> <script type="module" src="modules/io/cloud.js"></script>
<script type="module" src="modules/io/export.js"></script> <script type="module" src="modules/io/export.js"></script>
<script type="module" src="modules/io/formats.js"></script> <script type="module" src="modules/io/formats.js"></script>
<!-- Web Components -->
<script src="components/fill-box.js"></script>
</body> </body>
</html> </html>
f

View file

@ -1,5 +1,6 @@
import {restoreDefaultEvents} from "/src/scripts/events"; import {restoreDefaultEvents} from "/src/scripts/events";
import {findAll, findCell, getPackPolygon, isLand} from "/src/utils/graphUtils"; import {findAll, findCell, getPackPolygon, isLand} from "/src/utils/graphUtils";
import {showMainTip, clearMainTip} from "/src/scripts/tooltips";
import {byId} from "/src/utils/shorthands"; import {byId} from "/src/utils/shorthands";
const $body = insertEditorHtml(); const $body = insertEditorHtml();
@ -270,7 +271,7 @@ function culturesEditorAddLines() {
togglePercentageMode(); togglePercentageMode();
} }
applySorting($culturesHeader); applySorting($culturesHeader);
$("#culturesEditor").dialog({width: fitContent()}); $("#culturesEditor").dialog({width: "fit-content"});
} }
function getTypeOptions(type) { function getTypeOptions(type) {

View file

@ -1,5 +1,6 @@
import {restoreDefaultEvents} from "/src/scripts/events"; import {restoreDefaultEvents} from "/src/scripts/events";
import {findAll, findCell, getPackPolygon, isLand} from "/src/utils/graphUtils"; import {findAll, findCell, getPackPolygon, isLand} from "/src/utils/graphUtils";
import {showMainTip, clearMainTip} from "/src/scripts/tooltips";
import {byId} from "/src/utils/shorthands"; import {byId} from "/src/utils/shorthands";
const $body = insertEditorHtml(); const $body = insertEditorHtml();
@ -252,7 +253,7 @@ function religionsEditorAddLines() {
togglePercentageMode(); togglePercentageMode();
} }
applySorting(religionsHeader); applySorting(religionsHeader);
$("#religionsEditor").dialog({width: fitContent()}); $("#religionsEditor").dialog({width: "fit-content"});
} }
function getTypeOptions(type) { function getTypeOptions(type) {

View file

@ -1,6 +1,7 @@
import {restoreDefaultEvents} from "/src/scripts/events"; import {restoreDefaultEvents} from "/src/scripts/events";
import {findAll, findCell, getPackPolygon, isLand} from "/src/utils/graphUtils"; import {findAll, findCell, getPackPolygon, isLand} from "/src/utils/graphUtils";
import {byId} from "/src/utils/shorthands"; import {byId} from "/src/utils/shorthands";
import {showMainTip, clearMainTip} from "/src/scripts/tooltips";
const $body = insertEditorHtml(); const $body = insertEditorHtml();
addListeners(); addListeners();
@ -318,7 +319,7 @@ function statesEditorAddLines() {
togglePercentageMode(); togglePercentageMode();
} }
applySorting(statesHeader); applySorting(statesHeader);
$("#statesEditor").dialog({width: fitContent()}); $("#statesEditor").dialog({width: "fit-content"});
} }
function getCultureOptions(culture) { function getCultureOptions(culture) {
@ -843,7 +844,7 @@ function showStatesChart() {
$("#alert").dialog({ $("#alert").dialog({
title: "States bubble chart", title: "States bubble chart",
width: fitContent(), width: "fit-content",
position: {my: "left bottom", at: "left+10 bottom-10", of: "svg"}, position: {my: "left bottom", at: "left+10 bottom-10", of: "svg"},
buttons: {}, buttons: {},
close: () => { close: () => {

View file

@ -415,8 +415,7 @@ function selectElement(d) {
<input data-id="${i}" id="selectElementOrigin${i}" class="checkbox" type="checkbox" ${isChecked} /> <input data-id="${i}" id="selectElementOrigin${i}" class="checkbox" type="checkbox" ${isChecked} />
<label data-tip="Check to set as a secondary origin" for="selectElementOrigin${i}" class="checkbox-label"> <label data-tip="Check to set as a secondary origin" for="selectElementOrigin${i}" class="checkbox-label">
<fill-box fill="${color}" size=".8em" disabled></fill-box> <fill-box fill="${color}" size=".8em" disabled></fill-box>
${code}: ${name} ${code}: ${name}</label>
</label>
</div> </div>
`; `;
}); });

View file

@ -28,7 +28,7 @@ export class Battle {
$("#battleScreen").dialog({ $("#battleScreen").dialog({
title: this.name, title: this.name,
resizable: false, resizable: false,
width: fitContent(), width: "fit-content",
position: {my: "center", at: "center", of: "#map"}, position: {my: "center", at: "center", of: "#map"},
close: () => Battle.prototype.context.cancelResults() close: () => Battle.prototype.context.cancelResults()
}); });
@ -235,7 +235,7 @@ export class Battle {
$("#regimentSelectorScreen").dialog({ $("#regimentSelectorScreen").dialog({
resizable: false, resizable: false,
width: fitContent(), width: "fit-content",
title: "Add regiment to the battle", title: "Add regiment to the battle",
position: {my: "left center", at: "right+10 center", of: "#battleScreen"}, position: {my: "left center", at: "right+10 center", of: "#battleScreen"},
close: addSideClosed, close: addSideClosed,

View file

@ -1,5 +1,6 @@
import {restoreDefaultEvents} from "/src/scripts/events"; import {restoreDefaultEvents} from "/src/scripts/events";
import {findAll, findCell, getPackPolygon, isLand} from "/src/utils/graphUtils"; import {findAll, findCell, getPackPolygon, isLand} from "/src/utils/graphUtils";
import {showMainTip, clearMainTip} from "/src/scripts/tooltips";
export function editBiomes() { export function editBiomes() {
if (customization) return; if (customization) return;
@ -20,7 +21,7 @@ export function editBiomes() {
$("#biomesEditor").dialog({ $("#biomesEditor").dialog({
title: "Biomes Editor", title: "Biomes Editor",
resizable: false, resizable: false,
width: fitContent(), width: "fit-content",
close: closeBiomesEditor, close: closeBiomesEditor,
position: {my: "right top", at: "right-10 top+10", of: "svg"} position: {my: "right top", at: "right-10 top+10", of: "svg"}
}); });
@ -154,7 +155,7 @@ export function editBiomes() {
togglePercentageMode(); togglePercentageMode();
} }
applySorting(biomesHeader); applySorting(biomesHeader);
$("#biomesEditor").dialog({width: fitContent()}); $("#biomesEditor").dialog({width: "fit-content"});
} }
function biomeHighlightOn(event) { function biomeHighlightOn(event) {
@ -308,7 +309,7 @@ export function editBiomes() {
body.insertAdjacentHTML("beforeend", line); body.insertAdjacentHTML("beforeend", line);
biomesFooterBiomes.innerHTML = body.querySelectorAll(":scope > div").length; biomesFooterBiomes.innerHTML = body.querySelectorAll(":scope > div").length;
$("#biomesEditor").dialog({width: fitContent()}); $("#biomesEditor").dialog({width: "fit-content"});
} }
function removeCustomBiome(el) { function removeCustomBiome(el) {

View file

@ -1,4 +1,5 @@
import {findCell} from "/src/utils/graphUtils"; import {findCell} from "/src/utils/graphUtils";
import {clearMainTip} from "/src/scripts/tooltips";
export function editBurg(id) { export function editBurg(id) {
if (customization) return; if (customization) return;

View file

@ -1,5 +1,6 @@
import {restoreDefaultEvents} from "/src/scripts/events"; import {restoreDefaultEvents} from "/src/scripts/events";
import {findCell} from "/src/utils/graphUtils"; import {findCell} from "/src/utils/graphUtils";
import {clearMainTip} from "/src/scripts/tooltips";
export function overviewBurgs() { export function overviewBurgs() {
if (customization) return; if (customization) return;
@ -19,7 +20,7 @@ export function overviewBurgs() {
$("#burgsOverview").dialog({ $("#burgsOverview").dialog({
title: "Burgs Overview", title: "Burgs Overview",
resizable: false, resizable: false,
width: fitContent(), width: "fit-content",
close: exitAddBurgMode, close: exitAddBurgMode,
position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"} position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}
}); });
@ -110,14 +111,12 @@ export function overviewBurgs() {
data-tip="${b.capital ? " This burg is a state capital" : "Click to assign a capital status"}" data-tip="${b.capital ? " This burg is a state capital" : "Click to assign a capital status"}"
class="icon-star-empty${b.capital ? "" : " inactive pointer"}" class="icon-star-empty${b.capital ? "" : " inactive pointer"}"
></span> ></span>
<span data-tip="Click to toggle port status" class="icon-anchor pointer${ <span data-tip="Click to toggle port status"
b.port ? "" : " inactive" class="icon-anchor pointer${b.port ? "" : " inactive"}" style="font-size:.9em"></span>
}" style="font-size:.9em"></span>
</div> </div>
<span data-tip="Edit burg" class="icon-pencil"></span> <span data-tip="Edit burg" class="icon-pencil"></span>
<span class="locks pointer ${ <span data-tip="Toggle element lock. Lock will prevent it from regeneration"
b.lock ? "icon-lock" : "icon-lock-open inactive" class="locks pointer ${b.lock ? "icon-lock" : "icon-lock-open inactive"}"></span>
}" onmouseover="showElementLockTip(event)"></span>
<span data-tip="Remove burg" class="icon-trash-empty"></span> <span data-tip="Remove burg" class="icon-trash-empty"></span>
</div>`; </div>`;
} }
@ -473,7 +472,7 @@ export function overviewBurgs() {
$("#alert").dialog({ $("#alert").dialog({
title: "Burgs bubble chart", title: "Burgs bubble chart",
width: fitContent(), width: "fit-content",
position: {my: "left bottom", at: "left+10 bottom-10", of: "svg"}, position: {my: "left bottom", at: "left+10 bottom-10", of: "svg"},
buttons: {}, buttons: {},
close: () => (alertMessage.innerHTML = "") close: () => (alertMessage.innerHTML = "")

View file

@ -1,5 +1,6 @@
import {restoreDefaultEvents} from "/src/scripts/events"; import {restoreDefaultEvents} from "/src/scripts/events";
import {findCell} from "/src/utils/graphUtils"; import {findCell} from "/src/utils/graphUtils";
import {clearMainTip} from "/src/scripts/tooltips";
export function editDiplomacy() { export function editDiplomacy() {
if (customization) return; if (customization) return;
@ -65,7 +66,7 @@ export function editDiplomacy() {
$("#diplomacyEditor").dialog({ $("#diplomacyEditor").dialog({
title: "Diplomacy Editor", title: "Diplomacy Editor",
resizable: false, resizable: false,
width: fitContent(), width: "fit-content",
close: closeDiplomacyEditor, close: closeDiplomacyEditor,
position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"} position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}
}); });
@ -236,7 +237,7 @@ export function editDiplomacy() {
alertMessage.innerHTML = /* html */ `<div style="overflow: hidden">${header} ${options} ${footer}</div>`; alertMessage.innerHTML = /* html */ `<div style="overflow: hidden">${header} ${options} ${footer}</div>`;
$("#alert").dialog({ $("#alert").dialog({
width: fitContent(), width: "fit-content",
title: `Change relations`, title: `Change relations`,
buttons: { buttons: {
Apply: function () { Apply: function () {

View file

@ -2,35 +2,8 @@ import {restoreDefaultEvents} from "/src/scripts/events";
import {findCell} from "/src/utils/graphUtils"; import {findCell} from "/src/utils/graphUtils";
import {byId} from "/src/utils/shorthands"; import {byId} from "/src/utils/shorthands";
// on viewbox click event - run function based on target
function clicked() {
const el = d3.event.target;
if (!el || !el.parentElement || !el.parentElement.parentElement) return;
const parent = el.parentElement;
const grand = parent.parentElement;
const great = grand.parentElement;
const p = d3.mouse(this);
const i = findCell(p[0], p[1]);
if (grand.id === "emblems") editEmblem();
else if (parent.id === "rivers") editRiver(el.id);
else if (grand.id === "routes") editRoute();
else if (el.tagName === "tspan" && grand.parentNode.parentNode.id === "labels") editLabel();
else if (grand.id === "burgLabels") editBurg();
else if (grand.id === "burgIcons") editBurg();
else if (parent.id === "ice") editIce();
else if (parent.id === "terrain") editReliefIcon();
else if (grand.id === "markers" || great.id === "markers") editMarker();
else if (grand.id === "coastline") editCoastline();
else if (great.id === "armies") editRegiment();
else if (pack.cells.t[i] === 1) {
const node = byId("island_" + pack.cells.f[i]);
editCoastline(node);
} else if (grand.id === "lakes") editLake();
}
// clear elSelected variable // clear elSelected variable
function unselect() { export function unselect() {
restoreDefaultEvents(); restoreDefaultEvents();
if (!elSelected) return; if (!elSelected) return;
elSelected.call(d3.drag().on("drag", null)).attr("class", null); elSelected.call(d3.drag().on("drag", null)).attr("class", null);
@ -40,7 +13,7 @@ function unselect() {
} }
// close all dialogs except stated // close all dialogs except stated
function closeDialogs(except = "#except") { export function closeDialogs(except = "#except") {
try { try {
$(".dialog:visible") $(".dialog:visible")
.not(except) .not(except)
@ -51,7 +24,7 @@ function closeDialogs(except = "#except") {
} }
// move brush radius circle // move brush radius circle
function moveCircle(x, y, r = 20) { export function moveCircle(x, y, r = 20) {
let circle = byId("brushCircle"); let circle = byId("brushCircle");
if (!circle) { if (!circle) {
const html = /* html */ `<circle id="brushCircle" cx=${x} cy=${y} r=${r}></circle>`; const html = /* html */ `<circle id="brushCircle" cx=${x} cy=${y} r=${r}></circle>`;
@ -63,15 +36,10 @@ function moveCircle(x, y, r = 20) {
} }
} }
function removeCircle() { export function removeCircle() {
if (byId("brushCircle")) byId("brushCircle").remove(); if (byId("brushCircle")) byId("brushCircle").remove();
} }
// get browser-defined fit-content
function fitContent() {
return !window.chrome ? "-moz-max-content" : "fit-content";
}
// apply sorting behaviour for lines on Editor header click // apply sorting behaviour for lines on Editor header click
document.querySelectorAll(".sortable").forEach(function (event) { document.querySelectorAll(".sortable").forEach(function (event) {
event.on("click", function () { event.on("click", function () {
@ -348,118 +316,6 @@ function getMFCGlink(burg) {
return url.toString(); return url.toString();
} }
// draw legend box
function drawLegend(name, data) {
legend.selectAll("*").remove(); // fully redraw every time
legend.attr("data", data.join("|")); // store data
const itemsInCol = +styleLegendColItems.value;
const fontSize = +legend.attr("font-size");
const backClr = styleLegendBack.value;
const opacity = +styleLegendOpacity.value;
const lineHeight = Math.round(fontSize * 1.7);
const colorBoxSize = Math.round(fontSize / 1.7);
const colOffset = fontSize;
const vOffset = fontSize / 2;
// append items
const boxes = legend.append("g").attr("stroke-width", 0.5).attr("stroke", "#111111").attr("stroke-dasharray", "none");
const labels = legend.append("g").attr("fill", "#000000").attr("stroke", "none");
const columns = Math.ceil(data.length / itemsInCol);
for (let column = 0, i = 0; column < columns; column++) {
const linesInColumn = Math.ceil(data.length / columns);
const offset = column ? colOffset * 2 + legend.node().getBBox().width : colOffset;
for (let l = 0; l < linesInColumn && data[i]; l++, i++) {
boxes
.append("rect")
.attr("fill", data[i][1])
.attr("x", offset)
.attr("y", lineHeight + l * lineHeight + vOffset)
.attr("width", colorBoxSize)
.attr("height", colorBoxSize);
labels
.append("text")
.text(data[i][2])
.attr("x", offset + colorBoxSize * 1.6)
.attr("y", fontSize / 1.6 + lineHeight + l * lineHeight + vOffset);
}
}
// append label
const offset = colOffset + legend.node().getBBox().width / 2;
labels
.append("text")
.attr("text-anchor", "middle")
.attr("font-weight", "bold")
.attr("font-size", "1.2em")
.attr("id", "legendLabel")
.text(name)
.attr("x", offset)
.attr("y", fontSize * 1.1 + vOffset / 2);
// append box
const bbox = legend.node().getBBox();
const width = bbox.width + colOffset * 2;
const height = bbox.height + colOffset / 2 + vOffset;
legend
.insert("rect", ":first-child")
.attr("id", "legendBox")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height)
.attr("fill", backClr)
.attr("fill-opacity", opacity);
fitLegendBox();
}
// fit Legend box to canvas size
function fitLegendBox() {
if (!legend.selectAll("*").size()) return;
const px = isNaN(+legend.attr("data-x")) ? 99 : legend.attr("data-x") / 100;
const py = isNaN(+legend.attr("data-y")) ? 93 : legend.attr("data-y") / 100;
const bbox = legend.node().getBBox();
const x = rn(svgWidth * px - bbox.width),
y = rn(svgHeight * py - bbox.height);
legend.attr("transform", `translate(${x},${y})`);
}
// draw legend with the same data, but using different settings
function redrawLegend() {
if (!legend.select("rect").size()) return;
const name = legend.select("#legendLabel").text();
const data = legend
.attr("data")
.split("|")
.map(l => l.split(","));
drawLegend(name, data);
}
function dragLegendBox() {
const tr = parseTransform(this.getAttribute("transform"));
const x = +tr[0] - d3.event.x,
y = +tr[1] - d3.event.y;
const bbox = legend.node().getBBox();
d3.event.on("drag", function () {
const px = rn(((x + d3.event.x + bbox.width) / svgWidth) * 100, 2);
const py = rn(((y + d3.event.y + bbox.height) / svgHeight) * 100, 2);
const transform = `translate(${x + d3.event.x},${y + d3.event.y})`;
legend.attr("transform", transform).attr("data-x", px).attr("data-y", py);
});
}
function clearLegend() {
legend.selectAll("*").remove();
legend.attr("data", null);
}
// draw color (fill) picker // draw color (fill) picker
function createPicker() { function createPicker() {
const pos = () => tip("Drag to change the picker position"); const pos = () => tip("Drag to change the picker position");
@ -1097,7 +953,7 @@ function selectIcon(initial, callback) {
}; };
$("#iconSelector").dialog({ $("#iconSelector").dialog({
width: fitContent(), width: "fit-content",
title: "Select Icon", title: "Select Icon",
buttons: { buttons: {
Apply: function () { Apply: function () {

View file

@ -1,5 +1,6 @@
"use strict"; import {clearMainTip} from "/src/scripts/tooltips";
function editEmblem(type, id, el) {
export function editEmblem(type, id, el) {
if (customization) return; if (customization) return;
if (!id && d3.event) defineEmblemData(d3.event); if (!id && d3.event) defineEmblemData(d3.event);
@ -44,7 +45,12 @@ function editEmblem(type, id, el) {
function defineEmblemData(e) { function defineEmblemData(e) {
const parent = e.target.parentNode; const parent = e.target.parentNode;
const [g, t] = parent.id === "burgEmblems" ? [pack.burgs, "burg"] : parent.id === "provinceEmblems" ? [pack.provinces, "province"] : [pack.states, "state"]; const [g, t] =
parent.id === "burgEmblems"
? [pack.burgs, "burg"]
: parent.id === "provinceEmblems"
? [pack.provinces, "province"]
: [pack.states, "state"];
const i = +e.target.dataset.i; const i = +e.target.dataset.i;
type = t; type = t;
id = type + "COA" + i; id = type + "COA" + i;
@ -88,8 +94,12 @@ function editEmblem(type, id, el) {
emblemBurgs.options.length = 0; emblemBurgs.options.length = 0;
emblemBurgs.options.add(new Option("", 0, false, !burg)); emblemBurgs.options.add(new Option("", 0, false, !burg));
const burgList = validBurgs.filter(burg => (province ? pack.cells.province[burg.cell] === province : burg.state === state)); const burgList = validBurgs.filter(burg =>
burgList.forEach(b => emblemBurgs.options.add(new Option(b.capital ? "👑 " + b.name : b.name, b.i, false, b.i === burg))); province ? pack.cells.province[burg.cell] === province : burg.state === state
);
burgList.forEach(b =>
emblemBurgs.options.add(new Option(b.capital ? "👑 " + b.name : b.name, b.i, false, b.i === burg))
);
emblemBurgs.options[0].disabled = true; emblemBurgs.options[0].disabled = true;
COArenderer.trigger(id, el.coa); COArenderer.trigger(id, el.coa);
@ -224,12 +234,18 @@ function editEmblem(type, id, el) {
} }
function upload(type) { function upload(type) {
const input = type === "image" ? document.getElementById("emblemImageToLoad") : document.getElementById("emblemSVGToLoad"); const input =
type === "image" ? document.getElementById("emblemImageToLoad") : document.getElementById("emblemSVGToLoad");
const file = input.files[0]; const file = input.files[0];
input.value = ""; input.value = "";
if (file.size > 500000) { if (file.size > 500000) {
tip(`File is too big, please optimize file size up to 500kB and re-upload. Recommended size is 200x200 px and up to 100kB`, true, "error", 5000); tip(
`File is too big, please optimize file size up to 500kB and re-upload. Recommended size is 200x200 px and up to 100kB`,
true,
"error",
5000
);
return; return;
} }
@ -257,7 +273,11 @@ function editEmblem(type, id, el) {
const svg = el.querySelector("svg"); const svg = el.querySelector("svg");
if (!svg) { if (!svg) {
tip("The file should be prepated for load to FMG. Please use Armoria or other relevant tools", false, "error"); tip(
"The file should be prepated for load to FMG. Please use Armoria or other relevant tools",
false,
"error"
);
return; return;
} }
@ -351,7 +371,9 @@ function editEmblem(type, id, el) {
validStates validStates
.map(state => { .map(state => {
const el = document.getElementById("stateCOA" + state.i); const el = document.getElementById("stateCOA" + state.i);
return `<figure id="state_${state.i}"><a href="#provinces_${state.i}"><figcaption>${state.fullName}</figcaption>${getSVG(el, 200)}</a></figure>`; return `<figure id="state_${state.i}"><a href="#provinces_${state.i}"><figcaption>${
state.fullName
}</figcaption>${getSVG(el, 200)}</a></figure>`;
}) })
.join("") + .join("") +
`</div>`; `</div>`;
@ -362,13 +384,14 @@ function editEmblem(type, id, el) {
const figures = stateProvinces const figures = stateProvinces
.map(province => { .map(province => {
const el = document.getElementById("provinceCOA" + province.i); const el = document.getElementById("provinceCOA" + province.i);
return `<figure id="province_${province.i}"><a href="#burgs_${province.i}"><figcaption>${province.fullName}</figcaption>${getSVG( return `<figure id="province_${province.i}"><a href="#burgs_${province.i}"><figcaption>${
el, province.fullName
200 }</figcaption>${getSVG(el, 200)}</a></figure>`;
)}</a></figure>`;
}) })
.join(""); .join("");
return stateProvinces.length ? `<div id="provinces_${state.i}">${back}<h2>${state.fullName} provinces</h2>${figures}</div>` : ""; return stateProvinces.length
? `<div id="provinces_${state.i}">${back}<h2>${state.fullName} provinces</h2>${figures}</div>`
: "";
}) })
.join(""); .join("");
@ -385,7 +408,9 @@ function editEmblem(type, id, el) {
return `<figure id="burg_${burg.i}"><figcaption>${burg.name}</figcaption>${getSVG(el, 200)}</figure>`; return `<figure id="burg_${burg.i}"><figcaption>${burg.name}</figcaption>${getSVG(el, 200)}</figure>`;
}) })
.join(""); .join("");
return provinceBurgs.length ? `<div id="burgs_${province.i}">${back}<h2>${province.fullName} burgs</h2>${provinceBurgFigures}</div>` : ""; return provinceBurgs.length
? `<div id="burgs_${province.i}">${back}<h2>${province.fullName} burgs</h2>${provinceBurgFigures}</div>`
: "";
}) })
.join(""); .join("");

View file

@ -13,221 +13,6 @@ if (location.hostname && location.hostname !== "localhost" && location.hostname
window.onbeforeunload = () => "Are you sure you want to navigate away?"; window.onbeforeunload = () => "Are you sure you want to navigate away?";
} }
// Tooltips
const tooltip = document.getElementById("tooltip");
// show tip for non-svg elemets with data-tip
document.getElementById("dialogs").addEventListener("mousemove", showDataTip);
document.getElementById("optionsContainer").addEventListener("mousemove", showDataTip);
document.getElementById("exitCustomization").addEventListener("mousemove", showDataTip);
const tipBackgroundMap = {
info: "linear-gradient(0.1turn, #ffffff00, #5e5c5c80, #ffffff00)",
success: "linear-gradient(0.1turn, #ffffff00, #127912cc, #ffffff00)",
warn: "linear-gradient(0.1turn, #ffffff00, #be5d08cc, #ffffff00)",
error: "linear-gradient(0.1turn, #ffffff00, #e11d1dcc, #ffffff00)"
};
function tip(tip = "Tip is undefined", main = false, type = "info", time = 0) {
tooltip.innerHTML = tip;
tooltip.style.background = tipBackgroundMap[type];
if (main) {
tooltip.dataset.main = tip;
tooltip.dataset.color = tooltip.style.background;
}
if (time) setTimeout(clearMainTip, time);
}
function showMainTip() {
tooltip.style.background = tooltip.dataset.color;
tooltip.innerHTML = tooltip.dataset.main;
}
function clearMainTip() {
tooltip.dataset.color = "";
tooltip.dataset.main = "";
tooltip.innerHTML = "";
}
// show tip at the bottom of the screen, consider possible translation
function showDataTip(event) {
if (!event.target) return;
let dataTip = event.target.dataset.tip;
if (!dataTip && event.target.parentNode.dataset.tip) dataTip = event.target.parentNode.dataset.tip;
if (!dataTip) return;
const shortcut = event.target.dataset.shortcut;
if (shortcut && !MOBILE) dataTip += `. Shortcut: ${shortcut}`;
//const tooltip = lang === "en" ? dataTip : translate(e.target.dataset.t || e.target.parentNode.dataset.t, dataTip);
tip(dataTip);
}
function showElementLockTip(event) {
const locked = event?.target?.classList?.contains("icon-lock");
if (locked) {
tip("Click to unlock the element and allow it to be changed by regeneration tools");
} else {
tip("Click to lock the element and prevent changes to it by regeneration tools");
}
}
const onMouseMove = debounce(handleMouseMove, 100);
function handleMouseMove() {
const point = d3.mouse(this);
const i = findCell(point[0], point[1]); // pack cell id
if (i === undefined) return;
showNotes(d3.event);
const gridCell = findGridCell(point[0], point[1], grid);
if (tooltip.dataset.main) showMainTip();
else showMapTooltip(point, d3.event, i, gridCell);
if (cellInfo?.offsetParent) updateCellInfo(point, i, gridCell);
}
// show note box on hover (if any)
function showNotes(e) {
if (notesEditor?.offsetParent) return;
let id = e.target.id || e.target.parentNode.id || e.target.parentNode.parentNode.id;
if (e.target.parentNode.parentNode.id === "burgLabels") id = "burg" + e.target.dataset.id;
else if (e.target.parentNode.parentNode.id === "burgIcons") id = "burg" + e.target.dataset.id;
const note = notes.find(note => note.id === id);
if (note !== undefined && note.legend !== "") {
document.getElementById("notes").style.display = "block";
document.getElementById("notesHeader").innerHTML = note.name;
document.getElementById("notesBody").innerHTML = note.legend;
} else if (!options.pinNotes && !markerEditor?.offsetParent) {
document.getElementById("notes").style.display = "none";
document.getElementById("notesHeader").innerHTML = "";
document.getElementById("notesBody").innerHTML = "";
}
}
// show viewbox tooltip if main tooltip is blank
function showMapTooltip(point, e, i, g) {
tip(""); // clear tip
const path = e.composedPath ? e.composedPath() : getComposedPath(e.target); // apply polyfill
if (!path[path.length - 8]) return;
const group = path[path.length - 7].id;
const subgroup = path[path.length - 8].id;
const land = pack.cells.h[i] >= 20;
// specific elements
if (group === "armies") return tip(e.target.parentNode.dataset.name + ". Click to edit");
if (group === "emblems" && e.target.tagName === "use") {
const parent = e.target.parentNode;
const [g, type] =
parent.id === "burgEmblems"
? [pack.burgs, "burg"]
: parent.id === "provinceEmblems"
? [pack.provinces, "province"]
: [pack.states, "state"];
const i = +e.target.dataset.i;
if (event.shiftKey) highlightEmblemElement(type, g[i]);
d3.select(e.target).raise();
d3.select(parent).raise();
const name = g[i].fullName || g[i].name;
tip(`${name} ${type} emblem. Click to edit. Hold Shift to show associated area or place`);
return;
}
if (group === "rivers") {
const river = +e.target.id.slice(5);
const r = pack.rivers.find(r => r.i === river);
const name = r ? r.name + " " + r.type : "";
tip(name + ". Click to edit");
if (riversOverview?.offsetParent) highlightEditorLine(riversOverview, river, 5000);
return;
}
if (group === "routes") return tip("Click to edit the Route");
if (group === "terrain") return tip("Click to edit the Relief Icon");
if (subgroup === "burgLabels" || subgroup === "burgIcons") {
const burg = +path[path.length - 10].dataset.id;
const b = pack.burgs[burg];
const population = si(b.population * populationRate * urbanization);
tip(`${b.name}. Population: ${population}. Click to edit`);
if (burgsOverview?.offsetParent) highlightEditorLine(burgsOverview, burg, 5000);
return;
}
if (group === "labels") return tip("Click to edit the Label");
if (group === "markers") return tip("Click to edit the Marker and pin the marker note");
if (group === "ruler") {
const tag = e.target.tagName;
const className = e.target.getAttribute("class");
if (tag === "circle" && className === "edge")
return tip("Drag to adjust. Hold Ctrl and drag to add a point. Click to remove the point");
if (tag === "circle" && className === "control")
return tip("Drag to adjust. Hold Shift and drag to keep axial direction. Click to remove the point");
if (tag === "circle") return tip("Drag to adjust the measurer");
if (tag === "polyline") return tip("Click on drag to add a control point");
if (tag === "path") return tip("Drag to move the measurer");
if (tag === "text") return tip("Drag to move, click to remove the measurer");
}
if (subgroup === "burgIcons") return tip("Click to edit the Burg");
if (subgroup === "burgLabels") return tip("Click to edit the Burg");
if (group === "lakes" && !land) {
const lakeId = +e.target.dataset.f;
const name = pack.features[lakeId]?.name;
const fullName = subgroup === "freshwater" ? name : name + " " + subgroup;
tip(`${fullName} lake. Click to edit`);
return;
}
if (group === "coastline") return tip("Click to edit the coastline");
if (group === "zones") {
const zone = path[path.length - 8];
tip(zone.dataset.description);
if (zonesEditor?.offsetParent) highlightEditorLine(zonesEditor, zone.id, 5000);
return;
}
if (group === "ice") return tip("Click to edit the Ice");
// covering elements
if (layerIsOn("togglePrec") && land) tip("Annual Precipitation: " + getFriendlyPrecipitation(i));
else if (layerIsOn("togglePopulation")) tip(getPopulationTip(i));
else if (layerIsOn("toggleTemp")) tip("Temperature: " + convertTemperature(grid.cells.temp[g]));
else if (layerIsOn("toggleBiomes") && pack.cells.biome[i]) {
const biome = pack.cells.biome[i];
tip("Biome: " + biomesData.name[biome]);
if (biomesEditor?.offsetParent) highlightEditorLine(biomesEditor, biome);
} else if (layerIsOn("toggleReligions") && pack.cells.religion[i]) {
const religion = pack.cells.religion[i];
const r = pack.religions[religion];
const type = r.type === "Cult" || r.type == "Heresy" ? r.type : r.type + " religion";
tip(type + ": " + r.name);
if (religionsEditor?.offsetParent) highlightEditorLine(religionsEditor, religion);
} else if (pack.cells.state[i] && (layerIsOn("toggleProvinces") || layerIsOn("toggleStates"))) {
const state = pack.cells.state[i];
const stateName = pack.states[state].fullName;
const province = pack.cells.province[i];
const prov = province ? pack.provinces[province].fullName + ", " : "";
tip(prov + stateName);
if (document.getElementById("statesEditor")?.offsetParent) highlightEditorLine(statesEditor, state);
if (document.getElementById("diplomacyEditor")?.offsetParent) highlightEditorLine(diplomacyEditor, state);
if (document.getElementById("militaryOverview")?.offsetParent) highlightEditorLine(militaryOverview, state);
if (document.getElementById("provincesEditor")?.offsetParent) highlightEditorLine(provincesEditor, province);
} else if (layerIsOn("toggleCultures") && pack.cells.culture[i]) {
const culture = pack.cells.culture[i];
tip("Culture: " + pack.cultures[culture].name);
if (document.getElementById("culturesEditor")?.offsetParent) highlightEditorLine(culturesEditor, culture);
} else if (layerIsOn("toggleHeight")) tip("Height: " + getFriendlyHeight(point));
}
function highlightEditorLine(editor, id, timeout = 10000) { function highlightEditorLine(editor, id, timeout = 10000) {
Array.from(editor.getElementsByClassName("states hovered")).forEach(el => el.classList.remove("hovered")); // clear all hovered Array.from(editor.getElementsByClassName("states hovered")).forEach(el => el.classList.remove("hovered")); // clear all hovered
const hovered = Array.from(editor.querySelectorAll("div")).find(el => el.dataset.id == id); const hovered = Array.from(editor.querySelectorAll("div")).find(el => el.dataset.id == id);

View file

@ -1,6 +1,7 @@
import {restoreDefaultEvents} from "/src/scripts/events"; import {restoreDefaultEvents} from "/src/scripts/events";
import {findGridCell, findGridAll, findCell, getPackPolygon, getGridPolygon} from "/src/utils/graphUtils"; import {findGridCell, findGridAll, findCell, getPackPolygon, getGridPolygon} from "/src/utils/graphUtils";
import {last, createTypedArray} from "/src/utils/arrayUtils"; import {last, createTypedArray} from "/src/utils/arrayUtils";
import {showMainTip, clearMainTip} from "/src/scripts/tooltips";
import {byId} from "/src/utils/shorthands"; import {byId} from "/src/utils/shorthands";
export function editHeightmap(options) { export function editHeightmap(options) {

View file

@ -1,4 +1,5 @@
import {findGridCell, getGridPolygon} from "/src/utils/graphUtils"; import {findGridCell, getGridPolygon} from "/src/utils/graphUtils";
import {clearMainTip} from "/src/scripts/tooltips";
export function editIce() { export function editIce() {
if (customization) return; if (customization) return;

View file

@ -1,4 +1,5 @@
import {findCell} from "/src/utils/graphUtils"; import {findCell} from "/src/utils/graphUtils";
import {showMainTip} from "/src/scripts/tooltips";
export function editLabel() { export function editLabel() {
if (customization) return; if (customization) return;
@ -14,7 +15,7 @@ export function editLabel() {
$("#labelEditor").dialog({ $("#labelEditor").dialog({
title: "Edit Label", title: "Edit Label",
resizable: false, resizable: false,
width: fitContent(), width: "fit-content",
position: {my: "center top+10", at: "bottom", of: text, collision: "fit"}, position: {my: "center top+10", at: "bottom", of: text, collision: "fit"},
close: closeLabelEditor close: closeLabelEditor
}); });

View file

@ -1,5 +1,6 @@
import {restoreDefaultEvents} from "/src/scripts/events"; import {restoreDefaultEvents} from "/src/scripts/events";
import {findCell} from "/src/utils/graphUtils"; import {findCell} from "/src/utils/graphUtils";
import {clearMainTip} from "/src/scripts/tooltips";
export function editMarker(markerI) { export function editMarker(markerI) {
if (customization) return; if (customization) return;

View file

@ -1,4 +1,5 @@
import {restoreDefaultEvents} from "/src/scripts/events"; import {restoreDefaultEvents} from "/src/scripts/events";
import {clearMainTip} from "/src/scripts/tooltips";
export function overviewMarkers() { export function overviewMarkers() {
if (customization) return; if (customization) return;
@ -21,7 +22,7 @@ export function overviewMarkers() {
$("#markersOverview").dialog({ $("#markersOverview").dialog({
title: "Markers Overview", title: "Markers Overview",
resizable: false, resizable: false,
width: fitContent(), width: "fit-content",
close: close, close: close,
position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"} position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}
}); });
@ -51,16 +52,14 @@ export function overviewMarkers() {
function addLines() { function addLines() {
const lines = pack.markers const lines = pack.markers
.map(({i, type, icon, pinned, lock}) => { .map(({i, type, icon, pinned, lock}) => {
return `<div class="states" data-i=${i} data-type="${type}"> return /* html */ `<div class="states" data-i=${i} data-type="${type}">
<div data-tip="Marker icon and type" style="width:12em">${icon} ${type}</div> <div data-tip="Marker icon and type" style="width:12em">${icon} ${type}</div>
<span style="padding-right:.1em" data-tip="Edit marker" class="icon-pencil"></span> <span style="padding-right:.1em" data-tip="Edit marker" class="icon-pencil"></span>
<span style="padding-right:.1em" data-tip="Focus on marker position" class="icon-dot-circled pointer"></span> <span style="padding-right:.1em" data-tip="Focus on marker position" class="icon-dot-circled pointer"></span>
<span style="padding-right:.1em" data-tip="Pin marker (display only pinned markers)" class="icon-pin ${ <span style="padding-right:.1em" data-tip="Pin marker (display only pinned markers)"
pinned ? "" : "inactive" class="icon-pin ${pinned ? "" : "inactive"}" pointer"></span>
}" pointer"></span> <span style="padding-right:.1em" data-tip="Toggle element lock. Lock will prevent it from regeneration"
<span style="padding-right:.1em" class="locks pointer ${ class="locks pointer ${lock ? "icon-lock" : "icon-lock-open inactive"}" ></span>
lock ? "icon-lock" : "icon-lock-open inactive"
}" onmouseover="showElementLockTip(event)"></span>
<span data-tip="Remove marker" class="icon-trash-empty"></span> <span data-tip="Remove marker" class="icon-trash-empty"></span>
</div>`; </div>`;
}) })

View file

@ -17,7 +17,7 @@ function overviewMilitary() {
$("#militaryOverview").dialog({ $("#militaryOverview").dialog({
title: "Military Overview", title: "Military Overview",
resizable: false, resizable: false,
width: fitContent(), width: "fit-content",
position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"} position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}
}); });
@ -244,7 +244,7 @@ function overviewMilitary() {
$("#militaryOptions").dialog({ $("#militaryOptions").dialog({
title: "Edit Military Units", title: "Edit Military Units",
resizable: false, resizable: false,
width: fitContent(), width: "fit-content",
position: {my: "center", at: "center", of: "svg"}, position: {my: "center", at: "center", of: "svg"},
buttons: { buttons: {
Apply: applyMilitaryOptions, Apply: applyMilitaryOptions,
@ -382,7 +382,7 @@ function overviewMilitary() {
</table>`; </table>`;
$("#alert").dialog({ $("#alert").dialog({
width: fitContent(), width: "fit-content",
title: `Limit unit`, title: `Limit unit`,
buttons: { buttons: {
Invert: function () { Invert: function () {

View file

@ -1,7 +1,8 @@
import {applyOption} from "./general"; import {applyOption} from "./general";
import {last} from "/src/utils/arrayUtils"; import {last} from "/src/utils/arrayUtils";
import {byId, stored} from "/src/utils/shorthands";
import {lock, locked} from "/src/scripts/options/lock"; import {lock, locked} from "/src/scripts/options/lock";
import {clearMainTip} from "/src/scripts/tooltips";
import {byId, stored} from "/src/utils/shorthands";
$("#optionsContainer").draggable({handle: ".drag-trigger", snap: "svg", snapMode: "both"}); $("#optionsContainer").draggable({handle: ".drag-trigger", snap: "svg", snapMode: "both"});
$("#exitCustomization").draggable({handle: "div"}); $("#exitCustomization").draggable({handle: "div"});
@ -963,7 +964,7 @@ function toggle3dOptions() {
$("#options3d").dialog({ $("#options3d").dialog({
title: "3D mode settings", title: "3D mode settings",
resizable: false, resizable: false,
width: fitContent(), width: "fit-content",
position: {my: "right top", at: "right-30 top+10", of: "svg", collision: "fit"} position: {my: "right top", at: "right-30 top+10", of: "svg", collision: "fit"}
}); });

View file

@ -1,6 +1,7 @@
import {restoreDefaultEvents} from "/src/scripts/events"; import {restoreDefaultEvents} from "/src/scripts/events";
import {findAll, findCell, getPackPolygon, isLand} from "/src/utils/graphUtils"; import {findAll, findCell, getPackPolygon, isLand} from "/src/utils/graphUtils";
import {unique} from "/src/utils/arrayUtils"; import {unique} from "/src/utils/arrayUtils";
import {showMainTip, clearMainTip} from "/src/scripts/tooltips";
export function editProvinces() { export function editProvinces() {
if (customization) return; if (customization) return;
@ -20,7 +21,7 @@ export function editProvinces() {
$("#provincesEditor").dialog({ $("#provincesEditor").dialog({
title: "Provinces Editor", title: "Provinces Editor",
resizable: false, resizable: false,
width: fitContent(), width: "fit-content",
close: closeProvincesEditor, close: closeProvincesEditor,
position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"} position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}
}); });
@ -197,7 +198,7 @@ export function editProvinces() {
togglePercentageMode(); togglePercentageMode();
} }
applySorting(provincesHeader); applySorting(provincesHeader);
$("#provincesEditor").dialog({width: fitContent()}); $("#provincesEditor").dialog({width: "fit-content"});
} }
function getCapitalOptions(burgs, capital) { function getCapitalOptions(burgs, capital) {
@ -748,7 +749,7 @@ export function editProvinces() {
$("#alert").dialog({ $("#alert").dialog({
title: "Provinces chart", title: "Provinces chart",
width: fitContent(), width: "fit-content",
position: {my: "left bottom", at: "left+10 bottom-10", of: "svg"}, position: {my: "left bottom", at: "left+10 bottom-10", of: "svg"},
buttons: {}, buttons: {},
close: () => { close: () => {

View file

@ -1,6 +1,7 @@
import {restoreDefaultEvents} from "/src/scripts/events"; import {restoreDefaultEvents} from "/src/scripts/events";
import {findCell} from "/src/utils/graphUtils"; import {findCell} from "/src/utils/graphUtils";
import {last} from "/src/utils/arrayUtils"; import {last} from "/src/utils/arrayUtils";
import {clearMainTip} from "/src/scripts/tooltips";
export function editRegiment(selector) { export function editRegiment(selector) {
if (customization) return; if (customization) return;

View file

@ -1,5 +1,6 @@
import {findCell} from "/src/utils/graphUtils"; import {findCell} from "/src/utils/graphUtils";
import {last} from "/src/utils/arrayUtils"; import {last} from "/src/utils/arrayUtils";
import {clearMainTip} from "/src/scripts/tooltips";
export function overviewRegiments(state) { export function overviewRegiments(state) {
if (customization) return; if (customization) return;
@ -18,7 +19,7 @@ export function overviewRegiments(state) {
$("#regimentsOverview").dialog({ $("#regimentsOverview").dialog({
title: "Regiments Overview", title: "Regiments Overview",
resizable: false, resizable: false,
width: fitContent(), width: "fit-content",
position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"} position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}
}); });

View file

@ -1,5 +1,6 @@
import {restoreDefaultEvents} from "/src/scripts/events"; import {restoreDefaultEvents} from "/src/scripts/events";
import {findCell} from "/src/utils/graphUtils"; import {findCell} from "/src/utils/graphUtils";
import {showMainTip, clearMainTip} from "/src/scripts/tooltips";
export function editReliefIcon() { export function editReliefIcon() {
if (customization) return; if (customization) return;

View file

@ -1,6 +1,7 @@
import {restoreDefaultEvents} from "/src/scripts/events"; import {restoreDefaultEvents} from "/src/scripts/events";
import {getPackPolygon, findCell} from "/src/utils/graphUtils"; import {getPackPolygon, findCell} from "/src/utils/graphUtils";
import {last} from "/src/utils/arrayUtils"; import {last} from "/src/utils/arrayUtils";
import {clearMainTip} from "/src/scripts/tooltips";
export function createRiver() { export function createRiver() {
if (customization) return; if (customization) return;

View file

@ -1,4 +1,5 @@
import {findCell, getPackPolygon} from "/src/utils/graphUtils"; import {findCell, getPackPolygon} from "/src/utils/graphUtils";
import {clearMainTip} from "/src/scripts/tooltips";
export function editRiver(id) { export function editRiver(id) {
if (customization) return; if (customization) return;

View file

@ -14,7 +14,7 @@ function overviewRivers() {
$("#riversOverview").dialog({ $("#riversOverview").dialog({
title: "Rivers Overview", title: "Rivers Overview",
resizable: false, resizable: false,
width: fitContent(), width: "fit-content",
position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"} position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}
}); });

View file

@ -1,5 +1,6 @@
"use strict"; import {showMainTip, clearMainTip} from "/src/scripts/tooltips";
function editRoute(onClick) {
export function editRoute(onClick) {
if (customization) return; if (customization) return;
if (!onClick && elSelected && d3.event.target.id === elSelected.attr("id")) return; if (!onClick && elSelected && d3.event.target.id === elSelected.attr("id")) return;
closeDialogs(".stable"); closeDialogs(".stable");

View file

@ -1,4 +1,5 @@
import {byId} from "/src/utils/shorthands"; import {byId} from "/src/utils/shorthands";
import {clearMainTip} from "/src/scripts/tooltips";
window.UISubmap = (function () { window.UISubmap = (function () {
byId("submapPointsInput").addEventListener("input", function () { byId("submapPointsInput").addEventListener("input", function () {

View file

@ -1,6 +1,7 @@
import {restoreDefaultEvents} from "/src/scripts/events"; import {restoreDefaultEvents} from "/src/scripts/events";
import {findCell} from "/src/utils/graphUtils"; import {findCell} from "/src/utils/graphUtils";
import {last} from "/src/utils/arrayUtils"; import {last} from "/src/utils/arrayUtils";
import {clearMainTip} from "/src/scripts/tooltips";
// module to control the Tools options (click to edit, to re-geenerate, tp add) // module to control the Tools options (click to edit, to re-geenerate, tp add)

View file

@ -1,6 +1,7 @@
import {restoreDefaultEvents} from "/src/scripts/events"; import {restoreDefaultEvents} from "/src/scripts/events";
import {findAll, findCell, getPackPolygon} from "/src/utils/graphUtils"; import {findAll, findCell, getPackPolygon} from "/src/utils/graphUtils";
import {unique} from "/src/utils/arrayUtils"; import {unique} from "/src/utils/arrayUtils";
import {showMainTip, clearMainTip} from "/src/scripts/tooltips";
export function editZones() { export function editZones() {
closeDialogs(); closeDialogs();
@ -16,7 +17,7 @@ export function editZones() {
$("#zonesEditor").dialog({ $("#zonesEditor").dialog({
title: "Zones Editor", title: "Zones Editor",
resizable: false, resizable: false,
width: fitContent(), width: "fit-content",
close: () => exitZonesManualAssignment("close"), close: () => exitZonesManualAssignment("close"),
position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"} position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}
}); });
@ -136,7 +137,7 @@ export function editZones() {
body.dataset.type = "absolute"; body.dataset.type = "absolute";
togglePercentageMode(); togglePercentageMode();
} }
$("#zonesEditor").dialog({width: fitContent()}); $("#zonesEditor").dialog({width: "fit-content"});
} }
function zoneHighlightOn(event) { function zoneHighlightOn(event) {

View file

@ -0,0 +1,66 @@
import {tip} from "../scripts/tooltips";
const template = document.createElement("template");
template.innerHTML = /* html */ `
<style>
fill-box:not([disabled]) {
cursor: pointer;
}
fill-box > svg {
vertical-align: middle;
pointer-events: none;
}
fill-box > svg > rect {
stroke: #666666;
stroke-width: 2;
}
</style>
<svg>
<rect x="0" y="0" width="100%" height="100%">
</svg>
`;
class FillBox extends HTMLElement {
private tooltip: string;
constructor() {
super();
this.tooltip = this.dataset.tip || "Fill style. Click to change";
}
private showTip() {
tip(this.tooltip);
}
connectedCallback() {
// cannot use Shadow DOM here as need an access to svg hatches
this.appendChild(template.content.cloneNode(true));
this.querySelector("rect")?.setAttribute("fill", this.fill);
this.querySelector("svg")?.setAttribute("width", this.size);
this.querySelector("svg")?.setAttribute("height", this.size);
this.addEventListener("mousemove", this.showTip);
}
disconnectedCallback() {
this.removeEventListener("mousemove", this.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";
}
}
customElements.define("fill-box", FillBox);

1
src/components/index.ts Normal file
View file

@ -0,0 +1 @@
import "./fill-box";

View file

@ -20,6 +20,8 @@ import {Rulers, Ruler, drawScaleBar} from "./modules/measurers";
import {byId} from "./utils/shorthands"; import {byId} from "./utils/shorthands";
import {addGlobalListeners} from "./scripts/listeners"; import {addGlobalListeners} from "./scripts/listeners";
import {restoreDefaultEvents} from "./scripts/events"; import {restoreDefaultEvents} from "./scripts/events";
import {clearMainTip} from "./scripts/tooltips";
import "./components";
addGlobalListeners(); addGlobalListeners();

110
src/modules/legend.ts Normal file
View file

@ -0,0 +1,110 @@
export function drawLegend(name: string, data: unknown[]) {
legend.selectAll("*").remove(); // fully redraw every time
legend.attr("data", data.join("|")); // store data
const itemsInCol = +styleLegendColItems.value;
const fontSize = +legend.attr("font-size");
const backClr = styleLegendBack.value;
const opacity = +styleLegendOpacity.value;
const lineHeight = Math.round(fontSize * 1.7);
const colorBoxSize = Math.round(fontSize / 1.7);
const colOffset = fontSize;
const vOffset = fontSize / 2;
// append items
const boxes = legend.append("g").attr("stroke-width", 0.5).attr("stroke", "#111111").attr("stroke-dasharray", "none");
const labels = legend.append("g").attr("fill", "#000000").attr("stroke", "none");
const columns = Math.ceil(data.length / itemsInCol);
for (let column = 0, i = 0; column < columns; column++) {
const linesInColumn = Math.ceil(data.length / columns);
const offset = column ? colOffset * 2 + legend.node().getBBox().width : colOffset;
for (let l = 0; l < linesInColumn && data[i]; l++, i++) {
boxes
.append("rect")
.attr("fill", data[i][1])
.attr("x", offset)
.attr("y", lineHeight + l * lineHeight + vOffset)
.attr("width", colorBoxSize)
.attr("height", colorBoxSize);
labels
.append("text")
.text(data[i][2])
.attr("x", offset + colorBoxSize * 1.6)
.attr("y", fontSize / 1.6 + lineHeight + l * lineHeight + vOffset);
}
}
// append label
const offset = colOffset + legend.node().getBBox().width / 2;
labels
.append("text")
.attr("text-anchor", "middle")
.attr("font-weight", "bold")
.attr("font-size", "1.2em")
.attr("id", "legendLabel")
.text(name)
.attr("x", offset)
.attr("y", fontSize * 1.1 + vOffset / 2);
// append box
const bbox = legend.node().getBBox();
const width = bbox.width + colOffset * 2;
const height = bbox.height + colOffset / 2 + vOffset;
legend
.insert("rect", ":first-child")
.attr("id", "legendBox")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height)
.attr("fill", backClr)
.attr("fill-opacity", opacity);
fitLegendBox();
}
// fit Legend box to canvas size
export function fitLegendBox() {
if (!legend.selectAll("*").size()) return;
const px = isNaN(+legend.attr("data-x")) ? 99 : legend.attr("data-x") / 100;
const py = isNaN(+legend.attr("data-y")) ? 93 : legend.attr("data-y") / 100;
const bbox = legend.node().getBBox();
const x = rn(svgWidth * px - bbox.width),
y = rn(svgHeight * py - bbox.height);
legend.attr("transform", `translate(${x},${y})`);
}
// draw legend with the same data, but using different settings
export function redrawLegend() {
if (!legend.select("rect").size()) return;
const name = legend.select("#legendLabel").text();
const data = legend
.attr("data")
.split("|")
.map(l => l.split(","));
drawLegend(name, data);
}
export function dragLegendBox() {
const tr = parseTransform(this.getAttribute("transform"));
const x = +tr[0] - d3.event.x;
const y = +tr[1] - d3.event.y;
const bbox = legend.node().getBBox();
d3.event.on("drag", function () {
const px = rn(((x + d3.event.x + bbox.width) / svgWidth) * 100, 2);
const py = rn(((y + d3.event.y + bbox.height) / svgHeight) * 100, 2);
const transform = `translate(${x + d3.event.x},${y + d3.event.y})`;
legend.attr("transform", transform).attr("data-x", px).attr("data-y", py);
});
}
export function clearLegend() {
legend.selectAll("*").remove();
legend.attr("data", null);
}

View file

@ -1,5 +1,190 @@
import {dragLegendBox} from "../modules/legend";
import {findCell, findGridCell} from "../utils/graphUtils";
import {showMainTip} from "./tooltips";
export function restoreDefaultEvents() { export function restoreDefaultEvents() {
Zoom.setZoomBehavior(); Zoom.setZoomBehavior();
viewbox.style("cursor", "default").on(".drag", null).on("click", clicked).on("touchmove mousemove", onMouseMove); viewbox.style("cursor", "default").on(".drag", null).on("click", clicked).on("touchmove mousemove", onMouseMove);
legend.call(d3.drag().on("start", dragLegendBox)); legend.call(d3.drag().on("start", dragLegendBox));
} }
// on viewbox click event - run function based on target
function clicked() {
const el = d3.event.target;
if (!el || !el.parentElement || !el.parentElement.parentElement) return;
const parent = el.parentElement;
const grand = parent.parentElement;
const great = grand.parentElement;
const p = d3.mouse(this);
const i = findCell(p[0], p[1]);
if (grand.id === "emblems") editEmblem();
else if (parent.id === "rivers") editRiver(el.id);
else if (grand.id === "routes") editRoute();
else if (el.tagName === "tspan" && grand.parentNode.parentNode.id === "labels") editLabel();
else if (grand.id === "burgLabels") editBurg();
else if (grand.id === "burgIcons") editBurg();
else if (parent.id === "ice") editIce();
else if (parent.id === "terrain") editReliefIcon();
else if (grand.id === "markers" || great.id === "markers") editMarker();
else if (grand.id === "coastline") editCoastline();
else if (great.id === "armies") editRegiment();
else if (pack.cells.t[i] === 1) {
const node = byId("island_" + pack.cells.f[i]);
editCoastline(node);
} else if (grand.id === "lakes") editLake();
}
const onMouseMove = debounce(handleMouseMove, 100);
function handleMouseMove() {
const point = d3.mouse(this);
const i = findCell(point[0], point[1]); // pack cell id
if (i === undefined) return;
showNotes(d3.event);
const gridCell = findGridCell(point[0], point[1], grid);
if (tooltip.dataset.main) showMainTip();
else showMapTooltip(point, d3.event, i, gridCell);
if (cellInfo?.offsetParent) updateCellInfo(point, i, gridCell);
}
// show note box on hover (if any)
function showNotes(event: Event) {
if (notesEditor?.offsetParent) return;
let id = event.target.id || event.target.parentNode.id || event.target.parentNode.parentNode.id;
if (event.target.parentNode.parentNode.id === "burgLabels") id = "burg" + event.target.dataset.id;
else if (event.target.parentNode.parentNode.id === "burgIcons") id = "burg" + event.target.dataset.id;
const note = notes.find(note => note.id === id);
if (note !== undefined && note.legend !== "") {
document.getElementById("notes").style.display = "block";
document.getElementById("notesHeader").innerHTML = note.name;
document.getElementById("notesBody").innerHTML = note.legend;
} else if (!options.pinNotes && !markerEditor?.offsetParent) {
document.getElementById("notes").style.display = "none";
document.getElementById("notesHeader").innerHTML = "";
document.getElementById("notesBody").innerHTML = "";
}
}
// show viewbox tooltip if main tooltip is blank
function showMapTooltip(point: number[], event: Event, packCellId: number, gridCellId: number) {
tip(""); // clear tip
const path = event.composedPath ? event.composedPath() : getComposedPath(event.target); // apply polyfill
if (!path[path.length - 8]) return;
const group = path[path.length - 7].id;
const subgroup = path[path.length - 8].id;
const land = pack.cells.h[packCellId] >= 20;
// specific elements
if (group === "armies") return tip(event.target.parentNode.dataset.name + ". Click to edit");
if (group === "emblems" && event.target.tagName === "use") {
const parent = event.target.parentNode;
const [g, type] =
parent.id === "burgEmblems"
? [pack.burgs, "burg"]
: parent.id === "provinceEmblems"
? [pack.provinces, "province"]
: [pack.states, "state"];
const i = +event.target.dataset.i;
if (event.shiftKey) highlightEmblemElement(type, g[i]);
d3.select(event.target).raise();
d3.select(parent).raise();
const name = g[i].fullName || g[i].name;
tip(`${name} ${type} emblem. Click to edit. Hold Shift to show associated area or place`);
return;
}
if (group === "rivers") {
const river = +event.target.id.slice(5);
const r = pack.rivers.find(r => r.i === river);
const name = r ? r.name + " " + r.type : "";
tip(name + ". Click to edit");
if (riversOverview?.offsetParent) highlightEditorLine(riversOverview, river, 5000);
return;
}
if (group === "routes") return tip("Click to edit the Route");
if (group === "terrain") return tip("Click to edit the Relief Icon");
if (subgroup === "burgLabels" || subgroup === "burgIcons") {
const burg = +path[path.length - 10].dataset.id;
const b = pack.burgs[burg];
const population = si(b.population * populationRate * urbanization);
tip(`${b.name}. Population: ${population}. Click to edit`);
if (burgsOverview?.offsetParent) highlightEditorLine(burgsOverview, burg, 5000);
return;
}
if (group === "labels") return tip("Click to edit the Label");
if (group === "markers") return tip("Click to edit the Marker and pin the marker note");
if (group === "ruler") {
const tag = event.target.tagName;
const className = event.target.getAttribute("class");
if (tag === "circle" && className === "edge")
return tip("Drag to adjust. Hold Ctrl and drag to add a point. Click to remove the point");
if (tag === "circle" && className === "control")
return tip("Drag to adjust. Hold Shift and drag to keep axial direction. Click to remove the point");
if (tag === "circle") return tip("Drag to adjust the measurer");
if (tag === "polyline") return tip("Click on drag to add a control point");
if (tag === "path") return tip("Drag to move the measurer");
if (tag === "text") return tip("Drag to move, click to remove the measurer");
}
if (subgroup === "burgIcons") return tip("Click to edit the Burg");
if (subgroup === "burgLabels") return tip("Click to edit the Burg");
if (group === "lakes" && !land) {
const lakeId = +event.target.dataset.f;
const name = pack.features[lakeId]?.name;
const fullName = subgroup === "freshwater" ? name : name + " " + subgroup;
tip(`${fullName} lake. Click to edit`);
return;
}
if (group === "coastline") return tip("Click to edit the coastline");
if (group === "zones") {
const zone = path[path.length - 8];
tip(zone.dataset.description);
if (zonesEditor?.offsetParent) highlightEditorLine(zonesEditor, zone.id, 5000);
return;
}
if (group === "ice") return tip("Click to edit the Ice");
// covering elements
if (layerIsOn("togglePrec") && land) tip("Annual Precipitation: " + getFriendlyPrecipitation(packCellId));
else if (layerIsOn("togglePopulation")) tip(getPopulationTip(packCellId));
else if (layerIsOn("toggleTemp")) tip("Temperature: " + convertTemperature(grid.cells.temp[gridCellId]));
else if (layerIsOn("toggleBiomes") && pack.cells.biome[packCellId]) {
const biome = pack.cells.biome[packCellId];
tip("Biome: " + biomesData.name[biome]);
if (biomesEditor?.offsetParent) highlightEditorLine(biomesEditor, biome);
} else if (layerIsOn("toggleReligions") && pack.cells.religion[packCellId]) {
const religion = pack.cells.religion[packCellId];
const r = pack.religions[religion];
const type = r.type === "Cult" || r.type == "Heresy" ? r.type : r.type + " religion";
tip(type + ": " + r.name);
if (religionsEditor?.offsetParent) highlightEditorLine(religionsEditor, religion);
} else if (pack.cells.state[packCellId] && (layerIsOn("toggleProvinces") || layerIsOn("toggleStates"))) {
const state = pack.cells.state[packCellId];
const stateName = pack.states[state].fullName;
const province = pack.cells.province[packCellId];
const prov = province ? pack.provinces[province].fullName + ", " : "";
tip(prov + stateName);
if (document.getElementById("statesEditor")?.offsetParent) highlightEditorLine(statesEditor, state);
if (document.getElementById("diplomacyEditor")?.offsetParent) highlightEditorLine(diplomacyEditor, state);
if (document.getElementById("militaryOverview")?.offsetParent) highlightEditorLine(militaryOverview, state);
if (document.getElementById("provincesEditor")?.offsetParent) highlightEditorLine(provincesEditor, province);
} else if (layerIsOn("toggleCultures") && pack.cells.culture[packCellId]) {
const culture = pack.cells.culture[packCellId];
tip("Culture: " + pack.cultures[culture].name);
if (document.getElementById("culturesEditor")?.offsetParent) highlightEditorLine(culturesEditor, culture);
} else if (layerIsOn("toggleHeight")) tip("Height: " + getFriendlyHeight(point));
}

View file

@ -1,10 +1,12 @@
import {PRODUCTION} from "../constants"; import {PRODUCTION} from "../constants";
import {assignLockBehavior} from "./options/lock"; import {assignLockBehavior} from "./options/lock";
import {addTooptipListers} from "./tooltips";
export function addGlobalListeners() { export function addGlobalListeners() {
PRODUCTION && registerServiceWorker(); PRODUCTION && registerServiceWorker();
PRODUCTION && addInstallationPrompt(); PRODUCTION && addInstallationPrompt();
assignLockBehavior(); assignLockBehavior();
addTooptipListers();
} }
function registerServiceWorker() { function registerServiceWorker() {

57
src/scripts/tooltips.ts Normal file
View file

@ -0,0 +1,57 @@
import {MOBILE} from "../constants";
import {byId} from "../utils/shorthands";
const $tooltip = byId("tooltip")!;
const tipBackgroundMap = {
info: "linear-gradient(0.1turn, #ffffff00, #5e5c5c80, #ffffff00)",
success: "linear-gradient(0.1turn, #ffffff00, #127912cc, #ffffff00)",
warn: "linear-gradient(0.1turn, #ffffff00, #be5d08cc, #ffffff00)",
error: "linear-gradient(0.1turn, #ffffff00, #e11d1dcc, #ffffff00)"
} as const;
type TTooltipType = keyof typeof tipBackgroundMap;
export function tip(text: string, main = false, type: TTooltipType = "info", time = 0) {
$tooltip.textContent = text;
$tooltip.style.background = tipBackgroundMap[type];
if (main) {
$tooltip.dataset.main = text;
$tooltip.dataset.color = $tooltip.style.background;
}
if (time) setTimeout(clearMainTip, time);
}
export function clearMainTip() {
$tooltip.dataset.color = "";
$tooltip.dataset.main = "";
$tooltip.textContent = "";
}
export function showMainTip() {
$tooltip.style.background = $tooltip.dataset.color || "";
$tooltip.textContent = $tooltip.dataset.main || "";
}
function showDataTip(event: Event) {
if (!event.target) return;
const target = event.target as HTMLElement;
const {parentNode, dataset} = target;
const targetTip = dataset.tip;
const parentTip = (parentNode as HTMLElement)?.dataset.tip;
let tooltip = targetTip || parentTip;
if (!tooltip) return;
if (dataset.shortcut && !MOBILE) tooltip += `. Shortcut: ${dataset.shortcut}`;
tip(tooltip);
}
// show tip on mousemove for all non-svg elemets with data-tip
export function addTooptipListers() {
byId("dialogs")?.on("mousemove", showDataTip);
byId("optionsContainer")?.on("mousemove", showDataTip);
byId("exitCustomization")?.on("mousemove", showDataTip);
}