@tellegen/svelte 0.3.0 → 0.4.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/README.md +233 -0
- package/dist/Sparkline.svelte +2 -2
- package/dist/TellegenMap.svelte +201 -47
- package/dist/TellegenMap.svelte.d.ts +45 -0
- package/dist/TellegenProvider.svelte +36 -4
- package/dist/TellegenShell.svelte +14 -42
- package/dist/TellegenViewer.svelte +12 -2
- package/dist/api.d.ts +10 -2
- package/dist/api.js +2 -0
- package/dist/basemap.d.ts +192 -0
- package/dist/basemap.js +259 -0
- package/dist/colors.d.ts +18 -0
- package/dist/colors.js +26 -0
- package/dist/components/AppFooter.svelte +3 -2
- package/dist/components/AppHeader.svelte +21 -8
- package/dist/components/BindingLines.svelte +1 -1
- package/dist/components/BusPicker.svelte +3 -3
- package/dist/components/DemandSlider.svelte +16 -7
- package/dist/components/DiagramCanvas.svelte +1 -1
- package/dist/components/DisplayControls.svelte +5 -1
- package/dist/components/DropZone.svelte +1 -1
- package/dist/components/FormulationSelector.svelte +1 -1
- package/dist/components/LocalCaseDetails.svelte +6 -3
- package/dist/components/McResults.svelte +98 -35
- package/dist/components/McResults.svelte.d.ts +8 -2
- package/dist/components/ModelDetails.svelte +5 -5
- package/dist/components/MulticonductorDetails.svelte +9 -9
- package/dist/components/NotificationCenter.svelte +4 -2
- package/dist/components/PanelCard.svelte +72 -7
- package/dist/components/PanelHost.svelte +59 -34
- package/dist/components/PanelHost.svelte.d.ts +4 -1
- package/dist/components/PlacementCue.svelte +1 -1
- package/dist/components/PowerFlowDetails.svelte +1 -1
- package/dist/components/RatingSlider.svelte +14 -5
- package/dist/components/RestoreDefaultsButton.svelte +1 -1
- package/dist/components/SavedNetworkDetails.svelte +3 -3
- package/dist/components/SensitivityReadout.svelte +2 -2
- package/dist/components/ShellEnvironment.svelte +51 -0
- package/dist/components/ShellEnvironment.svelte.d.ts +18 -0
- package/dist/components/SizeLegend.svelte +2 -2
- package/dist/components/SolveCard.svelte +3 -3
- package/dist/components/TopMovers.svelte +1 -1
- package/dist/components.d.ts +3 -0
- package/dist/components.js +3 -0
- package/dist/context.svelte.d.ts +8 -1
- package/dist/context.svelte.js +8 -3
- package/dist/controller.svelte.d.ts +65 -5
- package/dist/controller.svelte.js +262 -76
- package/dist/format.d.ts +9 -0
- package/dist/format.js +12 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +2 -0
- package/dist/map.d.ts +4 -2
- package/dist/map.js +2 -1
- package/dist/maplibre-worker.d.ts +5 -0
- package/dist/maplibre-worker.dev.d.ts +11 -0
- package/dist/maplibre-worker.dev.js +15 -0
- package/dist/maplibre-worker.js +8 -0
- package/dist/notices.svelte.js +5 -1
- package/dist/panels.svelte.d.ts +6 -0
- package/dist/panels.svelte.js +31 -1
- package/dist/slider.d.ts +12 -0
- package/dist/slider.js +31 -0
- package/dist/state.svelte.d.ts +19 -2
- package/dist/state.svelte.js +12 -0
- package/dist/styles.css +59 -6
- package/package.json +13 -8
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { tick } from 'svelte';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
focusFirst,
|
|
5
|
+
getPanelLayout,
|
|
6
|
+
type PanelRegistration,
|
|
7
|
+
type PanelSide
|
|
8
|
+
} from '../panels.svelte.js';
|
|
4
9
|
let {
|
|
5
10
|
panel,
|
|
6
11
|
floating = false,
|
|
@@ -12,6 +17,7 @@
|
|
|
12
17
|
} = $props();
|
|
13
18
|
const layout = getPanelLayout();
|
|
14
19
|
let menu = $state(false);
|
|
20
|
+
let menuAnchor: HTMLDivElement | undefined = $state();
|
|
15
21
|
let card: HTMLElement;
|
|
16
22
|
const bounds = $derived(layout.bounds(panel));
|
|
17
23
|
const preferred = $derived(!floating && !mobile && layout.preferred(panel));
|
|
@@ -86,11 +92,40 @@
|
|
|
86
92
|
}
|
|
87
93
|
async function close() {
|
|
88
94
|
const title = panel.title;
|
|
95
|
+
const position = layout.position(panel);
|
|
96
|
+
const dock = position === 'floating' ? panel.side : position;
|
|
97
|
+
const opener = layout.opener(panel);
|
|
89
98
|
layout.close(panel);
|
|
90
99
|
await tick();
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
100
|
+
// The toolbar button for the panel; a host without the toolbar gets the
|
|
101
|
+
// control that opened it, else the panel's dock (the drawer when narrow).
|
|
102
|
+
focusFirst([
|
|
103
|
+
document.querySelector<HTMLElement>(
|
|
104
|
+
`.panel-toolbar button[aria-label="${CSS.escape(title)}"]`
|
|
105
|
+
),
|
|
106
|
+
opener,
|
|
107
|
+
document.querySelector<HTMLElement>(`[data-panel-dock="${dock}"], [data-panel-drawer]`)
|
|
108
|
+
]);
|
|
109
|
+
}
|
|
110
|
+
// Keyboard users can scroll an overflowing panel only through focus, so the
|
|
111
|
+
// content becomes a labelled, focusable region exactly while it overflows.
|
|
112
|
+
let overflowing = $state(false);
|
|
113
|
+
function trackOverflow(content: HTMLElement) {
|
|
114
|
+
const sync = () => (overflowing = content.scrollHeight > content.clientHeight + 1);
|
|
115
|
+
const resize = new ResizeObserver(sync);
|
|
116
|
+
const observe = () => {
|
|
117
|
+
resize.disconnect();
|
|
118
|
+
resize.observe(content);
|
|
119
|
+
for (const child of content.children) resize.observe(child);
|
|
120
|
+
sync();
|
|
121
|
+
};
|
|
122
|
+
const children = new MutationObserver(observe);
|
|
123
|
+
children.observe(content, { childList: true });
|
|
124
|
+
observe();
|
|
125
|
+
return () => {
|
|
126
|
+
resize.disconnect();
|
|
127
|
+
children.disconnect();
|
|
128
|
+
};
|
|
94
129
|
}
|
|
95
130
|
</script>
|
|
96
131
|
|
|
@@ -98,6 +133,9 @@
|
|
|
98
133
|
onkeydown={(event) => {
|
|
99
134
|
if (event.key === 'Escape') menu = false;
|
|
100
135
|
}}
|
|
136
|
+
onpointerdown={(event) => {
|
|
137
|
+
if (menu && !menuAnchor?.contains(event.target as Node)) menu = false;
|
|
138
|
+
}}
|
|
101
139
|
/>
|
|
102
140
|
<aside
|
|
103
141
|
bind:this={card}
|
|
@@ -127,7 +165,7 @@
|
|
|
127
165
|
>
|
|
128
166
|
{/if}
|
|
129
167
|
<div class="header-actions">{@render panel.headerActions?.()}</div>
|
|
130
|
-
{#if !mobile}<div class="menu-anchor">
|
|
168
|
+
{#if !mobile}<div class="menu-anchor" bind:this={menuAnchor}>
|
|
131
169
|
<button
|
|
132
170
|
class="icon"
|
|
133
171
|
aria-label={`${panel.title} panel options`}
|
|
@@ -150,7 +188,7 @@
|
|
|
150
188
|
</div>{/if}
|
|
151
189
|
</div>{/if}
|
|
152
190
|
<button
|
|
153
|
-
class="icon"
|
|
191
|
+
class="icon close"
|
|
154
192
|
aria-label={`Close ${panel.title.toLowerCase()} panel`}
|
|
155
193
|
title="Close panel"
|
|
156
194
|
onclick={close}
|
|
@@ -160,7 +198,18 @@
|
|
|
160
198
|
>
|
|
161
199
|
</button>
|
|
162
200
|
</header>
|
|
163
|
-
|
|
201
|
+
<!-- A scroll container must take focus for keyboard scrolling, and this one
|
|
202
|
+
takes it only while it overflows. -->
|
|
203
|
+
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
204
|
+
<div
|
|
205
|
+
class="panel-content"
|
|
206
|
+
{@attach trackOverflow}
|
|
207
|
+
tabindex={overflowing ? 0 : undefined}
|
|
208
|
+
role={overflowing ? 'region' : undefined}
|
|
209
|
+
aria-label={overflowing ? `${panel.title} panel content` : undefined}
|
|
210
|
+
>
|
|
211
|
+
{@render panel.children()}
|
|
212
|
+
</div>
|
|
164
213
|
{#if floating}<button
|
|
165
214
|
class="resize"
|
|
166
215
|
aria-label={`Resize ${panel.title} panel`}
|
|
@@ -238,6 +287,19 @@
|
|
|
238
287
|
.icon svg {
|
|
239
288
|
fill: currentColor;
|
|
240
289
|
}
|
|
290
|
+
/* Coarse pointers: a 44px hit area around the 26x28 close button. The
|
|
291
|
+
pseudo-element reaches into the header padding and the gap, so nothing
|
|
292
|
+
moves. */
|
|
293
|
+
@media (pointer: coarse) {
|
|
294
|
+
.close {
|
|
295
|
+
position: relative;
|
|
296
|
+
}
|
|
297
|
+
.close::after {
|
|
298
|
+
content: '';
|
|
299
|
+
position: absolute;
|
|
300
|
+
inset: -8px -10px -8px -8px;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
241
303
|
.header-actions {
|
|
242
304
|
display: flex;
|
|
243
305
|
align-items: center;
|
|
@@ -258,6 +320,9 @@
|
|
|
258
320
|
flex-direction: column;
|
|
259
321
|
border-radius: 0 0 5px 5px;
|
|
260
322
|
}
|
|
323
|
+
.panel-content:focus-visible {
|
|
324
|
+
outline-offset: -2px;
|
|
325
|
+
}
|
|
261
326
|
.menu-anchor {
|
|
262
327
|
position: relative;
|
|
263
328
|
}
|
|
@@ -4,6 +4,12 @@
|
|
|
4
4
|
import { getPanelLayout } from '../panels.svelte.js';
|
|
5
5
|
import { getNoticeCenter } from '../notices.svelte.js';
|
|
6
6
|
import PanelCard from './PanelCard.svelte';
|
|
7
|
+
|
|
8
|
+
// The dock insets come from `--tellegen-dock-left/right/bottom` on an
|
|
9
|
+
// ancestor (defaults 20px, 20px, 100px), so a custom shell with its own
|
|
10
|
+
// rail or status bar can move the docks without restyling them.
|
|
11
|
+
let { showToolbar = true }: { showToolbar?: boolean } = $props();
|
|
12
|
+
|
|
7
13
|
const app = getAppState();
|
|
8
14
|
const layout = getPanelLayout();
|
|
9
15
|
const notices = getNoticeCenter();
|
|
@@ -55,43 +61,59 @@
|
|
|
55
61
|
onpointerup={() => layout.finishGesture()}
|
|
56
62
|
onpointercancel={() => layout.finishGesture()}
|
|
57
63
|
/>
|
|
58
|
-
|
|
59
|
-
<div class="panel-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
{#if showToolbar}
|
|
65
|
+
<div class="panel-toolbar" style:top="{toolbarTop}px" aria-label="Panels">
|
|
66
|
+
<div class="panel-buttons">
|
|
67
|
+
{#each layout.ordered().filter((p) => p.side === 'left') as panel (panel.id)}
|
|
68
|
+
<button
|
|
69
|
+
aria-label={panel.title}
|
|
70
|
+
aria-pressed={layout.compact ? layout.drawer === panel.id : panel.open}
|
|
71
|
+
onclick={() => layout.toggle(panel)}>{panel.title}</button
|
|
72
|
+
>
|
|
73
|
+
{/each}
|
|
74
|
+
</div>
|
|
75
|
+
<div class="panel-buttons right">
|
|
76
|
+
{#each layout.ordered().filter((p) => p.side === 'right') as panel (panel.id)}
|
|
77
|
+
<button
|
|
78
|
+
aria-label={panel.title}
|
|
79
|
+
aria-pressed={layout.compact ? layout.drawer === panel.id : panel.open}
|
|
80
|
+
onclick={() => layout.toggle(panel)}>{panel.title}</button
|
|
81
|
+
>
|
|
82
|
+
{/each}
|
|
70
83
|
<button
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
84
|
+
class="reset-layout"
|
|
85
|
+
onclick={() => layout.reset()}
|
|
86
|
+
title="Restore the default panel positions">Reset layout</button
|
|
74
87
|
>
|
|
75
|
-
|
|
76
|
-
<button
|
|
77
|
-
class="reset-layout"
|
|
78
|
-
onclick={() => layout.reset()}
|
|
79
|
-
title="Restore the default panel positions">Reset layout</button
|
|
80
|
-
>
|
|
88
|
+
</div>
|
|
81
89
|
</div>
|
|
82
|
-
|
|
90
|
+
{/if}
|
|
83
91
|
{#if layout.storageError}<p class="layout-error" role="status" style:top="{panelTop}px">
|
|
84
92
|
{layout.storageError}
|
|
85
93
|
</p>{/if}
|
|
94
|
+
<!-- The docks and the drawer take focus (tabindex -1) when a closed panel has
|
|
95
|
+
no toolbar button or opener to hand it back to. The drawer stays rendered,
|
|
96
|
+
at zero height, while no panel is open in it. -->
|
|
86
97
|
{#if layout.compact}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
98
|
+
<div
|
|
99
|
+
class="drawer"
|
|
100
|
+
data-panel-drawer
|
|
101
|
+
role="region"
|
|
102
|
+
aria-label="Panels"
|
|
103
|
+
tabindex="-1"
|
|
104
|
+
style:bottom="{bottomReserve}px"
|
|
105
|
+
style:height="{drawer ? drawerHeight : 0}px"
|
|
106
|
+
>
|
|
107
|
+
{#if drawer}<PanelCard panel={drawer} mobile />{/if}
|
|
108
|
+
</div>
|
|
90
109
|
{:else}
|
|
91
110
|
<div
|
|
92
111
|
class="dock left"
|
|
93
|
-
style:bottom="
|
|
112
|
+
style:bottom="max(var(--tellegen-dock-bottom, 100px), {bottomReserve}px)"
|
|
94
113
|
data-panel-dock="left"
|
|
114
|
+
role="region"
|
|
115
|
+
aria-label="Left panels"
|
|
116
|
+
tabindex="-1"
|
|
95
117
|
style:top="{panelTop}px"
|
|
96
118
|
style:width="{layout.dockWidth('left')}px"
|
|
97
119
|
>
|
|
@@ -99,8 +121,11 @@
|
|
|
99
121
|
</div>
|
|
100
122
|
<div
|
|
101
123
|
class="dock right"
|
|
102
|
-
style:bottom="
|
|
124
|
+
style:bottom="max(var(--tellegen-dock-bottom, 100px), {bottomReserve}px)"
|
|
103
125
|
data-panel-dock="right"
|
|
126
|
+
role="region"
|
|
127
|
+
aria-label="Right panels"
|
|
128
|
+
tabindex="-1"
|
|
104
129
|
style:top="{panelTop}px"
|
|
105
130
|
style:width="{layout.dockWidth('right')}px"
|
|
106
131
|
>
|
|
@@ -113,8 +138,8 @@
|
|
|
113
138
|
.panel-toolbar {
|
|
114
139
|
position: fixed;
|
|
115
140
|
z-index: 30;
|
|
116
|
-
left: 20px;
|
|
117
|
-
right: 20px;
|
|
141
|
+
left: var(--tellegen-dock-left, 20px);
|
|
142
|
+
right: var(--tellegen-dock-right, 20px);
|
|
118
143
|
display: flex;
|
|
119
144
|
justify-content: space-between;
|
|
120
145
|
align-items: center;
|
|
@@ -159,7 +184,7 @@
|
|
|
159
184
|
.dock {
|
|
160
185
|
position: fixed;
|
|
161
186
|
z-index: 25;
|
|
162
|
-
bottom: 100px;
|
|
187
|
+
bottom: var(--tellegen-dock-bottom, 100px);
|
|
163
188
|
display: flex;
|
|
164
189
|
flex-direction: column;
|
|
165
190
|
align-items: stretch;
|
|
@@ -167,10 +192,10 @@
|
|
|
167
192
|
pointer-events: none;
|
|
168
193
|
}
|
|
169
194
|
.dock.left {
|
|
170
|
-
left: 20px;
|
|
195
|
+
left: var(--tellegen-dock-left, 20px);
|
|
171
196
|
}
|
|
172
197
|
.dock.right {
|
|
173
|
-
right: 20px;
|
|
198
|
+
right: var(--tellegen-dock-right, 20px);
|
|
174
199
|
}
|
|
175
200
|
.drawer {
|
|
176
201
|
position: fixed;
|
|
@@ -194,8 +219,8 @@
|
|
|
194
219
|
}
|
|
195
220
|
@media (max-width: 960px) {
|
|
196
221
|
.panel-toolbar {
|
|
197
|
-
left: 12px;
|
|
198
|
-
right: 12px;
|
|
222
|
+
left: var(--tellegen-dock-left, 12px);
|
|
223
|
+
right: var(--tellegen-dock-right, 12px);
|
|
199
224
|
gap: 6px;
|
|
200
225
|
}
|
|
201
226
|
.panel-buttons {
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
type $$ComponentProps = {
|
|
2
|
+
showToolbar?: boolean;
|
|
3
|
+
};
|
|
4
|
+
declare const PanelHost: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
2
5
|
type PanelHost = ReturnType<typeof PanelHost>;
|
|
3
6
|
export default PanelHost;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { getController } from '../context.svelte.js';
|
|
3
3
|
import { fmt, signed } from '../format.js';
|
|
4
|
+
import { sliderKeyValue } from '../slider.js';
|
|
4
5
|
|
|
5
6
|
const ctrl = getController();
|
|
6
7
|
|
|
@@ -64,7 +65,15 @@
|
|
|
64
65
|
bind:value={ctrl.ratingSliderCurrent, ctrl.setRatingPreview}
|
|
65
66
|
aria-label="rating delta at selected line"
|
|
66
67
|
onpointerdown={() => ctrl.setRatingPreview(ctrl.ratingSliderValue)}
|
|
67
|
-
onkeydown={() =>
|
|
68
|
+
onkeydown={(e) => {
|
|
69
|
+
const next = sliderKeyValue(e.key, ctrl.ratingSliderValue, bounds.min, bounds.max);
|
|
70
|
+
if (next === null) {
|
|
71
|
+
ctrl.setRatingPreview(ctrl.ratingSliderValue);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
e.preventDefault();
|
|
75
|
+
ctrl.setRatingPreview(next);
|
|
76
|
+
}}
|
|
68
77
|
onpointerup={(e) => ctrl.finishRatingInput(Number(e.currentTarget.value))}
|
|
69
78
|
onkeyup={(e) => ctrl.finishRatingInput(Number(e.currentTarget.value))}
|
|
70
79
|
onchange={(e) => ctrl.finishRatingInput(Number(e.currentTarget.value))}
|
|
@@ -105,7 +114,7 @@
|
|
|
105
114
|
.slider-head {
|
|
106
115
|
display: flex;
|
|
107
116
|
justify-content: space-between;
|
|
108
|
-
font-size:
|
|
117
|
+
font-size: 13.5px;
|
|
109
118
|
color: var(--text-secondary);
|
|
110
119
|
margin-bottom: 4px;
|
|
111
120
|
}
|
|
@@ -120,7 +129,7 @@
|
|
|
120
129
|
justify-content: space-between;
|
|
121
130
|
gap: 8px;
|
|
122
131
|
margin: 6px 0 7px;
|
|
123
|
-
font-size:
|
|
132
|
+
font-size: 12.5px;
|
|
124
133
|
}
|
|
125
134
|
|
|
126
135
|
.slider-track {
|
|
@@ -236,13 +245,13 @@
|
|
|
236
245
|
|
|
237
246
|
.pred {
|
|
238
247
|
margin: 2px 0 0;
|
|
239
|
-
font-size:
|
|
248
|
+
font-size: 13px;
|
|
240
249
|
min-height: 16px;
|
|
241
250
|
}
|
|
242
251
|
|
|
243
252
|
.score {
|
|
244
253
|
margin: 8px 0 0;
|
|
245
|
-
font-size:
|
|
254
|
+
font-size: 13px;
|
|
246
255
|
color: var(--ink);
|
|
247
256
|
min-height: 16px;
|
|
248
257
|
}
|
|
@@ -140,7 +140,7 @@
|
|
|
140
140
|
.state-label span {
|
|
141
141
|
flex: none;
|
|
142
142
|
color: var(--text-secondary);
|
|
143
|
-
font-size:
|
|
143
|
+
font-size: 13px;
|
|
144
144
|
}
|
|
145
145
|
dl {
|
|
146
146
|
margin: 0;
|
|
@@ -163,7 +163,7 @@
|
|
|
163
163
|
}
|
|
164
164
|
small {
|
|
165
165
|
display: block;
|
|
166
|
-
font-size:
|
|
166
|
+
font-size: 12px;
|
|
167
167
|
color: var(--text-secondary);
|
|
168
168
|
}
|
|
169
169
|
.live {
|
|
@@ -208,7 +208,7 @@
|
|
|
208
208
|
display: block;
|
|
209
209
|
margin-top: 3px;
|
|
210
210
|
color: var(--text-secondary);
|
|
211
|
-
font-size:
|
|
211
|
+
font-size: 13px;
|
|
212
212
|
font-weight: 400;
|
|
213
213
|
}
|
|
214
214
|
</style>
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
<style>
|
|
112
112
|
.chip {
|
|
113
113
|
font-family: var(--font-mono);
|
|
114
|
-
font-size:
|
|
114
|
+
font-size: 13px;
|
|
115
115
|
padding: 2px 8px;
|
|
116
116
|
border: 1px solid var(--accent);
|
|
117
117
|
color: var(--text-accent);
|
|
@@ -129,7 +129,7 @@
|
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
.sensitivity-copy {
|
|
132
|
-
font-size:
|
|
132
|
+
font-size: 13.5px;
|
|
133
133
|
line-height: 1.35;
|
|
134
134
|
white-space: nowrap;
|
|
135
135
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount } from 'svelte';
|
|
3
|
+
import { PANEL_COMPACT_QUERY } from '../panels.svelte.js';
|
|
4
|
+
import { getAppState, getController } from '../context.svelte.js';
|
|
5
|
+
|
|
6
|
+
// Renderless. Keeps the shared app state in step with the environment: the
|
|
7
|
+
// pointer that gates the file drop affordance, the compact-layout media
|
|
8
|
+
// query, and the viewport height panel and map framing both read. The
|
|
9
|
+
// default shell renders it; a custom shell on `PanelHost` must too.
|
|
10
|
+
|
|
11
|
+
// Gates the drag-and-drop affordance only. The upload button in the header
|
|
12
|
+
// is always rendered: a file picker works on every device, and the plain
|
|
13
|
+
// `hover`/`pointer` queries resolve false on a touchscreen laptop that also
|
|
14
|
+
// has a mouse, which hid the whole file flow there (#110). `any-*` match
|
|
15
|
+
// when at least one attached input can hover and point precisely.
|
|
16
|
+
const FILE_DROP_QUERY = '(any-hover: hover) and (any-pointer: fine)';
|
|
17
|
+
|
|
18
|
+
const app = getAppState();
|
|
19
|
+
const ctrl = getController();
|
|
20
|
+
|
|
21
|
+
onMount(() => {
|
|
22
|
+
const query = window.matchMedia(FILE_DROP_QUERY);
|
|
23
|
+
const syncFileDropUi = () => {
|
|
24
|
+
ctrl.showFileDropUi = query.matches;
|
|
25
|
+
if (!ctrl.showFileDropUi) app.dragOver = false;
|
|
26
|
+
};
|
|
27
|
+
syncFileDropUi();
|
|
28
|
+
query.addEventListener('change', syncFileDropUi);
|
|
29
|
+
|
|
30
|
+
const compact = window.matchMedia(PANEL_COMPACT_QUERY);
|
|
31
|
+
const syncCompact = () => {
|
|
32
|
+
app.compactLayout = compact.matches;
|
|
33
|
+
if (!compact.matches) app.sheetInset = 0;
|
|
34
|
+
};
|
|
35
|
+
syncCompact();
|
|
36
|
+
compact.addEventListener('change', syncCompact);
|
|
37
|
+
|
|
38
|
+
// Shared viewport measurements keep panel and map framing dimensions aligned.
|
|
39
|
+
const syncViewport = () => (app.viewportHeight = window.innerHeight);
|
|
40
|
+
syncViewport();
|
|
41
|
+
window.addEventListener('resize', syncViewport);
|
|
42
|
+
window.addEventListener('orientationchange', syncViewport);
|
|
43
|
+
|
|
44
|
+
return () => {
|
|
45
|
+
query.removeEventListener('change', syncFileDropUi);
|
|
46
|
+
compact.removeEventListener('change', syncCompact);
|
|
47
|
+
window.removeEventListener('resize', syncViewport);
|
|
48
|
+
window.removeEventListener('orientationchange', syncViewport);
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
</script>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
2
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
3
|
+
$$bindings?: Bindings;
|
|
4
|
+
} & Exports;
|
|
5
|
+
(internal: unknown, props: {
|
|
6
|
+
$$events?: Events;
|
|
7
|
+
$$slots?: Slots;
|
|
8
|
+
}): Exports & {
|
|
9
|
+
$set?: any;
|
|
10
|
+
$on?: any;
|
|
11
|
+
};
|
|
12
|
+
z_$$bindings?: Bindings;
|
|
13
|
+
}
|
|
14
|
+
declare const ShellEnvironment: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
15
|
+
[evt: string]: CustomEvent<any>;
|
|
16
|
+
}, {}, {}, string>;
|
|
17
|
+
type ShellEnvironment = InstanceType<typeof ShellEnvironment>;
|
|
18
|
+
export default ShellEnvironment;
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
display: flex;
|
|
20
20
|
align-items: center;
|
|
21
21
|
gap: 12px;
|
|
22
|
-
font-size:
|
|
22
|
+
font-size: 12px;
|
|
23
23
|
color: var(--text-secondary);
|
|
24
24
|
}
|
|
25
25
|
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
|
|
39
39
|
.caption {
|
|
40
40
|
margin-left: auto;
|
|
41
|
-
font-size:
|
|
41
|
+
font-size: 12px;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
@media (max-width: 760px) {
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
|
|
54
54
|
<style>
|
|
55
55
|
.solve-label {
|
|
56
|
-
font-size:
|
|
56
|
+
font-size: 13px;
|
|
57
57
|
margin-bottom: 8px;
|
|
58
58
|
}
|
|
59
59
|
.solvecard {
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
align-items: center;
|
|
66
66
|
flex-wrap: wrap;
|
|
67
67
|
gap: 10px 14px;
|
|
68
|
-
font-size:
|
|
68
|
+
font-size: 12px;
|
|
69
69
|
margin-top: 6px;
|
|
70
70
|
}
|
|
71
71
|
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
|
|
76
76
|
.fallback-reason {
|
|
77
77
|
margin: 6px 0 0;
|
|
78
|
-
font-size:
|
|
78
|
+
font-size: 12px;
|
|
79
79
|
line-height: 1.35;
|
|
80
80
|
overflow-wrap: anywhere;
|
|
81
81
|
}
|
package/dist/components.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export { default as ModelDetails } from './components/ModelDetails.svelte';
|
|
2
2
|
export { default as AppFooter } from './components/AppFooter.svelte';
|
|
3
|
+
export { default as NotificationCenter } from './components/NotificationCenter.svelte';
|
|
4
|
+
export { default as PanelHost } from './components/PanelHost.svelte';
|
|
5
|
+
export { default as ShellEnvironment } from './components/ShellEnvironment.svelte';
|
|
3
6
|
export { default as AppHeader } from './components/AppHeader.svelte';
|
|
4
7
|
export { default as BusPicker } from './components/BusPicker.svelte';
|
|
5
8
|
export { default as ControlPanel } from './components/ControlPanel.svelte';
|
package/dist/components.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export { default as ModelDetails } from './components/ModelDetails.svelte';
|
|
2
2
|
export { default as AppFooter } from './components/AppFooter.svelte';
|
|
3
|
+
export { default as NotificationCenter } from './components/NotificationCenter.svelte';
|
|
4
|
+
export { default as PanelHost } from './components/PanelHost.svelte';
|
|
5
|
+
export { default as ShellEnvironment } from './components/ShellEnvironment.svelte';
|
|
3
6
|
export { default as AppHeader } from './components/AppHeader.svelte';
|
|
4
7
|
export { default as BusPicker } from './components/BusPicker.svelte';
|
|
5
8
|
export { default as ControlPanel } from './components/ControlPanel.svelte';
|
package/dist/context.svelte.d.ts
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import type { AppState } from './state.svelte.js';
|
|
2
2
|
import type { Controller } from './controller.svelte.js';
|
|
3
3
|
export interface TellegenUiConfig {
|
|
4
|
-
|
|
4
|
+
/** Backend base path; `null` for a host with no backend at all. */
|
|
5
|
+
apiBase: string | null;
|
|
5
6
|
loadDefaultCases: boolean;
|
|
6
7
|
docsHref: string;
|
|
7
8
|
orgHref: string;
|
|
8
9
|
orgLabel: string;
|
|
9
10
|
showFooter: boolean;
|
|
11
|
+
/** `'dark'` sets `data-theme="dark"` on the document root while the
|
|
12
|
+
* provider is mounted, which switches the `styles.css` tokens. */
|
|
13
|
+
theme: 'light' | 'dark';
|
|
14
|
+
/** CARTO basemap key for the default shell's map, trimmed; empty for
|
|
15
|
+
* none. See `TellegenMap`'s `cartoApiKey`. */
|
|
16
|
+
cartoApiKey: string;
|
|
10
17
|
}
|
|
11
18
|
export type TellegenUiOptions = Partial<TellegenUiConfig>;
|
|
12
19
|
export declare const DEFAULT_TELLEGEN_UI_CONFIG: TellegenUiConfig;
|
package/dist/context.svelte.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getContext, setContext } from 'svelte';
|
|
2
|
+
import { normalizeCartoApiKey } from './basemap.js';
|
|
2
3
|
const APP = Symbol('tellegen.app');
|
|
3
4
|
const CTRL = Symbol('tellegen.controller');
|
|
4
5
|
const CONFIG = Symbol('tellegen.config');
|
|
@@ -8,16 +9,20 @@ export const DEFAULT_TELLEGEN_UI_CONFIG = {
|
|
|
8
9
|
docsHref: 'https://eigenergy.github.io/tellegen/',
|
|
9
10
|
orgHref: 'https://github.com/eigenergy',
|
|
10
11
|
orgLabel: 'eigenergy group @ michigan ece',
|
|
11
|
-
showFooter: true
|
|
12
|
+
showFooter: true,
|
|
13
|
+
theme: 'light',
|
|
14
|
+
cartoApiKey: ''
|
|
12
15
|
};
|
|
13
16
|
export function resolveTellegenUiConfig(options = {}) {
|
|
14
17
|
return {
|
|
15
|
-
apiBase: options.apiBase
|
|
18
|
+
apiBase: options.apiBase === undefined ? DEFAULT_TELLEGEN_UI_CONFIG.apiBase : options.apiBase,
|
|
16
19
|
loadDefaultCases: options.loadDefaultCases ?? DEFAULT_TELLEGEN_UI_CONFIG.loadDefaultCases,
|
|
17
20
|
docsHref: options.docsHref ?? DEFAULT_TELLEGEN_UI_CONFIG.docsHref,
|
|
18
21
|
orgHref: options.orgHref ?? DEFAULT_TELLEGEN_UI_CONFIG.orgHref,
|
|
19
22
|
orgLabel: options.orgLabel ?? DEFAULT_TELLEGEN_UI_CONFIG.orgLabel,
|
|
20
|
-
showFooter: options.showFooter ?? DEFAULT_TELLEGEN_UI_CONFIG.showFooter
|
|
23
|
+
showFooter: options.showFooter ?? DEFAULT_TELLEGEN_UI_CONFIG.showFooter,
|
|
24
|
+
theme: options.theme ?? DEFAULT_TELLEGEN_UI_CONFIG.theme,
|
|
25
|
+
cartoApiKey: normalizeCartoApiKey(options.cartoApiKey) ?? DEFAULT_TELLEGEN_UI_CONFIG.cartoApiKey
|
|
21
26
|
};
|
|
22
27
|
}
|
|
23
28
|
export const setAppState = (a) => setContext(APP, a);
|