matterviz 0.1.1 → 0.1.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.
Files changed (90) hide show
  1. package/dist/DraggablePanel.svelte +339 -0
  2. package/dist/DraggablePanel.svelte.d.ts +28 -0
  3. package/dist/api.js +12 -2
  4. package/dist/attachments.d.ts +8 -0
  5. package/dist/attachments.js +87 -0
  6. package/dist/colors/index.js +6 -6
  7. package/dist/composition/parse.d.ts +4 -0
  8. package/dist/composition/parse.js +6 -6
  9. package/dist/element/ElementTile.svelte +58 -73
  10. package/dist/element/ElementTile.svelte.d.ts +1 -1
  11. package/dist/icons.d.ts +22 -160
  12. package/dist/icons.js +22 -160
  13. package/dist/index.d.ts +6 -3
  14. package/dist/index.js +6 -3
  15. package/dist/io/decompress.d.ts +0 -1
  16. package/dist/io/decompress.js +1 -3
  17. package/dist/io/export.d.ts +5 -1
  18. package/dist/io/export.js +112 -101
  19. package/dist/io/parse.d.ts +3 -0
  20. package/dist/io/parse.js +130 -1
  21. package/dist/labels.d.ts +4 -3
  22. package/dist/labels.js +38 -60
  23. package/dist/math.d.ts +3 -2
  24. package/dist/math.js +4 -2
  25. package/dist/plot/ColorBar.svelte +10 -9
  26. package/dist/plot/Histogram.svelte +348 -0
  27. package/dist/plot/Histogram.svelte.d.ts +44 -0
  28. package/dist/plot/HistogramControls.svelte +346 -0
  29. package/dist/plot/HistogramControls.svelte.d.ts +26 -0
  30. package/dist/plot/PlotLegend.svelte +4 -7
  31. package/dist/plot/ScatterPlot.svelte +79 -232
  32. package/dist/plot/ScatterPlot.svelte.d.ts +9 -4
  33. package/dist/plot/ScatterPlotControls.svelte +301 -346
  34. package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
  35. package/dist/plot/ScatterPoint.svelte +4 -4
  36. package/dist/plot/data-transform.d.ts +13 -0
  37. package/dist/plot/data-transform.js +35 -0
  38. package/dist/plot/formatting.d.ts +1 -0
  39. package/dist/plot/formatting.js +40 -0
  40. package/dist/plot/index.d.ts +13 -1
  41. package/dist/plot/index.js +2 -2
  42. package/dist/plot/interactions.d.ts +5 -0
  43. package/dist/plot/interactions.js +15 -0
  44. package/dist/plot/layout.d.ts +13 -0
  45. package/dist/plot/layout.js +14 -0
  46. package/dist/plot/scales.d.ts +16 -0
  47. package/dist/plot/scales.js +167 -0
  48. package/dist/state.svelte.d.ts +14 -0
  49. package/dist/state.svelte.js +41 -0
  50. package/dist/structure/Bond.svelte +4 -3
  51. package/dist/structure/Bond.svelte.d.ts +4 -5
  52. package/dist/structure/Lattice.svelte +7 -8
  53. package/dist/structure/Structure.svelte +185 -106
  54. package/dist/structure/Structure.svelte.d.ts +4 -5
  55. package/dist/structure/StructureControls.svelte +470 -297
  56. package/dist/structure/StructureControls.svelte.d.ts +19 -5
  57. package/dist/structure/StructureInfoPanel.svelte +395 -0
  58. package/dist/structure/StructureInfoPanel.svelte.d.ts +11 -0
  59. package/dist/structure/StructureLegend.svelte +14 -74
  60. package/dist/structure/StructureLegend.svelte.d.ts +2 -6
  61. package/dist/structure/StructureScene.svelte +37 -10
  62. package/dist/structure/StructureScene.svelte.d.ts +11 -7
  63. package/dist/structure/Vector.svelte +37 -0
  64. package/dist/structure/Vector.svelte.d.ts +13 -0
  65. package/dist/structure/index.d.ts +24 -15
  66. package/dist/structure/index.js +33 -31
  67. package/dist/structure/pbc.js +3 -3
  68. package/dist/theme/ThemeControl.svelte +44 -0
  69. package/dist/theme/ThemeControl.svelte.d.ts +9 -0
  70. package/dist/theme/index.d.ts +33 -0
  71. package/dist/theme/index.js +87 -0
  72. package/dist/trajectory/Trajectory.svelte +419 -343
  73. package/dist/trajectory/Trajectory.svelte.d.ts +8 -4
  74. package/dist/trajectory/TrajectoryError.svelte +18 -13
  75. package/dist/trajectory/TrajectoryInfoPanel.svelte +253 -0
  76. package/dist/trajectory/{Sidebar.svelte.d.ts → TrajectoryInfoPanel.svelte.d.ts} +4 -5
  77. package/dist/trajectory/extract.js +24 -2
  78. package/dist/trajectory/index.d.ts +1 -1
  79. package/dist/trajectory/index.js +1 -1
  80. package/dist/trajectory/parse.d.ts +7 -10
  81. package/dist/trajectory/parse.js +684 -957
  82. package/dist/trajectory/plotting.d.ts +16 -5
  83. package/dist/trajectory/plotting.js +290 -58
  84. package/package.json +38 -24
  85. package/readme.md +20 -2
  86. package/dist/ControlPanel.svelte +0 -158
  87. package/dist/ControlPanel.svelte.d.ts +0 -18
  88. package/dist/structure/StructureCard.svelte +0 -26
  89. package/dist/structure/StructureCard.svelte.d.ts +0 -9
  90. package/dist/trajectory/Sidebar.svelte +0 -412
@@ -0,0 +1,339 @@
1
+ <script lang="ts">import { Icon } from './';
2
+ import { draggable } from './attachments';
3
+ let { show = $bindable(false), show_panel = true, children, toggle_props = {}, open_icon = `Cross`, closed_icon = `Settings`, icon_style = ``, offset = { x: 5, y: 5 }, max_width = `450px`, panel_props = {}, onclose = () => { }, on_drag_start = () => { }, custom_toggle = undefined, toggle_panel_btn, panel_div, has_been_dragged = $bindable(false), currently_dragging = $bindable(false), } = $props();
4
+ let initial_position = $state({ left: `50px`, top: `50px` });
5
+ let show_control_buttons = $state(false);
6
+ // Keyboard shortcuts
7
+ function on_keydown(event) {
8
+ if (event.key === `Escape`) {
9
+ event.preventDefault();
10
+ close_panel();
11
+ }
12
+ }
13
+ // Panel actions
14
+ function toggle_panel() {
15
+ show = !show;
16
+ if (!show)
17
+ onclose();
18
+ }
19
+ function close_panel() {
20
+ show = false;
21
+ onclose();
22
+ }
23
+ function reset_position() {
24
+ if (toggle_panel_btn) {
25
+ const pos = calculate_position();
26
+ initial_position = pos;
27
+ if (panel_div) {
28
+ Object.assign(panel_div.style, {
29
+ left: pos.left,
30
+ top: pos.top,
31
+ right: `auto`,
32
+ bottom: `auto`,
33
+ width: ``,
34
+ });
35
+ }
36
+ }
37
+ // Hide the control buttons after reset
38
+ show_control_buttons = false;
39
+ has_been_dragged = false;
40
+ }
41
+ // Drag handlers
42
+ function handle_drag_start() {
43
+ has_been_dragged = true;
44
+ show_control_buttons = true;
45
+ currently_dragging = true;
46
+ on_drag_start();
47
+ }
48
+ function handle_drag_end() {
49
+ currently_dragging = false;
50
+ }
51
+ // Position calculation
52
+ function calculate_position() {
53
+ if (!toggle_panel_btn)
54
+ return { left: `50px`, top: `50px` };
55
+ const toggle_rect = toggle_panel_btn.getBoundingClientRect();
56
+ const panel_width = panel_div?.getBoundingClientRect().width || 450;
57
+ const positioned_ancestor = toggle_panel_btn.offsetParent;
58
+ const ancestor_rect = positioned_ancestor?.getBoundingClientRect();
59
+ if (!ancestor_rect) {
60
+ // Fallback to document positioning
61
+ const scroll_x = window.scrollX || document.documentElement.scrollLeft;
62
+ const scroll_y = window.scrollY || document.documentElement.scrollTop;
63
+ return {
64
+ left: `${toggle_rect.right - panel_width + (offset.x ?? 5) + scroll_x}px`,
65
+ top: `${toggle_rect.bottom + (offset.y ?? 5) + scroll_y}px`,
66
+ };
67
+ }
68
+ // Position relative to positioned ancestor
69
+ return {
70
+ left: `${toggle_rect.right - ancestor_rect.left - panel_width + (offset.x ?? 5)}px`,
71
+ top: `${toggle_rect.bottom - ancestor_rect.top + (offset.y ?? 5)}px`,
72
+ };
73
+ }
74
+ // Click outside handler
75
+ function handle_click_outside(event) {
76
+ if (!show)
77
+ return;
78
+ const target = event.target;
79
+ const is_toggle_button = toggle_panel_btn &&
80
+ (target === toggle_panel_btn || toggle_panel_btn.contains(target));
81
+ const is_inside_panel = panel_div &&
82
+ (target === panel_div || panel_div.contains(target));
83
+ if (!is_toggle_button && !is_inside_panel && !has_been_dragged &&
84
+ !currently_dragging) {
85
+ close_panel();
86
+ }
87
+ }
88
+ // Button click handler
89
+ function handle_button_click(event, action) {
90
+ event.stopPropagation();
91
+ action();
92
+ }
93
+ // Position panel when shown
94
+ $effect(() => {
95
+ if (show && toggle_panel_btn && !has_been_dragged) {
96
+ const pos = calculate_position();
97
+ initial_position = pos;
98
+ if (panel_div) {
99
+ Object.assign(panel_div.style, {
100
+ left: pos.left,
101
+ top: pos.top,
102
+ right: `auto`,
103
+ bottom: `auto`,
104
+ });
105
+ }
106
+ }
107
+ });
108
+ </script>
109
+
110
+ <svelte:window onkeydown={on_keydown} />
111
+ <svelte:document onclick={handle_click_outside} />
112
+
113
+ {#if show_panel}
114
+ <button
115
+ bind:this={toggle_panel_btn}
116
+ onclick={(event) => handle_button_click(event, custom_toggle || toggle_panel)}
117
+ aria-expanded={show}
118
+ aria-controls="draggable-panel"
119
+ title={toggle_props.title ?? (show ? `Close panel` : `Open panel`)}
120
+ {...toggle_props}
121
+ class="panel-toggle {toggle_props.class ?? ``}"
122
+ >
123
+ <Icon icon={show ? open_icon : closed_icon} style={icon_style} />
124
+ </button>
125
+
126
+ <div
127
+ {@attach draggable({
128
+ handle_selector: `.drag-handle`,
129
+ on_drag_start: handle_drag_start,
130
+ on_drag_end: handle_drag_end,
131
+ })}
132
+ bind:this={panel_div}
133
+ role="dialog"
134
+ aria-label="Draggable panel"
135
+ aria-modal="false"
136
+ style:max-width={max_width}
137
+ style:top={initial_position.top}
138
+ style:left={initial_position.left}
139
+ style:display={show ? `grid` : `none`}
140
+ {...panel_props}
141
+ class="draggable-panel {show ? `panel-open` : ``} {panel_props.class ?? ``}"
142
+ >
143
+ <div class="panel-header">
144
+ <div class="control-buttons">
145
+ {#if show_control_buttons}
146
+ <button
147
+ class="reset-button"
148
+ onclick={(event) => handle_button_click(event, reset_position)}
149
+ title="Reset panel position"
150
+ aria-label="Reset panel position"
151
+ >
152
+ <Icon icon="Reset" style="width: 1.25em; height: 1.25em" />
153
+ </button>
154
+ <button
155
+ class="close-button"
156
+ onclick={(event) => handle_button_click(event, close_panel)}
157
+ title="Close panel"
158
+ aria-label="Close panel"
159
+ >
160
+ <Icon icon="Cross" style="width: 1.25em; height: 1.25em" />
161
+ </button>
162
+ {/if}
163
+ <Icon
164
+ icon="DragIndicator"
165
+ class="drag-handle"
166
+ style="width: 1.25em; height: 1.25em"
167
+ />
168
+ </div>
169
+ </div>
170
+
171
+ {@render children()}
172
+ </div>
173
+ {/if}
174
+
175
+ <style>
176
+ .panel-toggle {
177
+ box-sizing: border-box;
178
+ display: flex;
179
+ place-items: center;
180
+ padding: 4pt;
181
+ border-radius: var(--panel-toggle-border-radius, 3pt);
182
+ background-color: transparent;
183
+ transition: background-color 0.2s;
184
+ }
185
+ .panel-toggle:hover {
186
+ background-color: color-mix(in srgb, currentColor 8%, transparent);
187
+ }
188
+ .draggable-panel {
189
+ position: absolute; /* Use absolute so panel scrolls with page content */
190
+ background: var(--panel-bg, rgba(15, 23, 42, 0.95));
191
+ border: 1px solid var(--panel-border, rgba(255, 255, 255, 0.15));
192
+ border-radius: 6px;
193
+ padding: 5px 15px 15px;
194
+ box-sizing: border-box;
195
+ z-index: 10;
196
+ display: grid;
197
+ gap: 4pt;
198
+ text-align: left;
199
+ /* Exclude position from being transitioned to prevent sluggish dragging */
200
+ transition: opacity 0.3s, background-color 0.3s, border-color 0.3s, box-shadow 0.3s;
201
+ width: 28em;
202
+ max-width: 90cqw;
203
+ overflow: auto;
204
+ max-height: calc(100vh - 3em);
205
+ pointer-events: auto;
206
+ }
207
+ :global(body.fullscreen) .draggable-panel {
208
+ position: fixed !important; /* In fullscreen, we want viewport-relative positioning */
209
+ top: 3.3em !important;
210
+ right: 1em !important;
211
+ left: auto !important;
212
+ }
213
+ /* Panel content styling */
214
+ .draggable-panel :global(hr) {
215
+ border: none;
216
+ background: var(--panel-hr-bg, rgba(255, 255, 255, 0.1));
217
+ margin: 0;
218
+ height: 0.5px;
219
+ }
220
+ .draggable-panel :global(label) {
221
+ display: flex;
222
+ align-items: center;
223
+ gap: 2pt;
224
+ }
225
+ .draggable-panel :global(input[type='range']) {
226
+ margin-left: auto;
227
+ width: 100px;
228
+ flex-shrink: 0;
229
+ }
230
+ .draggable-panel :global(.slider-control input[type='range']) {
231
+ margin-left: 0;
232
+ }
233
+ .draggable-panel :global(input[type='number']) {
234
+ box-sizing: border-box;
235
+ text-align: center;
236
+ border-radius: 3pt;
237
+ width: 2.2em;
238
+ margin: 0 3pt 0 6pt;
239
+ flex-shrink: 0;
240
+ }
241
+ .draggable-panel :global(input::-webkit-inner-spin-button) {
242
+ display: none;
243
+ }
244
+ .draggable-panel :global(button) {
245
+ width: max-content;
246
+ background-color: var(--panel-btn-bg, rgba(255, 255, 255, 0.1));
247
+ }
248
+ .draggable-panel :global(button:hover) {
249
+ background-color: var(--panel-btn-hover-bg, rgba(255, 255, 255, 0.2));
250
+ }
251
+ .draggable-panel :global(select) {
252
+ margin: 0 0 0 5pt;
253
+ }
254
+ .draggable-panel :global(input[type='color']) {
255
+ width: 40px;
256
+ height: 16px;
257
+ margin: 0 0 0 5pt;
258
+ border: 1px solid rgba(255, 255, 255, 0.05);
259
+ box-sizing: border-box;
260
+ }
261
+ .draggable-panel :global(.section-heading) {
262
+ margin: 8pt 0 2pt;
263
+ font-size: 0.9em;
264
+ }
265
+ .draggable-panel :global(.panel-row) {
266
+ display: flex;
267
+ gap: 4pt;
268
+ align-items: flex-start;
269
+ }
270
+ .draggable-panel :global(.panel-row label) {
271
+ min-width: 0;
272
+ }
273
+ .draggable-panel :global(.panel-row label.compact) {
274
+ flex: 0 0 auto;
275
+ margin-right: 8pt;
276
+ }
277
+ .draggable-panel :global(.panel-row label.slider-control) {
278
+ flex: 1;
279
+ }
280
+ /* Panel header styling */
281
+ .draggable-panel .panel-header {
282
+ display: flex;
283
+ justify-content: flex-end;
284
+ align-items: center;
285
+ position: absolute;
286
+ top: 5px;
287
+ left: 5px;
288
+ right: 5px;
289
+ height: 1.3em;
290
+ z-index: 10;
291
+ pointer-events: none; /* Allow events to pass through to children */
292
+ }
293
+ .draggable-panel .control-buttons {
294
+ display: flex;
295
+ gap: 5px;
296
+ align-items: center;
297
+ pointer-events: auto; /* Re-enable pointer events for buttons */
298
+ }
299
+ .draggable-panel :global(.drag-handle) {
300
+ width: 1.3em;
301
+ height: 1.3em;
302
+ cursor: grab;
303
+ border-radius: 3px;
304
+ padding: 2px;
305
+ box-sizing: border-box;
306
+ opacity: 0.6;
307
+ background-color: color-mix(in srgb, currentColor 10%, transparent);
308
+ pointer-events: auto; /* Re-enable pointer events for drag handle */
309
+ }
310
+ .draggable-panel :global(.drag-handle:hover) {
311
+ opacity: 0.8;
312
+ background-color: color-mix(in srgb, currentColor 20%, transparent);
313
+ }
314
+ /* Ensure drag handle cursor changes properly */
315
+ .draggable-panel :global(.drag-handle:active) {
316
+ cursor: grabbing;
317
+ }
318
+ /* Reset and close button styling */
319
+ .draggable-panel .reset-button,
320
+ .draggable-panel .close-button {
321
+ background: none;
322
+ border: none;
323
+ padding: 2px;
324
+ border-radius: 3px;
325
+ box-sizing: border-box;
326
+ display: flex;
327
+ align-items: center;
328
+ justify-content: center;
329
+ transition: all 0.2s ease;
330
+ width: 1.3em;
331
+ height: 1.3em;
332
+ opacity: 0.6;
333
+ background-color: color-mix(in srgb, currentColor 10%, transparent);
334
+ }
335
+ .draggable-panel :where(.reset-button:hover, .close-button:hover) {
336
+ opacity: 0.8;
337
+ background-color: color-mix(in srgb, currentColor 20%, transparent);
338
+ }
339
+ </style>
@@ -0,0 +1,28 @@
1
+ import type { IconName } from './icons';
2
+ import type { Snippet } from 'svelte';
3
+ import type { HTMLAttributes } from 'svelte/elements';
4
+ interface Props {
5
+ show?: boolean;
6
+ show_panel?: boolean;
7
+ children: Snippet<[]>;
8
+ toggle_props?: HTMLAttributes<HTMLButtonElement>;
9
+ open_icon?: IconName;
10
+ closed_icon?: IconName;
11
+ icon_style?: string;
12
+ offset?: {
13
+ x?: number;
14
+ y?: number;
15
+ };
16
+ max_width?: string;
17
+ panel_props?: HTMLAttributes<HTMLDivElement>;
18
+ onclose?: () => void;
19
+ on_drag_start?: () => void;
20
+ custom_toggle?: () => void;
21
+ toggle_panel_btn?: HTMLButtonElement;
22
+ panel_div?: HTMLDivElement;
23
+ has_been_dragged?: boolean;
24
+ currently_dragging?: boolean;
25
+ }
26
+ declare const DraggablePanel: import("svelte").Component<Props, {}, "show" | "has_been_dragged" | "currently_dragging">;
27
+ type DraggablePanel = ReturnType<typeof DraggablePanel>;
28
+ export default DraggablePanel;
package/dist/api.js CHANGED
@@ -15,8 +15,8 @@ export async function fetch_zipped(url, { unzip = true } = {}) {
15
15
  return (await response.blob());
16
16
  return JSON.parse(await decompress(response.body));
17
17
  }
18
- // Function to download data to a file
19
- export function download(data, filename, type) {
18
+ // Original download implementation
19
+ function default_download(data, filename, type) {
20
20
  const file = new Blob([data], { type });
21
21
  const link = document.createElement(`a`);
22
22
  const url = URL.createObjectURL(file);
@@ -28,3 +28,13 @@ export function download(data, filename, type) {
28
28
  link.remove();
29
29
  URL.revokeObjectURL(url);
30
30
  }
31
+ // Function to download data to a file - checks for global override first
32
+ export function download(data, filename, type) {
33
+ // Check if there's a global download override (used by VSCode extension)
34
+ const global_download = globalThis.download;
35
+ if (typeof global_download === `function` && global_download !== download) {
36
+ return global_download(data, filename, type);
37
+ }
38
+ // Use default browser download
39
+ return default_download(data, filename, type);
40
+ }
@@ -0,0 +1,8 @@
1
+ import type { Attachment } from 'svelte/attachments';
2
+ export interface DraggableOptions {
3
+ handle_selector?: string;
4
+ on_drag_start?: (event: MouseEvent) => void;
5
+ on_drag?: (event: MouseEvent) => void;
6
+ on_drag_end?: (event: MouseEvent) => void;
7
+ }
8
+ export declare function draggable(options?: DraggableOptions): Attachment;
@@ -0,0 +1,87 @@
1
+ // Svelte 5 attachment factory to make an element draggable
2
+ // @param options - Configuration options for dragging behavior
3
+ // @returns Attachment function that sets up dragging on an element
4
+ export function draggable(options = {}) {
5
+ return (element) => {
6
+ const node = element;
7
+ // Use simple variables for maximum performance
8
+ let dragging = false;
9
+ let start = { x: 0, y: 0 };
10
+ const initial = { left: 0, top: 0, width: 0 };
11
+ const handle = options.handle_selector
12
+ ? node.querySelector(options.handle_selector)
13
+ : node;
14
+ if (!handle) {
15
+ console.warn(`Draggable: handle not found with selector "${options.handle_selector}"`);
16
+ return;
17
+ }
18
+ function handle_mousedown(event) {
19
+ // Only drag if mousedown is on the handle or its children
20
+ if (!handle?.contains?.(event.target))
21
+ return;
22
+ dragging = true;
23
+ // For position: fixed elements, use getBoundingClientRect for viewport-relative position
24
+ const computed_style = getComputedStyle(node);
25
+ if (computed_style.position === `fixed`) {
26
+ const rect = node.getBoundingClientRect();
27
+ initial.left = rect.left;
28
+ initial.top = rect.top;
29
+ initial.width = rect.width;
30
+ }
31
+ else {
32
+ // For other positioning, use offset values
33
+ initial.left = node.offsetLeft;
34
+ initial.top = node.offsetTop;
35
+ initial.width = node.offsetWidth;
36
+ }
37
+ node.style.left = `${initial.left}px`;
38
+ node.style.top = `${initial.top}px`;
39
+ node.style.width = `${initial.width}px`;
40
+ node.style.right = `auto`; // Prevent conflict with left
41
+ start = { x: event.clientX, y: event.clientY };
42
+ document.body.style.userSelect = `none`; // Prevent text selection during drag
43
+ if (handle)
44
+ handle.style.cursor = `grabbing`;
45
+ globalThis.addEventListener(`mousemove`, handle_mousemove);
46
+ globalThis.addEventListener(`mouseup`, handle_mouseup);
47
+ options.on_drag_start?.(event); // Call optional callback
48
+ }
49
+ function handle_mousemove(event) {
50
+ if (!dragging)
51
+ return;
52
+ // Use the exact same calculation as the fast old implementation
53
+ const dx = event.clientX - start.x;
54
+ const dy = event.clientY - start.y;
55
+ node.style.left = `${initial.left + dx}px`;
56
+ node.style.top = `${initial.top + dy}px`;
57
+ // Only call callback if it exists (minimize overhead)
58
+ if (options.on_drag)
59
+ options.on_drag(event);
60
+ }
61
+ function handle_mouseup(event) {
62
+ if (!dragging)
63
+ return;
64
+ dragging = false;
65
+ event.stopPropagation();
66
+ document.body.style.userSelect = ``;
67
+ if (handle)
68
+ handle.style.cursor = `grab`;
69
+ globalThis.removeEventListener(`mousemove`, handle_mousemove);
70
+ globalThis.removeEventListener(`mouseup`, handle_mouseup);
71
+ options.on_drag_end?.(event); // Call optional callback
72
+ }
73
+ if (handle) {
74
+ handle.addEventListener(`mousedown`, handle_mousedown);
75
+ handle.style.cursor = `grab`;
76
+ }
77
+ // Return cleanup function (this is the attachment pattern)
78
+ return () => {
79
+ globalThis.removeEventListener(`mousemove`, handle_mousemove);
80
+ globalThis.removeEventListener(`mouseup`, handle_mouseup);
81
+ if (handle) {
82
+ handle.removeEventListener(`mousedown`, handle_mousedown);
83
+ handle.style.cursor = ``; // Reset cursor
84
+ }
85
+ };
86
+ };
87
+ }
@@ -1,11 +1,11 @@
1
1
  import { rgb } from 'd3-color';
2
2
  import * as d3_sc from 'd3-scale-chromatic';
3
- import alloy_colors from './alloy-colors.json';
4
- import dark_mode_colors from './dark-mode-colors.json';
5
- import jmol_colors from './jmol-colors.json';
6
- import muted_colors from './muted-colors.json';
7
- import pastel_colors from './pastel-colors.json';
8
- import vesta_colors from './vesta-colors.json';
3
+ import alloy_colors from './alloy-colors.json' with { type: 'json' };
4
+ import dark_mode_colors from './dark-mode-colors.json' with { type: 'json' };
5
+ import jmol_colors from './jmol-colors.json' with { type: 'json' };
6
+ import muted_colors from './muted-colors.json' with { type: 'json' };
7
+ import pastel_colors from './pastel-colors.json' with { type: 'json' };
8
+ import vesta_colors from './vesta-colors.json' with { type: 'json' };
9
9
  // color values have to be in hex format since that's the only format
10
10
  // <input type="color"> supports
11
11
  // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/color#value
@@ -1,4 +1,8 @@
1
1
  import type { CompositionType, ElementSymbol } from '..';
2
+ export declare const atomic_number_to_symbol: Partial<Record<number, ElementSymbol>>;
3
+ export declare const symbol_to_atomic_number: Partial<Record<ElementSymbol, number>>;
4
+ export declare const atomic_weights: Map<"K" | "H" | "He" | "Li" | "Be" | "B" | "C" | "N" | "O" | "F" | "Ne" | "Na" | "Mg" | "Al" | "Si" | "P" | "S" | "Cl" | "Ar" | "Ca" | "Sc" | "Ti" | "V" | "Cr" | "Mn" | "Fe" | "Co" | "Ni" | "Cu" | "Zn" | "Ga" | "Ge" | "As" | "Se" | "Br" | "Kr" | "Rb" | "Sr" | "Y" | "Zr" | "Nb" | "Mo" | "Tc" | "Ru" | "Rh" | "Pd" | "Ag" | "Cd" | "In" | "Sn" | "Sb" | "Te" | "I" | "Xe" | "Cs" | "Ba" | "La" | "Ce" | "Pr" | "Nd" | "Pm" | "Sm" | "Eu" | "Gd" | "Tb" | "Dy" | "Ho" | "Er" | "Tm" | "Yb" | "Lu" | "Hf" | "Ta" | "W" | "Re" | "Os" | "Ir" | "Pt" | "Au" | "Hg" | "Tl" | "Pb" | "Bi" | "Po" | "At" | "Rn" | "Fr" | "Ra" | "Ac" | "Th" | "Pa" | "U" | "Np" | "Pu" | "Am" | "Cm" | "Bk" | "Cf" | "Es" | "Fm" | "Md" | "No" | "Lr" | "Rf" | "Db" | "Sg" | "Bh" | "Hs" | "Mt" | "Ds" | "Rg" | "Cn" | "Nh" | "Fl" | "Mc" | "Lv" | "Ts" | "Og", number>;
5
+ export declare const element_electronegativity_map: Map<"K" | "H" | "He" | "Li" | "Be" | "B" | "C" | "N" | "O" | "F" | "Ne" | "Na" | "Mg" | "Al" | "Si" | "P" | "S" | "Cl" | "Ar" | "Ca" | "Sc" | "Ti" | "V" | "Cr" | "Mn" | "Fe" | "Co" | "Ni" | "Cu" | "Zn" | "Ga" | "Ge" | "As" | "Se" | "Br" | "Kr" | "Rb" | "Sr" | "Y" | "Zr" | "Nb" | "Mo" | "Tc" | "Ru" | "Rh" | "Pd" | "Ag" | "Cd" | "In" | "Sn" | "Sb" | "Te" | "I" | "Xe" | "Cs" | "Ba" | "La" | "Ce" | "Pr" | "Nd" | "Pm" | "Sm" | "Eu" | "Gd" | "Tb" | "Dy" | "Ho" | "Er" | "Tm" | "Yb" | "Lu" | "Hf" | "Ta" | "W" | "Re" | "Os" | "Ir" | "Pt" | "Au" | "Hg" | "Tl" | "Pb" | "Bi" | "Po" | "At" | "Rn" | "Fr" | "Ra" | "Ac" | "Th" | "Pa" | "U" | "Np" | "Pu" | "Am" | "Cm" | "Bk" | "Cf" | "Es" | "Fm" | "Md" | "No" | "Lr" | "Rf" | "Db" | "Sg" | "Bh" | "Hs" | "Mt" | "Ds" | "Rg" | "Cn" | "Nh" | "Fl" | "Mc" | "Lv" | "Ts" | "Og", number>;
2
6
  export declare function atomic_number_to_element_symbol(atomic_number: number): ElementSymbol | null;
3
7
  export declare function element_symbol_to_atomic_number(symbol: ElementSymbol): number | null;
4
8
  export declare function convert_atomic_numbers_to_symbols(atomic_composition: Record<number, number>): CompositionType;
@@ -1,16 +1,16 @@
1
1
  import { elem_symbols } from '..';
2
2
  import element_data from '../element/data';
3
3
  // Create a mapping from atomic numbers to element symbols
4
- const atomic_number_to_symbol = {};
5
- const symbol_to_atomic_number = {};
4
+ export const atomic_number_to_symbol = {};
5
+ export const symbol_to_atomic_number = {};
6
6
  // Create mass/electronegativity maps for O(1) lookups in loops below
7
- const element_atomic_mass_map = new Map();
8
- const element_electronegativity_map = new Map();
7
+ export const atomic_weights = new Map();
8
+ export const element_electronegativity_map = new Map();
9
9
  // Populate maps at module load time
10
10
  for (const element of element_data) {
11
11
  atomic_number_to_symbol[element.number] = element.symbol;
12
12
  symbol_to_atomic_number[element.symbol] = element.number;
13
- element_atomic_mass_map.set(element.symbol, element.atomic_mass);
13
+ atomic_weights.set(element.symbol, element.atomic_mass);
14
14
  element_electronegativity_map.set(element.symbol, element.electronegativity ?? 0);
15
15
  }
16
16
  // Convert atomic number to element symbol
@@ -134,7 +134,7 @@ export function composition_to_percentages(composition, by_weight = false) {
134
134
  // Calculate weight for each element
135
135
  for (const [element, amount] of Object.entries(composition)) {
136
136
  if (typeof amount === `number` && amount > 0) {
137
- const atomic_mass = element_atomic_mass_map.get(element);
137
+ const atomic_mass = atomic_weights.get(element);
138
138
  if (atomic_mass === undefined) {
139
139
  throw new Error(`Unknown element: ${element}`);
140
140
  }