feat(foundry): close the operational map data loop
This commit is contained in:
@@ -72,16 +72,6 @@
|
||||
"requiredBehavior": ["selected state", "drag state", "assignees", "dates", "quick actions"],
|
||||
"reasonNotBaseline": "The visual pattern is mature, but task-domain fields must be separated from the reusable card shell."
|
||||
},
|
||||
{
|
||||
"id": "toast",
|
||||
"classification": "missing-primitive",
|
||||
"priority": "P2",
|
||||
"referenceSources": ["task-manager"],
|
||||
"expectedPackage": "@nodedc/ui-react",
|
||||
"dependsOn": ["status-badge", "button"],
|
||||
"requiredBehavior": ["stack", "timeout", "persistent error", "action", "screen-reader announcement"],
|
||||
"reasonNotBaseline": "No cross-application contract has been audited yet."
|
||||
},
|
||||
{
|
||||
"id": "tooltip",
|
||||
"classification": "missing-primitive",
|
||||
|
||||
@@ -5,16 +5,17 @@
|
||||
"id": "glass-surface",
|
||||
"status": "baseline",
|
||||
"package": "@nodedc/ui-react",
|
||||
"exports": ["GlassSurface", "GlassMaterialSurface"],
|
||||
"domContract": ["nodedc-glass", "nodedc-glass-material", "nodedc-material-rim"],
|
||||
"summary": "Matte application surfaces plus the canonical Engine V4 glass material for modal and Inspector shells.",
|
||||
"anatomy": ["theme-provided surface", "modal tint", "gradient rim", "backdrop blur/saturation/brightness", "content"],
|
||||
"variants": ["default", "strong", "soft"],
|
||||
"exports": ["GlassSurface", "GlassMaterialSurface", "MapGlassSurface"],
|
||||
"domContract": ["nodedc-glass", "nodedc-glass-material", "nodedc-map-glass", "nodedc-material-rim"],
|
||||
"summary": "Matte application surfaces, the Engine V4 modal material and the light translucent surface used over Cesium imagery.",
|
||||
"anatomy": ["theme-provided surface", "modal tint", "map-only light translucency", "gradient rim", "backdrop blur/saturation/brightness", "content"],
|
||||
"variants": ["default", "strong", "soft", "map"],
|
||||
"rules": [
|
||||
"Geometry is invariant across application themes.",
|
||||
"A material rim is a glass highlight, not a hard product-colored border.",
|
||||
"Nested surfaces must use a softer tone to avoid card-inside-card noise.",
|
||||
"GlassyMaterialSurface is restricted to modal Windows and the draggable Inspector."
|
||||
"GlassyMaterialSurface is restricted to modal Windows and the draggable Inspector.",
|
||||
"MapGlassSurface is restricted to overlays over Cesium/map imagery and must match the map Toolbar translucency."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -337,6 +338,21 @@
|
||||
"domContract": ["nodedc-status"],
|
||||
"summary": "Semantic status pill whose tone is independent from the application accent."
|
||||
},
|
||||
{
|
||||
"id": "toast",
|
||||
"status": "baseline",
|
||||
"package": "@nodedc/ui-react",
|
||||
"exports": ["ToastCard", "ToastStack", "ToastItem", "ToastTone"],
|
||||
"domContract": ["nodedc-toast-viewport", "nodedc-toast", "nodedc-toast__icon", "nodedc-toast__copy"],
|
||||
"summary": "Tasker-derived non-blocking status notification with a bottom-right glass stack.",
|
||||
"variants": ["success", "error", "warning", "info", "loading"],
|
||||
"behavior": ["controlled items", "optional auto-dismiss", "manual dismiss", "portal viewport", "polite live region"],
|
||||
"rules": [
|
||||
"Applications own operation state and message copy; ToastStack owns viewport geometry and dismissal timing.",
|
||||
"Loading notifications remain until the operation updates or dismisses them.",
|
||||
"Status notifications never replace a blocking confirmation modal."
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "drag-drop",
|
||||
"status": "baseline",
|
||||
|
||||
@@ -14,6 +14,39 @@
|
||||
"no_position"
|
||||
],
|
||||
"removeMode": "canonical-tombstone-or-snapshot-rebase"
|
||||
},
|
||||
{
|
||||
"id": "map-moving-object-current-v2",
|
||||
"version": "2.0.0",
|
||||
"dataProductId": "fleet.positions.current.v2",
|
||||
"productVersion": "2.0.0",
|
||||
"staleAfterMs": 60000,
|
||||
"terminalStatuses": [
|
||||
"inactive",
|
||||
"no-position",
|
||||
"no_position"
|
||||
],
|
||||
"removeMode": "canonical-tombstone-or-snapshot-rebase"
|
||||
},
|
||||
{
|
||||
"id": "map-moving-object-current-v4",
|
||||
"version": "4.0.0",
|
||||
"dataProductId": "fleet.positions.current.v4",
|
||||
"productVersion": "4.0.0",
|
||||
"staleAfterMs": null,
|
||||
"terminalStatuses": [
|
||||
"inactive"
|
||||
],
|
||||
"statusContract": {
|
||||
"attribute": "signal_state",
|
||||
"allowedValues": [
|
||||
"active",
|
||||
"inactive"
|
||||
],
|
||||
"missing": "reject",
|
||||
"freshness": "none"
|
||||
},
|
||||
"removeMode": "canonical-tombstone-or-snapshot-rebase"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
{
|
||||
"schemaVersion": "0.1.0",
|
||||
"fixtureId": "map.operational",
|
||||
"template": { "id": "map", "version": "0.1.0" },
|
||||
"title": "Operational spatial scene",
|
||||
"description": "Minimal provider-neutral scene covering the reusable spatial entities already proven in NODE.DC Engine.",
|
||||
"state": "ready",
|
||||
"viewport": {
|
||||
"center": [37.6176, 55.7558, 250],
|
||||
"heading": 15,
|
||||
"pitch": -42,
|
||||
"range": 18000
|
||||
},
|
||||
"capabilities": ["camera", "base-layer", "buildings", "grid", "labels", "pins", "moving-objects", "traces", "routes", "tracks", "stations", "zones", "selection"],
|
||||
"styleProfiles": [
|
||||
{ "id": "layer.base.neutral", "kind": "layer", "variant": "neutral-dark", "opacity": 1 },
|
||||
{ "id": "layer.buildings.default", "kind": "layer", "variant": "extruded-neutral", "opacity": 0.86 },
|
||||
{ "id": "grid.planetary.major", "kind": "grid", "variant": "major", "color": "#8f72dc", "opacity": 0.48, "width": 1 },
|
||||
{ "id": "label.place.large", "kind": "label", "variant": "place-large", "color": "#ffffff", "opacity": 1, "size": 28 },
|
||||
{ "id": "label.entity.compact", "kind": "label", "variant": "entity-compact", "color": "#ffffff", "opacity": 0.92, "size": 13 },
|
||||
{ "id": "label.station.medium", "kind": "label", "variant": "station-medium", "color": "#ffffff", "opacity": 0.95, "size": 16 },
|
||||
{
|
||||
"id": "pin.transport.active",
|
||||
"kind": "pin",
|
||||
"variant": "transport-active",
|
||||
"color": "#ff2f92",
|
||||
"opacity": 1,
|
||||
"size": 8,
|
||||
"pinPresentation": {
|
||||
"variant": "elevated-spike",
|
||||
"stemHeightMeters": 120,
|
||||
"headSizePx": 8,
|
||||
"stemWidthPx": 2,
|
||||
"outlineColor": "#0c0d12",
|
||||
"outlineOpacity": 0.6,
|
||||
"outlineWidthPx": 1,
|
||||
"labelOffsetX": 10,
|
||||
"labelOffsetY": 0,
|
||||
"pinHideCameraHeightMeters": 120000,
|
||||
"labelHideCameraHeightMeters": 85000
|
||||
}
|
||||
},
|
||||
{ "id": "pin.station.metro", "kind": "pin", "variant": "station-metro", "color": "#8f72dc", "opacity": 1, "size": 26 },
|
||||
{ "id": "pin.station.rail", "kind": "pin", "variant": "station-rail", "color": "#ffffff", "opacity": 1, "size": 22 },
|
||||
{ "id": "line.route.primary", "kind": "line", "variant": "route-primary", "color": "#ff2f92", "opacity": 0.9, "width": 4 },
|
||||
{ "id": "line.track.rail", "kind": "line", "variant": "track-rail", "color": "#8f72dc", "opacity": 0.8, "width": 2 },
|
||||
{ "id": "fill.zone.operational", "kind": "fill", "variant": "operational-zone", "color": "#ff2f92", "opacity": 0.22 }
|
||||
],
|
||||
"scene": {
|
||||
"layers": [
|
||||
{ "id": "base.default", "type": "base", "visible": true, "mode": "surface", "styleProfileId": "layer.base.neutral" },
|
||||
{ "id": "buildings.default", "type": "buildings", "visible": true, "mode": "volume", "styleProfileId": "layer.buildings.default", "visibility": { "minRange": 0, "maxRange": 70000 } },
|
||||
{
|
||||
"id": "grid.planetary",
|
||||
"type": "grid",
|
||||
"visible": true,
|
||||
"mode": "graticule",
|
||||
"styleProfileId": "grid.planetary.major",
|
||||
"lodLevels": [
|
||||
{ "level": 0, "minRange": 3000000, "maxRange": 50000000 },
|
||||
{ "level": 1, "minRange": 700000, "maxRange": 3000000 },
|
||||
{ "level": 2, "minRange": 180000, "maxRange": 700000 },
|
||||
{ "level": 3, "minRange": 40000, "maxRange": 180000 },
|
||||
{ "level": 4, "minRange": 0, "maxRange": 40000 }
|
||||
]
|
||||
}
|
||||
],
|
||||
"places": [
|
||||
{
|
||||
"id": "place.moscow",
|
||||
"name": "Москва",
|
||||
"position": [37.6176, 55.7558, 220],
|
||||
"label": { "text": "МОСКВА", "styleProfileId": "label.place.large" },
|
||||
"visibility": { "minRange": 100000, "maxRange": 50000000 }
|
||||
}
|
||||
],
|
||||
"movingObjects": [
|
||||
{
|
||||
"id": "vehicle.tram.017",
|
||||
"objectType": "tram",
|
||||
"status": "active",
|
||||
"position": [37.6242, 55.7604, 4],
|
||||
"heading": 124,
|
||||
"pinStyleProfileId": "pin.transport.active",
|
||||
"label": { "text": "ТМ 17", "styleProfileId": "label.entity.compact" },
|
||||
"trace": [[37.615, 55.756, 4], [37.619, 55.758, 4], [37.6242, 55.7604, 4]]
|
||||
},
|
||||
{
|
||||
"id": "vehicle.rail.mcd2",
|
||||
"objectType": "rail",
|
||||
"status": "delayed",
|
||||
"position": [37.579, 55.769, 4],
|
||||
"heading": 82,
|
||||
"pinStyleProfileId": "pin.transport.active",
|
||||
"label": { "text": "МЦД-2 · +3 мин", "styleProfileId": "label.entity.compact" }
|
||||
}
|
||||
],
|
||||
"stations": [
|
||||
{
|
||||
"id": "station.metro.tverskaya",
|
||||
"stationType": "metro",
|
||||
"position": [37.604, 55.7647, -20],
|
||||
"pinStyleProfileId": "pin.station.metro",
|
||||
"label": { "text": "Тверская", "styleProfileId": "label.station.medium" }
|
||||
},
|
||||
{
|
||||
"id": "station.rail.rizhskaya",
|
||||
"stationType": "rail",
|
||||
"position": [37.6328, 55.7927, 4],
|
||||
"pinStyleProfileId": "pin.station.rail",
|
||||
"label": { "text": "Рижская", "styleProfileId": "label.station.medium" }
|
||||
}
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"id": "route.tram.017",
|
||||
"coordinates": [[37.595, 55.748, 4], [37.607, 55.754, 4], [37.6242, 55.7604, 4], [37.64, 55.772, 4]],
|
||||
"styleProfileId": "line.route.primary",
|
||||
"label": { "text": "Маршрут 17", "styleProfileId": "label.entity.compact" }
|
||||
}
|
||||
],
|
||||
"tracks": [
|
||||
{
|
||||
"id": "track.rail.north-east",
|
||||
"coordinates": [[37.579, 55.769, 4], [37.606, 55.781, 4], [37.6328, 55.7927, 4]],
|
||||
"styleProfileId": "line.track.rail"
|
||||
}
|
||||
],
|
||||
"zones": [
|
||||
{
|
||||
"id": "zone.depot.control",
|
||||
"geometry": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [[[37.61, 55.752], [37.632, 55.752], [37.632, 55.765], [37.61, 55.765], [37.61, 55.752]]]
|
||||
},
|
||||
"styleProfileId": "fill.zone.operational",
|
||||
"label": { "text": "Операционная зона", "styleProfileId": "label.entity.compact" }
|
||||
}
|
||||
]
|
||||
},
|
||||
"selection": { "entityId": "vehicle.tram.017" },
|
||||
"acceptance": [
|
||||
{ "id": "scene.provider-neutral", "expect": ["Scene contains no renderer credentials or provider-specific object types."] },
|
||||
{ "id": "scene.shared-label-language", "expect": ["Places, vehicles, stations, routes and zones resolve labels through shared style profiles."] },
|
||||
{ "id": "scene.grid-lod", "expect": ["Grid changes density through five non-overlapping range bands without disappearing at a boundary."] },
|
||||
{ "id": "scene.selection", "expect": ["Selection resolves to an existing scene entity and can open the canonical Inspector."] }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
{
|
||||
"schemaVersion": "nodedc.map-presentation-profiles/v1",
|
||||
"profiles": [
|
||||
{
|
||||
"id": "map.moving-object.operational.default",
|
||||
"version": "1.3.0",
|
||||
"title": "Флот",
|
||||
"semanticTypes": ["map.moving_object"],
|
||||
"label": {
|
||||
"mode": "subject_id",
|
||||
"fields": ["display_name", "label", "name", "title"],
|
||||
"fontWeight": 600,
|
||||
"sizePx": 18,
|
||||
"color": "#f5f5f5",
|
||||
"outlineColor": "#0c0d12",
|
||||
"outlineWidthPx": 1,
|
||||
"backgroundColor": "#0c0d12",
|
||||
"backgroundOpacity": 0.86,
|
||||
"paddingX": 8,
|
||||
"paddingY": 5,
|
||||
"maxLength": 48,
|
||||
"offsetX": 15,
|
||||
"offsetY": 10,
|
||||
"hideCameraHeightMeters": 70000
|
||||
},
|
||||
"target": {
|
||||
"variant": "elevated-spike",
|
||||
"stemHeightMeters": 1500,
|
||||
"headSizePx": 9,
|
||||
"stemWidthPx": 5,
|
||||
"outlineColor": "#0c0d12",
|
||||
"outlineOpacity": 0.6,
|
||||
"outlineWidthPx": 1,
|
||||
"hideCameraHeightMeters": 100000
|
||||
},
|
||||
"facets": [
|
||||
{
|
||||
"id": "availability",
|
||||
"field": "availability_state",
|
||||
"label": "Связь",
|
||||
"filterable": true,
|
||||
"counter": true,
|
||||
"values": [
|
||||
{ "value": "online", "label": "Онлайн", "order": 0 },
|
||||
{ "value": "offline", "label": "Офлайн", "order": 1 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "motion",
|
||||
"field": "motion_state",
|
||||
"label": "Движение",
|
||||
"filterable": true,
|
||||
"counter": true,
|
||||
"values": [
|
||||
{ "value": "moving", "label": "В движении", "order": 0 },
|
||||
{ "value": "stationary", "label": "Стоят", "order": 1 }
|
||||
]
|
||||
}
|
||||
],
|
||||
"styles": [
|
||||
{ "id": "online", "color": "#86fdb8", "opacity": 0.9 },
|
||||
{ "id": "offline", "color": "#c2c2c2", "opacity": 0.7 },
|
||||
{ "id": "moving", "color": "#6fb5fb", "opacity": 0.9 },
|
||||
{ "id": "stationary", "color": "#ffd485", "opacity": 0.9 }
|
||||
],
|
||||
"classes": [
|
||||
{
|
||||
"id": "availability-offline",
|
||||
"label": "Офлайн",
|
||||
"priority": 400,
|
||||
"match": [{ "field": "availability_state", "equals": "offline" }],
|
||||
"styleId": "offline",
|
||||
"renderable": true
|
||||
},
|
||||
{
|
||||
"id": "motion-moving",
|
||||
"label": "В движении",
|
||||
"priority": 300,
|
||||
"match": [{ "field": "motion_state", "equals": "moving" }],
|
||||
"styleId": "moving",
|
||||
"renderable": true
|
||||
},
|
||||
{
|
||||
"id": "motion-stationary",
|
||||
"label": "Стоят",
|
||||
"priority": 200,
|
||||
"match": [{ "field": "motion_state", "equals": "stationary" }],
|
||||
"styleId": "stationary",
|
||||
"renderable": true
|
||||
},
|
||||
{
|
||||
"id": "availability-online",
|
||||
"label": "Онлайн",
|
||||
"priority": 100,
|
||||
"match": [{ "field": "availability_state", "equals": "online" }],
|
||||
"styleId": "online",
|
||||
"renderable": true
|
||||
}
|
||||
],
|
||||
"defaultClassId": "motion-stationary",
|
||||
"sort": [
|
||||
{ "field": "availability_state", "order": ["online", "offline"] },
|
||||
{ "field": "motion_state", "order": ["moving", "stationary"] }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -36,7 +36,6 @@
|
||||
"contracts": {
|
||||
"fixtureSchema": "registry/schemas/map-scene-fixture-v0.1.schema.json",
|
||||
"acceptanceFixtures": [
|
||||
"registry/fixtures/map/map-operational-v0.1.json",
|
||||
"registry/fixtures/map/map-empty-offline-v0.1.json"
|
||||
],
|
||||
"lodPolicySchema": "registry/schemas/map-lod-policy-v0.1.schema.json",
|
||||
|
||||
@@ -54,5 +54,6 @@
|
||||
"designProfileSchema": "schemas/design-profile-v0.1.schema.json",
|
||||
"mapSceneFixtureSchema": "schemas/map-scene-fixture-v0.1.schema.json",
|
||||
"mapLodPolicySchema": "schemas/map-lod-policy-v0.1.schema.json",
|
||||
"mapPresentationProfileRegistry": "map-presentation-profiles.json",
|
||||
"operatingRule": "Check this registry before creating a NODE.DC interface element. Reuse or extend the canonical export instead of creating an application-local copy."
|
||||
}
|
||||
|
||||
@@ -64,6 +64,91 @@
|
||||
"features": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "type": "boolean" }
|
||||
},
|
||||
"layout": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"map": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"dataProductBindings": {
|
||||
"type": "array",
|
||||
"maxItems": 64,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": { "type": "string", "minLength": 1 },
|
||||
"displayName": { "type": "string", "minLength": 1, "maxLength": 120 },
|
||||
"order": { "type": "integer", "minimum": 0, "maximum": 10000 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"subjectStates": {
|
||||
"type": "array",
|
||||
"maxItems": 64,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["bindingId", "visible", "filters", "window"],
|
||||
"properties": {
|
||||
"bindingId": { "type": "string", "minLength": 1 },
|
||||
"visible": { "type": "boolean" },
|
||||
"filters": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"items": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"window": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["open", "rect", "maximized", "zIndex"],
|
||||
"properties": {
|
||||
"open": { "type": "boolean" },
|
||||
"rect": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["x", "y", "width", "height"],
|
||||
"properties": {
|
||||
"x": { "type": "number" },
|
||||
"y": { "type": "number" },
|
||||
"width": { "type": "number", "minimum": 200 },
|
||||
"height": { "type": "number", "minimum": 160 }
|
||||
}
|
||||
},
|
||||
"maximized": { "type": "boolean" },
|
||||
"zIndex": { "type": "integer", "minimum": 1 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"designOverrides": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"map": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"settings": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "type": ["string", "number", "boolean"] }
|
||||
},
|
||||
"presentationProfiles": {
|
||||
"type": "array",
|
||||
"items": { "type": "object" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,6 +101,12 @@
|
||||
"lensCount": { "type": "integer", "minimum": 1, "maximum": 12 },
|
||||
"autoHide": { "type": "boolean" }
|
||||
}
|
||||
},
|
||||
"pageTypes": {
|
||||
"type": "object",
|
||||
"maxProperties": 64,
|
||||
"propertyNames": { "pattern": "^[a-z0-9-]+@[0-9]+\\.[0-9]+\\.[0-9]+$" },
|
||||
"additionalProperties": { "$ref": "#/$defs/mapPageFragment" }
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -129,6 +135,25 @@
|
||||
"fieldOpacity": { "$ref": "#/$defs/percent" },
|
||||
"nestedHex": { "$ref": "#/$defs/hex" }
|
||||
}
|
||||
},
|
||||
"mapPageFragment": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schemaVersion", "templateId", "templateVersion", "settings", "presentationProfiles"],
|
||||
"properties": {
|
||||
"schemaVersion": { "const": 1 },
|
||||
"templateId": { "const": "map" },
|
||||
"templateVersion": { "type": "string", "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$" },
|
||||
"settings": {
|
||||
"type": "object",
|
||||
"minProperties": 1,
|
||||
"additionalProperties": { "type": ["string", "number", "boolean"] }
|
||||
},
|
||||
"presentationProfiles": {
|
||||
"type": "array",
|
||||
"items": { "type": "object" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user