react-edge-dock 1.0.2 → 1.0.4
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.md +23 -3
- package/dist/types.d.ts +11 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/useEdgeDock.d.ts.map +1 -1
- package/dist/useEdgeDock.js +39 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -65,7 +65,7 @@ export default function MyComponent() {
|
|
|
65
65
|
- `dockMode`: `"free"` | `"auto"` | `"manual"` - Docking behavior
|
|
66
66
|
- `dockEdge`: `"left"` | `"right"` | `"top"` | `"bottom"` - Fixed edge (manual mode)
|
|
67
67
|
- `allowedEdges`: `DockEdge[]` - Restrict docking to specific edges (e.g., `['left', 'right']` for horizontal only)
|
|
68
|
-
- `edgeOffset`: `number` - Gap from
|
|
68
|
+
- `edgeOffset`: `number | { left?: number; right?: number; top?: number; bottom?: number }` - Gap from edges in pixels
|
|
69
69
|
- `animation`: `boolean` - Enable snap animations
|
|
70
70
|
- `popupGap`: `number` - Gap between button and popup
|
|
71
71
|
- `position`: `{ x: number; y: number }` - Initial/controlled position
|
|
@@ -75,13 +75,33 @@ export default function MyComponent() {
|
|
|
75
75
|
|
|
76
76
|
## Examples
|
|
77
77
|
|
|
78
|
+
### Same offset for all edges
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
<EdgeDock
|
|
82
|
+
dockMode="auto"
|
|
83
|
+
edgeOffset={16}
|
|
84
|
+
button={<button>🚀</button>}
|
|
85
|
+
/>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Different offset for each edge
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
<EdgeDock
|
|
92
|
+
dockMode="auto"
|
|
93
|
+
edgeOffset={{ left: 10, right: 20, top: 15, bottom: 25 }}
|
|
94
|
+
button={<button>🎯</button>}
|
|
95
|
+
/>
|
|
96
|
+
```
|
|
97
|
+
|
|
78
98
|
### Restrict to horizontal edges only (left/right)
|
|
79
99
|
|
|
80
100
|
```tsx
|
|
81
101
|
<EdgeDock
|
|
82
102
|
dockMode="auto"
|
|
83
103
|
allowedEdges={['left', 'right']}
|
|
84
|
-
edgeOffset={16}
|
|
104
|
+
edgeOffset={{ left: 16, right: 24 }}
|
|
85
105
|
button={<button>📱</button>}
|
|
86
106
|
/>
|
|
87
107
|
```
|
|
@@ -102,7 +122,7 @@ export default function MyComponent() {
|
|
|
102
122
|
<EdgeDock
|
|
103
123
|
dockMode="manual"
|
|
104
124
|
dockEdge="right"
|
|
105
|
-
edgeOffset={20}
|
|
125
|
+
edgeOffset={{ right: 20 }}
|
|
106
126
|
button={<button>➡️</button>}
|
|
107
127
|
/>
|
|
108
128
|
```
|
package/dist/types.d.ts
CHANGED
|
@@ -27,6 +27,15 @@ export interface DockState {
|
|
|
27
27
|
/** Whether the popup is currently open */
|
|
28
28
|
isPopupOpen: boolean;
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Edge offset configuration - can be a single number for all edges or individual values
|
|
32
|
+
*/
|
|
33
|
+
export type EdgeOffset = number | {
|
|
34
|
+
left?: number;
|
|
35
|
+
right?: number;
|
|
36
|
+
top?: number;
|
|
37
|
+
bottom?: number;
|
|
38
|
+
};
|
|
30
39
|
/**
|
|
31
40
|
* Configuration for EdgeDock component
|
|
32
41
|
*/
|
|
@@ -43,8 +52,8 @@ export interface EdgeDockConfig {
|
|
|
43
52
|
animation?: boolean;
|
|
44
53
|
/** Gap between button and popup in pixels */
|
|
45
54
|
popupGap?: number;
|
|
46
|
-
/** Offset from edge when docked
|
|
47
|
-
edgeOffset?:
|
|
55
|
+
/** Offset from edge when docked - number for all edges or object for individual edges */
|
|
56
|
+
edgeOffset?: EdgeOffset;
|
|
48
57
|
/** z-index for the dock container */
|
|
49
58
|
zIndex?: number;
|
|
50
59
|
/** Callback when dock state changes */
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAElC;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;AAE3D;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;AAElD;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,iCAAiC;IACjC,QAAQ,EAAE,QAAQ,CAAC;IACnB,iDAAiD;IACjD,UAAU,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC5B,oDAAoD;IACpD,UAAU,EAAE,OAAO,CAAC;IACpB,0CAA0C;IAC1C,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,4BAA4B;IAC5B,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,sCAAsC;IACtC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,6EAA6E;IAC7E,YAAY,CAAC,EAAE,QAAQ,EAAE,CAAC;IAC1B,qCAAqC;IACrC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,6BAA6B;IAC7B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useEdgeDock.d.ts","sourceRoot":"","sources":["../src/useEdgeDock.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EAId,iBAAiB,
|
|
1
|
+
{"version":3,"file":"useEdgeDock.d.ts","sourceRoot":"","sources":["../src/useEdgeDock.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EAId,iBAAiB,EAIlB,MAAM,SAAS,CAAC;AAkLjB;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,GAAE,cAAmB,GAAG,iBAAiB,CAgV1E"}
|
package/dist/useEdgeDock.js
CHANGED
|
@@ -12,6 +12,16 @@ function getViewport() {
|
|
|
12
12
|
}
|
|
13
13
|
return { width: window.innerWidth, height: window.innerHeight };
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Get offset value for a specific edge
|
|
17
|
+
*/
|
|
18
|
+
function getOffsetForEdge(edgeOffset, edge) {
|
|
19
|
+
if (!edgeOffset)
|
|
20
|
+
return 0;
|
|
21
|
+
if (typeof edgeOffset === 'number')
|
|
22
|
+
return edgeOffset;
|
|
23
|
+
return edgeOffset[edge] ?? 0;
|
|
24
|
+
}
|
|
15
25
|
/**
|
|
16
26
|
* Calculate which edge is closest to the given position
|
|
17
27
|
*/
|
|
@@ -38,9 +48,10 @@ function getClosestEdge(pos, viewport, allowedEdges) {
|
|
|
38
48
|
/**
|
|
39
49
|
* Snap position to edge based on dock mode
|
|
40
50
|
*/
|
|
41
|
-
function snapToEdge(pos, edge, viewport, buttonDimensions,
|
|
51
|
+
function snapToEdge(pos, edge, viewport, buttonDimensions, edgeOffset) {
|
|
42
52
|
const halfWidth = buttonDimensions.width / 2;
|
|
43
53
|
const halfHeight = buttonDimensions.height / 2;
|
|
54
|
+
const offset = getOffsetForEdge(edgeOffset, edge);
|
|
44
55
|
switch (edge) {
|
|
45
56
|
case 'left':
|
|
46
57
|
return {
|
|
@@ -128,13 +139,13 @@ function calculatePopupPosition(buttonPos, buttonDimensions, popupDimensions, vi
|
|
|
128
139
|
* Main hook for edge dock functionality
|
|
129
140
|
*/
|
|
130
141
|
export function useEdgeDock(config = {}) {
|
|
131
|
-
const { dockMode = 'auto', dockEdge, allowedEdges, position: controlledPosition, animation = true, popupGap = 12, edgeOffset
|
|
142
|
+
const { dockMode = 'auto', dockEdge, allowedEdges, position: controlledPosition, animation = true, popupGap = 12, edgeOffset, zIndex = 9999, onDockChange, isPopupOpen: controlledPopupOpen, onPopupChange, } = config;
|
|
132
143
|
const buttonRef = useRef(null);
|
|
133
144
|
const popupRef = useRef(null);
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
//
|
|
137
|
-
const [position, setPositionInternal] = useState(controlledPosition || { x:
|
|
145
|
+
const isMountedRef = useRef(false);
|
|
146
|
+
// Use a fixed initial position to avoid SSR hydration mismatch
|
|
147
|
+
// This will be updated after mount on the client
|
|
148
|
+
const [position, setPositionInternal] = useState(controlledPosition || { x: 100, y: 100 });
|
|
138
149
|
const [dockedEdge, setDockedEdge] = useState(null);
|
|
139
150
|
const [isDragging, setIsDragging] = useState(false);
|
|
140
151
|
const [isPopupOpenInternal, setIsPopupOpenInternal] = useState(false);
|
|
@@ -158,6 +169,28 @@ export function useEdgeDock(config = {}) {
|
|
|
158
169
|
isDragging,
|
|
159
170
|
isPopupOpen,
|
|
160
171
|
};
|
|
172
|
+
// Initialize position after mount (client-side only) to avoid hydration mismatch
|
|
173
|
+
useEffect(() => {
|
|
174
|
+
if (!isMountedRef.current && !controlledPosition && buttonRef.current) {
|
|
175
|
+
isMountedRef.current = true;
|
|
176
|
+
const viewport = getViewport();
|
|
177
|
+
const buttonRect = buttonRef.current.getBoundingClientRect();
|
|
178
|
+
const buttonDimensions = { width: buttonRect.width, height: buttonRect.height };
|
|
179
|
+
// Set initial position on client after mount
|
|
180
|
+
let initialPos = { x: viewport.width - 60, y: viewport.height - 60 };
|
|
181
|
+
initialPos = constrainToViewport(initialPos, viewport, buttonDimensions);
|
|
182
|
+
if (dockMode === 'auto') {
|
|
183
|
+
const edge = getClosestEdge(initialPos, viewport, allowedEdges);
|
|
184
|
+
initialPos = snapToEdge(initialPos, edge, viewport, buttonDimensions, edgeOffset);
|
|
185
|
+
setDockedEdge(edge);
|
|
186
|
+
}
|
|
187
|
+
else if (dockMode === 'manual' && dockEdge) {
|
|
188
|
+
initialPos = snapToEdge(initialPos, dockEdge, viewport, buttonDimensions, edgeOffset);
|
|
189
|
+
setDockedEdge(dockEdge);
|
|
190
|
+
}
|
|
191
|
+
setPositionInternal(initialPos);
|
|
192
|
+
}
|
|
193
|
+
}, [controlledPosition, dockMode, dockEdge, allowedEdges, edgeOffset]);
|
|
161
194
|
// Update controlled position
|
|
162
195
|
useEffect(() => {
|
|
163
196
|
if (controlledPosition) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-edge-dock",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "A zero-dependency React TypeScript library for customizable draggable edge-docked floating buttons with popup support",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.esm.js",
|