tailwind-grid-layout 1.0.1 → 1.0.2-beta.0

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.ko.md CHANGED
@@ -1,606 +1,272 @@
1
1
  # Tailwind Grid Layout
2
2
 
3
- React용 현대적이고 가벼운 그리드 레이아웃 시스템으로, Tailwind CSS로 구축되었습니다. react-grid-layout의 강력한 대안으로 모든 기능을 제공하면서도 더 작은 번들 크기를 자랑합니다.
3
+ React용 현대적이고 가벼운 그리드 레이아웃 시스템. Tailwind CSS로 구축되었으며, react-grid-layout의 강력한 대안입니다.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/tailwind-grid-layout.svg)](https://www.npmjs.com/package/tailwind-grid-layout)
6
6
  [![license](https://img.shields.io/npm/l/tailwind-grid-layout.svg)](https://github.com/Seungwoo321/tailwind-grid-layout/blob/main/LICENSE)
7
7
  [![bundle size](https://img.shields.io/bundlephobia/minzip/tailwind-grid-layout)](https://bundlephobia.com/package/tailwind-grid-layout)
8
8
 
9
- > 버전 0.1.0 - 번째 안정 릴리스
9
+ > [Live Demo](https://seungwoo321.github.io/tailwind-grid-layout/) | [Storybook](https://seungwoo321.github.io/tailwind-grid-layout/storybook)
10
10
 
11
11
  > [English](./README.md) | 한국어
12
12
 
13
+ ![Tailwind Grid Layout Screenshot](https://raw.githubusercontent.com/Seungwoo321/tailwind-grid-layout/main/assets/screenshot-hero.png)
14
+
13
15
  ## 특징
14
16
 
15
- - 🎯 **react-grid-layout과 완벽한 기능 호환성**
16
- - 🪶 **경량화** - Tailwind CSS를 사용한 더 작은 번들 크기
17
- - 🎨 **Tailwind 네이티브** - Tailwind CSS 유틸리티로 구축
18
- - 📱 **반응형** - 모든 화면 크기에서 작동
19
- - 📱 **모바일 터치** - 향상된 제스처 지원으로 터치 디바이스 완전 최적화
20
- - 터치 포인트 정확도 향상
21
- - 롱프레스 제스처 지원
22
- - 스크롤과 드래그 충돌 방지
23
- - 멀티터치 방지로 안정성 확보
24
- - 🔧 **TypeScript** - 완전한 TypeScript 지원
25
- - ⚡ **고성능** - 최적화된 렌더링과 애니메이션
26
- - 🧪 **철저한 테스트** - 100% 테스트 커버리지
17
+ - **react-grid-layout과 완벽한 기능 호환**
18
+ - **경량화** - 더 작은 번들 크기 (~15KB gzip)
19
+ - **Tailwind 네이티브** - Tailwind CSS v4 기반
20
+ - **반응형** - 모든 화면 크기 지원
21
+ - **모바일 터치** - 터치 디바이스 완전 지원
22
+ - **TypeScript** - 완벽한 타입 지원
23
+ - **철저한 테스트** - 100% 테스트 커버리지
27
24
 
28
25
  ## 설치
29
26
 
30
27
  ```bash
31
28
  npm install tailwind-grid-layout
32
- # 또는
33
- yarn add tailwind-grid-layout
34
- # 또는
35
- pnpm add tailwind-grid-layout
36
29
  ```
37
30
 
38
- ### 필수 요구사항
39
-
40
- - React 19.1.0
41
- - Tailwind CSS 4.1.8+ (v4 전용 - CSS 우선 구성)
42
- - Node.js 20.0.0+
43
- - pnpm 10.11.0+
44
-
45
- ## Tailwind CSS v4 설정
31
+ ### 요구사항
46
32
 
47
- 라이브러리는 새로운 CSS 우선 구성 방식을 사용하는 Tailwind CSS v4가 필요합니다. JavaScript 구성 파일은 필요하지 않습니다.
48
-
49
- ```css
50
- /* 메인 CSS 파일에서 */
51
- @import "tailwindcss";
52
-
53
- /* 선택사항: 커스텀 테마 구성 추가 */
54
- @theme {
55
- --color-grid-placeholder: oklch(0.7 0.15 210);
56
- --color-grid-handle: oklch(0.3 0.05 210);
57
- }
58
- ```
33
+ - React 18+
34
+ - Tailwind CSS 4.x
59
35
 
60
36
  ## 빠른 시작
61
37
 
62
38
  ```tsx
39
+ import { useState } from 'react'
63
40
  import { GridContainer } from 'tailwind-grid-layout'
41
+ import type { GridItem } from 'tailwind-grid-layout'
64
42
 
65
- const items = [
66
- { id: '1', x: 0, y: 0, w: 2, h: 2 },
67
- { id: '2', x: 2, y: 0, w: 2, h: 2 },
68
- { id: '3', x: 0, y: 2, w: 4, h: 2 }
43
+ const initialItems: GridItem[] = [
44
+ { id: '1', x: 0, y: 0, w: 3, h: 2 },
45
+ { id: '2', x: 3, y: 0, w: 5, h: 3 },
46
+ { id: '3', x: 8, y: 0, w: 4, h: 2 },
69
47
  ]
70
48
 
71
49
  function App() {
50
+ const [items, setItems] = useState(initialItems)
51
+
72
52
  return (
73
53
  <GridContainer
74
54
  items={items}
75
55
  cols={12}
76
- rowHeight={60}
77
- onLayoutChange={(newLayout) => console.log(newLayout)}
56
+ rowHeight={80}
57
+ gap={16}
58
+ onLayoutChange={setItems}
78
59
  >
79
60
  {(item) => (
80
- <div className="bg-blue-500 text-white p-4 rounded">
81
- Item {item.id}
82
- </div>
83
- )}
84
- </GridContainer>
85
- )
86
- }
87
- ```
88
-
89
- ## 테스트
90
-
91
- ```bash
92
- # 테스트 실행
93
- pnpm test
94
-
95
- # 테스트 감시 모드
96
- pnpm test:watch
97
-
98
- # 테스트 커버리지 리포트
99
- pnpm test:coverage
100
- ```
101
-
102
- ### 테스트 커버리지
103
-
104
- 이 라이브러리는 100% 테스트 커버리지를 유지하고 있습니다:
105
-
106
- - ✅ Lines: 100%
107
- - ✅ Statements: 100%
108
- - ✅ Functions: 100%
109
- - ✅ Branches: 100%
110
-
111
- ## Props 참조
112
-
113
- ### GridContainer Props
114
-
115
- | Prop | 타입 | 기본값 | 설명 |
116
- |------|------|---------|-------------|
117
- | **items** | `GridItem[]` | 필수 | 위치와 크기 정보를 포함한 그리드 아이템 배열 |
118
- | **children** | `(item: GridItem) => ReactNode` | 필수 | 그리드 아이템을 위한 렌더 함수 |
119
- | **cols** | `number` | `12` | 그리드의 열 개수 |
120
- | **rowHeight** | `number` | `60` | 각 행의 높이 (픽셀) |
121
- | **gap** | `number` | `16` | 그리드 아이템 간 간격 (픽셀) |
122
- | **margin** | `[number, number]` | `[gap, gap]` | 아이템 간 여백 [수평, 수직] |
123
- | **containerPadding** | `[number, number]` | `[16, 16]` | 그리드 컨테이너 내부 패딩 [수평, 수직] |
124
- | **maxRows** | `number` | - | 최대 행 개수 |
125
- | **isDraggable** | `boolean` | `true` | 드래그 활성화/비활성화 |
126
- | **isResizable** | `boolean` | `true` | 크기 조정 활성화/비활성화 |
127
- | **preventCollision** | `boolean` | `false` | 아이템 충돌 방지 |
128
- | **allowOverlap** | `boolean` | `false` | 아이템 겹침 허용 |
129
- | **isBounded** | `boolean` | `true` | 컨테이너 경계 내 아이템 유지 |
130
- | **compactType** | `'vertical' \| 'horizontal' \| null` | `'vertical'` | 압축 타입 |
131
- | **resizeHandles** | `Array<'s' \| 'w' \| 'e' \| 'n' \| 'sw' \| 'nw' \| 'se' \| 'ne'>` | `['se']` | 크기 조정 핸들 위치 |
132
- | **draggableCancel** | `string` | - | 드래그를 트리거하지 않을 요소의 CSS 선택자 |
133
- | **draggableHandle** | `string` | - | 드래그 핸들용 CSS 선택자 |
134
- | **autoSize** | `boolean` | `true` | 모든 아이템에 맞게 컨테이너 높이 자동 조정 |
135
- | **verticalCompact** | `boolean` | `true` | 더 이상 사용되지 않음: compactType 사용 |
136
- | **transformScale** | `number` | `1` | 확대/축소 시 드래그/크기 조정을 위한 스케일 팩터 |
137
- | **droppingItem** | `Partial<GridItem>` | - | 외부에서 드래그 중 미리보기 아이템 |
138
- | **className** | `string` | - | 컨테이너에 추가할 CSS 클래스 |
139
- | **style** | `React.CSSProperties` | - | 컨테이너용 인라인 스타일 |
140
- | **onLayoutChange** | `(layout: GridItem[]) => void` | - | 레이아웃 변경 시 콜백 |
141
- | **onDragStart** | `(layout, oldItem, newItem, placeholder, e, element) => void` | - | 드래그 시작 콜백 |
142
- | **onDrag** | `(layout, oldItem, newItem, placeholder, e, element) => void` | - | 드래그 중 콜백 |
143
- | **onDragStop** | `(layout, oldItem, newItem, placeholder, e, element) => void` | - | 드래그 종료 콜백 |
144
- | **onResizeStart** | `(layout, oldItem, newItem, placeholder, e, element) => void` | - | 크기 조정 시작 콜백 |
145
- | **onResize** | `(layout, oldItem, newItem, placeholder, e, element) => void` | - | 크기 조정 중 콜백 |
146
- | **onResizeStop** | `(layout, oldItem, newItem, placeholder, e, element) => void` | - | 크기 조정 종료 콜백 |
147
-
148
- ### GridItem 속성
149
-
150
- | 속성 | 타입 | 필수 | 설명 |
151
- |----------|------|----------|-------------|
152
- | **id** | `string` | ✓ | 아이템의 고유 식별자 |
153
- | **x** | `number` | ✓ | 그리드 단위의 X 위치 |
154
- | **y** | `number` | ✓ | 그리드 단위의 Y 위치 |
155
- | **w** | `number` | ✓ | 그리드 단위의 너비 |
156
- | **h** | `number` | ✓ | 그리드 단위의 높이 |
157
- | **minW** | `number` | - | 최소 너비 |
158
- | **minH** | `number` | - | 최소 높이 |
159
- | **maxW** | `number` | - | 최대 너비 |
160
- | **maxH** | `number` | - | 최대 높이 |
161
- | **isDraggable** | `boolean` | - | 컨테이너의 isDraggable 재정의 |
162
- | **isResizable** | `boolean` | - | 컨테이너의 isResizable 재정의 |
163
- | **static** | `boolean` | - | 아이템을 정적으로 만들기 (이동/크기조정 불가) |
164
- | **className** | `string` | - | 아이템에 추가할 CSS 클래스 |
165
-
166
- ### ResponsiveGridContainer Props
167
-
168
- | Prop | 타입 | 기본값 | 설명 |
169
- |------|------|---------|-------------|
170
- | **layouts** | `BreakpointLayouts` | 필수 | 각 브레이크포인트별 레이아웃 객체 |
171
- | **breakpoints** | `{ [breakpoint: string]: number }` | `{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }` | 각 브레이크포인트의 최소 너비 |
172
- | **cols** | `{ [breakpoint: string]: number }` | `{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }` | 각 브레이크포인트의 열 개수 |
173
- | **onLayoutChange** | `(layout: GridItem[], layouts: BreakpointLayouts) => void` | - | 레이아웃 변경 시 현재 레이아웃과 모든 레이아웃을 전달하는 콜백 |
174
- | **onBreakpointChange** | `(newBreakpoint: string, cols: number) => void` | - | 브레이크포인트 변경 시 호출되는 콜백 |
175
- | **width** | `number` | - | 컨테이너 너비 (WidthProvider가 제공) |
176
- | ...GridContainerProps | - | - | items, cols, onLayoutChange를 제외한 모든 GridContainer props |
177
-
178
- ## react-grid-layout과의 비교
179
-
180
- | 기능 | react-grid-layout | tailwind-grid-layout | 비고 |
181
- |---------|-------------------|---------------------|--------|
182
- | **핵심 기능** |
183
- | 드래그 & 드롭 | ✅ | ✅ | 완전 지원 |
184
- | 크기 조정 | ✅ | ✅ | 8방향 크기 조정 |
185
- | 충돌 감지 | ✅ | ✅ | 50% 겹침 규칙 |
186
- | 자동 압축 | ✅ | ✅ | 수직, 수평 또는 없음 |
187
- | 정적 아이템 | ✅ | ✅ | 완전 지원 |
188
- | 경계 내 이동 | ✅ | ✅ | 아이템을 경계 내에 유지 |
189
- | **레이아웃 옵션** |
190
- | 반응형 브레이크포인트 | ✅ | ✅ | ResizeObserver를 통한 실시간 반응형 레이아웃 |
191
- | 레이아웃 유지 | ✅ | ✅ | onLayoutChange를 통해 |
192
- | 최소/최대 크기 | ✅ | ✅ | 완전 지원 |
193
- | 충돌 방지 | ✅ | ✅ | 완전 지원 |
194
- | 겹침 허용 | ✅ | ✅ | 완전 지원 |
195
- | **이벤트** |
196
- | 레이아웃 변경 | ✅ | ✅ | 완전 지원 |
197
- | 드래그 이벤트 | ✅ | ✅ | 시작, 이동, 종료 |
198
- | 크기 조정 이벤트 | ✅ | ✅ | 시작, 조정, 종료 |
199
- | 외부에서 드롭 | ✅ | ✅ | DroppableGridContainer로 완전 지원 |
200
- | **스타일링** |
201
- | CSS-in-JS | ✅ | ❌ | Tailwind 사용 |
202
- | 커스텀 클래스 | ✅ | ✅ | 완전 지원 |
203
- | 애니메이션 | ✅ | ✅ | Tailwind 트랜지션 |
204
- | **성능** |
205
- | 번들 크기 | ~30KB | ~22KB (gzip) | 더 작은 번들 |
206
- | 의존성 | React만 | React + Tailwind | |
207
- | Tree-shaking | ✅ | ✅ | 완전 지원 |
208
-
209
- ## 고급 예제
210
-
211
- ### 커스텀 드래그 핸들
212
-
213
- ```tsx
214
- <GridContainer items={items}>
215
- {(item) => (
216
- <div className="bg-white rounded-lg shadow p-4">
217
- <div className="cursor-move p-2 bg-gray-100 rounded" data-drag-handle>
218
- <GripIcon className="w-4 h-4" />
219
- </div>
220
- <div className="p-4">
221
- {item.id}의 콘텐츠
222
- </div>
223
- </div>
224
- )}
225
- </GridContainer>
226
- ```
227
-
228
- ### 정적 아이템
229
-
230
- ```tsx
231
- const items = [
232
- { id: '1', x: 0, y: 0, w: 4, h: 2, static: true }, // 이 아이템은 이동할 수 없음
233
- { id: '2', x: 4, y: 0, w: 4, h: 2 },
234
- ]
235
- ```
236
-
237
- ### 반응형 브레이크포인트
238
-
239
- ```tsx
240
- import { ResponsiveGridContainer, WidthProvider } from 'tailwind-grid-layout'
241
-
242
- // 각 브레이크포인트별 레이아웃 정의
243
- const layouts = {
244
- lg: [
245
- { id: '1', x: 0, y: 0, w: 6, h: 2 },
246
- { id: '2', x: 6, y: 0, w: 6, h: 2 },
247
- { id: '3', x: 0, y: 2, w: 4, h: 2 },
248
- { id: '4', x: 4, y: 2, w: 8, h: 2 }
249
- ],
250
- md: [
251
- { id: '1', x: 0, y: 0, w: 10, h: 2 },
252
- { id: '2', x: 0, y: 2, w: 10, h: 2 },
253
- { id: '3', x: 0, y: 4, w: 5, h: 2 },
254
- { id: '4', x: 5, y: 4, w: 5, h: 2 }
255
- ],
256
- sm: [
257
- { id: '1', x: 0, y: 0, w: 6, h: 2 },
258
- { id: '2', x: 0, y: 2, w: 6, h: 2 },
259
- { id: '3', x: 0, y: 4, w: 6, h: 2 },
260
- { id: '4', x: 0, y: 6, w: 6, h: 2 }
261
- ],
262
- xs: [
263
- { id: '1', x: 0, y: 0, w: 4, h: 2 },
264
- { id: '2', x: 0, y: 2, w: 4, h: 2 },
265
- { id: '3', x: 0, y: 4, w: 4, h: 2 },
266
- { id: '4', x: 0, y: 6, w: 4, h: 2 }
267
- ],
268
- xxs: [
269
- { id: '1', x: 0, y: 0, w: 2, h: 2 },
270
- { id: '2', x: 0, y: 2, w: 2, h: 2 },
271
- { id: '3', x: 0, y: 4, w: 2, h: 2 },
272
- { id: '4', x: 0, y: 6, w: 2, h: 2 }
273
- ]
274
- }
275
-
276
- // 옵션 1: 수동 너비 추적
277
- function ResponsiveExample() {
278
- const [currentBreakpoint, setCurrentBreakpoint] = useState('lg')
279
-
280
- return (
281
- <ResponsiveGridContainer
282
- layouts={layouts}
283
- onBreakpointChange={(breakpoint) => {
284
- setCurrentBreakpoint(breakpoint)
285
- console.log(`${breakpoint} 브레이크포인트로 전환됨`)
286
- }}
287
- onLayoutChange={(layout, allLayouts) => {
288
- // 레이아웃을 상태나 백엔드에 저장
289
- console.log('레이아웃 변경됨:', allLayouts)
290
- }}
291
- >
292
- {(item) => (
293
- <div className="bg-blue-500 text-white p-4 rounded">
61
+ <div className="bg-white rounded-xl p-4 shadow">
294
62
  아이템 {item.id}
295
63
  </div>
296
64
  )}
297
- </ResponsiveGridContainer>
298
- )
299
- }
300
-
301
- // 옵션 2: WidthProvider를 사용한 자동 너비 감지
302
- const ResponsiveGridWithWidth = WidthProvider(ResponsiveGridContainer)
303
-
304
- function App() {
305
- return (
306
- <ResponsiveGridWithWidth
307
- layouts={layouts}
308
- // 커스텀 브레이크포인트 (선택사항)
309
- breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
310
- // 커스텀 열 구성 (선택사항)
311
- cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
312
- >
313
- {(item) => <div>아이템 {item.id}</div>}
314
- </ResponsiveGridWithWidth>
65
+ </GridContainer>
315
66
  )
316
67
  }
317
68
  ```
318
69
 
319
- ### 외부에서 드래그 앤 드롭
70
+ ## 컴포넌트
320
71
 
321
- ```tsx
322
- import { DroppableGridContainer } from 'tailwind-grid-layout'
72
+ ### GridContainer
323
73
 
324
- <DroppableGridContainer
325
- items={items}
326
- onDrop={(newItem) => setItems([...items, newItem])}
327
- droppingItem={{ w: 2, h: 2 }} // 드롭된 아이템의 기본 크기
328
- >
329
- {(item) => <div>드롭된 아이템 {item.id}</div>}
330
- </DroppableGridContainer>
331
- ```
332
-
333
- ### 커스텀 크기 조정 핸들
74
+ 드래그 및 크기 조절을 지원하는 기본 그리드 컨테이너.
334
75
 
335
76
  ```tsx
336
77
  <GridContainer
337
78
  items={items}
338
- resizeHandles={['se', 'sw', 'ne', 'nw']} // 모서리 핸들만 활성화
79
+ cols={12}
80
+ rowHeight={80}
81
+ gap={16}
82
+ isDraggable={true}
83
+ isResizable={true}
84
+ resizeHandles={['se', 'sw', 'ne', 'nw', 'n', 's', 'e', 'w']}
85
+ onLayoutChange={setItems}
339
86
  >
340
87
  {(item) => <div>아이템 {item.id}</div>}
341
88
  </GridContainer>
342
89
  ```
343
90
 
344
- ### 충돌 방지
91
+ #### GridContainer Props
345
92
 
346
- ```tsx
347
- <GridContainer
348
- items={items}
349
- preventCollision={true} // 아이템이 겹칠 없음
350
- allowOverlap={false}
351
- >
352
- {(item) => <div>아이템 {item.id}</div>}
353
- </GridContainer>
354
- ```
355
-
356
- ### 최대 수를 가진 경계 그리드
93
+ | Prop | 타입 | 기본값 | 설명 |
94
+ |------|------|--------|------|
95
+ | `items` | `GridItem[]` | **필수** | 그리드 아이템 배열 |
96
+ | `children` | `(item: GridItem) => ReactNode` | **필수** | 각 아이템의 렌더 함수 |
97
+ | `cols` | `number` | `12` | 컬럼 수 |
98
+ | `rowHeight` | `number` | `60` | 행 높이 (픽셀) |
99
+ | `gap` | `number` | `16` | 아이템 간격 (픽셀) |
100
+ | `margin` | `[number, number]` | `[gap, gap]` | 마진 [가로, 세로] |
101
+ | `containerPadding` | `[number, number]` | `[16, 16]` | 컨테이너 패딩 [가로, 세로] |
102
+ | `maxRows` | `number` | `undefined` | 최대 행 수 |
103
+ | `isDraggable` | `boolean` | `true` | 드래그 활성화/비활성화 |
104
+ | `isResizable` | `boolean` | `true` | 크기 조절 활성화/비활성화 |
105
+ | `preventCollision` | `boolean` | `false` | 아이템 충돌 방지 |
106
+ | `allowOverlap` | `boolean` | `false` | 아이템 겹침 허용 |
107
+ | `isBounded` | `boolean` | `true` | 컨테이너 경계 내 유지 |
108
+ | `compactType` | `'vertical' \| 'horizontal' \| null` | `'vertical'` | 압축 방향 |
109
+ | `resizeHandles` | `ResizeHandle[]` | `['se']` | 크기 조절 핸들 위치 |
110
+ | `draggableCancel` | `string` | `undefined` | 드래그 불가 요소 CSS 선택자 |
111
+ | `draggableHandle` | `string` | `undefined` | 드래그 핸들 CSS 선택자 |
112
+ | `autoSize` | `boolean` | `true` | 컨테이너 높이 자동 조절 |
113
+ | `preserveInitialHeight` | `boolean` | `false` | 초기 레이아웃 높이 유지 |
114
+ | `transformScale` | `number` | `1` | 중첩 트랜스폼용 스케일 |
115
+ | `className` | `string` | `undefined` | 추가 CSS 클래스 |
116
+ | `style` | `CSSProperties` | `undefined` | 추가 인라인 스타일 |
117
+
118
+ #### GridContainer 콜백
119
+
120
+ | 콜백 | 타입 | 설명 |
121
+ |------|------|------|
122
+ | `onLayoutChange` | `(layout: GridItem[]) => void` | 레이아웃 변경 시 호출 |
123
+ | `onDragStart` | `(layout, oldItem, newItem, placeholder, e, element) => void` | 드래그 시작 시 호출 |
124
+ | `onDrag` | `(layout, oldItem, newItem, placeholder, e, element) => void` | 드래그 중 호출 |
125
+ | `onDragStop` | `(layout, oldItem, newItem, placeholder, e, element) => void` | 드래그 종료 시 호출 |
126
+ | `onResizeStart` | `(layout, oldItem, newItem, placeholder, e, element) => void` | 크기 조절 시작 시 호출 |
127
+ | `onResize` | `(layout, oldItem, newItem, placeholder, e, element) => void` | 크기 조절 중 호출 |
128
+ | `onResizeStop` | `(layout, oldItem, newItem, placeholder, e, element) => void` | 크기 조절 종료 시 호출 |
129
+
130
+ ### ResponsiveGridContainer
131
+
132
+ 브레이크포인트 기반 반응형 그리드.
357
133
 
358
134
  ```tsx
359
- <GridContainer
360
- items={items}
361
- isBounded={true}
362
- maxRows={10} // 그리드를 10행으로 제한
363
- >
364
- {(item) => <div>아이템 {item.id}</div>}
365
- </GridContainer>
366
- ```
135
+ import { ResponsiveGridContainer, WidthProvider } from 'tailwind-grid-layout'
367
136
 
368
- ### AutoSize 컨테이너
137
+ const ResponsiveGrid = WidthProvider(ResponsiveGridContainer)
369
138
 
370
- ```tsx
371
- <GridContainer
372
- items={items}
373
- autoSize={true} // 컨테이너 높이가 자동으로 조정됨
139
+ const layouts = {
140
+ lg: [{ id: '1', x: 0, y: 0, w: 6, h: 2 }],
141
+ md: [{ id: '1', x: 0, y: 0, w: 10, h: 2 }],
142
+ sm: [{ id: '1', x: 0, y: 0, w: 6, h: 2 }],
143
+ }
144
+
145
+ <ResponsiveGrid
146
+ layouts={layouts}
147
+ breakpoints={{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }}
148
+ cols={{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }}
149
+ onLayoutChange={(layout, layouts) => console.log(layouts)}
150
+ onBreakpointChange={(breakpoint, cols) => console.log(breakpoint)}
374
151
  >
375
152
  {(item) => <div>아이템 {item.id}</div>}
376
- </GridContainer>
377
-
378
- // 고정 높이
379
- <div style={{ height: 400, overflow: 'auto' }}>
380
- <GridContainer
381
- items={items}
382
- autoSize={false}
383
- style={{ height: '100%' }}
384
- >
385
- {(item) => <div>아이템 {item.id}</div>}
386
- </GridContainer>
387
- </div>
153
+ </ResponsiveGrid>
388
154
  ```
389
155
 
390
- ### 실시간 반응형 업데이트
156
+ #### ResponsiveGridContainer Props
391
157
 
392
- 반응형 그리드는 윈도우 크기가 조정될 자동으로 레이아웃을 업데이트하며, 최적의 성능을 위해 디바운스 처리됩니다:
158
+ GridContainer의 모든 props를 상속하며, `items`, `cols`, `onLayoutChange`는 제외됩니다.
393
159
 
394
- ```tsx
395
- import { ResponsiveGridContainer } from 'tailwind-grid-layout'
396
-
397
- function DashboardExample() {
398
- const [layouts, setLayouts] = useState({
399
- lg: dashboardLayoutLg,
400
- md: dashboardLayoutMd,
401
- sm: dashboardLayoutSm,
402
- xs: dashboardLayoutXs,
403
- xxs: dashboardLayoutXxs
404
- })
405
- const [currentBreakpoint, setCurrentBreakpoint] = useState('')
406
- const [currentCols, setCurrentCols] = useState(12)
160
+ | Prop | 타입 | 기본값 | 설명 |
161
+ |------|------|--------|------|
162
+ | `layouts` | `{ [breakpoint: string]: GridItem[] }` | **필수** | 각 브레이크포인트별 레이아웃 |
163
+ | `breakpoints` | `{ [breakpoint: string]: number }` | `{ lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 }` | 브레이크포인트 너비 |
164
+ | `cols` | `{ [breakpoint: string]: number }` | `{ lg: 12, md: 10, sm: 6, xs: 4, xxs: 2 }` | 브레이크포인트별 컬럼 수 |
165
+ | `onLayoutChange` | `(layout: GridItem[], layouts: BreakpointLayouts) => void` | `undefined` | 레이아웃 변경 시 호출 |
166
+ | `onBreakpointChange` | `(breakpoint: string, cols: number) => void` | `undefined` | 브레이크포인트 변경 시 호출 |
167
+ | `width` | `number` | `undefined` | 컨테이너 너비 (WidthProvider용) |
407
168
 
408
- return (
409
- <>
410
- {/* 시각적 브레이크포인트 표시기 */}
411
- <div className="mb-4 p-2 bg-green-100 rounded">
412
- 현재: {currentBreakpoint} ({currentCols}열)
413
- </div>
414
-
415
- <ResponsiveGridContainer
416
- layouts={layouts}
417
- onLayoutChange={(layout, allLayouts) => {
418
- setLayouts(allLayouts)
419
- }}
420
- onBreakpointChange={(breakpoint, cols) => {
421
- setCurrentBreakpoint(breakpoint)
422
- setCurrentCols(cols)
423
- }}
424
- rowHeight={100}
425
- gap={16}
426
- containerPadding={[16, 16]}
427
- >
428
- {(item) => (
429
- <Card key={item.id}>
430
- <CardHeader>
431
- <CardTitle>{item.title}</CardTitle>
432
- </CardHeader>
433
- <CardContent>
434
- {item.content}
435
- </CardContent>
436
- </Card>
437
- )}
438
- </ResponsiveGridContainer>
439
- </>
440
- )
441
- }
442
- ```
169
+ ### DroppableGridContainer
443
170
 
444
- ### DroppingItem 미리보기
171
+ 외부 드래그 앤 드롭을 지원하는 그리드.
445
172
 
446
173
  ```tsx
174
+ import { DroppableGridContainer } from 'tailwind-grid-layout'
175
+
447
176
  <DroppableGridContainer
448
177
  items={items}
449
- droppingItem={{ w: 4, h: 2 }} // 드래그 중 미리보기 표시
178
+ droppingItem={{ w: 2, h: 2 }}
450
179
  onDrop={(newItem) => setItems([...items, newItem])}
451
180
  >
452
181
  {(item) => <div>아이템 {item.id}</div>}
453
182
  </DroppableGridContainer>
454
183
  ```
455
184
 
456
- ## 레이아웃 유틸리티
457
-
458
- ### generateLayouts
459
-
460
- 단일 레이아웃 정의에서 모든 브레이크포인트에 대한 동일한 레이아웃을 생성합니다.
461
-
462
- ```tsx
463
- import { generateLayouts } from 'tailwind-grid-layout'
464
-
465
- const items = [
466
- { id: '1', x: 0, y: 0, w: 4, h: 2 },
467
- { id: '2', x: 4, y: 0, w: 4, h: 2 }
468
- ]
469
-
470
- // lg, md, sm, xs, xxs에 대해 동일한 위치로 레이아웃 생성
471
- const layouts = generateLayouts(items)
472
- ```
473
-
474
- ### generateResponsiveLayouts
185
+ #### DroppableGridContainer Props
475
186
 
476
- 브레이크포인트별 다른 컬럼 수에 맞게 레이아웃을 자동으로 조정합니다.
187
+ GridContainer의 모든 props를 상속하며, `onDrop`은 제외됩니다.
477
188
 
478
- ```tsx
479
- import { generateResponsiveLayouts } from 'tailwind-grid-layout'
480
-
481
- const items = [
482
- { id: '1', x: 0, y: 0, w: 12, h: 2 },
483
- { id: '2', x: 0, y: 2, w: 6, h: 2 }
484
- ]
485
-
486
- // 컬럼 제약에 맞게 아이템 너비와 위치를 조정
487
- const layouts = generateResponsiveLayouts(items, {
488
- lg: 12,
489
- md: 10,
490
- sm: 6,
491
- xs: 4,
492
- xxs: 2
493
- })
494
- ```
189
+ | Prop | 타입 | 기본값 | 설명 |
190
+ |------|------|--------|------|
191
+ | `droppingItem` | `Partial<GridItem>` | `{ w: 2, h: 2 }` | 드롭되는 아이템 크기 |
192
+ | `onDrop` | `(item: GridItem) => void` | `undefined` | 아이템 드롭 시 호출 |
495
193
 
496
- ### WidthProvider HOC
194
+ ### WidthProvider
497
195
 
498
- 최적의 성능을 위해 ResizeObserver를 사용하여 ResponsiveGridContainer에 컨테이너 너비를 자동으로 제공합니다.
196
+ ResponsiveGridContainer에 너비를 제공하는 HOC.
499
197
 
500
198
  ```tsx
501
199
  import { ResponsiveGridContainer, WidthProvider } from 'tailwind-grid-layout'
502
200
 
503
- const ResponsiveGridWithWidth = WidthProvider(ResponsiveGridContainer)
504
-
505
- // 기본 사용법
506
- <ResponsiveGridWithWidth
507
- layouts={layouts}
508
- rowHeight={100}
509
- >
510
- {(item) => <div>아이템 {item.id}</div>}
511
- </ResponsiveGridWithWidth>
512
-
513
- // 초기 렌더링 시 레이아웃 변경을 방지하는 measureBeforeMount 사용
514
- <ResponsiveGridWithWidth
515
- layouts={layouts}
516
- measureBeforeMount={true}
517
- rowHeight={100}
518
- >
519
- {(item) => <div>아이템 {item.id}</div>}
520
- </ResponsiveGridWithWidth>
201
+ const ResponsiveGrid = WidthProvider(ResponsiveGridContainer)
521
202
 
522
- // WidthProvider 기능:
523
- // - 효율적인 너비 감지를 위해 ResizeObserver 사용
524
- // - ResizeObserver를 사용할 수 없는 경우 window resize 이벤트로 대체
525
- // - measureBeforeMount 옵션으로 SSR을 올바르게 처리
526
- // - 더 나은 성능을 위한 디바운스된 리사이즈 처리 (150ms)
203
+ <ResponsiveGrid measureBeforeMount={true}>
204
+ {/* ... */}
205
+ </ResponsiveGrid>
527
206
  ```
528
207
 
208
+ #### WidthProvider Props
529
209
 
530
- ## 스타일링 가이드
210
+ | Prop | 타입 | 기본값 | 설명 |
211
+ |------|------|--------|------|
212
+ | `measureBeforeMount` | `boolean` | `false` | 마운트 전 너비 측정 |
531
213
 
532
- ### Tailwind CSS와 함께 사용하기
214
+ ## GridItem 속성
533
215
 
534
- 라이브러리는 Tailwind CSS와 원활하게 작동하도록 제작되었습니다:
216
+ | 속성 | 타입 | 필수 | 설명 |
217
+ |------|------|------|------|
218
+ | `id` | `string` | Yes | 고유 식별자 |
219
+ | `x` | `number` | Yes | 그리드 단위 X 위치 |
220
+ | `y` | `number` | Yes | 그리드 단위 Y 위치 |
221
+ | `w` | `number` | Yes | 그리드 단위 너비 |
222
+ | `h` | `number` | Yes | 그리드 단위 높이 |
223
+ | `minW` | `number` | No | 최소 너비 |
224
+ | `maxW` | `number` | No | 최대 너비 |
225
+ | `minH` | `number` | No | 최소 높이 |
226
+ | `maxH` | `number` | No | 최대 높이 |
227
+ | `static` | `boolean` | No | 이동/크기조절 불가 설정 |
228
+ | `isDraggable` | `boolean` | No | 컨테이너의 isDraggable 재정의 |
229
+ | `isResizable` | `boolean` | No | 컨테이너의 isResizable 재정의 |
230
+ | `className` | `string` | No | 아이템 추가 CSS 클래스 |
231
+
232
+ ## 크기 조절 핸들
233
+
234
+ 사용 가능한 크기 조절 핸들 위치:
235
+
236
+ | 핸들 | 위치 |
237
+ |------|------|
238
+ | `n` | 북쪽 (상단 중앙) |
239
+ | `s` | 남쪽 (하단 중앙) |
240
+ | `e` | 동쪽 (우측 중앙) |
241
+ | `w` | 서쪽 (좌측 중앙) |
242
+ | `ne` | 북동쪽 (우상단 모서리) |
243
+ | `nw` | 북서쪽 (좌상단 모서리) |
244
+ | `se` | 남동쪽 (우하단 모서리) |
245
+ | `sw` | 남서쪽 (좌하단 모서리) |
246
+
247
+ ## 유틸리티 함수
535
248
 
536
249
  ```tsx
537
- <GridContainer items={items} className="bg-gray-50 rounded-lg">
538
- {(item) => (
539
- <div className="bg-white rounded-lg shadow-md hover:shadow-lg transition-shadow">
540
- <div className="p-4">
541
- <h3 className="text-lg font-semibold">아이템 {item.id}</h3>
542
- </div>
543
- </div>
544
- )}
545
- </GridContainer>
546
- ```
547
-
548
- ### 커스텀 플레이스홀더
250
+ import { generateLayouts, generateResponsiveLayouts } from 'tailwind-grid-layout'
549
251
 
550
- 드래그 크기 조정 플레이스홀더는 CSS를 통해 스타일링할 수 있습니다:
252
+ // 아이템에서 레이아웃 생성
253
+ const layout = generateLayouts(items)
551
254
 
552
- ```css
553
- /* 드래그 플레이스홀더 */
554
- .tailwind-grid-layout .drag-placeholder {
555
- background: rgba(59, 130, 246, 0.15);
556
- border: 2px dashed rgb(59, 130, 246);
557
- }
558
-
559
- /* 크기 조정 플레이스홀더 */
560
- .tailwind-grid-layout .resize-placeholder {
561
- background: rgba(59, 130, 246, 0.1);
562
- border: 2px dashed rgb(59, 130, 246);
563
- }
255
+ // 반응형 레이아웃 생성
256
+ const responsiveLayouts = generateResponsiveLayouts(items, breakpoints, cols)
564
257
  ```
565
258
 
566
- ## 성능 최적화
567
-
568
- - **하드웨어 가속**: will-change와 함께 CSS transform 사용
569
- - **제스처 디바운싱**: 최적화된 터치 이벤트 처리
570
- - 터치 이벤트는 16ms (60fps) 단위로 디바운싱
571
- - 불필요한 렌더링 최소화
572
- - **메모리 관리**: 이벤트 리스너의 적절한 정리
573
- - **번들 분할**: Tree-shakable exports
574
- - **ResizeObserver**: 효율적인 컨테이너 너비 감지
575
- - **애니메이션 제어**: 상호작용 중 트랜지션 비활성화
259
+ ## react-grid-layout과 비교
576
260
 
577
- ### 터치 이벤트 처리
578
-
579
- 모바일 환경에서 최적의 성능을 위한 터치 이벤트 처리:
580
-
581
- - **Passive 리스너**: 스크롤 성능 향상을 위해 passive 터치 이벤트 사용
582
- - **제스처 인식**: 탭, 롱프레스, 드래그를 정확하게 구분
583
- - **관성 스크롤**: 터치 종료 자연스러운 관성 효과
584
- - **Pointer Events API**: 터치, 마우스, 입력 통합 처리
585
-
586
- ## 브라우저 지원
587
-
588
- - Chrome (최신)
589
- - Firefox (최신)
590
- - Safari (최신)
591
- - Edge (최신)
592
- - **Mobile Safari** (iOS 12+)
593
- - **Chrome Mobile** (Android 7+)
594
- - **ResizeObserver 지원**이 최적 성능을 위해 필요
595
-
596
- ## 기여하기
597
-
598
- 기여를 환영합니다! 자세한 내용은 [기여 가이드](CONTRIBUTING.md)를 참조하세요.
261
+ | 기능 | react-grid-layout | tailwind-grid-layout |
262
+ |------|-------------------|---------------------|
263
+ | 번들 크기 | ~30KB | ~15KB |
264
+ | Tailwind 네이티브 | No | Yes |
265
+ | TypeScript | Yes | Yes |
266
+ | 터치 지원 | Yes | Yes |
267
+ | 반응형 | Yes | Yes |
268
+ | 외부 CSS | 필요 | 불필요 |
599
269
 
600
270
  ## 라이선스
601
271
 
602
272
  MIT © [Seungwoo, Lee](./LICENSE)
603
-
604
- ## 감사의 글
605
-
606
- 이 라이브러리는 [react-grid-layout](https://github.com/react-grid-layout/react-grid-layout)에서 영감을 받았으며, 현대적이고 Tailwind 우선의 대안을 제공하는 것을 목표로 합니다.