@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
|
@@ -268,6 +268,9 @@
|
|
|
268
268
|
ref="createDialog"
|
|
269
269
|
:schema="createSchema"
|
|
270
270
|
:item="null"
|
|
271
|
+
:register="content.register || ''"
|
|
272
|
+
:initialData="createInitialData"
|
|
273
|
+
:lockedFields="createLockedFields"
|
|
271
274
|
:size="formSize"
|
|
272
275
|
:columns="formColumns"
|
|
273
276
|
:includeFields="formIncludeFields"
|
|
@@ -293,6 +296,7 @@ import { followLinkClick, openRowTarget, resolveHref } from '../../utils/linkNav
|
|
|
293
296
|
import { objectFieldValue } from '../../utils/objectName.js'
|
|
294
297
|
import { dropOptionalUnresolved, hasUnresolvedTokens, resolveFilterTokens } from '../../utils/resolveFilterTokens.js'
|
|
295
298
|
import { markNewTabHandled } from '../../utils/rowAuxClick.js'
|
|
299
|
+
import { dispatchObjectCreated } from '../../utils/walkthroughSignals.js'
|
|
296
300
|
import { CnRowActions } from '../CnRowActions/index.js'
|
|
297
301
|
|
|
298
302
|
/**
|
|
@@ -1010,6 +1014,39 @@ export default {
|
|
|
1010
1014
|
return Array.isArray(c.formExcludeFields) ? c.formExcludeFields : []
|
|
1011
1015
|
},
|
|
1012
1016
|
|
|
1017
|
+
/**
|
|
1018
|
+
* Create-form seed values: the list's scalar filter values (the parent
|
|
1019
|
+
* this list is scoped to, e.g. `{ lead: '<uuid>' }`), limited to keys
|
|
1020
|
+
* the schema declares, so the new row shows its parent already chosen.
|
|
1021
|
+
*
|
|
1022
|
+
* @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md
|
|
1023
|
+
* @return {object}
|
|
1024
|
+
*/
|
|
1025
|
+
createInitialData() {
|
|
1026
|
+
const props = (this.createSchema && this.createSchema.properties) || {}
|
|
1027
|
+
const out = {}
|
|
1028
|
+
for (const [key, value] of Object.entries(this.resolvedFilter || {})) {
|
|
1029
|
+
if (!(key in props) || value === null || value === undefined || value === '' || typeof value === 'object') {
|
|
1030
|
+
continue
|
|
1031
|
+
}
|
|
1032
|
+
if (typeof value === 'string' && value.charAt(0) === '@') {
|
|
1033
|
+
continue
|
|
1034
|
+
}
|
|
1035
|
+
out[key] = value
|
|
1036
|
+
}
|
|
1037
|
+
return out
|
|
1038
|
+
},
|
|
1039
|
+
|
|
1040
|
+
/**
|
|
1041
|
+
* The seeded parent keys are locked: a row added to this list belongs
|
|
1042
|
+
* to the record the list is scoped to.
|
|
1043
|
+
*
|
|
1044
|
+
* @return {string[]}
|
|
1045
|
+
*/
|
|
1046
|
+
createLockedFields() {
|
|
1047
|
+
return Object.keys(this.createInitialData)
|
|
1048
|
+
},
|
|
1049
|
+
|
|
1013
1050
|
/** Per-field overrides for the create dialog (`content.formFieldOverrides`). */
|
|
1014
1051
|
formFieldOverrides() {
|
|
1015
1052
|
const c = this.content || {}
|
|
@@ -1358,7 +1395,8 @@ export default {
|
|
|
1358
1395
|
}
|
|
1359
1396
|
}
|
|
1360
1397
|
const url = generateUrl('/apps/openregister/api/objects/{register}/{schema}', { register: c.register, schema: c.schema })
|
|
1361
|
-
await axios.post(url, payload)
|
|
1398
|
+
const response = await axios.post(url, payload)
|
|
1399
|
+
dispatchObjectCreated({ register: c.register, schema: c.schema, object: (response && response.data) || payload })
|
|
1362
1400
|
if (this.$refs.createDialog) {
|
|
1363
1401
|
this.$refs.createDialog.setResult({ success: true })
|
|
1364
1402
|
}
|
|
@@ -130,6 +130,18 @@
|
|
|
130
130
|
</template>
|
|
131
131
|
{{ openLabel }}
|
|
132
132
|
</NcActionButton>
|
|
133
|
+
<!-- OpenRegister's Office page applies the object rule:
|
|
134
|
+
update edits, read views, no read is a 404. -->
|
|
135
|
+
<NcActionLink
|
|
136
|
+
v-if="officeOpens(file)"
|
|
137
|
+
:href="officeUrlFor(file)"
|
|
138
|
+
target="_blank"
|
|
139
|
+
:closeAfterClick="true">
|
|
140
|
+
<template #icon>
|
|
141
|
+
<FileDocumentEditOutline :size="20" />
|
|
142
|
+
</template>
|
|
143
|
+
{{ openInOfficeLabel }}
|
|
144
|
+
</NcActionLink>
|
|
133
145
|
<NcActionLink
|
|
134
146
|
v-if="file.id"
|
|
135
147
|
:href="downloadUrlFor(file)"
|
|
@@ -194,6 +206,7 @@
|
|
|
194
206
|
<script>
|
|
195
207
|
import { getCurrentUser } from '@nextcloud/auth'
|
|
196
208
|
import axios from '@nextcloud/axios'
|
|
209
|
+
import { getCapabilities } from '@nextcloud/capabilities'
|
|
197
210
|
import { translate as t } from '@nextcloud/l10n'
|
|
198
211
|
import { generateRemoteUrl, generateUrl } from '@nextcloud/router'
|
|
199
212
|
import {
|
|
@@ -209,6 +222,7 @@ import {
|
|
|
209
222
|
} from '@nextcloud/vue'
|
|
210
223
|
import Delete from 'vue-material-design-icons/Delete.vue'
|
|
211
224
|
import Download from 'vue-material-design-icons/Download.vue'
|
|
225
|
+
import FileDocumentEditOutline from 'vue-material-design-icons/FileDocumentEditOutline.vue'
|
|
212
226
|
import FileOutline from 'vue-material-design-icons/FileOutline.vue'
|
|
213
227
|
import FolderOutline from 'vue-material-design-icons/FolderOutline.vue'
|
|
214
228
|
import InformationOutline from 'vue-material-design-icons/InformationOutline.vue'
|
|
@@ -221,6 +235,23 @@ import { buildHeaders, prefixUrl } from '../../utils/index.js'
|
|
|
221
235
|
import { safeHref } from '../../utils/safeHref.js'
|
|
222
236
|
import { resolveObjectFolder } from '../CnFilesBrowser/filesBrowser.js'
|
|
223
237
|
|
|
238
|
+
/**
|
|
239
|
+
* The mime types Nextcloud Office opens by default for the current person.
|
|
240
|
+
*
|
|
241
|
+
* richdocuments publishes its capabilities only to a person it lets use
|
|
242
|
+
* Office, so an empty list means no Office here, or none for them.
|
|
243
|
+
*
|
|
244
|
+
* @return {Array<string>} The mime types, or an empty list.
|
|
245
|
+
*/
|
|
246
|
+
function readOfficeMimetypes() {
|
|
247
|
+
try {
|
|
248
|
+
const mimetypes = getCapabilities()?.richdocuments?.mimetypes
|
|
249
|
+
return Array.isArray(mimetypes) ? mimetypes.filter((mime) => typeof mime === 'string') : []
|
|
250
|
+
} catch {
|
|
251
|
+
return []
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
224
255
|
export default {
|
|
225
256
|
name: 'CnFilesTab',
|
|
226
257
|
|
|
@@ -237,6 +268,7 @@ export default {
|
|
|
237
268
|
NcLoadingIcon,
|
|
238
269
|
Delete,
|
|
239
270
|
Download,
|
|
271
|
+
FileDocumentEditOutline,
|
|
240
272
|
FileOutline,
|
|
241
273
|
FolderOutline,
|
|
242
274
|
InformationOutline,
|
|
@@ -261,6 +293,13 @@ export default {
|
|
|
261
293
|
noFilesLabel: { type: String, default: () => t('nextcloud-vue', 'No files attached') },
|
|
262
294
|
/** Label for the open/view file action */
|
|
263
295
|
openLabel: { type: String, default: () => t('nextcloud-vue', 'Open') },
|
|
296
|
+
/**
|
|
297
|
+
* Label of the action that opens a document in Nextcloud Office through
|
|
298
|
+
* OpenRegister's Office page. Shown only when Office is there for this
|
|
299
|
+
* person and opens the file's type; the page opens it for editing or
|
|
300
|
+
* read-only by the object's own rule.
|
|
301
|
+
*/
|
|
302
|
+
openInOfficeLabel: { type: String, default: () => t('nextcloud-vue', 'Open in Office') },
|
|
264
303
|
/** Label for the delete action */
|
|
265
304
|
deleteLabel: { type: String, default: () => t('nextcloud-vue', 'Delete') },
|
|
266
305
|
/** What the files browser's root crumb reads; null shows the folder's own name, as the Files app does. */
|
|
@@ -346,6 +385,8 @@ export default {
|
|
|
346
385
|
* @type {HTMLInputElement|null}
|
|
347
386
|
*/
|
|
348
387
|
fileInputEl: null,
|
|
388
|
+
/** The mime types Nextcloud Office opens for this person; empty without Office. */
|
|
389
|
+
officeMimetypes: readOfficeMimetypes(),
|
|
349
390
|
}
|
|
350
391
|
},
|
|
351
392
|
|
|
@@ -702,6 +743,30 @@ export default {
|
|
|
702
743
|
return `${this.apiBase}/files/${encodeURIComponent(String(file.id))}/download`
|
|
703
744
|
},
|
|
704
745
|
|
|
746
|
+
/**
|
|
747
|
+
* Whether Nextcloud Office opens this file for this person.
|
|
748
|
+
*
|
|
749
|
+
* @param {object} file The file row.
|
|
750
|
+
* @return {boolean} true when the Office action applies.
|
|
751
|
+
*/
|
|
752
|
+
officeOpens(file) {
|
|
753
|
+
const mime = file.type || file.mimetype || file.mimeType || ''
|
|
754
|
+
return Boolean(file.id) && mime !== '' && this.officeMimetypes.includes(mime)
|
|
755
|
+
},
|
|
756
|
+
|
|
757
|
+
/**
|
|
758
|
+
* OpenRegister's Office page for a file of this object, which checks the
|
|
759
|
+
* object rule and opens the document editable or read-only.
|
|
760
|
+
*
|
|
761
|
+
* @param {object} file The file row.
|
|
762
|
+
* @return {string} The url.
|
|
763
|
+
*/
|
|
764
|
+
officeUrlFor(file) {
|
|
765
|
+
const appBase = /\/api\/?$/.test(this.apiBase) ? this.apiBase.replace(/\/api\/?$/, '') : '/apps/openregister'
|
|
766
|
+
const segments = [this.register, this.schema, this.objectId, String(file.id)].map((part) => encodeURIComponent(String(part)))
|
|
767
|
+
return prefixUrl(`${appBase}/office/${segments.join('/')}`)
|
|
768
|
+
},
|
|
769
|
+
|
|
705
770
|
/**
|
|
706
771
|
* The Files app's permalink for a file: opens the Files app at the
|
|
707
772
|
* file's folder with the file selected.
|
|
@@ -436,6 +436,13 @@ export default {
|
|
|
436
436
|
padding: 16px;
|
|
437
437
|
}
|
|
438
438
|
|
|
439
|
+
/* Clear the Nextcloud navigation toggle button (44px wide, absolutely
|
|
440
|
+
positioned at the left edge of .app-content) plus 12px breathing room, as
|
|
441
|
+
CnPageHeader does. Only the header shifts; the body keeps the full width. */
|
|
442
|
+
.cn-reports-page__header {
|
|
443
|
+
padding-inline-start: 56px;
|
|
444
|
+
}
|
|
445
|
+
|
|
439
446
|
.cn-reports-page__title {
|
|
440
447
|
margin: 0 0 4px;
|
|
441
448
|
}
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
:disabled="disabled"
|
|
14
14
|
:placeholder="placeholder"
|
|
15
15
|
:filterable="false"
|
|
16
|
+
:multiple="multiple"
|
|
17
|
+
:keepOpen="multiple"
|
|
16
18
|
label="label"
|
|
17
19
|
@search="onSearch"
|
|
18
20
|
@update:modelValue="onInput">
|
|
@@ -93,12 +95,21 @@ export default {
|
|
|
93
95
|
required: true,
|
|
94
96
|
},
|
|
95
97
|
|
|
96
|
-
/** Currently-selected object id (v-model)
|
|
98
|
+
/** Currently-selected object id (v-model), or an array of ids when `multiple`. */
|
|
97
99
|
modelValue: {
|
|
98
|
-
type: [String, Number],
|
|
100
|
+
type: [String, Number, Array],
|
|
99
101
|
default: '',
|
|
100
102
|
},
|
|
101
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Pick several objects. `modelValue` is then an array of ids, and a
|
|
106
|
+
* create adds the new object to the selection.
|
|
107
|
+
*/
|
|
108
|
+
multiple: {
|
|
109
|
+
type: Boolean,
|
|
110
|
+
default: false,
|
|
111
|
+
},
|
|
112
|
+
|
|
102
113
|
/** Object field used as the option label AND written on create. */
|
|
103
114
|
labelField: {
|
|
104
115
|
type: String,
|
|
@@ -219,6 +230,9 @@ export default {
|
|
|
219
230
|
// The selected option object kept locally so the label shows even
|
|
220
231
|
// before/without a matching search result (e.g. a pre-set modelValue).
|
|
221
232
|
localSelected: null,
|
|
233
|
+
// Options seen so far, keyed by value, so a multiple selection
|
|
234
|
+
// keeps its labels when the search results move on.
|
|
235
|
+
known: {},
|
|
222
236
|
}
|
|
223
237
|
},
|
|
224
238
|
|
|
@@ -232,6 +246,19 @@ export default {
|
|
|
232
246
|
}
|
|
233
247
|
},
|
|
234
248
|
|
|
249
|
+
/**
|
|
250
|
+
* The selected ids as strings (multiple mode).
|
|
251
|
+
*
|
|
252
|
+
* @return {string[]}
|
|
253
|
+
*/
|
|
254
|
+
selectedIds() {
|
|
255
|
+
const value = this.modelValue
|
|
256
|
+
if (Array.isArray(value)) {
|
|
257
|
+
return value.filter((v) => v !== null && v !== undefined && v !== '').map((v) => String(v))
|
|
258
|
+
}
|
|
259
|
+
return value ? [String(value)] : []
|
|
260
|
+
},
|
|
261
|
+
|
|
235
262
|
/** The `${register}-${schema}` object-type slug used by the store. */
|
|
236
263
|
typeSlug() {
|
|
237
264
|
return `${this.register}-${this.schema}`
|
|
@@ -239,6 +266,11 @@ export default {
|
|
|
239
266
|
|
|
240
267
|
/** The currently-selected option, for NcSelect's model-value. */
|
|
241
268
|
selectedOption() {
|
|
269
|
+
if (this.multiple) {
|
|
270
|
+
return this.selectedIds.map((id) => this.known[id]
|
|
271
|
+
|| this.options.find((o) => o.value === id)
|
|
272
|
+
|| { value: id, label: id })
|
|
273
|
+
}
|
|
242
274
|
if (this.localSelected && this.localSelected.value === this.modelValue) {
|
|
243
275
|
return this.localSelected
|
|
244
276
|
}
|
|
@@ -310,9 +342,9 @@ export default {
|
|
|
310
342
|
// The scope moved, so anything already selected may no longer be
|
|
311
343
|
// in it. Drop the stale options + selection, then re-seed.
|
|
312
344
|
this.options = []
|
|
313
|
-
if (this.
|
|
345
|
+
if (this.selectedIds.length > 0) {
|
|
314
346
|
this.localSelected = null
|
|
315
|
-
this.$emit('update:modelValue', '')
|
|
347
|
+
this.$emit('update:modelValue', this.multiple ? [] : '')
|
|
316
348
|
}
|
|
317
349
|
if (this.search.trim().length >= this.minChars) {
|
|
318
350
|
this.onSearch(this.search)
|
|
@@ -347,7 +379,7 @@ export default {
|
|
|
347
379
|
const items = Array.isArray(collection)
|
|
348
380
|
? collection
|
|
349
381
|
: (this.objectStore.collections[this.typeSlug] || [])
|
|
350
|
-
this.options = items.map((o) => this.toOption(o))
|
|
382
|
+
this.options = this.remember(items.map((o) => this.toOption(o)))
|
|
351
383
|
} catch {
|
|
352
384
|
this.options = []
|
|
353
385
|
} finally {
|
|
@@ -392,7 +424,7 @@ export default {
|
|
|
392
424
|
const items = Array.isArray(collection)
|
|
393
425
|
? collection
|
|
394
426
|
: (this.objectStore.collections[this.typeSlug] || [])
|
|
395
|
-
this.options = items.map((o) => this.toOption(o))
|
|
427
|
+
this.options = this.remember(items.map((o) => this.toOption(o)))
|
|
396
428
|
} catch {
|
|
397
429
|
this.options = []
|
|
398
430
|
} finally {
|
|
@@ -408,6 +440,10 @@ export default {
|
|
|
408
440
|
* @return {Promise<void>}
|
|
409
441
|
*/
|
|
410
442
|
async onInput(option) {
|
|
443
|
+
if (this.multiple) {
|
|
444
|
+
await this.onMultipleInput(Array.isArray(option) ? option : (option ? [option] : []))
|
|
445
|
+
return
|
|
446
|
+
}
|
|
411
447
|
if (!option) {
|
|
412
448
|
this.localSelected = null
|
|
413
449
|
/**
|
|
@@ -425,19 +461,58 @@ export default {
|
|
|
425
461
|
this.$emit('update:modelValue', option.value)
|
|
426
462
|
},
|
|
427
463
|
|
|
464
|
+
/**
|
|
465
|
+
* Handle a change of a multiple selection. A synthetic create option
|
|
466
|
+
* creates the object and adds it; the rest emit their ids.
|
|
467
|
+
*
|
|
468
|
+
* @param {Array<object>} chosen The selected options.
|
|
469
|
+
* @return {Promise<void>}
|
|
470
|
+
*/
|
|
471
|
+
async onMultipleInput(chosen) {
|
|
472
|
+
const createOption = chosen.find((o) => o && o.__create)
|
|
473
|
+
const ids = chosen.filter((o) => o && !o.__create).map((o) => String(o.value))
|
|
474
|
+
this.remember(chosen.filter((o) => o && !o.__create))
|
|
475
|
+
if (!createOption) {
|
|
476
|
+
this.$emit('update:modelValue', ids)
|
|
477
|
+
return
|
|
478
|
+
}
|
|
479
|
+
const created = await this.createFromTerm(createOption.label, ids)
|
|
480
|
+
if (!created) {
|
|
481
|
+
this.$emit('update:modelValue', ids)
|
|
482
|
+
}
|
|
483
|
+
},
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Cache options by value so their labels survive a new search.
|
|
487
|
+
*
|
|
488
|
+
* @param {Array<object>} options Options to remember.
|
|
489
|
+
* @return {Array<object>} The same options.
|
|
490
|
+
*/
|
|
491
|
+
remember(options) {
|
|
492
|
+
const next = { ...this.known }
|
|
493
|
+
for (const o of options) {
|
|
494
|
+
if (o && o.value) {
|
|
495
|
+
next[o.value] = o
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
this.known = next
|
|
499
|
+
return options
|
|
500
|
+
},
|
|
501
|
+
|
|
428
502
|
/**
|
|
429
503
|
* Create a new object from the typed term and select it.
|
|
430
504
|
*
|
|
431
505
|
* @param {string} term The term to write to `labelField`.
|
|
432
|
-
* @
|
|
506
|
+
* @param {string[]} [keep] Multiple mode: the ids already selected, which the new id joins.
|
|
507
|
+
* @return {Promise<object|null>} The created object, or null.
|
|
433
508
|
*/
|
|
434
|
-
async createFromTerm(term) {
|
|
509
|
+
async createFromTerm(term, keep = []) {
|
|
435
510
|
if (!this.objectStore && !this.createHandler) {
|
|
436
|
-
return
|
|
511
|
+
return null
|
|
437
512
|
}
|
|
438
513
|
const name = (term || '').trim()
|
|
439
514
|
if (!name) {
|
|
440
|
-
return
|
|
515
|
+
return null
|
|
441
516
|
}
|
|
442
517
|
this.loading = true
|
|
443
518
|
try {
|
|
@@ -456,20 +531,26 @@ export default {
|
|
|
456
531
|
created = await this.objectStore.saveObject(this.typeSlug, payload)
|
|
457
532
|
}
|
|
458
533
|
if (!created) {
|
|
459
|
-
return
|
|
534
|
+
return null
|
|
460
535
|
}
|
|
461
536
|
const option = this.toOption(created)
|
|
537
|
+
this.remember([option])
|
|
462
538
|
this.options = [option, ...this.options.filter((o) => o.value !== option.value)]
|
|
463
539
|
this.localSelected = option
|
|
464
|
-
this
|
|
540
|
+
this.search = ''
|
|
541
|
+
this.$emit('update:modelValue', this.multiple
|
|
542
|
+
? [...keep.filter((id) => id !== option.value), option.value]
|
|
543
|
+
: option.value)
|
|
465
544
|
/**
|
|
466
545
|
* @event create A new object was created from the search term.
|
|
467
546
|
* @type {object} The created OpenRegister object.
|
|
468
547
|
*/
|
|
469
548
|
this.$emit('create', created)
|
|
549
|
+
return created
|
|
470
550
|
} catch (e) {
|
|
471
551
|
// eslint-disable-next-line no-console
|
|
472
552
|
console.warn('[CnResourceSelect] create failed', e)
|
|
553
|
+
return null
|
|
473
554
|
} finally {
|
|
474
555
|
this.loading = false
|
|
475
556
|
}
|
|
@@ -508,6 +589,10 @@ export default {
|
|
|
508
589
|
* @return {Promise<void>}
|
|
509
590
|
*/
|
|
510
591
|
async ensureSelectedLoaded() {
|
|
592
|
+
if (this.multiple) {
|
|
593
|
+
await this.ensureManyLoaded()
|
|
594
|
+
return
|
|
595
|
+
}
|
|
511
596
|
if (!this.modelValue || this.selectedOption || !this.objectStore) {
|
|
512
597
|
return
|
|
513
598
|
}
|
|
@@ -524,6 +609,32 @@ export default {
|
|
|
524
609
|
// Leave the id un-labelled rather than crash.
|
|
525
610
|
}
|
|
526
611
|
},
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* Multiple mode: fetch each selected id whose label is not known yet.
|
|
615
|
+
*
|
|
616
|
+
* @return {Promise<void>}
|
|
617
|
+
*/
|
|
618
|
+
async ensureManyLoaded() {
|
|
619
|
+
if (!this.objectStore || typeof this.objectStore.fetchObject !== 'function') {
|
|
620
|
+
return
|
|
621
|
+
}
|
|
622
|
+
const missing = this.selectedIds.filter((id) => !this.known[id] && !this.options.some((o) => o.value === id))
|
|
623
|
+
if (missing.length === 0) {
|
|
624
|
+
return
|
|
625
|
+
}
|
|
626
|
+
this.ensureRegistered()
|
|
627
|
+
for (const id of missing) {
|
|
628
|
+
try {
|
|
629
|
+
const obj = await this.objectStore.fetchObject(this.typeSlug, id)
|
|
630
|
+
if (obj) {
|
|
631
|
+
this.remember([this.toOption(obj)])
|
|
632
|
+
}
|
|
633
|
+
} catch {
|
|
634
|
+
// Leave the id un-labelled rather than crash.
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
},
|
|
527
638
|
},
|
|
528
639
|
}
|
|
529
640
|
</script>
|