stuff & things, add more UI
This commit is contained in:
parent
bb0991ef21
commit
d652568628
12 changed files with 328 additions and 27 deletions
Binary file not shown.
Before Width: | Height: | Size: 208 B |
Binary file not shown.
Before Width: | Height: | Size: 248 B |
|
@ -7,7 +7,8 @@ export class FloorManager {
|
|||
|
||||
floors = new Map([]);
|
||||
rooms = new Map([]);
|
||||
currentFloor = "";
|
||||
#currentFloor = "";
|
||||
#currentRoom = "";
|
||||
|
||||
constructor(map, ui, gameobj, events, lang) {
|
||||
this.map = map;
|
||||
|
@ -20,6 +21,38 @@ export class FloorManager {
|
|||
this.lang = lang;
|
||||
}
|
||||
|
||||
set currentFloor(floor) {
|
||||
this.#currentFloor = floor;
|
||||
}
|
||||
|
||||
get currentFloor() {
|
||||
return this.#currentFloor;
|
||||
}
|
||||
|
||||
get currentFloorItem() {
|
||||
return this.floors.get(this.#currentFloor);
|
||||
}
|
||||
|
||||
setCurrentRoom(room, maximize = true) {
|
||||
this.#currentRoom = room;
|
||||
this.ui.__updateEvents();
|
||||
if (maximize) this.ui.eventsMaximize();
|
||||
if (room == "") this.gameobj.zoomToFloor();
|
||||
else this.gameobj.zoomToRoom(room);
|
||||
}
|
||||
|
||||
get currentRoom() {
|
||||
return this.#currentRoom;
|
||||
}
|
||||
|
||||
get currentRoomItem() {
|
||||
return this.currentFloorRooms.get(this.#currentRoom);
|
||||
}
|
||||
|
||||
get currentFloorRooms() {
|
||||
return this.rooms.get(this.#currentFloor);
|
||||
}
|
||||
|
||||
async getFloors() {
|
||||
let allFloorsReqSend = fetch(`/data/${this.lang}/map`);
|
||||
|
||||
|
@ -51,7 +84,10 @@ export class FloorManager {
|
|||
let allRooms;
|
||||
if (allRoomsReq.ok) allRooms = await allRoomsReq.json();
|
||||
|
||||
this.rooms.set(id, allRooms);
|
||||
const roomMap = new Map([]);
|
||||
allRooms.forEach((room) => roomMap.set(room.id, room));
|
||||
|
||||
this.rooms.set(id, roomMap);
|
||||
|
||||
this.ui.setLoading(false);
|
||||
|
||||
|
@ -60,7 +96,7 @@ export class FloorManager {
|
|||
|
||||
async changeFloor(id) {
|
||||
const floor = this.floors.get(id);
|
||||
if (floor != null) this.currentFloor = id;
|
||||
if (floor != null) this.#currentFloor = id;
|
||||
|
||||
if (this.ui.floorsEmpty) this.ui.__updateFloorsHard();
|
||||
else this.ui.__updateFloorsSoft();
|
||||
|
@ -77,6 +113,8 @@ export class FloorManager {
|
|||
|
||||
this.ui.__updateEvents();
|
||||
|
||||
this.setCurrentRoom("", false);
|
||||
|
||||
this.ui.setLoading(false);
|
||||
|
||||
return floor;
|
||||
|
|
|
@ -4,7 +4,6 @@ export class GameObjManager {
|
|||
|
||||
floorObject;
|
||||
roomObjects = new Map([]);
|
||||
currentRoom;
|
||||
|
||||
constructor(map) {
|
||||
this.map = map;
|
||||
|
@ -55,6 +54,21 @@ export class GameObjManager {
|
|||
this.map.zoomToAbs(this.map.opts.minZoomLevel, bounds.center());
|
||||
}
|
||||
|
||||
zoomToFloor() {
|
||||
const objectBounds = this.floorObject.renderArea().bbox();
|
||||
|
||||
this.map.zoomToAbs(this.map.opts.minZoomLevel, objectBounds.center());
|
||||
}
|
||||
|
||||
zoomToRoom(id) {
|
||||
const selectedObject = this.roomObjects.get(id);
|
||||
if (selectedObject == null) return;
|
||||
|
||||
const objectBounds = selectedObject.renderArea().bbox();
|
||||
|
||||
this.map.zoomToAbs(this.map.opts.minZoomLevel + 1, objectBounds.center());
|
||||
}
|
||||
|
||||
generateRooms() {
|
||||
if (this.roomObjects.size > 0) {
|
||||
this.roomObjects.forEach((x) => x.destroy());
|
||||
|
@ -78,7 +92,7 @@ export class GameObjManager {
|
|||
this.map.kp.pos(),
|
||||
this.map.kp.z(6),
|
||||
{
|
||||
clickForgiveness: 1,
|
||||
clickForgiveness: 5,
|
||||
startClickPosition: null,
|
||||
},
|
||||
]);
|
||||
|
@ -108,7 +122,7 @@ export class GameObjManager {
|
|||
obj.startClickPosition &&
|
||||
obj.startClickPosition.dist(endClickPosition) < obj.clickForgiveness
|
||||
) {
|
||||
this.currentRoom = room.id;
|
||||
this.floormanager.setCurrentRoom(room.id);
|
||||
}
|
||||
obj.color = this.map.kp.Color.fromHex("505050");
|
||||
this.map.clearMouseMode();
|
||||
|
|
|
@ -4,7 +4,7 @@ export class KaplayMap {
|
|||
minZoomLevel: 2,
|
||||
maxZoomLevel: 5,
|
||||
dblClickDuration: 0.2, // s
|
||||
dblClickForgiveness: 30, // px
|
||||
dblClickForgiveness: 100, // px
|
||||
};
|
||||
|
||||
uiLayer;
|
||||
|
|
20
assets/scripts/KaplayMap/localization.js
Normal file
20
assets/scripts/KaplayMap/localization.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
export default new Map([
|
||||
[
|
||||
"en_US",
|
||||
{
|
||||
date_starting: "Starting",
|
||||
date_started: "Started",
|
||||
floors_header: "Floors",
|
||||
date_summary: (prefix, time) => `${prefix} ${time}`,
|
||||
parenthesis: (content) => `(${content})`,
|
||||
hours_long: (hours) =>
|
||||
hours == 1 ? `${hours} hour long` : `${hours} hours long`,
|
||||
minutes_long: (hours) =>
|
||||
hours == 1 ? `${hours} minute long` : `${hours} minutes long`,
|
||||
events_in: (name) => `Events (${name})`,
|
||||
view_events_in: (name) => `Return to events in ${name}`,
|
||||
minimize: "Hide events panel",
|
||||
maximize: "Show events panel",
|
||||
},
|
||||
],
|
||||
]);
|
|
@ -1,3 +1,9 @@
|
|||
import {
|
||||
getMinutesDifference,
|
||||
getRelativeTime,
|
||||
} from "../modules/relative_time.js";
|
||||
import localization from "./localization.js";
|
||||
|
||||
export class UIManager {
|
||||
ui;
|
||||
map;
|
||||
|
@ -21,6 +27,9 @@ export class UIManager {
|
|||
|
||||
// Floors
|
||||
this.uiElements.floors = this.ui.querySelector("#floors");
|
||||
this.uiElements.floorsHeading = this.ui.querySelector(
|
||||
"#floors .widget-heading"
|
||||
);
|
||||
this.uiElements.floorButtons =
|
||||
this.uiElements.floors.querySelector("#floor-buttons");
|
||||
|
||||
|
@ -29,10 +38,26 @@ export class UIManager {
|
|||
this.ui.querySelector("#events-container");
|
||||
this.uiElements.events =
|
||||
this.uiElements.eventsContainer.querySelector("#events");
|
||||
this.uiElements.eventsBackButton = this.uiElements.events.querySelector(
|
||||
"#events-header #back"
|
||||
);
|
||||
this.uiElements.eventsRoomName = this.uiElements.events.querySelector(
|
||||
"#events-header #room-name"
|
||||
);
|
||||
this.uiElements.eventsRoomDescription =
|
||||
this.uiElements.events.querySelector("#events-header #room-description");
|
||||
this.uiElements.eventList =
|
||||
this.uiElements.events.querySelector("#event-list");
|
||||
this.uiElements.eventsMinimized =
|
||||
this.uiElements.eventsContainer.querySelector("#events-minimized");
|
||||
this.uiElements.eventsMinimizedBackButton =
|
||||
this.uiElements.eventsMinimized.querySelector("#back");
|
||||
|
||||
this.uiElements.minimizeButton = this.uiElements.events.querySelector(
|
||||
"#footer #footer-buttons #minimize"
|
||||
);
|
||||
this.uiElements.maximizeButton =
|
||||
this.uiElements.eventsMinimized.querySelector("#maximize");
|
||||
|
||||
// Loading
|
||||
this.uiElements.loading = this.ui.querySelector("#loading");
|
||||
|
@ -64,33 +89,163 @@ export class UIManager {
|
|||
});
|
||||
}
|
||||
|
||||
toggleEventsMinimized() {
|
||||
this.uiElements.eventsContainer.classList.toggle("minimized");
|
||||
}
|
||||
|
||||
eventsMinimize() {
|
||||
this.uiElements.eventsContainer.classList.add("minimized");
|
||||
}
|
||||
|
||||
eventsMaximize() {
|
||||
this.uiElements.eventsContainer.classList.remove("minimized");
|
||||
}
|
||||
|
||||
__updateAll() {
|
||||
this.__updateEvents();
|
||||
this.__updateFloorsHard();
|
||||
}
|
||||
|
||||
__initEvents() {
|
||||
const minimizeButton = this.uiElements.events.querySelector(
|
||||
"#footer #footer-buttons #minimize"
|
||||
);
|
||||
minimizeButton.addEventListener("click", () => {
|
||||
this.uiElements.eventsBackButton.addEventListener("click", () => {
|
||||
this.floormanager.setCurrentRoom("");
|
||||
});
|
||||
this.uiElements.eventsMinimizedBackButton.addEventListener("click", () => {
|
||||
this.floormanager.setCurrentRoom("", false);
|
||||
});
|
||||
|
||||
this.uiElements.minimizeButton.addEventListener("click", () => {
|
||||
this.uiElements.eventsContainer.classList.add("minimized");
|
||||
});
|
||||
const maximizeButton =
|
||||
this.uiElements.eventsMinimized.querySelector("#maximize");
|
||||
maximizeButton.addEventListener("click", () => {
|
||||
this.uiElements.maximizeButton.addEventListener("click", () => {
|
||||
this.uiElements.eventsContainer.classList.remove("minimized");
|
||||
});
|
||||
}
|
||||
|
||||
__updateEvents() {
|
||||
const currentLocalization = localization.get(this.floormanager.lang);
|
||||
|
||||
this.uiElements.minimizeButton.title = currentLocalization.minimize;
|
||||
this.uiElements.maximizeButton.title = currentLocalization.maximize;
|
||||
// Figure out header
|
||||
|
||||
if (this.floormanager.currentRoomItem != null) {
|
||||
this.uiElements.eventsRoomName.textContent =
|
||||
currentLocalization.events_in(this.floormanager.currentRoomItem.name);
|
||||
this.uiElements.eventsRoomDescription.textContent =
|
||||
this.floormanager.currentRoomItem.description;
|
||||
this.uiElements.eventsBackButton.disabled = false;
|
||||
this.uiElements.eventsBackButton.title =
|
||||
currentLocalization.view_events_in(
|
||||
this.floormanager.currentFloorItem.name
|
||||
);
|
||||
this.uiElements.eventsMinimizedBackButton.disabled = false;
|
||||
this.uiElements.eventsMinimizedBackButton.title =
|
||||
currentLocalization.view_events_in(
|
||||
this.floormanager.currentFloorItem.name
|
||||
);
|
||||
} else {
|
||||
this.uiElements.eventsRoomName.textContent =
|
||||
currentLocalization.events_in(this.floormanager.currentFloorItem.name);
|
||||
this.uiElements.eventsRoomDescription.textContent =
|
||||
this.floormanager.currentFloorItem.description;
|
||||
this.uiElements.eventsBackButton.disabled = true;
|
||||
this.uiElements.eventsBackButton.title = "";
|
||||
this.uiElements.eventsMinimizedBackButton.disabled = true;
|
||||
this.uiElements.eventsMinimizedBackButton.title = "";
|
||||
}
|
||||
|
||||
// Remove all children
|
||||
this.uiElements.eventList.replaceChildren();
|
||||
|
||||
console.log(this.eventmanager.events);
|
||||
// Put them back
|
||||
|
||||
const currentDate = new Date();
|
||||
this.eventmanager.events.forEach((event, id) => {
|
||||
if (
|
||||
this.floormanager.currentRoomItem &&
|
||||
event.where != this.floormanager.currentRoom
|
||||
)
|
||||
return; // Don't display this event
|
||||
|
||||
const eventFloor = this.floormanager.currentFloorRooms.get(event.where);
|
||||
const eventDateStart = new Date(event.when.start);
|
||||
const eventDateEnd = new Date(event.when.end);
|
||||
|
||||
const eventStarted = currentDate > eventDateStart;
|
||||
const eventEnded = currentDate > eventDateEnd;
|
||||
|
||||
let datePrefix;
|
||||
|
||||
if (eventEnded) {
|
||||
return; // Don't display this event
|
||||
} else if (eventStarted) {
|
||||
datePrefix = currentLocalization.date_started;
|
||||
} else {
|
||||
datePrefix = currentLocalization.date_starting;
|
||||
}
|
||||
|
||||
const eventListContainer = document.createElement("details");
|
||||
eventListContainer.classList.add("event-list-container");
|
||||
eventListContainer.id = "event-" + id;
|
||||
|
||||
const eventListContainerSummary = document.createElement("summary");
|
||||
eventListContainerSummary.textContent = event.name;
|
||||
|
||||
const eventListContainerSummaryName = document.createElement("b");
|
||||
eventListContainerSummaryName.textContent = event.name;
|
||||
|
||||
eventListContainerSummary.appendChild(eventListContainerSummaryName);
|
||||
|
||||
eventListContainerSummary.appendChild(document.createTextNode(" "));
|
||||
|
||||
if (this.floormanager.currentRoomItem == null) {
|
||||
const eventListContainerSummaryRoom = document.createElement("button");
|
||||
eventListContainerSummaryRoom.classList.add("link");
|
||||
eventListContainerSummaryRoom.addEventListener("click", () => {
|
||||
this.floormanager.setCurrentRoom(eventFloor.id);
|
||||
});
|
||||
eventListContainerSummaryRoom.textContent =
|
||||
currentLocalization.parenthesis(eventFloor.name);
|
||||
|
||||
eventListContainerSummary.appendChild(eventListContainerSummaryRoom);
|
||||
}
|
||||
|
||||
eventListContainerSummary.appendChild(document.createElement("br"));
|
||||
|
||||
const eventListContainerSummarySmall = document.createElement("small");
|
||||
|
||||
const eventListContainerSummaryTime = document.createElement("span");
|
||||
eventListContainerSummaryTime.classList.add("clarification");
|
||||
eventListContainerSummaryTime.title = eventDateStart.toLocaleString(
|
||||
this.floormanager.lang.replace(/_/g, "-")
|
||||
);
|
||||
eventListContainerSummaryTime.textContent =
|
||||
currentLocalization.date_summary(
|
||||
datePrefix,
|
||||
getRelativeTime(
|
||||
eventDateStart,
|
||||
currentDate,
|
||||
this.floormanager.lang.replace(/_/g, "-")
|
||||
)
|
||||
);
|
||||
|
||||
eventListContainerSummarySmall.appendChild(eventListContainerSummaryTime);
|
||||
|
||||
eventListContainerSummarySmall.appendChild(document.createTextNode(" "));
|
||||
|
||||
const eventListContainerSummaryLength = document.createElement("span");
|
||||
eventListContainerSummaryLength.textContent =
|
||||
currentLocalization.parenthesis(
|
||||
currentLocalization.minutes_long(
|
||||
getMinutesDifference(eventDateStart, eventDateEnd)
|
||||
)
|
||||
);
|
||||
|
||||
eventListContainerSummarySmall.appendChild(
|
||||
eventListContainerSummaryLength
|
||||
);
|
||||
|
||||
eventListContainerSummary.appendChild(eventListContainerSummarySmall);
|
||||
|
||||
eventListContainer.appendChild(eventListContainerSummary);
|
||||
|
||||
|
@ -120,8 +275,11 @@ export class UIManager {
|
|||
}
|
||||
|
||||
__updateFloorsHard() {
|
||||
const currentLocalization = localization.get(this.floormanager.lang);
|
||||
// Remove all children
|
||||
this.uiElements.floorButtons.replaceChildren();
|
||||
this.uiElements.floorsHeading.textContent =
|
||||
currentLocalization.floors_header;
|
||||
|
||||
// Put them back
|
||||
this.floormanager.floors.forEach(({ name }, id) => {
|
||||
|
@ -148,6 +306,11 @@ export class UIManager {
|
|||
}
|
||||
|
||||
__updateFloorsSoft() {
|
||||
const currentLocalization = localization.get(this.floormanager.lang);
|
||||
|
||||
this.uiElements.floorsHeading.textContent =
|
||||
currentLocalization.floors_header;
|
||||
|
||||
this.floormanager.floors.forEach(({ name }, id) => {
|
||||
const child = this.uiElements.floorButtons.querySelector("#floor-" + id);
|
||||
if (id === this.floormanager.currentFloor)
|
||||
|
|
26
assets/scripts/modules/relative_time.js
Normal file
26
assets/scripts/modules/relative_time.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
// in miliseconds
|
||||
export const units = {
|
||||
year: 24 * 60 * 60 * 1000 * 365,
|
||||
month: (24 * 60 * 60 * 1000 * 365) / 12,
|
||||
day: 24 * 60 * 60 * 1000,
|
||||
hour: 60 * 60 * 1000,
|
||||
minute: 60 * 1000,
|
||||
second: 1000,
|
||||
};
|
||||
|
||||
export function getRelativeTime(d1, d2, locale) {
|
||||
let rtf = new Intl.RelativeTimeFormat(locale, {
|
||||
numeric: "auto",
|
||||
});
|
||||
|
||||
let elapsed = d1 - d2;
|
||||
|
||||
// "Math.abs" accounts for both "past" & "future" scenarios
|
||||
for (var u in units)
|
||||
if (Math.abs(elapsed) > units[u] || u == "second")
|
||||
return rtf.format(Math.round(elapsed / units[u]), u);
|
||||
}
|
||||
|
||||
export function getMinutesDifference(d1, d2) {
|
||||
return (d2.getTime() - d1.getTime()) / units.minute;
|
||||
}
|
|
@ -28,7 +28,7 @@ p.widget-description {
|
|||
padding-bottom: 2px; */
|
||||
}
|
||||
|
||||
button {
|
||||
button:not(.link) {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: inherit;
|
||||
|
@ -39,16 +39,31 @@ button {
|
|||
border: 1px solid #0008;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
button:not(.link):hover {
|
||||
background-color: #0004;
|
||||
}
|
||||
|
||||
button:active {
|
||||
button:not(.link):active {
|
||||
background-color: #0008;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
button:not(.link):disabled {
|
||||
background-color: #8884;
|
||||
color: #808080;
|
||||
}
|
||||
|
||||
button.link {
|
||||
background: none !important;
|
||||
border: none;
|
||||
padding: 0;
|
||||
font: inherit;
|
||||
color: currentColor;
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.clarification {
|
||||
text-decoration: dotted underline;
|
||||
}
|
||||
|
||||
#map {
|
||||
|
@ -147,7 +162,7 @@ button:disabled {
|
|||
|
||||
#map-ui #events-container #events {
|
||||
left: 16px;
|
||||
width: 300px;
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
#map-ui #events-container.minimized #events {
|
||||
|
@ -168,8 +183,20 @@ button:disabled {
|
|||
gap: 8px;
|
||||
}
|
||||
|
||||
#map-ui #events-container #events #events-header p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#map-ui #events-container #events #events-header {
|
||||
display: grid;
|
||||
grid-template-columns: 36px auto;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#map-ui #events-container #events #event-list {
|
||||
min-height: 100px;
|
||||
min-height: 200px;
|
||||
max-height: calc(50% - 16px);
|
||||
margin: 8px 0;
|
||||
padding: 8px;
|
||||
|
@ -184,6 +211,7 @@ button:disabled {
|
|||
white-space: pre-line;
|
||||
}
|
||||
|
||||
#map-ui #events-container #events #events-header button,
|
||||
#map-ui #events-container #footer #footer-buttons button,
|
||||
#map-ui #events-container #events-minimized button {
|
||||
width: 36px;
|
||||
|
|
8
package-lock.json
generated
8
package-lock.json
generated
|
@ -9,7 +9,8 @@
|
|||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"express": "^4.19.2"
|
||||
"express": "^4.19.2",
|
||||
"https": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.1.1"
|
||||
|
@ -499,6 +500,11 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/https": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz",
|
||||
"integrity": "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg=="
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
"type": "module",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"express": "^4.19.2"
|
||||
"express": "^4.19.2",
|
||||
"https": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.1.1"
|
||||
|
|
|
@ -26,20 +26,25 @@
|
|||
</div>
|
||||
<div id="events-container">
|
||||
<div id="events">
|
||||
<div id="events-header">
|
||||
<button id="back" disabled>←</button>
|
||||
<div>
|
||||
<h1 class="widget-heading" id="room-name">Events</h1>
|
||||
<p class="widget-description" id="room-description">Lorem ipsum dolor sit amet.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="event-list">
|
||||
|
||||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-buttons">
|
||||
<button id="minimize">_</button>
|
||||
<button id="back" disabled>←</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="events-minimized">
|
||||
<button id="maximize">+</button>
|
||||
<button id="back" disabled>←</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="loading">
|
||||
|
|
Loading…
Reference in a new issue