@conduction/nextcloud-vue 2.100.0 → 2.101.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/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 +51 -7
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +104 -73
- 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 +251 -71
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +317 -209
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/useNamedSource.js +4 -1
- 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/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/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/composables/indexSources.js +167 -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 +37 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +35 -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 +769 -251
- 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/validateManifestV2.compiled.js +3 -3
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +8977 -6843
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +769 -251
- package/l10n/en.json +37 -1
- package/l10n/nl.json +35 -1
- package/package.json +1 -1
- package/src/components/CnChoiceCards/CnChoiceCards.md +39 -0
- package/src/components/CnChoiceCards/CnChoiceCards.vue +93 -16
- package/src/components/CnDashboardPage/CnDashboardPage.vue +89 -20
- 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.vue +240 -4
- package/src/components/CnIndexPage/useNamedSource.js +4 -1
- package/src/components/CnMapWidget/CnMapWidget.vue +37 -0
- 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/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/index.js +3 -0
- package/src/composables/indexSources.js +166 -19
- package/src/composables/useEndpointSource.js +28 -6
- package/src/css/index-page.css +117 -0
- package/src/css/look-board.css +44 -0
- package/src/index.js +1 -0
- package/src/schemas/app-manifest-v2.schema.json +31 -1
- package/src/utils/languageMapText.js +46 -0
- package/src/utils/libraryWidgetKeys.js +3 -0
- package/src/utils/mapList.js +67 -0
- package/src/utils/validateManifestV2.compiled.js +3 -3
|
@@ -6,6 +6,8 @@ import HandBackRight from 'vue-material-design-icons/HandBackRight.vue';
|
|
|
6
6
|
import LightningBolt from 'vue-material-design-icons/LightningBolt.vue';
|
|
7
7
|
import Pencil from 'vue-material-design-icons/Pencil.vue';
|
|
8
8
|
import { formatRelativeDate } from '../utils/boardDate.js';
|
|
9
|
+
import { fetchReferenceObjects } from '../utils/boardReferenceColumns.js';
|
|
10
|
+
import { fillTemplate } from '../utils/fillTemplate.js';
|
|
9
11
|
import { flowRunStatuses, FLOW_RUN_ERROR_STATUSES } from '../utils/flowRunStatus.js';
|
|
10
12
|
import { fetchGroupMembers, fetchStatuses, fetchPresence } from '../utils/nextcloudDirectory.js';
|
|
11
13
|
import { readPath } from '../utils/readPath.js';
|
|
@@ -649,6 +651,10 @@ function readRowsPath(body, path) {
|
|
|
649
651
|
* requested: page-config keys must never become query parameters. The list
|
|
650
652
|
* stays empty and one console error names the page.
|
|
651
653
|
*
|
|
654
|
+
* A quick filter with `showCount` is counted with `count(config)`: the same
|
|
655
|
+
* request with that tab's filter, its total read at `sourceConfig.totalPath`
|
|
656
|
+
* (`total` by default), else the number of rows the answer holds.
|
|
657
|
+
*
|
|
652
658
|
* @return {object} The source adapter.
|
|
653
659
|
* @spec openspec/changes/index-endpoint-source/specs/index-page/spec.md#requirement-an-index-page-can-list-rows-from-an-app-endpoint
|
|
654
660
|
*/
|
|
@@ -657,6 +663,36 @@ function endpointSource() {
|
|
|
657
663
|
let seq = 0;
|
|
658
664
|
let warned = false;
|
|
659
665
|
|
|
666
|
+
/**
|
|
667
|
+
* The query parameters for a config: every key but the url and the paths,
|
|
668
|
+
* empty values left out, a list joined by commas.
|
|
669
|
+
*
|
|
670
|
+
* @param {object} rest The config without `url`, `rowsPath` and `totalPath`.
|
|
671
|
+
* @return {object} The parameters.
|
|
672
|
+
*/
|
|
673
|
+
const paramsOf = (rest) => {
|
|
674
|
+
const params = {};
|
|
675
|
+
for (const [key, value] of Object.entries(rest)) {
|
|
676
|
+
if (value === undefined || value === null || value === '') {
|
|
677
|
+
continue
|
|
678
|
+
}
|
|
679
|
+
params[key] = Array.isArray(value) ? value.join(',') : (typeof value === 'object' ? JSON.stringify(value) : value);
|
|
680
|
+
}
|
|
681
|
+
return params
|
|
682
|
+
};
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* Whether a config can be requested: it names a url and is not the page
|
|
686
|
+
* config handed over in its place.
|
|
687
|
+
*
|
|
688
|
+
* @param {object} config The config.
|
|
689
|
+
* @return {boolean}
|
|
690
|
+
*/
|
|
691
|
+
const requestable = (config) => {
|
|
692
|
+
const url = config && config.url;
|
|
693
|
+
return typeof url === 'string' && url !== '' && !Object.hasOwn(config, 'entitySource')
|
|
694
|
+
};
|
|
695
|
+
|
|
660
696
|
return {
|
|
661
697
|
/**
|
|
662
698
|
* Fetch the rows.
|
|
@@ -665,10 +701,9 @@ function endpointSource() {
|
|
|
665
701
|
* @return {Promise<void>} Resolves when loaded; a failed request leaves no rows and rethrows.
|
|
666
702
|
*/
|
|
667
703
|
load: async (config) => {
|
|
668
|
-
const { url, rowsPath, ...rest } = config || {};
|
|
704
|
+
const { url, rowsPath, totalPath, ...rest } = config || {};
|
|
669
705
|
const mine = ++seq;
|
|
670
|
-
|
|
671
|
-
if (typeof url !== 'string' || url === '' || pageConfig) {
|
|
706
|
+
if (!requestable(config)) {
|
|
672
707
|
state.rows = [];
|
|
673
708
|
if (!warned) {
|
|
674
709
|
warned = true;
|
|
@@ -678,13 +713,7 @@ function endpointSource() {
|
|
|
678
713
|
}
|
|
679
714
|
return
|
|
680
715
|
}
|
|
681
|
-
const params =
|
|
682
|
-
for (const [key, value] of Object.entries(rest)) {
|
|
683
|
-
if (value === undefined || value === null || value === '') {
|
|
684
|
-
continue
|
|
685
|
-
}
|
|
686
|
-
params[key] = Array.isArray(value) ? value.join(',') : (typeof value === 'object' ? JSON.stringify(value) : value);
|
|
687
|
-
}
|
|
716
|
+
const params = paramsOf(rest);
|
|
688
717
|
state.loading = true;
|
|
689
718
|
try {
|
|
690
719
|
const { default: axios } = await import('@nextcloud/axios');
|
|
@@ -706,6 +735,29 @@ function endpointSource() {
|
|
|
706
735
|
}
|
|
707
736
|
},
|
|
708
737
|
|
|
738
|
+
/**
|
|
739
|
+
* Count the rows for a config without touching the rows on screen, for
|
|
740
|
+
* a quick-filter tab with `showCount`.
|
|
741
|
+
*
|
|
742
|
+
* @param {object} config The page's source config with the tab's filter merged in.
|
|
743
|
+
* @return {Promise<number>} The total, or NaN when nothing can be requested.
|
|
744
|
+
*/
|
|
745
|
+
count: async (config) => {
|
|
746
|
+
if (!requestable(config)) {
|
|
747
|
+
return Number.NaN
|
|
748
|
+
}
|
|
749
|
+
const { url, rowsPath, totalPath, ...rest } = config;
|
|
750
|
+
const { default: axios } = await import('@nextcloud/axios');
|
|
751
|
+
const res = await axios.get(generateUrl(url), { params: paramsOf(rest) });
|
|
752
|
+
const body = res ? res.data : null;
|
|
753
|
+
const total = body && typeof body === 'object' && !Array.isArray(body) ? readRowsPath(body, totalPath || 'total') : undefined;
|
|
754
|
+
if (typeof total === 'number' && Number.isFinite(total)) {
|
|
755
|
+
return total
|
|
756
|
+
}
|
|
757
|
+
const rows = Array.isArray(body) ? body : readRowsPath(body, rowsPath || 'results');
|
|
758
|
+
return Array.isArray(rows) ? rows.length : Number.NaN
|
|
759
|
+
},
|
|
760
|
+
|
|
709
761
|
/**
|
|
710
762
|
* @return {Array<object>} The rows the endpoint returned.
|
|
711
763
|
*/
|
|
@@ -738,39 +790,102 @@ function fillRowTemplate(template, row) {
|
|
|
738
790
|
* and its count (DqZoekresultaten).
|
|
739
791
|
*
|
|
740
792
|
* `sourceConfig.kinds` lists what is searched:
|
|
741
|
-
* `{ id, label, register, schema, titleField?, subtitleField?, statusField?, href?, limit? }`
|
|
793
|
+
* `{ id, label, register, schema, titleField?, subtitleField?, statusField?, statusResolve?, linkedField?, linkedTemplate?, href?, limit? }`
|
|
742
794
|
* for objects (OpenRegister's `_search`, so the reader sees what they may
|
|
743
795
|
* read), or `{ id, label, files: true, limit? }` for the reader's files
|
|
744
796
|
* (Nextcloud's unified search). The term is `sourceConfig.term`, else the
|
|
745
797
|
* address's `?q=` (or `sourceConfig.termQuery`). A kind that fails shows no
|
|
746
798
|
* rows and no count; the others still answer.
|
|
747
799
|
*
|
|
800
|
+
* The kind's `label` goes through the app's translate (the "Soort" cell and
|
|
801
|
+
* the quick filters). `statusResolve: { register?, schema, labelField? }`
|
|
802
|
+
* reads `statusField` as a reference and shows the referenced object's label
|
|
803
|
+
* (`labelField`, else `title`, then `name`); the status column is a pill,
|
|
804
|
+
* coloured by `sourceConfig.statusColorMap`. `linkedTemplate` (a field
|
|
805
|
+
* template, "{case.title}") or `linkedField` (a path) fills the "Linked"
|
|
806
|
+
* column, drawn when a kind declares one; `sourceConfig.linkedLabel` names it.
|
|
807
|
+
* The page's title and count line can name the term as `{term}`, and the
|
|
808
|
+
* count is the active kind's total. Kind, link and status are filterable.
|
|
809
|
+
*
|
|
748
810
|
* @param {object} [config] The page's source config.
|
|
811
|
+
* @param {object} [context] The page's helpers.
|
|
812
|
+
* @param {Function} [context.translate] The app's translate.
|
|
749
813
|
* @return {object} The source adapter.
|
|
750
814
|
* @spec openspec/changes/index-search-source/specs/index-page/spec.md#requirement-an-index-page-can-search-several-schemas-and-files
|
|
751
815
|
*/
|
|
752
|
-
function searchSource(config = {}) {
|
|
816
|
+
function searchSource(config = {}, context = {}) {
|
|
817
|
+
const translate$1 = typeof context.translate === 'function' ? context.translate : (text) => text;
|
|
753
818
|
const kinds = (Array.isArray(config && config.kinds) ? config.kinds : [])
|
|
754
|
-
.filter((kind) => kind && typeof kind.id === 'string' && kind.id !== '' && (kind.files === true || (kind.register && kind.schema)))
|
|
755
|
-
|
|
819
|
+
.filter((kind) => kind && typeof kind.id === 'string' && kind.id !== '' && (kind.files === true || (kind.register && kind.schema)))
|
|
820
|
+
.map((kind) => ({ ...kind, label: translate$1(String(kind.label || kind.id)) }));
|
|
821
|
+
const hasLinked = kinds.some((kind) => kind.linkedTemplate || kind.linkedField);
|
|
822
|
+
const state = reactive({ rows: [], totals: {}, loading: false, kind: 'all', term: '', filters: {} });
|
|
756
823
|
let seq = 0;
|
|
757
824
|
|
|
825
|
+
/**
|
|
826
|
+
* The labels of the objects a kind's status points at, by id: the
|
|
827
|
+
* referenced schema read once per search.
|
|
828
|
+
*
|
|
829
|
+
* @param {object} kind The kind with `statusResolve`.
|
|
830
|
+
* @return {Promise<Map<string, string>>} Labels by id; empty when it cannot be read.
|
|
831
|
+
*/
|
|
832
|
+
const statusLabels = async (kind) => {
|
|
833
|
+
const resolve = kind.statusResolve;
|
|
834
|
+
if (!resolve || !resolve.schema) {
|
|
835
|
+
return new Map()
|
|
836
|
+
}
|
|
837
|
+
const objects = await fetchReferenceObjects({ register: resolve.register || kind.register, schema: resolve.schema }).catch(() => []);
|
|
838
|
+
const labels = new Map();
|
|
839
|
+
for (const object of objects) {
|
|
840
|
+
const id = object.id || object.uuid || (object['@self'] && object['@self'].id);
|
|
841
|
+
const label = readPath(object, resolve.labelField || 'title') ?? object.name;
|
|
842
|
+
if (id !== undefined && label !== undefined && label !== null) {
|
|
843
|
+
labels.set(String(id), String(label));
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
return labels
|
|
847
|
+
};
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* What a row shows in the Linked column.
|
|
851
|
+
*
|
|
852
|
+
* @param {object} kind The row's kind.
|
|
853
|
+
* @param {object} object The found object.
|
|
854
|
+
* @return {string}
|
|
855
|
+
*/
|
|
856
|
+
const linkedOf = (kind, object) => {
|
|
857
|
+
if (kind.linkedTemplate) {
|
|
858
|
+
return fillTemplate(translate$1(kind.linkedTemplate), object)
|
|
859
|
+
}
|
|
860
|
+
if (kind.linkedField) {
|
|
861
|
+
const value = readPath(object, kind.linkedField);
|
|
862
|
+
return value === undefined || value === null ? '' : String(value)
|
|
863
|
+
}
|
|
864
|
+
return ''
|
|
865
|
+
};
|
|
866
|
+
|
|
758
867
|
const searchObjects = async (axios, kind, term) => {
|
|
759
868
|
const url = generateUrl('/apps/openregister/api/objects/{register}/{schema}', { register: kind.register, schema: kind.schema });
|
|
760
869
|
const res = await axios.get(url, { params: { _search: term, _limit: kind.limit || 20 } });
|
|
761
870
|
const body = (res && res.data) || {};
|
|
762
871
|
const results = Array.isArray(body.results) ? body.results : (Array.isArray(body) ? body : []);
|
|
872
|
+
const labels = kind.statusResolve && kind.statusField ? await statusLabels(kind) : null;
|
|
763
873
|
return {
|
|
764
874
|
total: typeof body.total === 'number' ? body.total : results.length,
|
|
765
875
|
rows: results.map((object) => {
|
|
766
876
|
const id = object.id || object.uuid || (object['@self'] && object['@self'].id);
|
|
877
|
+
const rawStatus = kind.statusField ? readPath(object, kind.statusField) : undefined;
|
|
878
|
+
const status = rawStatus === undefined || rawStatus === null
|
|
879
|
+
? ''
|
|
880
|
+
: (labels ? (labels.get(String(rawStatus)) || '') : String(rawStatus));
|
|
767
881
|
return {
|
|
768
882
|
id: `${kind.id}:${id}`,
|
|
769
883
|
kind: kind.id,
|
|
770
884
|
kindLabel: kind.label,
|
|
771
885
|
title: String(readPath(object, kind.titleField || 'title') ?? object.name ?? id ?? ''),
|
|
772
886
|
subtitle: kind.subtitleField ? String(readPath(object, kind.subtitleField) ?? '') : '',
|
|
773
|
-
status
|
|
887
|
+
status,
|
|
888
|
+
linked: linkedOf(kind, object),
|
|
774
889
|
href: kind.href ? generateUrl(fillRowTemplate(kind.href, { ...object, id })) : '',
|
|
775
890
|
object,
|
|
776
891
|
}
|
|
@@ -793,6 +908,7 @@ function searchSource(config = {}) {
|
|
|
793
908
|
title: entry.title || '',
|
|
794
909
|
subtitle: entry.subline || '',
|
|
795
910
|
status: '',
|
|
911
|
+
linked: '',
|
|
796
912
|
href: entry.resourceUrl || '',
|
|
797
913
|
})),
|
|
798
914
|
}
|
|
@@ -807,6 +923,17 @@ function searchSource(config = {}) {
|
|
|
807
923
|
*/
|
|
808
924
|
load: async (cfg) => {
|
|
809
925
|
state.kind = cfg && typeof cfg.kind === 'string' ? cfg.kind : 'all';
|
|
926
|
+
// The header filters on Kind, Linked and Status narrow the answer
|
|
927
|
+
// already fetched; they do not search again.
|
|
928
|
+
const filters = {};
|
|
929
|
+
for (const key of ['kindLabel', 'linked', 'status']) {
|
|
930
|
+
const value = cfg && cfg[key];
|
|
931
|
+
const values = (Array.isArray(value) ? value : (value === undefined || value === null || value === '' ? [] : [value])).map(String);
|
|
932
|
+
if (values.length > 0) {
|
|
933
|
+
filters[key] = values;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
state.filters = filters;
|
|
810
937
|
const query = typeof window !== 'undefined' ? new URLSearchParams(window.location.search) : null;
|
|
811
938
|
const term = String((cfg && cfg.term) || (query && query.get((cfg && cfg.termQuery) || 'q')) || '').trim();
|
|
812
939
|
const mine = ++seq;
|
|
@@ -838,7 +965,20 @@ function searchSource(config = {}) {
|
|
|
838
965
|
/**
|
|
839
966
|
* @return {Array<object>} The results of the active kind, or all of them.
|
|
840
967
|
*/
|
|
841
|
-
rows: () => (state.kind === 'all' ? state.rows : state.rows.filter((row) => row.kind === state.kind))
|
|
968
|
+
rows: () => (state.kind === 'all' ? state.rows : state.rows.filter((row) => row.kind === state.kind))
|
|
969
|
+
.filter((row) => Object.entries(state.filters).every(([key, values]) => values.includes(String(row[key])))),
|
|
970
|
+
|
|
971
|
+
/**
|
|
972
|
+
* @return {string} The term searched for, for a `{term}` in the title or count line.
|
|
973
|
+
*/
|
|
974
|
+
term: () => state.term,
|
|
975
|
+
|
|
976
|
+
/**
|
|
977
|
+
* @return {number} The active kind's total, or every kind's together on All.
|
|
978
|
+
*/
|
|
979
|
+
total: () => (state.kind === 'all'
|
|
980
|
+
? Object.values(state.totals).reduce((sum, value) => sum + value, 0)
|
|
981
|
+
: (state.totals[state.kind] || 0)),
|
|
842
982
|
|
|
843
983
|
/**
|
|
844
984
|
* @return {boolean} Whether a search is in flight.
|
|
@@ -847,8 +987,14 @@ function searchSource(config = {}) {
|
|
|
847
987
|
|
|
848
988
|
columns: [
|
|
849
989
|
{ key: 'title', label: translate('nextcloud-vue', 'Result'), secondary: 'subtitle' },
|
|
850
|
-
{ key: 'kindLabel', label: translate('nextcloud-vue', 'Kind') },
|
|
851
|
-
{ key: '
|
|
990
|
+
{ key: 'kindLabel', label: translate('nextcloud-vue', 'Kind'), enum: kinds.map((kind) => kind.label) },
|
|
991
|
+
...(hasLinked ? [{ key: 'linked', label: config.linkedLabel ? translate$1(config.linkedLabel) : translate('nextcloud-vue', 'Linked') }] : []),
|
|
992
|
+
{
|
|
993
|
+
key: 'status',
|
|
994
|
+
label: translate('nextcloud-vue', 'Status'),
|
|
995
|
+
widget: 'badge',
|
|
996
|
+
widgetProps: { colorMap: (config && config.statusColorMap) || {} },
|
|
997
|
+
},
|
|
852
998
|
],
|
|
853
999
|
|
|
854
1000
|
quickFilters: kinds.length > 0
|
|
@@ -1119,10 +1265,11 @@ const indexSources = {
|
|
|
1119
1265
|
*
|
|
1120
1266
|
* @param {string} name The source name from `config.source`.
|
|
1121
1267
|
* @param {object} [config] The page's source config, for a source that builds its tabs from it.
|
|
1268
|
+
* @param {object} [context] The page's helpers a source may use, such as `translate` (the app's catalogue).
|
|
1122
1269
|
*
|
|
1123
1270
|
* @return {object|null} The adapter, or null when the name is not registered.
|
|
1124
1271
|
*/
|
|
1125
|
-
function resolveIndexSource(name, config) {
|
|
1272
|
+
function resolveIndexSource(name, config, context) {
|
|
1126
1273
|
const key = String(name || '');
|
|
1127
1274
|
if (!key) {
|
|
1128
1275
|
return null
|
|
@@ -1135,7 +1282,7 @@ function resolveIndexSource(name, config) {
|
|
|
1135
1282
|
return null
|
|
1136
1283
|
}
|
|
1137
1284
|
|
|
1138
|
-
return factory(config || {})
|
|
1285
|
+
return factory(config || {}, context || {})
|
|
1139
1286
|
}
|
|
1140
1287
|
|
|
1141
1288
|
/**
|