@cntrl-site/sdk-nextjs 1.5.2 → 1.6.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.
|
@@ -27,6 +27,7 @@ const useItemTriggers_1 = require("./useItemTriggers");
|
|
|
27
27
|
const useSizing_1 = require("./useSizing");
|
|
28
28
|
const useItemPointerEvents_1 = require("./useItemPointerEvents");
|
|
29
29
|
const useItemArea_1 = require("./useItemArea");
|
|
30
|
+
const useDraggable_1 = require("./useDraggable");
|
|
30
31
|
const stickyFix = `
|
|
31
32
|
-webkit-transform: translate3d(0, 0, 0);
|
|
32
33
|
transform: translate3d(0, 0, 0);
|
|
@@ -49,6 +50,8 @@ const Item = ({ item, sectionId, articleHeight, isParentVisible = true, isInGrou
|
|
|
49
50
|
const itemScale = (0, useItemScale_1.useItemScale)(item, sectionId);
|
|
50
51
|
const interactionCtrl = (0, useItemInteractionCtrl_1.useItemInteractionCtrl)(item.id);
|
|
51
52
|
const triggers = (0, useItemTriggers_1.useItemTriggers)(interactionCtrl);
|
|
53
|
+
const [position, setPosition] = (0, react_1.useState)({ x: 0, y: 0 });
|
|
54
|
+
const [isDraggingActive, setIsDraggingActive] = (0, react_1.useState)(false);
|
|
52
55
|
const wrapperStateProps = interactionCtrl === null || interactionCtrl === void 0 ? void 0 : interactionCtrl.getState(['top', 'left']);
|
|
53
56
|
const innerStateProps = interactionCtrl === null || interactionCtrl === void 0 ? void 0 : interactionCtrl.getState(['width', 'height', 'scale', 'top', 'left']);
|
|
54
57
|
const { width, height, top, left } = (0, useItemArea_1.useItemArea)(item, sectionId, {
|
|
@@ -68,6 +71,23 @@ const Item = ({ item, sectionId, articleHeight, isParentVisible = true, isInGrou
|
|
|
68
71
|
const sizingAxis = (0, useSizing_1.useSizing)(item);
|
|
69
72
|
const ItemComponent = itemsMap_1.itemsMap[item.type] || noop;
|
|
70
73
|
const sectionTop = rectObserver ? rectObserver.getSectionTop(sectionId) : 0;
|
|
74
|
+
const layoutParams = layout ? item.layoutParams[layout] : undefined;
|
|
75
|
+
const isDraggable = layoutParams && 'isDraggable' in layoutParams ? layoutParams.isDraggable : undefined;
|
|
76
|
+
(0, useDraggable_1.useDraggable)({ draggableRef: itemInnerRef.current, isEnabled: isDraggable !== null && isDraggable !== void 0 ? isDraggable : false }, ({ startX, startY, currentX, currentY, lastX, lastY, drag }) => {
|
|
77
|
+
const item = itemInnerRef.current;
|
|
78
|
+
if (!item)
|
|
79
|
+
return;
|
|
80
|
+
if (drag) {
|
|
81
|
+
setIsDraggingActive(true);
|
|
82
|
+
setPosition({
|
|
83
|
+
x: (currentX - startX) + lastX,
|
|
84
|
+
y: (currentY - startY) + lastY
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
setIsDraggingActive(false);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
71
91
|
const handleItemResize = (height) => {
|
|
72
92
|
if (!layout)
|
|
73
93
|
return;
|
|
@@ -99,7 +119,7 @@ const Item = ({ item, sectionId, articleHeight, isParentVisible = true, isInGrou
|
|
|
99
119
|
opacity: (keyframes.length !== 0 && !layout) ? 0 : 1,
|
|
100
120
|
top: `${stickyTop * 100}vw`,
|
|
101
121
|
height: isRichText && itemHeight !== undefined ? `${itemHeight * 100}vw` : 'unset',
|
|
102
|
-
}, children: (0, jsx_runtime_1.jsx)(RichTextWrapper_1.RichTextWrapper, { isRichText: isRichText, children: (0, jsx_runtime_1.jsx)("div", Object.assign({ className: `item-${item.id}-inner`, ref: itemInnerRef, style: Object.assign(Object.assign(Object.assign({}, ((width !== undefined && height !== undefined)
|
|
122
|
+
}, children: (0, jsx_runtime_1.jsx)(RichTextWrapper_1.RichTextWrapper, { isRichText: isRichText, children: (0, jsx_runtime_1.jsx)("div", Object.assign({ className: `item-${item.id}-inner`, ref: itemInnerRef, style: Object.assign(Object.assign(Object.assign({ top: `${position.y}px`, left: `${position.x}px` }, ((width !== undefined && height !== undefined)
|
|
103
123
|
? {
|
|
104
124
|
width: `${sizingAxis.x === 'manual'
|
|
105
125
|
? isRichText
|
|
@@ -108,7 +128,13 @@ const Item = ({ item, sectionId, articleHeight, isParentVisible = true, isInGrou
|
|
|
108
128
|
: 'max-content'}`,
|
|
109
129
|
height: `${sizingAxis.y === 'manual' ? `${height * 100}vw` : 'unset'}`
|
|
110
130
|
}
|
|
111
|
-
: {})), (scale !== undefined ? { transform: `scale(${scale})`, WebkitTransform: `scale(${scale})` } : {})), { transition: (_l = innerStateProps === null || innerStateProps === void 0 ? void 0 : innerStateProps.transition) !== null && _l !== void 0 ? _l : 'none', cursor:
|
|
131
|
+
: {})), (scale !== undefined ? { transform: `scale(${scale})`, WebkitTransform: `scale(${scale})` } : {})), { transition: (_l = innerStateProps === null || innerStateProps === void 0 ? void 0 : innerStateProps.transition) !== null && _l !== void 0 ? _l : 'none', cursor: isDraggingActive
|
|
132
|
+
? 'grabbing'
|
|
133
|
+
: isDraggable
|
|
134
|
+
? 'grab'
|
|
135
|
+
: hasClickTriggers
|
|
136
|
+
? 'pointer'
|
|
137
|
+
: 'unset', pointerEvents: allowPointerEvents ? 'auto' : 'none', userSelect: isDraggable ? 'none' : 'unset', WebkitUserSelect: isDraggable ? 'none' : 'unset', MozUserSelect: isDraggable ? 'none' : 'unset', msUserSelect: isDraggable ? 'none' : 'unset' }) }, triggers, { children: (0, jsx_runtime_1.jsx)(ItemComponent, { item: item, sectionId: sectionId, onResize: handleItemResize, articleHeight: articleHeight, interactionCtrl: interactionCtrl, onVisibilityChange: handleVisibilityChange }) })) }) }), (0, jsx_runtime_1.jsx)(style_1.default, { id: id, children: `
|
|
112
138
|
${(0, sdk_1.getLayoutStyles)(layouts, layoutValues, ([area, hidden, sticky, sectionHeight, layoutParams]) => {
|
|
113
139
|
const sizingAxis = (0, useSizing_1.parseSizing)(layoutParams.sizing);
|
|
114
140
|
const isScreenBasedBottom = area.positionType === sdk_1.PositionType.ScreenBased && area.anchorSide === sdk_1.AnchorSide.Bottom;
|
|
@@ -129,6 +155,7 @@ const Item = ({ item, sectionId, articleHeight, isParentVisible = true, isInGrou
|
|
|
129
155
|
height: ${sizingAxis.y === 'manual' ? `${area.height * 100}vw` : 'unset'};
|
|
130
156
|
transform-origin: ${ScaleAnchorMap_1.ScaleAnchorMap[scaleAnchor]};
|
|
131
157
|
transform: scale(${area.scale});
|
|
158
|
+
position: relative;
|
|
132
159
|
}
|
|
133
160
|
.item-wrapper-${item.id} {
|
|
134
161
|
position: ${area.positionType === sdk_1.PositionType.ScreenBased ? 'fixed' : 'absolute'};
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useDraggable = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const INITIAL_STATE = {
|
|
6
|
+
drag: false,
|
|
7
|
+
startX: 0,
|
|
8
|
+
startY: 0,
|
|
9
|
+
currentX: 0,
|
|
10
|
+
currentY: 0,
|
|
11
|
+
pivotX: 0,
|
|
12
|
+
pivotY: 0,
|
|
13
|
+
lastX: 0,
|
|
14
|
+
lastY: 0
|
|
15
|
+
};
|
|
16
|
+
function useDraggable(data, dragHandler, preventDragOnChildren = false) {
|
|
17
|
+
const dragStateRef = (0, react_1.useRef)(INITIAL_STATE);
|
|
18
|
+
const prevDragStateRef = (0, react_1.useRef)();
|
|
19
|
+
const dragHandlerRef = (0, react_1.useRef)();
|
|
20
|
+
const untrackMouseMoveRef = (0, react_1.useRef)();
|
|
21
|
+
const animationFrameRef = (0, react_1.useRef)();
|
|
22
|
+
dragHandlerRef.current = dragHandler;
|
|
23
|
+
const setDragState = (0, react_1.useCallback)(action => {
|
|
24
|
+
const state = isStateGetter(action)
|
|
25
|
+
? action(dragStateRef.current)
|
|
26
|
+
: action;
|
|
27
|
+
if (!prevDragStateRef.current) {
|
|
28
|
+
prevDragStateRef.current = dragStateRef.current;
|
|
29
|
+
}
|
|
30
|
+
dragStateRef.current = state;
|
|
31
|
+
if (animationFrameRef.current !== undefined)
|
|
32
|
+
return;
|
|
33
|
+
animationFrameRef.current = window.requestAnimationFrame(() => {
|
|
34
|
+
var _a;
|
|
35
|
+
animationFrameRef.current = undefined;
|
|
36
|
+
(_a = dragHandlerRef.current) === null || _a === void 0 ? void 0 : _a.call(dragHandlerRef, dragStateRef.current, prevDragStateRef.current);
|
|
37
|
+
prevDragStateRef.current = dragStateRef.current;
|
|
38
|
+
});
|
|
39
|
+
}, []);
|
|
40
|
+
const handleMouseMove = (0, react_1.useCallback)(event => {
|
|
41
|
+
event.stopPropagation();
|
|
42
|
+
const el = event.target;
|
|
43
|
+
if (!(el instanceof HTMLElement))
|
|
44
|
+
return;
|
|
45
|
+
setDragState(state => {
|
|
46
|
+
if (!state.drag) {
|
|
47
|
+
const clientRect = el.getBoundingClientRect();
|
|
48
|
+
return {
|
|
49
|
+
drag: true,
|
|
50
|
+
startX: event.clientX,
|
|
51
|
+
startY: event.clientY,
|
|
52
|
+
currentX: event.clientX,
|
|
53
|
+
currentY: event.clientY,
|
|
54
|
+
pivotX: event.clientX - clientRect.x,
|
|
55
|
+
pivotY: event.clientY - clientRect.y,
|
|
56
|
+
lastX: state.lastX,
|
|
57
|
+
lastY: state.lastY
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return Object.assign(Object.assign({}, state), { currentX: state.currentX + event.movementX, currentY: state.currentY + event.movementY });
|
|
61
|
+
});
|
|
62
|
+
}, [setDragState, data.draggableRef, preventDragOnChildren]);
|
|
63
|
+
const handleMouseUp = (0, react_1.useCallback)(event => {
|
|
64
|
+
var _a;
|
|
65
|
+
event.stopPropagation();
|
|
66
|
+
setDragState(state => (Object.assign(Object.assign({}, state), { drag: false, lastX: state.currentX - state.startX + state.lastX, lastY: state.currentY - state.startY + state.lastY })));
|
|
67
|
+
(_a = untrackMouseMoveRef.current) === null || _a === void 0 ? void 0 : _a.call(untrackMouseMoveRef);
|
|
68
|
+
}, [setDragState]);
|
|
69
|
+
const handleScroll = (0, react_1.useCallback)((event) => {
|
|
70
|
+
setDragState(state => state);
|
|
71
|
+
}, [setDragState]);
|
|
72
|
+
const trackMouseMove = (0, react_1.useCallback)(() => {
|
|
73
|
+
window.addEventListener('mousemove', handleMouseMove, { capture: true });
|
|
74
|
+
window.addEventListener('mouseup', handleMouseUp, { capture: true });
|
|
75
|
+
window.addEventListener('scroll', handleScroll, { capture: true, passive: true });
|
|
76
|
+
return () => {
|
|
77
|
+
window.removeEventListener('mousemove', handleMouseMove, { capture: true });
|
|
78
|
+
window.removeEventListener('mouseup', handleMouseUp, { capture: true });
|
|
79
|
+
window.removeEventListener('scroll', handleScroll, { capture: true });
|
|
80
|
+
};
|
|
81
|
+
}, [handleMouseMove, handleMouseUp, handleScroll]);
|
|
82
|
+
const handleMouseDown = (0, react_1.useCallback)(event => {
|
|
83
|
+
var _a;
|
|
84
|
+
event.stopPropagation();
|
|
85
|
+
if (preventDragOnChildren && event.target instanceof Node && data.draggableRef instanceof Node) {
|
|
86
|
+
if (event.target !== data.draggableRef && data.draggableRef.contains(event.target))
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (event.button !== 0)
|
|
90
|
+
return;
|
|
91
|
+
(_a = untrackMouseMoveRef.current) === null || _a === void 0 ? void 0 : _a.call(untrackMouseMoveRef);
|
|
92
|
+
untrackMouseMoveRef.current = trackMouseMove();
|
|
93
|
+
}, [trackMouseMove]);
|
|
94
|
+
const handleTouchMove = (0, react_1.useCallback)(event => {
|
|
95
|
+
event.stopPropagation();
|
|
96
|
+
event.preventDefault();
|
|
97
|
+
const touch = event.touches[0];
|
|
98
|
+
const el = event.target;
|
|
99
|
+
if (!(el instanceof HTMLElement))
|
|
100
|
+
return;
|
|
101
|
+
setDragState(state => {
|
|
102
|
+
if (!state.drag) {
|
|
103
|
+
const clientRect = el.getBoundingClientRect();
|
|
104
|
+
return {
|
|
105
|
+
drag: true,
|
|
106
|
+
startX: touch.clientX,
|
|
107
|
+
startY: touch.clientY,
|
|
108
|
+
currentX: touch.clientX,
|
|
109
|
+
currentY: touch.clientY,
|
|
110
|
+
pivotX: touch.clientX - clientRect.x,
|
|
111
|
+
pivotY: touch.clientY - clientRect.y,
|
|
112
|
+
lastX: state.lastX,
|
|
113
|
+
lastY: state.lastY
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
const movementX = touch.clientX - state.currentX;
|
|
117
|
+
const movementY = touch.clientY - state.currentY;
|
|
118
|
+
return Object.assign(Object.assign({}, state), { currentX: state.currentX + movementX, currentY: state.currentY + movementY });
|
|
119
|
+
});
|
|
120
|
+
}, [setDragState]);
|
|
121
|
+
const handleTouchEnd = (0, react_1.useCallback)(event => {
|
|
122
|
+
var _a;
|
|
123
|
+
event.stopPropagation();
|
|
124
|
+
document.body.style.overflow = '';
|
|
125
|
+
document.documentElement.style.overflow = '';
|
|
126
|
+
setDragState(state => (Object.assign(Object.assign({}, state), { drag: false, lastX: state.currentX - state.startX + state.lastX, lastY: state.currentY - state.startY + state.lastY })));
|
|
127
|
+
(_a = untrackMouseMoveRef.current) === null || _a === void 0 ? void 0 : _a.call(untrackMouseMoveRef);
|
|
128
|
+
}, [setDragState]);
|
|
129
|
+
const trackTouchMove = (0, react_1.useCallback)(() => {
|
|
130
|
+
window.addEventListener('touchmove', handleTouchMove, { capture: true, passive: false });
|
|
131
|
+
window.addEventListener('touchend', handleTouchEnd, { capture: true });
|
|
132
|
+
window.addEventListener('touchcancel', handleTouchEnd, { capture: true });
|
|
133
|
+
return () => {
|
|
134
|
+
window.removeEventListener('touchmove', handleTouchMove, { capture: true });
|
|
135
|
+
window.removeEventListener('touchend', handleTouchEnd, { capture: true });
|
|
136
|
+
window.removeEventListener('touchcancel', handleTouchEnd, { capture: true });
|
|
137
|
+
};
|
|
138
|
+
}, [handleTouchMove, handleTouchEnd]);
|
|
139
|
+
const handleTouchStart = (0, react_1.useCallback)(event => {
|
|
140
|
+
var _a;
|
|
141
|
+
event.stopPropagation();
|
|
142
|
+
if (preventDragOnChildren && event.target instanceof Node && data.draggableRef instanceof Node) {
|
|
143
|
+
if (event.target !== data.draggableRef && data.draggableRef.contains(event.target))
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
document.body.style.overflow = 'hidden';
|
|
147
|
+
document.documentElement.style.overflow = 'hidden';
|
|
148
|
+
const selection = document.getSelection();
|
|
149
|
+
if (selection) {
|
|
150
|
+
selection.removeAllRanges();
|
|
151
|
+
}
|
|
152
|
+
(_a = untrackMouseMoveRef.current) === null || _a === void 0 ? void 0 : _a.call(untrackMouseMoveRef);
|
|
153
|
+
untrackMouseMoveRef.current = trackTouchMove();
|
|
154
|
+
}, [trackTouchMove]);
|
|
155
|
+
(0, react_1.useEffect)(() => {
|
|
156
|
+
if (!data.draggableRef || !data.isEnabled)
|
|
157
|
+
return;
|
|
158
|
+
const mouseHandler = handleMouseDown;
|
|
159
|
+
const touchHandler = handleTouchStart;
|
|
160
|
+
data.draggableRef.addEventListener('mousedown', mouseHandler);
|
|
161
|
+
data.draggableRef.addEventListener('touchstart', touchHandler);
|
|
162
|
+
return () => {
|
|
163
|
+
var _a, _b, _c;
|
|
164
|
+
(_a = data.draggableRef) === null || _a === void 0 ? void 0 : _a.removeEventListener('mousedown', mouseHandler);
|
|
165
|
+
(_b = data.draggableRef) === null || _b === void 0 ? void 0 : _b.removeEventListener('touchstart', touchHandler);
|
|
166
|
+
(_c = untrackMouseMoveRef.current) === null || _c === void 0 ? void 0 : _c.call(untrackMouseMoveRef);
|
|
167
|
+
if (animationFrameRef.current !== undefined) {
|
|
168
|
+
window.cancelAnimationFrame(animationFrameRef.current);
|
|
169
|
+
}
|
|
170
|
+
document.body.style.overflow = '';
|
|
171
|
+
document.documentElement.style.overflow = '';
|
|
172
|
+
};
|
|
173
|
+
}, [data.draggableRef, data.isEnabled, handleMouseDown, handleTouchStart]);
|
|
174
|
+
}
|
|
175
|
+
exports.useDraggable = useDraggable;
|
|
176
|
+
function isStateGetter(action) {
|
|
177
|
+
return typeof action === 'function';
|
|
178
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cntrl-site/sdk-nextjs",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "SDK for Next.js",
|
|
5
5
|
"author": "arsen@momdesign.nyc",
|
|
6
6
|
"license": "MIT",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"@antfu/eslint-config": "^3.8.0",
|
|
32
32
|
"@cntrl-site/color": "^1.0.0",
|
|
33
33
|
"@cntrl-site/effects": "^1.3.2",
|
|
34
|
-
"@cntrl-site/sdk": "^1.19.
|
|
34
|
+
"@cntrl-site/sdk": "^1.19.2",
|
|
35
35
|
"@types/vimeo__player": "^2.18.0",
|
|
36
36
|
"@vimeo/player": "^2.25.0",
|
|
37
37
|
"html-react-parser": "^3.0.1",
|
|
@@ -34,6 +34,7 @@ import { useItemTriggers } from './useItemTriggers';
|
|
|
34
34
|
import { parseSizing, useSizing } from './useSizing';
|
|
35
35
|
import { useItemPointerEvents } from './useItemPointerEvents';
|
|
36
36
|
import { useItemArea } from './useItemArea';
|
|
37
|
+
import { useDraggable } from './useDraggable';
|
|
37
38
|
|
|
38
39
|
export interface ItemProps<I extends ItemAny> {
|
|
39
40
|
item: I;
|
|
@@ -78,6 +79,8 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isP
|
|
|
78
79
|
const itemScale = useItemScale(item, sectionId);
|
|
79
80
|
const interactionCtrl = useItemInteractionCtrl(item.id);
|
|
80
81
|
const triggers = useItemTriggers(interactionCtrl);
|
|
82
|
+
const [position, setPosition] = useState({ x: 0, y: 0 });
|
|
83
|
+
const [isDraggingActive, setIsDraggingActive] = useState(false);
|
|
81
84
|
const wrapperStateProps = interactionCtrl?.getState(['top', 'left']);
|
|
82
85
|
const innerStateProps = interactionCtrl?.getState(['width', 'height', 'scale', 'top', 'left']);
|
|
83
86
|
const { width, height, top, left } = useItemArea(item, sectionId, {
|
|
@@ -97,6 +100,21 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isP
|
|
|
97
100
|
const sizingAxis = useSizing(item);
|
|
98
101
|
const ItemComponent = itemsMap[item.type] || noop;
|
|
99
102
|
const sectionTop = rectObserver ? rectObserver.getSectionTop(sectionId) : 0;
|
|
103
|
+
const layoutParams = layout ? item.layoutParams[layout] : undefined;
|
|
104
|
+
const isDraggable = layoutParams && 'isDraggable' in layoutParams ? layoutParams.isDraggable : undefined;
|
|
105
|
+
useDraggable({ draggableRef: itemInnerRef.current, isEnabled: isDraggable ?? false }, ({ startX, startY, currentX, currentY, lastX, lastY, drag }) => {
|
|
106
|
+
const item = itemInnerRef.current;
|
|
107
|
+
if (!item) return;
|
|
108
|
+
if (drag) {
|
|
109
|
+
setIsDraggingActive(true);
|
|
110
|
+
setPosition({
|
|
111
|
+
x: (currentX - startX) + lastX,
|
|
112
|
+
y: (currentY - startY) + lastY
|
|
113
|
+
});
|
|
114
|
+
} else {
|
|
115
|
+
setIsDraggingActive(false);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
100
118
|
|
|
101
119
|
const handleItemResize = (height: number) => {
|
|
102
120
|
if (!layout) return;
|
|
@@ -151,6 +169,8 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isP
|
|
|
151
169
|
className={`item-${item.id}-inner`}
|
|
152
170
|
ref={itemInnerRef}
|
|
153
171
|
style={{
|
|
172
|
+
top: `${position.y}px`,
|
|
173
|
+
left: `${position.x}px`,
|
|
154
174
|
...((width !== undefined && height !== undefined)
|
|
155
175
|
? {
|
|
156
176
|
width: `${sizingAxis.x === 'manual'
|
|
@@ -163,8 +183,18 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isP
|
|
|
163
183
|
: {}),
|
|
164
184
|
...(scale !== undefined ? { transform: `scale(${scale})`, WebkitTransform: `scale(${scale})` } : {}),
|
|
165
185
|
transition: innerStateProps?.transition ?? 'none',
|
|
166
|
-
cursor:
|
|
167
|
-
|
|
186
|
+
cursor: isDraggingActive
|
|
187
|
+
? 'grabbing'
|
|
188
|
+
: isDraggable
|
|
189
|
+
? 'grab'
|
|
190
|
+
: hasClickTriggers
|
|
191
|
+
? 'pointer'
|
|
192
|
+
: 'unset',
|
|
193
|
+
pointerEvents: allowPointerEvents ? 'auto' : 'none',
|
|
194
|
+
userSelect: isDraggable ? 'none' : 'unset',
|
|
195
|
+
WebkitUserSelect: isDraggable ? 'none' : 'unset',
|
|
196
|
+
MozUserSelect: isDraggable ? 'none' : 'unset',
|
|
197
|
+
msUserSelect: isDraggable ? 'none' : 'unset'
|
|
168
198
|
}}
|
|
169
199
|
{...triggers}
|
|
170
200
|
>
|
|
@@ -200,6 +230,7 @@ export const Item: FC<ItemWrapperProps> = ({ item, sectionId, articleHeight, isP
|
|
|
200
230
|
height: ${sizingAxis.y === 'manual' ? `${area.height * 100}vw` : 'unset'};
|
|
201
231
|
transform-origin: ${ScaleAnchorMap[scaleAnchor]};
|
|
202
232
|
transform: scale(${area.scale});
|
|
233
|
+
position: relative;
|
|
203
234
|
}
|
|
204
235
|
.item-wrapper-${item.id} {
|
|
205
236
|
position: ${area.positionType === PositionType.ScreenBased ? 'fixed' : 'absolute'};
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Dispatch,
|
|
3
|
+
SetStateAction,
|
|
4
|
+
useCallback,
|
|
5
|
+
useEffect,
|
|
6
|
+
useRef
|
|
7
|
+
} from 'react';
|
|
8
|
+
|
|
9
|
+
const INITIAL_STATE: DragState = {
|
|
10
|
+
drag: false,
|
|
11
|
+
startX: 0,
|
|
12
|
+
startY: 0,
|
|
13
|
+
currentX: 0,
|
|
14
|
+
currentY: 0,
|
|
15
|
+
pivotX: 0,
|
|
16
|
+
pivotY: 0,
|
|
17
|
+
lastX: 0,
|
|
18
|
+
lastY: 0
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function useDraggable(
|
|
22
|
+
data: {
|
|
23
|
+
draggableRef: HTMLElement | null | undefined,
|
|
24
|
+
isEnabled: boolean
|
|
25
|
+
},
|
|
26
|
+
dragHandler?: DragHandler,
|
|
27
|
+
preventDragOnChildren: boolean = false,
|
|
28
|
+
) {
|
|
29
|
+
const dragStateRef = useRef<DragState>(INITIAL_STATE);
|
|
30
|
+
const prevDragStateRef = useRef<DragState | undefined>();
|
|
31
|
+
const dragHandlerRef = useRef<DragHandler | undefined>();
|
|
32
|
+
const untrackMouseMoveRef = useRef<(() => void) | undefined>();
|
|
33
|
+
const animationFrameRef = useRef<number | undefined>();
|
|
34
|
+
dragHandlerRef.current = dragHandler;
|
|
35
|
+
|
|
36
|
+
const setDragState = useCallback<Dispatch<SetStateAction<DragState>>>(
|
|
37
|
+
action => {
|
|
38
|
+
const state = isStateGetter<DragState>(action)
|
|
39
|
+
? action(dragStateRef.current)
|
|
40
|
+
: action;
|
|
41
|
+
if (!prevDragStateRef.current) {
|
|
42
|
+
prevDragStateRef.current = dragStateRef.current;
|
|
43
|
+
}
|
|
44
|
+
dragStateRef.current = state;
|
|
45
|
+
if (animationFrameRef.current !== undefined) return;
|
|
46
|
+
animationFrameRef.current = window.requestAnimationFrame(() => {
|
|
47
|
+
animationFrameRef.current = undefined;
|
|
48
|
+
dragHandlerRef.current?.(
|
|
49
|
+
dragStateRef.current,
|
|
50
|
+
prevDragStateRef.current
|
|
51
|
+
);
|
|
52
|
+
prevDragStateRef.current = dragStateRef.current;
|
|
53
|
+
});
|
|
54
|
+
},
|
|
55
|
+
[]
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const handleMouseMove = useCallback<EventHandler<MouseEvent>>(
|
|
59
|
+
event => {
|
|
60
|
+
event.stopPropagation();
|
|
61
|
+
const el = event.target;
|
|
62
|
+
if (!(el instanceof HTMLElement)) return;
|
|
63
|
+
setDragState(state => {
|
|
64
|
+
if (!state.drag) {
|
|
65
|
+
const clientRect = el.getBoundingClientRect();
|
|
66
|
+
return {
|
|
67
|
+
drag: true,
|
|
68
|
+
startX: event.clientX,
|
|
69
|
+
startY: event.clientY,
|
|
70
|
+
currentX: event.clientX,
|
|
71
|
+
currentY: event.clientY,
|
|
72
|
+
pivotX: event.clientX - clientRect.x,
|
|
73
|
+
pivotY: event.clientY - clientRect.y,
|
|
74
|
+
lastX: state.lastX,
|
|
75
|
+
lastY: state.lastY
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
...state,
|
|
80
|
+
currentX: state.currentX + event.movementX,
|
|
81
|
+
currentY: state.currentY + event.movementY
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
},
|
|
85
|
+
[setDragState, data.draggableRef, preventDragOnChildren]
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const handleMouseUp = useCallback<EventHandler<MouseEvent>>(
|
|
89
|
+
event => {
|
|
90
|
+
event.stopPropagation();
|
|
91
|
+
setDragState(state => ({
|
|
92
|
+
...state,
|
|
93
|
+
drag: false,
|
|
94
|
+
lastX: state.currentX - state.startX + state.lastX,
|
|
95
|
+
lastY: state.currentY - state.startY + state.lastY
|
|
96
|
+
}));
|
|
97
|
+
untrackMouseMoveRef.current?.();
|
|
98
|
+
},
|
|
99
|
+
[ setDragState]
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
const handleScroll = useCallback<EventHandler<Event>>((event) => {
|
|
103
|
+
setDragState(state => state);
|
|
104
|
+
}, [setDragState]);
|
|
105
|
+
|
|
106
|
+
const trackMouseMove = useCallback(() => {
|
|
107
|
+
window.addEventListener('mousemove', handleMouseMove, { capture: true });
|
|
108
|
+
window.addEventListener('mouseup', handleMouseUp, { capture: true });
|
|
109
|
+
window.addEventListener('scroll', handleScroll, { capture: true, passive: true });
|
|
110
|
+
return () => {
|
|
111
|
+
window.removeEventListener('mousemove', handleMouseMove, { capture: true });
|
|
112
|
+
window.removeEventListener('mouseup', handleMouseUp, { capture: true });
|
|
113
|
+
window.removeEventListener('scroll', handleScroll, { capture: true });
|
|
114
|
+
};
|
|
115
|
+
}, [handleMouseMove, handleMouseUp, handleScroll]);
|
|
116
|
+
|
|
117
|
+
const handleMouseDown = useCallback<EventHandler<MouseEvent>>(
|
|
118
|
+
event => {
|
|
119
|
+
event.stopPropagation();
|
|
120
|
+
if (preventDragOnChildren && event.target instanceof Node && data.draggableRef instanceof Node) {
|
|
121
|
+
if (event.target !== data.draggableRef && data.draggableRef.contains(event.target)) return;
|
|
122
|
+
}
|
|
123
|
+
if (event.button !== 0) return;
|
|
124
|
+
untrackMouseMoveRef.current?.();
|
|
125
|
+
untrackMouseMoveRef.current = trackMouseMove();
|
|
126
|
+
},
|
|
127
|
+
[trackMouseMove]
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const handleTouchMove = useCallback<EventHandler<TouchEvent>>(
|
|
131
|
+
event => {
|
|
132
|
+
event.stopPropagation();
|
|
133
|
+
event.preventDefault();
|
|
134
|
+
const touch = event.touches[0];
|
|
135
|
+
const el = event.target;
|
|
136
|
+
if (!(el instanceof HTMLElement)) return;
|
|
137
|
+
|
|
138
|
+
setDragState(state => {
|
|
139
|
+
if (!state.drag) {
|
|
140
|
+
const clientRect = el.getBoundingClientRect();
|
|
141
|
+
return {
|
|
142
|
+
drag: true,
|
|
143
|
+
startX: touch.clientX,
|
|
144
|
+
startY: touch.clientY,
|
|
145
|
+
currentX: touch.clientX,
|
|
146
|
+
currentY: touch.clientY,
|
|
147
|
+
pivotX: touch.clientX - clientRect.x,
|
|
148
|
+
pivotY: touch.clientY - clientRect.y,
|
|
149
|
+
lastX: state.lastX,
|
|
150
|
+
lastY: state.lastY
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const movementX = touch.clientX - state.currentX;
|
|
155
|
+
const movementY = touch.clientY - state.currentY;
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
...state,
|
|
159
|
+
currentX: state.currentX + movementX,
|
|
160
|
+
currentY: state.currentY + movementY
|
|
161
|
+
};
|
|
162
|
+
});
|
|
163
|
+
},
|
|
164
|
+
[setDragState]
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
const handleTouchEnd = useCallback<EventHandler<TouchEvent>>(
|
|
168
|
+
event => {
|
|
169
|
+
event.stopPropagation();
|
|
170
|
+
document.body.style.overflow = '';
|
|
171
|
+
document.documentElement.style.overflow = '';
|
|
172
|
+
setDragState(state => ({
|
|
173
|
+
...state,
|
|
174
|
+
drag: false,
|
|
175
|
+
lastX: state.currentX - state.startX + state.lastX,
|
|
176
|
+
lastY: state.currentY - state.startY + state.lastY
|
|
177
|
+
}));
|
|
178
|
+
untrackMouseMoveRef.current?.();
|
|
179
|
+
},
|
|
180
|
+
[setDragState]
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
const trackTouchMove = useCallback(() => {
|
|
184
|
+
window.addEventListener('touchmove', handleTouchMove, { capture: true, passive: false });
|
|
185
|
+
window.addEventListener('touchend', handleTouchEnd, { capture: true });
|
|
186
|
+
window.addEventListener('touchcancel', handleTouchEnd, { capture: true });
|
|
187
|
+
return () => {
|
|
188
|
+
window.removeEventListener('touchmove', handleTouchMove, { capture: true });
|
|
189
|
+
window.removeEventListener('touchend', handleTouchEnd, { capture: true });
|
|
190
|
+
window.removeEventListener('touchcancel', handleTouchEnd, { capture: true });
|
|
191
|
+
};
|
|
192
|
+
}, [handleTouchMove, handleTouchEnd]);
|
|
193
|
+
|
|
194
|
+
const handleTouchStart = useCallback<EventHandler<TouchEvent>>(
|
|
195
|
+
event => {
|
|
196
|
+
event.stopPropagation();
|
|
197
|
+
if (preventDragOnChildren && event.target instanceof Node && data.draggableRef instanceof Node) {
|
|
198
|
+
if (event.target !== data.draggableRef && data.draggableRef.contains(event.target)) return;
|
|
199
|
+
}
|
|
200
|
+
document.body.style.overflow = 'hidden';
|
|
201
|
+
document.documentElement.style.overflow = 'hidden';
|
|
202
|
+
const selection = document.getSelection();
|
|
203
|
+
if (selection) {
|
|
204
|
+
selection.removeAllRanges();
|
|
205
|
+
}
|
|
206
|
+
untrackMouseMoveRef.current?.();
|
|
207
|
+
untrackMouseMoveRef.current = trackTouchMove();
|
|
208
|
+
},
|
|
209
|
+
[trackTouchMove]
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
useEffect(() => {
|
|
213
|
+
if (!data.draggableRef || !data.isEnabled) return;
|
|
214
|
+
const mouseHandler = handleMouseDown;
|
|
215
|
+
const touchHandler = handleTouchStart;
|
|
216
|
+
|
|
217
|
+
data.draggableRef.addEventListener('mousedown', mouseHandler);
|
|
218
|
+
data.draggableRef.addEventListener('touchstart', touchHandler);
|
|
219
|
+
|
|
220
|
+
return () => {
|
|
221
|
+
data.draggableRef?.removeEventListener('mousedown', mouseHandler);
|
|
222
|
+
data.draggableRef?.removeEventListener('touchstart', touchHandler);
|
|
223
|
+
untrackMouseMoveRef.current?.();
|
|
224
|
+
if (animationFrameRef.current !== undefined) {
|
|
225
|
+
window.cancelAnimationFrame(animationFrameRef.current);
|
|
226
|
+
}
|
|
227
|
+
document.body.style.overflow = '';
|
|
228
|
+
document.documentElement.style.overflow = '';
|
|
229
|
+
};
|
|
230
|
+
}, [data.draggableRef, data.isEnabled, handleMouseDown, handleTouchStart]);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
interface DragState {
|
|
234
|
+
drag: boolean;
|
|
235
|
+
startX: number;
|
|
236
|
+
startY: number;
|
|
237
|
+
currentX: number;
|
|
238
|
+
currentY: number;
|
|
239
|
+
pivotX: number;
|
|
240
|
+
pivotY: number;
|
|
241
|
+
lastX: number;
|
|
242
|
+
lastY: number;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
type EventHandler<E extends Event> = (event: E) => void;
|
|
246
|
+
type DragHandler = (state: DragState, prevState?: DragState) => void;
|
|
247
|
+
|
|
248
|
+
function isStateGetter<T>(
|
|
249
|
+
action: SetStateAction<T>
|
|
250
|
+
): action is (prevState: T) => T {
|
|
251
|
+
return typeof action === 'function';
|
|
252
|
+
}
|