Appearance
Compose panels
Panels turn an application into a task surface. Every panel has a stable identity and a registered variant; shared configuration controls its title, visibility, size, and events while the variant owns its data and interaction model.
Use a single root panel for a focused tool. Use group or tabs when the user must coordinate several panels. Do not use panel nesting merely to create spacing: nesting changes visibility, event, state, and sizing ownership.
Prerequisites and ownership
- The application must provide
jsonData.panel. - Every panel needs a unique
idwithin the application. Selection registries, request state, action dispatch, tabs, and event lookups use it as an identity. typemust match a registered renderer even though the base TypeScript type declares it optional.- A variant may require API data, a selected object, package settings, or a parent container. Read its guide before adding shared actions or events.
Common panel parameters
json
{
"id": "orders",
"type": "table",
"ui": {
"title": { "en": "Orders", "ru": "Заказы" },
"emptyMessage": {
"en": "No orders match the current filter.",
"ru": "Нет заказов по текущему фильтру."
}
},
"condition": {
"scenario": "variables",
"variables": {
"ordersVisible": {
"value": true,
"compare": "EQUAL",
"cleanOnUnmounted": true
}
}
},
"panelLayout": {
"size": { "value": 60, "min": 25 }
},
"events": []
}| Capability | Fields | Runtime model |
|---|---|---|
| Identity and dispatch | id, type | id scopes state; type selects the renderer. |
| User-facing state | ui.title, ui.emptyMessage | Localized title; empty-message support is variant-dependent. |
| Conditional visibility | condition | Evaluated by parent composition and by the renderer lifecycle. |
| Container sizing | panelLayout.size.value, min | PrimeVue Splitter percentages for a child of a group; they are not CSS pixel widths. |
| Cross-panel behavior | events | Named occurrences run ordered event actions. |
Visibility and conditions
Three condition scenarios are declared:
| Scenario | Visible when | Important interaction |
|---|---|---|
default | Consumer-specific | The shared filtering helper does not explicitly accept it; use only where the variant guide proves behavior. |
variables | Every configured variable comparison passes | cleanOnUnmounted registers that variable for cleanup when the conditioned panel unmounts. |
objectSelected | The referenced event exists and contains addSelectedObjectToRequest | The event ID is the data dependency; an arbitrary selection event is insufficient. |
Group and Tabs filter their child collections, so hiding a child can unmount it and clean temporary variables. A Condition panel chooses among its own children with a separate last-match model. Do not assume every consumer interprets a condition identically.
Sizing and composition
panelLayout.size.value and min are meaningful for children of a group. Both are percentages passed to PrimeVue Splitter: value is the child's initial share and min is its minimum share. Configure sibling value shares relative to a total of 100; for example, 65 and 35 start at 65% and 35%. min constrains the same percentage space and must not be expressed in pixels. A nested variant can still own an internal scrollport or content constraint. When percentage minima conflict with each other or with the available viewport, the container's Splitter layout wins over a child's preferred share.
ui.emptyMessage is not consistently consumed by every legacy renderer. Table uses it; some variants show a fixed empty state or no empty text. Treat the variant guide as authoritative.
Registered variants
The current PanelType enum and PanelRenderer map contain 16 variants.
| Type | User problem | Guide |
|---|---|---|
group | Lay out ordered child panels in rows or columns | Group |
message | Show a localized heading and plain text | Message |
table | Query and act on flat object collections | Table |
tree | Navigate linked object hierarchies | Tree |
tabs | Switch between conditioned child panels | Tabs |
condition | Choose one child from condition matches | Condition |
attributes | Inspect or edit one event-selected object | Attributes |
gantt | Visualize an already-loaded schedule | Gantt |
files | List or preview datasets for a selected object | Files |
calendar | Show an APS calendar for a selected object | Calendar |
bpmn | View and mutate a method-backed workflow graph | BPMN |
scheduleOperations | Populate the Gantt unscheduled-operations drawer | Schedule Operations |
metaTypesCrud | Manage MetaTypes with package-owned UI | MetaTypes CRUD |
coreApps | Host a built-in administration surface | Core Apps |
tiptapPanel | Edit rich text in standalone or object-bound mode | Tiptap Panel |
textEditorPanel | Select the supported editor renderer, currently Tiptap only | Text Editor Panel |
Defaults, precedence, and interactions
- The application supplies the root.
- A composition variant determines which children are mounted and how their size hints are interpreted.
- The child variant chooses its request and internal state model.
- Events publish selection or mutations under stable IDs.
- Conditions and dependent requests consume those IDs.
- Panel and item actions run through the dispatcher for their current surface.
This order matters. A hidden or unmounted source panel cannot keep publishing new selections, and a dependent panel with the wrong event ID cannot resolve its object.
Minimal valid configuration
json
{
"id": "status",
"type": "message",
"ui": {
"title": "Status"
},
"content": {
"text": "The application is ready."
}
}Realistic composition example
This group allocates more space to the table and shows the detail panel only after selection. It is complete only when the ProductionOrder MetaType exists.
json
Open a checked-in multi-panel selection and detail fixture Open a vertical group with explicit child sizing {
"id": "orders-root",
"type": "group",
"groupLayout": { "type": "rows", "resizable": true },
"panels": [
{
"id": "orders",
"type": "table",
"panelLayout": { "size": { "value": 65, "min": 40 } },
"request": { "metaTypeName": "ProductionOrder" },
"events": [
{
"id": "orders-selected",
"name": "onObjectSelect",
"actions": [{ "name": "addSelectedObjectToRequest" }]
}
],
"columns": [{ "attribute": "name", "label": "Order" }]
},
{
"id": "details",
"type": "attributes",
"panelLayout": { "size": { "value": 35, "min": 25 } },
"condition": {
"scenario": "objectSelected",
"eventId": "orders-selected"
},
"request": { "getSelectedObjectForEventId": "orders-selected" },
"editor": {
"fields": [{ "attribute": "name", "readonly": true }]
}
}
]
}Limits and declaration/runtime drift
- The base type permits a missing
type; the renderer does not have a useful fallback for an unknown variant. ui.emptyMessage, actions, and events are not uniformly rendered by all variants.- Group
resizablestyling is currently only partially wired. Do not promise drag resizing solely because the declaration exists. - Conditions are consumer-specific. The shared type's
valuefield currently has no shared filtering consumer. - Package-backed variants can accept opaque settings whose behavior belongs to the installed package, not the common panel contract.
Exact property reference
panel.idpanel.typepanel.ui.titlepanel.ui.emptyMessagepanel.panelLayout.size.valuepanel.panelLayout.size.minpanel.condition.scenariopanel.events[].id