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.
- package/dist/DraggablePanel.svelte +339 -0
- package/dist/DraggablePanel.svelte.d.ts +28 -0
- package/dist/api.js +12 -2
- package/dist/attachments.d.ts +8 -0
- package/dist/attachments.js +87 -0
- package/dist/colors/index.js +6 -6
- package/dist/composition/parse.d.ts +4 -0
- package/dist/composition/parse.js +6 -6
- package/dist/element/ElementTile.svelte +58 -73
- package/dist/element/ElementTile.svelte.d.ts +1 -1
- package/dist/icons.d.ts +22 -160
- package/dist/icons.js +22 -160
- package/dist/index.d.ts +6 -3
- package/dist/index.js +6 -3
- package/dist/io/decompress.d.ts +0 -1
- package/dist/io/decompress.js +1 -3
- package/dist/io/export.d.ts +5 -1
- package/dist/io/export.js +112 -101
- package/dist/io/parse.d.ts +3 -0
- package/dist/io/parse.js +130 -1
- package/dist/labels.d.ts +4 -3
- package/dist/labels.js +38 -60
- package/dist/math.d.ts +3 -2
- package/dist/math.js +4 -2
- package/dist/plot/ColorBar.svelte +10 -9
- package/dist/plot/Histogram.svelte +348 -0
- package/dist/plot/Histogram.svelte.d.ts +44 -0
- package/dist/plot/HistogramControls.svelte +346 -0
- package/dist/plot/HistogramControls.svelte.d.ts +26 -0
- package/dist/plot/PlotLegend.svelte +4 -7
- package/dist/plot/ScatterPlot.svelte +79 -232
- package/dist/plot/ScatterPlot.svelte.d.ts +9 -4
- package/dist/plot/ScatterPlotControls.svelte +301 -346
- package/dist/plot/ScatterPlotControls.svelte.d.ts +1 -1
- package/dist/plot/ScatterPoint.svelte +4 -4
- package/dist/plot/data-transform.d.ts +13 -0
- package/dist/plot/data-transform.js +35 -0
- package/dist/plot/formatting.d.ts +1 -0
- package/dist/plot/formatting.js +40 -0
- package/dist/plot/index.d.ts +13 -1
- package/dist/plot/index.js +2 -2
- package/dist/plot/interactions.d.ts +5 -0
- package/dist/plot/interactions.js +15 -0
- package/dist/plot/layout.d.ts +13 -0
- package/dist/plot/layout.js +14 -0
- package/dist/plot/scales.d.ts +16 -0
- package/dist/plot/scales.js +167 -0
- package/dist/state.svelte.d.ts +14 -0
- package/dist/state.svelte.js +41 -0
- package/dist/structure/Bond.svelte +4 -3
- package/dist/structure/Bond.svelte.d.ts +4 -5
- package/dist/structure/Lattice.svelte +7 -8
- package/dist/structure/Structure.svelte +185 -106
- package/dist/structure/Structure.svelte.d.ts +4 -5
- package/dist/structure/StructureControls.svelte +470 -297
- package/dist/structure/StructureControls.svelte.d.ts +19 -5
- package/dist/structure/StructureInfoPanel.svelte +395 -0
- package/dist/structure/StructureInfoPanel.svelte.d.ts +11 -0
- package/dist/structure/StructureLegend.svelte +14 -74
- package/dist/structure/StructureLegend.svelte.d.ts +2 -6
- package/dist/structure/StructureScene.svelte +37 -10
- package/dist/structure/StructureScene.svelte.d.ts +11 -7
- package/dist/structure/Vector.svelte +37 -0
- package/dist/structure/Vector.svelte.d.ts +13 -0
- package/dist/structure/index.d.ts +24 -15
- package/dist/structure/index.js +33 -31
- package/dist/structure/pbc.js +3 -3
- package/dist/theme/ThemeControl.svelte +44 -0
- package/dist/theme/ThemeControl.svelte.d.ts +9 -0
- package/dist/theme/index.d.ts +33 -0
- package/dist/theme/index.js +87 -0
- package/dist/trajectory/Trajectory.svelte +419 -343
- package/dist/trajectory/Trajectory.svelte.d.ts +8 -4
- package/dist/trajectory/TrajectoryError.svelte +18 -13
- package/dist/trajectory/TrajectoryInfoPanel.svelte +253 -0
- package/dist/trajectory/{Sidebar.svelte.d.ts → TrajectoryInfoPanel.svelte.d.ts} +4 -5
- package/dist/trajectory/extract.js +24 -2
- package/dist/trajectory/index.d.ts +1 -1
- package/dist/trajectory/index.js +1 -1
- package/dist/trajectory/parse.d.ts +7 -10
- package/dist/trajectory/parse.js +684 -957
- package/dist/trajectory/plotting.d.ts +16 -5
- package/dist/trajectory/plotting.js +290 -58
- package/package.json +38 -24
- package/readme.md +20 -2
- package/dist/ControlPanel.svelte +0 -158
- package/dist/ControlPanel.svelte.d.ts +0 -18
- package/dist/structure/StructureCard.svelte +0 -26
- package/dist/structure/StructureCard.svelte.d.ts +0 -9
- 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
|
-
//
|
|
19
|
-
|
|
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
|
+
}
|
package/dist/colors/index.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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 =
|
|
137
|
+
const atomic_mass = atomic_weights.get(element);
|
|
138
138
|
if (atomic_mass === undefined) {
|
|
139
139
|
throw new Error(`Unknown element: ${element}`);
|
|
140
140
|
}
|