@conduction/nextcloud-vue 2.100.0 → 2.102.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/CnActionsBar/CnActionsBar.vue2.js +22 -0
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +8 -6
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue.js +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js +29 -4
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js +9 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue.js +1 -1
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js +22 -1
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js.map +1 -1
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js +26 -16
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +70 -6
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +105 -74
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue.js +9 -0
- package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue2.js +139 -0
- package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue3.js +76 -0
- package/dist/esm/components/CnExportsWidget/CnExportsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue.js +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +160 -2
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +206 -94
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/filesReview.js +61 -0
- package/dist/esm/components/CnFilesBrowser/filesReview.js.map +1 -0
- package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue.js +9 -0
- package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue2.js +279 -0
- package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue3.js +122 -0
- package/dist/esm/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnGroupedCardsWidget/index.js +29 -0
- package/dist/esm/components/CnGroupedCardsWidget/index.js.map +1 -0
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +320 -74
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +323 -213
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/useNamedSource.js +30 -3
- package/dist/esm/components/CnIndexPage/useNamedSource.js.map +1 -1
- package/dist/esm/components/CnMapWidget/CnMapWidget.vue.js +1 -1
- package/dist/esm/components/CnMapWidget/CnMapWidget.vue2.js +15 -0
- package/dist/esm/components/CnMapWidget/CnMapWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnMapWidget/CnMapWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnMenuWidget/CnMenuWidget.vue.js +1 -1
- package/dist/esm/components/CnMenuWidget/CnMenuWidget.vue2.js +25 -1
- package/dist/esm/components/CnMenuWidget/CnMenuWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnMenuWidget/CnMenuWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue.js +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +14 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +14 -6
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue.js +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +10 -0
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +6 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue.js +1 -1
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue2.js +91 -4
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue2.js.map +1 -1
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue3.js +63 -38
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue3.js.map +1 -1
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue.js +1 -1
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js +106 -4
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js.map +1 -1
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js +70 -25
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js.map +1 -1
- package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue.js +9 -0
- package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue.js.map +1 -0
- package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue2.js +175 -0
- package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue3.js +77 -0
- package/dist/esm/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue.js +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js +35 -2
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +4 -0
- 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/CnWidgetObjectTable/CnWidgetObjectTable.vue.js +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +2 -23
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
- package/dist/esm/composables/indexSources.js +176 -20
- package/dist/esm/composables/indexSources.js.map +1 -1
- package/dist/esm/composables/useEndpointSource.js +28 -6
- package/dist/esm/composables/useEndpointSource.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +41 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +39 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +939 -376
- package/dist/esm/utils/builtInFormatters.js +24 -4
- package/dist/esm/utils/builtInFormatters.js.map +1 -1
- package/dist/esm/utils/languageMapText.js +50 -0
- package/dist/esm/utils/languageMapText.js.map +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js +3 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/mapList.js +71 -0
- package/dist/esm/utils/mapList.js.map +1 -0
- package/dist/esm/utils/rowClassRules.js +43 -0
- package/dist/esm/utils/rowClassRules.js.map +1 -0
- package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/esm/utils/visibleWhen.js +19 -2
- package/dist/esm/utils/visibleWhen.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +9681 -7136
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +939 -376
- package/l10n/en.json +41 -1
- package/l10n/nl.json +39 -1
- package/package.json +1 -1
- package/src/components/CnActionsBar/CnActionsBar.md +1 -0
- package/src/components/CnActionsBar/CnActionsBar.vue +31 -4
- package/src/components/CnChartWidget/CnChartWidget.md +3 -2
- package/src/components/CnChartWidget/CnChartWidget.vue +42 -4
- package/src/components/CnChoiceCards/CnChoiceCards.md +39 -0
- package/src/components/CnChoiceCards/CnChoiceCards.vue +93 -16
- package/src/components/CnDashboardPage/CnDashboardPage.vue +110 -21
- package/src/components/CnExportsWidget/CnExportsWidget.vue +233 -0
- package/src/components/CnExportsWidget/index.js +7 -0
- package/src/components/CnFilesBrowser/CnFilesBrowser.vue +324 -0
- package/src/components/CnFilesBrowser/filesReview.js +57 -0
- package/src/components/CnGroupedCardsWidget/CnGroupedCardsWidget.vue +433 -0
- package/src/components/CnGroupedCardsWidget/index.js +28 -0
- package/src/components/CnIndexPage/CnIndexPage.md +5 -1
- package/src/components/CnIndexPage/CnIndexPage.vue +313 -9
- package/src/components/CnIndexPage/useNamedSource.js +30 -3
- package/src/components/CnMapWidget/CnMapWidget.vue +37 -0
- package/src/components/CnMenuWidget/CnMenuWidget.vue +25 -1
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +29 -1
- package/src/components/CnObjectSidebar/CnFilesTab.vue +15 -0
- package/src/components/CnOrderedListField/CnOrderedListField.vue +151 -5
- package/src/components/CnQuickFilterBar/CnQuickFilterBar.md +1 -0
- package/src/components/CnQuickFilterBar/CnQuickFilterBar.vue +159 -5
- package/src/components/CnSavedViewAlertWidget/CnSavedViewAlertWidget.vue +272 -0
- package/src/components/CnSavedViewAlertWidget/index.js +7 -0
- package/src/components/CnSetupWizard/CnSetupWizard.vue +35 -2
- package/src/components/CnWidgetGrid/builtInWidgets.js +4 -0
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
- package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +2 -23
- package/src/components/index.js +3 -0
- package/src/composables/indexSources.js +175 -19
- package/src/composables/useEndpointSource.js +28 -6
- package/src/css/index-page.css +117 -0
- package/src/css/look-board-index.css +10 -0
- package/src/css/look-board.css +44 -0
- package/src/index.js +1 -0
- package/src/schemas/app-manifest-v2.schema.json +85 -3
- package/src/utils/builtInFormatters.js +24 -4
- package/src/utils/languageMapText.js +46 -0
- package/src/utils/libraryWidgetKeys.js +3 -0
- package/src/utils/mapList.js +67 -0
- package/src/utils/rowClassRules.js +39 -0
- package/src/utils/validateManifestV2.compiled.js +3 -3
- package/src/utils/visibleWhen.js +19 -2
|
@@ -113,6 +113,22 @@
|
|
|
113
113
|
{{ hostLabel(action) }}
|
|
114
114
|
</NcActionButton>
|
|
115
115
|
</NcActions>
|
|
116
|
+
<!-- The host's own buttons in the bar ("Vraag een bestand aan een
|
|
117
|
+
partij", DqZaakDocumenten), dispatched like a New-menu entry with
|
|
118
|
+
the current folder; `#header-actions` adds anything else. -->
|
|
119
|
+
<NcButton v-for="action in headerActions"
|
|
120
|
+
:key="`header-${action.id}`"
|
|
121
|
+
:variant="action.variant === 'primary' ? 'primary' : 'secondary'"
|
|
122
|
+
:disabled="folder === null"
|
|
123
|
+
:data-testid="`cn-files-browser-header-action-${action.id}`"
|
|
124
|
+
@click="runHostAction(action, null)">
|
|
125
|
+
<template v-if="action.icon" #icon>
|
|
126
|
+
<CnIcon :name="action.icon" :size="20" />
|
|
127
|
+
</template>
|
|
128
|
+
{{ hostLabel(action) }}
|
|
129
|
+
</NcButton>
|
|
130
|
+
<!-- @slot header-actions Buttons in the bar, before the upload button. -->
|
|
131
|
+
<slot name="header-actions" />
|
|
116
132
|
<!-- A plain upload as its own button, when the host names it
|
|
117
133
|
(`uploadButton`). It takes the primary place and the New
|
|
118
134
|
menu steps back beside it. -->
|
|
@@ -147,6 +163,31 @@
|
|
|
147
163
|
</li>
|
|
148
164
|
</ul>
|
|
149
165
|
|
|
166
|
+
<!-- The review summary (`reviewSummary`): how many files are reviewed,
|
|
167
|
+
a bar in the review tones and a legend with the count per value. -->
|
|
168
|
+
<div v-if="review && review.total > 0 && !loading && error === ''"
|
|
169
|
+
class="cn-files-browser__review"
|
|
170
|
+
data-testid="cn-files-browser-review">
|
|
171
|
+
<div class="cn-files-browser__review-head">
|
|
172
|
+
<strong class="cn-files-browser__review-title">{{ reviewTitle }}</strong>
|
|
173
|
+
<span v-if="reviewSubtitle" class="cn-files-browser__review-sub">{{ reviewSubtitle }}</span>
|
|
174
|
+
</div>
|
|
175
|
+
<span class="cn-files-browser__review-bar" role="img" :aria-label="reviewTitle">
|
|
176
|
+
<span v-for="segment in review.segments"
|
|
177
|
+
v-show="segment.count > 0 && !isOpenValue(segment.value)"
|
|
178
|
+
:key="segment.value"
|
|
179
|
+
:class="`cn-files-browser__review-part cn-files-browser__review-part--${segment.tone}`"
|
|
180
|
+
:style="{ width: `${segment.share}%` }" />
|
|
181
|
+
</span>
|
|
182
|
+
<ul class="cn-files-browser__review-legend">
|
|
183
|
+
<li v-for="segment in review.segments" :key="segment.value" data-testid="cn-files-browser-review-entry">
|
|
184
|
+
<span :class="`cn-files-browser__review-dot cn-files-browser__review-part--${segment.tone}`" aria-hidden="true" />
|
|
185
|
+
{{ hostLabel(segment) }}
|
|
186
|
+
<strong>{{ segment.count }}</strong>
|
|
187
|
+
</li>
|
|
188
|
+
</ul>
|
|
189
|
+
</div>
|
|
190
|
+
|
|
150
191
|
<!-- The host's filter: per declared column, a chip per value in use
|
|
151
192
|
with how many files carry it. Chips narrow the list. -->
|
|
152
193
|
<div v-if="facetGroups.length > 0 && !loading && error === ''"
|
|
@@ -330,6 +371,9 @@
|
|
|
330
371
|
:class="`cn-files-browser__col-${column.key}`"
|
|
331
372
|
:data-testid="`cn-files-browser-cell-${column.key}`">
|
|
332
373
|
<span v-if="column.key === 'name'" class="cn-files-browser__name">{{ node.basename }}</span>
|
|
374
|
+
<span v-if="column.key === 'name' && nameSecondaryOf(node)"
|
|
375
|
+
class="cn-files-browser__name-secondary"
|
|
376
|
+
data-testid="cn-files-browser-name-secondary">{{ nameSecondaryOf(node) }}</span>
|
|
333
377
|
<template v-else-if="column.key === 'size'">
|
|
334
378
|
{{ isFolder(node) ? '' : formatSize(node.size) }}
|
|
335
379
|
</template>
|
|
@@ -347,6 +391,16 @@
|
|
|
347
391
|
:row="node" />
|
|
348
392
|
</td>
|
|
349
393
|
<td class="cn-files-browser__col-actions" @click.stop @auxclick.stop>
|
|
394
|
+
<!-- The host's one visible button on a file ("Beoordelen"),
|
|
395
|
+
primary while the row still asks for it. -->
|
|
396
|
+
<NcButton v-if="rowButton && !isFolder(node) && hostActionApplies(rowButton, node)"
|
|
397
|
+
:variant="rowButtonPrimary(node) ? 'primary' : 'secondary'"
|
|
398
|
+
class="cn-files-browser__row-button"
|
|
399
|
+
:aria-label="t('nextcloud-vue', '{action}: {name}', { action: hostLabel(rowButton), name: node.basename })"
|
|
400
|
+
data-testid="cn-files-browser-row-button"
|
|
401
|
+
@click="runHostAction(rowButton, node)">
|
|
402
|
+
{{ hostLabel(rowButton) }}
|
|
403
|
+
</NcButton>
|
|
350
404
|
<NcActions :forceMenu="true" :ariaLabel="t('nextcloud-vue', 'Actions for {name}', { name: node.basename })">
|
|
351
405
|
<NcActionButton
|
|
352
406
|
v-for="action in actionsFor(node)"
|
|
@@ -548,6 +602,14 @@
|
|
|
548
602
|
<Upload :size="16" />
|
|
549
603
|
{{ dropHintLabel }}
|
|
550
604
|
</p>
|
|
605
|
+
<!-- The host's own line under the list ("Elk bestand wordt bij het
|
|
606
|
+
uploaden op virussen gescand", DqDocumentenBeheer), apart from
|
|
607
|
+
the drop hint. -->
|
|
608
|
+
<p v-if="footnote && !loading && error === ''"
|
|
609
|
+
class="cn-files-browser__footnote"
|
|
610
|
+
data-testid="cn-files-browser-footnote">
|
|
611
|
+
{{ cnTranslate(footnote) }}
|
|
612
|
+
</p>
|
|
551
613
|
|
|
552
614
|
<NcDialog
|
|
553
615
|
v-if="renaming !== null"
|
|
@@ -688,6 +750,7 @@ import CnEmptyContent from '../CnEmptyContent/CnEmptyContent.vue'
|
|
|
688
750
|
import CnIcon from '../CnIcon/CnIcon.vue'
|
|
689
751
|
import { readUserPreference, writeUserPreference } from '../../composables/useUserPreferences.js'
|
|
690
752
|
import { dispatchAction } from '../../utils/actionsDispatcher.js'
|
|
753
|
+
import { fillTemplate } from '../../utils/fillTemplate.js'
|
|
691
754
|
import { isNewTabClick, isRowMiddleClick, preventMiddleClickAutoscroll } from '../../utils/rowAuxClick.js'
|
|
692
755
|
import { ACTIONS_NEEDING_THE_FILES_PAGE, crumbsFor, hostActionApplies, joinPath } from './filesBrowser.js'
|
|
693
756
|
import {
|
|
@@ -700,6 +763,7 @@ import {
|
|
|
700
763
|
sortNodesByColumn,
|
|
701
764
|
visibleFileColumns,
|
|
702
765
|
} from './filesBrowserColumns.js'
|
|
766
|
+
import { reviewSummary } from './filesReview.js'
|
|
703
767
|
|
|
704
768
|
let uploadSeq = 0
|
|
705
769
|
|
|
@@ -811,6 +875,50 @@ export default {
|
|
|
811
875
|
default: () => [],
|
|
812
876
|
},
|
|
813
877
|
|
|
878
|
+
/**
|
|
879
|
+
* The host's own buttons in the bar, before the upload button ("Vraag
|
|
880
|
+
* een bestand aan een partij", DqZaakDocumenten). Each is dispatched
|
|
881
|
+
* like a New-menu entry, with the current folder; `variant:
|
|
882
|
+
* 'primary'` draws it as the primary button.
|
|
883
|
+
*
|
|
884
|
+
* @type {Array<{id: string, label: string, icon?: string, variant?: string, type?: string, target?: string, props?: object, handler?: string, args?: Array}>}
|
|
885
|
+
*/
|
|
886
|
+
headerActions: {
|
|
887
|
+
type: Array,
|
|
888
|
+
default: () => [],
|
|
889
|
+
},
|
|
890
|
+
|
|
891
|
+
/**
|
|
892
|
+
* One visible button on every file row ("Beoordelen"), dispatched like a
|
|
893
|
+
* row action. `primaryWhen: { key, values }` draws it as the primary
|
|
894
|
+
* button while the row-data field `key` holds one of `values` (a file
|
|
895
|
+
* still to review), and secondary otherwise. `visibleIf` works as on a
|
|
896
|
+
* row action.
|
|
897
|
+
*
|
|
898
|
+
* @type {{id: string, label: string, type?: string, target?: string, props?: object, handler?: string, args?: Array, primaryWhen?: {key: string, values: Array<string>}, visibleIf?: object}|null}
|
|
899
|
+
*/
|
|
900
|
+
rowButton: {
|
|
901
|
+
type: Object,
|
|
902
|
+
default: null,
|
|
903
|
+
},
|
|
904
|
+
|
|
905
|
+
/**
|
|
906
|
+
* A review summary above the list (DqZaakDocumenten "4 van 7
|
|
907
|
+
* beoordeeld"): `{ key, open, values: [{ value, label?, tone? }], title?,
|
|
908
|
+
* subtitle? }`. `key` is the row-data field with the review state, `open`
|
|
909
|
+
* the value (or values) of a file still to review; a file without a
|
|
910
|
+
* listed value counts as open. `tone` is `success`, `warning`, `error`,
|
|
911
|
+
* `info` or `default`. `title` and `subtitle` take `{done}`, `{total}` and
|
|
912
|
+
* `{open}`, through the host's translate; they default to the library's
|
|
913
|
+
* "{done} of {total} reviewed" and "{open} still to review".
|
|
914
|
+
*
|
|
915
|
+
* @type {{key: string, open?: string|Array<string>, values: Array<{value: string, label?: string, tone?: string}>, title?: string, subtitle?: string}|null}
|
|
916
|
+
*/
|
|
917
|
+
reviewSummary: {
|
|
918
|
+
type: Object,
|
|
919
|
+
default: null,
|
|
920
|
+
},
|
|
921
|
+
|
|
814
922
|
/**
|
|
815
923
|
* Rows that are not nodes of this folder: files the host joined from
|
|
816
924
|
* another object's folder. Rendered after the folder's own rows with
|
|
@@ -930,6 +1038,29 @@ export default {
|
|
|
930
1038
|
default: false,
|
|
931
1039
|
},
|
|
932
1040
|
|
|
1041
|
+
/**
|
|
1042
|
+
* A line of the host's own under the list, apart from the drop hint
|
|
1043
|
+
* ("Elk bestand wordt bij het uploaden op virussen gescand en krijgt een
|
|
1044
|
+
* controlegetal", DqDocumentenBeheer). Translated with the host's
|
|
1045
|
+
* catalogue.
|
|
1046
|
+
*/
|
|
1047
|
+
footnote: {
|
|
1048
|
+
type: String,
|
|
1049
|
+
default: '',
|
|
1050
|
+
},
|
|
1051
|
+
|
|
1052
|
+
/**
|
|
1053
|
+
* A second line under each file's name (DqDocumentenBeheer "Inwoner ·
|
|
1054
|
+
* 4 okt"): a field template read from the file's row data, plus
|
|
1055
|
+
* `name`, `modified` (the change date) and `owner` (the owner's display
|
|
1056
|
+
* name), e.g. `"{uploadedBy} · {modified|date}"`. A line with an empty
|
|
1057
|
+
* value is left out. Translated with the host's catalogue first.
|
|
1058
|
+
*/
|
|
1059
|
+
nameSecondary: {
|
|
1060
|
+
type: String,
|
|
1061
|
+
default: '',
|
|
1062
|
+
},
|
|
1063
|
+
|
|
933
1064
|
/** The line `dropHint` shows. */
|
|
934
1065
|
dropHintLabel: {
|
|
935
1066
|
type: String,
|
|
@@ -1331,6 +1462,28 @@ export default {
|
|
|
1331
1462
|
return this.sorted.filter((node) => !this.isFolder(node))
|
|
1332
1463
|
},
|
|
1333
1464
|
|
|
1465
|
+
/**
|
|
1466
|
+
* The review counts over the folder's files, or null without a summary.
|
|
1467
|
+
*
|
|
1468
|
+
* @return {object|null}
|
|
1469
|
+
*/
|
|
1470
|
+
review() {
|
|
1471
|
+
return reviewSummary(this.reviewSummary, this.fileNodes, this.resolvedRowData)
|
|
1472
|
+
},
|
|
1473
|
+
|
|
1474
|
+
/** @return {string} The summary's title, counts filled in. */
|
|
1475
|
+
reviewTitle() {
|
|
1476
|
+
return this.reviewText('title', t('nextcloud-vue', '{done} of {total} reviewed'))
|
|
1477
|
+
},
|
|
1478
|
+
|
|
1479
|
+
/** @return {string} The summary's second line, or '' when nothing is open. */
|
|
1480
|
+
reviewSubtitle() {
|
|
1481
|
+
if (!this.review || this.review.open === 0) {
|
|
1482
|
+
return ''
|
|
1483
|
+
}
|
|
1484
|
+
return this.reviewText('subtitle', t('nextcloud-vue', '{open} still to review'))
|
|
1485
|
+
},
|
|
1486
|
+
|
|
1334
1487
|
/**
|
|
1335
1488
|
* Whether any facet value is chosen.
|
|
1336
1489
|
*
|
|
@@ -1793,6 +1946,73 @@ export default {
|
|
|
1793
1946
|
return this.cnTranslate(String(action?.label ?? ''))
|
|
1794
1947
|
},
|
|
1795
1948
|
|
|
1949
|
+
/**
|
|
1950
|
+
* The second line under a file's name, or '' without `nameSecondary`,
|
|
1951
|
+
* for a folder, or when a value in it is empty.
|
|
1952
|
+
*
|
|
1953
|
+
* @param {object} node The file.
|
|
1954
|
+
* @return {string}
|
|
1955
|
+
*/
|
|
1956
|
+
nameSecondaryOf(node) {
|
|
1957
|
+
if (!this.nameSecondary || this.isFolder(node)) {
|
|
1958
|
+
return ''
|
|
1959
|
+
}
|
|
1960
|
+
const byId = this.resolvedRowData && typeof this.resolvedRowData === 'object' ? this.resolvedRowData : {}
|
|
1961
|
+
const entry = byId[node.fileid] ?? byId[String(node.fileid)] ?? {}
|
|
1962
|
+
const owner = node.attributes ? node.attributes['owner-display-name'] : undefined
|
|
1963
|
+
const object = { ...entry, name: node.basename, modified: node.mtime, ...(owner ? { owner } : {}) }
|
|
1964
|
+
return fillTemplate(this.cnTranslate(this.nameSecondary), object)
|
|
1965
|
+
},
|
|
1966
|
+
|
|
1967
|
+
/**
|
|
1968
|
+
* A review summary line: the host's template through its translate, else
|
|
1969
|
+
* the library's, with the counts filled in.
|
|
1970
|
+
*
|
|
1971
|
+
* @param {string} key `title` or `subtitle`.
|
|
1972
|
+
* @param {string} fallback The library's line, already translated.
|
|
1973
|
+
* @return {string}
|
|
1974
|
+
*/
|
|
1975
|
+
reviewText(key, fallback) {
|
|
1976
|
+
const own = this.reviewSummary && typeof this.reviewSummary[key] === 'string' && this.reviewSummary[key] !== ''
|
|
1977
|
+
? this.cnTranslate(this.reviewSummary[key])
|
|
1978
|
+
: fallback
|
|
1979
|
+
const counts = this.review || { done: 0, total: 0, open: 0 }
|
|
1980
|
+
return own.replace(/\{(done|total|open)\}/g, (match, name) => String(counts[name]))
|
|
1981
|
+
},
|
|
1982
|
+
|
|
1983
|
+
/**
|
|
1984
|
+
* Whether a review value is one of the summary's open values.
|
|
1985
|
+
*
|
|
1986
|
+
* @param {string} value The value.
|
|
1987
|
+
* @return {boolean}
|
|
1988
|
+
*/
|
|
1989
|
+
isOpenValue(value) {
|
|
1990
|
+
const open = this.reviewSummary ? this.reviewSummary.open : undefined
|
|
1991
|
+
const list = (Array.isArray(open) ? open : [open]).filter((v) => v !== undefined && v !== null)
|
|
1992
|
+
return list.some((v) => String(v).toLowerCase() === String(value).toLowerCase())
|
|
1993
|
+
},
|
|
1994
|
+
|
|
1995
|
+
/**
|
|
1996
|
+
* Whether a file's row button is the primary one: the row-data field
|
|
1997
|
+
* `primaryWhen.key` holds one of `primaryWhen.values`.
|
|
1998
|
+
*
|
|
1999
|
+
* @param {object} node The file.
|
|
2000
|
+
* @return {boolean}
|
|
2001
|
+
*/
|
|
2002
|
+
rowButtonPrimary(node) {
|
|
2003
|
+
const when = this.rowButton && this.rowButton.primaryWhen
|
|
2004
|
+
if (!when || typeof when.key !== 'string' || !Array.isArray(when.values)) {
|
|
2005
|
+
return false
|
|
2006
|
+
}
|
|
2007
|
+
const raw = fileColumnValue({ key: when.key, source: 'row' }, node, this.resolvedRowData)
|
|
2008
|
+
if (raw === null || raw === undefined) {
|
|
2009
|
+
return false
|
|
2010
|
+
}
|
|
2011
|
+
return when.values.some((v) => String(v).toLowerCase() === String(raw).toLowerCase())
|
|
2012
|
+
},
|
|
2013
|
+
|
|
2014
|
+
hostActionApplies,
|
|
2015
|
+
|
|
1796
2016
|
/**
|
|
1797
2017
|
* Re-read the folder once after a burst of change signals. One upload
|
|
1798
2018
|
* of ten files answers ten `files:node:created`, and one PROPFIND
|
|
@@ -2938,6 +3158,89 @@ export default {
|
|
|
2938
3158
|
margin-inline-end: 8px;
|
|
2939
3159
|
}
|
|
2940
3160
|
|
|
3161
|
+
.cn-files-browser__review {
|
|
3162
|
+
display: flex;
|
|
3163
|
+
flex-direction: column;
|
|
3164
|
+
gap: 10px;
|
|
3165
|
+
margin-block-end: 12px;
|
|
3166
|
+
padding: 14px 16px;
|
|
3167
|
+
border-radius: 10px;
|
|
3168
|
+
background: var(--color-background-hover);
|
|
3169
|
+
}
|
|
3170
|
+
|
|
3171
|
+
.cn-files-browser__review-head {
|
|
3172
|
+
display: flex;
|
|
3173
|
+
flex-wrap: wrap;
|
|
3174
|
+
align-items: baseline;
|
|
3175
|
+
justify-content: space-between;
|
|
3176
|
+
gap: 8px;
|
|
3177
|
+
}
|
|
3178
|
+
|
|
3179
|
+
.cn-files-browser__review-title {
|
|
3180
|
+
font-size: 15px;
|
|
3181
|
+
}
|
|
3182
|
+
|
|
3183
|
+
.cn-files-browser__review-sub {
|
|
3184
|
+
color: var(--color-text-maxcontrast);
|
|
3185
|
+
font-size: 13px;
|
|
3186
|
+
}
|
|
3187
|
+
|
|
3188
|
+
.cn-files-browser__review-bar {
|
|
3189
|
+
display: flex;
|
|
3190
|
+
height: 8px;
|
|
3191
|
+
overflow: hidden;
|
|
3192
|
+
border-radius: 4px;
|
|
3193
|
+
background: var(--color-border);
|
|
3194
|
+
}
|
|
3195
|
+
|
|
3196
|
+
.cn-files-browser__review-legend {
|
|
3197
|
+
display: flex;
|
|
3198
|
+
flex-wrap: wrap;
|
|
3199
|
+
gap: 8px 18px;
|
|
3200
|
+
margin: 0;
|
|
3201
|
+
padding: 0;
|
|
3202
|
+
font-size: 14px;
|
|
3203
|
+
list-style: none;
|
|
3204
|
+
}
|
|
3205
|
+
|
|
3206
|
+
.cn-files-browser__review-legend li {
|
|
3207
|
+
display: inline-flex;
|
|
3208
|
+
align-items: center;
|
|
3209
|
+
gap: 8px;
|
|
3210
|
+
}
|
|
3211
|
+
|
|
3212
|
+
.cn-files-browser__review-dot {
|
|
3213
|
+
width: 10px;
|
|
3214
|
+
height: 10px;
|
|
3215
|
+
border-radius: 5px;
|
|
3216
|
+
}
|
|
3217
|
+
|
|
3218
|
+
.cn-files-browser__review-part--success {
|
|
3219
|
+
background: var(--color-success-text, var(--color-success));
|
|
3220
|
+
}
|
|
3221
|
+
|
|
3222
|
+
.cn-files-browser__review-part--warning {
|
|
3223
|
+
background: var(--color-warning);
|
|
3224
|
+
}
|
|
3225
|
+
|
|
3226
|
+
.cn-files-browser__review-part--error {
|
|
3227
|
+
background: var(--color-error-text, var(--color-error));
|
|
3228
|
+
}
|
|
3229
|
+
|
|
3230
|
+
.cn-files-browser__review-part--info {
|
|
3231
|
+
background: var(--color-primary-element);
|
|
3232
|
+
}
|
|
3233
|
+
|
|
3234
|
+
.cn-files-browser__review-part--default {
|
|
3235
|
+
background: var(--color-border-dark);
|
|
3236
|
+
}
|
|
3237
|
+
|
|
3238
|
+
.cn-files-browser__row-button {
|
|
3239
|
+
display: inline-flex;
|
|
3240
|
+
margin-inline-end: 8px;
|
|
3241
|
+
vertical-align: middle;
|
|
3242
|
+
}
|
|
3243
|
+
|
|
2941
3244
|
.cn-files-browser__facets {
|
|
2942
3245
|
display: flex;
|
|
2943
3246
|
flex-wrap: wrap;
|
|
@@ -2988,6 +3291,11 @@ export default {
|
|
|
2988
3291
|
|
|
2989
3292
|
.cn-files-browser__col-actions {
|
|
2990
3293
|
width: 48px;
|
|
3294
|
+
white-space: nowrap;
|
|
3295
|
+
}
|
|
3296
|
+
|
|
3297
|
+
.cn-files-browser__col-actions:has(.cn-files-browser__row-button) {
|
|
3298
|
+
width: auto;
|
|
2991
3299
|
}
|
|
2992
3300
|
|
|
2993
3301
|
.cn-files-browser__col-name {
|
|
@@ -3001,6 +3309,22 @@ export default {
|
|
|
3001
3309
|
white-space: nowrap;
|
|
3002
3310
|
}
|
|
3003
3311
|
|
|
3312
|
+
.cn-files-browser__name-secondary {
|
|
3313
|
+
display: block;
|
|
3314
|
+
overflow: hidden;
|
|
3315
|
+
color: var(--color-text-maxcontrast);
|
|
3316
|
+
font-size: 13px;
|
|
3317
|
+
text-overflow: ellipsis;
|
|
3318
|
+
white-space: nowrap;
|
|
3319
|
+
}
|
|
3320
|
+
|
|
3321
|
+
.cn-files-browser__footnote {
|
|
3322
|
+
margin: 0;
|
|
3323
|
+
padding-top: 12px;
|
|
3324
|
+
color: var(--color-text-maxcontrast);
|
|
3325
|
+
font-size: 13px;
|
|
3326
|
+
}
|
|
3327
|
+
|
|
3004
3328
|
.cn-files-browser__row--linked {
|
|
3005
3329
|
color: var(--color-text-maxcontrast);
|
|
3006
3330
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
3
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
4
|
+
*/
|
|
5
|
+
import { fileColumnValue } from './filesBrowserColumns.js'
|
|
6
|
+
|
|
7
|
+
/** The tones a review segment may take; anything else draws as `default`. */
|
|
8
|
+
const TONES = ['success', 'warning', 'error', 'info', 'default']
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The review summary of a file list (DqZaakDocumenten "4 van 7 beoordeeld"):
|
|
12
|
+
* how many files carry each review value, read from the host's row data.
|
|
13
|
+
*
|
|
14
|
+
* `spec` is `{ key, open, values: [{ value, label?, tone? }] }`: `key` is the
|
|
15
|
+
* row-data field that holds the review state, `open` the value (or values) of
|
|
16
|
+
* a file still to review. A file with no value, or one not listed, counts as
|
|
17
|
+
* open. Values compare without case.
|
|
18
|
+
*
|
|
19
|
+
* @param {object} spec The summary declaration.
|
|
20
|
+
* @param {Array<object>} nodes The file nodes (folders already left out).
|
|
21
|
+
* @param {object|null} rowData The host's row data, by file id.
|
|
22
|
+
* @return {{total: number, done: number, open: number, segments: Array<{value: string, label: string, tone: string, count: number, share: number}>}|null}
|
|
23
|
+
* The counts, or null when the declaration names no key or no values.
|
|
24
|
+
*/
|
|
25
|
+
export function reviewSummary(spec, nodes, rowData) {
|
|
26
|
+
if (!spec || typeof spec !== 'object' || typeof spec.key !== 'string' || spec.key === '' || !Array.isArray(spec.values) || spec.values.length === 0) {
|
|
27
|
+
return null
|
|
28
|
+
}
|
|
29
|
+
const files = Array.isArray(nodes) ? nodes : []
|
|
30
|
+
const openValues = (Array.isArray(spec.open) ? spec.open : [spec.open]).filter((v) => v !== undefined && v !== null).map((v) => String(v).toLowerCase())
|
|
31
|
+
const segments = spec.values
|
|
32
|
+
.filter((entry) => entry && entry.value !== undefined && entry.value !== null)
|
|
33
|
+
.map((entry) => ({
|
|
34
|
+
value: String(entry.value),
|
|
35
|
+
label: typeof entry.label === 'string' && entry.label !== '' ? entry.label : String(entry.value),
|
|
36
|
+
tone: TONES.includes(entry.tone) ? entry.tone : 'default',
|
|
37
|
+
count: 0,
|
|
38
|
+
share: 0,
|
|
39
|
+
}))
|
|
40
|
+
let open = 0
|
|
41
|
+
for (const node of files) {
|
|
42
|
+
const raw = fileColumnValue({ key: spec.key, source: 'row' }, node, rowData)
|
|
43
|
+
const value = raw === null || raw === undefined ? '' : String(raw).toLowerCase()
|
|
44
|
+
const segment = segments.find((s) => s.value.toLowerCase() === value)
|
|
45
|
+
if (segment) {
|
|
46
|
+
segment.count++
|
|
47
|
+
}
|
|
48
|
+
if (!segment || openValues.includes(value)) {
|
|
49
|
+
open++
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const total = files.length
|
|
53
|
+
for (const segment of segments) {
|
|
54
|
+
segment.share = total > 0 ? Math.round((segment.count / total) * 1000) / 10 : 0
|
|
55
|
+
}
|
|
56
|
+
return { total, done: total - open, open, segments }
|
|
57
|
+
}
|