@conduction/nextcloud-vue 2.61.0 → 2.63.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue.js +10 -0
- package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue.js.map +1 -0
- package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue2.js +189 -0
- package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue2.js.map +1 -0
- package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue3.js +72 -0
- package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue3.js.map +1 -0
- package/dist/esm/components/CnAppNav/CnAppNav.vue2.js +394 -27
- package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
- package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +99 -9
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +39 -18
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js +79 -2
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js +38 -25
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue2.js +21 -1
- package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue2.js.map +1 -1
- package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue3.js +4 -1
- package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue3.js.map +1 -1
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js.map +1 -1
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js +53 -37
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +30 -6
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +2 -2
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue.js +10 -0
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue.js.map +1 -0
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue2.js +293 -0
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue2.js.map +1 -0
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue3.js +189 -0
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue3.js.map +1 -0
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +256 -23
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +119 -51
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +2 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
- package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue2.js +3 -1
- package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue2.js.map +1 -1
- package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue3.js +1 -1
- package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue3.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +234 -9
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +96 -57
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +51 -2
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +5 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/selfModeActions.js +3 -3
- package/dist/esm/components/CnIndexPage/selfModeActions.js.map +1 -1
- package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue2.js.map +1 -1
- package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +36 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +4 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +53 -0
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +22 -5
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
- package/dist/esm/components/CnReportsPage/CnReportsPage.vue2.js.map +1 -1
- package/dist/esm/components/CnReportsPage/CnReportsPage.vue3.js.map +1 -1
- package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue2.js +121 -12
- package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue2.js.map +1 -1
- package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue3.js +3 -1
- package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue3.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js +326 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +112 -26
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
- package/dist/esm/components/CnStorePage/CnStorePage.vue2.js.map +1 -1
- package/dist/esm/components/CnStorePage/CnStorePage.vue3.js.map +1 -1
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue.js +10 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue.js.map +1 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js +337 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js +102 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnTimelineWidget/dashboardRegistration.js +34 -0
- package/dist/esm/components/CnTimelineWidget/dashboardRegistration.js.map +1 -0
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js +131 -8
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js.map +1 -1
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js +40 -38
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +8 -6
- package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
- package/dist/esm/components/CnWikiPage/CnWikiPage.vue2.js.map +1 -1
- package/dist/esm/components/CnWikiPage/CnWikiPage.vue3.js.map +1 -1
- package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue2.js +19 -0
- package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue3.js +1 -1
- package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue3.js.map +1 -1
- package/dist/esm/composables/useDependencyCheck.js +65 -0
- package/dist/esm/composables/useDependencyCheck.js.map +1 -0
- package/dist/esm/composables/useWalkthrough.js +218 -6
- package/dist/esm/composables/useWalkthrough.js.map +1 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +48 -0
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +48 -0
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +557 -9
- package/dist/esm/store/useObjectStore.js +8 -0
- package/dist/esm/store/useObjectStore.js.map +1 -1
- package/dist/esm/utils/columnFilters.js +311 -0
- package/dist/esm/utils/columnFilters.js.map +1 -0
- package/dist/esm/utils/groupAutocomplete.js +123 -0
- package/dist/esm/utils/groupAutocomplete.js.map +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js +2 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/pickerOptions.js +198 -0
- package/dist/esm/utils/pickerOptions.js.map +1 -0
- package/dist/esm/utils/schema.js +83 -7
- package/dist/esm/utils/schema.js.map +1 -1
- package/dist/esm/utils/timelineEvents.js +174 -0
- package/dist/esm/utils/timelineEvents.js.map +1 -0
- package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/esm/utils/walkthroughSignals.js +88 -0
- package/dist/esm/utils/walkthroughSignals.js.map +1 -0
- package/dist/nextcloud-vue.cjs.js +9779 -5236
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +557 -9
- package/l10n/en.json +48 -0
- package/l10n/nl.json +48 -0
- package/package.json +3 -2
- package/scripts/manifest-schema-hash.js +41 -0
- package/scripts/record-manifest-schema-hash.js +52 -0
- package/src/components/CnAdminActionCard/CnAdminActionCard.vue +234 -0
- package/src/components/CnAdminActionCard/index.js +4 -0
- package/src/components/CnAppNav/CnAppNav.vue +394 -27
- package/src/components/CnAppRoot/CnAppRoot.vue +129 -15
- package/src/components/CnBannerWidget/CnBannerWidget.vue +113 -2
- package/src/components/CnBrandStripe/CnBrandStripe.md +16 -0
- package/src/components/CnBrandStripe/CnBrandStripe.vue +58 -5
- package/src/components/CnChoiceCards/CnChoiceCards.vue +56 -31
- package/src/components/CnDashboardPage/CnDashboardPage.vue +32 -8
- package/src/components/CnDataTable/CnColumnFilterPopover.vue +524 -0
- package/src/components/CnDataTable/CnDataTable.md +4 -1
- package/src/components/CnDataTable/CnDataTable.vue +302 -21
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +2 -2
- package/src/components/CnFieldHelper/CnFieldHelper.vue +4 -2
- package/src/components/CnFormDialog/CnFormDialog.vue +290 -31
- package/src/components/CnIndexPage/CnIndexPage.vue +55 -2
- package/src/components/CnIndexPage/selfModeActions.js +3 -3
- package/src/components/CnLinkCardsPage/CnLinkCardsPage.vue +35 -2
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +39 -1
- package/src/components/CnObjectSidebar/CnFilesTab.vue +65 -0
- package/src/components/CnReportsPage/CnReportsPage.vue +7 -0
- package/src/components/CnResourceSelect/CnResourceSelect.vue +123 -12
- package/src/components/CnSetupWizard/CnSetupWizard.vue +392 -3
- package/src/components/CnStorePage/CnStorePage.vue +7 -0
- package/src/components/CnTimelineWidget/CnTimelineWidget.vue +477 -0
- package/src/components/CnTimelineWidget/dashboardRegistration.js +32 -0
- package/src/components/CnTimelineWidget/index.js +4 -0
- package/src/components/CnWalkthrough/CnWalkthrough.vue +143 -10
- package/src/components/CnWidgetGrid/builtInWidgets.js +2 -0
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
- package/src/components/CnWikiPage/CnWikiPage.vue +7 -0
- package/src/components/CnWizardDialog/CnWizardDialog.vue +20 -1
- package/src/components/index.js +2 -0
- package/src/composables/useDependencyCheck.js +61 -0
- package/src/composables/useWalkthrough.js +217 -5
- package/src/css/dashboard.css +49 -0
- package/src/css/detail-page.css +19 -0
- package/src/css/table.css +82 -2
- package/src/index.js +2 -0
- package/src/public/index.js +5 -0
- package/src/schemas/app-manifest-v2.schema.json +23 -1
- package/src/store/useObjectStore.js +8 -0
- package/src/utils/columnFilters.js +324 -0
- package/src/utils/groupAutocomplete.js +119 -0
- package/src/utils/libraryWidgetKeys.js +2 -0
- package/src/utils/pickerOptions.js +194 -0
- package/src/utils/schema.js +83 -7
- package/src/utils/timelineEvents.js +171 -0
- package/src/utils/validateManifestV2.compiled.js +2 -2
- package/src/utils/walkthroughSignals.js +94 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { dispatchObjectCreated } from '../../utils/walkthroughSignals.js';
|
|
1
2
|
import { cloneObjectForCopy, runSelfExportRequest, runSelfImportRequest } from './selfModeIO.js';
|
|
2
3
|
|
|
3
4
|
function resolveNameField(ctx) {
|
|
@@ -192,9 +193,8 @@ function createSelfModeActions(ctx) {
|
|
|
192
193
|
// @self.register/@self.schema are numeric DB ids, not slugs, so
|
|
193
194
|
// override with this page's own slug props (self-fetch mode
|
|
194
195
|
// guarantees ctx.schema() is already a string).
|
|
195
|
-
if (isCreate
|
|
196
|
-
|
|
197
|
-
window.dispatchEvent(new CustomEvent('cn-walkthrough:object-created', { detail }));
|
|
196
|
+
if (isCreate) {
|
|
197
|
+
dispatchObjectCreated({ register: ctx.register(), schema: ctx.schema(), object: saved });
|
|
198
198
|
}
|
|
199
199
|
refreshList(ctx);
|
|
200
200
|
if (isCreate && typeof ctx.afterCreateSuccess === 'function') {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selfModeActions.js","sources":["../../../../src/components/CnIndexPage/selfModeActions.js"],"sourcesContent":["import {\n\tcloneObjectForCopy,\n\trunSelfExportRequest,\n\trunSelfImportRequest,\n} from './selfModeIO.js'\n\nfunction resolveNameField(ctx) {\n\treturn ctx.effectiveSchema()?.configuration?.objectNameField\n\t\t|| ctx.massActionNameField()\n}\n\nfunction findSource(ctx, id) {\n\treturn ctx.effectiveObjects().find((o) => o.id === id || o['@self']?.id === id)\n}\n\nfunction selfModeReady(ctx) {\n\treturn ctx.isSelfFetchMode() && !!ctx.selfObjectStore() && !!ctx.selfObjectType()\n}\n\nfunction refreshList(ctx) {\n\tconst list = ctx.list()\n\tif (list && typeof list.refresh === 'function') {\n\t\tlist.refresh()\n\t}\n}\n\nfunction storeError(ctx) {\n\treturn ctx.selfObjectStore()?.getError?.(ctx.selfObjectType())\n}\n\nfunction storeErrorMessage(ctx, fallback) {\n\tconst err = storeError(ctx)\n\treturn (err && err.message) || fallback\n}\n\n/**\n * Self-mode action handlers for CnIndexPage. Each `handle*` returns `true`\n * when self-mode handled it (caller should NOT emit), `false` otherwise.\n *\n * @param {object} ctx Accessor closures + emit/setResults forwarders.\n * @return {object}\n */\nexport function createSelfModeActions(ctx) {\n\tasync function handleSingleDelete(id) {\n\t\tif (!selfModeReady(ctx)) {\n\t\t\treturn false\n\t\t}\n\t\ttry {\n\t\t\tconst ok = await ctx.selfObjectStore().deleteObject(ctx.selfObjectType(), id)\n\t\t\tif (ok) {\n\t\t\t\tctx.setResults.singleDelete({ success: true })\n\t\t\t\tctx.emit('delete', id)\n\t\t\t\trefreshList(ctx)\n\t\t\t} else {\n\t\t\t\tctx.setResults.singleDelete({ error: storeErrorMessage(ctx, 'Delete failed') })\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tctx.setResults.singleDelete({ error: (err && err.message) || 'Delete failed' })\n\t\t}\n\t\treturn true\n\t}\n\n\tasync function handleMassDelete(ids) {\n\t\tif (!selfModeReady(ctx)) {\n\t\t\treturn false\n\t\t}\n\t\ttry {\n\t\t\tconst { successfulIds, failedIds } = await ctx.selfObjectStore().deleteObjects(ctx.selfObjectType(), ids)\n\t\t\tif (failedIds.length === 0) {\n\t\t\t\tctx.setResults.massDelete({ success: true, successfulIds })\n\t\t\t} else {\n\t\t\t\tctx.setResults.massDelete({\n\t\t\t\t\terror: storeErrorMessage(ctx, `Failed to delete ${failedIds.length} item(s)`),\n\t\t\t\t\tsuccessfulIds,\n\t\t\t\t\tfailedIds,\n\t\t\t\t})\n\t\t\t}\n\t\t\trefreshList(ctx)\n\t\t} catch (err) {\n\t\t\tctx.setResults.massDelete({ error: (err && err.message) || 'Delete failed' })\n\t\t}\n\t\treturn true\n\t}\n\n\tasync function handleSingleCopy(payload) {\n\t\tif (!selfModeReady(ctx)) {\n\t\t\treturn false\n\t\t}\n\t\tconst { id, newName } = payload || {}\n\t\tconst source = findSource(ctx, id)\n\t\tif (!source) {\n\t\t\tctx.setResults.singleCopy({ error: 'Source object not found in current view' })\n\t\t\treturn true\n\t\t}\n\t\ttry {\n\t\t\tconst clone = cloneObjectForCopy(source, newName, resolveNameField(ctx))\n\t\t\tconst saved = await ctx.selfObjectStore().saveObject(ctx.selfObjectType(), clone)\n\t\t\tif (saved) {\n\t\t\t\tctx.setResults.singleCopy({ success: true })\n\t\t\t\tctx.emit('copy', payload)\n\t\t\t\trefreshList(ctx)\n\t\t\t} else {\n\t\t\t\tctx.setResults.singleCopy({ error: storeErrorMessage(ctx, 'Copy failed') })\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tctx.setResults.singleCopy({ error: (err && err.message) || 'Copy failed' })\n\t\t}\n\t\treturn true\n\t}\n\n\tasync function handleMassCopy(payload) {\n\t\tif (!selfModeReady(ctx)) {\n\t\t\treturn false\n\t\t}\n\t\tconst ids = (payload && payload.ids) || []\n\t\tconst nameField = resolveNameField(ctx)\n\t\tconst getName = (payload && payload.getName) || ((item) => item[ctx.massActionNameField()])\n\t\tconst successfulIds = []\n\t\tconst failedIds = []\n\t\tfor (const id of ids) {\n\t\t\tconst source = findSource(ctx, id)\n\t\t\tif (!source) {\n\t\t\t\tfailedIds.push(id)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst clone = cloneObjectForCopy(source, getName(source), nameField)\n\t\t\ttry {\n\t\t\t\tconst saved = await ctx.selfObjectStore().saveObject(ctx.selfObjectType(), clone)\n\t\t\t\tif (saved) {\n\t\t\t\t\tsuccessfulIds.push(id)\n\t\t\t\t} else {\n\t\t\t\t\tfailedIds.push(id)\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\tfailedIds.push(id)\n\t\t\t}\n\t\t}\n\t\tif (failedIds.length === 0) {\n\t\t\tctx.setResults.massCopy({ success: true, successfulIds })\n\t\t} else {\n\t\t\tctx.setResults.massCopy({\n\t\t\t\terror: storeErrorMessage(ctx, `Failed to copy ${failedIds.length} item(s)`),\n\t\t\t\tsuccessfulIds,\n\t\t\t\tfailedIds,\n\t\t\t})\n\t\t}\n\t\trefreshList(ctx)\n\t\treturn true\n\t}\n\n\tasync function handleMassExport(payload) {\n\t\tif (!ctx.isSelfFetchMode() || !ctx.register() || !ctx.schema()) {\n\t\t\treturn false\n\t\t}\n\t\ttry {\n\t\t\tawait runSelfExportRequest({\n\t\t\t\tregister: ctx.register(),\n\t\t\t\tschema: ctx.schema(),\n\t\t\t\tformat: payload && payload.format,\n\t\t\t})\n\t\t\tctx.setResults.massExport({ success: true })\n\t\t} catch (err) {\n\t\t\tctx.setResults.massExport({ error: (err && err.message) || 'Export failed' })\n\t\t}\n\t\treturn true\n\t}\n\n\tasync function handleMassImport(payload) {\n\t\tif (!ctx.isSelfFetchMode() || !ctx.register()) {\n\t\t\treturn false\n\t\t}\n\t\ttry {\n\t\t\tawait runSelfImportRequest({\n\t\t\t\tregister: ctx.register(),\n\t\t\t\tschema: ctx.schema(),\n\t\t\t\tfile: payload && payload.file,\n\t\t\t})\n\t\t\tctx.setResults.massImport({ success: true })\n\t\t\trefreshList(ctx)\n\t\t} catch (err) {\n\t\t\tctx.setResults.massImport({ error: (err && err.message) || 'Import failed' })\n\t\t}\n\t\treturn true\n\t}\n\n\tasync function handleFormSave(formData) {\n\t\tif (!selfModeReady(ctx)) {\n\t\t\treturn false\n\t\t}\n\t\ttry {\n\t\t\tconst saved = await ctx.selfObjectStore().saveObject(ctx.selfObjectType(), formData)\n\t\t\tif (saved) {\n\t\t\t\tctx.setResults.form({ success: true })\n\t\t\t\tconst isCreate = !ctx.editItem()\n\t\t\t\tctx.emit(isCreate ? 'create' : 'edit', saved)\n\t\t\t\t// @self.register/@self.schema are numeric DB ids, not slugs, so\n\t\t\t\t// override with this page's own slug props (self-fetch mode\n\t\t\t\t// guarantees ctx.schema() is already a string).\n\t\t\t\tif (isCreate && typeof window !== 'undefined') {\n\t\t\t\t\tconst detail = { ...saved, register: ctx.register(), schema: ctx.schema() }\n\t\t\t\t\twindow.dispatchEvent(new CustomEvent('cn-walkthrough:object-created', { detail }))\n\t\t\t\t}\n\t\t\t\trefreshList(ctx)\n\t\t\t\tif (isCreate && typeof ctx.afterCreateSuccess === 'function') {\n\t\t\t\t\tctx.afterCreateSuccess(saved)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst err = storeError(ctx)\n\t\t\t\tif (err && err.isValidation) {\n\t\t\t\t\t// Keep the form visible so the user can fix the invalid data.\n\t\t\t\t\tctx.setResults.formValidation(err.fields, err.message || 'Validation failed')\n\t\t\t\t} else {\n\t\t\t\t\tctx.setResults.form({ error: (err && err.message) || 'Save failed' })\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tctx.setResults.form({ error: (err && err.message) || 'Save failed' })\n\t\t}\n\t\treturn true\n\t}\n\n\treturn {\n\t\thandleSingleDelete,\n\t\thandleMassDelete,\n\t\thandleSingleCopy,\n\t\thandleMassCopy,\n\t\thandleMassExport,\n\t\thandleMassImport,\n\t\thandleFormSave,\n\t}\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"selfModeActions.js","sources":["../../../../src/components/CnIndexPage/selfModeActions.js"],"sourcesContent":["import { dispatchObjectCreated } from '../../utils/walkthroughSignals.js'\nimport {\n\tcloneObjectForCopy,\n\trunSelfExportRequest,\n\trunSelfImportRequest,\n} from './selfModeIO.js'\n\nfunction resolveNameField(ctx) {\n\treturn ctx.effectiveSchema()?.configuration?.objectNameField\n\t\t|| ctx.massActionNameField()\n}\n\nfunction findSource(ctx, id) {\n\treturn ctx.effectiveObjects().find((o) => o.id === id || o['@self']?.id === id)\n}\n\nfunction selfModeReady(ctx) {\n\treturn ctx.isSelfFetchMode() && !!ctx.selfObjectStore() && !!ctx.selfObjectType()\n}\n\nfunction refreshList(ctx) {\n\tconst list = ctx.list()\n\tif (list && typeof list.refresh === 'function') {\n\t\tlist.refresh()\n\t}\n}\n\nfunction storeError(ctx) {\n\treturn ctx.selfObjectStore()?.getError?.(ctx.selfObjectType())\n}\n\nfunction storeErrorMessage(ctx, fallback) {\n\tconst err = storeError(ctx)\n\treturn (err && err.message) || fallback\n}\n\n/**\n * Self-mode action handlers for CnIndexPage. Each `handle*` returns `true`\n * when self-mode handled it (caller should NOT emit), `false` otherwise.\n *\n * @param {object} ctx Accessor closures + emit/setResults forwarders.\n * @return {object}\n */\nexport function createSelfModeActions(ctx) {\n\tasync function handleSingleDelete(id) {\n\t\tif (!selfModeReady(ctx)) {\n\t\t\treturn false\n\t\t}\n\t\ttry {\n\t\t\tconst ok = await ctx.selfObjectStore().deleteObject(ctx.selfObjectType(), id)\n\t\t\tif (ok) {\n\t\t\t\tctx.setResults.singleDelete({ success: true })\n\t\t\t\tctx.emit('delete', id)\n\t\t\t\trefreshList(ctx)\n\t\t\t} else {\n\t\t\t\tctx.setResults.singleDelete({ error: storeErrorMessage(ctx, 'Delete failed') })\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tctx.setResults.singleDelete({ error: (err && err.message) || 'Delete failed' })\n\t\t}\n\t\treturn true\n\t}\n\n\tasync function handleMassDelete(ids) {\n\t\tif (!selfModeReady(ctx)) {\n\t\t\treturn false\n\t\t}\n\t\ttry {\n\t\t\tconst { successfulIds, failedIds } = await ctx.selfObjectStore().deleteObjects(ctx.selfObjectType(), ids)\n\t\t\tif (failedIds.length === 0) {\n\t\t\t\tctx.setResults.massDelete({ success: true, successfulIds })\n\t\t\t} else {\n\t\t\t\tctx.setResults.massDelete({\n\t\t\t\t\terror: storeErrorMessage(ctx, `Failed to delete ${failedIds.length} item(s)`),\n\t\t\t\t\tsuccessfulIds,\n\t\t\t\t\tfailedIds,\n\t\t\t\t})\n\t\t\t}\n\t\t\trefreshList(ctx)\n\t\t} catch (err) {\n\t\t\tctx.setResults.massDelete({ error: (err && err.message) || 'Delete failed' })\n\t\t}\n\t\treturn true\n\t}\n\n\tasync function handleSingleCopy(payload) {\n\t\tif (!selfModeReady(ctx)) {\n\t\t\treturn false\n\t\t}\n\t\tconst { id, newName } = payload || {}\n\t\tconst source = findSource(ctx, id)\n\t\tif (!source) {\n\t\t\tctx.setResults.singleCopy({ error: 'Source object not found in current view' })\n\t\t\treturn true\n\t\t}\n\t\ttry {\n\t\t\tconst clone = cloneObjectForCopy(source, newName, resolveNameField(ctx))\n\t\t\tconst saved = await ctx.selfObjectStore().saveObject(ctx.selfObjectType(), clone)\n\t\t\tif (saved) {\n\t\t\t\tctx.setResults.singleCopy({ success: true })\n\t\t\t\tctx.emit('copy', payload)\n\t\t\t\trefreshList(ctx)\n\t\t\t} else {\n\t\t\t\tctx.setResults.singleCopy({ error: storeErrorMessage(ctx, 'Copy failed') })\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tctx.setResults.singleCopy({ error: (err && err.message) || 'Copy failed' })\n\t\t}\n\t\treturn true\n\t}\n\n\tasync function handleMassCopy(payload) {\n\t\tif (!selfModeReady(ctx)) {\n\t\t\treturn false\n\t\t}\n\t\tconst ids = (payload && payload.ids) || []\n\t\tconst nameField = resolveNameField(ctx)\n\t\tconst getName = (payload && payload.getName) || ((item) => item[ctx.massActionNameField()])\n\t\tconst successfulIds = []\n\t\tconst failedIds = []\n\t\tfor (const id of ids) {\n\t\t\tconst source = findSource(ctx, id)\n\t\t\tif (!source) {\n\t\t\t\tfailedIds.push(id)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst clone = cloneObjectForCopy(source, getName(source), nameField)\n\t\t\ttry {\n\t\t\t\tconst saved = await ctx.selfObjectStore().saveObject(ctx.selfObjectType(), clone)\n\t\t\t\tif (saved) {\n\t\t\t\t\tsuccessfulIds.push(id)\n\t\t\t\t} else {\n\t\t\t\t\tfailedIds.push(id)\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\tfailedIds.push(id)\n\t\t\t}\n\t\t}\n\t\tif (failedIds.length === 0) {\n\t\t\tctx.setResults.massCopy({ success: true, successfulIds })\n\t\t} else {\n\t\t\tctx.setResults.massCopy({\n\t\t\t\terror: storeErrorMessage(ctx, `Failed to copy ${failedIds.length} item(s)`),\n\t\t\t\tsuccessfulIds,\n\t\t\t\tfailedIds,\n\t\t\t})\n\t\t}\n\t\trefreshList(ctx)\n\t\treturn true\n\t}\n\n\tasync function handleMassExport(payload) {\n\t\tif (!ctx.isSelfFetchMode() || !ctx.register() || !ctx.schema()) {\n\t\t\treturn false\n\t\t}\n\t\ttry {\n\t\t\tawait runSelfExportRequest({\n\t\t\t\tregister: ctx.register(),\n\t\t\t\tschema: ctx.schema(),\n\t\t\t\tformat: payload && payload.format,\n\t\t\t})\n\t\t\tctx.setResults.massExport({ success: true })\n\t\t} catch (err) {\n\t\t\tctx.setResults.massExport({ error: (err && err.message) || 'Export failed' })\n\t\t}\n\t\treturn true\n\t}\n\n\tasync function handleMassImport(payload) {\n\t\tif (!ctx.isSelfFetchMode() || !ctx.register()) {\n\t\t\treturn false\n\t\t}\n\t\ttry {\n\t\t\tawait runSelfImportRequest({\n\t\t\t\tregister: ctx.register(),\n\t\t\t\tschema: ctx.schema(),\n\t\t\t\tfile: payload && payload.file,\n\t\t\t})\n\t\t\tctx.setResults.massImport({ success: true })\n\t\t\trefreshList(ctx)\n\t\t} catch (err) {\n\t\t\tctx.setResults.massImport({ error: (err && err.message) || 'Import failed' })\n\t\t}\n\t\treturn true\n\t}\n\n\tasync function handleFormSave(formData) {\n\t\tif (!selfModeReady(ctx)) {\n\t\t\treturn false\n\t\t}\n\t\ttry {\n\t\t\tconst saved = await ctx.selfObjectStore().saveObject(ctx.selfObjectType(), formData)\n\t\t\tif (saved) {\n\t\t\t\tctx.setResults.form({ success: true })\n\t\t\t\tconst isCreate = !ctx.editItem()\n\t\t\t\tctx.emit(isCreate ? 'create' : 'edit', saved)\n\t\t\t\t// @self.register/@self.schema are numeric DB ids, not slugs, so\n\t\t\t\t// override with this page's own slug props (self-fetch mode\n\t\t\t\t// guarantees ctx.schema() is already a string).\n\t\t\t\tif (isCreate) {\n\t\t\t\t\tdispatchObjectCreated({ register: ctx.register(), schema: ctx.schema(), object: saved })\n\t\t\t\t}\n\t\t\t\trefreshList(ctx)\n\t\t\t\tif (isCreate && typeof ctx.afterCreateSuccess === 'function') {\n\t\t\t\t\tctx.afterCreateSuccess(saved)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst err = storeError(ctx)\n\t\t\t\tif (err && err.isValidation) {\n\t\t\t\t\t// Keep the form visible so the user can fix the invalid data.\n\t\t\t\t\tctx.setResults.formValidation(err.fields, err.message || 'Validation failed')\n\t\t\t\t} else {\n\t\t\t\t\tctx.setResults.form({ error: (err && err.message) || 'Save failed' })\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tctx.setResults.form({ error: (err && err.message) || 'Save failed' })\n\t\t}\n\t\treturn true\n\t}\n\n\treturn {\n\t\thandleSingleDelete,\n\t\thandleMassDelete,\n\t\thandleSingleCopy,\n\t\thandleMassCopy,\n\t\thandleMassExport,\n\t\thandleMassImport,\n\t\thandleFormSave,\n\t}\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnLinkCardsPage.vue2.js","sources":["../../../../src/components/CnLinkCardsPage/CnLinkCardsPage.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-link-cards-page\" data-testid=\"cn-link-cards-page\">\n\t\t<div class=\"cn-link-cards-page__header\">\n\t\t\t<h2 v-if=\"resolvedTitle\" class=\"cn-link-cards-page__title\">\n\t\t\t\t{{ resolvedTitle }}\n\t\t\t</h2>\n\t\t\t<p v-if=\"resolvedDescription\" class=\"cn-link-cards-page__description\">\n\t\t\t\t{{ resolvedDescription }}\n\t\t\t</p>\n\t\t</div>\n\n\t\t<p\n\t\t\tv-if=\"groups.length === 0\"\n\t\t\tclass=\"cn-link-cards-page__empty\"\n\t\t\tdata-testid=\"cn-link-cards-empty\">\n\t\t\t{{ resolvedEmptyLabel }}\n\t\t</p>\n\n\t\t<section\n\t\t\tv-for=\"group in groups\"\n\t\t\t:key=\"group.key\"\n\t\t\tclass=\"cn-link-cards-page__group\"\n\t\t\t:aria-labelledby=\"group.label ? group.headingId : null\"\n\t\t\t:aria-label=\"group.label ? null : resolvedTitle || null\"\n\t\t\tdata-testid=\"cn-link-cards-group\"\n\t\t\t:data-group=\"group.key\">\n\t\t\t<h3\n\t\t\t\tv-if=\"group.label\"\n\t\t\t\t:id=\"group.headingId\"\n\t\t\t\tclass=\"cn-link-cards-page__caption\">\n\t\t\t\t{{ group.label }}\n\t\t\t</h3>\n\t\t\t<ul class=\"cn-link-cards-page__grid\">\n\t\t\t\t<li v-for=\"card in group.cards\" :key=\"card.id\" class=\"cn-link-cards-page__cell\">\n\t\t\t\t\t<!-- A real <a href>, so the card can be middle-clicked, copied\n\t\t\t\t\t and announced as a link. A plain click on an in-app card\n\t\t\t\t\t is routed, which keeps the page from reloading. -->\n\t\t\t\t\t<a\n\t\t\t\t\t\tclass=\"cn-link-cards-page__card\"\n\t\t\t\t\t\tdata-testid=\"cn-link-card\"\n\t\t\t\t\t\t:data-card-id=\"card.id\"\n\t\t\t\t\t\t:href=\"card.url\"\n\t\t\t\t\t\t:target=\"card.external ? '_blank' : null\"\n\t\t\t\t\t\t:rel=\"card.external ? 'noopener noreferrer' : null\"\n\t\t\t\t\t\t:aria-describedby=\"card.description ? card.descriptionId : null\"\n\t\t\t\t\t\t@click=\"open(card, $event)\">\n\t\t\t\t\t\t<CnIcon\n\t\t\t\t\t\t\tv-if=\"card.icon\"\n\t\t\t\t\t\t\t:name=\"card.icon\"\n\t\t\t\t\t\t\t:size=\"24\"\n\t\t\t\t\t\t\tclass=\"cn-link-cards-page__icon\"\n\t\t\t\t\t\t\taria-hidden=\"true\" />\n\t\t\t\t\t\t<span class=\"cn-link-cards-page__body\">\n\t\t\t\t\t\t\t<span class=\"cn-link-cards-page__label\">{{ card.label }}</span>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"card.description\"\n\t\t\t\t\t\t\t\t:id=\"card.descriptionId\"\n\t\t\t\t\t\t\t\tclass=\"cn-link-cards-page__card-description\">\n\t\t\t\t\t\t\t\t{{ card.description }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</a>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t</section>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { isAppInstalled } from '../../utils/appInstalled.js'\nimport { followLinkClick, resolveHref } from '../../utils/linkNavigation.js'\nimport { passesContextPredicates } from '../../utils/visibleIfContext.js'\nimport { CnIcon } from '../CnIcon/index.js'\n\nlet linkCardsPageUid = 0\n\n/**\n * CnLinkCardsPage: the `type: \"links\"` page. Link cards, grouped under captions.\n *\n * One page that opens every page a short menu leaves out. A card has a\n * label, a one-line description, an optional icon and a target. Groups come\n * from `categories`, in the order they are declared; a card names its group\n * with `category`.\n *\n * ```json\n * { \"id\": \"Modules\", \"route\": \"/modules\", \"type\": \"links\", \"title\": \"Modules and more\",\n * \"config\": {\n * \"description\": \"Every page that is not in the daily menu.\",\n * \"categories\": { \"sales\": \"Sales\", \"marketing\": \"Marketing\" },\n * \"cards\": [\n * { \"id\": \"Leads\", \"label\": \"Leads\", \"description\": \"Every deal you are working on.\",\n * \"icon\": \"CashMultiple\", \"category\": \"sales\", \"route\": \"Leads\" }\n * ]\n * } }\n * ```\n *\n * Every string goes through the app's translate function. A card is hidden\n * by the same `visibleIf` and `permission` a menu entry takes, and a group\n * with no visible card is not drawn.\n */\nexport default {\n\tname: 'CnLinkCardsPage',\n\n\tcomponents: { CnIcon },\n\n\tinject: {\n\t\t/** The app's translate function, provided by CnAppRoot. Identity outside one. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** The effective manifest, for `visibleIf` predicates against `runtime`. */\n\t\tcnManifest: { default: null },\n\t\t/** The permissions the user holds. Empty means the app did not say, and allows. */\n\t\tcnPermissions: { default: () => [] },\n\t},\n\n\tprops: {\n\t\t/** The whole manifest page, for a host that mounts this component itself. CnPageRenderer passes the flattened props below instead. */\n\t\tpage: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * The cards: `{ id, label, description?, icon?, category?, route?, params?, query?, href?, visibleIf?, permission? }`.\n\t\t * `route` is a route name, or a path when it starts with `/`.\n\t\t *\n\t\t * @type {Array<object>|null}\n\t\t */\n\t\tcards: {\n\t\t\ttype: Array,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Group key to caption. Sets the order of the groups.\n\t\t *\n\t\t * @type {Record<string, string>|null}\n\t\t */\n\t\tcategories: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The heading. */\n\t\ttitle: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The lead paragraph under the heading. */\n\t\tdescription: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Text shown when no card is visible. */\n\t\temptyLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Translate function. Falls back to the injected `cnTranslate`.\n\t\t *\n\t\t * @type {((key: string) => string)|null}\n\t\t */\n\t\ttranslate: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\tdata() {\n\t\tlinkCardsPageUid += 1\n\t\treturn {\n\t\t\tuid: linkCardsPageUid,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The config, from the flattened props or from `page.config`. Props win.\n\t\t *\n\t\t * @return {object} The config.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-links-page-renders-grouped-link-cards\n\t\t */\n\t\tconfig() {\n\t\t\tconst fromPage = (this.page && this.page.config) || {}\n\t\t\tconst fromProps = {}\n\t\t\tfor (const key of ['cards', 'categories', 'description', 'title', 'emptyLabel']) {\n\t\t\t\tif (this[key] !== null && this[key] !== undefined) {\n\t\t\t\t\tfromProps[key] = this[key]\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { ...fromPage, ...fromProps }\n\t\t},\n\n\t\t/**\n\t\t * The heading, translated.\n\t\t *\n\t\t * @return {string} The title, or '' when the page declares none.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-links-page-renders-grouped-link-cards\n\t\t */\n\t\tresolvedTitle() {\n\t\t\treturn this.tr(this.config.title || (this.page && this.page.title) || '')\n\t\t},\n\n\t\t/**\n\t\t * The lead paragraph, translated.\n\t\t *\n\t\t * @return {string} The description.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-links-page-renders-grouped-link-cards\n\t\t */\n\t\tresolvedDescription() {\n\t\t\treturn this.tr(this.config.description || '')\n\t\t},\n\n\t\t/**\n\t\t * The empty-state text.\n\t\t *\n\t\t * @return {string} The text.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-links-page-renders-grouped-link-cards\n\t\t */\n\t\tresolvedEmptyLabel() {\n\t\t\treturn this.config.emptyLabel ? this.tr(this.config.emptyLabel) : t('nextcloud-vue', 'Nothing to open here.')\n\t\t},\n\n\t\t/**\n\t\t * The cards a reader may see and can open, with text translated and\n\t\t * the link resolved. A card that is gated away, or has no target that\n\t\t * resolves, is left out: a card that does nothing is worse than none.\n\t\t *\n\t\t * @return {Array<object>} The cards.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-card-is-a-real-link\n\t\t */\n\t\tvisibleCards() {\n\t\t\tconst declared = Array.isArray(this.config.cards) ? this.config.cards : []\n\t\t\tconst runtime = this.cnManifest?.runtime ?? null\n\t\t\treturn declared\n\t\t\t\t.filter((card) => card && card.id && card.label)\n\t\t\t\t.filter((card) => this.passesPermission(card) && this.passesVisibleIf(card, runtime))\n\t\t\t\t.map((card) => {\n\t\t\t\t\tconst target = this.targetOf(card)\n\t\t\t\t\treturn {\n\t\t\t\t\t\tid: String(card.id),\n\t\t\t\t\t\tlabel: this.tr(card.label),\n\t\t\t\t\t\tdescription: this.tr(card.description || ''),\n\t\t\t\t\t\tdescriptionId: `cn-link-card-desc-${this.uid}-${String(card.id).replace(/[^A-Za-z0-9_-]/g, '-')}`,\n\t\t\t\t\t\ticon: card.icon || '',\n\t\t\t\t\t\tcategory: card.category || '',\n\t\t\t\t\t\texternal: target.external,\n\t\t\t\t\t\tlocation: target.location,\n\t\t\t\t\t\turl: target.url,\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.filter((card) => card.url !== '')\n\t\t},\n\n\t\t/**\n\t\t * The groups to draw: ungrouped cards first under no caption, then\n\t\t * one group per category in declaration order. A group without a\n\t\t * visible card is left out.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, headingId: string, cards: Array<object>}>} The groups.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-links-page-renders-grouped-link-cards\n\t\t */\n\t\tgroups() {\n\t\t\tconst declared = (this.config.categories && typeof this.config.categories === 'object') ? this.config.categories : {}\n\t\t\tconst keys = Object.keys(declared)\n\t\t\tconst loose = this.visibleCards.filter((card) => !keys.includes(card.category))\n\t\t\tconst groups = loose.length > 0 ? [{ key: '', label: '', headingId: '', cards: loose }] : []\n\t\t\tkeys.forEach((key, index) => {\n\t\t\t\tconst cards = this.visibleCards.filter((card) => card.category === key)\n\t\t\t\tif (cards.length > 0) {\n\t\t\t\t\tgroups.push({\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tlabel: this.tr(declared[key]),\n\t\t\t\t\t\theadingId: `cn-link-cards-group-${this.uid}-${index}`,\n\t\t\t\t\t\tcards,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\t\t\treturn groups\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Translate a manifest string through the app's translate function.\n\t\t *\n\t\t * @param {string} value The declared string.\n\t\t * @return {string} The translation, or the input unchanged.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-links-page-renders-grouped-link-cards\n\t\t */\n\t\ttr(value) {\n\t\t\tif (typeof value !== 'string' || value === '') {\n\t\t\t\treturn value\n\t\t\t}\n\t\t\treturn (this.translate ?? this.cnTranslate)(value)\n\t\t},\n\n\t\t/**\n\t\t * Whether the user holds the card's `permission`. Same rule as the\n\t\t * menu: no permission declared, or no permissions known, allows.\n\t\t *\n\t\t * @param {object} card The card.\n\t\t * @return {boolean} True when the card may render.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-cards-honour-menu-visibility-conditions\n\t\t */\n\t\tpassesPermission(card) {\n\t\t\tconst held = Array.isArray(this.cnPermissions) ? this.cnPermissions : []\n\t\t\tif (!card.permission || held.length === 0) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn held.includes(card.permission)\n\t\t},\n\n\t\t/**\n\t\t * Evaluate a card's `visibleIf` the way CnAppNav evaluates a menu\n\t\t * entry's: `appInstalled` first, then dot-path predicates against\n\t\t * the manifest's `runtime`.\n\t\t *\n\t\t * @param {object} card The card.\n\t\t * @param {object|null} runtime `manifest.runtime`, or null.\n\t\t * @return {boolean} True when the card may render.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-cards-honour-menu-visibility-conditions\n\t\t */\n\t\tpassesVisibleIf(card, runtime) {\n\t\t\tconst condition = card.visibleIf\n\t\t\tif (!condition || typeof condition !== 'object') {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif (condition.appInstalled && !isAppInstalled(condition.appInstalled)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn passesContextPredicates(condition, runtime)\n\t\t},\n\n\t\t/**\n\t\t * Where a card goes. An `href` is an external URL. A `route` is a\n\t\t * route name, or a path when it starts with `/`, resolved by the\n\t\t * router so the link carries the app's base with or without\n\t\t * `/index.php`.\n\t\t *\n\t\t * @param {object} card The card.\n\t\t * @return {{external: boolean, location: object|null, url: string}} The target; `url` is '' when it cannot be resolved.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-card-is-a-real-link\n\t\t */\n\t\ttargetOf(card) {\n\t\t\tif (typeof card.route === 'string' && card.route !== '') {\n\t\t\t\tconst location = card.route.startsWith('/') ? { path: card.route } : { name: card.route }\n\t\t\t\tif (card.params && typeof card.params === 'object') {\n\t\t\t\t\tlocation.params = card.params\n\t\t\t\t}\n\t\t\t\tif (card.query && typeof card.query === 'object') {\n\t\t\t\t\tlocation.query = card.query\n\t\t\t\t}\n\t\t\t\treturn { external: false, location, url: resolveHref(location, this.$router) }\n\t\t\t}\n\t\t\tif (typeof card.href === 'string' && card.href !== '') {\n\t\t\t\treturn { external: true, location: null, url: card.href }\n\t\t\t}\n\t\t\treturn { external: false, location: null, url: '' }\n\t\t},\n\n\t\t/**\n\t\t * Route a plain click on an in-app card. A modified or middle click,\n\t\t * and every click on an external card, is left to the browser.\n\t\t *\n\t\t * @param {object} card The resolved card.\n\t\t * @param {MouseEvent} event The click.\n\t\t * @return {void}\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-card-is-a-real-link\n\t\t */\n\t\topen(card, event) {\n\t\t\tif (card.external || !card.location) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfollowLinkClick(event, card.location, this.$router)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-link-cards-page {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: calc(6 * var(--default-grid-baseline));\n\tpadding: calc(5 * var(--default-grid-baseline));\n}\n\n.cn-link-cards-page__title {\n\tmargin: 0 0 var(--default-grid-baseline);\n}\n\n.cn-link-cards-page__description,\n.cn-link-cards-page__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tmargin: 0;\n\tmax-width: 70ch;\n}\n\n.cn-link-cards-page__group {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: calc(3 * var(--default-grid-baseline));\n}\n\n.cn-link-cards-page__caption {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85em;\n\tfont-weight: 600;\n\tletter-spacing: 0.06em;\n\tmargin: 0;\n\ttext-transform: uppercase;\n}\n\n.cn-link-cards-page__grid {\n\tdisplay: grid;\n\tgap: calc(3 * var(--default-grid-baseline));\n\tgrid-template-columns: repeat(auto-fill, minmax(240px, 1fr));\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-link-cards-page__cell {\n\tdisplay: flex;\n}\n\n.cn-link-cards-page__card {\n\talign-items: flex-start;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border-dark);\n\tborder-radius: var(--border-radius-large);\n\tcolor: var(--color-main-text);\n\tdisplay: flex;\n\tflex: 1;\n\tgap: calc(3 * var(--default-grid-baseline));\n\tmin-width: 0;\n\tpadding: calc(4 * var(--default-grid-baseline));\n\ttext-decoration: none;\n}\n\n.cn-link-cards-page__card:hover {\n\tbackground: var(--color-background-hover);\n\tborder-color: var(--color-primary-element);\n}\n\n.cn-link-cards-page__card:focus-visible {\n\tborder-color: var(--color-primary-element);\n\toutline: 2px solid var(--color-primary-element);\n\toutline-offset: 2px;\n}\n\n.cn-link-cards-page__icon {\n\tcolor: var(--color-primary-element);\n\tflex: none;\n}\n\n.cn-link-cards-page__body {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: var(--default-grid-baseline);\n\tmin-width: 0;\n}\n\n.cn-link-cards-page__label {\n\tfont-weight: 600;\n\toverflow-wrap: anywhere;\n}\n\n.cn-link-cards-page__card-description {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tline-height: 1.4;\n}\n</style>\n"],"names":["CnIcon","t"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnLinkCardsPage.vue2.js","sources":["../../../../src/components/CnLinkCardsPage/CnLinkCardsPage.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-link-cards-page\" data-testid=\"cn-link-cards-page\">\n\t\t<div class=\"cn-link-cards-page__header\">\n\t\t\t<h2 v-if=\"resolvedTitle\" class=\"cn-link-cards-page__title\">\n\t\t\t\t{{ resolvedTitle }}\n\t\t\t</h2>\n\t\t\t<p v-if=\"resolvedDescription\" class=\"cn-link-cards-page__description\">\n\t\t\t\t{{ resolvedDescription }}\n\t\t\t</p>\n\t\t</div>\n\n\t\t<p\n\t\t\tv-if=\"groups.length === 0\"\n\t\t\tclass=\"cn-link-cards-page__empty\"\n\t\t\tdata-testid=\"cn-link-cards-empty\">\n\t\t\t{{ resolvedEmptyLabel }}\n\t\t</p>\n\n\t\t<section\n\t\t\tv-for=\"group in groups\"\n\t\t\t:key=\"group.key\"\n\t\t\tclass=\"cn-link-cards-page__group\"\n\t\t\t:aria-labelledby=\"group.label ? group.headingId : null\"\n\t\t\t:aria-label=\"group.label ? null : resolvedTitle || null\"\n\t\t\tdata-testid=\"cn-link-cards-group\"\n\t\t\t:data-group=\"group.key\">\n\t\t\t<h3\n\t\t\t\tv-if=\"group.label\"\n\t\t\t\t:id=\"group.headingId\"\n\t\t\t\tclass=\"cn-link-cards-page__caption\">\n\t\t\t\t{{ group.label }}\n\t\t\t</h3>\n\t\t\t<ul class=\"cn-link-cards-page__grid\">\n\t\t\t\t<li v-for=\"card in group.cards\" :key=\"card.id\" class=\"cn-link-cards-page__cell\">\n\t\t\t\t\t<!-- A real <a href>, so the card can be middle-clicked, copied\n\t\t\t\t\t and announced as a link. A plain click on an in-app card\n\t\t\t\t\t is routed, which keeps the page from reloading. -->\n\t\t\t\t\t<a\n\t\t\t\t\t\tclass=\"cn-link-cards-page__card\"\n\t\t\t\t\t\tdata-testid=\"cn-link-card\"\n\t\t\t\t\t\t:data-card-id=\"card.id\"\n\t\t\t\t\t\t:href=\"card.url\"\n\t\t\t\t\t\t:target=\"card.external ? '_blank' : null\"\n\t\t\t\t\t\t:rel=\"card.external ? 'noopener noreferrer' : null\"\n\t\t\t\t\t\t:aria-describedby=\"card.description ? card.descriptionId : null\"\n\t\t\t\t\t\t@click=\"open(card, $event)\">\n\t\t\t\t\t\t<CnIcon\n\t\t\t\t\t\t\tv-if=\"card.icon\"\n\t\t\t\t\t\t\t:name=\"card.icon\"\n\t\t\t\t\t\t\t:size=\"24\"\n\t\t\t\t\t\t\tclass=\"cn-link-cards-page__icon\"\n\t\t\t\t\t\t\taria-hidden=\"true\" />\n\t\t\t\t\t\t<span class=\"cn-link-cards-page__body\">\n\t\t\t\t\t\t\t<span class=\"cn-link-cards-page__label\">{{ card.label }}</span>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"card.description\"\n\t\t\t\t\t\t\t\t:id=\"card.descriptionId\"\n\t\t\t\t\t\t\t\tclass=\"cn-link-cards-page__card-description\">\n\t\t\t\t\t\t\t\t{{ card.description }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</a>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t</section>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { isAppInstalled } from '../../utils/appInstalled.js'\nimport { followLinkClick, resolveHref } from '../../utils/linkNavigation.js'\nimport { passesContextPredicates } from '../../utils/visibleIfContext.js'\nimport { CnIcon } from '../CnIcon/index.js'\n\nlet linkCardsPageUid = 0\n\n/**\n * CnLinkCardsPage: the `type: \"links\"` page. Link cards, grouped under captions.\n *\n * One page that opens every page a short menu leaves out. A card has a\n * label, a one-line description, an optional icon and a target. Groups come\n * from `categories`, in the order they are declared; a card names its group\n * with `category`.\n *\n * ```json\n * { \"id\": \"Modules\", \"route\": \"/modules\", \"type\": \"links\", \"title\": \"Modules and more\",\n * \"config\": {\n * \"description\": \"Every page that is not in the daily menu.\",\n * \"categories\": { \"sales\": \"Sales\", \"marketing\": \"Marketing\" },\n * \"cards\": [\n * { \"id\": \"Leads\", \"label\": \"Leads\", \"description\": \"Every deal you are working on.\",\n * \"icon\": \"CashMultiple\", \"category\": \"sales\", \"route\": \"Leads\" }\n * ]\n * } }\n * ```\n *\n * Every string goes through the app's translate function. A card is hidden\n * by the same `visibleIf` and `permission` a menu entry takes, and a group\n * with no visible card is not drawn.\n */\nexport default {\n\tname: 'CnLinkCardsPage',\n\n\tcomponents: { CnIcon },\n\n\tinject: {\n\t\t/** The app's translate function, provided by CnAppRoot. Identity outside one. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** The effective manifest, for `visibleIf` predicates against `runtime`. */\n\t\tcnManifest: { default: null },\n\t\t/** The permissions the user holds. Empty means the app did not say, and allows. */\n\t\tcnPermissions: { default: () => [] },\n\t},\n\n\tprops: {\n\t\t/** The whole manifest page, for a host that mounts this component itself. CnPageRenderer passes the flattened props below instead. */\n\t\tpage: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * The cards: `{ id, label, description?, icon?, category?, route?, params?, query?, href?, visibleIf?, permission? }`.\n\t\t * `route` is a route name, or a path when it starts with `/`.\n\t\t *\n\t\t * @type {Array<object>|null}\n\t\t */\n\t\tcards: {\n\t\t\ttype: Array,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Group key to caption. Sets the order of the groups.\n\t\t *\n\t\t * @type {Record<string, string>|null}\n\t\t */\n\t\tcategories: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The heading. */\n\t\ttitle: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The lead paragraph under the heading. */\n\t\tdescription: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Text shown when no card is visible. */\n\t\temptyLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Translate function. Falls back to the injected `cnTranslate`.\n\t\t *\n\t\t * @type {((key: string) => string)|null}\n\t\t */\n\t\ttranslate: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\tdata() {\n\t\tlinkCardsPageUid += 1\n\t\treturn {\n\t\t\tuid: linkCardsPageUid,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The config, from the flattened props or from `page.config`. Props win.\n\t\t *\n\t\t * @return {object} The config.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-links-page-renders-grouped-link-cards\n\t\t */\n\t\tconfig() {\n\t\t\tconst fromPage = (this.page && this.page.config) || {}\n\t\t\tconst fromProps = {}\n\t\t\tfor (const key of ['cards', 'categories', 'description', 'title', 'emptyLabel']) {\n\t\t\t\tif (this[key] !== null && this[key] !== undefined) {\n\t\t\t\t\tfromProps[key] = this[key]\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { ...fromPage, ...fromProps }\n\t\t},\n\n\t\t/**\n\t\t * The heading, translated.\n\t\t *\n\t\t * @return {string} The title, or '' when the page declares none.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-links-page-renders-grouped-link-cards\n\t\t */\n\t\tresolvedTitle() {\n\t\t\treturn this.tr(this.config.title || (this.page && this.page.title) || '')\n\t\t},\n\n\t\t/**\n\t\t * The lead paragraph, translated.\n\t\t *\n\t\t * @return {string} The description.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-links-page-renders-grouped-link-cards\n\t\t */\n\t\tresolvedDescription() {\n\t\t\treturn this.tr(this.config.description || '')\n\t\t},\n\n\t\t/**\n\t\t * The empty-state text.\n\t\t *\n\t\t * @return {string} The text.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-links-page-renders-grouped-link-cards\n\t\t */\n\t\tresolvedEmptyLabel() {\n\t\t\treturn this.config.emptyLabel ? this.tr(this.config.emptyLabel) : t('nextcloud-vue', 'Nothing to open here.')\n\t\t},\n\n\t\t/**\n\t\t * The cards a reader may see and can open, with text translated and\n\t\t * the link resolved. A card that is gated away, or has no target that\n\t\t * resolves, is left out: a card that does nothing is worse than none.\n\t\t *\n\t\t * @return {Array<object>} The cards.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-card-is-a-real-link\n\t\t */\n\t\tvisibleCards() {\n\t\t\tconst declared = Array.isArray(this.config.cards) ? this.config.cards : []\n\t\t\tconst runtime = this.cnManifest?.runtime ?? null\n\t\t\treturn declared\n\t\t\t\t.filter((card) => card && card.id && card.label)\n\t\t\t\t.filter((card) => this.passesPermission(card) && this.passesVisibleIf(card, runtime))\n\t\t\t\t.map((card) => {\n\t\t\t\t\tconst target = this.targetOf(card)\n\t\t\t\t\treturn {\n\t\t\t\t\t\tid: String(card.id),\n\t\t\t\t\t\tlabel: this.tr(card.label),\n\t\t\t\t\t\tdescription: this.tr(card.description || ''),\n\t\t\t\t\t\tdescriptionId: `cn-link-card-desc-${this.uid}-${String(card.id).replace(/[^A-Za-z0-9_-]/g, '-')}`,\n\t\t\t\t\t\ticon: card.icon || '',\n\t\t\t\t\t\tcategory: card.category || '',\n\t\t\t\t\t\texternal: target.external,\n\t\t\t\t\t\tlocation: target.location,\n\t\t\t\t\t\turl: target.url,\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.filter((card) => card.url !== '')\n\t\t},\n\n\t\t/**\n\t\t * The groups to draw: ungrouped cards first under no caption, then\n\t\t * one group per category in declaration order. A group without a\n\t\t * visible card is left out.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, headingId: string, cards: Array<object>}>} The groups.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-links-page-renders-grouped-link-cards\n\t\t */\n\t\tgroups() {\n\t\t\tconst declared = (this.config.categories && typeof this.config.categories === 'object') ? this.config.categories : {}\n\t\t\tconst keys = Object.keys(declared)\n\t\t\tconst loose = this.visibleCards.filter((card) => !keys.includes(card.category))\n\t\t\tconst groups = loose.length > 0 ? [{ key: '', label: '', headingId: '', cards: loose }] : []\n\t\t\tkeys.forEach((key, index) => {\n\t\t\t\tconst cards = this.visibleCards.filter((card) => card.category === key)\n\t\t\t\tif (cards.length > 0) {\n\t\t\t\t\tgroups.push({\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tlabel: this.tr(declared[key]),\n\t\t\t\t\t\theadingId: `cn-link-cards-group-${this.uid}-${index}`,\n\t\t\t\t\t\tcards,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\t\t\treturn groups\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Translate a manifest string through the app's translate function.\n\t\t *\n\t\t * @param {string} value The declared string.\n\t\t * @return {string} The translation, or the input unchanged.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-links-page-renders-grouped-link-cards\n\t\t */\n\t\ttr(value) {\n\t\t\tif (typeof value !== 'string' || value === '') {\n\t\t\t\treturn value\n\t\t\t}\n\t\t\treturn (this.translate ?? this.cnTranslate)(value)\n\t\t},\n\n\t\t/**\n\t\t * Whether the user holds the card's `permission`. Same rule as the\n\t\t * menu: no permission declared, or no permissions known, allows.\n\t\t *\n\t\t * @param {object} card The card.\n\t\t * @return {boolean} True when the card may render.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-cards-honour-menu-visibility-conditions\n\t\t */\n\t\tpassesPermission(card) {\n\t\t\tconst held = Array.isArray(this.cnPermissions) ? this.cnPermissions : []\n\t\t\tif (!card.permission || held.length === 0) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn held.includes(card.permission)\n\t\t},\n\n\t\t/**\n\t\t * Evaluate a card's `visibleIf` the way CnAppNav evaluates a menu\n\t\t * entry's: `appInstalled` first, then dot-path predicates against\n\t\t * the manifest's `runtime`.\n\t\t *\n\t\t * @param {object} card The card.\n\t\t * @param {object|null} runtime `manifest.runtime`, or null.\n\t\t * @return {boolean} True when the card may render.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-cards-honour-menu-visibility-conditions\n\t\t */\n\t\tpassesVisibleIf(card, runtime) {\n\t\t\tconst condition = card.visibleIf\n\t\t\tif (!condition || typeof condition !== 'object') {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif (condition.appInstalled && !isAppInstalled(condition.appInstalled)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn passesContextPredicates(condition, runtime)\n\t\t},\n\n\t\t/**\n\t\t * Where a card goes. An `href` is an external URL. A `route` is a\n\t\t * route name, or a path when it starts with `/`, resolved by the\n\t\t * router so the link carries the app's base with or without\n\t\t * `/index.php`.\n\t\t *\n\t\t * @param {object} card The card.\n\t\t * @return {{external: boolean, location: object|null, url: string}} The target; `url` is '' when it cannot be resolved.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-card-is-a-real-link\n\t\t */\n\t\ttargetOf(card) {\n\t\t\tif (typeof card.route === 'string' && card.route !== '') {\n\t\t\t\tconst location = card.route.startsWith('/') ? { path: card.route } : { name: card.route }\n\t\t\t\tif (card.params && typeof card.params === 'object') {\n\t\t\t\t\tlocation.params = card.params\n\t\t\t\t}\n\t\t\t\tif (card.query && typeof card.query === 'object') {\n\t\t\t\t\tlocation.query = card.query\n\t\t\t\t}\n\t\t\t\treturn { external: false, location, url: resolveHref(location, this.$router) }\n\t\t\t}\n\t\t\tif (typeof card.href === 'string' && card.href !== '') {\n\t\t\t\treturn { external: true, location: null, url: card.href }\n\t\t\t}\n\t\t\treturn { external: false, location: null, url: '' }\n\t\t},\n\n\t\t/**\n\t\t * Route a plain click on an in-app card. A modified or middle click,\n\t\t * and every click on an external card, is left to the browser.\n\t\t *\n\t\t * @param {object} card The resolved card.\n\t\t * @param {MouseEvent} event The click.\n\t\t * @return {void}\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-card-is-a-real-link\n\t\t */\n\t\topen(card, event) {\n\t\t\tif (card.external || !card.location) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfollowLinkClick(event, card.location, this.$router)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-link-cards-page {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: calc(6 * var(--default-grid-baseline));\n\tpadding: calc(5 * var(--default-grid-baseline));\n}\n\n/* Clear the Nextcloud navigation toggle button (44px wide, absolutely\n positioned at the left edge of .app-content) plus 12px breathing room, as\n CnPageHeader does. Only the header shifts; the cards keep the full width. */\n.cn-link-cards-page__header {\n\tpadding-inline-start: 56px;\n}\n\n.cn-link-cards-page__title {\n\tmargin: 0 0 var(--default-grid-baseline);\n}\n\n.cn-link-cards-page__description,\n.cn-link-cards-page__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tmargin: 0;\n\tmax-width: 70ch;\n}\n\n.cn-link-cards-page__group {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: calc(3 * var(--default-grid-baseline));\n}\n\n.cn-link-cards-page__caption {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85em;\n\tfont-weight: 600;\n\tletter-spacing: 0.06em;\n\tmargin: 0;\n\ttext-transform: uppercase;\n}\n\n.cn-link-cards-page__grid {\n\tdisplay: grid;\n\tgap: calc(3 * var(--default-grid-baseline));\n\tgrid-template-columns: repeat(auto-fill, minmax(240px, 1fr));\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-link-cards-page__cell {\n\tdisplay: flex;\n}\n\n.cn-link-cards-page__card {\n\talign-items: flex-start;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border-dark);\n\tborder-radius: var(--border-radius-large);\n\tcolor: var(--color-main-text);\n\tdisplay: flex;\n\tflex: 1;\n\tgap: calc(3 * var(--default-grid-baseline));\n\tmin-width: 0;\n\tpadding: calc(4 * var(--default-grid-baseline));\n\ttransition: background-color var(--animation-quick, 100ms) ease, border-color var(--animation-quick, 100ms) ease, box-shadow var(--animation-quick, 100ms) ease;\n}\n\n/* A card is a link, and it must not look like a line of prose. A theme may\n underline every link with `!important` (thematiq: `a { text-decoration:\n underline !important }`, specificity 0,0,1). Among `!important` rules the\n higher specificity wins, so this class rule has to carry it too; without it\n the label and the description both rendered underlined on a live Nextcloud. */\n.cn-link-cards-page__card,\n.cn-link-cards-page__card:hover,\n.cn-link-cards-page__card:focus,\n.cn-link-cards-page__card:active {\n\ttext-decoration: none !important;\n}\n\n/* Hover moves the whole card: border, background and a soft lift. */\n.cn-link-cards-page__card:hover {\n\tbackground: var(--color-background-hover);\n\tborder-color: var(--color-primary-element);\n\tbox-shadow: 0 2px 6px var(--color-box-shadow);\n}\n\n@media (prefers-reduced-motion: reduce) {\n\t.cn-link-cards-page__card {\n\t\ttransition: none;\n\t}\n}\n\n.cn-link-cards-page__card:focus-visible {\n\tborder-color: var(--color-primary-element);\n\toutline: 2px solid var(--color-primary-element);\n\toutline-offset: 2px;\n}\n\n.cn-link-cards-page__icon {\n\tcolor: var(--color-primary-element);\n\tflex: none;\n}\n\n.cn-link-cards-page__body {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: var(--default-grid-baseline);\n\tmin-width: 0;\n}\n\n/* The label is the card's title. Its colour is set here, on the span, because\n a theme may force its link colour on the `a` itself; an inherited link\n colour made the title read as a hyperlink. */\n.cn-link-cards-page__label {\n\tcolor: var(--color-main-text);\n\tfont-size: 1.05em;\n\tfont-weight: 700;\n\tline-height: 1.3;\n\toverflow-wrap: anywhere;\n}\n\n.cn-link-cards-page__card-description {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tline-height: 1.4;\n}\n</style>\n"],"names":["CnIcon","t"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnLinkCardsPage.vue3.js","sources":["../../../../src/components/CnLinkCardsPage/CnLinkCardsPage.vue?vue&type=template&id=2f9a546b&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-link-cards-page\" data-testid=\"cn-link-cards-page\">\n\t\t<div class=\"cn-link-cards-page__header\">\n\t\t\t<h2 v-if=\"resolvedTitle\" class=\"cn-link-cards-page__title\">\n\t\t\t\t{{ resolvedTitle }}\n\t\t\t</h2>\n\t\t\t<p v-if=\"resolvedDescription\" class=\"cn-link-cards-page__description\">\n\t\t\t\t{{ resolvedDescription }}\n\t\t\t</p>\n\t\t</div>\n\n\t\t<p\n\t\t\tv-if=\"groups.length === 0\"\n\t\t\tclass=\"cn-link-cards-page__empty\"\n\t\t\tdata-testid=\"cn-link-cards-empty\">\n\t\t\t{{ resolvedEmptyLabel }}\n\t\t</p>\n\n\t\t<section\n\t\t\tv-for=\"group in groups\"\n\t\t\t:key=\"group.key\"\n\t\t\tclass=\"cn-link-cards-page__group\"\n\t\t\t:aria-labelledby=\"group.label ? group.headingId : null\"\n\t\t\t:aria-label=\"group.label ? null : resolvedTitle || null\"\n\t\t\tdata-testid=\"cn-link-cards-group\"\n\t\t\t:data-group=\"group.key\">\n\t\t\t<h3\n\t\t\t\tv-if=\"group.label\"\n\t\t\t\t:id=\"group.headingId\"\n\t\t\t\tclass=\"cn-link-cards-page__caption\">\n\t\t\t\t{{ group.label }}\n\t\t\t</h3>\n\t\t\t<ul class=\"cn-link-cards-page__grid\">\n\t\t\t\t<li v-for=\"card in group.cards\" :key=\"card.id\" class=\"cn-link-cards-page__cell\">\n\t\t\t\t\t<!-- A real <a href>, so the card can be middle-clicked, copied\n\t\t\t\t\t and announced as a link. A plain click on an in-app card\n\t\t\t\t\t is routed, which keeps the page from reloading. -->\n\t\t\t\t\t<a\n\t\t\t\t\t\tclass=\"cn-link-cards-page__card\"\n\t\t\t\t\t\tdata-testid=\"cn-link-card\"\n\t\t\t\t\t\t:data-card-id=\"card.id\"\n\t\t\t\t\t\t:href=\"card.url\"\n\t\t\t\t\t\t:target=\"card.external ? '_blank' : null\"\n\t\t\t\t\t\t:rel=\"card.external ? 'noopener noreferrer' : null\"\n\t\t\t\t\t\t:aria-describedby=\"card.description ? card.descriptionId : null\"\n\t\t\t\t\t\t@click=\"open(card, $event)\">\n\t\t\t\t\t\t<CnIcon\n\t\t\t\t\t\t\tv-if=\"card.icon\"\n\t\t\t\t\t\t\t:name=\"card.icon\"\n\t\t\t\t\t\t\t:size=\"24\"\n\t\t\t\t\t\t\tclass=\"cn-link-cards-page__icon\"\n\t\t\t\t\t\t\taria-hidden=\"true\" />\n\t\t\t\t\t\t<span class=\"cn-link-cards-page__body\">\n\t\t\t\t\t\t\t<span class=\"cn-link-cards-page__label\">{{ card.label }}</span>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"card.description\"\n\t\t\t\t\t\t\t\t:id=\"card.descriptionId\"\n\t\t\t\t\t\t\t\tclass=\"cn-link-cards-page__card-description\">\n\t\t\t\t\t\t\t\t{{ card.description }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</a>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t</section>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { isAppInstalled } from '../../utils/appInstalled.js'\nimport { followLinkClick, resolveHref } from '../../utils/linkNavigation.js'\nimport { passesContextPredicates } from '../../utils/visibleIfContext.js'\nimport { CnIcon } from '../CnIcon/index.js'\n\nlet linkCardsPageUid = 0\n\n/**\n * CnLinkCardsPage: the `type: \"links\"` page. Link cards, grouped under captions.\n *\n * One page that opens every page a short menu leaves out. A card has a\n * label, a one-line description, an optional icon and a target. Groups come\n * from `categories`, in the order they are declared; a card names its group\n * with `category`.\n *\n * ```json\n * { \"id\": \"Modules\", \"route\": \"/modules\", \"type\": \"links\", \"title\": \"Modules and more\",\n * \"config\": {\n * \"description\": \"Every page that is not in the daily menu.\",\n * \"categories\": { \"sales\": \"Sales\", \"marketing\": \"Marketing\" },\n * \"cards\": [\n * { \"id\": \"Leads\", \"label\": \"Leads\", \"description\": \"Every deal you are working on.\",\n * \"icon\": \"CashMultiple\", \"category\": \"sales\", \"route\": \"Leads\" }\n * ]\n * } }\n * ```\n *\n * Every string goes through the app's translate function. A card is hidden\n * by the same `visibleIf` and `permission` a menu entry takes, and a group\n * with no visible card is not drawn.\n */\nexport default {\n\tname: 'CnLinkCardsPage',\n\n\tcomponents: { CnIcon },\n\n\tinject: {\n\t\t/** The app's translate function, provided by CnAppRoot. Identity outside one. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** The effective manifest, for `visibleIf` predicates against `runtime`. */\n\t\tcnManifest: { default: null },\n\t\t/** The permissions the user holds. Empty means the app did not say, and allows. */\n\t\tcnPermissions: { default: () => [] },\n\t},\n\n\tprops: {\n\t\t/** The whole manifest page, for a host that mounts this component itself. CnPageRenderer passes the flattened props below instead. */\n\t\tpage: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * The cards: `{ id, label, description?, icon?, category?, route?, params?, query?, href?, visibleIf?, permission? }`.\n\t\t * `route` is a route name, or a path when it starts with `/`.\n\t\t *\n\t\t * @type {Array<object>|null}\n\t\t */\n\t\tcards: {\n\t\t\ttype: Array,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Group key to caption. Sets the order of the groups.\n\t\t *\n\t\t * @type {Record<string, string>|null}\n\t\t */\n\t\tcategories: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The heading. */\n\t\ttitle: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The lead paragraph under the heading. */\n\t\tdescription: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Text shown when no card is visible. */\n\t\temptyLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Translate function. Falls back to the injected `cnTranslate`.\n\t\t *\n\t\t * @type {((key: string) => string)|null}\n\t\t */\n\t\ttranslate: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\tdata() {\n\t\tlinkCardsPageUid += 1\n\t\treturn {\n\t\t\tuid: linkCardsPageUid,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The config, from the flattened props or from `page.config`. Props win.\n\t\t *\n\t\t * @return {object} The config.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-links-page-renders-grouped-link-cards\n\t\t */\n\t\tconfig() {\n\t\t\tconst fromPage = (this.page && this.page.config) || {}\n\t\t\tconst fromProps = {}\n\t\t\tfor (const key of ['cards', 'categories', 'description', 'title', 'emptyLabel']) {\n\t\t\t\tif (this[key] !== null && this[key] !== undefined) {\n\t\t\t\t\tfromProps[key] = this[key]\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { ...fromPage, ...fromProps }\n\t\t},\n\n\t\t/**\n\t\t * The heading, translated.\n\t\t *\n\t\t * @return {string} The title, or '' when the page declares none.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-links-page-renders-grouped-link-cards\n\t\t */\n\t\tresolvedTitle() {\n\t\t\treturn this.tr(this.config.title || (this.page && this.page.title) || '')\n\t\t},\n\n\t\t/**\n\t\t * The lead paragraph, translated.\n\t\t *\n\t\t * @return {string} The description.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-links-page-renders-grouped-link-cards\n\t\t */\n\t\tresolvedDescription() {\n\t\t\treturn this.tr(this.config.description || '')\n\t\t},\n\n\t\t/**\n\t\t * The empty-state text.\n\t\t *\n\t\t * @return {string} The text.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-links-page-renders-grouped-link-cards\n\t\t */\n\t\tresolvedEmptyLabel() {\n\t\t\treturn this.config.emptyLabel ? this.tr(this.config.emptyLabel) : t('nextcloud-vue', 'Nothing to open here.')\n\t\t},\n\n\t\t/**\n\t\t * The cards a reader may see and can open, with text translated and\n\t\t * the link resolved. A card that is gated away, or has no target that\n\t\t * resolves, is left out: a card that does nothing is worse than none.\n\t\t *\n\t\t * @return {Array<object>} The cards.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-card-is-a-real-link\n\t\t */\n\t\tvisibleCards() {\n\t\t\tconst declared = Array.isArray(this.config.cards) ? this.config.cards : []\n\t\t\tconst runtime = this.cnManifest?.runtime ?? null\n\t\t\treturn declared\n\t\t\t\t.filter((card) => card && card.id && card.label)\n\t\t\t\t.filter((card) => this.passesPermission(card) && this.passesVisibleIf(card, runtime))\n\t\t\t\t.map((card) => {\n\t\t\t\t\tconst target = this.targetOf(card)\n\t\t\t\t\treturn {\n\t\t\t\t\t\tid: String(card.id),\n\t\t\t\t\t\tlabel: this.tr(card.label),\n\t\t\t\t\t\tdescription: this.tr(card.description || ''),\n\t\t\t\t\t\tdescriptionId: `cn-link-card-desc-${this.uid}-${String(card.id).replace(/[^A-Za-z0-9_-]/g, '-')}`,\n\t\t\t\t\t\ticon: card.icon || '',\n\t\t\t\t\t\tcategory: card.category || '',\n\t\t\t\t\t\texternal: target.external,\n\t\t\t\t\t\tlocation: target.location,\n\t\t\t\t\t\turl: target.url,\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.filter((card) => card.url !== '')\n\t\t},\n\n\t\t/**\n\t\t * The groups to draw: ungrouped cards first under no caption, then\n\t\t * one group per category in declaration order. A group without a\n\t\t * visible card is left out.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, headingId: string, cards: Array<object>}>} The groups.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-links-page-renders-grouped-link-cards\n\t\t */\n\t\tgroups() {\n\t\t\tconst declared = (this.config.categories && typeof this.config.categories === 'object') ? this.config.categories : {}\n\t\t\tconst keys = Object.keys(declared)\n\t\t\tconst loose = this.visibleCards.filter((card) => !keys.includes(card.category))\n\t\t\tconst groups = loose.length > 0 ? [{ key: '', label: '', headingId: '', cards: loose }] : []\n\t\t\tkeys.forEach((key, index) => {\n\t\t\t\tconst cards = this.visibleCards.filter((card) => card.category === key)\n\t\t\t\tif (cards.length > 0) {\n\t\t\t\t\tgroups.push({\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tlabel: this.tr(declared[key]),\n\t\t\t\t\t\theadingId: `cn-link-cards-group-${this.uid}-${index}`,\n\t\t\t\t\t\tcards,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\t\t\treturn groups\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Translate a manifest string through the app's translate function.\n\t\t *\n\t\t * @param {string} value The declared string.\n\t\t * @return {string} The translation, or the input unchanged.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-links-page-renders-grouped-link-cards\n\t\t */\n\t\ttr(value) {\n\t\t\tif (typeof value !== 'string' || value === '') {\n\t\t\t\treturn value\n\t\t\t}\n\t\t\treturn (this.translate ?? this.cnTranslate)(value)\n\t\t},\n\n\t\t/**\n\t\t * Whether the user holds the card's `permission`. Same rule as the\n\t\t * menu: no permission declared, or no permissions known, allows.\n\t\t *\n\t\t * @param {object} card The card.\n\t\t * @return {boolean} True when the card may render.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-cards-honour-menu-visibility-conditions\n\t\t */\n\t\tpassesPermission(card) {\n\t\t\tconst held = Array.isArray(this.cnPermissions) ? this.cnPermissions : []\n\t\t\tif (!card.permission || held.length === 0) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn held.includes(card.permission)\n\t\t},\n\n\t\t/**\n\t\t * Evaluate a card's `visibleIf` the way CnAppNav evaluates a menu\n\t\t * entry's: `appInstalled` first, then dot-path predicates against\n\t\t * the manifest's `runtime`.\n\t\t *\n\t\t * @param {object} card The card.\n\t\t * @param {object|null} runtime `manifest.runtime`, or null.\n\t\t * @return {boolean} True when the card may render.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-cards-honour-menu-visibility-conditions\n\t\t */\n\t\tpassesVisibleIf(card, runtime) {\n\t\t\tconst condition = card.visibleIf\n\t\t\tif (!condition || typeof condition !== 'object') {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif (condition.appInstalled && !isAppInstalled(condition.appInstalled)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn passesContextPredicates(condition, runtime)\n\t\t},\n\n\t\t/**\n\t\t * Where a card goes. An `href` is an external URL. A `route` is a\n\t\t * route name, or a path when it starts with `/`, resolved by the\n\t\t * router so the link carries the app's base with or without\n\t\t * `/index.php`.\n\t\t *\n\t\t * @param {object} card The card.\n\t\t * @return {{external: boolean, location: object|null, url: string}} The target; `url` is '' when it cannot be resolved.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-card-is-a-real-link\n\t\t */\n\t\ttargetOf(card) {\n\t\t\tif (typeof card.route === 'string' && card.route !== '') {\n\t\t\t\tconst location = card.route.startsWith('/') ? { path: card.route } : { name: card.route }\n\t\t\t\tif (card.params && typeof card.params === 'object') {\n\t\t\t\t\tlocation.params = card.params\n\t\t\t\t}\n\t\t\t\tif (card.query && typeof card.query === 'object') {\n\t\t\t\t\tlocation.query = card.query\n\t\t\t\t}\n\t\t\t\treturn { external: false, location, url: resolveHref(location, this.$router) }\n\t\t\t}\n\t\t\tif (typeof card.href === 'string' && card.href !== '') {\n\t\t\t\treturn { external: true, location: null, url: card.href }\n\t\t\t}\n\t\t\treturn { external: false, location: null, url: '' }\n\t\t},\n\n\t\t/**\n\t\t * Route a plain click on an in-app card. A modified or middle click,\n\t\t * and every click on an external card, is left to the browser.\n\t\t *\n\t\t * @param {object} card The resolved card.\n\t\t * @param {MouseEvent} event The click.\n\t\t * @return {void}\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-card-is-a-real-link\n\t\t */\n\t\topen(card, event) {\n\t\t\tif (card.external || !card.location) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfollowLinkClick(event, card.location, this.$router)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-link-cards-page {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: calc(6 * var(--default-grid-baseline));\n\tpadding: calc(5 * var(--default-grid-baseline));\n}\n\n.cn-link-cards-page__title {\n\tmargin: 0 0 var(--default-grid-baseline);\n}\n\n.cn-link-cards-page__description,\n.cn-link-cards-page__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tmargin: 0;\n\tmax-width: 70ch;\n}\n\n.cn-link-cards-page__group {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: calc(3 * var(--default-grid-baseline));\n}\n\n.cn-link-cards-page__caption {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85em;\n\tfont-weight: 600;\n\tletter-spacing: 0.06em;\n\tmargin: 0;\n\ttext-transform: uppercase;\n}\n\n.cn-link-cards-page__grid {\n\tdisplay: grid;\n\tgap: calc(3 * var(--default-grid-baseline));\n\tgrid-template-columns: repeat(auto-fill, minmax(240px, 1fr));\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-link-cards-page__cell {\n\tdisplay: flex;\n}\n\n.cn-link-cards-page__card {\n\talign-items: flex-start;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border-dark);\n\tborder-radius: var(--border-radius-large);\n\tcolor: var(--color-main-text);\n\tdisplay: flex;\n\tflex: 1;\n\tgap: calc(3 * var(--default-grid-baseline));\n\tmin-width: 0;\n\tpadding: calc(4 * var(--default-grid-baseline));\n\ttext-decoration: none;\n}\n\n.cn-link-cards-page__card:hover {\n\tbackground: var(--color-background-hover);\n\tborder-color: var(--color-primary-element);\n}\n\n.cn-link-cards-page__card:focus-visible {\n\tborder-color: var(--color-primary-element);\n\toutline: 2px solid var(--color-primary-element);\n\toutline-offset: 2px;\n}\n\n.cn-link-cards-page__icon {\n\tcolor: var(--color-primary-element);\n\tflex: none;\n}\n\n.cn-link-cards-page__body {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: var(--default-grid-baseline);\n\tmin-width: 0;\n}\n\n.cn-link-cards-page__label {\n\tfont-weight: 600;\n\toverflow-wrap: anywhere;\n}\n\n.cn-link-cards-page__card-description {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tline-height: 1.4;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_toDisplayString","_renderList","_createElementVNode","_createBlock"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnLinkCardsPage.vue3.js","sources":["../../../../src/components/CnLinkCardsPage/CnLinkCardsPage.vue?vue&type=template&id=2f9a546b&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-link-cards-page\" data-testid=\"cn-link-cards-page\">\n\t\t<div class=\"cn-link-cards-page__header\">\n\t\t\t<h2 v-if=\"resolvedTitle\" class=\"cn-link-cards-page__title\">\n\t\t\t\t{{ resolvedTitle }}\n\t\t\t</h2>\n\t\t\t<p v-if=\"resolvedDescription\" class=\"cn-link-cards-page__description\">\n\t\t\t\t{{ resolvedDescription }}\n\t\t\t</p>\n\t\t</div>\n\n\t\t<p\n\t\t\tv-if=\"groups.length === 0\"\n\t\t\tclass=\"cn-link-cards-page__empty\"\n\t\t\tdata-testid=\"cn-link-cards-empty\">\n\t\t\t{{ resolvedEmptyLabel }}\n\t\t</p>\n\n\t\t<section\n\t\t\tv-for=\"group in groups\"\n\t\t\t:key=\"group.key\"\n\t\t\tclass=\"cn-link-cards-page__group\"\n\t\t\t:aria-labelledby=\"group.label ? group.headingId : null\"\n\t\t\t:aria-label=\"group.label ? null : resolvedTitle || null\"\n\t\t\tdata-testid=\"cn-link-cards-group\"\n\t\t\t:data-group=\"group.key\">\n\t\t\t<h3\n\t\t\t\tv-if=\"group.label\"\n\t\t\t\t:id=\"group.headingId\"\n\t\t\t\tclass=\"cn-link-cards-page__caption\">\n\t\t\t\t{{ group.label }}\n\t\t\t</h3>\n\t\t\t<ul class=\"cn-link-cards-page__grid\">\n\t\t\t\t<li v-for=\"card in group.cards\" :key=\"card.id\" class=\"cn-link-cards-page__cell\">\n\t\t\t\t\t<!-- A real <a href>, so the card can be middle-clicked, copied\n\t\t\t\t\t and announced as a link. A plain click on an in-app card\n\t\t\t\t\t is routed, which keeps the page from reloading. -->\n\t\t\t\t\t<a\n\t\t\t\t\t\tclass=\"cn-link-cards-page__card\"\n\t\t\t\t\t\tdata-testid=\"cn-link-card\"\n\t\t\t\t\t\t:data-card-id=\"card.id\"\n\t\t\t\t\t\t:href=\"card.url\"\n\t\t\t\t\t\t:target=\"card.external ? '_blank' : null\"\n\t\t\t\t\t\t:rel=\"card.external ? 'noopener noreferrer' : null\"\n\t\t\t\t\t\t:aria-describedby=\"card.description ? card.descriptionId : null\"\n\t\t\t\t\t\t@click=\"open(card, $event)\">\n\t\t\t\t\t\t<CnIcon\n\t\t\t\t\t\t\tv-if=\"card.icon\"\n\t\t\t\t\t\t\t:name=\"card.icon\"\n\t\t\t\t\t\t\t:size=\"24\"\n\t\t\t\t\t\t\tclass=\"cn-link-cards-page__icon\"\n\t\t\t\t\t\t\taria-hidden=\"true\" />\n\t\t\t\t\t\t<span class=\"cn-link-cards-page__body\">\n\t\t\t\t\t\t\t<span class=\"cn-link-cards-page__label\">{{ card.label }}</span>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-if=\"card.description\"\n\t\t\t\t\t\t\t\t:id=\"card.descriptionId\"\n\t\t\t\t\t\t\t\tclass=\"cn-link-cards-page__card-description\">\n\t\t\t\t\t\t\t\t{{ card.description }}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</a>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t</section>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { isAppInstalled } from '../../utils/appInstalled.js'\nimport { followLinkClick, resolveHref } from '../../utils/linkNavigation.js'\nimport { passesContextPredicates } from '../../utils/visibleIfContext.js'\nimport { CnIcon } from '../CnIcon/index.js'\n\nlet linkCardsPageUid = 0\n\n/**\n * CnLinkCardsPage: the `type: \"links\"` page. Link cards, grouped under captions.\n *\n * One page that opens every page a short menu leaves out. A card has a\n * label, a one-line description, an optional icon and a target. Groups come\n * from `categories`, in the order they are declared; a card names its group\n * with `category`.\n *\n * ```json\n * { \"id\": \"Modules\", \"route\": \"/modules\", \"type\": \"links\", \"title\": \"Modules and more\",\n * \"config\": {\n * \"description\": \"Every page that is not in the daily menu.\",\n * \"categories\": { \"sales\": \"Sales\", \"marketing\": \"Marketing\" },\n * \"cards\": [\n * { \"id\": \"Leads\", \"label\": \"Leads\", \"description\": \"Every deal you are working on.\",\n * \"icon\": \"CashMultiple\", \"category\": \"sales\", \"route\": \"Leads\" }\n * ]\n * } }\n * ```\n *\n * Every string goes through the app's translate function. A card is hidden\n * by the same `visibleIf` and `permission` a menu entry takes, and a group\n * with no visible card is not drawn.\n */\nexport default {\n\tname: 'CnLinkCardsPage',\n\n\tcomponents: { CnIcon },\n\n\tinject: {\n\t\t/** The app's translate function, provided by CnAppRoot. Identity outside one. */\n\t\tcnTranslate: { default: () => (key) => key },\n\t\t/** The effective manifest, for `visibleIf` predicates against `runtime`. */\n\t\tcnManifest: { default: null },\n\t\t/** The permissions the user holds. Empty means the app did not say, and allows. */\n\t\tcnPermissions: { default: () => [] },\n\t},\n\n\tprops: {\n\t\t/** The whole manifest page, for a host that mounts this component itself. CnPageRenderer passes the flattened props below instead. */\n\t\tpage: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * The cards: `{ id, label, description?, icon?, category?, route?, params?, query?, href?, visibleIf?, permission? }`.\n\t\t * `route` is a route name, or a path when it starts with `/`.\n\t\t *\n\t\t * @type {Array<object>|null}\n\t\t */\n\t\tcards: {\n\t\t\ttype: Array,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Group key to caption. Sets the order of the groups.\n\t\t *\n\t\t * @type {Record<string, string>|null}\n\t\t */\n\t\tcategories: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The heading. */\n\t\ttitle: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The lead paragraph under the heading. */\n\t\tdescription: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Text shown when no card is visible. */\n\t\temptyLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Translate function. Falls back to the injected `cnTranslate`.\n\t\t *\n\t\t * @type {((key: string) => string)|null}\n\t\t */\n\t\ttranslate: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\tdata() {\n\t\tlinkCardsPageUid += 1\n\t\treturn {\n\t\t\tuid: linkCardsPageUid,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The config, from the flattened props or from `page.config`. Props win.\n\t\t *\n\t\t * @return {object} The config.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-links-page-renders-grouped-link-cards\n\t\t */\n\t\tconfig() {\n\t\t\tconst fromPage = (this.page && this.page.config) || {}\n\t\t\tconst fromProps = {}\n\t\t\tfor (const key of ['cards', 'categories', 'description', 'title', 'emptyLabel']) {\n\t\t\t\tif (this[key] !== null && this[key] !== undefined) {\n\t\t\t\t\tfromProps[key] = this[key]\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { ...fromPage, ...fromProps }\n\t\t},\n\n\t\t/**\n\t\t * The heading, translated.\n\t\t *\n\t\t * @return {string} The title, or '' when the page declares none.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-links-page-renders-grouped-link-cards\n\t\t */\n\t\tresolvedTitle() {\n\t\t\treturn this.tr(this.config.title || (this.page && this.page.title) || '')\n\t\t},\n\n\t\t/**\n\t\t * The lead paragraph, translated.\n\t\t *\n\t\t * @return {string} The description.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-links-page-renders-grouped-link-cards\n\t\t */\n\t\tresolvedDescription() {\n\t\t\treturn this.tr(this.config.description || '')\n\t\t},\n\n\t\t/**\n\t\t * The empty-state text.\n\t\t *\n\t\t * @return {string} The text.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-links-page-renders-grouped-link-cards\n\t\t */\n\t\tresolvedEmptyLabel() {\n\t\t\treturn this.config.emptyLabel ? this.tr(this.config.emptyLabel) : t('nextcloud-vue', 'Nothing to open here.')\n\t\t},\n\n\t\t/**\n\t\t * The cards a reader may see and can open, with text translated and\n\t\t * the link resolved. A card that is gated away, or has no target that\n\t\t * resolves, is left out: a card that does nothing is worse than none.\n\t\t *\n\t\t * @return {Array<object>} The cards.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-card-is-a-real-link\n\t\t */\n\t\tvisibleCards() {\n\t\t\tconst declared = Array.isArray(this.config.cards) ? this.config.cards : []\n\t\t\tconst runtime = this.cnManifest?.runtime ?? null\n\t\t\treturn declared\n\t\t\t\t.filter((card) => card && card.id && card.label)\n\t\t\t\t.filter((card) => this.passesPermission(card) && this.passesVisibleIf(card, runtime))\n\t\t\t\t.map((card) => {\n\t\t\t\t\tconst target = this.targetOf(card)\n\t\t\t\t\treturn {\n\t\t\t\t\t\tid: String(card.id),\n\t\t\t\t\t\tlabel: this.tr(card.label),\n\t\t\t\t\t\tdescription: this.tr(card.description || ''),\n\t\t\t\t\t\tdescriptionId: `cn-link-card-desc-${this.uid}-${String(card.id).replace(/[^A-Za-z0-9_-]/g, '-')}`,\n\t\t\t\t\t\ticon: card.icon || '',\n\t\t\t\t\t\tcategory: card.category || '',\n\t\t\t\t\t\texternal: target.external,\n\t\t\t\t\t\tlocation: target.location,\n\t\t\t\t\t\turl: target.url,\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.filter((card) => card.url !== '')\n\t\t},\n\n\t\t/**\n\t\t * The groups to draw: ungrouped cards first under no caption, then\n\t\t * one group per category in declaration order. A group without a\n\t\t * visible card is left out.\n\t\t *\n\t\t * @return {Array<{key: string, label: string, headingId: string, cards: Array<object>}>} The groups.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-links-page-renders-grouped-link-cards\n\t\t */\n\t\tgroups() {\n\t\t\tconst declared = (this.config.categories && typeof this.config.categories === 'object') ? this.config.categories : {}\n\t\t\tconst keys = Object.keys(declared)\n\t\t\tconst loose = this.visibleCards.filter((card) => !keys.includes(card.category))\n\t\t\tconst groups = loose.length > 0 ? [{ key: '', label: '', headingId: '', cards: loose }] : []\n\t\t\tkeys.forEach((key, index) => {\n\t\t\t\tconst cards = this.visibleCards.filter((card) => card.category === key)\n\t\t\t\tif (cards.length > 0) {\n\t\t\t\t\tgroups.push({\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tlabel: this.tr(declared[key]),\n\t\t\t\t\t\theadingId: `cn-link-cards-group-${this.uid}-${index}`,\n\t\t\t\t\t\tcards,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t})\n\t\t\treturn groups\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Translate a manifest string through the app's translate function.\n\t\t *\n\t\t * @param {string} value The declared string.\n\t\t * @return {string} The translation, or the input unchanged.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-links-page-renders-grouped-link-cards\n\t\t */\n\t\ttr(value) {\n\t\t\tif (typeof value !== 'string' || value === '') {\n\t\t\t\treturn value\n\t\t\t}\n\t\t\treturn (this.translate ?? this.cnTranslate)(value)\n\t\t},\n\n\t\t/**\n\t\t * Whether the user holds the card's `permission`. Same rule as the\n\t\t * menu: no permission declared, or no permissions known, allows.\n\t\t *\n\t\t * @param {object} card The card.\n\t\t * @return {boolean} True when the card may render.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-cards-honour-menu-visibility-conditions\n\t\t */\n\t\tpassesPermission(card) {\n\t\t\tconst held = Array.isArray(this.cnPermissions) ? this.cnPermissions : []\n\t\t\tif (!card.permission || held.length === 0) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn held.includes(card.permission)\n\t\t},\n\n\t\t/**\n\t\t * Evaluate a card's `visibleIf` the way CnAppNav evaluates a menu\n\t\t * entry's: `appInstalled` first, then dot-path predicates against\n\t\t * the manifest's `runtime`.\n\t\t *\n\t\t * @param {object} card The card.\n\t\t * @param {object|null} runtime `manifest.runtime`, or null.\n\t\t * @return {boolean} True when the card may render.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-cards-honour-menu-visibility-conditions\n\t\t */\n\t\tpassesVisibleIf(card, runtime) {\n\t\t\tconst condition = card.visibleIf\n\t\t\tif (!condition || typeof condition !== 'object') {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tif (condition.appInstalled && !isAppInstalled(condition.appInstalled)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn passesContextPredicates(condition, runtime)\n\t\t},\n\n\t\t/**\n\t\t * Where a card goes. An `href` is an external URL. A `route` is a\n\t\t * route name, or a path when it starts with `/`, resolved by the\n\t\t * router so the link carries the app's base with or without\n\t\t * `/index.php`.\n\t\t *\n\t\t * @param {object} card The card.\n\t\t * @return {{external: boolean, location: object|null, url: string}} The target; `url` is '' when it cannot be resolved.\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-card-is-a-real-link\n\t\t */\n\t\ttargetOf(card) {\n\t\t\tif (typeof card.route === 'string' && card.route !== '') {\n\t\t\t\tconst location = card.route.startsWith('/') ? { path: card.route } : { name: card.route }\n\t\t\t\tif (card.params && typeof card.params === 'object') {\n\t\t\t\t\tlocation.params = card.params\n\t\t\t\t}\n\t\t\t\tif (card.query && typeof card.query === 'object') {\n\t\t\t\t\tlocation.query = card.query\n\t\t\t\t}\n\t\t\t\treturn { external: false, location, url: resolveHref(location, this.$router) }\n\t\t\t}\n\t\t\tif (typeof card.href === 'string' && card.href !== '') {\n\t\t\t\treturn { external: true, location: null, url: card.href }\n\t\t\t}\n\t\t\treturn { external: false, location: null, url: '' }\n\t\t},\n\n\t\t/**\n\t\t * Route a plain click on an in-app card. A modified or middle click,\n\t\t * and every click on an external card, is left to the browser.\n\t\t *\n\t\t * @param {object} card The resolved card.\n\t\t * @param {MouseEvent} event The click.\n\t\t * @return {void}\n\t\t * @spec openspec/changes/link-cards-page/specs/link-cards-page/spec.md#requirement-a-card-is-a-real-link\n\t\t */\n\t\topen(card, event) {\n\t\t\tif (card.external || !card.location) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfollowLinkClick(event, card.location, this.$router)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-link-cards-page {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: calc(6 * var(--default-grid-baseline));\n\tpadding: calc(5 * var(--default-grid-baseline));\n}\n\n/* Clear the Nextcloud navigation toggle button (44px wide, absolutely\n positioned at the left edge of .app-content) plus 12px breathing room, as\n CnPageHeader does. Only the header shifts; the cards keep the full width. */\n.cn-link-cards-page__header {\n\tpadding-inline-start: 56px;\n}\n\n.cn-link-cards-page__title {\n\tmargin: 0 0 var(--default-grid-baseline);\n}\n\n.cn-link-cards-page__description,\n.cn-link-cards-page__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tmargin: 0;\n\tmax-width: 70ch;\n}\n\n.cn-link-cards-page__group {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: calc(3 * var(--default-grid-baseline));\n}\n\n.cn-link-cards-page__caption {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85em;\n\tfont-weight: 600;\n\tletter-spacing: 0.06em;\n\tmargin: 0;\n\ttext-transform: uppercase;\n}\n\n.cn-link-cards-page__grid {\n\tdisplay: grid;\n\tgap: calc(3 * var(--default-grid-baseline));\n\tgrid-template-columns: repeat(auto-fill, minmax(240px, 1fr));\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n}\n\n.cn-link-cards-page__cell {\n\tdisplay: flex;\n}\n\n.cn-link-cards-page__card {\n\talign-items: flex-start;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border-dark);\n\tborder-radius: var(--border-radius-large);\n\tcolor: var(--color-main-text);\n\tdisplay: flex;\n\tflex: 1;\n\tgap: calc(3 * var(--default-grid-baseline));\n\tmin-width: 0;\n\tpadding: calc(4 * var(--default-grid-baseline));\n\ttransition: background-color var(--animation-quick, 100ms) ease, border-color var(--animation-quick, 100ms) ease, box-shadow var(--animation-quick, 100ms) ease;\n}\n\n/* A card is a link, and it must not look like a line of prose. A theme may\n underline every link with `!important` (thematiq: `a { text-decoration:\n underline !important }`, specificity 0,0,1). Among `!important` rules the\n higher specificity wins, so this class rule has to carry it too; without it\n the label and the description both rendered underlined on a live Nextcloud. */\n.cn-link-cards-page__card,\n.cn-link-cards-page__card:hover,\n.cn-link-cards-page__card:focus,\n.cn-link-cards-page__card:active {\n\ttext-decoration: none !important;\n}\n\n/* Hover moves the whole card: border, background and a soft lift. */\n.cn-link-cards-page__card:hover {\n\tbackground: var(--color-background-hover);\n\tborder-color: var(--color-primary-element);\n\tbox-shadow: 0 2px 6px var(--color-box-shadow);\n}\n\n@media (prefers-reduced-motion: reduce) {\n\t.cn-link-cards-page__card {\n\t\ttransition: none;\n\t}\n}\n\n.cn-link-cards-page__card:focus-visible {\n\tborder-color: var(--color-primary-element);\n\toutline: 2px solid var(--color-primary-element);\n\toutline-offset: 2px;\n}\n\n.cn-link-cards-page__icon {\n\tcolor: var(--color-primary-element);\n\tflex: none;\n}\n\n.cn-link-cards-page__body {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: var(--default-grid-baseline);\n\tmin-width: 0;\n}\n\n/* The label is the card's title. Its colour is set here, on the span, because\n a theme may force its link colour on the `a` itself; an inherited link\n colour made the title read as a hyperlink. */\n.cn-link-cards-page__label {\n\tcolor: var(--color-main-text);\n\tfont-size: 1.05em;\n\tfont-weight: 700;\n\tline-height: 1.3;\n\toverflow-wrap: anywhere;\n}\n\n.cn-link-cards-page__card-description {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.9em;\n\tline-height: 1.4;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_toDisplayString","_renderList","_createElementVNode","_createBlock"],"mappings":"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"}
|
|
@@ -12,6 +12,7 @@ import { resolveHref, followLinkClick, openRowTarget } from '../../utils/linkNav
|
|
|
12
12
|
import { objectFieldValue } from '../../utils/objectName.js';
|
|
13
13
|
import { dropOptionalUnresolved, resolveFilterTokens, hasUnresolvedTokens } from '../../utils/resolveFilterTokens.js';
|
|
14
14
|
import { markNewTabHandled } from '../../utils/rowAuxClick.js';
|
|
15
|
+
import { dispatchObjectCreated } from '../../utils/walkthroughSignals.js';
|
|
15
16
|
import __sfc_main$4 from '../CnRowActions/CnRowActions.vue.js';
|
|
16
17
|
|
|
17
18
|
/**
|
|
@@ -729,6 +730,39 @@ var script = {
|
|
|
729
730
|
return Array.isArray(c.formExcludeFields) ? c.formExcludeFields : []
|
|
730
731
|
},
|
|
731
732
|
|
|
733
|
+
/**
|
|
734
|
+
* Create-form seed values: the list's scalar filter values (the parent
|
|
735
|
+
* this list is scoped to, e.g. `{ lead: '<uuid>' }`), limited to keys
|
|
736
|
+
* the schema declares, so the new row shows its parent already chosen.
|
|
737
|
+
*
|
|
738
|
+
* @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md
|
|
739
|
+
* @return {object}
|
|
740
|
+
*/
|
|
741
|
+
createInitialData() {
|
|
742
|
+
const props = (this.createSchema && this.createSchema.properties) || {};
|
|
743
|
+
const out = {};
|
|
744
|
+
for (const [key, value] of Object.entries(this.resolvedFilter || {})) {
|
|
745
|
+
if (!(key in props) || value === null || value === undefined || value === '' || typeof value === 'object') {
|
|
746
|
+
continue
|
|
747
|
+
}
|
|
748
|
+
if (typeof value === 'string' && value.charAt(0) === '@') {
|
|
749
|
+
continue
|
|
750
|
+
}
|
|
751
|
+
out[key] = value;
|
|
752
|
+
}
|
|
753
|
+
return out
|
|
754
|
+
},
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* The seeded parent keys are locked: a row added to this list belongs
|
|
758
|
+
* to the record the list is scoped to.
|
|
759
|
+
*
|
|
760
|
+
* @return {string[]}
|
|
761
|
+
*/
|
|
762
|
+
createLockedFields() {
|
|
763
|
+
return Object.keys(this.createInitialData)
|
|
764
|
+
},
|
|
765
|
+
|
|
732
766
|
/** Per-field overrides for the create dialog (`content.formFieldOverrides`). */
|
|
733
767
|
formFieldOverrides() {
|
|
734
768
|
const c = this.content || {};
|
|
@@ -1077,7 +1111,8 @@ var script = {
|
|
|
1077
1111
|
}
|
|
1078
1112
|
}
|
|
1079
1113
|
const url = generateUrl('/apps/openregister/api/objects/{register}/{schema}', { register: c.register, schema: c.schema });
|
|
1080
|
-
await axios.post(url, payload);
|
|
1114
|
+
const response = await axios.post(url, payload);
|
|
1115
|
+
dispatchObjectCreated({ register: c.register, schema: c.schema, object: (response && response.data) || payload });
|
|
1081
1116
|
if (this.$refs.createDialog) {
|
|
1082
1117
|
this.$refs.createDialog.setResult({ success: true });
|
|
1083
1118
|
}
|