@conduction/nextcloud-vue 1.0.0-beta.18 → 1.0.0-beta.19

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.
@@ -56485,9 +56485,34 @@ function validateActionsArray(cfg, pathSlash, pathBracket, errors) {
56485
56485
  if (typeof action.label !== 'string' || action.label.length === 0) {
56486
56486
  errors.push(`${actionPath}/label: must be a non-empty string`);
56487
56487
  }
56488
+ // REQ-MAD-1 / REQ-MAD-2 — `handler` (string, registry name OR
56489
+ // reserved keyword `navigate`/`emit`/`none`) and the matching
56490
+ // `navigate` requirement on `route`. Schema 1.3.0+.
56491
+ if (action.handler !== undefined) {
56492
+ if (typeof action.handler !== 'string') {
56493
+ errors.push(`${actionPath}/handler: must be a string when set`);
56494
+ } else if (!HANDLER_PATTERN.test(action.handler)) {
56495
+ errors.push(
56496
+ `${actionPath}/handler: "${action.handler}" must match `
56497
+ + '"navigate" | "emit" | "none" | [A-Za-z][A-Za-z0-9_]*',
56498
+ );
56499
+ }
56500
+ if (action.handler === 'navigate'
56501
+ && (typeof action.route !== 'string' || action.route.length === 0)) {
56502
+ errors.push(`${actionPath}/route: required when handler is "navigate"`);
56503
+ }
56504
+ }
56488
56505
  });
56489
56506
  }
56490
56507
 
56508
+ /**
56509
+ * REQ-MAD-1 — Allowed shapes for `actions[].handler`. Either a
56510
+ * reserved keyword (`navigate` | `emit` | `none`) or a JS-identifier
56511
+ * registry name (alphanumeric + underscore, leading letter). Mirrors
56512
+ * the schema's `pattern` on the `handler` property.
56513
+ */
56514
+ const HANDLER_PATTERN = /^(navigate|emit|none|[A-Za-z][A-Za-z0-9_]*)$/;
56515
+
56491
56516
  /**
56492
56517
  * Validate `config.widgets[]` for dashboard page type
56493
56518
  * (`manifest-config-refs` REQ-MCR). Each entry MUST be an object with
@@ -57466,14 +57491,20 @@ var script$L = {
57466
57491
  CnIndexSidebar: __vue_component__$M,
57467
57492
  },
57468
57493
 
57494
+ /**
57495
+ * Inject the customComponents registry from a CnAppRoot ancestor.
57496
+ * Used by:
57497
+ * - REQ-MAD-3 / REQ-MAD-8 (manifest-actions-dispatch): resolves
57498
+ * `actions[].handler` registry names to functions called on
57499
+ * row-action click.
57500
+ * - The cardComponent + form-dialog override paths: when set, the
57501
+ * prop-level `customComponents` wins, but the inject is the
57502
+ * default. See `effectiveCustomComponents`.
57503
+ *
57504
+ * Falls back to an empty object so `CnIndexPage` works standalone
57505
+ * (unit tests, isolated mount) without `CnAppRoot`.
57506
+ */
57469
57507
  inject: {
57470
- /**
57471
- * Custom-component registry provided by `CnAppRoot`. Falls back
57472
- * to an empty object so `CnIndexPage` works when mounted
57473
- * without `CnAppRoot` (e.g. in unit tests). The explicit
57474
- * `customComponents` prop wins when set — see
57475
- * `effectiveCustomComponents`.
57476
- */
57477
57508
  cnCustomComponents: { default: () => ({}) },
57478
57509
  },
57479
57510
 
@@ -57816,6 +57847,11 @@ var script$L = {
57816
57847
  * `cnCustomComponents`. Provided primarily so unit tests can
57817
57848
  * pass a registry without mounting `CnAppRoot`.
57818
57849
  *
57850
+ * Used by:
57851
+ * - `cardComponent` resolution (REQ-MCI from manifest-card-index)
57852
+ * - `actions[].handler` registry name resolution (REQ-MAD-3 from
57853
+ * manifest-actions-dispatch — handler funcs called on row-action click)
57854
+ *
57819
57855
  * @type {object|null}
57820
57856
  */
57821
57857
  customComponents: {
@@ -57920,9 +57956,56 @@ var script$L = {
57920
57956
  return builtIn
57921
57957
  },
57922
57958
 
57923
- /** Merged actions: app-provided first, then built-in defaults */
57959
+ /**
57960
+ * Effective customComponents registry — explicit prop wins over
57961
+ * the injected ancestor registry. Used to:
57962
+ * - Resolve `actions[].handler` registry names (REQ-MAD-3,
57963
+ * manifest-actions-dispatch).
57964
+ * - Resolve the `cardComponent` name for card-grid view (REQ-MCI,
57965
+ * manifest-card-index).
57966
+ *
57967
+ * @return {object}
57968
+ */
57969
+ effectiveCustomComponents() {
57970
+ return this.customComponents ?? this.cnCustomComponents ?? {}
57971
+ },
57972
+
57973
+ /**
57974
+ * Merged actions: app-provided first, then built-in defaults.
57975
+ *
57976
+ * REQ-MAD-3 / REQ-MAD-4 / REQ-MAD-5 / REQ-MAD-6 / REQ-MAD-7
57977
+ * (manifest-actions-dispatch) — for any action whose `handler`
57978
+ * is a string, resolve it through `resolveHandler()` so
57979
+ * `CnRowActions` sees the same `{ handler: fn }` shape it does
57980
+ * for built-in defaults. Function-typed handlers (the existing
57981
+ * runtime path) pass through untouched.
57982
+ */
57924
57983
  mergedActions() {
57925
- return [...this.actions, ...this.defaultActions]
57984
+ const dispatched = this.actions.map((action) => {
57985
+ if (typeof action.handler === 'function') {
57986
+ // Back-compat: programmatic function handler — keep as-is.
57987
+ return action
57988
+ }
57989
+ if (typeof action.handler !== 'string' || action.handler.length === 0) {
57990
+ // No handler → emit-only path (existing default).
57991
+ return action
57992
+ }
57993
+ const isNone = action.handler === 'none';
57994
+ const resolved = this.resolveHandler(action);
57995
+ if (resolved) {
57996
+ // `none` returns a sentinel no-op handler AND must
57997
+ // suppress the `@action` emit; flag it so onRowAction
57998
+ // can drop the bubbled event.
57999
+ return isNone
58000
+ ? { ...action, handler: resolved, _dispatchSuppress: true }
58001
+ : { ...action, handler: resolved }
58002
+ }
58003
+ // Either reserved keyword "emit" / unknown name / non-function
58004
+ // registry entry → page emits @action only; no handler call.
58005
+ const { handler, ...rest } = action;
58006
+ return rest
58007
+ });
58008
+ return [...dispatched, ...this.defaultActions]
57926
58009
  },
57927
58010
 
57928
58011
  hasRowActions() {
@@ -57970,17 +58053,6 @@ var script$L = {
57970
58053
  return (this.sidebar && this.sidebar.search) || {}
57971
58054
  },
57972
58055
 
57973
- /**
57974
- * Effective customComponents registry used to resolve the
57975
- * `cardComponent` name. Explicit prop wins, inject falls back,
57976
- * empty object is the last resort.
57977
- *
57978
- * @return {object}
57979
- */
57980
- effectiveCustomComponents() {
57981
- return this.customComponents ?? this.cnCustomComponents ?? {}
57982
- },
57983
-
57984
58056
  /**
57985
58057
  * Resolved card component for card-grid view mode. Returns
57986
58058
  * `null` when `cardComponent` is empty OR when the name is not
@@ -58017,6 +58089,89 @@ var script$L = {
58017
58089
  },
58018
58090
 
58019
58091
  methods: {
58092
+ /**
58093
+ * REQ-MAD-3 / REQ-MAD-4 / REQ-MAD-5 / REQ-MAD-6 / REQ-MAD-7
58094
+ * (manifest-actions-dispatch) — Resolve a manifest-declared
58095
+ * action's `handler` string into a `(row) => void` invocation
58096
+ * function. Returns null when the action should fall back to
58097
+ * the page's `@action`-event-only path.
58098
+ *
58099
+ * - Reserved keyword `"navigate"` → push the configured route
58100
+ * with `params: { id: row[rowKey] }`.
58101
+ * - Reserved keyword `"emit"` → null (page still bubbles
58102
+ * `@action`; explicit no-op).
58103
+ * - Reserved keyword `"none"` → returns a no-op function that
58104
+ * suppresses both the handler and the `@action` emit. The
58105
+ * suppression happens via the special `_dispatchSuppress`
58106
+ * flag on the cloned action; see mergedActions for the
58107
+ * detail.
58108
+ * - Registry name → look up in `effectiveCustomComponents`;
58109
+ * when it's a function, wrap as
58110
+ * `(row) => fn({ actionId: action.id, item: row })`. When
58111
+ * it's a non-function, console.warn and return null.
58112
+ * - Unknown registry name → silent fall-through (null).
58113
+ *
58114
+ * @param {object} action The manifest-shaped action object.
58115
+ * @return {Function|null}
58116
+ */
58117
+ resolveHandler(action) {
58118
+ const name = action.handler;
58119
+ if (typeof name !== 'string' || name.length === 0) return null
58120
+ if (name === 'navigate') {
58121
+ const route = action.route;
58122
+ if (typeof route !== 'string' || route.length === 0) {
58123
+ // eslint-disable-next-line no-console
58124
+ console.warn(
58125
+ `[CnIndexPage] action "${action.id}" declares handler:"navigate" `
58126
+ + 'but route is missing; falling back to @action-only.',
58127
+ );
58128
+ return null
58129
+ }
58130
+ return (row) => {
58131
+ this.$router.push({
58132
+ name: route,
58133
+ params: { id: row[this.rowKey] },
58134
+ });
58135
+ }
58136
+ }
58137
+ if (name === 'emit') return null
58138
+ if (name === 'none') {
58139
+ // Returns a sentinel that CnRowActions will treat as a
58140
+ // no-op; we additionally short-circuit @action emit in
58141
+ // `onRowAction` via the action's id.
58142
+ return () => {}
58143
+ }
58144
+ const fn = this.effectiveCustomComponents[name];
58145
+ if (typeof fn === 'function') {
58146
+ return (row) => fn({ actionId: action.id, item: row })
58147
+ }
58148
+ if (fn !== undefined) {
58149
+ // eslint-disable-next-line no-console
58150
+ console.warn(
58151
+ `[CnIndexPage] action.handler "${name}" resolved to a non-function in `
58152
+ + 'customComponents — components belong to slot overrides; falling '
58153
+ + 'back to @action-only.',
58154
+ );
58155
+ }
58156
+ return null
58157
+ },
58158
+
58159
+ /**
58160
+ * REQ-MAD-6 (manifest-actions-dispatch) — `handler: "none"`
58161
+ * blocks the `@action` emit entirely. CnRowActions emits
58162
+ * `@action` with `{ action: action.label, row }` and the page
58163
+ * forwards via `@action="$emit('action', $event)"`. This handler
58164
+ * intercepts so the `none`-flagged action is dropped before
58165
+ * re-emit.
58166
+ *
58167
+ * @param {{action: string, row: object}} payload The CnRowActions emit.
58168
+ */
58169
+ onRowAction(payload) {
58170
+ const matched = this.mergedActions.find((a) => a.label === payload.action);
58171
+ if (matched && matched._dispatchSuppress) return
58172
+ this.$emit('action', payload);
58173
+ },
58174
+
58020
58175
  /**
58021
58176
  * Handle row click — emits row-click event for the parent to handle navigation.
58022
58177
  * @param {object} row The clicked row object
@@ -58677,11 +58832,7 @@ var __vue_render__$L = function () {
58677
58832
  actions: _vm.mergedActions,
58678
58833
  row: row,
58679
58834
  },
58680
- on: {
58681
- action: function ($event) {
58682
- return _vm.$emit("action", $event)
58683
- },
58684
- },
58835
+ on: { action: _vm.onRowAction },
58685
58836
  }),
58686
58837
  ]
58687
58838
  },
@@ -58771,11 +58922,7 @@ var __vue_render__$L = function () {
58771
58922
  actions: _vm.mergedActions,
58772
58923
  row: object,
58773
58924
  },
58774
- on: {
58775
- action: function ($event) {
58776
- return _vm.$emit("action", $event)
58777
- },
58778
- },
58925
+ on: { action: _vm.onRowAction },
58779
58926
  }),
58780
58927
  ]
58781
58928
  },
@@ -58801,9 +58948,7 @@ var __vue_render__$L = function () {
58801
58948
  "update:open": function ($event) {
58802
58949
  _vm.contextMenuOpen = $event;
58803
58950
  },
58804
- action: function ($event) {
58805
- return _vm.$emit("action", $event)
58806
- },
58951
+ action: _vm.onRowAction,
58807
58952
  close: _vm.closeContextMenu,
58808
58953
  },
58809
58954
  }),