diff --git a/README.md b/README.md
index 96e2220..301733c 100644
--- a/README.md
+++ b/README.md
@@ -92,6 +92,7 @@ ReactDOM.render(, document.getElementById("root") as HTMLElement);
| overlapped | boolean | falseにするとウインドウが親ウインドウ内にのみ表示 |
| windowStyle | number | WindowStyle ビットの込み合わせ
TITLE:タイトルバー
MAX:最大化ボタン
MIN:最小化ボタン
CLOSE:クローズボタン
FRAME:枠の表示
RESIZE:サイズ変更
|
| windowState | WindowState | WindowState ウインドウの状態
NORMAL:通常
MAX:最大化
MIN:最小化
HIDE:非表示
|
+| clientStyle | React.CSSProperties | クライアント領域に適用するスタイル |
| onUpdate | function(p:WindowInfo) | null | ウインドウの状態が変化するとコールバックされる |
### 4.3 メソッド
diff --git a/images/resize.svg b/images/resize.svg
new file mode 100644
index 0000000..6f6c6e3
--- /dev/null
+++ b/images/resize.svg
@@ -0,0 +1,19 @@
+
+
+
diff --git a/src/splitView/index.tsx b/src/splitView/index.tsx
index 7977732..4979b5c 100644
--- a/src/splitView/index.tsx
+++ b/src/splitView/index.tsx
@@ -1,103 +1,244 @@
-import { Component, createRef } from "react";
-import React from "react";
-
-import styled from "styled-components";
+import ResizeObserver from "resize-observer-polyfill";
+import React, { Component, createRef, ReactNode } from "react";
import { Manager, JWFEvent, MovePoint } from "../lib/Manager";
+import { Root } from "./parts/Root";
+import { Bar } from "./parts/Bar";
+import { Child } from "./parts/Child";
+
+export type SplitType = "ns" | "sn" | "we" | "ew";
-interface StyleProps {
- type?: 'ns' | 'sn' | 'we' | 'ew';
+interface SplitProps {
+ type?: SplitType;
pos?: number;
+ activeSize?: number;
+ barSize?: number;
+ children?: ReactNode | null;
+ style?: React.CSSProperties;
}
-export const Root = styled.div.attrs(p => ({
- style: {
- }
-})) `
-& {
- position:absolute;
- left:0;
- right:0;
- top:0;
- bottom:0;
- background-color:blue;
-}
- `
-interface BarProps {
+/**
+ *画面分割コンポーネント
+ *
+ * @export
+ * @class SplitView
+ * @extends {Component}
+ */
+
+interface State {
pos: number;
- size: number;
- type: 'ns' | 'sn' | 'we' | 'ew';
+ activeMode: boolean;
+ barOpen: boolean;
}
-export const Bar = styled.div.attrs(p => {
- let style = {};
- switch (p.type) {
- case 'we':
- style = {
- top: 0,
- bottom: 0,
- left: p.pos + 'px',
- width: p.size + 'px',
- cursor: 'ew-resize'
- }
- break;
- case 'ew':
- style = {}
- break;
- case 'ns':
- style = {}
- break;
- case 'sn':
- style = {}
- break;
- }
- return { style };
-}) `
-
- position:absolute;
- box-sizing: border-box;
- background-color: #777777;
- border: outset 2px #b8b7b7;
- user-select: none;
- `
-export class SplitView extends Component {
- splitterRef = createRef();
- barType: 'ns' | 'sn' | 'we' | 'ew' = 'we';
- constructor(props: StyleProps) {
+export class SplitView extends Component {
+ static defaultProps = {
+ type: "we",
+ barSize: 8,
+ pos: 100,
+ activeSize: 300,
+ style: {}
+ };
+ private closeHandle?: number;
+ private resizeObserver?: ResizeObserver;
+ private rootRef = createRef();
+ private splitterRef = createRef();
+ private childRef = [createRef(), createRef()];
+ private children: (ReactNode | undefined)[] = [undefined, undefined];
+ public constructor(props: SplitProps) {
super(props);
- if (props.type)
- this.barType = props.type;
-
- this.state = { pos: props.pos !== undefined ? props.pos : 100 };
-
+ this.state = { pos: props.pos!, activeMode: false, barOpen: true };
+ if (props.children) {
+ if (props.children instanceof Array) {
+ this.children = props.children;
+ }
+ }
}
- render() {
+ public render() {
return (
-
-
- )
+
+ {this.children[1]}
+ {this.children[0]}
+ this.onOpen(open)}
+ >
+
+ );
+ }
+ componentDidUpdate() {
+ this.onLayout();
}
public componentDidMount() {
const node = this.splitterRef.current!;
node.addEventListener("move", this.onMove.bind(this));
+
+ this.resizeObserver = new ResizeObserver(() => {
+ this.onLayout();
+ });
+ this.resizeObserver.observe(this.rootRef.current! as Element);
+
+ this.onLayout();
}
public componentWillUnmount() {
const node = this.splitterRef.current!;
node.removeEventListener("move", this.onMove.bind(this));
+ if (this.resizeObserver) {
+ this.resizeObserver.disconnect();
+ this.resizeObserver = undefined;
+ }
}
- onMove(e: JWFEvent) {
+ protected onMove(e: JWFEvent) {
let p = e.params as MovePoint;
let pos = this.state.pos;
- switch (this.barType) {
+ switch (this.props.type!) {
case "we":
pos = p.nodePoint.x + p.nowPoint.x - p.basePoint.x;
break;
+ case "ew":
+ pos = p.nodePoint.x - (p.nowPoint.x - p.basePoint.x);
+ break;
+ case "ns":
+ pos = p.nodePoint.y + p.nowPoint.y - p.basePoint.y;
+ break;
+ case "sn":
+ pos = p.nodePoint.y - (p.nowPoint.y - p.basePoint.y);
+ break;
}
- console.log(pos);
this.setState({ pos });
}
- onMouseDown(e:
- | React.MouseEvent
- | React.TouchEvent) {
+ closeBar() {
+ if (this.closeHandle) {
+ clearTimeout(this.closeHandle);
+ }
+ setTimeout(() => {
+ // this.onOpen(false);
+ this.closeHandle = undefined;
+ }, 2000);
+ }
+ protected onLayout() {
+ let activeMode = false;
+ let pos = this.state.pos;
+ const rootRef = this.rootRef!.current!;
+ const children = [this.childRef[0].current!, this.childRef[1].current!];
+ const barSize = this.props.barSize!;
+ const barType = this.props.type;
+ const width = rootRef.offsetWidth;
+ const height = rootRef.offsetHeight;
+
+ if (barType === "we" || barType === "ew") {
+ const w = width - (pos + barSize);
+ if (w < this.props.activeSize!) {
+ activeMode = true;
+ if (!this.state.activeMode) {
+ children[1].style.left = "0";
+ children[1].style.right = "0";
+ children[1].style.width = null;
+ children[1].style.height = null;
+ children[1].style.top = "0";
+ children[1].style.bottom = "0";
+ this.closeBar();
+ }
+ }
+ } else {
+ const h = height - (pos + barSize);
+ if (h < this.props.activeSize!) {
+ activeMode = true;
+ if (!this.state.activeMode) {
+ children[1].style.left = "0";
+ children[1].style.right = "0";
+ children[1].style.top = "0";
+ children[1].style.bottom = "0";
+ children[1].style.width = null;
+ children[1].style.height = null;
+ this.closeBar();
+ }
+ }
+ }
+ if (activeMode !== this.state.activeMode) {
+ if (!activeMode) {
+ this.onOpen(true);
+ }
+ this.setState({ activeMode });
+ }
+
+ switch (barType) {
+ case "we":
+ children[0].style.left = "0";
+ children[0].style.right = null;
+ children[0].style.width = pos + "px";
+ children[0].style.height = null;
+ children[0].style.top = "0";
+ children[0].style.bottom = "0";
+ if (!activeMode) {
+ children[1].style.left = pos + barSize + "px";
+ children[1].style.right = "0";
+ children[1].style.width = null;
+ children[1].style.height = null;
+ children[1].style.top = "0";
+ children[1].style.bottom = "0";
+ }
+ break;
+ case "ew":
+ children[0].style.left = null;
+ children[0].style.right = "0";
+ children[0].style.width = pos + "px";
+ children[0].style.height = null;
+ children[0].style.top = "0";
+ children[0].style.bottom = "0";
+ if (!activeMode) {
+ children[1].style.left = "0";
+ children[1].style.right = pos + barSize + "px";
+ children[1].style.width = null;
+ children[1].style.height = null;
+ children[1].style.top = "0";
+ children[1].style.bottom = "0";
+ }
+ break;
+ case "ns":
+ children[0].style.top = "0";
+ children[0].style.bottom = null;
+ children[0].style.width = null;
+ children[0].style.height = pos + "px";
+ children[0].style.left = "0";
+ children[0].style.right = "0";
+ if (!activeMode) {
+ children[1].style.top = pos + barSize + "px";
+ children[1].style.bottom = "0";
+ children[1].style.width = null;
+ children[1].style.height = null;
+ children[1].style.left = "0";
+ children[1].style.right = "0";
+ }
+ break;
+ case "sn":
+ children[0].style.top = null;
+ children[0].style.bottom = "0";
+ children[0].style.width = null;
+ children[0].style.height = pos + "px";
+ children[0].style.left = "0";
+ children[0].style.right = "0";
+ if (!activeMode) {
+ children[1].style.top = "0";
+ children[1].style.bottom = pos + barSize + "px";
+ children[1].style.width = null;
+ children[1].style.height = null;
+ children[1].style.left = "0";
+ children[1].style.right = "0";
+ }
+ break;
+ }
+ }
+ protected onMouseDown(
+ e:
+ | React.MouseEvent
+ | React.TouchEvent
+ ) {
if (Manager.moveNode == null) {
const node = this.splitterRef.current!;
Manager.moveNode = node;
@@ -113,4 +254,15 @@ export class SplitView extends Component {
e.preventDefault();
}
}
-}
\ No newline at end of file
+ onOpen(open: boolean) {
+ const children = this.childRef[0].current!;
+ if (open) {
+ children.style.animation =
+ this.props.type + "DrawerShow 0.5s ease 0s forwards";
+ } else {
+ children.style.animation =
+ this.props.type + "DrawerClose 0.5s ease 0s forwards";
+ }
+ this.setState({ barOpen: open });
+ }
+}
diff --git a/src/splitView/parts/Bar.tsx b/src/splitView/parts/Bar.tsx
new file mode 100644
index 0000000..78e2468
--- /dev/null
+++ b/src/splitView/parts/Bar.tsx
@@ -0,0 +1,103 @@
+import styled from "styled-components";
+import React, { ReactNode, Component, RefObject } from "react";
+import { Resize } from "./Resize";
+import imgResize from "../../../images/resize.svg";
+
+interface BarProps extends React.HTMLAttributes {
+ pos: number;
+ size: number;
+ children?: ReactNode | null;
+ type: "ns" | "sn" | "we" | "ew";
+ refs: RefObject;
+ open: boolean;
+ activeMode:boolean;
+ procOpen:(open:boolean)=>void
+}
+const BarStyle = styled.div.attrs(p => {
+ let style = {};
+ switch (p.type) {
+ case "we":
+ style = {
+ top: 0,
+ bottom: 0,
+ right: null,
+ left: p.pos + "px",
+ width: p.size + "px",
+ cursor: "ew-resize",
+ paddingLeft: "2px"
+ };
+ break;
+ case "ew":
+ style = {
+ top: 0,
+ bottom: 0,
+ left: null,
+ right: p.pos + "px",
+ width: p.size + "px",
+ cursor: "ew-resize",
+ paddingLeft: "2px"
+ };
+ break;
+ case "ns":
+ style = {
+ left: 0,
+ right: 0,
+ top: p.pos + "px",
+ bottom: null,
+ height: p.size + "px",
+ paddingTop: "px",
+ cursor: "ns-resize"
+ };
+ break;
+ case "sn":
+ style = {
+ left: 0,
+ right: 0,
+ top: null,
+ bottom: p.pos + "px",
+ height: p.size + "px",
+ paddingTop: "px",
+ cursor: "ns-resize"
+ };
+ break;
+ }
+ return { style };
+})`
+ position: absolute;
+ overflow: visible;
+ box-sizing: border-box;
+ background-color: #777777;
+ border: outset 2px #b8b7b7;
+ user-select: none;
+ vertical-align: middle;
+`;
+
+
+interface BarState {
+ open: boolean;
+}
+export class Bar extends Component {
+ constructor(props: BarProps) {
+ super(props);
+ this.state = { open: true };
+ }
+ componentDidUpdate() {
+ if(!this.props.open && this.state.open){
+ const node = this.props.refs.current!;
+ node.style.animation = this.props.type + "DrawerClose 0.5s ease 0s forwards";
+ this.setState({open:false});
+ }else if(this.props.open && !this.state.open){
+ const node = this.props.refs.current!;
+ node.style.animation = this.props.type + "DrawerShow 0.5s ease 0s forwards";
+ this.setState({open:true});
+ }
+ }
+ render() {
+ return (
+
+ {this.props.activeMode && this.props.procOpen(!this.state.open)}/>}
+
+ );
+ }
+
+}
diff --git a/src/splitView/parts/Child.ts b/src/splitView/parts/Child.ts
new file mode 100644
index 0000000..9baeee4
--- /dev/null
+++ b/src/splitView/parts/Child.ts
@@ -0,0 +1,16 @@
+import { ReactNode } from "react";
+import styled from "styled-components";
+
+interface StyleProps {
+}
+
+export const Child = styled.div.attrs(p => ({
+ style: {
+ }
+})) `
+& {
+ position:absolute;
+ overflow:hidden;
+ background-color: rgba(255,255,255,0.8);
+}
+ `
\ No newline at end of file
diff --git a/src/splitView/parts/Resize.ts b/src/splitView/parts/Resize.ts
new file mode 100644
index 0000000..42128e8
--- /dev/null
+++ b/src/splitView/parts/Resize.ts
@@ -0,0 +1,30 @@
+import { ReactNode } from "react";
+import styled from "styled-components";
+
+interface StyleProps {
+ size:number;
+}
+
+export const Resize = styled.img.attrs(p => ({
+ style: {
+ }
+})) `
+& {
+ margin: auto;
+ position:relative;
+ width:${p=>p.size}px;
+ height:${p=>p.size}px;
+ margin-top:-${p=>p.size/2+2}px;
+ margin-left:-${p=>p.size/2+2}px;
+ top:50%;
+ left:50%;
+ cursor:pointer;
+ overflow:visible;
+ background-color:rgba(255,255,255,0.8);
+ border:solid 1px rgba(0,0,0,0.4);
+ border-radius:4px;
+ &:hover{
+ background-color:rgba(200,200,255,0.8);;
+ }
+}
+ `
\ No newline at end of file
diff --git a/src/splitView/parts/Root.ts b/src/splitView/parts/Root.ts
new file mode 100644
index 0000000..b63908e
--- /dev/null
+++ b/src/splitView/parts/Root.ts
@@ -0,0 +1,109 @@
+import { ReactNode } from "react";
+import styled from "styled-components";
+
+interface StyleProps {
+ type?: "ns" | "sn" | "we" | "ew";
+ pos?: number;
+ children?: ReactNode | null;
+}
+
+export const Root = styled.div.attrs(p => ({
+ style: {}
+}))`
+ & {
+ overflow: hidden;
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ }
+
+ @keyframes nsDrawerShow {
+ 0% {
+ top: 0;
+ transform: translateY(-100%);
+ }
+
+ 100% {
+ transform: translateY(0);
+ }
+ }
+
+ @keyframes nsDrawerClose {
+ 0% {
+ transform: translateY(0);
+ }
+
+ 100% {
+ top: 0;
+ transform: translateY(-100%);
+ }
+ }
+
+ @keyframes snDrawerShow {
+ 0% {
+ bottom: 0;
+ transform: translateY(100%);
+ }
+
+ 100% {
+ transform: translateY(0);
+ }
+ }
+
+ @keyframes snDrawerClose {
+ 0% {
+ transform: translateY(0);
+ }
+
+ 100% {
+ bottom: 0;
+ transform: translateY(100%);
+ }
+ }
+
+ @keyframes weDrawerShow {
+ 0% {
+ left: 0;
+ transform: translateX(-100%);
+ }
+
+ 100% {
+ transform: translateX(0);
+ }
+ }
+
+ @keyframes weDrawerClose {
+ 0% {
+ transform: translateX(0);
+ }
+
+ 100% {
+ left: 0;
+ transform: translateX(-100%);
+ }
+ }
+
+ @keyframes ewDrawerShow {
+ 0% {
+ transform: translateX(100%);
+ right: 0;
+ }
+
+ 100% {
+ transform: translateX(0);
+ }
+ }
+
+ @keyframes ewDrawerClose {
+ 0% {
+ transform: translateX(0);
+ }
+
+ 100% {
+ right: 0;
+ transform: translateX(100%);
+ }
+ }
+`;
diff --git a/src/window/index.tsx b/src/window/index.tsx
index 8c5bd43..2a90093 100644
--- a/src/window/index.tsx
+++ b/src/window/index.tsx
@@ -1,10 +1,10 @@
import ResizeObserver from "resize-observer-polyfill";
import React, { ReactNode, Component, createRef } from "react";
import { Manager, MovePoint, JWFEvent } from "../lib/Manager";
-import { Clinet } from "./Parts/Client";
-import { Title } from "./Parts/Title";
-import { Root } from "./Parts/Root";
-import { Border, borders } from "./Parts/Border";
+import { Clinet } from "./parts/Client";
+import { Title } from "./parts/Title";
+import { Root } from "./parts/Root";
+import { Border, borders } from "./parts/Border";
export enum WindowStyle {
TITLE = 1,
@@ -29,6 +29,7 @@ export interface WindowProps {
windowStyle?: number;
windowState?: WindowState;
onUpdate?: ((status: WindowInfo) => void) | null;
+ clientStyle?:React.CSSProperties;
}
type NonNullableType = {
[P in K]-?: T[P];
@@ -79,6 +80,23 @@ interface MoveParams {
* @extends {Component}
*/
export class JSWFWindow extends Component {
+ static defaultProps: WindowProps = {
+ x: null,
+ y: null,
+ width: 300,
+ height: 300,
+ moveable: false,
+ borderSize: 8,
+ titleSize: 32,
+ title: "",
+ active: false,
+ overlapped: true,
+ windowStyle: 0xff,
+ windowState: WindowState.NORMAL,
+ clientStyle:{},
+ onUpdate: null
+ };
+
private rootRef = createRef();
private titleRef = createRef();
private clientRef = createRef();
@@ -97,45 +115,26 @@ export class JSWFWindow extends Component {
public constructor(props: WindowProps) {
super(props);
let state: State;
- if (props) {
- state = {
- active: props.active || false,
- overlapped: props.overlapped == null ? true : props.overlapped,
- titlePrmisson:
- props.windowStyle !== undefined ? props.windowStyle : 0xff,
- titleSize:
- (props.windowStyle && props.windowStyle & WindowStyle.TITLE) === 0
- ? 0
- : props.titleSize || 32,
- borderSize: props.borderSize || 8,
- x: props.x === undefined ? null : props.x,
- y: props.y === undefined ? null : props.y,
- width: props.width || 300,
- height: props.height || 300,
- oldEnumState: WindowState.HIDE,
- windowState: props.windowState || WindowState.NORMAL,
- boxEnumState: WindowState.HIDE,
- clientWidth: 0,
- clientHeight: 0
- };
- } else {
- state = {
- active: false,
- overlapped: true,
- titlePrmisson: 0xff,
- titleSize: 32,
- borderSize: 8,
- x: null,
- y: null,
- width: 300,
- height: 300,
- oldEnumState: WindowState.HIDE,
- windowState: WindowState.NORMAL,
- boxEnumState: WindowState.HIDE,
- clientWidth: 0,
- clientHeight: 0
- };
- }
+
+ state = {
+ active: props.active!,
+ overlapped: props.overlapped!,
+ titlePrmisson:props.windowStyle!,
+ titleSize:(props.windowStyle! & WindowStyle.TITLE) === 0
+ ? 0
+ : props.titleSize!,
+ borderSize: props.borderSize!,
+ x: props.x!,
+ y: props.y!,
+ width: props.width!,
+ height: props.height!,
+ oldEnumState: WindowState.HIDE,
+ windowState: props.windowState!,
+ boxEnumState: WindowState.HIDE,
+ clientWidth: 0,
+ clientHeight: 0
+ };
+
this.state = state;
this.windowInfo = {
@@ -143,18 +142,19 @@ export class JSWFWindow extends Component {
y: state.y,
width: state.width,
height: state.height,
- moveable: (props && props.moveable) || false,
+ moveable: props.moveable!,
borderSize: state.borderSize,
- title: (props && props.title) || "",
+ title: props.title!,
titleSize: state.titleSize,
children: (props && props.children) || null,
active: state.active,
overlapped: state.overlapped,
- windowStyle: (props && props.windowStyle) || 0xff,
+ windowStyle: props.windowStyle!,
windowState: state.windowState,
- onUpdate: (props && props.onUpdate) || null,
+ onUpdate: props.onUpdate!,
clientWidth: 0,
clientHeight: 0,
+ clientStyle:props.clientStyle!,
realX: 0,
realY: 0,
realWidth: 0,
@@ -201,7 +201,7 @@ export class JSWFWindow extends Component {
node.removeEventListener("active", this.onActive.bind(this));
}
}
- componentDidUpdate() {
+ public componentDidUpdate() {
if (this.props.onUpdate) {
let flag = false;
for (const key of Object.keys(this.windowInfo)) {
@@ -415,6 +415,7 @@ export class JSWFWindow extends Component {
TitleSize={this.state.titleSize}
Width={clientWidth}
Height={clientHeight}
+ style={this.props.clientStyle!}
>
{this.props.children}
@@ -714,7 +715,7 @@ export class JSWFWindow extends Component {
width: pwidth
};
if (!this.moveHandle) {
- this.moveHandle=setTimeout(() => {
+ this.moveHandle = setTimeout(() => {
this.setState(this.moveParams!);
this.moveHandle = undefined;
}, 10);