From 3204043e5cd855855b045d8f410b320dcb772d25 Mon Sep 17 00:00:00 2001 From: MetroWind Date: Sun, 31 Aug 2025 09:10:28 -0700 Subject: Support multiple floors. Use 98 style. --- index.html | 5 +- scripts/main.js | 56 ++++++++-- styles.css | 309 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 357 insertions(+), 13 deletions(-) diff --git a/index.html b/index.html index f46f57c..54d6aa3 100644 --- a/index.html +++ b/index.html @@ -16,7 +16,10 @@ My test page -
+
+
NMS Freighter Planner
+
+
diff --git a/scripts/main.js b/scripts/main.js index 7347f37..b8de9e4 100644 --- a/scripts/main.js +++ b/scripts/main.js @@ -1,6 +1,7 @@ const DEFAULT_APP_STATE = { - floor: 1, - plan: new Array(GRID_SIZE_X * GRID_SIZE_Y).fill(0), + floor: 0, // This is 0-based. + plan: new Array(FLOOR_COUNT).fill( + new Array(GRID_SIZE_X * GRID_SIZE_Y).fill(0)), // mouse_state can be “normal”, or “dnd”. mouse_state: "normal", plan_code: "", @@ -9,7 +10,9 @@ const DEFAULT_APP_STATE = { async function serializePlan(app_state) { let rest = new Array(GRID_SIZE_X * GRID_SIZE_Y * (FLOOR_COUNT - 1)).fill(0); - return compressBytes(Uint8Array.from(app_state.plan.concat(rest))); + let concated_plans = + app_state.plan.reduce((big, floor_plan) => big.concat(floor_plan), []); + return compressBytes(Uint8Array.from(concated_plans)); } function genGrid(x_count, y_count, cell_size) @@ -105,11 +108,21 @@ ${cell_y * (CELL_SIZE + 1) + 1})`}, ASSET_WHITE_BG, TILES[idx].inner_svg); function PlanView({plan, mouse_state}) { + console.debug("Looking at plan ", plan); return h("div", {}, - h("p", {}, "The plan:"), h(PlanGridView, {content: plan, mouse_state: mouse_state})); } +// on_floor_change takes one argument, which is the 0-based floor number. +function FloorSelector({floor, on_floor_change}) +{ + return h("div", {"id": "FloorSelectorWrapper", "class": "ButtonRow"}, + h("label", {}, "Floor"), + h("input", {"id": "InputFloor", "type": "number", "step": 1, "min": 1, + "max": 15, "value": floor, + onchange: e => on_floor_change(e.target.value - 1),},)); +} + function App({initial_state}) { const [state, setState] = preactHooks.useState(initial_state); @@ -134,7 +147,7 @@ function App({initial_state}) let asset_index = parseInt(e.target.getAttribute("data-asset-index")); let new_state = structuredClone(state); - new_state.plan[cell_index] = asset_index; + new_state.plan[new_state.floor][cell_index] = asset_index; new_state.mouse_state = "normal"; serializePlan(new_state).then((s) => { new_state.plan_code = s; @@ -168,16 +181,27 @@ function App({initial_state}) encodeURIComponent(svg_str); } + // new_floor is 0-based. + function onFloorChange(new_floor) + { + let new_state = structuredClone(state); + new_state.floor = new_floor; + setState(new_state); + } + return h("div", {}, - h("h2", {}, `Floor ${state.floor}`), + h(FloorSelector, {floor: state.floor + 1, + on_floor_change: onFloorChange}), h(AssetsView, {on_drag_begin: onDragAssetBegin, on_drag_end: onDragAssetEnd}), - h(PlanView, {plan: state.plan, mouse_state: state.mouse_state}), - h("div", {}, - h("a", {"href": "javascript:void(0);", onclick: onClickSaveImg}, - "Open Image!")), + h("hr", {}), + h(PlanView, {plan: state.plan[state.floor], + mouse_state: state.mouse_state}), h("div", {}, h("textarea", {readonly: true}, state.plan_code)), + h("div", {"class": "ButtonRow"}, + h("button", {onclick: onClickSaveImg, type: "button"}, + "Open as Image!")), ); } @@ -198,7 +222,17 @@ else { decompressBytes(encoded_plan).then(a => { let state = structuredClone(DEFAULT_APP_STATE); - state.plan = Array.from(a.subarray(0, GRID_SIZE_X * GRID_SIZE_Y)); + let concated_plan = Array.from(a); + console.debug(concated_plan); + state.plan = []; + for(let i = 0; i < GRID_SIZE_X * GRID_SIZE_Y * FLOOR_COUNT; + i += GRID_SIZE_X * GRID_SIZE_Y) + { + let floor_slice = + concated_plan.slice(i, i + GRID_SIZE_X * GRID_SIZE_Y); + console.debug(floor_slice); + state.plan.push(floor_slice); + } state.plan_code = encoded_plan; render(state); }); diff --git a/styles.css b/styles.css index 73e6899..69ab118 100644 --- a/styles.css +++ b/styles.css @@ -1,5 +1,64 @@ -body +/* http://meyerweb.com/eric/tools/css/reset/ + v2.0 | 20110126 + License: none (public domain) +*/ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: unset; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} + +/* ========== Actual styles ======================================> */ + +:root { + --color-bg: #c0c0c0; + --color-fg: black; + --color-border-dark-1: #808080; + --color-border-dark-2: black; + --color-border-light-1: #dfdfdf; + --color-border-light-2: white; + --color-titlebar-1: navy; + --color-titlebar-2: #1084d0; } * @@ -7,6 +66,188 @@ body box-sizing: border-box; } +html +{ + background: #008080; + color: var(--color-fg); + font-family: sans-serif; + font-size: 12px; + padding: 12px; +} + +.Window +{ + box-shadow: inset -1px -1px var(--color-border-dark-2), + inset 1px 1px var(--color-border-light-1), + inset -2px -2px var(--color-border-dark-1), + inset 2px 2px var(--color-border-light-2); + background: var(--color-bg); + padding: 10px; + + .Titlebar + { + background: linear-gradient(90deg, var(--color-titlebar-1), var(--color-titlebar-2)); + margin: -6px -6px 10px -6px; + color: white; + padding: 3px; + } +} + +.Dialog +{ + max-width: 800px; + margin: 0px auto; +} + +input[type="number"] +{ + appearance: none; + border: none; + outline: none; + box-shadow: inset -1px -1px var(--color-border-light-2), + inset 1px 1px var(--color-border-dark-1), + inset -2px -2px var(--color-border-light-1), + inset 2px 2px var(--color-border-dark-2); + padding: 4px 0px 4px 6px; + font-family: monospace; + font-size: 0.9rem; +} + +input[type="text"], input[type="url"], input[type="email"], textarea +{ + appearance: none; + border: none; + outline: none; + box-shadow: inset -1px -1px var(--color-border-light-2), + inset 1px 1px var(--color-border-dark-1), + inset -2px -2px var(--color-border-light-1), + inset 2px 2px var(--color-border-dark-2); + width: 100%; + padding: 4px 6px; + font-family: monospace; + font-size: 0.9rem; +} + +a.Button, a.FloatButton +{ + color: black; + text-decoration: none; +} + +button, input[type="submit"] +{ + appearance: none; + border: none; + outline: none; + box-shadow: inset -1px -1px var(--color-border-dark-2), + inset 1px 1px var(--color-border-light-1), + inset -2px -2px var(--color-border-dark-1), + inset 2px 2px var(--color-border-light-2); + background: var(--color-bg); + min-height: 23px; + min-width: 75px; + padding: 0 12px; + text-align: center; +} + +.IconButton +{ + padding: 4px; + min-width: unset; + min-height: unset; +} + +.FloatButton +{ + padding: 4px; + min-width: unset; + min-height: unset; + box-shadow: none; +} + +.FloatButton:hover +{ + padding: 4px; + min-width: unset; + min-height: unset; + box-shadow: inset 1px 1px var(--color-border-light-1), + inset -1px -1px var(--color-border-dark-1); +} + +button:active, .FloatButton:active +{ + box-shadow: inset -1px -1px var(--color-border-light-2), + inset 1px 1px var(--color-border-dark-1), + inset -2px -2px var(--color-border-light-1), + inset 2px 2px var(--color-border-dark-2); +} + +hr +{ + border: none; + border-top: solid var(--color-border-dark-1) 1px; + border-bottom: solid var(--color-border-light-1) 1px; +} + +.ButtonRow +{ + display: flex; + justify-content: right; + gap: 10px; + margin: 10px 0; + align-items: baseline; +} + +.ButtonRowLeft +{ + text-align: Left; + margin-top: 10px; +} + +.Toolbar +{ + display: flex; + border-bottom: solid var(--color-border-dark-1) 1px; + box-shadow: 0px 1px var(--color-border-light-1); + position: relative; + left: -10px; + top: -10px; + width: calc(100% + 2ex); + padding: 2px 8px; +} + +table.InputFields +{ + border-collapse: collapse; + width: 100%; + table-layout: auto; + + td + { + padding: 2px 2px; + } + + td:nth-child(2) + { + width: 100%; + } + +} + +.StatusBar +{ + margin: 10px -6px -7px -6px; + display: flex; +} + +.StatusCell +{ + box-shadow: inset -2px -2px var(--color-border-light-2), + inset 2px 2px var(--color-border-dark-1); + padding: 4px; + flex-grow: 1; +} + .Asset { display: inline-block; @@ -15,8 +256,74 @@ body height: 34px; } + + /* Prevent SVG elements from interferring with drag n drop. */ #Grid * { pointer-events: none; } + +nav +{ + display: flex; + justify-content: space-between; +} + +#Links +{ + box-shadow: inset -1px -1px var(--color-border-light-2), + inset 1px 1px var(--color-border-dark-1), + inset -2px -2px var(--color-border-light-1), + inset 2px 2px var(--color-border-dark-2); + padding: 2px; +} + +table.TableView +{ + background-color: white; + border-collapse: collapse; + width: 100%; + + thead th + { + box-shadow: inset -1px -1px var(--color-border-dark-2), + inset 1px 1px var(--color-border-light-1), + inset -2px -2px var(--color-border-dark-1), + inset 2px 2px var(--color-border-light-2); + background-color: var(--color-bg); + padding: 2px 4px 4px 4px; + text-align: left; + } + + td + { + padding: 2px; + } +} + +#Links table +{ + table-layout: fixed; + + th:nth-child(1) + { + width: 10em; + } + + td:nth-child(2) + { + overflow: hidden; + white-space: nowrap; + } + + th:nth-child(3), th:nth-child(4) + { + width: 55px; + } + + td:nth-child(3), td:nth-child(4) + { + text-align: center; + } +} -- cgit v1.2.3-70-g09d2