nope/resources/ui/layout/tabs.tsx

307 lines
7.6 KiB
TypeScript
Raw Normal View History

2020-10-30 18:30:59 +00:00
/**
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @create date 2019-02-20 09:19:06
* @modify date 2020-10-29 09:55:30
* @desc [description]
2021-08-26 17:41:08 +00:00
*
2020-10-30 18:30:59 +00:00
* A Basic Layout.
2021-08-26 17:41:08 +00:00
* It uses a Toolbar, Sidebar
*
2020-10-30 18:30:59 +00:00
* Toast are implemented by https://fkhadra.github.io/react-toastify/introduction
*/
2021-08-26 17:41:08 +00:00
import React from "react";
import { Nav } from "react-bootstrap";
import { FaTimesCircle } from "react-icons/fa";
import { deepClone } from "../../../lib/helpers/objectMethods";
import { ITab } from "./interfaces/ITab";
import { ITabEntry } from "./interfaces/ITabEntry";
2020-10-30 18:30:59 +00:00
export interface ITabProps {
2021-08-26 17:41:08 +00:00
onMount?: (item: ITabEntry) => void;
onNewTab?: () => Promise<ITab | false>;
onTabSelect?: (oldTabId: ITab, newTabId: ITab) => Promise<boolean>;
onTabEdit?: (tab: ITab) => Promise<ITab>;
onTabDelete?: (tabId: ITab, forced?: boolean) => Promise<boolean>;
onNoTabSelected?: () => Promise<void>;
onConfigChanged?: (config: ITabProps) => void;
tabs: {
allowNewTabs?: boolean;
active: string;
items: ITab[];
};
2020-10-30 18:30:59 +00:00
}
2021-08-26 17:41:08 +00:00
class TabEntry
extends React.Component<ITabProps, ITabProps>
implements ITabEntry
{
protected _handleResize: () => void;
public hotkeysEnabled = true;
public pressedKey: string = null;
public currentMousePosition: MouseEvent;
constructor(props) {
super(props);
this.state = Object.assign({}, this.props);
}
/**
* Function will be called if the Item has been rendered sucessfully.
*/
componentDidMount() {
const _this = this;
if (typeof _this.state.onMount === "function") {
_this.state.onMount(_this);
2020-10-30 18:30:59 +00:00
}
2021-08-26 17:41:08 +00:00
}
2020-10-30 18:30:59 +00:00
2021-08-26 17:41:08 +00:00
protected _sendConfig() {
if (typeof this.props.onConfigChanged === "function") {
this.props.onConfigChanged(deepClone(this.state));
2020-10-30 18:30:59 +00:00
}
2021-08-26 17:41:08 +00:00
}
2020-10-30 18:30:59 +00:00
2021-08-26 17:41:08 +00:00
public get selectedTab() {
for (const tab of this.state.tabs.items) {
if (tab.id === this.state.tabs.active) {
return tab;
}
2020-10-30 18:30:59 +00:00
}
2021-08-26 17:41:08 +00:00
return null;
}
2020-10-30 18:30:59 +00:00
2021-08-26 17:41:08 +00:00
public async requestTab() {
if (typeof this.state.onNewTab === "function") {
const tab = await this.state.onNewTab();
2020-10-30 18:30:59 +00:00
2021-08-26 17:41:08 +00:00
if (typeof tab === "object") {
this.state.tabs.items.push(tab);
2020-10-30 18:30:59 +00:00
2021-08-26 17:41:08 +00:00
await this.selectTab(tab);
}
2020-10-30 18:30:59 +00:00
}
2021-08-26 17:41:08 +00:00
}
public async createTab(tab: ITab) {
const tabs = this.tabs;
tabs.push(tab);
this.tabs = tabs;
}
public get tabs(): ITab[] {
return this.state.tabs?.items || [];
}
public set tabs(value: ITab[]) {
this.setState({
tabs: {
active: this.activeTab,
items: value,
allowNewTabs: this.state.tabs.allowNewTabs,
},
});
}
public get activeTab(): string {
return this.state.tabs.active;
}
public set activeTab(value: string) {
this.setState({
tabs: {
active: value,
items: this.props.tabs.items,
allowNewTabs: this.props.tabs.allowNewTabs,
},
});
this._sendConfig();
}
/**
* Function to Select a Tab
* @param tab
*/
public async selectTab(tab: ITab | string) {
let newTabToDisplay: ITab;
if (typeof tab === "string") {
for (const _t of this.tabs) {
if (_t.id === tab) {
newTabToDisplay = _t;
break;
2020-10-30 18:30:59 +00:00
}
2021-08-26 17:41:08 +00:00
}
} else {
newTabToDisplay = tab;
}
2020-10-30 18:30:59 +00:00
2021-08-26 17:41:08 +00:00
if (typeof this.state.onTabSelect === "function") {
const currentlySelected = this.selectedTab;
2020-10-30 18:30:59 +00:00
2021-08-26 17:41:08 +00:00
// Call the Async Function to select a Tab.
if (await this.state.onTabSelect(currentlySelected, newTabToDisplay)) {
this.activeTab = newTabToDisplay.id;
2020-10-30 18:30:59 +00:00
2021-08-26 17:41:08 +00:00
return true;
}
2020-10-30 18:30:59 +00:00
2021-08-26 17:41:08 +00:00
return false;
} else {
// Assign the Tab ID
this.activeTab = newTabToDisplay.id;
2020-10-30 18:30:59 +00:00
2021-08-26 17:41:08 +00:00
return true;
2020-10-30 18:30:59 +00:00
}
2021-08-26 17:41:08 +00:00
}
2020-10-30 18:30:59 +00:00
2021-08-26 17:41:08 +00:00
public async editTab(tab: ITab | string) {
let tabToEdit: ITab;
let idx = -1;
2020-10-30 18:30:59 +00:00
2021-08-26 17:41:08 +00:00
const tabs = this.tabs;
2020-10-30 18:30:59 +00:00
2021-08-26 17:41:08 +00:00
for (const [_idx, _tab] of this.tabs.entries()) {
if (_tab.id === (typeof tab === "string" ? tab : tab.id)) {
tabToEdit = _tab;
idx = _idx;
break;
}
}
2020-10-30 18:30:59 +00:00
2021-08-26 17:41:08 +00:00
if (tabToEdit && typeof this.state.onTabEdit === "function") {
const editedTab = await this.state.onTabEdit(tabToEdit);
tabs[idx] = editedTab;
2020-10-30 18:30:59 +00:00
2021-08-26 17:41:08 +00:00
this.tabs = tabs;
2020-10-30 18:30:59 +00:00
}
2021-08-26 17:41:08 +00:00
}
2020-10-30 18:30:59 +00:00
2021-08-26 17:41:08 +00:00
public async delteTab(tab: ITab | string, forced = false) {
let idx = -1;
let _tab: ITab = null;
2020-10-30 18:30:59 +00:00
2021-08-26 17:41:08 +00:00
const tabs = this.state.tabs.items;
2020-10-30 18:30:59 +00:00
2021-08-26 17:41:08 +00:00
for (const [_idx, _t] of tabs.entries()) {
if (_t.id === (typeof tab === "string" ? tab : tab.id)) {
idx = _idx;
_tab = _t;
break;
}
}
2020-10-30 18:30:59 +00:00
2021-08-26 17:41:08 +00:00
if (_tab != null) {
let removed = false;
if (typeof this.state.onTabDelete === "function") {
// Call the Async Function to select a Tab.
if (await this.state.onTabDelete(_tab, forced)) {
removed = true;
2020-10-30 18:30:59 +00:00
}
2021-08-26 17:41:08 +00:00
} else {
removed = true;
}
if (removed) {
// Remove the Items.
tabs.splice(idx, 1);
// Check if the active element has been removed:
if (_tab.id == this.state.tabs.active) {
// Assign the ID of the Element.
const _idx = tabs.length > idx ? idx : tabs.length - 1;
if (_idx !== -1) {
// Assign the First ID.
const _newTab = tabs.length > _idx ? tabs[_idx] : null;
if (_newTab !== null) {
if (
!(await this.selectTab(_newTab)) &&
typeof this.state.onNoTabSelected === "function"
) {
// Send a Warning, that no Tabs has been selected
await this.state.onNoTabSelected();
}
} else if (typeof this.state.onNoTabSelected === "function") {
// Send a Warning, that no Tabs has been selected
await this.state.onNoTabSelected();
2020-10-30 18:30:59 +00:00
}
2021-08-26 17:41:08 +00:00
} else if (typeof this.state.onNoTabSelected === "function") {
// Send a Warning, that no Tabs has been selected
await this.state.onNoTabSelected();
}
2020-10-30 18:30:59 +00:00
}
2021-08-26 17:41:08 +00:00
this.tabs = tabs;
this._sendConfig();
}
2020-10-30 18:30:59 +00:00
}
2021-08-26 17:41:08 +00:00
}
public render() {
return (
<>
<Nav variant="tabs" activeKey={this.state.tabs.active} justify>
{this.state.tabs.items.map((tab, idx) => {
if (tab.delteable && this.state.tabs.allowNewTabs) {
return (
<Nav.Item key={tab.id} onDoubleClick={(_) => this.editTab(tab)}>
<Nav.Link
onSelect={(_) => {
this.selectTab(tab);
}}
eventKey={tab.id}
>
{tab.label + " "}
<FaTimesCircle
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
this.delteTab(tab);
}}
></FaTimesCircle>
</Nav.Link>
</Nav.Item>
);
} else {
return (
<Nav.Item key={tab.id}>
<Nav.Link
onSelect={(_) => {
this.selectTab(tab);
}}
eventKey={tab.id}
>
{tab.label}
</Nav.Link>
</Nav.Item>
);
}
})}
{this.state.tabs.allowNewTabs ? (
<Nav.Item>
<Nav.Link
eventKey="_NEW_ITEM"
onSelect={(_) => this.requestTab()}
>
+
</Nav.Link>
</Nav.Item>
) : (
""
)}
</Nav>
</>
);
}
2020-10-30 18:30:59 +00:00
}
2021-08-26 17:41:08 +00:00
export default TabEntry;