Skip to content

Commit 581794d

Browse files
author
weinStag
committed
Add better drag and resize rules and techs
1 parent 6281152 commit 581794d

File tree

6 files changed

+355
-30
lines changed

6 files changed

+355
-30
lines changed

src/components/ChartVisualization/ChartVisualization.react.js

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -551,11 +551,6 @@ const ChartVisualization = ({
551551

552552
return (
553553
<div className={styles.chartVisualization}>
554-
<div className={styles.chartHeader}>
555-
<h3 className={styles.chartTitle}>
556-
📊 Data Visualization ({selectedData.length} values selected)
557-
</h3>
558-
</div>
559554
<div className={styles.chartControls}>
560555
{chartData.type === 'numberSeries' && (
561556
<div className={styles.chartTypeSelector}>

src/components/ChartVisualization/ChartVisualization.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
height: 100%;
1414
background: white;
1515
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
16-
border-radius: 8px;
16+
border-radius: 0px 0px 8px 8px;
1717
overflow: hidden;
1818
min-width: 600px;
1919
min-height: 500px;
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/*
2+
* Copyright (c) 2016-present, Parse, LLC
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the license found in the LICENSE file in
6+
* the root directory of this source tree.
7+
*/
8+
import React, { useState, useRef, useEffect } from 'react';
9+
import { ResizableBox } from 'react-resizable';
10+
import Icon from 'components/Icon/Icon.react';
11+
import styles from './DraggableResizablePanel.scss';
12+
13+
const DraggableResizablePanel = ({
14+
children,
15+
width = 400,
16+
height = 400,
17+
minWidth = 300,
18+
maxWidth = 800,
19+
minHeight = 300,
20+
maxHeight = 600,
21+
title = "Panel",
22+
onClose,
23+
initialPosition = { x: 100, y: 100 }
24+
}) => {
25+
const [position, setPosition] = useState(initialPosition);
26+
const [size, setSize] = useState({ width, height });
27+
const [isDragging, setIsDragging] = useState(false);
28+
const [dragStart, setDragStart] = useState({ x: 0, y: 0 });
29+
const panelRef = useRef(null);
30+
const titleBarRef = useRef(null); const handleMouseDown = (e) => {
31+
// Verificar se o clique foi na barra de título ou seus filhos
32+
if (titleBarRef.current && (
33+
e.target === titleBarRef.current ||
34+
titleBarRef.current.contains(e.target)
35+
)) {
36+
37+
// Verificar se não clicou no botão fechar
38+
if (!e.target.closest('[data-close-button="true"]')) {
39+
setIsDragging(true);
40+
setDragStart({
41+
x: e.clientX - position.x,
42+
y: e.clientY - position.y
43+
});
44+
e.preventDefault();
45+
e.stopPropagation();
46+
}
47+
}
48+
}; const handleMouseMove = (e) => {
49+
if (isDragging) {
50+
const newX = e.clientX - dragStart.x;
51+
const newY = e.clientY - dragStart.y;
52+
53+
// Limitar a posição para não sair da tela
54+
const maxX = window.innerWidth - size.width;
55+
const maxY = window.innerHeight - size.height;
56+
57+
const newPosition = {
58+
x: Math.max(0, Math.min(newX, maxX)),
59+
y: Math.max(0, Math.min(newY, maxY))
60+
};
61+
62+
setPosition(newPosition);
63+
}
64+
};
65+
66+
const handleMouseUp = () => {
67+
setIsDragging(false);
68+
};
69+
70+
const handleResize = (event, { size: newSize }) => {
71+
setSize(newSize);
72+
};
73+
74+
useEffect(() => {
75+
if (isDragging) {
76+
document.addEventListener('mousemove', handleMouseMove);
77+
document.addEventListener('mouseup', handleMouseUp);
78+
document.body.style.cursor = 'move';
79+
document.body.style.userSelect = 'none';
80+
} else {
81+
document.removeEventListener('mousemove', handleMouseMove);
82+
document.removeEventListener('mouseup', handleMouseUp);
83+
document.body.style.cursor = '';
84+
document.body.style.userSelect = '';
85+
}
86+
87+
return () => {
88+
document.removeEventListener('mousemove', handleMouseMove);
89+
document.removeEventListener('mouseup', handleMouseUp);
90+
document.body.style.cursor = '';
91+
document.body.style.userSelect = '';
92+
};
93+
}, [isDragging, dragStart, position, size]);
94+
95+
return (
96+
<div
97+
ref={panelRef}
98+
className={styles.draggablePanel}
99+
style={{
100+
left: position.x,
101+
top: position.y,
102+
zIndex: 1000
103+
}}
104+
>
105+
<ResizableBox
106+
width={size.width}
107+
height={size.height}
108+
minConstraints={[minWidth, minHeight]}
109+
maxConstraints={[maxWidth, maxHeight]}
110+
onResize={handleResize}
111+
resizeHandles={['se', 'e', 's', 'w', 'n', 'ne', 'nw', 'sw']}
112+
className={styles.resizableContainer}
113+
>
114+
<div className={styles.panelContainer}>
115+
{/* Barra de título */}
116+
<div
117+
ref={titleBarRef}
118+
className={styles.titleBar}
119+
onMouseDown={handleMouseDown}
120+
>
121+
<div className={styles.title}>
122+
<Icon width={16} height={16} fill="currentColor" name="analytics-outline" />
123+
<span>{title}</span>
124+
</div>
125+
<div className={styles.controls}>
126+
<button
127+
className={styles.closeButton}
128+
onClick={onClose}
129+
title="Close Panel"
130+
data-close-button="true"
131+
>
132+
<Icon width={14} height={14} fill="currentColor" name="x-outline" />
133+
</button>
134+
</div>
135+
</div>
136+
137+
{/* Conteúdo do painel */}
138+
<div className={styles.content}>
139+
{children}
140+
</div>
141+
</div>
142+
</ResizableBox>
143+
</div>
144+
);
145+
};
146+
147+
export default DraggableResizablePanel;
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
.draggablePanel {
2+
position: fixed;
3+
z-index: 1000;
4+
border-radius: 8px;
5+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
6+
background: white;
7+
border: 1px solid #e1e5e9;
8+
}
9+
10+
.resizableContainer {
11+
border-radius: 8px;
12+
overflow: visible; // Mudança: permitir que os handles sejam visíveis fora da borda
13+
14+
:global(.react-resizable-handle) {
15+
position: absolute;
16+
z-index: 1001;
17+
opacity: 0;
18+
transition: opacity 0.2s;
19+
}
20+
21+
// Mostrar handles quando hover na janela
22+
&:hover :global(.react-resizable-handle) {
23+
opacity: 1;
24+
}
25+
26+
:global(.react-resizable-handle-se) {
27+
bottom: -5px;
28+
right: -5px;
29+
width: 16px;
30+
height: 16px;
31+
cursor: se-resize;
32+
border-radius: 50%;
33+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.6);
34+
//background: #1f8ce6;
35+
//border: 2px solid white;
36+
}
37+
38+
:global(.react-resizable-handle-e) {
39+
right: -4px;
40+
top: 20%;
41+
bottom: 20%;
42+
width: 8px;
43+
cursor: e-resize;
44+
border-radius: 4px;
45+
//background: #1f8ce6;
46+
//border: 1px solid white;
47+
}
48+
49+
:global(.react-resizable-handle-s) {
50+
bottom: -4px;
51+
left: 20%;
52+
right: 20%;
53+
height: 8px;
54+
cursor: s-resize;
55+
border-radius: 4px;
56+
//background: #1f8ce6;
57+
//border: 1px solid white;
58+
}
59+
60+
:global(.react-resizable-handle-w) {
61+
left: -4px;
62+
top: 20%;
63+
bottom: 20%;
64+
width: 8px;
65+
cursor: w-resize;
66+
border-radius: 4px;
67+
//background: #1f8ce6;
68+
//border: 1px solid white;
69+
}
70+
71+
:global(.react-resizable-handle-n) {
72+
top: -4px;
73+
left: 20%;
74+
right: 20%;
75+
height: 8px;
76+
cursor: n-resize;
77+
border-radius: 4px;
78+
//background: #1f8ce6;
79+
//border: 1px solid white;
80+
}
81+
82+
:global(.react-resizable-handle-ne) {
83+
top: -5px;
84+
right: -5px;
85+
width: 12px;
86+
height: 12px;
87+
cursor: ne-resize;
88+
border-radius: 50%;
89+
//background: #1f8ce6;
90+
//border: 2px solid white;
91+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.6);
92+
}
93+
94+
:global(.react-resizable-handle-nw) {
95+
top: -5px;
96+
left: -5px;
97+
width: 12px;
98+
height: 12px;
99+
cursor: nw-resize;
100+
border-radius: 50%;
101+
//background: #1f8ce6;
102+
//border: 2px solid white;
103+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.6);
104+
}
105+
106+
:global(.react-resizable-handle-sw) {
107+
bottom: -5px;
108+
left: -5px;
109+
width: 12px;
110+
height: 12px;
111+
cursor: sw-resize;
112+
border-radius: 50%;
113+
//background: #1f8ce6;
114+
//border: 2px solid white;
115+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.6);
116+
}
117+
}
118+
119+
.panelContainer {
120+
width: 100%;
121+
height: 100%;
122+
display: flex;
123+
flex-direction: column;
124+
background: white;
125+
border-radius: 8px;
126+
}
127+
128+
.titleBar {
129+
background: linear-gradient(135deg, #1f8ce6 0%, #1470c7 100%);
130+
color: white;
131+
padding: 8px 12px;
132+
display: flex;
133+
justify-content: space-between;
134+
align-items: center;
135+
cursor: move;
136+
user-select: none;
137+
border-radius: 8px 8px 0 0;
138+
min-height: 32px;
139+
140+
&:hover {
141+
background: linear-gradient(135deg, #1a7dd6 0%, #1260b7 100%);
142+
}
143+
}
144+
145+
.title {
146+
display: flex;
147+
align-items: center;
148+
gap: 8px;
149+
font-weight: 600;
150+
font-size: 14px;
151+
152+
span {
153+
margin: 0;
154+
}
155+
}
156+
157+
.controls {
158+
display: flex;
159+
align-items: center;
160+
gap: 4px;
161+
}
162+
163+
.closeButton {
164+
background: transparent;
165+
border: none;
166+
color: white;
167+
cursor: pointer;
168+
padding: 4px;
169+
border-radius: 4px;
170+
display: flex;
171+
align-items: center;
172+
justify-content: center;
173+
transition: background-color 0.2s;
174+
175+
&:hover {
176+
background: rgba(255, 255, 255, 0.2);
177+
}
178+
179+
&:active {
180+
background: rgba(255, 255, 255, 0.3);
181+
}
182+
}
183+
184+
.content {
185+
flex: 1;
186+
overflow: auto;
187+
border-radius: 0 0 8px 8px;
188+
}

0 commit comments

Comments
 (0)