From 29a1f62c4d207aebcef4f0d48401ecac8826e85d Mon Sep 17 00:00:00 2001 From: Fen Dweller Date: Sat, 29 Jan 2022 17:04:46 -0500 Subject: [PATCH] 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. --- macrovision.css | 60 +++++++- macrovision.html | 41 +++-- macrovision.js | 380 +++++++++++++++++++++++++++-------------------- 3 files changed, 307 insertions(+), 174 deletions(-) diff --git a/macrovision.css b/macrovision.css index 148337de..c9f63787 100644 --- a/macrovision.css +++ b/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; } \ No newline at end of file diff --git a/macrovision.html b/macrovision.html index 6210f61b..4e58567a 100644 --- a/macrovision.html +++ b/macrovision.html @@ -57,6 +57,23 @@
+
+
+ +
+
+ + +
Help
+
+ + +
Submit Your Character
+
+ + +
Donate
+
-

World Info

diff --git a/macrovision.js b/macrovision.js index 1b8a9a53..1cc7d55b 100644 --- a/macrovision.js +++ b/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;