@aceshooting/lyra-ui 0.1.2 → 0.1.4

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 (80) hide show
  1. package/README.md +2 -8
  2. package/custom-elements.json +3745 -2953
  3. package/dist/components/chart/bar-chart.js +17 -4
  4. package/dist/components/chart/box-plot.d.ts +8 -1
  5. package/dist/components/chart/box-plot.js +40 -4
  6. package/dist/components/chart/box-plot.styles.js +4 -0
  7. package/dist/components/chart/bubble-chart.d.ts +1 -2
  8. package/dist/components/chart/bubble-chart.js +17 -4
  9. package/dist/components/chart/chart-loader.d.ts +21 -4
  10. package/dist/components/chart/chart-loader.js +39 -12
  11. package/dist/components/chart/chart.d.ts +9 -3
  12. package/dist/components/chart/chart.js +42 -21
  13. package/dist/components/chart/doughnut-chart.d.ts +1 -2
  14. package/dist/components/chart/doughnut-chart.js +17 -4
  15. package/dist/components/chart/histogram-bin.js +12 -2
  16. package/dist/components/chart/histogram.d.ts +2 -2
  17. package/dist/components/chart/histogram.js +16 -2
  18. package/dist/components/chart/line-chart.js +17 -4
  19. package/dist/components/chart/pie-chart.d.ts +1 -2
  20. package/dist/components/chart/pie-chart.js +17 -4
  21. package/dist/components/chart/polar-area-chart.d.ts +1 -2
  22. package/dist/components/chart/polar-area-chart.js +17 -4
  23. package/dist/components/chart/radar-chart.d.ts +1 -2
  24. package/dist/components/chart/radar-chart.js +17 -4
  25. package/dist/components/chart/scatter-chart.js +17 -4
  26. package/dist/components/combobox/combobox.js +3 -4
  27. package/dist/components/combobox/combobox.stories.d.ts +6 -0
  28. package/dist/components/combobox/combobox.stories.js +27 -0
  29. package/dist/components/combobox/combobox.styles.js +2 -2
  30. package/dist/components/date-picker/date-input.stories.d.ts +5 -0
  31. package/dist/components/date-picker/date-input.stories.js +12 -0
  32. package/dist/components/date-picker/date-input.styles.js +2 -2
  33. package/dist/components/date-picker/date-picker.stories.d.ts +6 -0
  34. package/dist/components/date-picker/date-picker.stories.js +13 -0
  35. package/dist/components/empty/empty.d.ts +1 -1
  36. package/dist/components/empty/empty.js +1 -1
  37. package/dist/components/export-button/export-button.d.ts +2 -2
  38. package/dist/components/export-button/export-button.js +2 -2
  39. package/dist/components/file-input/accept.d.ts +1 -2
  40. package/dist/components/file-input/accept.js +1 -2
  41. package/dist/components/file-input/file-input.d.ts +2 -2
  42. package/dist/components/file-input/file-input.js +2 -2
  43. package/dist/components/flag/flag.stories.d.ts +5 -0
  44. package/dist/components/flag/flag.stories.js +17 -0
  45. package/dist/components/flag/flag.styles.js +1 -1
  46. package/dist/components/gauge/gauge.d.ts +1 -1
  47. package/dist/components/gauge/gauge.js +1 -1
  48. package/dist/components/graph/graph.d.ts +33 -4
  49. package/dist/components/graph/graph.js +146 -30
  50. package/dist/components/heatmap/calendar-grid.d.ts +31 -0
  51. package/dist/components/heatmap/calendar-grid.js +52 -0
  52. package/dist/components/heatmap/heatmap-scale.d.ts +8 -0
  53. package/dist/components/heatmap/heatmap-scale.js +21 -0
  54. package/dist/components/heatmap/heatmap.d.ts +19 -0
  55. package/dist/components/heatmap/heatmap.js +133 -20
  56. package/dist/components/heatmap/heatmap.styles.js +4 -0
  57. package/dist/components/map/map.d.ts +15 -0
  58. package/dist/components/map/map.js +89 -7
  59. package/dist/components/playback/playback.d.ts +2 -2
  60. package/dist/components/playback/playback.js +2 -2
  61. package/dist/components/skeleton/skeleton.d.ts +2 -2
  62. package/dist/components/skeleton/skeleton.js +2 -2
  63. package/dist/components/sparkline/sparkline.stories.d.ts +7 -0
  64. package/dist/components/sparkline/sparkline.stories.js +17 -0
  65. package/dist/components/stat/stat.d.ts +1 -1
  66. package/dist/components/stat/stat.js +6 -7
  67. package/dist/components/table/table.d.ts +1 -2
  68. package/dist/components/table/table.js +1 -2
  69. package/dist/components/table/table.styles.js +2 -3
  70. package/dist/components/time-range/time-range.js +4 -4
  71. package/dist/components/toast/toast.stories.d.ts +5 -0
  72. package/dist/components/toast/toast.stories.js +32 -0
  73. package/dist/components/tree/tree-node.d.ts +2 -2
  74. package/dist/components/tree/tree-node.js +2 -2
  75. package/dist/internal/form-associated.js +1 -2
  76. package/dist/internal/icons.js +1 -1
  77. package/dist/internal/tokens.styles.js +3 -3
  78. package/llms-full.txt +59 -83
  79. package/llms.txt +1 -2
  80. package/package.json +4 -1
@@ -6,9 +6,22 @@ import { defineElement } from '../../internal/prefix.js';
6
6
  * @customElement lyra-polar-area-chart
7
7
  */
8
8
  export class LyraPolarAreaChart extends LyraChart {
9
- constructor() {
10
- super(...arguments);
11
- this.type = 'polarArea';
12
- }
13
9
  }
10
+ // `type` is locked read-only rather than a settable class field with just a
11
+ // default value — `LyraChart` declares it as a plain (decorator-managed)
12
+ // class field, and TypeScript forbids a subclass from re-declaring a base
13
+ // field as a getter/setter pair via ordinary class syntax (TS2611) — so the
14
+ // accessor pair is installed directly on the prototype instead, which is
15
+ // runtime-equivalent (same shadowing semantics as a class-syntax override)
16
+ // without tripping that check.
17
+ Object.defineProperty(LyraPolarAreaChart.prototype, 'type', {
18
+ configurable: true,
19
+ enumerable: true,
20
+ get() {
21
+ return 'polarArea';
22
+ },
23
+ set(_v) {
24
+ /* locked to 'polarArea'; direct writes are ignored */
25
+ },
26
+ });
14
27
  defineElement('polar-area-chart', LyraPolarAreaChart);
@@ -1,12 +1,11 @@
1
1
  import { LyraChart } from './chart.js';
2
- import type { LyraChartType } from './chart.js';
3
2
  /**
4
3
  * `<lyra-radar-chart>` — `<lyra-chart>` with `type` locked to `"radar"`.
5
4
  *
6
5
  * @customElement lyra-radar-chart
7
6
  */
8
7
  export declare class LyraRadarChart extends LyraChart {
9
- type: LyraChartType;
8
+ type: 'radar';
10
9
  }
11
10
  declare global {
12
11
  interface HTMLElementTagNameMap {
@@ -6,9 +6,22 @@ import { defineElement } from '../../internal/prefix.js';
6
6
  * @customElement lyra-radar-chart
7
7
  */
8
8
  export class LyraRadarChart extends LyraChart {
9
- constructor() {
10
- super(...arguments);
11
- this.type = 'radar';
12
- }
13
9
  }
10
+ // `type` is locked read-only rather than a settable class field with just a
11
+ // default value — `LyraChart` declares it as a plain (decorator-managed)
12
+ // class field, and TypeScript forbids a subclass from re-declaring a base
13
+ // field as a getter/setter pair via ordinary class syntax (TS2611) — so the
14
+ // accessor pair is installed directly on the prototype instead, which is
15
+ // runtime-equivalent (same shadowing semantics as a class-syntax override)
16
+ // without tripping that check.
17
+ Object.defineProperty(LyraRadarChart.prototype, 'type', {
18
+ configurable: true,
19
+ enumerable: true,
20
+ get() {
21
+ return 'radar';
22
+ },
23
+ set(_v) {
24
+ /* locked to 'radar'; direct writes are ignored */
25
+ },
26
+ });
14
27
  defineElement('radar-chart', LyraRadarChart);
@@ -7,9 +7,22 @@ import { defineElement } from '../../internal/prefix.js';
7
7
  * @customElement lyra-scatter-chart
8
8
  */
9
9
  export class LyraScatterChart extends LyraChart {
10
- constructor() {
11
- super(...arguments);
12
- this.type = 'scatter';
13
- }
14
10
  }
11
+ // `type` is locked read-only rather than a settable class field with just a
12
+ // default value — `LyraChart` declares it as a plain (decorator-managed)
13
+ // class field, and TypeScript forbids a subclass from re-declaring a base
14
+ // field as a getter/setter pair via ordinary class syntax (TS2611) — so the
15
+ // accessor pair is installed directly on the prototype instead, which is
16
+ // runtime-equivalent (same shadowing semantics as a class-syntax override)
17
+ // without tripping that check.
18
+ Object.defineProperty(LyraScatterChart.prototype, 'type', {
19
+ configurable: true,
20
+ enumerable: true,
21
+ get() {
22
+ return 'scatter';
23
+ },
24
+ set(_v) {
25
+ /* locked to 'scatter'; direct writes are ignored */
26
+ },
27
+ });
15
28
  defineElement('scatter-chart', LyraScatterChart);
@@ -99,10 +99,9 @@ export class LyraCombobox extends LyraElement {
99
99
  this._defaultCaptured = true;
100
100
  // Seed the initial selection — and the reset default — from
101
101
  // declarative `<lyra-option selected>` markup — mirrors native
102
- // `<select><option selected>` the exact case the audit found
103
- // silently ignored (forms-core §combobox, Medium). This is the only
104
- // place `_defaultSelected` is set; picking an option later (the
105
- // `value` setter) never redefines the reset default, so an
102
+ // `<select><option selected>`, which was previously silently ignored.
103
+ // This is the only place `_defaultSelected` is set; picking an option
104
+ // later (the `value` setter) never redefines the reset default, so an
106
105
  // initially-unselected combobox always resets back to empty even
107
106
  // after the user has picked something.
108
107
  const declared = this.options.filter((o) => o.selected).map((o) => o.value);
@@ -0,0 +1,6 @@
1
+ import type { Meta, StoryObj } from '@storybook/web-components-vite';
2
+ declare const meta: Meta;
3
+ export default meta;
4
+ type Story = StoryObj;
5
+ export declare const Default: Story;
6
+ export declare const Multiple: Story;
@@ -0,0 +1,27 @@
1
+ import { html } from 'lit';
2
+ const meta = {
3
+ title: 'Combobox',
4
+ component: 'lyra-combobox',
5
+ tags: ['autodocs'],
6
+ };
7
+ export default meta;
8
+ export const Default = {
9
+ render: () => html `
10
+ <lyra-combobox label="Fruit" placeholder="Pick one…" with-clear style="max-width: 20rem">
11
+ <lyra-option value="a">Apple</lyra-option>
12
+ <lyra-option value="b">Banana</lyra-option>
13
+ <lyra-option value="c">Cherry</lyra-option>
14
+ <lyra-option value="d">Date</lyra-option>
15
+ </lyra-combobox>
16
+ `,
17
+ };
18
+ export const Multiple = {
19
+ render: () => html `
20
+ <lyra-combobox label="Fruit" multiple with-clear style="max-width: 20rem">
21
+ <lyra-option value="a">Apple</lyra-option>
22
+ <lyra-option value="b">Banana</lyra-option>
23
+ <lyra-option value="c">Cherry</lyra-option>
24
+ <lyra-option value="d">Date</lyra-option>
25
+ </lyra-combobox>
26
+ `,
27
+ };
@@ -89,8 +89,8 @@ export const styles = css `
89
89
  color: var(--lyra-color-text-quiet);
90
90
  padding: var(--lyra-space-xs);
91
91
  /* Real touch target in *both* dimensions (WCAG 2.2 SC 2.5.8 needs
92
- 24x24 CSS px, not just height — a design-review fix that only set
93
- min-block-size left these buttons ~21px wide). icon-button-size is
92
+ 24x24 CSS px, not just height — min-block-size alone left these
93
+ buttons ~21px wide). icon-button-size is
94
94
  the ceiling, but never grow past what the [part=combobox] row's own
95
95
  2.5rem min-block-size has room for once its own block padding is
96
96
  subtracted. */
@@ -0,0 +1,5 @@
1
+ import type { Meta, StoryObj } from '@storybook/web-components-vite';
2
+ declare const meta: Meta;
3
+ export default meta;
4
+ type Story = StoryObj;
5
+ export declare const Default: Story;
@@ -0,0 +1,12 @@
1
+ import { html } from 'lit';
2
+ const meta = {
3
+ title: 'DatePicker/WithInput',
4
+ component: 'lyra-date-input',
5
+ tags: ['autodocs'],
6
+ };
7
+ export default meta;
8
+ export const Default = {
9
+ render: () => html `
10
+ <lyra-date-input label="Start date" with-clear style="max-width: 16rem"></lyra-date-input>
11
+ `,
12
+ };
@@ -60,8 +60,8 @@ export const styles = css `
60
60
  color: var(--lyra-color-text-quiet);
61
61
  padding: var(--lyra-space-xs);
62
62
  /* Real touch target in *both* dimensions (WCAG 2.2 SC 2.5.8 needs
63
- 24x24 CSS px, not just height — a design-review fix that only set
64
- min-block-size left these buttons 24px tall but narrower than that).
63
+ 24x24 CSS px, not just height — min-block-size alone left these
64
+ buttons 24px tall but narrower than that).
65
65
  The row has no explicit min-block-size of its own (unlike combobox's
66
66
  [part=combobox]), so it can grow to fit the full touch target. */
67
67
  min-inline-size: var(--lyra-icon-button-size);
@@ -0,0 +1,6 @@
1
+ import type { Meta, StoryObj } from '@storybook/web-components-vite';
2
+ declare const meta: Meta;
3
+ export default meta;
4
+ type Story = StoryObj;
5
+ export declare const Single: Story;
6
+ export declare const Range: Story;
@@ -0,0 +1,13 @@
1
+ import { html } from 'lit';
2
+ const meta = {
3
+ title: 'DatePicker/Inline',
4
+ component: 'lyra-date-picker',
5
+ tags: ['autodocs'],
6
+ };
7
+ export default meta;
8
+ export const Single = {
9
+ render: () => html `<lyra-date-picker mode="single"></lyra-date-picker>`,
10
+ };
11
+ export const Range = {
12
+ render: () => html `<lyra-date-picker mode="range" months="2"></lyra-date-picker>`,
13
+ };
@@ -2,7 +2,7 @@ import { type TemplateResult } from 'lit';
2
2
  import { LyraElement } from '../../internal/lyra-element.js';
3
3
  /**
4
4
  * `<lyra-empty>` — a generic empty/no-data state. First-party invention (no
5
- * Web Awesome equivalent); fills the gap identified across ~30 surveyed repos.
5
+ * Web Awesome equivalent); fills a gap common to dashboard-style apps.
6
6
  *
7
7
  * @customElement lyra-empty
8
8
  * @slot - Custom icon or illustration (defaults to none).
@@ -11,7 +11,7 @@ import { defineElement } from '../../internal/prefix.js';
11
11
  import { styles } from './empty.styles.js';
12
12
  /**
13
13
  * `<lyra-empty>` — a generic empty/no-data state. First-party invention (no
14
- * Web Awesome equivalent); fills the gap identified across ~30 surveyed repos.
14
+ * Web Awesome equivalent); fills a gap common to dashboard-style apps.
15
15
  *
16
16
  * @customElement lyra-empty
17
17
  * @slot - Custom icon or illustration (defaults to none).
@@ -4,8 +4,8 @@ import { type CsvColumn } from './csv.js';
4
4
  export type ExportFormat = 'csv' | 'json';
5
5
  /**
6
6
  * `<lyra-export-button>` — a CSV/JSON download button, single-format or a
7
- * format-choice menu. First-party invention; consolidates ad-hoc "export CSV"
8
- * buttons duplicated across surveyed repos.
7
+ * format-choice menu. First-party invention; consolidates the ad-hoc
8
+ * "export CSV" button pattern common across dashboard UIs.
9
9
  *
10
10
  * @customElement lyra-export-button
11
11
  * @event lyra-export - `detail: { format }`, cancelable — call `preventDefault()`
@@ -13,8 +13,8 @@ import { buildCsv, downloadBlob } from './csv.js';
13
13
  import { styles } from './export-button.styles.js';
14
14
  /**
15
15
  * `<lyra-export-button>` — a CSV/JSON download button, single-format or a
16
- * format-choice menu. First-party invention; consolidates ad-hoc "export CSV"
17
- * buttons duplicated across surveyed repos.
16
+ * format-choice menu. First-party invention; consolidates the ad-hoc
17
+ * "export CSV" button pattern common across dashboard UIs.
18
18
  *
19
19
  * @customElement lyra-export-button
20
20
  * @event lyra-export - `detail: { format }`, cancelable — call `preventDefault()`
@@ -2,8 +2,7 @@
2
2
  * Parses a native-`accept`-style string (`".csv,.xlsx"`, `"text/csv"`,
3
3
  * `"image/*"`, or any mix, comma-separated) and reports whether `file`
4
4
  * matches it — the same three forms the browser's own file picker accepts,
5
- * now also enforced on the drag-drop path (2026-07-10 audit,
6
- * "map-file-input" §lyra-file-input, High: previously `accept` only
5
+ * now also enforced on the drag-drop path (previously `accept` only
7
6
  * constrained the native picker dialog and had no effect on drop).
8
7
  *
9
8
  * `file` may be a `DataTransferItem` cast as `File` (the dragenter-preview
@@ -2,8 +2,7 @@
2
2
  * Parses a native-`accept`-style string (`".csv,.xlsx"`, `"text/csv"`,
3
3
  * `"image/*"`, or any mix, comma-separated) and reports whether `file`
4
4
  * matches it — the same three forms the browser's own file picker accepts,
5
- * now also enforced on the drag-drop path (2026-07-10 audit,
6
- * "map-file-input" §lyra-file-input, High: previously `accept` only
5
+ * now also enforced on the drag-drop path (previously `accept` only
7
6
  * constrained the native picker dialog and had no effect on drop).
8
7
  *
9
8
  * `file` may be a `DataTransferItem` cast as `File` (the dragenter-preview
@@ -6,8 +6,8 @@ export interface RejectedFile {
6
6
  }
7
7
  /**
8
8
  * `<lyra-file-input>` — a drag-drop + click-to-browse file dropzone. Emits
9
- * raw `File[]`; parsing (CSV/XLSX/etc.) is left to the host, matching every
10
- * surveyed upload seed (all of which upload raw files to a backend).
9
+ * raw `File[]`; parsing (CSV/XLSX/etc.) is left to the host, since that's
10
+ * where files ultimately get uploaded and processed anyway.
11
11
  *
12
12
  * @customElement lyra-file-input
13
13
  * @slot - Custom drop-zone content, overrides the `label` attribute.
@@ -12,8 +12,8 @@ import { styles } from './file-input.styles.js';
12
12
  import { matchesAccept } from './accept.js';
13
13
  /**
14
14
  * `<lyra-file-input>` — a drag-drop + click-to-browse file dropzone. Emits
15
- * raw `File[]`; parsing (CSV/XLSX/etc.) is left to the host, matching every
16
- * surveyed upload seed (all of which upload raw files to a backend).
15
+ * raw `File[]`; parsing (CSV/XLSX/etc.) is left to the host, since that's
16
+ * where files ultimately get uploaded and processed anyway.
17
17
  *
18
18
  * @customElement lyra-file-input
19
19
  * @slot - Custom drop-zone content, overrides the `label` attribute.
@@ -0,0 +1,5 @@
1
+ import type { Meta, StoryObj } from '@storybook/web-components-vite';
2
+ declare const meta: Meta;
3
+ export default meta;
4
+ type Story = StoryObj;
5
+ export declare const Gallery: Story;
@@ -0,0 +1,17 @@
1
+ import { html } from 'lit';
2
+ const meta = {
3
+ title: 'Flag',
4
+ component: 'lyra-flag',
5
+ tags: ['autodocs'],
6
+ };
7
+ export default meta;
8
+ export const Gallery = {
9
+ render: () => html `
10
+ <div style="display:flex; gap:1rem; align-items:center;">
11
+ <lyra-flag country="fr" label="France" style="height: 1.5rem"></lyra-flag>
12
+ <lyra-flag language="en" label="English" style="height: 1.5rem"></lyra-flag>
13
+ <lyra-flag language="de" label="German" round style="height: 1.5rem"></lyra-flag>
14
+ <lyra-flag country="jp" label="Japan" round style="height: 1.5rem"></lyra-flag>
15
+ </div>
16
+ `,
17
+ };
@@ -10,7 +10,7 @@ export const styles = css `
10
10
  [part='image'] {
11
11
  display: block;
12
12
  block-size: 100%;
13
- inline-size: auto;
13
+ inline-size: 100%;
14
14
  border-radius: var(--lyra-flag-radius, 2px);
15
15
  box-shadow: 0 0 0 1px rgb(0 0 0 / 0.08) inset;
16
16
  object-fit: cover;
@@ -3,7 +3,7 @@ import { LyraElement } from '../../internal/lyra-element.js';
3
3
  export type GaugeType = 'radial' | 'linear';
4
4
  /**
5
5
  * `<lyra-gauge>` — a radial or linear meter. First-party invention; no
6
- * generic gauge widget was found across the surveyed repos.
6
+ * generic gauge widget exists in Web Awesome.
7
7
  *
8
8
  * @customElement lyra-gauge
9
9
  * @csspart base, track, fill, value, label
@@ -41,7 +41,7 @@ const RADIAL_ARC_D = arcPath(START_DEG, START_DEG + SWEEP_DEG);
41
41
  const RADIAL_ARC_LENGTH = (SWEEP_DEG / 360) * 2 * Math.PI * RADIUS;
42
42
  /**
43
43
  * `<lyra-gauge>` — a radial or linear meter. First-party invention; no
44
- * generic gauge widget was found across the surveyed repos.
44
+ * generic gauge widget exists in Web Awesome.
45
45
  *
46
46
  * @customElement lyra-gauge
47
47
  * @csspart base, track, fill, value, label
@@ -30,6 +30,8 @@ export declare class LyraGraph extends LyraElement {
30
30
  height: number;
31
31
  chargeStrength: number;
32
32
  linkDistance: number;
33
+ minZoom: number;
34
+ maxZoom: number;
33
35
  /** True until the lazy-loaded d3 peer dependencies have settled (success or failure). */
34
36
  private loading;
35
37
  private simNodes;
@@ -37,25 +39,52 @@ export declare class LyraGraph extends LyraElement {
37
39
  /** Current pan/zoom transform (SVG `transform` attribute syntax), driven by d3-zoom. */
38
40
  private transform;
39
41
  private simulation?;
42
+ /** The live charge/link force objects, kept so chargeStrength/linkDistance
43
+ * changes can retune them in place (see updated()) instead of requiring a
44
+ * full rebuildSimulation(). */
45
+ private chargeForce?;
46
+ private linkForce?;
40
47
  private d3?;
41
48
  /** The `<svg>` currently wired up with d3-zoom (guards a one-time bind). */
42
49
  private zoomedEl?;
43
50
  /** Node `<circle>`s already wired up with d3-drag; cleared on every simulation rebuild
44
51
  * so DOM elements Lit reuses across a rebuild get rebound to their fresh datum. */
45
52
  private boundNodeEls;
53
+ /** Node/link/label DOM elements, index-aligned with simNodes/simLinks, cached
54
+ * once per structural rebuild and written to directly by onTick() — this is
55
+ * what lets ticks update positions without going through Lit's reactive
56
+ * simNodes/simLinks properties (see rebuildSimulation()'s doc comment). */
57
+ private nodeEls;
58
+ private nodeLabelEls;
59
+ private linkEls;
46
60
  connectedCallback(): void;
47
61
  disconnectedCallback(): void;
48
62
  private nodeRadius;
63
+ protected willUpdate(changed: PropertyValues): void;
49
64
  protected updated(changed: PropertyValues): void;
50
65
  /**
51
66
  * Imperatively wires up d3-zoom (pan/zoom on the `<svg>`) and d3-drag
52
- * (per-node drag) against the just-rendered DOM. Runs after every render:
53
- * binding the `<svg>` is a one-time no-op guard (`zoomedEl`), and node
54
- * `<circle>`s are skipped once bound (`boundNodeEls`) a WeakSet reset on
55
- * every `rebuildSimulation()` so DOM nodes Lit reuses across a rebuild get
67
+ * (per-node drag) against the just-rendered DOM. The zoom bind is a
68
+ * one-time no-op guard (`zoomedEl`); the node-drag bind + node/link/label
69
+ * element caching for `onTick()` only run when `structural` is true (a
70
+ * fresh structural render just happened), not on every call otherwise
71
+ * this would re-scan the DOM via `querySelectorAll` on every Lit update,
72
+ * which used to include every single simulation tick. `<circle>`s already
73
+ * bound are skipped (`boundNodeEls`) — a WeakSet reset on every
74
+ * `rebuildSimulation()` so DOM nodes Lit reuses across a rebuild get
56
75
  * rebound against their new datum instead of a stale one.
57
76
  */
58
77
  private applyInteractions;
78
+ /**
79
+ * Runs on every d3-force simulation tick (up to ~300 while a graph settles
80
+ * on load, continuously while a node is being dragged via
81
+ * `alphaTarget(0.3)`). Writes positions straight to the already-rendered
82
+ * DOM via direct d3-selection `.attr()` calls instead of reassigning the
83
+ * reactive `simNodes`/`simLinks` properties, which would force a full Lit
84
+ * re-render (and, before the `structural` gate above, an unconditional
85
+ * `querySelectorAll` scan) on every single frame.
86
+ */
87
+ private onTick;
59
88
  private rebuildSimulation;
60
89
  private onNodeClick;
61
90
  private onLinkClick;