Skip to content

Commit 031e62b

Browse files
David SeifriedludovicMarkHarper
authored
Multi directional resizing (react-grid-layout#1917)
* Initial version by Ludovic * Constrain w/h during certain resize actions * Constrain left/right/top bounds properly * Update built files * No dist/ files aren't commiting per PR directions * Make sure se handle constrains width too * Clean up some function definitions and types * swap element above * add compaction type toggle to resizable demo * fix west dragging for horizontal and no compaction * remove logs * update snapshot width from max width change * PR feedback updates * Tests for PR feedback * dist files back to master version * Fix lint issues, address PR feedback --------- Co-authored-by: Ludovic Bouges <ludovic@wearecloud.com> Co-authored-by: Mark Harper <mark@privy.com>
1 parent f5e121e commit 031e62b

File tree

9 files changed

+718
-135
lines changed

9 files changed

+718
-135
lines changed

css/styles.css

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,17 @@
44
}
55
.react-grid-item {
66
transition: all 200ms ease;
7-
transition-property: left, top;
7+
transition-property: left, top, width, height;
88
}
99
.react-grid-item img {
1010
pointer-events: none;
11-
user-select: none;
11+
user-select: none;
1212
}
1313
.react-grid-item.cssTransforms {
14-
transition-property: transform;
14+
transition-property: transform, width, height;
1515
}
1616
.react-grid-item.resizing {
17+
transition: none;
1718
z-index: 1;
1819
will-change: width, height;
1920
}
@@ -40,6 +41,10 @@
4041
user-select: none;
4142
}
4243

44+
.react-grid-item.react-grid-placeholder.placeholder-resizing {
45+
transition: none;
46+
}
47+
4348
.react-grid-item > .react-resizable-handle {
4449
position: absolute;
4550
width: 20px;

lib/GridItem.jsx

Lines changed: 140 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,19 @@ type GridItemCallback<Data: GridDragEvent | GridResizeEvent> = (
4141
h: number,
4242
Data
4343
) => void;
44+
type GridItemResizeCallback = (
45+
e: Event,
46+
data: ResizeCallbackData,
47+
position: Position
48+
) => void;
49+
type ResizeCallbackData = {
50+
node: HTMLElement,
51+
size: Position,
52+
handle: string
53+
};
4454

4555
type State = {
46-
resizing: ?{ width: number, height: number },
56+
resizing: ?{ top: number, left: number, width: number, height: number },
4757
dragging: ?{ top: number, left: number },
4858
className: string
4959
};
@@ -356,6 +366,15 @@ export default class GridItem extends React.Component<Props, State> {
356366
);
357367
}
358368

369+
/**
370+
* Utility function to setup callback handler definitions for
371+
* similarily structured resize events.
372+
*/
373+
curryResizeHandler(position: Position, handler: function): function {
374+
return (e: Event, data: ResizeCallbackData): function =>
375+
handler(e, data, position);
376+
}
377+
359378
/**
360379
* Mix a Resizable instance into a child.
361380
* @param {Element} child Child element.
@@ -381,13 +400,7 @@ export default class GridItem extends React.Component<Props, State> {
381400
const positionParams = this.getPositionParams();
382401

383402
// This is the max possible width - doesn't go to infinity because of the width of the window
384-
const maxWidth = calcGridItemPosition(
385-
positionParams,
386-
0,
387-
0,
388-
cols - x,
389-
0
390-
).width;
403+
const maxWidth = calcGridItemPosition(positionParams, 0, 0, cols, 0).width;
391404

392405
// Calculate min/max constraints using our min & maxes
393406
const mins = calcGridItemPosition(positionParams, 0, 0, minW, minH);
@@ -408,9 +421,9 @@ export default class GridItem extends React.Component<Props, State> {
408421
height={position.height}
409422
minConstraints={minConstraints}
410423
maxConstraints={maxConstraints}
411-
onResizeStop={this.onResizeStop}
412-
onResizeStart={this.onResizeStart}
413-
onResize={this.onResize}
424+
onResizeStop={this.curryResizeHandler(position, this.onResizeStop)}
425+
onResizeStart={this.curryResizeHandler(position, this.onResizeStart)}
426+
onResize={this.curryResizeHandler(position, this.onResize)}
414427
transformScale={transformScale}
415428
resizeHandles={resizeHandles}
416429
handle={resizeHandle}
@@ -541,53 +554,31 @@ export default class GridItem extends React.Component<Props, State> {
541554
* @param {Event} e event data
542555
* @param {Object} callbackData an object with node and size information
543556
*/
544-
onResizeStop: (Event, { node: HTMLElement, size: Position }) => void = (
545-
e,
546-
callbackData
547-
) => {
548-
this.onResizeHandler(e, callbackData, "onResizeStop");
549-
};
557+
onResizeStop: GridItemResizeCallback = (e, callbackData, position) =>
558+
this.onResizeHandler(e, callbackData, position, "onResizeStop");
550559

551-
/**
552-
* onResizeStart event handler
553-
* @param {Event} e event data
554-
* @param {Object} callbackData an object with node and size information
555-
*/
556-
onResizeStart: (Event, { node: HTMLElement, size: Position }) => void = (
557-
e,
558-
callbackData
559-
) => {
560-
this.onResizeHandler(e, callbackData, "onResizeStart");
561-
};
560+
// onResizeStart event handler
561+
onResizeStart: GridItemResizeCallback = (e, callbackData, position) =>
562+
this.onResizeHandler(e, callbackData, position, "onResizeStart");
562563

563-
/**
564-
* onResize event handler
565-
* @param {Event} e event data
566-
* @param {Object} callbackData an object with node and size information
567-
*/
568-
onResize: (Event, { node: HTMLElement, size: Position }) => void = (
569-
e,
570-
callbackData
571-
) => {
572-
this.onResizeHandler(e, callbackData, "onResize");
573-
};
564+
// onResize event handler
565+
onResize: GridItemResizeCallback = (e, callbackData, position) =>
566+
this.onResizeHandler(e, callbackData, position, "onResize");
574567

575568
/**
576569
* Wrapper around drag events to provide more useful data.
577570
* All drag events call the function with the given handler name,
578571
* with the signature (index, x, y).
579-
*
580-
* @param {String} handlerName Handler name to wrap.
581-
* @return {Function} Handler function.
582572
*/
583573
onResizeHandler(
584574
e: Event,
585-
{ node, size }: { node: HTMLElement, size: Position },
575+
{ node, size, handle }: ResizeCallbackData,
576+
position: Position,
586577
handlerName: string
587578
): void {
588579
const handler = this.props[handlerName];
589580
if (!handler) return;
590-
const { cols, x, y, i, maxH, minH } = this.props;
581+
const { x, y, i, maxH, minH } = this.props;
591582
let { minW, maxW } = this.props;
592583

593584
// Get new XY
@@ -596,22 +587,121 @@ export default class GridItem extends React.Component<Props, State> {
596587
size.width,
597588
size.height,
598589
x,
599-
y
590+
y,
591+
handle
600592
);
601593

602594
// minW should be at least 1 (TODO propTypes validation?)
603595
minW = Math.max(minW, 1);
604596

605-
// maxW should be at most (cols - x)
606-
maxW = Math.min(maxW, cols - x);
607-
608597
// Min/max capping
609598
w = clamp(w, minW, maxW);
610599
h = clamp(h, minH, maxH);
611600

612-
this.setState({ resizing: handlerName === "onResizeStop" ? null : size });
601+
let updatedSize = size;
602+
if (node) {
603+
const currentLeft = position.left;
604+
const currentTop = position.top;
605+
const currentWidth = position.width;
606+
const currentHeight = position.height;
607+
608+
const resizeNorth = ({ left, height, width }) => {
609+
const top = currentTop - (height - currentHeight);
610+
611+
return {
612+
top,
613+
left,
614+
width,
615+
height: this.constrainHeight(top, currentHeight, height),
616+
top: this.constrainTop(top)
617+
};
618+
};
619+
620+
const resizeEast = ({ top, left, height, width }) => ({
621+
top,
622+
height,
623+
width: this.constrainWidth(currentLeft, currentWidth, width),
624+
left: this.constrainLeft(left)
625+
});
626+
627+
const resizeWest = ({ top, height, width }) => {
628+
const left = currentLeft - (width - currentWidth);
629+
630+
return {
631+
height,
632+
width:
633+
left <= 0
634+
? currentWidth
635+
: this.constrainWidth(currentLeft, currentWidth, width),
636+
top: this.constrainTop(top),
637+
left: this.constrainLeft(left)
638+
};
639+
};
640+
641+
const resizeSouth = ({ top, left, height, width }) => ({
642+
width,
643+
left,
644+
height: this.constrainHeight(top, currentHeight, height),
645+
top: this.constrainTop(top)
646+
});
647+
648+
const resizeNorthEast = (...args) => resizeNorth(resizeEast(...args));
649+
const resizeNorthWest = (...args) => resizeNorth(resizeWest(...args));
650+
const resizeSouthEast = (...args) => resizeSouth(resizeEast(...args));
651+
const resizeSouthWest = (...args) => resizeSouth(resizeWest(...args));
652+
653+
const ordinalResizeHandlerMap = {
654+
n: resizeNorth,
655+
ne: resizeNorthEast,
656+
e: resizeEast,
657+
se: resizeSouthEast,
658+
s: resizeSouth,
659+
sw: resizeSouthWest,
660+
w: resizeWest,
661+
nw: resizeNorthWest
662+
};
663+
664+
const resizeHandler = ordinalResizeHandlerMap[handle];
665+
updatedSize = resizeHandler
666+
? resizeHandler({
667+
top: currentTop,
668+
left: currentLeft,
669+
height: currentHeight,
670+
width: currentWidth,
671+
...size
672+
})
673+
: size;
674+
this.setState({
675+
resizing: handlerName === "onResizeStop" ? null : updatedSize
676+
});
677+
}
678+
679+
handler.call(this, i, w, h, { e, node, size: updatedSize, handle });
680+
}
681+
682+
/**
683+
* Helper functions to constrain dimensions of a GridItem
684+
*/
685+
constrainWidth(left: number, currentWidth: number, newWidth: number): number {
686+
return left + newWidth > this.props.containerWidth
687+
? currentWidth
688+
: newWidth;
689+
}
690+
691+
constrainHeight(
692+
top: number,
693+
currentHeight: number,
694+
newHeight: number
695+
): number {
696+
return top <= 0 ? currentHeight : newHeight;
697+
}
698+
699+
constrainLeft(left: number): number {
700+
return Math.max(0, left);
701+
}
613702

614-
handler.call(this, i, w, h, { e, node, size });
703+
constrainTop(top: number): number {
704+
return Math.max(0, top);
615705
}
616706

617707
render(): ReactNode {

0 commit comments

Comments
 (0)