matterviz 0.4.2 → 0.4.3
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 +40 -50
- package/dist/api/optimade.js +10 -7
- package/dist/app.css +15 -0
- package/dist/brillouin/BrillouinZone.svelte +18 -9
- package/dist/brillouin/BrillouinZoneScene.svelte +20 -29
- package/dist/brillouin/compute.js +10 -16
- package/dist/brillouin/geometry.js +2 -2
- package/dist/chempot-diagram/ChemPotDiagram.svelte +18 -15
- package/dist/chempot-diagram/ChemPotDiagram2D.svelte +17 -113
- package/dist/chempot-diagram/ChemPotDiagram3D.svelte +104 -264
- package/dist/chempot-diagram/async-compute.svelte.js +12 -13
- package/dist/chempot-diagram/color.d.ts +21 -1
- package/dist/chempot-diagram/color.js +70 -1
- package/dist/chempot-diagram/compute.d.ts +7 -0
- package/dist/chempot-diagram/compute.js +85 -26
- package/dist/chempot-diagram/export.js +1 -6
- package/dist/chempot-diagram/temperature.js +9 -10
- package/dist/composition/BarChart.svelte +7 -9
- package/dist/composition/Composition.svelte +3 -8
- package/dist/composition/Formula.svelte +2 -2
- package/dist/composition/FormulaFilter.svelte +27 -93
- package/dist/composition/format.js +6 -8
- package/dist/composition/parse.js +50 -105
- package/dist/constants.d.ts +0 -1
- package/dist/constants.js +6 -17
- package/dist/convex-hull/ConvexHull2D.svelte +0 -2
- package/dist/convex-hull/ConvexHull3D.svelte +19 -47
- package/dist/convex-hull/ConvexHull4D.svelte +65 -95
- package/dist/convex-hull/StructurePopup.svelte +1 -1
- package/dist/convex-hull/gas-thermodynamics.js +21 -53
- package/dist/convex-hull/helpers.js +1 -7
- package/dist/convex-hull/thermodynamics.js +19 -27
- package/dist/coordination/CoordinationBarPlot.svelte +12 -21
- package/dist/element/BohrAtom.svelte +0 -1
- package/dist/element/ElementTile.svelte +26 -44
- package/dist/element/data.d.ts +2 -0
- package/dist/element/data.js +5 -1
- package/dist/element/index.d.ts +1 -1
- package/dist/element/index.js +1 -1
- package/dist/fermi-surface/FermiSurface.svelte +22 -23
- package/dist/fermi-surface/FermiSurface.svelte.d.ts +2 -6
- package/dist/fermi-surface/FermiSurfaceScene.svelte +32 -77
- package/dist/fermi-surface/compute.js +22 -55
- package/dist/fermi-surface/parse.js +38 -70
- package/dist/file-viewer/JsonBrowser.svelte +1043 -0
- package/dist/file-viewer/JsonBrowser.svelte.d.ts +9 -0
- package/dist/file-viewer/PlotPanel.svelte +350 -0
- package/dist/file-viewer/PlotPanel.svelte.d.ts +9 -0
- package/dist/file-viewer/TrajectoryWithDos.svelte +30 -0
- package/dist/file-viewer/TrajectoryWithDos.svelte.d.ts +10 -0
- package/dist/file-viewer/detect.d.ts +11 -0
- package/dist/file-viewer/detect.js +381 -0
- package/dist/file-viewer/eligibility.d.ts +4 -0
- package/dist/file-viewer/eligibility.js +53 -0
- package/dist/file-viewer/host-protocol.d.ts +50 -0
- package/dist/file-viewer/host-transfer.d.ts +25 -0
- package/dist/file-viewer/host-transfer.js +42 -0
- package/dist/file-viewer/index.d.ts +5 -0
- package/dist/file-viewer/index.js +5 -0
- package/dist/file-viewer/main.d.ts +44 -0
- package/dist/file-viewer/main.js +535 -0
- package/dist/file-viewer/parse.d.ts +9 -0
- package/dist/file-viewer/parse.js +170 -0
- package/dist/file-viewer/plot-utils.d.ts +24 -0
- package/dist/file-viewer/plot-utils.js +191 -0
- package/dist/file-viewer/types.d.ts +4 -0
- package/dist/file-viewer/types.js +5 -0
- package/dist/heatmap-matrix/HeatmapMatrix.svelte +57 -84
- package/dist/heatmap-matrix/HeatmapMatrixControls.svelte.d.ts +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/io/ExportPane.svelte +1 -1
- package/dist/io/decompress.d.ts +2 -0
- package/dist/io/decompress.js +3 -2
- package/dist/io/export.d.ts +7 -4
- package/dist/io/export.js +70 -18
- package/dist/io/file-drop.js +60 -35
- package/dist/io/url-drop.d.ts +2 -0
- package/dist/io/url-drop.js +28 -16
- package/dist/isosurface/Isosurface.svelte +687 -196
- package/dist/isosurface/Isosurface.svelte.d.ts +7 -1
- package/dist/isosurface/IsosurfaceControls.svelte +564 -85
- package/dist/isosurface/IsosurfaceControls.svelte.d.ts +1 -1
- package/dist/isosurface/VolumeSlice.svelte +224 -0
- package/dist/isosurface/VolumeSlice.svelte.d.ts +28 -0
- package/dist/isosurface/coloring.d.ts +20 -0
- package/dist/isosurface/coloring.js +118 -0
- package/dist/isosurface/geometry-worker-types.d.ts +38 -0
- package/dist/isosurface/geometry.worker.d.ts +1 -0
- package/dist/isosurface/geometry.worker.js +59 -0
- package/dist/isosurface/grid.d.ts +10 -0
- package/dist/isosurface/grid.js +31 -0
- package/dist/isosurface/index.d.ts +4 -0
- package/dist/isosurface/index.js +4 -0
- package/dist/isosurface/profile.d.ts +12 -0
- package/dist/isosurface/profile.js +19 -0
- package/dist/isosurface/sampling.d.ts +34 -0
- package/dist/isosurface/sampling.js +408 -0
- package/dist/isosurface/slice-rendering.d.ts +13 -0
- package/dist/isosurface/slice-rendering.js +79 -0
- package/dist/isosurface/slice.d.ts +22 -2
- package/dist/isosurface/slice.js +200 -116
- package/dist/isosurface/types.d.ts +24 -1
- package/dist/isosurface/types.js +120 -8
- package/dist/json-path.d.ts +4 -0
- package/dist/json-path.js +89 -0
- package/dist/labels.js +1 -1
- package/dist/layout/InfoCard.svelte +1 -3
- package/dist/layout/SettingsSection.svelte +52 -61
- package/dist/layout/SettingsSection.svelte.d.ts +6 -7
- package/dist/layout/ViewerChrome.svelte +7 -1
- package/dist/layout/json-tree/JsonNode.svelte +14 -48
- package/dist/layout/json-tree/JsonTree.svelte +48 -95
- package/dist/layout/json-tree/JsonValue.svelte +3 -18
- package/dist/layout/json-tree/utils.d.ts +0 -3
- package/dist/layout/json-tree/utils.js +50 -179
- package/dist/marching-cubes.d.ts +8 -1
- package/dist/marching-cubes.js +115 -48
- package/dist/math.d.ts +3 -2
- package/dist/math.js +29 -38
- package/dist/overlays/ContextMenu.svelte +15 -32
- package/dist/overlays/DraggablePane.svelte +39 -58
- package/dist/overlays/DraggablePane.svelte.d.ts +1 -1
- package/dist/overlays/GlassChip.svelte +3 -1
- package/dist/periodic-table/PeriodicTableControls.svelte +26 -52
- package/dist/phase-diagram/IsobaricBinaryPhaseDiagram.svelte +18 -34
- package/dist/phase-diagram/build-diagram.js +1 -6
- package/dist/phase-diagram/svg-to-diagram.js +9 -14
- package/dist/plot/bar/BarPlot.svelte +38 -42
- package/dist/plot/bar/SpacegroupBarPlot.svelte +18 -42
- package/dist/plot/box/BoxPlot.svelte +27 -23
- package/dist/plot/core/auto-place.d.ts +1 -1
- package/dist/plot/core/auto-place.js +1 -1
- package/dist/plot/core/data-transform.d.ts +1 -2
- package/dist/plot/core/data-transform.js +0 -10
- package/dist/plot/core/layout.d.ts +12 -5
- package/dist/plot/core/layout.js +37 -27
- package/dist/plot/core/pan-zoom.svelte.d.ts +2 -4
- package/dist/plot/core/pan-zoom.svelte.js +34 -17
- package/dist/plot/core/placed-tween.svelte.d.ts +3 -0
- package/dist/plot/core/placed-tween.svelte.js +68 -21
- package/dist/plot/core/svg.js +0 -1
- package/dist/plot/core/utils/hierarchy-chart.d.ts +1 -0
- package/dist/plot/core/utils/hierarchy-chart.js +12 -0
- package/dist/plot/core/utils/hierarchy-labels.js +3 -2
- package/dist/plot/histogram/Histogram.svelte +21 -26
- package/dist/plot/sankey/sankey.js +13 -9
- package/dist/plot/scatter/BinnedScatterPlot.svelte +59 -43
- package/dist/plot/scatter/ScatterPlot.svelte +67 -52
- package/dist/plot/scatter/adaptive-density.d.ts +1 -1
- package/dist/plot/scatter/adaptive-density.js +36 -20
- package/dist/plot/sunburst/Sunburst.svelte +20 -20
- package/dist/plot/treemap/Treemap.svelte +175 -60
- package/dist/plot/treemap/Treemap.svelte.d.ts +6 -0
- package/dist/plot/treemap/index.d.ts +1 -0
- package/dist/plot/treemap/labels.d.ts +38 -0
- package/dist/plot/treemap/labels.js +80 -0
- package/dist/plot/treemap/treemap.d.ts +4 -2
- package/dist/plot/treemap/treemap.js +5 -2
- package/dist/rdf/RdfPlot.svelte +17 -35
- package/dist/rdf/calc-rdf.js +59 -44
- package/dist/rdf/index.d.ts +0 -1
- package/dist/sanitize.js +84 -26
- package/dist/scene/SceneCamera.svelte +1 -1
- package/dist/scene/SceneCamera.svelte.d.ts +1 -1
- package/dist/scene/index.d.ts +1 -2
- package/dist/scene/index.js +1 -1
- package/dist/scene/{props.js → props.svelte.js} +15 -1
- package/dist/settings.js +159 -482
- package/dist/spectral/Bands.svelte +151 -158
- package/dist/spectral/BrillouinBandsDos.svelte +4 -19
- package/dist/spectral/Dos.svelte +26 -24
- package/dist/spectral/helpers.js +40 -105
- package/dist/structure/AtomLegend.svelte +8 -13
- package/dist/structure/AtomLegend.svelte.d.ts +2 -3
- package/dist/structure/CellSelect.svelte +35 -10
- package/dist/structure/Structure.svelte +177 -99
- package/dist/structure/Structure.svelte.d.ts +1 -1
- package/dist/structure/StructureCarousel.svelte +10 -3
- package/dist/structure/StructureControls.svelte +8 -26
- package/dist/structure/StructureControls.svelte.d.ts +1 -1
- package/dist/structure/StructureInfoPane.svelte +65 -76
- package/dist/structure/StructureScene.svelte +75 -109
- package/dist/structure/StructureScene.svelte.d.ts +3 -1
- package/dist/structure/StructureViewport.svelte +120 -55
- package/dist/structure/StructureViewport.svelte.d.ts +3 -1
- package/dist/structure/atom-properties.d.ts +1 -1
- package/dist/structure/atom-properties.js +4 -3
- package/dist/structure/bond-order-perception.js +3 -8
- package/dist/structure/bonding.d.ts +0 -1
- package/dist/structure/bonding.js +16 -23
- package/dist/structure/export.js +50 -74
- package/dist/structure/format-detect.js +5 -7
- package/dist/structure/index.js +6 -12
- package/dist/structure/parse.js +38 -100
- package/dist/structure/partial-occupancy.js +7 -4
- package/dist/structure/pbc.js +8 -10
- package/dist/structure/polyhedra.js +5 -4
- package/dist/structure/prop-groups.d.ts +85 -0
- package/dist/structure/prop-groups.js +18 -0
- package/dist/symmetry/SymmetryStats.svelte +0 -4
- package/dist/symmetry/WyckoffTable.svelte +5 -4
- package/dist/symmetry/cell-transform.js +5 -14
- package/dist/symmetry/index.js +6 -9
- package/dist/table/HeatmapTable.svelte +61 -103
- package/dist/table/ToggleMenu.svelte +166 -44
- package/dist/table/ToggleMenu.svelte.d.ts +1 -1
- package/dist/theme/embedded.d.ts +18 -0
- package/dist/theme/embedded.js +206 -0
- package/dist/theme/index.d.ts +4 -0
- package/dist/theme/index.js +1 -2
- package/dist/theme/themes.mjs +31 -60
- package/dist/tooltip/TooltipContent.svelte +1 -1
- package/dist/tooltip/TooltipContent.svelte.d.ts +1 -1
- package/dist/tooltip/index.d.ts +8 -1
- package/dist/trajectory/Trajectory.svelte +140 -120
- package/dist/trajectory/TrajectoryError.svelte +1 -66
- package/dist/trajectory/TrajectoryExportPane.svelte +0 -1
- package/dist/trajectory/TrajectoryInfoPane.svelte +55 -80
- package/dist/trajectory/TrajectoryInfoPane.svelte.d.ts +2 -1
- package/dist/trajectory/extract.js +19 -29
- package/dist/trajectory/format-detect.d.ts +2 -0
- package/dist/trajectory/format-detect.js +7 -5
- package/dist/trajectory/frame-reader.js +3 -4
- package/dist/trajectory/helpers.js +16 -49
- package/dist/trajectory/index.js +2 -8
- package/dist/trajectory/parse/ase.js +1 -1
- package/dist/trajectory/parse/index.d.ts +4 -3
- package/dist/trajectory/parse/index.js +12 -17
- package/dist/trajectory/parse/vaspout-h5.d.ts +0 -1
- package/dist/trajectory/parse/vaspout-h5.js +1 -4
- package/dist/trajectory/parse/xyz.js +48 -2
- package/dist/trajectory/plotting.js +14 -37
- package/dist/xrd/XrdPlot.svelte +43 -68
- package/dist/xrd/calc-xrd.js +22 -58
- package/dist/xrd/parse.js +13 -22
- package/package.json +40 -8
- package/readme.md +6 -5
- package/dist/scene/types.d.ts +0 -26
- package/dist/tooltip/types.d.ts +0 -8
- package/dist/trajectory/constants.d.ts +0 -6
- package/dist/trajectory/constants.js +0 -11
- /package/dist/{scene/types.js → file-viewer/host-protocol.js} +0 -0
- /package/dist/{tooltip/types.js → isosurface/geometry-worker-types.js} +0 -0
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
hierarchy_legend_items,
|
|
27
27
|
is_activation_key,
|
|
28
28
|
node_handler_props,
|
|
29
|
+
observe_height,
|
|
29
30
|
pointer_pos,
|
|
30
31
|
prune_muted_ids,
|
|
31
32
|
safe_hierarchy_layout,
|
|
@@ -42,6 +43,15 @@
|
|
|
42
43
|
import type { Rect, Sides } from '../core/layout'
|
|
43
44
|
import { arrow_nav_target } from '../sunburst/render'
|
|
44
45
|
import type { PositionedArc } from '../sunburst/sunburst'
|
|
46
|
+
import {
|
|
47
|
+
normalize_treemap_label_lines,
|
|
48
|
+
place_treemap_label,
|
|
49
|
+
safe_font_size,
|
|
50
|
+
type TreemapLabelFit,
|
|
51
|
+
type TreemapLabelFormatter,
|
|
52
|
+
type TreemapLabelLine,
|
|
53
|
+
type TreemapLabelPlacement,
|
|
54
|
+
} from './labels'
|
|
45
55
|
import type { TreemapNode, TreemapNodeHandlerProps } from './treemap'
|
|
46
56
|
import { lerp_rects, tile_rects } from './treemap'
|
|
47
57
|
import { DEFAULTS } from '../../settings'
|
|
@@ -52,12 +62,16 @@
|
|
|
52
62
|
import { Tween, type TweenOptions } from 'svelte/motion'
|
|
53
63
|
import { SvelteSet } from 'svelte/reactivity'
|
|
54
64
|
|
|
55
|
-
|
|
65
|
+
// no outer inset by default: cells tile flush with the container (pass
|
|
66
|
+
// `padding` to reserve chart-edge space, e.g. for host-drawn annotations)
|
|
67
|
+
const DEFAULT_PADDING: Required<Sides> = { t: 0, b: 0, l: 0, r: 0 }
|
|
56
68
|
|
|
57
69
|
let {
|
|
58
70
|
data = $bindable([]),
|
|
59
71
|
value_mode = $bindable(DEFAULTS.treemap.value_mode),
|
|
60
|
-
|
|
72
|
+
// descending (unlike Sunburst's input-order default): squarified tiling
|
|
73
|
+
// reads best with the largest cell top-left and smallest bottom-right
|
|
74
|
+
sort = `descending`,
|
|
61
75
|
level_lighten = 0,
|
|
62
76
|
min_fraction = $bindable(DEFAULTS.treemap.min_fraction),
|
|
63
77
|
other_label = `Other`,
|
|
@@ -67,6 +81,11 @@
|
|
|
67
81
|
padding_outer = $bindable(DEFAULTS.treemap.padding_outer),
|
|
68
82
|
show_labels = $bindable(DEFAULTS.treemap.show_labels),
|
|
69
83
|
label_text = $bindable(DEFAULTS.treemap.label_text),
|
|
84
|
+
label_formatter,
|
|
85
|
+
label_fit = `shrink`,
|
|
86
|
+
label_min_font_size = 6,
|
|
87
|
+
label_max_font_size,
|
|
88
|
+
parent_label_font_size = 14,
|
|
70
89
|
zoom_on_click = $bindable(DEFAULTS.treemap.zoom_on_click),
|
|
71
90
|
zoom_root_id = $bindable(null),
|
|
72
91
|
show_breadcrumbs = $bindable(DEFAULTS.treemap.show_breadcrumbs),
|
|
@@ -102,7 +121,7 @@
|
|
|
102
121
|
Omit<BasePlotProps, `change`> & {
|
|
103
122
|
data?: TreemapNode<Metadata> | TreemapNode<Metadata>[]
|
|
104
123
|
value_mode?: SunburstValueMode
|
|
105
|
-
sort?: SunburstSort
|
|
124
|
+
sort?: SunburstSort // default 'descending' (largest top-left); 'none' keeps input order
|
|
106
125
|
level_lighten?: number
|
|
107
126
|
// Aggregate sibling cells below this fraction of the total into one 'Other'
|
|
108
127
|
// cell per parent (only when >= 2 qualify); 0 disables
|
|
@@ -114,6 +133,13 @@
|
|
|
114
133
|
padding_outer?: number // px inset of children within their parent (plotly marker.pad)
|
|
115
134
|
show_labels?: boolean
|
|
116
135
|
label_text?: SunburstLabelText // what labels display (plotly textinfo equivalent)
|
|
136
|
+
// Structured multiline labels. Unlike cell_content, this keeps built-in
|
|
137
|
+
// hover/focus/click and tooltip behavior on the underlying cell.
|
|
138
|
+
label_formatter?: TreemapLabelFormatter<Metadata>
|
|
139
|
+
label_fit?: TreemapLabelFit // shrink-to-fit (default), hide, or clip at max size
|
|
140
|
+
label_min_font_size?: number // px floor used by shrink mode
|
|
141
|
+
label_max_font_size?: number // px ceiling for leaf/cutoff labels
|
|
142
|
+
parent_label_font_size?: number // px size/ceiling for branch header labels
|
|
117
143
|
zoom_on_click?: boolean
|
|
118
144
|
zoom_root_id?: string | number | null // id of the cell the view is rooted on
|
|
119
145
|
show_breadcrumbs?: boolean // clickable ancestor trail when zoomed
|
|
@@ -167,9 +193,9 @@
|
|
|
167
193
|
let pad = $derived(filter_padding(padding, DEFAULT_PADDING))
|
|
168
194
|
let inner_width = $derived(Math.max(0, width - pad.l - pad.r))
|
|
169
195
|
let avail_height = $derived(Math.max(0, height - pad.t - pad.b))
|
|
170
|
-
// measured height of the bottom colorbar
|
|
171
|
-
//
|
|
172
|
-
// area so a bad measurement can't collapse the chart
|
|
196
|
+
// measured height of the bottom colorbar (via observe_height, which resets it
|
|
197
|
+
// to 0 on unmount), reserved from the chart so it never overlaps the cells;
|
|
198
|
+
// capped at half the area so a bad measurement can't collapse the chart
|
|
173
199
|
let colorbar_height = $state(0)
|
|
174
200
|
let colorbar_reserve = $derived(
|
|
175
201
|
colorbar_height > 0 ? Math.min(colorbar_height + 16, avail_height / 2) : 0,
|
|
@@ -289,11 +315,6 @@
|
|
|
289
315
|
)
|
|
290
316
|
const cell_color = (arc: PositionedArc<Metadata>): string =>
|
|
291
317
|
metric?.colors[arc.node_idx] ?? arc.color
|
|
292
|
-
// release the colorbar's reserved chart space when it's not rendered
|
|
293
|
-
$effect(() => {
|
|
294
|
-
if (!metric || colorbar == null) colorbar_height = 0
|
|
295
|
-
})
|
|
296
|
-
|
|
297
318
|
// Hovered cell + its ancestors/descendants stay fully opaque, others dim
|
|
298
319
|
let cell_dim = $derived(compute_node_dim(layout.arcs, muted_ids, hovered_idx))
|
|
299
320
|
|
|
@@ -424,6 +445,12 @@
|
|
|
424
445
|
let cells_clickable = $derived(Boolean(on_node_click) || zoom_on_click)
|
|
425
446
|
|
|
426
447
|
let label_font = $derived(svg_label_font(svg_element))
|
|
448
|
+
// leading "<n>px" of the CSS font shorthand (e.g. "11px sans-serif")
|
|
449
|
+
let label_font_size = $derived.by(() => {
|
|
450
|
+
const leading_num = Number(label_font.match(/^[\d.]+/)?.[0])
|
|
451
|
+
return safe_font_size(leading_num, 11)
|
|
452
|
+
})
|
|
453
|
+
let resolved_parent_label_font_size = $derived(safe_font_size(parent_label_font_size, 14))
|
|
427
454
|
const cached_text_width = make_cached_text_width()
|
|
428
455
|
|
|
429
456
|
// Per-cell label text, measured width, fill/label colors and aria string -
|
|
@@ -440,38 +467,95 @@
|
|
|
440
467
|
)
|
|
441
468
|
|
|
442
469
|
const LABEL_MARGIN = 6 // px clearance between label text and cell edges
|
|
443
|
-
|
|
470
|
+
const label_clip_id = (idx: number) => `treemap-label-clip-${uid}-${idx}`
|
|
471
|
+
const font_for_line = (line: TreemapLabelLine, font_size: number): string => {
|
|
472
|
+
const sized_font = label_font.replace(/(?:\d+(?:\.\d+)?|\.\d+)px/, `${font_size}px`)
|
|
473
|
+
return line.font_weight == null ? sized_font : `${line.font_weight} ${sized_font}`
|
|
474
|
+
}
|
|
475
|
+
const measure_label_line = (line: TreemapLabelLine, font_size: number): number => {
|
|
476
|
+
const measured_width = cached_text_width(line.text, font_for_line(line, font_size))
|
|
477
|
+
// Canvas text metrics can be unavailable during SSR and in lightweight DOM
|
|
478
|
+
// environments. A conservative fallback keeps fitting deterministic there.
|
|
479
|
+
return measured_width > 0 ? measured_width : line.text.length * font_size * 0.6
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Formatter output is layout-independent, so resolve it once per node instead
|
|
483
|
+
// of on every frame of a zoom tween. Without a formatter, fit modes use the
|
|
484
|
+
// richest built-in variant (compound text when configured).
|
|
485
|
+
// The depth-0 root never renders a cell, so the formatter is never invoked
|
|
486
|
+
// for it (for array data it's synthetic and e.g. carries no metadata).
|
|
487
|
+
let label_lines = $derived(
|
|
488
|
+
layout.arcs.map((arc, idx) =>
|
|
489
|
+
arc.depth === 0
|
|
490
|
+
? []
|
|
491
|
+
: normalize_treemap_label_lines(
|
|
492
|
+
label_formatter ? label_formatter(arc) : cell_info[idx].variants[0]?.text,
|
|
493
|
+
),
|
|
494
|
+
),
|
|
495
|
+
)
|
|
496
|
+
|
|
497
|
+
// Label text + placement for a cell; null means hide the label.
|
|
444
498
|
// Branch cells label their header strip (top-left); leaves (and branches at the
|
|
445
499
|
// depth cutoff, which render as plain cells) center their label, rotating 90°
|
|
446
|
-
// in thin-but-tall cells like the icicle shape does.
|
|
447
|
-
|
|
448
|
-
function label_attrs(
|
|
500
|
+
// in thin-but-tall cells like the icicle shape does.
|
|
501
|
+
function place_label(
|
|
449
502
|
arc: PositionedArc<Metadata>,
|
|
450
503
|
rect: Rect,
|
|
451
|
-
):
|
|
452
|
-
const { variants } = cell_info[arc.node_idx]
|
|
453
|
-
const { x, y, width: cell_w, height: cell_h } = rect
|
|
504
|
+
): TreemapLabelPlacement | null {
|
|
454
505
|
// Branches with visible children only ever label their header strip: a
|
|
455
506
|
// centered label would paint over the descendant cells that cover the rest
|
|
456
507
|
// of the cell, so when the strip is missing/too thin the label is dropped
|
|
457
508
|
const has_visible_children = !arc.is_leaf && arc.depth < depth_cutoff
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
509
|
+
|
|
510
|
+
const place = (lines: TreemapLabelLine[]) =>
|
|
511
|
+
place_treemap_label({
|
|
512
|
+
rect,
|
|
513
|
+
lines,
|
|
514
|
+
header: has_visible_children,
|
|
515
|
+
fit: label_fit,
|
|
516
|
+
min_font_size: label_min_font_size,
|
|
517
|
+
max_font_size: has_visible_children
|
|
518
|
+
? resolved_parent_label_font_size
|
|
519
|
+
: (label_max_font_size ?? label_font_size),
|
|
520
|
+
padding_top,
|
|
521
|
+
margin: LABEL_MARGIN,
|
|
522
|
+
measure_line: (line, font_size) =>
|
|
523
|
+
measure_label_line(
|
|
524
|
+
has_visible_children && line.font_weight == null
|
|
525
|
+
? { ...line, font_weight: 600 }
|
|
526
|
+
: line,
|
|
527
|
+
font_size,
|
|
528
|
+
),
|
|
529
|
+
})
|
|
530
|
+
|
|
531
|
+
if (!label_formatter && label_fit === `hide`) {
|
|
532
|
+
for (const { text } of cell_info[arc.node_idx].variants) {
|
|
533
|
+
const placement = place([{ text }])
|
|
534
|
+
if (placement) return placement
|
|
471
535
|
}
|
|
536
|
+
return null
|
|
537
|
+
}
|
|
538
|
+
return place(label_lines[arc.node_idx])
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// hide mode never overflows (unfitting labels return null), so clipPaths are
|
|
542
|
+
// only rendered/applied for the shrink/clip modes (with or without formatter)
|
|
543
|
+
let clip_labels = $derived(label_fit !== `hide`)
|
|
544
|
+
const label_clip_rect = (rect: Rect, header: boolean): Rect => {
|
|
545
|
+
const inset = 1
|
|
546
|
+
const clip_height = header ? Math.min(rect.height, padding_top) : rect.height
|
|
547
|
+
return {
|
|
548
|
+
x: rect.x + inset,
|
|
549
|
+
y: rect.y + inset,
|
|
550
|
+
width: Math.max(0, rect.width - 2 * inset),
|
|
551
|
+
height: Math.max(0, clip_height - 2 * inset),
|
|
472
552
|
}
|
|
473
|
-
return null
|
|
474
553
|
}
|
|
554
|
+
// Defs and visible text share these placements, avoiding duplicate text
|
|
555
|
+
// measurement and fitting work on every frame of a zoom tween.
|
|
556
|
+
let label_placements = $derived(
|
|
557
|
+
new Map(visible_idxs.map((idx) => [idx, place_label(layout.arcs[idx], rects[idx])])),
|
|
558
|
+
)
|
|
475
559
|
|
|
476
560
|
// Legend: one item per depth-1 category, toggling mutes (dims) rather than removes.
|
|
477
561
|
let depth1_arcs = $derived(layout.arcs.filter((arc) => arc.depth === 1))
|
|
@@ -591,6 +675,16 @@
|
|
|
591
675
|
<pattern id={hatch_pattern_id} patternUnits="userSpaceOnUse" width="8" height="8">
|
|
592
676
|
<path class="hatch-pattern-line" d="M-1,1 l2,-2 M0,8 l8,-8 M7,9 l2,-2" />
|
|
593
677
|
</pattern>
|
|
678
|
+
{#if show_labels && !cell_content && clip_labels}
|
|
679
|
+
{#each visible_idxs as idx (idx)}
|
|
680
|
+
{@const label = label_placements.get(idx)}
|
|
681
|
+
{#if label}
|
|
682
|
+
<clipPath id={label_clip_id(idx)}>
|
|
683
|
+
<rect {...label_clip_rect(rects[idx], label.header)} />
|
|
684
|
+
</clipPath>
|
|
685
|
+
{/if}
|
|
686
|
+
{/each}
|
|
687
|
+
{/if}
|
|
594
688
|
</defs>
|
|
595
689
|
<!-- Hover/click delegation sits on the chart group (not the cells group) so
|
|
596
690
|
labels - which carry the same data-treemap-node-idx and are selectable text -
|
|
@@ -650,21 +744,39 @@
|
|
|
650
744
|
{#if show_labels && !cell_content}
|
|
651
745
|
<g class="cell-labels">
|
|
652
746
|
{#each visible_idxs as idx (idx)}
|
|
653
|
-
{@const lbl =
|
|
747
|
+
{@const lbl = label_placements.get(idx)}
|
|
654
748
|
{#if lbl}
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
749
|
+
<!-- Keep the clip on this untransformed wrapper. Applying it to
|
|
750
|
+
rotated text rotates the clipping region and crops the wrong area. -->
|
|
751
|
+
<g clip-path={clip_labels ? `url(#${label_clip_id(idx)})` : undefined}>
|
|
752
|
+
<text
|
|
753
|
+
class="cell-label"
|
|
754
|
+
class:header={lbl.header}
|
|
755
|
+
data-treemap-node-idx={idx}
|
|
756
|
+
x={lbl.x}
|
|
757
|
+
y={lbl.lines[0].y}
|
|
758
|
+
dominant-baseline={lbl.dominant_baseline}
|
|
759
|
+
transform={lbl.transform}
|
|
760
|
+
fill={cell_info[idx].label_fill}
|
|
761
|
+
fill-opacity={cell_dim[idx].label_opacity}
|
|
762
|
+
font-size={lbl.font_size}
|
|
763
|
+
style:cursor={cells_clickable ? `pointer` : `text`}
|
|
764
|
+
>
|
|
765
|
+
{#each lbl.lines as line}
|
|
766
|
+
<tspan
|
|
767
|
+
class={line.class}
|
|
768
|
+
x={lbl.x}
|
|
769
|
+
y={line.y}
|
|
770
|
+
font-size={lbl.font_size * (line.font_scale ?? 1)}
|
|
771
|
+
font-weight={line.font_weight}
|
|
772
|
+
opacity={line.opacity}
|
|
773
|
+
fill={line.fill}
|
|
774
|
+
>
|
|
775
|
+
{line.text}
|
|
776
|
+
</tspan>
|
|
777
|
+
{/each}
|
|
778
|
+
</text>
|
|
779
|
+
</g>
|
|
668
780
|
{/if}
|
|
669
781
|
{/each}
|
|
670
782
|
</g>
|
|
@@ -716,17 +828,19 @@
|
|
|
716
828
|
{/if}
|
|
717
829
|
|
|
718
830
|
{#if metric && colorbar != null}
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
831
|
+
<!-- positioning + height measurement live on ColorBar's own root (via rest
|
|
832
|
+
props + attachment) instead of an extra wrapper div -->
|
|
833
|
+
<ColorBar
|
|
834
|
+
{color_scale}
|
|
835
|
+
range={metric.range}
|
|
836
|
+
{...colorbar}
|
|
837
|
+
wrapper_style="{colorbar?.orientation === `vertical`
|
|
838
|
+
? `--cbar-height: var(--treemap-colorbar-height, 150px);`
|
|
839
|
+
: ``} {colorbar?.wrapper_style ?? ``}"
|
|
840
|
+
style="position: absolute; bottom: var(--treemap-colorbar-bottom, 8px); left: 50%; transform: translateX(-50%); width: var(--treemap-colorbar-width, 40%); min-width: 120px; pointer-events: auto; {colorbar?.style ??
|
|
841
|
+
``}"
|
|
842
|
+
{@attach observe_height((px) => (colorbar_height = px))}
|
|
843
|
+
/>
|
|
730
844
|
{/if}
|
|
731
845
|
|
|
732
846
|
{@render children?.({ height, width, fullscreen })}
|
|
@@ -745,7 +859,9 @@
|
|
|
745
859
|
flex: var(--treemap-flex, 1 1 auto);
|
|
746
860
|
display: var(--treemap-display, flex);
|
|
747
861
|
flex-direction: column;
|
|
748
|
-
|
|
862
|
+
/* no bg shading by default: the cells are the chart; set --treemap-bg to
|
|
863
|
+
add a panel background */
|
|
864
|
+
background: var(--treemap-bg, transparent);
|
|
749
865
|
border-radius: var(--treemap-border-radius, var(--border-radius, 3pt));
|
|
750
866
|
}
|
|
751
867
|
.treemap.fullscreen {
|
|
@@ -787,7 +903,7 @@
|
|
|
787
903
|
background: var(--treemap-btn-bg, light-dark(#e3e6ea, #33383f));
|
|
788
904
|
color: inherit;
|
|
789
905
|
border: none;
|
|
790
|
-
padding:
|
|
906
|
+
padding: var(--treemap-breadcrumbs-padding, 0 14px 0 12px);
|
|
791
907
|
cursor: pointer;
|
|
792
908
|
font: inherit;
|
|
793
909
|
clip-path: polygon(
|
|
@@ -894,7 +1010,6 @@
|
|
|
894
1010
|
}
|
|
895
1011
|
.cell-label {
|
|
896
1012
|
text-anchor: middle;
|
|
897
|
-
dominant-baseline: central;
|
|
898
1013
|
/* selectable so labels can be copied; clicks/hover still reach the underlying
|
|
899
1014
|
cell via data-treemap-node-idx + delegation on the chart group */
|
|
900
1015
|
-webkit-user-select: text;
|
|
@@ -4,6 +4,7 @@ import type { BasePlotProps, LegendConfig, SunburstLabelText, SunburstSort, Sunb
|
|
|
4
4
|
import { ColorBar } from '..';
|
|
5
5
|
import type { Rect, Sides } from '../core/layout';
|
|
6
6
|
import type { PositionedArc } from '../sunburst/sunburst';
|
|
7
|
+
import { type TreemapLabelFit, type TreemapLabelFormatter } from './labels';
|
|
7
8
|
import type { TreemapNode, TreemapNodeHandlerProps } from './treemap';
|
|
8
9
|
import type { ComponentProps, Snippet } from 'svelte';
|
|
9
10
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
@@ -22,6 +23,11 @@ declare function $$render<Metadata extends Record<string, unknown> = Record<stri
|
|
|
22
23
|
padding_outer?: number;
|
|
23
24
|
show_labels?: boolean;
|
|
24
25
|
label_text?: SunburstLabelText;
|
|
26
|
+
label_formatter?: TreemapLabelFormatter<Metadata>;
|
|
27
|
+
label_fit?: TreemapLabelFit;
|
|
28
|
+
label_min_font_size?: number;
|
|
29
|
+
label_max_font_size?: number;
|
|
30
|
+
parent_label_font_size?: number;
|
|
25
31
|
zoom_on_click?: boolean;
|
|
26
32
|
zoom_root_id?: string | number | null;
|
|
27
33
|
show_breadcrumbs?: boolean;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Rect } from '../core/layout';
|
|
2
|
+
import type { TreemapArc } from './treemap';
|
|
3
|
+
export type TreemapLabelFit = `hide` | `shrink` | `clip`;
|
|
4
|
+
export interface TreemapLabelLine {
|
|
5
|
+
text: string;
|
|
6
|
+
class?: string;
|
|
7
|
+
font_scale?: number;
|
|
8
|
+
font_weight?: string | number;
|
|
9
|
+
opacity?: number;
|
|
10
|
+
fill?: string;
|
|
11
|
+
}
|
|
12
|
+
type TreemapLabelContent = string | TreemapLabelLine | null | undefined;
|
|
13
|
+
export type TreemapLabelFormatter<Metadata extends Record<string, unknown> = Record<string, unknown>> = (arc: TreemapArc<Metadata>) => TreemapLabelContent | readonly TreemapLabelContent[];
|
|
14
|
+
export interface TreemapLabelPlacement {
|
|
15
|
+
x: number;
|
|
16
|
+
lines: (TreemapLabelLine & {
|
|
17
|
+
y: number;
|
|
18
|
+
})[];
|
|
19
|
+
font_size: number;
|
|
20
|
+
header: boolean;
|
|
21
|
+
dominant_baseline: `central`;
|
|
22
|
+
transform?: string;
|
|
23
|
+
}
|
|
24
|
+
interface TreemapLabelPlacementOptions {
|
|
25
|
+
rect: Rect;
|
|
26
|
+
lines: readonly TreemapLabelLine[];
|
|
27
|
+
header: boolean;
|
|
28
|
+
fit: TreemapLabelFit;
|
|
29
|
+
min_font_size: number;
|
|
30
|
+
max_font_size: number;
|
|
31
|
+
padding_top: number;
|
|
32
|
+
margin: number;
|
|
33
|
+
measure_line: (line: TreemapLabelLine, font_size: number) => number;
|
|
34
|
+
}
|
|
35
|
+
export declare const safe_font_size: (font_size: number, fallback: number) => number;
|
|
36
|
+
export declare function normalize_treemap_label_lines(content: ReturnType<TreemapLabelFormatter>): TreemapLabelLine[];
|
|
37
|
+
export declare function place_treemap_label({ rect, lines, header, fit, min_font_size, max_font_size, padding_top, margin, measure_line, }: TreemapLabelPlacementOptions): TreemapLabelPlacement | null;
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const LINE_HEIGHT = 1.1;
|
|
2
|
+
const MIN_FONT_SIZE = 0.5;
|
|
3
|
+
const MIN_FONT_SCALE = 0.05;
|
|
4
|
+
// Coerce a user-provided font size to a usable px value (0.5px floor guards
|
|
5
|
+
// against zero/negative sizes; non-finite input falls back)
|
|
6
|
+
export const safe_font_size = (font_size, fallback) => Number.isFinite(font_size) ? Math.max(MIN_FONT_SIZE, font_size) : fallback;
|
|
7
|
+
export function normalize_treemap_label_lines(content) {
|
|
8
|
+
if (content == null)
|
|
9
|
+
return [];
|
|
10
|
+
const lines = Array.isArray(content) ? content : [content];
|
|
11
|
+
return lines.flatMap((line) => {
|
|
12
|
+
const normalized = typeof line === `string` ? { text: line } : line;
|
|
13
|
+
return normalized?.text ? [{ ...normalized }] : [];
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
const line_scale = (line) => Number.isFinite(line.font_scale) ? Math.max(MIN_FONT_SCALE, line.font_scale ?? 1) : 1;
|
|
17
|
+
function block_metrics(lines, font_size, measure_line) {
|
|
18
|
+
let width = 0;
|
|
19
|
+
let height = 0;
|
|
20
|
+
for (const line of lines) {
|
|
21
|
+
const line_font_size = font_size * line_scale(line);
|
|
22
|
+
width = Math.max(width, measure_line(line, line_font_size));
|
|
23
|
+
height += line_font_size * LINE_HEIGHT;
|
|
24
|
+
}
|
|
25
|
+
return { width, height };
|
|
26
|
+
}
|
|
27
|
+
export function place_treemap_label({ rect, lines, header, fit, min_font_size, max_font_size, padding_top, margin, measure_line, }) {
|
|
28
|
+
if (lines.length === 0)
|
|
29
|
+
return null;
|
|
30
|
+
const safe_max_font_size = safe_font_size(max_font_size, 11);
|
|
31
|
+
const safe_min_font_size = Math.min(safe_max_font_size, safe_font_size(min_font_size, MIN_FONT_SIZE));
|
|
32
|
+
const header_height = Math.min(rect.height, padding_top);
|
|
33
|
+
const available_width = Math.max(0, rect.width - 2 * margin);
|
|
34
|
+
const label_height = header ? header_height : rect.height;
|
|
35
|
+
const available_height = Math.max(0, label_height - 2 * (header ? 1 : margin));
|
|
36
|
+
// Single guard for all degenerate geometry (zero/negative/NaN rects, missing
|
|
37
|
+
// header strips, margin-swallowed slivers): negated > 0 so NaN also bails
|
|
38
|
+
// instead of emitting NaN SVG coordinates, same idiom as tile_rects.
|
|
39
|
+
if (!(available_width > 0) || !(available_height > 0))
|
|
40
|
+
return null;
|
|
41
|
+
const max_metrics = block_metrics(lines, safe_max_font_size, measure_line);
|
|
42
|
+
const fit_ratio = (block_width, block_height) => block_width > 0 && block_height > 0
|
|
43
|
+
? Math.min(1, available_width / block_width, available_height / block_height)
|
|
44
|
+
: 0;
|
|
45
|
+
const horizontal_ratio = fit_ratio(max_metrics.width, max_metrics.height);
|
|
46
|
+
// rotated 90°: the block's width runs along the cell's height (swapped args)
|
|
47
|
+
const vertical_ratio = header ? 0 : fit_ratio(max_metrics.height, max_metrics.width);
|
|
48
|
+
const rotated = vertical_ratio > horizontal_ratio;
|
|
49
|
+
const best_ratio = Math.max(horizontal_ratio, vertical_ratio);
|
|
50
|
+
if (fit === `hide` && best_ratio < 1)
|
|
51
|
+
return null;
|
|
52
|
+
const font_size = fit === `shrink`
|
|
53
|
+
? Math.max(safe_min_font_size, safe_max_font_size * best_ratio)
|
|
54
|
+
: safe_max_font_size;
|
|
55
|
+
// text metrics scale linearly with font size, so the block height at the
|
|
56
|
+
// final size derives from the max-size measurement — no re-measuring (which
|
|
57
|
+
// would miss the width cache on every frame of a zoom tween in shrink mode,
|
|
58
|
+
// since the fitted size varies continuously with the animated rect)
|
|
59
|
+
const block_height = max_metrics.height * (font_size / safe_max_font_size);
|
|
60
|
+
const center_x = rect.x + rect.width / 2;
|
|
61
|
+
const center_y = rect.y + label_height / 2;
|
|
62
|
+
const x = header ? rect.x + margin : center_x;
|
|
63
|
+
let line_top = center_y - block_height / 2;
|
|
64
|
+
const placed_lines = lines.map((line) => {
|
|
65
|
+
const font_scale = line_scale(line);
|
|
66
|
+
const line_height = font_size * font_scale * LINE_HEIGHT;
|
|
67
|
+
line_top += line_height / 2;
|
|
68
|
+
const y = line_top;
|
|
69
|
+
line_top += line_height / 2;
|
|
70
|
+
return { ...line, font_scale, y };
|
|
71
|
+
});
|
|
72
|
+
return {
|
|
73
|
+
x,
|
|
74
|
+
lines: placed_lines,
|
|
75
|
+
font_size,
|
|
76
|
+
header,
|
|
77
|
+
dominant_baseline: `central`,
|
|
78
|
+
...(rotated ? { transform: `rotate(-90, ${center_x}, ${center_y})` } : {}),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { Rect } from '../core/layout';
|
|
2
|
-
import type { PositionedArc, SunburstLayoutOptions, SunburstNode, SunburstNodeHandlerProps } from '../sunburst/sunburst';
|
|
2
|
+
import type { PositionedArc, SunburstLayoutOptions, SunburstNode, SunburstNodeHandlerProps, SunburstSort } from '../sunburst/sunburst';
|
|
3
3
|
export type TreemapNode<Metadata = Record<string, unknown>> = SunburstNode<Metadata>;
|
|
4
4
|
export type TreemapArc<Metadata = Record<string, unknown>> = PositionedArc<Metadata>;
|
|
5
5
|
export type TreemapNodeHandlerProps<Metadata = Record<string, unknown>> = SunburstNodeHandlerProps<Metadata>;
|
|
6
|
-
export
|
|
6
|
+
export interface TreemapLayoutOptions extends Omit<SunburstLayoutOptions, `sort`> {
|
|
7
|
+
sort?: SunburstSort;
|
|
8
|
+
}
|
|
7
9
|
export interface TilePadding {
|
|
8
10
|
padding_inner: number;
|
|
9
11
|
padding_top: number;
|
|
@@ -92,8 +92,11 @@ export function lerp_rects(prev, next, t) {
|
|
|
92
92
|
// re-tiles without recomputing tree semantics).
|
|
93
93
|
export function compute_treemap_layout(data, size, opts = {}) {
|
|
94
94
|
// padding fallbacks derive from DEFAULTS.treemap to prevent drift with the component
|
|
95
|
-
const { padding_inner = DEFAULTS.treemap.padding_inner, padding_top = DEFAULTS.treemap.padding_top, padding_outer = DEFAULTS.treemap.padding_outer,
|
|
96
|
-
|
|
95
|
+
const { padding_inner = DEFAULTS.treemap.padding_inner, padding_top = DEFAULTS.treemap.padding_top, padding_outer = DEFAULTS.treemap.padding_outer,
|
|
96
|
+
// descending (matching the Treemap component): squarified tiling reads best
|
|
97
|
+
// with the largest cell top-left and smallest bottom-right
|
|
98
|
+
sort = `descending`, ...tree_opts } = opts;
|
|
99
|
+
const { arcs, root, max_depth } = compute_sunburst_layout(data, { sort, ...tree_opts });
|
|
97
100
|
const rects = tile_rects(arcs, 0, size, { padding_inner, padding_top, padding_outer });
|
|
98
101
|
return { arcs, rects, root, max_depth };
|
|
99
102
|
}
|
package/dist/rdf/RdfPlot.svelte
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
y_axis = {},
|
|
22
22
|
cutoff = 15,
|
|
23
23
|
n_bins = 75,
|
|
24
|
-
pbc
|
|
24
|
+
pbc,
|
|
25
25
|
enable_drop = false,
|
|
26
26
|
on_file_drop,
|
|
27
27
|
loading = $bindable(false),
|
|
@@ -87,41 +87,23 @@
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
const entries = $derived.by(() => {
|
|
90
|
-
const result: RdfEntry[] =
|
|
90
|
+
const result: RdfEntry[] = patterns
|
|
91
|
+
? Array.isArray(patterns)
|
|
92
|
+
? [...patterns]
|
|
93
|
+
: [patterns]
|
|
94
|
+
: []
|
|
91
95
|
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
struct_list.push({
|
|
104
|
-
struct,
|
|
105
|
-
label: format_structure_label(struct, `Crystal ${idx + 1}`),
|
|
106
|
-
}),
|
|
107
|
-
)
|
|
108
|
-
} else if (is_crystal(structures)) {
|
|
109
|
-
struct_list.push({
|
|
110
|
-
struct: structures,
|
|
111
|
-
label: format_structure_label(structures, ``),
|
|
112
|
-
})
|
|
113
|
-
} else {
|
|
114
|
-
Object.entries(structures).forEach(([label, struct]) =>
|
|
115
|
-
struct_list.push({ struct, label: format_structure_label(struct, label) }),
|
|
116
|
-
)
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
drag_dropped.forEach((struct, idx) =>
|
|
120
|
-
struct_list.push({
|
|
121
|
-
struct,
|
|
122
|
-
label: format_structure_label(struct, `Dropped ${idx + 1}`),
|
|
123
|
-
}),
|
|
124
|
-
)
|
|
96
|
+
// Normalize structures prop (single, array, or dict) plus dropped files to labeled list
|
|
97
|
+
const struct_list = [
|
|
98
|
+
...(!structures
|
|
99
|
+
? []
|
|
100
|
+
: Array.isArray(structures)
|
|
101
|
+
? structures.map((struct, idx) => ({ struct, base: `Crystal ${idx + 1}` }))
|
|
102
|
+
: is_crystal(structures)
|
|
103
|
+
? [{ struct: structures, base: `` }]
|
|
104
|
+
: Object.entries(structures).map(([base, struct]) => ({ struct, base }))),
|
|
105
|
+
...drag_dropped.map((struct, idx) => ({ struct, base: `Dropped ${idx + 1}` })),
|
|
106
|
+
].map(({ struct, base }) => ({ struct, label: format_structure_label(struct, base) }))
|
|
125
107
|
|
|
126
108
|
for (const { struct, label } of struct_list) {
|
|
127
109
|
if (mode === `element_pairs`) {
|