@kahwee/sf-map-svg 5.1.3 → 5.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.
- package/CHANGELOG.md +4 -0
- package/dist/src/cluster-renderer.d.ts +1 -1
- package/dist/src/district-layer.d.ts +1 -1
- package/dist/src/district-layer.js +1 -1
- package/dist/src/district-style.d.ts +1 -1
- package/dist/src/explorer-data.d.ts +1 -1
- package/dist/src/guide-map.js +3 -3
- package/dist/src/{configuration-controller.d.ts → interactive/configuration-controller.d.ts} +7 -7
- package/dist/src/{configuration-controller.js → interactive/configuration-controller.js} +2 -2
- package/dist/src/{district-controller.d.ts → interactive/district-controller.d.ts} +12 -12
- package/dist/src/{district-controller.js → interactive/district-controller.js} +9 -9
- package/dist/src/{explorer-sidebar.d.ts → interactive/explorer-sidebar.d.ts} +6 -6
- package/dist/src/{explorer-sidebar.js → interactive/explorer-sidebar.js} +4 -4
- package/dist/src/{label-coordinator.d.ts → interactive/label-coordinator.d.ts} +7 -7
- package/dist/src/{label-coordinator.js → interactive/label-coordinator.js} +2 -2
- package/dist/src/{label-renderer.d.ts → interactive/label-renderer.d.ts} +2 -2
- package/dist/src/{label-renderer.js → interactive/label-renderer.js} +2 -2
- package/dist/src/{explorer-controls.d.ts → interactive/map-controls.d.ts} +4 -4
- package/dist/src/{explorer-controls.js → interactive/map-controls.js} +4 -4
- package/dist/src/{explorer-core.d.ts → interactive/map-engine.d.ts} +7 -7
- package/dist/src/{explorer-core.js → interactive/map-engine.js} +40 -40
- package/dist/src/{explorer-shell.d.ts → interactive/map-shell.d.ts} +2 -2
- package/dist/src/{explorer-shell.js → interactive/map-shell.js} +2 -2
- package/dist/src/{marker-controller.d.ts → interactive/marker-controller.d.ts} +8 -8
- package/dist/src/{marker-controller.js → interactive/marker-controller.js} +3 -3
- package/dist/src/{marker-layer.d.ts → interactive/marker-renderer.d.ts} +3 -3
- package/dist/src/{marker-layer.js → interactive/marker-renderer.js} +2 -2
- package/dist/src/map.js +4 -4
- package/dist/src/marker-catalog.d.ts +2 -2
- package/dist/src/{layers.d.ts → rendering/svg-layers.d.ts} +5 -5
- package/dist/src/{layers.js → rendering/svg-layers.js} +1 -1
- package/dist/src/{map-core.d.ts → rendering/svg-map-renderer.d.ts} +4 -4
- package/dist/src/{map-core.js → rendering/svg-map-renderer.js} +6 -6
- package/dist/src/static-labels.d.ts +1 -1
- package/dist/src/static-labels.js +1 -1
- package/dist/src/static.d.ts +2 -2
- package/dist/src/static.js +1 -1
- package/dist/src/transit.js +1 -1
- package/dist/src/types.d.ts +2 -2
- package/llms-full.txt +25 -21
- package/llms.txt +1 -1
- package/package.json +1 -1
- /package/dist/src/{svg.d.ts → rendering/svg-primitives.d.ts} +0 -0
- /package/dist/src/{svg.js → rendering/svg-primitives.js} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,10 @@ User-visible changes are recorded here. Unreleased entries describe changes on `
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 5.1.4 — 2026-11-05
|
|
8
|
+
|
|
9
|
+
- 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.
|
|
10
|
+
|
|
7
11
|
## 5.1.3 — 2026-11-05
|
|
8
12
|
|
|
9
13
|
- 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.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { normalizeFeatures } from './features.js';
|
|
2
|
-
import type { MarkerItem, MarkerVisual } from './marker-
|
|
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,4 +1,4 @@
|
|
|
1
|
-
import type { DistrictRowData } from './map-
|
|
1
|
+
import type { DistrictRowData } from './rendering/svg-map-renderer.js';
|
|
2
2
|
import type { DistrictStyle, DistrictYear, SFMapOptions } 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, }: {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { districtColors } from './map-
|
|
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-
|
|
1
|
+
import type { DistrictRowData } from './rendering/svg-map-renderer.js';
|
|
2
2
|
import type { DistrictStyle, SFMapOptions } from './types.js';
|
|
3
3
|
/** Evaluate user code once and copy its result before changing the map. */
|
|
4
4
|
export declare function prepareDistrictStyles(rows: readonly DistrictRowData[], callback: SFMapOptions['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-
|
|
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 {
|
package/dist/src/guide-map.js
CHANGED
|
@@ -1,7 +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';
|
|
3
|
+
import { createMapEngine } from './interactive/map-engine.js';
|
|
4
|
+
import { createMapShell } from './interactive/map-shell.js';
|
|
5
5
|
import { createMap } from './map.js';
|
|
6
6
|
import { guideOptions } from './presets.js';
|
|
7
7
|
import { validateExplorerOptions } from './validation.js';
|
|
@@ -62,7 +62,7 @@ function mountFrame(frame, map) {
|
|
|
62
62
|
/** Element-based compatibility API. Prefer createGuideController for new integrations. */
|
|
63
63
|
export function createGuideMap(options = {}) {
|
|
64
64
|
validateExplorerOptions(options);
|
|
65
|
-
return
|
|
65
|
+
return createMapEngine({
|
|
66
66
|
mode: 'neighborhoods',
|
|
67
67
|
...options,
|
|
68
68
|
layers: {
|
package/dist/src/{configuration-controller.d.ts → interactive/configuration-controller.d.ts}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { prepareConfiguration } from '
|
|
2
|
-
import type { MapConfiguration } from '
|
|
3
|
-
import type { NeighborhoodExplorerElement } from '
|
|
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("
|
|
16
|
-
layers: import("
|
|
17
|
-
controls: import("
|
|
18
|
-
appearance: import("
|
|
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 '
|
|
2
|
-
import { controlKeys, layerKeys } from '
|
|
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 '
|
|
2
|
-
import { prepareDistrictStyles } from '
|
|
3
|
-
import type { InteractiveSFMapData } from '
|
|
4
|
-
import type { normalizeFeatures } from '
|
|
5
|
-
import type { LabelItem } from '
|
|
6
|
-
import type { CameraOptions, DistrictSelection, DistrictYear, NeighborhoodExplorerOptions } from '
|
|
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
|
-
|
|
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("
|
|
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(
|
|
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(
|
|
60
|
-
readonly styles: ReadonlyMap<number, import("
|
|
61
|
-
commitStyles(style: ((district: import(
|
|
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 '
|
|
2
|
-
import { renderDistrictAppearance } from '
|
|
3
|
-
import { createDistrictMorph } from '
|
|
4
|
-
import { prepareDistrictStyles } from '
|
|
5
|
-
import { createDistrictTransition } from '
|
|
6
|
-
import { svgElement } from '
|
|
7
|
-
import { getLayerPathsWithData } from '
|
|
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,
|
|
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
|
-
|
|
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 '
|
|
2
|
-
import type { InteractiveSFMapData } from '
|
|
3
|
-
import type {
|
|
4
|
-
import type {
|
|
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
|
-
|
|
17
|
-
|
|
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 '
|
|
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
|
|
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
|
|
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 {
|
|
78
|
+
return { updateSelectionDetail, updateNeighborhoodResults, destroy: releaseDownloads };
|
|
79
79
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type { View } from '
|
|
2
|
-
import type { normalizeFeatures } from '
|
|
3
|
-
import { type LabelItem } from '
|
|
4
|
-
import type { createMarkerCatalog } from '
|
|
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-
|
|
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 '
|
|
2
|
-
import { labelCandidates, styleLabels, } from '
|
|
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 '
|
|
2
|
-
import type { RenderLabel } from '
|
|
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 '
|
|
2
|
-
import { layoutLabels } from '
|
|
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 {
|
|
2
|
-
import type {
|
|
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
|
-
|
|
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
|
|
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 '
|
|
2
|
-
import { attachNavigation } from '
|
|
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
|
|
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',
|
|
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 '
|
|
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 {
|
|
8
|
-
export type { ExplorerMode, NeighborhoodExplorerElement, NeighborhoodExplorerOptions, } from '
|
|
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
|
|
20
|
+
export declare function createMapEngine(options: NeighborhoodExplorerOptions | undefined, data: InteractiveSFMapData, ui: {
|
|
21
21
|
createShell: typeof createMapShell;
|
|
22
22
|
createSidebar?: typeof createExplorerSidebar;
|
|
23
23
|
}): MapEngineElement;
|
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import { copyAppearance } from '
|
|
2
|
-
import { createCamera, validateCameraOptions } from '
|
|
3
|
-
import { createClusterLayer } from '
|
|
4
|
-
import { clusterPoints } from '
|
|
1
|
+
import { copyAppearance } from '../appearance.js';
|
|
2
|
+
import { createCamera, validateCameraOptions } from '../camera.js';
|
|
3
|
+
import { createClusterLayer } from '../cluster-layer.js';
|
|
4
|
+
import { clusterPoints } from '../clusters.js';
|
|
5
|
+
import { prepareDistrictStyles } from '../district-style.js';
|
|
6
|
+
import { element, svgElement } from '../dom.js';
|
|
7
|
+
import { fitBounds, projectedBounds } from '../explorer-layout.js';
|
|
8
|
+
import { controlKeys, layerKeys, normalizeFeatures, validateSwitchPatch } from '../features.js';
|
|
9
|
+
import { createFrameScheduler } from '../frame-scheduler.js';
|
|
10
|
+
import { createLayerTransitions } from '../layer-transition.js';
|
|
11
|
+
import { createMarkerCatalog } from '../marker-catalog.js';
|
|
12
|
+
import { createMarkerChooser } from '../marker-chooser.js';
|
|
13
|
+
import { createMarkerNavigation } from '../marker-navigation.js';
|
|
14
|
+
import { createNeighborhoodLayer, createNeighborhoodLookup, formatSourceLabel, } from '../neighborhood-layer.js';
|
|
15
|
+
import { createOverlayLayer } from '../overlay-layer.js';
|
|
16
|
+
import { createSFMapWithData, districtColors, getLayerPathsWithData, resolveMapColors, } from '../rendering/svg-map-renderer.js';
|
|
17
|
+
import { validateExplorerOptions } from '../validation.js';
|
|
18
|
+
import { fitViewport, validateViewport } from '../viewport.js';
|
|
5
19
|
import { createDistrictController } from './district-controller.js';
|
|
6
|
-
import { prepareDistrictStyles } from './district-style.js';
|
|
7
|
-
import { element, svgElement } from './dom.js';
|
|
8
|
-
import { createExplorerControls } from './explorer-controls.js';
|
|
9
|
-
import { fitBounds, projectedBounds } from './explorer-layout.js';
|
|
10
|
-
import { controlKeys, layerKeys, normalizeFeatures, validateSwitchPatch } from './features.js';
|
|
11
|
-
import { createFrameScheduler } from './frame-scheduler.js';
|
|
12
20
|
import { createLabelCoordinator } from './label-coordinator.js';
|
|
13
|
-
import {
|
|
14
|
-
import { createSFMapWithData, districtColors, getLayerPathsWithData, resolveMapColors, } from './map-core.js';
|
|
15
|
-
import { createMarkerCatalog } from './marker-catalog.js';
|
|
16
|
-
import { createMarkerChooser } from './marker-chooser.js';
|
|
21
|
+
import { createMapControls } from './map-controls.js';
|
|
17
22
|
import { createMarkerController } from './marker-controller.js';
|
|
18
|
-
import {
|
|
19
|
-
import { createMarkerNavigation } from './marker-navigation.js';
|
|
20
|
-
import { createNeighborhoodLayer, createNeighborhoodLookup, formatSourceLabel, } from './neighborhood-layer.js';
|
|
21
|
-
import { createOverlayLayer } from './overlay-layer.js';
|
|
22
|
-
import { validateExplorerOptions } from './validation.js';
|
|
23
|
-
import { fitViewport, validateViewport } from './viewport.js';
|
|
23
|
+
import { createMarkerRenderer } from './marker-renderer.js';
|
|
24
24
|
/** Create an offline, browser-only neighborhood explorer. Call destroy() before disposal. */
|
|
25
|
-
export function
|
|
25
|
+
export function createMapEngine(options = {}, data, ui) {
|
|
26
26
|
validateExplorerOptions(options);
|
|
27
27
|
let { source = 'realtor', mode = 'neighborhoods', labels = true, neighborhood, year = 2022, theme = 'transit', colors = {}, labelStyle = {}, areaStyle = {}, districtStyle: initialDistrictStyle, motion = false, markerEntrance = false, selectedMarkerRing, clustering = false, legend: legendOptions = {}, attribution: attributionMode = 'full', northArrow = false, scaleBar = false, layerTransitions = false, districtMorph: districtMorphOption = false, interface: chrome = 'explorer', layers = {}, selectableNeighborhoods = true, labelSize = {}, fitPadding = 24, markers: initialMarkers = [], markerRadius = 6, markerHitSize = 44, markerColor = colors.marker ?? '#245b61', selectedMarkerColor = colors.selected ?? '#f04f32', onMarkerActivate, overlays: initialOverlays = [], onOverlayActivate, style: styleOptions = {}, strings = {}, controls = {}, } = options;
|
|
28
28
|
if (!['neighborhoods', 'districts', 'basemap'].includes(mode))
|
|
@@ -188,7 +188,7 @@ export function createNeighborhoodExplorerCore(options = {}, data, ui) {
|
|
|
188
188
|
reducedMotion,
|
|
189
189
|
enabled,
|
|
190
190
|
fitGeometry,
|
|
191
|
-
|
|
191
|
+
syncLayerComposition,
|
|
192
192
|
scheduleLabels,
|
|
193
193
|
get year() {
|
|
194
194
|
return year;
|
|
@@ -229,7 +229,7 @@ export function createNeighborhoodExplorerCore(options = {}, data, ui) {
|
|
|
229
229
|
cancel: (id) => cancelAnimationFrame(id),
|
|
230
230
|
render: drawLabels,
|
|
231
231
|
});
|
|
232
|
-
const markerRenderer =
|
|
232
|
+
const markerRenderer = createMarkerRenderer();
|
|
233
233
|
const cancelEntrances = markerRenderer.cancelEntrances;
|
|
234
234
|
const camera = createCamera({
|
|
235
235
|
read: getViewport,
|
|
@@ -271,13 +271,13 @@ export function createNeighborhoodExplorerCore(options = {}, data, ui) {
|
|
|
271
271
|
refresh: {
|
|
272
272
|
selection() {
|
|
273
273
|
scheduleLabels();
|
|
274
|
-
|
|
275
|
-
|
|
274
|
+
updateNeighborhoodResults();
|
|
275
|
+
updateSelectionDetail();
|
|
276
276
|
},
|
|
277
277
|
source() {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
278
|
+
updateNeighborhoodResults();
|
|
279
|
+
updateSelectionDetail();
|
|
280
|
+
syncLayerComposition();
|
|
281
281
|
},
|
|
282
282
|
},
|
|
283
283
|
moveView,
|
|
@@ -466,7 +466,7 @@ export function createNeighborhoodExplorerCore(options = {}, data, ui) {
|
|
|
466
466
|
isDestroyed: () => destroyed,
|
|
467
467
|
});
|
|
468
468
|
const setOverlays = overlays.set;
|
|
469
|
-
const mapControls =
|
|
469
|
+
const mapControls = createMapControls(shell, {
|
|
470
470
|
svg,
|
|
471
471
|
signal: controller.signal,
|
|
472
472
|
strings,
|
|
@@ -483,7 +483,7 @@ export function createNeighborhoodExplorerCore(options = {}, data, ui) {
|
|
|
483
483
|
setSource,
|
|
484
484
|
selectNeighborhood,
|
|
485
485
|
selectMarker,
|
|
486
|
-
|
|
486
|
+
updateNeighborhoodResults,
|
|
487
487
|
search(query) {
|
|
488
488
|
const exact = getNeighborhood(query, neighborhoods.source);
|
|
489
489
|
const matches = searchNeighborhoods(query, neighborhoods.source);
|
|
@@ -604,16 +604,16 @@ export function createNeighborhoodExplorerCore(options = {}, data, ui) {
|
|
|
604
604
|
labelCoordinator.draw();
|
|
605
605
|
}
|
|
606
606
|
const sidebar = ui.createSidebar?.(shell, data, chrome, strings.emptyResults, searchNeighborhoods);
|
|
607
|
-
function
|
|
608
|
-
sidebar?.
|
|
607
|
+
function updateSelectionDetail() {
|
|
608
|
+
sidebar?.updateSelectionDetail({
|
|
609
609
|
mode,
|
|
610
610
|
year,
|
|
611
611
|
source: neighborhoods.source,
|
|
612
612
|
selected: neighborhoods.selected,
|
|
613
613
|
});
|
|
614
614
|
}
|
|
615
|
-
function
|
|
616
|
-
sidebar?.
|
|
615
|
+
function updateNeighborhoodResults() {
|
|
616
|
+
sidebar?.updateNeighborhoodResults({
|
|
617
617
|
mode,
|
|
618
618
|
year,
|
|
619
619
|
source: neighborhoods.source,
|
|
@@ -629,9 +629,9 @@ export function createNeighborhoodExplorerCore(options = {}, data, ui) {
|
|
|
629
629
|
validateSwitchPatch(patch, layerKeys);
|
|
630
630
|
const next = { ...layers, ...patch };
|
|
631
631
|
layers = next;
|
|
632
|
-
|
|
632
|
+
syncLayerComposition();
|
|
633
633
|
}
|
|
634
|
-
function
|
|
634
|
+
function syncLayerComposition() {
|
|
635
635
|
for (const [name, key] of [
|
|
636
636
|
['landmarks', 'landmarks'],
|
|
637
637
|
['highways', 'highways'],
|
|
@@ -715,7 +715,7 @@ export function createNeighborhoodExplorerCore(options = {}, data, ui) {
|
|
|
715
715
|
controls = next;
|
|
716
716
|
mapControls.update(controls);
|
|
717
717
|
markerLabel.hidden = !markerCatalog.items.length || controls.markerPicker === false;
|
|
718
|
-
|
|
718
|
+
syncLayerComposition();
|
|
719
719
|
}
|
|
720
720
|
function setMode(next, reset = true) {
|
|
721
721
|
if (destroyed)
|
|
@@ -734,8 +734,8 @@ export function createNeighborhoodExplorerCore(options = {}, data, ui) {
|
|
|
734
734
|
const districts = mode !== 'neighborhoods';
|
|
735
735
|
for (const node of [sourceLabel, searchLabel, count, results])
|
|
736
736
|
node.hidden = districts;
|
|
737
|
-
|
|
738
|
-
|
|
737
|
+
syncLayerComposition();
|
|
738
|
+
updateSelectionDetail();
|
|
739
739
|
status.textContent =
|
|
740
740
|
mode === 'basemap'
|
|
741
741
|
? 'San Francisco basemap.'
|
|
@@ -755,7 +755,7 @@ export function createNeighborhoodExplorerCore(options = {}, data, ui) {
|
|
|
755
755
|
labels = visible;
|
|
756
756
|
mapControls.updateLabels(labels);
|
|
757
757
|
root.dataset.labels = String(labels);
|
|
758
|
-
|
|
758
|
+
syncLayerComposition();
|
|
759
759
|
if (!labels) {
|
|
760
760
|
labelLayer.replaceChildren();
|
|
761
761
|
root.dataset.visibleLabels = '0';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { NeighborhoodSource } from '
|
|
2
|
-
import type { NeighborhoodExplorerOptions } from '
|
|
1
|
+
import type { NeighborhoodSource } from '../../data/types.js';
|
|
2
|
+
import type { NeighborhoodExplorerOptions } from '../types.js';
|
|
3
3
|
/** Construct shared map chrome; optional explorer content has its own factory below. */
|
|
4
4
|
export declare function createMapShell({ chrome, styleOptions, strings, legendOptions, colors, attributionMode, }: {
|
|
5
5
|
source: NeighborhoodSource;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { element } from '
|
|
2
|
-
import { sourceNames } from '
|
|
1
|
+
import { element } from '../dom.js';
|
|
2
|
+
import { sourceNames } from '../neighborhood-layer.js';
|
|
3
3
|
let explorerCount = 0;
|
|
4
4
|
/** Construct shared map chrome; optional explorer content has its own factory below. */
|
|
5
5
|
export function createMapShell({ chrome, styleOptions, strings, legendOptions, colors, attributionMode, }) {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type { Position } from '
|
|
2
|
-
import { type View } from '
|
|
3
|
-
import type { normalizeFeatures } from '
|
|
4
|
-
import type { createMarkerCatalog } from '
|
|
5
|
-
import type { createMarkerChooser } from '
|
|
6
|
-
import type {
|
|
7
|
-
import type {
|
|
8
|
-
import type {
|
|
1
|
+
import type { Position } from '../../data/types.js';
|
|
2
|
+
import { type View } from '../explorer-layout.js';
|
|
3
|
+
import type { normalizeFeatures } from '../features.js';
|
|
4
|
+
import type { createMarkerCatalog } from '../marker-catalog.js';
|
|
5
|
+
import type { createMarkerChooser } from '../marker-chooser.js';
|
|
6
|
+
import type { createMarkerNavigation } from '../marker-navigation.js';
|
|
7
|
+
import type { CameraOptions, MapMarker, NeighborhoodExplorerOptions } from '../types.js';
|
|
8
|
+
import type { MarkerItem, MarkerVisual } from './marker-renderer.js';
|
|
9
9
|
interface MarkerHost {
|
|
10
10
|
root: HTMLElement;
|
|
11
11
|
canvas: HTMLElement;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { validateCameraOptions } from '
|
|
2
|
-
import { fitBounds } from '
|
|
3
|
-
import { fitViewport } from '
|
|
1
|
+
import { validateCameraOptions } from '../camera.js';
|
|
2
|
+
import { fitBounds } from '../explorer-layout.js';
|
|
3
|
+
import { fitViewport } from '../viewport.js';
|
|
4
4
|
/** Own selection revisions, activation and focus recovery independently of marker rendering. */
|
|
5
5
|
export function createMarkerController(host) {
|
|
6
6
|
const { root, canvas, svg, markerLayer, markerSelect, markerLabel, markerCatalog, markerNavigation, strings, fitPadding, onMarkerActivate, invalidateClusters, drawClusters, materializeMarker, scheduleLabels, syncFeatureControls, moveView, } = host;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { normalizeFeatures } from '
|
|
2
|
-
import type { MapMarker } from '
|
|
1
|
+
import type { normalizeFeatures } from '../features.js';
|
|
2
|
+
import type { MapMarker } from '../types.js';
|
|
3
3
|
export interface MarkerVisual {
|
|
4
4
|
node: SVGGElement;
|
|
5
5
|
dot: SVGCircleElement;
|
|
@@ -15,7 +15,7 @@ export interface MarkerItem {
|
|
|
15
15
|
visual: MarkerVisual | undefined;
|
|
16
16
|
}
|
|
17
17
|
/** Own marker visuals and entrances; selection/events remain with the controller. */
|
|
18
|
-
export declare function
|
|
18
|
+
export declare function createMarkerRenderer(): {
|
|
19
19
|
cancelEntrances: () => void;
|
|
20
20
|
remove(item: MarkerItem): void;
|
|
21
21
|
update(item: MarkerItem, marker: MapMarker, point: [number, number], index: number): void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { svgElement } from '
|
|
1
|
+
import { svgElement } from '../dom.js';
|
|
2
2
|
/** Own marker visuals and entrances; selection/events remain with the controller. */
|
|
3
|
-
export function
|
|
3
|
+
export function createMarkerRenderer() {
|
|
4
4
|
const records = new Set();
|
|
5
5
|
const animations = new Map();
|
|
6
6
|
function cancelEntrances() {
|
package/dist/src/map.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { copyAppearance } from './appearance.js';
|
|
2
2
|
import { validateCameraOptions } from './camera.js';
|
|
3
3
|
import { configurationPatch, expandMapOptions, prepareConfiguration } from './configuration.js';
|
|
4
|
-
import { createConfigurationController } from './configuration-controller.js';
|
|
5
|
-
import { createNeighborhoodExplorerCore } from './explorer-core.js';
|
|
6
|
-
import { createMapShell } from './explorer-shell.js';
|
|
7
4
|
import { layerKeys } from './features.js';
|
|
5
|
+
import { createConfigurationController } from './interactive/configuration-controller.js';
|
|
6
|
+
import { createMapEngine } from './interactive/map-engine.js';
|
|
7
|
+
import { createMapShell } from './interactive/map-shell.js';
|
|
8
8
|
import { assertOptions } from './validation.js';
|
|
9
9
|
/** No geography is imported. Supply a preset or your own immutable data. */
|
|
10
10
|
export function createMap(data, options = {}) {
|
|
@@ -15,7 +15,7 @@ export function createMap(data, options = {}) {
|
|
|
15
15
|
controls: options.controls,
|
|
16
16
|
appearance: options.appearance,
|
|
17
17
|
});
|
|
18
|
-
const element =
|
|
18
|
+
const element = createMapEngine({
|
|
19
19
|
mode: 'basemap',
|
|
20
20
|
...expanded,
|
|
21
21
|
interface: 'map',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { createMarkerRenderer, MarkerItem } from './interactive/marker-renderer.js';
|
|
2
2
|
import type { MapMarker } from './types.js';
|
|
3
3
|
/** Own ordered records and picker reconciliation; prepare before committing DOM changes. */
|
|
4
|
-
export declare function createMarkerCatalog(renderer: ReturnType<typeof
|
|
4
|
+
export declare function createMarkerCatalog(renderer: ReturnType<typeof createMarkerRenderer>, picker: HTMLSelectElement, project: (point: import('../data/types.js').Position) => [number, number]): {
|
|
5
5
|
readonly items: MarkerItem[];
|
|
6
6
|
prepare(markers: readonly MapMarker[], selected: string | null): {
|
|
7
7
|
selection: string | null;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { Geometry } from '
|
|
2
|
-
import type { DistrictRow } from '
|
|
3
|
-
import type { Project } from '
|
|
4
|
-
import type { BartStation, KeyRoad, Landmark } from '
|
|
5
|
-
import type { MapMarker, SFMapOptions } from '
|
|
1
|
+
import type { Geometry } from '../../data/types.js';
|
|
2
|
+
import type { DistrictRow } from '../data.js';
|
|
3
|
+
import type { Project } from '../geometry.js';
|
|
4
|
+
import type { BartStation, KeyRoad, Landmark } from '../overlays.js';
|
|
5
|
+
import type { MapMarker, SFMapOptions } from '../types.js';
|
|
6
6
|
interface LayerContext {
|
|
7
7
|
labels: boolean;
|
|
8
8
|
/** Animated static maps index items for staggering and measure lines for drawing. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { escapeXml, number, overlayLabel, stroke } from './svg.js';
|
|
1
|
+
import { escapeXml, number, overlayLabel, stroke } from './svg-primitives.js';
|
|
2
2
|
/** Custom JavaScript/JSON rows must not introduce markup or CSS through numeric IDs. */
|
|
3
3
|
function districtId(id) {
|
|
4
4
|
if (!Number.isFinite(id))
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { Geometry, Position } from '
|
|
2
|
-
import type { DistrictYear, SFMapOptions } from '
|
|
3
|
-
export type { DistrictYear, MapMarker, MapOverlay, SFMapOptions } from '
|
|
4
|
-
import { type Project } from '
|
|
1
|
+
import type { Geometry, Position } from '../../data/types.js';
|
|
2
|
+
import type { DistrictYear, SFMapOptions } from '../types.js';
|
|
3
|
+
export type { DistrictYear, MapMarker, MapOverlay, SFMapOptions } from '../types.js';
|
|
4
|
+
import { type Project } from '../geometry.js';
|
|
5
5
|
export interface SFMapData {
|
|
6
6
|
coast: Geometry;
|
|
7
7
|
districts?: Readonly<Partial<Record<DistrictYear, readonly DistrictRowData[]>>>;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { validateMarkers, validateOverlays } from '
|
|
2
|
-
import { prepareDistrictStyles } from '
|
|
3
|
-
import { geometryPath, positions, rawProject } from '
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import { escapeXml, stroke } from './svg.js';
|
|
1
|
+
import { validateMarkers, validateOverlays } from '../validation.js';
|
|
2
|
+
import { prepareDistrictStyles } from '../district-style.js';
|
|
3
|
+
import { geometryPath, positions, rawProject } from '../geometry.js';
|
|
4
|
+
import { normalizeStaticAnimation, staticAnimationStyle } from '../static-animation.js';
|
|
5
|
+
import * as layers from './svg-layers.js';
|
|
6
|
+
import { escapeXml, stroke } from './svg-primitives.js';
|
|
7
7
|
export const districtYears = Object.freeze([2002, 2012, 2022]);
|
|
8
8
|
export const districtColors = Object.freeze([
|
|
9
9
|
'#c8dce5',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Project } from './geometry.js';
|
|
2
|
-
import type { SFMapData } from './map-
|
|
2
|
+
import type { SFMapData } from './rendering/svg-map-renderer.js';
|
|
3
3
|
import type { SFMapOptions } from './types.js';
|
|
4
4
|
/** Conservative portable metrics; the browser refines these with its actual font. */
|
|
5
5
|
export declare function estimateLabelWidth(text: string, fontSize: number): number;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { interiorAnchor, layoutLabels, projectedBounds } from './explorer-layout.js';
|
|
2
2
|
import { labelCandidates, styleLabels } from './label-candidates.js';
|
|
3
3
|
import { spreadLabels } from './label-spread.js';
|
|
4
|
-
import { escapeXml, number } from './svg.js';
|
|
4
|
+
import { escapeXml, number } from './rendering/svg-primitives.js';
|
|
5
5
|
/** Conservative portable metrics; the browser refines these with its actual font. */
|
|
6
6
|
export function estimateLabelWidth(text, fontSize) {
|
|
7
7
|
return Array.from(text).reduce((width, char) => width +
|
package/dist/src/static.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { InteractiveSFMapData } from './explorer-data.js';
|
|
2
|
-
import { type SFMapData } from './map-
|
|
2
|
+
import { type SFMapData } from './rendering/svg-map-renderer.js';
|
|
3
3
|
import type { SFMapOptions } from './types.js';
|
|
4
4
|
/** Either compact static geography or the same source-aware geography used by createMap. */
|
|
5
5
|
export type StaticMapInput = SFMapData | InteractiveSFMapData;
|
|
6
|
-
export type { DistrictRowData, SFMapData as StaticMapData } from './map-
|
|
6
|
+
export type { DistrictRowData, SFMapData as StaticMapData } from './rendering/svg-map-renderer.js';
|
|
7
7
|
export type { DistrictStyle, DistrictYear, SFMapOptions as StaticMapOptions, StaticMapAppearance, StaticMapLayers, } from './types.js';
|
|
8
8
|
/** Server-safe SVG rendering with explicit data; returns SVG plus projection helpers. */
|
|
9
9
|
export declare function renderMap(data: StaticMapInput, options?: SFMapOptions): {
|
package/dist/src/static.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { prepareAppearance } from './appearance.js';
|
|
2
2
|
import { validateSwitchPatch } from './features.js';
|
|
3
|
-
import { createSFMapWithData, getLayerPathsWithData } from './map-
|
|
3
|
+
import { createSFMapWithData, getLayerPathsWithData, } from './rendering/svg-map-renderer.js';
|
|
4
4
|
import { normalizeStaticAnimation } from './static-animation.js';
|
|
5
5
|
import { staticLabels } from './static-labels.js';
|
|
6
6
|
import { assertOptions, validateMarkers, validateOverlays } from './validation.js';
|
package/dist/src/transit.js
CHANGED
|
@@ -2,7 +2,7 @@ import stations from '../data/bart-stations.json' with { type: 'json' };
|
|
|
2
2
|
import coast from '../data/coast.json' with { type: 'json' };
|
|
3
3
|
import districts from '../data/districts-2022.json' with { type: 'json' };
|
|
4
4
|
import parks from '../data/landmarks.json' with { type: 'json' };
|
|
5
|
-
import { createSFMapWithData } from './map-
|
|
5
|
+
import { createSFMapWithData } from './rendering/svg-map-renderer.js';
|
|
6
6
|
const districtData = districts;
|
|
7
7
|
const parkData = parks;
|
|
8
8
|
const stationData = stations;
|
package/dist/src/types.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export type DistrictYear = 2002 | 2012 | 2022;
|
|
|
11
11
|
export interface DistrictSelection {
|
|
12
12
|
id: number;
|
|
13
13
|
year: DistrictYear;
|
|
14
|
-
district: import('./map-
|
|
14
|
+
district: import('./rendering/svg-map-renderer.js').DistrictRowData;
|
|
15
15
|
}
|
|
16
16
|
export interface DistrictStyle {
|
|
17
17
|
fill?: string;
|
|
@@ -56,7 +56,7 @@ export interface SFMapOptions {
|
|
|
56
56
|
neighborhoodLabels?: boolean;
|
|
57
57
|
districtFills?: boolean;
|
|
58
58
|
/** Evaluated once per district at construction/style/year updates; call setDistrictStyle again when external data changes. */
|
|
59
|
-
districtStyle?: (district: import('./map-
|
|
59
|
+
districtStyle?: (district: import('./rendering/svg-map-renderer.js').DistrictRowData) => DistrictStyle;
|
|
60
60
|
districtLabels?: boolean;
|
|
61
61
|
/** Hide all visible text labels while retaining geographic symbols and accessible titles. */
|
|
62
62
|
labels?: boolean;
|
package/llms-full.txt
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
> Offline San Francisco SVG maps with explicit geographic imports, no runtime dependencies, static rendering, and a browser controller.
|
|
4
4
|
|
|
5
|
-
@kahwee/sf-map-svg 5.1.
|
|
5
|
+
@kahwee/sf-map-svg 5.1.4 · local working tree. Node >=24.0.0 for server rendering; browsers need a DOM and a JSON-capable bundler.
|
|
6
6
|
|
|
7
7
|
These are working-tree docs. Unreleased changelog entries may describe APIs absent from npm; verify the installed version before copying them.
|
|
8
8
|
|
|
9
9
|
Use renderMap(data, options) for SVG strings; createMap(data, options) returns a controller. Mount map.element and destroy it on unmount. Choose mode explicitly: createMap defaults to basemap. Geography is separate; staticMapData and fullMapData have different shapes.
|
|
10
10
|
|
|
11
|
-
<!-- Generated from README.md; @kahwee/sf-map-svg 5.1.
|
|
11
|
+
<!-- Generated from README.md; @kahwee/sf-map-svg 5.1.4 · local working tree. -->
|
|
12
12
|
|
|
13
13
|
# SF Map SVG
|
|
14
14
|
|
|
@@ -267,7 +267,7 @@ for raw samples, output-equivalence checks, and reproducible commands.
|
|
|
267
267
|
|
|
268
268
|
---
|
|
269
269
|
|
|
270
|
-
<!-- Generated from docs/developer-guide.md; @kahwee/sf-map-svg 5.1.
|
|
270
|
+
<!-- Generated from docs/developer-guide.md; @kahwee/sf-map-svg 5.1.4 · local working tree. -->
|
|
271
271
|
|
|
272
272
|
# Developer guide
|
|
273
273
|
|
|
@@ -433,7 +433,7 @@ See [API options](https://github.com/kahwee/sf-map-svg/blob/main/docs/API.md), [
|
|
|
433
433
|
|
|
434
434
|
---
|
|
435
435
|
|
|
436
|
-
<!-- Generated from docs/API.md; @kahwee/sf-map-svg 5.1.
|
|
436
|
+
<!-- Generated from docs/API.md; @kahwee/sf-map-svg 5.1.4 · local working tree. -->
|
|
437
437
|
|
|
438
438
|
# Map options and lifecycle
|
|
439
439
|
|
|
@@ -619,7 +619,7 @@ Call `binding.setMarkers(next)` after `map.setMarkers(next)` when the catalog ch
|
|
|
619
619
|
|
|
620
620
|
---
|
|
621
621
|
|
|
622
|
-
<!-- Generated from docs/EXAMPLES.md; @kahwee/sf-map-svg 5.1.
|
|
622
|
+
<!-- Generated from docs/EXAMPLES.md; @kahwee/sf-map-svg 5.1.4 · local working tree. -->
|
|
623
623
|
|
|
624
624
|
# Examples
|
|
625
625
|
|
|
@@ -760,7 +760,7 @@ See [query semantics and limitations](https://github.com/kahwee/sf-map-svg/blob/
|
|
|
760
760
|
|
|
761
761
|
---
|
|
762
762
|
|
|
763
|
-
<!-- Generated from docs/consumer-integration.md; @kahwee/sf-map-svg 5.1.
|
|
763
|
+
<!-- Generated from docs/consumer-integration.md; @kahwee/sf-map-svg 5.1.4 · local working tree. -->
|
|
764
764
|
|
|
765
765
|
# Embedding and progressive enhancement
|
|
766
766
|
|
|
@@ -938,7 +938,7 @@ keyboard, focus, and dismissal behavior. See [coordinates and units](https://git
|
|
|
938
938
|
|
|
939
939
|
---
|
|
940
940
|
|
|
941
|
-
<!-- Generated from docs/migration-v5.md; @kahwee/sf-map-svg 5.1.
|
|
941
|
+
<!-- Generated from docs/migration-v5.md; @kahwee/sf-map-svg 5.1.4 · local working tree. -->
|
|
942
942
|
|
|
943
943
|
# Migrating to v5
|
|
944
944
|
|
|
@@ -1043,7 +1043,7 @@ collections. Shared edges can match more than one feature. See the
|
|
|
1043
1043
|
|
|
1044
1044
|
---
|
|
1045
1045
|
|
|
1046
|
-
<!-- Generated from docs/migration-v3.md; @kahwee/sf-map-svg 5.1.
|
|
1046
|
+
<!-- Generated from docs/migration-v3.md; @kahwee/sf-map-svg 5.1.4 · local working tree. -->
|
|
1047
1047
|
|
|
1048
1048
|
# Migrate to version 3
|
|
1049
1049
|
|
|
@@ -1084,7 +1084,7 @@ District styles are now prepared once per district on construction and style/yea
|
|
|
1084
1084
|
|
|
1085
1085
|
---
|
|
1086
1086
|
|
|
1087
|
-
<!-- Generated from SOURCES.md; @kahwee/sf-map-svg 5.1.
|
|
1087
|
+
<!-- Generated from SOURCES.md; @kahwee/sf-map-svg 5.1.4 · local working tree. -->
|
|
1088
1088
|
|
|
1089
1089
|
# Geometry and provenance
|
|
1090
1090
|
|
|
@@ -1321,7 +1321,7 @@ Report-history points preserve only actual validated source counts and their dis
|
|
|
1321
1321
|
|
|
1322
1322
|
---
|
|
1323
1323
|
|
|
1324
|
-
<!-- Generated from CHANGELOG.md; @kahwee/sf-map-svg 5.1.
|
|
1324
|
+
<!-- Generated from CHANGELOG.md; @kahwee/sf-map-svg 5.1.4 · local working tree. -->
|
|
1325
1325
|
|
|
1326
1326
|
# Changelog
|
|
1327
1327
|
|
|
@@ -1329,6 +1329,10 @@ User-visible changes are recorded here. Unreleased entries describe changes on `
|
|
|
1329
1329
|
|
|
1330
1330
|
## Unreleased
|
|
1331
1331
|
|
|
1332
|
+
## 5.1.4 — 2026-11-05
|
|
1333
|
+
|
|
1334
|
+
- 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.
|
|
1335
|
+
|
|
1332
1336
|
## 5.1.3 — 2026-11-05
|
|
1333
1337
|
|
|
1334
1338
|
- 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.
|
|
@@ -1725,7 +1729,7 @@ consistent home, and makes animation and subscription ownership predictable.
|
|
|
1725
1729
|
|
|
1726
1730
|
# Published type contracts
|
|
1727
1731
|
|
|
1728
|
-
@kahwee/sf-map-svg 5.1.
|
|
1732
|
+
@kahwee/sf-map-svg 5.1.4 · local working tree. These declarations are generated by TypeScript. Use package export paths, not the internal filenames below. Imported relative contracts are included for context, not as additional public entry points.
|
|
1729
1733
|
|
|
1730
1734
|
| Public import | Declaration |
|
|
1731
1735
|
| --- | --- |
|
|
@@ -2111,7 +2115,7 @@ export type DistrictYear = 2002 | 2012 | 2022;
|
|
|
2111
2115
|
export interface DistrictSelection {
|
|
2112
2116
|
id: number;
|
|
2113
2117
|
year: DistrictYear;
|
|
2114
|
-
district: import('./map-
|
|
2118
|
+
district: import('./rendering/svg-map-renderer.js').DistrictRowData;
|
|
2115
2119
|
}
|
|
2116
2120
|
export interface DistrictStyle {
|
|
2117
2121
|
fill?: string;
|
|
@@ -2156,7 +2160,7 @@ export interface SFMapOptions {
|
|
|
2156
2160
|
neighborhoodLabels?: boolean;
|
|
2157
2161
|
districtFills?: boolean;
|
|
2158
2162
|
/** Evaluated once per district at construction/style/year updates; call setDistrictStyle again when external data changes. */
|
|
2159
|
-
districtStyle?: (district: import('./map-
|
|
2163
|
+
districtStyle?: (district: import('./rendering/svg-map-renderer.js').DistrictRowData) => DistrictStyle;
|
|
2160
2164
|
districtLabels?: boolean;
|
|
2161
2165
|
/** Hide all visible text labels while retaining geographic symbols and accessible titles. */
|
|
2162
2166
|
labels?: boolean;
|
|
@@ -2378,7 +2382,7 @@ export type { MapAppearance, MapCamera, MapCapabilities, MapConfiguration, MapCo
|
|
|
2378
2382
|
|
|
2379
2383
|
```ts
|
|
2380
2384
|
import type { DistrictProperties, FeatureCollection, NeighborhoodProperties, NeighborhoodSource } from '../data/types.js';
|
|
2381
|
-
import type { SFMapData } from './map-
|
|
2385
|
+
import type { SFMapData } from './rendering/svg-map-renderer.js';
|
|
2382
2386
|
import type { DistrictYear } from './types.js';
|
|
2383
2387
|
/** Geographic inputs for the data-injected interactive map entry point. */
|
|
2384
2388
|
export interface InteractiveSFMapData {
|
|
@@ -2388,13 +2392,13 @@ export interface InteractiveSFMapData {
|
|
|
2388
2392
|
}
|
|
2389
2393
|
```
|
|
2390
2394
|
|
|
2391
|
-
### dist/src/map-
|
|
2395
|
+
### dist/src/rendering/svg-map-renderer.d.ts
|
|
2392
2396
|
|
|
2393
2397
|
```ts
|
|
2394
|
-
import type { Geometry, Position } from '
|
|
2395
|
-
import type { DistrictYear, SFMapOptions } from '
|
|
2396
|
-
export type { DistrictYear, MapMarker, MapOverlay, SFMapOptions } from '
|
|
2397
|
-
import { type Project } from '
|
|
2398
|
+
import type { Geometry, Position } from '../../data/types.js';
|
|
2399
|
+
import type { DistrictYear, SFMapOptions } from '../types.js';
|
|
2400
|
+
export type { DistrictYear, MapMarker, MapOverlay, SFMapOptions } from '../types.js';
|
|
2401
|
+
import { type Project } from '../geometry.js';
|
|
2398
2402
|
export interface SFMapData {
|
|
2399
2403
|
coast: Geometry;
|
|
2400
2404
|
districts?: Readonly<Partial<Record<DistrictYear, readonly DistrictRowData[]>>>;
|
|
@@ -2516,11 +2520,11 @@ export declare function geometryContainsPoint(geometry: Geometry | null | undefi
|
|
|
2516
2520
|
|
|
2517
2521
|
```ts
|
|
2518
2522
|
import type { InteractiveSFMapData } from './explorer-data.js';
|
|
2519
|
-
import { type SFMapData } from './map-
|
|
2523
|
+
import { type SFMapData } from './rendering/svg-map-renderer.js';
|
|
2520
2524
|
import type { SFMapOptions } from './types.js';
|
|
2521
2525
|
/** Either compact static geography or the same source-aware geography used by createMap. */
|
|
2522
2526
|
export type StaticMapInput = SFMapData | InteractiveSFMapData;
|
|
2523
|
-
export type { DistrictRowData, SFMapData as StaticMapData } from './map-
|
|
2527
|
+
export type { DistrictRowData, SFMapData as StaticMapData } from './rendering/svg-map-renderer.js';
|
|
2524
2528
|
export type { DistrictStyle, DistrictYear, SFMapOptions as StaticMapOptions, StaticMapAppearance, StaticMapLayers, } from './types.js';
|
|
2525
2529
|
/** Server-safe SVG rendering with explicit data; returns SVG plus projection helpers. */
|
|
2526
2530
|
export declare function renderMap(data: StaticMapInput, options?: SFMapOptions): {
|
package/llms.txt
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> Offline San Francisco SVG maps with explicit geographic imports, no runtime dependencies, static rendering, and a browser controller.
|
|
4
4
|
|
|
5
|
-
@kahwee/sf-map-svg 5.1.
|
|
5
|
+
@kahwee/sf-map-svg 5.1.4 · local working tree. Node >=24.0.0 for server rendering; browsers need a DOM and a JSON-capable bundler.
|
|
6
6
|
|
|
7
7
|
These are working-tree docs. Unreleased changelog entries may describe APIs absent from npm; verify the installed version before copying them.
|
|
8
8
|
|
package/package.json
CHANGED
|
File without changes
|
|
File without changes
|