Part-DB.Part-DB-server/assets/js/tab_remember.js
2022-11-29 21:21:26 +01:00

121 lines
No EOL
4.3 KiB
JavaScript

/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2022 Jan Böhmer (https://github.com/jbtronics)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
"use strict";
import {Tab} from "bootstrap";
import tab from "bootstrap/js/src/tab";
/**
* This listener keeps track of which tab is currently selected (using hash and localstorage) and will try to open
* that tab on reload. That means that if the user changes something, he does not have to switch back to the tab
* where he was before submit.
*/
class TabRememberHelper {
constructor() {
document.addEventListener("turbo:load", this.onLoad.bind(this));
document.addEventListener("turbo:frame-render", this.handleSymfonyValidationErrors.bind(this));
//Capture is important here, as invalid events normally does not bubble
document.addEventListener("invalid", this.onInvalid.bind(this), {capture: true});
}
handleSymfonyValidationErrors(event) {
const responseCode = event.detail.fetchResponse.response.status;
//We only care about 422 (symfony validation error)
if(responseCode !== 422) {
return;
}
//Find the first offending element and show it
//Symfony validation errors can occur on multiple types
const inputErrors = document.getElementsByClassName('is-invalid');
const blockErrors = document.getElementsByClassName('form-error-message');
const merged = [...inputErrors, ...blockErrors];
const first_element = merged[0] ?? null;
if(first_element) {
this.revealElementOnTab(first_element);
}
debugger;
}
/**
* This functions is called when the browser side input validation fails on an input, jump to the tab to show this up
* @param event
*/
onInvalid(event) {
this.revealElementOnTab(event.target);
}
revealElementOnTab(element) {
let parent = element.closest('.tab-pane');
//Iterate over each parent tab and show it
while(parent) {
//Invoker can either be a button or a element
let tabInvoker = document.querySelector("button[data-content='#" + parent.id + "']")
?? document.querySelector("button[data-bs-target='#" + parent.id + "']")
?? document.querySelector("a[href='#" + parent.id + "']");
Tab.getOrCreateInstance(tabInvoker).show();
parent = parent.parentElement.closest('.tab-pane');
}
}
onLoad(event) {
//Determine which tab should be shown (use hash if specified, otherwise use localstorage)
let activeTab = null;
if (location.hash) {
activeTab = document.querySelector('[href=\'' + location.hash + '\']');
} else if (localStorage.getItem('activeTab')) {
activeTab = document.querySelector('[href="' + localStorage.getItem('activeTab') + '"]');
}
if (activeTab) {
//Reveal our tab selector (needed for nested tabs)
this.revealElementOnTab(activeTab);
//Finally show the active tab itself
Tab.getOrCreateInstance(activeTab).show();
}
//Register listener for tab change
document.addEventListener('shown.bs.tab', this.onTabChange.bind(this));
}
onTabChange(event) {
const tab = event.target;
let tab_name = tab.getAttribute('href')
if (history.replaceState) {
history.replaceState(null, null, tab_name)
} else {
location.hash = tab_name
}
localStorage.setItem('activeTab', tab_name)
}
}
export default new TabRememberHelper();