Skip to content

Commit bb99273

Browse files
authored
Support scale (#482)
* fix: window boundary * fix: tsc error * fix: parent scale * fix: body boundary * fix: selector boundary * fix: fix errors and add grid stories
1 parent 8dce16b commit bb99273

15 files changed

+549
-182
lines changed

package.json

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -46,35 +46,36 @@
4646
"@types/enzyme": "3.1.13",
4747
"@types/enzyme-adapter-react-16": "1.0.3",
4848
"@types/react": "16.4.1",
49-
"@types/react-dom": "16.0.7",
50-
"@types/sinon": "5.0.1",
49+
"@types/node": "^10.12.19",
50+
"@types/react-dom": "16.0.11",
51+
"@types/sinon": "7.0.5",
5152
"@types/storybook__addon-actions": "3.4.1",
5253
"@types/storybook__react": "4.0.0",
5354
"avaron": "0.2.0",
5455
"cpy-cli": "2.0.0",
55-
"enzyme": "3.5.0",
56-
"enzyme-adapter-react-16": "1.2.0",
57-
"gh-pages": "2.0.0",
56+
"enzyme": "3.8.0",
57+
"enzyme-adapter-react-16": "1.8.0",
58+
"gh-pages": "2.0.1",
5859
"light-ts-loader": "1.1.2",
5960
"npm-run-all": "4.1.5",
60-
"prettier": "1.15.2",
61-
"react": "16.4.2",
62-
"react-dom": "16.4.2",
63-
"react-test-renderer": "16.4.2",
61+
"prettier": "1.16.1",
62+
"react": "16.7.0",
63+
"react-dom": "16.7.0",
64+
"react-test-renderer": "16.7.0",
6465
"rollup": "0.61.1",
6566
"rollup-plugin-babel": "3.0.5",
6667
"rollup-plugin-commonjs": "9.2.0",
67-
"rollup-plugin-node-globals": "1.2.1",
68+
"rollup-plugin-node-globals": "1.4.0",
6869
"rollup-plugin-node-resolve": "3.3.0",
6970
"rollup-plugin-replace": "2.1.0",
7071
"rollup-plugin-typescript2": "0.15.0",
7172
"rollup-watch": "4.3.1",
72-
"sinon": "7.1.1",
73-
"tslint": "5.11.0",
73+
"sinon": "7.2.3",
74+
"tslint": "5.12.1",
7475
"tslint-eslint-rules": "5.4.0",
7576
"tslint-plugin-prettier": "2.0.1",
7677
"tslint-react": "3.6.0",
77-
"typescript": "3.0.1"
78+
"typescript": "3.2.4"
7879
},
7980
"files": [
8081
"lib"
@@ -83,8 +84,8 @@
8384
"fixture": "./fixture.html"
8485
},
8586
"dependencies": {
86-
"re-resizable": "4.9.3",
87-
"react-draggable": "3.0.5",
87+
"re-resizable": "4.11.0",
88+
"react-draggable": "3.1.1",
8889
"tslib": "1.9.3"
8990
}
9091
}

src/index.tsx

Lines changed: 93 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import * as React from "react";
2-
import Draggable from "react-draggable";
2+
import { DraggableEventHandler } from "react-draggable";
33
import Resizable, { ResizableDirection } from "re-resizable";
44

5+
// FIXME: https://github.com/mzabriskie/react-draggable/issues/381
6+
// I can not find `scale` too...
7+
type $TODO = any;
8+
const Draggable = require("react-draggable");
9+
510
export type Grid = [number, number];
611

712
export type Position = {
@@ -17,7 +22,13 @@ export type DraggableData = {
1722
lastY: number;
1823
} & Position;
1924

20-
export type RndDragCallback = (e: Event, data: DraggableData) => void | false;
25+
export type RndDragCallback = DraggableEventHandler;
26+
27+
export type RndDragEvent =
28+
| React.MouseEvent<HTMLElement | SVGElement>
29+
| React.TouchEvent<HTMLElement | SVGElement>
30+
| MouseEvent
31+
| TouchEvent;
2132

2233
export type RndResizeStartCallback = (
2334
e: React.MouseEvent<HTMLDivElement> | React.TouchEvent<HTMLDivElement>,
@@ -133,6 +144,7 @@ export interface Props {
133144
disableDragging?: boolean;
134145
cancel?: string;
135146
enableUserSelectHack?: boolean;
147+
scale?: number;
136148
[key: string]: any;
137149
}
138150

@@ -145,10 +157,23 @@ const resizableStyle = {
145157
left: 0,
146158
};
147159

160+
interface DefaultProps {
161+
maxWidth: number;
162+
maxHeight: number;
163+
onResizeStart: RndResizeStartCallback;
164+
onResize: RndResizeCallback;
165+
onResizeStop: RndResizeCallback;
166+
onDragStart: RndDragCallback;
167+
onDrag: RndDragCallback;
168+
onDragStop: RndDragCallback;
169+
scale: number;
170+
}
171+
148172
export class Rnd extends React.Component<Props, State> {
149-
static defaultProps = {
173+
public static defaultProps: DefaultProps = {
150174
maxWidth: Number.MAX_SAFE_INTEGER,
151175
maxHeight: Number.MAX_SAFE_INTEGER,
176+
scale: 1,
152177
onResizeStart: () => {},
153178
onResize: () => {},
154179
onResizeStop: () => {},
@@ -157,7 +182,7 @@ export class Rnd extends React.Component<Props, State> {
157182
onDragStop: () => {},
158183
};
159184
resizable!: Resizable;
160-
draggable!: Draggable;
185+
draggable!: $TODO; // Draggable;
161186
isResizing = false;
162187

163188
constructor(props: Props) {
@@ -221,27 +246,60 @@ export class Rnd extends React.Component<Props, State> {
221246
return this.resizable && this.resizable.resizable;
222247
}
223248

224-
onDragStart(e: Event, data: DraggableData) {
249+
getOffsetHeight(boundary: HTMLElement) {
250+
const scale = this.props.scale as number;
251+
switch (this.props.bounds) {
252+
case "window":
253+
return window.innerHeight / scale;
254+
case "body":
255+
return document.body.offsetHeight / scale;
256+
default:
257+
return boundary.offsetHeight;
258+
}
259+
}
260+
261+
getOffsetWidth(boundary: HTMLElement) {
262+
const scale = this.props.scale as number;
263+
switch (this.props.bounds) {
264+
case "window":
265+
return window.innerWidth / scale;
266+
case "body":
267+
return document.body.offsetWidth / scale;
268+
default:
269+
return boundary.offsetWidth;
270+
}
271+
}
272+
273+
onDragStart(e: RndDragEvent, data: DraggableData) {
225274
if (this.props.onDragStart) {
226275
this.props.onDragStart(e, data);
227276
}
228277
if (!this.props.bounds) return;
229278
const parent = this.getParent();
279+
const scale = this.props.scale as number;
230280
let boundary;
231281
if (this.props.bounds === "parent") {
232282
boundary = parent;
233283
} else if (this.props.bounds === "body") {
234-
boundary = document.body;
284+
const parentRect = parent.getBoundingClientRect();
285+
const parentLeft = parentRect.left;
286+
const parentTop = parentRect.top;
287+
const bodyRect = document.body.getBoundingClientRect();
288+
const left = -(parentLeft - parent.offsetLeft * scale - bodyRect.left) / scale;
289+
const top = -(parentTop - parent.offsetTop * scale - bodyRect.top) / scale;
290+
const right = (document.body.offsetWidth - this.resizable.size.width * scale) / scale + left;
291+
const bottom = (document.body.offsetHeight - this.resizable.size.height * scale) / scale + top;
292+
return this.setState({ bounds: { top, right, bottom, left } });
235293
} else if (this.props.bounds === "window") {
236294
if (!this.resizable) return;
237-
return this.setState({
238-
bounds: {
239-
top: 0,
240-
right: window.innerWidth - this.resizable.size.width,
241-
bottom: window.innerHeight - this.resizable.size.height,
242-
left: 0,
243-
},
244-
});
295+
const parentRect = parent.getBoundingClientRect();
296+
const parentLeft = parentRect.left;
297+
const parentTop = parentRect.top;
298+
const left = -(parentLeft - parent.offsetLeft * scale) / scale;
299+
const top = -(parentTop - parent.offsetTop * scale) / scale;
300+
const right = (window.innerWidth - this.resizable.size.width * scale) / scale + left;
301+
const bottom = (window.innerHeight - this.resizable.size.height * scale) / scale + top;
302+
return this.setState({ bounds: { top, right, bottom, left } });
245303
} else {
246304
boundary = document.querySelector(this.props.bounds);
247305
}
@@ -254,28 +312,28 @@ export class Rnd extends React.Component<Props, State> {
254312
const parentRect = parent.getBoundingClientRect();
255313
const parentLeft = parentRect.left;
256314
const parentTop = parentRect.top;
257-
const left = boundaryLeft - parentLeft;
315+
const left = (boundaryLeft - parentLeft) / scale;
258316
const top = boundaryTop - parentTop;
259317
if (!this.resizable) return;
260318
const offset = this.getOffsetFromParent();
261319
this.setState({
262320
bounds: {
263321
top: top - offset.top,
264-
right: left + (boundary.offsetWidth - this.resizable.size.width) - offset.left,
322+
right: left + (boundary.offsetWidth - this.resizable.size.width) - offset.left / scale,
265323
bottom: top + (boundary.offsetHeight - this.resizable.size.height) - offset.top,
266-
left: left - offset.left,
324+
left: left - offset.left / scale,
267325
},
268326
});
269327
}
270328

271-
onDrag(e: Event, data: DraggableData) {
329+
onDrag(e: RndDragEvent, data: DraggableData) {
272330
if (this.props.onDrag) {
273331
const offset = this.getOffsetFromParent();
274332
this.props.onDrag(e, { ...data, x: data.x - offset.left, y: data.y - offset.top });
275333
}
276334
}
277335

278-
onDragStop(e: Event, data: DraggableData) {
336+
onDragStop(e: RndDragEvent, data: DraggableData) {
279337
if (this.props.onDragStop) {
280338
const { left, top } = this.getOffsetFromParent();
281339
this.props.onDragStop(e, { ...data, x: data.x + left, y: data.y + top });
@@ -289,6 +347,7 @@ export class Rnd extends React.Component<Props, State> {
289347
) {
290348
e.stopPropagation();
291349
this.isResizing = true;
350+
const scale = this.props.scale as number;
292351
this.setState({
293352
original: this.getDraggablePosition(),
294353
});
@@ -335,30 +394,30 @@ export class Rnd extends React.Component<Props, State> {
335394
const boundaryRect = this.props.bounds === "window" ? { left: 0, top: 0 } : boundary.getBoundingClientRect();
336395
const boundaryLeft = boundaryRect.left;
337396
const boundaryTop = boundaryRect.top;
338-
const offsetWidth = this.props.bounds === "window" ? window.innerWidth : boundary.offsetWidth;
339-
const offsetHeight = this.props.bounds === "window" ? window.innerHeight : boundary.offsetHeight;
397+
const offsetWidth = this.getOffsetWidth(boundary);
398+
const offsetHeight = this.getOffsetHeight(boundary);
340399
const hasLeft = dir.toLowerCase().endsWith("left");
341400
const hasRight = dir.toLowerCase().endsWith("right");
342401
const hasTop = dir.startsWith("top");
343402
const hasBottom = dir.startsWith("bottom");
344403
if (hasLeft && this.resizable) {
345-
const max = selfLeft - boundaryLeft + this.resizable.size.width;
404+
const max = (selfLeft - boundaryLeft) / scale + this.resizable.size.width;
346405
this.setState({ maxWidth: max > Number(maxWidth) ? maxWidth : max });
347406
}
348407
// INFO: To set bounds in `lock aspect ratio with bounds` case. See also that story.
349408
if (hasRight || (this.props.lockAspectRatio && !hasLeft)) {
350-
const max = offsetWidth + (boundaryLeft - selfLeft);
409+
const max = offsetWidth + (boundaryLeft - selfLeft) / scale;
351410
this.setState({ maxWidth: max > Number(maxWidth) ? maxWidth : max });
352411
}
353412
if (hasTop && this.resizable) {
354-
const max = selfTop - boundaryTop + this.resizable.size.height;
413+
const max = (selfTop - boundaryTop) / scale + this.resizable.size.height;
355414
this.setState({
356415
maxHeight: max > Number(maxHeight) ? maxHeight : max,
357416
});
358417
}
359418
// INFO: To set bounds in `lock aspect ratio with bounds` case. See also that story.
360419
if (hasBottom || (this.props.lockAspectRatio && !hasTop)) {
361-
const max = offsetHeight + (boundaryTop - selfTop);
420+
const max = offsetHeight + (boundaryTop - selfTop) / scale;
362421
this.setState({
363422
maxHeight: max > Number(maxHeight) ? maxHeight : max,
364423
});
@@ -439,6 +498,7 @@ export class Rnd extends React.Component<Props, State> {
439498
}
440499

441500
getOffsetFromParent(): { top: number; left: number } {
501+
const scale = this.props.scale as number;
442502
const parent = this.getParent();
443503
if (!parent) {
444504
return {
@@ -452,8 +512,8 @@ export class Rnd extends React.Component<Props, State> {
452512
const selfRect = this.getSelfElement().getBoundingClientRect();
453513
const position = this.getDraggablePosition();
454514
return {
455-
left: selfRect.left - parentLeft - position.x,
456-
top: selfRect.top - parentTop - position.y,
515+
left: selfRect.left - parentLeft - position.x * scale,
516+
top: selfRect.top - parentTop - position.y * scale,
457517
};
458518
}
459519

@@ -482,6 +542,7 @@ export class Rnd extends React.Component<Props, State> {
482542
resizeGrid,
483543
resizeHandleWrapperClass,
484544
resizeHandleWrapperStyle,
545+
scale,
485546
...resizableProps
486547
} = this.props;
487548
const defaultValue = this.props.default ? { ...this.props.default } : undefined;
@@ -504,10 +565,9 @@ export class Rnd extends React.Component<Props, State> {
504565
}
505566
return (
506567
<Draggable
507-
ref={c => {
508-
if (c) {
509-
this.draggable = c;
510-
}
568+
ref={(c: $TODO) => {
569+
if (!c) return;
570+
this.draggable = c;
511571
}}
512572
handle={dragHandleClassName ? `.${dragHandleClassName}` : undefined}
513573
defaultPosition={defaultValue}
@@ -522,6 +582,7 @@ export class Rnd extends React.Component<Props, State> {
522582
position={draggablePosition}
523583
enableUserSelectHack={enableUserSelectHack}
524584
cancel={cancel}
585+
scale={scale}
525586
>
526587
<Resizable
527588
{...resizableProps}
@@ -549,6 +610,7 @@ export class Rnd extends React.Component<Props, State> {
549610
lockAspectRatioExtraHeight={this.props.lockAspectRatioExtraHeight}
550611
handleStyles={resizeHandleStyles}
551612
handleClasses={resizeHandleClasses}
613+
scale={this.props.scale}
552614
>
553615
{children}
554616
</Resizable>

stories/bounds/parent-controlled.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export default class Example extends React.Component<{}, State> {
3434
x: this.state.x,
3535
y: this.state.y,
3636
}}
37-
onDragStop={(e, d) => {
37+
onDragStop={(e: any, d: any) => {
3838
this.setState({ x: d.x, y: d.y });
3939
}}
4040
onResize={(e, direction, ref, delta, position) => {

0 commit comments

Comments
 (0)