mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
feat: added laser code change, target move, and delete
Note: deleted lasers are not removed from table and keep being drawn. Also added a cooler looking server page
This commit is contained in:
@@ -1,21 +0,0 @@
|
||||
import { DivIcon, LatLng } from "leaflet";
|
||||
import { CustomMarker } from "./custommarker";
|
||||
|
||||
export class DestinationPreviewHandle extends CustomMarker {
|
||||
constructor(latlng: LatLng) {
|
||||
super(latlng, { interactive: true, draggable: true });
|
||||
}
|
||||
|
||||
createIcon() {
|
||||
this.setIcon(
|
||||
new DivIcon({
|
||||
iconSize: [18, 18],
|
||||
iconAnchor: [9, 9],
|
||||
className: "leaflet-destination-preview-handle-marker",
|
||||
})
|
||||
);
|
||||
var el = document.createElement("div");
|
||||
el.classList.add("ol-destination-preview-handle-icon");
|
||||
this.getElement()?.appendChild(el);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Marker, LatLng, DivIcon, DomEvent, Map } from "leaflet";
|
||||
import { Marker, LatLng, DivIcon, Map } from "leaflet";
|
||||
|
||||
export class SpotEditMarker extends Marker {
|
||||
#textValue: string;
|
||||
@@ -6,14 +6,28 @@ export class SpotEditMarker extends Marker {
|
||||
#rotationAngle: number; // Rotation angle in radians
|
||||
#previousValue: string;
|
||||
|
||||
constructor(latlng: LatLng, textValue: string, rotationAngle: number = 0) {
|
||||
onValueUpdated: (value: number) => void = () => {};
|
||||
onDeleteButtonClicked: () => void = () => {};
|
||||
|
||||
/**
|
||||
* Constructor for SpotEditMarker
|
||||
* @param {LatLng} latlng - The geographical position of the marker.
|
||||
* @param {string} textValue - The initial text value to display.
|
||||
* @param {number} rotationAngle - The initial rotation angle in radians.
|
||||
*/
|
||||
constructor(latlng: LatLng, textValue: string, rotationAngle: number = 0, type: string) {
|
||||
super(latlng, {
|
||||
icon: new DivIcon({
|
||||
className: "leaflet-spot-input-marker",
|
||||
html: `<div class="container">
|
||||
html:
|
||||
type === "laser"
|
||||
? `<div class="container">
|
||||
<input class="input"/>
|
||||
<div class="text">${textValue}</div>
|
||||
<div class="delete">X</div>
|
||||
</div>`
|
||||
: `<div class="container">
|
||||
<div class="delete">X</div>
|
||||
</div>`,
|
||||
}),
|
||||
});
|
||||
@@ -23,21 +37,34 @@ export class SpotEditMarker extends Marker {
|
||||
this.#previousValue = textValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the marker is added to the map.
|
||||
* @param {Map} map - The map instance.
|
||||
* @returns {this} - The current instance of SpotEditMarker.
|
||||
*/
|
||||
onAdd(map: Map): this {
|
||||
super.onAdd(map);
|
||||
const element = this.getElement();
|
||||
if (element) {
|
||||
const text = element.querySelector(".text");
|
||||
const button = element.querySelector(".delete");
|
||||
const container = element.querySelector(".container") as HTMLDivElement;
|
||||
const button = element.querySelector(".delete") as HTMLDivElement;
|
||||
const input = element.querySelector(".input") as HTMLInputElement;
|
||||
|
||||
// Add click event listener to toggle edit mode
|
||||
text?.addEventListener("mousedown", (ev) => this.#toggleEditMode(ev));
|
||||
text?.addEventListener("mouseup", (ev) => this.#stopEventPropagation(ev));
|
||||
text?.addEventListener("dblclick", (ev) => this.#stopEventPropagation(ev));
|
||||
container?.addEventListener("mousedown", (ev) => {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
this.#setEditMode(ev, !this.#isEditable);
|
||||
});
|
||||
container?.addEventListener("mouseup", (ev) => this.#stopEventPropagation(ev));
|
||||
container?.addEventListener("dblclick", (ev) => this.#stopEventPropagation(ev));
|
||||
|
||||
// Add click event listener to delete spot
|
||||
button?.addEventListener("mousedown", (ev) => this.#stopEventPropagation(ev));
|
||||
button?.addEventListener("mousedown", (ev) => {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
this.#onButtonClicked(ev);
|
||||
});
|
||||
button?.addEventListener("mouseup", (ev) => this.#stopEventPropagation(ev));
|
||||
button?.addEventListener("dblclick", (ev) => this.#stopEventPropagation(ev));
|
||||
|
||||
@@ -45,46 +72,62 @@ export class SpotEditMarker extends Marker {
|
||||
input?.addEventListener("mousedown", (ev) => this.#stopEventPropagation(ev));
|
||||
input?.addEventListener("mouseup", (ev) => this.#stopEventPropagation(ev));
|
||||
input?.addEventListener("dblclick", (ev) => this.#stopEventPropagation(ev));
|
||||
input?.addEventListener("blur", (ev) => this.#toggleEditMode(ev));
|
||||
input?.addEventListener("keydown", (ev) => this.#acceptInput(ev));
|
||||
input?.addEventListener("blur", (ev) => this.#setEditMode(ev, false));
|
||||
input?.addEventListener("keydown", (ev) => this.#onKeyDown(ev));
|
||||
input?.addEventListener("input", (ev) => this.#validateInput(ev));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the marker is removed from the map.
|
||||
* @param {Map} map - The map instance.
|
||||
* @returns {this} - The current instance of SpotEditMarker.
|
||||
*/
|
||||
onRemove(map: Map): this {
|
||||
super.onRemove(map);
|
||||
|
||||
const element = this.getElement();
|
||||
if (element) {
|
||||
const text = element.querySelector(".text");
|
||||
const button = element.querySelector(".delete");
|
||||
const input = element.querySelector(".input");
|
||||
const container = element.querySelector(".container") as HTMLDivElement;
|
||||
const button = element.querySelector(".delete") as HTMLDivElement;
|
||||
const input = element.querySelector(".input") as HTMLInputElement;
|
||||
|
||||
// Add click event listener to toggle edit mode
|
||||
text?.removeEventListener("mousedown", (ev) => this.#toggleEditMode(ev));
|
||||
text?.removeEventListener("mouseup", (ev) => this.#stopEventPropagation(ev));
|
||||
text?.removeEventListener("dblclick", (ev) => this.#stopEventPropagation(ev));
|
||||
// Remove click event listener to toggle edit mode
|
||||
container?.removeEventListener("mousedown", (ev) => {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
this.#setEditMode(ev, !this.#isEditable);
|
||||
});
|
||||
container?.removeEventListener("mouseup", (ev) => this.#stopEventPropagation(ev));
|
||||
container?.removeEventListener("dblclick", (ev) => this.#stopEventPropagation(ev));
|
||||
|
||||
// Add click event listener to delete spot
|
||||
button?.removeEventListener("mousedown", (ev) => this.#stopEventPropagation(ev));
|
||||
// Remove click event listener to delete spot
|
||||
button?.removeEventListener("mousedown", (ev) => {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
this.#onButtonClicked(ev);
|
||||
});
|
||||
button?.removeEventListener("mouseup", (ev) => this.#stopEventPropagation(ev));
|
||||
button?.removeEventListener("dblclick", (ev) => this.#stopEventPropagation(ev));
|
||||
|
||||
// Add click event listener to input spot
|
||||
// Remove click event listener to input spot
|
||||
input?.removeEventListener("mousedown", (ev) => this.#stopEventPropagation(ev));
|
||||
input?.removeEventListener("mouseup", (ev) => this.#stopEventPropagation(ev));
|
||||
input?.removeEventListener("dblclick", (ev) => this.#stopEventPropagation(ev));
|
||||
input?.removeEventListener("blur", (ev) => this.#toggleEditMode(ev));
|
||||
input?.removeEventListener("keydown", (ev) => this.#acceptInput(ev));
|
||||
input?.removeEventListener("blur", (ev) => this.#setEditMode(ev, false));
|
||||
input?.removeEventListener("keydown", (ev) => this.#onKeyDown(ev));
|
||||
input?.removeEventListener("input", (ev) => this.#validateInput(ev));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// Method to set the text value
|
||||
/**
|
||||
* Sets the text value of the marker.
|
||||
* @param {string} textValue - The new text value.
|
||||
*/
|
||||
setTextValue(textValue: string) {
|
||||
this.#textValue = textValue;
|
||||
const element = this.getElement();
|
||||
@@ -94,12 +137,18 @@ export class SpotEditMarker extends Marker {
|
||||
}
|
||||
}
|
||||
|
||||
// Method to get the text value
|
||||
/**
|
||||
* Gets the text value of the marker.
|
||||
* @returns {string} - The current text value.
|
||||
*/
|
||||
getTextValue() {
|
||||
return this.#textValue;
|
||||
}
|
||||
|
||||
// Method to set the rotation angle in radians
|
||||
/**
|
||||
* Sets the rotation angle of the marker in radians.
|
||||
* @param {number} angle - The new rotation angle in radians.
|
||||
*/
|
||||
setRotationAngle(angle: number) {
|
||||
this.#rotationAngle = angle;
|
||||
if (!this.#isEditable) {
|
||||
@@ -107,12 +156,17 @@ export class SpotEditMarker extends Marker {
|
||||
}
|
||||
}
|
||||
|
||||
// Method to get the rotation angle in radians
|
||||
/**
|
||||
* Gets the rotation angle of the marker in radians.
|
||||
* @returns {number} - The current rotation angle in radians.
|
||||
*/
|
||||
getRotationAngle() {
|
||||
return this.#rotationAngle;
|
||||
}
|
||||
|
||||
// Method to update the rotation angle to ensure the text is always readable
|
||||
/**
|
||||
* Updates the rotation angle to ensure the text is always readable.
|
||||
*/
|
||||
#updateRotation() {
|
||||
const element = this.getElement();
|
||||
if (element) {
|
||||
@@ -129,11 +183,12 @@ export class SpotEditMarker extends Marker {
|
||||
}
|
||||
}
|
||||
|
||||
#toggleEditMode(ev) {
|
||||
this.#isEditable = !this.#isEditable;
|
||||
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
/**
|
||||
* Toggles the edit mode of the marker.
|
||||
* @param {Event} ev - The event object.
|
||||
*/
|
||||
#setEditMode(ev: Event, editable: boolean) {
|
||||
this.#isEditable = editable;
|
||||
|
||||
const element = this.getElement();
|
||||
if (element) {
|
||||
@@ -167,23 +222,43 @@ export class SpotEditMarker extends Marker {
|
||||
}
|
||||
}
|
||||
|
||||
#stopEventPropagation(ev) {
|
||||
/**
|
||||
* Stops the event propagation.
|
||||
* @param {Event} ev - The event object.
|
||||
*/
|
||||
#stopEventPropagation(ev: Event) {
|
||||
ev.stopPropagation();
|
||||
}
|
||||
|
||||
#validateInput(ev) {
|
||||
/**
|
||||
* Validates the input value.
|
||||
* @param {Event} ev - The event object.
|
||||
*/
|
||||
#validateInput(ev: Event) {
|
||||
const element = this.getElement();
|
||||
if (element) {
|
||||
const input = ev.target as HTMLInputElement;
|
||||
const text = element.querySelector(".text");
|
||||
|
||||
// Validate the input value
|
||||
// Validate the input value (must be a valid laser code)
|
||||
const value = input.value;
|
||||
|
||||
// Check if the value is a partial valid input
|
||||
// Conditions for partial validity:
|
||||
// 1. The first digit is 1.
|
||||
// 2. The first digit is 1 and the second digit is between 1 and 7.
|
||||
// 3. The first digit is 1, the second digit is between 1 and 7, and the third digit is between 1 and 8.
|
||||
// 4. The first digit is 1, the second digit is between 1 and 7, the third digit is between 1 and 8, and the fourth digit is between 1 and 8.
|
||||
const isPartialValid = /^[1]$|^[1][1-7]$|^[1][1-7][1-8]$|^[1][1-7][1-8][1-8]$/.test(value);
|
||||
|
||||
// Check if the value is a complete valid input
|
||||
// Conditions for complete validity:
|
||||
// 1. The input is a four-digit number where:
|
||||
// - The first digit is 1.
|
||||
// - The second digit is between 1 and 7.
|
||||
// - The third digit is between 1 and 8.
|
||||
// - The fourth digit is between 1 and 8.
|
||||
// 2. The number is between 1111 and 1788.
|
||||
const isValid = /^[1][1-7][1-8][1-8]$/.test(value) && Number(value) <= 1788;
|
||||
|
||||
if (isPartialValid || isValid) {
|
||||
@@ -197,6 +272,9 @@ export class SpotEditMarker extends Marker {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles invalid input by causing a small red flash around the input element.
|
||||
*/
|
||||
#errorFunction() {
|
||||
const element = this.getElement();
|
||||
if (element) {
|
||||
@@ -210,7 +288,59 @@ export class SpotEditMarker extends Marker {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the key down event.
|
||||
* @param {Event} ev - The keyboard event object.
|
||||
*/
|
||||
#onKeyDown(ev) {
|
||||
if (ev.key === "Enter") this.#acceptInput(ev);
|
||||
else if (ev.key === "Escape") this.#setEditMode(ev, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts the input value when the Enter key is pressed.
|
||||
* @param {Event} ev - The keyboard event object.
|
||||
*/
|
||||
#acceptInput(ev) {
|
||||
if (ev.key === "Enter") this.#toggleEditMode(ev);
|
||||
const element = this.getElement();
|
||||
if (element) {
|
||||
const input = element.querySelector(".input") as HTMLInputElement;
|
||||
if (input) {
|
||||
// Validate the input value (must be a valid laser code)
|
||||
const value = Number(input.value);
|
||||
if (value >= 1111 && value <= 1788) {
|
||||
this.#setEditMode(ev, false);
|
||||
this.onValueUpdated(value);
|
||||
this.#successFunction();
|
||||
} else {
|
||||
this.#errorFunction();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the button click event.
|
||||
* @param {Event} ev - The event object.
|
||||
*/
|
||||
#onButtonClicked(ev: Event) {
|
||||
if (this.#isEditable) this.#acceptInput(ev);
|
||||
else this.onDeleteButtonClicked();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles valid input by causing a green flash around the container.
|
||||
*/
|
||||
#successFunction() {
|
||||
const element = this.getElement();
|
||||
if (element) {
|
||||
const container = element.querySelector(".container") as HTMLDivElement;
|
||||
if (container) {
|
||||
container.classList.add("success-flash");
|
||||
setTimeout(() => {
|
||||
container.classList.remove("success-flash");
|
||||
}, 900); // Duration of the flash effect (3 flashes, 300ms each)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ import { CustomMarker } from "./custommarker";
|
||||
export class SpotMarker extends CustomMarker {
|
||||
constructor(latlng: LatLngExpression, options?: MarkerOptions) {
|
||||
super(latlng, options);
|
||||
this.options.interactive = false;
|
||||
this.options.interactive = true;
|
||||
this.options.draggable = true;
|
||||
this.setZIndexOffset(9999);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* Container for the spot input marker */
|
||||
.leaflet-spot-input-marker {
|
||||
text-align: center;
|
||||
display: flex !important;
|
||||
@@ -5,6 +6,7 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Delete button styles */
|
||||
.leaflet-spot-input-marker .delete {
|
||||
background-color: darkred;
|
||||
color: #fffd;
|
||||
@@ -17,10 +19,12 @@
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
/* Delete button hover effect */
|
||||
.leaflet-spot-input-marker .delete:hover {
|
||||
background-color: lightcoral;
|
||||
}
|
||||
|
||||
/* Container for the spot input marker content */
|
||||
.leaflet-spot-input-marker .container {
|
||||
width: fit-content;
|
||||
display: flex;
|
||||
@@ -31,14 +35,17 @@
|
||||
font-size: 13px;
|
||||
column-gap: 6px;
|
||||
align-content: center;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
/* Text inside the spot input marker */
|
||||
.leaflet-spot-input-marker .text {
|
||||
margin-left: 12px;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
/* Input field inside the spot input marker */
|
||||
.leaflet-spot-input-marker .input {
|
||||
display: none;
|
||||
color: white;
|
||||
@@ -50,25 +57,32 @@
|
||||
outline: none; /* Remove default outline */
|
||||
font-size: 13px;
|
||||
width: 80px;
|
||||
border-top-left-radius: 999px;
|
||||
border-bottom-left-radius: 999px;
|
||||
}
|
||||
|
||||
/* Input field focus effect */
|
||||
.leaflet-spot-input-marker .input:focus {
|
||||
border-color: #777; /* Border color on focus */
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.5); /* Shadow on focus */
|
||||
}
|
||||
|
||||
/* Hide input field inside the container */
|
||||
.leaflet-spot-input-marker .container .input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Hide text in edit mode */
|
||||
.leaflet-spot-input-marker .container .text.edit-mode {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Error flash animation for input field */
|
||||
.leaflet-spot-input-marker .input.error-flash {
|
||||
animation: error-flash 0.3s;
|
||||
}
|
||||
|
||||
/* Keyframes for error flash animation */
|
||||
@keyframes error-flash {
|
||||
0% {
|
||||
border-color: #555;
|
||||
@@ -81,10 +95,27 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Edit mode styles for delete button */
|
||||
.leaflet-spot-input-marker .delete.edit-mode {
|
||||
background-color: green;
|
||||
}
|
||||
|
||||
/* Edit mode hover effect for delete button */
|
||||
.leaflet-spot-input-marker .delete.edit-mode:hover {
|
||||
background-color: lightgreen;
|
||||
}
|
||||
|
||||
/* Success flash animation for container */
|
||||
.leaflet-spot-input-marker .container.success-flash {
|
||||
animation: success-flash 0.3s 3;
|
||||
}
|
||||
|
||||
/* Keyframes for success flash animation */
|
||||
@keyframes success-flash {
|
||||
0%, 100% {
|
||||
border-color: transparent;
|
||||
}
|
||||
33%, 66% {
|
||||
border-color: green;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user