@byeolnaerim/flex-layout 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +314 -0
- package/README.ko.md +312 -0
- package/README.md +314 -0
- package/dist/components.cjs +651 -657
- package/dist/components.cjs.map +1 -1
- package/dist/components.js +546 -553
- package/dist/components.js.map +1 -1
- package/dist/flex-layout/components/FlexLayout.d.ts +2 -0
- package/dist/flex-layout/components/FlexLayoutContainer.d.ts +2 -0
- package/dist/flex-layout/components/FlexLayoutResizePanel.d.ts +2 -0
- package/dist/flex-layout/components/FlexLayoutSplitScreen.d.ts +11 -0
- package/dist/flex-layout/components/FlexLayoutSplitScreenDragBox.d.ts +27 -0
- package/dist/flex-layout/components/FlexLayoutSplitScreenDragBoxContainer.d.ts +5 -0
- package/dist/flex-layout/components/FlexLayoutSplitScreenDragBoxItem.d.ts +8 -0
- package/dist/flex-layout/components/FlexLayoutSplitScreenDragBoxTitleMore.d.ts +3 -0
- package/dist/flex-layout/components/FlexLayoutSplitScreenScrollBox.d.ts +9 -0
- package/dist/flex-layout/components/FlexLayoutStickyBox.d.ts +29 -0
- package/dist/flex-layout/components/index.d.ts +7 -0
- package/dist/flex-layout/hooks/index.d.ts +2 -0
- package/dist/{useDrag-DR01Ob3s.d.ts → flex-layout/hooks/useDrag.d.ts} +19 -22
- package/dist/flex-layout/hooks/useFlexLayoutSplitScreen.d.ts +28 -0
- package/dist/{hooks.d.ts → flex-layout/hooks/useListPaging.d.ts} +6 -12
- package/dist/flex-layout/hooks/useSizes.d.ts +8 -0
- package/dist/flex-layout/index.d.ts +5 -0
- package/dist/flex-layout/providers/FlexLayoutContext.d.ts +8 -0
- package/dist/flex-layout/providers/FlexLayoutHooks.d.ts +51 -0
- package/dist/flex-layout/providers/index.d.ts +1 -0
- package/dist/flex-layout/store/FlexLayoutContainerStore.d.ts +62 -0
- package/dist/flex-layout/store/index.d.ts +1 -0
- package/dist/flex-layout/styles/FlexLayout.module.css +416 -0
- package/dist/flex-layout/styles/listScroll.module.css +85 -0
- package/dist/flex-layout/styles/shake.module.css +41 -0
- package/dist/flex-layout/types/FlexDirectionTypes.d.ts +6 -0
- package/dist/flex-layout/types/FlexLayoutTypes.d.ts +53 -0
- package/dist/flex-layout/utils/FlexLayoutUtils.d.ts +26 -0
- package/dist/flex-layout/utils/index.d.ts +1 -0
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.js.map +1 -1
- package/dist/index.cjs +3454 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +471 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.ts +1 -2
- package/dist/index.js +3395 -0
- package/dist/index.js.map +1 -1
- package/package.json +18 -64
- package/dist/FlexLayoutSplitScreenDragBox-eCtq4kLd.d.cts +0 -31
- package/dist/FlexLayoutSplitScreenDragBox-eCtq4kLd.d.ts +0 -31
- package/dist/components.d.cts +0 -122
- package/dist/components.d.ts +0 -122
- package/dist/hooks.d.cts +0 -37
- package/dist/index.d.cts +0 -2
- package/dist/providers.d.cts +0 -54
- package/dist/providers.d.ts +0 -54
- package/dist/store.d.cts +0 -67
- package/dist/store.d.ts +0 -67
- package/dist/useDrag-CYQnhUFk.d.cts +0 -108
- package/dist/utils.d.cts +0 -28
- package/dist/utils.d.ts +0 -28
package/README.en.md
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
# @byeolnaerim/flex-layout
|
|
2
|
+
|
|
3
|
+
> This document was drafted by ChatGPT using the codebase and real-world usage examples provided by the FlexLayout developer. It may contain inaccuracies, and the developer will verify and update it after review.
|
|
4
|
+
|
|
5
|
+
A set of components to quickly build **flex-based resizable panels + split screen + Drag & Drop** UI in React (Next.js).
|
|
6
|
+
|
|
7
|
+
The core of this library is **`<FlexLayout />`**.
|
|
8
|
+
With `FlexLayout` + `FlexLayoutContainer`, you can build layouts where panels are split (row/column), resized by dragging, and optionally opened/closed.
|
|
9
|
+
On top of that, it provides **Split Screen** (dynamic multi-pane views) and Drag & Drop based on **`FlexLayoutSplitScreenDragBox`** / **`useDragCapture`**.
|
|
10
|
+
|
|
11
|
+
> ⚠️ Many components rely on `window`, `ResizeObserver`, etc. For Next.js (App Router), using them as **Client Components** is recommended. (`"use client"`)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# npm
|
|
19
|
+
npm i @byeolnaerim/flex-layout
|
|
20
|
+
|
|
21
|
+
# yarn
|
|
22
|
+
yarn add @byeolnaerim/flex-layout
|
|
23
|
+
|
|
24
|
+
# pnpm
|
|
25
|
+
pnpm add @byeolnaerim/flex-layout
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
### 1) FlexLayout + FlexLayoutContainer (basic resizable layout)
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
"use client";
|
|
36
|
+
|
|
37
|
+
import { FlexLayout, FlexLayoutContainer } from "@byeolnaerim/flex-layout";
|
|
38
|
+
|
|
39
|
+
export default function Basic() {
|
|
40
|
+
return (
|
|
41
|
+
<div style={{ height: 500 }}>
|
|
42
|
+
<FlexLayout layoutName="basic" direction="row">
|
|
43
|
+
<>
|
|
44
|
+
<FlexLayoutContainer
|
|
45
|
+
containerName="left"
|
|
46
|
+
grow={1}
|
|
47
|
+
isResizePanel
|
|
48
|
+
>
|
|
49
|
+
<div>Left</div>
|
|
50
|
+
</FlexLayoutContainer>
|
|
51
|
+
|
|
52
|
+
<FlexLayoutContainer containerName="right" grow={1}>
|
|
53
|
+
<div>Right</div>
|
|
54
|
+
</FlexLayoutContainer>
|
|
55
|
+
</>
|
|
56
|
+
</FlexLayout>
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
- `direction="row"`: left/right split
|
|
63
|
+
- `direction="column"`: top/bottom split
|
|
64
|
+
- A container with `isResizePanel={true}` will render a **resize panel** after it.
|
|
65
|
+
|
|
66
|
+
> **Important:** `layoutName` and `containerName` are used as keys in internal Store/Subjects.
|
|
67
|
+
> If multiple instances of the same layout can appear on the screen, use a stable unique value (e.g. `useId()`) to avoid collisions.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## FlexLayout
|
|
72
|
+
|
|
73
|
+
### import
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import { FlexLayout } from "@byeolnaerim/flex-layout";
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Props
|
|
80
|
+
|
|
81
|
+
- `layoutName: string`
|
|
82
|
+
A key to identify the layout instance.
|
|
83
|
+
- `direction: "row" | "column"`
|
|
84
|
+
Flex direction (horizontal/vertical split).
|
|
85
|
+
- `children: ReactNode`
|
|
86
|
+
- `className?: string`
|
|
87
|
+
- `panelClassName?: string`
|
|
88
|
+
Class name for customizing the resize panel style.
|
|
89
|
+
- `panelMovementMode?: "default" | "bulldozer"`
|
|
90
|
+
How adjacent panels are pushed during resizing.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## FlexLayoutContainer (paired with FlexLayout)
|
|
95
|
+
|
|
96
|
+
### import
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
import { FlexLayoutContainer } from "@byeolnaerim/flex-layout";
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Props
|
|
103
|
+
|
|
104
|
+
- `containerName: string` _(required)_
|
|
105
|
+
Panel/container key.
|
|
106
|
+
- `children: ReactNode`
|
|
107
|
+
- `grow?: number`
|
|
108
|
+
Flex-grow ratio (e.g. left 2, right 1).
|
|
109
|
+
- `className?: string`
|
|
110
|
+
- `style?: React.CSSProperties`
|
|
111
|
+
- `isResizePanel?: boolean`
|
|
112
|
+
Whether to place a resize panel after this container.
|
|
113
|
+
- `panelMode?: "default" | "left-cylinder" | "right-cylinder" | "top-cylinder" | "bottom-cylinder"`
|
|
114
|
+
Controls the **visual orientation/anchor** of the resize panel (and the open/close motion).
|
|
115
|
+
- `isFitContent?: boolean`
|
|
116
|
+
Fit based on content size.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## (Advanced) Open/Close panels + dynamic grow control
|
|
121
|
+
|
|
122
|
+
The library provides an RxJS Subject map keyed by `containerName` to send **open/close** events to panels.
|
|
123
|
+
|
|
124
|
+
### Open/Close with containerOpenCloseSubjectMap
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
import { containerOpenCloseSubjectMap } from "@byeolnaerim/flex-layout/providers";
|
|
128
|
+
|
|
129
|
+
// Example: open right-panel
|
|
130
|
+
containerOpenCloseSubjectMap["right-panel"].next({
|
|
131
|
+
mode: "open",
|
|
132
|
+
openOption: { isPrevSizeOpen: true }, // restore previous size
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Example: close right-panel
|
|
136
|
+
containerOpenCloseSubjectMap["right-panel"].next({
|
|
137
|
+
mode: "close",
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
- `mode: "toggle" | "open" | "close"`
|
|
142
|
+
- `openOption.isPrevSizeOpen?: boolean`: restore previous opened size
|
|
143
|
+
- Optional callbacks: `onOpen?`, `onClose?`
|
|
144
|
+
|
|
145
|
+
### Control grow directly with useContainers
|
|
146
|
+
|
|
147
|
+
`useContainers(layoutName)` returns the actual DOM containers for that layout.
|
|
148
|
+
It’s useful for cases like: “only the selected tab container has grow=1, the others have grow=0”, with transitions.
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
import { useContainers } from "@byeolnaerim/flex-layout/providers";
|
|
152
|
+
|
|
153
|
+
const containers = useContainers(layoutName);
|
|
154
|
+
// e.g. containers.forEach(el => el.style.flex = "1 1 0%");
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Split Screen
|
|
160
|
+
|
|
161
|
+
Split Screen supports the pattern:
|
|
162
|
+
“drag and drop to left/right/top/bottom/center → dynamically create a new split view at that position.”
|
|
163
|
+
|
|
164
|
+
⚠️ Note: `FlexLayoutSplitScreen` has not been thoroughly validated for stability in real-world usage. It may not behave as you expect.
|
|
165
|
+
|
|
166
|
+
### 1) FlexLayoutSplitScreen (split root)
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
"use client";
|
|
170
|
+
|
|
171
|
+
import { FlexLayoutSplitScreen } from "@byeolnaerim/flex-layout";
|
|
172
|
+
|
|
173
|
+
export default function Page() {
|
|
174
|
+
return (
|
|
175
|
+
<FlexLayoutSplitScreen
|
|
176
|
+
layoutName="rootSplitScreen"
|
|
177
|
+
containerName="dashboard"
|
|
178
|
+
navigationTitle="Dashboard"
|
|
179
|
+
dropDocumentOutsideOption={{
|
|
180
|
+
openUrl: "/",
|
|
181
|
+
widthRatio: 0.7,
|
|
182
|
+
heightRatio: 0.5,
|
|
183
|
+
}}
|
|
184
|
+
>
|
|
185
|
+
<div>Dashboard content</div>
|
|
186
|
+
</FlexLayoutSplitScreen>
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**Props (summary)**
|
|
192
|
+
|
|
193
|
+
- `layoutName: string`: root key of the split-screen tree
|
|
194
|
+
- `containerName: string`: key for this screen/container
|
|
195
|
+
- `children: ReactNode`
|
|
196
|
+
- `navigationTitle?: string`: title for tabs/navigation
|
|
197
|
+
- `dropDocumentOutsideOption?: { openUrl: string; widthRatio?: number; heightRatio?: number }`
|
|
198
|
+
If dropped “outside the screen”, open it as a new window/document.
|
|
199
|
+
- `screenKey?: string`: a unique value used to identify a screen inside `FlexLayoutSplitScreen`. If empty, a 32-character random default is generated. For dynamic split-screen views you can’t control, leaving it empty is recommended.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## FlexLayoutSplitScreenDragBox (Split Screen drag source)
|
|
204
|
+
|
|
205
|
+
`FlexLayoutSplitScreenDragBox` is a **draggable source component**.
|
|
206
|
+
When you drag it and drop on a Split Screen boundary, it renders `targetComponent` at the drop position and creates a new split view.
|
|
207
|
+
|
|
208
|
+
```tsx
|
|
209
|
+
import { FlexLayoutSplitScreenDragBox } from "@byeolnaerim/flex-layout";
|
|
210
|
+
|
|
211
|
+
<FlexLayoutSplitScreenDragBox
|
|
212
|
+
containerName="menu:users"
|
|
213
|
+
navigationTitle="Users"
|
|
214
|
+
targetComponent={<UsersPage />}
|
|
215
|
+
dropDocumentOutsideOption={{
|
|
216
|
+
openUrl: "/admin/users",
|
|
217
|
+
widthRatio: 0.7,
|
|
218
|
+
heightRatio: 0.5,
|
|
219
|
+
}}
|
|
220
|
+
>
|
|
221
|
+
<button>Open Users</button>
|
|
222
|
+
</FlexLayoutSplitScreenDragBox>;
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Props (summary)**
|
|
226
|
+
|
|
227
|
+
- `containerName: string` _(required)_: unique key for the draggable item
|
|
228
|
+
- `children: ReactNode`: the visible UI
|
|
229
|
+
- `targetComponent?: ReactNode`: component to render in the new split pane
|
|
230
|
+
- `navigationTitle?: string`
|
|
231
|
+
- `dropDocumentOutsideOption?: { openUrl: string; widthRatio?: number; heightRatio?: number }`
|
|
232
|
+
- `customData?: any`: arbitrary data passed along on drop
|
|
233
|
+
- `scrollTargetRef?: RefObject<HTMLElement>`: scroll target while dragging (optional)
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## (Advanced) Use Drag & Drop only with FlexLayoutSplitScreenDragBox + useDragCapture
|
|
238
|
+
|
|
239
|
+
You can use it as **pure Drag & Drop**, without creating Split Screen.
|
|
240
|
+
|
|
241
|
+
- Drag source: `FlexLayoutSplitScreenDragBox`
|
|
242
|
+
- Drop target: `useDragCapture(ref)`
|
|
243
|
+
|
|
244
|
+
### Example: drop unitCard → slotCard to insert info
|
|
245
|
+
|
|
246
|
+
```tsx
|
|
247
|
+
import { useDragCapture } from "@byeolnaerim/flex-layout";
|
|
248
|
+
|
|
249
|
+
const dropRef = useRef<HTMLDivElement>(null);
|
|
250
|
+
const dragState = useDragCapture(dropRef);
|
|
251
|
+
|
|
252
|
+
useEffect(() => {
|
|
253
|
+
if (!dragState) return;
|
|
254
|
+
const {
|
|
255
|
+
isDrop,
|
|
256
|
+
containerName, // containerName of the dragged item
|
|
257
|
+
positionName, // boundary position (left/top/right/bottom/center)
|
|
258
|
+
customData, // customData passed from DragBox
|
|
259
|
+
} = dragState;
|
|
260
|
+
|
|
261
|
+
if (isDrop) {
|
|
262
|
+
// TODO: handle “equip/insert” logic based on containerName/customData
|
|
263
|
+
}
|
|
264
|
+
}, [dragState]);
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
`dragState` includes `isDrop`, `isDragging`, `isOver`, `positionName`, and coordinates (`x`, `y`).
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## Practical patterns (ideas)
|
|
272
|
+
|
|
273
|
+
- **Tabs + FlexLayout**
|
|
274
|
+
Control container `flex` values via `useContainers(layoutName)` and animate
|
|
275
|
+
“selected tab grow=1, others grow=0”.
|
|
276
|
+
- **Master–Detail (left list / right detail)**
|
|
277
|
+
Open/close the detail panel with
|
|
278
|
+
`containerOpenCloseSubjectMap["right"].next({ mode: selected ? "open" : "close" })`.
|
|
279
|
+
- **Admin Split Screen**
|
|
280
|
+
Drag sidebar items (`FlexLayoutSplitScreenDragBox`) → create a new split view at the desired position.
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## Export paths
|
|
285
|
+
|
|
286
|
+
Use whichever import style you prefer.
|
|
287
|
+
|
|
288
|
+
```ts
|
|
289
|
+
// 1) unified imports from root
|
|
290
|
+
import {
|
|
291
|
+
FlexLayout,
|
|
292
|
+
FlexLayoutContainer,
|
|
293
|
+
FlexLayoutSplitScreen,
|
|
294
|
+
FlexLayoutSplitScreenDragBox,
|
|
295
|
+
} from "@byeolnaerim/flex-layout";
|
|
296
|
+
|
|
297
|
+
// 2) components subpath (if preferred)
|
|
298
|
+
import {
|
|
299
|
+
FlexLayout,
|
|
300
|
+
FlexLayoutContainer,
|
|
301
|
+
} from "@byeolnaerim/flex-layout/components";
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## Tips
|
|
307
|
+
|
|
308
|
+
- Use a meaningful prefix for `containerName` (e.g. `left-container-${id}`, `menu:${identifierId}`)
|
|
309
|
+
This makes debugging and preventing collisions much easier in Split Screen.
|
|
310
|
+
- In Next.js, add `"use client"` at the top of files that use these layout components.
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## The internal implementation/style structure is still evolving, so the API may change over time.
|
package/README.ko.md
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
# @byeolnaerim/flex-layout
|
|
2
|
+
|
|
3
|
+
> 이 문서는 코드베이스와 사용 사례들을 제공받은 ChatGPT가 작성하였습니다. 문서의 내용이 정확하지 않을 수 있으며, FlexLayout 개발자가 검토 후 재수정할 예정입니다.
|
|
4
|
+
|
|
5
|
+
React(Next.js)에서 **flex 기반 리사이즈 패널 + 스플릿 스크린 + Drag & Drop** UI를 빠르게 만들기 위한 컴포넌트 모음입니다.
|
|
6
|
+
|
|
7
|
+
이 라이브러리의 핵심은 **`<FlexLayout />`** 입니다.
|
|
8
|
+
`FlexLayout` + `FlexLayoutContainer`로 “패널이 나뉘고(가로/세로), 드래그로 크기를 조절하고, 필요하면 열고/닫는” 레이아웃을 구성합니다.
|
|
9
|
+
그 위에 **Split Screen**(동적 분할 화면)과, 이를 위한 **`FlexLayoutSplitScreenDragBox`** / **`useDragCapture`** 기반 Drag & Drop을 제공합니다.
|
|
10
|
+
|
|
11
|
+
> ⚠️ 대부분의 컴포넌트가 `window`, `ResizeObserver` 등을 사용합니다. **Next.js(App Router)에서는 Client Component** 사용을 권장합니다. (`"use client"`)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 설치
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# npm
|
|
19
|
+
npm i @byeolnaerim/flex-layout
|
|
20
|
+
|
|
21
|
+
# yarn
|
|
22
|
+
yarn add @byeolnaerim/flex-layout
|
|
23
|
+
|
|
24
|
+
# pnpm
|
|
25
|
+
pnpm add @byeolnaerim/flex-layout
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 빠른 시작
|
|
31
|
+
|
|
32
|
+
### 1) FlexLayout + FlexLayoutContainer (기본 리사이즈 레이아웃)
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
"use client";
|
|
36
|
+
|
|
37
|
+
import { FlexLayout, FlexLayoutContainer } from "@byeolnaerim/flex-layout";
|
|
38
|
+
|
|
39
|
+
export default function Basic() {
|
|
40
|
+
return (
|
|
41
|
+
<div style={{ height: 500 }}>
|
|
42
|
+
<FlexLayout layoutName="basic" direction="row">
|
|
43
|
+
<>
|
|
44
|
+
<FlexLayoutContainer
|
|
45
|
+
containerName="left"
|
|
46
|
+
grow={1}
|
|
47
|
+
isResizePanel
|
|
48
|
+
>
|
|
49
|
+
<div>Left</div>
|
|
50
|
+
</FlexLayoutContainer>
|
|
51
|
+
|
|
52
|
+
<FlexLayoutContainer containerName="right" grow={1}>
|
|
53
|
+
<div>Right</div>
|
|
54
|
+
</FlexLayoutContainer>
|
|
55
|
+
</>
|
|
56
|
+
</FlexLayout>
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
- `direction="row"`: 좌/우 분할
|
|
63
|
+
- `direction="column"`: 상/하 분할
|
|
64
|
+
- `FlexLayoutContainer`의 `isResizePanel`이 `true`인 컨테이너 뒤에 **리사이즈 패널**이 붙습니다.
|
|
65
|
+
|
|
66
|
+
> **중요:** `layoutName`, `containerName`은 내부 Store/Subject의 키로 쓰입니다.
|
|
67
|
+
> 화면에 동일한 레이아웃이 여러 개 생길 수 있다면 `useId()` 같은 안정적인 값을 써서 유니크하게 관리하는 걸 권장합니다.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## FlexLayout
|
|
72
|
+
|
|
73
|
+
### import
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import { FlexLayout } from "@byeolnaerim/flex-layout";
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Props
|
|
80
|
+
|
|
81
|
+
- `layoutName: string`
|
|
82
|
+
레이아웃 인스턴스를 구분하는 이름(키).
|
|
83
|
+
- `direction: "row" | "column"`
|
|
84
|
+
flex 방향(가로/세로 분할).
|
|
85
|
+
- `children: ReactNode`
|
|
86
|
+
- `className?: string`
|
|
87
|
+
- `panelClassName?: string`
|
|
88
|
+
리사이즈 패널 커스텀 스타일을 위한 클래스.
|
|
89
|
+
- `panelMovementMode?: "default" | "bulldozer"`
|
|
90
|
+
패널 이동(리사이즈) 시 인접 패널들을 어떻게 밀어낼지에 대한 모드.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## FlexLayoutContainer (FlexLayout과 세트)
|
|
95
|
+
|
|
96
|
+
### import
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
import { FlexLayoutContainer } from "@byeolnaerim/flex-layout";
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Props
|
|
103
|
+
|
|
104
|
+
- `containerName: string` _(필수)_
|
|
105
|
+
컨테이너(패널) 이름(키).
|
|
106
|
+
- `children: ReactNode`
|
|
107
|
+
- `grow?: number`
|
|
108
|
+
flex-grow 기반 비율. (예: 좌 2, 우 1)
|
|
109
|
+
- `className?: string`
|
|
110
|
+
- `style?: React.CSSProperties`
|
|
111
|
+
- `isResizePanel?: boolean`
|
|
112
|
+
이 컨테이너 뒤에 리사이즈 패널을 붙일지 여부.
|
|
113
|
+
- `panelMode?: "default" | "left-cylinder" | "right-cylinder" | "top-cylinder" | "bottom-cylinder"`
|
|
114
|
+
리사이즈 패널(또는 open/close 모션)의 **UI 방향/앵커(기준)** 를 결정하는 옵션입니다.
|
|
115
|
+
- `isFitContent?: boolean`
|
|
116
|
+
콘텐츠 높이/너비를 기준으로 fit 하게 처리.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## (응용) 패널 열고/닫기 + grow 동적 제어
|
|
121
|
+
|
|
122
|
+
라이브러리는 `containerName`을 키로 **패널 open/close** 이벤트를 보낼 수 있도록 RxJS Subject 맵을 제공합니다.
|
|
123
|
+
|
|
124
|
+
### containerOpenCloseSubjectMap으로 열고 닫기
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
import { containerOpenCloseSubjectMap } from "@byeolnaerim/flex-layout/providers";
|
|
128
|
+
|
|
129
|
+
// 예: right-panel 열기
|
|
130
|
+
containerOpenCloseSubjectMap["right-panel"].next({
|
|
131
|
+
mode: "open",
|
|
132
|
+
openOption: { isPrevSizeOpen: true }, // 이전 사이즈로 열기
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// 예: right-panel 닫기
|
|
136
|
+
containerOpenCloseSubjectMap["right-panel"].next({
|
|
137
|
+
mode: "close",
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
- `mode: "toggle" | "open" | "close"`
|
|
142
|
+
- `openOption.isPrevSizeOpen?: boolean` : 이전에 열려있던 크기 복원 여부
|
|
143
|
+
- `onOpen?`, `onClose?` 콜백 제공
|
|
144
|
+
|
|
145
|
+
### useContainers로 grow를 직접 조절하기
|
|
146
|
+
|
|
147
|
+
`useContainers(layoutName)`은 해당 레이아웃의 실제 DOM 컨테이너 배열을 줍니다.
|
|
148
|
+
질문에 주신 예시처럼 **탭에 따라 특정 컨테이너만 grow=1**, 나머지는 grow=0으로 애니메이션 처리할 때 유용합니다.
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
import { useContainers } from "@byeolnaerim/flex-layout/providers";
|
|
152
|
+
|
|
153
|
+
const containers = useContainers(layoutName);
|
|
154
|
+
// containers.forEach(el => el.style.flex = "1 1 0%"); 같은 방식으로 제어
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Split Screen
|
|
160
|
+
|
|
161
|
+
Split Screen은 “드래그로 화면을 좌/우/상/하/중앙에 드롭 → 해당 위치에 새 화면을 동적으로 분할 생성”하는 패턴을 제공합니다.
|
|
162
|
+
⚠️ 주의 : FlexLayoutSplitScreen은 실제 사용 환경에서의 안정성을 제대로 확인하지 않았습니다. 당신이 의도한 대로 동작하지 않을 수도 있습니다.
|
|
163
|
+
|
|
164
|
+
### 1) FlexLayoutSplitScreen (스플릿 루트)
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
"use client";
|
|
168
|
+
|
|
169
|
+
import { FlexLayoutSplitScreen } from "@byeolnaerim/flex-layout";
|
|
170
|
+
|
|
171
|
+
export default function Page() {
|
|
172
|
+
return (
|
|
173
|
+
<FlexLayoutSplitScreen
|
|
174
|
+
layoutName="rootSplitScreen"
|
|
175
|
+
containerName="dashboard"
|
|
176
|
+
navigationTitle="대시보드"
|
|
177
|
+
dropDocumentOutsideOption={{
|
|
178
|
+
openUrl: "/",
|
|
179
|
+
widthRatio: 0.7,
|
|
180
|
+
heightRatio: 0.5,
|
|
181
|
+
}}
|
|
182
|
+
>
|
|
183
|
+
<div>대시보드 콘텐츠</div>
|
|
184
|
+
</FlexLayoutSplitScreen>
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Props (요약)**
|
|
190
|
+
|
|
191
|
+
- `layoutName: string` : 스플릿 화면 트리의 루트 키
|
|
192
|
+
- `containerName: string` : 이 화면(컨테이너)의 키
|
|
193
|
+
- `children: ReactNode`
|
|
194
|
+
- `navigationTitle?: string` : 탭/내비게이션용 타이틀
|
|
195
|
+
- `dropDocumentOutsideOption?: { openUrl: string; widthRatio?: number; heightRatio?: number }`
|
|
196
|
+
드롭을 “화면 밖”으로 했을 때 새 창/문서로 열기 옵션
|
|
197
|
+
- `screenKey?: string` : FlexLayoutSplitScreen내부에서 screen을 판별할 때 사용하는 유니크한 값입니다. 빈값일 경우 default 값으로 32자리의 랜덤 값을 생성합니다. 개발자가 제어할 수 없는 동적 분할 화면 뷰라면 가급적 빈값으로 이용하는 것을 권장합니다.
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## FlexLayoutSplitScreenDragBox (스플릿 스크린 드래그 소스)
|
|
202
|
+
|
|
203
|
+
`FlexLayoutSplitScreenDragBox`는 **드래그 가능한 소스 컴포넌트**입니다.
|
|
204
|
+
이걸 끌어서 Split Screen 경계에 드롭하면, drop 대상 위치에 `targetComponent`를 렌더링하며 분할 화면이 만들어집니다.
|
|
205
|
+
|
|
206
|
+
```tsx
|
|
207
|
+
import { FlexLayoutSplitScreenDragBox } from "@byeolnaerim/flex-layout";
|
|
208
|
+
|
|
209
|
+
<FlexLayoutSplitScreenDragBox
|
|
210
|
+
containerName="menu:users"
|
|
211
|
+
navigationTitle="유저 목록"
|
|
212
|
+
targetComponent={<UsersPage />}
|
|
213
|
+
dropDocumentOutsideOption={{
|
|
214
|
+
openUrl: "/admin/users",
|
|
215
|
+
widthRatio: 0.7,
|
|
216
|
+
heightRatio: 0.5,
|
|
217
|
+
}}
|
|
218
|
+
>
|
|
219
|
+
<button>유저 목록 열기</button>
|
|
220
|
+
</FlexLayoutSplitScreenDragBox>;
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**Props (요약)**
|
|
224
|
+
|
|
225
|
+
- `containerName: string` _(필수)_ : 드래그 항목 고유 키
|
|
226
|
+
- `children: ReactNode` : 실제 렌더링될 UI
|
|
227
|
+
- `targetComponent?: ReactNode` : 분할 화면에 새로 띄울 컴포넌트
|
|
228
|
+
- `navigationTitle?: string`
|
|
229
|
+
- `dropDocumentOutsideOption?: { openUrl: string; widthRatio?: number; heightRatio?: number }`
|
|
230
|
+
- `customData?: any` : 드롭 시 함께 전달할 임의 데이터
|
|
231
|
+
- `scrollTargetRef?: RefObject<HTMLElement>` : 드래그 중 스크롤 타겟(옵션)
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## (응용) FlexLayoutSplitScreenDragBox + useDragCapture로 Drag & Drop만 쓰기
|
|
236
|
+
|
|
237
|
+
Split Screen을 만들지 않고, **순수 Drag & Drop**으로도 활용할 수 있습니다.
|
|
238
|
+
|
|
239
|
+
- 드래그 소스: `FlexLayoutSplitScreenDragBox`
|
|
240
|
+
- 드롭 타겟: `useDragCapture(ref)`
|
|
241
|
+
|
|
242
|
+
### 예: unitCard → slotCard로 드롭해서 정보 삽입
|
|
243
|
+
|
|
244
|
+
```tsx
|
|
245
|
+
import { useDragCapture } from "@byeolnaerim/flex-layout";
|
|
246
|
+
|
|
247
|
+
const dropRef = useRef<HTMLDivElement>(null);
|
|
248
|
+
const dragState = useDragCapture(dropRef);
|
|
249
|
+
|
|
250
|
+
useEffect(() => {
|
|
251
|
+
if (!dragState) return;
|
|
252
|
+
const {
|
|
253
|
+
isDrop,
|
|
254
|
+
containerName, // 드래그된 item의 containerName
|
|
255
|
+
positionName, // 어느 경계에 놓였는지 (left/top/right/bottom/center)
|
|
256
|
+
customData, // DragBox에서 넘긴 customData
|
|
257
|
+
} = dragState;
|
|
258
|
+
|
|
259
|
+
if (isDrop) {
|
|
260
|
+
// TODO: containerName/customData 기반으로 “장착/삽입” 처리
|
|
261
|
+
}
|
|
262
|
+
}, [dragState]);
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
`dragState`에는 드롭 여부(`isDrop`), 드래그 중 여부(`isDragging`), 오버 여부(`isOver`), 위치(`positionName`), 좌표(`x`,`y`) 등이 포함됩니다.
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## 실사용 패턴 모음 (아이디어)
|
|
270
|
+
|
|
271
|
+
- **탭 UI + FlexLayout**
|
|
272
|
+
`useContainers(layoutName)`로 DOM 컨테이너의 `flex`를 직접 제어해서
|
|
273
|
+
“선택된 탭만 grow=1, 나머지 grow=0” 전환 애니메이션 구현.
|
|
274
|
+
- **마스터-디테일(좌 리스트 / 우 상세)**
|
|
275
|
+
`containerOpenCloseSubjectMap["right"].next({ mode: selected ? "open" : "close" })`로
|
|
276
|
+
상세 패널을 상황에 따라 열고 닫기.
|
|
277
|
+
- **어드민 화면 Split Screen**
|
|
278
|
+
사이드바 메뉴(`FlexLayoutSplitScreenDragBox`)를 드래그 → 원하는 위치에 새 화면 분할.
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Export 경로
|
|
283
|
+
|
|
284
|
+
일반적으로 아래 둘 중 편한 방식으로 import 하면 됩니다.
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
// 1) 루트에서 통합 import
|
|
288
|
+
import {
|
|
289
|
+
FlexLayout,
|
|
290
|
+
FlexLayoutContainer,
|
|
291
|
+
FlexLayoutSplitScreen,
|
|
292
|
+
FlexLayoutSplitScreenDragBox,
|
|
293
|
+
} from "@byeolnaerim/flex-layout";
|
|
294
|
+
|
|
295
|
+
// 2) components 서브패스 (선호 시)
|
|
296
|
+
import {
|
|
297
|
+
FlexLayout,
|
|
298
|
+
FlexLayoutContainer,
|
|
299
|
+
} from "@byeolnaerim/flex-layout/components";
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## Tips
|
|
305
|
+
|
|
306
|
+
- **containerName은 가능한 한 “의미 있는 prefix”**를 붙이세요. (예: `left-container-${id}`, `menu:${identifierId}`)
|
|
307
|
+
Split Screen에서 중복 방지/트리 구성 시 디버깅이 훨씬 쉬워집니다.
|
|
308
|
+
- Next.js에서 서버 컴포넌트로 쓰면 오류가 날 수 있으니, 레이아웃 관련 파일 상단에 `"use client"`를 붙이세요.
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## 내부 구현/스타일 구조는 계속 발전 중이라 API는 조금씩 바뀔 수 있습니다.
|