From f4738729a8600de547cd1fc14c3fe1aff0c452df Mon Sep 17 00:00:00 2001 From: Adrian Klingen Date: Tue, 4 Jun 2024 10:41:11 +0200 Subject: [PATCH 1/3] Add APL reset button --- .../{rotation_tab.ts => rotation_tab.tsx} | 88 +++++++++++-------- ui/core/components/input.tsx | 4 +- ui/core/individual_sim_ui.tsx | 11 +++ 3 files changed, 64 insertions(+), 39 deletions(-) rename ui/core/components/individual_sim_ui/{rotation_tab.ts => rotation_tab.tsx} (73%) diff --git a/ui/core/components/individual_sim_ui/rotation_tab.ts b/ui/core/components/individual_sim_ui/rotation_tab.tsx similarity index 73% rename from ui/core/components/individual_sim_ui/rotation_tab.ts rename to ui/core/components/individual_sim_ui/rotation_tab.tsx index 2888a07b4a..c4f90c4596 100644 --- a/ui/core/components/individual_sim_ui/rotation_tab.ts +++ b/ui/core/components/individual_sim_ui/rotation_tab.tsx @@ -1,4 +1,6 @@ -import * as Tooltips from '../../constants/tooltips.js'; +import { ref } from 'tsx-vanilla'; + +import * as Tooltips from '../../constants/tooltips'; import { IndividualSimUI, InputSection } from '../../individual_sim_ui'; import { Player } from '../../player'; import { APLRotation, APLRotation_Type as APLRotationType } from '../../proto/apl'; @@ -7,7 +9,7 @@ import { EventID, TypedEvent } from '../../typed_event'; import { BooleanPicker } from '../boolean_picker'; import { ContentBlock } from '../content_block'; import { EnumPicker } from '../enum_picker'; -import * as IconInputs from '../icon_inputs.js'; +import * as IconInputs from '../icon_inputs'; import { Input } from '../input'; import { NumberPicker } from '../number_picker'; import { SavedDataManager } from '../saved_data_manager'; @@ -25,11 +27,9 @@ export class RotationTab extends SimTab { super(parentElem, simUI, { identifier: 'rotation-tab', title: 'Rotation' }); this.simUI = simUI; - this.leftPanel = document.createElement('div'); - this.leftPanel.classList.add('rotation-tab-left', 'tab-panel-left'); + this.leftPanel = (
) as HTMLElement; - this.rightPanel = document.createElement('div'); - this.rightPanel.classList.add('rotation-tab-right', 'tab-panel-right'); + this.rightPanel = (
) as HTMLElement; this.contentContainer.appendChild(this.leftPanel); this.contentContainer.appendChild(this.rightPanel); @@ -51,27 +51,50 @@ export class RotationTab extends SimTab { } private updateSections() { - this.rootElem.classList.remove('rotation-type-auto'); - this.rootElem.classList.remove('rotation-type-simple'); - this.rootElem.classList.remove('rotation-type-apl'); - this.rootElem.classList.remove('rotation-type-legacy'); - - const rotType = this.simUI.player.getRotationType(); - if (rotType == APLRotationType.TypeAuto) { - this.rootElem.classList.add('rotation-type-auto'); - } else if (rotType == APLRotationType.TypeSimple) { - this.rootElem.classList.add('rotation-type-simple'); - } else if (rotType == APLRotationType.TypeAPL) { - this.rootElem.classList.add('rotation-type-apl'); + this.rootElem.classList.remove('rotation-type-auto', 'rotation-type-simple', 'rotation-type-apl', 'rotation-type-legacy'); + + const rotationType = this.simUI.player.getRotationType(); + let rotationClass = ''; + switch (rotationType) { + case APLRotationType.TypeAuto: + rotationClass = 'rotation-type-auto'; + break; + case APLRotationType.TypeSimple: + rotationClass = 'rotation-type-simple'; + break; + case APLRotationType.TypeAPL: + rotationClass = 'rotation-type-apl'; + break; } + + this.rootElem.classList.add(rotationClass); } private buildHeader() { - const header = document.createElement('div'); - header.classList.add('rotation-tab-header'); - this.leftPanel.appendChild(header); + const headerRef = ref(); + const resetButtonRef = ref(); + const rotationTypeSelectRef = ref(); + this.leftPanel.appendChild( +
+
+ +
, + ); + + resetButtonRef.value!.addEventListener('click', () => { + this.simUI.applyEmptyAplRotation(TypedEvent.nextEventID()); + }); + + this.simUI.player.rotationChangeEmitter.on(() => { + const type = this.simUI.player.getRotationType(); + resetButtonRef.value?.classList[type === APLRotationType.TypeAPL ? 'remove' : 'add']('hide'); + }); - new EnumPicker(header, this.simUI.player, { + new EnumPicker(rotationTypeSelectRef.value!, this.simUI.player, { + extraCssClasses: ['w-auto'], id: 'rotation-tab-rotation-type', label: 'Rotation Type', labelTooltip: 'Which set of options to use for specifying the rotation.', @@ -96,17 +119,14 @@ export class RotationTab extends SimTab { } private buildAutoContent() { - const content = document.createElement('div'); - content.classList.add('rotation-tab-auto'); - this.leftPanel.appendChild(content); + this.leftPanel.appendChild(
); } private buildAplContent() { - const content = document.createElement('div'); - content.classList.add('rotation-tab-apl'); - this.leftPanel.appendChild(content); + const contentRef = ref(); + this.leftPanel.appendChild(
); - new APLRotationPicker(content, this.simUI, this.simUI.player); + new APLRotationPicker(contentRef.value!, this.simUI, this.simUI.player); } private buildSimpleContent() { @@ -133,11 +153,6 @@ export class RotationTab extends SimTab { } this.configureInputSection(contentBlock.bodyElement, this.simUI.individualConfig.rotationInputs); - - contentBlock.bodyElement.querySelectorAll('.input-root').forEach(elem => { - elem.classList.add('input-inline'); - }); - const cooldownsContentBlock = new ContentBlock(this.leftPanel, 'cooldown-settings', { header: { title: 'Cooldowns', tooltip: Tooltips.COOLDOWNS_SECTION }, }); @@ -148,10 +163,11 @@ export class RotationTab extends SimTab { private configureInputSection(sectionElem: HTMLElement, sectionConfig: InputSection) { sectionConfig.inputs.forEach(inputConfig => { + inputConfig.extraCssClasses = [...(inputConfig.extraCssClasses || []), 'input-inline']; if (inputConfig.type == 'number') { new NumberPicker(sectionElem, this.simUI.player, inputConfig); } else if (inputConfig.type == 'boolean') { - new BooleanPicker(sectionElem, this.simUI.player, { ...inputConfig }); + new BooleanPicker(sectionElem, this.simUI.player, inputConfig); } else if (inputConfig.type == 'enum') { new EnumPicker(sectionElem, this.simUI.player, inputConfig); } @@ -159,7 +175,7 @@ export class RotationTab extends SimTab { } private configureIconSection(sectionElem: HTMLElement, iconPickers: Array, adjustColumns?: boolean) { - if (iconPickers.length == 0) { + if (!iconPickers.length) { sectionElem.classList.add('hide'); } else if (adjustColumns) { if (iconPickers.length <= 4) { diff --git a/ui/core/components/input.tsx b/ui/core/components/input.tsx index 7628692fa7..31cd1b84c9 100644 --- a/ui/core/components/input.tsx +++ b/ui/core/components/input.tsx @@ -155,8 +155,6 @@ export abstract class Input extends Component { } static newGroupContainer(): HTMLElement { - const group = document.createElement('div'); - group.classList.add('picker-group'); - return group; + return (
) as HTMLElement; } } diff --git a/ui/core/individual_sim_ui.tsx b/ui/core/individual_sim_ui.tsx index 2b49515278..1ea2fdfe73 100644 --- a/ui/core/individual_sim_ui.tsx +++ b/ui/core/individual_sim_ui.tsx @@ -471,6 +471,17 @@ export abstract class IndividualSimUI extends SimUI { }); } + applyEmptyAplRotation(eventID: EventID) { + TypedEvent.freezeAllAndDo(() => { + this.player.setAplRotation( + eventID, + APLRotation.create({ + type: APLRotationType.TypeAPL, + }), + ); + }); + } + applyDefaults(eventID: EventID) { TypedEvent.freezeAllAndDo(() => { const tankSpec = this.player.getPlayerSpec().isTankSpec; From dbf639eecc222212899f1e73171900b51fc4067c Mon Sep 17 00:00:00 2001 From: Adrian Klingen Date: Tue, 4 Jun 2024 10:41:11 +0200 Subject: [PATCH 2/3] Add APL reset button --- .../{rotation_tab.ts => rotation_tab.tsx} | 91 +++++++++++-------- ui/core/components/input.tsx | 4 +- ui/core/individual_sim_ui.tsx | 11 +++ 3 files changed, 65 insertions(+), 41 deletions(-) rename ui/core/components/individual_sim_ui/{rotation_tab.ts => rotation_tab.tsx} (73%) diff --git a/ui/core/components/individual_sim_ui/rotation_tab.ts b/ui/core/components/individual_sim_ui/rotation_tab.tsx similarity index 73% rename from ui/core/components/individual_sim_ui/rotation_tab.ts rename to ui/core/components/individual_sim_ui/rotation_tab.tsx index 2888a07b4a..dc84de8637 100644 --- a/ui/core/components/individual_sim_ui/rotation_tab.ts +++ b/ui/core/components/individual_sim_ui/rotation_tab.tsx @@ -1,4 +1,6 @@ -import * as Tooltips from '../../constants/tooltips.js'; +import { ref } from 'tsx-vanilla'; + +import * as Tooltips from '../../constants/tooltips'; import { IndividualSimUI, InputSection } from '../../individual_sim_ui'; import { Player } from '../../player'; import { APLRotation, APLRotation_Type as APLRotationType } from '../../proto/apl'; @@ -7,7 +9,7 @@ import { EventID, TypedEvent } from '../../typed_event'; import { BooleanPicker } from '../boolean_picker'; import { ContentBlock } from '../content_block'; import { EnumPicker } from '../enum_picker'; -import * as IconInputs from '../icon_inputs.js'; +import * as IconInputs from '../icon_inputs'; import { Input } from '../input'; import { NumberPicker } from '../number_picker'; import { SavedDataManager } from '../saved_data_manager'; @@ -25,11 +27,9 @@ export class RotationTab extends SimTab { super(parentElem, simUI, { identifier: 'rotation-tab', title: 'Rotation' }); this.simUI = simUI; - this.leftPanel = document.createElement('div'); - this.leftPanel.classList.add('rotation-tab-left', 'tab-panel-left'); + this.leftPanel = (
) as HTMLElement; - this.rightPanel = document.createElement('div'); - this.rightPanel.classList.add('rotation-tab-right', 'tab-panel-right'); + this.rightPanel = (
) as HTMLElement; this.contentContainer.appendChild(this.leftPanel); this.contentContainer.appendChild(this.rightPanel); @@ -51,27 +51,50 @@ export class RotationTab extends SimTab { } private updateSections() { - this.rootElem.classList.remove('rotation-type-auto'); - this.rootElem.classList.remove('rotation-type-simple'); - this.rootElem.classList.remove('rotation-type-apl'); - this.rootElem.classList.remove('rotation-type-legacy'); - - const rotType = this.simUI.player.getRotationType(); - if (rotType == APLRotationType.TypeAuto) { - this.rootElem.classList.add('rotation-type-auto'); - } else if (rotType == APLRotationType.TypeSimple) { - this.rootElem.classList.add('rotation-type-simple'); - } else if (rotType == APLRotationType.TypeAPL) { - this.rootElem.classList.add('rotation-type-apl'); + this.rootElem.classList.remove('rotation-type-auto', 'rotation-type-simple', 'rotation-type-apl', 'rotation-type-legacy'); + + const rotationType = this.simUI.player.getRotationType(); + let rotationClass = ''; + switch (rotationType) { + case APLRotationType.TypeAuto: + rotationClass = 'rotation-type-auto'; + break; + case APLRotationType.TypeSimple: + rotationClass = 'rotation-type-simple'; + break; + case APLRotationType.TypeAPL: + rotationClass = 'rotation-type-apl'; + break; } + + this.rootElem.classList.add(rotationClass); } private buildHeader() { - const header = document.createElement('div'); - header.classList.add('rotation-tab-header'); - this.leftPanel.appendChild(header); + const headerRef = ref(); + const resetButtonRef = ref(); + const rotationTypeSelectRef = ref(); + this.leftPanel.appendChild( +
+
+ +
, + ); + + resetButtonRef.value!.addEventListener('click', () => { + this.simUI.applyEmptyAplRotation(TypedEvent.nextEventID()); + }); + + this.simUI.player.rotationChangeEmitter.on(() => { + const type = this.simUI.player.getRotationType(); + resetButtonRef.value?.classList[type === APLRotationType.TypeAPL ? 'remove' : 'add']('hide'); + }); - new EnumPicker(header, this.simUI.player, { + new EnumPicker(rotationTypeSelectRef.value!, this.simUI.player, { + extraCssClasses: ['w-auto'], id: 'rotation-tab-rotation-type', label: 'Rotation Type', labelTooltip: 'Which set of options to use for specifying the rotation.', @@ -96,17 +119,14 @@ export class RotationTab extends SimTab { } private buildAutoContent() { - const content = document.createElement('div'); - content.classList.add('rotation-tab-auto'); - this.leftPanel.appendChild(content); + this.leftPanel.appendChild(
); } private buildAplContent() { - const content = document.createElement('div'); - content.classList.add('rotation-tab-apl'); - this.leftPanel.appendChild(content); + const contentRef = ref(); + this.leftPanel.appendChild(
); - new APLRotationPicker(content, this.simUI, this.simUI.player); + new APLRotationPicker(contentRef.value!, this.simUI, this.simUI.player); } private buildSimpleContent() { @@ -133,11 +153,6 @@ export class RotationTab extends SimTab { } this.configureInputSection(contentBlock.bodyElement, this.simUI.individualConfig.rotationInputs); - - contentBlock.bodyElement.querySelectorAll('.input-root').forEach(elem => { - elem.classList.add('input-inline'); - }); - const cooldownsContentBlock = new ContentBlock(this.leftPanel, 'cooldown-settings', { header: { title: 'Cooldowns', tooltip: Tooltips.COOLDOWNS_SECTION }, }); @@ -149,17 +164,17 @@ export class RotationTab extends SimTab { private configureInputSection(sectionElem: HTMLElement, sectionConfig: InputSection) { sectionConfig.inputs.forEach(inputConfig => { if (inputConfig.type == 'number') { - new NumberPicker(sectionElem, this.simUI.player, inputConfig); + new NumberPicker(sectionElem, this.simUI.player, { ...inputConfig, inline: true }); } else if (inputConfig.type == 'boolean') { - new BooleanPicker(sectionElem, this.simUI.player, { ...inputConfig }); + new BooleanPicker(sectionElem, this.simUI.player, { ...inputConfig, inline: true }); } else if (inputConfig.type == 'enum') { - new EnumPicker(sectionElem, this.simUI.player, inputConfig); + new EnumPicker(sectionElem, this.simUI.player, { ...inputConfig, inline: true }); } }); } private configureIconSection(sectionElem: HTMLElement, iconPickers: Array, adjustColumns?: boolean) { - if (iconPickers.length == 0) { + if (!iconPickers.length) { sectionElem.classList.add('hide'); } else if (adjustColumns) { if (iconPickers.length <= 4) { diff --git a/ui/core/components/input.tsx b/ui/core/components/input.tsx index 7628692fa7..31cd1b84c9 100644 --- a/ui/core/components/input.tsx +++ b/ui/core/components/input.tsx @@ -155,8 +155,6 @@ export abstract class Input extends Component { } static newGroupContainer(): HTMLElement { - const group = document.createElement('div'); - group.classList.add('picker-group'); - return group; + return (
) as HTMLElement; } } diff --git a/ui/core/individual_sim_ui.tsx b/ui/core/individual_sim_ui.tsx index 2b49515278..1ea2fdfe73 100644 --- a/ui/core/individual_sim_ui.tsx +++ b/ui/core/individual_sim_ui.tsx @@ -471,6 +471,17 @@ export abstract class IndividualSimUI extends SimUI { }); } + applyEmptyAplRotation(eventID: EventID) { + TypedEvent.freezeAllAndDo(() => { + this.player.setAplRotation( + eventID, + APLRotation.create({ + type: APLRotationType.TypeAPL, + }), + ); + }); + } + applyDefaults(eventID: EventID) { TypedEvent.freezeAllAndDo(() => { const tankSpec = this.player.getPlayerSpec().isTankSpec; From 090058224c70c2cf57465de696b4f43bb83a1c33 Mon Sep 17 00:00:00 2001 From: Adrian Klingen Date: Tue, 4 Jun 2024 19:39:30 +0200 Subject: [PATCH 3/3] Saved data manger to tsx --- ...data_manager.ts => saved_data_manager.tsx} | 94 +++++++++---------- 1 file changed, 45 insertions(+), 49 deletions(-) rename ui/core/components/{saved_data_manager.ts => saved_data_manager.tsx} (70%) diff --git a/ui/core/components/saved_data_manager.ts b/ui/core/components/saved_data_manager.tsx similarity index 70% rename from ui/core/components/saved_data_manager.ts rename to ui/core/components/saved_data_manager.tsx index 8c1a210611..b4a1daaf86 100644 --- a/ui/core/components/saved_data_manager.ts +++ b/ui/core/components/saved_data_manager.tsx @@ -1,7 +1,8 @@ import tippy from 'tippy.js'; +import { ref } from 'tsx-vanilla'; -import { Component } from '../components/component.js'; -import { EventID, TypedEvent } from '../typed_event.js'; +import { EventID, TypedEvent } from '../typed_event'; +import { Component } from './component'; import { ContentBlock, ContentBlockHeaderConfig } from './content_block'; export type SavedDataManagerConfig = { @@ -44,7 +45,7 @@ export class SavedDataManager extends Component { private readonly savedDataDiv: HTMLElement; private readonly presetDataDiv: HTMLElement; private readonly customDataDiv: HTMLElement; - private readonly saveInput?: HTMLInputElement; + private saveInput?: HTMLInputElement; private frozen: boolean; @@ -59,19 +60,22 @@ export class SavedDataManager extends Component { const contentBlock = new ContentBlock(this.rootElem, 'saved-data', { header: config.header }); - contentBlock.bodyElement.innerHTML = ` -
-
-
-
- `; - this.savedDataDiv = contentBlock.bodyElement.querySelector('.saved-data-container') as HTMLElement; - this.presetDataDiv = contentBlock.bodyElement.querySelector('.saved-data-presets') as HTMLElement; - this.customDataDiv = contentBlock.bodyElement.querySelector('.saved-data-custom') as HTMLElement; + const savedDataRef = ref(); + const presetDataRef = ref(); + const customDataRef = ref(); + contentBlock.bodyElement.replaceChildren( +
+
+
+
, + ); + + this.savedDataDiv = savedDataRef.value!; + this.presetDataDiv = presetDataRef.value!; + this.customDataDiv = customDataRef.value!; if (!config.presetsOnly) { contentBlock.bodyElement.appendChild(this.buildCreateContainer()); - this.saveInput = contentBlock.bodyElement.querySelector('.saved-data-save-input') as HTMLInputElement; } } @@ -96,38 +100,29 @@ export class SavedDataManager extends Component { } private makeSavedData(config: SavedDataConfig): SavedData { - const dataElemFragment = document.createElement('fragment'); - dataElemFragment.innerHTML = ` -
- ${config.name} + const deleteButtonRef = ref(); + const dataElem = ( +
+ + {config.name} + + {!config.isPreset && ( + + + + )}
- `; + ) as HTMLElement; - const dataElem = dataElemFragment.children[0] as HTMLElement; dataElem.addEventListener('click', () => { this.config.setData(TypedEvent.nextEventID(), this.modObject, config.data); if (this.saveInput) this.saveInput.value = config.name; }); - if (!config.isPreset) { - const deleteFragment = document.createElement('fragment'); - deleteFragment.innerHTML = ` - - - - `; - - const deleteButton = deleteFragment.children[0] as HTMLElement; - dataElem.appendChild(deleteButton); - - const tooltip = tippy(deleteButton, { content: `Delete saved ${this.config.label}` }); - - deleteButton.addEventListener('click', event => { + if (!config.isPreset && deleteButtonRef.value) { + const tooltip = tippy(deleteButtonRef.value, { content: `Delete saved ${this.config.label}` }); + deleteButtonRef.value.addEventListener('click', event => { event.stopPropagation(); const shouldDelete = confirm(`Delete saved ${this.config.label} '${config.name}'?`); if (!shouldDelete) return; @@ -216,18 +211,20 @@ export class SavedDataManager extends Component { } private buildCreateContainer(): HTMLElement { - const savedDataCreateFragment = document.createElement('fragment'); - savedDataCreateFragment.innerHTML = ` -
- - - + const saveButtonRef = ref(); + const saveInputRef = ref(); + const savedDataCreateFragment = ( +
+ + +
- `; + ) as HTMLElement; - const saveButton = savedDataCreateFragment.querySelector('.saved-data-save-button') as HTMLButtonElement; - - saveButton.addEventListener('click', () => { + this.saveInput = saveInputRef.value!; + saveButtonRef.value?.addEventListener('click', () => { if (this.frozen) return; const newName = this.saveInput?.value; @@ -240,7 +237,6 @@ export class SavedDataManager extends Component { alert(`${this.config.label} with name ${newName} already exists.`); return; } - this.addSavedData({ name: newName, data: this.config.getData(this.modObject), @@ -248,6 +244,6 @@ export class SavedDataManager extends Component { this.saveUserData(); }); - return savedDataCreateFragment.children[0] as HTMLElement; + return savedDataCreateFragment; } }