@contraagent/elements 0.6.2 → 0.7.1

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.
@@ -173,6 +173,7 @@ function node(h: GraphHost, n: GraphNode, parent: Rect | null) {
173
173
  : nothing}</span>
174
174
  </div>
175
175
  ${bar ? statusBar(h, n, group) : marks(h, group)}
176
+ ${n.expected ? html`<span class="expected" part="expected" aria-hidden="true" title=${n.expected.label}>${n.expected.text}</span>` : nothing}
176
177
  ${n.notes?.length ? html`<div class="ex" aria-hidden="true">${notes(n)}</div>` : nothing}
177
178
  </section>`;
178
179
  }
@@ -212,6 +213,7 @@ function mark(h: GraphHost, g: MarkGroup, s: Slot, i: number, tabStop: boolean,
212
213
  const { key, on } = h.lit();
213
214
  const hlKey = hlKeyOf(s);
214
215
  const lit = on !== null && ((s.series !== '' && s.series === key) || on.has(m.id));
216
+ const link = lit && on !== null && 'get' in on ? (on.get(m.id) ?? null) : null;
215
217
  const sel = h.selected === m.id ? 'sel' : '';
216
218
  const focus = (e: FocusEvent) => {
217
219
  h.hover = hlKey;
@@ -227,7 +229,7 @@ function mark(h: GraphHost, g: MarkGroup, s: Slot, i: number, tabStop: boolean,
227
229
  }
228
230
  const look = h.states[m.state];
229
231
  return html`<button type="button" class="mark ${lit ? 'on' : ''} ${sel}" part="mark" data-tone=${toneAttr(look?.tone)} data-shape=${look?.shape ?? nothing}
230
- data-i=${i} data-n=${m.badge ?? nothing} tabindex=${tabStop ? 0 : -1}
232
+ data-link=${link ?? nothing} data-i=${i} data-n=${m.badge ?? nothing} tabindex=${tabStop ? 0 : -1}
231
233
  title=${m.tip ?? nothing}
232
234
  aria-label=${m.label} aria-pressed=${!!sel}
233
235
  @mouseenter=${() => (h.hover = hlKey)}
@@ -59,7 +59,7 @@ const legendMark = (h: GraphHost, state: string, badge?: string) => {
59
59
 
60
60
  /**
61
61
  * What the view shows that needs a word: the states of the marks shown, a badge when a
62
- * mark shows one, the icons the host names, the edge styles when both are drawn, a
62
+ * mark shows one, the icons the host names, the edge styles when another than solid is drawn, a
63
63
  * cluster's return, and the mark of an item with an inner view.
64
64
  */
65
65
  function legend(h: GraphHost, mx: MarkMatrixInput | null) {
@@ -74,12 +74,15 @@ function legend(h: GraphHost, mx: MarkMatrixInput | null) {
74
74
  const shown = marks.filter((m): m is { state: string; badge: string | undefined } => m !== null);
75
75
  const present = new Set(shown.map((m) => m.state));
76
76
  const badge = shown.some((m) => m.badge);
77
- const dashed = !mx && !flat && [...g.edges, ...g.stubs].some((e) => e.style === 'dashed');
77
+ const styles = new Set(!mx && !flat ? [...g.edges, ...g.stubs].map((e) => e.style) : []);
78
+ const edgeStyles = styles.size > 1 || (styles.size === 1 && !styles.has('solid')) ? (['solid', 'dotted', 'dashed'] as const).filter((s) => styles.has(s)) : [];
78
79
  const icons = new Set(mx ? mx.head.flat().flatMap((c) => c.icons ?? []) : g.nodes.flatMap((n) => n.icons ?? []));
79
80
  const cycles = !mx && !flat && g.clusters.some((c) => c.cycle);
80
81
  const inner = !mx && ((!flat && g.clusters.some((c) => c.drill)) || g.nodes.some((n) => n.drill));
81
82
  const first = Object.keys(h.states)[0] ?? '';
82
- const line = (dash: boolean) => svg`<svg width="22" height="8" aria-hidden="true"><path d="M1,4 H21" stroke-dasharray=${dash ? '5 4' : 'none'}></path></svg>`;
83
+ const line = (style: 'solid' | 'dotted' | 'dashed') =>
84
+ svg`<svg width="22" height="8" aria-hidden="true"><path class=${style} d="M1,4 H21" stroke-dasharray=${style === 'dashed' ? '5 4' : style === 'dotted' ? '0.1 4' : 'none'}></path></svg>`;
85
+ const edgeWord = { solid: h.words.solidEdge, dotted: h.words.dottedEdge, dashed: h.words.dashedEdge };
83
86
  const items: LegendItem[] = [
84
87
  ...Object.entries(h.states)
85
88
  .filter(([s]) => present.has(s))
@@ -88,12 +91,7 @@ function legend(h: GraphHost, mx: MarkMatrixInput | null) {
88
91
  ...Object.entries(h.words.iconNames)
89
92
  .filter(([name]) => icons.has(name))
90
93
  .map(([name, label]) => ({ label, icon: h.renderIcon(name) })),
91
- ...(dashed
92
- ? [
93
- { label: h.words.solidEdge, icon: line(false) },
94
- { label: h.words.dashedEdge, icon: line(true) },
95
- ]
96
- : []),
94
+ ...edgeStyles.map((s) => ({ label: edgeWord[s], icon: line(s) })),
97
95
  ...(cycles
98
96
  ? [
99
97
  {
@@ -7,13 +7,13 @@ import { cycleIcon, innerIcon } from '../icons.js';
7
7
  import type { EdgeStyle, GraphInput, GraphNode } from '../types.js';
8
8
  import { notes } from './dag.js';
9
9
 
10
- /** The names of the nodes `n` is joined to: by a solid edge, by a dashed one, and the later ones. */
10
+ /** The names of the nodes `n` is joined to: by a solid edge, a dotted one or a dashed one, and the later ones. */
11
11
  export function neighbours(
12
12
  n: GraphNode,
13
13
  g: Pick<GraphInput, 'edges' | 'stubs'>,
14
14
  nodes: ReadonlyMap<string, GraphNode>,
15
15
  inCluster: (label: string) => string,
16
- ): { solid: string[]; dashed: string[]; later: string[] } {
16
+ ): { solid: string[]; dotted: string[]; dashed: string[]; later: string[] } {
17
17
  const label = (id: string) => {
18
18
  const x = nodes.get(id);
19
19
  return !x ? id : x.cluster ? inCluster(x.label) : x.label;
@@ -22,7 +22,7 @@ export function neighbours(
22
22
  ...g.stubs.filter((s) => s.to === n.id && s.style === style).map((s) => s.label),
23
23
  ...g.edges.filter((e) => e.to === n.id && e.style === style).map((e) => label(e.from)),
24
24
  ];
25
- return { solid: into('solid'), dashed: into('dashed'), later: [...new Set(g.edges.filter((e) => e.from === n.id).map((e) => label(e.to)))] };
25
+ return { solid: into('solid'), dotted: into('dotted'), dashed: into('dashed'), later: [...new Set(g.edges.filter((e) => e.from === n.id).map((e) => label(e.to)))] };
26
26
  }
27
27
 
28
28
  /**
@@ -33,6 +33,7 @@ export function neighbours(
33
33
  export function nodeTable(h: GraphHost) {
34
34
  const g = h.drawn;
35
35
  const w = h.words;
36
+ const dotted = [...g.edges, ...g.stubs].some((e) => e.style === 'dotted');
36
37
  const dashed = [...g.edges, ...g.stubs].some((e) => e.style === 'dashed');
37
38
  const list = (xs: string[]) => w.list(xs) || w.none;
38
39
  // The frame keeps the drawing's height when the drawing of this graph was laid out (left to right); else the table's own, up to the frame's limit.
@@ -46,6 +47,7 @@ export function nodeTable(h: GraphHost) {
46
47
  <th scope="col" class="fit">${w.nameHeader}</th>
47
48
  <th scope="col" class="fit">${w.statusHeader}</th>
48
49
  <th scope="col">${w.solidHeader}</th>
50
+ ${dotted ? html`<th scope="col">${w.dottedHeader}</th>` : nothing}
49
51
  ${dashed ? html`<th scope="col">${w.dashedHeader}</th>` : nothing}
50
52
  <th scope="col">${w.laterHeader}</th>
51
53
  </tr>
@@ -72,6 +74,7 @@ export function nodeTable(h: GraphHost) {
72
74
  </th>
73
75
  <td class="fit st">${statusLine(n)}</td>
74
76
  <td class="rel"><span>${list(nb.solid)}</span></td>
77
+ ${dotted ? html`<td class="rel"><span>${list(nb.dotted)}</span></td>` : nothing}
75
78
  ${dashed ? html`<td class="rel"><span>${list(nb.dashed)}</span></td>` : nothing}
76
79
  <td class="rel"><span>${list(nb.later)}</span></td>
77
80
  </tr>`;