aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMetroWind <chris.corsair@gmail.com>2025-08-31 09:10:28 -0700
committerMetroWind <chris.corsair@gmail.com>2025-08-31 09:10:28 -0700
commit3204043e5cd855855b045d8f410b320dcb772d25 (patch)
tree5588ae66f826ecd1cd431d14b71bb7aae49cf33b
parent6a6cc2f22e806be895f9fddc9ed6cabab395b6cb (diff)
Support multiple floors. Use 98 style.
-rw-r--r--index.html5
-rw-r--r--scripts/main.js56
-rw-r--r--styles.css309
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 @@
16 <title>My test page</title> 16 <title>My test page</title>
17 </head> 17 </head>
18 <body> 18 <body>
19 <div id="Plan"> 19 <div class="Window Dialog">
20 <div class="Titlebar">NMS Freighter Planner</div>
21 <div id="Plan">
22 </div>
20 </div> 23 </div>
21 </body> 24 </body>
22</html> 25</html>
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 @@
1const DEFAULT_APP_STATE = { 1const DEFAULT_APP_STATE = {
2 floor: 1, 2 floor: 0, // This is 0-based.
3 plan: new Array(GRID_SIZE_X * GRID_SIZE_Y).fill(0), 3 plan: new Array(FLOOR_COUNT).fill(
4 new Array(GRID_SIZE_X * GRID_SIZE_Y).fill(0)),
4 // mouse_state can be “normal”, or “dnd”. 5 // mouse_state can be “normal”, or “dnd”.
5 mouse_state: "normal", 6 mouse_state: "normal",
6 plan_code: "", 7 plan_code: "",
@@ -9,7 +10,9 @@ const DEFAULT_APP_STATE = {
9async function serializePlan(app_state) 10async function serializePlan(app_state)
10{ 11{
11 let rest = new Array(GRID_SIZE_X * GRID_SIZE_Y * (FLOOR_COUNT - 1)).fill(0); 12 let rest = new Array(GRID_SIZE_X * GRID_SIZE_Y * (FLOOR_COUNT - 1)).fill(0);
12 return compressBytes(Uint8Array.from(app_state.plan.concat(rest))); 13 let concated_plans =
14 app_state.plan.reduce((big, floor_plan) => big.concat(floor_plan), []);
15 return compressBytes(Uint8Array.from(concated_plans));
13} 16}
14 17
15function genGrid(x_count, y_count, cell_size) 18function genGrid(x_count, y_count, cell_size)
@@ -105,11 +108,21 @@ ${cell_y * (CELL_SIZE + 1) + 1})`}, ASSET_WHITE_BG, TILES[idx].inner_svg);
105 108
106function PlanView({plan, mouse_state}) 109function PlanView({plan, mouse_state})
107{ 110{
111 console.debug("Looking at plan ", plan);
108 return h("div", {}, 112 return h("div", {},
109 h("p", {}, "The plan:"),
110 h(PlanGridView, {content: plan, mouse_state: mouse_state})); 113 h(PlanGridView, {content: plan, mouse_state: mouse_state}));
111} 114}
112 115
116// on_floor_change takes one argument, which is the 0-based floor number.
117function FloorSelector({floor, on_floor_change})
118{
119 return h("div", {"id": "FloorSelectorWrapper", "class": "ButtonRow"},
120 h("label", {}, "Floor"),
121 h("input", {"id": "InputFloor", "type": "number", "step": 1, "min": 1,
122 "max": 15, "value": floor,
123 onchange: e => on_floor_change(e.target.value - 1),},));
124}
125
113function App({initial_state}) 126function App({initial_state})
114{ 127{
115 const [state, setState] = preactHooks.useState(initial_state); 128 const [state, setState] = preactHooks.useState(initial_state);
@@ -134,7 +147,7 @@ function App({initial_state})
134 let asset_index = parseInt(e.target.getAttribute("data-asset-index")); 147 let asset_index = parseInt(e.target.getAttribute("data-asset-index"));
135 148
136 let new_state = structuredClone(state); 149 let new_state = structuredClone(state);
137 new_state.plan[cell_index] = asset_index; 150 new_state.plan[new_state.floor][cell_index] = asset_index;
138 new_state.mouse_state = "normal"; 151 new_state.mouse_state = "normal";
139 serializePlan(new_state).then((s) => { 152 serializePlan(new_state).then((s) => {
140 new_state.plan_code = s; 153 new_state.plan_code = s;
@@ -168,16 +181,27 @@ function App({initial_state})
168 encodeURIComponent(svg_str); 181 encodeURIComponent(svg_str);
169 } 182 }
170 183
184 // new_floor is 0-based.
185 function onFloorChange(new_floor)
186 {
187 let new_state = structuredClone(state);
188 new_state.floor = new_floor;
189 setState(new_state);
190 }
191
171 return h("div", {}, 192 return h("div", {},
172 h("h2", {}, `Floor ${state.floor}`), 193 h(FloorSelector, {floor: state.floor + 1,
194 on_floor_change: onFloorChange}),
173 h(AssetsView, {on_drag_begin: onDragAssetBegin, 195 h(AssetsView, {on_drag_begin: onDragAssetBegin,
174 on_drag_end: onDragAssetEnd}), 196 on_drag_end: onDragAssetEnd}),
175 h(PlanView, {plan: state.plan, mouse_state: state.mouse_state}), 197 h("hr", {}),
176 h("div", {}, 198 h(PlanView, {plan: state.plan[state.floor],
177 h("a", {"href": "javascript:void(0);", onclick: onClickSaveImg}, 199 mouse_state: state.mouse_state}),
178 "Open Image!")),
179 h("div", {}, 200 h("div", {},
180 h("textarea", {readonly: true}, state.plan_code)), 201 h("textarea", {readonly: true}, state.plan_code)),
202 h("div", {"class": "ButtonRow"},
203 h("button", {onclick: onClickSaveImg, type: "button"},
204 "Open as Image!")),
181 ); 205 );
182} 206}
183 207
@@ -198,7 +222,17 @@ else
198{ 222{
199 decompressBytes(encoded_plan).then(a => { 223 decompressBytes(encoded_plan).then(a => {
200 let state = structuredClone(DEFAULT_APP_STATE); 224 let state = structuredClone(DEFAULT_APP_STATE);
201 state.plan = Array.from(a.subarray(0, GRID_SIZE_X * GRID_SIZE_Y)); 225 let concated_plan = Array.from(a);
226 console.debug(concated_plan);
227 state.plan = [];
228 for(let i = 0; i < GRID_SIZE_X * GRID_SIZE_Y * FLOOR_COUNT;
229 i += GRID_SIZE_X * GRID_SIZE_Y)
230 {
231 let floor_slice =
232 concated_plan.slice(i, i + GRID_SIZE_X * GRID_SIZE_Y);
233 console.debug(floor_slice);
234 state.plan.push(floor_slice);
235 }
202 state.plan_code = encoded_plan; 236 state.plan_code = encoded_plan;
203 render(state); 237 render(state);
204 }); 238 });
diff --git a/styles.css b/styles.css
index 73e6899..69ab118 100644
--- a/styles.css
+++ b/styles.css
@@ -1,5 +1,64 @@
1body 1/* http://meyerweb.com/eric/tools/css/reset/
2 v2.0 | 20110126
3 License: none (public domain)
4*/
5
6html, body, div, span, applet, object, iframe,
7h1, h2, h3, h4, h5, h6, p, blockquote, pre,
8a, abbr, acronym, address, big, cite, code,
9del, dfn, em, img, ins, kbd, q, s, samp,
10small, strike, strong, sub, sup, tt, var,
11b, u, i, center,
12dl, dt, dd, ol, ul, li,
13fieldset, form, label, legend,
14table, caption, tbody, tfoot, thead, tr, th, td,
15article, aside, canvas, details, embed,
16figure, figcaption, footer, header, hgroup,
17menu, nav, output, ruby, section, summary,
18time, mark, audio, video {
19 margin: 0;
20 padding: 0;
21 border: 0;
22 font-size: unset;
23 font: inherit;
24 vertical-align: baseline;
25}
26/* HTML5 display-role reset for older browsers */
27article, aside, details, figcaption, figure,
28footer, header, hgroup, menu, nav, section {
29 display: block;
30}
31body {
32 line-height: 1;
33}
34ol, ul {
35 list-style: none;
36}
37blockquote, q {
38 quotes: none;
39}
40blockquote:before, blockquote:after,
41q:before, q:after {
42 content: '';
43 content: none;
44}
45table {
46 border-collapse: collapse;
47 border-spacing: 0;
48}
49
50/* ========== Actual styles ======================================> */
51
52:root
2{ 53{
54 --color-bg: #c0c0c0;
55 --color-fg: black;
56 --color-border-dark-1: #808080;
57 --color-border-dark-2: black;
58 --color-border-light-1: #dfdfdf;
59 --color-border-light-2: white;
60 --color-titlebar-1: navy;
61 --color-titlebar-2: #1084d0;
3} 62}
4 63
5* 64*
@@ -7,6 +66,188 @@ body
7 box-sizing: border-box; 66 box-sizing: border-box;
8} 67}
9 68
69html
70{
71 background: #008080;
72 color: var(--color-fg);
73 font-family: sans-serif;
74 font-size: 12px;
75 padding: 12px;
76}
77
78.Window
79{
80 box-shadow: inset -1px -1px var(--color-border-dark-2),
81 inset 1px 1px var(--color-border-light-1),
82 inset -2px -2px var(--color-border-dark-1),
83 inset 2px 2px var(--color-border-light-2);
84 background: var(--color-bg);
85 padding: 10px;
86
87 .Titlebar
88 {
89 background: linear-gradient(90deg, var(--color-titlebar-1), var(--color-titlebar-2));
90 margin: -6px -6px 10px -6px;
91 color: white;
92 padding: 3px;
93 }
94}
95
96.Dialog
97{
98 max-width: 800px;
99 margin: 0px auto;
100}
101
102input[type="number"]
103{
104 appearance: none;
105 border: none;
106 outline: none;
107 box-shadow: inset -1px -1px var(--color-border-light-2),
108 inset 1px 1px var(--color-border-dark-1),
109 inset -2px -2px var(--color-border-light-1),
110 inset 2px 2px var(--color-border-dark-2);
111 padding: 4px 0px 4px 6px;
112 font-family: monospace;
113 font-size: 0.9rem;
114}
115
116input[type="text"], input[type="url"], input[type="email"], textarea
117{
118 appearance: none;
119 border: none;
120 outline: none;
121 box-shadow: inset -1px -1px var(--color-border-light-2),
122 inset 1px 1px var(--color-border-dark-1),
123 inset -2px -2px var(--color-border-light-1),
124 inset 2px 2px var(--color-border-dark-2);
125 width: 100%;
126 padding: 4px 6px;
127 font-family: monospace;
128 font-size: 0.9rem;
129}
130
131a.Button, a.FloatButton
132{
133 color: black;
134 text-decoration: none;
135}
136
137button, input[type="submit"]
138{
139 appearance: none;
140 border: none;
141 outline: none;
142 box-shadow: inset -1px -1px var(--color-border-dark-2),
143 inset 1px 1px var(--color-border-light-1),
144 inset -2px -2px var(--color-border-dark-1),
145 inset 2px 2px var(--color-border-light-2);
146 background: var(--color-bg);
147 min-height: 23px;
148 min-width: 75px;
149 padding: 0 12px;
150 text-align: center;
151}
152
153.IconButton
154{
155 padding: 4px;
156 min-width: unset;
157 min-height: unset;
158}
159
160.FloatButton
161{
162 padding: 4px;
163 min-width: unset;
164 min-height: unset;
165 box-shadow: none;
166}
167
168.FloatButton:hover
169{
170 padding: 4px;
171 min-width: unset;
172 min-height: unset;
173 box-shadow: inset 1px 1px var(--color-border-light-1),
174 inset -1px -1px var(--color-border-dark-1);
175}
176
177button:active, .FloatButton:active
178{
179 box-shadow: inset -1px -1px var(--color-border-light-2),
180 inset 1px 1px var(--color-border-dark-1),
181 inset -2px -2px var(--color-border-light-1),
182 inset 2px 2px var(--color-border-dark-2);
183}
184
185hr
186{
187 border: none;
188 border-top: solid var(--color-border-dark-1) 1px;
189 border-bottom: solid var(--color-border-light-1) 1px;
190}
191
192.ButtonRow
193{
194 display: flex;
195 justify-content: right;
196 gap: 10px;
197 margin: 10px 0;
198 align-items: baseline;
199}
200
201.ButtonRowLeft
202{
203 text-align: Left;
204 margin-top: 10px;
205}
206
207.Toolbar
208{
209 display: flex;
210 border-bottom: solid var(--color-border-dark-1) 1px;
211 box-shadow: 0px 1px var(--color-border-light-1);
212 position: relative;
213 left: -10px;
214 top: -10px;
215 width: calc(100% + 2ex);
216 padding: 2px 8px;
217}
218
219table.InputFields
220{
221 border-collapse: collapse;
222 width: 100%;
223 table-layout: auto;
224
225 td
226 {
227 padding: 2px 2px;
228 }
229
230 td:nth-child(2)
231 {
232 width: 100%;
233 }
234
235}
236
237.StatusBar
238{
239 margin: 10px -6px -7px -6px;
240 display: flex;
241}
242
243.StatusCell
244{
245 box-shadow: inset -2px -2px var(--color-border-light-2),
246 inset 2px 2px var(--color-border-dark-1);
247 padding: 4px;
248 flex-grow: 1;
249}
250
10.Asset 251.Asset
11{ 252{
12 display: inline-block; 253 display: inline-block;
@@ -15,8 +256,74 @@ body
15 height: 34px; 256 height: 34px;
16} 257}
17 258
259
260
18/* Prevent SVG elements from interferring with drag n drop. */ 261/* Prevent SVG elements from interferring with drag n drop. */
19#Grid * 262#Grid *
20{ 263{
21 pointer-events: none; 264 pointer-events: none;
22} 265}
266
267nav
268{
269 display: flex;
270 justify-content: space-between;
271}
272
273#Links
274{
275 box-shadow: inset -1px -1px var(--color-border-light-2),
276 inset 1px 1px var(--color-border-dark-1),
277 inset -2px -2px var(--color-border-light-1),
278 inset 2px 2px var(--color-border-dark-2);
279 padding: 2px;
280}
281
282table.TableView
283{
284 background-color: white;
285 border-collapse: collapse;
286 width: 100%;
287
288 thead th
289 {
290 box-shadow: inset -1px -1px var(--color-border-dark-2),
291 inset 1px 1px var(--color-border-light-1),
292 inset -2px -2px var(--color-border-dark-1),
293 inset 2px 2px var(--color-border-light-2);
294 background-color: var(--color-bg);
295 padding: 2px 4px 4px 4px;
296 text-align: left;
297 }
298
299 td
300 {
301 padding: 2px;
302 }
303}
304
305#Links table
306{
307 table-layout: fixed;
308
309 th:nth-child(1)
310 {
311 width: 10em;
312 }
313
314 td:nth-child(2)
315 {
316 overflow: hidden;
317 white-space: nowrap;
318 }
319
320 th:nth-child(3), th:nth-child(4)
321 {
322 width: 55px;
323 }
324
325 td:nth-child(3), td:nth-child(4)
326 {
327 text-align: center;
328 }
329}