@conduction/nextcloud-vue 2.88.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.
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue.js +1 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js +64 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +27 -3
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue2.js +19 -0
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js +4 -4
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +5 -5
- package/dist/nextcloud-vue.cjs.js +112 -8
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +5 -5
- package/package.json +1 -1
- package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +64 -1
- package/src/components/CnAppRoot/CnAppRoot.vue +27 -3
- package/src/components/CnInteractionFormWidget/CnInteractionFormWidget.vue +20 -0
|
@@ -34796,28 +34796,28 @@ to {
|
|
|
34796
34796
|
}
|
|
34797
34797
|
|
|
34798
34798
|
|
|
34799
|
-
.cn-admin-settings-shell[data-v-
|
|
34799
|
+
.cn-admin-settings-shell[data-v-c3ceda64] {
|
|
34800
34800
|
max-width: 900px;
|
|
34801
34801
|
}
|
|
34802
34802
|
|
|
34803
34803
|
/* The board look lifts the width and lays the header and cards on a grid (look-board.css). */
|
|
34804
|
-
.cn-support-info[data-v-
|
|
34804
|
+
.cn-support-info[data-v-c3ceda64] {
|
|
34805
34805
|
margin-top: 16px;
|
|
34806
34806
|
padding-top: 16px;
|
|
34807
34807
|
border-top: 1px solid var(--color-border);
|
|
34808
34808
|
}
|
|
34809
|
-
.cn-support-info h4[data-v-
|
|
34809
|
+
.cn-support-info h4[data-v-c3ceda64] {
|
|
34810
34810
|
margin: 0 0 8px 0;
|
|
34811
34811
|
font-size: 14px;
|
|
34812
34812
|
font-weight: 600;
|
|
34813
34813
|
color: var(--color-main-text);
|
|
34814
34814
|
}
|
|
34815
|
-
.cn-support-info p[data-v-
|
|
34815
|
+
.cn-support-info p[data-v-c3ceda64] {
|
|
34816
34816
|
margin: 4px 0;
|
|
34817
34817
|
color: var(--color-text-maxcontrast);
|
|
34818
34818
|
font-size: 14px;
|
|
34819
34819
|
}
|
|
34820
|
-
.cn-support-info a[data-v-
|
|
34820
|
+
.cn-support-info a[data-v-c3ceda64] {
|
|
34821
34821
|
color: var(--color-primary-element);
|
|
34822
34822
|
}
|
|
34823
34823
|
|
|
@@ -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
|
-
},
|
|
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;
|
|
@@ -257113,6 +257131,43 @@ function readTabFromAddress(key) {
|
|
|
257113
257131
|
return new URLSearchParams(window.location.search).get(key) || ''
|
|
257114
257132
|
}
|
|
257115
257133
|
|
|
257134
|
+
/**
|
|
257135
|
+
* The tab remembered under a storage key, or '' without a key, a value or
|
|
257136
|
+
* usable storage (a private window can refuse it).
|
|
257137
|
+
*
|
|
257138
|
+
* @param {string} key The storage key.
|
|
257139
|
+
* @return {string} The remembered tab id.
|
|
257140
|
+
*/
|
|
257141
|
+
function readStoredTab(key) {
|
|
257142
|
+
if (!key) {
|
|
257143
|
+
return ''
|
|
257144
|
+
}
|
|
257145
|
+
try {
|
|
257146
|
+
return window.localStorage.getItem(key) || ''
|
|
257147
|
+
} catch {
|
|
257148
|
+
return ''
|
|
257149
|
+
}
|
|
257150
|
+
}
|
|
257151
|
+
|
|
257152
|
+
/**
|
|
257153
|
+
* Remember the open tab under a storage key; nothing without a key or
|
|
257154
|
+
* usable storage.
|
|
257155
|
+
*
|
|
257156
|
+
* @param {string} key The storage key.
|
|
257157
|
+
* @param {string} id The tab's id.
|
|
257158
|
+
* @return {void}
|
|
257159
|
+
*/
|
|
257160
|
+
function storeTab(key, id) {
|
|
257161
|
+
if (!key) {
|
|
257162
|
+
return
|
|
257163
|
+
}
|
|
257164
|
+
try {
|
|
257165
|
+
window.localStorage.setItem(key, id);
|
|
257166
|
+
} catch {
|
|
257167
|
+
// Storage refused (private window): the address still holds the tab.
|
|
257168
|
+
}
|
|
257169
|
+
}
|
|
257170
|
+
|
|
257116
257171
|
/**
|
|
257117
257172
|
* Put the open tab in the address without a new history entry.
|
|
257118
257173
|
*
|
|
@@ -257323,6 +257378,18 @@ var script$2k = {
|
|
|
257323
257378
|
default: 'tab',
|
|
257324
257379
|
},
|
|
257325
257380
|
|
|
257381
|
+
/**
|
|
257382
|
+
* A browser storage key under which the shell remembers the last
|
|
257383
|
+
* opened tab, so the page opens on it again when the address names no
|
|
257384
|
+
* tab. Empty (the default) remembers nothing. The address still wins.
|
|
257385
|
+
*
|
|
257386
|
+
* @spec openspec/changes/admin-shell-remembers-tab/specs/settings-board-look/spec.md#requirement-the-admin-shell-can-remember-the-last-tab
|
|
257387
|
+
*/
|
|
257388
|
+
tabStorageKey: {
|
|
257389
|
+
type: String,
|
|
257390
|
+
default: '',
|
|
257391
|
+
},
|
|
257392
|
+
|
|
257326
257393
|
/**
|
|
257327
257394
|
* How the `tabs` strip is drawn, as CnTabs' `variant`: `line`, the
|
|
257328
257395
|
* folder tabs (the default), or `underline`, plain labels on a rule
|
|
@@ -257495,7 +257562,7 @@ var script$2k = {
|
|
|
257495
257562
|
reimporting: false,
|
|
257496
257563
|
setupWizardOpen: false,
|
|
257497
257564
|
// The open tab's id; empty until a tab is picked or the address names one.
|
|
257498
|
-
chosenTabId: readTabFromAddress(this.tabQueryKey),
|
|
257565
|
+
chosenTabId: readTabFromAddress(this.tabQueryKey) || readStoredTab(this.tabStorageKey),
|
|
257499
257566
|
}
|
|
257500
257567
|
},
|
|
257501
257568
|
|
|
@@ -257655,6 +257722,19 @@ var script$2k = {
|
|
|
257655
257722
|
},
|
|
257656
257723
|
},
|
|
257657
257724
|
|
|
257725
|
+
/**
|
|
257726
|
+
* A page that opens on a tab other than the first (named in the address,
|
|
257727
|
+
* or remembered) tells the app once, as a click would, so a breadcrumb or
|
|
257728
|
+
* title that follows the tab is right from the first paint.
|
|
257729
|
+
*
|
|
257730
|
+
* @spec openspec/changes/admin-shell-remembers-tab/specs/settings-board-look/spec.md#requirement-the-admin-shell-can-remember-the-last-tab
|
|
257731
|
+
*/
|
|
257732
|
+
mounted() {
|
|
257733
|
+
if (this.tabItems.length > 0 && this.activeTabId !== this.tabItems[0].id) {
|
|
257734
|
+
this.$emit('tab-change', this.activeTabId);
|
|
257735
|
+
}
|
|
257736
|
+
},
|
|
257737
|
+
|
|
257658
257738
|
methods: {
|
|
257659
257739
|
/**
|
|
257660
257740
|
* A tab was picked in the strip: remember it, put it in the address
|
|
@@ -257671,6 +257751,7 @@ var script$2k = {
|
|
|
257671
257751
|
}
|
|
257672
257752
|
this.chosenTabId = tab.id;
|
|
257673
257753
|
writeTabToAddress(this.tabQueryKey, tab.id);
|
|
257754
|
+
storeTab(this.tabStorageKey, tab.id);
|
|
257674
257755
|
/**
|
|
257675
257756
|
* @event tab-change Emitted when the reader opens another tab. Payload: its `id`.
|
|
257676
257757
|
*/
|
|
@@ -258008,7 +258089,7 @@ function render$2k(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
258008
258089
|
}
|
|
258009
258090
|
|
|
258010
258091
|
script$2k.render = render$2k;
|
|
258011
|
-
script$2k.__scopeId = "data-v-
|
|
258092
|
+
script$2k.__scopeId = "data-v-c3ceda64";
|
|
258012
258093
|
const __sfc_main$2k = script$2k;
|
|
258013
258094
|
|
|
258014
258095
|
/**
|
|
@@ -358472,6 +358553,7 @@ var script$h = {
|
|
|
358472
358553
|
// write lands; this warns the user while there are unsaved/in-flight
|
|
358473
358554
|
// changes. Registered before the early-return so it always installs.
|
|
358474
358555
|
window.addEventListener('beforeunload', this.onBeforeUnload);
|
|
358556
|
+
window.addEventListener(OBJECTS_CHANGED_EVENT, this.onObjectsChanged);
|
|
358475
358557
|
|
|
358476
358558
|
// Opt-out fast-path: empty `requiresApps` already initialised
|
|
358477
358559
|
// `capabilitiesLoading` to `false` in data(); skip the check.
|
|
@@ -358517,6 +358599,7 @@ var script$h = {
|
|
|
358517
358599
|
this.removeDiagnostics = null;
|
|
358518
358600
|
}
|
|
358519
358601
|
window.removeEventListener('beforeunload', this.onBeforeUnload);
|
|
358602
|
+
window.removeEventListener(OBJECTS_CHANGED_EVENT, this.onObjectsChanged);
|
|
358520
358603
|
this.cnScopedTheme.teardown(this.appId);
|
|
358521
358604
|
// Scope the side effect to the shell's lifetime. Layers already written
|
|
358522
358605
|
// stay on screen (see `uninstallModalStack`), so a dialog outliving the
|
|
@@ -359363,13 +359446,17 @@ var script$h = {
|
|
|
359363
359446
|
* response `total` written to `cnMenuItemCounts[item.id]`. The
|
|
359364
359447
|
* request goes straight through axios rather than the object store,
|
|
359365
359448
|
* so the index page's whole-schema total in the store is left alone.
|
|
359366
|
-
* 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.
|
|
359367
359451
|
*
|
|
359368
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.
|
|
359369
359454
|
* @return {void}
|
|
359370
359455
|
* @private
|
|
359371
359456
|
*/
|
|
359372
|
-
_hydrateMenuItemCounts() {
|
|
359457
|
+
_hydrateMenuItemCounts(changed = null) {
|
|
359458
|
+
const register = changed?.register ?? null;
|
|
359459
|
+
const schema = changed?.schema ?? null;
|
|
359373
359460
|
const collect = (items) => {
|
|
359374
359461
|
const out = [];
|
|
359375
359462
|
for (const item of items ?? []) {
|
|
@@ -359384,10 +359471,27 @@ var script$h = {
|
|
|
359384
359471
|
return out
|
|
359385
359472
|
};
|
|
359386
359473
|
for (const item of collect(this.manifest?.menu)) {
|
|
359387
|
-
|
|
359474
|
+
if ((register === null || item.count.register === register) && (schema === null || item.count.schema === schema)) {
|
|
359475
|
+
this._fetchMenuItemCount(item);
|
|
359476
|
+
}
|
|
359388
359477
|
}
|
|
359389
359478
|
},
|
|
359390
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
|
+
|
|
359391
359495
|
/**
|
|
359392
359496
|
* One filtered-count request for a menu entry; see
|
|
359393
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:
|
|
4
|
-
sha256:
|
|
3
|
+
size: 40766945 bytes
|
|
4
|
+
sha256: 4d5c39c1fefeba7aa7f2181f991b71ad3354d5678ea63646f53060e929223d66
|
package/dist/nextcloud-vue.css
CHANGED
|
@@ -34796,28 +34796,28 @@ to {
|
|
|
34796
34796
|
}
|
|
34797
34797
|
|
|
34798
34798
|
|
|
34799
|
-
.cn-admin-settings-shell[data-v-
|
|
34799
|
+
.cn-admin-settings-shell[data-v-c3ceda64] {
|
|
34800
34800
|
max-width: 900px;
|
|
34801
34801
|
}
|
|
34802
34802
|
|
|
34803
34803
|
/* The board look lifts the width and lays the header and cards on a grid (look-board.css). */
|
|
34804
|
-
.cn-support-info[data-v-
|
|
34804
|
+
.cn-support-info[data-v-c3ceda64] {
|
|
34805
34805
|
margin-top: 16px;
|
|
34806
34806
|
padding-top: 16px;
|
|
34807
34807
|
border-top: 1px solid var(--color-border);
|
|
34808
34808
|
}
|
|
34809
|
-
.cn-support-info h4[data-v-
|
|
34809
|
+
.cn-support-info h4[data-v-c3ceda64] {
|
|
34810
34810
|
margin: 0 0 8px 0;
|
|
34811
34811
|
font-size: 14px;
|
|
34812
34812
|
font-weight: 600;
|
|
34813
34813
|
color: var(--color-main-text);
|
|
34814
34814
|
}
|
|
34815
|
-
.cn-support-info p[data-v-
|
|
34815
|
+
.cn-support-info p[data-v-c3ceda64] {
|
|
34816
34816
|
margin: 4px 0;
|
|
34817
34817
|
color: var(--color-text-maxcontrast);
|
|
34818
34818
|
font-size: 14px;
|
|
34819
34819
|
}
|
|
34820
|
-
.cn-support-info a[data-v-
|
|
34820
|
+
.cn-support-info a[data-v-c3ceda64] {
|
|
34821
34821
|
color: var(--color-primary-element);
|
|
34822
34822
|
}
|
|
34823
34823
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@conduction/nextcloud-vue",
|
|
3
|
-
"version": "2.
|
|
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": {
|
|
@@ -232,6 +232,43 @@ function readTabFromAddress(key) {
|
|
|
232
232
|
return new URLSearchParams(window.location.search).get(key) || ''
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
+
/**
|
|
236
|
+
* The tab remembered under a storage key, or '' without a key, a value or
|
|
237
|
+
* usable storage (a private window can refuse it).
|
|
238
|
+
*
|
|
239
|
+
* @param {string} key The storage key.
|
|
240
|
+
* @return {string} The remembered tab id.
|
|
241
|
+
*/
|
|
242
|
+
function readStoredTab(key) {
|
|
243
|
+
if (!key) {
|
|
244
|
+
return ''
|
|
245
|
+
}
|
|
246
|
+
try {
|
|
247
|
+
return window.localStorage.getItem(key) || ''
|
|
248
|
+
} catch {
|
|
249
|
+
return ''
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Remember the open tab under a storage key; nothing without a key or
|
|
255
|
+
* usable storage.
|
|
256
|
+
*
|
|
257
|
+
* @param {string} key The storage key.
|
|
258
|
+
* @param {string} id The tab's id.
|
|
259
|
+
* @return {void}
|
|
260
|
+
*/
|
|
261
|
+
function storeTab(key, id) {
|
|
262
|
+
if (!key) {
|
|
263
|
+
return
|
|
264
|
+
}
|
|
265
|
+
try {
|
|
266
|
+
window.localStorage.setItem(key, id)
|
|
267
|
+
} catch {
|
|
268
|
+
// Storage refused (private window): the address still holds the tab.
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
235
272
|
/**
|
|
236
273
|
* Put the open tab in the address without a new history entry.
|
|
237
274
|
*
|
|
@@ -442,6 +479,18 @@ export default {
|
|
|
442
479
|
default: 'tab',
|
|
443
480
|
},
|
|
444
481
|
|
|
482
|
+
/**
|
|
483
|
+
* A browser storage key under which the shell remembers the last
|
|
484
|
+
* opened tab, so the page opens on it again when the address names no
|
|
485
|
+
* tab. Empty (the default) remembers nothing. The address still wins.
|
|
486
|
+
*
|
|
487
|
+
* @spec openspec/changes/admin-shell-remembers-tab/specs/settings-board-look/spec.md#requirement-the-admin-shell-can-remember-the-last-tab
|
|
488
|
+
*/
|
|
489
|
+
tabStorageKey: {
|
|
490
|
+
type: String,
|
|
491
|
+
default: '',
|
|
492
|
+
},
|
|
493
|
+
|
|
445
494
|
/**
|
|
446
495
|
* How the `tabs` strip is drawn, as CnTabs' `variant`: `line`, the
|
|
447
496
|
* folder tabs (the default), or `underline`, plain labels on a rule
|
|
@@ -614,7 +663,7 @@ export default {
|
|
|
614
663
|
reimporting: false,
|
|
615
664
|
setupWizardOpen: false,
|
|
616
665
|
// The open tab's id; empty until a tab is picked or the address names one.
|
|
617
|
-
chosenTabId: readTabFromAddress(this.tabQueryKey),
|
|
666
|
+
chosenTabId: readTabFromAddress(this.tabQueryKey) || readStoredTab(this.tabStorageKey),
|
|
618
667
|
}
|
|
619
668
|
},
|
|
620
669
|
|
|
@@ -774,6 +823,19 @@ export default {
|
|
|
774
823
|
},
|
|
775
824
|
},
|
|
776
825
|
|
|
826
|
+
/**
|
|
827
|
+
* A page that opens on a tab other than the first (named in the address,
|
|
828
|
+
* or remembered) tells the app once, as a click would, so a breadcrumb or
|
|
829
|
+
* title that follows the tab is right from the first paint.
|
|
830
|
+
*
|
|
831
|
+
* @spec openspec/changes/admin-shell-remembers-tab/specs/settings-board-look/spec.md#requirement-the-admin-shell-can-remember-the-last-tab
|
|
832
|
+
*/
|
|
833
|
+
mounted() {
|
|
834
|
+
if (this.tabItems.length > 0 && this.activeTabId !== this.tabItems[0].id) {
|
|
835
|
+
this.$emit('tab-change', this.activeTabId)
|
|
836
|
+
}
|
|
837
|
+
},
|
|
838
|
+
|
|
777
839
|
methods: {
|
|
778
840
|
/**
|
|
779
841
|
* A tab was picked in the strip: remember it, put it in the address
|
|
@@ -790,6 +852,7 @@ export default {
|
|
|
790
852
|
}
|
|
791
853
|
this.chosenTabId = tab.id
|
|
792
854
|
writeTabToAddress(this.tabQueryKey, tab.id)
|
|
855
|
+
storeTab(this.tabStorageKey, tab.id)
|
|
793
856
|
/**
|
|
794
857
|
* @event tab-change Emitted when the reader opens another tab. Payload: its `id`.
|
|
795
858
|
*/
|
|
@@ -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
|
-
|
|
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
|
},
|