figrecipe 0.7.4__py3-none-any.whl → 0.9.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- figrecipe/__init__.py +74 -76
- figrecipe/__main__.py +12 -0
- figrecipe/_api/_panel.py +67 -0
- figrecipe/_api/_save.py +100 -4
- figrecipe/_cli/__init__.py +7 -0
- figrecipe/_cli/_compose.py +87 -0
- figrecipe/_cli/_convert.py +117 -0
- figrecipe/_cli/_crop.py +82 -0
- figrecipe/_cli/_edit.py +70 -0
- figrecipe/_cli/_extract.py +128 -0
- figrecipe/_cli/_fonts.py +47 -0
- figrecipe/_cli/_info.py +67 -0
- figrecipe/_cli/_main.py +58 -0
- figrecipe/_cli/_reproduce.py +79 -0
- figrecipe/_cli/_style.py +77 -0
- figrecipe/_cli/_validate.py +66 -0
- figrecipe/_cli/_version.py +50 -0
- figrecipe/_composition/__init__.py +32 -0
- figrecipe/_composition/_alignment.py +452 -0
- figrecipe/_composition/_compose.py +179 -0
- figrecipe/_composition/_import_axes.py +127 -0
- figrecipe/_composition/_visibility.py +125 -0
- figrecipe/_dev/__init__.py +2 -0
- figrecipe/_dev/browser/__init__.py +69 -0
- figrecipe/_dev/browser/_audio.py +240 -0
- figrecipe/_dev/browser/_caption.py +356 -0
- figrecipe/_dev/browser/_click_effect.py +146 -0
- figrecipe/_dev/browser/_cursor.py +196 -0
- figrecipe/_dev/browser/_highlight.py +105 -0
- figrecipe/_dev/browser/_narration.py +237 -0
- figrecipe/_dev/browser/_recorder.py +446 -0
- figrecipe/_dev/browser/_utils.py +178 -0
- figrecipe/_dev/browser/_video_trim/__init__.py +152 -0
- figrecipe/_dev/browser/_video_trim/_detection.py +223 -0
- figrecipe/_dev/browser/_video_trim/_markers.py +140 -0
- figrecipe/_editor/__init__.py +36 -36
- figrecipe/_editor/_bbox/_extract.py +155 -9
- figrecipe/_editor/_bbox/_extract_text.py +124 -0
- figrecipe/_editor/_call_overrides.py +183 -0
- figrecipe/_editor/_datatable_plot_handlers.py +249 -0
- figrecipe/_editor/_figure_layout.py +211 -0
- figrecipe/_editor/_flask_app.py +157 -16
- figrecipe/_editor/_helpers.py +17 -8
- figrecipe/_editor/_hitmap/_detect.py +89 -32
- figrecipe/_editor/_hitmap_main.py +4 -4
- figrecipe/_editor/_overrides.py +4 -1
- figrecipe/_editor/_plot_types_registry.py +190 -0
- figrecipe/_editor/_render_overrides.py +38 -11
- figrecipe/_editor/_renderer.py +46 -1
- figrecipe/_editor/_routes_annotation.py +114 -0
- figrecipe/_editor/_routes_axis.py +35 -6
- figrecipe/_editor/_routes_captions.py +130 -0
- figrecipe/_editor/_routes_composition.py +270 -0
- figrecipe/_editor/_routes_core.py +15 -173
- figrecipe/_editor/_routes_datatable.py +364 -0
- figrecipe/_editor/_routes_element.py +37 -19
- figrecipe/_editor/_routes_files.py +443 -0
- figrecipe/_editor/_routes_image.py +200 -0
- figrecipe/_editor/_routes_snapshot.py +94 -0
- figrecipe/_editor/_routes_style.py +28 -8
- figrecipe/_editor/_templates/__init__.py +40 -2
- figrecipe/_editor/_templates/_html.py +97 -103
- figrecipe/_editor/_templates/_html_components/__init__.py +13 -0
- figrecipe/_editor/_templates/_html_components/_composition_toolbar.py +79 -0
- figrecipe/_editor/_templates/_html_components/_file_browser.py +41 -0
- figrecipe/_editor/_templates/_html_datatable.py +92 -0
- figrecipe/_editor/_templates/_scripts/__init__.py +58 -0
- figrecipe/_editor/_templates/_scripts/_accordion.py +328 -0
- figrecipe/_editor/_templates/_scripts/_annotation_drag.py +504 -0
- figrecipe/_editor/_templates/_scripts/_api.py +1 -1
- figrecipe/_editor/_templates/_scripts/_canvas_context_menu.py +182 -0
- figrecipe/_editor/_templates/_scripts/_captions.py +231 -0
- figrecipe/_editor/_templates/_scripts/_composition.py +283 -0
- figrecipe/_editor/_templates/_scripts/_core.py +94 -37
- figrecipe/_editor/_templates/_scripts/_datatable/__init__.py +59 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_cell_edit.py +97 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_clipboard.py +164 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_context_menu.py +221 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_core.py +150 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_editable.py +511 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_import.py +161 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_plot.py +261 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_selection.py +438 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_table.py +256 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_tabs.py +354 -0
- figrecipe/_editor/_templates/_scripts/_element_editor.py +17 -2
- figrecipe/_editor/_templates/_scripts/_files.py +274 -40
- figrecipe/_editor/_templates/_scripts/_files_context_menu.py +240 -0
- figrecipe/_editor/_templates/_scripts/_hitmap.py +87 -84
- figrecipe/_editor/_templates/_scripts/_image_drop.py +428 -0
- figrecipe/_editor/_templates/_scripts/_legend_drag.py +5 -0
- figrecipe/_editor/_templates/_scripts/_multi_select.py +198 -0
- figrecipe/_editor/_templates/_scripts/_panel_drag.py +219 -48
- figrecipe/_editor/_templates/_scripts/_panel_drag_snapshot.py +33 -0
- figrecipe/_editor/_templates/_scripts/_panel_position.py +238 -54
- figrecipe/_editor/_templates/_scripts/_panel_resize.py +230 -0
- figrecipe/_editor/_templates/_scripts/_panel_snap.py +307 -0
- figrecipe/_editor/_templates/_scripts/_region_select.py +255 -0
- figrecipe/_editor/_templates/_scripts/_selection.py +8 -1
- figrecipe/_editor/_templates/_scripts/_sync.py +242 -0
- figrecipe/_editor/_templates/_scripts/_undo_redo.py +348 -0
- figrecipe/_editor/_templates/_scripts/_zoom.py +52 -19
- figrecipe/_editor/_templates/_styles/__init__.py +9 -0
- figrecipe/_editor/_templates/_styles/_base.py +47 -0
- figrecipe/_editor/_templates/_styles/_buttons.py +127 -6
- figrecipe/_editor/_templates/_styles/_composition.py +87 -0
- figrecipe/_editor/_templates/_styles/_controls.py +168 -3
- figrecipe/_editor/_templates/_styles/_datatable/__init__.py +40 -0
- figrecipe/_editor/_templates/_styles/_datatable/_editable.py +203 -0
- figrecipe/_editor/_templates/_styles/_datatable/_panel.py +268 -0
- figrecipe/_editor/_templates/_styles/_datatable/_table.py +479 -0
- figrecipe/_editor/_templates/_styles/_datatable/_toolbar.py +384 -0
- figrecipe/_editor/_templates/_styles/_datatable/_vars.py +123 -0
- figrecipe/_editor/_templates/_styles/_dynamic_props.py +5 -5
- figrecipe/_editor/_templates/_styles/_file_browser.py +466 -0
- figrecipe/_editor/_templates/_styles/_forms.py +98 -0
- figrecipe/_editor/_templates/_styles/_hitmap.py +7 -0
- figrecipe/_editor/_templates/_styles/_modals.py +29 -0
- figrecipe/_editor/_templates/_styles/_overlays.py +5 -5
- figrecipe/_editor/_templates/_styles/_preview.py +213 -8
- figrecipe/_editor/_templates/_styles/_spinner.py +117 -0
- figrecipe/_editor/static/audio/click.mp3 +0 -0
- figrecipe/_editor/static/click.mp3 +0 -0
- figrecipe/_editor/static/icons/favicon.ico +0 -0
- figrecipe/_integrations/__init__.py +17 -0
- figrecipe/_integrations/_scitex_stats.py +298 -0
- figrecipe/_params/_DECORATION_METHODS.py +2 -0
- figrecipe/_recorder.py +28 -3
- figrecipe/_reproducer/_core.py +60 -49
- figrecipe/_utils/__init__.py +3 -0
- figrecipe/_utils/_bundle.py +205 -0
- figrecipe/_wrappers/_axes.py +150 -2
- figrecipe/_wrappers/_caption_generator.py +218 -0
- figrecipe/_wrappers/_figure.py +26 -1
- figrecipe/_wrappers/_stat_annotation.py +274 -0
- figrecipe/styles/_style_applier.py +10 -2
- figrecipe/styles/presets/SCITEX.yaml +11 -4
- {figrecipe-0.7.4.dist-info → figrecipe-0.9.0.dist-info}/METADATA +144 -146
- figrecipe-0.9.0.dist-info/RECORD +277 -0
- figrecipe-0.9.0.dist-info/entry_points.txt +2 -0
- figrecipe-0.7.4.dist-info/RECORD +0 -188
- {figrecipe-0.7.4.dist-info → figrecipe-0.9.0.dist-info}/WHEEL +0 -0
- {figrecipe-0.7.4.dist-info → figrecipe-0.9.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Datatable right-click context menu JavaScript."""
|
|
4
|
+
|
|
5
|
+
JS_DATATABLE_CONTEXT_MENU = """
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Context Menu (Right-Click Menu)
|
|
8
|
+
// ============================================================================
|
|
9
|
+
let datatableContextMenu = null;
|
|
10
|
+
|
|
11
|
+
function createContextMenu() {
|
|
12
|
+
if (datatableContextMenu) return;
|
|
13
|
+
|
|
14
|
+
datatableContextMenu = document.createElement('div');
|
|
15
|
+
datatableContextMenu.className = 'datatable-context-menu';
|
|
16
|
+
datatableContextMenu.style.display = 'none';
|
|
17
|
+
datatableContextMenu.innerHTML = `
|
|
18
|
+
<div class="context-menu-item" data-action="cut">
|
|
19
|
+
Cut<span class="shortcut">Ctrl+X</span>
|
|
20
|
+
</div>
|
|
21
|
+
<div class="context-menu-item" data-action="copy">
|
|
22
|
+
Copy<span class="shortcut">Ctrl+C</span>
|
|
23
|
+
</div>
|
|
24
|
+
<div class="context-menu-item" data-action="paste">
|
|
25
|
+
Paste<span class="shortcut">Ctrl+V</span>
|
|
26
|
+
</div>
|
|
27
|
+
<div class="context-menu-divider"></div>
|
|
28
|
+
<div class="context-menu-item" data-action="clear">
|
|
29
|
+
Clear cells<span class="shortcut">Del</span>
|
|
30
|
+
</div>
|
|
31
|
+
<div class="context-menu-divider"></div>
|
|
32
|
+
<div class="context-menu-item" data-action="insert-row">
|
|
33
|
+
Insert row below
|
|
34
|
+
</div>
|
|
35
|
+
<div class="context-menu-item" data-action="insert-col">
|
|
36
|
+
Insert column right
|
|
37
|
+
</div>
|
|
38
|
+
<div class="context-menu-divider"></div>
|
|
39
|
+
<div class="context-menu-item" data-action="delete-row">
|
|
40
|
+
Delete row
|
|
41
|
+
</div>
|
|
42
|
+
<div class="context-menu-item" data-action="delete-col">
|
|
43
|
+
Delete column
|
|
44
|
+
</div>
|
|
45
|
+
`;
|
|
46
|
+
document.body.appendChild(datatableContextMenu);
|
|
47
|
+
setupContextMenuListeners();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function setupContextMenuListeners() {
|
|
51
|
+
if (!datatableContextMenu) return;
|
|
52
|
+
|
|
53
|
+
// Click on menu items
|
|
54
|
+
datatableContextMenu.querySelectorAll('.context-menu-item').forEach(item => {
|
|
55
|
+
item.addEventListener('click', (e) => {
|
|
56
|
+
e.stopPropagation();
|
|
57
|
+
const action = item.dataset.action;
|
|
58
|
+
handleContextMenuAction(action);
|
|
59
|
+
hideContextMenu();
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Hide on click outside
|
|
64
|
+
document.addEventListener('click', hideContextMenu);
|
|
65
|
+
document.addEventListener('scroll', hideContextMenu, true);
|
|
66
|
+
document.addEventListener('keydown', (e) => {
|
|
67
|
+
if (e.key === 'Escape') hideContextMenu();
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function handleContextMenuAction(action) {
|
|
72
|
+
if (!datatableData) return;
|
|
73
|
+
|
|
74
|
+
switch (action) {
|
|
75
|
+
case 'cut':
|
|
76
|
+
// Simulate Ctrl+X
|
|
77
|
+
document.execCommand('cut');
|
|
78
|
+
break;
|
|
79
|
+
case 'copy':
|
|
80
|
+
// Copy selected cells as TSV
|
|
81
|
+
const copyText = getSelectedCellsAsTSV();
|
|
82
|
+
navigator.clipboard.writeText(copyText).catch(console.error);
|
|
83
|
+
break;
|
|
84
|
+
case 'paste':
|
|
85
|
+
// Paste from clipboard
|
|
86
|
+
navigator.clipboard.readText().then(text => {
|
|
87
|
+
if (!text || !datatableCurrentCell) return;
|
|
88
|
+
const rows = text.split('\\n').map(line => line.split('\\t'));
|
|
89
|
+
const startRow = datatableCurrentCell.row;
|
|
90
|
+
const startCol = datatableCurrentCell.col;
|
|
91
|
+
rows.forEach((rowData, rOffset) => {
|
|
92
|
+
const targetRow = startRow + rOffset;
|
|
93
|
+
if (targetRow >= datatableData.rows.length) return;
|
|
94
|
+
rowData.forEach((value, cOffset) => {
|
|
95
|
+
const targetCol = startCol + cOffset;
|
|
96
|
+
if (targetCol >= datatableData.columns.length) return;
|
|
97
|
+
datatableData.rows[targetRow][targetCol] = value;
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
renderDatatable();
|
|
101
|
+
}).catch(console.error);
|
|
102
|
+
break;
|
|
103
|
+
case 'clear':
|
|
104
|
+
clearSelectedCells();
|
|
105
|
+
break;
|
|
106
|
+
case 'insert-row':
|
|
107
|
+
insertRowBelow();
|
|
108
|
+
break;
|
|
109
|
+
case 'insert-col':
|
|
110
|
+
insertColumnRight();
|
|
111
|
+
break;
|
|
112
|
+
case 'delete-row':
|
|
113
|
+
deleteCurrentRow();
|
|
114
|
+
break;
|
|
115
|
+
case 'delete-col':
|
|
116
|
+
deleteCurrentColumn();
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function insertRowBelow() {
|
|
122
|
+
if (!datatableData || !datatableCurrentCell) return;
|
|
123
|
+
const row = datatableCurrentCell.row;
|
|
124
|
+
const newRow = datatableData.columns.map(() => '');
|
|
125
|
+
datatableData.rows.splice(row + 1, 0, newRow);
|
|
126
|
+
renderDatatable();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function insertColumnRight() {
|
|
130
|
+
if (!datatableData || !datatableCurrentCell) return;
|
|
131
|
+
const col = datatableCurrentCell.col;
|
|
132
|
+
const newColIdx = datatableData.columns.length;
|
|
133
|
+
datatableData.columns.splice(col + 1, 0, {
|
|
134
|
+
name: `col${newColIdx + 1}`,
|
|
135
|
+
type: 'numeric',
|
|
136
|
+
index: col + 1
|
|
137
|
+
});
|
|
138
|
+
// Reindex columns
|
|
139
|
+
datatableData.columns.forEach((c, i) => c.index = i);
|
|
140
|
+
// Add cell to all rows
|
|
141
|
+
datatableData.rows.forEach(row => row.splice(col + 1, 0, ''));
|
|
142
|
+
renderDatatable();
|
|
143
|
+
updateVarAssignSlots();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function deleteCurrentRow() {
|
|
147
|
+
if (!datatableData || !datatableCurrentCell) return;
|
|
148
|
+
if (datatableData.rows.length <= 1) return; // Keep at least 1 row
|
|
149
|
+
const row = datatableCurrentCell.row;
|
|
150
|
+
datatableData.rows.splice(row, 1);
|
|
151
|
+
if (datatableCurrentCell.row >= datatableData.rows.length) {
|
|
152
|
+
datatableCurrentCell.row = datatableData.rows.length - 1;
|
|
153
|
+
}
|
|
154
|
+
renderDatatable();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function deleteCurrentColumn() {
|
|
158
|
+
if (!datatableData || !datatableCurrentCell) return;
|
|
159
|
+
if (datatableData.columns.length <= 1) return; // Keep at least 1 col
|
|
160
|
+
const col = datatableCurrentCell.col;
|
|
161
|
+
datatableData.columns.splice(col, 1);
|
|
162
|
+
// Reindex columns
|
|
163
|
+
datatableData.columns.forEach((c, i) => c.index = i);
|
|
164
|
+
// Remove cell from all rows
|
|
165
|
+
datatableData.rows.forEach(row => row.splice(col, 1));
|
|
166
|
+
if (datatableCurrentCell.col >= datatableData.columns.length) {
|
|
167
|
+
datatableCurrentCell.col = datatableData.columns.length - 1;
|
|
168
|
+
}
|
|
169
|
+
renderDatatable();
|
|
170
|
+
updateVarAssignSlots();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function showContextMenu(e) {
|
|
174
|
+
if (!datatableContextMenu) createContextMenu();
|
|
175
|
+
|
|
176
|
+
e.preventDefault();
|
|
177
|
+
e.stopPropagation();
|
|
178
|
+
|
|
179
|
+
const x = e.clientX;
|
|
180
|
+
const y = e.clientY;
|
|
181
|
+
|
|
182
|
+
// Position off-screen to measure
|
|
183
|
+
datatableContextMenu.style.left = '-9999px';
|
|
184
|
+
datatableContextMenu.style.top = '-9999px';
|
|
185
|
+
datatableContextMenu.style.display = 'block';
|
|
186
|
+
|
|
187
|
+
const menuWidth = datatableContextMenu.offsetWidth;
|
|
188
|
+
const menuHeight = datatableContextMenu.offsetHeight;
|
|
189
|
+
|
|
190
|
+
// Adjust position to fit in viewport
|
|
191
|
+
let left = x;
|
|
192
|
+
let top = y;
|
|
193
|
+
if (x + menuWidth > window.innerWidth - 10) {
|
|
194
|
+
left = x - menuWidth;
|
|
195
|
+
}
|
|
196
|
+
if (y + menuHeight > window.innerHeight - 10) {
|
|
197
|
+
top = y - menuHeight;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
datatableContextMenu.style.left = `${Math.max(10, left)}px`;
|
|
201
|
+
datatableContextMenu.style.top = `${Math.max(10, top)}px`;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function hideContextMenu() {
|
|
205
|
+
if (datatableContextMenu) {
|
|
206
|
+
datatableContextMenu.style.display = 'none';
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Attach context menu to table
|
|
211
|
+
function attachContextMenuListener() {
|
|
212
|
+
const table = document.querySelector('.datatable-table');
|
|
213
|
+
if (table) {
|
|
214
|
+
table.addEventListener('contextmenu', showContextMenu);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
"""
|
|
218
|
+
|
|
219
|
+
__all__ = ["JS_DATATABLE_CONTEXT_MENU"]
|
|
220
|
+
|
|
221
|
+
# EOF
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Core datatable JavaScript: state, panel toggle, initialization."""
|
|
4
|
+
|
|
5
|
+
JS_DATATABLE_CORE = """
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Datatable Panel State
|
|
8
|
+
// ============================================================================
|
|
9
|
+
let datatableData = null; // Parsed data: {columns: [...], rows: [...]}
|
|
10
|
+
let datatableSelectedColumns = new Set(); // Selected column indices
|
|
11
|
+
let datatablePlotType = 'plot'; // Default plot type
|
|
12
|
+
let datatableTargetAxis = null; // null = new figure, 0+ = existing axis index
|
|
13
|
+
let datatableVarAssignments = {}; // Variable name -> column index mapping
|
|
14
|
+
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Panel Initialization
|
|
17
|
+
// ============================================================================
|
|
18
|
+
function initDatatablePanel() {
|
|
19
|
+
// Initialize sub-modules
|
|
20
|
+
initDatatableDropzone();
|
|
21
|
+
initPlotTypeButtons();
|
|
22
|
+
|
|
23
|
+
// Initialize plot button (New = create new panel)
|
|
24
|
+
const plotBtn = document.getElementById('btn-datatable-plot');
|
|
25
|
+
if (plotBtn) {
|
|
26
|
+
plotBtn.addEventListener('click', () => {
|
|
27
|
+
datatableTargetAxis = null; // New panel
|
|
28
|
+
plotFromVarAssignments();
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Initialize split button dropdown
|
|
33
|
+
initPlotDropdown();
|
|
34
|
+
|
|
35
|
+
// Load existing data if available from figure
|
|
36
|
+
loadExistingData();
|
|
37
|
+
|
|
38
|
+
// Hook into canvas selection to sync with datatable
|
|
39
|
+
hookCanvasSelection();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function hookCanvasSelection() {
|
|
43
|
+
// Wrap the selectElement function to add datatable sync
|
|
44
|
+
if (typeof window.selectElement === 'function') {
|
|
45
|
+
const originalSelectElement = window.selectElement;
|
|
46
|
+
window.selectElement = function(element) {
|
|
47
|
+
originalSelectElement(element);
|
|
48
|
+
if (typeof syncDatatableToElement === 'function') {
|
|
49
|
+
syncDatatableToElement(element);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Also hook clearSelection
|
|
55
|
+
if (typeof window.clearSelection === 'function') {
|
|
56
|
+
const originalClearSelection = window.clearSelection;
|
|
57
|
+
window.clearSelection = function() {
|
|
58
|
+
originalClearSelection();
|
|
59
|
+
if (typeof clearDatatableHighlight === 'function') {
|
|
60
|
+
clearDatatableHighlight();
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function initPlotDropdown() {
|
|
67
|
+
const dropdownBtn = document.getElementById('btn-plot-dropdown');
|
|
68
|
+
const dropdownMenu = document.getElementById('plot-dropdown-menu');
|
|
69
|
+
if (!dropdownBtn || !dropdownMenu) return;
|
|
70
|
+
|
|
71
|
+
// Toggle dropdown
|
|
72
|
+
dropdownBtn.addEventListener('click', (e) => {
|
|
73
|
+
e.stopPropagation();
|
|
74
|
+
dropdownMenu.classList.toggle('show');
|
|
75
|
+
if (dropdownMenu.classList.contains('show')) {
|
|
76
|
+
populatePlotDropdown();
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Close on outside click
|
|
81
|
+
document.addEventListener('click', () => {
|
|
82
|
+
dropdownMenu.classList.remove('show');
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function populatePlotDropdown() {
|
|
87
|
+
const menu = document.getElementById('plot-dropdown-menu');
|
|
88
|
+
if (!menu) return;
|
|
89
|
+
|
|
90
|
+
fetch('/get_axes_positions').then(r => r.json()).then(data => {
|
|
91
|
+
const axes = Object.keys(data)
|
|
92
|
+
.filter(k => k.startsWith('ax_'))
|
|
93
|
+
.sort((a, b) => {
|
|
94
|
+
const matchA = a.match(/ax_(\\d+)_(\\d+)/);
|
|
95
|
+
const matchB = b.match(/ax_(\\d+)_(\\d+)/);
|
|
96
|
+
if (matchA && matchB) {
|
|
97
|
+
return parseInt(matchA[2]) - parseInt(matchB[2]) || parseInt(matchA[1]) - parseInt(matchB[1]);
|
|
98
|
+
}
|
|
99
|
+
return 0;
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
let html = '';
|
|
103
|
+
axes.forEach((key, i) => {
|
|
104
|
+
html += `<button class="dropdown-item" onclick="addToPanel(${i})">Add to P${i + 1}</button>`;
|
|
105
|
+
});
|
|
106
|
+
menu.innerHTML = html || '<div class="dropdown-item" style="color:var(--text-secondary)">No panels</div>';
|
|
107
|
+
}).catch(() => {
|
|
108
|
+
menu.innerHTML = '<div class="dropdown-item" style="color:var(--text-secondary)">No panels</div>';
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function addToPanel(axisIndex) {
|
|
113
|
+
datatableTargetAxis = axisIndex;
|
|
114
|
+
document.getElementById('plot-dropdown-menu').classList.remove('show');
|
|
115
|
+
plotFromVarAssignments();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ============================================================================
|
|
119
|
+
// Clear Data
|
|
120
|
+
// ============================================================================
|
|
121
|
+
function clearDatatableData() {
|
|
122
|
+
datatableData = null;
|
|
123
|
+
datatableSelectedColumns.clear();
|
|
124
|
+
datatableVarAssignments = {};
|
|
125
|
+
|
|
126
|
+
const content = document.getElementById('datatable-content');
|
|
127
|
+
if (content) {
|
|
128
|
+
content.innerHTML = '';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Show dropzone again
|
|
132
|
+
const dropzone = document.getElementById('datatable-dropzone');
|
|
133
|
+
if (dropzone) dropzone.style.display = 'block';
|
|
134
|
+
|
|
135
|
+
const toolbar = document.querySelector('.datatable-toolbar');
|
|
136
|
+
if (toolbar) toolbar.style.display = 'none';
|
|
137
|
+
|
|
138
|
+
const varAssign = document.getElementById('datatable-var-assign');
|
|
139
|
+
if (varAssign) varAssign.style.display = 'none';
|
|
140
|
+
|
|
141
|
+
updateSelectionInfo();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Initialize on page load
|
|
145
|
+
document.addEventListener('DOMContentLoaded', initDatatablePanel);
|
|
146
|
+
"""
|
|
147
|
+
|
|
148
|
+
__all__ = ["JS_DATATABLE_CORE"]
|
|
149
|
+
|
|
150
|
+
# EOF
|