CnObjectSidebar
Right sidebar for entity detail pages. Provides standardized tabs — Files, Notes, Tags, Tasks, and Audit Trail — that integrate with OpenRegister API endpoints bridging to Nextcloud-native APIs. Each tab is optional and independently overridable via slots.
Wraps: NcAppSidebar, NcAppSidebarTab
Try it
Tabs
| Tab ID | Label | Content |
|---|---|---|
files | Files | File attachments via CnFilesTab |
notes | Notes | Notes list and add form via CnNotesTab |
tags | Tags | Tag management via CnTagsTab |
tasks | Tasks | Task list via CnTasksTab |
auditTrail | Audit Trail | Change history via CnAuditTrailTab |
Usage
<!-- Basic usage -->
<CnObjectSidebar
object-type="pipelinq_lead"
:object-id="lead.id"
:register="registerConfig.register"
:schema="registerConfig.schema" />
<!-- Hide specific tabs -->
<CnObjectSidebar
object-type="pipelinq_lead"
:object-id="lead.id"
:hidden-tabs="['tasks', 'tags']" />
<!-- Override a tab with custom content -->
<CnObjectSidebar object-type="pipelinq_lead" :object-id="lead.id">
<template #tab-notes="{ objectId }">
<MyCustomNotesComponent :id="objectId" />
</template>
</CnObjectSidebar>
<!-- Add an extra custom tab -->
<CnObjectSidebar object-type="pipelinq_lead" :object-id="lead.id">
<template #extra-tabs>
<NcAppSidebarTab id="relations" name="Relations" :order="6">
<template #icon><LinkVariant :size="20" /></template>
<RelationsList :object-id="lead.id" />
</NcAppSidebarTab>
</template>
</CnObjectSidebar>
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
objectType | String | ✓ | — | Entity type identifier (e.g. 'pipelinq_lead') — used as the sidebar title fallback |
objectId | String | ✓ | — | Object UUID passed to all tab components |
register | String | '' | OpenRegister register ID | |
schema | String | '' | OpenRegister schema ID | |
hiddenTabs | Array | [] | Tab IDs to hide: 'files', 'notes', 'tags', 'tasks', 'auditTrail' | |
open | Boolean | true | Whether the sidebar is visible | |
title | String | '' | Sidebar title (defaults to objectType) | |
subtitle | String | '' | Sidebar subtitle | |
subtitleProp | String | '' | Deprecated — use subtitle instead | |
apiBase | String | '/apps/openregister/api' | Base URL for OpenRegister API calls | |
filesLabel | String | 'Files' | Files tab label | |
notesLabel | String | 'Notes' | Notes tab label | |
tagsLabel | String | 'Tags' | Tags tab label | |
tasksLabel | String | 'Tasks' | Tasks tab label | |
auditTrailLabel | String | 'Audit Trail' | Audit Trail tab label | |
tabs | Array | null | Open-enum tab definitions [\{ id, label, icon?, widgets?, component?, order? \}]. When set with at least one entry, REPLACES the hard-coded built-in tab set. See Custom tabs below. | |
customComponents | Object | null | Custom-component registry for tab component names and unknown widget type values. Falls back to the injected cnCustomComponents from a CnAppRoot ancestor. |
Events
| Event | Payload | Description |
|---|---|---|
update:open | boolean | Emitted when the sidebar is closed; use with .sync |
Slots
| Slot | Scope | Description |
|---|---|---|
tab-files | { objectId, objectType } | Override the Files tab content |
tab-notes | { objectId, objectType } | Override the Notes tab content |
tab-tags | { objectId, objectType } | Override the Tags tab content |
tab-tasks | { objectId, objectType } | Override the Tasks tab content |
tab-audit-trail | { objectId, objectType } | Override the Audit Trail tab content |
extra-tabs | — | Additional NcAppSidebarTab elements appended after the built-in tabs |
Custom tabs
The tabs prop opens up the closed-enum tab set so apps can drive CnObjectSidebar directly from manifest.json (pages[].config.sidebarProps.tabs). When tabs is set with at least one entry, the built-in tabs (Files / Notes / Tags / Tasks / Audit Trail) do NOT render — the consumer-supplied array drives the UI.
<CnObjectSidebar
object-type="decision"
:object-id="decisionId"
:tabs="[
{ id: 'overview', label: 'Overview', icon: 'eye',
widgets: [
{ type: 'data', props: { schema, objectData } },
{ type: 'metadata', props: { objectData } },
] },
{ id: 'related', label: 'Related', icon: 'link',
component: 'MyRelatedTab' },
]"
:custom-components="{ MyRelatedTab }" />
Tab definition shape
| Field | Type | Notes |
|---|---|---|
id | String | Required. Unique within the array; used for active-tab tracking. |
label | String | Required. Display label (i18n key already resolved by the consumer). |
icon | String | Optional MDI icon name; rendered via CnIcon. |
widgets | Array | Optional. List of \{ type, props? \} widget specs (see below). |
component | String | Optional. Registry name resolved against customComponents. Mutually exclusive with widgets — when both are set, component wins and a console.warn is logged. |
order | Number | Optional. Defaults to array index + 1. |
Built-in widget types
Widget type | Resolved component | Required props |
|---|---|---|
data | CnObjectDataWidget | schema, objectData (forward via per-widget props) |
metadata | CnObjectMetadataWidget | objectData |
Any other type value resolves against the customComponents registry — the explicit customComponents prop wins over the injected cnCustomComponents (mirroring CnPageRenderer's pattern).
Shared object context
Every widget and component mounted inside a custom tab receives the parent CnObjectSidebar's objectId / objectType / register / schema / apiBase as default props (matching the context the built-in tabs receive). Per-widget props win on conflict, so a tab can override objectData, apiBase, etc. without losing the rest of the context.
Backwards compatibility
Apps satisfied with the default tab set make NO changes — leave tabs unset and the hard-coded built-in tabs render exactly as today, including the #tab-files / #tab-notes / #tab-tags / #tab-tasks / #tab-audit-trail / #extra-tabs slot overrides. The tabs prop is purely additive.
Live updates (collaborative editing)
CnObjectSidebar auto-subscribes to live updates for the active object when both objectStore and (objectType + objectId) are provided. This wires useObjectSubscription into the sidebar lifecycle so the cached object stays fresh as remote users edit, and downstream tabs (CnObjectDataWidget, CnAuditTrailTab, etc.) re-render reactively without polling.
| Prop | Default | Behaviour |
|---|---|---|
subscribe | true | When false, skips the auto-subscribe (useful for read-only / archive surfaces). |
objectStore | null | Pinia store instance. When omitted, the auto-subscribe is skipped. |
The locked-banner UX lives on CnDetailPage for v1 — sidebars host so many editor surfaces (each tab) that the banner would compete with tab content. Consumers needing lock UX inside a sidebar tab should consume useObjectLock directly inside the tab component.
Reference (auto-generated)
The tables below are generated from the SFC source via vue-docgen-cli. They reflect what's actually in CnObjectSidebar.vue and update automatically whenever the component changes.
Props
| Name | Type | Required | Default | Description |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- | -------- | -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| objectType | string | ✓ | — | The entity type (e.g., "pipelinq_lead", "procest_case") |
| objectId | string | ✓ | — | The object UUID |
| register | string | | '' | OpenRegister register ID |
| schema | string | | '' | OpenRegister schema ID |
| hiddenTabs | array | | [] | Array of tab IDs to hide: 'files', 'notes', 'tags', 'tasks', 'auditTrail' |
| open | boolean | | true | Whether the sidebar is open |
| title | string | | '' | Sidebar title (defaults to objectType) |
| subtitle | string | | '' | Sidebar subtitle |
| subtitleProp | string | | '' | |
| apiBase | string | | '/apps/openregister/api' | Base API URL for OpenRegister |
| subscribe | boolean | | true | Whether to auto-subscribe to live updates for the current object. Defaults to true. The sidebar calls objectStore.subscribe(objectType, objectId) on mount and unsubscribes on unmount via tryOnScopeDispose. |
| objectStore | union | | null | Optional explicit Pinia store instance. When omitted, the sidebar skips auto-subscribe (Pinia not yet active in the consumer context). |
| filesLabel | string | | () => t('nextcloud-vue', 'Files') | |
| notesLabel | string | | () => t('nextcloud-vue', 'Notes') | |
| tagsLabel | string | | () => t('nextcloud-vue', 'Tags') | |
| tasksLabel | string | | () => t('nextcloud-vue', 'Tasks') | |
| auditTrailLabel | string | | () => t('nextcloud-vue', 'Audit trail') | |
| tabs | Array<{ id: string, label: string, icon?: string, widgets?: Array<{ type: string, props?: object }>, component?: string, order?: number }> | null | | null | Open-enum tab definitions. When provided with at least one entry, REPLACES the hard-coded built-in tabs (Files, Notes, Tags, Tasks, Audit Trail). When unset (the default), the built-in tabs render as today. Each entry shape: - id (string, required) — unique tab id, used for active-tab tracking. - label (string, required) — tab display label (caller-resolved i18n). - icon (string, optional) — MDI icon name resolved via CnIcon. - widgets (array, optional) — list of widget specs { type, props? } to render inside the tab. Built-in types: data → CnObjectDataWidget, metadata → CnObjectMetadataWidget. Any other type resolves against the customComponents registry. - component (string, optional) — name resolved against the customComponents registry. Mutually exclusive with widgets (when both are set, component wins and a console.warn is logged). - order (number, optional) — explicit order; defaults to array index + 1. |
| customComponents | union | | null | Custom-component registry. Keys are names referenced by tabs[].component and unknown tabs[].widgets[].type values. Falls back to the injected cnCustomComponents from a CnAppRoot ancestor when omitted. |
Events
| Name | Payload | Description |
|---|---|---|
update:open | — |
Slots
| Name | Bindings | Description |
|---|---|---|
tab-files | object-id, object-type | |
tab-notes | object-id, object-type | |
tab-tags | object-id, object-type | |
tab-tasks | object-id, object-type | |
tab-audit-trail | object-id, object-type | |
extra-tabs | — |