matterviz 0.3.0 → 0.3.2
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.
- package/dist/FilePicker.svelte +37 -20
- package/dist/Icon.svelte +2 -2
- package/dist/MillerIndexInput.svelte +60 -0
- package/dist/MillerIndexInput.svelte.d.ts +7 -0
- package/dist/app.css +38 -2
- package/dist/brillouin/BrillouinZone.svelte +20 -62
- package/dist/brillouin/BrillouinZone.svelte.d.ts +1 -1
- package/dist/brillouin/BrillouinZoneExportPane.svelte +12 -20
- package/dist/brillouin/BrillouinZoneScene.svelte +2 -2
- package/dist/brillouin/BrillouinZoneScene.svelte.d.ts +1 -1
- package/dist/chempot-diagram/ChemPotDiagram.svelte +192 -0
- package/dist/chempot-diagram/ChemPotDiagram.svelte.d.ts +13 -0
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +677 -0
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte.d.ts +16 -0
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +2688 -0
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte.d.ts +16 -0
- package/dist/chempot-diagram/ChemPotScene3D.svelte +8 -0
- package/dist/chempot-diagram/ChemPotScene3D.svelte.d.ts +7 -0
- package/dist/chempot-diagram/color.d.ts +10 -0
- package/dist/chempot-diagram/color.js +33 -0
- package/dist/chempot-diagram/compute.d.ts +38 -0
- package/dist/chempot-diagram/compute.js +650 -0
- package/dist/chempot-diagram/index.d.ts +5 -0
- package/dist/chempot-diagram/index.js +5 -0
- package/dist/chempot-diagram/pointer.d.ts +16 -0
- package/dist/chempot-diagram/pointer.js +40 -0
- package/dist/chempot-diagram/temperature.d.ts +15 -0
- package/dist/chempot-diagram/temperature.js +37 -0
- package/dist/chempot-diagram/types.d.ts +83 -0
- package/dist/chempot-diagram/types.js +27 -0
- package/dist/colors/index.d.ts +3 -1
- package/dist/colors/index.js +4 -0
- package/dist/composition/BarChart.svelte +13 -22
- package/dist/composition/BubbleChart.svelte +5 -3
- package/dist/composition/FormulaFilter.svelte +770 -90
- package/dist/composition/FormulaFilter.svelte.d.ts +37 -1
- package/dist/composition/PieChart.svelte +43 -18
- package/dist/composition/PieChart.svelte.d.ts +1 -1
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +2 -0
- package/dist/convex-hull/ConvexHull.svelte +14 -1
- package/dist/convex-hull/ConvexHull.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull2D.svelte +14 -45
- package/dist/convex-hull/ConvexHull2D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull3D.svelte +396 -134
- package/dist/convex-hull/ConvexHull3D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHull4D.svelte +93 -42
- package/dist/convex-hull/ConvexHull4D.svelte.d.ts +1 -1
- package/dist/convex-hull/ConvexHullControls.svelte +94 -31
- package/dist/convex-hull/ConvexHullControls.svelte.d.ts +4 -2
- package/dist/convex-hull/ConvexHullStats.svelte +697 -128
- package/dist/convex-hull/ConvexHullStats.svelte.d.ts +6 -1
- package/dist/convex-hull/ConvexHullTooltip.svelte +1 -0
- package/dist/convex-hull/GasPressureControls.svelte +72 -38
- package/dist/convex-hull/GasPressureControls.svelte.d.ts +2 -1
- package/dist/convex-hull/TemperatureSlider.svelte +46 -19
- package/dist/convex-hull/TemperatureSlider.svelte.d.ts +2 -1
- package/dist/convex-hull/demo-temperature.d.ts +6 -0
- package/dist/convex-hull/demo-temperature.js +36 -0
- package/dist/convex-hull/gas-thermodynamics.js +16 -5
- package/dist/convex-hull/helpers.d.ts +7 -1
- package/dist/convex-hull/helpers.js +45 -15
- package/dist/convex-hull/index.d.ts +15 -1
- package/dist/convex-hull/index.js +1 -0
- package/dist/convex-hull/thermodynamics.d.ts +8 -21
- package/dist/convex-hull/thermodynamics.js +106 -17
- package/dist/convex-hull/types.d.ts +7 -0
- package/dist/convex-hull/types.js +11 -0
- package/dist/coordination/CoordinationBarPlot.svelte +29 -46
- package/dist/element/BohrAtom.svelte +1 -1
- package/dist/element/data.js +2 -14
- package/dist/element/data.json.gz +0 -0
- package/dist/element/index.d.ts +1 -1
- package/dist/element/index.js +1 -0
- package/dist/element/types.d.ts +1 -0
- package/dist/fermi-surface/FermiSurface.svelte +21 -65
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceControls.svelte.d.ts +1 -1
- package/dist/fermi-surface/FermiSurfaceScene.svelte +1 -1
- package/dist/fermi-surface/FermiSurfaceScene.svelte.d.ts +1 -1
- package/dist/fermi-surface/compute.js +1 -21
- package/dist/fermi-surface/marching-cubes.d.ts +2 -13
- package/dist/fermi-surface/marching-cubes.js +2 -519
- package/dist/fermi-surface/parse.js +17 -23
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +1273 -0
- package/dist/heatmap-matrix/HeatmapMatrix.svelte.d.ts +110 -0
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte +171 -0
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +31 -0
- package/dist/heatmap-matrix/index.d.ts +53 -0
- package/dist/heatmap-matrix/index.js +100 -0
- package/dist/heatmap-matrix/shared.d.ts +2 -0
- package/dist/heatmap-matrix/shared.js +4 -0
- package/dist/icons.d.ts +119 -0
- package/dist/icons.js +119 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.js +6 -1
- package/dist/io/export.js +15 -3
- package/dist/io/file-drop.d.ts +7 -0
- package/dist/io/file-drop.js +43 -0
- package/dist/io/index.d.ts +2 -2
- package/dist/io/index.js +2 -112
- package/dist/io/types.d.ts +1 -0
- package/dist/io/url-drop.d.ts +2 -0
- package/dist/io/url-drop.js +118 -0
- package/dist/isosurface/Isosurface.svelte +231 -0
- package/dist/isosurface/Isosurface.svelte.d.ts +8 -0
- package/dist/isosurface/IsosurfaceControls.svelte +273 -0
- package/dist/isosurface/IsosurfaceControls.svelte.d.ts +9 -0
- package/dist/isosurface/index.d.ts +5 -0
- package/dist/isosurface/index.js +6 -0
- package/dist/isosurface/parse.d.ts +6 -0
- package/dist/isosurface/parse.js +548 -0
- package/dist/isosurface/slice.d.ts +11 -0
- package/dist/isosurface/slice.js +145 -0
- package/dist/isosurface/types.d.ts +55 -0
- package/dist/isosurface/types.js +178 -0
- package/dist/labels.d.ts +2 -1
- package/dist/labels.js +1 -0
- package/dist/layout/InfoTag.svelte +62 -62
- package/dist/layout/SubpageGrid.svelte +74 -0
- package/dist/layout/SubpageGrid.svelte.d.ts +14 -0
- package/dist/layout/index.d.ts +1 -0
- package/dist/layout/index.js +1 -0
- package/dist/layout/json-tree/JsonNode.svelte +226 -53
- package/dist/layout/json-tree/JsonTree.svelte +425 -51
- package/dist/layout/json-tree/JsonTree.svelte.d.ts +1 -1
- package/dist/layout/json-tree/JsonValue.svelte +218 -97
- package/dist/layout/json-tree/types.d.ts +27 -2
- package/dist/layout/json-tree/utils.d.ts +14 -1
- package/dist/layout/json-tree/utils.js +254 -0
- package/dist/marching-cubes.d.ts +14 -0
- package/dist/marching-cubes.js +519 -0
- package/dist/math.d.ts +8 -0
- package/dist/math.js +374 -7
- package/dist/overlays/ContextMenu.svelte +3 -2
- package/dist/overlays/DraggablePane.svelte +163 -58
- package/dist/overlays/DraggablePane.svelte.d.ts +2 -0
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +232 -77
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +6 -2
- package/dist/phase-diagram/PhaseDiagramControls.svelte +32 -11
- package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +3 -2
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte +103 -0
- package/dist/phase-diagram/PhaseDiagramEditorPane.svelte.d.ts +15 -0
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte +102 -95
- package/dist/phase-diagram/PhaseDiagramExportPane.svelte.d.ts +7 -0
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte +100 -26
- package/dist/phase-diagram/PhaseDiagramTooltip.svelte.d.ts +6 -3
- package/dist/phase-diagram/index.d.ts +2 -0
- package/dist/phase-diagram/index.js +2 -0
- package/dist/phase-diagram/svg-to-diagram.d.ts +2 -0
- package/dist/phase-diagram/svg-to-diagram.js +865 -0
- package/dist/phase-diagram/types.d.ts +10 -0
- package/dist/phase-diagram/utils.d.ts +7 -4
- package/dist/phase-diagram/utils.js +149 -59
- package/dist/plot/AxisLabel.svelte +26 -0
- package/dist/plot/AxisLabel.svelte.d.ts +16 -0
- package/dist/plot/BarPlot.svelte +473 -228
- package/dist/plot/BarPlot.svelte.d.ts +3 -3
- package/dist/plot/BarPlotControls.svelte +3 -2
- package/dist/plot/BarPlotControls.svelte.d.ts +1 -1
- package/dist/plot/ColorBar.svelte +54 -54
- package/dist/plot/ColorBar.svelte.d.ts +1 -1
- package/dist/plot/ElementScatter.svelte +4 -3
- package/dist/plot/FillArea.svelte +4 -1
- package/dist/plot/Histogram.svelte +320 -230
- package/dist/plot/Histogram.svelte.d.ts +2 -2
- package/dist/plot/HistogramControls.svelte +29 -10
- package/dist/plot/HistogramControls.svelte.d.ts +6 -2
- package/dist/plot/InteractiveAxisLabel.svelte.d.ts +2 -2
- package/dist/plot/PlotControls.svelte +109 -27
- package/dist/plot/PlotControls.svelte.d.ts +1 -1
- package/dist/plot/PlotLegend.svelte +1 -1
- package/dist/plot/PortalSelect.svelte +2 -1
- package/dist/plot/ReferenceLine.svelte +2 -1
- package/dist/plot/ReferenceLine.svelte.d.ts +1 -0
- package/dist/plot/ReferencePlane.svelte +1 -3
- package/dist/plot/ScatterPlot.svelte +343 -209
- package/dist/plot/ScatterPlot.svelte.d.ts +3 -3
- package/dist/plot/ScatterPlot3D.svelte.d.ts +2 -2
- package/dist/plot/ScatterPlot3DControls.svelte +203 -250
- package/dist/plot/ScatterPlot3DScene.svelte +4 -7
- package/dist/plot/ScatterPlot3DScene.svelte.d.ts +2 -2
- package/dist/plot/ScatterPlotControls.svelte +95 -55
- package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
- package/dist/plot/ZeroLines.svelte +44 -0
- package/dist/plot/ZeroLines.svelte.d.ts +32 -0
- package/dist/plot/ZoomRect.svelte +21 -0
- package/dist/plot/ZoomRect.svelte.d.ts +8 -0
- package/dist/plot/axis-utils.d.ts +1 -1
- package/dist/plot/data-cleaning.js +1 -5
- package/dist/plot/index.d.ts +6 -2
- package/dist/plot/index.js +6 -2
- package/dist/plot/interactions.d.ts +8 -10
- package/dist/plot/interactions.js +10 -19
- package/dist/plot/layout.d.ts +7 -1
- package/dist/plot/layout.js +12 -4
- package/dist/plot/reference-line.d.ts +4 -21
- package/dist/plot/reference-line.js +7 -81
- package/dist/plot/types.d.ts +42 -17
- package/dist/plot/types.js +10 -0
- package/dist/plot/utils/label-placement.js +14 -11
- package/dist/plot/utils.d.ts +1 -0
- package/dist/plot/utils.js +14 -0
- package/dist/rdf/RdfPlot.svelte +55 -66
- package/dist/rdf/RdfPlot.svelte.d.ts +1 -1
- package/dist/rdf/index.d.ts +1 -1
- package/dist/rdf/index.js +1 -1
- package/dist/settings.d.ts +5 -0
- package/dist/settings.js +37 -3
- package/dist/spectral/Bands.svelte +515 -143
- package/dist/spectral/Bands.svelte.d.ts +22 -2
- package/dist/spectral/helpers.d.ts +23 -1
- package/dist/spectral/helpers.js +65 -9
- package/dist/spectral/types.d.ts +2 -0
- package/dist/structure/AtomLegend.svelte +31 -10
- package/dist/structure/AtomLegend.svelte.d.ts +1 -1
- package/dist/structure/CellSelect.svelte +92 -22
- package/dist/structure/Lattice.svelte +2 -0
- package/dist/structure/Structure.svelte +716 -173
- package/dist/structure/Structure.svelte.d.ts +7 -2
- package/dist/structure/StructureControls.svelte +26 -14
- package/dist/structure/StructureControls.svelte.d.ts +5 -1
- package/dist/structure/StructureInfoPane.svelte +7 -1
- package/dist/structure/StructureScene.svelte +386 -95
- package/dist/structure/StructureScene.svelte.d.ts +15 -4
- package/dist/structure/atom-properties.d.ts +6 -2
- package/dist/structure/atom-properties.js +38 -25
- package/dist/structure/export.js +10 -7
- package/dist/structure/ferrox-wasm-types.d.ts +3 -2
- package/dist/structure/ferrox-wasm-types.js +0 -3
- package/dist/structure/ferrox-wasm.d.ts +3 -2
- package/dist/structure/ferrox-wasm.js +1 -2
- package/dist/structure/index.d.ts +7 -0
- package/dist/structure/index.js +22 -0
- package/dist/structure/parse.js +19 -16
- package/dist/structure/partial-occupancy.d.ts +25 -0
- package/dist/structure/partial-occupancy.js +102 -0
- package/dist/structure/validation.js +6 -3
- package/dist/symmetry/SymmetryStats.svelte +18 -4
- package/dist/symmetry/WyckoffTable.svelte +18 -10
- package/dist/symmetry/index.d.ts +7 -4
- package/dist/symmetry/index.js +83 -18
- package/dist/table/HeatmapTable.svelte +468 -69
- package/dist/table/HeatmapTable.svelte.d.ts +13 -1
- package/dist/table/ToggleMenu.svelte +291 -44
- package/dist/table/ToggleMenu.svelte.d.ts +4 -1
- package/dist/table/index.d.ts +3 -0
- package/dist/tooltip/index.d.ts +1 -1
- package/dist/tooltip/index.js +1 -0
- package/dist/trajectory/Trajectory.svelte +147 -145
- package/dist/trajectory/TrajectoryExportPane.svelte +13 -9
- package/dist/trajectory/TrajectoryExportPane.svelte.d.ts +1 -1
- package/dist/trajectory/constants.d.ts +6 -0
- package/dist/trajectory/constants.js +7 -0
- package/dist/trajectory/extract.js +3 -5
- package/dist/trajectory/format-detect.d.ts +9 -0
- package/dist/trajectory/format-detect.js +76 -0
- package/dist/trajectory/frame-reader.d.ts +17 -0
- package/dist/trajectory/frame-reader.js +339 -0
- package/dist/trajectory/helpers.d.ts +15 -0
- package/dist/trajectory/helpers.js +187 -0
- package/dist/trajectory/index.d.ts +1 -0
- package/dist/trajectory/index.js +11 -4
- package/dist/trajectory/parse/ase.d.ts +2 -0
- package/dist/trajectory/parse/ase.js +76 -0
- package/dist/trajectory/parse/hdf5.d.ts +2 -0
- package/dist/trajectory/parse/hdf5.js +121 -0
- package/dist/trajectory/parse/index.d.ts +12 -0
- package/dist/trajectory/parse/index.js +304 -0
- package/dist/trajectory/parse/lammps.d.ts +5 -0
- package/dist/trajectory/parse/lammps.js +169 -0
- package/dist/trajectory/parse/vasp.d.ts +2 -0
- package/dist/trajectory/parse/vasp.js +65 -0
- package/dist/trajectory/parse/xyz.d.ts +2 -0
- package/dist/trajectory/parse/xyz.js +109 -0
- package/dist/trajectory/types.d.ts +11 -0
- package/dist/trajectory/types.js +1 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +4 -0
- package/dist/xrd/XrdPlot.svelte +6 -4
- package/dist/xrd/calc-xrd.js +0 -1
- package/package.json +33 -23
- package/readme.md +4 -4
- package/dist/trajectory/parse.d.ts +0 -42
- package/dist/trajectory/parse.js +0 -1267
- /package/dist/element/{data.json.d.ts → data.json.gz.d.ts} +0 -0
|
@@ -3,26 +3,26 @@
|
|
|
3
3
|
generics="Metadata extends Record<string, unknown> = Record<string, unknown>"
|
|
4
4
|
>import { format_value, symbol_names } from '../labels';
|
|
5
5
|
import { FullscreenToggle, set_fullscreen_bg } from '../layout';
|
|
6
|
-
import { ColorBar, compute_element_placement, FillArea, get_tick_label,
|
|
7
|
-
import {
|
|
6
|
+
import { AxisLabel, ColorBar, compute_element_placement, FillArea, get_tick_label, Line, PlotLegend, PlotTooltip, ReferenceLine, ScatterPlotControls, ScatterPoint, ZeroLines, ZoomRect, } from './';
|
|
7
|
+
import { create_axis_change_handler } from './axis-utils';
|
|
8
8
|
import { get_series_color, get_series_symbol, process_prop, } from './data-transform';
|
|
9
9
|
import { AXIS_DEFAULTS } from './defaults';
|
|
10
|
-
import { untrack } from 'svelte';
|
|
11
10
|
import { create_dimension_tracker, create_hover_lock, } from './hover-lock.svelte';
|
|
12
|
-
import { DEFAULT_GRID_STYLE, DEFAULT_MARKERS, get_scale_type_name, } from './types';
|
|
11
|
+
import { DEFAULT_GRID_STYLE, DEFAULT_MARKERS, get_scale_type_name, is_time_scale, } from './types';
|
|
13
12
|
import { compute_label_positions } from './utils/label-placement';
|
|
14
13
|
import { handle_legend_double_click, toggle_group_visibility, toggle_series_visibility, } from './utils/series-visibility';
|
|
15
14
|
import { DEFAULTS } from '../settings';
|
|
16
15
|
import { extent } from 'd3-array';
|
|
17
16
|
import { scaleTime } from 'd3-scale';
|
|
17
|
+
import { untrack } from 'svelte';
|
|
18
18
|
import { Tween } from 'svelte/motion';
|
|
19
19
|
import { SvelteSet } from 'svelte/reactivity';
|
|
20
20
|
import { apply_range_constraints, apply_where_condition, clamp_for_log_scale, convert_error_band_to_fill_region, generate_fill_path, is_fill_gradient, resolve_boundary, } from './fill-utils';
|
|
21
21
|
import { expand_range_if_needed, get_relative_coords, normalize_y2_sync, pan_range, PINCH_ZOOM_THRESHOLD, pixels_to_data_delta, sync_y2_range, } from './interactions';
|
|
22
|
-
import { calc_auto_padding, constrain_tooltip_position, filter_padding, } from './layout';
|
|
22
|
+
import { calc_auto_padding, constrain_tooltip_position, filter_padding, LABEL_GAP_DEFAULT, measure_max_tick_width, } from './layout';
|
|
23
23
|
import { group_ref_lines_by_z, index_ref_lines } from './reference-line';
|
|
24
24
|
import { create_color_scale, create_scale, create_size_scale, generate_ticks, get_nice_data_range, } from './scales';
|
|
25
|
-
let { series = $bindable([]), x_axis = $bindable({}), y_axis = $bindable({}), y2_axis = $bindable({}), display = $bindable(DEFAULTS.scatter.display), styles: styles_init = {}, controls: controls_init = {}, padding = {}, range_padding = 0.05, current_x_value = null, tooltip_point = $bindable(null), selected_point = null, hovered = $bindable(false), tooltip, user_content, change = () => { }, color_scale = {
|
|
25
|
+
let { series = $bindable([]), x_axis = $bindable({}), x2_axis = $bindable({}), y_axis = $bindable({}), y2_axis = $bindable({}), display = $bindable(DEFAULTS.scatter.display), styles: styles_init = {}, controls: controls_init = {}, padding = {}, range_padding = 0.05, current_x_value = null, tooltip_point = $bindable(null), selected_point = null, hovered = $bindable(false), tooltip, user_content, change = () => { }, color_scale = {
|
|
26
26
|
type: `linear`,
|
|
27
27
|
scheme: `interpolateViridis`,
|
|
28
28
|
value_range: undefined,
|
|
@@ -34,7 +34,15 @@ const final_x_axis = $derived({
|
|
|
34
34
|
...(x_axis ?? {}),
|
|
35
35
|
});
|
|
36
36
|
const final_y_axis = $derived({ ...AXIS_DEFAULTS, ...(y_axis ?? {}) });
|
|
37
|
+
const final_x2_axis = $derived({
|
|
38
|
+
...AXIS_DEFAULTS,
|
|
39
|
+
label_shift: { x: 0, y: 40 }, // x2-axis label above top edge
|
|
40
|
+
...(x2_axis ?? {}),
|
|
41
|
+
});
|
|
37
42
|
const final_y2_axis = $derived({ ...AXIS_DEFAULTS, ...(y2_axis ?? {}) });
|
|
43
|
+
// Cache time-axis check — used in ~10 places for scale/tick/tooltip logic
|
|
44
|
+
let is_time_x = $derived(is_time_scale(final_x_axis.scale_type, final_x_axis.format));
|
|
45
|
+
let is_time_x2 = $derived(is_time_scale(final_x2_axis.scale_type, final_x2_axis.format));
|
|
38
46
|
const final_display = $derived({ ...DEFAULTS.scatter.display, ...(display ?? {}) });
|
|
39
47
|
// Local state for styles (initialized from prop, owned by this component for controls)
|
|
40
48
|
// Using $state because styles has bindings in ScatterPlotControls
|
|
@@ -68,9 +76,11 @@ let drag_start_coords = $state(null);
|
|
|
68
76
|
let drag_current_coords = $state(null);
|
|
69
77
|
// Zoom/pan state - track both initial (data-driven) and current (after pan/zoom) ranges
|
|
70
78
|
let initial_x_range = $state([0, 1]);
|
|
79
|
+
let initial_x2_range = $state([0, 1]);
|
|
71
80
|
let initial_y_range = $state([0, 1]);
|
|
72
81
|
let initial_y2_range = $state([0, 1]);
|
|
73
82
|
let zoom_x_range = $state([0, 1]);
|
|
83
|
+
let zoom_x2_range = $state([0, 1]);
|
|
74
84
|
let zoom_y_range = $state([0, 1]);
|
|
75
85
|
let zoom_y2_range = $state([0, 1]);
|
|
76
86
|
let previous_series_visibility = $state(null);
|
|
@@ -135,10 +145,11 @@ let points_by_axis = $derived.by(() => {
|
|
|
135
145
|
const all = [];
|
|
136
146
|
const y1 = [];
|
|
137
147
|
const y2 = [];
|
|
148
|
+
const x2 = [];
|
|
138
149
|
for (const srs of series_with_ids) {
|
|
139
150
|
if (!srs)
|
|
140
151
|
continue;
|
|
141
|
-
const { x: xs, y: ys, visible = true, y_axis = `y1` } = srs;
|
|
152
|
+
const { x: xs, y: ys, visible = true, y_axis = `y1`, x_axis: x_ax = `x1` } = srs;
|
|
142
153
|
for (let idx = 0; idx < xs.length; idx++) {
|
|
143
154
|
const point = { x: xs[idx], y: ys[idx] };
|
|
144
155
|
all.push(point);
|
|
@@ -147,28 +158,36 @@ let points_by_axis = $derived.by(() => {
|
|
|
147
158
|
y2.push(point);
|
|
148
159
|
else
|
|
149
160
|
y1.push(point);
|
|
161
|
+
if (x_ax === `x2`)
|
|
162
|
+
x2.push(point);
|
|
150
163
|
}
|
|
151
164
|
}
|
|
152
165
|
}
|
|
153
|
-
return { all, y1, y2 };
|
|
166
|
+
return { all, y1, y2, x2 };
|
|
154
167
|
});
|
|
155
168
|
let all_points = $derived(points_by_axis.all);
|
|
156
169
|
let y1_points = $derived(points_by_axis.y1);
|
|
157
170
|
let y2_points = $derived(points_by_axis.y2);
|
|
171
|
+
let x2_points = $derived(points_by_axis.x2);
|
|
158
172
|
// Layout: dynamic padding based on tick label widths
|
|
159
173
|
const default_padding = { t: 5, b: 50, l: 50, r: 20 };
|
|
160
|
-
let pad = $
|
|
174
|
+
let pad = $state(untrack(() => filter_padding(padding, default_padding)));
|
|
161
175
|
// Update padding when format or ticks change
|
|
162
176
|
$effect(() => {
|
|
163
|
-
const new_pad = width && height &&
|
|
177
|
+
const new_pad = width && height &&
|
|
178
|
+
(y_tick_values.length || y2_tick_values.length || x2_tick_values.length)
|
|
164
179
|
? calc_auto_padding({
|
|
165
180
|
padding,
|
|
166
181
|
default_padding,
|
|
182
|
+
x2_axis: { ...final_x2_axis, tick_values: x2_tick_values },
|
|
167
183
|
y_axis: { ...final_y_axis, tick_values: y_tick_values },
|
|
168
184
|
y2_axis: { ...final_y2_axis, tick_values: y2_tick_values },
|
|
169
185
|
})
|
|
170
186
|
: filter_padding(padding, default_padding);
|
|
171
|
-
if (
|
|
187
|
+
if (pad.t !== new_pad.t ||
|
|
188
|
+
pad.b !== new_pad.b ||
|
|
189
|
+
pad.l !== new_pad.l ||
|
|
190
|
+
pad.r !== new_pad.r)
|
|
172
191
|
pad = new_pad;
|
|
173
192
|
});
|
|
174
193
|
// Reactive clip area dimensions to ensure proper responsiveness
|
|
@@ -206,9 +225,10 @@ let series_value_arrays = $derived.by(() => {
|
|
|
206
225
|
});
|
|
207
226
|
let all_color_values = $derived(series_value_arrays.color_values);
|
|
208
227
|
// Compute auto ranges based on data and limits
|
|
209
|
-
let auto_x_range = $derived(get_nice_data_range(all_points, ({ x }) => x,
|
|
210
|
-
let auto_y_range = $derived(get_nice_data_range(y1_points, ({ y }) => y,
|
|
211
|
-
let
|
|
228
|
+
let auto_x_range = $derived(get_nice_data_range(all_points, ({ x }) => x, final_x_axis.range ?? [null, null], final_x_axis.scale_type ?? `linear`, range_padding, is_time_x));
|
|
229
|
+
let auto_y_range = $derived(get_nice_data_range(y1_points, ({ y }) => y, final_y_axis.range ?? [null, null], final_y_axis.scale_type ?? `linear`, range_padding, false));
|
|
230
|
+
let auto_x2_range = $derived(get_nice_data_range(x2_points, ({ x }) => x, final_x2_axis.range ?? [null, null], final_x2_axis.scale_type ?? `linear`, range_padding, is_time_x2));
|
|
231
|
+
let auto_y2_range = $derived(get_nice_data_range(y2_points, ({ y }) => y, final_y2_axis.range ?? [null, null], final_y2_axis.scale_type ?? `linear`, range_padding, false));
|
|
212
232
|
// Update zoom ranges when auto ranges or explicit ranges change
|
|
213
233
|
// - Explicit ranges (from zoom/pan): apply directly
|
|
214
234
|
// - Auto ranges (from data changes): use lazy expansion to preserve view context
|
|
@@ -222,6 +242,7 @@ $effect(() => {
|
|
|
222
242
|
};
|
|
223
243
|
};
|
|
224
244
|
const x = get_range(final_x_axis, auto_x_range);
|
|
245
|
+
const x2 = get_range(final_x2_axis, auto_x2_range);
|
|
225
246
|
const y = get_range(final_y_axis, auto_y_range);
|
|
226
247
|
const y2 = get_range(final_y2_axis, auto_y2_range);
|
|
227
248
|
// X axis: explicit → direct, auto → lazy expand
|
|
@@ -235,6 +256,17 @@ $effect(() => {
|
|
|
235
256
|
[initial_x_range, zoom_x_range] = [result.range, result.range];
|
|
236
257
|
}
|
|
237
258
|
}
|
|
259
|
+
// X2 axis: explicit → direct, auto → lazy expand
|
|
260
|
+
if (x2.explicit) {
|
|
261
|
+
zoom_x2_range = x2.range;
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
const result = expand_range_if_needed(initial_x2_range, x2.range);
|
|
265
|
+
if (result.changed) {
|
|
266
|
+
;
|
|
267
|
+
[initial_x2_range, zoom_x2_range] = [result.range, result.range];
|
|
268
|
+
}
|
|
269
|
+
}
|
|
238
270
|
// Y axis: explicit → direct, auto → lazy expand
|
|
239
271
|
if (y.explicit) {
|
|
240
272
|
zoom_y_range = y.range;
|
|
@@ -264,6 +296,7 @@ $effect(() => {
|
|
|
264
296
|
}
|
|
265
297
|
});
|
|
266
298
|
let [x_min, x_max] = $derived(zoom_x_range);
|
|
299
|
+
let [x2_min, x2_max] = $derived(zoom_x2_range);
|
|
267
300
|
let [y_min, y_max] = $derived(zoom_y_range);
|
|
268
301
|
let [y2_min, y2_max] = $derived(zoom_y2_range);
|
|
269
302
|
// Create auto color range
|
|
@@ -274,7 +307,7 @@ all_color_values.length > 0
|
|
|
274
307
|
: [0, 1]);
|
|
275
308
|
// Create scale functions
|
|
276
309
|
// For time scales, use scaleTime directly; otherwise use create_scale (supports linear/log/arcsinh)
|
|
277
|
-
let x_scale_fn = $derived(
|
|
310
|
+
let x_scale_fn = $derived(is_time_x
|
|
278
311
|
? scaleTime()
|
|
279
312
|
.domain([new Date(x_min), new Date(x_max)])
|
|
280
313
|
.range([pad.l, width - pad.r])
|
|
@@ -282,6 +315,14 @@ let x_scale_fn = $derived(final_x_axis.format?.startsWith(`%`)
|
|
|
282
315
|
pad.l,
|
|
283
316
|
width - pad.r,
|
|
284
317
|
]));
|
|
318
|
+
let x2_scale_fn = $derived(is_time_x2
|
|
319
|
+
? scaleTime()
|
|
320
|
+
.domain([new Date(x2_min), new Date(x2_max)])
|
|
321
|
+
.range([pad.l, width - pad.r])
|
|
322
|
+
: create_scale(final_x2_axis.scale_type ?? `linear`, [x2_min, x2_max], [
|
|
323
|
+
pad.l,
|
|
324
|
+
width - pad.r,
|
|
325
|
+
]));
|
|
285
326
|
let y_scale_fn = $derived(create_scale(final_y_axis.scale_type ?? `linear`, [y_min, y_max], [
|
|
286
327
|
height - pad.b,
|
|
287
328
|
pad.t,
|
|
@@ -334,14 +375,18 @@ let filtered_series = $derived(series_with_ids
|
|
|
334
375
|
point_idx,
|
|
335
376
|
size_value: size_values?.[point_idx],
|
|
336
377
|
}));
|
|
337
|
-
// Filter to points within the plot bounds
|
|
338
|
-
const
|
|
339
|
-
|
|
378
|
+
// Filter to points within the plot bounds (handles inverted ranges like [3.5, 1.4])
|
|
379
|
+
const in_range = (val, lo, hi) => val != null && !isNaN(val) && val >= Math.min(lo, hi) &&
|
|
380
|
+
val <= Math.max(lo, hi);
|
|
381
|
+
// Determine which ranges to use based on series axis properties
|
|
382
|
+
const [series_x_min, series_x_max] = (data_series.x_axis ?? `x1`) === `x2`
|
|
383
|
+
? [x2_min, x2_max]
|
|
384
|
+
: [x_min, x_max];
|
|
340
385
|
const [series_y_min, series_y_max] = (data_series.y_axis ?? `y1`) === `y2`
|
|
341
386
|
? [y2_min, y2_max]
|
|
342
387
|
: [y_min, y_max];
|
|
343
|
-
const filtered_data_with_extras = processed_points.filter(({ x, y }) =>
|
|
344
|
-
|
|
388
|
+
const filtered_data_with_extras = processed_points.filter(({ x, y }) => in_range(x, series_x_min, series_x_max) &&
|
|
389
|
+
in_range(y, series_y_min, series_y_max));
|
|
345
390
|
// Return structure consistent with DataSeries but acknowledge internal data structure (filtered_data)
|
|
346
391
|
return {
|
|
347
392
|
...data_series,
|
|
@@ -360,10 +405,13 @@ let plot_points_for_placement = $derived.by(() => {
|
|
|
360
405
|
for (const series_data of filtered_series) {
|
|
361
406
|
if (!series_data?.filtered_data)
|
|
362
407
|
continue;
|
|
408
|
+
const use_x2_scale = series_data.x_axis === `x2`;
|
|
363
409
|
for (const point of series_data.filtered_data) {
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
410
|
+
const active_x_scale = use_x2_scale ? x2_scale_fn : x_scale_fn;
|
|
411
|
+
const active_is_time_x = use_x2_scale ? is_time_x2 : is_time_x;
|
|
412
|
+
const point_x_coord = active_is_time_x
|
|
413
|
+
? active_x_scale(new Date(point.x))
|
|
414
|
+
: active_x_scale(point.x);
|
|
367
415
|
const point_y_coord = (series_data.y_axis === `y2` ? y2_scale_fn : y_scale_fn)(point.y);
|
|
368
416
|
if (isFinite(point_x_coord) && isFinite(point_y_coord)) {
|
|
369
417
|
points.push({ x: point_x_coord, y: point_y_coord });
|
|
@@ -704,14 +752,20 @@ $effect(() => {
|
|
|
704
752
|
// Generate axis ticks - consolidated into single derived for efficiency
|
|
705
753
|
let axis_ticks = $derived.by(() => {
|
|
706
754
|
if (!width || !height)
|
|
707
|
-
return { x: [], y: [], y2: [] };
|
|
755
|
+
return { x: [], x2: [], y: [], y2: [] };
|
|
708
756
|
// X-axis ticks: choose appropriate scale for tick generation
|
|
709
757
|
// Time scales (format starts with %) use scaleTime for better tick placement
|
|
710
|
-
const x_scale_for_ticks =
|
|
758
|
+
const x_scale_for_ticks = is_time_x
|
|
711
759
|
? scaleTime().domain([new Date(x_min), new Date(x_max)])
|
|
712
760
|
: create_scale(final_x_axis.scale_type ?? `linear`, [x_min, x_max], [0, 1]);
|
|
761
|
+
const x2_scale_for_ticks = is_time_x2
|
|
762
|
+
? scaleTime().domain([new Date(x2_min), new Date(x2_max)])
|
|
763
|
+
: create_scale(final_x2_axis.scale_type ?? `linear`, [x2_min, x2_max], [0, 1]);
|
|
713
764
|
return {
|
|
714
765
|
x: generate_ticks([x_min, x_max], final_x_axis.scale_type ?? `linear`, final_x_axis.ticks, x_scale_for_ticks, { format: final_x_axis.format }),
|
|
766
|
+
x2: x2_points.length > 0
|
|
767
|
+
? generate_ticks([x2_min, x2_max], final_x2_axis.scale_type ?? `linear`, final_x2_axis.ticks, x2_scale_for_ticks, { format: final_x2_axis.format })
|
|
768
|
+
: [],
|
|
715
769
|
y: generate_ticks([y_min, y_max], final_y_axis.scale_type ?? `linear`, final_y_axis.ticks, y_scale_fn, { default_count: 5 }),
|
|
716
770
|
y2: y2_points.length > 0
|
|
717
771
|
? generate_ticks([y2_min, y2_max], final_y2_axis.scale_type ?? `linear`, final_y2_axis.ticks, y2_scale_fn, { default_count: 5 })
|
|
@@ -719,8 +773,16 @@ let axis_ticks = $derived.by(() => {
|
|
|
719
773
|
};
|
|
720
774
|
});
|
|
721
775
|
let x_tick_values = $derived(axis_ticks.x);
|
|
776
|
+
let x2_tick_values = $derived(axis_ticks.x2);
|
|
722
777
|
let y_tick_values = $derived(axis_ticks.y);
|
|
723
778
|
let y2_tick_values = $derived(axis_ticks.y2);
|
|
779
|
+
// Cache measured tick-label widths so expensive text measurement only runs
|
|
780
|
+
// when tick values/format change, not on every template rerender.
|
|
781
|
+
let tick_label_widths = $derived({
|
|
782
|
+
x2_max: measure_max_tick_width(x2_tick_values, final_x2_axis.format ?? ``),
|
|
783
|
+
y_max: measure_max_tick_width(y_tick_values, final_y_axis.format ?? ``),
|
|
784
|
+
y2_max: measure_max_tick_width(y2_tick_values, final_y2_axis.format ?? ``),
|
|
785
|
+
});
|
|
724
786
|
// Define global handlers reference for adding/removing listeners
|
|
725
787
|
const on_window_mouse_move = (evt) => {
|
|
726
788
|
if (!drag_start_coords || !svg_bounding_box)
|
|
@@ -786,6 +848,21 @@ const on_window_mouse_up = (_evt) => {
|
|
|
786
848
|
// Y2 sync is handled by the effect that reacts to y_axis changes
|
|
787
849
|
x_axis = { ...x_axis, range: next_x_range };
|
|
788
850
|
y_axis = { ...y_axis, range: next_y_range };
|
|
851
|
+
// X2 axis: invert screen coords using x2 scale
|
|
852
|
+
if (x2_points.length > 0) {
|
|
853
|
+
const start_x2_val = x2_scale_fn.invert(drag_start_coords.x);
|
|
854
|
+
const end_x2_val = x2_scale_fn.invert(drag_current_coords.x);
|
|
855
|
+
const x2_a = start_x2_val instanceof Date
|
|
856
|
+
? start_x2_val.getTime()
|
|
857
|
+
: start_x2_val;
|
|
858
|
+
const x2_b = end_x2_val instanceof Date
|
|
859
|
+
? end_x2_val.getTime()
|
|
860
|
+
: end_x2_val;
|
|
861
|
+
x2_axis = {
|
|
862
|
+
...x2_axis,
|
|
863
|
+
range: [Math.min(x2_a, x2_b), Math.max(x2_a, x2_b)],
|
|
864
|
+
};
|
|
865
|
+
}
|
|
789
866
|
}
|
|
790
867
|
}
|
|
791
868
|
// Reset states and remove listeners
|
|
@@ -808,9 +885,11 @@ const on_pan_move = (evt) => {
|
|
|
808
885
|
const plot_height = Math.max(1, height - pad.t - pad.b);
|
|
809
886
|
const sensitivity = pan?.drag_sensitivity ?? 1;
|
|
810
887
|
const x_delta = pixels_to_data_delta(-dx * sensitivity, pan_drag_state.initial_x_range, plot_width);
|
|
888
|
+
const x2_delta = pixels_to_data_delta(-dx * sensitivity, pan_drag_state.initial_x2_range, plot_width);
|
|
811
889
|
const y_delta = pixels_to_data_delta(dy * sensitivity, pan_drag_state.initial_y_range, plot_height);
|
|
812
890
|
const y2_delta = pixels_to_data_delta(dy * sensitivity, pan_drag_state.initial_y2_range, plot_height);
|
|
813
891
|
zoom_x_range = pan_range(pan_drag_state.initial_x_range, x_delta);
|
|
892
|
+
zoom_x2_range = pan_range(pan_drag_state.initial_x2_range, x2_delta);
|
|
814
893
|
zoom_y_range = pan_range(pan_drag_state.initial_y_range, y_delta);
|
|
815
894
|
zoom_y2_range = get_synced_y2(zoom_y_range, pan_range(pan_drag_state.initial_y2_range, y2_delta));
|
|
816
895
|
};
|
|
@@ -830,6 +909,7 @@ function handle_mouse_down(evt) {
|
|
|
830
909
|
pan_drag_state = {
|
|
831
910
|
start: { x: evt.clientX, y: evt.clientY },
|
|
832
911
|
initial_x_range: [...zoom_x_range],
|
|
912
|
+
initial_x2_range: [...zoom_x2_range],
|
|
833
913
|
initial_y_range: [...zoom_y_range],
|
|
834
914
|
initial_y2_range: [...zoom_y2_range],
|
|
835
915
|
};
|
|
@@ -866,10 +946,12 @@ function handle_wheel(evt) {
|
|
|
866
946
|
// Determine pan direction based on wheel delta
|
|
867
947
|
// deltaX for horizontal scroll (trackpad), deltaY for vertical
|
|
868
948
|
const x_delta = pixels_to_data_delta(evt.deltaX * sensitivity, zoom_x_range, plot_width);
|
|
949
|
+
const x2_delta = pixels_to_data_delta(evt.deltaX * sensitivity, zoom_x2_range, plot_width);
|
|
869
950
|
const y_delta = pixels_to_data_delta(evt.deltaY * sensitivity, zoom_y_range, plot_height);
|
|
870
951
|
const y2_delta = pixels_to_data_delta(evt.deltaY * sensitivity, zoom_y2_range, plot_height);
|
|
871
952
|
if (Math.abs(evt.deltaX) > Math.abs(evt.deltaY)) {
|
|
872
953
|
zoom_x_range = pan_range(zoom_x_range, x_delta);
|
|
954
|
+
zoom_x2_range = pan_range(zoom_x2_range, x2_delta);
|
|
873
955
|
}
|
|
874
956
|
else {
|
|
875
957
|
zoom_y_range = pan_range(zoom_y_range, y_delta);
|
|
@@ -886,6 +968,7 @@ function handle_touch_start(evt) {
|
|
|
886
968
|
touch_state = {
|
|
887
969
|
start_touches: touches.map((touch) => ({ x: touch.clientX, y: touch.clientY })),
|
|
888
970
|
initial_x_range: [...zoom_x_range],
|
|
971
|
+
initial_x2_range: [...zoom_x2_range],
|
|
889
972
|
initial_y_range: [...zoom_y_range],
|
|
890
973
|
initial_y2_range: [...zoom_y2_range],
|
|
891
974
|
};
|
|
@@ -920,13 +1003,20 @@ function handle_touch_move(evt) {
|
|
|
920
1003
|
// Pinch zoom centered on gesture center
|
|
921
1004
|
// Divide by scale so spread (scale > 1) = smaller span (zoom in)
|
|
922
1005
|
const x_span = touch_state.initial_x_range[1] - touch_state.initial_x_range[0];
|
|
1006
|
+
const x2_span = touch_state.initial_x2_range[1] -
|
|
1007
|
+
touch_state.initial_x2_range[0];
|
|
923
1008
|
const y_span = touch_state.initial_y_range[1] - touch_state.initial_y_range[0];
|
|
924
1009
|
const y2_span = touch_state.initial_y2_range[1] -
|
|
925
1010
|
touch_state.initial_y2_range[0];
|
|
926
1011
|
const x_center = (touch_state.initial_x_range[0] + touch_state.initial_x_range[1]) / 2;
|
|
1012
|
+
const x2_center = (touch_state.initial_x2_range[0] + touch_state.initial_x2_range[1]) / 2;
|
|
927
1013
|
const y_center = (touch_state.initial_y_range[0] + touch_state.initial_y_range[1]) / 2;
|
|
928
1014
|
const y2_center = (touch_state.initial_y2_range[0] + touch_state.initial_y2_range[1]) / 2;
|
|
929
1015
|
zoom_x_range = [x_center - x_span / scale / 2, x_center + x_span / scale / 2];
|
|
1016
|
+
zoom_x2_range = [
|
|
1017
|
+
x2_center - x2_span / scale / 2,
|
|
1018
|
+
x2_center + x2_span / scale / 2,
|
|
1019
|
+
];
|
|
930
1020
|
zoom_y_range = [y_center - y_span / scale / 2, y_center + y_span / scale / 2];
|
|
931
1021
|
zoom_y2_range = get_synced_y2(zoom_y_range, [
|
|
932
1022
|
y2_center - y2_span / scale / 2,
|
|
@@ -936,9 +1026,11 @@ function handle_touch_move(evt) {
|
|
|
936
1026
|
else {
|
|
937
1027
|
// Pan
|
|
938
1028
|
const x_delta = pixels_to_data_delta(-dx, touch_state.initial_x_range, plot_width);
|
|
1029
|
+
const x2_delta = pixels_to_data_delta(-dx, touch_state.initial_x2_range, plot_width);
|
|
939
1030
|
const y_delta = pixels_to_data_delta(dy, touch_state.initial_y_range, plot_height);
|
|
940
1031
|
const y2_delta = pixels_to_data_delta(dy, touch_state.initial_y2_range, plot_height);
|
|
941
1032
|
zoom_x_range = pan_range(touch_state.initial_x_range, x_delta);
|
|
1033
|
+
zoom_x2_range = pan_range(touch_state.initial_x2_range, x2_delta);
|
|
942
1034
|
zoom_y_range = pan_range(touch_state.initial_y_range, y_delta);
|
|
943
1035
|
zoom_y2_range = get_synced_y2(zoom_y_range, pan_range(touch_state.initial_y2_range, y2_delta));
|
|
944
1036
|
}
|
|
@@ -959,11 +1051,14 @@ function update_tooltip_point(x_rel, y_rel, evt) {
|
|
|
959
1051
|
for (const series_data of filtered_series) {
|
|
960
1052
|
if (!series_data?.filtered_data)
|
|
961
1053
|
continue;
|
|
1054
|
+
const tooltip_use_x2 = series_data.x_axis === `x2`;
|
|
1055
|
+
const tooltip_x_scale = tooltip_use_x2 ? x2_scale_fn : x_scale_fn;
|
|
1056
|
+
const tooltip_is_time_x = tooltip_use_x2 ? is_time_x2 : is_time_x;
|
|
962
1057
|
for (const point of series_data.filtered_data) {
|
|
963
1058
|
// Calculate screen coordinates of the point
|
|
964
|
-
const point_cx =
|
|
965
|
-
?
|
|
966
|
-
:
|
|
1059
|
+
const point_cx = tooltip_is_time_x
|
|
1060
|
+
? tooltip_x_scale(new Date(point.x))
|
|
1061
|
+
: tooltip_x_scale(point.x);
|
|
967
1062
|
const point_cy = (series_data.y_axis === `y2` ? y2_scale_fn : y_scale_fn)(point.y);
|
|
968
1063
|
// Calculate squared screen distance between mouse and point
|
|
969
1064
|
const screen_dx = x_rel - point_cx;
|
|
@@ -1031,6 +1126,8 @@ function handle_legend_drag_start(event) {
|
|
|
1031
1126
|
legend_is_dragging = true;
|
|
1032
1127
|
// Get the actual rendered position of the legend element (accounts for transforms)
|
|
1033
1128
|
const legend_el = event.currentTarget;
|
|
1129
|
+
if (!(legend_el instanceof HTMLElement))
|
|
1130
|
+
return;
|
|
1034
1131
|
const legend_rect = legend_el.getBoundingClientRect();
|
|
1035
1132
|
// Calculate offset from mouse to legend's actual rendered position relative to SVG
|
|
1036
1133
|
const [x, y] = [event.clientX - legend_rect.left, event.clientY - legend_rect.top];
|
|
@@ -1053,9 +1150,12 @@ function handle_legend_drag(event) {
|
|
|
1053
1150
|
}
|
|
1054
1151
|
function get_screen_coords(point, series) {
|
|
1055
1152
|
// convert data coordinates to potentially non-finite screen coordinates
|
|
1056
|
-
const
|
|
1057
|
-
|
|
1058
|
-
|
|
1153
|
+
const use_x2 = series?.x_axis === `x2`;
|
|
1154
|
+
const active_x_scale = use_x2 ? x2_scale_fn : x_scale_fn;
|
|
1155
|
+
const active_is_time_x = use_x2 ? is_time_x2 : is_time_x;
|
|
1156
|
+
const screen_x = active_is_time_x
|
|
1157
|
+
? active_x_scale(new Date(point.x))
|
|
1158
|
+
: active_x_scale(point.x);
|
|
1059
1159
|
const y_val = point.y;
|
|
1060
1160
|
// Determine which y-scale to use based on series y_axis property
|
|
1061
1161
|
const use_y2 = series?.y_axis === `y2`;
|
|
@@ -1075,17 +1175,23 @@ function construct_handler_props(point) {
|
|
|
1075
1175
|
if (!hovered_series)
|
|
1076
1176
|
return null;
|
|
1077
1177
|
const { x, y, color_value, metadata, series_idx } = point;
|
|
1078
|
-
const
|
|
1079
|
-
|
|
1080
|
-
|
|
1178
|
+
const handler_use_x2 = hovered_series.x_axis === `x2`;
|
|
1179
|
+
const handler_x_scale = handler_use_x2 ? x2_scale_fn : x_scale_fn;
|
|
1180
|
+
const handler_is_time_x = handler_use_x2 ? is_time_x2 : is_time_x;
|
|
1181
|
+
const cx = handler_is_time_x ? handler_x_scale(new Date(x)) : handler_x_scale(x);
|
|
1081
1182
|
const cy = (hovered_series.y_axis === `y2` ? y2_scale_fn : y_scale_fn)(y);
|
|
1183
|
+
const active_x_config = handler_use_x2 ? final_x2_axis : final_x_axis;
|
|
1184
|
+
const active_y_config = hovered_series.y_axis === `y2`
|
|
1185
|
+
? final_y2_axis
|
|
1186
|
+
: final_y_axis;
|
|
1082
1187
|
const coords = {
|
|
1083
1188
|
x,
|
|
1084
1189
|
y,
|
|
1085
1190
|
cx,
|
|
1086
1191
|
cy,
|
|
1087
|
-
x_axis:
|
|
1088
|
-
|
|
1192
|
+
x_axis: active_x_config,
|
|
1193
|
+
x2_axis: final_x2_axis,
|
|
1194
|
+
y_axis: active_y_config,
|
|
1089
1195
|
y2_axis: final_y2_axis,
|
|
1090
1196
|
};
|
|
1091
1197
|
return {
|
|
@@ -1094,10 +1200,8 @@ function construct_handler_props(point) {
|
|
|
1094
1200
|
metadata,
|
|
1095
1201
|
label: hovered_series.label ?? null,
|
|
1096
1202
|
series_idx,
|
|
1097
|
-
x_formatted: format_value(x,
|
|
1098
|
-
y_formatted: format_value(y,
|
|
1099
|
-
? final_y2_axis.format
|
|
1100
|
-
: final_y_axis.format) || `.3~s`),
|
|
1203
|
+
x_formatted: format_value(x, active_x_config.format || `.3~s`),
|
|
1204
|
+
y_formatted: format_value(y, active_y_config.format || `.3~s`),
|
|
1101
1205
|
color_value: color_value ?? null,
|
|
1102
1206
|
colorbar: {
|
|
1103
1207
|
value: color_value ?? null,
|
|
@@ -1115,17 +1219,32 @@ let handler_props = $derived.by(() => {
|
|
|
1115
1219
|
});
|
|
1116
1220
|
let using_controls = $derived(controls.show);
|
|
1117
1221
|
let has_multiple_series = $derived(series_with_ids.filter(Boolean).length > 1);
|
|
1222
|
+
// Precompute non-click event names from point_events so we don't rebuild
|
|
1223
|
+
// the entries array on every point render.
|
|
1224
|
+
let point_event_names = $derived(point_events
|
|
1225
|
+
? Object.keys(point_events).filter((name) => name !== `onclick`)
|
|
1226
|
+
: []);
|
|
1118
1227
|
// Set theme-aware background when entering fullscreen
|
|
1119
1228
|
$effect(() => {
|
|
1120
1229
|
set_fullscreen_bg(wrapper, fullscreen, `--scatter-fullscreen-bg`);
|
|
1121
1230
|
});
|
|
1122
1231
|
// State accessors for shared axis change handler
|
|
1123
1232
|
const axis_state = {
|
|
1124
|
-
get_axis: (axis) =>
|
|
1233
|
+
get_axis: (axis) => {
|
|
1234
|
+
if (axis === `x`)
|
|
1235
|
+
return x_axis;
|
|
1236
|
+
if (axis === `x2`)
|
|
1237
|
+
return x2_axis;
|
|
1238
|
+
if (axis === `y`)
|
|
1239
|
+
return y_axis;
|
|
1240
|
+
return y2_axis;
|
|
1241
|
+
},
|
|
1125
1242
|
set_axis: (axis, config) => {
|
|
1126
1243
|
// Spread into existing state to preserve merged type structure
|
|
1127
1244
|
if (axis === `x`)
|
|
1128
1245
|
x_axis = { ...x_axis, ...config };
|
|
1246
|
+
else if (axis === `x2`)
|
|
1247
|
+
x2_axis = { ...x2_axis, ...config };
|
|
1129
1248
|
else if (axis === `y`)
|
|
1130
1249
|
y_axis = { ...y_axis, ...config };
|
|
1131
1250
|
else
|
|
@@ -1192,11 +1311,12 @@ $effect(() => {
|
|
|
1192
1311
|
<ReferenceLine
|
|
1193
1312
|
ref_line={line}
|
|
1194
1313
|
line_idx={line.idx}
|
|
1195
|
-
{x_min}
|
|
1196
|
-
{x_max}
|
|
1314
|
+
x_min={line.x_axis === `x2` ? x2_min : x_min}
|
|
1315
|
+
x_max={line.x_axis === `x2` ? x2_max : x_max}
|
|
1197
1316
|
y_min={line.y_axis === `y2` ? y2_min : y_min}
|
|
1198
1317
|
y_max={line.y_axis === `y2` ? y2_max : y_max}
|
|
1199
1318
|
x_scale={x_scale_fn}
|
|
1319
|
+
x2_scale={x2_scale_fn}
|
|
1200
1320
|
y_scale={y_scale_fn}
|
|
1201
1321
|
y2_scale={y2_scale_fn}
|
|
1202
1322
|
{clip_path_id}
|
|
@@ -1247,6 +1367,9 @@ $effect(() => {
|
|
|
1247
1367
|
<svg
|
|
1248
1368
|
bind:this={svg_element}
|
|
1249
1369
|
role="application"
|
|
1370
|
+
aria-label={rest[`aria-label`] ??
|
|
1371
|
+
([final_x_axis.label, final_y_axis.label].filter(Boolean).join(` vs `) ||
|
|
1372
|
+
`Scatter plot`)}
|
|
1250
1373
|
tabindex="0"
|
|
1251
1374
|
onfocusin={() => (is_focused = true)}
|
|
1252
1375
|
onfocusout={() => (is_focused = false)}
|
|
@@ -1265,13 +1388,16 @@ $effect(() => {
|
|
|
1265
1388
|
// Reset to current auto ranges (not stale initial_*_range which may have expanded)
|
|
1266
1389
|
// This ensures lazy expansion restarts fresh from current data bounds
|
|
1267
1390
|
initial_x_range = [...auto_x_range] as [number, number]
|
|
1391
|
+
initial_x2_range = [...auto_x2_range] as [number, number]
|
|
1268
1392
|
initial_y_range = [...auto_y_range] as [number, number]
|
|
1269
1393
|
initial_y2_range = [...auto_y2_range] as [number, number]
|
|
1270
1394
|
zoom_x_range = [...auto_x_range] as [number, number]
|
|
1395
|
+
zoom_x2_range = [...auto_x2_range] as [number, number]
|
|
1271
1396
|
zoom_y_range = [...auto_y_range] as [number, number]
|
|
1272
1397
|
zoom_y2_range = get_synced_y2(auto_y_range, [...auto_y2_range] as Vec2)
|
|
1273
1398
|
// Also reset axis props so future data changes recalculate auto ranges
|
|
1274
1399
|
x_axis = { ...x_axis, range: [null, null] }
|
|
1400
|
+
x2_axis = { ...x2_axis, range: [null, null] }
|
|
1275
1401
|
y_axis = { ...y_axis, range: [null, null] }
|
|
1276
1402
|
y2_axis = { ...y2_axis, range: [null, null] }
|
|
1277
1403
|
}}
|
|
@@ -1289,10 +1415,12 @@ $effect(() => {
|
|
|
1289
1415
|
height,
|
|
1290
1416
|
width,
|
|
1291
1417
|
x_scale_fn,
|
|
1418
|
+
x2_scale_fn,
|
|
1292
1419
|
y_scale_fn,
|
|
1293
1420
|
y2_scale_fn,
|
|
1294
1421
|
pad,
|
|
1295
1422
|
x_range: [x_min, x_max],
|
|
1423
|
+
x2_range: [x2_min, x2_max],
|
|
1296
1424
|
y_range: [y_min, y_max],
|
|
1297
1425
|
y2_range: [y2_min, y2_max],
|
|
1298
1426
|
fullscreen,
|
|
@@ -1306,9 +1434,7 @@ $effect(() => {
|
|
|
1306
1434
|
<g class="x-axis">
|
|
1307
1435
|
{#if width > 0 && height > 0}
|
|
1308
1436
|
{#each x_tick_values as tick (tick)}
|
|
1309
|
-
{@const tick_pos_raw =
|
|
1310
|
-
? x_scale_fn(new Date(tick))
|
|
1311
|
-
: x_scale_fn(tick)}
|
|
1437
|
+
{@const tick_pos_raw = is_time_x ? x_scale_fn(new Date(tick)) : x_scale_fn(tick)}
|
|
1312
1438
|
{#if isFinite(tick_pos_raw)}
|
|
1313
1439
|
// Check if tick position is finite
|
|
1314
1440
|
{@const tick_pos = tick_pos_raw}
|
|
@@ -1325,14 +1451,19 @@ $effect(() => {
|
|
|
1325
1451
|
{/if}
|
|
1326
1452
|
<line y1="0" y2={inside ? -5 : 5} stroke="var(--border-color, gray)" />
|
|
1327
1453
|
|
|
1328
|
-
{#if tick >= x_min && tick <= x_max}
|
|
1454
|
+
{#if tick >= Math.min(x_min, x_max) && tick <= Math.max(x_min, x_max)}
|
|
1329
1455
|
{@const base_y = inside ? -8 : 20}
|
|
1330
1456
|
{@const shift = final_x_axis.tick?.label?.shift ?? { x: 0, y: 0 }}
|
|
1331
1457
|
{@const x = shift.x ?? 0}
|
|
1332
1458
|
{@const y = base_y + (shift.y ?? 0)}
|
|
1333
1459
|
{@const custom_label = get_tick_label(tick, final_x_axis.ticks)}
|
|
1334
1460
|
{@const dominant_baseline = inside ? `auto` : `hanging`}
|
|
1335
|
-
<text
|
|
1461
|
+
<text
|
|
1462
|
+
{x}
|
|
1463
|
+
{y}
|
|
1464
|
+
dominant-baseline={dominant_baseline}
|
|
1465
|
+
fill={final_x_axis.color}
|
|
1466
|
+
>
|
|
1336
1467
|
{custom_label ?? format_value(tick, final_x_axis.format ?? ``)}
|
|
1337
1468
|
</text>
|
|
1338
1469
|
{/if}
|
|
@@ -1344,7 +1475,7 @@ $effect(() => {
|
|
|
1344
1475
|
|
|
1345
1476
|
<!-- Current frame indicator -->
|
|
1346
1477
|
{#if current_x_value !== null && current_x_value !== undefined}
|
|
1347
|
-
{@const current_pos_raw =
|
|
1478
|
+
{@const current_pos_raw = is_time_x
|
|
1348
1479
|
? x_scale_fn(new Date(current_x_value))
|
|
1349
1480
|
: x_scale_fn(current_x_value)}
|
|
1350
1481
|
{#if isFinite(current_pos_raw)}
|
|
@@ -1366,28 +1497,18 @@ $effect(() => {
|
|
|
1366
1497
|
{/if}
|
|
1367
1498
|
|
|
1368
1499
|
{#if final_x_axis.label || final_x_axis.options?.length}
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
y={height - pad.b - (
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
options={final_x_axis.options}
|
|
1382
|
-
selected_key={final_x_axis.selected_key}
|
|
1383
|
-
loading={axis_loading === `x`}
|
|
1384
|
-
axis_type="x"
|
|
1385
|
-
color={final_x_axis.color}
|
|
1386
|
-
on_select={(key) => handle_axis_change(`x`, key)}
|
|
1387
|
-
class="axis-label x-label"
|
|
1388
|
-
/>
|
|
1389
|
-
</div>
|
|
1390
|
-
</foreignObject>
|
|
1500
|
+
{@const { label_shift, label = ``, options, selected_key, color } = final_x_axis}
|
|
1501
|
+
<AxisLabel
|
|
1502
|
+
x={width / 2 + (label_shift?.x ?? 0)}
|
|
1503
|
+
y={height - pad.b - (label_shift?.y ?? -40)}
|
|
1504
|
+
{label}
|
|
1505
|
+
{options}
|
|
1506
|
+
{selected_key}
|
|
1507
|
+
loading={axis_loading === `x`}
|
|
1508
|
+
axis_type="x"
|
|
1509
|
+
{color}
|
|
1510
|
+
on_select={(key) => handle_axis_change(`x`, key)}
|
|
1511
|
+
/>
|
|
1391
1512
|
{/if}
|
|
1392
1513
|
</g>
|
|
1393
1514
|
|
|
@@ -1415,7 +1536,7 @@ $effect(() => {
|
|
|
1415
1536
|
stroke="var(--border-color, gray)"
|
|
1416
1537
|
/>
|
|
1417
1538
|
|
|
1418
|
-
{#if tick >= y_min && tick <= y_max}
|
|
1539
|
+
{#if tick >= Math.min(y_min, y_max) && tick <= Math.max(y_min, y_max)}
|
|
1419
1540
|
{@const base_x = inside ? 8 : -8}
|
|
1420
1541
|
{@const shift = final_y_axis.tick?.label?.shift ?? { x: 0, y: 0 }}
|
|
1421
1542
|
{@const x = base_x + (shift.x ?? 0)}
|
|
@@ -1436,31 +1557,26 @@ $effect(() => {
|
|
|
1436
1557
|
{/if}
|
|
1437
1558
|
|
|
1438
1559
|
{#if height > 0 && (final_y_axis.label || final_y_axis.options?.length)}
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
on_select={(key) => handle_axis_change(`y`, key)}
|
|
1460
|
-
class="axis-label y-label"
|
|
1461
|
-
/>
|
|
1462
|
-
</div>
|
|
1463
|
-
</foreignObject>
|
|
1560
|
+
{@const { label_shift, label = ``, options, selected_key, color, tick } =
|
|
1561
|
+
final_y_axis}
|
|
1562
|
+
{@const y_inside = tick?.label?.inside ?? false}
|
|
1563
|
+
{@const y_label_x = Math.max(
|
|
1564
|
+
12,
|
|
1565
|
+
pad.l - (y_inside ? 0 : tick_label_widths.y_max) - LABEL_GAP_DEFAULT,
|
|
1566
|
+
) +
|
|
1567
|
+
(label_shift?.x ?? 0)}
|
|
1568
|
+
<AxisLabel
|
|
1569
|
+
x={y_label_x}
|
|
1570
|
+
y={pad.t + (height - pad.t - pad.b) / 2 + (label_shift?.y ?? 0)}
|
|
1571
|
+
rotate
|
|
1572
|
+
{label}
|
|
1573
|
+
{options}
|
|
1574
|
+
{selected_key}
|
|
1575
|
+
loading={axis_loading === `y`}
|
|
1576
|
+
axis_type="y"
|
|
1577
|
+
{color}
|
|
1578
|
+
on_select={(key) => handle_axis_change(`y`, key)}
|
|
1579
|
+
/>
|
|
1464
1580
|
{/if}
|
|
1465
1581
|
</g>
|
|
1466
1582
|
|
|
@@ -1490,7 +1606,7 @@ $effect(() => {
|
|
|
1490
1606
|
stroke="var(--border-color, gray)"
|
|
1491
1607
|
/>
|
|
1492
1608
|
|
|
1493
|
-
{#if tick >= y2_min && tick <= y2_max}
|
|
1609
|
+
{#if tick >= Math.min(y2_min, y2_max) && tick <= Math.max(y2_min, y2_max)}
|
|
1494
1610
|
{@const base_x = inside ? -8 : 8}
|
|
1495
1611
|
{@const shift = final_y2_axis.tick?.label?.shift ?? { x: 0, y: 0 }}
|
|
1496
1612
|
{@const x = base_x + (shift.x ?? 0)}
|
|
@@ -1511,94 +1627,122 @@ $effect(() => {
|
|
|
1511
1627
|
{/if}
|
|
1512
1628
|
|
|
1513
1629
|
{#if height > 0 && (final_y2_axis.label || final_y2_axis.options?.length)}
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
selected_key={final_y2_axis.selected_key}
|
|
1533
|
-
loading={axis_loading === `y2`}
|
|
1534
|
-
axis_type="y2"
|
|
1535
|
-
color={final_y2_axis.color}
|
|
1536
|
-
on_select={(key) => handle_axis_change(`y2`, key)}
|
|
1537
|
-
class="axis-label y2-label"
|
|
1538
|
-
/>
|
|
1539
|
-
</div>
|
|
1540
|
-
</foreignObject>
|
|
1630
|
+
{@const { label_shift, label = ``, options, selected_key, color, tick } =
|
|
1631
|
+
final_y2_axis}
|
|
1632
|
+
{@const inside = tick?.label?.inside ?? false}
|
|
1633
|
+
{@const tick_shift = inside ? 0 : (tick?.label?.shift?.x ?? 0) + 8}
|
|
1634
|
+
{@const tick_width_contribution = inside ? 0 : tick_label_widths.y2_max}
|
|
1635
|
+
<AxisLabel
|
|
1636
|
+
x={width - pad.r + tick_shift + tick_width_contribution +
|
|
1637
|
+
LABEL_GAP_DEFAULT + (label_shift?.x ?? 0)}
|
|
1638
|
+
y={pad.t + (height - pad.t - pad.b) / 2 + (label_shift?.y ?? 0)}
|
|
1639
|
+
rotate
|
|
1640
|
+
{label}
|
|
1641
|
+
{options}
|
|
1642
|
+
{selected_key}
|
|
1643
|
+
loading={axis_loading === `y2`}
|
|
1644
|
+
axis_type="y2"
|
|
1645
|
+
{color}
|
|
1646
|
+
on_select={(key) => handle_axis_change(`y2`, key)}
|
|
1647
|
+
/>
|
|
1541
1648
|
{/if}
|
|
1542
1649
|
</g>
|
|
1543
1650
|
{/if}
|
|
1544
1651
|
|
|
1545
|
-
<!--
|
|
1652
|
+
<!-- X2-axis (Top) -->
|
|
1653
|
+
{#if x2_points.length > 0}
|
|
1654
|
+
<g class="x2-axis">
|
|
1655
|
+
{#if width > 0 && height > 0}
|
|
1656
|
+
{#each x2_tick_values as tick (tick)}
|
|
1657
|
+
{@const tick_pos_raw = is_time_x2
|
|
1658
|
+
? x2_scale_fn(new Date(tick))
|
|
1659
|
+
: x2_scale_fn(tick)}
|
|
1660
|
+
{#if isFinite(tick_pos_raw)}
|
|
1661
|
+
{@const tick_pos = tick_pos_raw}
|
|
1662
|
+
{#if tick_pos >= pad.l && tick_pos <= width - pad.r}
|
|
1663
|
+
{@const inside = final_x2_axis.tick?.label?.inside ?? false}
|
|
1664
|
+
<g class="tick" transform="translate({tick_pos}, {pad.t})">
|
|
1665
|
+
{#if final_display.x2_grid}
|
|
1666
|
+
<line
|
|
1667
|
+
y1="0"
|
|
1668
|
+
y2={height - pad.b - pad.t}
|
|
1669
|
+
{...DEFAULT_GRID_STYLE}
|
|
1670
|
+
{...(final_x2_axis.grid_style ?? {})}
|
|
1671
|
+
/>
|
|
1672
|
+
{/if}
|
|
1673
|
+
<line
|
|
1674
|
+
y1="0"
|
|
1675
|
+
y2={inside ? 5 : -5}
|
|
1676
|
+
stroke={final_x2_axis.color || `var(--border-color, gray)`}
|
|
1677
|
+
/>
|
|
1546
1678
|
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1679
|
+
{#if tick >= Math.min(x2_min, x2_max) && tick <= Math.max(x2_min, x2_max)}
|
|
1680
|
+
{@const base_y = inside ? 8 : -20}
|
|
1681
|
+
{@const shift = final_x2_axis.tick?.label?.shift ?? { x: 0, y: 0 }}
|
|
1682
|
+
{@const x = shift.x ?? 0}
|
|
1683
|
+
{@const y = base_y + (shift.y ?? 0)}
|
|
1684
|
+
{@const custom_label = get_tick_label(tick, final_x2_axis.ticks)}
|
|
1685
|
+
{@const dominant_baseline = inside ? `hanging` : `auto`}
|
|
1686
|
+
<text
|
|
1687
|
+
{x}
|
|
1688
|
+
{y}
|
|
1689
|
+
dominant-baseline={dominant_baseline}
|
|
1690
|
+
fill={final_x2_axis.color}
|
|
1691
|
+
>
|
|
1692
|
+
{custom_label ?? format_value(tick, final_x2_axis.format ?? ``)}
|
|
1693
|
+
</text>
|
|
1694
|
+
{/if}
|
|
1695
|
+
</g>
|
|
1696
|
+
{/if}
|
|
1697
|
+
{/if}
|
|
1698
|
+
{/each}
|
|
1699
|
+
{/if}
|
|
1557
1700
|
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
get_scale_type_name(final_y_axis.scale_type) !== `log` &&
|
|
1575
|
-
y_min <= 0 && y_max >= 0}
|
|
1576
|
-
{@const zero_y_pos = y_scale_fn(0)}
|
|
1577
|
-
{#if isFinite(zero_y_pos)}
|
|
1578
|
-
<line
|
|
1579
|
-
class="zero-line"
|
|
1580
|
-
x1={pad.l}
|
|
1581
|
-
x2={width - pad.r}
|
|
1582
|
-
y1={zero_y_pos}
|
|
1583
|
-
y2={zero_y_pos}
|
|
1584
|
-
/>
|
|
1585
|
-
{/if}
|
|
1586
|
-
{/if}
|
|
1587
|
-
{#if final_display.y_zero_line && y2_points.length > 0 &&
|
|
1588
|
-
get_scale_type_name(final_y2_axis.scale_type) !== `log` && y2_min <= 0 &&
|
|
1589
|
-
y2_max >= 0}
|
|
1590
|
-
{@const zero_y2_pos = y2_scale_fn(0)}
|
|
1591
|
-
{#if isFinite(zero_y2_pos)}
|
|
1592
|
-
<line
|
|
1593
|
-
class="zero-line"
|
|
1594
|
-
x1={pad.l}
|
|
1595
|
-
x2={width - pad.r}
|
|
1596
|
-
y1={zero_y2_pos}
|
|
1597
|
-
y2={zero_y2_pos}
|
|
1598
|
-
/>
|
|
1599
|
-
{/if}
|
|
1701
|
+
{#if final_x2_axis.label || final_x2_axis.options?.length}
|
|
1702
|
+
{@const { label_shift, label = ``, options, selected_key, color } =
|
|
1703
|
+
final_x2_axis}
|
|
1704
|
+
<AxisLabel
|
|
1705
|
+
x={width / 2 + (label_shift?.x ?? 0)}
|
|
1706
|
+
y={Math.max(12, pad.t - (label_shift?.y ?? 40))}
|
|
1707
|
+
{label}
|
|
1708
|
+
{options}
|
|
1709
|
+
{selected_key}
|
|
1710
|
+
loading={axis_loading === `x2`}
|
|
1711
|
+
axis_type="x2"
|
|
1712
|
+
{color}
|
|
1713
|
+
on_select={(key) => handle_axis_change(`x2`, key)}
|
|
1714
|
+
/>
|
|
1715
|
+
{/if}
|
|
1716
|
+
</g>
|
|
1600
1717
|
{/if}
|
|
1601
1718
|
|
|
1719
|
+
<!-- Tooltip rendered inside overlay (moved outside SVG for stacking above colorbar) -->
|
|
1720
|
+
|
|
1721
|
+
<ZoomRect start={drag_start_coords} current={drag_current_coords} />
|
|
1722
|
+
|
|
1723
|
+
<ZeroLines
|
|
1724
|
+
display={final_display}
|
|
1725
|
+
{x_scale_fn}
|
|
1726
|
+
{x2_scale_fn}
|
|
1727
|
+
{y_scale_fn}
|
|
1728
|
+
{y2_scale_fn}
|
|
1729
|
+
x_range={zoom_x_range}
|
|
1730
|
+
x2_range={zoom_x2_range}
|
|
1731
|
+
y_range={zoom_y_range}
|
|
1732
|
+
y2_range={zoom_y2_range}
|
|
1733
|
+
x_scale_type={final_x_axis.scale_type}
|
|
1734
|
+
x2_scale_type={final_x2_axis.scale_type}
|
|
1735
|
+
y_scale_type={final_y_axis.scale_type}
|
|
1736
|
+
y2_scale_type={final_y2_axis.scale_type}
|
|
1737
|
+
x_is_time={is_time_x}
|
|
1738
|
+
x2_is_time={is_time_x2}
|
|
1739
|
+
has_x2={x2_points.length > 0}
|
|
1740
|
+
has_y2={y2_points.length > 0}
|
|
1741
|
+
{width}
|
|
1742
|
+
{height}
|
|
1743
|
+
{pad}
|
|
1744
|
+
/>
|
|
1745
|
+
|
|
1602
1746
|
<defs>
|
|
1603
1747
|
<clipPath id={clip_path_id}>
|
|
1604
1748
|
<rect
|
|
@@ -1644,9 +1788,7 @@ $effect(() => {
|
|
|
1644
1788
|
<Line
|
|
1645
1789
|
points={finite_screen_points}
|
|
1646
1790
|
origin={[
|
|
1647
|
-
|
|
1648
|
-
? x_scale_fn(new Date(x_min))
|
|
1649
|
-
: x_scale_fn(x_min),
|
|
1791
|
+
is_time_x ? x_scale_fn(new Date(x_min)) : x_scale_fn(x_min),
|
|
1650
1792
|
series_data.y_axis === `y2` ? y2_scale_fn(y2_min) : y_scale_fn(y_min),
|
|
1651
1793
|
]}
|
|
1652
1794
|
line_color={(tc(`line.color`) ? styles.line?.color : null) ?? color_fallback}
|
|
@@ -1685,9 +1827,7 @@ $effect(() => {
|
|
|
1685
1827
|
...label_style,
|
|
1686
1828
|
offset: {
|
|
1687
1829
|
x: calculated_label_pos.x -
|
|
1688
|
-
(
|
|
1689
|
-
? x_scale_fn(new Date(point.x))
|
|
1690
|
-
: x_scale_fn(point.x)),
|
|
1830
|
+
(is_time_x ? x_scale_fn(new Date(point.x)) : x_scale_fn(point.x)),
|
|
1691
1831
|
y: calculated_label_pos.y - (series_data.y_axis === `y2`
|
|
1692
1832
|
? y2_scale_fn(point.y)
|
|
1693
1833
|
: y_scale_fn(point.y)),
|
|
@@ -1744,10 +1884,9 @@ $effect(() => {
|
|
|
1744
1884
|
series_default_color}
|
|
1745
1885
|
{...point_events &&
|
|
1746
1886
|
Object.fromEntries(
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
) => [event_name, (event: Event) => handler({ point, event })]),
|
|
1887
|
+
point_event_names.map((name) => [name, (event: Event) =>
|
|
1888
|
+
point_events?.[name]?.({ point, event })]
|
|
1889
|
+
),
|
|
1751
1890
|
)}
|
|
1752
1891
|
onclick={(event: MouseEvent) => {
|
|
1753
1892
|
// Call user-provided onclick handler first if it exists
|
|
@@ -1830,9 +1969,16 @@ $effect(() => {
|
|
|
1830
1969
|
{#if tooltip}
|
|
1831
1970
|
{@render tooltip(handler_props)}
|
|
1832
1971
|
{:else}
|
|
1833
|
-
{@
|
|
1834
|
-
|
|
1835
|
-
}<br
|
|
1972
|
+
{@const hp = handler_props}
|
|
1973
|
+
{#if has_multiple_series && hp.label}<strong>{hp.label}</strong><br />{/if}
|
|
1974
|
+
{@html point_label?.text ? `${point_label.text}<br />` : ``}
|
|
1975
|
+
{@html hp.x_axis.label || `x`}: {hp.x_formatted}<br />
|
|
1976
|
+
{@html hp.y_axis.label || `y`}: {hp.y_formatted}
|
|
1977
|
+
{#if hp.colorbar?.value != null}
|
|
1978
|
+
<br />{@html hp.colorbar.title || `Color`}: {
|
|
1979
|
+
format_value(hp.colorbar.value, hp.colorbar.tick_format || `.3~g`)
|
|
1980
|
+
}
|
|
1981
|
+
{/if}
|
|
1836
1982
|
{/if}
|
|
1837
1983
|
</PlotTooltip>
|
|
1838
1984
|
{/if}
|
|
@@ -1843,21 +1989,24 @@ $effect(() => {
|
|
|
1843
1989
|
toggle_props={{
|
|
1844
1990
|
...controls.toggle_props,
|
|
1845
1991
|
style:
|
|
1846
|
-
`--ctrl-btn-right: var(--fullscreen-btn-offset,
|
|
1992
|
+
`--ctrl-btn-right: var(--fullscreen-btn-offset, 30px); top: var(--ctrl-btn-top, 5pt); ${
|
|
1847
1993
|
controls.toggle_props?.style ?? ``
|
|
1848
1994
|
}`,
|
|
1849
1995
|
}}
|
|
1850
1996
|
pane_props={controls.pane_props}
|
|
1851
1997
|
bind:x_axis
|
|
1998
|
+
bind:x2_axis
|
|
1852
1999
|
bind:y_axis
|
|
1853
2000
|
bind:y2_axis
|
|
1854
2001
|
bind:display
|
|
1855
2002
|
bind:styles
|
|
1856
2003
|
{auto_x_range}
|
|
2004
|
+
{auto_x2_range}
|
|
1857
2005
|
{auto_y_range}
|
|
1858
2006
|
{auto_y2_range}
|
|
1859
2007
|
bind:selected_series_idx
|
|
1860
2008
|
series={series_with_ids}
|
|
2009
|
+
has_x2_points={x2_points.length > 0}
|
|
1861
2010
|
has_y2_points={y2_points.length > 0}
|
|
1862
2011
|
children={controls_extra}
|
|
1863
2012
|
on_touch={(key) => touched.add(key)}
|
|
@@ -2057,19 +2206,16 @@ $effect(() => {
|
|
|
2057
2206
|
stroke-dasharray: var(--scatter-grid-dash, 4);
|
|
2058
2207
|
stroke-width: var(--scatter-grid-width, 0.4);
|
|
2059
2208
|
}
|
|
2060
|
-
g.x-axis text {
|
|
2209
|
+
g:is(.x-axis, .x2-axis) text {
|
|
2061
2210
|
text-anchor: middle;
|
|
2062
2211
|
dominant-baseline: top;
|
|
2063
2212
|
}
|
|
2064
2213
|
g:is(.y-axis, .y2-axis) text {
|
|
2065
2214
|
dominant-baseline: central;
|
|
2066
2215
|
}
|
|
2067
|
-
g:is(.x-axis, .y-axis, .y2-axis) .tick text {
|
|
2216
|
+
g:is(.x-axis, .x2-axis, .y-axis, .y2-axis) .tick text {
|
|
2068
2217
|
font-size: var(--tick-font-size, 0.8em); /* shrink tick labels */
|
|
2069
2218
|
}
|
|
2070
|
-
foreignobject {
|
|
2071
|
-
overflow: visible;
|
|
2072
|
-
}
|
|
2073
2219
|
.scatter :global(.axis-label) {
|
|
2074
2220
|
text-align: center;
|
|
2075
2221
|
width: 100%;
|
|
@@ -2092,16 +2238,4 @@ $effect(() => {
|
|
|
2092
2238
|
.current-frame-indicator:hover {
|
|
2093
2239
|
opacity: 0.8;
|
|
2094
2240
|
}
|
|
2095
|
-
.zoom-rect {
|
|
2096
|
-
fill: var(--scatter-zoom-rect-fill, rgba(100, 100, 255, 0.2));
|
|
2097
|
-
stroke: var(--scatter-zoom-rect-stroke, rgba(100, 100, 255, 0.8));
|
|
2098
|
-
stroke-width: var(--scatter-zoom-rect-stroke-width, 1);
|
|
2099
|
-
pointer-events: none; /* Prevent rect from interfering with mouse events */
|
|
2100
|
-
}
|
|
2101
|
-
.zero-line {
|
|
2102
|
-
stroke: var(--scatter-zero-line-color, light-dark(black, white));
|
|
2103
|
-
stroke-width: var(--scatter-zero-line-width, 1);
|
|
2104
|
-
stroke-dasharray: none;
|
|
2105
|
-
opacity: var(--scatter-zero-line-opacity, 0.3);
|
|
2106
|
-
}
|
|
2107
2241
|
</style>
|