Skip to content

Commit cb73122

Browse files
authored
fix: Canvas is not expandable or scrollable beyond current view size (#3140)
1 parent bd05eda commit cb73122

File tree

4 files changed

+132
-12
lines changed

4 files changed

+132
-12
lines changed

src/dashboard/Data/CustomDashboard/CanvasElement.react.js

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,26 @@ const CanvasElement = ({
1515
onSelect,
1616
onPositionChange,
1717
onSizeChange,
18+
onDrag,
19+
onResize,
1820
children,
1921
}) => {
22+
const handleDrag = (e, d) => {
23+
if (onDrag) {
24+
onDrag(element.id, d.x, d.y, element.width, element.height);
25+
}
26+
};
27+
2028
const handleDragStop = (e, d) => {
2129
onPositionChange(element.id, d.x, d.y);
2230
};
2331

32+
const handleResize = (e, direction, ref, delta, position) => {
33+
if (onResize) {
34+
onResize(element.id, ref.offsetWidth, ref.offsetHeight, position.x, position.y);
35+
}
36+
};
37+
2438
const handleResizeStop = (e, direction, ref, delta, position) => {
2539
onSizeChange(
2640
element.id,
@@ -40,13 +54,14 @@ const CanvasElement = ({
4054
<Rnd
4155
position={{ x: element.x, y: element.y }}
4256
size={{ width: element.width, height: element.height }}
43-
dragGrid={[16, 16]}
44-
resizeGrid={[16, 16]}
57+
dragGrid={[50, 50]}
58+
resizeGrid={[50, 50]}
4559
minWidth={100}
4660
minHeight={50}
47-
bounds="parent"
4861
dragHandleClassName={styles.dragHandle}
62+
onDrag={handleDrag}
4963
onDragStop={handleDragStop}
64+
onResize={handleResize}
5065
onResizeStop={handleResizeStop}
5166
className={`${styles.canvasElement} ${isSelected ? styles.selected : ''}`}
5267
enableResizing={{

src/dashboard/Data/CustomDashboard/CanvasElement.scss

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,4 @@
4141

4242
.resizeHandle {
4343
background: transparent !important;
44-
45-
&:hover {
46-
background: rgba(22, 156, 238, 0.3) !important;
47-
}
4844
}

src/dashboard/Data/CustomDashboard/CustomDashboard.react.js

Lines changed: 113 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,16 @@ class CustomDashboard extends DashboardView {
7676
currentCanvasFavorite: false,
7777
hasUnsavedChanges: false,
7878
isFullscreen: false,
79+
// Track dragging element position for canvas auto-extend
80+
dragPosition: null,
7981
};
8082
this.autoReloadTimer = null;
8183
this.autoReloadProgressTimer = null;
8284
this.autoReloadStartTime = null;
8385
this.canvasPreferencesManager = null;
8486
this._isMounted = false;
8587
this._elementSeq = {};
88+
this.canvasRef = React.createRef();
8689
}
8790

8891
componentDidMount() {
@@ -622,22 +625,103 @@ class CustomDashboard extends DashboardView {
622625
}
623626
};
624627

628+
// Snap value to grid
629+
snapToGrid(value, gridSize = 50) {
630+
return Math.round(value / gridSize) * gridSize;
631+
}
632+
625633
handlePositionChange = (id, x, y) => {
634+
// Snap to grid and ensure position is not negative
635+
const safeX = Math.max(0, this.snapToGrid(x));
636+
const safeY = Math.max(0, this.snapToGrid(y));
626637
this.setState(state => ({
627638
elements: state.elements.map(el =>
628-
el.id === id ? { ...el, x, y } : el
639+
el.id === id ? { ...el, x: safeX, y: safeY } : el
629640
),
630641
}), this.markUnsavedChanges);
631642
};
632643

633644
handleSizeChange = (id, width, height, x, y) => {
645+
// Snap to grid and ensure position is not negative when resizing from left/top edges
646+
const safeX = Math.max(0, this.snapToGrid(x));
647+
const safeY = Math.max(0, this.snapToGrid(y));
648+
const snappedWidth = this.snapToGrid(width);
649+
const snappedHeight = this.snapToGrid(height);
634650
this.setState(state => ({
635651
elements: state.elements.map(el =>
636-
el.id === id ? { ...el, width, height, x, y } : el
652+
el.id === id ? { ...el, width: snappedWidth, height: snappedHeight, x: safeX, y: safeY } : el
637653
),
638654
}), this.markUnsavedChanges);
639655
};
640656

657+
handleDrag = (id, x, y, width, height) => {
658+
// Update drag position to trigger canvas auto-extend during drag
659+
this.setState({
660+
dragPosition: { id, x, y, width, height },
661+
});
662+
};
663+
664+
handleDragEnd = () => {
665+
// Clear drag position when drag ends
666+
this.setState({ dragPosition: null });
667+
};
668+
669+
handleResize = (id, width, height, x, y) => {
670+
// Update drag position to trigger canvas auto-extend during resize
671+
this.setState({
672+
dragPosition: { id, x, y, width, height },
673+
});
674+
};
675+
676+
handleResizeEnd = () => {
677+
// Clear drag position when resize ends
678+
this.setState({ dragPosition: null });
679+
};
680+
681+
// Calculate the required canvas size based on all elements and current drag position
682+
// Returns dimensions only when content extends beyond the default CSS size
683+
getCanvasSize() {
684+
const { elements, dragPosition } = this.state;
685+
const padding = 50; // Extra padding to allow easy placement at edges
686+
687+
let maxRight = 0;
688+
let maxBottom = 0;
689+
690+
// Calculate bounds from all elements
691+
elements.forEach(el => {
692+
const right = el.x + el.width;
693+
const bottom = el.y + el.height;
694+
if (right > maxRight) {
695+
maxRight = right;
696+
}
697+
if (bottom > maxBottom) {
698+
maxBottom = bottom;
699+
}
700+
});
701+
702+
// Include current drag position if dragging
703+
if (dragPosition) {
704+
const dragRight = dragPosition.x + dragPosition.width;
705+
const dragBottom = dragPosition.y + dragPosition.height;
706+
if (dragRight > maxRight) {
707+
maxRight = dragRight;
708+
}
709+
if (dragBottom > maxBottom) {
710+
maxBottom = dragBottom;
711+
}
712+
}
713+
714+
// Add padding to content bounds
715+
const contentWidth = maxRight + padding;
716+
const contentHeight = maxBottom + padding;
717+
718+
// Return the content-based dimensions (CSS handles minimum via width:100% and min-height)
719+
return {
720+
minWidth: contentWidth,
721+
minHeight: contentHeight,
722+
};
723+
}
724+
641725
handleDeleteElement = (id) => {
642726
this.setState(state => ({
643727
elements: state.elements.filter(el => el.id !== id),
@@ -1065,13 +1149,30 @@ class CustomDashboard extends DashboardView {
10651149
wrapperClasses.push(styles.fullscreen);
10661150
}
10671151

1152+
// Calculate dynamic canvas size based on element positions
1153+
const canvasSize = this.getCanvasSize();
1154+
10681155
return (
10691156
<div className={wrapperClasses.join(' ')}>
10701157
<div
1158+
ref={this.canvasRef}
10711159
className={styles.canvas}
10721160
onClick={this.handleDeselectElement}
10731161
tabIndex={0}
10741162
>
1163+
{/* Invisible sizing element that expands the canvas when elements extend beyond */}
1164+
{(canvasSize.minWidth > 0 || canvasSize.minHeight > 0) && (
1165+
<div
1166+
style={{
1167+
position: 'absolute',
1168+
top: 0,
1169+
left: 0,
1170+
width: canvasSize.minWidth,
1171+
height: canvasSize.minHeight,
1172+
pointerEvents: 'none',
1173+
}}
1174+
/>
1175+
)}
10751176
{elements.length === 0 && !isFullscreen ? (
10761177
<EmptyState
10771178
icon="canvas-outline"
@@ -1087,8 +1188,16 @@ class CustomDashboard extends DashboardView {
10871188
element={element}
10881189
isSelected={element.id === selectedElement}
10891190
onSelect={this.handleSelectElement}
1090-
onPositionChange={this.handlePositionChange}
1091-
onSizeChange={this.handleSizeChange}
1191+
onPositionChange={(id, x, y) => {
1192+
this.handleDragEnd();
1193+
this.handlePositionChange(id, x, y);
1194+
}}
1195+
onSizeChange={(id, width, height, x, y) => {
1196+
this.handleResizeEnd();
1197+
this.handleSizeChange(id, width, height, x, y);
1198+
}}
1199+
onDrag={this.handleDrag}
1200+
onResize={this.handleResize}
10921201
>
10931202
{this.renderElementContent(element)}
10941203
</CanvasElement>

src/dashboard/Data/CustomDashboard/CustomDashboard.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
position: relative;
1212
width: 100%;
1313
min-height: calc(100vh - 96px);
14-
background-color: #f4f5f7;
14+
background-color: #ffffff;
1515
overflow: auto;
1616
outline: none;
1717
}

0 commit comments

Comments
 (0)