figrecipe 0.6.0__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 +161 -1030
- figrecipe/__main__.py +12 -0
- figrecipe/_api/__init__.py +48 -0
- figrecipe/_api/_extract.py +108 -0
- figrecipe/_api/_notebook.py +61 -0
- figrecipe/_api/_panel.py +113 -0
- figrecipe/_api/_save.py +287 -0
- figrecipe/_api/_seaborn_proxy.py +34 -0
- figrecipe/_api/_style_manager.py +153 -0
- figrecipe/_api/_subplots.py +333 -0
- figrecipe/_api/_validate.py +82 -0
- 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 +4 -93
- figrecipe/_dev/_plotters.py +76 -0
- figrecipe/_dev/_run_demos.py +56 -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/_dev/demo_plotters/__init__.py +35 -166
- figrecipe/_dev/demo_plotters/_categories.py +81 -0
- figrecipe/_dev/demo_plotters/_figure_creators.py +119 -0
- figrecipe/_dev/demo_plotters/_helpers.py +31 -0
- figrecipe/_dev/demo_plotters/_registry.py +50 -0
- figrecipe/_dev/demo_plotters/bar_categorical/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/contour_surface/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/distribution/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/image_matrix/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/line_curve/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/{plot_plot.py → line_curve/plot_plot.py} +3 -2
- figrecipe/_dev/demo_plotters/scatter_points/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/special/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/{plot_pie.py → special/plot_pie.py} +5 -1
- figrecipe/_dev/demo_plotters/spectral_signal/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/vector_flow/__init__.py +4 -0
- figrecipe/_editor/__init__.py +61 -13
- figrecipe/_editor/_bbox/__init__.py +43 -0
- figrecipe/_editor/_bbox/_collections.py +177 -0
- figrecipe/_editor/_bbox/_elements.py +159 -0
- figrecipe/_editor/_bbox/_extract.py +402 -0
- figrecipe/_editor/_bbox/_extract_axes.py +370 -0
- figrecipe/_editor/_bbox/_extract_text.py +466 -0
- figrecipe/_editor/_bbox/_lines.py +173 -0
- figrecipe/_editor/_bbox/_transforms.py +146 -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 +200 -1030
- figrecipe/_editor/_helpers.py +251 -0
- figrecipe/_editor/_hitmap/__init__.py +76 -0
- figrecipe/_editor/_hitmap/_artists/__init__.py +21 -0
- figrecipe/_editor/_hitmap/_artists/_collections.py +345 -0
- figrecipe/_editor/_hitmap/_artists/_images.py +68 -0
- figrecipe/_editor/_hitmap/_artists/_lines.py +107 -0
- figrecipe/_editor/_hitmap/_artists/_patches.py +163 -0
- figrecipe/_editor/_hitmap/_artists/_text.py +190 -0
- figrecipe/_editor/_hitmap/_colors.py +181 -0
- figrecipe/_editor/_hitmap/_detect.py +194 -0
- figrecipe/_editor/_hitmap/_restore.py +154 -0
- figrecipe/_editor/_hitmap_main.py +182 -0
- figrecipe/_editor/_overrides.py +4 -1
- figrecipe/_editor/_plot_types_registry.py +190 -0
- figrecipe/_editor/_preferences.py +135 -0
- figrecipe/_editor/_render_overrides.py +507 -0
- figrecipe/_editor/_renderer.py +81 -186
- figrecipe/_editor/_routes_annotation.py +114 -0
- figrecipe/_editor/_routes_axis.py +482 -0
- figrecipe/_editor/_routes_captions.py +130 -0
- figrecipe/_editor/_routes_composition.py +270 -0
- figrecipe/_editor/_routes_core.py +126 -0
- figrecipe/_editor/_routes_datatable.py +364 -0
- figrecipe/_editor/_routes_element.py +335 -0
- 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 +243 -0
- figrecipe/_editor/_templates/__init__.py +116 -1
- figrecipe/_editor/_templates/_html.py +154 -64
- 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 +178 -0
- figrecipe/_editor/_templates/_scripts/_accordion.py +328 -0
- figrecipe/_editor/_templates/_scripts/_annotation_drag.py +504 -0
- figrecipe/_editor/_templates/_scripts/_api.py +228 -0
- figrecipe/_editor/_templates/_scripts/_canvas_context_menu.py +182 -0
- figrecipe/_editor/_templates/_scripts/_captions.py +231 -0
- figrecipe/_editor/_templates/_scripts/_colors.py +485 -0
- figrecipe/_editor/_templates/_scripts/_composition.py +283 -0
- figrecipe/_editor/_templates/_scripts/_core.py +493 -0
- 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/_debug_snapshot.py +186 -0
- figrecipe/_editor/_templates/_scripts/_element_editor.py +325 -0
- figrecipe/_editor/_templates/_scripts/_files.py +429 -0
- figrecipe/_editor/_templates/_scripts/_files_context_menu.py +240 -0
- figrecipe/_editor/_templates/_scripts/_hitmap.py +512 -0
- figrecipe/_editor/_templates/_scripts/_image_drop.py +428 -0
- figrecipe/_editor/_templates/_scripts/_inspector.py +315 -0
- figrecipe/_editor/_templates/_scripts/_labels.py +464 -0
- figrecipe/_editor/_templates/_scripts/_legend_drag.py +270 -0
- figrecipe/_editor/_templates/_scripts/_modals.py +226 -0
- figrecipe/_editor/_templates/_scripts/_multi_select.py +198 -0
- figrecipe/_editor/_templates/_scripts/_overlays.py +292 -0
- figrecipe/_editor/_templates/_scripts/_panel_drag.py +505 -0
- figrecipe/_editor/_templates/_scripts/_panel_drag_snapshot.py +33 -0
- figrecipe/_editor/_templates/_scripts/_panel_position.py +463 -0
- 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 +244 -0
- figrecipe/_editor/_templates/_scripts/_sync.py +242 -0
- figrecipe/_editor/_templates/_scripts/_tabs.py +89 -0
- figrecipe/_editor/_templates/_scripts/_undo_redo.py +348 -0
- figrecipe/_editor/_templates/_scripts/_view_mode.py +107 -0
- figrecipe/_editor/_templates/_scripts/_zoom.py +212 -0
- figrecipe/_editor/_templates/_styles/__init__.py +78 -0
- figrecipe/_editor/_templates/_styles/_base.py +111 -0
- figrecipe/_editor/_templates/_styles/_buttons.py +327 -0
- figrecipe/_editor/_templates/_styles/_color_input.py +123 -0
- figrecipe/_editor/_templates/_styles/_composition.py +87 -0
- figrecipe/_editor/_templates/_styles/_controls.py +430 -0
- 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 +144 -0
- figrecipe/_editor/_templates/_styles/_file_browser.py +466 -0
- figrecipe/_editor/_templates/_styles/_forms.py +224 -0
- figrecipe/_editor/_templates/_styles/_hitmap.py +191 -0
- figrecipe/_editor/_templates/_styles/_inspector.py +90 -0
- figrecipe/_editor/_templates/_styles/_labels.py +118 -0
- figrecipe/_editor/_templates/_styles/_modals.py +127 -0
- figrecipe/_editor/_templates/_styles/_overlays.py +130 -0
- figrecipe/_editor/_templates/_styles/_preview.py +430 -0
- figrecipe/_editor/_templates/_styles/_selection.py +73 -0
- 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 +8 -0
- figrecipe/_recorder.py +63 -109
- figrecipe/_recorder_utils.py +124 -0
- figrecipe/_reproducer/__init__.py +18 -0
- figrecipe/_reproducer/_core.py +509 -0
- figrecipe/_reproducer/_custom_plots.py +279 -0
- figrecipe/_reproducer/_seaborn.py +100 -0
- figrecipe/_reproducer/_violin.py +186 -0
- figrecipe/_signatures/_kwargs.py +273 -0
- figrecipe/_signatures/_loader.py +21 -423
- figrecipe/_signatures/_parsing.py +147 -0
- figrecipe/_utils/__init__.py +3 -0
- figrecipe/_utils/_bundle.py +205 -0
- figrecipe/_wrappers/_axes.py +252 -895
- figrecipe/_wrappers/_axes_helpers.py +136 -0
- figrecipe/_wrappers/_axes_plots.py +418 -0
- figrecipe/_wrappers/_axes_seaborn.py +157 -0
- figrecipe/_wrappers/_caption_generator.py +218 -0
- figrecipe/_wrappers/_figure.py +188 -1
- figrecipe/_wrappers/_panel_labels.py +127 -0
- figrecipe/_wrappers/_plot_helpers.py +143 -0
- figrecipe/_wrappers/_stat_annotation.py +274 -0
- figrecipe/_wrappers/_violin_helpers.py +180 -0
- figrecipe/styles/__init__.py +8 -6
- figrecipe/styles/_dotdict.py +72 -0
- figrecipe/styles/_finalize.py +134 -0
- figrecipe/styles/_fonts.py +77 -0
- figrecipe/styles/_kwargs_converter.py +178 -0
- figrecipe/styles/_plot_styles.py +209 -0
- figrecipe/styles/_style_applier.py +42 -480
- figrecipe/styles/_style_loader.py +16 -192
- figrecipe/styles/_themes.py +151 -0
- figrecipe/styles/presets/MATPLOTLIB.yaml +2 -1
- figrecipe/styles/presets/SCITEX.yaml +40 -28
- figrecipe-0.9.0.dist-info/METADATA +427 -0
- figrecipe-0.9.0.dist-info/RECORD +277 -0
- figrecipe-0.9.0.dist-info/entry_points.txt +2 -0
- figrecipe/_editor/_bbox.py +0 -978
- figrecipe/_editor/_hitmap.py +0 -937
- figrecipe/_editor/_templates/_scripts.py +0 -2778
- figrecipe/_editor/_templates/_styles.py +0 -1326
- figrecipe/_reproducer.py +0 -975
- figrecipe-0.6.0.dist-info/METADATA +0 -394
- figrecipe-0.6.0.dist-info/RECORD +0 -90
- /figrecipe/_dev/demo_plotters/{plot_bar.py → bar_categorical/plot_bar.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_barh.py → bar_categorical/plot_barh.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_contour.py → contour_surface/plot_contour.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_contourf.py → contour_surface/plot_contourf.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_tricontour.py → contour_surface/plot_tricontour.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_tricontourf.py → contour_surface/plot_tricontourf.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_tripcolor.py → contour_surface/plot_tripcolor.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_triplot.py → contour_surface/plot_triplot.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_boxplot.py → distribution/plot_boxplot.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_ecdf.py → distribution/plot_ecdf.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_hist.py → distribution/plot_hist.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_hist2d.py → distribution/plot_hist2d.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_violinplot.py → distribution/plot_violinplot.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_hexbin.py → image_matrix/plot_hexbin.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_imshow.py → image_matrix/plot_imshow.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_matshow.py → image_matrix/plot_matshow.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_pcolor.py → image_matrix/plot_pcolor.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_pcolormesh.py → image_matrix/plot_pcolormesh.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_spy.py → image_matrix/plot_spy.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_errorbar.py → line_curve/plot_errorbar.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_fill.py → line_curve/plot_fill.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_fill_between.py → line_curve/plot_fill_between.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_fill_betweenx.py → line_curve/plot_fill_betweenx.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_stackplot.py → line_curve/plot_stackplot.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_stairs.py → line_curve/plot_stairs.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_step.py → line_curve/plot_step.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_scatter.py → scatter_points/plot_scatter.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_eventplot.py → special/plot_eventplot.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_loglog.py → special/plot_loglog.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_semilogx.py → special/plot_semilogx.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_semilogy.py → special/plot_semilogy.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_stem.py → special/plot_stem.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_acorr.py → spectral_signal/plot_acorr.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_angle_spectrum.py → spectral_signal/plot_angle_spectrum.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_cohere.py → spectral_signal/plot_cohere.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_csd.py → spectral_signal/plot_csd.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_magnitude_spectrum.py → spectral_signal/plot_magnitude_spectrum.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_phase_spectrum.py → spectral_signal/plot_phase_spectrum.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_psd.py → spectral_signal/plot_psd.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_specgram.py → spectral_signal/plot_specgram.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_xcorr.py → spectral_signal/plot_xcorr.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_barbs.py → vector_flow/plot_barbs.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_quiver.py → vector_flow/plot_quiver.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_streamplot.py → vector_flow/plot_streamplot.py} +0 -0
- {figrecipe-0.6.0.dist-info → figrecipe-0.9.0.dist-info}/WHEEL +0 -0
- {figrecipe-0.6.0.dist-info → figrecipe-0.9.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Selection drawing and property synchronization JavaScript.
|
|
4
|
+
|
|
5
|
+
This module contains the JavaScript code for:
|
|
6
|
+
- Drawing selection overlays (polylines, circles, rectangles)
|
|
7
|
+
- Clearing selection state
|
|
8
|
+
- Syncing properties panel to selected element
|
|
9
|
+
- Updating element property highlights
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
SCRIPTS_SELECTION = """
|
|
13
|
+
// ===== SELECTION AND PROPERTY SYNC =====
|
|
14
|
+
|
|
15
|
+
// Clear current selection
|
|
16
|
+
function clearSelection() {
|
|
17
|
+
selectedElement = null;
|
|
18
|
+
clearSelectionOverlay();
|
|
19
|
+
|
|
20
|
+
// Clear section and field highlights
|
|
21
|
+
document.querySelectorAll('.section-highlighted').forEach(s => s.classList.remove('section-highlighted'));
|
|
22
|
+
document.querySelectorAll('.field-highlighted').forEach(f => f.classList.remove('field-highlighted'));
|
|
23
|
+
|
|
24
|
+
// Clear panel selection
|
|
25
|
+
if (typeof clearPanelSelection === 'function') {
|
|
26
|
+
clearPanelSelection();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Switch back to Figure tab when nothing selected
|
|
30
|
+
switchTab('figure');
|
|
31
|
+
|
|
32
|
+
// Update hint and show all if in filter mode
|
|
33
|
+
const hint = document.getElementById('selection-hint');
|
|
34
|
+
if (hint && viewMode === 'selected') {
|
|
35
|
+
hint.textContent = '';
|
|
36
|
+
hint.style.color = '';
|
|
37
|
+
showAllProperties();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Draw selection shape(s) - handles lines, scatter, and rectangles
|
|
42
|
+
function drawSelection(key) {
|
|
43
|
+
const overlay = document.getElementById('selection-overlay');
|
|
44
|
+
overlay.innerHTML = '';
|
|
45
|
+
|
|
46
|
+
const img = document.getElementById('preview-image');
|
|
47
|
+
if (!img.naturalWidth || !img.naturalHeight) return;
|
|
48
|
+
|
|
49
|
+
// Set SVG viewBox to match natural image size
|
|
50
|
+
overlay.setAttribute('viewBox', `0 0 ${img.naturalWidth} ${img.naturalHeight}`);
|
|
51
|
+
overlay.style.width = `${img.naturalWidth}px`;
|
|
52
|
+
overlay.style.height = `${img.naturalHeight}px`;
|
|
53
|
+
|
|
54
|
+
const scaleX = 1.0;
|
|
55
|
+
const scaleY = 1.0;
|
|
56
|
+
const offsetX = 0;
|
|
57
|
+
const offsetY = 0;
|
|
58
|
+
|
|
59
|
+
// Determine which elements to highlight
|
|
60
|
+
let elementsToHighlight = [key];
|
|
61
|
+
if (selectedElement && selectedElement.groupElements) {
|
|
62
|
+
elementsToHighlight = selectedElement.groupElements.map(e => e.key);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Draw selection for each element
|
|
66
|
+
for (const elemKey of elementsToHighlight) {
|
|
67
|
+
const bbox = currentBboxes[elemKey];
|
|
68
|
+
if (!bbox) continue;
|
|
69
|
+
|
|
70
|
+
// Get element color from colorMap (primary source) or bbox (fallback)
|
|
71
|
+
const colorMapInfo = (colorMap && colorMap[elemKey]) || {};
|
|
72
|
+
const elementColor = colorMapInfo.original_color || bbox.original_color || '#2563eb';
|
|
73
|
+
const isPrimary = elemKey === key;
|
|
74
|
+
|
|
75
|
+
if (bbox.type === 'line' && bbox.points && bbox.points.length > 1) {
|
|
76
|
+
_drawLineSelection(overlay, bbox, elementColor, isPrimary, offsetX, offsetY, scaleX, scaleY);
|
|
77
|
+
} else if (bbox.type === 'scatter' && bbox.points && bbox.points.length > 0) {
|
|
78
|
+
_drawScatterSelection(overlay, bbox, elementColor, isPrimary, offsetX, offsetY, scaleX, scaleY);
|
|
79
|
+
} else {
|
|
80
|
+
_drawRectSelection(overlay, bbox, elementColor, isPrimary, offsetX, offsetY, scaleX, scaleY);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Helper: Draw line selection (polyline)
|
|
86
|
+
function _drawLineSelection(overlay, bbox, elementColor, isPrimary, offsetX, offsetY, scaleX, scaleY) {
|
|
87
|
+
const points = bbox.points.map(pt => {
|
|
88
|
+
const x = offsetX + pt[0] * scaleX;
|
|
89
|
+
const y = offsetY + pt[1] * scaleY;
|
|
90
|
+
return `${x},${y}`;
|
|
91
|
+
}).join(' ');
|
|
92
|
+
|
|
93
|
+
const polyline = document.createElementNS('http://www.w3.org/2000/svg', 'polyline');
|
|
94
|
+
polyline.setAttribute('points', points);
|
|
95
|
+
polyline.setAttribute('class', 'selection-polyline');
|
|
96
|
+
polyline.style.setProperty('--element-color', elementColor);
|
|
97
|
+
if (isPrimary) {
|
|
98
|
+
polyline.style.strokeWidth = '10';
|
|
99
|
+
polyline.style.strokeOpacity = '0.6';
|
|
100
|
+
}
|
|
101
|
+
overlay.appendChild(polyline);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Helper: Draw scatter selection (circles)
|
|
105
|
+
function _drawScatterSelection(overlay, bbox, elementColor, isPrimary, offsetX, offsetY, scaleX, scaleY) {
|
|
106
|
+
bbox.points.forEach(pt => {
|
|
107
|
+
const cx = offsetX + pt[0] * scaleX;
|
|
108
|
+
const cy = offsetY + pt[1] * scaleY;
|
|
109
|
+
|
|
110
|
+
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
|
111
|
+
circle.setAttribute('cx', cx);
|
|
112
|
+
circle.setAttribute('cy', cy);
|
|
113
|
+
circle.setAttribute('r', isPrimary ? 4 : 3);
|
|
114
|
+
circle.setAttribute('class', 'selection-circle-subtle');
|
|
115
|
+
circle.style.setProperty('--element-color', elementColor);
|
|
116
|
+
overlay.appendChild(circle);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Helper: Draw rectangle selection
|
|
121
|
+
function _drawRectSelection(overlay, bbox, elementColor, isPrimary, offsetX, offsetY, scaleX, scaleY) {
|
|
122
|
+
const x = offsetX + bbox.x * scaleX;
|
|
123
|
+
const y = offsetY + bbox.y * scaleY;
|
|
124
|
+
const width = bbox.width * scaleX;
|
|
125
|
+
const height = bbox.height * scaleY;
|
|
126
|
+
|
|
127
|
+
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
|
128
|
+
rect.setAttribute('x', x);
|
|
129
|
+
rect.setAttribute('y', y);
|
|
130
|
+
rect.setAttribute('width', Math.max(width, 2));
|
|
131
|
+
rect.setAttribute('height', Math.max(height, 2));
|
|
132
|
+
rect.setAttribute('class', 'selection-rect');
|
|
133
|
+
rect.style.setProperty('--element-color', elementColor);
|
|
134
|
+
|
|
135
|
+
if (isPrimary) {
|
|
136
|
+
rect.classList.add('selection-primary');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
overlay.appendChild(rect);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Clear selection overlay
|
|
143
|
+
function clearSelectionOverlay() {
|
|
144
|
+
document.getElementById('selection-overlay').innerHTML = '';
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Sync properties panel to selected element
|
|
148
|
+
function syncPropertiesToElement(element) {
|
|
149
|
+
// Always show dynamic call properties for the selected element
|
|
150
|
+
showDynamicCallProperties(element);
|
|
151
|
+
|
|
152
|
+
// In 'selected' mode, only show call properties (no section highlighting)
|
|
153
|
+
if (viewMode === 'selected') {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Map element types to section IDs (for 'all' mode)
|
|
158
|
+
const sectionMap = {
|
|
159
|
+
'axes': 'section-dimensions',
|
|
160
|
+
'line': 'section-lines',
|
|
161
|
+
'scatter': 'section-markers',
|
|
162
|
+
'bar': 'section-lines',
|
|
163
|
+
'fill': 'section-lines',
|
|
164
|
+
'boxplot': 'section-boxplot',
|
|
165
|
+
'violin': 'section-violin',
|
|
166
|
+
'title': 'section-fonts',
|
|
167
|
+
'xlabel': 'section-fonts',
|
|
168
|
+
'ylabel': 'section-fonts',
|
|
169
|
+
'xticks': 'section-ticks',
|
|
170
|
+
'yticks': 'section-ticks',
|
|
171
|
+
'legend': 'section-legend',
|
|
172
|
+
'spine': 'section-dimensions',
|
|
173
|
+
'contour': 'section-dimensions',
|
|
174
|
+
'image': 'section-dimensions',
|
|
175
|
+
'pie': 'section-dimensions',
|
|
176
|
+
'hist': 'section-lines',
|
|
177
|
+
'quiver': 'section-lines',
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const sectionId = sectionMap[element.type] || 'section-dimensions';
|
|
181
|
+
|
|
182
|
+
// Close all sections and remove highlights (accordion behavior)
|
|
183
|
+
document.querySelectorAll('.section').forEach(section => {
|
|
184
|
+
section.classList.remove('section-highlighted');
|
|
185
|
+
if (section.id && section.id !== 'section-download') {
|
|
186
|
+
section.removeAttribute('open');
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// Find and highlight the relevant section
|
|
191
|
+
const section = document.getElementById(sectionId);
|
|
192
|
+
if (section) {
|
|
193
|
+
section.setAttribute('open', '');
|
|
194
|
+
section.classList.add('section-highlighted');
|
|
195
|
+
setTimeout(() => {
|
|
196
|
+
section.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
|
197
|
+
}, 50);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
updateElementProperties(element);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Update property values for selected element
|
|
204
|
+
function updateElementProperties(element) {
|
|
205
|
+
// Clear previous field highlights
|
|
206
|
+
document.querySelectorAll('.form-row').forEach(row => {
|
|
207
|
+
row.classList.remove('field-highlighted');
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// Map element types to relevant form field IDs
|
|
211
|
+
const fieldMap = {
|
|
212
|
+
'line': ['lines_trace_mm', 'lines_errorbar_mm', 'lines_errorbar_cap_mm'],
|
|
213
|
+
'scatter': ['markers_size_mm', 'markers_scatter_mm', 'markers_edge_width_mm'],
|
|
214
|
+
'bar': ['lines_trace_mm'],
|
|
215
|
+
'fill': ['lines_trace_mm'],
|
|
216
|
+
'boxplot': ['lines_trace_mm', 'markers_flier_mm', 'boxplot_median_color'],
|
|
217
|
+
'violin': ['lines_trace_mm'],
|
|
218
|
+
'title': ['fonts_title_pt', 'fonts_family'],
|
|
219
|
+
'xlabel': ['fonts_axis_label_pt', 'fonts_family'],
|
|
220
|
+
'ylabel': ['fonts_axis_label_pt', 'fonts_family'],
|
|
221
|
+
'xticks': ['fonts_tick_label_pt', 'ticks_length_mm', 'ticks_direction'],
|
|
222
|
+
'yticks': ['fonts_tick_label_pt', 'ticks_length_mm', 'ticks_direction'],
|
|
223
|
+
'legend': ['fonts_legend_pt', 'legend_frameon', 'legend_loc'],
|
|
224
|
+
'spine': ['axes_thickness_mm'],
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const relevantFields = fieldMap[element.type] || [];
|
|
228
|
+
|
|
229
|
+
// Highlight relevant form rows
|
|
230
|
+
relevantFields.forEach(fieldId => {
|
|
231
|
+
const row = document.querySelector(`[data-field="${fieldId}"]`);
|
|
232
|
+
if (row) {
|
|
233
|
+
row.classList.add('field-highlighted');
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// Show dynamic call properties for this element
|
|
238
|
+
showDynamicCallProperties(element);
|
|
239
|
+
}
|
|
240
|
+
"""
|
|
241
|
+
|
|
242
|
+
__all__ = ["SCRIPTS_SELECTION"]
|
|
243
|
+
|
|
244
|
+
# EOF
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Tri-directional synchronization between Data/Canvas/Properties panes.
|
|
4
|
+
|
|
5
|
+
This module coordinates selection state across:
|
|
6
|
+
- Data pane (datatable tabs)
|
|
7
|
+
- Canvas pane (figure preview with hit regions)
|
|
8
|
+
- Properties pane (Figure/Axis/Element tabs)
|
|
9
|
+
|
|
10
|
+
Sync directions:
|
|
11
|
+
- Canvas → Data: Selecting element highlights datatable tab
|
|
12
|
+
- Canvas → Properties: Selecting element switches to appropriate tab
|
|
13
|
+
- Data → Canvas: Clicking datatable tab selects element on canvas
|
|
14
|
+
- Data → Properties: Clicking datatable tab switches to Element tab
|
|
15
|
+
- Properties → Canvas: Clicking tab filters/selects canvas elements
|
|
16
|
+
- Properties → Data: Clicking tab highlights relevant data
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
SCRIPTS_SYNC = """
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Tri-directional Pane Synchronization
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
// Sync state flags to prevent infinite loops
|
|
25
|
+
let _syncingFromCanvas = false;
|
|
26
|
+
let _syncingFromData = false;
|
|
27
|
+
let _syncingFromProperties = false;
|
|
28
|
+
|
|
29
|
+
// ============================================================================
|
|
30
|
+
// Initialize Sync Hooks
|
|
31
|
+
// ============================================================================
|
|
32
|
+
function initPaneSync() {
|
|
33
|
+
console.log('[PaneSync] Initializing tri-directional synchronization');
|
|
34
|
+
|
|
35
|
+
// Hook datatable tab selection
|
|
36
|
+
hookDatatableTabSync();
|
|
37
|
+
|
|
38
|
+
// Hook properties tab clicks
|
|
39
|
+
hookPropertiesTabSync();
|
|
40
|
+
|
|
41
|
+
// Canvas selection is already hooked via hookCanvasSelection() in datatable core
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ============================================================================
|
|
45
|
+
// Data Pane -> Canvas/Properties Sync
|
|
46
|
+
// ============================================================================
|
|
47
|
+
function hookDatatableTabSync() {
|
|
48
|
+
// Wrap selectTab to add canvas/properties sync
|
|
49
|
+
if (typeof window.selectTab === 'function') {
|
|
50
|
+
const originalSelectTab = window.selectTab;
|
|
51
|
+
window.selectTab = function(tabId) {
|
|
52
|
+
originalSelectTab(tabId);
|
|
53
|
+
|
|
54
|
+
// Avoid infinite loops
|
|
55
|
+
if (_syncingFromCanvas || _syncingFromProperties) return;
|
|
56
|
+
_syncingFromData = true;
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
syncCanvasFromDatatableTab(tabId);
|
|
60
|
+
syncPropertiesFromDatatableTab(tabId);
|
|
61
|
+
} finally {
|
|
62
|
+
_syncingFromData = false;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
console.log('[PaneSync] Datatable tab sync hooked');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function syncCanvasFromDatatableTab(tabId) {
|
|
70
|
+
if (!tabId || typeof datatableTabs === 'undefined') return;
|
|
71
|
+
const tabState = datatableTabs[tabId];
|
|
72
|
+
if (!tabState) return;
|
|
73
|
+
|
|
74
|
+
const callId = tabState.callId || tabState.name;
|
|
75
|
+
if (!callId) return;
|
|
76
|
+
|
|
77
|
+
console.log('[PaneSync] Data->Canvas: Looking for element matching callId:', callId);
|
|
78
|
+
|
|
79
|
+
// Search currentBboxes for matching element
|
|
80
|
+
if (typeof currentBboxes !== 'undefined' && currentBboxes) {
|
|
81
|
+
// First pass: exact match on call_id or label
|
|
82
|
+
for (const [key, bbox] of Object.entries(currentBboxes)) {
|
|
83
|
+
if (key === '_meta' || !bbox) continue;
|
|
84
|
+
if (bbox.call_id === callId || bbox.label === callId) {
|
|
85
|
+
if (typeof selectElement === 'function') {
|
|
86
|
+
selectElement({ key, ...bbox });
|
|
87
|
+
console.log('[PaneSync] Data->Canvas: Selected (exact)', key);
|
|
88
|
+
}
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Second pass: key contains callId (e.g., "scatter" in "ax1_scatter0")
|
|
94
|
+
for (const [key, bbox] of Object.entries(currentBboxes)) {
|
|
95
|
+
if (key === '_meta' || !bbox) continue;
|
|
96
|
+
// Match pattern: ax{N}_{callId}{N} like ax1_scatter0
|
|
97
|
+
const pattern = new RegExp(`ax\\d+_${callId}\\d*$`, 'i');
|
|
98
|
+
if (pattern.test(key)) {
|
|
99
|
+
if (typeof selectElement === 'function') {
|
|
100
|
+
selectElement({ key, ...bbox });
|
|
101
|
+
console.log('[PaneSync] Data->Canvas: Selected (pattern)', key);
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Third pass: looser match - key contains callId anywhere
|
|
108
|
+
for (const [key, bbox] of Object.entries(currentBboxes)) {
|
|
109
|
+
if (key === '_meta' || !bbox) continue;
|
|
110
|
+
if (key.toLowerCase().includes(callId.toLowerCase())) {
|
|
111
|
+
if (typeof selectElement === 'function') {
|
|
112
|
+
selectElement({ key, ...bbox });
|
|
113
|
+
console.log('[PaneSync] Data->Canvas: Selected (contains)', key);
|
|
114
|
+
}
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
console.log('[PaneSync] Data->Canvas: No matching element found for', callId);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Fallback: select the panel associated with this tab
|
|
123
|
+
if (tabState.targetAxis !== null && tabState.targetAxis !== undefined) {
|
|
124
|
+
const axKey = `ax${tabState.targetAxis}_axes`;
|
|
125
|
+
if (typeof currentBboxes !== 'undefined' && currentBboxes[axKey]) {
|
|
126
|
+
const bbox = currentBboxes[axKey];
|
|
127
|
+
if (typeof selectElement === 'function') {
|
|
128
|
+
selectElement({ key: axKey, ...bbox, type: 'axes', ax_index: tabState.targetAxis });
|
|
129
|
+
console.log('[PaneSync] Data->Canvas: Selected panel', tabState.targetAxis);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function syncPropertiesFromDatatableTab(tabId) {
|
|
136
|
+
if (!tabId || typeof datatableTabs === 'undefined') return;
|
|
137
|
+
const tabState = datatableTabs[tabId];
|
|
138
|
+
if (!tabState) return;
|
|
139
|
+
|
|
140
|
+
// Data tabs represent plot elements, so switch to Element tab
|
|
141
|
+
if (typeof switchTab === 'function') {
|
|
142
|
+
switchTab('element');
|
|
143
|
+
console.log('[PaneSync] Data->Properties: Switched to Element tab');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ============================================================================
|
|
148
|
+
// Properties Pane -> Canvas/Data Sync
|
|
149
|
+
// ============================================================================
|
|
150
|
+
function hookPropertiesTabSync() {
|
|
151
|
+
// Add click listeners to Figure/Axis/Element tab buttons
|
|
152
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
153
|
+
const tabBtns = document.querySelectorAll('.tab-btn');
|
|
154
|
+
tabBtns.forEach(btn => {
|
|
155
|
+
btn.addEventListener('click', (e) => {
|
|
156
|
+
// Avoid infinite loops
|
|
157
|
+
if (_syncingFromCanvas || _syncingFromData) return;
|
|
158
|
+
_syncingFromProperties = true;
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
const tabName = btn.id.replace('tab-', '');
|
|
162
|
+
syncCanvasFromPropertiesTab(tabName);
|
|
163
|
+
syncDataFromPropertiesTab(tabName);
|
|
164
|
+
} finally {
|
|
165
|
+
_syncingFromProperties = false;
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
console.log('[PaneSync] Properties tab sync hooked');
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function syncCanvasFromPropertiesTab(tabName) {
|
|
174
|
+
// When switching to a properties tab, optionally clear or filter canvas selection
|
|
175
|
+
// For now, we'll just log - actual behavior depends on UX requirements
|
|
176
|
+
console.log('[PaneSync] Properties->Canvas: Tab', tabName, 'clicked');
|
|
177
|
+
|
|
178
|
+
// If Figure tab, clear element selection (show figure-level props)
|
|
179
|
+
if (tabName === 'figure' && typeof clearSelection === 'function') {
|
|
180
|
+
// Don't auto-clear as it might be disruptive
|
|
181
|
+
// clearSelection();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function syncDataFromPropertiesTab(tabName) {
|
|
186
|
+
// When switching to Element tab, try to highlight the currently selected element's data
|
|
187
|
+
if (tabName === 'element' && typeof selectedElement !== 'undefined' && selectedElement) {
|
|
188
|
+
if (typeof syncDatatableToElement === 'function') {
|
|
189
|
+
syncDatatableToElement(selectedElement);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
console.log('[PaneSync] Properties->Data: Tab', tabName, 'clicked');
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ============================================================================
|
|
196
|
+
// Enhanced Canvas -> Data/Properties Sync (augments existing hooks)
|
|
197
|
+
// ============================================================================
|
|
198
|
+
function enhanceCanvasSync() {
|
|
199
|
+
// Wrap selectElement to add enhanced sync
|
|
200
|
+
if (typeof window.selectElement === 'function') {
|
|
201
|
+
const originalSelectElement = window.selectElement;
|
|
202
|
+
window.selectElement = function(element) {
|
|
203
|
+
// Avoid infinite loops
|
|
204
|
+
if (_syncingFromData || _syncingFromProperties) {
|
|
205
|
+
originalSelectElement(element);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
_syncingFromCanvas = true;
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
originalSelectElement(element);
|
|
212
|
+
|
|
213
|
+
// Auto-switch Properties tab based on element type
|
|
214
|
+
if (element && typeof autoSwitchTab === 'function') {
|
|
215
|
+
autoSwitchTab(element.type);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Sync datatable to element (already done in hookCanvasSelection, but ensure it happens)
|
|
219
|
+
if (element && typeof syncDatatableToElement === 'function') {
|
|
220
|
+
syncDatatableToElement(element);
|
|
221
|
+
}
|
|
222
|
+
} finally {
|
|
223
|
+
_syncingFromCanvas = false;
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
console.log('[PaneSync] Canvas selection sync enhanced');
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Initialize sync on page load
|
|
231
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
232
|
+
// Delay to ensure other modules are loaded
|
|
233
|
+
setTimeout(() => {
|
|
234
|
+
initPaneSync();
|
|
235
|
+
enhanceCanvasSync();
|
|
236
|
+
}, 100);
|
|
237
|
+
});
|
|
238
|
+
"""
|
|
239
|
+
|
|
240
|
+
__all__ = ["SCRIPTS_SYNC"]
|
|
241
|
+
|
|
242
|
+
# EOF
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Tab navigation JavaScript for the figure editor.
|
|
4
|
+
|
|
5
|
+
This module contains the JavaScript code for:
|
|
6
|
+
- Tab switching (Figure/Axis/Element)
|
|
7
|
+
- Auto-switching based on selected element
|
|
8
|
+
- Tab hints management
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
SCRIPTS_TABS = """
|
|
12
|
+
// ===== TAB NAVIGATION =====
|
|
13
|
+
|
|
14
|
+
// Current active tab
|
|
15
|
+
let currentTab = 'figure';
|
|
16
|
+
|
|
17
|
+
// Element type to tab mapping
|
|
18
|
+
const AXIS_TYPES = ['title', 'xlabel', 'ylabel', 'suptitle', 'supxlabel', 'supylabel', 'legend'];
|
|
19
|
+
const ELEMENT_TYPES = ['line', 'scatter', 'bar', 'hist', 'fill', 'boxplot', 'violin', 'image', 'linecollection', 'quiver', 'pie', 'contour', 'specgram'];
|
|
20
|
+
|
|
21
|
+
// Switch between Figure/Axis/Element tabs
|
|
22
|
+
function switchTab(tabName) {
|
|
23
|
+
currentTab = tabName;
|
|
24
|
+
|
|
25
|
+
// Update tab buttons
|
|
26
|
+
document.querySelectorAll('.tab-btn').forEach(btn => {
|
|
27
|
+
btn.classList.toggle('active', btn.id === `tab-${tabName}`);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Update tab content
|
|
31
|
+
document.querySelectorAll('.tab-content').forEach(content => {
|
|
32
|
+
content.classList.toggle('active', content.id === `tab-content-${tabName}`);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Update hints based on selection state
|
|
36
|
+
updateTabHints();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Get appropriate tab for element type
|
|
40
|
+
function getTabForElementType(elementType) {
|
|
41
|
+
if (!elementType) return 'figure';
|
|
42
|
+
if (AXIS_TYPES.includes(elementType)) return 'axis';
|
|
43
|
+
if (ELEMENT_TYPES.includes(elementType)) return 'element';
|
|
44
|
+
return 'figure';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Auto-switch to appropriate tab based on selected element
|
|
48
|
+
function autoSwitchTab(elementType) {
|
|
49
|
+
const targetTab = getTabForElementType(elementType);
|
|
50
|
+
if (targetTab !== currentTab) {
|
|
51
|
+
switchTab(targetTab);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Update tab hints based on current state
|
|
56
|
+
function updateTabHints() {
|
|
57
|
+
const axisHint = document.getElementById('axis-tab-hint');
|
|
58
|
+
const elementHint = document.getElementById('element-tab-hint');
|
|
59
|
+
const elementPanel = document.getElementById('selected-element-panel');
|
|
60
|
+
const dynamicProps = document.getElementById('dynamic-call-properties');
|
|
61
|
+
|
|
62
|
+
if (currentTab === 'axis') {
|
|
63
|
+
if (selectedElement && AXIS_TYPES.includes(selectedElement.type)) {
|
|
64
|
+
if (axisHint) axisHint.style.display = 'none';
|
|
65
|
+
} else {
|
|
66
|
+
if (axisHint) axisHint.style.display = 'block';
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (currentTab === 'element') {
|
|
71
|
+
if (selectedElement && ELEMENT_TYPES.includes(selectedElement.type)) {
|
|
72
|
+
if (elementHint) elementHint.style.display = 'none';
|
|
73
|
+
if (elementPanel) {
|
|
74
|
+
elementPanel.style.display = 'block';
|
|
75
|
+
document.getElementById('element-type-badge').textContent = selectedElement.type;
|
|
76
|
+
document.getElementById('element-name').textContent = selectedElement.label || selectedElement.key;
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
if (elementHint) elementHint.style.display = 'block';
|
|
80
|
+
if (elementPanel) elementPanel.style.display = 'none';
|
|
81
|
+
if (dynamicProps) dynamicProps.style.display = 'none';
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
__all__ = ["SCRIPTS_TABS"]
|
|
88
|
+
|
|
89
|
+
# EOF
|