matterviz 0.1.0
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/BohrAtom.svelte +105 -0
- package/dist/BohrAtom.svelte.d.ts +21 -0
- package/dist/ControlPanel.svelte +158 -0
- package/dist/ControlPanel.svelte.d.ts +18 -0
- package/dist/Icon.svelte +23 -0
- package/dist/Icon.svelte.d.ts +8 -0
- package/dist/InfoCard.svelte +79 -0
- package/dist/InfoCard.svelte.d.ts +23 -0
- package/dist/Nucleus.svelte +64 -0
- package/dist/Nucleus.svelte.d.ts +16 -0
- package/dist/Spinner.svelte +44 -0
- package/dist/Spinner.svelte.d.ts +7 -0
- package/dist/api.d.ts +6 -0
- package/dist/api.js +30 -0
- package/dist/colors/alloy-colors.json +111 -0
- package/dist/colors/dark-mode-colors.json +111 -0
- package/dist/colors/index.d.ts +26 -0
- package/dist/colors/index.js +72 -0
- package/dist/colors/jmol-colors.json +111 -0
- package/dist/colors/muted-colors.json +111 -0
- package/dist/colors/pastel-colors.json +111 -0
- package/dist/colors/vesta-colors.json +111 -0
- package/dist/composition/BarChart.svelte +260 -0
- package/dist/composition/BarChart.svelte.d.ts +33 -0
- package/dist/composition/BubbleChart.svelte +166 -0
- package/dist/composition/BubbleChart.svelte.d.ts +30 -0
- package/dist/composition/Composition.svelte +73 -0
- package/dist/composition/Composition.svelte.d.ts +27 -0
- package/dist/composition/PieChart.svelte +236 -0
- package/dist/composition/PieChart.svelte.d.ts +36 -0
- package/dist/composition/index.d.ts +5 -0
- package/dist/composition/index.js +5 -0
- package/dist/composition/parse.d.ts +14 -0
- package/dist/composition/parse.js +307 -0
- package/dist/element/ElementHeading.svelte +21 -0
- package/dist/element/ElementHeading.svelte.d.ts +8 -0
- package/dist/element/ElementPhoto.svelte +56 -0
- package/dist/element/ElementPhoto.svelte.d.ts +9 -0
- package/dist/element/ElementStats.svelte +73 -0
- package/dist/element/ElementStats.svelte.d.ts +8 -0
- package/dist/element/ElementTile.svelte +449 -0
- package/dist/element/ElementTile.svelte.d.ts +25 -0
- package/dist/element/data.d.ts +4958 -0
- package/dist/element/data.js +5628 -0
- package/dist/element/index.d.ts +4 -0
- package/dist/element/index.js +4 -0
- package/dist/icons.d.ts +435 -0
- package/dist/icons.js +435 -0
- package/dist/index.d.ts +82 -0
- package/dist/index.js +43 -0
- package/dist/io/decompress.d.ts +16 -0
- package/dist/io/decompress.js +78 -0
- package/dist/io/export.d.ts +9 -0
- package/dist/io/export.js +205 -0
- package/dist/io/parse.d.ts +53 -0
- package/dist/io/parse.js +747 -0
- package/dist/labels.d.ts +31 -0
- package/dist/labels.js +209 -0
- package/dist/material/MaterialCard.svelte +135 -0
- package/dist/material/MaterialCard.svelte.d.ts +10 -0
- package/dist/material/SymmetryCard.svelte +23 -0
- package/dist/material/SymmetryCard.svelte.d.ts +9 -0
- package/dist/material/index.d.ts +2 -0
- package/dist/material/index.js +2 -0
- package/dist/math.d.ts +24 -0
- package/dist/math.js +216 -0
- package/dist/periodic-table/PeriodicTable.svelte +284 -0
- package/dist/periodic-table/PeriodicTable.svelte.d.ts +50 -0
- package/dist/periodic-table/PropertySelect.svelte +20 -0
- package/dist/periodic-table/PropertySelect.svelte.d.ts +13 -0
- package/dist/periodic-table/TableInset.svelte +18 -0
- package/dist/periodic-table/TableInset.svelte.d.ts +9 -0
- package/dist/periodic-table/index.d.ts +9 -0
- package/dist/periodic-table/index.js +3 -0
- package/dist/plot/ColorBar.svelte +414 -0
- package/dist/plot/ColorBar.svelte.d.ts +22 -0
- package/dist/plot/ColorScaleSelect.svelte +31 -0
- package/dist/plot/ColorScaleSelect.svelte.d.ts +15 -0
- package/dist/plot/ElementScatter.svelte +38 -0
- package/dist/plot/ElementScatter.svelte.d.ts +14 -0
- package/dist/plot/Line.svelte +42 -0
- package/dist/plot/Line.svelte.d.ts +15 -0
- package/dist/plot/PlotLegend.svelte +206 -0
- package/dist/plot/PlotLegend.svelte.d.ts +18 -0
- package/dist/plot/ScatterPlot.svelte +1753 -0
- package/dist/plot/ScatterPlot.svelte.d.ts +114 -0
- package/dist/plot/ScatterPlotControls.svelte +505 -0
- package/dist/plot/ScatterPlotControls.svelte.d.ts +33 -0
- package/dist/plot/ScatterPoint.svelte +72 -0
- package/dist/plot/ScatterPoint.svelte.d.ts +17 -0
- package/dist/plot/index.d.ts +168 -0
- package/dist/plot/index.js +46 -0
- package/dist/state.svelte.d.ts +12 -0
- package/dist/state.svelte.js +11 -0
- package/dist/structure/Bond.svelte +68 -0
- package/dist/structure/Bond.svelte.d.ts +13 -0
- package/dist/structure/Lattice.svelte +115 -0
- package/dist/structure/Lattice.svelte.d.ts +15 -0
- package/dist/structure/Structure.svelte +298 -0
- package/dist/structure/Structure.svelte.d.ts +28 -0
- package/dist/structure/StructureCard.svelte +26 -0
- package/dist/structure/StructureCard.svelte.d.ts +9 -0
- package/dist/structure/StructureControls.svelte +383 -0
- package/dist/structure/StructureControls.svelte.d.ts +23 -0
- package/dist/structure/StructureLegend.svelte +130 -0
- package/dist/structure/StructureLegend.svelte.d.ts +17 -0
- package/dist/structure/StructureScene.svelte +331 -0
- package/dist/structure/StructureScene.svelte.d.ts +47 -0
- package/dist/structure/bonding.d.ts +16 -0
- package/dist/structure/bonding.js +150 -0
- package/dist/structure/index.d.ts +98 -0
- package/dist/structure/index.js +114 -0
- package/dist/structure/pbc.d.ts +6 -0
- package/dist/structure/pbc.js +72 -0
- package/dist/trajectory/Sidebar.svelte +412 -0
- package/dist/trajectory/Sidebar.svelte.d.ts +14 -0
- package/dist/trajectory/Trajectory.svelte +1084 -0
- package/dist/trajectory/Trajectory.svelte.d.ts +49 -0
- package/dist/trajectory/TrajectoryError.svelte +120 -0
- package/dist/trajectory/TrajectoryError.svelte.d.ts +12 -0
- package/dist/trajectory/extract.d.ts +5 -0
- package/dist/trajectory/extract.js +157 -0
- package/dist/trajectory/index.d.ts +16 -0
- package/dist/trajectory/index.js +49 -0
- package/dist/trajectory/parse.d.ts +13 -0
- package/dist/trajectory/parse.js +1093 -0
- package/dist/trajectory/plotting.d.ts +12 -0
- package/dist/trajectory/plotting.js +148 -0
- package/license +21 -0
- package/package.json +131 -0
- package/readme.md +95 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
<script lang="ts">"use strict";
|
|
2
|
+
let { symbol = ``, name = ``, shells, adapt_size = false, shell_width = 20, size = adapt_size ? (shells.length + 1) * 2 * shell_width + 50 : 270, base_fill = `white`, orbital_period = 3, nucleus_props = {}, shell_props = {}, electron_props = {}, highlight_shell = null, style = ``, number_electrons = false, electron_label_props = {}, ...rest } = $props();
|
|
3
|
+
// Bohr atom electron orbital period is given by
|
|
4
|
+
// T = (n^3 h^3) / (4pi^2 m K e^4 Z^2) = 1.52 * 10^-16 * n^3 / Z^2 s
|
|
5
|
+
// with n the shell number, Z the atomic number, m the mass of the electron
|
|
6
|
+
let _nucleus_props = $derived({
|
|
7
|
+
r: 20,
|
|
8
|
+
fill: `white`,
|
|
9
|
+
'fill-opacity': `0.3`,
|
|
10
|
+
...nucleus_props,
|
|
11
|
+
});
|
|
12
|
+
let _shell_props = $derived({
|
|
13
|
+
stroke: `white`,
|
|
14
|
+
'stroke-width': 1,
|
|
15
|
+
fill: `none`,
|
|
16
|
+
...shell_props,
|
|
17
|
+
});
|
|
18
|
+
let _electron_props = $derived({
|
|
19
|
+
r: 3,
|
|
20
|
+
stroke: `white`,
|
|
21
|
+
'stroke-width': 1,
|
|
22
|
+
fill: `blue`,
|
|
23
|
+
...electron_props,
|
|
24
|
+
});
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<svg
|
|
28
|
+
fill={base_fill}
|
|
29
|
+
viewBox="-{size / 2}, -{size / 2}, {size}, {size}"
|
|
30
|
+
role="presentation"
|
|
31
|
+
{style}
|
|
32
|
+
{...rest}
|
|
33
|
+
>
|
|
34
|
+
<!-- nucleus -->
|
|
35
|
+
<circle class="nucleus" {..._nucleus_props}>
|
|
36
|
+
{#if name}
|
|
37
|
+
<title>{name}</title>
|
|
38
|
+
{/if}
|
|
39
|
+
</circle>
|
|
40
|
+
{#if symbol}
|
|
41
|
+
<text>{symbol}</text>
|
|
42
|
+
{/if}
|
|
43
|
+
|
|
44
|
+
<!-- electron orbitals -->
|
|
45
|
+
{#each shells as electrons, shell_idx ([electrons, shell_idx])}
|
|
46
|
+
{@const n = shell_idx + 1}
|
|
47
|
+
{@const shell_radius = _nucleus_props.r + n * shell_width}
|
|
48
|
+
{@const active = n === highlight_shell}
|
|
49
|
+
<g class="shell" style:animation-duration="{orbital_period * n ** 1.5}s">
|
|
50
|
+
<circle
|
|
51
|
+
r={shell_radius}
|
|
52
|
+
{..._shell_props}
|
|
53
|
+
style:stroke-width={active ? 2 : 1}
|
|
54
|
+
style:stroke={active ? `yellow` : `white`}
|
|
55
|
+
/>
|
|
56
|
+
|
|
57
|
+
<!-- electrons -->
|
|
58
|
+
{#each Array(electrons) as _, elec_idx (elec_idx)}
|
|
59
|
+
{@const elec_x = Math.cos((2 * Math.PI * elec_idx) / electrons) * shell_radius}
|
|
60
|
+
{@const elec_y = Math.sin((2 * Math.PI * elec_idx) / electrons) * shell_radius}
|
|
61
|
+
<circle class="electron" cx={elec_x} cy={elec_y} {..._electron_props}>
|
|
62
|
+
<title>Electron {elec_idx + 1}</title>
|
|
63
|
+
</circle>
|
|
64
|
+
{#if number_electrons}
|
|
65
|
+
<text
|
|
66
|
+
x={elec_x}
|
|
67
|
+
y={elec_y}
|
|
68
|
+
{...electron_label_props}
|
|
69
|
+
transform="rotate({(elec_idx * 360) / electrons} {elec_x} {elec_y})"
|
|
70
|
+
>
|
|
71
|
+
{#if typeof number_electrons === `function`}
|
|
72
|
+
{number_electrons(elec_idx)}
|
|
73
|
+
{:else if number_electrons === `hierarchical`}
|
|
74
|
+
{shell_idx + 1}.{elec_idx + 1}
|
|
75
|
+
<!-- {:else if [`sequential`, true].includes(number_electrons)} -->
|
|
76
|
+
{:else}
|
|
77
|
+
{@const nth_electron = shells.slice(0, shell_idx).reduce((a, b) => a + b, 0) +
|
|
78
|
+
elec_idx + 1}
|
|
79
|
+
{nth_electron}
|
|
80
|
+
{/if}
|
|
81
|
+
</text>
|
|
82
|
+
{/if}
|
|
83
|
+
{/each}
|
|
84
|
+
</g>
|
|
85
|
+
{/each}
|
|
86
|
+
</svg>
|
|
87
|
+
|
|
88
|
+
<style>
|
|
89
|
+
svg {
|
|
90
|
+
overflow: visible;
|
|
91
|
+
width: 100%;
|
|
92
|
+
}
|
|
93
|
+
g.shell {
|
|
94
|
+
animation: spin-right linear infinite;
|
|
95
|
+
}
|
|
96
|
+
text {
|
|
97
|
+
text-anchor: middle;
|
|
98
|
+
dominant-baseline: central;
|
|
99
|
+
}
|
|
100
|
+
@keyframes spin-right {
|
|
101
|
+
100% {
|
|
102
|
+
transform: rotate(360deg);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
</style>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
symbol?: string;
|
|
3
|
+
name?: string;
|
|
4
|
+
shells: number[];
|
|
5
|
+
adapt_size?: boolean;
|
|
6
|
+
shell_width?: number;
|
|
7
|
+
size?: number;
|
|
8
|
+
base_fill?: string;
|
|
9
|
+
orbital_period?: number;
|
|
10
|
+
nucleus_props?: Record<string, string | number>;
|
|
11
|
+
shell_props?: Record<string, string | number>;
|
|
12
|
+
electron_props?: Record<string, string | number>;
|
|
13
|
+
highlight_shell?: number | null;
|
|
14
|
+
style?: string;
|
|
15
|
+
number_electrons?: boolean | `hierarchical` | `sequential` | ((idx: number) => string);
|
|
16
|
+
electron_label_props?: Record<string, string | number>;
|
|
17
|
+
[key: string]: unknown;
|
|
18
|
+
}
|
|
19
|
+
declare const BohrAtom: import("svelte").Component<Props, {}, "">;
|
|
20
|
+
type BohrAtom = ReturnType<typeof BohrAtom>;
|
|
21
|
+
export default BohrAtom;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
<script lang="ts">import { Icon } from './';
|
|
2
|
+
let { controls_open = $bindable(false), show_controls = true, controls_content, toggle_button = {}, panel_props = {}, show_toggle_button = true, open_icon = `Cross`, closed_icon = `Settings`, toggle_controls_btn, controls, } = $props();
|
|
3
|
+
function on_keydown(event) {
|
|
4
|
+
if (event.key === `Escape`)
|
|
5
|
+
controls_open = false;
|
|
6
|
+
}
|
|
7
|
+
function toggle_controls() {
|
|
8
|
+
controls_open = !controls_open;
|
|
9
|
+
}
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<svelte:window onkeydown={on_keydown} />
|
|
13
|
+
|
|
14
|
+
{#if show_controls}
|
|
15
|
+
{#if show_toggle_button}
|
|
16
|
+
<button
|
|
17
|
+
bind:this={toggle_controls_btn}
|
|
18
|
+
onclick={toggle_controls}
|
|
19
|
+
aria-expanded={controls_open}
|
|
20
|
+
aria-controls="controls-panel"
|
|
21
|
+
title={toggle_button.title ?? (controls_open ? `Close controls` : `Open controls`)}
|
|
22
|
+
{...toggle_button}
|
|
23
|
+
class="controls-toggle {toggle_button.class ?? ``}"
|
|
24
|
+
>
|
|
25
|
+
<Icon
|
|
26
|
+
icon={controls_open ? open_icon : closed_icon}
|
|
27
|
+
style="width: 24px; height: 24px"
|
|
28
|
+
/>
|
|
29
|
+
</button>
|
|
30
|
+
{/if}
|
|
31
|
+
|
|
32
|
+
<div
|
|
33
|
+
class:controls-open={controls_open}
|
|
34
|
+
bind:this={controls}
|
|
35
|
+
role="dialog"
|
|
36
|
+
{...panel_props}
|
|
37
|
+
class="controls {panel_props.class ?? ``}"
|
|
38
|
+
>
|
|
39
|
+
{#if controls_content}
|
|
40
|
+
{@render controls_content()}
|
|
41
|
+
{/if}
|
|
42
|
+
</div>
|
|
43
|
+
{/if}
|
|
44
|
+
|
|
45
|
+
<style>
|
|
46
|
+
.controls-toggle {
|
|
47
|
+
background-color: transparent;
|
|
48
|
+
min-width: 28px;
|
|
49
|
+
height: 28px;
|
|
50
|
+
padding: 0.125rem 0.25rem;
|
|
51
|
+
font-size: 0.8rem;
|
|
52
|
+
box-sizing: border-box;
|
|
53
|
+
}
|
|
54
|
+
.controls-toggle:hover {
|
|
55
|
+
background-color: transparent !important;
|
|
56
|
+
}
|
|
57
|
+
.controls {
|
|
58
|
+
position: absolute;
|
|
59
|
+
left: unset;
|
|
60
|
+
background: transparent;
|
|
61
|
+
border: none;
|
|
62
|
+
display: grid;
|
|
63
|
+
gap: var(--controls-gap, 4pt);
|
|
64
|
+
text-align: var(--controls-text-align, left);
|
|
65
|
+
transition:
|
|
66
|
+
visibility var(--controls-transition-duration, 0.3s),
|
|
67
|
+
opacity var(--controls-transition-duration, 0.3s);
|
|
68
|
+
box-sizing: border-box;
|
|
69
|
+
top: var(--controls-top, 30pt);
|
|
70
|
+
right: var(--controls-right, 6pt);
|
|
71
|
+
background: var(--controls-bg, rgba(10, 10, 10, 0.95));
|
|
72
|
+
padding: var(--controls-padding, 6pt 9pt);
|
|
73
|
+
border-radius: var(--controls-border-radius, 3pt);
|
|
74
|
+
width: var(--controls-width, 20em);
|
|
75
|
+
max-width: var(--controls-max-width, 90cqw);
|
|
76
|
+
color: var(--controls-text-color);
|
|
77
|
+
overflow: auto;
|
|
78
|
+
max-height: var(--controls-max-height, calc(100vh - 3em));
|
|
79
|
+
visibility: hidden;
|
|
80
|
+
opacity: 0;
|
|
81
|
+
z-index: var(--controls-z-index, 1);
|
|
82
|
+
pointer-events: none;
|
|
83
|
+
}
|
|
84
|
+
.controls.controls-open {
|
|
85
|
+
visibility: visible !important;
|
|
86
|
+
opacity: 1 !important;
|
|
87
|
+
pointer-events: auto !important;
|
|
88
|
+
}
|
|
89
|
+
.controls :global(hr) {
|
|
90
|
+
border: none;
|
|
91
|
+
background: var(--controls-hr-bg, gray);
|
|
92
|
+
margin: var(--controls-hr-margin, 0);
|
|
93
|
+
height: var(--controls-hr-height, 0.5px);
|
|
94
|
+
}
|
|
95
|
+
.controls :global(label) {
|
|
96
|
+
display: flex;
|
|
97
|
+
align-items: center;
|
|
98
|
+
gap: var(--controls-label-gap, 2pt);
|
|
99
|
+
}
|
|
100
|
+
.controls :global(input[type='range']) {
|
|
101
|
+
margin-left: auto;
|
|
102
|
+
width: var(--controls-input-range-width, 100px);
|
|
103
|
+
flex-shrink: 0;
|
|
104
|
+
}
|
|
105
|
+
.controls :global(.slider-control input[type='range']) {
|
|
106
|
+
margin-left: 0;
|
|
107
|
+
}
|
|
108
|
+
.controls :global(input[type='number']) {
|
|
109
|
+
box-sizing: border-box;
|
|
110
|
+
text-align: center;
|
|
111
|
+
border-radius: var(--controls-input-num-border-radius, 3pt);
|
|
112
|
+
width: var(--controls-input-num-width, 2.2em);
|
|
113
|
+
border: var(--controls-input-num-border, none);
|
|
114
|
+
background: var(--controls-input-num-bg, rgba(255, 255, 255, 0.15));
|
|
115
|
+
margin-right: 3pt;
|
|
116
|
+
margin-left: var(--controls-input-num-margin-left, 6pt);
|
|
117
|
+
flex-shrink: 0;
|
|
118
|
+
}
|
|
119
|
+
.controls :global(input::-webkit-inner-spin-button) {
|
|
120
|
+
display: none;
|
|
121
|
+
}
|
|
122
|
+
.controls :global(button) {
|
|
123
|
+
width: max-content;
|
|
124
|
+
background-color: var(--controls-btn-bg, rgba(255, 255, 255, 0.2));
|
|
125
|
+
}
|
|
126
|
+
.controls :global(select) {
|
|
127
|
+
margin: var(--controls-select-margin, 0 0 0 5pt);
|
|
128
|
+
color: var(--controls-select-color, white);
|
|
129
|
+
background-color: var(--controls-select-bg, rgba(255, 255, 255, 0.1));
|
|
130
|
+
}
|
|
131
|
+
.controls :global(input[type='color']) {
|
|
132
|
+
width: var(--input-color-width, 40px);
|
|
133
|
+
height: var(--input-color-height, 16px);
|
|
134
|
+
margin: var(--input-color-margin, 0 0 0 5pt);
|
|
135
|
+
border: var(--input-color-border, 1px solid rgba(255, 255, 255, 0.05));
|
|
136
|
+
box-sizing: border-box;
|
|
137
|
+
}
|
|
138
|
+
.controls :global(.section-heading) {
|
|
139
|
+
margin: 8pt 0 2pt;
|
|
140
|
+
font-size: 0.9em;
|
|
141
|
+
color: var(--text-muted, #ccc);
|
|
142
|
+
}
|
|
143
|
+
.controls :global(.control-row) {
|
|
144
|
+
display: flex;
|
|
145
|
+
gap: 4pt;
|
|
146
|
+
align-items: flex-start;
|
|
147
|
+
}
|
|
148
|
+
.controls :global(.control-row label) {
|
|
149
|
+
min-width: 0;
|
|
150
|
+
}
|
|
151
|
+
.controls :global(.control-row label.compact) {
|
|
152
|
+
flex: 0 0 auto;
|
|
153
|
+
margin-right: 8pt;
|
|
154
|
+
}
|
|
155
|
+
.controls :global(.control-row label.slider-control) {
|
|
156
|
+
flex: 1;
|
|
157
|
+
}
|
|
158
|
+
</style>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { IconName } from './icons';
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
interface Props {
|
|
5
|
+
controls_open?: boolean;
|
|
6
|
+
show_controls?: boolean;
|
|
7
|
+
controls_content?: Snippet<[]>;
|
|
8
|
+
toggle_button?: HTMLAttributes<HTMLButtonElement>;
|
|
9
|
+
panel_props?: HTMLAttributes<HTMLDivElement>;
|
|
10
|
+
show_toggle_button?: boolean;
|
|
11
|
+
open_icon?: IconName;
|
|
12
|
+
closed_icon?: IconName;
|
|
13
|
+
toggle_controls_btn?: HTMLButtonElement;
|
|
14
|
+
controls?: HTMLDivElement;
|
|
15
|
+
}
|
|
16
|
+
declare const ControlPanel: import("svelte").Component<Props, {}, "controls_open">;
|
|
17
|
+
type ControlPanel = ReturnType<typeof ControlPanel>;
|
|
18
|
+
export default ControlPanel;
|
package/dist/Icon.svelte
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script lang="ts">import { icon_data } from './icons';
|
|
2
|
+
let { icon, ...rest } = $props();
|
|
3
|
+
const data = $derived.by(() => {
|
|
4
|
+
if (!(icon in icon_data)) {
|
|
5
|
+
console.error(`Icon '${icon}' not found`);
|
|
6
|
+
return icon_data.Alert; // fallback
|
|
7
|
+
}
|
|
8
|
+
return icon_data[icon];
|
|
9
|
+
});
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<svg viewBox={data.viewBox} fill="currentColor" {...rest}>
|
|
13
|
+
<path d={data.path} />
|
|
14
|
+
</svg>
|
|
15
|
+
|
|
16
|
+
<style>
|
|
17
|
+
svg {
|
|
18
|
+
width: 1em;
|
|
19
|
+
height: 1em;
|
|
20
|
+
display: inline-block;
|
|
21
|
+
vertical-align: middle;
|
|
22
|
+
}
|
|
23
|
+
</style>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<script lang="ts">import { format_num } from './';
|
|
2
|
+
let { data = [], title = ``, fallback = ``, fmt = `.2f`, as = `section`, style = null, title_snippet, fallback_snippet, ...rest } = $props();
|
|
3
|
+
// rename fmt as default_fmt internally
|
|
4
|
+
let default_fmt = $derived(fmt);
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<svelte:element this={as} class="info-card" {style} {...rest}>
|
|
8
|
+
{#if title || title_snippet}
|
|
9
|
+
<h2>
|
|
10
|
+
{#if title_snippet}{@render title_snippet()}{:else}
|
|
11
|
+
{@html title}
|
|
12
|
+
{/if}
|
|
13
|
+
</h2>
|
|
14
|
+
{/if}
|
|
15
|
+
{#each data.filter((itm) =>
|
|
16
|
+
(!(`condition` in itm) || itm?.condition) &&
|
|
17
|
+
![undefined, null].includes(itm.value)
|
|
18
|
+
) as
|
|
19
|
+
{ title, value, unit, fmt = default_fmt, tooltip }
|
|
20
|
+
(title + value + unit + fmt)
|
|
21
|
+
}
|
|
22
|
+
<div>
|
|
23
|
+
<span class="title" {title}>
|
|
24
|
+
{@html title}
|
|
25
|
+
</span>
|
|
26
|
+
<strong title={tooltip ?? null}>
|
|
27
|
+
{@html typeof value == `number` ? format_num(value, fmt) : value}
|
|
28
|
+
{#if unit}
|
|
29
|
+
<small>{unit}</small>
|
|
30
|
+
{/if}
|
|
31
|
+
</strong>
|
|
32
|
+
</div>
|
|
33
|
+
{:else}
|
|
34
|
+
{#if fallback_snippet}{@render fallback_snippet()}{:else}
|
|
35
|
+
{fallback}
|
|
36
|
+
{/if}
|
|
37
|
+
{/each}
|
|
38
|
+
</svelte:element>
|
|
39
|
+
|
|
40
|
+
<style>
|
|
41
|
+
.info-card {
|
|
42
|
+
display: grid;
|
|
43
|
+
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
44
|
+
box-sizing: border-box;
|
|
45
|
+
border-radius: var(--ic-radius, 3pt);
|
|
46
|
+
padding: var(--ic-padding, 10pt 12pt);
|
|
47
|
+
margin: var(--ic-margin, 1em 0);
|
|
48
|
+
gap: var(--ic-gap, 10pt 5%);
|
|
49
|
+
background-color: var(--ic-bg, rgba(255, 255, 255, 0.1));
|
|
50
|
+
font-size: var(--ic-font-size);
|
|
51
|
+
width: var(--ic-width);
|
|
52
|
+
}
|
|
53
|
+
h2 {
|
|
54
|
+
grid-column: 1 / -1;
|
|
55
|
+
margin: 0;
|
|
56
|
+
border-bottom: 1px solid var(--ic-title-border-color, rgba(255, 255, 255, 0.3));
|
|
57
|
+
}
|
|
58
|
+
div {
|
|
59
|
+
display: flex;
|
|
60
|
+
justify-content: space-between;
|
|
61
|
+
align-items: center;
|
|
62
|
+
white-space: nowrap;
|
|
63
|
+
gap: var(--ic-value-gap);
|
|
64
|
+
}
|
|
65
|
+
div > span.title {
|
|
66
|
+
text-overflow: ellipsis;
|
|
67
|
+
overflow: hidden;
|
|
68
|
+
}
|
|
69
|
+
strong {
|
|
70
|
+
font-weight: 600;
|
|
71
|
+
margin: var(--ic-value-margin);
|
|
72
|
+
background-color: var(--ic-value-bg, rgba(255, 255, 255, 0.1));
|
|
73
|
+
padding: var(--ic-value-padding, 0 4pt);
|
|
74
|
+
border-radius: var(--ic-value-radius, 3pt);
|
|
75
|
+
}
|
|
76
|
+
strong small {
|
|
77
|
+
font-weight: normal;
|
|
78
|
+
}
|
|
79
|
+
</style>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { PropertyOrigin } from '../types';
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
interface Props {
|
|
4
|
+
data?: {
|
|
5
|
+
title: string;
|
|
6
|
+
value?: string | number | number[] | null | PropertyOrigin;
|
|
7
|
+
unit?: string;
|
|
8
|
+
fmt?: string;
|
|
9
|
+
condition?: boolean | number | null;
|
|
10
|
+
tooltip?: string;
|
|
11
|
+
}[];
|
|
12
|
+
title?: string;
|
|
13
|
+
fallback?: string;
|
|
14
|
+
fmt?: string;
|
|
15
|
+
as?: string;
|
|
16
|
+
style?: string | null;
|
|
17
|
+
title_snippet?: Snippet;
|
|
18
|
+
fallback_snippet?: Snippet;
|
|
19
|
+
[key: string]: unknown;
|
|
20
|
+
}
|
|
21
|
+
declare const InfoCard: import("svelte").Component<Props, {}, "">;
|
|
22
|
+
type InfoCard = ReturnType<typeof InfoCard>;
|
|
23
|
+
export default InfoCard;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<script lang="ts">"use strict";
|
|
2
|
+
let { protons, neutrons, size = 100, radius = $bindable(size / 2), proton_color = `cornflowerblue`, neutron_color = `orange`, stroke = ``, proton_label = ` P`, neutron_label = ` N`, text_color = `white`, symbol = ``, } = $props();
|
|
3
|
+
$effect(() => {
|
|
4
|
+
radius = size / 2;
|
|
5
|
+
});
|
|
6
|
+
let proton_frac = $derived(protons / (protons + neutrons));
|
|
7
|
+
let neutron_frac = $derived(1 - proton_frac);
|
|
8
|
+
let proton_circ = $derived(Math.PI * radius * proton_frac);
|
|
9
|
+
let dash_array = $derived(`0 ${Math.PI * radius - proton_circ} ${proton_circ}`);
|
|
10
|
+
let text = $derived({
|
|
11
|
+
'dominant-baseline': `middle`,
|
|
12
|
+
'text-anchor': `middle`,
|
|
13
|
+
fill: text_color,
|
|
14
|
+
});
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<svg width="100%" height="100%" viewBox="0 0 {size} {size}">
|
|
18
|
+
<circle r={radius} cx={radius} cy={radius} fill={neutron_color} {stroke}>
|
|
19
|
+
<title>Neutrons: {neutrons}</title>
|
|
20
|
+
</circle>
|
|
21
|
+
|
|
22
|
+
<circle
|
|
23
|
+
r={radius / 2}
|
|
24
|
+
cx={radius}
|
|
25
|
+
cy={radius}
|
|
26
|
+
fill={neutron_color}
|
|
27
|
+
stroke={proton_color}
|
|
28
|
+
stroke-width={radius}
|
|
29
|
+
stroke-dasharray={dash_array}
|
|
30
|
+
>
|
|
31
|
+
<title>Protons: {protons}</title>
|
|
32
|
+
</circle>
|
|
33
|
+
|
|
34
|
+
<text
|
|
35
|
+
x={radius + (radius / 2) * Math.cos(Math.PI * -proton_frac)}
|
|
36
|
+
y={radius + (radius / 2) * Math.sin(Math.PI * -proton_frac)}
|
|
37
|
+
{...text}
|
|
38
|
+
>
|
|
39
|
+
{protons} {proton_label}
|
|
40
|
+
</text>
|
|
41
|
+
|
|
42
|
+
<text
|
|
43
|
+
x={radius + (radius / 2) * Math.cos(Math.PI * neutron_frac)}
|
|
44
|
+
y={radius + (radius / 2) * Math.sin(Math.PI * neutron_frac)}
|
|
45
|
+
{...text}
|
|
46
|
+
>
|
|
47
|
+
{neutrons} {neutron_label}
|
|
48
|
+
</text>
|
|
49
|
+
|
|
50
|
+
{#if symbol}
|
|
51
|
+
<text class="symbol" x={radius} y={radius} {...text}>
|
|
52
|
+
{symbol}
|
|
53
|
+
</text>
|
|
54
|
+
{/if}
|
|
55
|
+
</svg>
|
|
56
|
+
|
|
57
|
+
<style>
|
|
58
|
+
text {
|
|
59
|
+
font-size: 11pt;
|
|
60
|
+
}
|
|
61
|
+
text.symbol {
|
|
62
|
+
font-size: 15pt;
|
|
63
|
+
}
|
|
64
|
+
</style>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
protons: number;
|
|
3
|
+
neutrons: number;
|
|
4
|
+
radius?: number;
|
|
5
|
+
size?: number;
|
|
6
|
+
proton_color?: string;
|
|
7
|
+
neutron_color?: string;
|
|
8
|
+
stroke?: string;
|
|
9
|
+
proton_label?: string;
|
|
10
|
+
neutron_label?: string;
|
|
11
|
+
text_color?: string;
|
|
12
|
+
symbol?: string;
|
|
13
|
+
}
|
|
14
|
+
declare const Nucleus: import("svelte").Component<Props, {}, "radius">;
|
|
15
|
+
type Nucleus = ReturnType<typeof Nucleus>;
|
|
16
|
+
export default Nucleus;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<script lang="ts">"use strict";
|
|
2
|
+
let { text = `Loading...`, ...rest } = $props();
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<div class="spinner-overlay" role="status" aria-live="polite" {...rest}>
|
|
6
|
+
<div class="spinner" aria-hidden="true"></div>
|
|
7
|
+
<p>{text}</p>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<style>
|
|
11
|
+
.spinner-overlay {
|
|
12
|
+
position: absolute;
|
|
13
|
+
top: 0;
|
|
14
|
+
left: 0;
|
|
15
|
+
right: 0;
|
|
16
|
+
bottom: 0;
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
align-items: center;
|
|
20
|
+
justify-content: center;
|
|
21
|
+
background: var(--spinner-bg, rgba(255, 255, 255, 0.9));
|
|
22
|
+
color: var(--spinner-text-color, #333);
|
|
23
|
+
backdrop-filter: blur(4px);
|
|
24
|
+
z-index: var(--spinner-z-index, 10);
|
|
25
|
+
}
|
|
26
|
+
.spinner {
|
|
27
|
+
width: var(--spinner-size, 40px);
|
|
28
|
+
height: var(--spinner-size, 40px);
|
|
29
|
+
border: var(--spinner-border-width, 4px) solid var(--spinner-track-color, #f3f3f3);
|
|
30
|
+
border-top: var(--spinner-border-width, 4px) solid var(--spinner-color, #007acc);
|
|
31
|
+
border-radius: 50%;
|
|
32
|
+
animation: spin 1s linear infinite;
|
|
33
|
+
margin-bottom: 1rem;
|
|
34
|
+
box-sizing: border-box;
|
|
35
|
+
}
|
|
36
|
+
@keyframes spin {
|
|
37
|
+
0% {
|
|
38
|
+
transform: rotate(0deg);
|
|
39
|
+
}
|
|
40
|
+
100% {
|
|
41
|
+
transform: rotate(360deg);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
</style>
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const mp_build_bucket = "https://materialsproject-build.s3.amazonaws.com/collections/2022-10-28";
|
|
2
|
+
export declare function decompress(blob: ReadableStream<Uint8Array> | null): Promise<string>;
|
|
3
|
+
export declare function fetch_zipped<T>(url: string, { unzip }?: {
|
|
4
|
+
unzip?: boolean | undefined;
|
|
5
|
+
}): Promise<T | null>;
|
|
6
|
+
export declare function download(data: string | Blob, filename: string, type: string): void;
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// TODO update to get MP details pages working again
|
|
2
|
+
export const mp_build_bucket = `https://materialsproject-build.s3.amazonaws.com/collections/2022-10-28`;
|
|
3
|
+
export async function decompress(blob) {
|
|
4
|
+
const unzip = new DecompressionStream(`gzip`);
|
|
5
|
+
const stream = blob?.pipeThrough(unzip);
|
|
6
|
+
return await new Response(stream).text();
|
|
7
|
+
}
|
|
8
|
+
export async function fetch_zipped(url, { unzip = true } = {}) {
|
|
9
|
+
const response = await fetch(url);
|
|
10
|
+
if (!response.ok) {
|
|
11
|
+
console.error(`${response.status} ${response.statusText} for ${response.url}`);
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
if (!unzip)
|
|
15
|
+
return (await response.blob());
|
|
16
|
+
return JSON.parse(await decompress(response.body));
|
|
17
|
+
}
|
|
18
|
+
// Function to download data to a file
|
|
19
|
+
export function download(data, filename, type) {
|
|
20
|
+
const file = new Blob([data], { type });
|
|
21
|
+
const link = document.createElement(`a`);
|
|
22
|
+
const url = URL.createObjectURL(file);
|
|
23
|
+
link.style.display = `none`;
|
|
24
|
+
link.href = url;
|
|
25
|
+
link.download = filename;
|
|
26
|
+
document.body.appendChild(link);
|
|
27
|
+
link.click();
|
|
28
|
+
link.remove();
|
|
29
|
+
URL.revokeObjectURL(url);
|
|
30
|
+
}
|