BytesMap
Build and investigate

View composition

Follow shared datasets, nested views, publication and runtime bindings.

Open Markdown

A component describes content and behavior: a chart, table, record list, details, metric, form, button, text, progress indicator or embedded view. The shared PANEL_COMPONENTS registry supplies the editor's library; PanelComponent is its serializable discriminated union. Domain entities such as users and products are data, not component types.

A ViewDefinition is the canonical reusable document: datasets, components, declared parameters and interaction references. ViewComponent and ViewDataset are exported from @bytesmap/flow/views and the SDK. bytesmap.views.* (/api/views) is the public authoring API. Existing storage IDs and internal Panel* aliases delegate to that same model; they do not create a separate editor, renderer or executor.

A section owns an end-to-end read/calculation flow and its visible output. It can contain related metrics from one summary, or a filter and the records controlled by that filter. Independent dashboard sections belong in separate saved Views. A composition contains type: "view" references with input mappings and no duplicate datasets. A panel is a presentation of this composition, not another initial dashboard section. Existing multi-component documents remain valid.

viewSections derives boundaries from the existing binding and dataset dependency logic. Shared datasets, calculation dependencies, local selections, filter inputs, workflow refresh targets and interleaved reading order remain together. It never rewrites SQL or duplicates shared summaries. planViewSections / applyViewSections in the SDK create reusable sections and retain the original ID as a composition. Optimistic versions protect concurrent edits. Organize into sections in the View settings uses that same operation; save current edits first. dashboardSectionLayout promotes a full-width composition into dashboard placements while retaining widget width/height overrides. Fixed, narrow or explicitly annotated placements remain compositions. Installers can pass a desired definition to planViewSections(client, id, definition). The plan retains existing child IDs by section key, and applying it updates those sections without replacing the composition with an all-in-one View. Identical definitions do not create new sections or revisions. Shared metrics retain their proportional dashboard width.

The View builder opens in Flow mode with one compact View context node for its name, declared inputs and settings. Local source, transform, join and component nodes appear directly on the original ChartFlow canvas. They use its existing controllers, node renderers and inspector shell. The context node has no data ports and never enters query execution. Design arranges the components; preview actions open a dialog for the selected component or complete View.

Solid arrows show data and workflow direction; dashed arrows show component interactions. Dotted, open arrows labeled In this view connect the View context to its visible roots. These read-only membership links replace mandatory enclosing frames without creating data dependencies. Their hidden metadata anchors cannot be connected or edited, and neither anchors nor membership edges are saved in the View's queries or workflow definitions.

Saved data flows and workflow references start collapsed in the complete View. Expanding loads their steps on that same canvas. A native query expands into its formatted SQL node without a second reference wrapper. Collapsing an opened scope retains its controller, draft and cached result. Show all steps remains an explicit canvas display option. Canvas display also offers optional data-flow frames and collapsing of reusable references. These are presentation choices; dataset parameter namespaces, result outputs, permissions and reviewed workflow revisions remain intact. Switching display modes does not execute, copy or save anything. Component nodes show configured field mappings until an explicit Run provides real cached preview data. Design uses those same mapping previews for configured components without loaded data. Widget selection uses the editor's neutral controls, and the shared inspector docks beside the artboard on desktop and overlays it on narrow screens.

Inspect flow on a component initially shows that component and its data dependencies. Show full view reveals its siblings on the same canvas. Dataset editing and reference selection focus the existing node and its original inspector. Data and workflow inspection retain the same canvas, header, controller state and cached previews. Workflow inspectors reuse the standalone editor's Save draft, Publish and explicit Run controls. Publishing from the View updates its matching workflow references in the View draft; saving or formatting never publishes or executes a workflow. A saved View reference opens that View's flow in place. Back restores the parent View draft. Connections from dataset outputs to components change their real dataset binding. Every data component also exposes Inspect flow, selecting its exact dataset and result node and preserving dashboard/project navigation context.

Arrange lays out expanded data and workflow steps using the same measured graph layout as their standalone editors. View arrangement changes the displayed layout; standalone workflow arrangement is an ordinary editable draft position change. Fit all changes only the viewport. Initial fitting waits for the expanded scope metadata and preserves a readable zoom; shrinking a large graph to fit remains explicit.

Nested View editors show their ancestry beneath the title using the shared CanvasBreadcrumb. Ancestor links return through the same unsaved-change guard as Back; the current item is not a navigation control. Inspecting a data or workflow step stays in the current View and adds no navigation level. Explicit workflow creation retains the parent context until its new reference can join the graph.

Dashboards place Views and share parameters. Hidden detail pages use the same composition runtime and publication; their presentation does not introduce a separate kind of content. Existing saved charts remain inspectable through their original query ID and selected flow output.

Dashboard flow composes the complete original View editors on one React Flow canvas. Views initially appear as compact references. Expand flow exposes their inputs, native queries, data-flow steps, components, interactions and workflows in place. The header's all/specific View selection controls visibility independently from expansion. ViewCanvasHost receives the original FlowComposition; it never rebuilds a simplified graph from the component list. Only drawing IDs and positions are scoped. Saved IDs, parameter bindings, execution, node renderers, controllers, inspectors and Save remain owned by each original View editor. The section count comes from the dashboard's placed content, not the number of reachable destinations. A composed destination links to its actual section flows; embedded-view placeholders are not drawn a second time. Compact View reference nodes own inputs/settings, with the original query, calculation and component nodes beside them on a flat canvas. There are no enclosing dashboard frames. Auto-arrange follows left-to-right dependencies and packs independent branches into a balanced footprint. The standalone View context node is omitted so ownership is represented once. Navigation connects the existing interaction/workflow node to the destination. Selecting a node activates that View's existing toolbar and inspector in place. Collapsing a View retains its draft and cached results. Design and the preview dialog reuse that View's existing surfaces. Pages remain layout membership, not extra nodes. Back preserves the dashboard's filters and mounted content. Inspection loads metadata without executing queries or creating another saved definition.

Presentation belongs to the host. The same view renderer supports a full page, side drawer, centered modal or embedded section. A mini dashboard is another view, or an existing dashboard's composition in a smaller placement. There is no separate mini-dashboard schema. Navigation is a typed destination reference and parameter map, never arbitrary executable JavaScript.

Required inputs and destinations

Declare inputs with the shared parameter editor in a View, query flow or dashboard. ParamSpec.required requests a value before dependent reads; labels, descriptions, defaults, allowed choices and numeric bounds use the same controls as filters. A default satisfies a required input. Zero and false are valid inputs. Opening a destination without its required values shows the shared input form. Independent reads, such as an account search, still load; dependent components explain which selection is needed. The client and server use panelReadParameters to follow the same transitive read dependencies. Workflow submissions validate all required inputs. Publishing and choosing a destination only inspect metadata.

The action editor chooses exactly one destination:

TargetMeaning
viewIdReusable saved View, including charts, records, forms and actions
pageIdPage in the current published workspace
dashboardIdIndependent dashboard with its own publication and permissions

Map known inputs from $selection, $page, a form, dataset or workflow $result. Leave others unmapped to request them on entry. Choose a side panel, modal or full page independently of the target. The same editor is used by buttons, record/chart interactions and workflow completion effects; the Flow canvas displays those saved references.

The navigation host keeps a return stack. Browser Back, Forward, panel Back and Escape traverse its entries. history: 'replace' replaces the current step; the default pushes a step. Expanding a panel retains its inputs and return path. Destination and input values are reflected in the URL. Reloading a link resolves the destination using the current user's own access; it does not transport an originating dashboard's delegated authority. Do not put secrets in input values. Within a session, nested saved View paths retain their frozen parameter bindings and accept only unmapped child filter overrides.

Search, select and return

An input can attach a saved View through picker, using viewId, valueKey and optional labelKey. picker.params maps picker input names to caller input names. For example, a user search receives the selected account ID. The same parameter editor exposes this configuration in a View or dashboard. Both the initial prompt and Filters reuse ParameterControl. Authors can choose manual entry, a saved record browser, or both. With a picker attached, manual entry remains available by default; picker.allowManual: false hides that alternative. This controls the input UI, not authorization or which records the user may access.

When one required input has a picker, the destination renders that saved browser inline, with manual entry alongside it. There is no intermediate search button or mode toggle. The host uses the same view renderer, bindings, return interaction and actor access as a modal picker; it does not inherit the destination's publication authority. Dependent charts remain gated until a value is selected or submitted. Filters on an already-open view still use the compact modal selector.

A record browser is a normal saved view, not a separate search implementation. It can show an initial list, source search, pagination, and filter-form controls together. Bind the form fields to dataset parameters; use behavior: "filter" and optionally applyOnChange: true to refresh reads as those fields change. Filtering resets the affected list to its first page, preserves its search text, and invalidates the previous row selection. It never runs the form's workflow. The editor exposes this on the form's Actions tab. The query defines the meaning of a filter: for example, the ONVO demo's Plus filter checks for a current active Plus subscription, excluding pending, expired and future subscriptions.

The picker uses an ordinary table or list with a return interaction:

{
  "type": "return",
  "params": {
    "id": { "kind": "input", "name": "$selection", "path": ["id"] },
    "name": { "kind": "input", "name": "$selection", "path": ["name"] }
  }
}

The host validates the returned value before closing, updates the existing caller, and returns through its history. A navigation action can also declare returnParams: { "userId": "id" } to map returned keys into caller inputs. A published workflow can use the same return effect with $result in after. Returning never reruns that workflow. Opening or cancelling a picker does not change caller inputs. Expanded pickers retain their original return destination.

dependsOn declares cascading inputs. Picker input mappings also contribute dependencies. changeParameters clears dependent selections transitively; an explicit atomic update can provide both the new parent and child. Explicitly cleared dependent values do not fall back to stale defaults. Definitions reject unknown dependencies and cycles.

Picker reads use the current actor's own grants, including when opened from a published workspace. They do not borrow the publisher's authority or bypass source permissions. Navigation return mappings cannot override bound child inputs.

Data and interactions

Each dataset owns either an inline read definition or a reference to a reusable saved data flow, with an explicit result step. Inline definitions support the same portable queries, native queries and multi-source flows. Applying an edit to a reusable reference creates a snapshot in the View; it does not modify the shared query. Saving a View requires no preview execution. Column metadata uses the same schema propagation and cache as the source canvas.

Add data source extends the active local data flow, or the first local flow when none is selected, so multiple sources can feed the same join. An empty View creates its first flow with an ordinary source node. Separate saved flows remain available from the dataset inspector. The source inspector exposes the existing explicit Run and output picker in flat mode. View inputs use the same $page binding namespace as the runtime; changing an input alone never fetches data or runs an action.

Bindings map view parameters, selected records, form values and earlier dataset results to query inputs. Read-only datasets can use authorized connected sources and the existing lookup join executor. Workflow transactions retain their single-source contract.

Record lists map a key, title, optional image, subtitle, badge and supporting fields. Image URLs may be HTTP(S) or local paths; missing images use initials. Only the current page of records and images is mounted. By default search and pagination describe the loaded query result. To search the source, a dataset declares controls.searchParam, and optional paired offsetParam/limitParam with pageSize (1–100). These refer to inputs on the existing query; its SQL or flow implements filtering, a stable order and pagination. The runtime replaces only those bindings and preserves the others. It requests one extra row and trims it to indicate whether a next page exists. Search resets the offset, invalidates the previous row selection and debounces reads. In-flight results from older inputs or selections cannot replace newer content.

Charts, tables and lists share the same selection contract and interaction editor:

  • A primary action filters parameters, opens a view, returns a selection or runs a published workflow.
  • Named contextual actions use the same dispatcher and bindings.
  • $selection contains scalar selected fields. Other component and dataset bindings remain available inside reusable groups.
  • Selection state can also drive details, queries, forms and embedded view parameters without a navigation action.

Components sharing a dataset reuse its one resolved result. Dependent datasets use the same binding dependency order. First reads render component-shaped skeletons; refresh keeps mounted content, selection, search and scroll state. Inside a fixed dashboard card, a single auto-height collection fills the available body: its description, search and pagination stay visible while only records scroll. Standalone and multi-component Views retain content-driven layout. Opened drawers and modals use natural widget sizing and a single vertical scroll area. Tables size to their visible records, record facts wrap on narrow screens, and structured values expand in place. Parent content stays mounted while a nested destination is open.

Queries and image loading cannot execute workflows. Workflow events reference reviewed revisions and use the existing authenticated executor and idempotency keys. Selection values remain untrusted input; source permissions and workflow business rules enforce access to records.

Workflow triggers

A workflow is one reusable action definition regardless of entry point: a View interaction, an explicit manual run, or a schedule trigger. Its Start inspector opens the existing schedule editor in place, pinned to the reviewed workflow revision. The scheduler and schedule records are unchanged; there is no second scheduler or inline write executor. Schedules are no longer a separate primary authoring tab; the overview route remains for existing links and operational use. Saving, publishing or scheduling configuration does not execute the workflow.

Embedded views

An embedded component references a saved View directly with viewId. It uses the same ViewDefinition, renderer, dataflow pipeline and explicit workflow executor. No wrapper dashboard or hidden page is created. Existing pageId references retain their dashboard-page behavior. Embedded references and navigation effects select exactly one target; saved Views can open as a page, drawer, modal or inline section.

Publication freezes all recursively referenced View definitions, inline or reusable queries, and reviewed workflow revisions. It validates target parameters and the combined page/View graph, rejecting direct and indirect embedding cycles while allowing ordinary navigation loops. Editing a child draft cannot silently change the published parent. Rearranging a dashboard retains all dependencies of the visible components and prunes unreachable descendants.

Workspace reads and submissions identify the final component document and its viewPath. Each frame names the saved parent View, referencing component, current component state and dataset results. An optional navigation index uses the shared componentViewReferences helper, so a named menu and the primary action resolve the exact frozen binding. The server follows this path and derives each child's parameters; passing another View ID or overriding a parent-mapped key is rejected. Unmapped declared child parameters accept local filter overrides through viewParams (and an intermediate frame's params). Root page parameters remain separate. Actor page access and the publisher's current dependency authority still bound every read and effect. Standalone previews use the same resolver with the caller's own grants and current root version, without publication authority.

Each placement has its own selection/form state, filter overrides and runtime namespace. Changing the incoming record resets that placement's filter overrides. Refresh preserves the mounted composition and existing results. Legacy page embeds continue to require the actor's independent target-page access.

Extending the system

Add component definitions, validation and reference traversal in packages/flow/src/actions/panels.ts. Put rendering and controls in packages/ui, using the shared tokens and primitives. Hosts supply transport and view placement through PageRuntime; components never import an app's routing or credentials. Extend the existing query, binding, selection or workflow contract when behavior is shared. Do not create entity-specific runtimes or separate modal/dashboard execution paths.

The dashboard adapter lives in apps/dashboard/src/components/views; workflows and their trigger authoring live in components/workflows. Both embed the existing source and action canvases. Shared presentation and controls stay in packages/ui.

The same operation catalog serves the dashboard, SDK, CLI and MCP. Views is the single public component-authoring tool family. Internal panel compatibility operations are excluded from tool and CLI discovery. Authoring saves configuration; data execution and workflow execution remain explicit.

On this page