Skip to content

Commit d58894a

Browse files
committed
Initial layers implementation
1 parent aab7a49 commit d58894a

File tree

9 files changed

+161
-54
lines changed

9 files changed

+161
-54
lines changed

demo/src/ui-demo/Layers.scss

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
.layers-demo {
2+
aside {
3+
opacity: 0.7;
4+
background-color: #e0eeee;
5+
}
6+
7+
nav {
8+
background-color: #eee0e0;
9+
}
10+
11+
main {
12+
background-color: #eee;
13+
}
14+
15+
.description {
16+
color: black;
17+
font-size: 120%;
18+
}
19+
}

demo/src/ui-demo/Layers.tsx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import * as React from 'react';
2+
import * as Space from 'react-spaces';
3+
import './Layers.scss';
4+
5+
export const LayersDemo = () => {
6+
const [ fixedSideBarHovered, setFixedSideBarHovered ] = React.useState(false);
7+
const [ floatingSideBarHovered, setFloatingSideBarHovered ] = React.useState(false);
8+
9+
return (
10+
<Space.Fill className="layers-demo">
11+
<Space.Layer zIndex={0}>
12+
<Space.Left
13+
size={fixedSideBarHovered ? "40%" : "15%" }
14+
onMouseEnter={() => setFixedSideBarHovered(true)}
15+
onMouseLeave={() => setFixedSideBarHovered(false)}
16+
as="nav">
17+
</Space.Left>
18+
19+
<Space.Fill as="main">
20+
{Description("Fill space not affected by floated left space in different layer")}
21+
</Space.Fill>
22+
</Space.Layer>
23+
24+
<Space.Layer zIndex={1}>
25+
<Space.Left
26+
size={floatingSideBarHovered ? "30%" : 50 }
27+
onMouseEnter={() => setFloatingSideBarHovered(true)}
28+
onMouseLeave={() => setFloatingSideBarHovered(false)}
29+
as="aside">
30+
{Description("Left floated")}
31+
</Space.Left>
32+
</Space.Layer>
33+
</Space.Fill>
34+
)
35+
}
36+
37+
const Description = (props: string) => (
38+
<Space.Centered>
39+
<div className="description">
40+
<strong>{props}</strong>
41+
</div>
42+
</Space.Centered>
43+
);

demo/src/ui-demo/UI.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import './UI.scss';
55
import { CodeEditor } from './CodeEditor';
66
import { Resizable } from './Resizable';
77
import { ScrollableDemo } from './Scrollable';
8+
import { LayersDemo } from './Layers';
89

910
export const UI = () => {
1011
const [ selectedDemo, setSelectedDemo ] = React.useState(1);
@@ -56,6 +57,14 @@ const DemoSelection = (props: {
5657
</li>
5758
<li className={props.selectedDemo === 3 ? "active" : undefined}>
5859
<a onClick={() => props.setSelectedDemo(3)}>
60+
Layers
61+
<span>
62+
Layered spaces
63+
</span>
64+
</a>
65+
</li>
66+
<li className={props.selectedDemo === 4 ? "active" : undefined}>
67+
<a onClick={() => props.setSelectedDemo(4)}>
5968
Code editor
6069
<span>
6170
A code editor interface with docked panels, menu bars and tabs
@@ -99,7 +108,8 @@ const Main = (props: { selectedDemo: number, showSpaces: boolean }) => {
99108
<Space.Fill debug={props.showSpaces}>
100109
{ props.selectedDemo === 1 ? <Resizable /> : null }
101110
{ props.selectedDemo === 2 ? <ScrollableDemo /> : null }
102-
{ props.selectedDemo === 3 ? <CodeEditor /> : null }
111+
{ props.selectedDemo === 3 ? <LayersDemo /> : null }
112+
{ props.selectedDemo === 4 ? <CodeEditor /> : null }
103113
</Space.Fill>
104114
)
105115
}

react-spaces/src/Globals/Contexts.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ import * as React from 'react';
22
import { ISpaceContext, ISpaceInfo } from './Types';
33

44
export const SpaceContext = React.createContext<ISpaceContext | null>(null);
5+
export const SpaceLayerContext = React.createContext<number | null>(null);
56
export const SpaceInfoContext = React.createContext<ISpaceInfo | null>(null);

react-spaces/src/Globals/Helpers.tsx

Lines changed: 65 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Resizable, ResizeType } from '../Resizable';
33
import { Guid } from 'guid-typescript';
44
import { CenteredVertically, Centered } from '../Centered';
55
import { AnchorType, AllProps, IState, ISpaceContext, ISpaceTaker, CenterType } from './Types';
6+
import { SpaceContext, SpaceLayerContext } from './Contexts';
67

78
const getSizeString =
89
(size: string | number) => typeof(size) === "string" ? size : `${size}px`;
@@ -33,10 +34,16 @@ export const initialState = (props: AllProps) => ({
3334
debug: props.debug !== undefined ? props.debug : false,
3435
})
3536

36-
const createContext = (state: IState, setState: (stateDelta: Partial<IState>) => void, parent: ISpaceContext | null) => {
37+
const createContext = (
38+
state: IState,
39+
setState: (stateDelta: Partial<IState>) => void,
40+
parent: ISpaceContext | null,
41+
zIndex: number) => {
42+
3743
return {
3844
debug: parent ? (parent.debug ? true : state.debug) : state.debug,
3945
level: parent ? parent.level + 1 : 0,
46+
zIndex: zIndex,
4047
width: state.currentWidth,
4148
height: state.currentHeight,
4249
spaceTakers: state.spaceTakers,
@@ -83,6 +90,7 @@ const createContext = (state: IState, setState: (stateDelta: Partial<IState>) =>
8390
})
8491
}
8592
}
93+
8694
}
8795

8896
const applyResize = (props: AllProps, state: IState, setState: (stateDelta: Partial<IState>) => void, parentContext: ISpaceContext | null, handleSize: number) => {
@@ -131,13 +139,24 @@ const applyResize = (props: AllProps, state: IState, setState: (stateDelta: Part
131139
return { resizeHandle: null, resizeType: null };
132140
}
133141

134-
export const calculateSpace = (props: AllProps, state: IState, setState: (stateDelta: Partial<IState>) => void, registerOnRemove: (handler: () => void) => void, parentContext: ISpaceContext | null) => {
142+
const anchorTypes = [
143+
AnchorType.Left,
144+
AnchorType.Right,
145+
AnchorType.Bottom,
146+
AnchorType.Top
147+
];
148+
149+
export const useSpace = (props: AllProps, state: IState, setState: (stateDelta: Partial<IState>) => void, registerOnRemove: (handler: () => void) => void) => {
150+
151+
const parentContext = React.useContext(SpaceContext);
152+
const currentZIndex = React.useContext(SpaceLayerContext) || 0;
135153

136154
const outerStyle = {
137155
left: (state.left !== undefined ? `calc(${state.left}px)` : undefined) as string | undefined,
138156
top: (state.top !== undefined ? `calc(${state.top}px)` : undefined) as string,
139157
right: (state.right !== undefined ? `calc(${state.right}px)` : undefined) as string,
140158
bottom: (state.bottom !== undefined ? `calc(${state.bottom}px)` : undefined) as string,
159+
zIndex: currentZIndex,
141160
width:
142161
isHorizontalSpace(props) ?
143162
`calc(${getSizeString(props.anchorSize || 0)} + ${state.adjustedSize}px)`
@@ -158,50 +177,47 @@ export const calculateSpace = (props: AllProps, state: IState, setState: (stateD
158177
let adjustedRight: string[] = [];
159178
let adjustedBottom: string[] = [];
160179

161-
[ AnchorType.Left,
162-
AnchorType.Right,
163-
AnchorType.Bottom,
164-
AnchorType.Top ]
165-
.forEach(a => {
166-
const spaceTakers =
167-
parentContext.spaceTakers
168-
.filter(t => t.anchorType === a)
169-
.sort((a, b) => a.order - b.order);
180+
for (let at = 0; at < anchorTypes.length; at ++)
181+
{
182+
const spaceTakers =
183+
parentContext.spaceTakers
184+
.filter(t => t.zIndex === currentZIndex && t.anchorType === anchorTypes[at])
185+
.sort((a, b) => a.order - b.order);
170186

171-
for (let i = 0; i < spaceTakers.length; i ++)
172-
{
173-
const t = spaceTakers[i];
174-
if (t.id !== state.id) {
175-
const adjustedSize = t.adjustedSize !== 0 ?` + ${t.adjustedSize}px` : ``;
176-
if (isFilledSpace(props))
177-
{
178-
if (t.anchorType === AnchorType.Top) {
179-
adjustedTop.push(`${getSizeString(t.size)}${adjustedSize}`);
180-
} else if (t.anchorType === AnchorType.Left) {
181-
adjustedLeft.push(`${getSizeString(t.size)}${adjustedSize}`);
182-
} else if (t.anchorType === AnchorType.Bottom) {
183-
adjustedBottom.push(`${getSizeString(t.size)}${adjustedSize}`);
184-
} else if (t.anchorType === AnchorType.Right) {
185-
adjustedRight.push(`${getSizeString(t.size)}${adjustedSize}`);
186-
}
187+
for (let i = 0; i < spaceTakers.length; i ++)
188+
{
189+
const t = spaceTakers[i];
190+
if (t.id !== state.id) {
191+
const adjustedSize = t.adjustedSize !== 0 ?` + ${t.adjustedSize}px` : ``;
192+
if (isFilledSpace(props))
193+
{
194+
if (t.anchorType === AnchorType.Top) {
195+
adjustedTop.push(`${getSizeString(t.size)}${adjustedSize}`);
196+
} else if (t.anchorType === AnchorType.Left) {
197+
adjustedLeft.push(`${getSizeString(t.size)}${adjustedSize}`);
198+
} else if (t.anchorType === AnchorType.Bottom) {
199+
adjustedBottom.push(`${getSizeString(t.size)}${adjustedSize}`);
200+
} else if (t.anchorType === AnchorType.Right) {
201+
adjustedRight.push(`${getSizeString(t.size)}${adjustedSize}`);
187202
}
188-
else
189-
{
190-
if (t.anchorType === AnchorType.Top && outerStyle.top !== undefined) {
191-
adjustedTop.push(`${getSizeString(t.size)}${adjustedSize}`);
192-
} else if (t.anchorType === AnchorType.Left && outerStyle.left !== undefined) {
193-
adjustedLeft.push(`${getSizeString(t.size)}${adjustedSize}`);
194-
} else if (t.anchorType === AnchorType.Bottom && outerStyle.bottom !== undefined) {
195-
adjustedBottom.push(`${getSizeString(t.size)}${adjustedSize}`);
196-
} else if (t.anchorType === AnchorType.Right && outerStyle.right !== undefined) {
197-
adjustedRight.push(`${getSizeString(t.size)}${adjustedSize}`);
198-
}
203+
}
204+
else
205+
{
206+
if (t.anchorType === AnchorType.Top && outerStyle.top !== undefined) {
207+
adjustedTop.push(`${getSizeString(t.size)}${adjustedSize}`);
208+
} else if (t.anchorType === AnchorType.Left && outerStyle.left !== undefined) {
209+
adjustedLeft.push(`${getSizeString(t.size)}${adjustedSize}`);
210+
} else if (t.anchorType === AnchorType.Bottom && outerStyle.bottom !== undefined) {
211+
adjustedBottom.push(`${getSizeString(t.size)}${adjustedSize}`);
212+
} else if (t.anchorType === AnchorType.Right && outerStyle.right !== undefined) {
213+
adjustedRight.push(`${getSizeString(t.size)}${adjustedSize}`);
199214
}
200-
} else {
201-
break;
202215
}
216+
} else {
217+
break;
203218
}
204-
});
219+
}
220+
}
205221

206222
[
207223
{ adjusted: adjustedTop, setter: (value: string) => outerStyle.top = value },
@@ -213,6 +229,7 @@ export const calculateSpace = (props: AllProps, state: IState, setState: (stateD
213229
if (props.anchor) {
214230
parentContext.registerSpaceTaker({
215231
id: state.id,
232+
zIndex: currentZIndex,
216233
order: props.order || 1,
217234
anchorType: props.anchor,
218235
size: props.anchorSize || 0,
@@ -221,7 +238,7 @@ export const calculateSpace = (props: AllProps, state: IState, setState: (stateD
221238
}
222239
}
223240

224-
const currentContext = createContext(state, setState, parentContext);
241+
const currentContext = createContext(state, setState, parentContext, currentZIndex);
225242
const handleSize = props.handleSize || 5;
226243
const overlayHandle = props.overlayHandle !== undefined ? props.overlayHandle : true;
227244
let children = props.children;
@@ -245,12 +262,17 @@ export const calculateSpace = (props: AllProps, state: IState, setState: (stateD
245262
}
246263
};
247264

265+
const debug =
266+
parentContext ? parentContext.debug : false ||
267+
props.debug !== undefined ? props.debug : false;
268+
248269
return {
249270
currentContext,
250271
resizeHandle: resize.resizeHandle,
251272
resizeType: resize.resizeType,
252273
outerStyle,
253274
innerStyle,
254-
children
275+
children,
276+
debug
255277
}
256278
}

react-spaces/src/Globals/Types.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ export interface IPublicProps {
2020
centerContent?: CenterType,
2121
as?: string,
2222
children?: React.ReactNode,
23-
debug?: boolean
23+
debug?: boolean,
24+
onMouseEnter?: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void,
25+
onMouseLeave?: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void
2426
}
2527

2628
export interface IPrivateProps {
@@ -63,6 +65,7 @@ export interface IState {
6365

6466
export interface ISpaceContext {
6567
debug: boolean,
68+
zIndex: number,
6669
level: number,
6770
width: number,
6871
height: number,
@@ -76,6 +79,7 @@ export interface ISpaceContext {
7679
export interface ISpaceTaker {
7780
id: string,
7881
order: number,
82+
zIndex: number,
7983
anchorType: AnchorType,
8084
size: number | string,
8185
adjustedSize: number

react-spaces/src/Layer.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import * as React from 'react';
2+
import { SpaceLayerContext } from './Globals/Contexts';
3+
4+
interface IProps {
5+
zIndex: number
6+
}
7+
8+
export const Layer : React.FC<IProps> = (props) => {
9+
return <SpaceLayerContext.Provider value={props.zIndex}>{props.children}</SpaceLayerContext.Provider>
10+
}

react-spaces/src/Space.tsx

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as React from 'react';
22
import { ResizeSensor } from 'css-element-queries';
33
import { IPublicProps, IAnchoredProps, AnchorType, IResizableProps, AllProps, IState } from './Globals/Types';
4-
import { isHorizontalSpace, isVerticalSpace, calculateSpace, initialState } from './Globals/Helpers';
4+
import { isHorizontalSpace, isVerticalSpace, initialState, useSpace } from './Globals/Helpers';
55
import { SpaceContext, SpaceInfoContext } from './Globals/Contexts';
66
import './Styles.css';
77

@@ -16,7 +16,6 @@ export const BottomResizable : React.FC<IPublicProps & IAnchoredProps & IResizab
1616
export const RightResizable : React.FC<IPublicProps & IAnchoredProps & IResizableProps> = (props) => <Space {...props} anchor={AnchorType.Right} anchorSize={props.size} resizable={true} />
1717

1818
const Space : React.FC<AllProps> = (props) => {
19-
const parentContext = React.useContext(SpaceContext);
2019
const [ state, changeState ] = React.useState<IState>(initialState(props));
2120
const divElementRef = React.useRef<HTMLDivElement>();
2221
const resizeSensor = React.useRef<ResizeSensor | undefined>(undefined);
@@ -74,12 +73,8 @@ const Space : React.FC<AllProps> = (props) => {
7473
return cleanup;
7574
}, [])
7675

77-
const debug =
78-
parentContext ? parentContext.debug : false ||
79-
props.debug !== undefined ? props.debug : false;
80-
81-
const { currentContext, outerStyle, innerStyle, resizeType, resizeHandle, children } =
82-
calculateSpace(props, state, setState, (handler) => { onRemove.current = handler; }, parentContext);
76+
const { currentContext, outerStyle, innerStyle, resizeType, resizeHandle, children, debug } =
77+
useSpace(props, state, setState, (handler) => { onRemove.current = handler; });
8378

8479
const outerClasses = [
8580
"spaces-space",
@@ -106,7 +101,9 @@ const Space : React.FC<AllProps> = (props) => {
106101
id: props.id,
107102
ref: divElementRef,
108103
className: outerClasses.join(' '),
109-
style: outerStyle
104+
style: outerStyle,
105+
onMouseEnter: props.onMouseEnter,
106+
onMouseLeave: props.onMouseLeave
110107
},
111108
<>
112109
{ resizeHandle }

react-spaces/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export * from './ClearFix';
33
export * from './FixedSizeContainer';
44
export * from './FullPageContainer';
55
export * from './Space';
6+
export * from './Layer';
67
export * from './SpaceInfo';
78

89
export {

0 commit comments

Comments
 (0)