瀏覽代碼

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 3 年之前
父節點
當前提交
29a1f62c4d
共有 3 個檔案被更改,包括 307 行新增174 行删除
  1. +59
    -1
      macrovision.css
  2. +26
    -15
      macrovision.html
  3. +222
    -158
      macrovision.js

+ 59
- 1
macrovision.css 查看文件

@@ -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 查看文件

@@ -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 查看文件

@@ -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;


Loading…
取消
儲存