Add panel drag resize ctrl #10
| @ -7,7 +7,6 @@ div.app-container { | |||||||
| 	height: 100%; | 	height: 100%; | ||||||
| 	display: flex; | 	display: flex; | ||||||
| 	overflow: hidden; | 	overflow: hidden; | ||||||
| 	justify-content: center; |  | ||||||
| 	box-sizing: border-box; | 	box-sizing: border-box; | ||||||
| 
 | 
 | ||||||
| 	div.drag-bar { | 	div.drag-bar { | ||||||
| @ -22,13 +21,20 @@ div.app-container { | |||||||
| 			z-index: 10; | 			z-index: 10; | ||||||
| 			width: 100%; | 			width: 100%; | ||||||
| 			height: 100%; | 			height: 100%; | ||||||
| 			border: 4.5px solid rgba($color: #000000, $alpha: 0); | 			border: 2px solid rgba($color: #000000, $alpha: 0); | ||||||
|  | 			transition: all 300ms ease-in-out; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		div:hover { | ||||||
|  | 			background-color: blue; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	div.app-tab-header { | 	div.app-tab-header { | ||||||
| 		height: $container-header-tab-bar-height; | 		height: $container-header-tab-bar-height; | ||||||
| 		flex-shrink: 0; | 		min-height: $container-header-tab-bar-height; | ||||||
|  | 		flex-shrink: 1; | ||||||
|  | 		width: 100%; | ||||||
| 		display: flex; | 		display: flex; | ||||||
| 		user-select: none; | 		user-select: none; | ||||||
| 		 | 		 | ||||||
| @ -66,6 +72,10 @@ div.app-container { | |||||||
| 				vertical-align: middle; | 				vertical-align: middle; | ||||||
| 				position: relative; | 				position: relative; | ||||||
| 				transition: all 300ms ease-in-out; | 				transition: all 300ms ease-in-out; | ||||||
|  | 				word-break: keep-all; | ||||||
|  | 				white-space: nowrap; | ||||||
|  | 				overflow: hidden; | ||||||
|  | 				text-overflow: ellipsis; | ||||||
| 				z-index: 1; | 				z-index: 1; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| @ -94,6 +104,15 @@ div.app-container { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | div.app-panel { | ||||||
|  | 	overflow: scroll; | ||||||
|  | 	-ms-overflow-style: none; | ||||||
|  | } | ||||||
|  | div.app-panel::-webkit-scrollbar { | ||||||
|  | 	width: 0 !important; | ||||||
|  | 	height: 0 !important; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| div.dark.app-container.end-containe { | div.dark.app-container.end-containe { | ||||||
| 	border: .8px solid $lt-bg-color-lvl3-dark; | 	border: .8px solid $lt-bg-color-lvl3-dark; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| import { Theme, BackgroundLevel, FontLevel } from "@Component/Theme/Theme"; | import { Theme, BackgroundLevel, FontLevel } from "@Component/Theme/Theme"; | ||||||
|  | import { Themes } from "@Context/Setting"; | ||||||
| import { ILayout, LayoutDirection } from "@Model/Layout"; | import { ILayout, LayoutDirection } from "@Model/Layout"; | ||||||
| import { Component, ReactNode } from "react"; | import { Component, ReactNode } from "react"; | ||||||
| import "./Container.scss"; | import "./Container.scss"; | ||||||
| @ -6,29 +7,50 @@ import "./Container.scss"; | |||||||
| interface IContainerProps extends ILayout { | interface IContainerProps extends ILayout { | ||||||
| 	showBar?: boolean; | 	showBar?: boolean; | ||||||
| 	isRoot?: boolean; | 	isRoot?: boolean; | ||||||
|  | 	theme?: Themes; | ||||||
|  | 	focusId?: string; | ||||||
|  | 	onScaleChange?: (id: number, scale: number) => any; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function getPanelById(id: string) { | function getPanelById(id: string) { | ||||||
| 	return <Theme className="app-panel">{id}</Theme> | 	return <Theme | ||||||
|  | 		className="app-panel" draggable={false} | ||||||
|  | 	>{id}</Theme> | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class Container extends Component<IContainerProps> { | class Container extends Component<IContainerProps> { | ||||||
| 
 | 
 | ||||||
|  | 	private focusEdgeId: number | undefined; | ||||||
|  | 	private readonly edgeInfo = { | ||||||
|  | 		direction: LayoutDirection.Y, | ||||||
|  | 		rootWidth: 0, | ||||||
|  | 		rootHeight: 0, | ||||||
|  | 		edgeWidth: 0, | ||||||
|  | 		edgeHeight: 0, | ||||||
|  | 		mouseX: 0, | ||||||
|  | 		mouseY: 0 | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
| 	private renderPanel(panles: string[], showBar: boolean) { | 	private renderPanel(panles: string[], showBar: boolean) { | ||||||
|  | 
 | ||||||
|  | 		const classList: string[] = []; | ||||||
|  | 		const theme: Themes = this.props.theme ?? Themes.dark; | ||||||
|  | 
 | ||||||
|  | 		classList.push(theme === Themes.light ? "light" : "dark"); | ||||||
|  | 		classList.push(`background-${BackgroundLevel.Level3}`); | ||||||
|  | 		classList.push(`font-${FontLevel.Level3}`); | ||||||
|  | 		classList.push("app-tab-header"); | ||||||
|  | 
 | ||||||
| 		return <> | 		return <> | ||||||
| 			{showBar ?  | 			{showBar ?  | ||||||
| 				<Theme | 				<div className={classList.join(" ")} >{ | ||||||
| 					className="app-tab-header" |  | ||||||
| 					backgroundLevel={BackgroundLevel.Level3} |  | ||||||
| 					fontLevel={FontLevel.Level3} |  | ||||||
| 				>{ |  | ||||||
| 					panles.map((panelId: string) => { | 					panles.map((panelId: string) => { | ||||||
| 						return <div key={panelId} className="app-tab-header-item"> | 						return <div key={panelId} className="app-tab-header-item"> | ||||||
| 							<div className="border-view"></div> | 							<div className="border-view"></div> | ||||||
| 							<div className="title-view" >{panelId}</div> | 							<div className="title-view" >{panelId}</div> | ||||||
| 						</div> | 						</div> | ||||||
| 					}) | 					}) | ||||||
| 				}</Theme> : null | 				}</div> : null | ||||||
| 			} | 			} | ||||||
| 			{getPanelById(panles[0])} | 			{getPanelById(panles[0])} | ||||||
| 		</> | 		</> | ||||||
| @ -46,16 +68,56 @@ class Container extends Component<IContainerProps> { | |||||||
| 		const layout: LayoutDirection = props.layout ?? LayoutDirection.Y; | 		const layout: LayoutDirection = props.layout ?? LayoutDirection.Y; | ||||||
| 		const scale: number = props.scale ?? 50; | 		const scale: number = props.scale ?? 50; | ||||||
| 		const isRoot: boolean = !!props.isRoot; | 		const isRoot: boolean = !!props.isRoot; | ||||||
|  | 		const classList: string[] = []; | ||||||
|  | 		const theme: Themes = this.props.theme ?? Themes.dark; | ||||||
| 
 | 
 | ||||||
| 		return <Theme | 		classList.push(theme === Themes.light ? "light" : "dark"); | ||||||
| 			className={"app-container" + (panles.length > 0 && !items ? " end-containe" : "")} | 		classList.push(`background-${BackgroundLevel.Level4}`); | ||||||
| 			backgroundLevel={BackgroundLevel.Level4} | 		classList.push(`font-${FontLevel.normal}`); | ||||||
| 			fontLevel={FontLevel.normal} | 		classList.push("app-container"); | ||||||
|  | 		if (panles.length > 0 && !items) classList.push("end-containe"); | ||||||
|  | 
 | ||||||
|  | 		return <div | ||||||
|  | 			className={classList.join(" ")} | ||||||
|  | 			draggable={false} | ||||||
| 			style={{ | 			style={{ | ||||||
|  | 				transition: "none", | ||||||
| 				flexDirection: layout === LayoutDirection.Y ? "column" : undefined, | 				flexDirection: layout === LayoutDirection.Y ? "column" : undefined, | ||||||
| 				width: isRoot ? "100%" : selfLayout === LayoutDirection.X ? `${selfScale}%` : undefined, | 				width: isRoot ? "100%" : selfLayout === LayoutDirection.X ? `${selfScale}%` : undefined, | ||||||
| 				height: isRoot ? "100%" : selfLayout === LayoutDirection.Y ? `${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} | ||||||
| 		> | 		> | ||||||
| 			{panles.length > 0 && !items ? this.renderPanel(panles, showBar) : null} | 			{panles.length > 0 && !items ? this.renderPanel(panles, showBar) : null} | ||||||
| 			{items && items[0] ? this.renderContainer(items[0], scale, layout) : null} | 			{items && items[0] ? this.renderContainer(items[0], scale, layout) : null} | ||||||
| @ -63,13 +125,32 @@ class Container extends Component<IContainerProps> { | |||||||
| 				width: layout === LayoutDirection.Y ? "100%" : 0, | 				width: layout === LayoutDirection.Y ? "100%" : 0, | ||||||
| 				height: layout === LayoutDirection.X ? "100%" : 0 | 				height: layout === LayoutDirection.X ? "100%" : 0 | ||||||
| 			}}> | 			}}> | ||||||
| 				<div style={{ | 				<div | ||||||
| 					cursor: layout === LayoutDirection.Y ? "n-resize" : "e-resize" | 					style={{ cursor: layout === LayoutDirection.Y ? "n-resize" : "e-resize" }} | ||||||
| 				}}> | 					onMouseDown={(e) => { | ||||||
|  | 						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; | ||||||
|  | 					}} | ||||||
|  | 					onMouseUp={() => { this.focusEdgeId = undefined }} | ||||||
|  | 				> | ||||||
| 				</div> | 				</div> | ||||||
| 			</div> : null} | 			</div> : null} | ||||||
| 			{items && items[1] ? this.renderContainer(items[1], 100 - scale, layout) : null} | 			{items && items[1] ? this.renderContainer(items[1], 100 - scale, layout) : null} | ||||||
| 		</Theme> | 		</div> | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	public render(): ReactNode { | 	public render(): ReactNode { | ||||||
|  | |||||||
							
								
								
									
										43
									
								
								source/Component/Container/RootContainer.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								source/Component/Container/RootContainer.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | |||||||
|  | import { Component, ReactNode } from "react"; | ||||||
|  | import { useSetting, IMixinSettingProps, Themes } from "@Context/Setting"; | ||||||
|  | import { Container } from "./Container"; | ||||||
|  | 
 | ||||||
|  | @useSetting | ||||||
|  | class RootContainer extends Component<IMixinSettingProps> { | ||||||
|  | 
 | ||||||
|  | 	private handelChange = () => { | ||||||
|  | 		this.forceUpdate(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public componentDidMount() { | ||||||
|  | 		if (this.props.setting) { | ||||||
|  | 			this.props.setting.layout.on("layoutChange", this.handelChange); | ||||||
|  | 			this.props.setting.layout.on("scaleChange", this.handelChange); | ||||||
|  | 			this.props.setting.on("themes", this.handelChange); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public componentWillUnmount() { | ||||||
|  | 		if (this.props.setting) { | ||||||
|  | 			this.props.setting.layout.off("layoutChange", this.handelChange); | ||||||
|  | 			this.props.setting.layout.off("scaleChange", this.handelChange); | ||||||
|  | 			this.props.setting.off("themes", this.handelChange); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public render(): ReactNode { | ||||||
|  | 		const layoutData = this.props.setting ? this.props.setting.layout.getData() : {}; | ||||||
|  | 		const theme = this.props.setting?.themes ?? Themes.dark; | ||||||
|  | 		return <Container | ||||||
|  | 			scale={layoutData.scale} | ||||||
|  | 			items={layoutData.items} | ||||||
|  | 			layout={layoutData.layout} | ||||||
|  | 			theme={theme} | ||||||
|  | 			isRoot={true} | ||||||
|  | 			onScaleChange={this.props.setting?.layout.setScale} | ||||||
|  | 			id={layoutData.id} | ||||||
|  | 		/> | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export { RootContainer } | ||||||
| @ -1,5 +1,6 @@ | |||||||
| import { createContext, Component, FunctionComponent } from "react"; | import { createContext, Component, FunctionComponent } from "react"; | ||||||
| import { Emitter } from "@Model/Emitter"; | import { Emitter } from "@Model/Emitter"; | ||||||
|  | import { Layout } from "@Model/Layout"; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * 主题模式 |  * 主题模式 | ||||||
| @ -25,6 +26,11 @@ class Setting extends Emitter< | |||||||
|      */ |      */ | ||||||
|     public language: Language = "EN_US"; |     public language: Language = "EN_US"; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * 布局 | ||||||
|  |      */ | ||||||
|  |     public layout: Layout = new Layout(); | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * 设置参数 |      * 设置参数 | ||||||
|      */ |      */ | ||||||
|  | |||||||
| @ -10,12 +10,58 @@ class ILayout { | |||||||
| 	panles?: string[]; | 	panles?: string[]; | ||||||
| 	layout?: LayoutDirection; | 	layout?: LayoutDirection; | ||||||
| 	scale?: number; | 	scale?: number; | ||||||
|  | 	id?: number; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class Layout extends Emitter<{}> { | interface ILayoutEvent { | ||||||
|  | 	layoutChange: Layout; | ||||||
|  | 	scaleChange: Layout; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | class Layout extends Emitter<ILayoutEvent> { | ||||||
|  | 
 | ||||||
|  | 	private id: number = 0; | ||||||
| 
 | 
 | ||||||
| 	private data: ILayout = {}; | 	private data: ILayout = {}; | ||||||
| 
 | 
 | ||||||
|  | 	private map(fn: (layout: ILayout) => boolean | void, layout?: ILayout) { | ||||||
|  | 		const currentLayout = layout ? layout : this.data; | ||||||
|  | 		if( fn(currentLayout) ) return; | ||||||
|  | 		if (currentLayout.items && currentLayout.items[0]) { | ||||||
|  | 			this.map(fn, currentLayout.items[0]); | ||||||
|  | 		} | ||||||
|  | 		if (currentLayout.items && currentLayout.items[1]) { | ||||||
|  | 			this.map(fn, currentLayout.items[1]); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public getData = (): ILayout => { | ||||||
|  | 		return this.data; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public setData = (data: ILayout) => { | ||||||
|  | 		this.data = data; | ||||||
|  | 		this.id = 0; | ||||||
|  | 		this.map((layout) => { | ||||||
|  | 			layout.id = this.id; | ||||||
|  | 			this.id ++; | ||||||
|  | 		}); | ||||||
|  | 		this.emit("layoutChange", this); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public setScale = (id: number, scale: number) => { | ||||||
|  | 		let change = false; | ||||||
|  | 		this.map((layout) => { | ||||||
|  | 			if (layout.id === id) { | ||||||
|  | 				layout.scale = scale; | ||||||
|  | 				change = true;	 | ||||||
|  | 			} | ||||||
|  | 			return change; | ||||||
|  | 		}) | ||||||
|  | 		if (change) { | ||||||
|  | 			this.emit("scaleChange", this); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export { Layout, ILayout, LayoutDirection }; | export { Layout, ILayout, LayoutDirection }; | ||||||
| @ -6,7 +6,7 @@ import { Entry } from "../Entry/Entry"; | |||||||
| import { StatusProvider, Status } from "@Context/Status"; | import { StatusProvider, Status } from "@Context/Status"; | ||||||
| import { ClassicRenderer } from "@GLRender/ClassicRenderer"; | import { ClassicRenderer } from "@GLRender/ClassicRenderer"; | ||||||
| import { initializeIcons } from '@fluentui/font-icons-mdl2'; | import { initializeIcons } from '@fluentui/font-icons-mdl2'; | ||||||
| import { Container } from "@Component/Container/Container"; | import { RootContainer } from "@Component/Container/RootContainer"; | ||||||
| import { LayoutDirection } from "@Model/Layout"; | import { LayoutDirection } from "@Model/Layout"; | ||||||
| import "./SimulatorWeb.scss"; | import "./SimulatorWeb.scss"; | ||||||
| import { CommandBar } from "@Component/CommandBar/CommandBar"; | import { CommandBar } from "@Component/CommandBar/CommandBar"; | ||||||
| @ -55,26 +55,9 @@ class SimulatorWeb extends Component { | |||||||
|         (window as any).s = this; |         (window as any).s = this; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public render(): ReactNode { |     public componentDidMount() { | ||||||
|         return <SettingProvider value={this.setting}> |         this.setting.layout.setData({ | ||||||
|             <StatusProvider value={this.status}> |             items: [ | ||||||
|                 {this.renderContent()} |  | ||||||
|             </StatusProvider> |  | ||||||
|         </SettingProvider> |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private renderContent(): ReactNode { |  | ||||||
|         return <Theme  |  | ||||||
|             className="app-root" |  | ||||||
|             backgroundLevel={BackgroundLevel.Level5} |  | ||||||
|             fontLevel={FontLevel.Level3} |  | ||||||
|         > |  | ||||||
|             <HeaderBar height={45}/> |  | ||||||
|             <div className="app-root-space" style={{ |  | ||||||
|                 height: `calc( 100% - ${45}px)` |  | ||||||
|             }}> |  | ||||||
|                 <CommandBar width={45}/> |  | ||||||
|                 <Container items={[ |  | ||||||
|                 { |                 { | ||||||
|                     items: [ |                     items: [ | ||||||
|                         {panles: ["Label A", "Label Aa Bb", "Label aaa"]}, |                         {panles: ["Label A", "Label Aa Bb", "Label aaa"]}, | ||||||
| @ -96,11 +79,32 @@ class SimulatorWeb extends Component { | |||||||
|                     }], |                     }], | ||||||
|                     layout: LayoutDirection.Y |                     layout: LayoutDirection.Y | ||||||
|                 } |                 } | ||||||
|                     ]} |             ], | ||||||
|                     scale={60} |             scale: 60, | ||||||
|                     layout={LayoutDirection.X} |             layout: LayoutDirection.X | ||||||
|                     isRoot={true} |         }) | ||||||
|                 /> |     } | ||||||
|  | 
 | ||||||
|  |     public render(): ReactNode { | ||||||
|  |         return <SettingProvider value={this.setting}> | ||||||
|  |             <StatusProvider value={this.status}> | ||||||
|  |                 {this.renderContent()} | ||||||
|  |             </StatusProvider> | ||||||
|  |         </SettingProvider> | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private renderContent(): ReactNode { | ||||||
|  |         return <Theme  | ||||||
|  |             className="app-root" | ||||||
|  |             backgroundLevel={BackgroundLevel.Level5} | ||||||
|  |             fontLevel={FontLevel.Level3} | ||||||
|  |         > | ||||||
|  |             <HeaderBar height={45}/> | ||||||
|  |             <div className="app-root-space" style={{ | ||||||
|  |                 height: `calc( 100% - ${45}px)` | ||||||
|  |             }}> | ||||||
|  |                 <CommandBar width={45}/> | ||||||
|  |                 <RootContainer /> | ||||||
|             </div> |             </div> | ||||||
|         </Theme> |         </Theme> | ||||||
|     } |     } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user