mirror of
https://github.com/AndrioCelos/TableturfBattleApp.git
synced 2026-08-21 18:34:00 -05:00
Rework deck editor page and allow drag/drop of decks and cards
This commit is contained in:
@@ -31,9 +31,33 @@ class CheckButtonGroup<TValue> {
|
||||
}
|
||||
|
||||
replace(index: number, button: CheckButton, value: TValue) {
|
||||
const existingChild = this.entries[index].button.buttonElement;
|
||||
this.entries[index] = { button, value };
|
||||
this.setupButton(button, value);
|
||||
// The caller is responsible for adding the button to the DOM.
|
||||
this.parentElement?.insertBefore(button.buttonElement, existingChild);
|
||||
this.parentElement?.removeChild(existingChild);
|
||||
}
|
||||
|
||||
insert(index: number, button: CheckButton, value: TValue) {
|
||||
const existingChild = this.entries[index].button.buttonElement;
|
||||
this.entries.splice(index, 0, { button, value });
|
||||
this.setupButton(button, value);
|
||||
this.parentElement?.insertBefore(button.buttonElement, existingChild);
|
||||
}
|
||||
|
||||
removeAt(index: number) {
|
||||
const existingChild = this.entries[index].button.buttonElement;
|
||||
this.entries.splice(index, 1)
|
||||
this.parentElement?.removeChild(existingChild);
|
||||
}
|
||||
|
||||
move(index: number, newIndex: number | null) {
|
||||
const entry = this.entries[index];
|
||||
this.entries.splice(index, 1)
|
||||
const existingChild = newIndex == null || newIndex >= this.entries.length ? null : this.entries[newIndex].button.buttonElement;
|
||||
if (newIndex == null) this.entries.push(entry);
|
||||
else this.entries.splice(newIndex, 0, entry);
|
||||
this.parentElement?.insertBefore(entry.button.buttonElement, existingChild);
|
||||
}
|
||||
|
||||
clear() {
|
||||
|
||||
@@ -7,3 +7,4 @@ interface Config {
|
||||
}
|
||||
|
||||
declare var config: Config;
|
||||
declare var polyfillActive: boolean;
|
||||
|
||||
@@ -17,7 +17,7 @@ const testStageSelectionDialog = document.getElementById('testStageSelectionDial
|
||||
|
||||
const deckEditCardButtons = new CheckButtonGroup<number>(deckCardListEdit);
|
||||
|
||||
let selectedDeckCardIndex: number | null = null;
|
||||
let draggingCardButton: Element | null = null;
|
||||
|
||||
function deckEditInitCardDatabase(cards: Card[]) {
|
||||
for (const card of cards) {
|
||||
@@ -32,8 +32,9 @@ function deckEditInitCardDatabase(cards: Card[]) {
|
||||
button2.checked = false;
|
||||
}
|
||||
|
||||
if (selectedDeckCardIndex == null) return;
|
||||
const oldEntry = deckEditCardButtons.entries[selectedDeckCardIndex];
|
||||
const index = deckEditCardButtons.entries.findIndex(el => el.button.checked);
|
||||
if (index < 0) return;
|
||||
const oldEntry = deckEditCardButtons.entries[index];
|
||||
const oldCardNumber = oldEntry.value;
|
||||
|
||||
if (oldCardNumber != 0)
|
||||
@@ -43,11 +44,7 @@ function deckEditInitCardDatabase(cards: Card[]) {
|
||||
const button3 = createDeckEditCardButton(card.number);
|
||||
button3.checked = true;
|
||||
|
||||
const oldElement = oldEntry.button.buttonElement;
|
||||
deckCardListEdit.insertBefore(button3.buttonElement, oldElement);
|
||||
deckCardListEdit.removeChild(oldElement);
|
||||
|
||||
deckEditCardButtons.replace(selectedDeckCardIndex, button3, card.number);
|
||||
deckEditCardButtons.replace(index, button3, card.number);
|
||||
deckEditUpdateSize();
|
||||
|
||||
cardList.listElement.parentElement!.classList.remove('selecting');
|
||||
@@ -94,7 +91,6 @@ function editDeck() {
|
||||
deckNameLabel2.innerText = selectedDeck.name;
|
||||
|
||||
deckEditCardButtons.clear();
|
||||
selectedDeckCardIndex = null;
|
||||
|
||||
for (let i = 0; i < 15; i++) {
|
||||
if (selectedDeck.cards[i]) {
|
||||
@@ -117,27 +113,88 @@ function editDeck() {
|
||||
|
||||
function createDeckEditCardButton(cardNumber: number) {
|
||||
const button = new CardButton(cardDatabase.get(cardNumber));
|
||||
button.buttonElement.draggable = true;
|
||||
button.buttonElement.addEventListener('click', () => {
|
||||
selectedDeckCardIndex = deckEditCardButtons.entries.findIndex(e => e.button == button);
|
||||
for (const button2 of cardList.cardButtons) {
|
||||
button2.checked = button2.card.number == cardNumber;
|
||||
}
|
||||
cardList.listElement.parentElement!.classList.add('selecting');
|
||||
});
|
||||
button.buttonElement.addEventListener('dragstart', e => {
|
||||
if (e.dataTransfer == null) return;
|
||||
const index = deckEditCardButtons.entries.findIndex(el => el.button.buttonElement == e.currentTarget);
|
||||
draggingCardButton = button.buttonElement;
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
e.dataTransfer.setData('application/tableturf-card-index', index.toString());
|
||||
button.buttonElement.classList.add('dragging');
|
||||
});
|
||||
button.buttonElement.addEventListener('dragend', e => {
|
||||
button.buttonElement.classList.remove('dragging');
|
||||
if (draggingCardButton != null && e.currentTarget == draggingCardButton) {
|
||||
const index = deckEditCardButtons.entries.findIndex(el => el.button.buttonElement == e.currentTarget) + 1;
|
||||
deckCardListEdit.insertBefore(draggingCardButton, index >= deckEditCardButtons.entries.length ? null : deckEditCardButtons.entries[index].button.buttonElement);
|
||||
draggingCardButton = null;
|
||||
}
|
||||
});
|
||||
button.buttonElement.addEventListener('dragenter', e => e.preventDefault());
|
||||
button.buttonElement.addEventListener('dragover', deckEditCardButton_dragover);
|
||||
button.buttonElement.addEventListener('drop', deckEditCardButton_drop);
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
function deckEditCardButton_dragover(e: DragEvent) {
|
||||
e.preventDefault();
|
||||
if (e.dataTransfer == null) return;
|
||||
const indexString = e.dataTransfer.getData('application/tableturf-card-index');
|
||||
if (indexString != '' && draggingCardButton != null) {
|
||||
e.dataTransfer.dropEffect = 'move';
|
||||
if (e.currentTarget != draggingCardButton && e.currentTarget != deckCardListEdit) {
|
||||
// Move the card being dragged into the new position as a preview.
|
||||
for (let el = draggingCardButton.nextElementSibling; el != null; el = el.nextElementSibling) {
|
||||
if (el == e.currentTarget) {
|
||||
deckCardListEdit.insertBefore(draggingCardButton, el.nextElementSibling);
|
||||
return;
|
||||
}
|
||||
}
|
||||
deckCardListEdit.insertBefore(draggingCardButton, e.currentTarget as Node);
|
||||
}
|
||||
} else if (e.dataTransfer.getData('text/plain'))
|
||||
e.dataTransfer.dropEffect = 'copy';
|
||||
}
|
||||
|
||||
function deckEditCardButton_drop(e: DragEvent) {
|
||||
e.preventDefault();
|
||||
if (e.dataTransfer == null) return;
|
||||
const indexString = e.dataTransfer.getData('application/tableturf-card-index');
|
||||
if (indexString) {
|
||||
const index = parseInt(indexString);
|
||||
let newIndex = 0;
|
||||
for (let el = deckCardListEdit.firstElementChild; el != null; el = el.nextElementSibling) {
|
||||
if (el == draggingCardButton) break;
|
||||
newIndex++;
|
||||
}
|
||||
if (newIndex == index) return;
|
||||
console.log(`Moving card ${index} to ${newIndex}.`);
|
||||
|
||||
deckEditCardButtons.move(index, newIndex);
|
||||
draggingCardButton = null;
|
||||
}
|
||||
}
|
||||
|
||||
function createDeckEditEmptySlotButton() {
|
||||
const buttonElement = document.createElement('button');
|
||||
const button = new CheckButton(buttonElement);
|
||||
buttonElement.type = 'button';
|
||||
buttonElement.className = 'card emptySlot';
|
||||
buttonElement.addEventListener('click', () => {
|
||||
selectedDeckCardIndex = deckEditCardButtons.entries.findIndex(e => e.button == button);
|
||||
for (const button2 of cardList.cardButtons)
|
||||
button2.checked = false;
|
||||
cardList.listElement.parentElement!.classList.add('selecting');
|
||||
});
|
||||
buttonElement.addEventListener('dragenter', e => e.preventDefault());
|
||||
buttonElement.addEventListener('dragover', deckEditCardButton_dragover);
|
||||
buttonElement.addEventListener('drop', deckEditCardButton_drop);
|
||||
return button;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
const deckListPage = document.getElementById('deckListPage')!;
|
||||
const deckListBackButton = document.getElementById('deckListBackButton') as HTMLLinkElement;
|
||||
const deckViewBackButton = document.getElementById('deckViewBackButton') as HTMLLinkElement;
|
||||
const deckEditorDeckViewSection = document.getElementById('deckEditorDeckViewSection')!;
|
||||
@@ -38,7 +39,10 @@ const deckImportFileBox = document.getElementById('deckImportFileBox') as HTMLIn
|
||||
const deckImportErrorBox = document.getElementById('deckImportErrorBox')!;
|
||||
const deckImportOkButton = document.getElementById('deckImportOkButton') as HTMLButtonElement;
|
||||
|
||||
const deckButtons = new CheckButtonGroup<Deck>();
|
||||
const deckButtons = new CheckButtonGroup<Deck>(deckList);
|
||||
|
||||
let deckListTouchMode = false;
|
||||
let draggingDeckButton: Element | null = null;
|
||||
|
||||
function showDeckList() {
|
||||
showPage('deckList');
|
||||
@@ -46,6 +50,18 @@ function showDeckList() {
|
||||
deckButtons.deselect();
|
||||
}
|
||||
|
||||
deckList.addEventListener('touchstart', deckListEnableTouchMode);
|
||||
|
||||
function deckListEnableTouchMode() {
|
||||
if (deckListTouchMode) return;
|
||||
deckListTouchMode = true;
|
||||
deckListPage.classList.add('touchmode');
|
||||
for (var b of deckButtons.buttons) {
|
||||
b.buttonElement.draggable = false;
|
||||
(b.buttonElement.getElementsByClassName('handle')[0] as HTMLElement).draggable = true;
|
||||
}
|
||||
}
|
||||
|
||||
deckListBackButton.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
showPage('preGame');
|
||||
@@ -65,7 +81,7 @@ deckViewBackButton.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
clearChildren(deckCardListView);
|
||||
deselectDeck();
|
||||
deckEditorDeckViewSection.hidden = true;
|
||||
deckListPage.classList.remove('showingDeck');
|
||||
});
|
||||
|
||||
function saveDecks() {
|
||||
@@ -90,25 +106,99 @@ function saveDecks() {
|
||||
}
|
||||
|
||||
for (let i = 0; i < decks.length; i++) {
|
||||
createDeckButton(i, decks[i]);
|
||||
createDeckButton(decks[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function createDeckButton(index: number, deck: Deck) {
|
||||
function createDeckButton(deck: Deck) {
|
||||
const buttonElement = document.createElement('button');
|
||||
buttonElement.type = 'button';
|
||||
buttonElement.draggable = true;
|
||||
const button = new CheckButton(buttonElement);
|
||||
deckButtons.add(button, deck);
|
||||
buttonElement.addEventListener('click', () => {
|
||||
selectedDeck = deckButtons.value;
|
||||
selectDeck();
|
||||
});
|
||||
buttonElement.innerText = deck.name;
|
||||
buttonElement.addEventListener('dragstart', e => {
|
||||
if (e.dataTransfer == null) return;
|
||||
const index = decks.indexOf(deck);
|
||||
draggingDeckButton = buttonElement;
|
||||
e.dataTransfer.effectAllowed = 'copyMove';
|
||||
e.dataTransfer.setData('text/plain', JSON.stringify(deck, [ 'name', 'cards' ]));
|
||||
e.dataTransfer.setData('application/tableturf-deck-index', index.toString());
|
||||
buttonElement.classList.add('dragging');
|
||||
});
|
||||
buttonElement.addEventListener('dragend', e => {
|
||||
buttonElement.classList.remove('dragging');
|
||||
if (draggingDeckButton != null && e.currentTarget == draggingDeckButton) {
|
||||
const index = deckButtons.entries.findIndex(el => el.button.buttonElement == e.currentTarget) + 1;
|
||||
deckList.insertBefore(draggingDeckButton, index >= deckButtons.entries.length ? null : deckButtons.entries[index].button.buttonElement);
|
||||
draggingDeckButton = null;
|
||||
}
|
||||
});
|
||||
buttonElement.addEventListener('dragenter', e => e.preventDefault());
|
||||
buttonElement.addEventListener('dragover', deckButton_dragover);
|
||||
buttonElement.addEventListener('drop', deckButton_drop);
|
||||
|
||||
const handle = document.createElement('div');
|
||||
handle.className = 'handle';
|
||||
buttonElement.appendChild(handle);
|
||||
buttonElement.appendChild(document.createTextNode(deck.name));
|
||||
|
||||
deckList.insertBefore(buttonElement, addDeckControls);
|
||||
return button;
|
||||
}
|
||||
|
||||
function deckButton_dragover(e: DragEvent) {
|
||||
e.preventDefault();
|
||||
if (e.dataTransfer == null) return;
|
||||
const indexString = e.dataTransfer.getData('application/tableturf-deck-index');
|
||||
if (indexString != '' && draggingDeckButton != null) {
|
||||
e.dataTransfer.dropEffect = 'move';
|
||||
if (e.currentTarget != draggingDeckButton && e.currentTarget != deckList) {
|
||||
// Move the deck being dragged into the new position as a preview.
|
||||
for (let el = draggingDeckButton.nextElementSibling; el != null; el = el.nextElementSibling) {
|
||||
if (el == e.currentTarget) {
|
||||
deckList.insertBefore(draggingDeckButton, el.nextElementSibling);
|
||||
return;
|
||||
}
|
||||
}
|
||||
deckList.insertBefore(draggingDeckButton, e.currentTarget as Node);
|
||||
}
|
||||
} else if (e.dataTransfer.getData('text/plain'))
|
||||
e.dataTransfer.dropEffect = 'copy';
|
||||
}
|
||||
|
||||
function deckButton_drop(e: DragEvent) {
|
||||
e.preventDefault();
|
||||
if (e.dataTransfer == null) return;
|
||||
const indexString = e.dataTransfer.getData('application/tableturf-deck-index');
|
||||
if (indexString) {
|
||||
const index = parseInt(indexString);
|
||||
let newIndex = 0;
|
||||
for (let el = deckList.firstElementChild; el != null; el = el.nextElementSibling) {
|
||||
if (el == draggingDeckButton) break;
|
||||
newIndex++;
|
||||
}
|
||||
if (newIndex == index) return;
|
||||
console.log(`Moving deck ${index} to ${newIndex}.`);
|
||||
|
||||
deckButtons.move(index, newIndex);
|
||||
const deck = decks[index];
|
||||
decks.splice(index, 1);
|
||||
decks.splice(newIndex, 0, deck);
|
||||
saveDecks();
|
||||
draggingDeckButton = null;
|
||||
return;
|
||||
}
|
||||
const text = e.dataTransfer.getData('text/plain');
|
||||
if (text) {
|
||||
const data = JSON.parse(text);
|
||||
const decks = (data instanceof Array ? data : [ data ]) as Deck[];
|
||||
importDecks(decks);
|
||||
}
|
||||
}
|
||||
|
||||
function importDecks(decksToImport: (Deck | number[])[]) {
|
||||
let newSelectedDeck: Deck | null = null;
|
||||
for (const el of decksToImport) {
|
||||
@@ -119,7 +209,7 @@ function importDecks(decksToImport: (Deck | number[])[]) {
|
||||
deck = el;
|
||||
if (!deck.name) deck.name = `Imported Deck ${decks.length + 1}`;
|
||||
}
|
||||
createDeckButton(decks.length, deck);
|
||||
createDeckButton(deck);
|
||||
decks.push(deck);
|
||||
newSelectedDeck ??= deck;
|
||||
}
|
||||
@@ -134,7 +224,7 @@ function importDecks(decksToImport: (Deck | number[])[]) {
|
||||
|
||||
newDeckButton.addEventListener('click', () => {
|
||||
selectedDeck = new Deck(`Deck ${decks.length + 1}`, [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], false);
|
||||
createDeckButton(decks.length, selectedDeck);
|
||||
createDeckButton(selectedDeck);
|
||||
decks.push(selectedDeck);
|
||||
editDeck();
|
||||
});
|
||||
@@ -196,20 +286,29 @@ function selectDeck() {
|
||||
}
|
||||
}
|
||||
|
||||
deckListTestButton.disabled = false;
|
||||
deckExportButton.disabled = false;
|
||||
deckCopyButton.disabled = false;
|
||||
deckEditButton.disabled = selectedDeck.isReadOnly;
|
||||
deckRenameButton.disabled = selectedDeck.isReadOnly;
|
||||
deckCopyButton.disabled = false;
|
||||
deckDeleteButton.disabled = selectedDeck.isReadOnly;
|
||||
deckViewSize.innerText = size.toString();
|
||||
deckEditorDeckViewSection.hidden = false;
|
||||
deckListPage.classList.add('showingDeck');
|
||||
}
|
||||
|
||||
function deselectDeck() {
|
||||
selectedDeck = null;
|
||||
deckButtons.deselect();
|
||||
clearChildren(deckCardListView);
|
||||
deckNameLabel.innerText = '\u00a0';
|
||||
deckViewSize.innerText = '0';
|
||||
deckListTestButton.disabled = true;
|
||||
deckExportButton.disabled = true;
|
||||
deckCopyButton.disabled = true;
|
||||
deckEditButton.disabled = true;
|
||||
deckRenameButton.disabled = true;
|
||||
deckCopyButton.disabled = true;
|
||||
deckDeleteButton.disabled = true;
|
||||
deckListPage.classList.remove('showingDeck');
|
||||
}
|
||||
|
||||
deckExportButton.addEventListener('click', () => {
|
||||
@@ -244,19 +343,10 @@ deckDeleteButton.addEventListener('click', () => {
|
||||
if (selectedDeck == null) return;
|
||||
if (!confirm(`Are you sure you want to delete ${selectedDeck.name}?`)) return;
|
||||
|
||||
for (const el of deckButtons.entries) {
|
||||
if (el.value == selectedDeck) {
|
||||
deckList.removeChild(el.button.buttonElement);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const index = decks.indexOf(selectedDeck);
|
||||
if (index >= 0) decks.splice(index, 1);
|
||||
deckButtons.entries.splice(index, 1);
|
||||
deckButtons.deselect();
|
||||
selectedDeck = null;
|
||||
deckEditorDeckViewSection.hidden = true;
|
||||
deckButtons.removeAt(index);
|
||||
deselectDeck();
|
||||
saveDecks();
|
||||
});
|
||||
|
||||
|
||||
@@ -195,6 +195,8 @@ document.getElementById('preGameBackButton')!.addEventListener('click', e => {
|
||||
backPreGameForm(true);
|
||||
})
|
||||
|
||||
preGameDeckEditorButton.addEventListener('touchstart', deckListEnableTouchMode);
|
||||
|
||||
preGameDeckEditorButton.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
showDeckList();
|
||||
|
||||
Reference in New Issue
Block a user