307 lines
7.6 KiB
TypeScript
307 lines
7.6 KiB
TypeScript
/**
|
|
* @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]
|
|
*
|
|
* A Basic Layout.
|
|
* It uses a Toolbar, Sidebar
|
|
*
|
|
* Toast are implemented by https://fkhadra.github.io/react-toastify/introduction
|
|
*/
|
|
|
|
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";
|
|
|
|
export interface ITabProps {
|
|
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[];
|
|
};
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
protected _sendConfig() {
|
|
if (typeof this.props.onConfigChanged === "function") {
|
|
this.props.onConfigChanged(deepClone(this.state));
|
|
}
|
|
}
|
|
|
|
public get selectedTab() {
|
|
for (const tab of this.state.tabs.items) {
|
|
if (tab.id === this.state.tabs.active) {
|
|
return tab;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public async requestTab() {
|
|
if (typeof this.state.onNewTab === "function") {
|
|
const tab = await this.state.onNewTab();
|
|
|
|
if (typeof tab === "object") {
|
|
this.state.tabs.items.push(tab);
|
|
|
|
await this.selectTab(tab);
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
} else {
|
|
newTabToDisplay = tab;
|
|
}
|
|
|
|
if (typeof this.state.onTabSelect === "function") {
|
|
const currentlySelected = this.selectedTab;
|
|
|
|
// Call the Async Function to select a Tab.
|
|
if (await this.state.onTabSelect(currentlySelected, newTabToDisplay)) {
|
|
this.activeTab = newTabToDisplay.id;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
} else {
|
|
// Assign the Tab ID
|
|
this.activeTab = newTabToDisplay.id;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public async editTab(tab: ITab | string) {
|
|
let tabToEdit: ITab;
|
|
let idx = -1;
|
|
|
|
const tabs = this.tabs;
|
|
|
|
for (const [_idx, _tab] of this.tabs.entries()) {
|
|
if (_tab.id === (typeof tab === "string" ? tab : tab.id)) {
|
|
tabToEdit = _tab;
|
|
idx = _idx;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (tabToEdit && typeof this.state.onTabEdit === "function") {
|
|
const editedTab = await this.state.onTabEdit(tabToEdit);
|
|
tabs[idx] = editedTab;
|
|
|
|
this.tabs = tabs;
|
|
}
|
|
}
|
|
|
|
public async delteTab(tab: ITab | string, forced = false) {
|
|
let idx = -1;
|
|
let _tab: ITab = null;
|
|
|
|
const tabs = this.state.tabs.items;
|
|
|
|
for (const [_idx, _t] of tabs.entries()) {
|
|
if (_t.id === (typeof tab === "string" ? tab : tab.id)) {
|
|
idx = _idx;
|
|
_tab = _t;
|
|
break;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
} 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();
|
|
}
|
|
} else if (typeof this.state.onNoTabSelected === "function") {
|
|
// Send a Warning, that no Tabs has been selected
|
|
await this.state.onNoTabSelected();
|
|
}
|
|
}
|
|
this.tabs = tabs;
|
|
this._sendConfig();
|
|
}
|
|
}
|
|
}
|
|
|
|
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>
|
|
</>
|
|
);
|
|
}
|
|
}
|
|
|
|
export default TabEntry;
|