From 51931f11fe5193a2c5555df9dd92568e3e4bfb9f Mon Sep 17 00:00:00 2001 From: MrKBear Date: Wed, 2 Mar 2022 14:51:47 +0800 Subject: [PATCH] Add switch tab --- source/Component/Container/Container.scss | 10 +- source/Component/Container/Container.tsx | 188 ++++++++++++------- source/Component/Container/RootContainer.tsx | 5 + source/Model/Layout.ts | 30 +++ 4 files changed, 160 insertions(+), 73 deletions(-) diff --git a/source/Component/Container/Container.scss b/source/Component/Container/Container.scss index 6f6609d..fd7064d 100644 --- a/source/Component/Container/Container.scss +++ b/source/Component/Container/Container.scss @@ -43,6 +43,7 @@ div.app-container { box-sizing: border-box; display: flex; border: .8px solid rgba($color: #000000, $alpha: 0); + // transition: all 300ms ease-in-out; justify-content: space-between; align-items: stretch; flex-direction: column; @@ -63,7 +64,7 @@ div.app-container { box-sizing: border-box; height: 32.8px; border: .8px solid rgba($color: #000000, $alpha: 0); - transition: all 300ms ease-in-out; + // transition: all 300ms ease-in-out; } div.title-view { @@ -82,6 +83,7 @@ div.app-container { div.app-tab-header-item.active { border: .8px solid blue; + transition: none; } div.app-tab-header-item::after { @@ -116,8 +118,10 @@ div.app-panel::-webkit-scrollbar { div.dark.app-container.end-containe { border: .8px solid $lt-bg-color-lvl3-dark; + div.app-tab-header-item.tab, div.app-tab-header-item.active, div.app-tab-header-item:hover { + transition: none; background-color: $lt-bg-color-lvl4-dark; color: rgba($color: #FFFFFF, $alpha: .85); } @@ -125,6 +129,7 @@ div.dark.app-container.end-containe { div.app-tab-header-item.active { div.border-view::after { background-color: $lt-bg-color-lvl4-dark; + transition: none; } } } @@ -132,14 +137,17 @@ div.dark.app-container.end-containe { div.light.app-container.end-containe { border: .8px solid $lt-bg-color-lvl3-light; + div.app-tab-header-item.tab, div.app-tab-header-item.active, div.app-tab-header-item:hover { + transition: none; color: rgba($color: #000000, $alpha: .85); } div.app-tab-header-item.active { div.border-view::after { background-color: $lt-bg-color-lvl4-light; + transition: none; } } } \ No newline at end of file diff --git a/source/Component/Container/Container.tsx b/source/Component/Container/Container.tsx index 1c88ed0..8daa18a 100644 --- a/source/Component/Container/Container.tsx +++ b/source/Component/Container/Container.tsx @@ -1,7 +1,7 @@ import { Theme, BackgroundLevel, FontLevel } from "@Component/Theme/Theme"; import { Themes } from "@Context/Setting"; import { ILayout, LayoutDirection } from "@Model/Layout"; -import { Component, ReactNode } from "react"; +import { Component, ReactNode, MouseEvent } from "react"; import "./Container.scss"; interface IContainerProps extends ILayout { @@ -10,17 +10,17 @@ interface IContainerProps extends ILayout { theme?: Themes; focusId?: string; onScaleChange?: (id: number, scale: number) => any; + onFocusTab?: (id: string) => any; } function getPanelById(id: string) { - return {id} + return {id} } class Container extends Component { private focusEdgeId: number | undefined; + private readonly edgeInfo = { direction: LayoutDirection.Y, rootWidth: 0, @@ -31,34 +31,116 @@ class Container extends Component { mouseY: 0 }; - private renderPanel(panles: string[], showBar: boolean) { + /** + * 渲染此 Tab 下的 ELE + */ + private renderPanel(panles: string[], showBar: boolean, focus?: string) { const classList: string[] = []; const theme: Themes = this.props.theme ?? Themes.dark; + const showPanelId = focus ?? panles[0]; classList.push(theme === Themes.light ? "light" : "dark"); classList.push(`background-${BackgroundLevel.Level3}`); classList.push(`font-${FontLevel.Level3}`); classList.push("app-tab-header"); + const hasActivePanel = panles.some((id) => id === this.props.focusId); + return <> {showBar ?
{ panles.map((panelId: string) => { - return
+ + const classList: string[] = ["app-tab-header-item"]; + if (panelId === this.props.focusId) classList.push("active"); + if (panelId === showPanelId) classList.push("tab"); + + return
this.props.onFocusTab ? this.props.onFocusTab(panelId) : undefined} + >
-
{panelId}
+
{panelId}
}) }
: null } - {getPanelById(panles[0])} +
this.props.onFocusTab ? this.props.onFocusTab(showPanelId) : undefined} + className={"app-panel" + (hasActivePanel ? " active" : "")} + draggable={false} + > + {getPanelById(showPanelId)} +
} - private renderContainer( - props: IContainerProps, - selfScale: number = 50, + /** + * 处理鼠标移动数据 + */ + private handelMouseMove = (e: MouseEvent) => { + + if (this.props.onScaleChange && this.focusEdgeId !== undefined) { + e.preventDefault(); + let mouveDist: number = 0; + let rootSize: number = 0; + let edgeSize: number = 0; + let newSize: number = 0; + + if (this.edgeInfo.direction === LayoutDirection.X) { + mouveDist = e.clientX - this.edgeInfo.mouseX; + rootSize = this.edgeInfo.rootWidth; + edgeSize = this.edgeInfo.edgeWidth; + newSize = edgeSize + mouveDist; + } + + if (this.edgeInfo.direction === LayoutDirection.Y) { + mouveDist = e.clientY - this.edgeInfo.mouseY; + rootSize = this.edgeInfo.rootHeight; + edgeSize = this.edgeInfo.edgeHeight + newSize = edgeSize + mouveDist; + } + + if (newSize < 38) { newSize = 38; } + if ((rootSize - newSize) < 38) { newSize = rootSize - 38; } + + let newScale = newSize / rootSize; + this.props.onScaleChange(this.focusEdgeId, newScale * 100); + } + } + + /** + * 处理鼠标按下事件 + * 记录鼠标数据 + */ + private handelMouseDown = (props: ILayout, e: MouseEvent) => { + const targetNode = e.target; + + if (targetNode instanceof HTMLDivElement) { + let root = targetNode.parentNode?.parentNode; + let firstDiv = targetNode.parentNode?.parentNode?.childNodes[0]; + + if (root instanceof HTMLDivElement && firstDiv instanceof HTMLDivElement) { + this.edgeInfo.rootWidth = root.offsetWidth; + this.edgeInfo.rootHeight = root.offsetHeight; + this.edgeInfo.edgeWidth = firstDiv.offsetWidth; + this.edgeInfo.edgeHeight = firstDiv.offsetHeight; + } + } + this.edgeInfo.mouseX = e.clientX; + this.edgeInfo.mouseY = e.clientY; + + this.edgeInfo.direction = props.layout ?? LayoutDirection.Y; + this.focusEdgeId = props.id ?? 0; + } + + /** + * 递归渲染全部容器 + */ + private renderContainer ( + props: IContainerProps, selfScale: number = 50, selfLayout: LayoutDirection = LayoutDirection.Y ) { @@ -70,6 +152,7 @@ class Container extends Component { const isRoot: boolean = !!props.isRoot; const classList: string[] = []; const theme: Themes = this.props.theme ?? Themes.dark; + const focusPanel: string | undefined = props.focusPanel; classList.push(theme === Themes.light ? "light" : "dark"); classList.push(`background-${BackgroundLevel.Level4}`); @@ -86,69 +169,30 @@ class Container extends Component { width: isRoot ? "100%" : selfLayout === LayoutDirection.X ? `${selfScale}%` : undefined, height: isRoot ? "100%" : selfLayout === LayoutDirection.Y ? `${selfScale}%` : undefined }} - onMouseMove={isRoot ? (e) => { - if (this.props.onScaleChange && this.focusEdgeId !== undefined) { - e.preventDefault(); - let mouveDist: number = 0; - let rootSize: number = 0; - let edgeSize: number = 0; - let newSize: number = 0; - - if (this.edgeInfo.direction === LayoutDirection.X) { - mouveDist = e.clientX - this.edgeInfo.mouseX; - rootSize = this.edgeInfo.rootWidth; - edgeSize = this.edgeInfo.edgeWidth; - newSize = edgeSize + mouveDist; - } - - if (this.edgeInfo.direction === LayoutDirection.Y) { - mouveDist = e.clientY - this.edgeInfo.mouseY; - rootSize = this.edgeInfo.rootHeight; - edgeSize = this.edgeInfo.edgeHeight - newSize = edgeSize + mouveDist; - } - - if (newSize < 38) { newSize = 38; } - if ((rootSize - newSize) < 38) { newSize = rootSize - 38; } - - let newScale = newSize / rootSize; - this.props.onScaleChange(this.focusEdgeId, newScale * 100); - } - } : undefined} - onMouseUp={isRoot ? () => { - this.focusEdgeId = undefined; - } : undefined} + onMouseMove={isRoot ? this.handelMouseMove : undefined} + onMouseUp={isRoot ? () => this.focusEdgeId = undefined : undefined} > - {panles.length > 0 && !items ? this.renderPanel(panles, showBar) : null} - {items && items[0] ? this.renderContainer(items[0], scale, layout) : null} - {items && items[1] ?
-
{ - const targetNode = e.target; - if (targetNode instanceof HTMLDivElement) { - let root = targetNode.parentNode?.parentNode; - let firstDiv = targetNode.parentNode?.parentNode?.childNodes[0]; + {/* 渲染 Panel */} + {panles.length > 0 && !items ? this.renderPanel(panles, showBar, focusPanel) : null} - if (root instanceof HTMLDivElement && firstDiv instanceof HTMLDivElement) { - this.edgeInfo.rootWidth = root.offsetWidth; - this.edgeInfo.rootHeight = root.offsetHeight; - this.edgeInfo.edgeWidth = firstDiv.offsetWidth; - this.edgeInfo.edgeHeight = firstDiv.offsetHeight; - } - } - this.edgeInfo.mouseX = e.clientX; - this.edgeInfo.mouseY = e.clientY; - this.edgeInfo.direction = props.layout ?? LayoutDirection.Y; - this.focusEdgeId = props.id ?? 0; - }} - onMouseUp={() => { this.focusEdgeId = undefined }} - > -
-
: null} + {/* 渲染第一部分 */} + {items && items[0] ? this.renderContainer(items[0], scale, layout) : null} + + {/* 渲染拖拽条 */} + {items && items[1] ? +
+
this.focusEdgeId = undefined } + /> +
: null + } + + {/* 渲染第二部分 */} {items && items[1] ? this.renderContainer(items[1], 100 - scale, layout) : null}
} diff --git a/source/Component/Container/RootContainer.tsx b/source/Component/Container/RootContainer.tsx index c80d1fa..6f01c42 100644 --- a/source/Component/Container/RootContainer.tsx +++ b/source/Component/Container/RootContainer.tsx @@ -13,6 +13,7 @@ class RootContainer extends Component { if (this.props.setting) { this.props.setting.layout.on("layoutChange", this.handelChange); this.props.setting.layout.on("scaleChange", this.handelChange); + this.props.setting.layout.on("switchTab", this.handelChange); this.props.setting.on("themes", this.handelChange); } } @@ -21,6 +22,7 @@ class RootContainer extends Component { if (this.props.setting) { this.props.setting.layout.off("layoutChange", this.handelChange); this.props.setting.layout.off("scaleChange", this.handelChange); + this.props.setting.layout.off("switchTab", this.handelChange); this.props.setting.off("themes", this.handelChange); } } @@ -28,6 +30,7 @@ class RootContainer extends Component { public render(): ReactNode { const layoutData = this.props.setting ? this.props.setting.layout.getData() : {}; const theme = this.props.setting?.themes ?? Themes.dark; + const focusId = this.props.setting?.layout.focusId ?? ""; return { theme={theme} isRoot={true} onScaleChange={this.props.setting?.layout.setScale} + onFocusTab={this.props.setting?.layout.focus} id={layoutData.id} + focusId={focusId} /> } } diff --git a/source/Model/Layout.ts b/source/Model/Layout.ts index 5ce21ac..8f6b100 100644 --- a/source/Model/Layout.ts +++ b/source/Model/Layout.ts @@ -8,6 +8,7 @@ enum LayoutDirection { class ILayout { items?: [ILayout, ILayout]; panles?: string[]; + focusPanel?: string; layout?: LayoutDirection; scale?: number; id?: number; @@ -16,6 +17,7 @@ class ILayout { interface ILayoutEvent { layoutChange: Layout; scaleChange: Layout; + switchTab: Layout; } class Layout extends Emitter { @@ -24,6 +26,11 @@ class Layout extends Emitter { private data: ILayout = {}; + /** + * 焦点面板 ID + */ + public focusId: string = ""; + private map(fn: (layout: ILayout) => boolean | void, layout?: ILayout) { const currentLayout = layout ? layout : this.data; if( fn(currentLayout) ) return; @@ -44,6 +51,9 @@ class Layout extends Emitter { this.id = 0; this.map((layout) => { layout.id = this.id; + if (!layout.focusPanel && layout.panles && layout.panles.length > 0) { + layout.focusPanel = layout.panles[0] + } this.id ++; }); this.emit("layoutChange", this); @@ -62,6 +72,26 @@ class Layout extends Emitter { this.emit("scaleChange", this); } } + + public focus = (panelId: string) => { + this.map((layout) => { + if (layout.panles && layout.panles.length > 0) { + let index = -1; + for (let i = 0; i < layout.panles.length; i++) { + if (layout.panles[i] === panelId) { + index = i; + break; + } + } + if (index >= 0) { + layout.focusPanel = panelId; + this.focusId = panelId; + this.emit("switchTab", this); + return true; + } + } + }) + } }; export { Layout, ILayout, LayoutDirection }; \ No newline at end of file