CnAppRoot
Top-level wrapper for manifest-driven Conduction apps. Provides the manifest, custom-component registry, page-type registry, and translate function to descendants via provide / inject. Orchestrates three rendering phases: loading → dependency-check → shell.
CnAppRoot is the full-shell convenience for the JSON manifest renderer. Apps that want manifest-driven pages but their own root layout can skip CnAppRoot and use CnPageRenderer / CnAppNav directly with explicit props.
Wraps: NcContent, NcAppContent, CnAppLoading, CnDependencyMissing, CnAppNav
Phases
| Phase | When | Default rendering | Override slot |
|---|---|---|---|
loading | While isLoading is true | <CnAppLoading /> | #loading |
dependency-missing | After loading; any entry in manifest.dependencies is not installed/enabled | <CnDependencyMissing /> | #dependency-missing |
shell | Manifest loaded + dependencies satisfied | <CnAppNav /> + default slot content | #menu, default slot, #header-actions, #sidebar, #footer |
Dependency status is resolved by useAppStatus — one call per id in manifest.dependencies, cached for the page lifetime.
Usage
<template>
<CnAppRoot
:manifest="manifest"
app-id="decidesk"
:is-loading="isLoading"
:custom-components="customComponents"
:page-types="pageTypes"
:translate="translate"
:permissions="permissions">
<!-- Optional overrides -->
<template #loading>
<MyBrandedLoadingScreen />
</template>
</CnAppRoot>
</template>
<script>
import { CnAppRoot, useAppManifest, defaultPageTypes } from '@conduction/nextcloud-vue'
import bundledManifest from './manifest.json'
import MyReportPage from './views/MyReportPage.vue'
export default {
components: { CnAppRoot },
setup() {
const { manifest, isLoading } = useAppManifest('decidesk', bundledManifest)
return {
manifest,
isLoading,
customComponents: { /* keys referenced by page.component for type:"custom" pages */ },
pageTypes: { ...defaultPageTypes, report: MyReportPage },
translate: (key) => t('decidesk', key),
permissions: ['decisions.read', 'decisions.write'],
}
},
}
</script>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
manifest | Object | — (required) | Reactive manifest. The renderer reads manifest.dependencies and manifest.menu; descendants inject('cnManifest'). |
appId | String | — (required) | Nextcloud app id. Forwarded to NcContent as app-name and to CnDependencyMissing. |
isLoading | Boolean | false | Wire to useAppManifest().isLoading. Apps using only the bundled manifest skip the loading phase. |
customComponents | Object | {} | Registry consumed by CnPageRenderer for type: "custom" pages and slot overrides. Provided as cnCustomComponents. |
pageTypes | Object | null | null | Map of pages[].type → Vue component. Provided to descendant renderers as cnPageTypes. When omitted, the renderer falls back to defaultPageTypes. |
translate | Function | identity | App-supplied translator — typically (key) => t(appId, key). Named translate (not t) to avoid shadowing the global t() mixin. Provided as cnTranslate. |
permissions | Array<string> | [] | Permission strings the current user holds. Forwarded to CnAppNav for menu filtering. |
requiresApps | Array<string> | ['openregister'] | App ids that MUST be installed for the host app to function. Checked against the OCS capabilities API on mount. When any required app is missing, CnAppRoot renders the or-missing slot (default <NcEmptyContent>) instead of the renderer. Pass [] to opt out (e.g. mydash, the docs/styleguide app). See App-availability guard. |
Provided values
CnAppRoot calls provide() with the following keys; descendants inject these:
| Inject key | Provided value |
|---|---|
cnManifest | The manifest prop |
cnCustomComponents | The customComponents prop |
cnTranslate | The translate prop |
cnPageTypes | The pageTypes prop |
Slots
| Slot | Scope | Default | Description |
|---|---|---|---|
| (default) | — | — | Page content area inside NcAppContent. In real apps, pass <router-view /> here. |
loading | — | <CnAppLoading /> | Shown during the loading phase |
dependency-missing | { dependencies } | <CnDependencyMissing :dependencies /> | Shown when any dependency is missing or disabled |
or-missing | { missingApps } | Default <NcEmptyContent> linking to the OpenRegister app-store integration page | Shown when any app in requiresApps is missing per the OCS capabilities check. Override to fully replace the empty state. |
menu | — | <CnAppNav :permissions /> | Replaces the default app navigation |
header-actions | — | — | Mounted inside NcAppContent, alongside the default slot |
sidebar | — | The resolved cnPageSidebarComponent when set, otherwise empty | Mounted next to NcAppContent (e.g. for NcAppSidebar). Gated by the cnPageSidebarVisible inject — when a descendant CnPageRenderer flips it to false (because the current manifest page declares sidebar.show: false), this slot stops rendering. The default (no provider) is value-true so the slot keeps rendering. The slot's default content is driven by the cnPageSidebarComponent inject — when the current page declares a sidebarComponent registry name, the resolved component renders here unless the consumer supplies a #sidebar slot override (override wins). See Per-page sidebar visibility and Per-page sidebar component. |
footer | — | — | Mounted inside NcAppContent, after the default slot |
Hoisted index sidebar
CnAppRoot provides a reactive holder, cnIndexSidebarConfig, that descendants — specifically CnIndexPage — write to in order to mount their embedded CnIndexSidebar at NcContent level. NcAppSidebar must be a direct child of NcContent to render as the proper right-side overlay; nested anywhere deeper it falls back to in-flow layout, which is why the lib hoists.
The hoist is automatic — apps using CnAppRoot get correct positioning the moment they pass a sidebar: { enabled: true } config on a type: 'index' manifest page. No consumer template changes required. The hoisted sidebar mounts as a sibling of the consumer's #sidebar slot, so existing #sidebar content (e.g. CnObjectSidebar for detail pages) keeps working unchanged.
Apps mounting CnIndexPage standalone (without CnAppRoot) keep the legacy inline rendering — the cnHostsIndexSidebar sentinel defaults to false in that case, so CnIndexPage renders the sidebar in-tree as before.
Related
- useAppManifest — Loads/validates the manifest passed in.
- useAppStatus — Backs the dependency-check phase.
- CnPageRenderer — Mounts inside
<router-view>to dispatch by page type. - CnAppNav — Default
#menurendering. - defaultPageTypes — Built-in page-type registry.
- validateManifest — The validator used inside
useAppManifest. - migrating-to-manifest — Tier-by-tier adoption guide.