Add BaseTablist base class with keyboard navigation
This commit is contained in:
@@ -0,0 +1,177 @@
|
|||||||
|
/* The Mia! Accounting Project
|
||||||
|
* base-tablist.js: The JavaScript for base abstract tablist
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Copyright (c) 2026 imacat.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Author: imacat@mail.imacat.idv.tw (imacat)
|
||||||
|
* First written: 2026/4/16
|
||||||
|
*/
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base abstract tablist.
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
* @template {BaseTab} T
|
||||||
|
*/
|
||||||
|
class BaseTablist {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The tabs.
|
||||||
|
* @type {T[]}
|
||||||
|
*/
|
||||||
|
tabs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current tab.
|
||||||
|
* @type {T}
|
||||||
|
*/
|
||||||
|
currentTab;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new base abstract tablist.
|
||||||
|
*
|
||||||
|
* @param tablist {HTMLUListElement} the tab list
|
||||||
|
*/
|
||||||
|
constructor(tablist) {
|
||||||
|
tablist.onkeydown = this.onTabKeyDown.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actions when keys are pressed on the tabs.
|
||||||
|
*
|
||||||
|
* @param event {KeyboardEvent} the key event
|
||||||
|
*/
|
||||||
|
onTabKeyDown(event) {
|
||||||
|
const currentIndex = this.tabs.indexOf(this.currentTab);
|
||||||
|
if (currentIndex === -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let newIndex = currentIndex;
|
||||||
|
switch (event.key) {
|
||||||
|
case "ArrowRight":
|
||||||
|
newIndex = (newIndex + 1) % this.tabs.length;
|
||||||
|
break;
|
||||||
|
case "ArrowLeft":
|
||||||
|
newIndex = (newIndex - 1 + this.tabs.length) % this.tabs.length;
|
||||||
|
break;
|
||||||
|
case "Home":
|
||||||
|
newIndex = 0;
|
||||||
|
break;
|
||||||
|
case "End":
|
||||||
|
newIndex = this.tabs.length - 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
this.tabs[newIndex].focus();
|
||||||
|
this.onTabFocus(this.tabs[newIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actions when a tab is focused.
|
||||||
|
*
|
||||||
|
* @param tab {T} the tab
|
||||||
|
*/
|
||||||
|
onTabFocus(tab) { /* Do nothing */ }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switches to a tab.
|
||||||
|
*
|
||||||
|
* @param tab {T} the tab
|
||||||
|
*/
|
||||||
|
switchTo(tab) {
|
||||||
|
this.tabs.forEach(t => t.setActive(t === tab));
|
||||||
|
this.currentTab = tab;
|
||||||
|
this.currentTab.onActivated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base abstract tab.
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
*/
|
||||||
|
class BaseTab {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The tab element.
|
||||||
|
* @type {HTMLButtonElement}
|
||||||
|
*/
|
||||||
|
#tab;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The panel element.
|
||||||
|
* @type {HTMLDivElement}
|
||||||
|
*/
|
||||||
|
#panel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new base abstract tab.
|
||||||
|
*
|
||||||
|
* @param tab {HTMLButtonElement} The tab element.
|
||||||
|
* @param panel {HTMLDivElement} The panel element.
|
||||||
|
* @param switchTo {function(BaseTab): void} The function to switch to the tab.
|
||||||
|
*/
|
||||||
|
constructor(tab, panel, switchTo) {
|
||||||
|
this.#tab = tab;
|
||||||
|
this.#panel = panel;
|
||||||
|
this.#tab.onclick = () => switchTo(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the active state of the tab.
|
||||||
|
*
|
||||||
|
* @param isActive {boolean} true if the tab is active, false otherwise
|
||||||
|
*/
|
||||||
|
setActive(isActive) {
|
||||||
|
if (isActive) {
|
||||||
|
this.#tab.classList.add("active");
|
||||||
|
this.#tab.tabIndex = 0;
|
||||||
|
this.#tab.ariaSelected = "true";
|
||||||
|
this.#panel.classList.remove("d-none");
|
||||||
|
} else {
|
||||||
|
this.#tab.classList.remove("active");
|
||||||
|
this.#tab.tabIndex = -1;
|
||||||
|
this.#tab.ariaSelected = "false";
|
||||||
|
this.#panel.classList.add("d-none");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the tab is active.
|
||||||
|
*
|
||||||
|
* @returns {boolean} true if the tab is active, false otherwise
|
||||||
|
*/
|
||||||
|
isActive() {
|
||||||
|
return this.#tab.classList.contains("active");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actions when the tab is activated.
|
||||||
|
*/
|
||||||
|
onActivated() { /* Do nothing */ }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Focuses the tab.
|
||||||
|
*/
|
||||||
|
focus() {
|
||||||
|
this.#tab.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,8 +25,9 @@
|
|||||||
/**
|
/**
|
||||||
* A description editor.
|
* A description editor.
|
||||||
*
|
*
|
||||||
|
* @extends {BaseTablist<BaseDescriptionEditorTab>}
|
||||||
*/
|
*/
|
||||||
class DescriptionEditor {
|
class DescriptionEditor extends BaseTablist {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The line item editor
|
* The line item editor
|
||||||
@@ -58,12 +59,6 @@ class DescriptionEditor {
|
|||||||
*/
|
*/
|
||||||
debitCredit;
|
debitCredit;
|
||||||
|
|
||||||
/**
|
|
||||||
* The current tab
|
|
||||||
* @type {DescriptionEditorTabPlane}
|
|
||||||
*/
|
|
||||||
currentTab;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The description input
|
* The description input
|
||||||
* @type {HTMLInputElement}
|
* @type {HTMLInputElement}
|
||||||
@@ -125,10 +120,10 @@ class DescriptionEditor {
|
|||||||
selectedAccount = null;
|
selectedAccount = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tab planes
|
* The tabs by their ID.
|
||||||
* @type {{general: DescriptionEditorGeneralTagTab, travel: DescriptionEditorGeneralTripTab, bus: DescriptionEditorBusTripTab, recurring: DescriptionEditorRecurringTab, annotation: DescriptionEditorAnnotationTab}}
|
* @type {DescriptionEditorTabFactory}
|
||||||
*/
|
*/
|
||||||
tabPlanes = {};
|
#tabsByID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a description editor.
|
* Constructs a description editor.
|
||||||
@@ -137,23 +132,23 @@ class DescriptionEditor {
|
|||||||
* @param debitCredit {string} either "debit" or "credit"
|
* @param debitCredit {string} either "debit" or "credit"
|
||||||
*/
|
*/
|
||||||
constructor(lineItemEditor, debitCredit) {
|
constructor(lineItemEditor, debitCredit) {
|
||||||
|
const prefix = `accounting-description-editor-${debitCredit}`;
|
||||||
|
super(document.getElementById(`${prefix}-tab-list`));
|
||||||
|
this.prefix = prefix;
|
||||||
this.lineItemEditor = lineItemEditor;
|
this.lineItemEditor = lineItemEditor;
|
||||||
this.debitCredit = debitCredit;
|
this.debitCredit = debitCredit;
|
||||||
this.prefix = `accounting-description-editor-${debitCredit}`;
|
this.#form = document.getElementById(prefix);
|
||||||
this.#form = document.getElementById(this.prefix);
|
this.#modal = document.getElementById(`${prefix}-modal`);
|
||||||
this.#modal = document.getElementById(`${this.prefix}-modal`);
|
this.#descriptionInput = document.getElementById(`${prefix}-description`);
|
||||||
this.#descriptionInput = document.getElementById(`${this.prefix}-description`);
|
this.#offsetButton = document.getElementById(`${prefix}-offset`);
|
||||||
this.#offsetButton = document.getElementById(`${this.prefix}-offset`);
|
this.number = document.getElementById(`${prefix}-annotation-number`);
|
||||||
this.number = document.getElementById(`${this.prefix}-annotation-number`);
|
this.note = document.getElementById(`${prefix}-annotation-note`);
|
||||||
this.note = document.getElementById(`${this.prefix}-annotation-note`);
|
this.#confirmedAccountPlaceholder = new DescriptionEditorConfirmedAccount(this, document.getElementById(`${prefix}-account-confirmed`));
|
||||||
this.#confirmedAccountPlaceholder = new DescriptionEditorConfirmedAccount(this, document.getElementById(`${this.prefix}-account-confirmed`));
|
this.#allSuggestedAccounts = Array.from(document.getElementsByClassName(`${prefix}-account`)).map((button) => new DescriptionEditorSuggestedAccount(this, button));
|
||||||
this.#allSuggestedAccounts = Array.from(document.getElementsByClassName(`${this.prefix}-account`)).map((button) => new DescriptionEditorSuggestedAccount(this, button));
|
|
||||||
|
|
||||||
for (const cls of [DescriptionEditorGeneralTagTab, DescriptionEditorGeneralTripTab, DescriptionEditorBusTripTab, DescriptionEditorRecurringTab, DescriptionEditorAnnotationTab]) {
|
this.#tabsByID = new DescriptionEditorTabFactory(this);
|
||||||
const tab = new cls(this);
|
this.tabs = [this.#tabsByID.general, this.#tabsByID.travel, this.#tabsByID.bus, this.#tabsByID.recurring, this.#tabsByID.annotation];
|
||||||
this.tabPlanes[tab.tabId()] = tab;
|
this.currentTab = this.tabs[0];
|
||||||
}
|
|
||||||
this.currentTab = this.tabPlanes.general;
|
|
||||||
this.#descriptionInput.onchange = () => this.#onDescriptionChange();
|
this.#descriptionInput.onchange = () => this.#onDescriptionChange();
|
||||||
this.#offsetButton.onclick = () => this.lineItemEditor.originalLineItemSelector.onOpen();
|
this.#offsetButton.onclick = () => this.lineItemEditor.originalLineItemSelector.onOpen();
|
||||||
this.#form.onsubmit = () => {
|
this.#form.onsubmit = () => {
|
||||||
@@ -199,26 +194,26 @@ class DescriptionEditor {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#onDescriptionChange() {
|
#onDescriptionChange() {
|
||||||
this.#resetTabPlanes();
|
this.#resetTabs();
|
||||||
this.selectedAccount = null;
|
this.selectedAccount = null;
|
||||||
this.description = this.description.trim();
|
this.description = this.description.trim();
|
||||||
for (const tabPlane of [this.tabPlanes.recurring, this.tabPlanes.bus, this.tabPlanes.travel, this.tabPlanes.general]) {
|
for (const tab of [this.#tabsByID.recurring, this.#tabsByID.bus, this.#tabsByID.travel, this.#tabsByID.general]) {
|
||||||
if (tabPlane.populate()) {
|
if (tab.populate()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.tabPlanes.annotation.populate();
|
this.#tabsByID.annotation.populate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the tab planes.
|
* Resets the tabs.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#resetTabPlanes() {
|
#resetTabs() {
|
||||||
for (const tabPlane of Object.values(this.tabPlanes)) {
|
for (const tab of this.tabs) {
|
||||||
tabPlane.reset();
|
tab.reset();
|
||||||
}
|
}
|
||||||
this.tabPlanes.general.switchToMe();
|
this.switchTo(this.tabs[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -463,12 +458,63 @@ class DescriptionEditorConfirmedAccount extends DescriptionEditorAccount {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A tab plane.
|
* The tab factory.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
class DescriptionEditorTabFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The general tag tab
|
||||||
|
* @type {GeneralTagTab}
|
||||||
|
*/
|
||||||
|
general;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The general trip tab
|
||||||
|
* @type {GeneralTripTab}
|
||||||
|
*/
|
||||||
|
travel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bus trip tab
|
||||||
|
* @type {BusTripTab}
|
||||||
|
*/
|
||||||
|
bus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The recurring transactions tab
|
||||||
|
* @type {RecurringTab}
|
||||||
|
*/
|
||||||
|
recurring;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The annotation tab
|
||||||
|
* @type {AnnotationTab}
|
||||||
|
*/
|
||||||
|
annotation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs the tab factory
|
||||||
|
*
|
||||||
|
* @param editor {DescriptionEditor} the parent description editor
|
||||||
|
*/
|
||||||
|
constructor(editor) {
|
||||||
|
this.general = new GeneralTagTab(editor);
|
||||||
|
this.travel = new GeneralTripTab(editor);
|
||||||
|
this.bus = new BusTripTab(editor);
|
||||||
|
this.recurring = new RecurringTab(editor);
|
||||||
|
this.annotation = new AnnotationTab(editor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base abstract tab in the description editor.
|
||||||
*
|
*
|
||||||
* @abstract
|
* @abstract
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class DescriptionEditorTabPlane {
|
class BaseDescriptionEditorTab extends BaseTab {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The parent description editor
|
* The parent description editor
|
||||||
@@ -483,47 +529,29 @@ class DescriptionEditorTabPlane {
|
|||||||
prefix;
|
prefix;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tab
|
* Constructs a base abstract tab in the description editor.
|
||||||
* @type {HTMLSpanElement}
|
|
||||||
*/
|
|
||||||
#tab;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The page
|
|
||||||
* @type {HTMLDivElement}
|
|
||||||
*/
|
|
||||||
#page;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a tab plane.
|
|
||||||
*
|
*
|
||||||
|
* @param tabID {string} the tab ID
|
||||||
* @param editor {DescriptionEditor} the parent description editor
|
* @param editor {DescriptionEditor} the parent description editor
|
||||||
*/
|
*/
|
||||||
constructor(editor) {
|
constructor(tabID, editor) {
|
||||||
|
const prefix = `${editor.prefix}-${tabID}`;
|
||||||
|
const tab = document.getElementById(`${prefix}-tab`);
|
||||||
|
const panel = document.getElementById(`${prefix}-panel`);
|
||||||
|
super(tab, panel, editor.switchTo.bind(editor));
|
||||||
this.editor = editor;
|
this.editor = editor;
|
||||||
this.prefix = `${this.editor.prefix}-${this.tabId()}`;
|
this.prefix = prefix;
|
||||||
this.#tab = document.getElementById(`${this.prefix}-tab`);
|
|
||||||
this.#page = document.getElementById(`${this.prefix}-page`);
|
|
||||||
this.#tab.onclick = () => this.switchToMe();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tab ID
|
* Resets the tab panel input.
|
||||||
*
|
|
||||||
* @return {string}
|
|
||||||
* @abstract
|
|
||||||
*/
|
|
||||||
tabId() { throw new Error("Method not implemented.") };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resets the tab plane input.
|
|
||||||
*
|
*
|
||||||
* @abstract
|
* @abstract
|
||||||
*/
|
*/
|
||||||
reset() { throw new Error("Method not implemented."); }
|
reset() { throw new Error("Method not implemented."); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populates the tab plane with the description input.
|
* Populates the tab panel with the description input.
|
||||||
*
|
*
|
||||||
* @return {boolean} true if the description input matches this tab, or false otherwise
|
* @return {boolean} true if the description input matches this tab, or false otherwise
|
||||||
* @abstract
|
* @abstract
|
||||||
@@ -531,39 +559,21 @@ class DescriptionEditorTabPlane {
|
|||||||
populate() { throw new Error("Method not implemented."); }
|
populate() { throw new Error("Method not implemented."); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the input in the tab plane.
|
* Validates the input in the tab panel.
|
||||||
*
|
*
|
||||||
* @return {boolean} true if valid, or false otherwise
|
* @return {boolean} true if valid, or false otherwise
|
||||||
* @abstract
|
* @abstract
|
||||||
*/
|
*/
|
||||||
validate() { throw new Error("Method not implemented."); }
|
validate() { throw new Error("Method not implemented."); }
|
||||||
|
|
||||||
/**
|
|
||||||
* Switches to the tab plane.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
switchToMe() {
|
|
||||||
for (const tabPlane of Object.values(this.editor.tabPlanes)) {
|
|
||||||
tabPlane.#tab.classList.remove("active")
|
|
||||||
tabPlane.#tab.ariaCurrent = "false";
|
|
||||||
tabPlane.#page.classList.add("d-none");
|
|
||||||
tabPlane.#page.ariaCurrent = "false";
|
|
||||||
}
|
|
||||||
this.#tab.classList.add("active");
|
|
||||||
this.#tab.ariaCurrent = "page";
|
|
||||||
this.#page.classList.remove("d-none");
|
|
||||||
this.#page.ariaCurrent = "page";
|
|
||||||
this.editor.currentTab = this;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A tag plane with selectable tags.
|
* The base abstract tab with selectable tags.
|
||||||
*
|
*
|
||||||
* @abstract
|
* @abstract
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class DescriptionEditorTagTabPlane extends DescriptionEditorTabPlane {
|
class BaseTagTab extends BaseDescriptionEditorTab {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tag input
|
* The tag input
|
||||||
@@ -581,20 +591,21 @@ class DescriptionEditorTagTabPlane extends DescriptionEditorTabPlane {
|
|||||||
* The tag buttons
|
* The tag buttons
|
||||||
* @type {HTMLButtonElement[]}
|
* @type {HTMLButtonElement[]}
|
||||||
*/
|
*/
|
||||||
tagButtons;
|
#tagButtons;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a tab plane.
|
* Constructs a base abstract tab with selectable tags.
|
||||||
*
|
*
|
||||||
|
* @param tabID {string} the tab ID
|
||||||
* @param editor {DescriptionEditor} the parent description editor
|
* @param editor {DescriptionEditor} the parent description editor
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
constructor(editor) {
|
constructor(tabID, editor) {
|
||||||
super(editor);
|
super(tabID, editor);
|
||||||
this.tag = document.getElementById(`${this.prefix}-tag`);
|
this.tag = document.getElementById(`${this.prefix}-tag`);
|
||||||
this.tagError = document.getElementById(`${this.prefix}-tag-error`);
|
this.tagError = document.getElementById(`${this.prefix}-tag-error`);
|
||||||
// noinspection JSValidateTypes
|
// noinspection JSValidateTypes
|
||||||
this.tagButtons = Array.from(document.getElementsByClassName(`${this.prefix}-btn-tag`));
|
this.#tagButtons = Array.from(document.getElementsByClassName(`${this.prefix}-btn-tag`));
|
||||||
this.initializeTagButtons();
|
this.initializeTagButtons();
|
||||||
this.tag.onchange = () => {
|
this.tag.onchange = () => {
|
||||||
this.onTagChange();
|
this.onTagChange();
|
||||||
@@ -609,7 +620,7 @@ class DescriptionEditorTagTabPlane extends DescriptionEditorTabPlane {
|
|||||||
onTagChange() {
|
onTagChange() {
|
||||||
this.tag.value = this.tag.value.trim();
|
this.tag.value = this.tag.value.trim();
|
||||||
let isMatched = false;
|
let isMatched = false;
|
||||||
for (const tagButton of this.tagButtons) {
|
for (const tagButton of this.#tagButtons) {
|
||||||
if (tagButton.dataset.value === this.tag.value) {
|
if (tagButton.dataset.value === this.tag.value) {
|
||||||
tagButton.classList.remove("btn-outline-primary");
|
tagButton.classList.remove("btn-outline-primary");
|
||||||
tagButton.classList.add("btn-primary");
|
tagButton.classList.add("btn-primary");
|
||||||
@@ -627,19 +638,18 @@ class DescriptionEditorTagTabPlane extends DescriptionEditorTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the description according to the input in the tab plane.
|
* Updates the description according to the input in the tab panel.
|
||||||
*
|
*
|
||||||
* @abstract
|
* @abstract
|
||||||
*/
|
*/
|
||||||
updateDescription() { throw new Error("Method not implemented."); }
|
updateDescription() { throw new Error("Method not implemented."); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Switches to the tab plane.
|
* @inheritDoc
|
||||||
*
|
* @override
|
||||||
*/
|
*/
|
||||||
switchToMe() {
|
onActivated() {
|
||||||
super.switchToMe();
|
for (const tagButton of this.#tagButtons) {
|
||||||
for (const tagButton of this.tagButtons) {
|
|
||||||
if (tagButton.classList.contains("btn-primary")) {
|
if (tagButton.classList.contains("btn-primary")) {
|
||||||
this.editor.updateCurrentSuggestedAccounts(tagButton);
|
this.editor.updateCurrentSuggestedAccounts(tagButton);
|
||||||
return;
|
return;
|
||||||
@@ -653,9 +663,9 @@ class DescriptionEditorTagTabPlane extends DescriptionEditorTabPlane {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
initializeTagButtons() {
|
initializeTagButtons() {
|
||||||
for (const tagButton of this.tagButtons) {
|
for (const tagButton of this.#tagButtons) {
|
||||||
tagButton.onclick = () => {
|
tagButton.onclick = () => {
|
||||||
for (const otherButton of this.tagButtons) {
|
for (const otherButton of this.#tagButtons) {
|
||||||
otherButton.classList.remove("btn-primary");
|
otherButton.classList.remove("btn-primary");
|
||||||
otherButton.classList.add("btn-outline-primary");
|
otherButton.classList.add("btn-outline-primary");
|
||||||
}
|
}
|
||||||
@@ -701,7 +711,7 @@ class DescriptionEditorTagTabPlane extends DescriptionEditorTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the tab plane input.
|
* Resets the tab panel input.
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
@@ -709,7 +719,7 @@ class DescriptionEditorTagTabPlane extends DescriptionEditorTabPlane {
|
|||||||
this.tag.value = "";
|
this.tag.value = "";
|
||||||
this.tag.classList.remove("is-invalid");
|
this.tag.classList.remove("is-invalid");
|
||||||
this.tagError.innerText = "";
|
this.tagError.innerText = "";
|
||||||
for (const tagButton of this.tagButtons) {
|
for (const tagButton of this.#tagButtons) {
|
||||||
tagButton.classList.remove("btn-primary");
|
tagButton.classList.remove("btn-primary");
|
||||||
tagButton.classList.add("btn-outline-primary");
|
tagButton.classList.add("btn-outline-primary");
|
||||||
}
|
}
|
||||||
@@ -717,24 +727,24 @@ class DescriptionEditorTagTabPlane extends DescriptionEditorTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The general tag tab plane.
|
* The general tag tab.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class DescriptionEditorGeneralTagTab extends DescriptionEditorTagTabPlane {
|
class GeneralTagTab extends BaseTagTab {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tab ID
|
* Constructs a general tag tab.
|
||||||
*
|
*
|
||||||
* @return {string}
|
* @param editor {DescriptionEditor} the parent description editor
|
||||||
* @abstract
|
* @override
|
||||||
*/
|
*/
|
||||||
tabId() {
|
constructor(editor) {
|
||||||
return "general";
|
super("general", editor);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the description according to the input in the tab plane.
|
* Updates the description according to the input in the tab panel.
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
@@ -749,7 +759,7 @@ class DescriptionEditorGeneralTagTab extends DescriptionEditorTagTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populates the tab plane with the description input.
|
* Populates the tab panel with the description input.
|
||||||
*
|
*
|
||||||
* @return {boolean} true if the description input matches this tab, or false otherwise
|
* @return {boolean} true if the description input matches this tab, or false otherwise
|
||||||
* @override
|
* @override
|
||||||
@@ -763,12 +773,12 @@ class DescriptionEditorGeneralTagTab extends DescriptionEditorTagTabPlane {
|
|||||||
this.tag.value = found[1];
|
this.tag.value = found[1];
|
||||||
this.onTagChange();
|
this.onTagChange();
|
||||||
}
|
}
|
||||||
this.switchToMe();
|
this.editor.switchTo(this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the input in the tab plane.
|
* Validates the input in the tab panel.
|
||||||
*
|
*
|
||||||
* @return {boolean} true if valid, or false otherwise
|
* @return {boolean} true if valid, or false otherwise
|
||||||
*/
|
*/
|
||||||
@@ -778,11 +788,11 @@ class DescriptionEditorGeneralTagTab extends DescriptionEditorTagTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The general trip tab plane.
|
* The general trip tab.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class DescriptionEditorGeneralTripTab extends DescriptionEditorTagTabPlane {
|
class GeneralTripTab extends BaseTagTab {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The origin
|
* The origin
|
||||||
@@ -815,13 +825,13 @@ class DescriptionEditorGeneralTripTab extends DescriptionEditorTagTabPlane {
|
|||||||
#directionButtons;
|
#directionButtons;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a tab plane.
|
* Constructs a general trip tab.
|
||||||
*
|
*
|
||||||
* @param editor {DescriptionEditor} the parent description editor
|
* @param editor {DescriptionEditor} the parent description editor
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
constructor(editor) {
|
constructor(editor) {
|
||||||
super(editor);
|
super("travel", editor);
|
||||||
this.#from = document.getElementById(`${this.prefix}-from`);
|
this.#from = document.getElementById(`${this.prefix}-from`);
|
||||||
this.#fromError = document.getElementById(`${this.prefix}-from-error`);
|
this.#fromError = document.getElementById(`${this.prefix}-from-error`);
|
||||||
this.#to = document.getElementById(`${this.prefix}-to`);
|
this.#to = document.getElementById(`${this.prefix}-to`);
|
||||||
@@ -852,17 +862,7 @@ class DescriptionEditorGeneralTripTab extends DescriptionEditorTagTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tab ID
|
* Updates the description according to the input in the tab panel.
|
||||||
*
|
|
||||||
* @return {string}
|
|
||||||
* @abstract
|
|
||||||
*/
|
|
||||||
tabId() {
|
|
||||||
return "travel";
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the description according to the input in the tab plane.
|
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
@@ -878,7 +878,7 @@ class DescriptionEditorGeneralTripTab extends DescriptionEditorTagTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the tab plane input.
|
* Resets the tab panel input.
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
@@ -902,7 +902,7 @@ class DescriptionEditorGeneralTripTab extends DescriptionEditorTagTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populates the tab plane with the description input.
|
* Populates the tab panel with the description input.
|
||||||
*
|
*
|
||||||
* @return {boolean} true if the description input matches this tab, or false otherwise
|
* @return {boolean} true if the description input matches this tab, or false otherwise
|
||||||
* @override
|
* @override
|
||||||
@@ -927,12 +927,12 @@ class DescriptionEditorGeneralTripTab extends DescriptionEditorTagTabPlane {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.#to.value = found[4];
|
this.#to.value = found[4];
|
||||||
this.switchToMe();
|
this.editor.switchTo(this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the input in the tab plane.
|
* Validates the input in the tab panel.
|
||||||
*
|
*
|
||||||
* @return {boolean} true if valid, or false otherwise
|
* @return {boolean} true if valid, or false otherwise
|
||||||
* @override
|
* @override
|
||||||
@@ -977,11 +977,11 @@ class DescriptionEditorGeneralTripTab extends DescriptionEditorTagTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The bus trip tab plane.
|
* The bus trip tab.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class DescriptionEditorBusTripTab extends DescriptionEditorTagTabPlane {
|
class BusTripTab extends BaseTagTab {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The route
|
* The route
|
||||||
@@ -1020,13 +1020,13 @@ class DescriptionEditorBusTripTab extends DescriptionEditorTagTabPlane {
|
|||||||
#toError;
|
#toError;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a tab plane.
|
* Constructs a bus trip tab.
|
||||||
*
|
*
|
||||||
* @param editor {DescriptionEditor} the parent description editor
|
* @param editor {DescriptionEditor} the parent description editor
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
constructor(editor) {
|
constructor(editor) {
|
||||||
super(editor);
|
super("bus", editor);
|
||||||
this.#route = document.getElementById(`${this.prefix}-route`);
|
this.#route = document.getElementById(`${this.prefix}-route`);
|
||||||
this.#routeError = document.getElementById(`${this.prefix}-route-error`);
|
this.#routeError = document.getElementById(`${this.prefix}-route-error`);
|
||||||
this.#from = document.getElementById(`${this.prefix}-from`);
|
this.#from = document.getElementById(`${this.prefix}-from`);
|
||||||
@@ -1051,17 +1051,7 @@ class DescriptionEditorBusTripTab extends DescriptionEditorTagTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tab ID
|
* Updates the description according to the input in the tab panel.
|
||||||
*
|
|
||||||
* @return {string}
|
|
||||||
* @abstract
|
|
||||||
*/
|
|
||||||
tabId() {
|
|
||||||
return "bus";
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the description according to the input in the tab plane.
|
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
@@ -1070,7 +1060,7 @@ class DescriptionEditorBusTripTab extends DescriptionEditorTagTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the tab plane input.
|
* Resets the tab panel input.
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
@@ -1088,7 +1078,7 @@ class DescriptionEditorBusTripTab extends DescriptionEditorTagTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populates the tab plane with the description input.
|
* Populates the tab panel with the description input.
|
||||||
*
|
*
|
||||||
* @return {boolean} true if the description input matches this tab, or false otherwise
|
* @return {boolean} true if the description input matches this tab, or false otherwise
|
||||||
* @override
|
* @override
|
||||||
@@ -1105,12 +1095,12 @@ class DescriptionEditorBusTripTab extends DescriptionEditorTagTabPlane {
|
|||||||
this.#route.value = found[2];
|
this.#route.value = found[2];
|
||||||
this.#from.value = found[3];
|
this.#from.value = found[3];
|
||||||
this.#to.value = found[4];
|
this.#to.value = found[4];
|
||||||
this.switchToMe();
|
this.editor.switchTo(this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the input in the tab plane.
|
* Validates the input in the tab panel.
|
||||||
*
|
*
|
||||||
* @return {boolean} true if valid, or false otherwise
|
* @return {boolean} true if valid, or false otherwise
|
||||||
*/
|
*/
|
||||||
@@ -1165,11 +1155,11 @@ class DescriptionEditorBusTripTab extends DescriptionEditorTagTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The recurring transaction tab plane.
|
* The recurring transaction tab.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class DescriptionEditorRecurringTab extends DescriptionEditorTabPlane {
|
class RecurringTab extends BaseDescriptionEditorTab {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The month names
|
* The month names
|
||||||
@@ -1184,13 +1174,13 @@ class DescriptionEditorRecurringTab extends DescriptionEditorTabPlane {
|
|||||||
#itemButtons;
|
#itemButtons;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a tab plane.
|
* Constructs a recurring transaction tab.
|
||||||
*
|
*
|
||||||
* @param editor {DescriptionEditor} the parent description editor
|
* @param editor {DescriptionEditor} the parent description editor
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
constructor(editor) {
|
constructor(editor) {
|
||||||
super(editor);
|
super("recurring", editor);
|
||||||
this.#monthNames = [
|
this.#monthNames = [
|
||||||
"",
|
"",
|
||||||
A_("January"), A_("February"), A_("March"), A_("April"),
|
A_("January"), A_("February"), A_("March"), A_("April"),
|
||||||
@@ -1232,17 +1222,7 @@ class DescriptionEditorRecurringTab extends DescriptionEditorTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tab ID
|
* Resets the tab panel input.
|
||||||
*
|
|
||||||
* @return {string}
|
|
||||||
* @abstract
|
|
||||||
*/
|
|
||||||
tabId() {
|
|
||||||
return "recurring";
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resets the tab plane input.
|
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
@@ -1254,7 +1234,7 @@ class DescriptionEditorRecurringTab extends DescriptionEditorTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populates the tab plane with the description input.
|
* Populates the tab panel with the description input.
|
||||||
*
|
*
|
||||||
* @return {boolean} true if the description input matches this tab, or false otherwise
|
* @return {boolean} true if the description input matches this tab, or false otherwise
|
||||||
* @override
|
* @override
|
||||||
@@ -1264,7 +1244,7 @@ class DescriptionEditorRecurringTab extends DescriptionEditorTabPlane {
|
|||||||
if (this.#getDescription(itemButton) === this.editor.description) {
|
if (this.#getDescription(itemButton) === this.editor.description) {
|
||||||
itemButton.classList.add("btn-primary");
|
itemButton.classList.add("btn-primary");
|
||||||
itemButton.classList.remove("btn-outline-primary");
|
itemButton.classList.remove("btn-outline-primary");
|
||||||
this.switchToMe();
|
this.editor.switchTo(this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1272,11 +1252,10 @@ class DescriptionEditorRecurringTab extends DescriptionEditorTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Switches to the tab plane.
|
* @inheritDoc
|
||||||
*
|
* @override
|
||||||
*/
|
*/
|
||||||
switchToMe() {
|
onActivated() {
|
||||||
super.switchToMe();
|
|
||||||
for (const itemButton of this.#itemButtons) {
|
for (const itemButton of this.#itemButtons) {
|
||||||
if (itemButton.classList.contains("btn-primary")) {
|
if (itemButton.classList.contains("btn-primary")) {
|
||||||
this.editor.updateCurrentSuggestedAccounts(itemButton);
|
this.editor.updateCurrentSuggestedAccounts(itemButton);
|
||||||
@@ -1287,7 +1266,7 @@ class DescriptionEditorRecurringTab extends DescriptionEditorTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the input in the tab plane.
|
* Validates the input in the tab panel.
|
||||||
*
|
*
|
||||||
* @return {boolean} true if valid, or false otherwise
|
* @return {boolean} true if valid, or false otherwise
|
||||||
* @override
|
* @override
|
||||||
@@ -1298,20 +1277,20 @@ class DescriptionEditorRecurringTab extends DescriptionEditorTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The annotation tab plane.
|
* The annotation tab.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class DescriptionEditorAnnotationTab extends DescriptionEditorTabPlane {
|
class AnnotationTab extends BaseDescriptionEditorTab {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a tab plane.
|
* Constructs an annotation tab.
|
||||||
*
|
*
|
||||||
* @param editor {DescriptionEditor} the parent description editor
|
* @param editor {DescriptionEditor} the parent description editor
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
constructor(editor) {
|
constructor(editor) {
|
||||||
super(editor);
|
super("annotation", editor);
|
||||||
this.editor.number.onchange = () => this.updateDescription();
|
this.editor.number.onchange = () => this.updateDescription();
|
||||||
this.editor.note.onchange = () => {
|
this.editor.note.onchange = () => {
|
||||||
this.editor.note.value = this.editor.note.value.trim();
|
this.editor.note.value = this.editor.note.value.trim();
|
||||||
@@ -1320,17 +1299,7 @@ class DescriptionEditorAnnotationTab extends DescriptionEditorTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tab ID
|
* Updates the description according to the input in the tab panel.
|
||||||
*
|
|
||||||
* @return {string}
|
|
||||||
* @abstract
|
|
||||||
*/
|
|
||||||
tabId() {
|
|
||||||
return "annotation";
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the description according to the input in the tab plane.
|
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
@@ -1348,7 +1317,7 @@ class DescriptionEditorAnnotationTab extends DescriptionEditorTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the tab plane input.
|
* Resets the tab panel input.
|
||||||
*
|
*
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
@@ -1358,7 +1327,7 @@ class DescriptionEditorAnnotationTab extends DescriptionEditorTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populates the tab plane with the description input.
|
* Populates the tab panel with the description input.
|
||||||
*
|
*
|
||||||
* @return {boolean} true if the description input matches this tab, or false otherwise
|
* @return {boolean} true if the description input matches this tab, or false otherwise
|
||||||
* @override
|
* @override
|
||||||
@@ -1382,7 +1351,7 @@ class DescriptionEditorAnnotationTab extends DescriptionEditorTabPlane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the input in the tab plane.
|
* Validates the input in the tab panel.
|
||||||
*
|
*
|
||||||
* @return {boolean} true if valid, or false otherwise
|
* @return {boolean} true if valid, or false otherwise
|
||||||
* @override
|
* @override
|
||||||
|
|||||||
@@ -30,21 +30,16 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
/**
|
/**
|
||||||
* The period chooser.
|
* The period chooser.
|
||||||
*
|
*
|
||||||
|
* @extends {BaseTablist<BasePeriodTab>}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class PeriodChooser {
|
class PeriodChooser extends BaseTablist {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The modal of the period chooser
|
* The URL template for different periods.
|
||||||
* @type {HTMLDivElement}
|
* @type {string}
|
||||||
*/
|
*/
|
||||||
modal;
|
urlTemplate;
|
||||||
|
|
||||||
/**
|
|
||||||
* The tab planes
|
|
||||||
* @type {{month: MonthTab, year: YearTab, day: DayTab, custom: CustomTab}}
|
|
||||||
*/
|
|
||||||
tabPlanes = {};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs the period chooser.
|
* Constructs the period chooser.
|
||||||
@@ -52,13 +47,24 @@ class PeriodChooser {
|
|||||||
*/
|
*/
|
||||||
constructor() {
|
constructor() {
|
||||||
const prefix = "accounting-period-chooser";
|
const prefix = "accounting-period-chooser";
|
||||||
this.modal = document.getElementById(`${prefix}-modal`);
|
super(document.getElementById(`${prefix}-tab-list`));
|
||||||
for (const cls of [MonthTab, YearTab, DayTab, CustomTab]) {
|
this.tabs = [new MonthTab(this), new YearTab(this), new DayTab(this), new CustomTab(this)];
|
||||||
const tab = new cls(this);
|
const modal = document.getElementById(`${prefix}-modal`);
|
||||||
this.tabPlanes[tab.tabId()] = tab;
|
this.urlTemplate = modal.dataset.urlTemplate;
|
||||||
|
for (const tab of this.tabs) {
|
||||||
|
if (tab.isActive()) {
|
||||||
|
this.currentTab = tab;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
onTabFocus(tab) { this.switchTo(tab); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The period chooser.
|
* The period chooser.
|
||||||
* @type {PeriodChooser}
|
* @type {PeriodChooser}
|
||||||
@@ -75,12 +81,12 @@ class PeriodChooser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A tab plane.
|
* A base abstract period tab.
|
||||||
*
|
*
|
||||||
* @abstract
|
* @abstract
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class TabPlane {
|
class BasePeriodTab extends BaseTab {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The period chooser
|
* The period chooser
|
||||||
@@ -95,62 +101,27 @@ class TabPlane {
|
|||||||
prefix;
|
prefix;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tab
|
* Constructs a base abstract period tab.
|
||||||
* @type {HTMLSpanElement}
|
|
||||||
*/
|
|
||||||
#tab;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The page
|
|
||||||
* @type {HTMLDivElement}
|
|
||||||
*/
|
|
||||||
#page;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a tab plane.
|
|
||||||
*
|
*
|
||||||
|
* @param tabID {string} the tab ID
|
||||||
* @param chooser {PeriodChooser} the period chooser
|
* @param chooser {PeriodChooser} the period chooser
|
||||||
*/
|
*/
|
||||||
constructor(chooser) {
|
constructor(tabID, chooser) {
|
||||||
|
const prefix = `accounting-period-chooser-${tabID}`;
|
||||||
|
const tab = document.getElementById(`${prefix}-tab`);
|
||||||
|
const panel = document.getElementById(`${prefix}-panel`);
|
||||||
|
super(tab, panel, chooser.switchTo.bind(chooser));
|
||||||
this.chooser = chooser;
|
this.chooser = chooser;
|
||||||
this.prefix = `accounting-period-chooser-${this.tabId()}`;
|
this.prefix = prefix;
|
||||||
this.#tab = document.getElementById(`${this.prefix}-tab`);
|
|
||||||
this.#page = document.getElementById(`${this.prefix}-page`);
|
|
||||||
this.#tab.onclick = () => this.#switchToMe();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The tab ID
|
|
||||||
*
|
|
||||||
* @return {string}
|
|
||||||
* @abstract
|
|
||||||
*/
|
|
||||||
tabId() { throw new Error("Method not implemented.") };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Switches to the tab plane.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#switchToMe() {
|
|
||||||
for (const tabPlane of Object.values(this.chooser.tabPlanes)) {
|
|
||||||
tabPlane.#tab.classList.remove("active")
|
|
||||||
tabPlane.#tab.ariaCurrent = "false";
|
|
||||||
tabPlane.#page.classList.add("d-none");
|
|
||||||
tabPlane.#page.ariaCurrent = "false";
|
|
||||||
}
|
|
||||||
this.#tab.classList.add("active");
|
|
||||||
this.#tab.ariaCurrent = "page";
|
|
||||||
this.#page.classList.remove("d-none");
|
|
||||||
this.#page.ariaCurrent = "page";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The month tab plane.
|
* The month tab.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class MonthTab extends TabPlane {
|
class MonthTab extends BasePeriodTab {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The month chooser.
|
* The month chooser.
|
||||||
@@ -159,12 +130,12 @@ class MonthTab extends TabPlane {
|
|||||||
#monthChooser
|
#monthChooser
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a tab plane.
|
* Constructs a month tab.
|
||||||
*
|
*
|
||||||
* @param chooser {PeriodChooser} the period chooser
|
* @param chooser {PeriodChooser} the period chooser
|
||||||
*/
|
*/
|
||||||
constructor(chooser) {
|
constructor(chooser) {
|
||||||
super(chooser);
|
super("month", chooser);
|
||||||
const monthChooser = document.getElementById(`${this.prefix}-chooser`);
|
const monthChooser = document.getElementById(`${this.prefix}-chooser`);
|
||||||
if (monthChooser !== null) {
|
if (monthChooser !== null) {
|
||||||
this.#monthChooser = new tempusDominus.TempusDominus(monthChooser, {
|
this.#monthChooser = new tempusDominus.TempusDominus(monthChooser, {
|
||||||
@@ -184,45 +155,36 @@ class MonthTab extends TabPlane {
|
|||||||
const date = e.detail.date;
|
const date = e.detail.date;
|
||||||
const zeroPaddedMonth = `0${date.month + 1}`.slice(-2)
|
const zeroPaddedMonth = `0${date.month + 1}`.slice(-2)
|
||||||
const period = `${date.year}-${zeroPaddedMonth}`;
|
const period = `${date.year}-${zeroPaddedMonth}`;
|
||||||
window.location = chooser.modal.dataset.urlTemplate
|
window.location = chooser.urlTemplate
|
||||||
.replaceAll("PERIOD", period);
|
.replaceAll("PERIOD", period);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The year tab.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
class YearTab extends BasePeriodTab {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The tab ID
|
* Constructs a year tab.
|
||||||
*
|
*
|
||||||
* @return {string}
|
* @param chooser {PeriodChooser} the period chooser
|
||||||
*/
|
*/
|
||||||
tabId() {
|
constructor(chooser) {
|
||||||
return "month";
|
super("year", chooser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The year tab plane.
|
* The day tab.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class YearTab extends TabPlane {
|
class DayTab extends BasePeriodTab {
|
||||||
|
|
||||||
/**
|
|
||||||
* The tab ID
|
|
||||||
*
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
tabId() {
|
|
||||||
return "year";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The day tab plane.
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
class DayTab extends TabPlane {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The day input
|
* The day input
|
||||||
@@ -237,18 +199,18 @@ class DayTab extends TabPlane {
|
|||||||
#dateError;
|
#dateError;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a tab plane.
|
* Constructs a day tab.
|
||||||
*
|
*
|
||||||
* @param chooser {PeriodChooser} the period chooser
|
* @param chooser {PeriodChooser} the period chooser
|
||||||
*/
|
*/
|
||||||
constructor(chooser) {
|
constructor(chooser) {
|
||||||
super(chooser);
|
super("day", chooser);
|
||||||
this.#date = document.getElementById(`${this.prefix}-date`);
|
this.#date = document.getElementById(`${this.prefix}-date`);
|
||||||
this.#dateError = document.getElementById(`${this.prefix}-date-error`);
|
this.#dateError = document.getElementById(`${this.prefix}-date-error`);
|
||||||
if (this.#date !== null) {
|
if (this.#date !== null) {
|
||||||
this.#date.onchange = () => {
|
this.#date.onchange = () => {
|
||||||
if (this.#validateDate()) {
|
if (this.#validateDate()) {
|
||||||
window.location = chooser.modal.dataset.urlTemplate
|
window.location = chooser.urlTemplate
|
||||||
.replaceAll("PERIOD", this.#date.value);
|
.replaceAll("PERIOD", this.#date.value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -275,23 +237,14 @@ class DayTab extends TabPlane {
|
|||||||
this.#dateError.innerText = "";
|
this.#dateError.innerText = "";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The tab ID
|
|
||||||
*
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
tabId() {
|
|
||||||
return "day";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The custom tab plane.
|
* The custom tab.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
class CustomTab extends TabPlane {
|
class CustomTab extends BasePeriodTab {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The start of the period
|
* The start of the period
|
||||||
@@ -324,12 +277,12 @@ class CustomTab extends TabPlane {
|
|||||||
#confirm;
|
#confirm;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a tab plane.
|
* Constructs a custom tab.
|
||||||
*
|
*
|
||||||
* @param chooser {PeriodChooser} the period chooser
|
* @param chooser {PeriodChooser} the period chooser
|
||||||
*/
|
*/
|
||||||
constructor(chooser) {
|
constructor(chooser) {
|
||||||
super(chooser);
|
super("custom", chooser);
|
||||||
this.#start = document.getElementById(`${this.prefix}-start`);
|
this.#start = document.getElementById(`${this.prefix}-start`);
|
||||||
this.#startError = document.getElementById(`${this.prefix}-start-error`);
|
this.#startError = document.getElementById(`${this.prefix}-start-error`);
|
||||||
this.#end = document.getElementById(`${this.prefix}-end`);
|
this.#end = document.getElementById(`${this.prefix}-end`);
|
||||||
@@ -351,7 +304,7 @@ class CustomTab extends TabPlane {
|
|||||||
isValid = this.#validateStart() && isValid;
|
isValid = this.#validateStart() && isValid;
|
||||||
isValid = this.#validateEnd() && isValid;
|
isValid = this.#validateEnd() && isValid;
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
window.location = chooser.modal.dataset.urlTemplate
|
window.location = chooser.urlTemplate
|
||||||
.replaceAll("PERIOD", `${this.#start.value}-${this.#end.value}`);
|
.replaceAll("PERIOD", `${this.#start.value}-${this.#end.value}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -407,13 +360,4 @@ class CustomTab extends TabPlane {
|
|||||||
this.#endError.innerText = "";
|
this.#endError.innerText = "";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The tab ID
|
|
||||||
*
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
tabId() {
|
|
||||||
return "custom";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-11
@@ -39,36 +39,36 @@ First written: 2023/2/28
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# Tab navigation #}
|
{# Tab navigation #}
|
||||||
<ul class="nav nav-tabs mb-2" role="tablist">
|
<ul id="accounting-description-editor-{{ description_editor.debit_credit }}-tab-list" class="nav nav-tabs mb-2" role="tablist" aria-label="{{ A_("Description Type") }}">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<button id="accounting-description-editor-{{ description_editor.debit_credit }}-general-tab" class="nav-link active accounting-clickable" type="button" role="tab" aria-controls="accounting-description-editor-{{ description_editor.debit_credit }}-general-page" aria-selected="true" aria-current="page">
|
<button id="accounting-description-editor-{{ description_editor.debit_credit }}-general-tab" class="nav-link active accounting-clickable" type="button" tabindex="0" role="tab" aria-controls="accounting-description-editor-{{ description_editor.debit_credit }}-general-panel" aria-selected="true">
|
||||||
{{ A_("General") }}
|
{{ A_("General") }}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<button id="accounting-description-editor-{{ description_editor.debit_credit }}-travel-tab" class="nav-link accounting-clickable" type="button" role="tab" aria-controls="accounting-description-editor-{{ description_editor.debit_credit }}-travel-page" aria-selected="false" aria-current="false">
|
<button id="accounting-description-editor-{{ description_editor.debit_credit }}-travel-tab" class="nav-link accounting-clickable" type="button" tabindex="-1" role="tab" aria-controls="accounting-description-editor-{{ description_editor.debit_credit }}-travel-panel" aria-selected="false">
|
||||||
{{ A_("Travel") }}
|
{{ A_("Travel") }}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<button id="accounting-description-editor-{{ description_editor.debit_credit }}-bus-tab" class="nav-link accounting-clickable" type="button" role="tab" aria-controls="accounting-description-editor-{{ description_editor.debit_credit }}-bus-page" aria-selected="false" aria-current="false">
|
<button id="accounting-description-editor-{{ description_editor.debit_credit }}-bus-tab" class="nav-link accounting-clickable" type="button" tabindex="-1" role="tab" aria-controls="accounting-description-editor-{{ description_editor.debit_credit }}-bus-panel" aria-selected="false">
|
||||||
{{ A_("Bus") }}
|
{{ A_("Bus") }}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<button id="accounting-description-editor-{{ description_editor.debit_credit }}-recurring-tab" class="nav-link accounting-clickable" type="button" role="tab" aria-controls="accounting-description-editor-{{ description_editor.debit_credit }}-recurring-page" aria-selected="false" aria-current="false">
|
<button id="accounting-description-editor-{{ description_editor.debit_credit }}-recurring-tab" class="nav-link accounting-clickable" type="button" tabindex="-1" role="tab" aria-controls="accounting-description-editor-{{ description_editor.debit_credit }}-recurring-panel" aria-selected="false">
|
||||||
{{ A_("Recurring") }}
|
{{ A_("Recurring") }}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<button id="accounting-description-editor-{{ description_editor.debit_credit }}-annotation-tab" class="nav-link accounting-clickable" type="button" role="tab" aria-controls="accounting-description-editor-{{ description_editor.debit_credit }}-annotation-page" aria-selected="false" aria-current="false">
|
<button id="accounting-description-editor-{{ description_editor.debit_credit }}-annotation-tab" class="nav-link accounting-clickable" type="button" tabindex="-1" role="tab" aria-controls="accounting-description-editor-{{ description_editor.debit_credit }}-annotation-panel" aria-selected="false">
|
||||||
{{ A_("Annotation") }}
|
{{ A_("Annotation") }}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
{# A general description with a tag #}
|
{# A general description with a tag #}
|
||||||
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-general-page" role="tabpanel" aria-current="page" aria-labelledby="accounting-description-editor-{{ description_editor.debit_credit }}-general-tab">
|
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-general-panel" role="tabpanel" aria-labelledby="accounting-description-editor-{{ description_editor.debit_credit }}-general-tab">
|
||||||
<div class="form-floating mb-2">
|
<div class="form-floating mb-2">
|
||||||
<input id="accounting-description-editor-{{ description_editor.debit_credit }}-general-tag" class="form-control" type="text" value="" placeholder=" ">
|
<input id="accounting-description-editor-{{ description_editor.debit_credit }}-general-tag" class="form-control" type="text" value="" placeholder=" ">
|
||||||
<label class="form-label" for="accounting-description-editor-{{ description_editor.debit_credit }}-general-tag">{{ A_("Tag") }}</label>
|
<label class="form-label" for="accounting-description-editor-{{ description_editor.debit_credit }}-general-tag">{{ A_("Tag") }}</label>
|
||||||
@@ -85,7 +85,7 @@ First written: 2023/2/28
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# A general trip with the origin and distination #}
|
{# A general trip with the origin and distination #}
|
||||||
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-travel-page" class="d-none" role="tabpanel" aria-current="false" aria-labelledby="accounting-description-editor-{{ description_editor.debit_credit }}-travel-tab">
|
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-travel-panel" class="d-none" role="tabpanel" aria-labelledby="accounting-description-editor-{{ description_editor.debit_credit }}-travel-tab">
|
||||||
<div class="form-floating mb-2">
|
<div class="form-floating mb-2">
|
||||||
<input id="accounting-description-editor-{{ description_editor.debit_credit }}-travel-tag" class="form-control" type="text" value="" placeholder=" ">
|
<input id="accounting-description-editor-{{ description_editor.debit_credit }}-travel-tag" class="form-control" type="text" value="" placeholder=" ">
|
||||||
<label class="form-label" for="accounting-description-editor-{{ description_editor.debit_credit }}-travel-tag">{{ A_("Tag") }}</label>
|
<label class="form-label" for="accounting-description-editor-{{ description_editor.debit_credit }}-travel-tag">{{ A_("Tag") }}</label>
|
||||||
@@ -119,7 +119,7 @@ First written: 2023/2/28
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# A bus trip with the route name or route number, the origin and distination #}
|
{# A bus trip with the route name or route number, the origin and distination #}
|
||||||
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-bus-page" class="d-none" role="tabpanel" aria-current="false" aria-labelledby="accounting-description-editor-{{ description_editor.debit_credit }}-bus-tab">
|
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-bus-panel" class="d-none" role="tabpanel" aria-labelledby="accounting-description-editor-{{ description_editor.debit_credit }}-bus-tab">
|
||||||
<div class="d-flex justify-content-between mb-2">
|
<div class="d-flex justify-content-between mb-2">
|
||||||
<div class="form-floating me-2">
|
<div class="form-floating me-2">
|
||||||
<input id="accounting-description-editor-{{ description_editor.debit_credit }}-bus-tag" class="form-control" type="text" value="" placeholder=" ">
|
<input id="accounting-description-editor-{{ description_editor.debit_credit }}-bus-tag" class="form-control" type="text" value="" placeholder=" ">
|
||||||
@@ -156,7 +156,7 @@ First written: 2023/2/28
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# A recurring transaction #}
|
{# A recurring transaction #}
|
||||||
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-recurring-page" class="d-none" role="tabpanel" aria-current="false" aria-labelledby="accounting-description-editor-{{ description_editor.debit_credit }}-recurring-tab">
|
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-recurring-panel" class="d-none" role="tabpanel" aria-labelledby="accounting-description-editor-{{ description_editor.debit_credit }}-recurring-tab">
|
||||||
<div class="accounting-description-editor-buttons">
|
<div class="accounting-description-editor-buttons">
|
||||||
{% for recurring in description_editor.recurring %}
|
{% for recurring in description_editor.recurring %}
|
||||||
<button class="btn btn-outline-primary accounting-description-editor-{{ description_editor.debit_credit }}-recurring-item" type="button" tabindex="-1" data-template="{{ recurring.description_template }}" data-accounts="{{ recurring.account_codes|tojson|forceescape }}">
|
<button class="btn btn-outline-primary accounting-description-editor-{{ description_editor.debit_credit }}-recurring-item" type="button" tabindex="-1" data-template="{{ recurring.description_template }}" data-accounts="{{ recurring.account_codes|tojson|forceescape }}">
|
||||||
@@ -167,7 +167,7 @@ First written: 2023/2/28
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# The annotation #}
|
{# The annotation #}
|
||||||
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-annotation-page" class="d-none" role="tabpanel" aria-current="false" aria-labelledby="accounting-description-editor-{{ description_editor.debit_credit }}-annotation-tab">
|
<div id="accounting-description-editor-{{ description_editor.debit_credit }}-annotation-panel" class="d-none" role="tabpanel" aria-labelledby="accounting-description-editor-{{ description_editor.debit_credit }}-annotation-tab">
|
||||||
<div class="form-floating">
|
<div class="form-floating">
|
||||||
<input id="accounting-description-editor-{{ description_editor.debit_credit }}-annotation-number" class="form-control" type="number" min="1" value="" placeholder=" ">
|
<input id="accounting-description-editor-{{ description_editor.debit_credit }}-annotation-number" class="form-control" type="number" min="1" value="" placeholder=" ">
|
||||||
<label class="form-label" for="accounting-description-editor-{{ description_editor.debit_credit }}-annotation-number">{{ A_("The Number of Items") }}</label>
|
<label class="form-label" for="accounting-description-editor-{{ description_editor.debit_credit }}-annotation-number">{{ A_("The Number of Items") }}</label>
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ First written: 2023/2/26
|
|||||||
<script src="{{ url_for("accounting.static", filename="js/journal-entry-line-item-editor.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/journal-entry-line-item-editor.js") }}"></script>
|
||||||
<script src="{{ url_for("accounting.static", filename="js/journal-entry-account-selector.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/journal-entry-account-selector.js") }}"></script>
|
||||||
<script src="{{ url_for("accounting.static", filename="js/original-line-item-selector.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/original-line-item-selector.js") }}"></script>
|
||||||
|
<script src="{{ url_for("accounting.static", filename="js/base-tablist.js") }}"></script>
|
||||||
<script src="{{ url_for("accounting.static", filename="js/description-editor.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/description-editor.js") }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ First written: 2023/3/7
|
|||||||
|
|
||||||
{% block accounting_scripts %}
|
{% block accounting_scripts %}
|
||||||
<script src="{{ url_for("accounting.static", filename="js/material-fab-speed-dial.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/material-fab-speed-dial.js") }}"></script>
|
||||||
|
<script src="{{ url_for("accounting.static", filename="js/base-tablist.js") }}"></script>
|
||||||
<script src="{{ url_for("accounting.static", filename="js/period-chooser.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/period-chooser.js") }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|||||||
@@ -28,31 +28,31 @@ First written: 2023/3/4
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
{# Tab navigation #}
|
{# Tab navigation #}
|
||||||
<ul class="nav nav-tabs mb-2" role="tablist">
|
<ul id="accounting-period-chooser-tab-list" class="nav nav-tabs mb-2" role="tablist" aria-label="{{ A_("Period Type") }}">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<button id="accounting-period-chooser-month-tab" class="nav-link {% if report.period.is_type_month %} active {% endif %} accounting-clickable" type="button" role="tab" aria-controls="accounting-period-chooser-month-page" aria-selected="true" aria-current="{% if report.period.is_type_month %} page {% else %} false {% endif %}" data-tab-id="month">
|
<button id="accounting-period-chooser-month-tab" class="nav-link {% if report.period.is_type_month %} active {% endif %} accounting-clickable" type="button" tabindex="{% if report.period.is_type_month %}0{% else %}-1{% endif %}" role="tab" aria-controls="accounting-period-chooser-month-panel" aria-selected="{% if report.period.is_type_month %}true{% else %}false{% endif %}">
|
||||||
{{ A_("Month") }}
|
{{ A_("Month") }}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<button id="accounting-period-chooser-year-tab" class="nav-link {% if report.period.is_a_year %} active {% endif %} accounting-clickable" type="button" role="tab" aria-controls="accounting-period-chooser-year-page" aria-selected="false" aria-current="{% if report.period.is_a_year %} page {% else %} false {% endif %}" data-tab-id="year">
|
<button id="accounting-period-chooser-year-tab" class="nav-link {% if report.period.is_a_year %} active {% endif %} accounting-clickable" type="button" tabindex="{% if report.period.is_a_year %}0{% else %}-1{% endif %}" role="tab" aria-controls="accounting-period-chooser-year-panel" aria-selected="{% if report.period.is_a_year %}true{% else %}false{% endif %}">
|
||||||
{{ A_("Year") }}
|
{{ A_("Year") }}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<button id="accounting-period-chooser-day-tab" class="nav-link {% if report.period.is_a_day %} active {% endif %} accounting-clickable" type="button" role="tab" aria-controls="accounting-period-chooser-day-page" aria-selected="false" aria-current="{% if report.period.is_a_day %} page {% else %} false {% endif %}" data-tab-id="day">
|
<button id="accounting-period-chooser-day-tab" class="nav-link {% if report.period.is_a_day %} active {% endif %} accounting-clickable" type="button" tabindex="{% if report.period.is_a_day %}0{% else %}-1{% endif %}" role="tab" aria-controls="accounting-period-chooser-day-panel" aria-selected="{% if report.period.is_a_day %}true{% else %}false{% endif %}">
|
||||||
{{ A_("Day") }}
|
{{ A_("Day") }}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<button id="accounting-period-chooser-custom-tab" class="nav-link {% if report.period.is_type_arbitrary %} active {% endif %} accounting-clickable" type="button" role="tab" aria-controls="accounting-period-chooser-custom-page" aria-selected="false" aria-current="{% if report.period.is_type_arbitrary %} page {% else %} false {% endif %}" data-tab-id="custom">
|
<button id="accounting-period-chooser-custom-tab" class="nav-link {% if report.period.is_type_arbitrary %} active {% endif %} accounting-clickable" type="button" tabindex="{% if report.period.is_type_arbitrary %}0{% else %}-1{% endif %}" role="tab" aria-controls="accounting-period-chooser-custom-panel" aria-selected="{% if report.period.is_type_arbitrary %}true{% else %}false{% endif %}">
|
||||||
{{ A_("Custom") }}
|
{{ A_("Custom") }}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
{# The month periods #}
|
{# The month periods #}
|
||||||
<div id="accounting-period-chooser-month-page" {% if report.period.is_type_month %} aria-current="page" {% else %} class="d-none" role="tabpanel" aria-current="false" {% endif %} aria-labelledby="accounting-period-chooser-month-tab">
|
<div id="accounting-period-chooser-month-panel" {% if not report.period.is_type_month %} class="d-none" {% endif %} role="tabpanel" aria-labelledby="accounting-period-chooser-month-tab">
|
||||||
<div>
|
<div>
|
||||||
<a class="btn {% if report.period.is_this_month %} btn-primary {% else %} btn-outline-primary {% endif %}" role="button" href="{{ report.period_chooser.this_month_url }}">
|
<a class="btn {% if report.period.is_this_month %} btn-primary {% else %} btn-outline-primary {% endif %}" role="button" href="{{ report.period_chooser.this_month_url }}">
|
||||||
{{ A_("This Month") }}
|
{{ A_("This Month") }}
|
||||||
@@ -72,7 +72,7 @@ First written: 2023/3/4
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# The year periods #}
|
{# The year periods #}
|
||||||
<div id="accounting-period-chooser-year-page" {% if report.period.is_a_year %} aria-current="page" {% else %} class="d-none" role="tabpanel" aria-current="false" {% endif %} aria-labelledby="accounting-period-chooser-year-tab">
|
<div id="accounting-period-chooser-year-panel" {% if not report.period.is_a_year %} class="d-none" {% endif %} role="tabpanel" aria-labelledby="accounting-period-chooser-year-tab">
|
||||||
<a class="btn {% if report.period.is_this_year %} btn-primary {% else %} btn-outline-primary {% endif %}" role="button" href="{{ report.period_chooser.this_year_url }}">
|
<a class="btn {% if report.period.is_this_year %} btn-primary {% else %} btn-outline-primary {% endif %}" role="button" href="{{ report.period_chooser.this_year_url }}">
|
||||||
{{ A_("This Year") }}
|
{{ A_("This Year") }}
|
||||||
</a>
|
</a>
|
||||||
@@ -93,7 +93,7 @@ First written: 2023/3/4
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# The day periods #}
|
{# The day periods #}
|
||||||
<div id="accounting-period-chooser-day-page" {% if report.period.is_a_day %} aria-current="page" {% else %} class="d-none" role="tabpanel" aria-current="false" {% endif %} aria-labelledby="accounting-period-chooser-day-tab">
|
<div id="accounting-period-chooser-day-panel" {% if not report.period.is_a_day %} class="d-none" {% endif %} role="tabpanel" aria-labelledby="accounting-period-chooser-day-tab">
|
||||||
<div>
|
<div>
|
||||||
<a class="btn {% if report.period.is_today %} btn-primary {% else %} btn-outline-primary {% endif %}" role="button" href="{{ report.period_chooser.today_url }}">
|
<a class="btn {% if report.period.is_today %} btn-primary {% else %} btn-outline-primary {% endif %}" role="button" href="{{ report.period_chooser.today_url }}">
|
||||||
{{ A_("Today") }}
|
{{ A_("Today") }}
|
||||||
@@ -116,7 +116,7 @@ First written: 2023/3/4
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# The custom periods #}
|
{# The custom periods #}
|
||||||
<div id="accounting-period-chooser-custom-page" {% if report.period.is_type_arbitrary %} aria-current="page" {% else %} class="d-none" role="tabpanel" aria-current="false" {% endif %} aria-labelledby="accounting-period-chooser-custom-tab">
|
<div id="accounting-period-chooser-custom-panel" {% if not report.period.is_type_arbitrary %} class="d-none" {% endif %} role="tabpanel" aria-labelledby="accounting-period-chooser-custom-tab">
|
||||||
<div>
|
<div>
|
||||||
<a class="btn {% if report.period.is_all %} btn-primary {% else %} btn-outline-primary {% endif %}" role="button" href="{{ report.period_chooser.all_url }}">
|
<a class="btn {% if report.period.is_all %} btn-primary {% else %} btn-outline-primary {% endif %}" role="button" href="{{ report.period_chooser.all_url }}">
|
||||||
{{ A_("All") }}
|
{{ A_("All") }}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ First written: 2023/3/5
|
|||||||
|
|
||||||
{% block accounting_scripts %}
|
{% block accounting_scripts %}
|
||||||
<script src="{{ url_for("accounting.static", filename="js/material-fab-speed-dial.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/material-fab-speed-dial.js") }}"></script>
|
||||||
|
<script src="{{ url_for("accounting.static", filename="js/base-tablist.js") }}"></script>
|
||||||
<script src="{{ url_for("accounting.static", filename="js/period-chooser.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/period-chooser.js") }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ First written: 2023/3/7
|
|||||||
|
|
||||||
{% block accounting_scripts %}
|
{% block accounting_scripts %}
|
||||||
<script src="{{ url_for("accounting.static", filename="js/material-fab-speed-dial.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/material-fab-speed-dial.js") }}"></script>
|
||||||
|
<script src="{{ url_for("accounting.static", filename="js/base-tablist.js") }}"></script>
|
||||||
<script src="{{ url_for("accounting.static", filename="js/period-chooser.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/period-chooser.js") }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ First written: 2023/3/4
|
|||||||
|
|
||||||
{% block accounting_scripts %}
|
{% block accounting_scripts %}
|
||||||
<script src="{{ url_for("accounting.static", filename="js/material-fab-speed-dial.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/material-fab-speed-dial.js") }}"></script>
|
||||||
|
<script src="{{ url_for("accounting.static", filename="js/base-tablist.js") }}"></script>
|
||||||
<script src="{{ url_for("accounting.static", filename="js/period-chooser.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/period-chooser.js") }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ First written: 2023/3/5
|
|||||||
|
|
||||||
{% block accounting_scripts %}
|
{% block accounting_scripts %}
|
||||||
<script src="{{ url_for("accounting.static", filename="js/material-fab-speed-dial.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/material-fab-speed-dial.js") }}"></script>
|
||||||
|
<script src="{{ url_for("accounting.static", filename="js/base-tablist.js") }}"></script>
|
||||||
<script src="{{ url_for("accounting.static", filename="js/period-chooser.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/period-chooser.js") }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ First written: 2023/3/5
|
|||||||
|
|
||||||
{% block accounting_scripts %}
|
{% block accounting_scripts %}
|
||||||
<script src="{{ url_for("accounting.static", filename="js/material-fab-speed-dial.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/material-fab-speed-dial.js") }}"></script>
|
||||||
|
<script src="{{ url_for("accounting.static", filename="js/base-tablist.js") }}"></script>
|
||||||
<script src="{{ url_for("accounting.static", filename="js/period-chooser.js") }}"></script>
|
<script src="{{ url_for("accounting.static", filename="js/period-chooser.js") }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user