Explorar el Código

Move filters into their own menu. Add an info menu

This allows for multiple filters to be applied simultaneously. This commit
also moves some misc stuff, like the help and donate buttons, into a new
info menu. This also fixes touches outside of a popout menu not causing
the menu to close.
master
Fen Dweller hace 3 años
padre
commit
29a1f62c4d
Se han modificado 3 ficheros con 307 adiciones y 174 borrados
  1. +59
    -1
      macrovision.css
  2. +26
    -15
      macrovision.html
  3. +222
    -158
      macrovision.js

+ 59
- 1
macrovision.css Ver fichero

@@ -230,6 +230,9 @@ body.show-extra-options .options-block.options-block-optional {
width: 100%;
text-align: center;
margin-top: 10px;
display: flex;
flex-direction: row;
flex-wrap: wrap;
}

.options-banner-button {
@@ -246,7 +249,6 @@ body.show-extra-options .options-block.options-block-optional {
border-color: #666;
border-width: 3pt;
border-style: outset;
min-width: 85%;
max-width: 100%;
}

@@ -1038,4 +1040,60 @@ body.screenshot-mode .scroll-button {
#ground {
--ground-color: #000;
background-color: var(--ground-color);
}

.filter-holder {
user-select: none;
-webkit-user-select: none;
}

.filter-holder > select {
font-size: 200%;
width: 100%;
}

.filter-holder > div {
font-size: 200%;
flex-basis: 150pt;
}

.filter-holder {
display: flex;
align-items: center;
justify-content: left space-between;
padding: 10px 20px 10px 10px;
background: gray;
border-color: darkslategray;
border-width: 5px;
border-style: solid;
}

.filter-holder.enabled {
background: green;
border-color: darkgreen;
}

.info-holder > div,
.info-holder > i {
font-size: 200%;
margin: 10px;
}

.info-holder {
display: flex;
align-items: center;
justify-content: left;
padding: 10px 20px 10px 10px;
background: gray;
border-color: darkslategray;
border-width: 5px;
border-style: solid;
text-decoration: none;
user-select: none;
-webkit-user-select: none;
color: white;
}

.info-holder:hover {
background-color: lightgray;
}

+ 26
- 15
macrovision.html Ver fichero

@@ -57,6 +57,23 @@
</div>
<div class="popout-menu" id="settings-menu">
</div>
<div class="popout-menu" id="filters-menu">
</div>
<div class="popout-menu" id="info-menu">
<a class="info-holder" target="_blank" href="https://www.notion.so/Macrovision-5c7f9377424743358ddf6db5671f439e">
<i class="fas fa-question-circle"></i>
<div>Help</div>
</a>
<a class="info-holder" target="_blank" href="https://docs.google.com/forms/d/e/1FAIpQLSdCiZ2_GVVdXV0bqty4rymbCCwaFq-PLdwmK1vUIakPjv7f2g/viewform">
<i class="fas fa-paper-plane"></i>
<div>Submit Your Character</div>
</a>
<a class="info-holder" target="_blank" href="https://ko-fi.com/P5P5ACDA">
<i class="fas fa-donate"></i>
<div>Donate</div>
</a>
</div>
<div id="menubar">
<span class="menubar-group">
@@ -66,6 +83,12 @@
<button id="toggle-settings">
<i class="fas fa-cogs"></i>
</button>
<button id="toggle-filters">
<i class="fas fa-filter"></i>
</button>
<button id="toggle-info">
<i class="fas fa-info-circle"></i>
</button>
</span>
<span class="menubar-group">
<button id="copy-screenshot">
@@ -80,27 +103,15 @@
</span>
<span class="menubar-group" id="spawners">

</span>
<span class="menubar-group" id="filters">
</span>
<span class="menubar-group" id="search">
<input id="search-box" type="text" placeholder="Search...">
<span class="menubar-group" id="search">
<input id="search-box" type="text" placeholder="Search...">
</span>
</span>
<span class="menubar-group">
<button id="open-help">
<i class="far fa-question-circle"></i>
<span class="sr-only">Help</span>
</button>
</span>
</div>
<div id="main-area">
<div id="options" class="">
<div class="options-banner-buttons">
<a href='https://ko-fi.com/P5P5ACDA' target='_blank'><img style='border:0px;height:36px;transform: translateZ(0);' src='https://cdn.ko-fi.com/cdn/kofi5.png?v=2' border='0' alt='Buy Me a Coffee at ko-fi.com' /></a>
<a target="_blank" href="https://docs.google.com/forms/d/e/1FAIpQLSdCiZ2_GVVdXV0bqty4rymbCCwaFq-PLdwmK1vUIakPjv7f2g/viewform"
class="options-banner-button">Submit Your Character</a>
</div>
<h3 class="options-header">World Info</h3>
<div id="options-world">
<div class="options-label">


+ 222
- 158
macrovision.js Ver fichero

@@ -1452,7 +1452,7 @@ function drawHorizontalScale(ifDirty = false) {
// calling the constructor -- e.g. making a list of authors and
// owners. So, this function is used to generate that information.
// It is invoked like makeEntity so that it can be dropped in easily,
// but returns an object that lets you construct many copies of an entity,
// but returns an object that lets you construct many copies of an entity,
// rather than creating a new entity.
function createEntityMaker(info, views, sizes, forms) {
const maker = {};
@@ -3679,15 +3679,6 @@ document.addEventListener("DOMContentLoaded", () => {
prepareMenu();
prepareEntities();

document.querySelector("#open-help").addEventListener("click", (e) => {
setHelpDate();
document.querySelector("#open-help").classList.remove("highlighted");
window.open(
"https://www.notion.so/Macrovision-5c7f9377424743358ddf6db5671f439e",
"_blank"
);
});

document
.querySelector("#copy-screenshot")
.addEventListener("click", (e) => {
@@ -3734,10 +3725,18 @@ document.addEventListener("DOMContentLoaded", () => {
e.stopPropagation();
});

document.querySelector("#sidebar-menu").addEventListener("touchstart", (e) => {
e.stopPropagation();
});

document.addEventListener("click", (e) => {
document.querySelector("#sidebar-menu").classList.remove("visible");
});

document.addEventListener("touchstart", (e) => {
document.querySelector("#sidebar-menu").classList.remove("visible");
});

document
.querySelector("#toggle-settings")
.addEventListener("click", (e) => {
@@ -3767,10 +3766,96 @@ document.addEventListener("DOMContentLoaded", () => {
e.stopPropagation();
});

document.querySelector("#settings-menu").addEventListener("touchstart", (e) => {
e.stopPropagation();
});

document.addEventListener("click", (e) => {
document.querySelector("#settings-menu").classList.remove("visible");
});

document.addEventListener("touchstart", (e) => {
document.querySelector("#settings-menu").classList.remove("visible");
});

document.querySelector("#toggle-filters").addEventListener("click", (e) => {
const popoutMenu = document.querySelector("#filters-menu");
if (popoutMenu.classList.contains("visible")) {
popoutMenu.classList.remove("visible");
} else {
document
.querySelectorAll(".popout-menu")
.forEach((menu) => menu.classList.remove("visible"));
const rect = e.target.getBoundingClientRect();
popoutMenu.classList.add("visible");
popoutMenu.style.left = rect.x + rect.width + 10 + "px";
popoutMenu.style.top = rect.y + rect.height + 10 + "px";

let menuWidth = popoutMenu.getBoundingClientRect().width;
let screenWidth = window.innerWidth;

if (menuWidth * 1.5 > screenWidth) {
popoutMenu.style.left = 25 + "px";
}
}
e.stopPropagation();
});

document.querySelector("#filters-menu").addEventListener("click", (e) => {
e.stopPropagation();
});

document.querySelector("#filters-menu").addEventListener("touchstart", (e) => {
e.stopPropagation();
});

document.addEventListener("click", (e) => {
document.querySelector("#filters-menu").classList.remove("visible");
});

document.addEventListener("touchstart", (e) => {
document.querySelector("#filters-menu").classList.remove("visible");
});

document.querySelector("#toggle-info").addEventListener("click", (e) => {
const popoutMenu = document.querySelector("#info-menu");
if (popoutMenu.classList.contains("visible")) {
popoutMenu.classList.remove("visible");
} else {
document
.querySelectorAll(".popout-menu")
.forEach((menu) => menu.classList.remove("visible"));
const rect = e.target.getBoundingClientRect();
popoutMenu.classList.add("visible");
popoutMenu.style.left = rect.x + rect.width + 10 + "px";
popoutMenu.style.top = rect.y + rect.height + 10 + "px";

let menuWidth = popoutMenu.getBoundingClientRect().width;
let screenWidth = window.innerWidth;

if (menuWidth * 1.5 > screenWidth) {
popoutMenu.style.left = 25 + "px";
}
}
e.stopPropagation();
});

document.querySelector("#info-menu").addEventListener("click", (e) => {
e.stopPropagation();
});

document.querySelector("#info-menu").addEventListener("touchstart", (e) => {
e.stopPropagation();
});

document.addEventListener("click", (e) => {
document.querySelector("#info-menu").classList.remove("visible");
});

document.addEventListener("touchstart", (e) => {
document.querySelector("#info-menu").classList.remove("visible");
});

window.addEventListener("unload", () => {
saveScene("autosave");
setUserSettings(exportUserSettings());
@@ -4845,6 +4930,8 @@ document.addEventListener("DOMContentLoaded", () => {
);
}
};

updateFilter();
});

let searchText = "";
@@ -4898,13 +4985,6 @@ function makeCustomEntity(url, x = 0.5, y = 0.5) {
}

const filterDefs = {
none: {
id: "none",
name: "No Filter",
extract: (maker) => [],
render: (name) => name,
sort: (tag1, tag2) => tag1[1].localeCompare(tag2[1]),
},
author: {
id: "author",
name: "Authors",
@@ -5027,6 +5107,8 @@ const filterDefs = {
},
};

const filterStates = {};

const sizeCategories = {
atomic: math.unit(100, "angstroms"),
microscopic: math.unit(100, "micrometers"),
@@ -5079,20 +5161,18 @@ function prepareEntities() {
return x.name.localeCompare(y.name);
});
const holder = document.querySelector("#spawners");
const filterHolder = document.querySelector("#filters");
const filterMenu = document.querySelector("#filters-menu");

const categorySelect = document.createElement("select");
categorySelect.id = "category-picker";
const filterSelect = document.createElement("select");
filterSelect.id = "filter-picker";

holder.appendChild(categorySelect);
filterHolder.appendChild(filterSelect);

const filterSets = {};

Object.values(filterDefs).forEach((filter) => {
filterSets[filter.id] = new Set();
filterStates[filter.id] = false;
});

Object.entries(availableEntities).forEach(([category, entityList]) => {
@@ -5183,76 +5263,36 @@ function prepareEntities() {
});

Object.values(filterDefs).forEach((filter) => {
const option = document.createElement("option");
option.innerText = filter.name;
option.value = filter.id;
filterSelect.appendChild(option);
const filterHolder = document.createElement("label");
filterHolder.setAttribute("for", "filter-toggle-" + filter.id);
filterHolder.classList.add("filter-holder");

const filterToggle = document.createElement("input");
filterToggle.type = "checkbox";
filterToggle.id = "filter-toggle-" + filter.id;
filterHolder.appendChild(filterToggle);

filterToggle.addEventListener("input", e => {
filterStates[filter.id] = filterToggle.checked
if (filterToggle.checked) {
filterHolder.classList.add("enabled");
} else {
filterHolder.classList.remove("enabled");
}
clearFilter();
updateFilter();
});

const filterLabel = document.createElement("div");
filterLabel.innerText = filter.name;
filterHolder.appendChild(filterLabel);

const filterNameSelect = document.createElement("select");
filterNameSelect.classList.add("filter-select");
filterNameSelect.id = "filter-" + filter.id;
filterHolder.appendChild(filterNameSelect);

const button = document.createElement("button");
button.classList.add("filter-button");
button.id = "create-filtered-" + filter.id + "-button";
filterHolder.appendChild(button);

const counter = document.createElement("div");
counter.classList.add("button-counter");
counter.innerText = "10";
button.appendChild(counter);
const i = document.createElement("i");
i.classList.add("fas");
i.classList.add("fa-plus");
button.appendChild(i);

button.addEventListener("click", (e) => {
const makers = Array.from(
document.querySelector(".entity-select.category-visible")
).filter((element) => !element.classList.contains("filtered"));
const count = makers.length + 2;
let index = 1;

if (makers.length > 50) {
if (
!confirm(
"Really spawn " + makers.length + " things at once?"
)
) {
return;
}
}

const worldWidth =
(config.height.toNumber("meters") / canvasHeight) * canvasWidth;

const spawned = makers.map((element) => {
const category =
document.querySelector("#category-picker").value;
const maker = availableEntities[category][element.value];
const entity = maker.constructor();
displayEntity(
entity,
entity.view,
-worldWidth * 0.45 +
config.x +
(worldWidth * 0.9 * index) / (count - 1),
config.y
);
index += 1;
return entityIndex - 1;
});
updateSizes(true);

if (config.autoFitAdd) {
let targets = {};
spawned.forEach((key) => {
targets[key] = entities[key];
});
fitEntities(targets);
}
});
filterMenu.appendChild(filterHolder);

Array.from(filterSets[filter.id])
.map((name) => [name, filter.render(name)])
@@ -5269,6 +5309,14 @@ function prepareEntities() {
});
});

const spawnButton = document.createElement("button");
spawnButton.id = "spawn-all"
spawnButton.addEventListener("click", e => {
spawnAll();
});

filterMenu.appendChild(spawnButton);

console.log(
"Loaded " + Object.keys(availableEntitiesByName).length + " entities"
);
@@ -5298,20 +5346,49 @@ function prepareEntities() {

recomputeFilters();

filterSelect.addEventListener("input", (e) => {
const oldSelect = document.querySelector(
".filter-select.category-visible"
);
if (oldSelect) oldSelect.classList.remove("category-visible");
ratioInfo = document.body.querySelector(".extra-info");
}

const newSelect = document.querySelector("#filter-" + e.target.value);
if (newSelect && e.target.value != "none")
newSelect.classList.add("category-visible");
function spawnAll() {
const makers = Array.from(
document.querySelector(".entity-select.category-visible")
).filter((element) => !element.classList.contains("filtered"));
const count = makers.length + 2;
let index = 1;

updateFilter();
if (makers.length > 50) {
if (!confirm("Really spawn " + makers.length + " things at once?")) {
return;
}
}

const worldWidth =
(config.height.toNumber("meters") / canvasHeight) * canvasWidth;

const spawned = makers.map((element) => {
const category = document.querySelector("#category-picker").value;
const maker = availableEntities[category][element.value];
const entity = maker.constructor();
displayEntity(
entity,
entity.view,
-worldWidth * 0.45 +
config.x +
(worldWidth * 0.9 * index) / (count - 1),
config.y
);
index += 1;
return entityIndex - 1;
});
updateSizes(true);

ratioInfo = document.body.querySelector(".extra-info");
if (config.autoFitAdd) {
let targets = {};
spawned.forEach((key) => {
targets[key] = entities[key];
});
fitEntities(targets);
}
}

// Only display authors and owners if they appear
@@ -5325,75 +5402,64 @@ function recomputeFilters() {
filterSets[filter.id] = new Set();
});

document
.querySelectorAll(".entity-select.category-visible > option")
.forEach((element) => {
const entity = availableEntities[category][element.value];

Object.values(filterDefs).forEach((filter) => {
filter.extract(entity).forEach((result) => {
filterSets[filter.id].add(result);
});
availableEntities[category].forEach((entity) => {
Object.values(filterDefs).forEach((filter) => {
filter.extract(entity).forEach((result) => {
filterSets[filter.id].add(result);
});
});
});

Object.values(filterDefs).forEach((filter) => {
filterStates[filter.id] = false;
document.querySelector("#filter-toggle-" + filter.id).checked = false
document.querySelector("#filter-toggle-" + filter.id).dispatchEvent(new Event("click"))
// always show the "none" option
let found = filter.id == "none";
const filterSelect = document.querySelector("#filter-" + filter.id);
const filterSelectHolder = filterSelect.parentElement;
filterSelect.querySelectorAll("option").forEach((element) => {
if (
filterSets[filter.id].has(element.value) ||
filter.id == "none"
) {
element.classList.remove("filtered");
element.disabled = false;
found = true;
} else {
element.classList.add("filtered");
element.disabled = true;
}
});

document
.querySelectorAll("#filter-" + filter.id + " > option")
.forEach((element) => {
if (
filterSets[filter.id].has(element.value) ||
filter.id == "none"
) {
element.classList.remove("filtered");
element.disabled = false;
found = true;
} else {
element.classList.add("filtered");
element.disabled = true;
}
});

const filterOption = document.querySelector(
"#filter-picker > option[value='" + filter.id + "']"
);
if (found) {
filterOption.classList.remove("filtered");
filterOption.disabled = false;
filterSelectHolder.style.display = "";
} else {
filterOption.classList.add("filtered");
filterOption.disabled = true;
filterSelectHolder.style.display = "none";
}
});

document.querySelector("#filter-picker").value = "none";
document.querySelector("#filter-picker").dispatchEvent(new Event("input"));
}

function updateFilter() {
const category = document.querySelector("#category-picker").value;
const type = document.querySelector("#filter-picker").value;
const filterKeySelect = document.querySelector(
".filter-select.category-visible"
);

clearFilter();
const types = Object.values(filterDefs).filter(def => filterStates[def.id]).map(def => def.id)

const keys = {

const noFilter = !filterKeySelect;
}

types.forEach(type => {
const filterKeySelect = document.querySelector("#filter-" + type);
keys[type] = filterKeySelect.value;
})

clearFilter();

let key;
let current = document.querySelector(
".entity-select.category-visible"
).value;

if (!noFilter) {
key = filterKeySelect.value;
current;
}

let replace = current == "";
let first = null;

@@ -5403,16 +5469,18 @@ function updateFilter() {
document
.querySelectorAll(".entity-select.category-visible > option")
.forEach((element) => {
let keep = noFilter;
let keep = true

if (
!noFilter &&
filterDefs[type]
.extract(availableEntities[category][element.value])
.indexOf(key) >= 0
) {
keep = true;
}
types.forEach(type => {
if (
!(filterDefs[type]
.extract(availableEntities[category][element.value])
.indexOf(keys[type]) >= 0)
) {
keep = false;
}
})

if (
searchText != "" &&
@@ -5438,13 +5506,9 @@ function updateFilter() {
}
});

const button = document.querySelector(
".filter-select.category-visible + button"
);

if (button) {
button.querySelector(".button-counter").innerText = count;
}
const button = document.querySelector("#spawn-all")
button.innerText = "Spawn " + count + " filtered " + (count == 1 ? "entity" : "entities") + ".";

if (replace) {
document.querySelector(".entity-select.category-visible").value = first;


Cargando…
Cancelar
Guardar