Skip to content

Commit d4a98d9

Browse files
author
Nikita Stepochkin
committed
feat: multiple windows
1 parent 19957db commit d4a98d9

File tree

1 file changed

+187
-80
lines changed

1 file changed

+187
-80
lines changed

index.html

Lines changed: 187 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -5,73 +5,162 @@
55
<title>Plotly</title>
66
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
77
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" rel="stylesheet">
8-
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
8+
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
99
<script src="https://cdn.plot.ly/plotly-2.32.0.min.js"></script>
1010
<style>
1111
body {
12-
display: flex;
1312
height: 100vh;
1413
margin: 0;
1514
overflow: hidden;
1615
font-family: Arial, sans-serif;
17-
}
18-
19-
#sidebar {
20-
width: 300px;
21-
padding: 15px;
22-
background-color: #f8f9fa;
23-
overflow-y: auto;
24-
flex-shrink: 0;
16+
position: relative;
2517
}
2618

2719
.box {
2820
width: 300px;
29-
height: 200px;
21+
height: 300px;
3022
background-color: #fff;
31-
border: 1px solid dodgerblue;
23+
border: 1px solid lightblue;
3224
border-radius: 5px;
3325
box-shadow: 0 15px 15px lightgrey;
26+
position: absolute;
27+
top: 50px;
28+
left: 50px;
29+
resize: both;
30+
overflow: hidden;
31+
z-index: 0;
3432
}
3533

3634
.box-header {
3735
color: #fff;
38-
background-color: dodgerblue;
39-
padding: 10px 15px;
36+
padding: 5px 10px;
37+
display: flex;
38+
justify-content: space-between;
39+
align-items: center;
40+
cursor: pointer;
41+
height: 40px;
42+
flex-shrink: 0;
4043
}
4144

42-
.drag-handle {
43-
cursor: pointer;
45+
.header-buttons {
46+
display: flex;
47+
gap: 5px;
48+
flex-shrink: 0;
4449
}
4550

46-
.box-header {
47-
color: #fff;
48-
background-color: dodgerblue;
51+
.box-body {
52+
height: calc(100% - 40px);
53+
padding: 10px;
54+
overflow: auto;
55+
background-color: lightblue;
56+
}
57+
58+
.btn {
59+
border: 1px solid #ccc;
4960
padding: 5px 10px;
61+
background-color: white;
62+
cursor: pointer;
63+
flex-shrink: 0;
64+
}
65+
66+
.btn:hover {
67+
background-color: #f0f0f0;
68+
}
69+
70+
#add-box-btn {
71+
position: absolute;
72+
top: 10px;
73+
left: 10px;
74+
cursor: pointer;
75+
z-index: 1000;
76+
color: lightblue;
77+
}
78+
79+
#add-box-btn:hover {
80+
color: lightskyblue;
81+
}
82+
83+
#overlay-text {
84+
position: absolute;
85+
top: 10px;
86+
left: 50%;
87+
transform: translateX(-50%);
88+
font-size: 24px;
89+
color: rgba(0, 0, 0, 0.5);
90+
pointer-events: none;
91+
z-index: -1000;
5092
}
5193
</style>
5294
</head>
5395
<body>
54-
<div id="sidebar">
55-
<h1>Plotly JSON Visualizer</h1>
56-
<div>
57-
<label for="json-input">JSON Data:</label><br>
58-
<textarea id="json-input" class="form-control" rows="10"></textarea><br>
59-
<label for="json-path">JSON Path (optional):</label><br>
60-
<input type="text" id="json-path" class="form-control" placeholder="e.g., data.chart"><br><br>
61-
<button onclick="reloadPlot()" class="btn btn-success">Reload Plot</button>
62-
</div>
63-
</div>
64-
<div class="box" data-draggable="true" data-resizable="true">
65-
<div class="box-header drag-handle" data-drag-handle="true">Drag here</div>
66-
<div class="box-body" id="div-plotly"></div>
67-
</div>
96+
<i id="add-box-btn" class="fas fa-plus-circle fa-2x"></i>
97+
<div id="overlay-text">Plotly JSON Visualizer</div>
6898

6999
<script>
70-
const plotDiv = document.getElementById('div-plotly');
100+
document.getElementById('add-box-btn').addEventListener('click', addNewBox);
101+
102+
function addNewBox() {
103+
const box = document.createElement('div');
104+
box.className = 'box';
105+
box.innerHTML = `
106+
<div class="box-header" data-drag-handle="true">
107+
<div class="header-buttons">
108+
<button class="btn btn-generate">Generate</button>
109+
<button class="btn btn-back" style="display:none;">Back</button>
110+
</div>
111+
<span class="btn btn-close">X</span>
112+
</div>
113+
<div class="box-body">
114+
<div class="view-json">
115+
<label for="json-input">JSON Data:</label><br>
116+
<textarea class="json-input form-control" rows="10"></textarea><br>
117+
<button class="btn btn-beautify">Beautify JSON</button><br><br>
118+
<label for="json-path">JSON Path (optional):</label><br>
119+
<input type="text" class="json-path form-control" placeholder="e.g., data.chart"><br><br>
120+
</div>
121+
<div class="view-chart" style="display:none;">
122+
<div class="chart" style="width: 100%; height: 100%;"></div>
123+
</div>
124+
</div>
125+
`;
126+
document.body.appendChild(box);
71127

72-
function reloadPlot() {
73-
const jsonData = document.getElementById('json-input').value;
74-
const jsonPath = document.getElementById('json-path').value;
128+
setupDraggable(box);
129+
setupResizable(box);
130+
setupZIndex(box);
131+
132+
box.querySelector('.btn-generate').addEventListener('click', () => reloadPlot(box));
133+
box.querySelector('.btn-back').addEventListener('click', () => switchToJson(box));
134+
box.querySelector('.btn-close').addEventListener('click', () => box.remove());
135+
box.querySelector('.btn-beautify').addEventListener('click', () => beautifyJson(box));
136+
}
137+
138+
function switchToChart(box) {
139+
const viewJson = box.querySelector('.view-json');
140+
const viewChart = box.querySelector('.view-chart');
141+
const btnGenerate = box.querySelector('.btn-generate');
142+
const btnBack = box.querySelector('.btn-back');
143+
viewJson.style.display = 'none';
144+
viewChart.style.display = 'block';
145+
btnGenerate.style.display = 'none';
146+
btnBack.style.display = 'block';
147+
updatePlotSize(box);
148+
}
149+
150+
function switchToJson(box) {
151+
const viewJson = box.querySelector('.view-json');
152+
const viewChart = box.querySelector('.view-chart');
153+
const btnGenerate = box.querySelector('.btn-generate');
154+
const btnBack = box.querySelector('.btn-back');
155+
viewJson.style.display = 'block';
156+
viewChart.style.display = 'none';
157+
btnGenerate.style.display = 'block';
158+
btnBack.style.display = 'none';
159+
}
160+
161+
function reloadPlot(box) {
162+
const jsonData = box.querySelector('.json-input').value;
163+
const jsonPath = box.querySelector('.json-path').value;
75164

76165
try {
77166
let plotData = JSON.parse(jsonData);
@@ -92,71 +181,89 @@ <h1>Plotly JSON Visualizer</h1>
92181
responsive: true
93182
};
94183

184+
const plotDiv = box.querySelector('.chart');
95185
Plotly.newPlot(plotDiv, plotData, layout, config);
186+
187+
switchToChart(box); // Switch to chart view immediately after generating the plot
96188
} catch (error) {
97189
console.error('Error:', error);
98190
alert('Invalid JSON data or JSON path');
99191
}
100192
}
101193

102-
let dragEl;
103-
let dragHandleEl
104-
const lastPosition = {};
194+
function beautifyJson(box) {
195+
const jsonInput = box.querySelector('.json-input');
196+
try {
197+
const jsonData = JSON.parse(jsonInput.value);
198+
jsonInput.value = JSON.stringify(jsonData, null, 4);
199+
} catch (error) {
200+
console.error('Error:', error);
201+
alert('Invalid JSON data');
202+
}
203+
}
105204

106-
setupResizable();
107-
setupDraggable();
205+
function setupDraggable(box) {
206+
const dragHandleEl = box.querySelector('[data-drag-handle]');
207+
let dragEl;
208+
let lastPosition = {};
108209

109-
function setupDraggable() {
110-
dragHandleEl = document.querySelector('[data-drag-handle]');
111210
dragHandleEl.addEventListener('mousedown', dragStart);
112211
dragHandleEl.addEventListener('mouseup', dragEnd);
113212
dragHandleEl.addEventListener('mouseout', dragEnd);
114-
}
115213

116-
function setupResizable() {
117-
const resizeEl = document.querySelector('[data-resizable]');
118-
resizeEl.style.setProperty('resize', 'both');
119-
resizeEl.style.setProperty('overflow', 'hidden');
120-
new ResizeObserver(() => {
121-
updatePlotSize();
122-
}).observe(resizeEl);
123-
}
214+
function dragStart(event) {
215+
dragEl = box;
216+
dragEl.style.setProperty('position', 'absolute');
217+
lastPosition.left = event.clientX;
218+
lastPosition.top = event.clientY;
219+
dragHandleEl.classList.add('dragging');
220+
dragHandleEl.addEventListener('mousemove', dragMove);
221+
}
124222

125-
function dragStart(event) {
126-
dragEl = getDraggableAncestor(event.target);
127-
dragEl.style.setProperty('position', 'absolute');
128-
lastPosition.left = event.target.clientX;
129-
lastPosition.top = event.target.clientY;
130-
dragHandleEl.classList.add('dragging');
131-
dragHandleEl.addEventListener('mousemove', dragMove);
132-
}
223+
function dragMove(event) {
224+
const dragElRect = dragEl.getBoundingClientRect();
225+
const newLeft = dragElRect.left + event.clientX - lastPosition.left;
226+
const newTop = dragElRect.top + event.clientY - lastPosition.top;
227+
dragEl.style.setProperty('left', `${newLeft}px`);
228+
dragEl.style.setProperty('top', `${newTop}px`);
229+
lastPosition.left = event.clientX;
230+
lastPosition.top = event.clientY;
231+
window.getSelection().removeAllRanges();
232+
updatePlotSize(box);
233+
}
133234

134-
function dragMove(event) {
135-
const dragElRect = dragEl.getBoundingClientRect();
136-
const newLeft = dragElRect.left + event.clientX - lastPosition.left;
137-
const newTop = dragElRect.top + event.clientY - lastPosition.top;
138-
dragEl.style.setProperty('left', `${newLeft}px`);
139-
dragEl.style.setProperty('top', `${newTop}px`);
140-
lastPosition.left = event.clientX;
141-
lastPosition.top = event.clientY;
142-
window.getSelection().removeAllRanges();
143-
updatePlotSize();
235+
function dragEnd() {
236+
dragHandleEl.classList.remove('dragging');
237+
dragHandleEl.removeEventListener('mousemove', dragMove);
238+
dragEl = null;
239+
}
144240
}
145241

146-
function updatePlotSize() {
147-
Plotly.Plots.resize(plotDiv);
242+
function setupResizable(box) {
243+
box.style.setProperty('resize', 'both');
244+
box.style.setProperty('overflow', 'hidden');
245+
246+
new ResizeObserver(() => {
247+
updatePlotSize(box);
248+
}).observe(box);
148249
}
149250

150-
function getDraggableAncestor(element) {
151-
if (element.getAttribute('data-draggable')) return element;
152-
return getDraggableAncestor(element.parentElement);
251+
function setupZIndex(box) {
252+
box.addEventListener('mousedown', () => {
253+
document.querySelectorAll('.box').forEach(b => b.style.zIndex = 0);
254+
box.style.zIndex = 10;
255+
});
153256
}
154257

155-
function dragEnd() {
156-
dragHandleEl.classList.remove('dragging');
157-
dragHandleEl.removeEventListener('mousemove', dragMove);
158-
dragEl = null;
258+
function updatePlotSize(box) {
259+
const plotDiv = box.querySelector('.chart');
260+
if (plotDiv) {
261+
Plotly.Plots.resize(plotDiv);
262+
}
159263
}
264+
265+
// Add initial box on load
266+
addNewBox();
160267
</script>
161268
</body>
162269
</html>

0 commit comments

Comments
 (0)