react-zeugma 0.5.1 → 0.5.3

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