matterviz 0.3.3 → 0.3.5
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 +1 -1
- package/dist/app.css +7 -0
- package/dist/brillouin/BrillouinZone.svelte +5 -2
- package/dist/brillouin/compute.js +8 -4
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +6 -6
- package/dist/chempot-diagram/async-compute.svelte.js +6 -5
- package/dist/chempot-diagram/chempot-worker.js +2 -2
- package/dist/chempot-diagram/compute.js +16 -16
- package/dist/composition/FormulaFilter.svelte +3 -3
- package/dist/constants.js +2 -8
- package/dist/convex-hull/ConvexHull.svelte +2 -2
- package/dist/convex-hull/ConvexHull2D.svelte +11 -10
- package/dist/convex-hull/ConvexHull3D.svelte +16 -14
- package/dist/convex-hull/ConvexHull4D.svelte +26 -14
- package/dist/convex-hull/ConvexHullControls.svelte +1 -1
- package/dist/convex-hull/ConvexHullInfoPane.svelte +68 -61
- package/dist/convex-hull/ConvexHullStats.svelte +23 -6
- package/dist/convex-hull/GasPressureControls.svelte +3 -3
- package/dist/convex-hull/TemperatureSlider.svelte +1 -1
- package/dist/convex-hull/barycentric-coords.js +2 -2
- package/dist/convex-hull/helpers.js +45 -27
- package/dist/convex-hull/thermodynamics.js +2 -2
- package/dist/element/BohrAtom.svelte +25 -27
- package/dist/element/BohrAtom.svelte.d.ts +2 -2
- package/dist/element/data.d.ts +2 -3
- package/dist/element/data.js +1 -1
- package/dist/fermi-surface/FermiSurface.svelte +5 -2
- package/dist/fermi-surface/compute.js +3 -3
- package/dist/fermi-surface/parse.js +2 -2
- package/dist/fermi-surface/symmetry.js +1 -1
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +8 -8
- package/dist/icons.d.ts +6 -6
- package/dist/icons.js +6 -6
- package/dist/io/decompress.js +12 -7
- package/dist/io/export.js +20 -16
- package/dist/io/is-binary.js +19 -4
- package/dist/isosurface/parse.js +8 -8
- package/dist/isosurface/types.js +9 -9
- package/dist/layout/InfoTag.svelte +1 -1
- package/dist/layout/json-tree/JsonNode.svelte +1 -0
- package/dist/layout/json-tree/utils.js +2 -1
- package/dist/marching-cubes.js +1 -1
- package/dist/math.js +1 -1
- package/dist/overlays/CopyButton.svelte +45 -0
- package/dist/overlays/CopyButton.svelte.d.ts +8 -0
- package/dist/overlays/InfoPaneCards.svelte +149 -0
- package/dist/overlays/InfoPaneCards.svelte.d.ts +22 -0
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +33 -35
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte.d.ts +2 -2
- package/dist/phase-diagram/PhaseDiagramControls.svelte +27 -29
- package/dist/phase-diagram/PhaseDiagramControls.svelte.d.ts +2 -2
- package/dist/phase-diagram/parse.js +3 -3
- package/dist/phase-diagram/svg-to-diagram.js +10 -12
- package/dist/plot/BarPlot.svelte +24 -15
- package/dist/plot/BarPlot.svelte.d.ts +3 -2
- package/dist/plot/FillArea.svelte +2 -3
- package/dist/plot/FillArea.svelte.d.ts +3 -2
- package/dist/plot/Histogram.svelte +37 -19
- package/dist/plot/Line.svelte +2 -3
- package/dist/plot/Line.svelte.d.ts +2 -2
- package/dist/plot/PlotLegend.svelte +79 -8
- package/dist/plot/PlotLegend.svelte.d.ts +4 -0
- package/dist/plot/PortalSelect.svelte +5 -5
- package/dist/plot/ScatterPlot.svelte +47 -33
- package/dist/plot/ScatterPlot.svelte.d.ts +5 -4
- package/dist/plot/ScatterPlot3D.svelte +6 -3
- package/dist/plot/ScatterPoint.svelte +10 -4
- package/dist/plot/ScatterPoint.svelte.d.ts +4 -2
- package/dist/plot/SpacegroupBarPlot.svelte +5 -4
- package/dist/plot/data-cleaning.js +9 -9
- package/dist/plot/index.d.ts +0 -6
- package/dist/plot/scales.d.ts +3 -3
- package/dist/plot/scales.js +29 -29
- package/dist/plot/types.d.ts +5 -9
- package/dist/rdf/calc-rdf.js +1 -1
- package/dist/sanitize.js +22 -15
- package/dist/settings.d.ts +2 -0
- package/dist/settings.js +12 -3
- package/dist/spectral/Bands.svelte +6 -6
- package/dist/spectral/BandsAndDos.svelte +4 -4
- package/dist/spectral/BrillouinBandsDos.svelte +3 -3
- package/dist/spectral/Dos.svelte +2 -2
- package/dist/spectral/helpers.js +1 -1
- package/dist/structure/AtomLegend.svelte +4 -4
- package/dist/structure/AtomLegend.svelte.d.ts +1 -1
- package/dist/structure/Cylinder.svelte +7 -7
- package/dist/structure/Structure.svelte +169 -27
- package/dist/structure/Structure.svelte.d.ts +6 -2
- package/dist/structure/StructureControls.svelte +130 -16
- package/dist/structure/StructureControls.svelte.d.ts +1 -1
- package/dist/structure/StructureInfoPane.svelte +519 -218
- package/dist/structure/StructureInfoPane.svelte.d.ts +2 -1
- package/dist/structure/StructureScene.svelte +399 -68
- package/dist/structure/StructureScene.svelte.d.ts +8 -4
- package/dist/structure/atom-properties.js +3 -1
- package/dist/structure/bond-order-perception.d.ts +13 -0
- package/dist/structure/bond-order-perception.js +367 -0
- package/dist/structure/bonding.d.ts +10 -1
- package/dist/structure/bonding.js +232 -11
- package/dist/structure/export.js +6 -4
- package/dist/structure/index.d.ts +19 -4
- package/dist/structure/index.js +3 -0
- package/dist/structure/label-placement.d.ts +14 -0
- package/dist/structure/label-placement.js +72 -0
- package/dist/structure/parse.d.ts +2 -1
- package/dist/structure/parse.js +25 -36
- package/dist/structure/supercell.js +35 -2
- package/dist/symmetry/SymmetryStats.svelte +1 -1
- package/dist/symmetry/cell-transform.js +15 -1
- package/dist/symmetry/index.js +3 -3
- package/dist/table/HeatmapTable.svelte +3 -3
- package/dist/table/ToggleMenu.svelte +1 -1
- package/dist/trajectory/Trajectory.svelte +2 -2
- package/dist/trajectory/TrajectoryInfoPane.svelte +14 -88
- package/dist/trajectory/extract.js +4 -4
- package/dist/trajectory/frame-reader.js +2 -2
- package/dist/trajectory/parse/ase.js +2 -6
- package/dist/trajectory/parse/hdf5.js +1 -3
- package/dist/trajectory/plotting.js +1 -1
- package/dist/utils.js +1 -1
- package/dist/xrd/calc-xrd.js +1 -1
- package/package.json +23 -38
- package/dist/structure/ferrox-wasm-types.d.ts +0 -46
- package/dist/structure/ferrox-wasm-types.js +0 -18
- package/dist/structure/ferrox-wasm.d.ts +0 -94
- package/dist/structure/ferrox-wasm.js +0 -249
|
@@ -218,6 +218,7 @@
|
|
|
218
218
|
|
|
219
219
|
// Legend placement stability state
|
|
220
220
|
let legend_element = $state<HTMLDivElement | undefined>()
|
|
221
|
+
let hovered_legend_series_idx = $state<number | null>(null)
|
|
221
222
|
const legend_hover = create_hover_lock()
|
|
222
223
|
const dim_tracker = create_dimension_tracker()
|
|
223
224
|
let has_initial_legend_placement = $state(false)
|
|
@@ -226,12 +227,17 @@
|
|
|
226
227
|
$effect(() => () => legend_hover.cleanup())
|
|
227
228
|
|
|
228
229
|
// Derived data
|
|
230
|
+
type IndexedSeries = { series_data: DataSeries; series_idx: number }
|
|
231
|
+
let selected_series_entries = $derived<IndexedSeries[]>(
|
|
232
|
+
series
|
|
233
|
+
.map((series_data: DataSeries, series_idx: number) => ({ series_data, series_idx }))
|
|
234
|
+
.filter(({ series_data }) =>
|
|
235
|
+
(series_data.visible ?? true) &&
|
|
236
|
+
(mode !== `single` || !selected_property || series_data.label === selected_property)
|
|
237
|
+
),
|
|
238
|
+
)
|
|
229
239
|
let selected_series = $derived(
|
|
230
|
-
|
|
231
|
-
? series.filter((srs: DataSeries) =>
|
|
232
|
-
(srs.visible ?? true) && srs.label === selected_property
|
|
233
|
-
)
|
|
234
|
-
: series.filter((srs: DataSeries) => srs.visible ?? true),
|
|
240
|
+
selected_series_entries.map(({ series_data }) => series_data),
|
|
235
241
|
)
|
|
236
242
|
|
|
237
243
|
// Separate series by y-axis
|
|
@@ -472,7 +478,7 @@
|
|
|
472
478
|
const x2_hist_generator = x2_series.length > 0
|
|
473
479
|
? bin().domain([ranges.current.x2[0], ranges.current.x2[1]]).thresholds(bins)
|
|
474
480
|
: null
|
|
475
|
-
return
|
|
481
|
+
return selected_series_entries.map(({ series_data, series_idx }) => {
|
|
476
482
|
const use_x2 = series_data.x_axis === `x2`
|
|
477
483
|
const active_hist = use_x2 && x2_hist_generator
|
|
478
484
|
? x2_hist_generator
|
|
@@ -551,15 +557,15 @@
|
|
|
551
557
|
|
|
552
558
|
const points: { x: number; y: number }[] = []
|
|
553
559
|
|
|
554
|
-
for (const { bins, x_scale, y_scale } of histogram_data) {
|
|
555
|
-
for (const
|
|
556
|
-
if (
|
|
557
|
-
const bar_x = x_scale(((
|
|
558
|
-
const bar_y = y_scale(
|
|
560
|
+
for (const { bins: series_bins, x_scale, y_scale } of histogram_data) {
|
|
561
|
+
for (const series_bin of series_bins) {
|
|
562
|
+
if (series_bin.length > 0) {
|
|
563
|
+
const bar_x = x_scale(((series_bin.x0 ?? 0) + (series_bin.x1 ?? 0)) / 2)
|
|
564
|
+
const bar_y = y_scale(series_bin.length)
|
|
559
565
|
if (isFinite(bar_x) && isFinite(bar_y)) {
|
|
560
566
|
// Add multiple points for taller bars to increase their weight
|
|
561
567
|
// Cap to prevent O(N·count/10) blow-ups for large counts
|
|
562
|
-
const weight = Math.min(20, Math.ceil(
|
|
568
|
+
const weight = Math.min(20, Math.ceil(series_bin.length / 10))
|
|
563
569
|
for (let idx = 0; idx < weight; idx++) points.push({ x: bar_x, y: bar_y })
|
|
564
570
|
}
|
|
565
571
|
}
|
|
@@ -596,7 +602,7 @@
|
|
|
596
602
|
// untrack() explicitly captures initial tween config (intentional - config set once at mount)
|
|
597
603
|
const tweened_legend_coords = new Tween(
|
|
598
604
|
{ x: 0, y: 0 },
|
|
599
|
-
untrack(() => ({ duration: 400, ...
|
|
605
|
+
untrack(() => ({ duration: 400, ...legend?.tween })),
|
|
600
606
|
)
|
|
601
607
|
|
|
602
608
|
// Update legend position with stability checks
|
|
@@ -1431,11 +1437,18 @@
|
|
|
1431
1437
|
|
|
1432
1438
|
<!-- Histogram bars (rendered after axes so bars appear above grid lines) -->
|
|
1433
1439
|
{#each histogram_data as
|
|
1434
|
-
{ id, bins, color, label, x_scale, y_scale, x_axis: srs_x_axis, y_axis },
|
|
1435
|
-
|
|
1436
|
-
(id ??
|
|
1440
|
+
{ id, bins, color, label, x_scale, y_scale, x_axis: srs_x_axis, y_axis, series_idx },
|
|
1441
|
+
idx
|
|
1442
|
+
(id ?? idx)
|
|
1437
1443
|
}
|
|
1438
|
-
<g
|
|
1444
|
+
<g
|
|
1445
|
+
class="histogram-series"
|
|
1446
|
+
data-series-idx={series_idx}
|
|
1447
|
+
opacity={hovered_legend_series_idx !== null &&
|
|
1448
|
+
hovered_legend_series_idx !== series_idx
|
|
1449
|
+
? 0.25
|
|
1450
|
+
: 1}
|
|
1451
|
+
>
|
|
1439
1452
|
{#each bins as bin, bin_idx (bin_idx)}
|
|
1440
1453
|
{@const bar_x = x_scale(bin.x0!)}
|
|
1441
1454
|
{@const bar_width = Math.max(1, Math.abs(x_scale(bin.x1!) - bar_x))}
|
|
@@ -1561,6 +1574,11 @@
|
|
|
1561
1574
|
series_data={legend_data}
|
|
1562
1575
|
on_toggle={legend?.on_toggle || toggle_series_visibility}
|
|
1563
1576
|
on_hover_change={legend_hover.set_locked}
|
|
1577
|
+
on_item_hover={(series_idx) =>
|
|
1578
|
+
(hovered_legend_series_idx = series_idx != null && series_idx >= 0
|
|
1579
|
+
? series_idx
|
|
1580
|
+
: null)}
|
|
1581
|
+
active_series_idx={hover_info?.series_idx ?? hovered_legend_series_idx}
|
|
1564
1582
|
style={`
|
|
1565
1583
|
position: absolute;
|
|
1566
1584
|
left: ${legend_placement ? tweened_legend_coords.current.x : pad.l + 10}px;
|
|
@@ -1595,8 +1613,8 @@
|
|
|
1595
1613
|
left: 0;
|
|
1596
1614
|
width: 100vw !important;
|
|
1597
1615
|
height: 100vh !important;
|
|
1598
|
-
/* Must be higher than Structure.svelte's --struct-buttons-z-index
|
|
1599
|
-
z-index: var(--histogram-fullscreen-z-index, 100000001);
|
|
1616
|
+
/* Must be higher than Structure.svelte's --struct-buttons-z-index. */
|
|
1617
|
+
z-index: var(--histogram-fullscreen-z-index, var(--z-index-overlay-nav, 100000001));
|
|
1600
1618
|
margin: 0;
|
|
1601
1619
|
border-radius: 0;
|
|
1602
1620
|
background: var(--histogram-fullscreen-bg, var(--histogram-bg, var(--plot-bg)));
|
package/dist/plot/Line.svelte
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type { TweenedOptions } from './'
|
|
3
2
|
import { DEFAULTS } from '../settings'
|
|
4
3
|
import { extent, min } from 'd3-array'
|
|
5
4
|
import { interpolatePath } from 'd3-interpolate-path'
|
|
@@ -7,7 +6,7 @@
|
|
|
7
6
|
import { untrack } from 'svelte'
|
|
8
7
|
import { linear } from 'svelte/easing'
|
|
9
8
|
import type { SVGAttributes } from 'svelte/elements'
|
|
10
|
-
import { Tween } from 'svelte/motion'
|
|
9
|
+
import { Tween, type TweenOptions } from 'svelte/motion'
|
|
11
10
|
|
|
12
11
|
let {
|
|
13
12
|
points,
|
|
@@ -26,7 +25,7 @@
|
|
|
26
25
|
line_width?: number
|
|
27
26
|
area_color?: string
|
|
28
27
|
area_stroke?: string | null
|
|
29
|
-
line_tween?:
|
|
28
|
+
line_tween?: TweenOptions<string>
|
|
30
29
|
line_dash?: string
|
|
31
30
|
} = $props()
|
|
32
31
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { TweenedOptions } from './';
|
|
2
1
|
import type { SVGAttributes } from 'svelte/elements';
|
|
2
|
+
import { type TweenOptions } from 'svelte/motion';
|
|
3
3
|
type $$ComponentProps = Omit<SVGAttributes<SVGPathElement>, `origin` | `points`> & {
|
|
4
4
|
points: readonly [number, number][];
|
|
5
5
|
origin: [number, number];
|
|
@@ -7,7 +7,7 @@ type $$ComponentProps = Omit<SVGAttributes<SVGPathElement>, `origin` | `points`>
|
|
|
7
7
|
line_width?: number;
|
|
8
8
|
area_color?: string;
|
|
9
9
|
area_stroke?: string | null;
|
|
10
|
-
line_tween?:
|
|
10
|
+
line_tween?: TweenOptions<string>;
|
|
11
11
|
line_dash?: string;
|
|
12
12
|
};
|
|
13
13
|
declare const Line: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
@@ -26,6 +26,10 @@
|
|
|
26
26
|
on_drag = () => {},
|
|
27
27
|
on_drag_end = () => {},
|
|
28
28
|
on_hover_change,
|
|
29
|
+
on_item_hover,
|
|
30
|
+
active_series_idx = null,
|
|
31
|
+
filterable = true,
|
|
32
|
+
filter_threshold = 12,
|
|
29
33
|
draggable = true,
|
|
30
34
|
root_element = $bindable<HTMLDivElement | undefined>(undefined),
|
|
31
35
|
...rest
|
|
@@ -54,6 +58,10 @@
|
|
|
54
58
|
on_drag_end?: (event: MouseEvent) => void
|
|
55
59
|
// Callback when hover state changes (for placement stability)
|
|
56
60
|
on_hover_change?: (is_hovered: boolean) => void
|
|
61
|
+
on_item_hover?: (series_idx: number | null) => void
|
|
62
|
+
active_series_idx?: number | null
|
|
63
|
+
filterable?: boolean
|
|
64
|
+
filter_threshold?: number
|
|
57
65
|
draggable?: boolean
|
|
58
66
|
// Bindable reference to the root DOM element for size measurements
|
|
59
67
|
root_element?: HTMLDivElement
|
|
@@ -61,9 +69,14 @@
|
|
|
61
69
|
|
|
62
70
|
let is_dragging = $state(false)
|
|
63
71
|
let drag_start_coords = $state<{ x: number; y: number } | null>(null)
|
|
72
|
+
let legend_filter = $state(``)
|
|
64
73
|
|
|
65
74
|
// Group series by legend_group, preserving order
|
|
66
|
-
type GroupedData = {
|
|
75
|
+
type GroupedData = {
|
|
76
|
+
group_name: string | null
|
|
77
|
+
items: LegendItem[]
|
|
78
|
+
all_items?: LegendItem[]
|
|
79
|
+
}
|
|
67
80
|
let grouped_series = $derived.by<GroupedData[]>(() => {
|
|
68
81
|
const groups: GroupedData[] = []
|
|
69
82
|
const group_map = new SvelteMap<string | null, LegendItem[]>()
|
|
@@ -88,6 +101,22 @@
|
|
|
88
101
|
),
|
|
89
102
|
)
|
|
90
103
|
|
|
104
|
+
let show_filter = $derived(filterable && series_data.length >= filter_threshold)
|
|
105
|
+
|
|
106
|
+
let filtered_grouped_series = $derived.by<GroupedData[]>(() => {
|
|
107
|
+
const filter = show_filter ? legend_filter.trim().toLowerCase() : ``
|
|
108
|
+
if (!filter) return grouped_series
|
|
109
|
+
return grouped_series
|
|
110
|
+
.map(({ group_name, items }) => ({
|
|
111
|
+
group_name,
|
|
112
|
+
all_items: items,
|
|
113
|
+
items: items.filter((item) =>
|
|
114
|
+
`${group_name ?? ``} ${strip_html(item.label)}`.toLowerCase().includes(filter)
|
|
115
|
+
),
|
|
116
|
+
}))
|
|
117
|
+
.filter(({ items }) => items.length > 0)
|
|
118
|
+
})
|
|
119
|
+
|
|
91
120
|
function toggle_group_collapse(group_name: string) {
|
|
92
121
|
// Normalize to SvelteSet if a plain Set was passed (ensures reactivity)
|
|
93
122
|
if (!(collapsed_groups instanceof SvelteSet)) {
|
|
@@ -111,7 +140,10 @@
|
|
|
111
140
|
document.body.style.userSelect = `auto`
|
|
112
141
|
}
|
|
113
142
|
}
|
|
114
|
-
onDestroy(
|
|
143
|
+
onDestroy(() => {
|
|
144
|
+
cleanup_drag_listeners()
|
|
145
|
+
on_item_hover?.(null)
|
|
146
|
+
})
|
|
115
147
|
|
|
116
148
|
function handle_legend_mouse_down(event: MouseEvent) {
|
|
117
149
|
if (!draggable) return
|
|
@@ -185,6 +217,7 @@
|
|
|
185
217
|
<div
|
|
186
218
|
class="legend-item"
|
|
187
219
|
class:hidden={!series.visible}
|
|
220
|
+
class:active={active_series_idx === series.series_idx}
|
|
188
221
|
class:indented={indent}
|
|
189
222
|
class:fill-item={is_fill_item}
|
|
190
223
|
style={item_style}
|
|
@@ -204,6 +237,10 @@
|
|
|
204
237
|
toggle_item(series)
|
|
205
238
|
}
|
|
206
239
|
}}
|
|
240
|
+
onmouseenter={() => on_item_hover?.(series.series_idx)}
|
|
241
|
+
onmouseleave={() => on_item_hover?.(null)}
|
|
242
|
+
onfocus={() => on_item_hover?.(series.series_idx)}
|
|
243
|
+
onblur={() => on_item_hover?.(null)}
|
|
207
244
|
role="button"
|
|
208
245
|
tabindex="0"
|
|
209
246
|
aria-pressed={series.visible}
|
|
@@ -312,28 +349,43 @@
|
|
|
312
349
|
class:is-dragging={is_dragging}
|
|
313
350
|
class:grouped={has_groups}
|
|
314
351
|
>
|
|
315
|
-
{#
|
|
352
|
+
{#if show_filter}
|
|
353
|
+
<input
|
|
354
|
+
class="legend-filter"
|
|
355
|
+
type="search"
|
|
356
|
+
bind:value={legend_filter}
|
|
357
|
+
placeholder="Filter legend"
|
|
358
|
+
aria-label="Filter legend items"
|
|
359
|
+
onclick={(event) => event.stopPropagation()}
|
|
360
|
+
onmousedown={(event) => event.stopPropagation()}
|
|
361
|
+
/>
|
|
362
|
+
{/if}
|
|
363
|
+
{#if show_filter && legend_filter && filtered_grouped_series.length === 0}
|
|
364
|
+
<span class="legend-empty">No legend items</span>
|
|
365
|
+
{/if}
|
|
366
|
+
{#each filtered_grouped_series as { group_name, items, all_items } (group_name ?? `__ungrouped__`)}
|
|
316
367
|
{#if group_name !== null && has_groups}
|
|
317
368
|
<!-- Group header -->
|
|
369
|
+
{@const group_items = all_items ?? items}
|
|
318
370
|
{@const is_collapsed = collapsed_groups.has(group_name)}
|
|
319
|
-
{@const group_visible =
|
|
371
|
+
{@const group_visible = group_items.some((item) => item.visible)}
|
|
320
372
|
<div
|
|
321
373
|
class="legend-group-header"
|
|
322
374
|
class:hidden={!group_visible}
|
|
323
375
|
onclick={(event: MouseEvent) => {
|
|
324
376
|
event.preventDefault()
|
|
325
377
|
event.stopPropagation()
|
|
326
|
-
handle_group_click(group_name,
|
|
378
|
+
handle_group_click(group_name, group_items)
|
|
327
379
|
}}
|
|
328
380
|
ondblclick={(event: MouseEvent) => {
|
|
329
381
|
event.preventDefault()
|
|
330
382
|
event.stopPropagation()
|
|
331
|
-
on_group_double_click?.(group_name,
|
|
383
|
+
on_group_double_click?.(group_name, group_items.map((item) => item.series_idx))
|
|
332
384
|
}}
|
|
333
385
|
onkeydown={(event) => {
|
|
334
386
|
if ([`Enter`, ` `].includes(event.key)) {
|
|
335
387
|
event.preventDefault()
|
|
336
|
-
handle_group_click(group_name,
|
|
388
|
+
handle_group_click(group_name, group_items)
|
|
337
389
|
}
|
|
338
390
|
}}
|
|
339
391
|
role="button"
|
|
@@ -425,6 +477,22 @@
|
|
|
425
477
|
transition: var(--plot-legend-item-transition, opacity 0.3s ease);
|
|
426
478
|
color: var(--plot-legend-item-color);
|
|
427
479
|
}
|
|
480
|
+
.legend-filter {
|
|
481
|
+
box-sizing: border-box;
|
|
482
|
+
width: calc(100% - 6px);
|
|
483
|
+
min-width: 10em;
|
|
484
|
+
margin: 3px;
|
|
485
|
+
padding: 2px 5px;
|
|
486
|
+
border: 1px solid color-mix(in srgb, currentColor 20%, transparent);
|
|
487
|
+
border-radius: var(--border-radius, 3pt);
|
|
488
|
+
background: color-mix(in srgb, var(--plot-legend-bg-color, Canvas) 88%, currentColor);
|
|
489
|
+
color: inherit;
|
|
490
|
+
font: inherit;
|
|
491
|
+
}
|
|
492
|
+
.legend-empty {
|
|
493
|
+
padding: var(--plot-legend-item-padding, 1px 8px 1px 3px);
|
|
494
|
+
opacity: 0.7;
|
|
495
|
+
}
|
|
428
496
|
.legend-item.indented {
|
|
429
497
|
padding: var(--plot-legend-item-padding, 0 8px 1px 3px);
|
|
430
498
|
padding-left: var(--plot-legend-group-indent, 16px);
|
|
@@ -432,9 +500,12 @@
|
|
|
432
500
|
.legend-item.hidden {
|
|
433
501
|
opacity: var(--plot-legend-item-hidden-opacity, 0.5);
|
|
434
502
|
}
|
|
435
|
-
.legend-item:hover, .legend-item:focus {
|
|
503
|
+
.legend-item:hover, .legend-item:focus, .legend-item.active {
|
|
436
504
|
background-color: var(--plot-legend-item-hover-bg-color);
|
|
437
505
|
}
|
|
506
|
+
.legend-item.active {
|
|
507
|
+
box-shadow: inset 2px 0 0 var(--accent-color, currentColor);
|
|
508
|
+
}
|
|
438
509
|
.legend-marker {
|
|
439
510
|
display: inline-flex; /* Use flex to align items */
|
|
440
511
|
align-items: center; /* Vertically center items */
|
|
@@ -17,6 +17,10 @@ type $$ComponentProps = Omit<HTMLAttributes<HTMLDivElement>, `style`> & {
|
|
|
17
17
|
on_drag?: (event: MouseEvent) => void;
|
|
18
18
|
on_drag_end?: (event: MouseEvent) => void;
|
|
19
19
|
on_hover_change?: (is_hovered: boolean) => void;
|
|
20
|
+
on_item_hover?: (series_idx: number | null) => void;
|
|
21
|
+
active_series_idx?: number | null;
|
|
22
|
+
filterable?: boolean;
|
|
23
|
+
filter_threshold?: number;
|
|
20
24
|
draggable?: boolean;
|
|
21
25
|
root_element?: HTMLDivElement;
|
|
22
26
|
};
|
|
@@ -100,15 +100,15 @@
|
|
|
100
100
|
btn.setAttribute(`role`, `option`)
|
|
101
101
|
btn.innerHTML = sanitize_html(format_option(opt))
|
|
102
102
|
style_sub_sup(btn)
|
|
103
|
-
btn.
|
|
104
|
-
btn.
|
|
103
|
+
btn.addEventListener(`click`, () => select(opt.key))
|
|
104
|
+
btn.addEventListener(`mouseenter`, () => {
|
|
105
105
|
if (!btn.classList.contains(`selected`)) {
|
|
106
106
|
btn.style.background = `rgba(128, 128, 128, 0.15)`
|
|
107
107
|
}
|
|
108
|
-
}
|
|
109
|
-
btn.
|
|
108
|
+
})
|
|
109
|
+
btn.addEventListener(`mouseleave`, () => {
|
|
110
110
|
if (!btn.classList.contains(`selected`)) btn.style.background = `transparent`
|
|
111
|
-
}
|
|
111
|
+
})
|
|
112
112
|
if (opt.key === selected_key) {
|
|
113
113
|
btn.classList.add(`selected`)
|
|
114
114
|
btn.style.cssText = portal_styles.btn + portal_styles.btn_selected
|
|
@@ -32,7 +32,6 @@
|
|
|
32
32
|
ScatterHandlerProps,
|
|
33
33
|
Sides,
|
|
34
34
|
StyleOverrides,
|
|
35
|
-
TweenedOptions,
|
|
36
35
|
UserContentProps,
|
|
37
36
|
XyObj,
|
|
38
37
|
} from './'
|
|
@@ -81,7 +80,7 @@
|
|
|
81
80
|
import type { ComponentProps, Snippet } from 'svelte'
|
|
82
81
|
import { untrack } from 'svelte'
|
|
83
82
|
import type { HTMLAttributes } from 'svelte/elements'
|
|
84
|
-
import { Tween } from 'svelte/motion'
|
|
83
|
+
import { Tween, type TweenOptions } from 'svelte/motion'
|
|
85
84
|
import { SvelteSet } from 'svelte/reactivity'
|
|
86
85
|
import type { FillPathPoint } from './fill-utils'
|
|
87
86
|
import {
|
|
@@ -206,15 +205,15 @@
|
|
|
206
205
|
color_bar?:
|
|
207
206
|
| (ComponentProps<typeof ColorBar> & {
|
|
208
207
|
margin?: number | Sides
|
|
209
|
-
tween?:
|
|
208
|
+
tween?: TweenOptions<XyObj>
|
|
210
209
|
responsive?: boolean // Allow colorbar to reposition if density changes (default: false)
|
|
211
210
|
})
|
|
212
211
|
| null
|
|
213
212
|
label_placement_config?: Partial<LabelPlacementConfig>
|
|
214
213
|
hover_config?: Partial<HoverConfig>
|
|
215
214
|
legend?: LegendConfig | null
|
|
216
|
-
point_tween?:
|
|
217
|
-
line_tween?:
|
|
215
|
+
point_tween?: TweenOptions<XyObj>
|
|
216
|
+
line_tween?: TweenOptions<string>
|
|
218
217
|
point_events?: Record<
|
|
219
218
|
string,
|
|
220
219
|
(payload: { point: InternalPoint<Metadata>; event: Event }) => void
|
|
@@ -245,15 +244,15 @@
|
|
|
245
244
|
const final_x_axis = $derived({
|
|
246
245
|
...AXIS_DEFAULTS,
|
|
247
246
|
label_shift: { x: 0, y: -40 }, // x-axis needs different label position
|
|
248
|
-
...
|
|
247
|
+
...x_axis,
|
|
249
248
|
})
|
|
250
|
-
const final_y_axis = $derived({ ...AXIS_DEFAULTS, ...
|
|
249
|
+
const final_y_axis = $derived({ ...AXIS_DEFAULTS, ...y_axis })
|
|
251
250
|
const final_x2_axis = $derived({
|
|
252
251
|
...AXIS_DEFAULTS,
|
|
253
252
|
label_shift: { x: 0, y: 40 }, // x2-axis label above top edge
|
|
254
|
-
...
|
|
253
|
+
...x2_axis,
|
|
255
254
|
})
|
|
256
|
-
const final_y2_axis = $derived({ ...AXIS_DEFAULTS, ...
|
|
255
|
+
const final_y2_axis = $derived({ ...AXIS_DEFAULTS, ...y2_axis })
|
|
257
256
|
// Cache time-axis check — used in ~10 places for scale/tick/tooltip logic
|
|
258
257
|
let is_time_x = $derived(
|
|
259
258
|
is_time_scale(final_x_axis.scale_type, final_x_axis.format),
|
|
@@ -261,16 +260,16 @@
|
|
|
261
260
|
let is_time_x2 = $derived(
|
|
262
261
|
is_time_scale(final_x2_axis.scale_type, final_x2_axis.format),
|
|
263
262
|
)
|
|
264
|
-
const final_display = $derived({ ...DEFAULTS.scatter.display, ...
|
|
263
|
+
const final_display = $derived({ ...DEFAULTS.scatter.display, ...display })
|
|
265
264
|
// Local state for styles (initialized from prop, owned by this component for controls)
|
|
266
265
|
// Using $state because styles has bindings in ScatterPlotControls
|
|
267
266
|
// untrack() explicitly captures initial prop value (intentional - props provide initial config)
|
|
268
267
|
let styles = $state(untrack(() => ({
|
|
269
268
|
show_points: DEFAULTS.scatter.show_points,
|
|
270
269
|
show_lines: DEFAULTS.scatter.show_lines,
|
|
271
|
-
point: { ...DEFAULTS.scatter.point, ...
|
|
272
|
-
line: { ...DEFAULTS.scatter.line, ...
|
|
273
|
-
...
|
|
270
|
+
point: { ...DEFAULTS.scatter.point, ...styles_init?.point },
|
|
271
|
+
line: { ...DEFAULTS.scatter.line, ...styles_init?.line },
|
|
272
|
+
...styles_init,
|
|
274
273
|
})))
|
|
275
274
|
let controls = $derived({ show: true, open: false, ...controls_init })
|
|
276
275
|
|
|
@@ -364,6 +363,7 @@
|
|
|
364
363
|
let legend_is_dragging = $state(false)
|
|
365
364
|
let legend_drag_offset = $state<{ x: number; y: number }>({ x: 0, y: 0 })
|
|
366
365
|
let legend_manual_position = $state<{ x: number; y: number } | null>(null)
|
|
366
|
+
let hovered_legend_series_idx = $state<number | null>(null)
|
|
367
367
|
|
|
368
368
|
// State for legend/colorbar placement stability
|
|
369
369
|
let legend_element = $state<HTMLDivElement | undefined>()
|
|
@@ -394,13 +394,13 @@
|
|
|
394
394
|
|
|
395
395
|
for (const srs of series_with_ids) {
|
|
396
396
|
if (!srs) continue
|
|
397
|
-
const { x: xs, y: ys, visible = true, y_axis = `y1`, x_axis: x_ax = `x1` } =
|
|
397
|
+
const { x: xs, y: ys, visible = true, y_axis: series_y_axis = `y1`, x_axis: x_ax = `x1` } =
|
|
398
398
|
srs as DataSeries
|
|
399
399
|
for (let idx = 0; idx < xs.length; idx++) {
|
|
400
400
|
const point = { x: xs[idx], y: ys[idx] }
|
|
401
401
|
all.push(point)
|
|
402
402
|
if (visible) {
|
|
403
|
-
if (
|
|
403
|
+
if (series_y_axis === `y2`) y2.push(point)
|
|
404
404
|
else y1.push(point)
|
|
405
405
|
if (x_ax === `x2`) x2.push(point)
|
|
406
406
|
}
|
|
@@ -666,7 +666,7 @@
|
|
|
666
666
|
}
|
|
667
667
|
}
|
|
668
668
|
|
|
669
|
-
const { x: xs, y: ys, color_values, size_values, ...
|
|
669
|
+
const { x: xs, y: ys, color_values, size_values, ...series_rest } = data_series
|
|
670
670
|
|
|
671
671
|
// Process points internally, adding properties beyond the base Point type
|
|
672
672
|
const processed_points: InternalPoint<Metadata>[] = xs.map(
|
|
@@ -674,11 +674,11 @@
|
|
|
674
674
|
x: x_val,
|
|
675
675
|
y: ys[point_idx],
|
|
676
676
|
color_value: color_values?.[point_idx],
|
|
677
|
-
metadata: process_prop(
|
|
678
|
-
point_style: process_prop(
|
|
679
|
-
point_hover: process_prop(
|
|
680
|
-
point_label: process_prop(
|
|
681
|
-
point_offset: process_prop(
|
|
677
|
+
metadata: process_prop(series_rest.metadata, point_idx) as Metadata | undefined,
|
|
678
|
+
point_style: process_prop(series_rest.point_style, point_idx),
|
|
679
|
+
point_hover: process_prop(series_rest.point_hover, point_idx),
|
|
680
|
+
point_label: process_prop(series_rest.point_label, point_idx),
|
|
681
|
+
point_offset: process_prop(series_rest.point_offset, point_idx),
|
|
682
682
|
series_idx,
|
|
683
683
|
point_idx,
|
|
684
684
|
size_value: size_values?.[point_idx],
|
|
@@ -1141,12 +1141,12 @@
|
|
|
1141
1141
|
// untrack() explicitly captures initial tween config (intentional - config set once at mount)
|
|
1142
1142
|
const tweened_colorbar_coords = new Tween(
|
|
1143
1143
|
{ x: 0, y: 0 },
|
|
1144
|
-
untrack(() => ({ duration: 400, ...
|
|
1144
|
+
untrack(() => ({ duration: 400, ...color_bar?.tween })),
|
|
1145
1145
|
)
|
|
1146
1146
|
// Initialize tweened values for legend position - create once, update target via effect
|
|
1147
1147
|
const tweened_legend_coords = new Tween(
|
|
1148
1148
|
{ x: 0, y: 0 },
|
|
1149
|
-
untrack(() => ({ duration: 400, ...
|
|
1149
|
+
untrack(() => ({ duration: 400, ...legend?.tween })),
|
|
1150
1150
|
)
|
|
1151
1151
|
|
|
1152
1152
|
// Update placement positions (with animation and stability checks)
|
|
@@ -1727,9 +1727,9 @@
|
|
|
1727
1727
|
legend_manual_position = { x: constrained_x, y: constrained_y }
|
|
1728
1728
|
}
|
|
1729
1729
|
|
|
1730
|
-
function get_screen_coords(point: Point,
|
|
1730
|
+
function get_screen_coords(point: Point, data_series?: DataSeries): [number, number] {
|
|
1731
1731
|
// convert data coordinates to potentially non-finite screen coordinates
|
|
1732
|
-
const use_x2 =
|
|
1732
|
+
const use_x2 = data_series?.x_axis === `x2`
|
|
1733
1733
|
const active_x_scale = use_x2 ? x2_scale_fn : x_scale_fn
|
|
1734
1734
|
const active_is_time_x = use_x2 ? is_time_x2 : is_time_x
|
|
1735
1735
|
const screen_x = active_is_time_x
|
|
@@ -1738,7 +1738,7 @@
|
|
|
1738
1738
|
|
|
1739
1739
|
const y_val = point.y
|
|
1740
1740
|
// Determine which y-scale to use based on series y_axis property
|
|
1741
|
-
const use_y2 =
|
|
1741
|
+
const use_y2 = data_series?.y_axis === `y2`
|
|
1742
1742
|
const y_scale = use_y2 ? y2_scale_fn : y_scale_fn
|
|
1743
1743
|
const y_scale_type = use_y2
|
|
1744
1744
|
? get_scale_type_name(final_y2_axis.scale_type)
|
|
@@ -2035,7 +2035,7 @@
|
|
|
2035
2035
|
y1={-(height - pad.b - pad.t)}
|
|
2036
2036
|
y2="0"
|
|
2037
2037
|
{...DEFAULT_GRID_STYLE}
|
|
2038
|
-
{...
|
|
2038
|
+
{...final_x_axis.grid_style}
|
|
2039
2039
|
/>
|
|
2040
2040
|
{/if}
|
|
2041
2041
|
<line y1="0" y2={inside ? -5 : 5} stroke="var(--border-color, gray)" />
|
|
@@ -2116,7 +2116,7 @@
|
|
|
2116
2116
|
x1="0"
|
|
2117
2117
|
x2={width - pad.l - pad.r}
|
|
2118
2118
|
{...DEFAULT_GRID_STYLE}
|
|
2119
|
-
{...
|
|
2119
|
+
{...final_y_axis.grid_style}
|
|
2120
2120
|
/>
|
|
2121
2121
|
{/if}
|
|
2122
2122
|
<line
|
|
@@ -2186,7 +2186,7 @@
|
|
|
2186
2186
|
x1={-(width - pad.l - pad.r)}
|
|
2187
2187
|
x2="0"
|
|
2188
2188
|
{...DEFAULT_GRID_STYLE}
|
|
2189
|
-
{...
|
|
2189
|
+
{...final_y2_axis.grid_style}
|
|
2190
2190
|
/>
|
|
2191
2191
|
{/if}
|
|
2192
2192
|
<line
|
|
@@ -2256,7 +2256,7 @@
|
|
|
2256
2256
|
y1="0"
|
|
2257
2257
|
y2={height - pad.b - pad.t}
|
|
2258
2258
|
{...DEFAULT_GRID_STYLE}
|
|
2259
|
-
{...
|
|
2259
|
+
{...final_x2_axis.grid_style}
|
|
2260
2260
|
/>
|
|
2261
2261
|
{/if}
|
|
2262
2262
|
<line
|
|
@@ -2353,7 +2353,14 @@
|
|
|
2353
2353
|
{#each filtered_series ?? [] as series_data (series_data._id)}
|
|
2354
2354
|
{@const series_markers = series_data.markers ?? DEFAULT_MARKERS}
|
|
2355
2355
|
{@const series_default_color = get_series_color(series_data.orig_series_idx ?? 0)}
|
|
2356
|
-
<g
|
|
2356
|
+
<g
|
|
2357
|
+
data-series-id={series_data._id}
|
|
2358
|
+
clip-path="url(#{clip_path_id})"
|
|
2359
|
+
opacity={hovered_legend_series_idx !== null &&
|
|
2360
|
+
hovered_legend_series_idx !== series_data.orig_series_idx
|
|
2361
|
+
? 0.25
|
|
2362
|
+
: 1}
|
|
2363
|
+
>
|
|
2357
2364
|
{#if series_markers?.includes(`line`)}
|
|
2358
2365
|
{@const all_line_points = series_data.x.map((x, idx) => ({
|
|
2359
2366
|
x,
|
|
@@ -2439,6 +2446,8 @@
|
|
|
2439
2446
|
<ScatterPoint
|
|
2440
2447
|
x={screen_x}
|
|
2441
2448
|
y={screen_y}
|
|
2449
|
+
is_dimmed={hovered_legend_series_idx !== null &&
|
|
2450
|
+
hovered_legend_series_idx !== point.series_idx}
|
|
2442
2451
|
is_hovered={tooltip_point?.series_idx === point.series_idx &&
|
|
2443
2452
|
tooltip_point?.point_idx === point.point_idx}
|
|
2444
2453
|
is_selected={selected_point?.series_idx === point.series_idx &&
|
|
@@ -2662,6 +2671,11 @@
|
|
|
2662
2671
|
on_drag={handle_legend_drag}
|
|
2663
2672
|
on_drag_end={() => (legend_is_dragging = false)}
|
|
2664
2673
|
on_hover_change={legend_hover.set_locked}
|
|
2674
|
+
on_item_hover={(series_idx) =>
|
|
2675
|
+
(hovered_legend_series_idx = series_idx != null && series_idx >= 0
|
|
2676
|
+
? series_idx
|
|
2677
|
+
: null)}
|
|
2678
|
+
active_series_idx={tooltip_point?.series_idx ?? hovered_legend_series_idx}
|
|
2665
2679
|
draggable={legend?.draggable ?? true}
|
|
2666
2680
|
{...legend}
|
|
2667
2681
|
on_toggle={legend?.on_toggle ??
|
|
@@ -2745,8 +2759,8 @@
|
|
|
2745
2759
|
left: 0;
|
|
2746
2760
|
width: 100vw !important;
|
|
2747
2761
|
height: 100vh !important;
|
|
2748
|
-
/* Must be higher than Structure.svelte's --struct-buttons-z-index
|
|
2749
|
-
z-index: var(--scatter-fullscreen-z-index, 100000001);
|
|
2762
|
+
/* Must be higher than Structure.svelte's --struct-buttons-z-index. */
|
|
2763
|
+
z-index: var(--scatter-fullscreen-z-index, var(--z-index-overlay-nav, 100000001));
|
|
2750
2764
|
margin: 0;
|
|
2751
2765
|
border-radius: 0;
|
|
2752
2766
|
background: var(--scatter-fullscreen-bg, var(--scatter-bg, var(--plot-bg)));
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { D3ColorSchemeName, D3InterpolateName } from '../colors';
|
|
2
|
-
import type { AxisLoadError, BasePlotProps, ControlsConfig, DataLoaderFn, DataSeries, ErrorBand, FillHandlerEvent, FillRegion, HoverConfig, InternalPoint, LabelPlacementConfig, LegendConfig, PanConfig, PlotConfig, Point, RefLine, RefLineEvent, ScaleType, ScatterHandlerEvent, ScatterHandlerProps, Sides, StyleOverrides,
|
|
2
|
+
import type { AxisLoadError, BasePlotProps, ControlsConfig, DataLoaderFn, DataSeries, ErrorBand, FillHandlerEvent, FillRegion, HoverConfig, InternalPoint, LabelPlacementConfig, LegendConfig, PanConfig, PlotConfig, Point, RefLine, RefLineEvent, ScaleType, ScatterHandlerEvent, ScatterHandlerProps, Sides, StyleOverrides, UserContentProps, XyObj } from './';
|
|
3
3
|
import { ColorBar } from './';
|
|
4
4
|
import type { ComponentProps, Snippet } from 'svelte';
|
|
5
5
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
6
|
+
import { type TweenOptions } from 'svelte/motion';
|
|
6
7
|
declare function $$render<Metadata extends Record<string, unknown> = Record<string, unknown>>(): {
|
|
7
8
|
props: HTMLAttributes<HTMLDivElement> & Omit<BasePlotProps, "change"> & PlotConfig & {
|
|
8
9
|
series?: DataSeries<Metadata>[];
|
|
@@ -40,14 +41,14 @@ declare function $$render<Metadata extends Record<string, unknown> = Record<stri
|
|
|
40
41
|
};
|
|
41
42
|
color_bar?: (ComponentProps<typeof ColorBar> & {
|
|
42
43
|
margin?: number | Sides;
|
|
43
|
-
tween?:
|
|
44
|
+
tween?: TweenOptions<XyObj>;
|
|
44
45
|
responsive?: boolean;
|
|
45
46
|
}) | null;
|
|
46
47
|
label_placement_config?: Partial<LabelPlacementConfig>;
|
|
47
48
|
hover_config?: Partial<HoverConfig>;
|
|
48
49
|
legend?: LegendConfig | null;
|
|
49
|
-
point_tween?:
|
|
50
|
-
line_tween?:
|
|
50
|
+
point_tween?: TweenOptions<XyObj>;
|
|
51
|
+
line_tween?: TweenOptions<string>;
|
|
51
52
|
point_events?: Record<string, (payload: {
|
|
52
53
|
point: InternalPoint<Metadata>;
|
|
53
54
|
event: Event;
|
|
@@ -355,8 +355,10 @@
|
|
|
355
355
|
}}
|
|
356
356
|
pane_props={{
|
|
357
357
|
...controls.pane_props,
|
|
358
|
-
// z-index must exceed fullscreen z-index
|
|
359
|
-
style: `--pane-z-index: 100000002; ${
|
|
358
|
+
// z-index must exceed fullscreen z-index to remain clickable in fullscreen mode
|
|
359
|
+
style: `--pane-z-index: var(--z-index-overlay-dialog, 100000002); ${
|
|
360
|
+
controls.pane_props?.style ?? ``
|
|
361
|
+
}`,
|
|
360
362
|
}}
|
|
361
363
|
bind:x_axis
|
|
362
364
|
bind:y_axis
|
|
@@ -394,6 +396,7 @@
|
|
|
394
396
|
<PlotLegend
|
|
395
397
|
series_data={legend_data}
|
|
396
398
|
on_toggle={toggle_series_visibility}
|
|
399
|
+
active_series_idx={tooltip_point?.series_idx ?? null}
|
|
397
400
|
draggable={legend?.draggable ?? true}
|
|
398
401
|
{...legend}
|
|
399
402
|
style={`position: absolute; top: 2.5em; right: 1em; ${legend?.style ?? ``}`}
|
|
@@ -427,7 +430,7 @@
|
|
|
427
430
|
left: 0;
|
|
428
431
|
width: 100vw !important;
|
|
429
432
|
height: 100vh !important;
|
|
430
|
-
z-index: var(--scatter3d-fullscreen-z-index, 100000001);
|
|
433
|
+
z-index: var(--scatter3d-fullscreen-z-index, var(--z-index-overlay-nav, 100000001));
|
|
431
434
|
margin: 0;
|
|
432
435
|
border-radius: 0;
|
|
433
436
|
max-height: none !important;
|