react-zeugma 0.5.1 → 0.5.2

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.
Files changed (2) hide show
  1. package/README.md +306 -58
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,34 +1,26 @@
1
- <div align="center">
1
+ {/_ @meta title="Introduction" _/}
2
2
 
3
3
  # react-zeugma
4
4
 
5
- **Recursive drag-and-drop dashboard layout engine for React**
5
+ **A recursive, drag-and-drop dashboard layout engine for React.**
6
6
 
7
- Combines the tree-based, arbitrary splitting of [react-mosaic](https://github.com/nomcopter/react-mosaic) with the declarative, state-driven API of [react-grid-layout](https://github.com/react-grid-layout/react-grid-layout).
7
+ `react-zeugma` combines the tree-based, arbitrary splitting capabilities of `react-mosaic` with the declarative, state-driven API model of `react-grid-layout`. Built with React 18+, TypeScript, and [`@dnd-kit`](https://dndkit.com).
8
8
 
9
9
  [![npm version](https://img.shields.io/npm/v/react-zeugma?color=brightgreen&style=flat-square)](https://www.npmjs.com/package/react-zeugma)
10
10
  [![bundle size](https://img.shields.io/bundlephobia/minzip/react-zeugma?color=blue&style=flat-square)](https://bundlephobia.com/package/react-zeugma)
11
11
  [![license](https://img.shields.io/npm/l/react-zeugma?color=yellow&style=flat-square)](./LICENSE)
12
12
  [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue?style=flat-square&logo=typescript)](https://www.typescriptlang.org)
13
13
 
14
- </div>
15
-
16
- ---
17
-
18
- ## Introduction
19
-
20
- **react-zeugma** is a recursive drag-and-drop dashboard layout engine for React. It combines the tree-based, arbitrary splitting of _react-mosaic_ with the declarative, state-driven API of _react-grid-layout_, built on top of [`@dnd-kit`](https://dndkit.com).
21
-
14
+ > [!TIP]
22
15
  > **Headless Design System** — react-zeugma is entirely style-agnostic and relies on your class name configurations for styling visual states. You bring your own CSS/Tailwind rules, and we handle the complex drag-and-drop mechanics, resize handle math, and layout tree calculations.
23
16
 
24
17
  ### Core Features
25
18
 
26
- - **Recursive Split Trees** Nest rows and columns to any depth using a simple serialized JSON node structure.
27
- - **5-Zone Docking Previews** Drag panels on the top, bottom, left, or right edges of another pane to split it, or onto the center to swap their positions.
28
- - **Native Flexbox Resizers** Fluid, non-blocking split handles built on pointer events.
29
- - **Accessible Drag-and-Drop** Built on top of the performant and accessible [`@dnd-kit`](https://dndkit.com) toolkit.
30
- - **Fullscreen Zoom Toggle** Programmatically expand any pane to cover the entire viewport and snap it back instantly.
31
- - **Tree-shakeable & Tiny** — ESM-first with zero runtime CSS. Bring your own styles.
19
+ - **Recursive Split Trees**: Nest rows and columns to any depth using a simple serialized JSON node structure.
20
+ - **5-Zone Docking Previews**: Drag panels on the top, bottom, left, or right edges of another pane to split it, or onto the center to swap their positions.
21
+ - **Native Flexbox Resizers**: Fluid, non-blocking split handles built on pointer events.
22
+ - **Accessible Drag-and-Drop**: Built on top of the performant and accessible [`@dnd-kit`](https://dndkit.com) toolkit.
23
+ - **Fullscreen Zoom Toggle**: Programmatically expand any pane to cover the entire viewport and snap it back instantly.
32
24
 
33
25
  ---
34
26
 
@@ -40,7 +32,8 @@ Install the package into your React project using your preferred package manager
40
32
  npm install react-zeugma
41
33
  ```
42
34
 
43
- > **Peer Dependencies:** react-zeugma is compatible with both **React 18** and **React 19** (along with matching `react-dom`).
35
+ > [!NOTE]
36
+ > **Peer Dependencies** — react-zeugma is compatible with both **React 18** and **React 19** (along with matching `react-dom`).
44
37
 
45
38
  ---
46
39
 
@@ -51,7 +44,6 @@ Import the core components and configure the layout state inside your React appl
51
44
  ```tsx
52
45
  import { useState } from 'react'
53
46
  import { DashboardProvider, PaneTree, Pane, DragHandle, TreeNode } from 'react-zeugma'
54
- import './Dashboard.css' // Import your custom styles here
55
47
 
56
48
  const initialLayout: TreeNode = {
57
49
  type: 'split',
@@ -71,16 +63,16 @@ function MyPane({ id }: { id: string }) {
71
63
  return (
72
64
  <Pane id={id}>
73
65
  {({ isDragging, remove }) => (
74
- <div className={`zeugma-pane-container ${isDragging ? 'is-dragging' : ''}`}>
66
+ <div className={`h-full flex flex-col bg-[#18181b] ${isDragging ? 'opacity-30' : ''}`}>
75
67
  <DragHandle>
76
- <div className="zeugma-pane-header">
77
- <span className="zeugma-pane-title">{id}</span>
78
- <button onClick={remove} className="zeugma-pane-close">
68
+ <div className="px-3 py-2 bg-[#27272a] border-b border-[#3f3f46] flex items-center justify-between cursor-grab">
69
+ <span className="text-xs uppercase text-zinc-300 font-bold">{id}</span>
70
+ <button onClick={remove} className="text-zinc-500 hover:text-rose-400 text-xs">
79
71
  ×
80
72
  </button>
81
73
  </div>
82
74
  </DragHandle>
83
- <div className="zeugma-pane-content">Content for {id}</div>
75
+ <div className="flex-1 p-4 text-sm text-zinc-400">Content for {id}</div>
84
76
  </div>
85
77
  )}
86
78
  </Pane>
@@ -92,7 +84,7 @@ export default function Dashboard() {
92
84
 
93
85
  return (
94
86
  <DashboardProvider layout={layout} onChange={setLayout} renderPane={(id) => <MyPane id={id} />}>
95
- <div className="zeugma-dashboard-wrapper">
87
+ <div className="w-screen h-screen">
96
88
  <PaneTree />
97
89
  </div>
98
90
  </DashboardProvider>
@@ -108,17 +100,25 @@ export default function Dashboard() {
108
100
 
109
101
  The context provider that sets up the drag-and-drop state machine, monitors active drags, and registers layout change notifications.
110
102
 
111
- | Prop | Type | Required | Description |
112
- | ------------------------ | ------------------------------------ | -------- | ----------------------------------------------------------------------------------------- |
113
- | `layout` | `TreeNode \| null` | Yes | The serializable tree layout schema. |
114
- | `onChange` | `(layout: TreeNode \| null) => void` | Yes | Fires when resizes, splits, swaps, or removes modify the tree. |
115
- | `renderPane` | `(paneId: string) => ReactNode` | Yes | Renderer function lookup that returns a `<Pane>` structure. |
116
- | `renderDragOverlay` | `(activeId: string) => ReactNode` | No | Renders a custom cursor-following drag preview. |
117
- | `classNames` | `ZeugmaClassNames` | No | Custom classes for overriding pane, resizer, and drop preview overlays. |
118
- | `fullscreenPaneId` | `string \| null` | No | Active ID of the pane taking full viewport coverage. |
119
- | `onFullscreenChange` | `(paneId: string \| null) => void` | No | Callback triggered when a pane enters/leaves fullscreen. |
120
- | `onRemove` | `(paneId: string) => void` | No | Callback triggered when a pane is closed/removed. |
121
- | `dragActivationDistance` | `number` | No | Minimum pointer drag distance (in pixels) required to activate dragging. Defaults to `8`. |
103
+ | Prop | Type | Required | Description |
104
+ | ------------------------ | --------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
105
+ | `layout` | `TreeNode \| null` | Yes | The serializable tree layout schema. |
106
+ | `onChange` | `(layout: TreeNode \| null) => void` | Yes | Fires when resizes, splits, swaps, or removes modify the tree. |
107
+ | `renderPane` | `(paneId: string) => ReactNode` | Yes | Renderer function lookup that returns a `<Pane>` structure. |
108
+ | `classNames` | `ZeugmaClassNames` | No | Custom classes for overriding pane, resizer, and drop preview overlays. |
109
+ | `fullscreenPaneId` | `string \| null` | No | Active ID of the pane taking full viewport coverage. |
110
+ | `renderDragOverlay` | `(activeId: string) => ReactNode` | No | Renders a custom cursor-following drag preview overlay. |
111
+ | `onFullscreenChange` | `(paneId: string \| null) => void` | No | Callback triggered when a pane enters or leaves fullscreen. |
112
+ | `onRemove` | `(paneId: string) => void` | No | Callback triggered when a pane is closed/removed from the layout tree. |
113
+ | `dragActivationDistance` | `number` | No | Minimum pointer drag distance (in pixels) required to activate dragging. Defaults to `8`. |
114
+ | `onDragStart` | `(activeId: string) => void` | No | Callback triggered when dragging starts on a pane. |
115
+ | `onDragEnd` | `(activeId: string, overId: string \| null, dropAction: any) => void` | No | Callback triggered when dragging ends, providing swap or split details. The `overId` is set to `'root'` if dropped onto outer boundaries to split the entire dashboard root. |
116
+ | `onResizeStart` | `(currentNode: SplitNode) => void` | No | Callback triggered when resizing starts on a split node. |
117
+ | `onResize` | `(currentNode: SplitNode, percentage: number) => void` | No | Callback triggered continuously while resizing a split node. |
118
+ | `onResizeEnd` | `(currentNode: SplitNode, percentage: number) => void` | No | Callback triggered when resizing ends on a split node. |
119
+ | `renderResizer` | `(props: ResizerRenderProps) => ReactNode` | No | Custom renderer function for rendering custom-styled resizer bars. |
120
+ | `minSplitPercentage` | `number` | No | Minimum resizing limit percentage. Defaults to `5`. |
121
+ | `maxSplitPercentage` | `number` | No | Maximum resizing limit percentage. Defaults to `95`. |
122
122
 
123
123
  ### `<PaneTree>`
124
124
 
@@ -153,7 +153,7 @@ Defines the interactive drag region inside a `<Pane>`. **Must be placed inside a
153
153
 
154
154
  | Prop | Type | Required | Description |
155
155
  | ----------- | --------------------- | -------- | ---------------------------------------------------------------- |
156
- | `children` | `React.ReactNode` | Yes | Element(s) that function as the drag handle (e.g., pane header). |
156
+ | `children` | `ReactNode` | Yes | Element(s) that function as the drag handle (e.g., pane header). |
157
157
  | `className` | `string` | No | Custom CSS class for the drag handle wrapper. |
158
158
  | `style` | `React.CSSProperties` | No | Inline styles for the drag handle wrapper. |
159
159
 
@@ -179,7 +179,7 @@ Swaps the positions of `idA` and `idB` nodes directly inside the tree structure.
179
179
 
180
180
  Splits the targeted `targetId` pane inside the tree with `direction` (_row_ / _column_) and type (_left_, _right_, _top_, _bottom_) to insert `paneToAdd`.
181
181
 
182
- #### `splitRoot(tree: TreeNode | null, draggingId: string, splitType: 'left' | 'right' | 'top' | 'bottom'): TreeNode | null`
182
+ #### `splitRoot(tree, draggingId, splitType)`
183
183
 
184
184
  Splits the entire dashboard tree at the root, placing the dragged `draggingId` pane on one half and the rest of the layout tree on the other.
185
185
 
@@ -196,11 +196,12 @@ Use custom CSS or styling rules to style resizers, dragging states, drop preview
196
196
  renderPane={renderPane}
197
197
  classNames={{
198
198
  // resizer handles
199
- resizer: 'zeugma-resizer',
199
+ resizer:
200
+ 'bg-transparent hover:bg-indigo-500/50 active:bg-indigo-500 transition-colors duration-150',
200
201
  // split previews
201
- dropPreview: 'zeugma-drop-preview',
202
+ dropPreview: 'bg-indigo-500/10 border-2 border-dashed border-indigo-500/50 backdrop-blur-xs',
202
203
  // swap previews
203
- swapPreview: 'zeugma-swap-preview',
204
+ swapPreview: 'bg-amber-500/10 border-2 border-dashed border-amber-500/50 backdrop-blur-xs',
204
205
  }}
205
206
  >
206
207
  <PaneTree />
@@ -245,45 +246,284 @@ export interface PaneRenderProps {
245
246
  toggleFullscreen: () => void
246
247
  remove: () => void
247
248
  }
249
+
250
+ export interface ResizerRenderProps {
251
+ direction: SplitDirection
252
+ splitPercentage: number
253
+ resizerSize: number
254
+ isResizing: boolean
255
+ onPointerDown: (e: React.PointerEvent<HTMLDivElement>) => void
256
+ }
257
+
258
+ export interface DashboardContextValue {
259
+ layout: TreeNode | null
260
+ onLayoutChange: (newLayout: TreeNode | null) => void
261
+ renderPane: (paneId: string) => ReactNode
262
+ activeId: string | null
263
+ fullscreenPaneId: string | null
264
+ classNames: ZeugmaClassNames
265
+ onRemove?: (paneId: string) => void
266
+ onFullscreenChange?: (paneId: string | null) => void
267
+ snapThreshold?: number
268
+ onResizeStart?: (currentNode: SplitNode) => void
269
+ onResize?: (currentNode: SplitNode, percentage: number) => void
270
+ onResizeEnd?: (currentNode: SplitNode, percentage: number) => void
271
+ renderResizer?: (props: ResizerRenderProps) => ReactNode
272
+ minSplitPercentage?: number
273
+ maxSplitPercentage?: number
274
+ removePane: (paneId: string) => void
275
+ addPane: (paneId: string) => void
276
+ swapPanes: (paneIdA: string, paneIdB: string) => void
277
+ splitPane: (
278
+ targetId: string,
279
+ direction: SplitDirection,
280
+ splitType: 'left' | 'right' | 'top' | 'bottom',
281
+ paneToAdd: string,
282
+ ) => void
283
+ updateSplitPercentage: (currentNode: SplitNode, percentage: number) => void
284
+ }
248
285
  ```
249
286
 
250
287
  ---
251
288
 
252
289
  ## SKILL.md
253
290
 
254
- A comprehensive developer skill configuration is published alongside the docs for AI agents and reference integrations. Download it from the [documentation site](https://react-zeugma.com/docs#skill-md).
291
+ Below is the comprehensive developer skill configuration for integrations, tree manipulation, and styling patterns within `react-zeugma`. Copy or download it for AI agents or reference.
255
292
 
293
+ ````markdown
294
+ ---
295
+ name: use-react-zeugma
296
+ description: Integrate, configure, style, and programmatically manipulate dashboard layouts using the react-zeugma package.
256
297
  ---
257
298
 
258
- ## Local Development
299
+ # Skill: Using react-zeugma
259
300
 
260
- ```bash
261
- # Clone & install
262
- git clone [https://github.com/yusufarsln98/react-zeugma.git](https://github.com/yusufarsln98/react-zeugma.git)
263
- cd react-zeugma
264
- npm install
301
+ `react-zeugma` is a recursive drag-and-drop dashboard layout engine for React. It combines tree-based pane splitting (similar to `react-mosaic`) with a declarative, state-driven API (similar to `react-grid-layout`), built using `@dnd-kit/core`.
302
+
303
+ ---
265
304
 
266
- # Run the interactive demo
267
- npm run demo
305
+ ## 1. Data Model (Tree Nodes)
268
306
 
269
- # Run Storybook docs
270
- npm run storybook
307
+ The entire dashboard layout is represented as a serializable recursive tree structure.
308
+
309
+ ### Types & Interface
310
+
311
+ ```ts
312
+ export type SplitDirection = 'row' | 'column'
313
+
314
+ export interface SplitNode {
315
+ type: 'split'
316
+ direction: SplitDirection
317
+ first: TreeNode
318
+ second: TreeNode
319
+ splitPercentage: number // 0 to 100
320
+ }
321
+
322
+ export interface PaneNode {
323
+ type: 'pane'
324
+ paneId: string
325
+ }
271
326
 
272
- # Build the library
273
- npm run build
327
+ export type TreeNode = SplitNode | PaneNode
274
328
  ```
275
329
 
330
+ - **`PaneNode` (Leaf):** Represents a single content pane. It must have a unique `paneId`.
331
+ - **`SplitNode` (Branch):** Splits its area horizontally (`column`) or vertically (`row`) into two child `TreeNode` nodes (`first` and `second`), based on `splitPercentage`.
332
+
276
333
  ---
277
334
 
278
- ## Contributing
335
+ ## 2. Core Components
336
+
337
+ ### `<DashboardProvider>`
338
+
339
+ The root context provider. It handles the drag-and-drop event loop and coordinates the layout state.
340
+
341
+ #### Props
342
+
343
+ - `layout: TreeNode | null` — The current dashboard layout tree.
344
+ - `onChange: (newLayout: TreeNode | null) => void` — Callback triggered when the layout tree changes (resizing, dragging to split, dragging to swap).
345
+ - `renderPane: (paneId: string) => ReactNode` — Callback to render the contents of a pane given its ID.
346
+ - `renderDragOverlay?: (activeId: string) => ReactNode` — (Optional) Renders a custom cursor-following drag preview.
347
+ - `classNames?: ZeugmaClassNames` — (Optional) CSS class overrides for styling various layout elements.
348
+ - `fullscreenPaneId?: string | null` — (Optional) ID of the pane currently in fullscreen mode.
349
+ - `onFullscreenChange?: (paneId: string | null) => void` — (Optional) Callback triggered when a pane enters/leaves fullscreen.
350
+ - `onRemove?: (paneId: string) => void` — (Optional) Callback triggered when a pane is closed/removed.
351
+ - `dragActivationDistance?: number` — (Optional) Minimum pointer drag distance (in pixels) required to activate dragging. Defaults to `8`.
352
+ - `onDragStart?: (activeId: string) => void` — (Optional) Callback triggered when dragging starts on a pane.
353
+ - `onDragEnd?: (activeId: string, overId: string | null, dropAction: any) => void` — (Optional) Callback triggered when dragging ends. The `overId` will be `'root'` if the pane was dropped onto the outer dashboard boundaries to split the root layout.
354
+ - `onResizeStart?: (currentNode: SplitNode) => void` — (Optional) Callback triggered when resizing starts.
355
+ - `onResize?: (currentNode: SplitNode, percentage: number) => void` — (Optional) Callback triggered during resizing.
356
+ - `onResizeEnd?: (currentNode: SplitNode, percentage: number) => void` — (Optional) Callback triggered when resizing ends.
357
+ - `renderResizer?: (props: ResizerRenderProps) => ReactNode` — (Optional) Custom resizer bar component renderer.
358
+ - `minSplitPercentage?: number` — (Optional) Minimum resizing limit percentage (defaults to `5`).
359
+ - `maxSplitPercentage?: number` — (Optional) Maximum resizing limit percentage (defaults to `95`).
360
+
361
+ ### `<PaneTree>`
362
+
363
+ Recursively renders the split nodes and pane nodes. Must be placed inside `<DashboardProvider>`.
364
+
365
+ #### Props
366
+
367
+ - `tree?: TreeNode | null` — (Optional) Custom subtree to render. Defaults to the provider's root `layout`.
368
+ - `resizerSize?: number` — (Optional) Thickness of the split resizer bars in pixels. Defaults to `4`.
369
+
370
+ ### `<Pane>`
371
+
372
+ Wraps the contents of an individual pane. It sets up draggable and droppable zones.
279
373
 
280
- See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines on how to get started.
374
+ #### Props
375
+
376
+ - `id: string` — The unique ID corresponding to a `PaneNode`'s `paneId`.
377
+ - `children: (props: PaneRenderProps) => ReactNode` — Render prop function.
378
+
379
+ #### `PaneRenderProps`
380
+
381
+ ```ts
382
+ interface PaneRenderProps {
383
+ isDragging: boolean
384
+ isFullscreen: boolean
385
+ toggleFullscreen: () => void
386
+ remove: () => void
387
+ }
388
+ ```
389
+
390
+ ### `<DragHandle>`
391
+
392
+ Defines the interactive drag region inside a `<Pane>`. **Must be placed inside a `<Pane>` component.**
393
+
394
+ #### Props
395
+
396
+ - `children: React.ReactNode` — Element(s) that function as the drag handle (e.g., pane header).
397
+ - `className?: string`
398
+ - `style?: React.CSSProperties`
281
399
 
282
400
  ---
283
401
 
284
- ## License
402
+ ## 3. Programmatic State Utilities
403
+
404
+ Import these helpers from `react-zeugma` to manipulate the tree layout programmatically in your state handlers:
405
+
406
+ - **`removePane(tree: TreeNode | null, idToRemove: string): TreeNode | null`**
407
+ Removes a pane from the tree and collapses the leftover sibling split node.
408
+ - **`splitPane(tree: TreeNode | null, targetId: string, direction: SplitDirection, splitType: 'left' | 'right' | 'top' | 'bottom', paneToAdd: string): TreeNode | null`**
409
+ Splits a specific target pane by nesting it under a new `SplitNode` along with a new pane.
410
+ - **`splitRoot(tree: TreeNode | null, draggingId: string, splitType: 'left' | 'right' | 'top' | 'bottom'): TreeNode | null`**
411
+ Splits the entire dashboard tree at the root, placing the dragged pane on one half and the remaining layout tree on the other.
412
+ - **`swapPanes(tree: TreeNode | null, idA: string, idB: string): TreeNode | null`**
413
+ Swaps the positions of two panes in the tree.
285
414
 
286
- [MIT](./LICENSE) © yusufarsln98
415
+ Alternatively, you can consume the convenient mutation helpers directly from the **`useDashboard()`** context hook inside pane components without importing utilities:
416
+
417
+ - **`removePane(paneId: string) => void`**
418
+ - **`addPane(paneId: string) => void`**
419
+ - **`swapPanes(paneIdA: string, paneIdB: string) => void`**
420
+ - **`splitPane(targetId: string, direction: SplitDirection, splitType: string, paneToAdd: string) => void`**
421
+ - **`updateSplitPercentage(currentNode: SplitNode, percentage: number) => void`**
422
+
423
+ ---
424
+
425
+ ## 4. Basic Integration Recipe
426
+
427
+ ```tsx
428
+ import { useState } from 'react'
429
+ import { DashboardProvider, PaneTree, Pane, DragHandle, TreeNode } from 'react-zeugma'
430
+
431
+ const initialLayout: TreeNode = {
432
+ type: 'split',
433
+ direction: 'row',
434
+ splitPercentage: 50,
435
+ first: { type: 'pane', paneId: 'sidebar' },
436
+ second: { type: 'pane', paneId: 'main' },
437
+ }
438
+
439
+ function CustomPane({ id }: { id: string }) {
440
+ return (
441
+ <Pane id={id}>
442
+ {({ isDragging, isFullscreen, toggleFullscreen, remove }) => (
443
+ <div style={{ height: '100%', border: '1px solid #ccc', opacity: isDragging ? 0.5 : 1 }}>
444
+ <div style={{ display: 'flex', background: '#eee', padding: 8 }}>
445
+ <DragHandle style={{ flex: 1 }}>
446
+ <strong>Header: {id}</strong>
447
+ </DragHandle>
448
+ <button onClick={toggleFullscreen}>
449
+ {isFullscreen ? 'Exit Fullscreen' : 'Fullscreen'}
450
+ </button>
451
+ <button onClick={remove}>Close</button>
452
+ </div>
453
+ <div style={{ padding: 16 }}>Content for {id}</div>
454
+ </div>
455
+ )}
456
+ </Pane>
457
+ )
458
+ }
459
+
460
+ export default function App() {
461
+ const [layout, setLayout] = useState<TreeNode | null>(initialLayout)
462
+ const [fullscreenId, setFullscreenId] = useState<string | null>(null)
463
+
464
+ const handleRemove = (paneId: string) => {
465
+ // Remove the pane and update layout
466
+ setLayout((prev) => removePane(prev, paneId))
467
+ }
468
+
469
+ return (
470
+ <DashboardProvider
471
+ layout={layout}
472
+ onChange={setLayout}
473
+ renderPane={(id) => <CustomPane id={id} />}
474
+ fullscreenPaneId={fullscreenId}
475
+ onFullscreenChange={setFullscreenId}
476
+ onRemove={handleRemove}
477
+ >
478
+ <div style={{ width: '100vw', height: '100vh' }}>
479
+ <PaneTree />
480
+ </div>
481
+ </DashboardProvider>
482
+ )
483
+ }
484
+ ```
485
+
486
+ ---
487
+
488
+ ## 5. Styling Customization
489
+
490
+ `react-zeugma` is style-agnostic and relies on class name configuration for visual states. Define classes in your styling framework and pass them via the `classNames` prop on `<DashboardProvider>`:
491
+
492
+ ```ts
493
+ interface ZeugmaClassNames {
494
+ pane?: string // Applied to the outer wrapper of <Pane>
495
+ dropPreview?: string // Applied to the preview box when hovering over edge dropzones
496
+ swapPreview?: string // Applied to the preview box when hovering over center dropzone
497
+ dragOverlay?: string // Applied to the cursor-following drag preview portal
498
+ resizer?: string // Applied to the drag-to-resize split bar
499
+ }
500
+ ```
501
+
502
+ ### CSS Example:
503
+
504
+ ```css
505
+ /* Custom resizer style */
506
+ .my-resizer {
507
+ background-color: #e2e8f0;
508
+ transition: background-color 0.2s;
509
+ }
510
+ .my-resizer:hover {
511
+ background-color: #3b82f6;
512
+ }
513
+
514
+ /* Edge drop previews */
515
+ .my-drop-preview {
516
+ background-color: rgba(59, 130, 246, 0.2);
517
+ border: 2px dashed #3b82f6;
518
+ }
519
+
520
+ /* Center swap preview */
521
+ .my-swap-preview {
522
+ background-color: rgba(16, 185, 129, 0.25);
523
+ border: 2px solid #10b981;
524
+ }
525
+ ```
526
+ ````
287
527
 
288
528
  ---
289
529
 
@@ -294,3 +534,11 @@ _Zeugma_ is an ancient city of Commagene, located in modern-day **Gaziantep, Tur
294
534
  During modern excavation efforts, archeologists discovered some of the most breathtaking Greco-Roman mosaic panels in history, now housed inside the **Zeugma Mosaic Museum** in Gaziantep. The famous _"Gypsy Girl" (Çingene Kızı)_ mosaic, with her hauntingly detailed eyes, has become a global icon of the city.
295
535
 
296
536
  > _"We chose the name Zeugma because of this ancient craftsmanship. Mosaics are assembled from hundreds of tiny, individual tesserae tiles to form a magnificent, cohesive picture. In the same spirit, react-zeugma lets you build beautiful, customized application workspaces from simple, individual components. Many tiles, one masterpiece."_
537
+
538
+ ---
539
+
540
+ ## Links
541
+
542
+ - [GitHub Repository](https://github.com/yusufarsln98/react-zeugma)
543
+ - [npm Package](https://www.npmjs.com/package/react-zeugma)
544
+ - [Contributing Guide](https://github.com/yusufarsln98/react-zeugma/blob/master/CONTRIBUTING.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-zeugma",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Recursive drag-and-drop dashboard layout engine for React — combining the tree-based splitting of react-mosaic with the declarative API of react-grid-layout.",
5
5
  "type": "module",
6
6
  "sideEffects": false,