-
Notifications
You must be signed in to change notification settings - Fork 113
Expand file tree
/
Copy pathindex.tsx
More file actions
110 lines (97 loc) · 4 KB
/
index.tsx
File metadata and controls
110 lines (97 loc) · 4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
'use client';
import { debounce, useStylesheet, useSyncRef } from '@ui5/webcomponents-react-base';
import { clsx } from 'clsx';
import { forwardRef, useEffect, useRef, useState } from 'react';
import { SplitterLayoutContext } from '../../internal/SplitterLayoutContext.js';
import { classNames, styleData } from './SplitterLayout.module.css.js';
import type { SplitterLayoutPropTypes } from './types.js';
import { useConcatSplitterElements } from './useConcatSplitterElements.js';
/**
* A layout that contains several content areas. The content that is added to the `SplitterLayout` should be wrapped
* into 0-n `SplitterElement`s which define the size and size constraints of the content area.
* The orientation of the `SplitterLayout` can be set to horizontal (default) or vertical. All content areas of the
* layout will be arranged in that way. In order to split vertically and horizontally at the same time, splitters need
* to be nested.
* By adding or changing `SplitterElement`s to the `SplitterLayout` that make up the content areas, the size can be changed
* programmatically. Additionally, the content areas can be made non-resizable individually and a minimal size (in px)
* can be set.
* The splitter bars are focusable to enable resizing of the content areas via keyboard. The size of the content areas
* can be manipulated when the splitter bar is focused and Left/Down/Right/Up are pressed.
*
* __Note:__ In order to preserve the intended design, at least one `SplitterElement` should have a dynamic `size`.
*/
const SplitterLayout = forwardRef<HTMLDivElement, SplitterLayoutPropTypes>((props, ref) => {
const { vertical, children, title, style, className, options, onResize, ...rest } = props;
const [componentRef, sLRef] = useSyncRef(ref);
const [reset, setReset] = useState(undefined);
const prevSize = useRef({ width: undefined, height: undefined });
const initialChildren = useRef(true);
const initialCustomDep = useRef(true);
const layoutElements = useConcatSplitterElements({
children: children ?? [],
width: style?.width,
height: style?.height,
vertical,
onResize,
});
useStylesheet(styleData, SplitterLayout.displayName);
useEffect(() => {
if (!initialChildren.current && options?.resetOnChildrenChange) {
setReset(true);
}
initialChildren.current = false;
}, [children, options?.resetOnChildrenChange]);
useEffect(() => {
if (!initialCustomDep.current) {
setReset(true);
}
initialCustomDep.current = false;
// Can't determine external dependencies
// eslint-disable-next-line react-hooks/exhaustive-deps
}, options?.resetOnCustomDepsChange ?? []);
useEffect(() => {
if (options?.resetOnSizeChange) {
const layoutObserver = new ResizeObserver(
debounce(([container]) => {
const containerRect = container.target.getBoundingClientRect();
if (!vertical && containerRect.width !== prevSize.current.width) {
prevSize.current.width = containerRect.width;
setReset(true);
} else if (vertical && containerRect.height !== prevSize.current.height) {
prevSize.current.height = containerRect.height;
setReset(true);
}
}, 60),
);
layoutObserver.observe(sLRef.current);
return () => {
layoutObserver.disconnect();
};
}
}, [vertical, options?.resetOnSizeChange, sLRef]);
useEffect(() => {
if (reset) {
setReset(false);
}
}, [reset]);
return (
<SplitterLayoutContext.Provider value={{ vertical, reset }}>
<div
style={{
flexDirection: vertical ? 'column' : 'row',
...style,
}}
title={title}
{...rest}
className={clsx(classNames.splitterLayout, className)}
ref={componentRef}
data-splitter-vertical={vertical}
>
{layoutElements}
</div>
</SplitterLayoutContext.Provider>
);
});
SplitterLayout.displayName = 'SplitterLayout';
export type { SplitterLayoutPropTypes };
export { SplitterLayout };