@conduction/nextcloud-vue 2.89.0 → 2.89.1

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.
@@ -199632,6 +199632,11 @@ var script$3e = {
199632
199632
  cnTranslate: { default: () => (key) => key },
199633
199633
  },
199634
199634
 
199635
+ // A dashboard binds the widget's `content` keys as attributes too. Falling
199636
+ // through, `submitLabel` would reach CnFormWidgetBase and win over the
199637
+ // translated label the widget passes, so attributes go through rootAttrs.
199638
+ inheritAttrs: false,
199639
+
199635
199640
  props: {
199636
199641
  /**
199637
199642
  * Persisted configuration blob (see component description for the shape).
@@ -199667,6 +199672,19 @@ var script$3e = {
199667
199672
  },
199668
199673
 
199669
199674
  computed: {
199675
+ /**
199676
+ * The attributes for the root form: everything the widget was given,
199677
+ * minus the names CnFormWidgetBase takes as props, which the widget
199678
+ * sets itself.
199679
+ *
199680
+ * @spec openspec/changes/interaction-form-fits-and-reads-dutch/specs/interaction-form-widget/spec.md#requirement-the-interaction-form-fits-its-card-and-reads-in-the-readers-language
199681
+ * @return {object} The attributes to bind.
199682
+ */
199683
+ rootAttrs() {
199684
+ const own = new Set(Object.keys(__sfc_main$3f.props || {}));
199685
+ return Object.fromEntries(Object.entries(this.$attrs).filter(([key]) => !own.has(vue$1.camelize(key))))
199686
+ },
199687
+
199670
199688
  register() {
199671
199689
  return this.content.register || 'pipelinq'
199672
199690
  },
@@ -200034,7 +200052,7 @@ function render$3e(_ctx, _cache, $props, $setup, $data, $options) {
200034
200052
  const _component_NcButton = vue$1.resolveComponent("NcButton");
200035
200053
  const _component_CnFormWidgetBase = vue$1.resolveComponent("CnFormWidgetBase");
200036
200054
 
200037
- return (vue$1.openBlock(), vue$1.createBlock(_component_CnFormWidgetBase, {
200055
+ return (vue$1.openBlock(), vue$1.createBlock(_component_CnFormWidgetBase, vue$1.mergeProps($options.rootAttrs, {
200038
200056
  blockClass: "cn-interaction-form-widget",
200039
200057
  fields: $options.formFields,
200040
200058
  model: $data.form,
@@ -200046,7 +200064,7 @@ function render$3e(_ctx, _cache, $props, $setup, $data, $options) {
200046
200064
  errorMessage: $data.errorMessage,
200047
200065
  "onUpdate:field": $options.onFieldUpdate,
200048
200066
  onSubmit: $options.onRegister
200049
- }, {
200067
+ }), {
200050
200068
  "field-client": vue$1.withCtx(() => [
200051
200069
  vue$1.createVNode(_component_CnResourceSelect, {
200052
200070
  register: $options.register,
@@ -200079,7 +200097,7 @@ function render$3e(_ctx, _cache, $props, $setup, $data, $options) {
200079
200097
  : vue$1.createCommentVNode("v-if", true)
200080
200098
  ]),
200081
200099
  _: 1 /* STABLE */
200082
- }, 8 /* PROPS */, ["fields", "model", "errors", "canSubmit", "submitting", "submitLabel", "submittingLabel", "errorMessage", "onUpdate:field", "onSubmit"]))
200100
+ }, 16 /* FULL_PROPS */, ["fields", "model", "errors", "canSubmit", "submitting", "submitLabel", "submittingLabel", "errorMessage", "onUpdate:field", "onSubmit"]))
200083
200101
  }
200084
200102
 
200085
200103
  script$3e.render = render$3e;
@@ -358535,6 +358553,7 @@ var script$h = {
358535
358553
  // write lands; this warns the user while there are unsaved/in-flight
358536
358554
  // changes. Registered before the early-return so it always installs.
358537
358555
  window.addEventListener('beforeunload', this.onBeforeUnload);
358556
+ window.addEventListener(OBJECTS_CHANGED_EVENT, this.onObjectsChanged);
358538
358557
 
358539
358558
  // Opt-out fast-path: empty `requiresApps` already initialised
358540
358559
  // `capabilitiesLoading` to `false` in data(); skip the check.
@@ -358580,6 +358599,7 @@ var script$h = {
358580
358599
  this.removeDiagnostics = null;
358581
358600
  }
358582
358601
  window.removeEventListener('beforeunload', this.onBeforeUnload);
358602
+ window.removeEventListener(OBJECTS_CHANGED_EVENT, this.onObjectsChanged);
358583
358603
  this.cnScopedTheme.teardown(this.appId);
358584
358604
  // Scope the side effect to the shell's lifetime. Layers already written
358585
358605
  // stay on screen (see `uninstallModalStack`), so a dialog outliving the
@@ -359426,13 +359446,17 @@ var script$h = {
359426
359446
  * response `total` written to `cnMenuItemCounts[item.id]`. The
359427
359447
  * request goes straight through axios rather than the object store,
359428
359448
  * so the index page's whole-schema total in the store is left alone.
359429
- * Failures leave the badge unrendered.
359449
+ * Failures leave the badge unrendered. Given a changed register and
359450
+ * schema, only the entries counting that list are read again.
359430
359451
  *
359431
359452
  * @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-a-menu-entry-counts-a-filtered-list
359453
+ * @param {{register?: string|null, schema?: string|null}|null} [changed] The list that changed; null reads every count.
359432
359454
  * @return {void}
359433
359455
  * @private
359434
359456
  */
359435
- _hydrateMenuItemCounts() {
359457
+ _hydrateMenuItemCounts(changed = null) {
359458
+ const register = changed?.register ?? null;
359459
+ const schema = changed?.schema ?? null;
359436
359460
  const collect = (items) => {
359437
359461
  const out = [];
359438
359462
  for (const item of items ?? []) {
@@ -359447,10 +359471,27 @@ var script$h = {
359447
359471
  return out
359448
359472
  };
359449
359473
  for (const item of collect(this.manifest?.menu)) {
359450
- this._fetchMenuItemCount(item);
359474
+ if ((register === null || item.count.register === register) && (schema === null || item.count.schema === schema)) {
359475
+ this._fetchMenuItemCount(item);
359476
+ }
359451
359477
  }
359452
359478
  },
359453
359479
 
359480
+ /**
359481
+ * Objects changed outside the list showing them (`cn:objects-changed`,
359482
+ * see `dispatchObjectsChanged`): read the filtered menu counts of that
359483
+ * register and schema again, so a badge follows a claim, a close or a
359484
+ * bulk action instead of keeping the number it had at mount. A signal
359485
+ * without a register or schema reads every filtered count again.
359486
+ *
359487
+ * @spec openspec/changes/menu-counts-follow-changed-objects/specs/layout-components/spec.md#requirement-a-filtered-menu-count-follows-changed-objects
359488
+ * @param {CustomEvent} event The signal, with `detail: { register, schema, id }`.
359489
+ * @return {void}
359490
+ */
359491
+ onObjectsChanged(event) {
359492
+ this._hydrateMenuItemCounts(event?.detail ?? null);
359493
+ },
359494
+
359454
359495
  /**
359455
359496
  * One filtered-count request for a menu entry; see
359456
359497
  * `_hydrateMenuItemCounts`.
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: package/dist/nextcloud-vue.cjs.js.map
3
- size: 40761222 bytes
4
- sha256: 74c8351e2250accf05102ce32e61fbb7e7f3c5c7ace613b0d9df9cee7b9249ae
3
+ size: 40766945 bytes
4
+ sha256: 4d5c39c1fefeba7aa7f2181f991b71ad3354d5678ea63646f53060e929223d66
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@conduction/nextcloud-vue",
3
- "version": "2.89.0",
3
+ "version": "2.89.1",
4
4
  "description": "Shared Vue component library for Conduction Nextcloud apps — complements @nextcloud/vue with higher-level components, OpenRegister integration, and NL Design System support",
5
5
  "homepage": "https://github.com/ConductionNL/nextcloud-vue#readme",
6
6
  "bugs": {
@@ -740,6 +740,7 @@ import { DEFAULT_FORGE, resolveForge } from '../../utils/forge.js'
740
740
  import { BUILT_IN_KB_PROVIDERS } from '../../utils/kbSearchProviders.js'
741
741
  import { createManifestTranslate } from '../../utils/manifestTranslate.js'
742
742
  import { installModalStack, uninstallModalStack } from '../../utils/modalStack.js'
743
+ import { OBJECTS_CHANGED_EVENT } from '../../utils/objectSignals.js'
743
744
  import { resolveBrand } from '../../utils/resolveBrand.js'
744
745
  import { resolveFilterTokens } from '../../utils/resolveFilterTokens.js'
745
746
  import { passesContextPredicates } from '../../utils/visibleIfContext.js'
@@ -3221,6 +3222,7 @@ export default {
3221
3222
  // write lands; this warns the user while there are unsaved/in-flight
3222
3223
  // changes. Registered before the early-return so it always installs.
3223
3224
  window.addEventListener('beforeunload', this.onBeforeUnload)
3225
+ window.addEventListener(OBJECTS_CHANGED_EVENT, this.onObjectsChanged)
3224
3226
 
3225
3227
  // Opt-out fast-path: empty `requiresApps` already initialised
3226
3228
  // `capabilitiesLoading` to `false` in data(); skip the check.
@@ -3266,6 +3268,7 @@ export default {
3266
3268
  this.removeDiagnostics = null
3267
3269
  }
3268
3270
  window.removeEventListener('beforeunload', this.onBeforeUnload)
3271
+ window.removeEventListener(OBJECTS_CHANGED_EVENT, this.onObjectsChanged)
3269
3272
  this.cnScopedTheme.teardown(this.appId)
3270
3273
  // Scope the side effect to the shell's lifetime. Layers already written
3271
3274
  // stay on screen (see `uninstallModalStack`), so a dialog outliving the
@@ -4112,13 +4115,17 @@ export default {
4112
4115
  * response `total` written to `cnMenuItemCounts[item.id]`. The
4113
4116
  * request goes straight through axios rather than the object store,
4114
4117
  * so the index page's whole-schema total in the store is left alone.
4115
- * Failures leave the badge unrendered.
4118
+ * Failures leave the badge unrendered. Given a changed register and
4119
+ * schema, only the entries counting that list are read again.
4116
4120
  *
4117
4121
  * @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-a-menu-entry-counts-a-filtered-list
4122
+ * @param {{register?: string|null, schema?: string|null}|null} [changed] The list that changed; null reads every count.
4118
4123
  * @return {void}
4119
4124
  * @private
4120
4125
  */
4121
- _hydrateMenuItemCounts() {
4126
+ _hydrateMenuItemCounts(changed = null) {
4127
+ const register = changed?.register ?? null
4128
+ const schema = changed?.schema ?? null
4122
4129
  const collect = (items) => {
4123
4130
  const out = []
4124
4131
  for (const item of items ?? []) {
@@ -4133,10 +4140,27 @@ export default {
4133
4140
  return out
4134
4141
  }
4135
4142
  for (const item of collect(this.manifest?.menu)) {
4136
- this._fetchMenuItemCount(item)
4143
+ if ((register === null || item.count.register === register) && (schema === null || item.count.schema === schema)) {
4144
+ this._fetchMenuItemCount(item)
4145
+ }
4137
4146
  }
4138
4147
  },
4139
4148
 
4149
+ /**
4150
+ * Objects changed outside the list showing them (`cn:objects-changed`,
4151
+ * see `dispatchObjectsChanged`): read the filtered menu counts of that
4152
+ * register and schema again, so a badge follows a claim, a close or a
4153
+ * bulk action instead of keeping the number it had at mount. A signal
4154
+ * without a register or schema reads every filtered count again.
4155
+ *
4156
+ * @spec openspec/changes/menu-counts-follow-changed-objects/specs/layout-components/spec.md#requirement-a-filtered-menu-count-follows-changed-objects
4157
+ * @param {CustomEvent} event The signal, with `detail: { register, schema, id }`.
4158
+ * @return {void}
4159
+ */
4160
+ onObjectsChanged(event) {
4161
+ this._hydrateMenuItemCounts(event?.detail ?? null)
4162
+ },
4163
+
4140
4164
  /**
4141
4165
  * One filtered-count request for a menu entry; see
4142
4166
  * `_hydrateMenuItemCounts`.
@@ -4,6 +4,7 @@
4
4
  -->
5
5
  <template>
6
6
  <CnFormWidgetBase
7
+ v-bind="rootAttrs"
7
8
  blockClass="cn-interaction-form-widget"
8
9
  :fields="formFields"
9
10
  :model="form"
@@ -51,6 +52,7 @@
51
52
  <script>
52
53
  import { translate as t } from '@nextcloud/l10n'
53
54
  import { NcButton } from '@nextcloud/vue'
55
+ import { camelize } from 'vue'
54
56
  import OpenInNew from 'vue-material-design-icons/OpenInNew.vue'
55
57
  import CnFormWidgetBase from '../CnFormWidgetBase/CnFormWidgetBase.vue'
56
58
  import CnResourceSelect from '../CnResourceSelect/CnResourceSelect.vue'
@@ -125,6 +127,11 @@ export default {
125
127
  cnTranslate: { default: () => (key) => key },
126
128
  },
127
129
 
130
+ // A dashboard binds the widget's `content` keys as attributes too. Falling
131
+ // through, `submitLabel` would reach CnFormWidgetBase and win over the
132
+ // translated label the widget passes, so attributes go through rootAttrs.
133
+ inheritAttrs: false,
134
+
128
135
  props: {
129
136
  /**
130
137
  * Persisted configuration blob (see component description for the shape).
@@ -160,6 +167,19 @@ export default {
160
167
  },
161
168
 
162
169
  computed: {
170
+ /**
171
+ * The attributes for the root form: everything the widget was given,
172
+ * minus the names CnFormWidgetBase takes as props, which the widget
173
+ * sets itself.
174
+ *
175
+ * @spec openspec/changes/interaction-form-fits-and-reads-dutch/specs/interaction-form-widget/spec.md#requirement-the-interaction-form-fits-its-card-and-reads-in-the-readers-language
176
+ * @return {object} The attributes to bind.
177
+ */
178
+ rootAttrs() {
179
+ const own = new Set(Object.keys(CnFormWidgetBase.props || {}))
180
+ return Object.fromEntries(Object.entries(this.$attrs).filter(([key]) => !own.has(camelize(key))))
181
+ },
182
+
163
183
  register() {
164
184
  return this.content.register || 'pipelinq'
165
185
  },