Skip to content

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 id within the application. Selection registries, request state, action dispatch, tabs, and event lookups use it as an identity.
  • type must 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": []
}
CapabilityFieldsRuntime model
Identity and dispatchid, typeid scopes state; type selects the renderer.
User-facing stateui.title, ui.emptyMessageLocalized title; empty-message support is variant-dependent.
Conditional visibilityconditionEvaluated by parent composition and by the renderer lifecycle.
Container sizingpanelLayout.size.value, minPrimeVue Splitter percentages for a child of a group; they are not CSS pixel widths.
Cross-panel behavioreventsNamed occurrences run ordered event actions.

Visibility and conditions

Three condition scenarios are declared:

ScenarioVisible whenImportant interaction
defaultConsumer-specificThe shared filtering helper does not explicitly accept it; use only where the variant guide proves behavior.
variablesEvery configured variable comparison passescleanOnUnmounted registers that variable for cleanup when the conditioned panel unmounts.
objectSelectedThe referenced event exists and contains addSelectedObjectToRequestThe 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.

TypeUser problemGuide
groupLay out ordered child panels in rows or columnsGroup
messageShow a localized heading and plain textMessage
tableQuery and act on flat object collectionsTable
treeNavigate linked object hierarchiesTree
tabsSwitch between conditioned child panelsTabs
conditionChoose one child from condition matchesCondition
attributesInspect or edit one event-selected objectAttributes
ganttVisualize an already-loaded scheduleGantt
filesList or preview datasets for a selected objectFiles
calendarShow an APS calendar for a selected objectCalendar
bpmnView and mutate a method-backed workflow graphBPMN
scheduleOperationsPopulate the Gantt unscheduled-operations drawerSchedule Operations
metaTypesCrudManage MetaTypes with package-owned UIMetaTypes CRUD
coreAppsHost a built-in administration surfaceCore Apps
tiptapPanelEdit rich text in standalone or object-bound modeTiptap Panel
textEditorPanelSelect the supported editor renderer, currently Tiptap onlyText Editor Panel

Defaults, precedence, and interactions

  1. The application supplies the root.
  2. A composition variant determines which children are mounted and how their size hints are interpreted.
  3. The child variant chooses its request and internal state model.
  4. Events publish selection or mutations under stable IDs.
  5. Conditions and dependent requests consume those IDs.
  6. 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
{
  "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 }]
      }
    }
  ]
}
Open a checked-in multi-panel selection and detail fixture Open a vertical group with explicit child sizing

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 resizable styling is currently only partially wired. Do not promise drag resizing solely because the declaration exists.
  • Conditions are consumer-specific. The shared type's value field 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

Next tasks