@kahwee/sf-map-svg 4.1.0 → 4.3.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.
- package/CHANGELOG.md +23 -0
- package/README.md +11 -15
- package/dist/src/appearance.d.ts +5 -0
- package/dist/src/appearance.js +54 -0
- package/dist/src/configuration.d.ts +3 -1
- package/dist/src/configuration.js +32 -23
- package/dist/src/controller-types.d.ts +81 -8
- package/dist/src/explorer-core.d.ts +14 -2
- package/dist/src/explorer-core.js +174 -38
- package/dist/src/guide-map.js +1 -1
- package/dist/src/guide-static.js +3 -3
- package/dist/src/map-core.d.ts +14 -0
- package/dist/src/map-core.js +4 -1
- package/dist/src/map.js +164 -18
- package/dist/src/static.d.ts +6 -3
- package/dist/src/static.js +57 -2
- package/dist/src/types.d.ts +10 -2
- package/docs/API.md +67 -1
- package/docs/api-audit.md +7 -7
- package/docs/consumer-integration.md +35 -11
- package/docs/developer-guide.md +6 -4
- package/llms-full.txt +263 -54
- package/llms.txt +1 -1
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
User-visible changes are recorded here. Unreleased entries describe changes on `main` that are not part of a tagged package release.
|
|
4
4
|
|
|
5
|
+
## Unreleased
|
|
6
|
+
|
|
7
|
+
## 4.3.0 — 2026-10-02
|
|
8
|
+
|
|
9
|
+
- Add `selectFeature` with typed marker, neighborhood source/ID and district year/ID references. Missing or mismatched identities preserve current geography and selection.
|
|
10
|
+
- Accept the same source-aware map data in static and interactive rendering. Static maps can explicitly select a supplied neighborhood definition without manually rebuilding renderer rows; compact static inputs remain supported.
|
|
11
|
+
- Include current mode, source, year and labels in configuration snapshots. Route controller mode/source/label setters through atomic configuration and support explicit camera preservation with `{ resetView: false }`.
|
|
12
|
+
- Refine the Pages atlas opening with concise copy, a map ahead of statistics on phones, coordinate captions and three direct paths into designing, exploring and building. Improve shared theme-button touch targets and stack API reference records on phones while retaining table semantics.
|
|
13
|
+
|
|
14
|
+
- Clarify the documentation paths, progressive enhancement and cleanup examples; consolidate browser validation and release instructions into one maintenance runbook. Check README and integration recipes against the packed package.
|
|
15
|
+
|
|
16
|
+
## 4.2.0 — 2026-10-01
|
|
17
|
+
|
|
18
|
+
- Reuse one native dropdown treatment across Pages, with an inset chevron, a reserved end gutter, and preserved labels, keyboard behavior, and mobile pickers.
|
|
19
|
+
- Strengthen the playground with strict TypeScript, checked JavaScript/TypeScript snippets, versioned design links, persistent explicit layer choices and independent static rendering. Keep mobile controls unobstructed and picker labels legible in dark mode.
|
|
20
|
+
- Express explicit configuration resets for consumers using `exactOptionalPropertyTypes`, including nested appearance tokens. Make Pages API and guide references describe their actual package version without internal preview warnings.
|
|
21
|
+
|
|
22
|
+
- Share grouped `layers` and supported `appearance` options between static and interactive maps, retaining flat static options and existing defaults; accept readonly static marker arrays.
|
|
23
|
+
- Update interactive appearance, mode, source, district year and labels through `configure`, preserving camera and retained marker nodes. Merge appearance tokens by key and validate combined patches before commit.
|
|
24
|
+
- Add `getResolvedConfiguration()` and `getCapabilities()` for effective layer switches and supplied geography, plus an additive `selectionchange` event with consistent current/previous snapshots.
|
|
25
|
+
- Add a live Pages map design playground with six editable palettes, layers, typography, sample pins/routes, browser/static previews, undo/redo, shareable designs, copyable code and full-city SVG export. Keep released-package previews compatible with their installed API.
|
|
26
|
+
- Refresh the Pages site: a new SF Map SVG mark traced from the real coastline, a reframed header with a release link, card-style reference tables that no longer collapse, a live filter for the API reference, an aligned playground with one-row-per-colour inks, and 12px minimum type for labels and badges.
|
|
27
|
+
|
|
5
28
|
## 4.1.0 — 2026-10-01
|
|
6
29
|
|
|
7
30
|
- Validate canonical and generated GeoJSON during data checks, rejecting invalid WGS84 coordinates and malformed polygon rings before release.
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Offline, self-contained San Francisco SVG maps. The package has no runtime dependencies. Geography is always an explicit import; the root entry does not bundle data.
|
|
4
4
|
|
|
5
|
-
[Live examples](https://kahwee.github.io/sf-map-svg/examples.html) · [Storybook source](stories/) · [Geographic sources](SOURCES.md) · [v3 migration](docs/migration-v3.md)
|
|
5
|
+
[Getting started](docs/developer-guide.md) · [API reference](docs/API.md) · [Design playground](https://kahwee.github.io/sf-map-svg/playground.html) · [Live examples](https://kahwee.github.io/sf-map-svg/examples.html) · [Storybook source](stories/) · [Geographic sources](SOURCES.md) · [v3 migration](docs/migration-v3.md)
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -45,14 +45,19 @@ const map = createMap(fullMapData, {
|
|
|
45
45
|
features: { motion: true },
|
|
46
46
|
appearance: { theme: 'districts' },
|
|
47
47
|
});
|
|
48
|
-
document.querySelector('#map')
|
|
48
|
+
const host = document.querySelector('#map');
|
|
49
|
+
if (!host) throw new Error('Missing #map container');
|
|
50
|
+
host.append(map.element);
|
|
49
51
|
map.on('neighborhoodchange', ({ name }) => console.log(name));
|
|
50
52
|
map.camera.zoom(1.5);
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
+
// Call when your application removes this view.
|
|
54
|
+
function disposeMap() {
|
|
55
|
+
map.destroy();
|
|
56
|
+
map.element.remove();
|
|
57
|
+
}
|
|
53
58
|
```
|
|
54
59
|
|
|
55
|
-
`createMap(data, options)` returns a controller. Use `map.element` for mounting, `map.configure({ features, layers, controls })` for runtime switches, `map.camera` for pan/zoom/fit/reset, and `map.on()` for typed events. Appearance
|
|
60
|
+
`createMap(data, options)` returns a controller. Use `map.element` for mounting, `map.configure({ features, layers, controls, appearance, mode, source, year, labels })` for runtime switches, `map.camera` for pan/zoom/fit/reset, and `map.on()` for typed events. Appearance can change live while preserving camera, selection, and focus. `getResolvedConfiguration()` explains effective layers; `getCapabilities()` reports supplied geography. SFAR realtor neighborhoods are the default when supplied; SF Find and analysis are explicit alternate sources. The `/data/full` preset includes all three collections and historical districts.
|
|
56
61
|
|
|
57
62
|
See [controller options, events, and lifecycle](docs/API.md#interactive-maps),
|
|
58
63
|
[examples](docs/EXAMPLES.md), and [consumer integration](docs/consumer-integration.md).
|
|
@@ -66,13 +71,4 @@ Call `destroy()` when removing an interactive map.
|
|
|
66
71
|
|
|
67
72
|
Canonical geography is in [`data/`](data/README.md), with provenance in [`SOURCES.md`](SOURCES.md). The public `/data` entry exposes lookup, catalog, district maps, and source-specific neighborhood collections. These are deeply frozen; clone before editing.
|
|
68
73
|
|
|
69
|
-
|
|
70
|
-
pnpm install --frozen-lockfile
|
|
71
|
-
pnpm check
|
|
72
|
-
pnpm demo
|
|
73
|
-
pnpm build-storybook
|
|
74
|
-
pnpm test:stories:coverage
|
|
75
|
-
pnpm test:package
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
The [contributing guide](CONTRIBUTING.md) explains the geographic and release checks. If you are upgrading from v2, follow the [v3 migration guide](docs/migration-v3.md).
|
|
74
|
+
Install with `pnpm install --frozen-lockfile`. See the [contributing guide](CONTRIBUTING.md) for architecture and the [validation and release runbook](https://github.com/kahwee/sf-map-svg/blob/main/docs/maintenance.md) for checks, browser inspection, and publishing. If you are upgrading from v2, follow the [v3 migration guide](docs/migration-v3.md).
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { MapAppearance } from './controller-types.js';
|
|
2
|
+
export declare const appearanceKeys: readonly ['theme', 'colors', 'labelStyle', 'areaStyle', 'districtStyle', 'labelSize', 'style', 'markerRadius', 'markerHitSize', 'markerColor', 'selectedMarkerColor'];
|
|
3
|
+
/** Copy configuration records without cloning or invoking styling callbacks. */
|
|
4
|
+
export declare function copyAppearance(value?: MapAppearance): MapAppearance;
|
|
5
|
+
export declare function prepareAppearance(current: MapAppearance, patch: MapAppearance | undefined): MapAppearance;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { assertOptions, validateExplorerOptions } from './validation.js';
|
|
2
|
+
export const appearanceKeys = [
|
|
3
|
+
'theme',
|
|
4
|
+
'colors',
|
|
5
|
+
'labelStyle',
|
|
6
|
+
'areaStyle',
|
|
7
|
+
'districtStyle',
|
|
8
|
+
'labelSize',
|
|
9
|
+
'style',
|
|
10
|
+
'markerRadius',
|
|
11
|
+
'markerHitSize',
|
|
12
|
+
'markerColor',
|
|
13
|
+
'selectedMarkerColor',
|
|
14
|
+
];
|
|
15
|
+
/** Copy configuration records without cloning or invoking styling callbacks. */
|
|
16
|
+
export function copyAppearance(value = {}) {
|
|
17
|
+
const { districtStyle, ...rest } = value;
|
|
18
|
+
return { ...structuredClone(rest), ...(districtStyle ? { districtStyle } : {}) };
|
|
19
|
+
}
|
|
20
|
+
export function prepareAppearance(current, patch) {
|
|
21
|
+
if (patch === undefined)
|
|
22
|
+
return {};
|
|
23
|
+
assertOptions(patch, 'appearance', appearanceKeys);
|
|
24
|
+
const next = { ...current, ...patch };
|
|
25
|
+
for (const key of ['colors', 'labelStyle', 'areaStyle', 'labelSize', 'style']) {
|
|
26
|
+
if (Object.hasOwn(patch, key) && patch[key] !== undefined) {
|
|
27
|
+
assertOptions(patch[key], key);
|
|
28
|
+
const tokens = { ...current[key], ...patch[key] };
|
|
29
|
+
for (const name of Object.keys(tokens))
|
|
30
|
+
if (tokens[name] === undefined)
|
|
31
|
+
delete tokens[name];
|
|
32
|
+
Object.assign(next, { [key]: tokens });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
for (const key of appearanceKeys)
|
|
36
|
+
if (next[key] === undefined)
|
|
37
|
+
delete next[key];
|
|
38
|
+
validateExplorerOptions(next);
|
|
39
|
+
if (next.theme !== undefined && !['districts', 'transit'].includes(next.theme))
|
|
40
|
+
throw new TypeError('Theme must be districts or transit.');
|
|
41
|
+
const min = next.labelSize?.min ?? 11, max = next.labelSize?.max ?? 12;
|
|
42
|
+
const radius = next.markerRadius ?? 6, hit = next.markerHitSize ?? 44;
|
|
43
|
+
if (![min, max, radius, hit].every(Number.isFinite) ||
|
|
44
|
+
min < 8 ||
|
|
45
|
+
max < min ||
|
|
46
|
+
max > 32 ||
|
|
47
|
+
radius <= 0 ||
|
|
48
|
+
hit < radius * 2)
|
|
49
|
+
throw new RangeError('Use label sizes from 8–32px and a positive marker radius inside its hit target.');
|
|
50
|
+
const weight = next.labelStyle?.fontWeight;
|
|
51
|
+
if (weight !== undefined && (!Number.isFinite(weight) || weight < 1 || weight > 1000))
|
|
52
|
+
throw new RangeError('Label font weight must be finite and between 1 and 1000.');
|
|
53
|
+
return copyAppearance(next);
|
|
54
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { MapConfiguration, MapConfigurationSnapshot, MapOptions } from './controller-types.js';
|
|
2
2
|
import type { NeighborhoodExplorerOptions } from './types.js';
|
|
3
3
|
export declare function expandMapOptions(options: MapOptions): NeighborhoodExplorerOptions;
|
|
4
|
+
type PresentationSnapshot = Pick<MapConfigurationSnapshot, 'features' | 'layers' | 'controls' | 'appearance'>;
|
|
4
5
|
/** Prepare the entire configuration before any DOM mutation. */
|
|
5
|
-
export declare function prepareConfiguration(current:
|
|
6
|
+
export declare function prepareConfiguration(current: PresentationSnapshot, patch: MapConfiguration): PresentationSnapshot;
|
|
7
|
+
export {};
|
|
@@ -1,18 +1,6 @@
|
|
|
1
|
+
import { appearanceKeys, copyAppearance, prepareAppearance } from './appearance.js';
|
|
1
2
|
import { controlKeys, layerKeys, normalizeFeatures, validateSwitchPatch } from './features.js';
|
|
2
3
|
import { assertOptions, validateExplorerOptions } from './validation.js';
|
|
3
|
-
const appearanceKeys = [
|
|
4
|
-
'theme',
|
|
5
|
-
'colors',
|
|
6
|
-
'labelStyle',
|
|
7
|
-
'areaStyle',
|
|
8
|
-
'districtStyle',
|
|
9
|
-
'labelSize',
|
|
10
|
-
'style',
|
|
11
|
-
'markerRadius',
|
|
12
|
-
'markerHitSize',
|
|
13
|
-
'markerColor',
|
|
14
|
-
'selectedMarkerColor',
|
|
15
|
-
];
|
|
16
4
|
const featureKeys = [
|
|
17
5
|
'motion',
|
|
18
6
|
'markerEntrance',
|
|
@@ -48,18 +36,34 @@ export function expandMapOptions(options) {
|
|
|
48
36
|
? `Use ${replacement} instead of the flat ${key} option.`
|
|
49
37
|
: 'interface is internal and is not a supported createMap option.');
|
|
50
38
|
}
|
|
51
|
-
|
|
52
|
-
assertOptions(options.appearance, 'appearance', appearanceKeys);
|
|
39
|
+
const preparedAppearance = options.appearance === undefined ? undefined : prepareAppearance({}, options.appearance);
|
|
53
40
|
if (options.features !== undefined)
|
|
54
41
|
normalizeFeatures(options.features);
|
|
55
|
-
const { features, appearance, ...rest } = options;
|
|
56
|
-
const expanded = { ...rest, ...
|
|
42
|
+
const { features, appearance: _appearance, ...rest } = options;
|
|
43
|
+
const expanded = { ...rest, ...preparedAppearance, ...features };
|
|
57
44
|
validateExplorerOptions(expanded);
|
|
58
45
|
return expanded;
|
|
59
46
|
}
|
|
60
47
|
/** Prepare the entire configuration before any DOM mutation. */
|
|
61
48
|
export function prepareConfiguration(current, patch) {
|
|
62
|
-
assertOptions(patch, 'configuration', [
|
|
49
|
+
assertOptions(patch, 'configuration', [
|
|
50
|
+
'features',
|
|
51
|
+
'layers',
|
|
52
|
+
'controls',
|
|
53
|
+
'appearance',
|
|
54
|
+
'mode',
|
|
55
|
+
'source',
|
|
56
|
+
'year',
|
|
57
|
+
'labels',
|
|
58
|
+
]);
|
|
59
|
+
if (patch.mode !== undefined && !['basemap', 'neighborhoods', 'districts'].includes(patch.mode))
|
|
60
|
+
throw new RangeError('Unknown map mode.');
|
|
61
|
+
if (patch.source !== undefined && !['realtor', 'sf-find', 'analysis'].includes(patch.source))
|
|
62
|
+
throw new RangeError('Unknown neighborhood source.');
|
|
63
|
+
if (patch.year !== undefined && ![2002, 2012, 2022].includes(patch.year))
|
|
64
|
+
throw new RangeError('District year must be 2002, 2012, or 2022.');
|
|
65
|
+
if (patch.labels !== undefined && typeof patch.labels !== 'boolean')
|
|
66
|
+
throw new TypeError('Labels must be a boolean.');
|
|
63
67
|
const features = 'features' in patch
|
|
64
68
|
? normalizeFeatures(patch.features === undefined
|
|
65
69
|
? {}
|
|
@@ -78,11 +82,16 @@ export function prepareConfiguration(current, patch) {
|
|
|
78
82
|
delete next[property];
|
|
79
83
|
return next;
|
|
80
84
|
};
|
|
81
|
-
return
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
85
|
+
return {
|
|
86
|
+
...structuredClone({
|
|
87
|
+
features,
|
|
88
|
+
layers: switches('layers', current.layers, layerKeys),
|
|
89
|
+
controls: switches('controls', current.controls, controlKeys),
|
|
90
|
+
}),
|
|
91
|
+
appearance: 'appearance' in patch
|
|
92
|
+
? prepareAppearance(current.appearance ?? {}, patch.appearance)
|
|
93
|
+
: copyAppearance(current.appearance),
|
|
94
|
+
};
|
|
86
95
|
}
|
|
87
96
|
function checkedFeatures(value) {
|
|
88
97
|
normalizeFeatures(value);
|
|
@@ -1,24 +1,84 @@
|
|
|
1
1
|
import type { Geometry, NeighborhoodSource } from '../data/types.js';
|
|
2
2
|
import type { CameraOptions, DistrictSelection, DistrictStyle, DistrictYear, InteractiveLayers, MapFeatures, MapMarker, MapOverlay, MapPadding, MapViewport, NeighborhoodExplorerOptions, NeighborhoodSelection } from './types.js';
|
|
3
|
-
|
|
3
|
+
/** Patch properties can explicitly remove overrides, including with exactOptionalPropertyTypes. */
|
|
4
|
+
type Resettable<T> = {
|
|
5
|
+
[K in keyof T]?: T[K] | undefined;
|
|
6
|
+
};
|
|
7
|
+
type AppearanceOptions = Pick<NeighborhoodExplorerOptions, 'theme' | 'colors' | 'labelStyle' | 'areaStyle' | 'districtStyle' | 'labelSize' | 'style' | 'markerRadius' | 'markerHitSize' | 'markerColor' | 'selectedMarkerColor'>;
|
|
8
|
+
export type MapAppearance = {
|
|
9
|
+
[K in keyof AppearanceOptions]?: K extends 'colors' | 'style' | 'labelStyle' | 'labelSize' | 'areaStyle' ? Resettable<NonNullable<AppearanceOptions[K]>> | undefined : AppearanceOptions[K] | undefined;
|
|
10
|
+
};
|
|
4
11
|
export type MapControls = NonNullable<NeighborhoodExplorerOptions['controls']>;
|
|
5
|
-
/** Omitted
|
|
12
|
+
/** Omitted keys retain values. Undefined presentation groups reset; undefined mode/source/year/labels retain their current value. */
|
|
6
13
|
export interface MapConfiguration {
|
|
7
|
-
features?: MapFeatures;
|
|
8
|
-
layers?: InteractiveLayers;
|
|
9
|
-
controls?: MapControls;
|
|
14
|
+
features?: Resettable<MapFeatures> | undefined;
|
|
15
|
+
layers?: Resettable<InteractiveLayers> | undefined;
|
|
16
|
+
controls?: Resettable<MapControls> | undefined;
|
|
17
|
+
appearance?: MapAppearance | undefined;
|
|
18
|
+
mode?: NonNullable<NeighborhoodExplorerOptions['mode']> | undefined;
|
|
19
|
+
source?: NeighborhoodSource | undefined;
|
|
20
|
+
year?: DistrictYear | undefined;
|
|
21
|
+
labels?: boolean | undefined;
|
|
10
22
|
}
|
|
11
23
|
export interface MapOptions extends Omit<NeighborhoodExplorerOptions, keyof MapFeatures | keyof MapAppearance | 'interface' | 'onMarkerActivate' | 'onOverlayActivate'> {
|
|
12
24
|
features?: MapFeatures;
|
|
13
|
-
appearance?: MapAppearance;
|
|
25
|
+
appearance?: MapAppearance | undefined;
|
|
14
26
|
}
|
|
27
|
+
/** Current geographic choices and detached presentation overrides; excludes camera and selection. */
|
|
15
28
|
export interface MapConfigurationSnapshot {
|
|
29
|
+
mode: NonNullable<MapOptions['mode']>;
|
|
30
|
+
source: NeighborhoodSource;
|
|
31
|
+
year: DistrictYear;
|
|
32
|
+
labels: boolean;
|
|
16
33
|
features: MapFeatures;
|
|
17
34
|
/** Explicit overrides; absent values continue to follow the current mode. */
|
|
18
35
|
layers: InteractiveLayers;
|
|
19
36
|
controls: MapControls;
|
|
37
|
+
appearance: MapAppearance;
|
|
38
|
+
}
|
|
39
|
+
export interface MapCapabilities {
|
|
40
|
+
sources: readonly NeighborhoodSource[];
|
|
41
|
+
years: readonly DistrictYear[];
|
|
42
|
+
/** Data availability, independent of requested layer visibility. */
|
|
43
|
+
layers: Readonly<Record<keyof InteractiveLayers, boolean>>;
|
|
44
|
+
}
|
|
45
|
+
export interface ResolvedMapConfiguration extends MapConfigurationSnapshot {
|
|
46
|
+
mode: NonNullable<MapOptions['mode']>;
|
|
47
|
+
source: NeighborhoodSource;
|
|
48
|
+
year: DistrictYear;
|
|
49
|
+
labels: boolean;
|
|
50
|
+
/** Effective switches after mode defaults and data availability; label switches honor labels. */
|
|
51
|
+
layers: Record<keyof InteractiveLayers, boolean>;
|
|
20
52
|
}
|
|
53
|
+
export type MapSelectionChange = {
|
|
54
|
+
kind: 'marker';
|
|
55
|
+
current: MapMarker | null;
|
|
56
|
+
previous: MapMarker | null;
|
|
57
|
+
} | {
|
|
58
|
+
kind: 'neighborhood';
|
|
59
|
+
current: NeighborhoodSelection | null;
|
|
60
|
+
previous: NeighborhoodSelection | null;
|
|
61
|
+
} | {
|
|
62
|
+
kind: 'district';
|
|
63
|
+
current: DistrictSelection | null;
|
|
64
|
+
previous: DistrictSelection | null;
|
|
65
|
+
};
|
|
66
|
+
/** Dataset identity is explicit; selection never switches geography implicitly. */
|
|
67
|
+
export type MapFeatureReference = {
|
|
68
|
+
kind: 'marker';
|
|
69
|
+
id: string | null;
|
|
70
|
+
} | {
|
|
71
|
+
kind: 'neighborhood';
|
|
72
|
+
source: NeighborhoodSource;
|
|
73
|
+
id: string | null;
|
|
74
|
+
} | {
|
|
75
|
+
kind: 'district';
|
|
76
|
+
year: DistrictYear;
|
|
77
|
+
id: number | null;
|
|
78
|
+
};
|
|
21
79
|
export interface MapEvents {
|
|
80
|
+
/** Common selection envelope; existing change events retain their payloads. */
|
|
81
|
+
selectionchange: MapSelectionChange;
|
|
22
82
|
districtchange: DistrictSelection | {
|
|
23
83
|
id: null;
|
|
24
84
|
year: DistrictYear;
|
|
@@ -55,12 +115,18 @@ export interface MapEvents {
|
|
|
55
115
|
};
|
|
56
116
|
mapresize: undefined;
|
|
57
117
|
}
|
|
118
|
+
/** Convenience setters retain their historic reset default; false preserves the camera. */
|
|
119
|
+
export interface MapViewUpdateOptions {
|
|
120
|
+
resetView?: boolean;
|
|
121
|
+
}
|
|
58
122
|
export interface MapCamera {
|
|
123
|
+
/** Fixed 800 × 800 projected space, not longitude/latitude. */
|
|
59
124
|
get(): MapViewport;
|
|
60
125
|
set(view: MapViewport, options?: CameraOptions): void;
|
|
61
126
|
fit(geometry: Geometry, options?: CameraOptions & {
|
|
62
127
|
padding?: number | MapPadding;
|
|
63
128
|
}): void;
|
|
129
|
+
/** Delta in projected map units. */
|
|
64
130
|
pan(x: number, y: number, options?: CameraOptions): void;
|
|
65
131
|
zoom(factor: number, options?: CameraOptions): void;
|
|
66
132
|
reset(options?: CameraOptions): void;
|
|
@@ -74,10 +140,16 @@ export interface MapController {
|
|
|
74
140
|
readonly destroyed: boolean;
|
|
75
141
|
configure(patch: MapConfiguration): void;
|
|
76
142
|
getConfiguration(): MapConfigurationSnapshot;
|
|
143
|
+
getResolvedConfiguration(): ResolvedMapConfiguration;
|
|
144
|
+
getCapabilities(): MapCapabilities;
|
|
77
145
|
on<K extends keyof MapEvents>(type: K, listener: (detail: MapEvents[K]) => void): () => void;
|
|
78
146
|
/** Reconcile stable IDs; retained markers preserve nodes, focus, and entrance animations. */
|
|
79
147
|
setMarkers(markers: readonly MapMarker[]): void;
|
|
80
148
|
setOverlays(overlays: readonly MapOverlay[]): void;
|
|
149
|
+
/** False for missing IDs or a source/year other than the current geography. */
|
|
150
|
+
selectFeature(reference: MapFeatureReference, options?: CameraOptions & {
|
|
151
|
+
fit?: boolean;
|
|
152
|
+
}): boolean;
|
|
81
153
|
selectMarker(id: string | null, options?: CameraOptions & {
|
|
82
154
|
fit?: boolean;
|
|
83
155
|
}): boolean;
|
|
@@ -92,8 +164,8 @@ export interface MapController {
|
|
|
92
164
|
getSelectedDistrict(): DistrictSelection | null;
|
|
93
165
|
setDistrictYear(year: DistrictYear, options?: CameraOptions): void;
|
|
94
166
|
setDistrictStyle(style: ((district: DistrictSelection['district']) => DistrictStyle) | undefined): void;
|
|
95
|
-
setSource(source: NeighborhoodSource): void;
|
|
96
|
-
setMode(mode: NonNullable<MapOptions['mode']
|
|
167
|
+
setSource(source: NeighborhoodSource, options?: MapViewUpdateOptions): void;
|
|
168
|
+
setMode(mode: NonNullable<MapOptions['mode']>, options?: MapViewUpdateOptions): void;
|
|
97
169
|
setLabels(visible: boolean): void;
|
|
98
170
|
setTouchNavigation(enabled: boolean): void;
|
|
99
171
|
projectToScreen(lng: number, lat: number): {
|
|
@@ -104,3 +176,4 @@ export interface MapController {
|
|
|
104
176
|
/** Idempotent; stops work and managed subscriptions. Does not remove host-owned DOM. */
|
|
105
177
|
destroy(): void;
|
|
106
178
|
}
|
|
179
|
+
export {};
|
|
@@ -1,5 +1,17 @@
|
|
|
1
|
+
import type { NeighborhoodSource } from '../data/types.js';
|
|
2
|
+
import type { MapConfiguration } from './controller-types.js';
|
|
1
3
|
import type { InteractiveSFMapData } from './explorer-data.js';
|
|
2
|
-
import type { NeighborhoodExplorerElement, NeighborhoodExplorerOptions } from './types.js';
|
|
4
|
+
import type { DistrictYear, ExplorerMode, NeighborhoodExplorerElement, NeighborhoodExplorerOptions } from './types.js';
|
|
3
5
|
export type { ExplorerMode, NeighborhoodExplorerElement, NeighborhoodExplorerOptions, } from './types.js';
|
|
6
|
+
/** Private bridge between the renderer and its public lifecycle controller. */
|
|
7
|
+
type MapEngineElement = NeighborhoodExplorerElement & {
|
|
8
|
+
applyPresentation(patch: MapConfiguration, onCommit?: () => void, isCurrent?: () => boolean): boolean;
|
|
9
|
+
getMapState(): {
|
|
10
|
+
mode: ExplorerMode;
|
|
11
|
+
source: NeighborhoodSource;
|
|
12
|
+
year: DistrictYear;
|
|
13
|
+
labels: boolean;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
4
16
|
/** Create an offline, browser-only neighborhood explorer. Call destroy() before disposal. */
|
|
5
|
-
export declare function createNeighborhoodExplorerCore(options: NeighborhoodExplorerOptions | undefined, data: InteractiveSFMapData):
|
|
17
|
+
export declare function createNeighborhoodExplorerCore(options: NeighborhoodExplorerOptions | undefined, data: InteractiveSFMapData): MapEngineElement;
|