@kahwee/sf-map-svg 5.1.3 → 6.0.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.
Files changed (59) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +1 -5
  3. package/dist/data/README.md +1 -1
  4. package/dist/src/cluster-renderer.d.ts +1 -1
  5. package/dist/src/district-layer.d.ts +4 -4
  6. package/dist/src/district-layer.js +1 -1
  7. package/dist/src/district-style.d.ts +3 -3
  8. package/dist/src/explorer-data.d.ts +1 -1
  9. package/dist/src/guide-map.d.ts +0 -7
  10. package/dist/src/guide-map.js +0 -33
  11. package/dist/src/guide-static.d.ts +4 -4
  12. package/dist/src/guide-static.js +14 -11
  13. package/dist/src/guide.d.ts +2 -2
  14. package/dist/src/guide.js +1 -1
  15. package/dist/src/{configuration-controller.d.ts → interactive/configuration-controller.d.ts} +7 -7
  16. package/dist/src/{configuration-controller.js → interactive/configuration-controller.js} +2 -2
  17. package/dist/src/{district-controller.d.ts → interactive/district-controller.d.ts} +12 -12
  18. package/dist/src/{district-controller.js → interactive/district-controller.js} +9 -9
  19. package/dist/src/{explorer-sidebar.d.ts → interactive/explorer-sidebar.d.ts} +6 -6
  20. package/dist/src/{explorer-sidebar.js → interactive/explorer-sidebar.js} +4 -4
  21. package/dist/src/{label-coordinator.d.ts → interactive/label-coordinator.d.ts} +7 -7
  22. package/dist/src/{label-coordinator.js → interactive/label-coordinator.js} +2 -2
  23. package/dist/src/{label-renderer.d.ts → interactive/label-renderer.d.ts} +2 -2
  24. package/dist/src/{label-renderer.js → interactive/label-renderer.js} +2 -2
  25. package/dist/src/{explorer-controls.d.ts → interactive/map-controls.d.ts} +4 -4
  26. package/dist/src/{explorer-controls.js → interactive/map-controls.js} +4 -4
  27. package/dist/src/{explorer-core.d.ts → interactive/map-engine.d.ts} +7 -7
  28. package/dist/src/{explorer-core.js → interactive/map-engine.js} +40 -40
  29. package/dist/src/{explorer-shell.d.ts → interactive/map-shell.d.ts} +2 -2
  30. package/dist/src/{explorer-shell.js → interactive/map-shell.js} +2 -2
  31. package/dist/src/{marker-controller.d.ts → interactive/marker-controller.d.ts} +8 -8
  32. package/dist/src/{marker-controller.js → interactive/marker-controller.js} +3 -3
  33. package/dist/src/{marker-layer.d.ts → interactive/marker-renderer.d.ts} +3 -3
  34. package/dist/src/{marker-layer.js → interactive/marker-renderer.js} +2 -2
  35. package/dist/src/map.js +4 -4
  36. package/dist/src/marker-catalog.d.ts +2 -2
  37. package/dist/src/palettes.d.ts +2 -2
  38. package/dist/src/{layers.d.ts → rendering/svg-layers.d.ts} +7 -7
  39. package/dist/src/{layers.js → rendering/svg-layers.js} +1 -1
  40. package/dist/src/{map-core.d.ts → rendering/svg-map-renderer.d.ts} +7 -7
  41. package/dist/src/{map-core.js → rendering/svg-map-renderer.js} +6 -6
  42. package/dist/src/static-animation.d.ts +2 -2
  43. package/dist/src/static-labels.d.ts +3 -3
  44. package/dist/src/static-labels.js +2 -2
  45. package/dist/src/static.d.ts +6 -6
  46. package/dist/src/static.js +4 -9
  47. package/dist/src/transit.js +1 -1
  48. package/dist/src/types.d.ts +13 -11
  49. package/docs/API.md +7 -11
  50. package/docs/EXAMPLES.md +2 -7
  51. package/docs/api-audit.md +3 -3
  52. package/docs/consumer-integration.md +1 -1
  53. package/docs/developer-guide.md +1 -6
  54. package/docs/migration-v6.md +57 -0
  55. package/llms-full.txt +132 -82
  56. package/llms.txt +2 -1
  57. package/package.json +2 -1
  58. /package/dist/src/{svg.d.ts → rendering/svg-primitives.d.ts} +0 -0
  59. /package/dist/src/{svg.js → rendering/svg-primitives.js} +0 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,17 @@ User-visible changes are recorded here. Unreleased entries describe changes on `
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 6.0.0 — 2026-11-05
8
+
9
+ - Breaking: remove flat static presentation options. Use `layers` for geographic switches and `appearance` for theme, colors and district styles.
10
+ - Breaking: remove element-based `createGuideMap` and `mountGuideMap` factories and their option/element aliases. Use `createGuideController` and `mountGuideController`, mount `.element`, and dispose the controller.
11
+ - Remove older-package capability adapters from the site and snippet generator; require v6 or newer for Pages builds and versioned playground share links. Add a v6 migration guide and rejection coverage for removed contracts.
12
+ - Use official sources only on the Pages election views: remove all newspaper and agency-compilation tallies, projections, history and their validation from the feed, views, trend charts and tests. Local measures without official counts read Pending until official counts are imported.
13
+
14
+ ## 5.1.4 — 2026-11-05
15
+
16
+ - Group internal browser controllers, renderers and shells under `src/interactive`, and SVG generation under `src/rendering`. Rename the general map engine, controls and marker renderer to match their responsibilities; clarify selection-detail, neighborhood-results and layer-composition operations. Keep public package entry points unchanged.
17
+
7
18
  ## 5.1.3 — 2026-11-05
8
19
 
9
20
  - Give configuration snapshots and revisions a dedicated internal owner. Split presentation preparation from a guarded, single-use commit, rejecting stale or disposed updates before mutating presentation. Preserve public configuration and reset behavior.
package/README.md CHANGED
@@ -27,11 +27,7 @@ Upgrading from v3 to v4: update your Node runtime to 24 or newer. The map API, b
27
27
  import { renderMap } from '@kahwee/sf-map-svg';
28
28
  import { staticMapData } from '@kahwee/sf-map-svg/data/static';
29
29
 
30
- const { svg, project, viewBox } = renderMap(staticMapData, {
31
- year: 2022,
32
- landmarks: true,
33
- bartStations: true,
34
- });
30
+ const { svg, project, viewBox } = renderMap(staticMapData, { year: 2022, layers: { landmarks: true, bartStations: true } });
35
31
  ```
36
32
 
37
33
  `renderMap(data, options)` returns SVG markup and matching projection helpers. `/data/static` includes the complete static map without interactive lookup collections. Small maps can compose selected JSON exports from `/data/*` and pass them as `StaticMapData`. `getLayerPaths(data, options)` returns fitted geographic paths without SVG markup.
@@ -135,7 +135,7 @@ Run `pnpm data:normalize-realtor` when updating realtor geometry, then `pnpm dat
135
135
 
136
136
  ## Lightweight guide preset
137
137
 
138
- Import `@kahwee/sf-map-svg/guide` for the guide preset. It statically imports only a simplified coastline, simplified SFAR neighborhoods, six major park outlines, selected US 101 / I-280 / Highway 1 segments, six curated streets, and eight BART points. It does not import historical districts, alternate neighborhood sources, or the full catalog. `createGuideMap()` uses this overview by default. `loadGuideDetailedData()` explicitly loads detailed versions of those same selected datasets; it does not add historical or alternative datasets. Regenerate derived geometry with `pnpm data:guide` after updating canonical source files.
138
+ Import `@kahwee/sf-map-svg/guide` for the guide preset. It statically imports only a simplified coastline, simplified SFAR neighborhoods, six major park outlines, selected US 101 / I-280 / Highway 1 segments, six curated streets, and eight BART points. It does not import historical districts, alternate neighborhood sources, or the full catalog. `createGuideController()` uses this overview by default. `loadGuideDetailedData()` explicitly loads detailed versions of those same selected datasets; it does not add historical or alternative datasets. Regenerate derived geometry with `pnpm data:guide` after updating canonical source files.
139
139
 
140
140
  Neighborhood overview boundaries use a shared coordinate graph: connected boundary arcs are simplified once and retained vertices are applied to all adjacent polygons. The 0.00009 degree tolerance is approximately subpixel at an 800px city map; overview coastline and park geometry use similarly subpixel tolerances. Roads crossing park polygons remain separate overlays and are drawn above park fills. Detailed collections retain their canonical source coordinates.
141
141
 
@@ -1,5 +1,5 @@
1
1
  import type { normalizeFeatures } from './features.js';
2
- import type { MarkerItem, MarkerVisual } from './marker-layer.js';
2
+ import type { MarkerItem, MarkerVisual } from './interactive/marker-renderer.js';
3
3
  import type { createMarkerNavigation } from './marker-navigation.js';
4
4
  import type { MapViewport } from './types.js';
5
5
  interface ClusterState {
@@ -1,5 +1,5 @@
1
- import type { DistrictRowData } from './map-core.js';
2
- import type { DistrictStyle, DistrictYear, SFMapOptions } from './types.js';
1
+ import type { DistrictRowData } from './rendering/svg-map-renderer.js';
2
+ import type { DistrictStyle, DistrictYear, SvgRenderOptions } from './types.js';
3
3
  /** Apply prepared styles and interaction state without invoking consumer callbacks. */
4
4
  export declare function renderDistrictAppearance({ svg, rows, styles, selectedDistrict, hoveredDistrict, year, theme, colors, fills, lines, }: {
5
5
  svg: SVGSVGElement;
@@ -8,8 +8,8 @@ export declare function renderDistrictAppearance({ svg, rows, styles, selectedDi
8
8
  selectedDistrict: number | null;
9
9
  hoveredDistrict: number | null;
10
10
  year: DistrictYear;
11
- theme: SFMapOptions['theme'];
12
- colors: NonNullable<SFMapOptions['colors']>;
11
+ theme: SvgRenderOptions['theme'];
12
+ colors: NonNullable<SvgRenderOptions['colors']>;
13
13
  fills: boolean;
14
14
  lines: boolean;
15
15
  }): void;
@@ -1,4 +1,4 @@
1
- import { districtColors } from './map-core.js';
1
+ import { districtColors } from './rendering/svg-map-renderer.js';
2
2
  /** Apply prepared styles and interaction state without invoking consumer callbacks. */
3
3
  export function renderDistrictAppearance({ svg, rows, styles, selectedDistrict, hoveredDistrict, year, theme, colors, fills, lines, }) {
4
4
  for (const row of rows) {
@@ -1,4 +1,4 @@
1
- import type { DistrictRowData } from './map-core.js';
2
- import type { DistrictStyle, SFMapOptions } from './types.js';
1
+ import type { DistrictRowData } from './rendering/svg-map-renderer.js';
2
+ import type { DistrictStyle, SvgRenderOptions } from './types.js';
3
3
  /** Evaluate user code once and copy its result before changing the map. */
4
- export declare function prepareDistrictStyles(rows: readonly DistrictRowData[], callback: SFMapOptions['districtStyle']): ReadonlyMap<number, DistrictStyle>;
4
+ export declare function prepareDistrictStyles(rows: readonly DistrictRowData[], callback: SvgRenderOptions['districtStyle']): ReadonlyMap<number, DistrictStyle>;
@@ -1,5 +1,5 @@
1
1
  import type { DistrictProperties, FeatureCollection, NeighborhoodProperties, NeighborhoodSource } from '../data/types.js';
2
- import type { SFMapData } from './map-core.js';
2
+ import type { SFMapData } from './rendering/svg-map-renderer.js';
3
3
  import type { DistrictYear } from './types.js';
4
4
  /** Geographic inputs for the data-injected interactive map entry point. */
5
5
  export interface InteractiveSFMapData {
@@ -1,12 +1,5 @@
1
1
  import type { MapController, MapOptions } from './controller-types.js';
2
- import type { NeighborhoodExplorerElement as InteractiveSFMapElement, NeighborhoodExplorerOptions as InteractiveSFMapOptions } from './types.js';
3
2
  /** Preferred guide API: the same grouped options, events, camera and lifecycle as createMap. */
4
3
  export declare function createGuideController(options?: MapOptions): MapController;
5
4
  /** Enhance a static guide shell and return its owning controller. */
6
5
  export declare function mountGuideController(shell: HTMLElement, options?: MapOptions): MapController;
7
- /** Element-based compatibility API. Prefer createGuideController for new integrations. */
8
- export declare function createGuideMap(options?: InteractiveSFMapOptions): InteractiveSFMapElement;
9
- /** Enhance createGuideShell() in place with a fixed compact chrome layout.
10
- * Keep an accessible external place list when hiding the native pickers.
11
- */
12
- export declare function mountGuideMap(shell: HTMLElement, options?: InteractiveSFMapOptions): InteractiveSFMapElement;
@@ -1,10 +1,7 @@
1
1
  import { expandMapOptions, prepareConfiguration } from './configuration.js';
2
- import { createNeighborhoodExplorerCore } from './explorer-core.js';
3
- import { createMapShell } from './explorer-shell.js';
4
2
  import { guideMapData } from './guide-data.js';
5
3
  import { createMap } from './map.js';
6
4
  import { guideOptions } from './presets.js';
7
- import { validateExplorerOptions } from './validation.js';
8
5
  function controllerOptions(options) {
9
6
  expandMapOptions(options);
10
7
  const config = prepareConfiguration({ features: {}, layers: guideOptions.layers ?? {}, controls: {}, appearance: {} }, {
@@ -59,33 +56,3 @@ function mountFrame(frame, map) {
59
56
  map.querySelector('.sf-explorer-map-column > details')?.classList.add('sf-guide-sources');
60
57
  frame.replaceWith(map);
61
58
  }
62
- /** Element-based compatibility API. Prefer createGuideController for new integrations. */
63
- export function createGuideMap(options = {}) {
64
- validateExplorerOptions(options);
65
- return createNeighborhoodExplorerCore({
66
- mode: 'neighborhoods',
67
- ...options,
68
- layers: {
69
- ...guideOptions.layers,
70
- ...options.layers,
71
- },
72
- interface: 'map',
73
- }, guideMapData, { createShell: createMapShell });
74
- }
75
- /** Enhance createGuideShell() in place with a fixed compact chrome layout.
76
- * Keep an accessible external place list when hiding the native pickers.
77
- */
78
- export function mountGuideMap(shell, options = {}) {
79
- validateExplorerOptions(options);
80
- const frame = shellFrame(shell, options.attribution);
81
- const map = createGuideMap({
82
- ...options,
83
- attribution: 'compact',
84
- controls: {
85
- ...shellControls,
86
- ...options.controls,
87
- },
88
- });
89
- mountFrame(frame, map);
90
- return map;
91
- }
@@ -1,9 +1,9 @@
1
- import type { SFMapOptions } from './types.js';
1
+ import type { StaticMapOptions } from './types.js';
2
2
  /** Server-safe overview using exactly the guide browser geography. */
3
- export declare function createGuideSVG(options?: SFMapOptions): {
3
+ export declare function createGuideSVG(options?: StaticMapOptions): {
4
4
  svg: string;
5
5
  project: (coordinates: import("./geometry.js").Position) => [number, number];
6
6
  viewBox: [number, number, number, number];
7
7
  };
8
- /** Stable compact frame, progressively enhanced with mountGuideMap(). No browser globals. */
9
- export declare function createGuideShell(options?: SFMapOptions): string;
8
+ /** Stable compact frame, progressively enhanced with mountGuideController(). No browser globals. */
9
+ export declare function createGuideShell(options?: StaticMapOptions): string;
@@ -4,20 +4,23 @@ import { renderMap } from './static.js';
4
4
  /** Server-safe overview using exactly the guide browser geography. */
5
5
  export function createGuideSVG(options = {}) {
6
6
  return renderMap(guideMapData.map, {
7
- theme: 'transit',
8
- districtFills: false,
9
- districtLines: false,
10
- districtLabels: false,
11
- neighborhoodLines: true,
12
- neighborhoodLabels: options.labelLayout !== undefined,
13
- landmarks: true,
14
- highways: true,
15
- keyRoads: true,
16
- bartStations: true,
17
7
  ...options,
8
+ appearance: { theme: 'transit', ...options.appearance },
9
+ layers: {
10
+ districtFills: false,
11
+ districtLines: false,
12
+ districtLabels: false,
13
+ neighborhoodLines: true,
14
+ neighborhoodLabels: options.labelLayout !== undefined,
15
+ landmarks: true,
16
+ highways: true,
17
+ keyRoads: true,
18
+ bartStations: true,
19
+ ...options.layers,
20
+ },
18
21
  });
19
22
  }
20
- /** Stable compact frame, progressively enhanced with mountGuideMap(). No browser globals. */
23
+ /** Stable compact frame, progressively enhanced with mountGuideController(). No browser globals. */
21
24
  export function createGuideShell(options = {}) {
22
25
  if ((options.width !== undefined && options.width !== 800) ||
23
26
  (options.height !== undefined && options.height !== 800) ||
@@ -2,5 +2,5 @@ export type { MapController, MapOptions } from './controller-types.js';
2
2
  export type { InteractiveSFMapData } from './explorer-data.js';
3
3
  export { guideMapData } from './guide-data.js';
4
4
  export { loadGuideDetailedData } from './guide-detailed.js';
5
- export { createGuideController, createGuideMap, mountGuideController, mountGuideMap, } from './guide-map.js';
6
- export type { CameraOptions, MapFeatures, MapMarker, NeighborhoodExplorerElement as InteractiveSFMapElement, NeighborhoodExplorerOptions as InteractiveSFMapOptions, } from './types.js';
5
+ export { createGuideController, mountGuideController, } from './guide-map.js';
6
+ export type { CameraOptions, MapFeatures, MapMarker, } from './types.js';
package/dist/src/guide.js CHANGED
@@ -1,3 +1,3 @@
1
1
  export { guideMapData } from './guide-data.js';
2
2
  export { loadGuideDetailedData } from './guide-detailed.js';
3
- export { createGuideController, createGuideMap, mountGuideController, mountGuideMap, } from './guide-map.js';
3
+ export { createGuideController, mountGuideController, } from './guide-map.js';
@@ -1,6 +1,6 @@
1
- import { prepareConfiguration } from './configuration.js';
2
- import type { MapConfiguration } from './controller-types.js';
3
- import type { NeighborhoodExplorerElement } from './types.js';
1
+ import { prepareConfiguration } from '../configuration.js';
2
+ import type { MapConfiguration } from '../controller-types.js';
3
+ import type { NeighborhoodExplorerElement } from '../types.js';
4
4
  /** A prepared engine update may commit once, only while all revisions remain current. */
5
5
  export interface PreparedPresentation {
6
6
  commit(onCommit: () => void, isCurrent: () => boolean): boolean;
@@ -12,10 +12,10 @@ export interface ConfigurationTarget extends Pick<NeighborhoodExplorerElement, '
12
12
  export declare function createConfigurationController(initial: ReturnType<typeof prepareConfiguration>, element: ConfigurationTarget, isDestroyed: () => boolean): {
13
13
  configure: (patch: MapConfiguration) => boolean;
14
14
  readonly current: {
15
- features: import("./types.js").MapFeatures;
16
- layers: import("./types.js").InteractiveLayers;
17
- controls: import("./controller-types.js").MapControls;
18
- appearance: import("./controller-types.js").MapAppearance;
15
+ features: import("../types.js").MapFeatures;
16
+ layers: import("../types.js").InteractiveLayers;
17
+ controls: import("../controller-types.js").MapControls;
18
+ appearance: import("../controller-types.js").MapAppearance;
19
19
  };
20
20
  invalidate(): void;
21
21
  };
@@ -1,5 +1,5 @@
1
- import { prepareConfiguration } from './configuration.js';
2
- import { controlKeys, layerKeys } from './features.js';
1
+ import { prepareConfiguration } from '../configuration.js';
2
+ import { controlKeys, layerKeys } from '../features.js';
3
3
  /** Own the public configuration snapshot and invalidate stale updates before DOM mutation. */
4
4
  export function createConfigurationController(initial, element, isDestroyed) {
5
5
  let config = initial;
@@ -1,9 +1,9 @@
1
- import type { Geometry, Position } from '../data/types.js';
2
- import { prepareDistrictStyles } from './district-style.js';
3
- import type { InteractiveSFMapData } from './explorer-data.js';
4
- import type { normalizeFeatures } from './features.js';
5
- import type { LabelItem } from './label-candidates.js';
6
- import type { CameraOptions, DistrictSelection, DistrictYear, NeighborhoodExplorerOptions } from './types.js';
1
+ import type { Geometry, Position } from '../../data/types.js';
2
+ import { prepareDistrictStyles } from '../district-style.js';
3
+ import type { InteractiveSFMapData } from '../explorer-data.js';
4
+ import type { normalizeFeatures } from '../features.js';
5
+ import type { LabelItem } from '../label-candidates.js';
6
+ import type { CameraOptions, DistrictSelection, DistrictYear, NeighborhoodExplorerOptions } from '../types.js';
7
7
  type Host = {
8
8
  root: HTMLElement;
9
9
  svg: SVGSVGElement;
@@ -23,12 +23,12 @@ type Host = {
23
23
  fitGeometry(geometry: Geometry, padding: undefined, options: CameraOptions): void;
24
24
  project(point: Position): [number, number];
25
25
  setLabels(items: LabelItem[]): void;
26
- updateComposition(): void;
26
+ syncLayerComposition(): void;
27
27
  scheduleLabels(): void;
28
28
  };
29
29
  /** Owns district selection, styles, revisions and cancellable motion. */
30
30
  export declare function createDistrictController(host: Host, initialDistrictStyle: NeighborhoodExplorerOptions['districtStyle']): {
31
- districtRow: (id: number) => import("./map-core.js").DistrictRowData | undefined;
31
+ districtRow: (id: number) => import("../rendering/svg-map-renderer.js").DistrictRowData | undefined;
32
32
  districtSelection: (id: number | null) => DistrictSelection | {
33
33
  id: null;
34
34
  year: DistrictYear;
@@ -40,7 +40,7 @@ export declare function createDistrictController(host: Host, initialDistrictStyl
40
40
  fit?: boolean;
41
41
  } & CameraOptions) => boolean;
42
42
  activateDistrict: (id: number) => boolean;
43
- setDistrictStyle: (style: ((district: import('./map-core.js').DistrictRowData) => import("./types.js").DistrictStyle) | undefined) => void;
43
+ setDistrictStyle: (style: ((district: import("../rendering/svg-map-renderer.js").DistrictRowData) => import("../types.js").DistrictStyle) | undefined) => void;
44
44
  setDistrictYear: (next: DistrictYear, options?: CameraOptions, preparedStyles?: ReturnType<typeof prepareDistrictStyles>) => void;
45
45
  transition: {
46
46
  cancel(): void;
@@ -56,9 +56,9 @@ export declare function createDistrictController(host: Host, initialDistrictStyl
56
56
  };
57
57
  readonly revision: number;
58
58
  invalidate(): void;
59
- readonly style: ((district: import('./map-core.js').DistrictRowData) => import("./types.js").DistrictStyle) | undefined;
60
- readonly styles: ReadonlyMap<number, import("./types.js").DistrictStyle>;
61
- commitStyles(style: ((district: import('./map-core.js').DistrictRowData) => import("./types.js").DistrictStyle) | undefined, styles: ReadonlyMap<number, import("./types.js").DistrictStyle>): void;
59
+ readonly style: ((district: import("../rendering/svg-map-renderer.js").DistrictRowData) => import("../types.js").DistrictStyle) | undefined;
60
+ readonly styles: ReadonlyMap<number, import("../types.js").DistrictStyle>;
61
+ commitStyles(style: ((district: import("../rendering/svg-map-renderer.js").DistrictRowData) => import("../types.js").DistrictStyle) | undefined, styles: ReadonlyMap<number, import("../types.js").DistrictStyle>): void;
62
62
  hovered: number | null;
63
63
  };
64
64
  export {};
@@ -1,13 +1,13 @@
1
- import { validateCameraOptions } from './camera.js';
2
- import { renderDistrictAppearance } from './district-layer.js';
3
- import { createDistrictMorph } from './district-morph.js';
4
- import { prepareDistrictStyles } from './district-style.js';
5
- import { createDistrictTransition } from './district-transition.js';
6
- import { svgElement } from './dom.js';
7
- import { getLayerPathsWithData } from './map-core.js';
1
+ import { validateCameraOptions } from '../camera.js';
2
+ import { renderDistrictAppearance } from '../district-layer.js';
3
+ import { createDistrictMorph } from '../district-morph.js';
4
+ import { prepareDistrictStyles } from '../district-style.js';
5
+ import { createDistrictTransition } from '../district-transition.js';
6
+ import { svgElement } from '../dom.js';
7
+ import { getLayerPathsWithData } from '../rendering/svg-map-renderer.js';
8
8
  /** Owns district selection, styles, revisions and cancellable motion. */
9
9
  export function createDistrictController(host, initialDistrictStyle) {
10
- const { root, svg, status, data, districtLayers, reducedMotion, enabled, fitGeometry, updateComposition, scheduleLabels, } = host;
10
+ const { root, svg, status, data, districtLayers, reducedMotion, enabled, fitGeometry, syncLayerComposition, scheduleLabels, } = host;
11
11
  const districtMorph = createDistrictMorph();
12
12
  let selectedDistrict = null;
13
13
  let hoveredDistrict = null;
@@ -160,7 +160,7 @@ export function createDistrictController(host, initialDistrictStyle) {
160
160
  if (selectedDistrict === null)
161
161
  delete root.dataset.selectedDistrict;
162
162
  updateDistrictAppearance();
163
- updateComposition();
163
+ syncLayerComposition();
164
164
  if (morph && lineLayer && previousOutlines)
165
165
  districtMorph.start(lineLayer, previousOutlines, {
166
166
  duration: morph.duration,
@@ -1,7 +1,7 @@
1
- import type { NeighborhoodFeature, NeighborhoodSource } from '../data/types.js';
2
- import type { InteractiveSFMapData } from './explorer-data.js';
3
- import type { createExplorerShell } from './explorer-shell.js';
4
- import type { DistrictYear, ExplorerMode } from './types.js';
1
+ import type { NeighborhoodFeature, NeighborhoodSource } from '../../data/types.js';
2
+ import type { InteractiveSFMapData } from '../explorer-data.js';
3
+ import type { DistrictYear, ExplorerMode } from '../types.js';
4
+ import type { createExplorerShell } from './map-shell.js';
5
5
  interface SidebarState {
6
6
  mode: ExplorerMode;
7
7
  year: DistrictYear;
@@ -13,8 +13,8 @@ export declare function createExplorerSidebar({ detail, results, search, count,
13
13
  id: string;
14
14
  canonicalName: string;
15
15
  }[]): {
16
- updateDetail: ({ mode, year, source, selected }: SidebarState) => void;
17
- updateResults: ({ source, selected }: SidebarState) => void;
16
+ updateSelectionDetail: ({ mode, year, source, selected }: SidebarState) => void;
17
+ updateNeighborhoodResults: ({ source, selected }: SidebarState) => void;
18
18
  destroy: () => void;
19
19
  };
20
20
  export {};
@@ -1,4 +1,4 @@
1
- import { element } from './dom.js';
1
+ import { element } from '../dom.js';
2
2
  /** Render sidebar snapshots and own every download URL until replacement or disposal. */
3
3
  export function createExplorerSidebar({ detail, results, search, count, }, data, chrome, emptyResults, searchNeighborhoods) {
4
4
  const downloads = new Set();
@@ -15,7 +15,7 @@ export function createExplorerSidebar({ detail, results, search, count, }, data,
15
15
  link.download = filename;
16
16
  return link;
17
17
  }
18
- function updateDetail({ mode, year, source, selected }) {
18
+ function updateSelectionDetail({ mode, year, source, selected }) {
19
19
  releaseDownloads();
20
20
  if (chrome === 'map')
21
21
  return;
@@ -53,7 +53,7 @@ export function createExplorerSidebar({ detail, results, search, count, }, data,
53
53
  provenance.append(element('summary', 'About these boundaries'), element('p', collection.definition.description));
54
54
  detail.append(provenance);
55
55
  }
56
- function updateResults({ source, selected }) {
56
+ function updateNeighborhoodResults({ source, selected }) {
57
57
  if (chrome === 'map')
58
58
  return;
59
59
  const activeElement = document.activeElement;
@@ -75,5 +75,5 @@ export function createExplorerSidebar({ detail, results, search, count, }, data,
75
75
  if (!matches.length)
76
76
  results.append(element('p', emptyResults ?? 'No matches. Try another name or definition source.', 'sf-explorer-note'));
77
77
  }
78
- return { updateDetail, updateResults, destroy: releaseDownloads };
78
+ return { updateSelectionDetail, updateNeighborhoodResults, destroy: releaseDownloads };
79
79
  }
@@ -1,11 +1,11 @@
1
- import type { View } from './explorer-layout.js';
2
- import type { normalizeFeatures } from './features.js';
3
- import { type LabelItem } from './label-candidates.js';
4
- import type { createMarkerCatalog } from './marker-catalog.js';
1
+ import type { View } from '../explorer-layout.js';
2
+ import type { normalizeFeatures } from '../features.js';
3
+ import { type LabelItem } from '../label-candidates.js';
4
+ import type { createMarkerCatalog } from '../marker-catalog.js';
5
+ import type { createNeighborhoodLayer } from '../neighborhood-layer.js';
6
+ import type { ExplorerMode, InteractiveLayers, NeighborhoodExplorerOptions } from '../types.js';
5
7
  import type { createMarkerController } from './marker-controller.js';
6
- import type { MarkerItem, MarkerVisual } from './marker-layer.js';
7
- import type { createNeighborhoodLayer } from './neighborhood-layer.js';
8
- import type { ExplorerMode, InteractiveLayers, NeighborhoodExplorerOptions } from './types.js';
8
+ import type { MarkerItem, MarkerVisual } from './marker-renderer.js';
9
9
  interface LabelHost {
10
10
  root: HTMLElement;
11
11
  canvas: HTMLElement;
@@ -1,5 +1,5 @@
1
- import { setAttributeIfChanged } from './dom.js';
2
- import { labelCandidates, styleLabels, } from './label-candidates.js';
1
+ import { setAttributeIfChanged } from '../dom.js';
2
+ import { labelCandidates, styleLabels, } from '../label-candidates.js';
3
3
  import { createLabelRenderer } from './label-renderer.js';
4
4
  /** Own candidate caching, screen-space collision layout and label metrics invalidation. */
5
5
  export function createLabelCoordinator(host) {
@@ -1,5 +1,5 @@
1
- import type { Bounds } from '../data/types.js';
2
- import type { RenderLabel } from './label-candidates.js';
1
+ import type { Bounds } from '../../data/types.js';
2
+ import type { RenderLabel } from '../label-candidates.js';
3
3
  /** Reuse text nodes and screen-space metrics throughout a camera animation. */
4
4
  export declare function createLabelRenderer(layer: SVGGElement): {
5
5
  invalidateMetrics(): void;
@@ -1,5 +1,5 @@
1
- import { setAttributeIfChanged } from './dom.js';
2
- import { layoutLabels } from './explorer-layout.js';
1
+ import { setAttributeIfChanged } from '../dom.js';
2
+ import { layoutLabels } from '../explorer-layout.js';
3
3
  /** Reuse text nodes and screen-space metrics throughout a camera animation. */
4
4
  export function createLabelRenderer(layer) {
5
5
  const entries = new Map();
@@ -1,5 +1,5 @@
1
- import type { createExplorerShell } from './explorer-shell.js';
2
- import type { MapViewport, NeighborhoodExplorerElement, NeighborhoodExplorerOptions } from './types.js';
1
+ import type { MapViewport, NeighborhoodExplorerElement, NeighborhoodExplorerOptions } from '../types.js';
2
+ import type { createExplorerShell } from './map-shell.js';
3
3
  type Controls = NonNullable<NeighborhoodExplorerOptions['controls']>;
4
4
  type Shell = Pick<ReturnType<typeof createExplorerShell>, 'root' | 'toolbar' | 'canvas' | 'hint' | 'status' | 'modeSelect' | 'sourceSelect' | 'search' | 'results' | 'neighborhoodSelect' | 'markerSelect'>;
5
5
  type Commands = Pick<NeighborhoodExplorerElement, 'getViewport' | 'zoomBy' | 'resetView' | 'setLabels' | 'stopAnimation' | 'setMode' | 'setSource' | 'selectNeighborhood' | 'selectMarker'> & {
@@ -10,13 +10,13 @@ type Commands = Pick<NeighborhoodExplorerElement, 'getViewport' | 'zoomBy' | 're
10
10
  getLabels(): boolean;
11
11
  moveView(view: MapViewport): void;
12
12
  setView(view: MapViewport): void;
13
- updateResults(): void;
13
+ updateNeighborhoodResults(): void;
14
14
  search(query: string): string | undefined;
15
15
  };
16
16
  /** Own toolbar and form events, touch navigation and control visibility.
17
17
  * Commands read current map state; all listeners share the map's disposal signal.
18
18
  */
19
- export declare function createExplorerControls({ root, toolbar, canvas, hint, status, modeSelect, sourceSelect, search, results, neighborhoodSelect, markerSelect, }: Shell, { svg, signal, strings, isDestroyed, getLabels, getViewport, moveView, zoomBy, resetView, setLabels, stopAnimation, setView, setMode, setSource, selectNeighborhood, selectMarker, updateResults, search: findNeighborhood, }: Commands): {
19
+ export declare function createMapControls({ root, toolbar, canvas, hint, status, modeSelect, sourceSelect, search, results, neighborhoodSelect, markerSelect, }: Shell, { svg, signal, strings, isDestroyed, getLabels, getViewport, moveView, zoomBy, resetView, setLabels, stopAnimation, setView, setMode, setSource, selectNeighborhood, selectMarker, updateNeighborhoodResults, search: findNeighborhood, }: Commands): {
20
20
  setTouchNavigation: (enabled: boolean) => void;
21
21
  cancelNavigation: () => void;
22
22
  update(controls: Controls): void;
@@ -1,9 +1,9 @@
1
- import { element } from './dom.js';
2
- import { attachNavigation } from './navigation.js';
1
+ import { element } from '../dom.js';
2
+ import { attachNavigation } from '../navigation.js';
3
3
  /** Own toolbar and form events, touch navigation and control visibility.
4
4
  * Commands read current map state; all listeners share the map's disposal signal.
5
5
  */
6
- export function createExplorerControls({ root, toolbar, canvas, hint, status, modeSelect, sourceSelect, search, results, neighborhoodSelect, markerSelect, }, { svg, signal, strings, isDestroyed, getLabels, getViewport, moveView, zoomBy, resetView, setLabels, stopAnimation, setView, setMode, setSource, selectNeighborhood, selectMarker, updateResults, search: findNeighborhood, }) {
6
+ export function createMapControls({ root, toolbar, canvas, hint, status, modeSelect, sourceSelect, search, results, neighborhoodSelect, markerSelect, }, { svg, signal, strings, isDestroyed, getLabels, getViewport, moveView, zoomBy, resetView, setLabels, stopAnimation, setView, setMode, setSource, selectNeighborhood, selectMarker, updateNeighborhoodResults, search: findNeighborhood, }) {
7
7
  function listen(node, event, handler) {
8
8
  node.addEventListener(event, (incoming) => handler(incoming), { signal });
9
9
  }
@@ -63,7 +63,7 @@ export function createExplorerControls({ root, toolbar, canvas, hint, status, mo
63
63
  if (next === 'neighborhoods' || next === 'districts' || next === 'basemap')
64
64
  setMode(next);
65
65
  });
66
- listen(search, 'input', updateResults);
66
+ listen(search, 'input', updateNeighborhoodResults);
67
67
  listen(search, 'keydown', (event) => {
68
68
  if (event.key === 'Enter') {
69
69
  const match = findNeighborhood(search.value);
@@ -1,11 +1,11 @@
1
- import type { NeighborhoodSource } from '../data/types.js';
1
+ import type { NeighborhoodSource } from '../../data/types.js';
2
+ import type { MapConfiguration } from '../controller-types.js';
3
+ import type { InteractiveSFMapData } from '../explorer-data.js';
4
+ import type { DistrictYear, ExplorerMode, NeighborhoodExplorerElement, NeighborhoodExplorerOptions } from '../types.js';
2
5
  import type { PreparedPresentation } from './configuration-controller.js';
3
- import type { MapConfiguration } from './controller-types.js';
4
- import type { InteractiveSFMapData } from './explorer-data.js';
5
- import type { createMapShell } from './explorer-shell.js';
6
6
  import type { createExplorerSidebar } from './explorer-sidebar.js';
7
- import type { DistrictYear, ExplorerMode, NeighborhoodExplorerElement, NeighborhoodExplorerOptions } from './types.js';
8
- export type { ExplorerMode, NeighborhoodExplorerElement, NeighborhoodExplorerOptions, } from './types.js';
7
+ import type { createMapShell } from './map-shell.js';
8
+ export type { ExplorerMode, NeighborhoodExplorerElement, NeighborhoodExplorerOptions, } from '../types.js';
9
9
  /** Private bridge between the renderer and its public lifecycle controller. */
10
10
  type MapEngineElement = NeighborhoodExplorerElement & {
11
11
  preparePresentation(patch: MapConfiguration): PreparedPresentation | undefined;
@@ -17,7 +17,7 @@ type MapEngineElement = NeighborhoodExplorerElement & {
17
17
  };
18
18
  };
19
19
  /** Create an offline, browser-only neighborhood explorer. Call destroy() before disposal. */
20
- export declare function createNeighborhoodExplorerCore(options: NeighborhoodExplorerOptions | undefined, data: InteractiveSFMapData, ui: {
20
+ export declare function createMapEngine(options: NeighborhoodExplorerOptions | undefined, data: InteractiveSFMapData, ui: {
21
21
  createShell: typeof createMapShell;
22
22
  createSidebar?: typeof createExplorerSidebar;
23
23
  }): MapEngineElement;