Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions configure/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

120 changes: 120 additions & 0 deletions configure/src/metaconfigs/tab-ui-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,126 @@
}
]
},
{
"name": "Floating Panels (Center Area)",
"components": [
{
"field": "panelSettings.floatingPanels",
"name": "Floating Panels",
"description": "Panels that float over the center map area. Each floating panel is anchored to one of six positions. At most one panel per position is allowed.",
"type": "objectarray",
"width": 24,
"object": [
{
"field": "id",
"name": "ID",
"description": "Unique identifier for this floating panel (e.g., 'timeline-panel', 'legend-float').",
"type": "text",
"width": 6
},
{
"field": "position",
"name": "Position",
"description": "Where the panel floats within the map area.",
"type": "dropdown",
"options": [
"float-top-left",
"float-top-center",
"float-top-right",
"float-bottom-left",
"float-bottom-center",
"float-bottom-right"
],
"default": "float-bottom-center",
"width": 6
},
{
"field": "hasHeader",
"name": "Has Header",
"description": "Whether the panel shows a title bar with control buttons.",
"type": "checkbox",
"width": 4,
"defaultChecked": false
},
{
"field": "title",
"name": "Title",
"description": "Display title shown in the header (only relevant when Has Header is enabled).",
"type": "text",
"width": 4
},
{
"field": "stateConstraints.allowedStates",
"name": "Allowed States",
"description": "Permitted states for this panel. Floating panels support collapsed (hidden) and expanded (visible) only.",
"type": "multiselect",
"options": ["collapsed", "expanded"],
"default": ["collapsed", "expanded"],
"width": 12
},
{
"field": "stateConstraints.defaultState",
"name": "Default State",
"description": "Initial visibility state when the panel is created.",
"type": "dropdown",
"options": ["collapsed", "expanded"],
"default": "expanded",
"width": 12
},
{
"field": "dimensions.defaultWidth",
"name": "Default Width",
"description": "Initial width of each tool card. Accepts any CSS unit (e.g. '300px', '40%', '20vw'). Leave empty to size to content.",
"type": "text",
"width": 4
},
{
"field": "dimensions.defaultHeight",
"name": "Default Height",
"description": "Initial height of each tool card. Accepts any CSS unit (e.g. '200px', '30%', '25vh'). Leave empty to size to content.",
"type": "text",
"width": 4
},
{
"field": "dimensions.minWidth",
"name": "Min Width",
"description": "Minimum width of each tool card. Accepts any CSS unit (e.g. '100px', '10%').",
"type": "text",
"width": 4
},
{
"field": "dimensions.maxWidth",
"name": "Max Width",
"description": "Maximum width of each tool card. Accepts any CSS unit (e.g. '600px', '50%').",
"type": "text",
"width": 4
},
{
"field": "dimensions.minHeight",
"name": "Min Height",
"description": "Minimum height of each tool card. Accepts any CSS unit (e.g. '100px', '10vh').",
"type": "text",
"width": 4
},
{
"field": "dimensions.maxHeight",
"name": "Max Height",
"description": "Maximum height of each tool card. Accepts any CSS unit (e.g. '400px', '40%', '50vh'). Useful for preventing top/bottom float zones from overlapping.",
"type": "text",
"width": 4
},
{
"field": "panelTools",
"name": "Tools",
"description": "Tool names to display in this floating panel. Each tool renders as its own card with a gap between cards. Must match tool names in the mission's tools array (e.g., 'Timeline', 'Legend').",
"type": "textarray",
"default": [],
"width": 24
}
]
}
]
},
{
"name": "Map Panel",
"components": [
Expand Down
53 changes: 53 additions & 0 deletions docs/MISSION_CONFIG_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,37 @@ Determines where the panel is located:
- **`"left"`**: Vertical panel on the left side
- **`"right"`**: Vertical panel on the right side

**Floating positions** (render inside the center map area as overlays):

- **`"float-top-left"`**: Floats in the top-left corner of the map
- **`"float-top-center"`**: Floats at the top-center of the map
- **`"float-top-right"`**: Floats in the top-right corner of the map
- **`"float-bottom-left"`**: Floats in the bottom-left corner of the map
- **`"float-bottom-center"`**: Floats at the bottom-center of the map
- **`"float-bottom-right"`**: Floats in the bottom-right corner of the map

**Floating panel rules**:
- Multiple tools can be assigned to one floating panel — each tool renders as its own card with a gap between cards
- Only **one** floating panel is allowed per position — the validator rejects configs that assign more than one panel (in `panels` or `floatingPanels`) to the same float position
- Supported states: `"collapsed"` and `"expanded"` only (`"iconified"` and `"focused"` are not supported)
- `layoutType` is ignored for floating panels
- `capabilities.resizable` is not supported for floating panels
- In `overlay` layout style the panel has rounded corners; in `compact` layout style it has sharp corners
- A gap between cards is applied automatically; no outer gap is added in overlay mode (the panel's own margin handles it)

**Floating panel `dimensions` fields**:

All values accept any CSS unit string (e.g. `"40%"`, `"50vh"`, `"300px"`) or a plain number (treated as `px`). Dimensions apply to each individual tool card.

| Field | Description |
|---|---|
| `defaultWidth` | Initial width (omit to size to content) |
| `defaultHeight` | Initial height (omit to size to content) |
| `minWidth` | Minimum width |
| `maxWidth` | Maximum width |
| `minHeight` | Minimum height |
| `maxHeight` | Maximum height — useful for capping top/bottom float zones so they never overlap (e.g. top panel `maxHeight: "40%"`, bottom panel `maxHeight: "50%"` leaves a 10% gap) |

### priority

Controls the order in which panels claim viewport space:
Expand Down Expand Up @@ -441,6 +472,28 @@ Full mission configuration with four panels:
"expandedSize": 300
},
"tools": ["Draw", "RasterTile", "Identifier"]
},
{
"id": "timeline-panel",
"position": "float-bottom-center",
"priority": 0,
"layoutType": "stacked",
"stateConstraints": {
"allowedStates": ["collapsed", "expanded"],
"defaultState": "expanded"
},
"panelTools": ["Timeline"]
},
{
"id": "legend-float-panel",
"position": "float-bottom-right",
"priority": 0,
"layoutType": "stacked",
"stateConstraints": {
"allowedStates": ["collapsed", "expanded"],
"defaultState": "expanded"
},
"panelTools": ["Legend"]
}
]
},
Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@
"cesium"
],
"scripts": {
"db:start": "docker-compose -f docker-compose.db.yml up -d",
"db:stop": "docker-compose -f docker-compose.db.yml down",
"db:logs": "docker-compose -f docker-compose.db.yml logs -f",
"prestart": "docker-compose -f docker-compose.db.yml up -d --wait",
"db:start": "docker compose -f docker-compose.db.yml up -d",
"db:stop": "docker compose -f docker-compose.db.yml down",
"db:logs": "docker compose -f docker-compose.db.yml logs -f",
"prestart": "docker compose -f docker-compose.db.yml up -d --wait",
"start": "node scripts/init-db.js && node scripts/server.js",
"start:no-docker": "node scripts/init-db.js && node scripts/server.js",
"start:prod": "node scripts/init-db.js && cross-env NODE_ENV=production node scripts/server.js",
Expand Down
10 changes: 9 additions & 1 deletion src/essence/Basics/PanelManager_/PanelManager_.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ToolOrientation, ToolMetadata } from '../ToolController_/types/tool';
import { PanelPosition, PanelState, PanelLayoutType, PANEL_STATE } from './types/layout';
import { PanelPosition, PanelState, PanelLayoutType, PANEL_STATE, FLOAT_POSITIONS } from './types/layout';
import { PanelConfig, PanelStateObject, PanelManager as PanelManagerInterface } from './types/panel';
import { mmgisAPI } from '../../mmgisAPI/mmgisAPI';

Expand Down Expand Up @@ -157,6 +157,14 @@ class PanelManager implements PanelManagerInterface {
throw new Error(`Panel with ID ${panelId} not found`);
}

if ((FLOAT_POSITIONS as Set<string>).has(panel.config.position) &&
(newState === PANEL_STATE.ICONIFIED || newState === PANEL_STATE.FOCUSED)) {
throw new Error(
`Float panels do not support '${newState}' state. ` +
`Only 'collapsed' and 'expanded' are allowed for float panel ${panelId}.`
);
}

if (!panel.config.stateConstraints.allowedStates.includes(newState)) {
throw new Error(`State transition to ${newState} is not allowed for panel ${panelId}`);
}
Expand Down
19 changes: 19 additions & 0 deletions src/essence/Basics/PanelManager_/types/layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,28 @@ export const PANEL_POSITION = {
LEFT: 'left',
RIGHT: 'right',
BOTTOM: 'bottom',
FLOAT_TOP_LEFT: 'float-top-left',
FLOAT_TOP_CENTER: 'float-top-center',
FLOAT_TOP_RIGHT: 'float-top-right',
FLOAT_BOTTOM_LEFT: 'float-bottom-left',
FLOAT_BOTTOM_CENTER: 'float-bottom-center',
FLOAT_BOTTOM_RIGHT: 'float-bottom-right',
} as const
export type PanelPosition = (typeof PANEL_POSITION)[keyof typeof PANEL_POSITION]

/**
* Set of all float positions for quick membership checks.
* Float panels render inside the center map area as overlays.
*/
export const FLOAT_POSITIONS = new Set([
PANEL_POSITION.FLOAT_TOP_LEFT,
PANEL_POSITION.FLOAT_TOP_CENTER,
PANEL_POSITION.FLOAT_TOP_RIGHT,
PANEL_POSITION.FLOAT_BOTTOM_LEFT,
PANEL_POSITION.FLOAT_BOTTOM_CENTER,
PANEL_POSITION.FLOAT_BOTTOM_RIGHT,
] as const)

/**
* Visual states of a panel:
* - collapsed: Hidden completely, takes no space in the viewport
Expand Down
14 changes: 14 additions & 0 deletions src/essence/Basics/PanelManager_/types/panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,20 @@ export interface PanelDimensions {
* - For left/right: this is the width
*/
expandedSize?: PanelSize;

/**
* CSS sizing for floating panels — applied directly as CSS properties on the panel element.
* Numbers are treated as px; strings are passed through as-is (e.g. "50%", "40vh", "300px").
*
* Distinct from PanelCapabilities.minSize/maxSize, which constrain drag-resize handles
* (single-axis, pixels only). These apply to both axes and support all CSS units.
*/
defaultWidth?: number | string;
defaultHeight?: number | string;
minWidth?: number | string;
maxWidth?: number | string;
minHeight?: number | string;
maxHeight?: number | string;
}

/**
Expand Down
Loading
Loading