@conduction/nextcloud-vue 2.66.0 → 2.68.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 +19 -0
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +10 -3
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
- package/dist/esm/components/CnAppNav/CnAppNav.vue2.js +2 -2
- package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
- package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
- package/dist/esm/components/CnContextMenu/CnContextMenu.vue2.js +37 -26
- package/dist/esm/components/CnContextMenu/CnContextMenu.vue2.js.map +1 -1
- package/dist/esm/components/CnContextMenu/CnContextMenu.vue3.js +3 -3
- package/dist/esm/components/CnContextMenu/CnContextMenu.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +4 -4
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +87 -18
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +6 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +89 -49
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +337 -315
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/defaultActions.js +6 -7
- package/dist/esm/components/CnIndexPage/defaultActions.js.map +1 -1
- package/dist/esm/components/CnRowActions/CnRowActions.vue2.js +33 -34
- package/dist/esm/components/CnRowActions/CnRowActions.vue2.js.map +1 -1
- package/dist/esm/components/CnRowActions/CnRowActions.vue3.js +3 -3
- package/dist/esm/components/CnRowActions/CnRowActions.vue3.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js +13 -13
- 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/CnTimelineWidget/CnTimelineWidget.vue2.js +3 -2
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js.map +1 -1
- package/dist/esm/composables/useContextMenu.js +4 -2
- package/dist/esm/composables/useContextMenu.js.map +1 -1
- package/dist/esm/composables/useDependencyCheck.js +44 -1
- package/dist/esm/composables/useDependencyCheck.js.map +1 -1
- package/dist/esm/composables/useListView.js +43 -1
- package/dist/esm/composables/useListView.js.map +1 -1
- package/dist/esm/composables/useSetupStatus.js +26 -3
- package/dist/esm/composables/useSetupStatus.js.map +1 -1
- package/dist/esm/l10n/en.json.js +4 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +4 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +20 -2
- package/dist/esm/store/plugins/liveUpdates.js +19 -9
- package/dist/esm/store/plugins/liveUpdates.js.map +1 -1
- package/dist/esm/store/useObjectStore.js +20 -9
- package/dist/esm/store/useObjectStore.js.map +1 -1
- package/dist/esm/utils/resolveRowActions.js +130 -0
- package/dist/esm/utils/resolveRowActions.js.map +1 -0
- package/dist/esm/utils/rowActionItem.js +104 -0
- package/dist/esm/utils/rowActionItem.js.map +1 -0
- package/dist/esm/utils/timelineEvents.js +44 -1
- package/dist/esm/utils/timelineEvents.js.map +1 -1
- package/dist/esm/utils/validateManifest.js +59 -3
- package/dist/esm/utils/validateManifest.js.map +1 -1
- package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +1431 -802
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +20 -2
- package/l10n/en.json +4 -1
- package/l10n/nl.json +4 -1
- package/package.json +1 -1
- package/scripts/.jsdoc-baselines.json +2 -2
- package/src/components/CnActionsBar/CnActionsBar.md +2 -1
- package/src/components/CnActionsBar/CnActionsBar.vue +25 -1
- package/src/components/CnAppNav/CnAppNav.vue +2 -2
- package/src/components/CnContextMenu/CnContextMenu.vue +40 -29
- package/src/components/CnDataTable/CnDataTable.md +1 -1
- package/src/components/CnDataTable/CnDataTable.vue +4 -4
- package/src/components/CnFormDialog/CnFormDialog.vue +92 -18
- package/src/components/CnIndexPage/CnIndexPage.md +23 -1
- package/src/components/CnIndexPage/CnIndexPage.vue +108 -52
- package/src/components/CnIndexPage/defaultActions.js +6 -7
- package/src/components/CnRowActions/CnRowActions.vue +36 -37
- package/src/components/CnSetupWizard/CnSetupWizard.vue +13 -13
- package/src/components/CnTimelineWidget/CnTimelineWidget.vue +3 -2
- package/src/composables/useContextMenu.js +4 -2
- package/src/composables/useDependencyCheck.js +43 -0
- package/src/composables/useListView.js +43 -1
- package/src/composables/useSetupStatus.js +26 -3
- package/src/css/actions-bar.css +20 -2
- package/src/schemas/app-manifest-v2.schema.json +48 -10
- package/src/schemas/app-manifest.schema.json +1 -1
- package/src/store/plugins/liveUpdates.js +19 -9
- package/src/store/useObjectStore.js +19 -8
- package/src/utils/resolveRowActions.js +127 -0
- package/src/utils/rowActionItem.js +100 -0
- package/src/utils/timelineEvents.js +43 -0
- package/src/utils/validateManifest.js +60 -3
- package/src/utils/validateManifestV2.compiled.js +2 -2
|
@@ -14,9 +14,8 @@ import TrashCanOutline from 'vue-material-design-icons/TrashCanOutline.vue';
|
|
|
14
14
|
* without it, or when it returns null, View is a button.
|
|
15
15
|
* @param {{ onView: (row: object) => void, onEdit: (row: object) => void, onCopy: (row: object) => void, onDelete: (row: object) => void }} opts.handlers
|
|
16
16
|
* Click handlers bound to each emitted action, in the same order as `flags`.
|
|
17
|
-
* @return {Array<object>} The enabled actions in menu order (view, edit, copy,
|
|
18
|
-
*
|
|
19
|
-
* view additionally `to` when `viewTo` is given.
|
|
17
|
+
* @return {Array<object>} The enabled actions in menu order (view, edit, copy, delete), each `{id, builtin: true, label, icon, handler}`.
|
|
18
|
+
* The id is `view`, `edit`, `copy` or `delete`; delete additionally carries `destructive`, view additionally `to` when `viewTo` is given.
|
|
20
19
|
*/
|
|
21
20
|
function buildDefaultActions({ flags, viewIcon, viewTo = null, handlers }) {
|
|
22
21
|
// t() at BUILD-actions time, not module time: the labels were string
|
|
@@ -26,20 +25,20 @@ function buildDefaultActions({ flags, viewIcon, viewTo = null, handlers }) {
|
|
|
26
25
|
// consuming app's language is already resolved.
|
|
27
26
|
const out = [];
|
|
28
27
|
if (flags.view) {
|
|
29
|
-
const view = { label: translate('nextcloud-vue', 'View'), icon: viewIcon, handler: handlers.onView };
|
|
28
|
+
const view = { id: 'view', builtin: true, label: translate('nextcloud-vue', 'View'), icon: viewIcon, handler: handlers.onView };
|
|
30
29
|
if (typeof viewTo === 'function') {
|
|
31
30
|
view.to = viewTo;
|
|
32
31
|
}
|
|
33
32
|
out.push(view);
|
|
34
33
|
}
|
|
35
34
|
if (flags.edit) {
|
|
36
|
-
out.push({ label: translate('nextcloud-vue', 'Edit'), icon: Pencil, handler: handlers.onEdit });
|
|
35
|
+
out.push({ id: 'edit', builtin: true, label: translate('nextcloud-vue', 'Edit'), icon: Pencil, handler: handlers.onEdit });
|
|
37
36
|
}
|
|
38
37
|
if (flags.copy) {
|
|
39
|
-
out.push({ label: translate('nextcloud-vue', 'Copy'), icon: ContentCopy, handler: handlers.onCopy });
|
|
38
|
+
out.push({ id: 'copy', builtin: true, label: translate('nextcloud-vue', 'Copy'), icon: ContentCopy, handler: handlers.onCopy });
|
|
40
39
|
}
|
|
41
40
|
if (flags.del) {
|
|
42
|
-
out.push({ label: translate('nextcloud-vue', 'Delete'), icon: TrashCanOutline, destructive: true, handler: handlers.onDelete });
|
|
41
|
+
out.push({ id: 'delete', builtin: true, label: translate('nextcloud-vue', 'Delete'), icon: TrashCanOutline, destructive: true, handler: handlers.onDelete });
|
|
43
42
|
}
|
|
44
43
|
return out
|
|
45
44
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defaultActions.js","sources":["../../../../src/components/CnIndexPage/defaultActions.js"],"sourcesContent":["import { translate as t } from '@nextcloud/l10n'\nimport ContentCopy from 'vue-material-design-icons/ContentCopy.vue'\nimport Pencil from 'vue-material-design-icons/Pencil.vue'\nimport TrashCanOutline from 'vue-material-design-icons/TrashCanOutline.vue'\n\n/**\n * Build CnIndexPage's built-in row actions from the show*Action flags.\n *\n * @param {object} opts The action-building options.\n * @param {{ view: boolean, edit: boolean, copy: boolean, del: boolean }} opts.flags\n * Which built-in actions to include, from the page's `show*Action` props.\n * @param {object} opts.viewIcon Component to use as the View action icon.\n * @param {((row: object) => object|null)|null} [opts.viewTo] Where View links to;\n * without it, or when it returns null, View is a button.\n * @param {{ onView: (row: object) => void, onEdit: (row: object) => void, onCopy: (row: object) => void, onDelete: (row: object) => void }} opts.handlers\n * Click handlers bound to each emitted action, in the same order as `flags`.\n * @return {Array<object>} The enabled actions in menu order (view, edit, copy
|
|
1
|
+
{"version":3,"file":"defaultActions.js","sources":["../../../../src/components/CnIndexPage/defaultActions.js"],"sourcesContent":["import { translate as t } from '@nextcloud/l10n'\nimport ContentCopy from 'vue-material-design-icons/ContentCopy.vue'\nimport Pencil from 'vue-material-design-icons/Pencil.vue'\nimport TrashCanOutline from 'vue-material-design-icons/TrashCanOutline.vue'\n\n/**\n * Build CnIndexPage's built-in row actions from the show*Action flags.\n *\n * @param {object} opts The action-building options.\n * @param {{ view: boolean, edit: boolean, copy: boolean, del: boolean }} opts.flags\n * Which built-in actions to include, from the page's `show*Action` props.\n * @param {object} opts.viewIcon Component to use as the View action icon.\n * @param {((row: object) => object|null)|null} [opts.viewTo] Where View links to;\n * without it, or when it returns null, View is a button.\n * @param {{ onView: (row: object) => void, onEdit: (row: object) => void, onCopy: (row: object) => void, onDelete: (row: object) => void }} opts.handlers\n * Click handlers bound to each emitted action, in the same order as `flags`.\n * @return {Array<object>} The enabled actions in menu order (view, edit, copy, delete), each `{id, builtin: true, label, icon, handler}`.\n * The id is `view`, `edit`, `copy` or `delete`; delete additionally carries `destructive`, view additionally `to` when `viewTo` is given.\n */\nexport function buildDefaultActions({ flags, viewIcon, viewTo = null, handlers }) {\n\t// t() at BUILD-actions time, not module time: the labels were string\n\t// literals for the menu's whole life, which is why Edit/Copy/Delete\n\t// rendered in English in every locale despite their catalog entries\n\t// having existed all along. Runs after registerTranslations(), so the\n\t// consuming app's language is already resolved.\n\tconst out = []\n\tif (flags.view) {\n\t\tconst view = { id: 'view', builtin: true, label: t('nextcloud-vue', 'View'), icon: viewIcon, handler: handlers.onView }\n\t\tif (typeof viewTo === 'function') {\n\t\t\tview.to = viewTo\n\t\t}\n\t\tout.push(view)\n\t}\n\tif (flags.edit) {\n\t\tout.push({ id: 'edit', builtin: true, label: t('nextcloud-vue', 'Edit'), icon: Pencil, handler: handlers.onEdit })\n\t}\n\tif (flags.copy) {\n\t\tout.push({ id: 'copy', builtin: true, label: t('nextcloud-vue', 'Copy'), icon: ContentCopy, handler: handlers.onCopy })\n\t}\n\tif (flags.del) {\n\t\tout.push({ id: 'delete', builtin: true, label: t('nextcloud-vue', 'Delete'), icon: TrashCanOutline, destructive: true, handler: handlers.onDelete })\n\t}\n\treturn out\n}\n"],"names":["t"],"mappings":";;;;;AAKA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,mBAAmB,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,QAAQ,EAAE,EAAE;AAClF;AACA;AACA;AACA;AACA;AACA,CAAC,MAAM,GAAG,GAAG,GAAE;AACf,CAAC,IAAI,KAAK,CAAC,IAAI,EAAE;AACjB,EAAE,MAAM,IAAI,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAEA,SAAC,CAAC,eAAe,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC,MAAM,GAAE;AACzH,EAAE,IAAI,OAAO,MAAM,KAAK,UAAU,EAAE;AACpC,GAAG,IAAI,CAAC,EAAE,GAAG,OAAM;AACnB,GAAG;AACH,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,EAAC;AAChB,EAAE;AACF,CAAC,IAAI,KAAK,CAAC,IAAI,EAAE;AACjB,EAAE,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAEA,SAAC,CAAC,eAAe,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,MAAM,EAAE,EAAC;AACpH,EAAE;AACF,CAAC,IAAI,KAAK,CAAC,IAAI,EAAE;AACjB,EAAE,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAEA,SAAC,CAAC,eAAe,EAAE,MAAM,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,CAAC,MAAM,EAAE,EAAC;AACzH,EAAE;AACF,CAAC,IAAI,KAAK,CAAC,GAAG,EAAE;AAChB,EAAE,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAEA,SAAC,CAAC,eAAe,EAAE,QAAQ,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,QAAQ,EAAE,EAAC;AACtJ,EAAE;AACF,CAAC,OAAO,GAAG;AACX;;;;"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NcActions, NcActionButton, NcActionLink } from '@nextcloud/vue';
|
|
2
2
|
import { resolveItemActionLink, followItemActionLink } from '../../utils/actionLink.js';
|
|
3
3
|
import { isModifiedClick } from '../../utils/linkNavigation.js';
|
|
4
|
-
import {
|
|
4
|
+
import { isRowActionVisible, rowActionPayload, slugifyActionLabel, rowActionTestId, rowActionKey } from '../../utils/rowActionItem.js';
|
|
5
5
|
import __sfc_main from '../CnIcon/CnIcon.vue.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -62,11 +62,13 @@ var script = {
|
|
|
62
62
|
* - `to` (string | object | (row) => string | object) — render the entry as a link to this
|
|
63
63
|
* vue-router location; a plain click routes in place. Ignored when the router cannot resolve it.
|
|
64
64
|
* - `linkTarget` (string) — the link's `target`, e.g. `_blank`
|
|
65
|
+
* - `id` (string) — the action's id, passed in the `action` payload
|
|
66
|
+
* - `builtin` (boolean) — set by CnIndexPage on its built-in View / Edit / Copy / Delete; a built-in's testid is `cn-action-item-<id>` in every locale and its key `builtin:<id>`
|
|
65
67
|
*
|
|
66
68
|
* A link entry still emits `action`, but its `handler` is not called: the link is the
|
|
67
69
|
* navigation. A disabled entry, or a `to` without a resolvable route, stays a button.
|
|
68
70
|
*
|
|
69
|
-
* @type {Array<{label: string, icon: object | string, handler: (targetItem: object) => void, disabled: boolean | ((targetItem: object) => boolean), visible: boolean | ((targetItem: object) => boolean), title: string | ((targetItem: object) => string), destructive: boolean, href: string | ((targetItem: object) => string), to: string | object | ((targetItem: object) => string | object), linkTarget: string}>}
|
|
71
|
+
* @type {Array<{label: string, id: string, builtin: boolean, icon: object | string, handler: (targetItem: object) => void, disabled: boolean | ((targetItem: object) => boolean), visible: boolean | ((targetItem: object) => boolean), title: string | ((targetItem: object) => string), destructive: boolean, href: string | ((targetItem: object) => string), to: string | object | ((targetItem: object) => string | object), linkTarget: string}>}
|
|
70
72
|
*/
|
|
71
73
|
actions: {
|
|
72
74
|
type: Array,
|
|
@@ -96,31 +98,13 @@ var script = {
|
|
|
96
98
|
|
|
97
99
|
computed: {
|
|
98
100
|
/**
|
|
99
|
-
* Filter actions by their `visible`
|
|
100
|
-
*
|
|
101
|
+
* Filter actions by their `visibleWhen` and `visible` gates, with the same rule CnContextMenu uses.
|
|
102
|
+
* An action without either always shows.
|
|
101
103
|
*
|
|
102
104
|
* @return {Array} Visible actions for the current row.
|
|
103
105
|
*/
|
|
104
106
|
visibleActions() {
|
|
105
|
-
return this.actions.filter((action) =>
|
|
106
|
-
// A manifest is JSON and cannot hold a function, so `visibleWhen` is
|
|
107
|
-
// the only per-row gate an app configured from one can express. Only
|
|
108
|
-
// a locally decidable condition is gated on: an endpoint/source one is
|
|
109
|
-
// not this evaluator's question, and nothing gated a row action at all
|
|
110
|
-
// before, so answering `false` to it would delete the entry outright.
|
|
111
|
-
if (action.visibleWhen
|
|
112
|
-
&& isLocallyDecidableVisibleWhen(action.visibleWhen)
|
|
113
|
-
&& evaluateVisibleWhenLocal(action.visibleWhen, this.row) === false) {
|
|
114
|
-
return false
|
|
115
|
-
}
|
|
116
|
-
if (action.visible === undefined) {
|
|
117
|
-
return true
|
|
118
|
-
}
|
|
119
|
-
if (typeof action.visible === 'function') {
|
|
120
|
-
return !!action.visible(this.row)
|
|
121
|
-
}
|
|
122
|
-
return !!action.visible
|
|
123
|
-
})
|
|
107
|
+
return this.actions.filter((action) => isRowActionVisible(action, this.row))
|
|
124
108
|
},
|
|
125
109
|
|
|
126
110
|
/**
|
|
@@ -169,7 +153,7 @@ var script = {
|
|
|
169
153
|
if (action.handler && typeof action.handler === 'function') {
|
|
170
154
|
action.handler(this.row);
|
|
171
155
|
}
|
|
172
|
-
this.$emit('action',
|
|
156
|
+
this.$emit('action', rowActionPayload(action, this.row));
|
|
173
157
|
},
|
|
174
158
|
|
|
175
159
|
/**
|
|
@@ -188,26 +172,41 @@ var script = {
|
|
|
188
172
|
}
|
|
189
173
|
followItemActionLink(event, link, this.$router);
|
|
190
174
|
/**
|
|
191
|
-
* @event action User picked an entry. Payload: the action's
|
|
192
|
-
* @type {{ action: string, row: object|null }}
|
|
175
|
+
* @event action User picked an entry. Payload: `action` (the label), the row, the action's `id` when it has one, and `builtin: true` for a CnIndexPage built-in. A button entry has already run its `handler`; a link entry navigates instead, and a modified (new-tab) click on a link emits nothing.
|
|
176
|
+
* @type {{ action: string, row: object|null, id?: string, builtin?: boolean }}
|
|
193
177
|
*/
|
|
194
|
-
this.$emit('action',
|
|
178
|
+
this.$emit('action', rowActionPayload(action, this.row));
|
|
195
179
|
},
|
|
196
180
|
|
|
197
181
|
/**
|
|
198
182
|
* Slugify an action label for use in stable `data-testid` selectors.
|
|
199
|
-
* Lowercase, kebab-case, strip non-alphanumeric.
|
|
200
|
-
* `:data-testid` binding on NcActionButton — does not affect runtime
|
|
201
|
-
* behaviour or rendered text.
|
|
183
|
+
* Lowercase, kebab-case, strip non-alphanumeric.
|
|
202
184
|
*
|
|
203
185
|
* @param {string} label - The action's display label
|
|
204
186
|
* @return {string} kebab-case slug suitable for a testid suffix.
|
|
205
187
|
*/
|
|
206
188
|
slugifyLabel(label) {
|
|
207
|
-
return
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
189
|
+
return slugifyActionLabel(label)
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* The entry's `data-testid`: `cn-action-item-<id>` for a built-in, the label slug otherwise.
|
|
194
|
+
*
|
|
195
|
+
* @param {object} action The action definition.
|
|
196
|
+
* @return {string} The testid.
|
|
197
|
+
*/
|
|
198
|
+
actionTestId(action) {
|
|
199
|
+
return rowActionTestId(action)
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* The entry's render key: `builtin:<id>` for a built-in, the label otherwise.
|
|
204
|
+
*
|
|
205
|
+
* @param {object} action The action definition.
|
|
206
|
+
* @return {string} The key.
|
|
207
|
+
*/
|
|
208
|
+
actionKey(action) {
|
|
209
|
+
return rowActionKey(action)
|
|
211
210
|
},
|
|
212
211
|
},
|
|
213
212
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnRowActions.vue2.js","sources":["../../../../src/components/CnRowActions/CnRowActions.vue"],"sourcesContent":["<template>\n\t<NcActions :forceMenu=\"visibleActions.length > 3\"\n\t\t:primary=\"primary\"\n\t\t:menuName=\"menuName\"\n\t\tdata-testid=\"cn-row-actions\">\n\t\t<template v-for=\"{ action, link } in renderedActions\" :key=\"action.label\">\n\t\t\t<!-- A navigate-only action is a real link, so it can be middle-clicked, opened in a new tab or copied. -->\n\t\t\t<NcActionLink\n\t\t\t\tv-if=\"link\"\n\t\t\t\t:href=\"link.href\"\n\t\t\t\t:target=\"link.target\"\n\t\t\t\t:title=\"getTitle(action)\"\n\t\t\t\t:class=\"{ 'cn-row-action--destructive': action.destructive }\"\n\t\t\t\t:data-testid=\"`cn-action-item-${slugifyLabel(action.label)}`\"\n\t\t\t\tcloseAfterClick\n\t\t\t\t@click=\"onLinkAction(action, link, $event)\">\n\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t<CnIcon v-if=\"typeof action.icon === 'string'\" :name=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t<component :is=\"action.icon\" v-else :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ action.label }}\n\t\t\t</NcActionLink>\n\t\t\t<NcActionButton\n\t\t\t\tv-else\n\t\t\t\t:title=\"getTitle(action)\"\n\t\t\t\t:disabled=\"isDisabled(action)\"\n\t\t\t\t:class=\"{ 'cn-row-action--destructive': action.destructive }\"\n\t\t\t\t:data-testid=\"`cn-action-item-${slugifyLabel(action.label)}`\"\n\t\t\t\tcloseAfterClick\n\t\t\t\t@click=\"onAction(action)\">\n\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t<CnIcon v-if=\"typeof action.icon === 'string'\" :name=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t<component :is=\"action.icon\" v-else :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ action.label }}\n\t\t\t</NcActionButton>\n\t\t</template>\n\t</NcActions>\n</template>\n\n<script>\nimport { NcActionButton, NcActionLink, NcActions } from '@nextcloud/vue'\nimport { followItemActionLink, resolveItemActionLink } from '../../utils/actionLink.js'\nimport { isModifiedClick } from '../../utils/linkNavigation.js'\nimport { evaluateVisibleWhenLocal, isLocallyDecidableVisibleWhen } from '../../utils/visibleWhen.js'\nimport { CnIcon } from '../CnIcon/index.js'\n\n/**\n * CnRowActions — Action menu wrapper for table rows and cards.\n *\n * Wraps NcActions + NcActionButton for consistent row/card action menus.\n * Actions are defined as an array of objects with label, icon, handler, etc.\n *\n * ```vue\n * <CnRowActions\n * :actions=\"[\n * { label: 'Edit', icon: PencilIcon, handler: (row) => editRow(row) },\n * { label: 'Delete', icon: TrashIcon, handler: (row) => deleteRow(row), destructive: true },\n * ]\"\n * :row=\"row\" />\n * ```\n *\n * An action whose only job is to go somewhere carries `href` (a URL) or `to`\n * (a vue-router location) instead of a navigating `handler`, and renders as a\n * real link — middle-click, \"open in new tab\" and \"copy link\" all work:\n *\n * ```vue\n * <CnRowActions\n * :actions=\"[\n * { label: 'View', icon: 'Eye', to: (row) => ({ name: 'LeadDetail', params: { id: row.id } }) },\n * { label: 'Website', icon: 'Web', href: (row) => row.url, linkTarget: '_blank' },\n * ]\"\n * :row=\"row\" />\n * ```\n */\nexport default {\n\tname: 'CnRowActions',\n\n\tcomponents: {\n\t\tNcActions,\n\t\tNcActionButton,\n\t\tNcActionLink,\n\t\tCnIcon,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Action definitions.\n\t\t *\n\t\t * Each action supports:\n\t\t * - `label` (string, required) — display text\n\t\t * - `icon` (component | string) — MDI icon. A component renders\n\t\t * directly; a string is treated as a registry name and rendered\n\t\t * via `CnIcon` (PascalCase, e.g. `\"Eye\"`), falling back to the\n\t\t * help-circle when unregistered. The string form lets manifest\n\t\t * (JSON) actions declare icons by name.\n\t\t * - `handler` (function) — called with `row` on click\n\t\t * - `disabled` (boolean | (row) => boolean) — gray out the entry\n\t\t * - `visible` (boolean | (row) => boolean) — when `false`, hide the entry from the menu (default: shown)\n\t\t * - `title` (string | (row) => string) — native tooltip shown on hover (useful to explain why an entry is disabled)\n\t\t * - `destructive` (boolean) — apply error color styling\n\t\t * - `href` (string | (row) => string) — render the entry as a link to this URL\n\t\t * - `to` (string | object | (row) => string | object) — render the entry as a link to this\n\t\t * vue-router location; a plain click routes in place. Ignored when the router cannot resolve it.\n\t\t * - `linkTarget` (string) — the link's `target`, e.g. `_blank`\n\t\t *\n\t\t * A link entry still emits `action`, but its `handler` is not called: the link is the\n\t\t * navigation. A disabled entry, or a `to` without a resolvable route, stays a button.\n\t\t *\n\t\t * @type {Array<{label: string, icon: object | string, handler: (targetItem: object) => void, disabled: boolean | ((targetItem: object) => boolean), visible: boolean | ((targetItem: object) => boolean), title: string | ((targetItem: object) => string), destructive: boolean, href: string | ((targetItem: object) => string), to: string | object | ((targetItem: object) => string | object), linkTarget: string}>}\n\t\t */\n\t\tactions: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** The row/object data (passed to action handlers) */\n\t\trow: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Whether to use primary styling for the action menu trigger */\n\t\tprimary: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Label shown on the action menu trigger button */\n\t\tmenuName: {\n\t\t\ttype: String,\n\t\t\tdefault: undefined,\n\t\t},\n\t},\n\n\temits: ['action'],\n\n\tcomputed: {\n\t\t/**\n\t\t * Filter actions by their `visible` predicate. An action without a\n\t\t * `visible` field is always shown (backwards compatible).\n\t\t *\n\t\t * @return {Array} Visible actions for the current row.\n\t\t */\n\t\tvisibleActions() {\n\t\t\treturn this.actions.filter((action) => {\n\t\t\t\t// A manifest is JSON and cannot hold a function, so `visibleWhen` is\n\t\t\t\t// the only per-row gate an app configured from one can express. Only\n\t\t\t\t// a locally decidable condition is gated on: an endpoint/source one is\n\t\t\t\t// not this evaluator's question, and nothing gated a row action at all\n\t\t\t\t// before, so answering `false` to it would delete the entry outright.\n\t\t\t\tif (action.visibleWhen\n\t\t\t\t\t&& isLocallyDecidableVisibleWhen(action.visibleWhen)\n\t\t\t\t\t&& evaluateVisibleWhenLocal(action.visibleWhen, this.row) === false) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\tif (action.visible === undefined) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tif (typeof action.visible === 'function') {\n\t\t\t\t\treturn !!action.visible(this.row)\n\t\t\t\t}\n\t\t\t\treturn !!action.visible\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Visible actions paired with the link each renders as (null for a button).\n\t\t *\n\t\t * @return {Array<{action: object, link: object|null}>}\n\t\t */\n\t\trenderedActions() {\n\t\t\treturn this.visibleActions.map((action) => ({\n\t\t\t\taction,\n\t\t\t\tlink: this.isDisabled(action) ? null : resolveItemActionLink(action, this.row, this.$router),\n\t\t\t}))\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Resolve disabled state for an action — supports both boolean and function.\n\t\t *\n\t\t * @param {object} action - The action definition\n\t\t * @return {boolean} Whether the action is disabled\n\t\t */\n\t\tisDisabled(action) {\n\t\t\tif (typeof action.disabled === 'function') {\n\t\t\t\treturn action.disabled(this.row)\n\t\t\t}\n\t\t\treturn !!action.disabled\n\t\t},\n\n\t\t/**\n\t\t * Resolve the title (native tooltip) for an action — supports both\n\t\t * string and function forms. Returns undefined when no title is\n\t\t * provided so the attribute is not rendered.\n\t\t *\n\t\t * @param {object} action - The action definition\n\t\t * @return {string|undefined} The resolved tooltip text, or undefined.\n\t\t */\n\t\tgetTitle(action) {\n\t\t\tif (typeof action.title === 'function') {\n\t\t\t\treturn action.title(this.row) || undefined\n\t\t\t}\n\t\t\treturn action.title || undefined\n\t\t},\n\n\t\tonAction(action) {\n\t\t\tif (action.handler && typeof action.handler === 'function') {\n\t\t\t\taction.handler(this.row)\n\t\t\t}\n\t\t\tthis.$emit('action', { action: action.label, row: this.row })\n\t\t},\n\n\t\t/**\n\t\t * A link entry was clicked: route a plain in-app click, leave the rest to\n\t\t * the browser, and emit `action` as a button would. The `handler` is not\n\t\t * called, since navigating is what the link already does. A modified\n\t\t * click opens a new tab, so nothing is emitted in this one.\n\t\t *\n\t\t * @param {object} action The action definition.\n\t\t * @param {object} link The resolved link.\n\t\t * @param {MouseEvent} event The click event.\n\t\t */\n\t\tonLinkAction(action, link, event) {\n\t\t\tif (isModifiedClick(event)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfollowItemActionLink(event, link, this.$router)\n\t\t\t/**\n\t\t\t * @event action User picked an entry. Payload: the action's label and the row. A button entry has already run its `handler`; a link entry navigates instead, and a modified (new-tab) click on a link emits nothing.\n\t\t\t * @type {{ action: string, row: object|null }}\n\t\t\t */\n\t\t\tthis.$emit('action', { action: action.label, row: this.row })\n\t\t},\n\n\t\t/**\n\t\t * Slugify an action label for use in stable `data-testid` selectors.\n\t\t * Lowercase, kebab-case, strip non-alphanumeric. Used solely by the\n\t\t * `:data-testid` binding on NcActionButton — does not affect runtime\n\t\t * behaviour or rendered text.\n\t\t *\n\t\t * @param {string} label - The action's display label\n\t\t * @return {string} kebab-case slug suitable for a testid suffix.\n\t\t */\n\t\tslugifyLabel(label) {\n\t\t\treturn String(label || '')\n\t\t\t\t.toLowerCase()\n\t\t\t\t.replace(/[^a-z0-9]+/g, '-')\n\t\t\t\t.replace(/^-+|-+$/g, '')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-row-action--destructive {\n\tcolor: var(--color-error) !important;\n}\n</style>\n"],"names":["CnIcon"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnRowActions.vue2.js","sources":["../../../../src/components/CnRowActions/CnRowActions.vue"],"sourcesContent":["<template>\n\t<NcActions :forceMenu=\"visibleActions.length > 3\"\n\t\t:primary=\"primary\"\n\t\t:menuName=\"menuName\"\n\t\tdata-testid=\"cn-row-actions\">\n\t\t<template v-for=\"{ action, link } in renderedActions\" :key=\"actionKey(action)\">\n\t\t\t<!-- A navigate-only action is a real link, so it can be middle-clicked, opened in a new tab or copied. -->\n\t\t\t<NcActionLink\n\t\t\t\tv-if=\"link\"\n\t\t\t\t:href=\"link.href\"\n\t\t\t\t:target=\"link.target\"\n\t\t\t\t:title=\"getTitle(action)\"\n\t\t\t\t:class=\"{ 'cn-row-action--destructive': action.destructive }\"\n\t\t\t\t:data-testid=\"actionTestId(action)\"\n\t\t\t\tcloseAfterClick\n\t\t\t\t@click=\"onLinkAction(action, link, $event)\">\n\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t<CnIcon v-if=\"typeof action.icon === 'string'\" :name=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t<component :is=\"action.icon\" v-else :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ action.label }}\n\t\t\t</NcActionLink>\n\t\t\t<NcActionButton\n\t\t\t\tv-else\n\t\t\t\t:title=\"getTitle(action)\"\n\t\t\t\t:disabled=\"isDisabled(action)\"\n\t\t\t\t:class=\"{ 'cn-row-action--destructive': action.destructive }\"\n\t\t\t\t:data-testid=\"actionTestId(action)\"\n\t\t\t\tcloseAfterClick\n\t\t\t\t@click=\"onAction(action)\">\n\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t<CnIcon v-if=\"typeof action.icon === 'string'\" :name=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t<component :is=\"action.icon\" v-else :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ action.label }}\n\t\t\t</NcActionButton>\n\t\t</template>\n\t</NcActions>\n</template>\n\n<script>\nimport { NcActionButton, NcActionLink, NcActions } from '@nextcloud/vue'\nimport { followItemActionLink, resolveItemActionLink } from '../../utils/actionLink.js'\nimport { isModifiedClick } from '../../utils/linkNavigation.js'\nimport { isRowActionVisible, rowActionKey, rowActionPayload, rowActionTestId, slugifyActionLabel } from '../../utils/rowActionItem.js'\nimport { CnIcon } from '../CnIcon/index.js'\n\n/**\n * CnRowActions — Action menu wrapper for table rows and cards.\n *\n * Wraps NcActions + NcActionButton for consistent row/card action menus.\n * Actions are defined as an array of objects with label, icon, handler, etc.\n *\n * ```vue\n * <CnRowActions\n * :actions=\"[\n * { label: 'Edit', icon: PencilIcon, handler: (row) => editRow(row) },\n * { label: 'Delete', icon: TrashIcon, handler: (row) => deleteRow(row), destructive: true },\n * ]\"\n * :row=\"row\" />\n * ```\n *\n * An action whose only job is to go somewhere carries `href` (a URL) or `to`\n * (a vue-router location) instead of a navigating `handler`, and renders as a\n * real link — middle-click, \"open in new tab\" and \"copy link\" all work:\n *\n * ```vue\n * <CnRowActions\n * :actions=\"[\n * { label: 'View', icon: 'Eye', to: (row) => ({ name: 'LeadDetail', params: { id: row.id } }) },\n * { label: 'Website', icon: 'Web', href: (row) => row.url, linkTarget: '_blank' },\n * ]\"\n * :row=\"row\" />\n * ```\n */\nexport default {\n\tname: 'CnRowActions',\n\n\tcomponents: {\n\t\tNcActions,\n\t\tNcActionButton,\n\t\tNcActionLink,\n\t\tCnIcon,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Action definitions.\n\t\t *\n\t\t * Each action supports:\n\t\t * - `label` (string, required) — display text\n\t\t * - `icon` (component | string) — MDI icon. A component renders\n\t\t * directly; a string is treated as a registry name and rendered\n\t\t * via `CnIcon` (PascalCase, e.g. `\"Eye\"`), falling back to the\n\t\t * help-circle when unregistered. The string form lets manifest\n\t\t * (JSON) actions declare icons by name.\n\t\t * - `handler` (function) — called with `row` on click\n\t\t * - `disabled` (boolean | (row) => boolean) — gray out the entry\n\t\t * - `visible` (boolean | (row) => boolean) — when `false`, hide the entry from the menu (default: shown)\n\t\t * - `title` (string | (row) => string) — native tooltip shown on hover (useful to explain why an entry is disabled)\n\t\t * - `destructive` (boolean) — apply error color styling\n\t\t * - `href` (string | (row) => string) — render the entry as a link to this URL\n\t\t * - `to` (string | object | (row) => string | object) — render the entry as a link to this\n\t\t * vue-router location; a plain click routes in place. Ignored when the router cannot resolve it.\n\t\t * - `linkTarget` (string) — the link's `target`, e.g. `_blank`\n\t\t * - `id` (string) — the action's id, passed in the `action` payload\n\t\t * - `builtin` (boolean) — set by CnIndexPage on its built-in View / Edit / Copy / Delete; a built-in's testid is `cn-action-item-<id>` in every locale and its key `builtin:<id>`\n\t\t *\n\t\t * A link entry still emits `action`, but its `handler` is not called: the link is the\n\t\t * navigation. A disabled entry, or a `to` without a resolvable route, stays a button.\n\t\t *\n\t\t * @type {Array<{label: string, id: string, builtin: boolean, icon: object | string, handler: (targetItem: object) => void, disabled: boolean | ((targetItem: object) => boolean), visible: boolean | ((targetItem: object) => boolean), title: string | ((targetItem: object) => string), destructive: boolean, href: string | ((targetItem: object) => string), to: string | object | ((targetItem: object) => string | object), linkTarget: string}>}\n\t\t */\n\t\tactions: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** The row/object data (passed to action handlers) */\n\t\trow: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Whether to use primary styling for the action menu trigger */\n\t\tprimary: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Label shown on the action menu trigger button */\n\t\tmenuName: {\n\t\t\ttype: String,\n\t\t\tdefault: undefined,\n\t\t},\n\t},\n\n\temits: ['action'],\n\n\tcomputed: {\n\t\t/**\n\t\t * Filter actions by their `visibleWhen` and `visible` gates, with the same rule CnContextMenu uses.\n\t\t * An action without either always shows.\n\t\t *\n\t\t * @return {Array} Visible actions for the current row.\n\t\t */\n\t\tvisibleActions() {\n\t\t\treturn this.actions.filter((action) => isRowActionVisible(action, this.row))\n\t\t},\n\n\t\t/**\n\t\t * Visible actions paired with the link each renders as (null for a button).\n\t\t *\n\t\t * @return {Array<{action: object, link: object|null}>}\n\t\t */\n\t\trenderedActions() {\n\t\t\treturn this.visibleActions.map((action) => ({\n\t\t\t\taction,\n\t\t\t\tlink: this.isDisabled(action) ? null : resolveItemActionLink(action, this.row, this.$router),\n\t\t\t}))\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Resolve disabled state for an action — supports both boolean and function.\n\t\t *\n\t\t * @param {object} action - The action definition\n\t\t * @return {boolean} Whether the action is disabled\n\t\t */\n\t\tisDisabled(action) {\n\t\t\tif (typeof action.disabled === 'function') {\n\t\t\t\treturn action.disabled(this.row)\n\t\t\t}\n\t\t\treturn !!action.disabled\n\t\t},\n\n\t\t/**\n\t\t * Resolve the title (native tooltip) for an action — supports both\n\t\t * string and function forms. Returns undefined when no title is\n\t\t * provided so the attribute is not rendered.\n\t\t *\n\t\t * @param {object} action - The action definition\n\t\t * @return {string|undefined} The resolved tooltip text, or undefined.\n\t\t */\n\t\tgetTitle(action) {\n\t\t\tif (typeof action.title === 'function') {\n\t\t\t\treturn action.title(this.row) || undefined\n\t\t\t}\n\t\t\treturn action.title || undefined\n\t\t},\n\n\t\tonAction(action) {\n\t\t\tif (action.handler && typeof action.handler === 'function') {\n\t\t\t\taction.handler(this.row)\n\t\t\t}\n\t\t\tthis.$emit('action', rowActionPayload(action, this.row))\n\t\t},\n\n\t\t/**\n\t\t * A link entry was clicked: route a plain in-app click, leave the rest to\n\t\t * the browser, and emit `action` as a button would. The `handler` is not\n\t\t * called, since navigating is what the link already does. A modified\n\t\t * click opens a new tab, so nothing is emitted in this one.\n\t\t *\n\t\t * @param {object} action The action definition.\n\t\t * @param {object} link The resolved link.\n\t\t * @param {MouseEvent} event The click event.\n\t\t */\n\t\tonLinkAction(action, link, event) {\n\t\t\tif (isModifiedClick(event)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfollowItemActionLink(event, link, this.$router)\n\t\t\t/**\n\t\t\t * @event action User picked an entry. Payload: `action` (the label), the row, the action's `id` when it has one, and `builtin: true` for a CnIndexPage built-in. A button entry has already run its `handler`; a link entry navigates instead, and a modified (new-tab) click on a link emits nothing.\n\t\t\t * @type {{ action: string, row: object|null, id?: string, builtin?: boolean }}\n\t\t\t */\n\t\t\tthis.$emit('action', rowActionPayload(action, this.row))\n\t\t},\n\n\t\t/**\n\t\t * Slugify an action label for use in stable `data-testid` selectors.\n\t\t * Lowercase, kebab-case, strip non-alphanumeric.\n\t\t *\n\t\t * @param {string} label - The action's display label\n\t\t * @return {string} kebab-case slug suitable for a testid suffix.\n\t\t */\n\t\tslugifyLabel(label) {\n\t\t\treturn slugifyActionLabel(label)\n\t\t},\n\n\t\t/**\n\t\t * The entry's `data-testid`: `cn-action-item-<id>` for a built-in, the label slug otherwise.\n\t\t *\n\t\t * @param {object} action The action definition.\n\t\t * @return {string} The testid.\n\t\t */\n\t\tactionTestId(action) {\n\t\t\treturn rowActionTestId(action)\n\t\t},\n\n\t\t/**\n\t\t * The entry's render key: `builtin:<id>` for a built-in, the label otherwise.\n\t\t *\n\t\t * @param {object} action The action definition.\n\t\t * @return {string} The key.\n\t\t */\n\t\tactionKey(action) {\n\t\t\treturn rowActionKey(action)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-row-action--destructive {\n\tcolor: var(--color-error) !important;\n}\n</style>\n"],"names":["CnIcon"],"mappings":"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"}
|
|
@@ -15,7 +15,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
15
15
|
default: withCtx(() => [
|
|
16
16
|
(openBlock(true), createElementBlock(Fragment, null, renderList($options.renderedActions, ({ action, link }) => {
|
|
17
17
|
return (openBlock(), createElementBlock(Fragment, {
|
|
18
|
-
key: action
|
|
18
|
+
key: $options.actionKey(action)
|
|
19
19
|
}, [
|
|
20
20
|
link
|
|
21
21
|
? (openBlock(), createBlock(_component_NcActionLink, {
|
|
@@ -24,7 +24,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
24
24
|
target: link.target,
|
|
25
25
|
title: $options.getTitle(action),
|
|
26
26
|
class: normalizeClass({ 'cn-row-action--destructive': action.destructive }),
|
|
27
|
-
"data-testid":
|
|
27
|
+
"data-testid": $options.actionTestId(action),
|
|
28
28
|
closeAfterClick: "",
|
|
29
29
|
onClick: $event => ($options.onLinkAction(action, link, $event))
|
|
30
30
|
}, createSlots({
|
|
@@ -57,7 +57,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
57
57
|
title: $options.getTitle(action),
|
|
58
58
|
disabled: $options.isDisabled(action),
|
|
59
59
|
class: normalizeClass({ 'cn-row-action--destructive': action.destructive }),
|
|
60
|
-
"data-testid":
|
|
60
|
+
"data-testid": $options.actionTestId(action),
|
|
61
61
|
closeAfterClick: "",
|
|
62
62
|
onClick: $event => ($options.onAction(action))
|
|
63
63
|
}, createSlots({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnRowActions.vue3.js","sources":["../../../../src/components/CnRowActions/CnRowActions.vue?vue&type=template&id=5d9db5e2&lang.js"],"sourcesContent":["<template>\n\t<NcActions :forceMenu=\"visibleActions.length > 3\"\n\t\t:primary=\"primary\"\n\t\t:menuName=\"menuName\"\n\t\tdata-testid=\"cn-row-actions\">\n\t\t<template v-for=\"{ action, link } in renderedActions\" :key=\"action.label\">\n\t\t\t<!-- A navigate-only action is a real link, so it can be middle-clicked, opened in a new tab or copied. -->\n\t\t\t<NcActionLink\n\t\t\t\tv-if=\"link\"\n\t\t\t\t:href=\"link.href\"\n\t\t\t\t:target=\"link.target\"\n\t\t\t\t:title=\"getTitle(action)\"\n\t\t\t\t:class=\"{ 'cn-row-action--destructive': action.destructive }\"\n\t\t\t\t:data-testid=\"`cn-action-item-${slugifyLabel(action.label)}`\"\n\t\t\t\tcloseAfterClick\n\t\t\t\t@click=\"onLinkAction(action, link, $event)\">\n\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t<CnIcon v-if=\"typeof action.icon === 'string'\" :name=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t<component :is=\"action.icon\" v-else :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ action.label }}\n\t\t\t</NcActionLink>\n\t\t\t<NcActionButton\n\t\t\t\tv-else\n\t\t\t\t:title=\"getTitle(action)\"\n\t\t\t\t:disabled=\"isDisabled(action)\"\n\t\t\t\t:class=\"{ 'cn-row-action--destructive': action.destructive }\"\n\t\t\t\t:data-testid=\"`cn-action-item-${slugifyLabel(action.label)}`\"\n\t\t\t\tcloseAfterClick\n\t\t\t\t@click=\"onAction(action)\">\n\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t<CnIcon v-if=\"typeof action.icon === 'string'\" :name=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t<component :is=\"action.icon\" v-else :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ action.label }}\n\t\t\t</NcActionButton>\n\t\t</template>\n\t</NcActions>\n</template>\n\n<script>\nimport { NcActionButton, NcActionLink, NcActions } from '@nextcloud/vue'\nimport { followItemActionLink, resolveItemActionLink } from '../../utils/actionLink.js'\nimport { isModifiedClick } from '../../utils/linkNavigation.js'\nimport { evaluateVisibleWhenLocal, isLocallyDecidableVisibleWhen } from '../../utils/visibleWhen.js'\nimport { CnIcon } from '../CnIcon/index.js'\n\n/**\n * CnRowActions — Action menu wrapper for table rows and cards.\n *\n * Wraps NcActions + NcActionButton for consistent row/card action menus.\n * Actions are defined as an array of objects with label, icon, handler, etc.\n *\n * ```vue\n * <CnRowActions\n * :actions=\"[\n * { label: 'Edit', icon: PencilIcon, handler: (row) => editRow(row) },\n * { label: 'Delete', icon: TrashIcon, handler: (row) => deleteRow(row), destructive: true },\n * ]\"\n * :row=\"row\" />\n * ```\n *\n * An action whose only job is to go somewhere carries `href` (a URL) or `to`\n * (a vue-router location) instead of a navigating `handler`, and renders as a\n * real link — middle-click, \"open in new tab\" and \"copy link\" all work:\n *\n * ```vue\n * <CnRowActions\n * :actions=\"[\n * { label: 'View', icon: 'Eye', to: (row) => ({ name: 'LeadDetail', params: { id: row.id } }) },\n * { label: 'Website', icon: 'Web', href: (row) => row.url, linkTarget: '_blank' },\n * ]\"\n * :row=\"row\" />\n * ```\n */\nexport default {\n\tname: 'CnRowActions',\n\n\tcomponents: {\n\t\tNcActions,\n\t\tNcActionButton,\n\t\tNcActionLink,\n\t\tCnIcon,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Action definitions.\n\t\t *\n\t\t * Each action supports:\n\t\t * - `label` (string, required) — display text\n\t\t * - `icon` (component | string) — MDI icon. A component renders\n\t\t * directly; a string is treated as a registry name and rendered\n\t\t * via `CnIcon` (PascalCase, e.g. `\"Eye\"`), falling back to the\n\t\t * help-circle when unregistered. The string form lets manifest\n\t\t * (JSON) actions declare icons by name.\n\t\t * - `handler` (function) — called with `row` on click\n\t\t * - `disabled` (boolean | (row) => boolean) — gray out the entry\n\t\t * - `visible` (boolean | (row) => boolean) — when `false`, hide the entry from the menu (default: shown)\n\t\t * - `title` (string | (row) => string) — native tooltip shown on hover (useful to explain why an entry is disabled)\n\t\t * - `destructive` (boolean) — apply error color styling\n\t\t * - `href` (string | (row) => string) — render the entry as a link to this URL\n\t\t * - `to` (string | object | (row) => string | object) — render the entry as a link to this\n\t\t * vue-router location; a plain click routes in place. Ignored when the router cannot resolve it.\n\t\t * - `linkTarget` (string) — the link's `target`, e.g. `_blank`\n\t\t *\n\t\t * A link entry still emits `action`, but its `handler` is not called: the link is the\n\t\t * navigation. A disabled entry, or a `to` without a resolvable route, stays a button.\n\t\t *\n\t\t * @type {Array<{label: string, icon: object | string, handler: (targetItem: object) => void, disabled: boolean | ((targetItem: object) => boolean), visible: boolean | ((targetItem: object) => boolean), title: string | ((targetItem: object) => string), destructive: boolean, href: string | ((targetItem: object) => string), to: string | object | ((targetItem: object) => string | object), linkTarget: string}>}\n\t\t */\n\t\tactions: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** The row/object data (passed to action handlers) */\n\t\trow: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Whether to use primary styling for the action menu trigger */\n\t\tprimary: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Label shown on the action menu trigger button */\n\t\tmenuName: {\n\t\t\ttype: String,\n\t\t\tdefault: undefined,\n\t\t},\n\t},\n\n\temits: ['action'],\n\n\tcomputed: {\n\t\t/**\n\t\t * Filter actions by their `visible` predicate. An action without a\n\t\t * `visible` field is always shown (backwards compatible).\n\t\t *\n\t\t * @return {Array} Visible actions for the current row.\n\t\t */\n\t\tvisibleActions() {\n\t\t\treturn this.actions.filter((action) => {\n\t\t\t\t// A manifest is JSON and cannot hold a function, so `visibleWhen` is\n\t\t\t\t// the only per-row gate an app configured from one can express. Only\n\t\t\t\t// a locally decidable condition is gated on: an endpoint/source one is\n\t\t\t\t// not this evaluator's question, and nothing gated a row action at all\n\t\t\t\t// before, so answering `false` to it would delete the entry outright.\n\t\t\t\tif (action.visibleWhen\n\t\t\t\t\t&& isLocallyDecidableVisibleWhen(action.visibleWhen)\n\t\t\t\t\t&& evaluateVisibleWhenLocal(action.visibleWhen, this.row) === false) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\tif (action.visible === undefined) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tif (typeof action.visible === 'function') {\n\t\t\t\t\treturn !!action.visible(this.row)\n\t\t\t\t}\n\t\t\t\treturn !!action.visible\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Visible actions paired with the link each renders as (null for a button).\n\t\t *\n\t\t * @return {Array<{action: object, link: object|null}>}\n\t\t */\n\t\trenderedActions() {\n\t\t\treturn this.visibleActions.map((action) => ({\n\t\t\t\taction,\n\t\t\t\tlink: this.isDisabled(action) ? null : resolveItemActionLink(action, this.row, this.$router),\n\t\t\t}))\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Resolve disabled state for an action — supports both boolean and function.\n\t\t *\n\t\t * @param {object} action - The action definition\n\t\t * @return {boolean} Whether the action is disabled\n\t\t */\n\t\tisDisabled(action) {\n\t\t\tif (typeof action.disabled === 'function') {\n\t\t\t\treturn action.disabled(this.row)\n\t\t\t}\n\t\t\treturn !!action.disabled\n\t\t},\n\n\t\t/**\n\t\t * Resolve the title (native tooltip) for an action — supports both\n\t\t * string and function forms. Returns undefined when no title is\n\t\t * provided so the attribute is not rendered.\n\t\t *\n\t\t * @param {object} action - The action definition\n\t\t * @return {string|undefined} The resolved tooltip text, or undefined.\n\t\t */\n\t\tgetTitle(action) {\n\t\t\tif (typeof action.title === 'function') {\n\t\t\t\treturn action.title(this.row) || undefined\n\t\t\t}\n\t\t\treturn action.title || undefined\n\t\t},\n\n\t\tonAction(action) {\n\t\t\tif (action.handler && typeof action.handler === 'function') {\n\t\t\t\taction.handler(this.row)\n\t\t\t}\n\t\t\tthis.$emit('action', { action: action.label, row: this.row })\n\t\t},\n\n\t\t/**\n\t\t * A link entry was clicked: route a plain in-app click, leave the rest to\n\t\t * the browser, and emit `action` as a button would. The `handler` is not\n\t\t * called, since navigating is what the link already does. A modified\n\t\t * click opens a new tab, so nothing is emitted in this one.\n\t\t *\n\t\t * @param {object} action The action definition.\n\t\t * @param {object} link The resolved link.\n\t\t * @param {MouseEvent} event The click event.\n\t\t */\n\t\tonLinkAction(action, link, event) {\n\t\t\tif (isModifiedClick(event)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfollowItemActionLink(event, link, this.$router)\n\t\t\t/**\n\t\t\t * @event action User picked an entry. Payload: the action's label and the row. A button entry has already run its `handler`; a link entry navigates instead, and a modified (new-tab) click on a link emits nothing.\n\t\t\t * @type {{ action: string, row: object|null }}\n\t\t\t */\n\t\t\tthis.$emit('action', { action: action.label, row: this.row })\n\t\t},\n\n\t\t/**\n\t\t * Slugify an action label for use in stable `data-testid` selectors.\n\t\t * Lowercase, kebab-case, strip non-alphanumeric. Used solely by the\n\t\t * `:data-testid` binding on NcActionButton — does not affect runtime\n\t\t * behaviour or rendered text.\n\t\t *\n\t\t * @param {string} label - The action's display label\n\t\t * @return {string} kebab-case slug suitable for a testid suffix.\n\t\t */\n\t\tslugifyLabel(label) {\n\t\t\treturn String(label || '')\n\t\t\t\t.toLowerCase()\n\t\t\t\t.replace(/[^a-z0-9]+/g, '-')\n\t\t\t\t.replace(/^-+|-+$/g, '')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-row-action--destructive {\n\tcolor: var(--color-error) !important;\n}\n</style>\n"],"names":["_createBlock","_openBlock","_createElementBlock","_Fragment","_renderList","_normalizeClass","_toDisplayString","_resolveDynamicComponent"],"mappings":";;;;;;;;uBACCA,WAoCW,CAAA,oBAAA,EAAA;IApCC,SAAS,EAAE,QAAc,CAAA,cAAA,CAAC,MAAK,GAAA,CAAA;AACzC,IAAA,OAAO,EAAE,MAAO,CAAA,OAAA;AAChB,IAAA,QAAQ,EAAE,MAAQ,CAAA,QAAA;AACnB,IAAA,aAAW,EAAC,gBAAgB;;qBAClB,MAA0C;AAApD,OAAAC,SAAA,CAAA,IAAA,CAAA,EAAAC,kBAAA,CA+BUC,QA/B2B,EAAA,IAAA,EAAAC,UAAA,CAAA,QAAA,CAAA,eAAe,EAAjC,CAAA,EAAA,MAAM,EAAE,IAAG,EAAA,KAAA;;AAA8B,UAAA,GAAA,EAAA,MAAM,CAAC,KAAK;;UAGhE,IAAI;4BADXJ,WAcc,CAAA,uBAAA,EAAA;;gBAZZ,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,KAAK,EAAE,QAAQ,CAAA,QAAA,CAAC,MAAM,CAAA;gBACtB,KAAK,EAAAK,cAAA,CAAA,EAAA,4BAAA,EAAkC,MAAM,CAAC,WAAU,EAAA,CAAA;AACxD,gBAAA,aAAW,EAAoB,CAAA,eAAA,EAAA,QAAA,CAAA,YAAY,CAAC,MAAM,CAAC,KAAK,CAAA,CAAA,CAAA;AACzD,gBAAA,eAAc,EAAd,EAAc;AACb,gBAAA,OAAK,aAAE,QAAY,CAAA,YAAA,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAA,CAAA;;iCAI/B,MACV;kCADU,GACV,GAAAC,eAAA,CAAG,MAAM,CAAC;;;;AAJM,gBAAA,CAAA,MAAM,CAAC,IAAI;;4BAAG,MAAI;kCACjC,MAA+E;AAA1D,wBAAA,CAAA,OAAA,MAAM,CAAC,IAAK,KAAA,QAAA;0CAAjCN,WAA+E,CAAA,iBAAA,EAAA;;8BAA/B,IAAI,EAAE,MAAM,CAAC,IAAI;AAAG,8BAAA,IAAI,EAAE,EAAE;;0CAC5EA,WAAgD,CAAAO,uBAAA,CAAhC,MAAM,CAAC,IAAI,CAAA,EAAA;;AAAU,8BAAA,IAAI,EAAE,EAAE;;;;;;;4BAI/CP,WAagB,CAAA,yBAAA,EAAA;;gBAXd,KAAK,EAAE,QAAQ,CAAA,QAAA,CAAC,MAAM,CAAA;gBACtB,QAAQ,EAAE,QAAU,CAAA,UAAA,CAAC,MAAM,CAAA;gBAC3B,KAAK,EAAAK,cAAA,CAAA,EAAA,4BAAA,EAAkC,MAAM,CAAC,WAAU,EAAA,CAAA;AACxD,gBAAA,aAAW,EAAoB,CAAA,eAAA,EAAA,QAAA,CAAA,YAAY,CAAC,MAAM,CAAC,KAAK,CAAA,CAAA,CAAA;AACzD,gBAAA,eAAc,EAAd,EAAc;gBACb,OAAK,EAAA,MAAA,KAAE,QAAQ,CAAA,QAAA,CAAC,MAAM,CAAA,CAAA;;iCAIb,MACV;kCADU,GACV,GAAAC,eAAA,CAAG,MAAM,CAAC;;;;AAJM,gBAAA,CAAA,MAAM,CAAC,IAAI;;4BAAG,MAAI;kCACjC,MAA+E;AAA1D,wBAAA,CAAA,OAAA,MAAM,CAAC,IAAK,KAAA,QAAA;0CAAjCN,WAA+E,CAAA,iBAAA,EAAA;;8BAA/B,IAAI,EAAE,MAAM,CAAC,IAAI;AAAG,8BAAA,IAAI,EAAE,EAAE;;0CAC5EA,WAAgD,CAAAO,uBAAA,CAAhC,MAAM,CAAC,IAAI,CAAA,EAAA;;AAAU,8BAAA,IAAI,EAAE,EAAE;;;;;;;;;;;;;;;;"}
|
|
1
|
+
{"version":3,"file":"CnRowActions.vue3.js","sources":["../../../../src/components/CnRowActions/CnRowActions.vue?vue&type=template&id=5d9db5e2&lang.js"],"sourcesContent":["<template>\n\t<NcActions :forceMenu=\"visibleActions.length > 3\"\n\t\t:primary=\"primary\"\n\t\t:menuName=\"menuName\"\n\t\tdata-testid=\"cn-row-actions\">\n\t\t<template v-for=\"{ action, link } in renderedActions\" :key=\"actionKey(action)\">\n\t\t\t<!-- A navigate-only action is a real link, so it can be middle-clicked, opened in a new tab or copied. -->\n\t\t\t<NcActionLink\n\t\t\t\tv-if=\"link\"\n\t\t\t\t:href=\"link.href\"\n\t\t\t\t:target=\"link.target\"\n\t\t\t\t:title=\"getTitle(action)\"\n\t\t\t\t:class=\"{ 'cn-row-action--destructive': action.destructive }\"\n\t\t\t\t:data-testid=\"actionTestId(action)\"\n\t\t\t\tcloseAfterClick\n\t\t\t\t@click=\"onLinkAction(action, link, $event)\">\n\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t<CnIcon v-if=\"typeof action.icon === 'string'\" :name=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t<component :is=\"action.icon\" v-else :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ action.label }}\n\t\t\t</NcActionLink>\n\t\t\t<NcActionButton\n\t\t\t\tv-else\n\t\t\t\t:title=\"getTitle(action)\"\n\t\t\t\t:disabled=\"isDisabled(action)\"\n\t\t\t\t:class=\"{ 'cn-row-action--destructive': action.destructive }\"\n\t\t\t\t:data-testid=\"actionTestId(action)\"\n\t\t\t\tcloseAfterClick\n\t\t\t\t@click=\"onAction(action)\">\n\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t<CnIcon v-if=\"typeof action.icon === 'string'\" :name=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t<component :is=\"action.icon\" v-else :size=\"20\" />\n\t\t\t\t</template>\n\t\t\t\t{{ action.label }}\n\t\t\t</NcActionButton>\n\t\t</template>\n\t</NcActions>\n</template>\n\n<script>\nimport { NcActionButton, NcActionLink, NcActions } from '@nextcloud/vue'\nimport { followItemActionLink, resolveItemActionLink } from '../../utils/actionLink.js'\nimport { isModifiedClick } from '../../utils/linkNavigation.js'\nimport { isRowActionVisible, rowActionKey, rowActionPayload, rowActionTestId, slugifyActionLabel } from '../../utils/rowActionItem.js'\nimport { CnIcon } from '../CnIcon/index.js'\n\n/**\n * CnRowActions — Action menu wrapper for table rows and cards.\n *\n * Wraps NcActions + NcActionButton for consistent row/card action menus.\n * Actions are defined as an array of objects with label, icon, handler, etc.\n *\n * ```vue\n * <CnRowActions\n * :actions=\"[\n * { label: 'Edit', icon: PencilIcon, handler: (row) => editRow(row) },\n * { label: 'Delete', icon: TrashIcon, handler: (row) => deleteRow(row), destructive: true },\n * ]\"\n * :row=\"row\" />\n * ```\n *\n * An action whose only job is to go somewhere carries `href` (a URL) or `to`\n * (a vue-router location) instead of a navigating `handler`, and renders as a\n * real link — middle-click, \"open in new tab\" and \"copy link\" all work:\n *\n * ```vue\n * <CnRowActions\n * :actions=\"[\n * { label: 'View', icon: 'Eye', to: (row) => ({ name: 'LeadDetail', params: { id: row.id } }) },\n * { label: 'Website', icon: 'Web', href: (row) => row.url, linkTarget: '_blank' },\n * ]\"\n * :row=\"row\" />\n * ```\n */\nexport default {\n\tname: 'CnRowActions',\n\n\tcomponents: {\n\t\tNcActions,\n\t\tNcActionButton,\n\t\tNcActionLink,\n\t\tCnIcon,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Action definitions.\n\t\t *\n\t\t * Each action supports:\n\t\t * - `label` (string, required) — display text\n\t\t * - `icon` (component | string) — MDI icon. A component renders\n\t\t * directly; a string is treated as a registry name and rendered\n\t\t * via `CnIcon` (PascalCase, e.g. `\"Eye\"`), falling back to the\n\t\t * help-circle when unregistered. The string form lets manifest\n\t\t * (JSON) actions declare icons by name.\n\t\t * - `handler` (function) — called with `row` on click\n\t\t * - `disabled` (boolean | (row) => boolean) — gray out the entry\n\t\t * - `visible` (boolean | (row) => boolean) — when `false`, hide the entry from the menu (default: shown)\n\t\t * - `title` (string | (row) => string) — native tooltip shown on hover (useful to explain why an entry is disabled)\n\t\t * - `destructive` (boolean) — apply error color styling\n\t\t * - `href` (string | (row) => string) — render the entry as a link to this URL\n\t\t * - `to` (string | object | (row) => string | object) — render the entry as a link to this\n\t\t * vue-router location; a plain click routes in place. Ignored when the router cannot resolve it.\n\t\t * - `linkTarget` (string) — the link's `target`, e.g. `_blank`\n\t\t * - `id` (string) — the action's id, passed in the `action` payload\n\t\t * - `builtin` (boolean) — set by CnIndexPage on its built-in View / Edit / Copy / Delete; a built-in's testid is `cn-action-item-<id>` in every locale and its key `builtin:<id>`\n\t\t *\n\t\t * A link entry still emits `action`, but its `handler` is not called: the link is the\n\t\t * navigation. A disabled entry, or a `to` without a resolvable route, stays a button.\n\t\t *\n\t\t * @type {Array<{label: string, id: string, builtin: boolean, icon: object | string, handler: (targetItem: object) => void, disabled: boolean | ((targetItem: object) => boolean), visible: boolean | ((targetItem: object) => boolean), title: string | ((targetItem: object) => string), destructive: boolean, href: string | ((targetItem: object) => string), to: string | object | ((targetItem: object) => string | object), linkTarget: string}>}\n\t\t */\n\t\tactions: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** The row/object data (passed to action handlers) */\n\t\trow: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Whether to use primary styling for the action menu trigger */\n\t\tprimary: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Label shown on the action menu trigger button */\n\t\tmenuName: {\n\t\t\ttype: String,\n\t\t\tdefault: undefined,\n\t\t},\n\t},\n\n\temits: ['action'],\n\n\tcomputed: {\n\t\t/**\n\t\t * Filter actions by their `visibleWhen` and `visible` gates, with the same rule CnContextMenu uses.\n\t\t * An action without either always shows.\n\t\t *\n\t\t * @return {Array} Visible actions for the current row.\n\t\t */\n\t\tvisibleActions() {\n\t\t\treturn this.actions.filter((action) => isRowActionVisible(action, this.row))\n\t\t},\n\n\t\t/**\n\t\t * Visible actions paired with the link each renders as (null for a button).\n\t\t *\n\t\t * @return {Array<{action: object, link: object|null}>}\n\t\t */\n\t\trenderedActions() {\n\t\t\treturn this.visibleActions.map((action) => ({\n\t\t\t\taction,\n\t\t\t\tlink: this.isDisabled(action) ? null : resolveItemActionLink(action, this.row, this.$router),\n\t\t\t}))\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Resolve disabled state for an action — supports both boolean and function.\n\t\t *\n\t\t * @param {object} action - The action definition\n\t\t * @return {boolean} Whether the action is disabled\n\t\t */\n\t\tisDisabled(action) {\n\t\t\tif (typeof action.disabled === 'function') {\n\t\t\t\treturn action.disabled(this.row)\n\t\t\t}\n\t\t\treturn !!action.disabled\n\t\t},\n\n\t\t/**\n\t\t * Resolve the title (native tooltip) for an action — supports both\n\t\t * string and function forms. Returns undefined when no title is\n\t\t * provided so the attribute is not rendered.\n\t\t *\n\t\t * @param {object} action - The action definition\n\t\t * @return {string|undefined} The resolved tooltip text, or undefined.\n\t\t */\n\t\tgetTitle(action) {\n\t\t\tif (typeof action.title === 'function') {\n\t\t\t\treturn action.title(this.row) || undefined\n\t\t\t}\n\t\t\treturn action.title || undefined\n\t\t},\n\n\t\tonAction(action) {\n\t\t\tif (action.handler && typeof action.handler === 'function') {\n\t\t\t\taction.handler(this.row)\n\t\t\t}\n\t\t\tthis.$emit('action', rowActionPayload(action, this.row))\n\t\t},\n\n\t\t/**\n\t\t * A link entry was clicked: route a plain in-app click, leave the rest to\n\t\t * the browser, and emit `action` as a button would. The `handler` is not\n\t\t * called, since navigating is what the link already does. A modified\n\t\t * click opens a new tab, so nothing is emitted in this one.\n\t\t *\n\t\t * @param {object} action The action definition.\n\t\t * @param {object} link The resolved link.\n\t\t * @param {MouseEvent} event The click event.\n\t\t */\n\t\tonLinkAction(action, link, event) {\n\t\t\tif (isModifiedClick(event)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfollowItemActionLink(event, link, this.$router)\n\t\t\t/**\n\t\t\t * @event action User picked an entry. Payload: `action` (the label), the row, the action's `id` when it has one, and `builtin: true` for a CnIndexPage built-in. A button entry has already run its `handler`; a link entry navigates instead, and a modified (new-tab) click on a link emits nothing.\n\t\t\t * @type {{ action: string, row: object|null, id?: string, builtin?: boolean }}\n\t\t\t */\n\t\t\tthis.$emit('action', rowActionPayload(action, this.row))\n\t\t},\n\n\t\t/**\n\t\t * Slugify an action label for use in stable `data-testid` selectors.\n\t\t * Lowercase, kebab-case, strip non-alphanumeric.\n\t\t *\n\t\t * @param {string} label - The action's display label\n\t\t * @return {string} kebab-case slug suitable for a testid suffix.\n\t\t */\n\t\tslugifyLabel(label) {\n\t\t\treturn slugifyActionLabel(label)\n\t\t},\n\n\t\t/**\n\t\t * The entry's `data-testid`: `cn-action-item-<id>` for a built-in, the label slug otherwise.\n\t\t *\n\t\t * @param {object} action The action definition.\n\t\t * @return {string} The testid.\n\t\t */\n\t\tactionTestId(action) {\n\t\t\treturn rowActionTestId(action)\n\t\t},\n\n\t\t/**\n\t\t * The entry's render key: `builtin:<id>` for a built-in, the label otherwise.\n\t\t *\n\t\t * @param {object} action The action definition.\n\t\t * @return {string} The key.\n\t\t */\n\t\tactionKey(action) {\n\t\t\treturn rowActionKey(action)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-row-action--destructive {\n\tcolor: var(--color-error) !important;\n}\n</style>\n"],"names":["_createBlock","_openBlock","_createElementBlock","_Fragment","_renderList","_normalizeClass","_toDisplayString","_resolveDynamicComponent"],"mappings":";;;;;;;;uBACCA,WAoCW,CAAA,oBAAA,EAAA;IApCC,SAAS,EAAE,QAAc,CAAA,cAAA,CAAC,MAAK,GAAA,CAAA;AACzC,IAAA,OAAO,EAAE,MAAO,CAAA,OAAA;AAChB,IAAA,QAAQ,EAAE,MAAQ,CAAA,QAAA;AACnB,IAAA,aAAW,EAAC,gBAAgB;;qBAClB,MAA4C;AAAtD,OAAAC,SAAA,CAAA,IAAA,CAAA,EAAAC,kBAAA,CA+BUC,QA/B2B,EAAA,IAAA,EAAAC,UAAA,CAAA,QAAA,CAAA,eAAe,EAAjC,CAAA,EAAA,MAAM,EAAE,IAAG,EAAA,KAAA;;AAA8B,UAAA,GAAA,EAAA,QAAA,CAAA,SAAS,CAAC,MAAM,CAAA;;UAGpE,IAAI;4BADXJ,WAcc,CAAA,uBAAA,EAAA;;gBAZZ,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,KAAK,EAAE,QAAQ,CAAA,QAAA,CAAC,MAAM,CAAA;gBACtB,KAAK,EAAAK,cAAA,CAAA,EAAA,4BAAA,EAAkC,MAAM,CAAC,WAAU,EAAA,CAAA;gBACxD,aAAW,EAAE,QAAY,CAAA,YAAA,CAAC,MAAM,CAAA;AACjC,gBAAA,eAAc,EAAd,EAAc;AACb,gBAAA,OAAK,aAAE,QAAY,CAAA,YAAA,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAA,CAAA;;iCAI/B,MACV;kCADU,GACV,GAAAC,eAAA,CAAG,MAAM,CAAC;;;;AAJM,gBAAA,CAAA,MAAM,CAAC,IAAI;;4BAAG,MAAI;kCACjC,MAA+E;AAA1D,wBAAA,CAAA,OAAA,MAAM,CAAC,IAAK,KAAA,QAAA;0CAAjCN,WAA+E,CAAA,iBAAA,EAAA;;8BAA/B,IAAI,EAAE,MAAM,CAAC,IAAI;AAAG,8BAAA,IAAI,EAAE,EAAE;;0CAC5EA,WAAgD,CAAAO,uBAAA,CAAhC,MAAM,CAAC,IAAI,CAAA,EAAA;;AAAU,8BAAA,IAAI,EAAE,EAAE;;;;;;;4BAI/CP,WAagB,CAAA,yBAAA,EAAA;;gBAXd,KAAK,EAAE,QAAQ,CAAA,QAAA,CAAC,MAAM,CAAA;gBACtB,QAAQ,EAAE,QAAU,CAAA,UAAA,CAAC,MAAM,CAAA;gBAC3B,KAAK,EAAAK,cAAA,CAAA,EAAA,4BAAA,EAAkC,MAAM,CAAC,WAAU,EAAA,CAAA;gBACxD,aAAW,EAAE,QAAY,CAAA,YAAA,CAAC,MAAM,CAAA;AACjC,gBAAA,eAAc,EAAd,EAAc;gBACb,OAAK,EAAA,MAAA,KAAE,QAAQ,CAAA,QAAA,CAAC,MAAM,CAAA,CAAA;;iCAIb,MACV;kCADU,GACV,GAAAC,eAAA,CAAG,MAAM,CAAC;;;;AAJM,gBAAA,CAAA,MAAM,CAAC,IAAI;;4BAAG,MAAI;kCACjC,MAA+E;AAA1D,wBAAA,CAAA,OAAA,MAAM,CAAC,IAAK,KAAA,QAAA;0CAAjCN,WAA+E,CAAA,iBAAA,EAAA;;8BAA/B,IAAI,EAAE,MAAM,CAAC,IAAI;AAAG,8BAAA,IAAI,EAAE,EAAE;;0CAC5EA,WAAgD,CAAAO,uBAAA,CAAhC,MAAM,CAAC,IAAI,CAAA,EAAA;;AAAU,8BAAA,IAAI,EAAE,EAAE;;;;;;;;;;;;;;;;"}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { getCurrentUser } from '@nextcloud/auth';
|
|
2
|
-
import { loadState } from '@nextcloud/initial-state';
|
|
3
2
|
import { translate } from '@nextcloud/l10n';
|
|
4
3
|
import { NcButton, NcNoteCard, NcSelect, NcTextField, NcCheckboxRadioSwitch, NcLoadingIcon } from '@nextcloud/vue';
|
|
5
4
|
import __sfc_main$1 from '../CnChoiceCards/CnChoiceCards.vue.js';
|
|
6
5
|
import __sfc_main$2 from '../CnLeafDependencySettings/CnLeafDependencySettings.vue.js';
|
|
7
6
|
import __sfc_main from '../CnWizardDialog/CnWizardDialog.vue.js';
|
|
8
|
-
import { checkDependencies, isDependencyResolved } from '../../composables/useDependencyCheck.js';
|
|
7
|
+
import { missingRequiredApps, readServerAppStatuses, checkDependencies, isDependencyResolved } from '../../composables/useDependencyCheck.js';
|
|
9
8
|
import { useSetupStatus } from '../../composables/useSetupStatus.js';
|
|
10
9
|
import { fieldsFromSchema } from '../../utils/schema.js';
|
|
11
10
|
|
|
@@ -274,16 +273,15 @@ var script = {
|
|
|
274
273
|
*
|
|
275
274
|
* @return {Array<{ step: object, missing: Array<string> }>}
|
|
276
275
|
* @spec openspec/changes/setup-wizard-card-load-and-dependency-gate/specs/cn-setup-wizard/spec.md#requirement-the-wizard-checks-dependencies-before-any-step
|
|
276
|
+
* @spec openspec/changes/optional-step-requires/specs/cn-setup-wizard/spec.md#requirement-a-step-whose-required-apps-are-absent-is-not-applicable
|
|
277
277
|
*/
|
|
278
278
|
skippedSteps() {
|
|
279
279
|
return this.declaredSteps
|
|
280
280
|
.filter((s) => Array.isArray(s.requires) && s.requires.length > 0)
|
|
281
281
|
.map((step) => {
|
|
282
282
|
const known = Object.fromEntries(this.dependencyRows.map((d) => [d.id, d]));
|
|
283
|
-
const missing =
|
|
284
|
-
.map((row) => known[row.id] || row)
|
|
285
|
-
.filter((row) => !isDependencyResolved(row))
|
|
286
|
-
.map((row) => row.name || row.id);
|
|
283
|
+
const missing = missingRequiredApps(step.requires, this.serverAppStatuses)
|
|
284
|
+
.map((row) => (known[row.id] || row).name || row.id);
|
|
287
285
|
return { step, missing }
|
|
288
286
|
})
|
|
289
287
|
.filter((entry) => entry.missing.length > 0)
|
|
@@ -323,11 +321,7 @@ var script = {
|
|
|
323
321
|
* @return {object}
|
|
324
322
|
*/
|
|
325
323
|
serverAppStatuses() {
|
|
326
|
-
|
|
327
|
-
return loadState(this.appId, 'dependency_statuses', {}) || {}
|
|
328
|
-
} catch {
|
|
329
|
-
return {}
|
|
330
|
-
}
|
|
324
|
+
return readServerAppStatuses(this.appId)
|
|
331
325
|
},
|
|
332
326
|
|
|
333
327
|
/**
|
|
@@ -447,9 +441,15 @@ var script = {
|
|
|
447
441
|
value = this.choiceLabel(step, raw);
|
|
448
442
|
}
|
|
449
443
|
} else if (step.type === 'config-fields') {
|
|
444
|
+
// Only the fields the reader filled in: a row of empty
|
|
445
|
+
// "Name: , KvK: ," reads as broken, not as "not set".
|
|
450
446
|
value = this.fieldsFor(step)
|
|
451
|
-
.
|
|
452
|
-
|
|
447
|
+
.filter((f) => {
|
|
448
|
+
const v = this.configModel[f.key];
|
|
449
|
+
return v !== null && v !== undefined && String(v).trim() !== ''
|
|
450
|
+
})
|
|
451
|
+
.map((f) => `${f.label}: ${this.configModel[f.key]}`)
|
|
452
|
+
.join(', ') || translate('nextcloud-vue', 'Not set');
|
|
453
453
|
}
|
|
454
454
|
if (this.isOnDemand(step)) {
|
|
455
455
|
const ran = this.ranThisSession(step.id);
|