Polish map workspace density
This commit is contained in:
@@ -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>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user