Changelog
What changed for users of active-geo, per version.
@workspace/active-geo 0.4.0Developmentunreleased, in development in Foundationbaseline ce081dc3
The version line at the top of this page is read from the package and the release register of Foundation. No release of this package is recorded. The section Unreleased lists what changed since 0.4.0, including the changes made since Foundation maintains the package.
Notable changes to @workspace/active-geo. Format based on
Keep a Changelog; versioning per
Semantic Versioning. Before 1.0.0 the
API is workable but not stable.
[Unreleased]#
Maintained in Foundation (since 2026-10-21)#
- Dependencies:
@workspace/active-helpersreplaces@workspace/active-utils. The CSV field serialization and the menu pipeline (./serialization,./menu-items) now come from@workspace/active-helpers, the one package that owns them. Same functions, same behaviour; the public API is unchanged. - The clean-consumer proof names the real closure. Its allowlist named
active-i18nandactive-typesbut notactive-format, which the package declares and reaches; it now names the declared owners (active-format,active-helpers,active-virtual). - A label test states its data dependency. "Labels keep clear of SVG point symbols" is skipped, like its sibling, when the local NL research source is absent: its synthetic fallback lies outside the view over the Netherlands and measured nothing.
- Documentation: references to documents and absolute paths of the source
repository are text, marked
(source repository: …); links to review artefacts that were never part of the package are text too.
Changed — formatting from @workspace/active-format (2026-10-18, platform ADR-113)#
- The manifest declares
@workspace/active-formatinstead of@workspace/active-i18n; the package imports nothing from a translation package any more. No prop, export or behaviour changed.
Changed — canonical transfer form (2026-10-18, platform ADR-112)#
- The manifest is the transferable manifest (RPS-D7): no workspace
package in
devDependencies(what the package's own development takes from Datyx, such as the lint preset, is host tooling in the reusable package register); the declaration packages of what the source imports sit in the field of their module (@types/reactand@types/react-domas optional peers beside thereactpeers;@types/d3-geoand@types/d3-scaleas dependencies). Nothing changes for a Datyx consumer. tsconfig.jsonis self-contained: it no longer extends@workspace/typescript-config, with the same compiler options, because a bundler in a consumer reads it for every source file it compiles.
P1 public switch (P1e, 2026-10-07)#
The public entry is now the P1 implementation of the API-first contract v0.5 (D-42 to D-47); the 0.4 component and its API are removed in the same change (no compatibility layer, no v1 mode). P1 publishes only what works: an input of a later phase (P2 to P4) does not exist in the types and is refused at runtime with GEO_OPTION_UNAVAILABLE. V1 is not accepted, nothing is published to npm, and the performance acceptance of P1 is still open (ROADMAP.md).
Added#
- Measures and encodings (W-1, D-43):
measuresper layer (number withsemantics, or category withcategories), values inline (values) or joined (data), withheld{suppressed: true};fill(own classed scale: threshold, quantize, category, diverging) and pointmarker.size; readings and encoding statuses in the row model. - The family menu (D-44): right click, long press, Shift+F10, ContextMenu key, optional menu button (
contextMenu.trigger: 'button'); groupshost,copy,table,navigation(place-bound),level,export;contextMenu.items(context),features,groupOrder. - Navigation on demand (D-45):
zoom.controls(on-demanddefault,always,false; position, orientation, actions),zoom.gestures,keyboardShortcuts,minZoom/maxZoom. - Legend model (v0.5 § 7):
legendposition (alsoend,top, a corner over the map,outlet),arrangement,sectionswith status blocks and custom entries,order,explanations,interaction(highlight,none,host),highlightChip;useGeoLegend()for a host legend;handle.getLegendModel(),activateLegendEntry(entryId);onLegendActivate(cancellable). - Highlight as controllable state:
highlight,defaultHighlight,onHighlightChange(GeoHighlight, round-trip without casts). - View (W-9):
view,defaultView,home,onViewChange(GeoViewwithframe/freemode). - Own accessible table (D-46) from the row model, opened from the menu,
table.open/defaultOpen, orrevealInTable; the host table protocolonTableRequestwith focus intent and cancellation. - Text fields: per layer
tooltip: {fields},label: {fields, max, placement},describe(context). - State v2 (D-47) with v1 migration; CSV v2 (D-43) with
csv_version, measure and detail namespaces. englishGeoMessages(the full key catalogue with English words);GeoConfigurationErrorcodesGEO_ID_INVALID,GEO_MEASURE_INVALID,GEO_MEASURE_ID_DUPLICATE,GEO_ENCODING_REFERENCE_INVALID,GEO_SCALE_INCOMPATIBLE,GEO_PALETTE_INVALID,GEO_DATA_SOURCE_CONFLICT,GEO_LEGEND_GROUP_ID_DUPLICATE,GEO_OPTION_CONFLICT,GEO_OPTION_UNAVAILABLE.- Translation keys
geo.nav.home,geo.nav.controlsHint,geo.nav.keysHint,geo.keyboardHintMenu,geo.menu.label,geo.menu.labelFor,geo.menu.showTable,geo.menu.hideTable,geo.menu.zoomHere,geo.menu.zoomTo.
Changed#
- The default is quiet: title, map and source line in rest; navigation on demand; the legend only when something is explained; no footer.
- Export and the table are reached through the menu and the handle; the image legend may stand at the end (
export.image.legend: {content, position: 'end'}). GeoDefaultsmerges by the configuration schema (v0.5 § 8):falseswitches off,nullresets a declared path to the package default, arrays replace.- Diagnostics carry a code and a configuration path (
presentation-value-ignored,variant-field-ignored,reference-pending,text-callback-failed,data-excluded,table-focus-violation,table-request-failed,outlet-missing,scope-second-map,export-failed).
Removed#
- The 0.4 component and its props
presentation,objectActions,viewport/defaultViewport/onViewportChange,exportLimits/exportThemes(nowexport.limits/export.themes); the layer fieldsvalues: [{key, value}],scale,valueLabel,valueSemantics,labels(areas) andsize,color,labels,valueLabel,valueSemantics(points), and the point datum fieldsvalue,label,colorValue,colorSuppressed. - The object card, the footer (Table, Export), the export menu button, the presentation presets.
- Translation keys no P1 route asks for (29):
geo.column.value,geo.noValue,geo.keyboardHintCard,geo.excluded.summary,geo.partial.summary,geo.records.caption,geo.records.summary,geo.reason.invalidValue,geo.record.excluded,geo.record.ambiguous,geo.record.invalidKey,geo.record.typeMismatch,geo.keyType.number,geo.keyType.text,geo.legend.highlight,geo.legend.color,geo.legend.size,geo.legend.noValue,geo.legend.sizeNote,geo.announce.location,geo.table.toggle,geo.panel.location,geo.panel.results,geo.panel.empty,geo.panel.key,geo.panel.coordinates,geo.menu.showOnMap,geo.menu.exportPng,geo.menu.exportSvg. The Datyxgeobundles drop them in the same change. - Types
GeoMapPresentation,GeoLegendPresentation,GeoLegendPlacement,GeoLegendArrangement,GeoLegendDisplay,GeoLegendEncoding,GeoLegendEntry,GeoTablePresentation,GeoEffectiveLegendEntry,GeoObjectAction,GeoObjectActionsBuilder,GeoObjectContext,GeoAreaValue,GeoAreaScale,GeoCategory,GeoAreaLabels,GeoLabelContent,GeoPointLabels,GeoPointLabelSource,GeoPointSize,GeoPointColor,GeoViewport,GeoDefaultsValue; state v1 as output; CSV v1.
Migration: 0.4 to P1#
0.x, no deprecation path (Critical Rule Eén API per werkstroom): every consumer in this repository is migrated in the same change.
| 0.4 | P1 |
|---|---|
presentation={{title: 'hide'}} | header={false} (title stays the accessible name) |
presentation={{legend: {placement: 'end', arrangement}}} | legend={{position: 'end', arrangement}} |
presentation={{legend: false}} | legend={false} |
presentation={{navigation: false}} | zoom={{controls: false}} (on-demand is the default; {visibility: 'always'} keeps them visible) |
presentation={{table: 'toggle' | 'open' | 'outlet' | false}} | the menu's "Show the table" / table={{default / table={{position: 'outlet'}} / table={false} |
presentation={{export: false}} | context (and export. to limit formats) |
area values: [{key, value}], value, value, scale | measures: [{id, label, semantics}], data: [{key, values: {id: n}}] (or inline values), fill: {measure: id, scale} |
{key, value: null} / suppressed values | values: {id: null} (missing) / values: {id: {suppressed: true}} (withheld) |
point value, label, color/color; layer size, color (D-29) | values, name; marker: {size: {measure}, fill: {measure, scale}} |
layer labels (areas D-30, points D-31) | layer label: {fields: ['name', {measure: id}], max, placement} |
object card + object | the menu: context; "objects at one place" in context. |
| footer Export (menu) | the menu's export group, or handle. |
viewport, default, on | view, default, on with {mode: 'frame'} or {mode: 'free', center, zoom} |
handle. | handle. |
export, export props | export={{limits, themes}} |
export | export (default), 'all', false or {content, position: 'end'} |
get v1 {version: 1, view: {zoom, center, extent}, selection, layers, level} | v2 {version: 2, view: {mode, …, extent}, selection, layers, encodings, scales, highlight, filter: []}; apply migrates v1 |
CSV v1 (row_ value-, value, value_, unit, color_, reason) | CSV v2: csv_, row_ record, measure:<id>:value|unit|status|text, geometry_, link_, issues, details |
t with the 0.4 keys | the P1 catalogue (english): ten keys added, 29 removed (above) |
Geo + outlets with presentation placements | the same components with legend. / table.; a host legend through use |
data- geo-, geo-, geo-, geo- | the menu (Base UI roles menu/menuitem), geo-, geo- |
The admin app's AppDataDefaults passes header={false} (ADMIN_GEO_HEADER) instead of presentation={{title: 'hide'}}; the Datyx geo catalogue in packages/locales carries the new keys in five languages.
Superseded before release: UI redesign (D-38 to D-41)#
The block below describes the unreleased 0.4 redesign that the P1 switch above replaced. It stays as history; none of it is the current API.
Added (UI redesign, D-38 to D-41)#
- Configurable presentation (D-38):
GeoMapProps.presentationandGeoDefaults.presentation(GeoMapPresentation:title,legend,navigation,table,export,attribution;GeoLegendPresentationwithplacement,arrangement,display,entries; typesGeoLegendPlacement,GeoLegendArrangement,GeoLegendDisplay,GeoLegendEncoding,GeoLegendKey,GeoLegendEntry,GeoTablePresentation). Short forms (legend: true,legend: 'end'), inheritance package default ← providers ← prop (per word; insidelegendper field;entriesatomic), one explanation per encoding (layer:fill,:color,:size,:symbol) with'*'anywhere in the list.GeoMapHandle.getPresentation()(GeoEffectivePresentation,GeoEffectiveLegendEntry) and the root attributesdata-legend-placement,data-legend-requested,data-legend-arrangement,data-table,data-title. A side legend stands below the map under 760 px of container width and keeps its representation (A-1). A malformed legend key or presentation value is reported once (warn:GEO_PRESENTATION_LEGEND_KEY,GEO_PRESENTATION_UNKNOWN,GEO_PRESENTATION_INVALID); a key of an absent layer waits (debug). - Legend (D-38): a compact stepped bar per numeric scale (steps named by exact class and count, boundary values, a reserved caption line with the exact class in focus, under the pointer or highlighted); a list display with counts; the size explained as "Area: ‹value heading›" (
geo.legend.size); the highlight chip in the group's heading, and on the map when the explanation is hidden; hidden explanations in the map's description. - Object card (D-39): one card for an object's details and actions (Copy value, Show in table, the host's), opened by click or tap, Enter, Shift+F10, the ContextMenu key, a right click, a long press and a table row; the objects at a stacked place in a list.
GeoMapProps.objectActions(GeoObjectActionsBuilder,GeoObjectAction,GeoObjectContext). - Table (D-39): tabs per layer and "Not on the map", a search over the whole layer, pages of 100 rows, a row that shows its object on the map, behind Table in the footer (
table: 'toggle'), always open ('open'), in an outlet or none. - Footer: the source, Table and Export (the map's one export entry, a menu on Base UI
Menu). - Outlets (D-41):
GeoMapScope,GeoLegendOutlet,GeoTableOutlet(GeoMapScopeProps,GeoOutletProps); diagnosticsGEO_PRESENTATION_OUTLET_MISSING,GEO_OUTLET_DUPLICATE,GEO_SCOPE_SHARED. - Image legend (D-40):
GeoImageExportOptions.legendis'presentation'(default),'all'orfalse; the image draws compact scales as a stepped bar and lists as rows with counts, always below the map. - Translation keys
geo.table.toggle,geo.card.showInTable,geo.legend.size,geo.text.pair,geo.text.list(amendment A-9),geo.keyboardHintCard,geo.announce.focusMoved. GeoButtonSlotProps['aria-describedby'](the reason of an unavailable card action).
Changed (UI redesign, D-38 to D-41)#
- Default changes, with migration below: the title is shown (
presentation.title, default'show'; wasshowTitle, defaultfalse); the navigation is a block in the map's corner, the whole map and the pan directions only once zoomed in (was a toolbar under the map with a Move disclosure); the legend's numeric scales are compact bars below the map, groups side by side (was a wrapping list of toggles and a status line); a click opens the object's card (was pinned details and, open, the panel's details); export is in the footer (was the menu button and the context menu). - Every pair of texts (label and value, a class and its side, the parts of an accessible name, a caption and its reference, the copied text) is the language's pattern
geo.text.pair/geo.text.list(A-8): a host translator that changes its whole output now changes the names inside those patterns too. geo.announce.locationreads "3 objects at this place" (was "…, listed in the object panel"). The hidden data alternative's caption of a diverging layer joins title and reference withgeo.text.list.GeoImageExportOptions.legendwas a boolean;trueis now'presentation'(the explanations shown, which for the default presentation is every explanation of the visible layers, as before).
Removed (UI redesign, D-38 to D-41)#
- Props
showTitle,legend,panel,navigationandcontextMenu; typesGeoPanelOptions,GeoContextMenuOptions,GeoContextMenuFeatures,GeoContextMenuBuilder,GeoContextMenuItem,GeoContextMenuContext,GeoContextMenuTarget. - The object panel (list, details view, data view), the context menu, the menu button, the header actions Objects and Data, the Move disclosure, the legend's status line with its permanent "Clear highlight", and
contextMenu.pngScale(the Export action exports PNG at scale 2; another scale throughexportAsPng({scale})). - Translation keys
geo.panel.title,geo.panel.deselect,geo.panel.detailsEmpty,geo.panel.viewMap,geo.panel.clearLocation,geo.panel.countMissing,geo.panel.listMap,geo.panel.listMissing,geo.nav.pan,geo.menu.label,geo.menu.labelFor,geo.menu.showAllHere,geo.menu.fitMap,geo.menu.alreadyFitted,geo.keyboardHintPlace,geo.keyboardHintMenu.
Migration: UI redesign (D-38 to D-41)#
0.x, no deprecation path (Critical Rule Eén API per werkstroom; every consumer in this repository is migrated in the same change):
| Before | After |
|---|---|
show (default false) | shown by default; presentation={{title: 'hide'}} where the page shows the title |
legend={false} | presentation={{legend: false}} |
navigation={false} | presentation={{navigation: false}} |
panel={false} | presentation={{table: false}} (the hidden data alternative stays) |
panel={{default | presentation={{table: 'open'}} |
panel={{placement: 'end' | 'start'}} | no equivalent: the table opens below the footer, or in a Geo of the host |
context | object (flat) |
context | presentation={{export: false}} |
context, context | nothing: there is no menu button; the card always exists |
export | export (default) or 'all' |
data- geo-, geo-, geo-, geo-, geo- | geo-, geo-, geo-, geo-, geo- |
A host translator adds the new keys and drops the removed ones (the Datyx catalogue in packages/locales carries them in five languages). The admin app sets presentation={{title: 'hide'}} application-wide in AppDataDefaults (ADMIN_GEO_PRESENTATION).
Added#
-
One composition (D-35):
showTitle(defaultfalse) showstitleabove the map,subtitlea short line under it; beside them the map's actions Objects (with its count), Data and the menu button.attributionis now also shown in small print under the map (the export is unchanged). -
The panel region (D-35): one region with the object list, the details of one object (from a row, or following a map click while open; Deselect with selection on) and the data view (the data alternative's tables on screen, 100 rows per table with Previous and Next). Opening Objects focuses the search, Data its heading, a row its details' heading; closing returns the focus to the opener.
-
Host components (D-36):
GeoMapProps.componentsandGeoDefaults.components(GeoComponents:Button,Toggle,Input; slot propsGeoButtonSlotProps,GeoToggleSlotProps,GeoInputSlotProps). Optional per slot; the map keeps every action, state, ARIA and keyboard route. -
Translation keys
geo.legend.status,geo.nav.pan,geo.panel.close,geo.panel.deselect,geo.panel.detailsEmpty,geo.data.title,geo.data.range,geo.data.previous,geo.data.next. -
Point labels and one mixed placement (D-31, VIS-4B):
GeoPointLayer.labels(GeoPointLabels={content: 'name', max?}or{content: 'value' | 'name-and-value', source: 'size' | 'color', max?},GeoPointLabelSource), off by default. A value needs an explicitsource:sizewrites the point'svaluewith the layer's semantics (zero is "0", missing has none);colorwrites the colour attribute as the details say it (a category's display label, or the colour number with the colour's semantics), only for a valid colour value; missing, invalid and withheld colours have none, and a withheld value never reaches a label, a measurement, the order or an export. Refused (GEO_LAYER_OPTION_INVALID): a value withoutsource, an unknownsource, asourcewithcontent: 'name',source: 'color'withoutcolor. A label stands beside its own symbol at one of four positions (the reading side first, right or left in right-to-left), just outside the symbol's drawn reach (a hollow marker's 3 px, its halo, a selected ring); it covers no symbol of any shown layer, no selection ring, no other label, no selection band of a selected area and no area or context area of a layer drawn above, or it is left out; symbols are never hidden or shrunk. Area and point labels share one placement: selected objects, then the uppermost layer, then the layer's geometric priority (area size; for proportional points filled before hollow, then drawn size), then the typed key; at most 300 objects per cycle, shared max-min fairly over the labelled layers; at most 60 labels per map. SVG and Canvas point layers are labelled the same. Image exports carry area and point labels in one placement. No new translation key; the state and the CSV are unchanged. -
Area labels (D-30, VIS-4A):
GeoAreaLayer.labels(GeoAreaLabels={content: 'name' | 'value' | 'name-and-value', max?},GeoAreaLabelContent), off by default;max1 to 60, default 30; at most 60 labels per map. A label stands inside the largest component of its own area in view, with its whole text and halo at least 4 px from the outline and any hole, clear of other labels, of every drawn point symbol and of the areas of layers drawn above; otherwise it is left out. Order: selected, uppermost layer, size, typed key. Text from the existing descriptions: the name, the formatted value or category, or both; no value to show means the name alone or no label; withheld values never appear; isolated runs; only names are shortened (graphemes, an ellipsis, at least four graphemes kept), never a value. Placement runs after the map is drawn, in chunks, and only labels of the current state are shown. Labels arearia-hiddenand catch no pointer.labelson a point or context layer is refused (GEO_LAYER_OPTION_INVALID). Image exports carry the labels as text (GeoImageExportOptions.labels, defaulttrue;falseleaves them out), placed again for the image and measured for the fonts an image can use. No new translation key; the state and the CSV are unchanged. -
Point colour from a separate attribute (D-29, VIS-3), independent of the size:
GeoPointLayer.color(GeoPointColor={label, scale, valueSemantics?},labelandscalerequired) and per pointcolorValue(a category text or a number) andcolorSuppressed. The scale is one of the area scales (category,threshold,quantize,diverging, with the VIS-2 meaning); a data domain reads only the valid colour values of drawable points. Four statuses stay apart:value,missing,invalid(an undeclared category, a number on a category scale: never a silent other;1is never"1"),suppressed(the value is never kept, shown, searched, copied, stored or exported; presentation, not protection). A colour problem never excludes a point; a point without a size keeps the 3 px hollow marker with its ring in the class colour. A coloured point is its class colour on a two-tone edge; a point without a usable colour is the plain point. The legend shows "Colour: ‹label›", point-shaped swatches per class, the reference of a diverging colour, one entry per special status that occurs, then the size key; every entry highlights, dimming only that layer's points and never their selection ring. Names, details, panel, data alternative and copied text carry the colour (and its class); the panel search finds a category label. SVG and Canvas paint the same (Canvas probes per class token; the stamp cache keys every paint change and starts over per appearance or token set). Image exports carry the class colours and the colour legend. New translation keygeo.legend.color. The state fingerprint of a coloured point layer is{"encoding":"color","label":…,"scale":…}; area fingerprints are unchanged. -
CSV: five columns appended after
key_encoded(D-29):color_label,color_value,color_value_type,color_value_encoded(v1:plus the JSON of the value) andcolor_status. The sixteen earlier columns keep their names, order and meaning; area rows, value-record rows and point layers withoutcolorleave the five empty (not applicable, nevermissing). A reader that indexes columns by name is unaffected; one that expects exactly sixteen columns must accept the new ones. -
The diverging area scale (D-28, VIS-2):
{type: 'diverging', reference, thresholds, neutral?}(GeoDivergingScale). Classes below, exactly at and above a reference the host names; an optional closed neutral band{from, to}that contains the reference. The exact reference, and both ends of a band, are neutral; no tolerance or rounding. Up to four classes per side. Refused configurations (GEO_LAYER_OPTION_INVALID): a missing or non-finite reference, unordered or duplicate breaks, a break on the reference or inside the band, an empty, reversed or misplaced band, more than three breaks on one side. New tokens--geo-diverging-lower-1…4,--geo-diverging-neutral,--geo-diverging-upper-1…4(orange, neutral grey, blue; light and dark defaults). The legend names the reference; the class of an area says its side wherever it is shown; the details and copied text add the reference value; image exports carry the reference line. New translation keysgeo.column.reference,geo.legend.above,aboveRange,exactly,band,reference,geo.relation.*. The state fingerprint of a diverging layer carries its type and reference; the fingerprint of the other scales is unchanged. The CSV is unchanged. -
--geo-halo(D-27): the opposite tone paired with points, selection and focus, so each stands out from any fill; default the host's--popover. -
The optional starter collection
@workspace/active-geo/collections/world-50m(D-26):world50m(metadata: dataset identity, source and version, rights, boundary policy, bounds, build) andloadWorld50m(), which loads Natural Earth Admin 0 – Countries 1:50m (theme version 5.1.1, release v5.1.2; 242 areas keyed byADM0_A3) as a separate chunk on its first call and resolves to frozen area features. The runtime entry.does not reach it. Reproducible from pinned sources withscripts/build-world-50m.mjs. -
exportThemes(GeoExportThemes,GeoExportThemeMarker): a host whose colour modes are an attribute (for exampledata-theme) declares it, so an explicitlightordarkimage export reproduces that mode. A malformed declaration rejects the image export withEXPORT_INVALID_OPTION.
Fixed#
-
GEO-REV-001 (D-37): the focus indication of the map's controls reached 2.65:1 against the dark surface (the family's half-transparent ring), and an unavailable control's
opacityfaded its focus too. Every control now draws an opaque 2 px line in--geo-focus, 2 px outside it, at least 3:1 against both its sides in light and dark (measured on pixels); unavailable controls are dimmed in colours. The relaxed 2.6 test threshold is gone. -
Pressing a legend entry or a panel view button moved the layout: the pressed state made its text bold, so the entry grew by 2 to 4 px and pushed every entry after it (also right to left, and at a wrap boundary the rows below). A state no longer changes any size (D-34).
-
Image exports and fonts (R-19, D-32). A PNG of a page with a web font narrower than the system fallback showed legend entries on top of each other: the legend, title and notes were measured with the web font, which an image cannot use. An SVG opened where its named font is missing fell back to a wider face that nothing was measured for: labels covered each other and point symbols, legend entries overlapped (also with no web font at all, wherever the receiving default is wider). An image now names and measures only the stack an image can reach (the map's stack without the page's web fonts, a generic family last), every
<text>carries its measured width (textLength,lengthAdjust="spacingAndGlyphs"), and a label reserves a line box of at least 1.1 em above and 0.3 em below its baseline. A legend entry wider than the image wraps under its own swatch; a word longer than the line breaks between characters. Text stays text; no font is embedded or fetched; the CSV and the screen are unchanged. -
AGEO-REV-001: the CSV could merge two valid text keys. The formula neutralization of
keyprefixes an apostrophe to=1+1and leaves'=1+1alone, so both read back as onekeywith onekey_type. A new last columnkey_encodedcarries the exact typed key:v1:plus its JSON (v1:1,v1:"1",v1:"=1+1"), empty for a missing or invalid key.keystays the spreadsheet-safe presentation;key_encodedis the field to recover a key from (D-23, supplement of 2026-10-05). -
applyStateon a controlled selection reporteddelegatedwith reasoncontrolledeven when saved objects no longer existed and were left out of the request; it now reports the loss (objects-missingormalformed), as an uncontrolled selection does withadjusted. -
The navigation toolbar's arrow keys followed the DOM order in a right-to-left page, where the buttons are mirrored: ArrowRight moved the focus to the left. They now follow the direction, as the menu does.
-
Text on a right-to-left page or beside right-to-left words could reorder: a negative amount lost its minus sign to the neighbouring text, a legend class read as a positive number and a coordinate pair swapped its order. Every visible text run (tooltip, panel rows, legend, size key, level path) is now an isolated run, and a value isolated inside its "heading: value".
-
An explicit
darkexport of a host with an attribute-based theme mixed the host's light tokens with the package's dark defaults; withexportThemesit is the host's dark mode. Without the declaration an attribute theme is not reproduced (documented in API § 11). -
An explicit
lightordarkexport now honours host token rules that name an ancestor's element (main { … }); the export frame copied every ancestor as adiv. -
A level request that settled after the map was removed still ran the failure path and started a hint timer; only the newest request of a mounted map now ends the wait, reports a failure or moves the focus.
-
The level documentation overstated the guarantee: whether a stale answer is applied is the host's decision (
signalorrequestId), not the map's.
Changed#
-
Default changes (D-35), with migration:
- The panel starts closed (
panel.defaultCollapseddefaulttrue). Keep the list open from the start withpanel={{defaultCollapsed: false}}. The collapse toggle in the panel is gone: the Objects action above the map opens and closes it (data-testid="geo-panel-toggle"is now that action). A click on the map never opens a closed region (also not on a stack: the place list waits, the floating details give the count); Enter on the map still opens the place list. - The menu button is shown by default (
contextMenu.triggerdefault'button'), among the actions above the map instead of in the navigation toolbar.trigger: 'none'removes it. - The graticule defaults to on only for a map fitted to the globe (
equal-earth,fit: 'sphere'); a regional map has none unlessgraticuleis set. An explicitgraticulewins either way. - The four pan controls sit behind a Move disclosure in the same toolbar (one tab stop as before).
- Panel rows show name, layer and value; class, colour, reference, status and coordinates moved to the details and the data view.
- The font is
--geo-font-family, else--font-sans, else a system sans-serif (was inherited: the browser's serif on a page without styles). Labels and image exports measure the new font; on the hostless W50 world map in Chromium on Windows that is 6 labels at zoom 1 and 10 at 2.25 (serif: 6 and 11–12). Set--geo-font-familyto keep another font. - Legend entries are quiet (D-35): no outline at rest, the muted fill on hover, the fill and an inset line when pressed; one status line ("Highlighted: …" and "Clear highlight") ends the legend. Toggles are Active UI's
defaultvariant (wasoutline). - Tab order: the map's actions above the map come before it.
- The panel starts closed (
-
Removed translation keys
geo.panel.showandgeo.panel.hide(no collapse toggle any more). -
The map's own buttons and toggles follow the family's Active UI controls, taken over locally without a dependency (D-34):
Buttonoutlinesm(navigationicon-sm) for the navigation, "Clear highlight" and panel buttons,Toggleoutlinesmfor legend entries and the panel's views. They are 32 px high with 14 px medium text; pressed is the muted fill (no bolder text, no thicker border); focus is the family's ring; disabled is half opacity. A long legend label wraps inside its entry. The controls read the host's--card,--muted,--input,--ringand--radius, with defaults equal to the Active UI default theme. Forced colours keep a system-colour border on a pressed toggle and an outline on focus. -
Breaking (type only):
GeoAreaLabelContentis nowGeoLabelContent, one name for area and point labels (D-31); the old name is removed, with no alias. Migration: replace every import and use ofGeoAreaLabelContentwithGeoLabelContent; the union ('name' | 'value' | 'name-and-value') and every runtime behaviour are the same. (The VIS-4A entry under Added still names the type as it was introduced.) -
An exported image's text may use another font than the screen: the page's web fonts are no longer named in the file (D-32). Labels in an image can differ from before (the image's own stack and the line-box reserve); the screen is unchanged.
-
The label of an object the legend highlight dims is dimmed with it (not when the object is selected); before, an area label kept its full strength (D-31 point 6, amending D-30 point 5).
-
A context area (land or water, not a line) drawn above a labelled layer now covers its labels as an area of a higher layer does; before, context above was no obstacle (D-31 point 6).
-
With several labelled layers, the 300 objects a placement considers are shared max-min fairly over the layers; before, area candidates were taken in priority order over all layers, so an upper layer could use them all (D-31 point 5).
-
The visual base (D-27, VIS-1). Default colours: points are a dark core with a light halo (light theme) and a light core with a dark halo (dark theme), outside the blue class ramp; a hollow marker swaps the tones.
--geo-area-strokeis a mid tone at 1.5 px (was white, 0.75 px, in light and the dark ground in dark), so an area close to the water or land keeps its outline. Selection is drawn as its own outline above the area layer (a line over a halo; was the area's own 3 px stroke) and as a ring around a selected point (was a thicker symbol stroke), on screen, on a canvas and in the export. Focus is a dashed line over the halo, narrower than the selection line. A host that sets its own tokens keeps them; it should pair--geo-halowith its--geo-point,--geo-selectedand--geo-focus. -
"Clear highlight" stays in place and is
aria-disabled(focusable, inert) while nothing is highlighted; it was always active. -
A reprojection no longer computes every area's anchor: an anchor is computed when one object needs it (details, focus, a keyboard menu). Firefox W50 reprojection medians went from 181–205 ms to 159–175 ms in the package harness; Chromium did not measurably change.
[0.4.0] — 2026-10-04#
Added#
- A context menu (
contextMenu, default on) on the map and on rows of the object panel: right click, Shift+F10, the ContextMenu key, an optional menu button in the toolbar (trigger: 'button', for the map) and a long press on the map. The target is what a click would pick, SVG and Canvas points alike, with every object at that place; opening never changes the selection. The host's items (items, the product family's item vocabulary) come first; the map's groups (copy value, show all objects here, fit the map or show on map, export) can be switched off (features). A menu closes when its object is gone or hidden, the view moves or the level changes. refwithGeoMapHandle:exportAsPng,exportAsSvg,exportAsCsv,getState,applyState,requestLevel.- Exports built from the map's model under an explicit export theme (
lightby default,dark,current), of the current view or the whole map (extent: 'fitted'), with the visible layers, the selection, the legend, title, description andattribution. PNG at scale 1 to 4; true vector SVG (Canvas points as circles) withinexportLimits.svgMaxBytes(default 8 MiB); one CSV per area or point layer with every supplied object and value record and their statuses, or only the selected objects. One export at a time per map (EXPORT_BUSY);onExportCompleteandonExportError;GeoExportErrorwith its codes. - Restorable state:
GeoMapState(version 1: dataset, level, view with its geographic corners, typed selection, layer visibility, scale fingerprints, highlight), validated onapplyStatewith a report per field;datasetprop andisCompatiblefor the host's compatibility declaration.onSelectionChangereports a restored selection withsource: 'restore'. - Host-driven levels:
levelPathdraws a clickable level path above the map;onLevelRequestreceives{level, item?, source, requestId, signal}; a newer request aborts the older one'ssignal. - Words:
geo.menu.*,geo.export.*,geo.level.*,geo.keyboardHintMenuand the announcementsgeo.announce.copied,copyFailed,targetGone,exported,exportFailed,level,levelFailed. A host translator must provide them. - Dependencies:
@base-ui/react(the menu) and the inlined@workspace/active-utilsentries./menu-itemsand./serialization.
Fixed#
- A canvas point layer no longer catches the pointer over the whole stage: an area under it is hovered, clicked and tapped again.
- A tap within a point's touch tolerance above an area picks that point also when a lower point, covered by the area, contains the tap; before, the area won.
Changed#
- Symbols reached only through the tolerance are ordered by layer and draw order, as symbols that contain the pointer are, no longer by their distance to the pointer.
[0.3.0] — 2026-10-04#
Added#
- Canvas point layers:
renderer: 'svg' | 'canvas' | 'auto'per point layer, or{type: 'auto', maxSvgPoints}.auto(the default) draws SVG up to and including 1,000 renderable points and a canvas above. A canvas sits at its layer's place in the layer order and paints what SVG paints (tokens, light, dark, forced colours, device pixel ratio). A browser without a 2D context gets SVG and a diagnostic. - Picking by symbol containment at the drawn size, the same for SVG and Canvas: a containing symbol beats one reached through the tolerance (3 px mouse, 8 px touch), then the later layer and draw order.
- The object panel (
panel, default on;{placement: 'end' | 'start' | 'bottom', defaultCollapsed}): a searchable, collapsible, virtualized list of every object on the map, and of every object and value record that is not, with its reason. It is the map's data alternative when on. - Objects at one place: a click or tap on stacked symbols, or Enter on the map, lists every symbol there in the panel, top-most first.
- Pan and zoom (
navigation, default on): controls under the map (zoom, fit, pan without dragging), drag to pan, Ctrl or Command with the wheel to zoom, two fingers to pan and pinch on touch.viewport,defaultViewportandonViewportChange(GeoViewport {zoom, center}). visibleon every layer: a hidden layer is not drawn, hit, walked or listed, and still frames the map.- New words (
geo.panel.*,geo.nav.*,geo.announce.location,geo.announce.zoom,geo.announce.fitted,geo.keyboardHintPlace), a token--geo-active-row, and the runtime dependency@workspace/active-virtual(inlined in an artifact).
Changed#
- The data alternative is the object panel when it is on; the hidden tables remain only with
panel: false. - Points are hit through the symbol index, no longer by a 12 px DOM target: the target is the drawn symbol plus a small tolerance.
- The navigation controls sit between the map and its legend in the tab order.
- Details are measured whenever their content changes and stay invisible until measured, so they never cover their object.
- Details (tooltip) are hoverable and dismissible (WCAG 1.4.13): they stay open for 300 ms after the pointer leaves their object, and as long as the pointer rests on them; Escape closes them wherever the focus is, without changing the selection; they return only on a new trigger (another object, re-entering, a keyboard move). Entering another object replaces them at once.
[0.2.0] — 2026-10-04#
Added#
GeoMap: one map with typed layers.area(Polygon/MultiPolygon with values joined by key, classed by athreshold,quantizeorcategoryscale),context(land, water and lines;surface,fit) andpoint(SVG symbols, fixed or proportional by area).- Projections through d3-geo, named by the host:
equal-earth,conic-equal-area(parameters from the content by a fixed rule),mercator(opt-in). - Geometry contract: a declared ring convention per layer (
rfc7946orspherical); RFC 7946 winding and straight edges are normalized without changing what an area encloses; degenerate parts are excluded visibly; invalid or ambiguous geometry is rejected with a reason. - Typed identity per layer (
1and"1"differ), every holder of a repeated key excluded, one value record per area (ambiguous-recordotherwise), value and link statuses kept apart. - Selection (
selection,selectedItems,defaultSelectedItems,onSelectionChangewithpointer,keyboard,update), on the active-charts contract. - A legend that highlights a class or category and dims the rest; it never hides data, changes the selection or recomputes a scale.
GeoDefaults(locale,t,diagnostics) with the precedence ofChartDefaults;GeoConfigurationErrorwith stable codes.GeoValueSemantics, the shape ofChartValueSemantics, giving the same text as the charts.- Tokens for classes, categories, no-value hatching, selection, water lines and area edges, with light and dark defaults.
- Runtime dependencies
d3-geoandd3-scale.
Changed#
- Duplicate keys: every holder is now excluded (0.1.0 kept the first).
- The data alternative lists, per interactive layer, values, classes and status, plus objects not drawn and value records without an area.
tandlocalemay come fromGeoDefaults; a missing one throwsGeoConfigurationError.
Removed#
GeoPointMap,GeoPointMapProps,GeoPointandGeoBoundary. Migration: apointlayer (idbecomeskey) plus acontextlayer for the oldboundary, and aprojection(conic-equal-areafor a region).
[0.1.0] — 2026-10-03#
Added#
GeoPointMap: points at their latitude and longitude over an optional GeoJSONPolygonorMultiPolygonboundary and a graticule, in SVG. Equirectangular projection with the standard parallel at the centre of the extent; the width follows the container, the height is a prop.- Data truth: points with an invalid identifier, a duplicate identifier, a missing or out-of-range coordinate or a non-finite value are excluded with a reason, counted under the map and listed in the data alternative; nothing is defaulted, clamped or swapped.
- Interaction: tooltip anchored to the point, one tab stop with
aria-activedescendant, arrow keys, Home, End and Escape, a polite live region. - Data alternative: a visually hidden table of every drawn point and one of every excluded point with its reason.
- Theme tokens
--geo-water,--geo-land,--geo-boundary,--geo-graticule,--geo-point,--geo-focuswith complete light and.darkpackage defaults, and forced-colours styles. englishGeoTranslatorand the translation-key types;GeoDiagnosticand its sink.