@tellegen/svelte 0.4.3 → 0.4.5
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 +45 -0
- package/dist/TellegenMap.svelte +63 -22
- package/dist/TellegenProvider.svelte +7 -4
- package/dist/TellegenShell.svelte +12 -1
- package/dist/TellegenViewer.svelte +2 -0
- package/dist/basemap.d.ts +14 -0
- package/dist/basemap.js +27 -0
- package/dist/colors.d.ts +6 -1
- package/dist/colors.js +11 -4
- package/dist/components/DiagnosticLogView.svelte +176 -0
- package/dist/components/DiagnosticLogView.svelte.d.ts +11 -0
- package/dist/components/FormulationSelector.svelte +1 -1
- package/dist/components/LocalCaseDetails.svelte +18 -3
- package/dist/components/LogEntryView.svelte +188 -0
- package/dist/components/LogEntryView.svelte.d.ts +11 -0
- package/dist/components/LogPanel.svelte +25 -0
- package/dist/components/LogPanel.svelte.d.ts +3 -0
- package/dist/components/McResults.svelte +18 -3
- package/dist/components/McWorkbench.svelte +688 -0
- package/dist/components/McWorkbench.svelte.d.ts +38 -0
- package/dist/components/MultiWire.svelte +136 -0
- package/dist/components/MultiWire.svelte.d.ts +7 -0
- package/dist/components/MulticonductorDetails.svelte +7 -0
- package/dist/components/NotificationCenter.svelte +37 -6
- package/dist/components/OmniSearch.svelte +163 -0
- package/dist/components/OmniSearch.svelte.d.ts +14 -0
- package/dist/components/ResultsTable.svelte +259 -0
- package/dist/components/ResultsTable.svelte.d.ts +13 -0
- package/dist/components/SchematicCanvas.svelte +1896 -0
- package/dist/components/SchematicCanvas.svelte.d.ts +13 -0
- package/dist/components/SolveCard.svelte +6 -0
- package/dist/components/schematic/PickCard.svelte +126 -0
- package/dist/components/schematic/PickCard.svelte.d.ts +56 -0
- package/dist/components/schematic/export.d.ts +21 -0
- package/dist/components/schematic/export.js +110 -0
- package/dist/components/schematic/layers.d.ts +161 -0
- package/dist/components/schematic/layers.js +756 -0
- package/dist/components/schematic/paint.d.ts +28 -0
- package/dist/components/schematic/paint.js +69 -0
- package/dist/components/schematic/palette.d.ts +34 -0
- package/dist/components/schematic/palette.js +54 -0
- package/dist/components/schematic/text.d.ts +83 -0
- package/dist/components/schematic/text.js +190 -0
- package/dist/components/schematic/view-prefs.svelte.d.ts +30 -0
- package/dist/components/schematic/view-prefs.svelte.js +77 -0
- package/dist/components.d.ts +4 -0
- package/dist/components.js +4 -0
- package/dist/context.svelte.d.ts +2 -0
- package/dist/context.svelte.js +2 -0
- package/dist/controller.svelte.d.ts +40 -5
- package/dist/controller.svelte.js +230 -25
- package/dist/diagram/basemap.d.ts +28 -0
- package/dist/diagram/basemap.js +39 -0
- package/dist/diagram/bus-stacks.d.ts +19 -0
- package/dist/diagram/bus-stacks.js +99 -0
- package/dist/diagram/colour-modes.d.ts +18 -0
- package/dist/diagram/colour-modes.js +33 -0
- package/dist/diagram/connections.d.ts +30 -0
- package/dist/diagram/connections.js +129 -0
- package/dist/diagram/display-tree.d.ts +21 -0
- package/dist/diagram/display-tree.js +65 -0
- package/dist/diagram/energisation.d.ts +10 -0
- package/dist/diagram/energisation.js +31 -0
- package/dist/diagram/feeders.d.ts +30 -0
- package/dist/diagram/feeders.js +57 -0
- package/dist/diagram/galvanic-zones.d.ts +60 -0
- package/dist/diagram/galvanic-zones.js +210 -0
- package/dist/diagram/graph-inputs.d.ts +18 -0
- package/dist/diagram/graph-inputs.js +103 -0
- package/dist/diagram/index.d.ts +28 -0
- package/dist/diagram/index.js +28 -0
- package/dist/diagram/landmarks.d.ts +50 -0
- package/dist/diagram/landmarks.js +153 -0
- package/dist/diagram/layout-morph.d.ts +15 -0
- package/dist/diagram/layout-morph.js +32 -0
- package/dist/diagram/multi-wire.d.ts +44 -0
- package/dist/diagram/multi-wire.js +65 -0
- package/dist/diagram/phase-bars.d.ts +35 -0
- package/dist/diagram/phase-bars.js +44 -0
- package/dist/diagram/results-table.d.ts +47 -0
- package/dist/diagram/results-table.js +173 -0
- package/dist/diagram/schematic-equipment.d.ts +133 -0
- package/dist/diagram/schematic-equipment.js +645 -0
- package/dist/diagram/schematic-layout.d.ts +51 -0
- package/dist/diagram/schematic-layout.js +240 -0
- package/dist/diagram/search.d.ts +36 -0
- package/dist/diagram/search.js +48 -0
- package/dist/diagram/shared-routes.d.ts +21 -0
- package/dist/diagram/shared-routes.js +40 -0
- package/dist/diagram/short-names.d.ts +11 -0
- package/dist/diagram/short-names.js +54 -0
- package/dist/diagram/snapshot.d.ts +115 -0
- package/dist/diagram/snapshot.js +242 -0
- package/dist/diagram/summary.d.ts +65 -0
- package/dist/diagram/summary.js +144 -0
- package/dist/diagram/svg-export.d.ts +80 -0
- package/dist/diagram/svg-export.js +108 -0
- package/dist/diagram/tree-walk.d.ts +13 -0
- package/dist/diagram/tree-walk.js +83 -0
- package/dist/diagram/value-labels.d.ts +24 -0
- package/dist/diagram/value-labels.js +69 -0
- package/dist/diagram/view-state.d.ts +12 -0
- package/dist/diagram/view-state.js +22 -0
- package/dist/diagram/voltage-levels.d.ts +14 -0
- package/dist/diagram/voltage-levels.js +20 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/log.svelte.d.ts +77 -0
- package/dist/log.svelte.js +149 -0
- package/dist/notices.svelte.d.ts +3 -0
- package/dist/state.svelte.d.ts +42 -5
- package/dist/state.svelte.js +56 -4
- package/dist/transformer-icon.d.ts +22 -13
- package/dist/transformer-icon.js +23 -13
- package/package.json +2 -2
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { DistGraph } from '@tellegen/engine';
|
|
2
|
+
import { type GraphInputs, type ResultSnapshot, type SearchIndex } from '../diagram/index.js';
|
|
3
|
+
type $$ComponentProps = {
|
|
4
|
+
/** The operating point projected onto `inputs` (`snapshotFromMcPf`); `null` while it is being
|
|
5
|
+
* fetched. `MulticonductorCase.overviewSnapshot` holds a case's, shared with the diagram. */
|
|
6
|
+
snapshot: ResultSnapshot | null;
|
|
7
|
+
graph: DistGraph;
|
|
8
|
+
/** `graphInputs(graph)`; `MulticonductorCase.overviewInputs` holds a case's. */
|
|
9
|
+
inputs: GraphInputs;
|
|
10
|
+
/** `buildSearchIndex(inputs.entities)`; `MulticonductorCase.overviewSearch` holds a case's. */
|
|
11
|
+
search: SearchIndex;
|
|
12
|
+
loading?: boolean;
|
|
13
|
+
selectedBus: string | null;
|
|
14
|
+
selectedEdge: string | null;
|
|
15
|
+
onselectbus: (id: string) => void;
|
|
16
|
+
onselectedge: (id: string) => void;
|
|
17
|
+
/** Case name, used in exported file names and provenance. */
|
|
18
|
+
label?: string;
|
|
19
|
+
/** When set, the overview is computed on request rather than after every solve. */
|
|
20
|
+
onanalyse?: () => void;
|
|
21
|
+
/** Whether the case is shown as a schematic, and how to toggle it. */
|
|
22
|
+
schematic?: boolean;
|
|
23
|
+
onschematic?: (show: boolean) => void;
|
|
24
|
+
/** Bus or branch under the pointer in any linked view. */
|
|
25
|
+
hovered?: {
|
|
26
|
+
kind: 'bus' | 'branch';
|
|
27
|
+
id: string;
|
|
28
|
+
} | null;
|
|
29
|
+
onhover?: (h: {
|
|
30
|
+
kind: 'bus' | 'branch';
|
|
31
|
+
id: string;
|
|
32
|
+
} | null) => void;
|
|
33
|
+
/** Bring the current selection into view again, for an issue clicked while selected. */
|
|
34
|
+
onreveal?: () => void;
|
|
35
|
+
};
|
|
36
|
+
declare const McWorkbench: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
37
|
+
type McWorkbench = ReturnType<typeof McWorkbench>;
|
|
38
|
+
export default McWorkbench;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { rgbaCss } from '../format.js';
|
|
3
|
+
import { isPhaseTerminal, phaseColor } from '../multiconductor.js';
|
|
4
|
+
import type { MultiWireView } from '../diagram/index.js';
|
|
5
|
+
|
|
6
|
+
let { view }: { view: MultiWireView } = $props();
|
|
7
|
+
|
|
8
|
+
const colour = (terminal: string) =>
|
|
9
|
+
isPhaseTerminal(terminal) ? rgbaCss(phaseColor(terminal)) : 'var(--text-secondary)';
|
|
10
|
+
const amps = (a: number | null) => (a === null ? '' : `${a.toFixed(a >= 100 ? 0 : 1)} A`);
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<figure class="multi-wire">
|
|
14
|
+
<svg
|
|
15
|
+
viewBox="0 0 {view.width} {view.height}"
|
|
16
|
+
role="img"
|
|
17
|
+
aria-label="Multi-wire view of {view.kind} {view.id}: {view.wires
|
|
18
|
+
.length} conductor segments between {view.buses[0].id} and {view.buses[1].id}"
|
|
19
|
+
>
|
|
20
|
+
{#each view.buses as bus, side (side)}
|
|
21
|
+
<text class="bus" x={bus.x} y="12" text-anchor="middle">{bus.id}</text>
|
|
22
|
+
{#each bus.terminals as t, r (r)}
|
|
23
|
+
<g>
|
|
24
|
+
{#if t.attachments.length}<title>{t.name}: {t.attachments.join(', ')}</title>{/if}
|
|
25
|
+
<line
|
|
26
|
+
class="rail"
|
|
27
|
+
x1={bus.x - 8}
|
|
28
|
+
x2={bus.x + 8}
|
|
29
|
+
y1={t.y}
|
|
30
|
+
y2={t.y}
|
|
31
|
+
stroke={colour(t.name)}
|
|
32
|
+
/>
|
|
33
|
+
<text
|
|
34
|
+
class="terminal"
|
|
35
|
+
x={bus.x + (bus.x < view.width / 2 ? -12 : 12)}
|
|
36
|
+
y={t.y + 3}
|
|
37
|
+
text-anchor={bus.x < view.width / 2 ? 'end' : 'start'}>{t.name}</text
|
|
38
|
+
>
|
|
39
|
+
{#if t.grounded}
|
|
40
|
+
<!-- Earth symbol: three shrinking bars below the rail end. -->
|
|
41
|
+
<g
|
|
42
|
+
class="earth"
|
|
43
|
+
transform="translate({bus.x + (bus.x < view.width / 2 ? -26 : 26)} {t.y})"
|
|
44
|
+
>
|
|
45
|
+
<line x1="-4" x2="4" y1="4" y2="4" /><line
|
|
46
|
+
x1="-2.6"
|
|
47
|
+
x2="2.6"
|
|
48
|
+
y1="6.5"
|
|
49
|
+
y2="6.5"
|
|
50
|
+
/><line x1="-1.2" x2="1.2" y1="9" y2="9" />
|
|
51
|
+
</g>
|
|
52
|
+
{/if}
|
|
53
|
+
</g>
|
|
54
|
+
{/each}
|
|
55
|
+
{/each}
|
|
56
|
+
{#if view.winding}
|
|
57
|
+
<rect
|
|
58
|
+
class="winding"
|
|
59
|
+
x={view.winding.x}
|
|
60
|
+
y={view.winding.y}
|
|
61
|
+
width={view.winding.width}
|
|
62
|
+
height={view.winding.height}
|
|
63
|
+
rx="4"
|
|
64
|
+
/>
|
|
65
|
+
<text
|
|
66
|
+
class="terminal"
|
|
67
|
+
x={view.width / 2}
|
|
68
|
+
y={view.winding.y + view.winding.height + 12}
|
|
69
|
+
text-anchor="middle">windings</text
|
|
70
|
+
>
|
|
71
|
+
{/if}
|
|
72
|
+
{#each view.wires as w, i (i)}
|
|
73
|
+
<line
|
|
74
|
+
class="wire"
|
|
75
|
+
class:open={!view.closed}
|
|
76
|
+
x1={w.x1}
|
|
77
|
+
y1={w.y1}
|
|
78
|
+
x2={w.x2}
|
|
79
|
+
y2={w.y2}
|
|
80
|
+
stroke={colour(w.terminal)}
|
|
81
|
+
/>
|
|
82
|
+
{#if w.amps !== null}
|
|
83
|
+
<text class="amps" x={(w.x1 + w.x2) / 2} y={(w.y1 + w.y2) / 2 - 3} text-anchor="middle"
|
|
84
|
+
>{amps(w.amps)}</text
|
|
85
|
+
>
|
|
86
|
+
{/if}
|
|
87
|
+
{/each}
|
|
88
|
+
</svg>
|
|
89
|
+
<figcaption>
|
|
90
|
+
Every conductor between the two terminal sets{view.winding
|
|
91
|
+
? '; transformer windings couple magnetically, with no conductor continuity across them'
|
|
92
|
+
: ''}{view.closed ? '' : '; open'}. Currents are magnitudes at the from end{view.winding
|
|
93
|
+
? ' and the to end'
|
|
94
|
+
: ''}. Hover a terminal for its attachments.
|
|
95
|
+
</figcaption>
|
|
96
|
+
</figure>
|
|
97
|
+
|
|
98
|
+
<style>
|
|
99
|
+
.multi-wire {
|
|
100
|
+
margin: 8px 0 0;
|
|
101
|
+
}
|
|
102
|
+
svg {
|
|
103
|
+
display: block;
|
|
104
|
+
width: 100%;
|
|
105
|
+
height: auto;
|
|
106
|
+
}
|
|
107
|
+
svg text {
|
|
108
|
+
font: 9px var(--font-mono);
|
|
109
|
+
fill: var(--text-secondary);
|
|
110
|
+
}
|
|
111
|
+
svg text.bus {
|
|
112
|
+
fill: var(--ink);
|
|
113
|
+
}
|
|
114
|
+
.rail {
|
|
115
|
+
stroke-width: 3;
|
|
116
|
+
}
|
|
117
|
+
.wire {
|
|
118
|
+
stroke-width: 1.6;
|
|
119
|
+
}
|
|
120
|
+
.wire.open {
|
|
121
|
+
stroke-dasharray: 4 3;
|
|
122
|
+
}
|
|
123
|
+
.winding {
|
|
124
|
+
fill: var(--paper);
|
|
125
|
+
stroke: var(--ink);
|
|
126
|
+
stroke-width: 1;
|
|
127
|
+
}
|
|
128
|
+
.earth line {
|
|
129
|
+
stroke: var(--text-secondary);
|
|
130
|
+
stroke-width: 1;
|
|
131
|
+
}
|
|
132
|
+
figcaption {
|
|
133
|
+
color: var(--text-secondary);
|
|
134
|
+
font-size: 11px;
|
|
135
|
+
}
|
|
136
|
+
</style>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { MultiWireView } from '../diagram/index.js';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
view: MultiWireView;
|
|
4
|
+
};
|
|
5
|
+
declare const MultiWire: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
6
|
+
type MultiWire = ReturnType<typeof MultiWire>;
|
|
7
|
+
export default MultiWire;
|
|
@@ -262,6 +262,13 @@
|
|
|
262
262
|
</span>
|
|
263
263
|
</div>
|
|
264
264
|
|
|
265
|
+
{#if mc.graph}<button
|
|
266
|
+
class="reset"
|
|
267
|
+
aria-pressed={mc.schematic}
|
|
268
|
+
title="Symbols and the engine's schematic; results once solved"
|
|
269
|
+
onclick={() => (mc.schematic = !mc.schematic)}
|
|
270
|
+
>{mc.schematic ? 'Show map' : 'Show diagram'}</button
|
|
271
|
+
>{/if}
|
|
265
272
|
<button class="reset" onclick={() => app.requestFrame(mc.id)}>Fit case</button>
|
|
266
273
|
<button class="reset" onclick={() => ctrl.removeMultiCase(mc)}>Remove</button>
|
|
267
274
|
{/if}
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
import { onDestroy, untrack } from 'svelte';
|
|
3
3
|
import { formatPowerIoDiagnostic } from '@tellegen/engine';
|
|
4
4
|
import { getAppState, getController } from '../context.svelte.js';
|
|
5
|
+
import { powerIoEntry, type LogEntryInput } from '../log.svelte.js';
|
|
5
6
|
import { getNoticeCenter, errorNoticeTitle } from '../notices.svelte.js';
|
|
7
|
+
import LogEntryView from './LogEntryView.svelte';
|
|
6
8
|
const app = getAppState();
|
|
7
9
|
const ctrl = getController();
|
|
8
10
|
const notices = getNoticeCenter();
|
|
@@ -33,7 +35,20 @@
|
|
|
33
35
|
const multi = app.activeMulti;
|
|
34
36
|
const summary = local?.summary ?? multi?.summary;
|
|
35
37
|
const geo = local?.geoWarnings ?? multi?.geoWarnings;
|
|
36
|
-
|
|
38
|
+
const subject = local?.fileName ?? multi?.fileName;
|
|
39
|
+
const entries: LogEntryInput[] = summary
|
|
40
|
+
? [
|
|
41
|
+
...summary.diagnostics.map((d) => powerIoEntry(d, { stage: 'Open', subject })),
|
|
42
|
+
...('warnings' in summary ? summary.warnings : []).map((message) => ({
|
|
43
|
+
severity: 'note' as const,
|
|
44
|
+
stage: 'Open',
|
|
45
|
+
source: 'tellegen',
|
|
46
|
+
subject,
|
|
47
|
+
message
|
|
48
|
+
}))
|
|
49
|
+
]
|
|
50
|
+
: [];
|
|
51
|
+
for (const [identity, title, details, items] of [
|
|
37
52
|
[
|
|
38
53
|
summary,
|
|
39
54
|
'Case import notes',
|
|
@@ -42,13 +57,15 @@
|
|
|
42
57
|
...summary.diagnostics.map(formatPowerIoDiagnostic),
|
|
43
58
|
...('warnings' in summary ? summary.warnings : [])
|
|
44
59
|
].join('\n\n')
|
|
45
|
-
: ''
|
|
60
|
+
: '',
|
|
61
|
+
entries
|
|
46
62
|
],
|
|
47
|
-
[geo, 'Coordinate import notes', geo?.join('\n\n') ?? '']
|
|
48
|
-
] as Array<[object | undefined, string, string]>) {
|
|
63
|
+
[geo, 'Coordinate import notes', geo?.join('\n\n') ?? '', undefined]
|
|
64
|
+
] as Array<[object | undefined, string, string, LogEntryInput[] | undefined]>) {
|
|
49
65
|
if (identity && !observed.has(identity)) {
|
|
50
66
|
observed.add(identity);
|
|
51
|
-
if (details)
|
|
67
|
+
if (details)
|
|
68
|
+
untrack(() => notices.push({ kind: 'warning', title, details, entries: items }));
|
|
52
69
|
}
|
|
53
70
|
}
|
|
54
71
|
});
|
|
@@ -92,7 +109,15 @@
|
|
|
92
109
|
>{entry.title}{#if entry.count > 1}<span class="count">{entry.count}</span
|
|
93
110
|
>{/if}</summary
|
|
94
111
|
>
|
|
95
|
-
|
|
112
|
+
{#if entry.entries?.length}
|
|
113
|
+
<ol class="entries" data-testid="notification-details">
|
|
114
|
+
{#each entry.entries as item, i (i)}
|
|
115
|
+
<li><LogEntryView entry={item} showSubject={false} /></li>
|
|
116
|
+
{/each}
|
|
117
|
+
</ol>
|
|
118
|
+
{:else}
|
|
119
|
+
<pre data-testid="notification-details">{entry.details}</pre>
|
|
120
|
+
{/if}
|
|
96
121
|
</details>
|
|
97
122
|
{#if entry.retry && entry.canRetry?.()}<button onclick={entry.retry}>Retry</button
|
|
98
123
|
>{/if}
|
|
@@ -227,6 +252,12 @@
|
|
|
227
252
|
summary {
|
|
228
253
|
cursor: pointer;
|
|
229
254
|
}
|
|
255
|
+
.entries {
|
|
256
|
+
margin-top: 8px;
|
|
257
|
+
}
|
|
258
|
+
.entries li {
|
|
259
|
+
padding: 6px 0;
|
|
260
|
+
}
|
|
230
261
|
pre {
|
|
231
262
|
font: 11px/1.5 var(--font-mono);
|
|
232
263
|
white-space: pre-wrap;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { search, type SearchIndex, type SearchMatch } from '../diagram/index.js';
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
index,
|
|
6
|
+
onselect,
|
|
7
|
+
hint = () => null,
|
|
8
|
+
placeholder = 'Find a bus or branch'
|
|
9
|
+
}: {
|
|
10
|
+
index: SearchIndex;
|
|
11
|
+
onselect: (match: SearchMatch) => void;
|
|
12
|
+
/** A short result value shown beside each match, such as a voltage or loading. */
|
|
13
|
+
hint?: (match: SearchMatch) => { text: string; state?: string } | null;
|
|
14
|
+
placeholder?: string;
|
|
15
|
+
} = $props();
|
|
16
|
+
|
|
17
|
+
const id = $props.id();
|
|
18
|
+
let query = $state('');
|
|
19
|
+
let active = $state(0);
|
|
20
|
+
let open = $state(false);
|
|
21
|
+
const found = $derived(search(index, query, { limit: 10 }));
|
|
22
|
+
|
|
23
|
+
function choose(match: SearchMatch | undefined) {
|
|
24
|
+
if (!match) return;
|
|
25
|
+
onselect(match);
|
|
26
|
+
query = '';
|
|
27
|
+
open = false;
|
|
28
|
+
}
|
|
29
|
+
function onkeydown(e: KeyboardEvent) {
|
|
30
|
+
const n = found.matches.length;
|
|
31
|
+
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
|
|
32
|
+
e.preventDefault();
|
|
33
|
+
if (n) active = (active + (e.key === 'ArrowDown' ? 1 : -1) + n) % n;
|
|
34
|
+
open = true;
|
|
35
|
+
} else if (e.key === 'Enter') {
|
|
36
|
+
e.preventDefault();
|
|
37
|
+
choose(found.matches[active]);
|
|
38
|
+
} else if (e.key === 'Escape') {
|
|
39
|
+
if (query) query = '';
|
|
40
|
+
else open = false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
<div class="omni">
|
|
46
|
+
<input
|
|
47
|
+
type="search"
|
|
48
|
+
role="combobox"
|
|
49
|
+
aria-label={placeholder}
|
|
50
|
+
aria-expanded={open && !!query.trim()}
|
|
51
|
+
aria-controls="{id}-results"
|
|
52
|
+
aria-activedescendant={open && found.matches.length ? `${id}-${active}` : undefined}
|
|
53
|
+
autocomplete="off"
|
|
54
|
+
spellcheck="false"
|
|
55
|
+
{placeholder}
|
|
56
|
+
bind:value={query}
|
|
57
|
+
oninput={() => {
|
|
58
|
+
active = 0;
|
|
59
|
+
open = true;
|
|
60
|
+
}}
|
|
61
|
+
onfocus={() => (open = true)}
|
|
62
|
+
onblur={() => setTimeout(() => (open = false), 120)}
|
|
63
|
+
{onkeydown}
|
|
64
|
+
/>
|
|
65
|
+
{#if open && query.trim()}
|
|
66
|
+
<ul id="{id}-results" role="listbox" aria-label="Search results">
|
|
67
|
+
{#each found.matches as match, i (`${match.kind}:${match.index}`)}
|
|
68
|
+
{@const h = hint(match)}
|
|
69
|
+
<li
|
|
70
|
+
id="{id}-{i}"
|
|
71
|
+
role="option"
|
|
72
|
+
aria-selected={i === active}
|
|
73
|
+
onpointerdown={(e) => {
|
|
74
|
+
e.preventDefault();
|
|
75
|
+
choose(match);
|
|
76
|
+
}}
|
|
77
|
+
>
|
|
78
|
+
<span class="kind">{match.kind === 'bus' ? 'bus' : match.family}</span>
|
|
79
|
+
<span class="id">{match.id}</span>
|
|
80
|
+
{#if h}<span class="hint {h.state ?? ''}">{h.text}</span>{/if}
|
|
81
|
+
</li>
|
|
82
|
+
{:else}
|
|
83
|
+
<li class="empty" role="presentation">No bus or branch ID contains that text.</li>
|
|
84
|
+
{/each}
|
|
85
|
+
{#if found.total > found.matches.length}<li class="more" role="presentation">
|
|
86
|
+
{found.total.toLocaleString()} matches · keep typing to narrow
|
|
87
|
+
</li>{/if}
|
|
88
|
+
</ul>
|
|
89
|
+
{/if}
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<style>
|
|
93
|
+
.omni {
|
|
94
|
+
position: relative;
|
|
95
|
+
}
|
|
96
|
+
input {
|
|
97
|
+
width: 100%;
|
|
98
|
+
box-sizing: border-box;
|
|
99
|
+
font: 11px var(--font-mono);
|
|
100
|
+
color: var(--ink);
|
|
101
|
+
background: var(--paper);
|
|
102
|
+
border: 1px solid var(--line);
|
|
103
|
+
border-radius: 3px;
|
|
104
|
+
padding: 6px 8px;
|
|
105
|
+
}
|
|
106
|
+
ul {
|
|
107
|
+
position: absolute;
|
|
108
|
+
z-index: 5;
|
|
109
|
+
left: 0;
|
|
110
|
+
right: 0;
|
|
111
|
+
top: calc(100% + 2px);
|
|
112
|
+
margin: 0;
|
|
113
|
+
padding: 3px;
|
|
114
|
+
list-style: none;
|
|
115
|
+
max-height: 260px;
|
|
116
|
+
overflow: auto;
|
|
117
|
+
background: var(--paper);
|
|
118
|
+
border: 1px solid var(--line);
|
|
119
|
+
border-radius: 3px;
|
|
120
|
+
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.16);
|
|
121
|
+
}
|
|
122
|
+
li {
|
|
123
|
+
display: grid;
|
|
124
|
+
grid-template-columns: 70px 1fr auto;
|
|
125
|
+
gap: 6px;
|
|
126
|
+
align-items: baseline;
|
|
127
|
+
padding: 5px 6px;
|
|
128
|
+
border-radius: 2px;
|
|
129
|
+
cursor: pointer;
|
|
130
|
+
}
|
|
131
|
+
li[aria-selected='true'] {
|
|
132
|
+
background: var(--accent-soft);
|
|
133
|
+
}
|
|
134
|
+
.kind,
|
|
135
|
+
.empty,
|
|
136
|
+
.more {
|
|
137
|
+
color: var(--text-secondary);
|
|
138
|
+
}
|
|
139
|
+
.kind {
|
|
140
|
+
font: 9px var(--font-mono);
|
|
141
|
+
text-transform: uppercase;
|
|
142
|
+
}
|
|
143
|
+
.id,
|
|
144
|
+
.hint {
|
|
145
|
+
font-family: var(--font-mono);
|
|
146
|
+
white-space: nowrap;
|
|
147
|
+
}
|
|
148
|
+
.id {
|
|
149
|
+
overflow: hidden;
|
|
150
|
+
text-overflow: ellipsis;
|
|
151
|
+
}
|
|
152
|
+
.hint.violation {
|
|
153
|
+
color: var(--text-danger);
|
|
154
|
+
}
|
|
155
|
+
.hint.warning {
|
|
156
|
+
color: var(--text-accent);
|
|
157
|
+
}
|
|
158
|
+
.empty,
|
|
159
|
+
.more {
|
|
160
|
+
display: block;
|
|
161
|
+
cursor: default;
|
|
162
|
+
}
|
|
163
|
+
</style>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type SearchIndex, type SearchMatch } from '../diagram/index.js';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
index: SearchIndex;
|
|
4
|
+
onselect: (match: SearchMatch) => void;
|
|
5
|
+
/** A short result value shown beside each match, such as a voltage or loading. */
|
|
6
|
+
hint?: (match: SearchMatch) => {
|
|
7
|
+
text: string;
|
|
8
|
+
state?: string;
|
|
9
|
+
} | null;
|
|
10
|
+
placeholder?: string;
|
|
11
|
+
};
|
|
12
|
+
declare const OmniSearch: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
13
|
+
type OmniSearch = ReturnType<typeof OmniSearch>;
|
|
14
|
+
export default OmniSearch;
|