Skip to content

Commit 95156ee

Browse files
committed
add new grid-layout package
1 parent 508676f commit 95156ee

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+9210
-217
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "@heswell/grid-layout",
3+
"version": "0.0.26",
4+
"description": "VUU Layout Components",
5+
"main": "src/index.ts",
6+
"author": "heswell",
7+
"license": "Apache-2.0",
8+
"scripts": {
9+
"build:dev": "node ../../scripts/run-build.mjs",
10+
"build": "node ../../scripts/run-build-rollup.mjs",
11+
"type-defs": "node ../../scripts/build-type-defs.mjs"
12+
},
13+
"types": "src/index.ts",
14+
"dependencies": {
15+
"@finos/vuu-utils": "0.0.26",
16+
"@salt-ds/core": "1.37.1",
17+
"@salt-ds/lab": "1.0.0-alpha.62",
18+
"@salt-ds/styles": "0.2.1",
19+
"@salt-ds/window": "0.1.1"
20+
},
21+
"peerDependencies": {
22+
"clsx": "^2.0.0",
23+
"react": ">=17.0.2",
24+
"react-dom": ">=17.0.2"
25+
},
26+
"sideEffects": false
27+
}
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
.vuuGridLayout {
2+
--vuu-layout-tabs-height: 32px;
3+
4+
--vuu-grid-gap: 6px;
5+
--vuu-grid-header-height: 25px;
6+
display: grid;
7+
padding: var(--vuu-grid-gap);
8+
9+
&.vuuDragging {
10+
/*
11+
While we are dragging, do not let drag events fire on anything
12+
more granular than GridLayoutItem. Exception for vuuDraggableItem
13+
is for Tabs
14+
*/
15+
[data-drop-target] > :not(.vuuDraggableItem) {
16+
pointer-events: none;
17+
}
18+
}
19+
20+
&.vuuResizing {
21+
transition: .3s;
22+
}
23+
}
24+
25+
.vuuGridLayoutItem {
26+
--vuu-header-height: var(--header-height, 25px);
27+
border: solid 1px white;
28+
display: flex;
29+
flex-direction: column;
30+
position: relative;
31+
32+
&.vuuGridLayoutItem-dragging {
33+
display: none;
34+
grid-column: 0/0 !important;
35+
grid-row: 0/0;
36+
}
37+
}
38+
39+
.vuuGridLayoutItem-active {
40+
border: dashed 1px blue;
41+
position: relative;
42+
}
43+
44+
/* .vuuGridLayoutItem > * {
45+
position: absolute;
46+
inset: calc(var(--vuu-header-height) + var(--vuu-grid-gap))
47+
var(--vuu-grid-gap) var(--vuu-grid-gap) var(--vuu-grid-gap);
48+
} */
49+
50+
.vuuGridLayoutItemHeader {
51+
align-items: center;
52+
background-color: #ccc;
53+
cursor: grab;
54+
display: flex;
55+
flex: 0 0 var(--vuu-grid-header-height);
56+
/* inset: var(--vuu-grid-gap) var(--vuu-grid-gap) auto var(--vuu-grid-gap); */
57+
height: var(--vuu-grid-header-height);
58+
padding: 0 var(--salt-spacing-100) 0 0;
59+
position: relative;
60+
61+
[data-align="right"] {
62+
margin-left: auto;
63+
}
64+
65+
.vuuGridLayoutItemHeader-title {
66+
align-items: center;
67+
display: flex;
68+
height: 100%;
69+
padding: 0 var(--salt-spacing-200);
70+
}
71+
}
72+
73+
.vuuGridLayoutStackedItem {
74+
/* height: var(--vuu-layout-tabs-height); */
75+
.saltTabsNext {
76+
margin-top: 3px;
77+
}
78+
}
79+
80+
.vuu-detached {
81+
visibility: hidden;
82+
}
83+
84+
.vuu-stacked {
85+
margin-top: var(--vuu-layout-tabs-height)
86+
}
87+
88+
.vuuGridLayoutItemHeader-close {
89+
margin-left: auto;
90+
}
91+
92+
.vuuGridLayoutStackedItemContent,
93+
.vuuGridLayoutItemContent {
94+
flex: 1 1 auto;
95+
position: relative;
96+
}
97+
98+
.vuuGridLayoutStackedItemContent > *,
99+
.vuuGridLayoutItemContent > * {
100+
position: absolute;
101+
inset: 0;
102+
}
103+
104+
105+
.vuuDropTarget-east {
106+
--grid-dropzone-top: 0px;
107+
--grid-dropzone-left: 50%;
108+
--grid-dropzone-bottom: 0px;
109+
--grid-dropzone-right: 0px;
110+
}
111+
.vuuDropTarget-west {
112+
--grid-dropzone-top: 0px;
113+
--grid-dropzone-left: 0px;
114+
--grid-dropzone-bottom: 0px;
115+
--grid-dropzone-right: 50%;
116+
}
117+
.vuuDropTarget-north {
118+
--grid-dropzone-top: 0px;
119+
--grid-dropzone-left: 0px;
120+
--grid-dropzone-bottom: 50%;
121+
--grid-dropzone-right: 0px;
122+
}
123+
.vuuDropTarget-south {
124+
--grid-dropzone-top: 50%;
125+
--grid-dropzone-left: 0px;
126+
--grid-dropzone-bottom: 0px;
127+
--grid-dropzone-right: 0px;
128+
}
129+
130+
.vuuDropTarget-centre {
131+
--grid-dropzone-top: 0px;
132+
--grid-dropzone-left: 0px;
133+
--grid-dropzone-bottom: 0px;
134+
--grid-dropzone-right: 0px;
135+
}
136+
137+
/* we could simplify this by assigning a className to the content */
138+
.vuu-stacked {
139+
& > .vuuDropTarget-north {
140+
--grid-dropzone-top: -32px;
141+
}
142+
& > .vuuDropTarget-east {
143+
--grid-dropzone-top: -32px;
144+
}
145+
& > .vuuDropTarget-west {
146+
--grid-dropzone-top: -32px;
147+
}
148+
}
149+
150+
.vuuDropTarget-centre:after,
151+
.vuuDropTarget-north:after,
152+
.vuuDropTarget-east:after,
153+
.vuuDropTarget-south:after,
154+
.vuuDropTarget-west:after {
155+
background-color: cornflowerblue;
156+
content: "";
157+
opacity: 0.3;
158+
pointer-events: none;
159+
position: absolute;
160+
top: var(--grid-dropzone-top);
161+
right: var(--grid-dropzone-right);
162+
bottom: var(--grid-dropzone-bottom);
163+
left: var(--grid-dropzone-left);
164+
transition-property: top, left, right, bottom;
165+
transition-duration: 0.3s;
166+
transition-timing-function: ease-in-out;
167+
z-index: 100;
168+
}
169+
.vuuDropTarget-header:after {
170+
background-color: red;
171+
content: "";
172+
inset: 0px;
173+
opacity: 0.3;
174+
pointer-events: none;
175+
position: absolute;
176+
transition-property: top, left, right, bottom;
177+
transition-duration: 0.3s;
178+
transition-timing-function: ease-in-out;
179+
z-index: 100;
180+
}
181+
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
import { registerComponent } from "@finos/vuu-utils";
2+
import { useIdMemo } from "@salt-ds/core";
3+
import { useComponentCssInjection } from "@salt-ds/styles";
4+
import { useWindow } from "@salt-ds/window";
5+
import cx from "clsx";
6+
import { CSSProperties, HTMLAttributes, ReactElement } from "react";
7+
import { DragDropProviderNext } from "./drag-drop-next/DragDropProviderNext";
8+
import type { ResizeOrientation } from "./grid-dom-utils";
9+
import { getGridArea } from "./grid-layout-utils";
10+
import { GridLayoutContext } from "./GridLayoutContext";
11+
import { GridLayoutItemProps } from "./GridLayoutItem";
12+
import { GridLayoutStackedItem } from "./GridLayoutStackedtem";
13+
import {
14+
AriaOrientation,
15+
GridColumnsAndRows,
16+
GridLayoutChangeHandler,
17+
} from "./GridModel";
18+
import { GridPlaceholder } from "./GridPlaceholder";
19+
import { useGridLayout } from "./useGridLayout";
20+
import { useGridSplitterResizing } from "./useGridSplitterResizing";
21+
22+
import gridLayoutCss from "./GridLayout.css";
23+
24+
const classBase = "vuuGridLayout";
25+
26+
export type GridResizeable = "h" | "v" | "hv";
27+
28+
export interface GridSplitterProps extends HTMLAttributes<HTMLDivElement> {
29+
"aria-controls": string;
30+
ariaOrientation: AriaOrientation;
31+
orientation: ResizeOrientation;
32+
}
33+
34+
const NO_DRAG_SOURCES = {} as const;
35+
36+
export const GridSplitter = ({
37+
"aria-controls": ariaControls,
38+
ariaOrientation,
39+
orientation,
40+
...htmlAttributes
41+
}: GridSplitterProps) => {
42+
const id = `${ariaControls}-splitter-${orientation[0]}`;
43+
return (
44+
<div
45+
{...htmlAttributes}
46+
aria-controls={ariaControls}
47+
aria-orientation={ariaOrientation}
48+
className="vuuGridSplitter"
49+
id={id}
50+
role="separator"
51+
/>
52+
);
53+
};
54+
55+
export interface GridLayoutProps
56+
extends Omit<HTMLAttributes<HTMLDivElement>, "onChange"> {
57+
children?:
58+
| ReactElement<GridLayoutItemProps>
59+
| ReactElement<GridLayoutItemProps>[];
60+
"full-page"?: boolean;
61+
colsAndRows?: GridColumnsAndRows;
62+
onChange?: GridLayoutChangeHandler;
63+
}
64+
65+
export const GridLayout = ({
66+
id: idProp,
67+
children: childrenProp,
68+
className,
69+
"full-page": fullPage,
70+
colsAndRows,
71+
onClick,
72+
onChange,
73+
style: styleProp,
74+
...htmlAttributes
75+
}: GridLayoutProps) => {
76+
const targetWindow = useWindow();
77+
useComponentCssInjection({
78+
testId: "vuu-grid-layout",
79+
css: gridLayoutCss,
80+
window: targetWindow,
81+
});
82+
83+
const id = useIdMemo(idProp);
84+
85+
const {
86+
children,
87+
containerCallback,
88+
dispatchGridLayoutAction,
89+
gridLayoutModel,
90+
gridModel,
91+
nonContentGridItems: { placeholders, splitters, stackedItems },
92+
onDetachTab,
93+
onDragEnd,
94+
onDragStart,
95+
onDrop,
96+
onDropStackedItem,
97+
} = useGridLayout({
98+
children: childrenProp,
99+
id,
100+
colsAndRows,
101+
onChange,
102+
});
103+
104+
const splitterLayoutProps = useGridSplitterResizing({
105+
gridLayoutModel,
106+
gridModel,
107+
id,
108+
onClick,
109+
});
110+
111+
const style = {
112+
...gridModel.tracks.css,
113+
...styleProp,
114+
} as CSSProperties;
115+
116+
return (
117+
<GridLayoutContext.Provider
118+
value={{
119+
dispatchGridLayoutAction,
120+
gridLayoutModel,
121+
gridModel,
122+
id,
123+
onDragEnd,
124+
onDragStart,
125+
onDrop,
126+
}}
127+
>
128+
<DragDropProviderNext
129+
dragSources={NO_DRAG_SOURCES}
130+
onDetachTab={onDetachTab}
131+
onDrop={onDropStackedItem}
132+
>
133+
<div
134+
{...htmlAttributes}
135+
{...splitterLayoutProps}
136+
id={id}
137+
ref={containerCallback}
138+
style={style}
139+
className={cx(classBase, className, {
140+
vuuFullPage: fullPage,
141+
})}
142+
onDragEnd={onDragEnd}
143+
>
144+
{stackedItems.map((stackedItem) => (
145+
<GridLayoutStackedItem
146+
id={stackedItem.id}
147+
key={stackedItem.id}
148+
style={{
149+
gridArea: getGridArea(stackedItem),
150+
}}
151+
/>
152+
))}
153+
{children}
154+
{placeholders.map((placeholder) => (
155+
<GridPlaceholder
156+
id={placeholder.id}
157+
key={placeholder.id}
158+
style={{
159+
gridArea: getGridArea(placeholder),
160+
}}
161+
/>
162+
))}
163+
{splitters.map((splitter) => (
164+
<GridSplitter
165+
aria-controls={splitter.controls}
166+
ariaOrientation={splitter.ariaOrientation}
167+
id={splitter.id}
168+
key={splitter.id}
169+
orientation={splitter.orientation}
170+
style={{
171+
gridArea: getGridArea(splitter),
172+
}}
173+
/>
174+
))}
175+
</div>
176+
</DragDropProviderNext>
177+
</GridLayoutContext.Provider>
178+
);
179+
};
180+
181+
GridLayout.displayName = "Grid";
182+
183+
registerComponent("Grid", GridLayout, "container");

0 commit comments

Comments
 (0)