begin file loading implementation
This commit is contained in:
parent
ba3adc63c4
commit
585335c21c
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1 @@
|
|||
{"addons":[],"configuration":{"nuclearcraft:overhaul_sfr":{"blocks":[{"name":"nuclearcraft:solid_fission_controller","type":"legacy_block"},{"name":"nuclearcraft:fission_vent","type":"legacy_block","blockstate":{"active":false}},{"name":"nuclearcraft:fission_vent","type":"legacy_block","blockstate":{"active":true}},{"name":"nuclearcraft:fission_casing","type":"legacy_block"},{"name":"nuclearcraft:fission_glass","type":"legacy_block"},{"metadata":2,"name":"nuclearcraft:fission_source","type":"legacy_block","blockstate":{"type":"californium"}},{"metadata":1,"name":"nuclearcraft:solid_fission_sink","type":"legacy_block","blockstate":{"type":"iron"}},{"metadata":7,"name":"nuclearcraft:solid_fission_sink","type":"legacy_block","blockstate":{"type":"lapis"}},{"metadata":8,"name":"nuclearcraft:solid_fission_sink","type":"legacy_block","blockstate":{"type":"gold"}},{"metadata":12,"name":"nuclearcraft:solid_fission_sink","type":"legacy_block","blockstate":{"type":"purpur"}},{"name":"nuclearcraft:solid_fission_cell","type":"legacy_block","modules":{"ncpf:block_recipes":{"recipes":[{"oredict":"ingotHEU233ZA","type":"oredict"}]}}},{"name":"nuclearcraft:fission_cell_port","type":"legacy_block","blockstate":{"active":false},"modules":{"ncpf:block_recipes":{"recipes":[{"oredict":"ingotHEU233ZA","type":"oredict"}]}}},{"name":"nuclearcraft:fission_cell_port","type":"legacy_block","blockstate":{"active":true},"modules":{"ncpf:block_recipes":{"recipes":[{"oredict":"ingotHEU233ZA","type":"oredict"}]}}},{"name":"nuclearcraft:heavy_water_moderator","type":"legacy_block"},{"metadata":0,"name":"nuclearcraft:fission_reflector","type":"legacy_block","blockstate":{"type":"beryllium_carbon"}}],"coolant_recipes":[{"elements":[{"name":"water","type":"legacy_fluid"},{"name":"condensate_water","type":"legacy_fluid"}],"type":"list"}]}},"designs":[{"design":[[[3,3,3,3,3],[3,0,1,2,3],[3,4,4,11,3],[3,3,3,3,3]],[[3,12,4,4,3],[5,10,13,14,4],[4,7,6,9,4],[3,4,4,4,3]],[[3,4,4,4,3],[4,7,6,9,4],[4,-1,8,-1,4],[3,4,4,4,3]],[[3,3,3,3,3],[3,4,4,4,3],[3,4,4,4,3],[3,3,3,3,3]]],"block_recipes":[[[0]],[[0],[0]]],"type":"nuclearcraft:overhaul_sfr","coolant_recipe":0,"dimensions":[4,4,5]}],"version":1}
|
||||
17
index.html
17
index.html
|
|
@ -54,7 +54,7 @@
|
|||
<input type="file" data-input="configuration" accept=".ncpf.json" />
|
||||
</div>
|
||||
<div data-state="planning">
|
||||
<div class="list" id="planner-list">
|
||||
<div class="list" id="planner">
|
||||
<template>
|
||||
<div class="grid m2">
|
||||
<template>
|
||||
|
|
@ -71,5 +71,20 @@
|
|||
<footer id="bottom-menu">
|
||||
<span data-resize="bottom-menu"></span>
|
||||
</footer>
|
||||
<div class="dialog hidden" data-dialog="fileLoad:chooseDesign">
|
||||
<section>
|
||||
<header>Choose a design to open</header>
|
||||
<main class="list-choice">
|
||||
<template>
|
||||
<button>
|
||||
<span data-text="Name" class="bold"></span> by <span data-text="Author"></span>
|
||||
</button>
|
||||
</template>
|
||||
</main>
|
||||
<footer>
|
||||
<button data-dialog-action="cancel">Cancel</button>
|
||||
</footer>
|
||||
</section>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
import { DomList } from "./dom-list.js";
|
||||
import { addEventToElement, setText } from "./dom-utils.js";
|
||||
import { emitEvent, listenTo } from "./events.js";
|
||||
|
||||
export function initializeDialogs() {
|
||||
listenTo("fileLoad:chooseDesign", designChoiceDialog);
|
||||
}
|
||||
|
||||
function designChoiceDialog(e) {
|
||||
const dialog = document.querySelector('[data-dialog="fileLoad:chooseDesign"]');
|
||||
|
||||
const dismiss = (data) => {
|
||||
dialog.classList.add("hidden");
|
||||
emitEvent("fileLoad:chooseDesign:choice", data);
|
||||
}
|
||||
|
||||
addEventToElement(
|
||||
"dialog-action", "cancel", "click",
|
||||
() => dismiss(-1),
|
||||
{ element: dialog },
|
||||
);
|
||||
dialog.addEventListener("click", e => {
|
||||
if (e.target !== e.currentTarget) return;
|
||||
dismiss(-1);
|
||||
});
|
||||
|
||||
const list = new DomList(dialog.querySelector(":scope > section > main"));
|
||||
list.clear();
|
||||
|
||||
for (const designIndex in e.detail.designs) {
|
||||
const design = e.detail.designs[designIndex];
|
||||
const button = list.makeItem();
|
||||
setText("Name", design.modules["plannerator:metadata"].Name, { element: button });
|
||||
setText("Author", design.modules["plannerator:metadata"].Author, { element: button });
|
||||
button.addEventListener("click", () => dismiss(designIndex));
|
||||
list.addItem(button);
|
||||
}
|
||||
|
||||
dialog.classList.remove("hidden");
|
||||
}
|
||||
|
|
@ -1,16 +1,21 @@
|
|||
export function getElements(dataType, dataValue, props = {}) {
|
||||
export function getElements(dataType, dataValue, {
|
||||
additionalDataFilters = [],
|
||||
additionalPropertyFilters = [],
|
||||
element = document,
|
||||
} = {}) {
|
||||
// props: { additionalDataFilters: [key, value][], additionalPropertyFilters: [key, value][] }
|
||||
|
||||
const queryString = [
|
||||
...[[dataType, dataValue], ...(props.additionalDataFilters ?? [])]
|
||||
...[[dataType, dataValue], ...additionalDataFilters]
|
||||
.map(([k, v]) => [`data-${k}`, v]),
|
||||
...(props.additionalPropertyFilters ?? []),
|
||||
...additionalPropertyFilters,
|
||||
].map(([k, v]) => v === '*' ? `[${k}]` : `[${k}='${v}']`).join("");
|
||||
return document.querySelectorAll(queryString);
|
||||
return element.querySelectorAll(queryString);
|
||||
}
|
||||
|
||||
export function addEventToElement(dataType, dataValue, event, callback, props = {}) {
|
||||
getElements(dataType, dataValue, props).forEach(e => e.addEventListener(event, callback));
|
||||
getElements(dataType, dataValue, props)
|
||||
.forEach(e => e.addEventListener(event, callback));
|
||||
}
|
||||
|
||||
export function addEventToInput(dataValue, event, callback, props = {}) {
|
||||
|
|
@ -20,6 +25,11 @@ export function addEventToButton(dataValue, event, callback, props = {}) {
|
|||
addEventToElement("button", dataValue, event, callback, props);
|
||||
}
|
||||
|
||||
export function setText(dataValue, text, props = {}) {
|
||||
getElements("text", dataValue, props)
|
||||
.forEach(e => e.innerText = text);
|
||||
}
|
||||
|
||||
export function debounce(fn, milliseconds = 200) {
|
||||
let timeout = null;
|
||||
return (e) => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
import { awaitEvent, emitEvent } from "./events.js";
|
||||
import { clearPlanner } from "./planner.js";
|
||||
|
||||
export function showOpenFileDialog() {
|
||||
const input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.accept = ".ncpf.json";
|
||||
input.addEventListener("change", async () => {
|
||||
const data = await new Promise(resolve => {
|
||||
const reader = new FileReader();
|
||||
reader.addEventListener("load", () => {
|
||||
resolve(JSON.parse(reader.result));
|
||||
});
|
||||
reader.readAsText(input.files[0]);
|
||||
});
|
||||
void loadFile(data);
|
||||
});
|
||||
input.click();
|
||||
}
|
||||
|
||||
export async function loadExampleFile(filename) {
|
||||
const data = await fetch(`./examples/${filename}.ncpf.json`)
|
||||
.then(data => data.json());
|
||||
return loadFile(data);
|
||||
}
|
||||
|
||||
export async function loadFile(data) {
|
||||
if (!data.designs) throw new Error("invalid file");
|
||||
if (data.designs.length === 0) throw new Error("no designs");
|
||||
|
||||
let design = data.designs[0];
|
||||
if (data.designs.length > 1) {
|
||||
const prepChoice = awaitEvent("fileLoad:chooseDesign:choice");
|
||||
emitEvent("fileLoad:chooseDesign", data);
|
||||
const res = await prepChoice;
|
||||
design = data.designs[res.detail];
|
||||
}
|
||||
|
||||
if (!design) throw new Error("no design");
|
||||
|
||||
clearPlanner(design.dimensions, design.type);
|
||||
console.log(data);
|
||||
console.log(design);
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
import { loadConfiguration } from "./configuration.js";
|
||||
import { initializeDialogs } from "./dialogs.js";
|
||||
import { addEventToButton, addEventToInput, debounce, getElements } from "./dom-utils.js";
|
||||
import { emitEvent, listenTo } from "./events.js";
|
||||
import { awaitEvent, emitEvent, listenTo } from "./events.js";
|
||||
import { loadExampleFile, showOpenFileDialog } from "./files.js";
|
||||
import { initializePlanner } from "./planner.js";
|
||||
import { initializeMenuResizing } from "./resize-menus.js";
|
||||
import { getState, setState, STATE } from "./state.js";
|
||||
|
|
@ -22,6 +24,7 @@ window.addEventListener("load", () => {
|
|||
addEventToInput("block-filter", "input", debounce(e => {
|
||||
emitEvent("block-picker:filter", e.target.value);
|
||||
}));
|
||||
addEventToButton("open-file", "click", showOpenFileDialog);
|
||||
|
||||
listenTo("state", () => {
|
||||
console.log("State changed", getState());
|
||||
|
|
@ -39,8 +42,12 @@ window.addEventListener("load", () => {
|
|||
initializeMenuResizing();
|
||||
initializeTooltips();
|
||||
initializeMenuBar();
|
||||
initializeDialogs();
|
||||
|
||||
emitEvent("setup-end");
|
||||
|
||||
void loadConfiguration("addons");
|
||||
void loadConfiguration("catastrophe");
|
||||
|
||||
awaitEvent("config:end").then(() => loadExampleFile("tiny reactor"));
|
||||
// awaitEvent("config:end").then(() => loadExampleFile("multi-reactor"));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,13 +6,14 @@ import { createTooltip } from "./tooltip.js";
|
|||
import { lookupFromMetadata, resolveBlockName } from "./ncpf-utils.js";
|
||||
|
||||
let suppressHover = false;
|
||||
let multiblockSize = [7, 7, 5]; // width (screen x), depth (screen y), height (xy grids)
|
||||
let multiblockSize = [9, 7, 9]; // width (screen x), height (xy grids), depth (screen y)
|
||||
let multiblockType = "nuclearcraft:overhaul_sfr";
|
||||
let activeItem = -1;
|
||||
|
||||
function cellHoverEvent(e) {
|
||||
if (suppressHover) return;
|
||||
|
||||
const cells = document.querySelectorAll("#planner-list [data-x]");
|
||||
const cells = document.querySelectorAll("#planner [data-x]");
|
||||
cells.forEach(c => {
|
||||
delete c.dataset.highlight;
|
||||
delete c.dataset.highlightDistance;
|
||||
|
|
@ -58,7 +59,7 @@ function cellClickEvent(e) {
|
|||
window.removeEventListener("mouseup", cellClickEvent);
|
||||
emitEvent("planner:drag:end");
|
||||
|
||||
const cells = document.querySelectorAll("#planner-list [data-x]");
|
||||
const cells = document.querySelectorAll("#planner [data-x]");
|
||||
cells.forEach(c => {
|
||||
if (c.dataset.preview) {
|
||||
c.dataset.tile = c.dataset.preview;
|
||||
|
|
@ -118,7 +119,7 @@ function buildBlockPicker() {
|
|||
grid.clear();
|
||||
|
||||
const blocks = atlases.flatMap(a => a.metadata)
|
||||
.filter(m => m.configuration === "nuclearcraft:overhaul_sfr" && m.configurationList === "blocks");
|
||||
.filter(m => m.configuration === multiblockType && m.configurationList === "blocks");
|
||||
console.log(blocks);
|
||||
blocks.forEach(m => {
|
||||
const item = grid.makeItem();
|
||||
|
|
@ -141,21 +142,32 @@ function buildPlanner() {
|
|||
}
|
||||
});
|
||||
|
||||
const list = new DomList(document.querySelector("#planner-list"));
|
||||
const planner = document.querySelector("#planner");
|
||||
planner.dataset.casing = "hide";
|
||||
|
||||
const list = new DomList(planner);
|
||||
list.clear();
|
||||
|
||||
for (let z = 0; z < multiblockSize[2]; z++) {
|
||||
for (let y = 0; y < multiblockSize[1]; y++) {
|
||||
const item = list.makeItem();
|
||||
item.style.setProperty("--width", multiblockSize[1]);
|
||||
item.style.setProperty("--width", multiblockSize[0]);
|
||||
if (y === 0 || y === multiblockSize[1] - 1) {
|
||||
item.dataset.casing = "";
|
||||
}
|
||||
const grid = new DomList(item);
|
||||
grid.clear();
|
||||
for (let y = 0; y < multiblockSize[1]; y++) {
|
||||
for (let z = 0; z < multiblockSize[2]; z++) {
|
||||
for (let x = 0; x < multiblockSize[0]; x++) {
|
||||
const cell = grid.makeItem();
|
||||
cell.dataset.x = x.toString(10);
|
||||
cell.dataset.y = y.toString(10);
|
||||
cell.dataset.z = z.toString(10);
|
||||
cell.dataset.tile = (-1).toString(10);
|
||||
if (x === 0 || x === multiblockSize[0] - 1
|
||||
|| y === 0 || y === multiblockSize[1] - 1
|
||||
|| z === 0 || z === multiblockSize[2] - 1) {
|
||||
cell.dataset.casing = "";
|
||||
}
|
||||
cell.appendChild(makeAtlasImage(-1));
|
||||
cell.addEventListener("mouseenter", cellHoverEvent);
|
||||
cell.addEventListener("mouseleave", cellHoverEvent);
|
||||
|
|
@ -170,6 +182,12 @@ function buildPlanner() {
|
|||
}
|
||||
}
|
||||
|
||||
export function clearPlanner(size, type) {
|
||||
multiblockSize = size;
|
||||
multiblockType = type;
|
||||
buildPlanner();
|
||||
}
|
||||
|
||||
function* getBlockTooltip(metadata) {
|
||||
const data = lookupFromMetadata(metadata);
|
||||
|
||||
|
|
|
|||
70
style.css
70
style.css
|
|
@ -24,6 +24,10 @@
|
|||
display: none;
|
||||
}
|
||||
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
|
@ -33,7 +37,7 @@
|
|||
}
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(var(--width), calc(var(--scale-factor) * var(--default-size)));
|
||||
grid-template-columns: repeat(var(--width-override, var(--width)), calc(var(--scale-factor) * var(--default-size)));
|
||||
}
|
||||
|
||||
.m1 {
|
||||
|
|
@ -194,6 +198,14 @@ input:not([type="checkbox"]):not([type="radio"]):not([type="file"]) {
|
|||
background-color: var(--highlight-color, #f808);
|
||||
}
|
||||
[data-highlight='y'] .atlas-texture:after, [data-highlight='y'] .atlas-air:after {
|
||||
inset:
|
||||
calc(var(--scale-factor) * var(--size-y, var(--default-size)) * 0.4)
|
||||
calc(var(--scale-factor) * var(--size-x, var(--default-size)) * 0.4)
|
||||
calc(var(--scale-factor) * var(--size-y, var(--default-size)) * 0.4)
|
||||
calc(var(--scale-factor) * var(--size-x, var(--default-size)) * 0.4);
|
||||
background-color: var(--highlight-color, #f808);
|
||||
}
|
||||
[data-highlight='z'] .atlas-texture:after, [data-highlight='z'] .atlas-air:after {
|
||||
inset:
|
||||
0
|
||||
calc(var(--scale-factor) * var(--size-x, var(--default-size)) * 0.4)
|
||||
|
|
@ -201,13 +213,11 @@ input:not([type="checkbox"]):not([type="radio"]):not([type="file"]) {
|
|||
calc(var(--scale-factor) * var(--size-x, var(--default-size)) * 0.4);
|
||||
background-color: var(--highlight-color, #f808);
|
||||
}
|
||||
[data-highlight='z'] .atlas-texture:after, [data-highlight='z'] .atlas-air:after {
|
||||
inset:
|
||||
calc(var(--scale-factor) * var(--size-y, var(--default-size)) * 0.4)
|
||||
calc(var(--scale-factor) * var(--size-x, var(--default-size)) * 0.4)
|
||||
calc(var(--scale-factor) * var(--size-y, var(--default-size)) * 0.4)
|
||||
calc(var(--scale-factor) * var(--size-x, var(--default-size)) * 0.4);
|
||||
background-color: var(--highlight-color, #f808);
|
||||
[data-casing="hide"] > * {
|
||||
--width-override: calc(var(--width) - 2);
|
||||
}
|
||||
[data-casing="hide"] [data-casing] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#block-list [data-active] {
|
||||
|
|
@ -249,7 +259,7 @@ input:not([type="checkbox"]):not([type="radio"]):not([type="file"]) {
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
z-index: 100;
|
||||
box-shadow: var(--menu-shadow, 0 2px 4px rgba(0, 0, 0, 0.2));
|
||||
box-shadow: var(--menu-shadow, 0 2px 4px #0002);
|
||||
}
|
||||
.menu-bar button {
|
||||
all: initial;
|
||||
|
|
@ -274,3 +284,45 @@ input:not([type="checkbox"]):not([type="radio"]):not([type="file"]) {
|
|||
.menu-bar .alt-key {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.dialog.hidden {
|
||||
display: none;
|
||||
}
|
||||
.dialog {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: var(--dialog-backdrop, #0008);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 10;
|
||||
padding: var(--dialog-margin, 1em);
|
||||
}
|
||||
.dialog > section {
|
||||
z-index: 11;
|
||||
padding: var(--dialog-padding, 1em);
|
||||
background: var(--dialog-background, #fff);
|
||||
color: var(--dialog-foreground, #000);
|
||||
border-radius: var(--dialog-border-radius, .25em);
|
||||
box-shadow: var(--dialog-box-shaodw, 0 4px 8px #0002);
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
.dialog > section > header {
|
||||
font-size: var(--dialog-header-size, 1.25em);
|
||||
}
|
||||
.dialog > section > main {
|
||||
padding: 1em 0;
|
||||
}
|
||||
.dialog > section > footer {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
.dialog > section > .list-choice {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: .25em;
|
||||
}
|
||||
.dialog > section > .list-choice > * {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue