Polish map workspace density
GeoIntel CI / docs-smoke (push) Has been cancelled
GeoIntel CI / contract-smoke (push) Has been cancelled

This commit is contained in:
Codex
2026-06-20 20:03:52 +02:00
parent 1549fd8d7a
commit c571dfe96c
7 changed files with 347 additions and 110 deletions
+135 -107
View File
@@ -50,6 +50,7 @@ export function MapWorkspace({
onSetMapLayerOpacity,
onSelectMapFeature,
}: MapWorkspaceProps): JSX.Element {
const selectedMapArea = areas.find((area) => area.id === selectedMapAreaId)
const featureProperties = selectedMapFeature?.properties ?? null
const featureSummaryEntries = featureProperties
? Object.entries(featureProperties)
@@ -58,7 +59,7 @@ export function MapWorkspace({
: []
return (
<section data-testid="map-workspace">
<section className="map-workspace-shell" data-testid="map-workspace">
<div className="panel-title-row">
<div>
<p className="eyebrow">Spatial review</p>
@@ -67,89 +68,110 @@ export function MapWorkspace({
<span className="count-pill">{mapFeatureCollection ? `${mapFeatureCount} features` : 'no layer'}</span>
</div>
<div className="map-toolbar">
<label>
Area
<select
value={selectedMapAreaId}
onChange={(event) => onSelectMapArea(event.target.value)}
disabled={areas.length === 0}
data-testid="map-area-select"
>
<option value="">No area</option>
{areas.map((area) => (
<option key={area.id} value={area.id}>
{area.name}
</option>
))}
</select>
</label>
<div className="layer-control-card">
<label className="checkbox-row">
<input
checked={areaLayerVisible}
disabled={!areaFeatureCollection}
type="checkbox"
onChange={(event) => onSetAreaLayerVisible(event.target.checked)}
data-testid="map-area-visible"
/>
AOI
</label>
<input
aria-label="Area opacity"
disabled={!areaFeatureCollection}
max="0.7"
min="0.05"
step="0.05"
type="range"
value={areaLayerOpacity}
onChange={(event) => onSetAreaLayerOpacity(Number(event.target.value))}
data-testid="map-area-opacity"
/>
<div className="map-context-summary" aria-label="Map layer status">
<div>
<span>Area of interest</span>
<strong>{selectedMapArea?.name ?? 'No area selected'}</strong>
<small>{areaFeatureCollection ? `${areaFeatureCount} AOI features loaded` : 'AOI overlay disabled'}</small>
</div>
<div className="layer-control-card">
<label className="checkbox-row">
<input
checked={mapLayerVisible}
disabled={!mapFeatureCollection}
type="checkbox"
onChange={(event) => onSetMapLayerVisible(event.target.checked)}
data-testid="map-layer-visible"
/>
Active layer
</label>
<input
aria-label="Layer opacity"
disabled={!mapFeatureCollection}
max="1"
min="0.05"
step="0.05"
type="range"
value={mapLayerOpacity}
onChange={(event) => onSetMapLayerOpacity(Number(event.target.value))}
data-testid="map-layer-opacity"
/>
</div>
<div className="map-status">
<div>
<span>Active layer</span>
<strong>{mapLayerLabel}</strong>
<span>{areaFeatureCollection ? `${areaFeatureCount} AOI loaded` : 'No AOI loaded'}</span>
<span>{mapFeatureCollection ? `${mapFeatureCount} features loaded` : 'No vector/result layer loaded'}</span>
<small>{mapLayerSourceLabel}</small>
</div>
<div>
<span>Feature state</span>
<strong>{mapFeatureCollection ? `${mapFeatureCount} rendered features` : 'No layer rendered'}</strong>
<small>{mapLayerProvenance}</small>
</div>
</div>
<div className="layer-provenance-rail" aria-label="Active map layer provenance">
<div>
<span>Layer source</span>
<strong>{mapLayerSourceLabel}</strong>
<div className="map-control-surface" aria-label="Map workspace controls">
<div className="map-toolbar">
<label>
Area
<select
value={selectedMapAreaId}
onChange={(event) => onSelectMapArea(event.target.value)}
disabled={areas.length === 0}
data-testid="map-area-select"
>
<option value="">No area</option>
{areas.map((area) => (
<option key={area.id} value={area.id}>
{area.name}
</option>
))}
</select>
</label>
<div className="layer-control-card">
<label className="checkbox-row">
<input
checked={areaLayerVisible}
disabled={!areaFeatureCollection}
type="checkbox"
onChange={(event) => onSetAreaLayerVisible(event.target.checked)}
data-testid="map-area-visible"
/>
AOI
</label>
<input
aria-label="Area opacity"
disabled={!areaFeatureCollection}
max="0.7"
min="0.05"
step="0.05"
type="range"
value={areaLayerOpacity}
onChange={(event) => onSetAreaLayerOpacity(Number(event.target.value))}
data-testid="map-area-opacity"
/>
</div>
<div className="layer-control-card">
<label className="checkbox-row">
<input
checked={mapLayerVisible}
disabled={!mapFeatureCollection}
type="checkbox"
onChange={(event) => onSetMapLayerVisible(event.target.checked)}
data-testid="map-layer-visible"
/>
Active layer
</label>
<input
aria-label="Layer opacity"
disabled={!mapFeatureCollection}
max="1"
min="0.05"
step="0.05"
type="range"
value={mapLayerOpacity}
onChange={(event) => onSetMapLayerOpacity(Number(event.target.value))}
data-testid="map-layer-opacity"
/>
</div>
<div className="map-status">
<strong>{mapLayerLabel}</strong>
<span>{areaFeatureCollection ? `${areaFeatureCount} AOI loaded` : 'No AOI loaded'}</span>
<span>{mapFeatureCollection ? `${mapFeatureCount} features loaded` : 'No vector/result layer loaded'}</span>
</div>
</div>
<div>
<span>Provenance</span>
<strong>{mapLayerProvenance}</strong>
</div>
<div>
<span>Draw state</span>
<strong>{mapFeatureCollection ? `${mapFeatureCount} rendered features` : 'No active vector or result layer'}</strong>
<div className="layer-provenance-rail" aria-label="Active map layer provenance">
<div>
<span>Layer source</span>
<strong>{mapLayerSourceLabel}</strong>
</div>
<div>
<span>Provenance</span>
<strong>{mapLayerProvenance}</strong>
</div>
<div>
<span>Draw state</span>
<strong>{mapFeatureCollection ? `${mapFeatureCount} rendered features` : 'No active vector or result layer'}</strong>
</div>
</div>
</div>
{!mapFeatureCollection ? (
<div className="empty-state map-empty-state">
<strong>No active vector or result layer</strong>
@@ -171,37 +193,43 @@ export function MapWorkspace({
)}
</div>
) : null}
<GeoMap
data={mapFeatureCollection}
areaData={areaFeatureCollection}
visible={mapLayerVisible}
opacity={mapLayerOpacity}
areaVisible={areaLayerVisible}
areaOpacity={areaLayerOpacity}
onFeatureSelect={onSelectMapFeature}
/>
<div className="feature-inspector">
<div className="panel-title-row">
<h3>Feature inspector</h3>
<span className="count-pill">{selectedMapFeature?.geometry?.type ?? 'none'}</span>
<div className="map-frame-surface">
<GeoMap
data={mapFeatureCollection}
areaData={areaFeatureCollection}
visible={mapLayerVisible}
opacity={mapLayerOpacity}
areaVisible={areaLayerVisible}
areaOpacity={areaLayerOpacity}
onFeatureSelect={onSelectMapFeature}
/>
</div>
<div className="map-inspection-surface">
<div className="feature-inspector">
<div className="panel-title-row">
<h3>Feature inspector</h3>
<span className="count-pill">{selectedMapFeature?.geometry?.type ?? 'none'}</span>
</div>
{selectedMapFeature ? (
<>
{featureSummaryEntries.length > 0 ? (
<div className="feature-summary-grid" aria-label="Selected feature summary">
{featureSummaryEntries.map(([key, value]) => (
<div className="feature-property-chip" key={key}>
<span>{key}</span>
<strong>{String(value)}</strong>
</div>
))}
</div>
) : null}
<pre className="job-result">{JSON.stringify(selectedMapFeature.properties ?? {}, null, 2)}</pre>
</>
) : (
<p className="muted">Click a visible map feature to inspect its properties.</p>
)}
</div>
{selectedMapFeature ? (
<>
{featureSummaryEntries.length > 0 ? (
<div className="feature-summary-grid" aria-label="Selected feature summary">
{featureSummaryEntries.map(([key, value]) => (
<div className="feature-property-chip" key={key}>
<span>{key}</span>
<strong>{String(value)}</strong>
</div>
))}
</div>
) : null}
<pre className="job-result">{JSON.stringify(selectedMapFeature.properties ?? {}, null, 2)}</pre>
</>
) : (
<p className="muted">Click a visible map feature to inspect its properties.</p>
)}
</div>
</section>
)
+108 -3
View File
@@ -2007,12 +2007,96 @@ button.entity-card {
overflow-wrap: anywhere;
}
.map-toolbar {
.map-workspace-shell {
display: grid;
gap: 0.75rem;
}
.map-context-summary {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
gap: 0.6rem;
}
.map-context-summary > div {
min-width: 0;
border: 1px solid var(--line);
border-left: 4px solid var(--accent);
border-radius: 8px;
padding: 0.68rem 0.78rem;
background: linear-gradient(180deg, #ffffff, #f7fbf8);
}
.map-context-summary span,
.map-context-summary small {
display: block;
}
.map-context-summary span {
color: var(--muted);
font-size: 0.68rem;
font-weight: 850;
letter-spacing: 0.05em;
text-transform: uppercase;
}
.map-context-summary strong {
display: block;
margin-top: 0.2rem;
overflow: hidden;
color: var(--ink);
font-size: 0.98rem;
line-height: 1.22;
text-overflow: ellipsis;
white-space: nowrap;
}
.map-context-summary small {
margin-top: 0.25rem;
overflow: hidden;
color: var(--muted);
font-size: 0.78rem;
line-height: 1.25;
text-overflow: ellipsis;
white-space: nowrap;
}
.map-control-surface,
.map-frame-surface,
.map-inspection-surface {
min-width: 0;
border: 1px solid var(--line);
border-radius: 8px;
padding: 0.72rem;
background: #ffffff;
}
.map-control-surface {
display: grid;
gap: 0.68rem;
}
.map-frame-surface {
padding: 0.52rem;
background: #f7fbf8;
}
.map-frame-surface .map-container {
margin: 0;
}
.map-inspection-surface .feature-inspector {
margin-top: 0;
border-top: 0;
padding-top: 0;
}
.map-workspace-shell .map-toolbar {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
gap: 0.75rem;
align-items: end;
margin-bottom: 0.85rem;
margin-bottom: 0;
}
.layer-control-card {
@@ -2049,7 +2133,7 @@ button.entity-card {
display: grid;
grid-template-columns: 0.85fr minmax(0, 1.5fr) 0.85fr;
gap: 0.65rem;
margin-bottom: 0.85rem;
margin-bottom: 0;
}
.layer-provenance-rail > div,
@@ -2645,6 +2729,27 @@ button.entity-card {
gap: 0.55rem;
}
.map-workspace-shell .map-context-summary {
grid-template-columns: repeat(auto-fit, minmax(8.25rem, 1fr));
gap: 0.5rem;
}
.map-workspace-shell .map-toolbar {
grid-template-columns: repeat(auto-fit, minmax(8.25rem, 1fr));
gap: 0.5rem;
}
.map-workspace-shell .layer-provenance-rail {
grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));
gap: 0.5rem;
}
.map-control-surface,
.map-frame-surface,
.map-inspection-surface {
padding: 0.58rem;
}
.layer-control-card {
padding: 0.52rem 0.58rem;
}