feat(frontend): add socket support and styling for individual items
This commit is contained in:
@@ -0,0 +1,258 @@
|
||||
// Minifigure Socket class
|
||||
class BrickMinifigureSocket extends BrickSocket {
|
||||
constructor(id, path, namespace, messages) {
|
||||
super(id, path, namespace, messages, false);
|
||||
|
||||
// Listeners
|
||||
this.add_listener = undefined;
|
||||
this.input_listener = undefined;
|
||||
this.confirm_listener = undefined;
|
||||
|
||||
// Form elements (built based on the initial id)
|
||||
this.html_button = document.getElementById(id);
|
||||
this.html_input = document.getElementById(`${id}-set`);
|
||||
this.html_no_confim = document.getElementById(`${id}-no-confirm`);
|
||||
this.html_owners = document.getElementById(`${id}-owners`);
|
||||
this.html_purchase_location = document.getElementById(`${id}-purchase-location`);
|
||||
this.html_storage = document.getElementById(`${id}-storage`);
|
||||
this.html_tags = document.getElementById(`${id}-tags`);
|
||||
|
||||
// Card elements
|
||||
this.html_card = document.getElementById(`${id}-card`);
|
||||
this.html_card_set = document.getElementById(`${id}-card-set`);
|
||||
this.html_card_name = document.getElementById(`${id}-card-name`);
|
||||
this.html_card_image_container = document.getElementById(`${id}-card-image-container`);
|
||||
this.html_card_image = document.getElementById(`${id}-card-image`);
|
||||
this.html_card_footer = document.getElementById(`${id}-card-footer`);
|
||||
this.html_card_confirm = document.getElementById(`${id}-card-confirm`);
|
||||
this.html_card_dismiss = document.getElementById(`${id}-card-dismiss`);
|
||||
|
||||
if (this.html_button) {
|
||||
this.add_listener = this.html_button.addEventListener("click", ((bricksocket) => (e) => {
|
||||
bricksocket.execute();
|
||||
})(this));
|
||||
|
||||
this.input_listener = this.html_input.addEventListener("keyup", ((bricksocket) => (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
bricksocket.execute();
|
||||
}
|
||||
})(this))
|
||||
}
|
||||
|
||||
if (this.html_card_dismiss && this.html_card) {
|
||||
this.html_card_dismiss.addEventListener("click", ((card) => (e) => {
|
||||
card.classList.add("d-none");
|
||||
})(this.html_card));
|
||||
}
|
||||
|
||||
// Setup the socket
|
||||
this.setup();
|
||||
}
|
||||
|
||||
// Clear form
|
||||
clear() {
|
||||
super.clear();
|
||||
|
||||
if (this.html_card) {
|
||||
this.html_card.classList.add("d-none");
|
||||
}
|
||||
|
||||
if (this.html_card_footer) {
|
||||
this.html_card_footer.classList.add("d-none");
|
||||
|
||||
if (this.html_card_confirm) {
|
||||
this.html_card_footer.classList.add("d-none");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the action
|
||||
execute() {
|
||||
if (!this.disabled && this.socket !== undefined && this.socket.connected) {
|
||||
this.toggle(false);
|
||||
|
||||
if (this.html_no_confim && this.html_no_confim.checked) {
|
||||
this.import_minifigure(true);
|
||||
} else {
|
||||
this.load_minifigure();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Import a minifigure
|
||||
import_minifigure(no_confirm, figure) {
|
||||
if (this.html_input) {
|
||||
if (no_confirm) {
|
||||
this.clear();
|
||||
} else {
|
||||
this.clear_status();
|
||||
}
|
||||
|
||||
// Grab the owners
|
||||
const owners = [];
|
||||
if (this.html_owners) {
|
||||
this.html_owners.querySelectorAll('input').forEach(input => {
|
||||
if (input.checked) {
|
||||
owners.push(input.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Grab the purchase location
|
||||
let purchase_location = null;
|
||||
if (this.html_purchase_location) {
|
||||
purchase_location = this.html_purchase_location.value;
|
||||
}
|
||||
|
||||
// Grab the storage
|
||||
let storage = null;
|
||||
if (this.html_storage) {
|
||||
storage = this.html_storage.value;
|
||||
}
|
||||
|
||||
// Grab the tags
|
||||
const tags = [];
|
||||
if (this.html_tags) {
|
||||
this.html_tags.querySelectorAll('input').forEach(input => {
|
||||
if (input.checked) {
|
||||
tags.push(input.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.spinner(true);
|
||||
|
||||
if (this.html_progress_bar) {
|
||||
this.html_progress_bar.scrollIntoView();
|
||||
}
|
||||
|
||||
this.socket.emit(this.messages.IMPORT_MINIFIGURE, {
|
||||
figure: (figure !== undefined) ? figure : this.html_input.value,
|
||||
owners: owners,
|
||||
purchase_location: purchase_location,
|
||||
storage: storage,
|
||||
tags: tags,
|
||||
quantity: 1
|
||||
});
|
||||
} else {
|
||||
this.fail("Could not find the input field for the minifigure number");
|
||||
}
|
||||
}
|
||||
|
||||
// Load a minifigure
|
||||
load_minifigure() {
|
||||
if (this.html_input) {
|
||||
// Reset the progress
|
||||
this.clear()
|
||||
this.spinner(true);
|
||||
|
||||
this.socket.emit(this.messages.LOAD_MINIFIGURE, {
|
||||
figure: this.html_input.value
|
||||
});
|
||||
} else {
|
||||
this.fail("Could not find the input field for the minifigure number");
|
||||
}
|
||||
}
|
||||
|
||||
// Minifigure is loaded
|
||||
minifigure_loaded(data) {
|
||||
if (this.html_card) {
|
||||
this.html_card.classList.remove("d-none");
|
||||
|
||||
if (this.html_card_set) {
|
||||
this.html_card_set.textContent = data["figure"];
|
||||
}
|
||||
|
||||
if (this.html_card_name) {
|
||||
this.html_card_name.textContent = data["name"];
|
||||
}
|
||||
|
||||
if (this.html_card_image_container) {
|
||||
this.html_card_image_container.setAttribute("style", `background-image: url(${data["image"]})`);
|
||||
}
|
||||
|
||||
if (this.html_card_image) {
|
||||
this.html_card_image.setAttribute("src", data["image"]);
|
||||
this.html_card_image.setAttribute("alt", data["figure"]);
|
||||
}
|
||||
|
||||
if (this.html_card_footer) {
|
||||
this.html_card_footer.classList.add("d-none");
|
||||
|
||||
if (!data.download) {
|
||||
this.html_card_footer.classList.remove("d-none");
|
||||
|
||||
if (this.html_card_confirm) {
|
||||
if (this.confirm_listener !== undefined) {
|
||||
this.html_card_confirm.removeEventListener("click", this.confirm_listener);
|
||||
}
|
||||
|
||||
this.confirm_listener = ((bricksocket, figure) => (e) => {
|
||||
if (!bricksocket.disabled) {
|
||||
bricksocket.toggle(false);
|
||||
bricksocket.import_minifigure(false, figure);
|
||||
}
|
||||
})(this, data["figure"]);
|
||||
|
||||
this.html_card_confirm.addEventListener("click", this.confirm_listener);
|
||||
|
||||
this.html_card_confirm.scrollIntoView();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setup the actual socket
|
||||
setup() {
|
||||
super.setup();
|
||||
|
||||
if (this.socket !== undefined) {
|
||||
// Minifigure loaded
|
||||
this.socket.on(this.messages.MINIFIGURE_LOADED, ((bricksocket) => (data) => {
|
||||
bricksocket.minifigure_loaded(data);
|
||||
})(this));
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle clicking on the button, or sending events
|
||||
toggle(enabled) {
|
||||
super.toggle(enabled);
|
||||
|
||||
if (this.html_button) {
|
||||
this.html_button.disabled = !enabled;
|
||||
}
|
||||
|
||||
if (this.html_input) {
|
||||
this.html_input.disabled = !enabled;
|
||||
}
|
||||
|
||||
if (this.html_no_confim) {
|
||||
this.html_no_confim.disabled = !enabled;
|
||||
}
|
||||
|
||||
if (this.html_owners) {
|
||||
this.html_owners.querySelectorAll('input').forEach(input => input.disabled = !enabled);
|
||||
}
|
||||
|
||||
if (this.html_purchase_location) {
|
||||
this.html_purchase_location.disabled = !enabled;
|
||||
}
|
||||
|
||||
if (this.html_storage) {
|
||||
this.html_storage.disabled = !enabled;
|
||||
}
|
||||
|
||||
if (this.html_tags) {
|
||||
this.html_tags.querySelectorAll('input').forEach(input => input.disabled = !enabled);
|
||||
}
|
||||
|
||||
if (this.html_card_confirm) {
|
||||
this.html_card_confirm.disabled = !enabled;
|
||||
}
|
||||
|
||||
if (this.html_card_dismiss) {
|
||||
this.html_card_dismiss.disabled = !enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -271,6 +271,56 @@ class BrickSetSocket extends BrickSocket {
|
||||
}
|
||||
}
|
||||
|
||||
// Minifigure is loaded (when bulk adding minifigures through set socket)
|
||||
minifigure_loaded(data) {
|
||||
if (this.html_card) {
|
||||
this.html_card.classList.remove("d-none");
|
||||
|
||||
if (this.html_card_set) {
|
||||
this.html_card_set.textContent = data["figure"];
|
||||
}
|
||||
|
||||
if (this.html_card_name) {
|
||||
this.html_card_name.textContent = data["name"];
|
||||
}
|
||||
|
||||
if (this.html_card_image_container) {
|
||||
this.html_card_image_container.setAttribute("style", `background-image: url(${data["image"]})`);
|
||||
}
|
||||
|
||||
if (this.html_card_image) {
|
||||
this.html_card_image.setAttribute("src", data["image"]);
|
||||
this.html_card_image.setAttribute("alt", data["figure"]);
|
||||
}
|
||||
|
||||
if (this.html_card_footer) {
|
||||
this.html_card_footer.classList.add("d-none");
|
||||
|
||||
if (!data.download) {
|
||||
this.html_card_footer.classList.remove("d-none");
|
||||
|
||||
if (this.html_card_confirm) {
|
||||
if (this.confirm_listener !== undefined) {
|
||||
this.html_card_confirm.removeEventListener("click", this.confirm_listener);
|
||||
}
|
||||
|
||||
this.confirm_listener = ((bricksocket, figure) => (e) => {
|
||||
if (!bricksocket.disabled) {
|
||||
bricksocket.toggle(false);
|
||||
// For minifigures, we use import_set with the figure number
|
||||
bricksocket.import_set(false, figure);
|
||||
}
|
||||
})(this, data["figure"]);
|
||||
|
||||
this.html_card_confirm.addEventListener("click", this.confirm_listener);
|
||||
|
||||
this.html_card_confirm.scrollIntoView();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setup the actual socket
|
||||
setup() {
|
||||
super.setup();
|
||||
@@ -280,6 +330,13 @@ class BrickSetSocket extends BrickSocket {
|
||||
this.socket.on(this.messages.SET_LOADED, ((bricksocket) => (data) => {
|
||||
bricksocket.set_loaded(data);
|
||||
})(this));
|
||||
|
||||
// Minifigure loaded (for bulk add with mixed sets/minifigures)
|
||||
if (this.messages.MINIFIGURE_LOADED) {
|
||||
this.socket.on(this.messages.MINIFIGURE_LOADED, ((bricksocket) => (data) => {
|
||||
bricksocket.minifigure_loaded(data);
|
||||
})(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -272,3 +272,52 @@
|
||||
[data-bs-theme="dark"] .table tbody th {
|
||||
color: var(--bs-body-color);
|
||||
}
|
||||
|
||||
/* Navbar split button dropdown styling */
|
||||
.navbar-nav .dropdown {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.navbar-nav .dropdown .dropdown-toggle-split {
|
||||
padding-left: 0.375rem;
|
||||
padding-right: 0.375rem;
|
||||
margin-left: -1px;
|
||||
}
|
||||
|
||||
.navbar-nav .dropdown .dropdown-toggle-split::after {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
/* Navbar dropdown positioning */
|
||||
.navbar-nav .dropdown-menu {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
margin-top: 0.125rem;
|
||||
}
|
||||
|
||||
/* Navbar dropdown styling to match navbar theme */
|
||||
.navbar-dark .navbar-nav .dropdown-menu {
|
||||
background-color: #212529;
|
||||
border-color: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
.navbar-dark .navbar-nav .dropdown-menu .dropdown-item {
|
||||
color: rgba(255, 255, 255, 0.55);
|
||||
}
|
||||
|
||||
.navbar-dark .navbar-nav .dropdown-menu .dropdown-item:hover,
|
||||
.navbar-dark .navbar-nav .dropdown-menu .dropdown-item:focus {
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
/* Navbar dropdown hover support for desktop */
|
||||
@media (min-width: 992px) {
|
||||
.navbar-nav .dropdown:hover > .dropdown-menu {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user