Ga naar hoofdinhoud

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: loadingdependency-checkshell.

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

PhaseWhenDefault renderingOverride slot
loadingWhile isLoading is true<CnAppLoading />#loading
dependency-missingAfter loading; any entry in manifest.dependencies is not installed/enabled<CnDependencyMissing />#dependency-missing
shellManifest 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

PropTypeDefaultDescription
manifestObject— (required)Reactive manifest. The renderer reads manifest.dependencies and manifest.menu; descendants inject('cnManifest').
appIdString— (required)Nextcloud app id. Forwarded to NcContent as app-name and to CnDependencyMissing.
isLoadingBooleanfalseWire to useAppManifest().isLoading. Apps using only the bundled manifest skip the loading phase.
customComponentsObject{}Registry consumed by CnPageRenderer for type: "custom" pages and slot overrides. Provided as cnCustomComponents.
pageTypesObject | nullnullMap of pages[].type → Vue component. Provided to descendant renderers as cnPageTypes. When omitted, the renderer falls back to defaultPageTypes.
translateFunctionidentityApp-supplied translator — typically (key) => t(appId, key). Named translate (not t) to avoid shadowing the global t() mixin. Provided as cnTranslate.
permissionsArray<string>[]Permission strings the current user holds. Forwarded to CnAppNav for menu filtering.
requiresAppsArray<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 keyProvided value
cnManifestThe manifest prop
cnCustomComponentsThe customComponents prop
cnTranslateThe translate prop
cnPageTypesThe pageTypes prop

Slots

SlotScopeDefaultDescription
(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 pageShown 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-actionsMounted inside NcAppContent, alongside the default slot
sidebarThe resolved cnPageSidebarComponent when set, otherwise emptyMounted 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.
footerMounted 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.