@conduction/nextcloud-vue 2.103.0 → 2.104.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 +23 -0
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +94 -72
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +18 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +3 -3
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +39 -2
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +14 -0
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +40 -2
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +159 -3
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +105 -85
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/useSelfFetchList.js +7 -2
- package/dist/esm/components/CnIndexPage/useSelfFetchList.js.map +1 -1
- package/dist/esm/components/CnObjectRow/CnObjectRow.vue.js +1 -1
- package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js +57 -5
- package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js +37 -4
- package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js.map +1 -1
- package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue.js +1 -1
- package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue.js +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +18 -2
- 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 +7 -7
- package/dist/esm/composables/indexSources.js.map +1 -1
- package/dist/esm/l10n/en.json.js +4 -0
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +4 -0
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +114 -33
- package/dist/esm/utils/nextcloudDirectory.js +18 -1
- package/dist/esm/utils/nextcloudDirectory.js.map +1 -1
- package/dist/esm/utils/sortOrder.js +61 -0
- package/dist/esm/utils/sortOrder.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/nextcloud-vue.cjs.js +4172 -3662
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +114 -33
- package/l10n/en.json +4 -0
- package/l10n/nl.json +4 -0
- package/package.json +1 -1
- package/src/components/CnActionsBar/CnActionsBar.md +1 -0
- package/src/components/CnActionsBar/CnActionsBar.vue +39 -2
- package/src/components/CnAppRoot/CnAppRoot.md +1 -1
- package/src/components/CnAppRoot/CnAppRoot.vue +21 -4
- package/src/components/CnDataTable/CnDataTable.md +1 -0
- package/src/components/CnDataTable/CnDataTable.vue +49 -0
- package/src/components/CnDetailPage/CnDetailPage.md +1 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +40 -2
- package/src/components/CnIndexPage/CnIndexPage.md +4 -2
- package/src/components/CnIndexPage/CnIndexPage.vue +174 -8
- package/src/components/CnIndexPage/useSelfFetchList.js +6 -1
- package/src/components/CnObjectRow/CnObjectRow.vue +95 -1
- package/src/components/CnUserSettingsDialog/CnUserSettingsDialog.vue +7 -2
- package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +18 -2
- package/src/composables/indexSources.js +7 -7
- package/src/css/index-page.css +19 -0
- package/src/css/look-board-index.css +48 -0
- package/src/schemas/app-manifest-v2.schema.json +23 -1
- package/src/utils/nextcloudDirectory.js +17 -0
- package/src/utils/sortOrder.js +58 -0
- package/src/utils/validateManifestV2.compiled.js +3 -3
|
@@ -1267,7 +1267,7 @@ import { isActivityWidget, resolveHeaderMeta } from '../../utils/headerMeta.js'
|
|
|
1267
1267
|
import { patchStoredSelf } from '../../utils/patchStoredSelf.js'
|
|
1268
1268
|
import { setReadState } from '../../utils/recordInteractions.js'
|
|
1269
1269
|
import { slotRenders } from '../../utils/slotContent.js'
|
|
1270
|
-
import { evaluateVisibleWhen, isLocallyDecidableVisibleWhen } from '../../utils/visibleWhen.js'
|
|
1270
|
+
import { evaluateVisibleWhen, isLocallyDecidableVisibleWhen, readVisibleWhenPath } from '../../utils/visibleWhen.js'
|
|
1271
1271
|
import {
|
|
1272
1272
|
isCardWidgetDef,
|
|
1273
1273
|
isContentOnlyWidgetDef,
|
|
@@ -2582,6 +2582,22 @@ export default {
|
|
|
2582
2582
|
default: () => [],
|
|
2583
2583
|
},
|
|
2584
2584
|
|
|
2585
|
+
/**
|
|
2586
|
+
* A field path whose value picks the side column and its order for
|
|
2587
|
+
* this record (manifest `config.sideColumnFrom`), read like a
|
|
2588
|
+
* `visibleWhen` field: `@context.caseType.sideCards` reads the list a
|
|
2589
|
+
* case type keeps. A non-empty list of ids keeps those `sideColumn`
|
|
2590
|
+
* entries, in that order, and leaves the rest out; each still follows
|
|
2591
|
+
* its own `visibleWhen`. Any other value keeps `sideColumn` as
|
|
2592
|
+
* declared, so a record whose list is empty changes nothing.
|
|
2593
|
+
*
|
|
2594
|
+
* @spec openspec/changes/detail-side-column-from/specs/detail-page/spec.md#requirement-a-record-can-pick-the-side-column-and-its-order
|
|
2595
|
+
*/
|
|
2596
|
+
sideColumnFrom: {
|
|
2597
|
+
type: String,
|
|
2598
|
+
default: '',
|
|
2599
|
+
},
|
|
2600
|
+
|
|
2585
2601
|
/**
|
|
2586
2602
|
* Draw the header as a card (manifest `config.headerCard: true`): the
|
|
2587
2603
|
* pills, the title and the actions on the page's surface colour inside
|
|
@@ -3492,7 +3508,7 @@ export default {
|
|
|
3492
3508
|
* @spec openspec/changes/detail-side-visible-when/specs/detail-page/spec.md#requirement-a-side-column-widget-shows-only-while-its-visiblewhen-holds
|
|
3493
3509
|
*/
|
|
3494
3510
|
sideColumnWidgets() {
|
|
3495
|
-
const resolved = this.
|
|
3511
|
+
const resolved = this.pickedSideWidgets.filter((widget) => this.sideWidgetVisible(widget))
|
|
3496
3512
|
if (!this.isBoard) {
|
|
3497
3513
|
return resolved
|
|
3498
3514
|
}
|
|
@@ -3504,6 +3520,28 @@ export default {
|
|
|
3504
3520
|
.map(({ widget }) => widget)
|
|
3505
3521
|
},
|
|
3506
3522
|
|
|
3523
|
+
/**
|
|
3524
|
+
* The side column's definitions as the record picks them
|
|
3525
|
+
* (`sideColumnFrom`): the ids it lists, in its order, or every
|
|
3526
|
+
* declared entry when it lists none. Edit mode shows them all.
|
|
3527
|
+
*
|
|
3528
|
+
* @return {Array<object>}
|
|
3529
|
+
* @spec openspec/changes/detail-side-column-from/specs/detail-page/spec.md#requirement-a-record-can-pick-the-side-column-and-its-order
|
|
3530
|
+
*/
|
|
3531
|
+
pickedSideWidgets() {
|
|
3532
|
+
const declared = this.declaredSideWidgets
|
|
3533
|
+
if (!this.sideColumnFrom || this.editingBody || !this.widgetVisibilityData) {
|
|
3534
|
+
return declared
|
|
3535
|
+
}
|
|
3536
|
+
const ids = readVisibleWhenPath(this.widgetVisibilityData, this.sideColumnFrom)
|
|
3537
|
+
if (!Array.isArray(ids) || ids.length === 0) {
|
|
3538
|
+
return declared
|
|
3539
|
+
}
|
|
3540
|
+
return ids
|
|
3541
|
+
.map((id) => declared.find((widget) => widget.id === id) || null)
|
|
3542
|
+
.filter(Boolean)
|
|
3543
|
+
},
|
|
3544
|
+
|
|
3507
3545
|
/**
|
|
3508
3546
|
* The side column's entries resolved to definitions, before any
|
|
3509
3547
|
* `visibleWhen` is read: a string names a widget declared in
|
|
@@ -389,6 +389,7 @@ export default {
|
|
|
389
389
|
| `customComponents` | Object | `null` | Custom-component / handler registry. When set, takes precedence over the injected `cnCustomComponents` from CnAppRoot. Used to resolve `actions[].handler` registry names declared in the manifest (manifest-actions-dispatch). Named handlers resolve out of the v2 `registry` (a `kind: "handler"` entry) FIRST and fall back to this map. |
|
|
390
390
|
| `showViewToggle` | Boolean | `true` | Whether to show the Cards/Table view toggle |
|
|
391
391
|
| `inlineSearch` | Boolean | `false` | Show an inline search field in the actions bar (in addition to / instead of the sidebar search). Fed from the manifest as `pages[].config.inlineSearch`. The field takes the place of the "Showing X of Y" counter unless `showCountWithSearch` is set. |
|
|
392
|
+
| `search` | Boolean | `true` | `false` draws no inline search in the actions bar, also in the board look, which otherwise draws one on every index page (manifest: `config.search`). Wins over `inlineSearch`. |
|
|
392
393
|
| `showCountWithSearch` | Boolean | `false` | Keep the "Showing X of Y" counter visible beside the inline search field, after the search and any `#after-search` controls (only relevant with `inlineSearch`, which otherwise takes the counter's place). Fed from the manifest as `pages[].config.showCountWithSearch`. |
|
|
393
394
|
| `searchInFiles` | Boolean | `false` | "Also search inside files" switch beside the search box (manifest `config.searchInFiles`). On, a search with a term sends `_content_search=true`; rows found through a file show "Found in {file}" (`@self.matchedFile`). State kept in the route as `contentSearch=1`. Emits `content-search`. |
|
|
394
395
|
| `searchPlaceholder` | String | `''` | Placeholder for the inline search field (manifest `config.searchPlaceholder`). |
|
|
@@ -878,8 +879,9 @@ The list view (`view-mode="list"`) and standalone sort dropdown add these props:
|
|
|
878
879
|
| `listConfig` | Object | `{}` | Field mapping for the default list rows (`CnObjectRow`). |
|
|
879
880
|
| `listComponent` | String | `''` | Custom row component, resolved against the v2 `registry` (any kind carrying a `component`) and then the legacy `customComponents` map. |
|
|
880
881
|
| `showSortSelect` | Boolean | `false` | Show a standalone sort dropdown in the actions bar. |
|
|
881
|
-
| `sortSelectOptions` | Array | `[]` | Options `{ value, label }` for the sort dropdown. |
|
|
882
|
-
| `sortSelectValue` | String | `''` | Selected sort dropdown value
|
|
882
|
+
| `sortSelectOptions` | Array | `[]` | Options `{ value, label, order? }` for the sort dropdown. On a self-fetch page a choice orders the list as a header click does (`_order`, kept in the address): by the option's `order`, else its `value` read as an order (`"-createdAt"`, `"priority:desc,title"`). |
|
|
883
|
+
| `sortSelectValue` | String | `''` | Selected sort dropdown value. Empty lets a self-fetch page show the option matching its current order, so a header click that matches an option shows it. |
|
|
884
|
+
| `defaultOrder` | String, Object or Array | `''` | The order a self-fetch page opens on when neither the address nor `sortKeys`/`sortKey` gives one (manifest `config.defaultOrder`): `"-createdAt"`, `"priority:desc,title"`, an `_order` map `{ "priority": "desc" }` or a `[{ key, order }]` list. |
|
|
883
885
|
|
|
884
886
|
The `#list-item`, `#row-icon`, `#row-badges`, and `#row-actions` slots override the list rows (see [CnObjectList](./cn-object-list.md)). Emits `@sort-change` with the chosen sort value.
|
|
885
887
|
|
|
@@ -191,8 +191,9 @@
|
|
|
191
191
|
:listIcon="listIcon"
|
|
192
192
|
:showSortSelect="showSortSelect"
|
|
193
193
|
:sortOptions="sortSelectOptions"
|
|
194
|
-
:sortValue="
|
|
195
|
-
:showSearch="inlineSearch || isBoardLook"
|
|
194
|
+
:sortValue="effectiveSortSelectValue"
|
|
195
|
+
:showSearch="search !== false && (inlineSearch || isBoardLook)"
|
|
196
|
+
:searchPlacement="searchPlacement"
|
|
196
197
|
:searchValue="effectiveSearchValue"
|
|
197
198
|
:searchPlaceholder="presented('searchPlaceholder') ? cnTranslate(presented('searchPlaceholder')) : presented('searchPlaceholder')"
|
|
198
199
|
:showCountWithSearch="showCountWithSearch"
|
|
@@ -209,7 +210,7 @@
|
|
|
209
210
|
:bulkActions="mergedBulkActions"
|
|
210
211
|
:documentationUrl="documentationUrl"
|
|
211
212
|
:documentationLabel="documentationLabel || undefined"
|
|
212
|
-
@sortChange="
|
|
213
|
+
@sortChange="onSortSelectChange"
|
|
213
214
|
@add="onAddClick"
|
|
214
215
|
@clearSelection="onSelect([])"
|
|
215
216
|
@removeFilter="onRemoveActiveFilter"
|
|
@@ -675,8 +676,8 @@
|
|
|
675
676
|
:partial="folderSidebarPartial"
|
|
676
677
|
:filesPath="folderSidebar.filesPath || '/'"
|
|
677
678
|
:selectedId="selectedFolderId"
|
|
678
|
-
:allLabel="folderSidebar.allLabel
|
|
679
|
-
:title="folderSidebar.title
|
|
679
|
+
:allLabel="folderSidebar.allLabel ? cnTranslate(folderSidebar.allLabel) : undefined"
|
|
680
|
+
:title="folderSidebar.title ? cnTranslate(folderSidebar.title) : ''"
|
|
680
681
|
:idField="folderPassthroughIdField"
|
|
681
682
|
:nameField="folderPassthroughNameField"
|
|
682
683
|
:allowCreate="Boolean(folderSidebar.allowCreate)"
|
|
@@ -701,6 +702,15 @@
|
|
|
701
702
|
<!-- @slot before-collection Content rendered above the collection in EVERY view mode (table, cards, list, map) — e.g. a folder/group strip that must stay visually separate from the objects instead of masquerading as rows or cards. -->
|
|
702
703
|
<slot name="before-collection" />
|
|
703
704
|
|
|
705
|
+
<!-- The selected folder as a heading above the list (a group on
|
|
706
|
+
DqAfdelingen: its name, then its parent and member count). -->
|
|
707
|
+
<div v-if="scopeHeading" class="cn-index-page__scope-heading" data-testid="cn-index-page-scope-heading">
|
|
708
|
+
<h2 class="cn-index-page__scope-title">
|
|
709
|
+
{{ scopeHeading.name }}
|
|
710
|
+
</h2>
|
|
711
|
+
<span v-if="scopeHeading.subtitle" class="cn-index-page__scope-subtitle" data-testid="cn-index-page-scope-subtitle">{{ scopeHeading.subtitle }}</span>
|
|
712
|
+
</div>
|
|
713
|
+
|
|
704
714
|
<!-- Loading state — initial fetch only; a background refresh keeps the table visible -->
|
|
705
715
|
<div v-if="showInitialLoader" class="cn-index-page__loading">
|
|
706
716
|
<!-- name gives NcLoadingIcon a non-empty aria-label (WCAG role-img-alt); empty name ships an unlabeled role="img" -->
|
|
@@ -1300,6 +1310,7 @@ import { DEFAULT_ROW_INDICATOR_CAP } from '../../utils/rowIndicators.js'
|
|
|
1300
1310
|
import { buildRouteQueryFromViewState, buildViewCreatePayload, extractViewState, extractViewStateFromRouteQuery, normalizeSharedWith, savedViewScope, viewMatchesScope } from '../../utils/savedViewHelpers.js'
|
|
1301
1311
|
import { columnsFromSchema, fieldsFromSchema } from '../../utils/schema.js'
|
|
1302
1312
|
import { resolveScopeLayout, resolveScopePresentation } from '../../utils/scopeListLayout.js'
|
|
1313
|
+
import { orderToSortKeys, sameSortKeys } from '../../utils/sortOrder.js'
|
|
1303
1314
|
import { dispatchObjectCreated } from '../../utils/walkthroughSignals.js'
|
|
1304
1315
|
import { CnActionButtons } from '../CnActionButtons/index.js'
|
|
1305
1316
|
import { CnActionsBar } from '../CnActionsBar/index.js'
|
|
@@ -1969,7 +1980,10 @@ export default {
|
|
|
1969
1980
|
* case" (manifest `config.headerButtons`). Each is `{ label?, action,
|
|
1970
1981
|
* variant?, icon?, format?, id? }`: `action` is `add` (the Add flow;
|
|
1971
1982
|
* its label defaults to the Add label), `export` (the export leaf in
|
|
1972
|
-
* `format`, `csv` by default, else the export dialog
|
|
1983
|
+
* `format`, `csv` by default, else the export dialog; with `run` the
|
|
1984
|
+
* `headerActions` entry of that id; on an `entitySource` page without
|
|
1985
|
+
* `run` and without a `@mass-export` listener it is not drawn),
|
|
1986
|
+
* `import`,
|
|
1973
1987
|
* `refresh`, `save-view` (the save-current-view dialog: the board's
|
|
1974
1988
|
* "Zoekopdracht opslaan", label "Save view" by default), or the id of a
|
|
1975
1989
|
* `headerActions` entry; `variant` is
|
|
@@ -2149,6 +2163,20 @@ export default {
|
|
|
2149
2163
|
validator: (v) => ['', 'toolbar'].includes(v),
|
|
2150
2164
|
},
|
|
2151
2165
|
|
|
2166
|
+
/**
|
|
2167
|
+
* Where the board look's search field sits (manifest
|
|
2168
|
+
* `config.searchPlacement`): `''` (the default) opens row 2 with it,
|
|
2169
|
+
* `'toolbar'` puts it on row 1 before the Filter button and the view
|
|
2170
|
+
* switch (DqTeamwachtrij).
|
|
2171
|
+
*
|
|
2172
|
+
* @type {''|'toolbar'}
|
|
2173
|
+
*/
|
|
2174
|
+
searchPlacement: {
|
|
2175
|
+
type: String,
|
|
2176
|
+
default: '',
|
|
2177
|
+
validator: (v) => ['', 'toolbar'].includes(v),
|
|
2178
|
+
},
|
|
2179
|
+
|
|
2152
2180
|
/**
|
|
2153
2181
|
* How the quick filters render: `'chips'` (pill strip, default) or
|
|
2154
2182
|
* `'dropdown'` (a single `NcSelect`). Sourced from the manifest as
|
|
@@ -3095,6 +3123,16 @@ export default {
|
|
|
3095
3123
|
default: false,
|
|
3096
3124
|
},
|
|
3097
3125
|
|
|
3126
|
+
/**
|
|
3127
|
+
* `false` draws no inline search in the actions bar, also in the board
|
|
3128
|
+
* look, which otherwise draws one on every index page. Fed from the
|
|
3129
|
+
* manifest as `pages[].config.search`.
|
|
3130
|
+
*/
|
|
3131
|
+
search: {
|
|
3132
|
+
type: Boolean,
|
|
3133
|
+
default: true,
|
|
3134
|
+
},
|
|
3135
|
+
|
|
3098
3136
|
/**
|
|
3099
3137
|
* Keep the "Showing X of Y" counter visible beside the inline search field,
|
|
3100
3138
|
* after any `#after-search` controls (only relevant with `inlineSearch`,
|
|
@@ -3162,12 +3200,28 @@ export default {
|
|
|
3162
3200
|
default: () => [],
|
|
3163
3201
|
},
|
|
3164
3202
|
|
|
3165
|
-
/**
|
|
3203
|
+
/**
|
|
3204
|
+
* Selected value of the standalone sort dropdown. Empty lets a
|
|
3205
|
+
* self-fetch page show the option matching its current order.
|
|
3206
|
+
*/
|
|
3166
3207
|
sortSelectValue: {
|
|
3167
3208
|
type: String,
|
|
3168
3209
|
default: '',
|
|
3169
3210
|
},
|
|
3170
3211
|
|
|
3212
|
+
/**
|
|
3213
|
+
* The order a self-fetch page opens on when neither the address nor
|
|
3214
|
+
* `sortKeys`/`sortKey` gives one (manifest `config.defaultOrder`):
|
|
3215
|
+
* `"-createdAt"`, `"priority:desc,title"`, an `_order` map or a
|
|
3216
|
+
* `[{ key, order }]` list.
|
|
3217
|
+
*
|
|
3218
|
+
* @type {string|object|Array}
|
|
3219
|
+
*/
|
|
3220
|
+
defaultOrder: {
|
|
3221
|
+
type: [String, Object, Array],
|
|
3222
|
+
default: '',
|
|
3223
|
+
},
|
|
3224
|
+
|
|
3171
3225
|
/** MDI icon name for the list view-toggle option (manifest `config.listIcon`) */
|
|
3172
3226
|
listIcon: {
|
|
3173
3227
|
type: String,
|
|
@@ -4896,6 +4950,60 @@ export default {
|
|
|
4896
4950
|
return match || null
|
|
4897
4951
|
},
|
|
4898
4952
|
|
|
4953
|
+
/**
|
|
4954
|
+
* The heading above the list for the selected folder: on a `groups`
|
|
4955
|
+
* pane (or with `folderSidebar.heading: true`), its name and, under
|
|
4956
|
+
* it, its parent folder and its count. The count reads
|
|
4957
|
+
* `folderSidebar.countText` (`{count}`, through the app's translate),
|
|
4958
|
+
* else "{count} members" on a groups pane.
|
|
4959
|
+
*
|
|
4960
|
+
* @return {{name: string, subtitle: string}|null} The heading, or null on All.
|
|
4961
|
+
*/
|
|
4962
|
+
scopeHeading() {
|
|
4963
|
+
const cfg = this.folderSidebar
|
|
4964
|
+
const scope = this.activeScope
|
|
4965
|
+
if (!cfg || !scope || !(cfg.source === 'groups' || cfg.heading === true)) {
|
|
4966
|
+
return null
|
|
4967
|
+
}
|
|
4968
|
+
const idField = this.folderPassthroughIdField
|
|
4969
|
+
const name = String(this.getByPath(scope, this.folderPassthroughNameField) ?? this.selectedFolderId)
|
|
4970
|
+
const parentId = scope.parentId ?? scope.parent ?? null
|
|
4971
|
+
const parent = parentId === null || parentId === undefined
|
|
4972
|
+
? null
|
|
4973
|
+
: this.folderSidebarFolders.find((f) => f && String(this.getByPath(f, idField)) === String(parentId))
|
|
4974
|
+
const parts = []
|
|
4975
|
+
if (parent) {
|
|
4976
|
+
parts.push(String(this.getByPath(parent, this.folderPassthroughNameField) ?? parentId))
|
|
4977
|
+
}
|
|
4978
|
+
if (typeof scope.count === 'number') {
|
|
4979
|
+
if (cfg.countText) {
|
|
4980
|
+
parts.push(this.cnTranslate(cfg.countText).replace('{count}', String(scope.count)))
|
|
4981
|
+
} else if (cfg.source === 'groups') {
|
|
4982
|
+
parts.push(n('nextcloud-vue', '{count} member', '{count} members', scope.count, { count: scope.count }))
|
|
4983
|
+
}
|
|
4984
|
+
}
|
|
4985
|
+
return { name, subtitle: parts.join(' · ') }
|
|
4986
|
+
},
|
|
4987
|
+
|
|
4988
|
+
/**
|
|
4989
|
+
* The sort select's shown value: `sortSelectValue` when the host
|
|
4990
|
+
* controls it, else on a self-fetch page the option whose order is the
|
|
4991
|
+
* list's current order (a header click that matches one shows it).
|
|
4992
|
+
*
|
|
4993
|
+
* @return {string} The option value, or '' when none matches.
|
|
4994
|
+
*/
|
|
4995
|
+
effectiveSortSelectValue() {
|
|
4996
|
+
if (this.sortSelectValue) {
|
|
4997
|
+
return this.sortSelectValue
|
|
4998
|
+
}
|
|
4999
|
+
if (!this.isSelfFetchMode || !this.list || !this.list.sortKeys) {
|
|
5000
|
+
return ''
|
|
5001
|
+
}
|
|
5002
|
+
const current = this.list.sortKeys.value || []
|
|
5003
|
+
const match = (this.sortSelectOptions || []).find((o) => o && sameSortKeys(this.sortSelectKeys(o), current))
|
|
5004
|
+
return match ? match.value : ''
|
|
5005
|
+
},
|
|
5006
|
+
|
|
4899
5007
|
/**
|
|
4900
5008
|
* The `x-index` block the active scope's own schema row carries, for a
|
|
4901
5009
|
* folder list derived from a register. Empty for every other source:
|
|
@@ -6669,6 +6777,7 @@ export default {
|
|
|
6669
6777
|
variant: button.variant === 'primary' ? 'primary' : 'secondary',
|
|
6670
6778
|
icon,
|
|
6671
6779
|
format: button.format === 'excel' ? 'excel' : 'csv',
|
|
6780
|
+
run: typeof button.run === 'string' ? button.run : '',
|
|
6672
6781
|
items: Array.isArray(button.items)
|
|
6673
6782
|
? button.items.filter((item) => (typeof item === 'string' && item !== '')
|
|
6674
6783
|
|| (item && typeof item === 'object' && typeof item.heading === 'string' && item.heading !== ''))
|
|
@@ -6676,6 +6785,10 @@ export default {
|
|
|
6676
6785
|
}
|
|
6677
6786
|
})
|
|
6678
6787
|
.filter((button) => button.label !== '')
|
|
6788
|
+
// An Export with nothing to export through is not drawn: a
|
|
6789
|
+
// named or endpoint source with no `run` and no `@mass-export`
|
|
6790
|
+
// listener.
|
|
6791
|
+
.filter((button) => button.action !== 'export' || this.headerExportAvailable(button))
|
|
6679
6792
|
// The Actions menu lists the page's header actions (or the
|
|
6680
6793
|
// entries its `items` name), so a menu with nothing to list
|
|
6681
6794
|
// is not drawn.
|
|
@@ -8464,7 +8577,9 @@ export default {
|
|
|
8464
8577
|
if (button.action === 'add') {
|
|
8465
8578
|
this.onAddClick()
|
|
8466
8579
|
} else if (button.action === 'export') {
|
|
8467
|
-
if (
|
|
8580
|
+
if (button.run) {
|
|
8581
|
+
this.onHeaderAction({ action: button.run, id: button.run })
|
|
8582
|
+
} else if (this.register && this.exportSchemaSlug) {
|
|
8468
8583
|
this.onExportClick(button.format)
|
|
8469
8584
|
} else {
|
|
8470
8585
|
this.showExportDialog = true
|
|
@@ -8480,6 +8595,25 @@ export default {
|
|
|
8480
8595
|
}
|
|
8481
8596
|
},
|
|
8482
8597
|
|
|
8598
|
+
/**
|
|
8599
|
+
* Whether a header Export has something to export through. Any page
|
|
8600
|
+
* listing OpenRegister objects (self-fetch or the consumer's
|
|
8601
|
+
* `objects`) keeps it; a named or endpoint source (`entitySource`)
|
|
8602
|
+
* needs a `run` naming one of the page's header actions, or a
|
|
8603
|
+
* `@mass-export` listener.
|
|
8604
|
+
*
|
|
8605
|
+
* @spec openspec/changes/index-header-export-source/specs/index-page/spec.md#requirement-a-header-export-is-drawn-only-when-it-can-export
|
|
8606
|
+
* @param {{run?: string}} button The resolved header button.
|
|
8607
|
+
* @return {boolean}
|
|
8608
|
+
*/
|
|
8609
|
+
headerExportAvailable(button) {
|
|
8610
|
+
if (!this.isNamedSource || (button && button.run)) {
|
|
8611
|
+
return true
|
|
8612
|
+
}
|
|
8613
|
+
const props = this.$ && this.$.vnode && this.$.vnode.props
|
|
8614
|
+
return Boolean(props && typeof props.onMassExport === 'function')
|
|
8615
|
+
},
|
|
8616
|
+
|
|
8483
8617
|
/**
|
|
8484
8618
|
* Click dispatch from CnActionsBar's `@header-action`. Looks
|
|
8485
8619
|
* up the resolved entry by id, invokes its handler if any,
|
|
@@ -8600,6 +8734,38 @@ export default {
|
|
|
8600
8734
|
this.$emit('quick-filter-change', this.selectedQuickFilterIndices)
|
|
8601
8735
|
},
|
|
8602
8736
|
|
|
8737
|
+
/**
|
|
8738
|
+
* The sort keys a sort-select option stands for: its `order` (any
|
|
8739
|
+
* `orderToSortKeys` form), else its `value` read as an order.
|
|
8740
|
+
*
|
|
8741
|
+
* @param {object} option A `sortSelectOptions` entry.
|
|
8742
|
+
* @return {Array<{key: string, order: string}>} The sort keys.
|
|
8743
|
+
*/
|
|
8744
|
+
sortSelectKeys(option) {
|
|
8745
|
+
if (!option) {
|
|
8746
|
+
return []
|
|
8747
|
+
}
|
|
8748
|
+
return orderToSortKeys(option.order !== undefined ? option.order : option.value)
|
|
8749
|
+
},
|
|
8750
|
+
|
|
8751
|
+
/**
|
|
8752
|
+
* A choice in the sort select. On a self-fetch page it orders the list
|
|
8753
|
+
* as a header click does (`_order`, kept in the address); every page
|
|
8754
|
+
* also emits `sort-change` with the value.
|
|
8755
|
+
*
|
|
8756
|
+
* @param {string} value The chosen option's value.
|
|
8757
|
+
* @return {void}
|
|
8758
|
+
*/
|
|
8759
|
+
onSortSelectChange(value) {
|
|
8760
|
+
const option = (this.sortSelectOptions || []).find((o) => o && o.value === value)
|
|
8761
|
+
const keys = this.sortSelectKeys(option)
|
|
8762
|
+
if (option && keys.length > 0 && this.isSelfFetchMode && typeof this.list.onSort === 'function') {
|
|
8763
|
+
this.list.onSort({ key: keys[0].key, order: keys[0].order, keys })
|
|
8764
|
+
this.persistViewStateToRoute(this.currentViewState())
|
|
8765
|
+
}
|
|
8766
|
+
this.$emit('sort-change', value)
|
|
8767
|
+
},
|
|
8768
|
+
|
|
8603
8769
|
/**
|
|
8604
8770
|
* @param {{key: string, order: string, keys?: Array<{key: string, order: string}>}} payload Sort change from CnDataTable.
|
|
8605
8771
|
* @return {void}
|
|
@@ -7,6 +7,7 @@ import { useObjectStore } from '../../store/index.js'
|
|
|
7
7
|
// without pulling in this composable's index-only sidebar/subscription wiring.
|
|
8
8
|
import { withPersonalLenses } from '../../utils/personalLenses.js'
|
|
9
9
|
import { parseSortKeysFromQuery, resolveFilterMap, resolveQueryFilters } from '../../utils/routeFilters.js'
|
|
10
|
+
import { orderToSortKeys } from '../../utils/sortOrder.js'
|
|
10
11
|
|
|
11
12
|
function resolveInitialQuickFilterIndex(quickFilters) {
|
|
12
13
|
const tabs = Array.isArray(quickFilters) ? quickFilters : null
|
|
@@ -219,9 +220,12 @@ export function useSelfFetchList(props, instance, inject, extras = {}) {
|
|
|
219
220
|
// legacy single-key `sortKey`/`sortOrder` props.
|
|
220
221
|
const initialRoute = instance && instance.proxy && instance.proxy.$route
|
|
221
222
|
// The page's own order, which a lens without a `sort` goes back to.
|
|
223
|
+
// `defaultOrder` (manifest `config.defaultOrder`) is the order of last
|
|
224
|
+
// resort, after `sortKeys` and `sortKey`.
|
|
225
|
+
const defaultOrderKeys = orderToSortKeys(props.defaultOrder)
|
|
222
226
|
const pageSortKeys = (Array.isArray(props.sortKeys) && props.sortKeys.length > 0)
|
|
223
227
|
? props.sortKeys
|
|
224
|
-
: (props.sortKey ? [{ key: props.sortKey, order: props.sortOrder || 'asc' }] :
|
|
228
|
+
: (props.sortKey ? [{ key: props.sortKey, order: props.sortOrder || 'asc' }] : defaultOrderKeys)
|
|
225
229
|
// The lens that is active on mount may set its own order.
|
|
226
230
|
const initialTabs = withPersonalLenses(props.quickFilters, props.personalLenses)
|
|
227
231
|
const initialTabSort = (!isMultiQuickFilter && Array.isArray(initialTabs))
|
|
@@ -230,6 +234,7 @@ export function useSelfFetchList(props, instance, inject, extras = {}) {
|
|
|
230
234
|
const initialSortKeys = parseSortKeysFromQuery(initialRoute)
|
|
231
235
|
|| initialTabSort
|
|
232
236
|
|| (Array.isArray(props.sortKeys) && props.sortKeys.length > 0 ? props.sortKeys : undefined)
|
|
237
|
+
|| (!props.sortKey && defaultOrderKeys.length > 0 ? defaultOrderKeys : undefined)
|
|
233
238
|
// Same deep-link restore for facet filters and search, so opening a
|
|
234
239
|
// shared/bookmarked URL reproduces the same view (CnIndexPage persists
|
|
235
240
|
// both back to the route on change).
|
|
@@ -15,6 +15,22 @@
|
|
|
15
15
|
@update:modelValue="$emit('select', object)" />
|
|
16
16
|
</div>
|
|
17
17
|
|
|
18
|
+
<!-- `config.leading`: one cell before the title, as a table cell renders
|
|
19
|
+
it (`{ key, widget?, widgetProps?, formatter? }`), e.g. the age
|
|
20
|
+
"6 dagen" in the team queue. -->
|
|
21
|
+
<span
|
|
22
|
+
v-if="leadingCell"
|
|
23
|
+
class="cn-object-row__lead"
|
|
24
|
+
:style="leadingCell.width ? { width: leadingCell.width } : null"
|
|
25
|
+
data-testid="cn-object-row-lead">
|
|
26
|
+
<CnCellRenderer
|
|
27
|
+
:value="cellValue(leadingCell)"
|
|
28
|
+
:formatter="leadingCell.formatter || null"
|
|
29
|
+
:widget="leadingCell.widget || null"
|
|
30
|
+
:widgetProps="leadingCell.widgetProps || undefined"
|
|
31
|
+
:row="object" />
|
|
32
|
+
</span>
|
|
33
|
+
|
|
18
34
|
<!-- Leading icon / image — omitted entirely when nothing is configured
|
|
19
35
|
(no image, no icon name, no slot). -->
|
|
20
36
|
<span v-if="hasLeadingIcon" class="cn-object-row__icon">
|
|
@@ -52,6 +68,22 @@
|
|
|
52
68
|
</slot>
|
|
53
69
|
</span>
|
|
54
70
|
|
|
71
|
+
<!-- `config.trailing`: cells after the badges, each in its own column
|
|
72
|
+
(the case type badge, the due text). -->
|
|
73
|
+
<span
|
|
74
|
+
v-for="(cell, index) in trailingCells"
|
|
75
|
+
:key="cell.key + '-' + index"
|
|
76
|
+
class="cn-object-row__trail"
|
|
77
|
+
:style="cell.width ? { width: cell.width } : null"
|
|
78
|
+
data-testid="cn-object-row-trail">
|
|
79
|
+
<CnCellRenderer
|
|
80
|
+
:value="cellValue(cell)"
|
|
81
|
+
:formatter="cell.formatter || null"
|
|
82
|
+
:widget="cell.widget || null"
|
|
83
|
+
:widgetProps="cell.widgetProps || undefined"
|
|
84
|
+
:row="object" />
|
|
85
|
+
</span>
|
|
86
|
+
|
|
55
87
|
<!-- Trailing actions -->
|
|
56
88
|
<span v-if="$slots.actions"
|
|
57
89
|
class="cn-object-row__actions"
|
|
@@ -67,8 +99,11 @@
|
|
|
67
99
|
<script>
|
|
68
100
|
import { NcCheckboxRadioSwitch } from '@nextcloud/vue'
|
|
69
101
|
import { useClickDragGuard } from '../../composables/useClickDragGuard.js'
|
|
102
|
+
import { joinTemplateParts } from '../../utils/fillTemplate.js'
|
|
103
|
+
import { readPath } from '../../utils/readPath.js'
|
|
70
104
|
import { resolveImageUrl } from '../../utils/resolveImageUrl.js'
|
|
71
105
|
import { isRowMiddleClick, preventMiddleClickAutoscroll } from '../../utils/rowAuxClick.js'
|
|
106
|
+
import { CnCellRenderer } from '../CnCellRenderer/index.js'
|
|
72
107
|
import { CnIcon } from '../CnIcon/index.js'
|
|
73
108
|
import { CnStatusBadge } from '../CnStatusBadge/index.js'
|
|
74
109
|
|
|
@@ -98,6 +133,7 @@ export default {
|
|
|
98
133
|
|
|
99
134
|
components: {
|
|
100
135
|
NcCheckboxRadioSwitch,
|
|
136
|
+
CnCellRenderer,
|
|
101
137
|
CnIcon,
|
|
102
138
|
CnStatusBadge,
|
|
103
139
|
},
|
|
@@ -119,7 +155,15 @@ export default {
|
|
|
119
155
|
* Explicit field mapping. Every key is optional and overrides the
|
|
120
156
|
* schema-configuration default.
|
|
121
157
|
*
|
|
122
|
-
*
|
|
158
|
+
* `subtitleTemplate` fills `{field}` tokens from the row (empty parts and
|
|
159
|
+
* their separators drop). `leading` (one cell) and `trailing` (a cell or
|
|
160
|
+
* a list) draw cells as a table column does, `{ key, widget?,
|
|
161
|
+
* widgetProps?, formatter? }`: `{ key: "createdAt", widget: "age" }`
|
|
162
|
+
* reads "6 dagen", `{ key: "deadline", widget: "date", widgetProps: {
|
|
163
|
+
* due: {} } }` the due text. A cell's `width` (CSS) lines the column
|
|
164
|
+
* up across rows.
|
|
165
|
+
*
|
|
166
|
+
* @type {{ titleField?: string, subtitleField?: string, subtitleTemplate?: string, imageField?: string, iconField?: string, iconName?: string, badgeField?: string, badgeVariantField?: string, badgeVariant?: string, badgeColorMap?: object, leading?: object, trailing?: (object|Array<object>) }}
|
|
123
167
|
*/
|
|
124
168
|
config: {
|
|
125
169
|
type: Object,
|
|
@@ -168,6 +212,13 @@ export default {
|
|
|
168
212
|
},
|
|
169
213
|
|
|
170
214
|
subtitle() {
|
|
215
|
+
if (typeof this.config.subtitleTemplate === 'string' && this.config.subtitleTemplate !== '') {
|
|
216
|
+
const text = joinTemplateParts(this.config.subtitleTemplate, (key) => {
|
|
217
|
+
const value = readPath(this.object, key)
|
|
218
|
+
return value === null || value === undefined ? '' : String(value)
|
|
219
|
+
})
|
|
220
|
+
return text.trim() || null
|
|
221
|
+
}
|
|
171
222
|
const field = this.config.subtitleField || this.schemaConfig.objectDescriptionField
|
|
172
223
|
if (field && this.object[field] !== null && this.object[field] !== undefined && this.object[field] !== '') {
|
|
173
224
|
return String(this.object[field])
|
|
@@ -215,9 +266,42 @@ export default {
|
|
|
215
266
|
badgeColorMap() {
|
|
216
267
|
return this.config.badgeColorMap || null
|
|
217
268
|
},
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* The `config.leading` cell, or null.
|
|
272
|
+
*
|
|
273
|
+
* @spec openspec/changes/index-row-list-cells/specs/object-row/spec.md#requirement-a-list-row-can-carry-leading-and-trailing-cells
|
|
274
|
+
* @return {object|null}
|
|
275
|
+
*/
|
|
276
|
+
leadingCell() {
|
|
277
|
+
const cell = this.config.leading
|
|
278
|
+
return cell && typeof cell === 'object' && typeof cell.key === 'string' && cell.key !== '' ? cell : null
|
|
279
|
+
},
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* The `config.trailing` cells (one object or a list), those with a key.
|
|
283
|
+
*
|
|
284
|
+
* @spec openspec/changes/index-row-list-cells/specs/object-row/spec.md#requirement-a-list-row-can-carry-leading-and-trailing-cells
|
|
285
|
+
* @return {Array<object>}
|
|
286
|
+
*/
|
|
287
|
+
trailingCells() {
|
|
288
|
+
const cells = this.config.trailing
|
|
289
|
+
const list = Array.isArray(cells) ? cells : (cells && typeof cells === 'object' ? [cells] : [])
|
|
290
|
+
return list.filter((cell) => cell && typeof cell === 'object' && typeof cell.key === 'string' && cell.key !== '')
|
|
291
|
+
},
|
|
218
292
|
},
|
|
219
293
|
|
|
220
294
|
methods: {
|
|
295
|
+
/**
|
|
296
|
+
* A cell's value: the dot-path `key` read off the row.
|
|
297
|
+
*
|
|
298
|
+
* @param {{key: string}} cell The cell.
|
|
299
|
+
* @return {unknown} The value.
|
|
300
|
+
*/
|
|
301
|
+
cellValue(cell) {
|
|
302
|
+
return readPath(this.object, cell.key)
|
|
303
|
+
},
|
|
304
|
+
|
|
221
305
|
/**
|
|
222
306
|
* Row-body click: emits `select` when `selectable` (ignoring drags),
|
|
223
307
|
* otherwise emits `click` for navigation.
|
|
@@ -297,6 +381,16 @@ export default {
|
|
|
297
381
|
background-color: var(--color-primary-element-light);
|
|
298
382
|
}
|
|
299
383
|
|
|
384
|
+
.cn-object-row__lead,
|
|
385
|
+
.cn-object-row__trail {
|
|
386
|
+
flex: 0 0 auto;
|
|
387
|
+
min-width: 0;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.cn-object-row__lead {
|
|
391
|
+
font-weight: 600;
|
|
392
|
+
}
|
|
393
|
+
|
|
300
394
|
.cn-object-row__checkbox {
|
|
301
395
|
flex-shrink: 0;
|
|
302
396
|
}
|
|
@@ -339,7 +339,11 @@ export default {
|
|
|
339
339
|
list-style: none;
|
|
340
340
|
}
|
|
341
341
|
|
|
342
|
-
|
|
342
|
+
/* A navigation entry, not a link in running text: no underline, also not
|
|
343
|
+
on hover. `!important` because a theme's `a { text-decoration }` carries
|
|
344
|
+
it (thematiq), and a bare `a` with `!important` beats any class. */
|
|
345
|
+
.cn-user-settings-dialog__nav-link,
|
|
346
|
+
.cn-user-settings-dialog__nav-link:visited {
|
|
343
347
|
display: flex;
|
|
344
348
|
align-items: center;
|
|
345
349
|
gap: 10px;
|
|
@@ -348,11 +352,12 @@ export default {
|
|
|
348
352
|
border-radius: var(--border-radius-element, 8px);
|
|
349
353
|
font-size: 15px;
|
|
350
354
|
color: var(--color-main-text);
|
|
351
|
-
text-decoration: none;
|
|
355
|
+
text-decoration: none !important;
|
|
352
356
|
}
|
|
353
357
|
|
|
354
358
|
.cn-user-settings-dialog__nav-link:hover {
|
|
355
359
|
background: var(--color-background-hover);
|
|
360
|
+
text-decoration: none !important;
|
|
356
361
|
}
|
|
357
362
|
|
|
358
363
|
.cn-user-settings-dialog__nav-link:focus-visible {
|
|
@@ -368,6 +368,20 @@ export default {
|
|
|
368
368
|
default: 0,
|
|
369
369
|
},
|
|
370
370
|
|
|
371
|
+
/**
|
|
372
|
+
* Rows per page: the board's pager under the table ("4 of 7", page
|
|
373
|
+
* buttons, Next), forwarded to CnDataTable. It pages the rows the
|
|
374
|
+
* widget holds; with a `source`, `source.limit` is how many are read
|
|
375
|
+
* (OpenRegister's default page otherwise). 0 (the default) shows no
|
|
376
|
+
* pager.
|
|
377
|
+
*
|
|
378
|
+
* @spec openspec/changes/object-table-pager/specs/object-table/spec.md#requirement-an-object-table-can-page-its-rows
|
|
379
|
+
*/
|
|
380
|
+
pageSize: {
|
|
381
|
+
type: Number,
|
|
382
|
+
default: 0,
|
|
383
|
+
},
|
|
384
|
+
|
|
371
385
|
/**
|
|
372
386
|
* vue-router route object for the "View all" footer link. Forwarded
|
|
373
387
|
* to CnDataTable (footer shows when the rows are a `limit`-ed subset).
|
|
@@ -611,7 +625,9 @@ export default {
|
|
|
611
625
|
params[`_order[${field}]`] = dir === 'desc' ? 'desc' : 'asc'
|
|
612
626
|
}
|
|
613
627
|
if (typeof src.limit === 'number' && src.limit > 0) {
|
|
614
|
-
|
|
628
|
+
// Under a pager `limit` is how many rows the pages hold, not a
|
|
629
|
+
// "View all" cap that needs to see one more.
|
|
630
|
+
params._limit = this.pageSize > 0 ? src.limit : src.limit + 1
|
|
615
631
|
}
|
|
616
632
|
// `source.extend` (#91 Wave 3): forwarded as OR's repeated
|
|
617
633
|
// `_extend[]` param so virtual/declarative fields (e.g.
|
|
@@ -736,7 +752,7 @@ export default {
|
|
|
736
752
|
inner.register = this.source.register
|
|
737
753
|
inner.schemaId = this.source.schema
|
|
738
754
|
inner.fetchParams = this.resolvedFetchParams
|
|
739
|
-
if (typeof this.source.limit === 'number' && this.source.limit > 0) {
|
|
755
|
+
if (typeof this.source.limit === 'number' && this.source.limit > 0 && !(this.pageSize > 0)) {
|
|
740
756
|
inner.limit = this.source.limit
|
|
741
757
|
}
|
|
742
758
|
}
|
|
@@ -14,7 +14,7 @@ import { formatRelativeDate } from '../utils/boardDate.js'
|
|
|
14
14
|
import { fetchReferenceObjects } from '../utils/boardReferenceColumns.js'
|
|
15
15
|
import { fillTemplate } from '../utils/fillTemplate.js'
|
|
16
16
|
import { FLOW_RUN_ERROR_STATUSES, flowRunStatuses } from '../utils/flowRunStatus.js'
|
|
17
|
-
import { fetchGroupMembers, fetchPresence, fetchStatuses } from '../utils/nextcloudDirectory.js'
|
|
17
|
+
import { fetchGroupMembers, fetchPresence, fetchStatuses, fetchUsers } from '../utils/nextcloudDirectory.js'
|
|
18
18
|
import { readPath } from '../utils/readPath.js'
|
|
19
19
|
import { useFlowStore } from './useFlowStore.js'
|
|
20
20
|
import { organisationRecord, useOrganisationStore } from './useOrganisationStore.js'
|
|
@@ -1054,7 +1054,8 @@ function searchSource(config = {}, context = {}) {
|
|
|
1054
1054
|
* DqAfdelingen). Pair it with `folderSidebar: { source: 'groups',
|
|
1055
1055
|
* filterField: 'group' }`, which lists the groups as the folder tree; the
|
|
1056
1056
|
* selected group arrives as `group`. `sourceConfig.group` is the group shown
|
|
1057
|
-
* before one is picked
|
|
1057
|
+
* before one is picked; without one, All lists every user the reader may
|
|
1058
|
+
* list (all users for an admin, their groups' users for a sub-admin).
|
|
1058
1059
|
*
|
|
1059
1060
|
* Today is the dav app's out-of-office period when one runs now ("Absent
|
|
1060
1061
|
* until 14 Oct"), else the user status (present, away) and its message. The
|
|
@@ -1088,14 +1089,13 @@ function usersSource() {
|
|
|
1088
1089
|
load: async (config) => {
|
|
1089
1090
|
const group = String((config && (config.group || config.defaultGroup)) || '')
|
|
1090
1091
|
const mine = ++seq
|
|
1091
|
-
if (group === '') {
|
|
1092
|
-
state.rows = []
|
|
1093
|
-
return
|
|
1094
|
-
}
|
|
1095
1092
|
state.loading = true
|
|
1096
1093
|
try {
|
|
1097
1094
|
const { default: axios } = await import('@nextcloud/axios')
|
|
1098
|
-
const [members, statuses] = await Promise.all([
|
|
1095
|
+
const [members, statuses] = await Promise.all([
|
|
1096
|
+
group === '' ? fetchUsers(axios, { limit: (config && config.limit) || 500 }) : fetchGroupMembers(axios, group),
|
|
1097
|
+
fetchStatuses(axios),
|
|
1098
|
+
])
|
|
1099
1099
|
const functionField = (config && config.functionField) || 'headline'
|
|
1100
1100
|
const rolesField = (config && config.rolesField) || 'role'
|
|
1101
1101
|
const rows = await Promise.all(members.map(async (user) => {
|