@conduction/nextcloud-vue 2.124.0 → 2.125.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/CnActionDialog/CnActionDialog.vue.js +1 -1
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js +120 -11
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js +78 -32
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +8 -7
- 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/CnAssignDialog/CnAssignDialog.vue.js +1 -1
- package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue2.js +2 -1
- package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue3.js +2 -2
- package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnDialog/CnDialog.vue2.js +5 -0
- package/dist/esm/components/CnDialog/CnDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnDialog/CnDialog.vue3.js +1 -1
- package/dist/esm/components/CnDialog/CnDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +13 -0
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +2 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue.js +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +7 -2
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +159 -10
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +151 -90
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue2.js +12 -0
- package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue2.js.map +1 -1
- package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue3.js +1 -1
- package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue3.js.map +1 -1
- package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue.js +1 -1
- package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue2.js +39 -68
- package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue3.js +70 -116
- package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue.js +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +19 -3
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
- package/dist/esm/l10n/en.json.js +8 -8
- package/dist/esm/l10n/nl.json.js +8 -8
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +316 -185
- package/dist/nextcloud-vue.cjs.js +1149 -808
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +316 -185
- package/l10n/en.json +8 -8
- package/l10n/nl.json +8 -8
- package/package.json +1 -1
- package/src/components/CnActionDialog/CnActionDialog.vue +173 -14
- package/src/components/CnAppRoot/CnAppRoot.vue +8 -7
- package/src/components/CnAssignDialog/CnAssignDialog.vue +4 -3
- package/src/components/CnDialog/CnDialog.vue +6 -1
- package/src/components/CnIndexPage/CnIndexPage.vue +14 -0
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +7 -2
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +237 -23
- package/src/components/CnSavedViewsControl/CnSavedViewsControl.vue +13 -1
- package/src/components/CnUserSettingsDialog/CnUserSettingsDialog.vue +147 -145
- package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +19 -3
- package/src/css/dialog.css +23 -0
- package/src/css/look-board-index.css +5 -1
- package/src/css/look-board.css +19 -0
package/l10n/en.json
CHANGED
|
@@ -2220,6 +2220,13 @@
|
|
|
2220
2220
|
"This could not be read: {reason}": "This could not be read: {reason}",
|
|
2221
2221
|
"Still open": "Still open",
|
|
2222
2222
|
"The checklist could not be read.": "The checklist could not be read.",
|
|
2223
|
+
"Flag it with a reason, so it stands out to everyone who works on it.": "Flag it with a reason, so it stands out to everyone who works on it.",
|
|
2224
|
+
"Clear the flag with a reason once it no longer needs attention.": "Clear the flag with a reason once it no longer needs attention.",
|
|
2225
|
+
"Compose mail": "Compose mail",
|
|
2226
|
+
"Link mail": "Link mail",
|
|
2227
|
+
"Enter a quarter as YYYY-Qn, for example 2026-Q3.": "Enter a quarter as YYYY-Qn, for example 2026-Q3.",
|
|
2228
|
+
"Enter a year as four digits, for example 2026.": "Enter a year as four digits, for example 2026.",
|
|
2229
|
+
"This value has the wrong form.": "This value has the wrong form.",
|
|
2223
2230
|
"The value for {key} must be above {min}.": "The value for {key} must be above {min}.",
|
|
2224
2231
|
"Add a row": "Add a row",
|
|
2225
2232
|
"Key": "Key",
|
|
@@ -2229,14 +2236,7 @@
|
|
|
2229
2236
|
"How this app compares": "How this app compares",
|
|
2230
2237
|
"This field is required": "This field is required",
|
|
2231
2238
|
"Creating…": "Creating…",
|
|
2232
|
-
"This could not be created. Try again.": "This could not be created. Try again."
|
|
2233
|
-
"Flag it with a reason, so it stands out to everyone who works on it.": "Flag it with a reason, so it stands out to everyone who works on it.",
|
|
2234
|
-
"Clear the flag with a reason once it no longer needs attention.": "Clear the flag with a reason once it no longer needs attention.",
|
|
2235
|
-
"Compose mail": "Compose mail",
|
|
2236
|
-
"Link mail": "Link mail",
|
|
2237
|
-
"Enter a quarter as YYYY-Qn, for example 2026-Q3.": "Enter a quarter as YYYY-Qn, for example 2026-Q3.",
|
|
2238
|
-
"Enter a year as four digits, for example 2026.": "Enter a year as four digits, for example 2026.",
|
|
2239
|
-
"This value has the wrong form.": "This value has the wrong form."
|
|
2239
|
+
"This could not be created. Try again.": "This could not be created. Try again."
|
|
2240
2240
|
},
|
|
2241
2241
|
"plurals": {
|
|
2242
2242
|
"{count} member": [
|
package/l10n/nl.json
CHANGED
|
@@ -2470,6 +2470,13 @@
|
|
|
2470
2470
|
"This could not be read: {reason}": "Dit kon niet worden gelezen: {reason}",
|
|
2471
2471
|
"Still open": "Nog open",
|
|
2472
2472
|
"The checklist could not be read.": "De lijst kon niet worden gelezen.",
|
|
2473
|
+
"Flag it with a reason, so it stands out to everyone who works on it.": "Markeer het met een reden, zodat het opvalt bij iedereen die eraan werkt.",
|
|
2474
|
+
"Clear the flag with a reason once it no longer needs attention.": "Haal de markering weg met een reden zodra het geen aandacht meer nodig heeft.",
|
|
2475
|
+
"Compose mail": "Mail opstellen",
|
|
2476
|
+
"Link mail": "Mail koppelen",
|
|
2477
|
+
"Enter a quarter as YYYY-Qn, for example 2026-Q3.": "Vul een kwartaal in als JJJJ-Qn, bijvoorbeeld 2026-Q3.",
|
|
2478
|
+
"Enter a year as four digits, for example 2026.": "Vul een jaar in met vier cijfers, bijvoorbeeld 2026.",
|
|
2479
|
+
"This value has the wrong form.": "Deze waarde heeft niet de goede vorm.",
|
|
2473
2480
|
"The value for {key} must be above {min}.": "De waarde voor {key} moet hoger zijn dan {min}.",
|
|
2474
2481
|
"Add a row": "Een rij toevoegen",
|
|
2475
2482
|
"Key": "Sleutel",
|
|
@@ -2479,14 +2486,7 @@
|
|
|
2479
2486
|
"How this app compares": "Hoe deze app zich verhoudt",
|
|
2480
2487
|
"This field is required": "Dit veld is verplicht",
|
|
2481
2488
|
"Creating…": "Aanmaken…",
|
|
2482
|
-
"This could not be created. Try again.": "Dit kon niet worden aangemaakt. Probeer het opnieuw."
|
|
2483
|
-
"Flag it with a reason, so it stands out to everyone who works on it.": "Markeer het met een reden, zodat het opvalt bij iedereen die eraan werkt.",
|
|
2484
|
-
"Clear the flag with a reason once it no longer needs attention.": "Haal de markering weg met een reden zodra het geen aandacht meer nodig heeft.",
|
|
2485
|
-
"Compose mail": "Mail opstellen",
|
|
2486
|
-
"Link mail": "Mail koppelen",
|
|
2487
|
-
"Enter a quarter as YYYY-Qn, for example 2026-Q3.": "Vul een kwartaal in als JJJJ-Qn, bijvoorbeeld 2026-Q3.",
|
|
2488
|
-
"Enter a year as four digits, for example 2026.": "Vul een jaar in met vier cijfers, bijvoorbeeld 2026.",
|
|
2489
|
-
"This value has the wrong form.": "Deze waarde heeft niet de goede vorm."
|
|
2489
|
+
"This could not be created. Try again.": "Dit kon niet worden aangemaakt. Probeer het opnieuw."
|
|
2490
2490
|
},
|
|
2491
2491
|
"plurals": {
|
|
2492
2492
|
"{count} member": [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@conduction/nextcloud-vue",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.125.0",
|
|
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": {
|
|
@@ -68,10 +68,35 @@
|
|
|
68
68
|
:key="field.field"
|
|
69
69
|
class="cn-action-dialog__field"
|
|
70
70
|
:data-testid="`cn-action-dialog-field-${field.field}`">
|
|
71
|
-
|
|
71
|
+
<!-- A checkbox field: a tinted box, the label filled from the
|
|
72
|
+
record, a hint under it (DqArchiveren "Sanne de Vries laten
|
|
73
|
+
weten..."). -->
|
|
74
|
+
<NcCheckboxRadioSwitch v-if="field.type === 'checkbox'"
|
|
75
|
+
class="cn-action-dialog__checkbox"
|
|
76
|
+
:modelValue="checkboxValue(field)"
|
|
77
|
+
type="checkbox"
|
|
78
|
+
@update:modelValue="(on) => setChecked(field.field, on)">
|
|
79
|
+
<span class="cn-action-dialog__checkbox-text">
|
|
80
|
+
<strong class="cn-action-dialog__checkbox-label">{{ fill(field.label) || field.field }}</strong>
|
|
81
|
+
<span v-if="field.hint" class="cn-action-dialog__muted">{{ fill(field.hint) }}</span>
|
|
82
|
+
</span>
|
|
83
|
+
</NcCheckboxRadioSwitch>
|
|
84
|
+
<!-- A search field: records of `source` found as the reader
|
|
85
|
+
types (DqZaakKoppelen "Zaak koppelen"). -->
|
|
86
|
+
<CnSearchSelect v-else-if="field.type === 'search'"
|
|
87
|
+
:modelValue="values[field.field] || null"
|
|
88
|
+
:options="searchOptions[field.field] || []"
|
|
89
|
+
:inputLabel="fill(field.label) || field.field"
|
|
90
|
+
:labelOutside="true"
|
|
91
|
+
:loading="searching === field.field"
|
|
92
|
+
:filterable="false"
|
|
93
|
+
:placeholder="fill(field.placeholder)"
|
|
94
|
+
@search="(typed) => onSearch(field, typed)"
|
|
95
|
+
@update:modelValue="(value) => setValue(field.field, value && typeof value === 'object' ? value.id : value)" />
|
|
96
|
+
<NcSelect v-else-if="field.type === 'select'"
|
|
72
97
|
:modelValue="selectedOption(field)"
|
|
73
98
|
:options="optionsOf(field)"
|
|
74
|
-
:inputLabel="
|
|
99
|
+
:inputLabel="fill(field.label) || field.field"
|
|
75
100
|
:clearable="field.required === false"
|
|
76
101
|
@update:modelValue="(option) => pick(field, option)" />
|
|
77
102
|
<NcTextField v-else-if="field.type === 'secret'"
|
|
@@ -84,8 +109,9 @@
|
|
|
84
109
|
<NcTextField v-else
|
|
85
110
|
:modelValue="values[field.field] || ''"
|
|
86
111
|
:type="field.type === 'datetime' ? 'datetime-local' : (field.type === 'date' ? 'date' : 'text')"
|
|
87
|
-
:label="
|
|
112
|
+
:label="fill(field.label) || field.field"
|
|
88
113
|
@update:modelValue="(value) => setValue(field.field, value)" />
|
|
114
|
+
<span v-if="field.hint && field.type !== 'checkbox'" class="cn-action-dialog__muted">{{ fill(field.hint) }}</span>
|
|
89
115
|
</div>
|
|
90
116
|
|
|
91
117
|
<div v-if="config.reason" class="cn-action-dialog__field">
|
|
@@ -142,7 +168,7 @@ import axios from '@nextcloud/axios'
|
|
|
142
168
|
import { emit } from '@nextcloud/event-bus'
|
|
143
169
|
import { translate as t } from '@nextcloud/l10n'
|
|
144
170
|
import { generateUrl } from '@nextcloud/router'
|
|
145
|
-
import { NcButton, NcNoteCard, NcSelect, NcTextArea, NcTextField } from '@nextcloud/vue'
|
|
171
|
+
import { NcButton, NcCheckboxRadioSwitch, NcNoteCard, NcSelect, NcTextArea, NcTextField } from '@nextcloud/vue'
|
|
146
172
|
import CheckCircle from 'vue-material-design-icons/CheckCircle.vue'
|
|
147
173
|
import CircleOutline from 'vue-material-design-icons/CircleOutline.vue'
|
|
148
174
|
import HelpCircleOutline from 'vue-material-design-icons/HelpCircleOutline.vue'
|
|
@@ -151,6 +177,10 @@ import { fillTemplate } from '../../utils/fillTemplate.js'
|
|
|
151
177
|
import { readPath } from '../../utils/readPath.js'
|
|
152
178
|
import { CnDialog } from '../CnDialog/index.js'
|
|
153
179
|
import { CnIcon } from '../CnIcon/index.js'
|
|
180
|
+
import { CnSearchSelect } from '../CnSearchSelect/index.js'
|
|
181
|
+
|
|
182
|
+
/** How long typing rests before a search field searches, in ms. */
|
|
183
|
+
const SEARCH_DELAY = 250
|
|
154
184
|
|
|
155
185
|
/** The page refresh channel the built-in widgets listen on. */
|
|
156
186
|
const PAGE_REFRESH = 'cn:page:refresh'
|
|
@@ -180,10 +210,17 @@ let reasonSeq = 0
|
|
|
180
210
|
* the action runs only while `allowedPath` reads true; `reasonPath` says
|
|
181
211
|
* why not. With `listPath` the paths read the entry of that list whose
|
|
182
212
|
* fields equal `match` (`{ act: "archive" }`).
|
|
183
|
-
* - `fields`: `[{ field, type?: "text"|"date"|"datetime"|"select", label?,
|
|
184
|
-
* required?, options?: [{ value, label, fills? }] }]`: inputs
|
|
185
|
-
* the reason, required by default; a select option's `fills`
|
|
186
|
-
* fields that are still empty.
|
|
213
|
+
* - `fields`: `[{ field, type?: "text"|"date"|"datetime"|"select"|"checkbox", label?,
|
|
214
|
+
* required?, options?: [{ value, label, fills? }], hint?, default? }]`: inputs
|
|
215
|
+
* sent beside the reason, required by default; a select option's `fills`
|
|
216
|
+
* sets other fields that are still empty. A `checkbox` is sent as a boolean,
|
|
217
|
+
* starts at `default` (false), fills its `label` and `hint` from the record,
|
|
218
|
+
* and is optional unless `required: true` (then it must be ticked). A
|
|
219
|
+
* `search` field finds records of `source: { register, schema }` as the
|
|
220
|
+
* reader types, shows each as `optionLabel` with `optionSecondary` under
|
|
221
|
+
* it (templates on the found record), leaves the record in hand out with
|
|
222
|
+
* `excludeSelf`, and sends the chosen record's id. Every field's `label`,
|
|
223
|
+
* `hint` and `placeholder` are templates filled from the record.
|
|
187
224
|
* - `reason`: `{ label?, hint?, required?, field?, rows? }`: a text area; required by
|
|
188
225
|
* default; sent as `field` (default `reason`). `rows` is its height in lines
|
|
189
226
|
* (default 2; board DqBesluit draws 4).
|
|
@@ -197,7 +234,7 @@ let reasonSeq = 0
|
|
|
197
234
|
export default {
|
|
198
235
|
name: 'CnActionDialog',
|
|
199
236
|
|
|
200
|
-
components: { CheckCircle, CircleOutline, CnDialog, CnIcon, HelpCircleOutline, NcButton, NcNoteCard, NcSelect, NcTextArea, NcTextField },
|
|
237
|
+
components: { CheckCircle, CircleOutline, CnDialog, CnIcon, CnSearchSelect, HelpCircleOutline, NcButton, NcCheckboxRadioSwitch, NcNoteCard, NcSelect, NcTextArea, NcTextField },
|
|
201
238
|
|
|
202
239
|
inject: {
|
|
203
240
|
/** The host app's translate (CnAppRoot `cnTranslate`). */
|
|
@@ -244,6 +281,10 @@ export default {
|
|
|
244
281
|
allowed: true,
|
|
245
282
|
reason: '',
|
|
246
283
|
values: {},
|
|
284
|
+
/** A search field's found options, by field name. */
|
|
285
|
+
searchOptions: {},
|
|
286
|
+
/** The search field whose search runs, or ''. */
|
|
287
|
+
searching: '',
|
|
247
288
|
busy: false,
|
|
248
289
|
refusal: '',
|
|
249
290
|
done: false,
|
|
@@ -362,6 +403,20 @@ export default {
|
|
|
362
403
|
return this.record ? this.fill(this.config.eyebrow) || this.text(this.config.eyebrow) : this.text(this.config.eyebrow)
|
|
363
404
|
},
|
|
364
405
|
|
|
406
|
+
/**
|
|
407
|
+
* The value of every field as sent: a checkbox as a boolean, the
|
|
408
|
+
* others as entered ('' when empty).
|
|
409
|
+
*
|
|
410
|
+
* @return {object} Values by field name.
|
|
411
|
+
*/
|
|
412
|
+
fieldValues() {
|
|
413
|
+
const out = { ...this.values }
|
|
414
|
+
for (const field of this.fields) {
|
|
415
|
+
out[field.field] = field.type === 'checkbox' ? this.checkboxValue(field) : (this.values[field.field] ?? '')
|
|
416
|
+
}
|
|
417
|
+
return out
|
|
418
|
+
},
|
|
419
|
+
|
|
365
420
|
/** @return {Array<object>} The fields with a name. */
|
|
366
421
|
fields() {
|
|
367
422
|
return (Array.isArray(this.config.fields) ? this.config.fields : []).filter((f) => f && f.field)
|
|
@@ -374,7 +429,12 @@ export default {
|
|
|
374
429
|
* @return {boolean}
|
|
375
430
|
*/
|
|
376
431
|
fieldsFilled() {
|
|
377
|
-
return this.fields.every((f) =>
|
|
432
|
+
return this.fields.every((f) => {
|
|
433
|
+
if (f.type === 'checkbox') {
|
|
434
|
+
return f.required !== true || this.checkboxValue(f)
|
|
435
|
+
}
|
|
436
|
+
return f.required === false || this.secretStored(f) || String(this.values[f.field] ?? '').trim() !== ''
|
|
437
|
+
})
|
|
378
438
|
},
|
|
379
439
|
},
|
|
380
440
|
|
|
@@ -392,6 +452,10 @@ export default {
|
|
|
392
452
|
await Promise.all([this.loadRecord(), this.loadChecklist(), this.loadPermission()])
|
|
393
453
|
},
|
|
394
454
|
|
|
455
|
+
beforeUnmount() {
|
|
456
|
+
clearTimeout(this.searchTimer)
|
|
457
|
+
},
|
|
458
|
+
|
|
395
459
|
methods: {
|
|
396
460
|
t,
|
|
397
461
|
|
|
@@ -401,6 +465,76 @@ export default {
|
|
|
401
465
|
* @param {unknown} value The text.
|
|
402
466
|
* @return {string}
|
|
403
467
|
*/
|
|
468
|
+
/**
|
|
469
|
+
* Search a search field's records once typing rests.
|
|
470
|
+
*
|
|
471
|
+
* @param {object} field The field.
|
|
472
|
+
* @param {string} typed What the reader typed.
|
|
473
|
+
* @return {void}
|
|
474
|
+
* @spec openspec/changes/action-dialog-search-field/specs/action-dialog-search-field/spec.md#requirement-an-action-dialog-field-can-search-records
|
|
475
|
+
*/
|
|
476
|
+
onSearch(field, typed) {
|
|
477
|
+
clearTimeout(this.searchTimer)
|
|
478
|
+
this.searchTimer = setTimeout(() => this.searchRecords(field, String(typed || '').trim()), SEARCH_DELAY)
|
|
479
|
+
},
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Read the records of a search field's `source` that match the term.
|
|
483
|
+
*
|
|
484
|
+
* @param {object} field The field.
|
|
485
|
+
* @param {string} term The term.
|
|
486
|
+
* @return {Promise<void>}
|
|
487
|
+
*/
|
|
488
|
+
async searchRecords(field, term) {
|
|
489
|
+
const source = field.source || {}
|
|
490
|
+
if (!term || !source.register || !source.schema) {
|
|
491
|
+
this.searchOptions = { ...this.searchOptions, [field.field]: [] }
|
|
492
|
+
return
|
|
493
|
+
}
|
|
494
|
+
this.searching = field.field
|
|
495
|
+
try {
|
|
496
|
+
const url = generateUrl(`/apps/openregister/api/objects/${encodeURIComponent(source.register)}/${encodeURIComponent(source.schema)}`)
|
|
497
|
+
const { data } = await axios.get(url, { params: { _search: term, _limit: 10 } })
|
|
498
|
+
const rows = Array.isArray(data) ? data : (data && Array.isArray(data.results) ? data.results : [])
|
|
499
|
+
const self = String(this.recordId || '')
|
|
500
|
+
const options = rows
|
|
501
|
+
.map((row) => ({ row, id: String(row.id || (row['@self'] && row['@self'].id) || '') }))
|
|
502
|
+
.filter(({ id }) => id !== '' && !(field.excludeSelf && id === self))
|
|
503
|
+
.map(({ row, id }) => {
|
|
504
|
+
const option = { id, label: this.fill(field.optionLabel || '{title}', row) || id }
|
|
505
|
+
const secondary = this.fill(field.optionSecondary, row)
|
|
506
|
+
return secondary ? { ...option, secondary } : option
|
|
507
|
+
})
|
|
508
|
+
this.searchOptions = { ...this.searchOptions, [field.field]: options }
|
|
509
|
+
} catch {
|
|
510
|
+
this.searchOptions = { ...this.searchOptions, [field.field]: [] }
|
|
511
|
+
} finally {
|
|
512
|
+
this.searching = ''
|
|
513
|
+
}
|
|
514
|
+
},
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* A checkbox field's state: what was ticked, else its `default`.
|
|
518
|
+
*
|
|
519
|
+
* @param {object} field The field.
|
|
520
|
+
* @return {boolean}
|
|
521
|
+
* @spec openspec/changes/action-dialog-checkbox-field/specs/action-dialog-checkbox-field/spec.md#requirement-an-action-dialog-field-can-be-a-checkbox
|
|
522
|
+
*/
|
|
523
|
+
checkboxValue(field) {
|
|
524
|
+
return field.field in this.values ? this.values[field.field] === true : field.default === true
|
|
525
|
+
},
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Tick or untick a checkbox field.
|
|
529
|
+
*
|
|
530
|
+
* @param {string} name The field.
|
|
531
|
+
* @param {boolean} on Ticked.
|
|
532
|
+
* @return {void}
|
|
533
|
+
*/
|
|
534
|
+
setChecked(name, on) {
|
|
535
|
+
this.values = { ...this.values, [name]: on === true }
|
|
536
|
+
},
|
|
537
|
+
|
|
404
538
|
text(value) {
|
|
405
539
|
return typeof value === 'string' && value !== '' ? this.cnTranslate(value) : ''
|
|
406
540
|
},
|
|
@@ -557,21 +691,22 @@ export default {
|
|
|
557
691
|
if (this.config.reason) {
|
|
558
692
|
body[this.config.reason.field || 'reason'] = reason
|
|
559
693
|
}
|
|
694
|
+
const values = { ...this.fieldValues }
|
|
560
695
|
for (const field of this.fields) {
|
|
561
696
|
if (field.type === 'secret' && String(this.values[field.field] ?? '') === '') {
|
|
562
|
-
|
|
697
|
+
delete values[field.field]
|
|
563
698
|
}
|
|
564
|
-
body[field.field] = this.values[field.field] ?? ''
|
|
565
699
|
}
|
|
700
|
+
Object.assign(body, values)
|
|
566
701
|
const method = String(request.method || 'POST').toLowerCase()
|
|
567
702
|
await axios[['post', 'put', 'patch', 'delete'].includes(method) ? method : 'post'](/^https?:\/\//i.test(url) ? url : generateUrl(url), body)
|
|
568
703
|
} else if (this.confirmConfig.action && typeof this.cnDispatchAction === 'function') {
|
|
569
|
-
await this.cnDispatchAction({ ...this.confirmConfig.action, args: [...(this.confirmConfig.action.args || []), { ...this.
|
|
704
|
+
await this.cnDispatchAction({ ...this.confirmConfig.action, args: [...(this.confirmConfig.action.args || []), { ...this.fieldValues, reason }] })
|
|
570
705
|
}
|
|
571
706
|
this.done = true
|
|
572
707
|
emit(PAGE_REFRESH, {})
|
|
573
708
|
/** @event done The action ran. Payload: the field values and `reason`. */
|
|
574
|
-
this.$emit('done', { ...this.
|
|
709
|
+
this.$emit('done', { ...this.fieldValues, reason })
|
|
575
710
|
} catch (failure) {
|
|
576
711
|
const data = failure && failure.response && failure.response.data
|
|
577
712
|
this.refusal = String((data && (data.message || data.error)) || t('nextcloud-vue', 'The action could not be completed.'))
|
|
@@ -729,6 +864,30 @@ export default {
|
|
|
729
864
|
font-size: 14px;
|
|
730
865
|
}
|
|
731
866
|
|
|
867
|
+
/* A checkbox field is a tinted box with a 2px edge, its label bold and its
|
|
868
|
+
hint under it (DqArchiveren). */
|
|
869
|
+
.cn-action-dialog__checkbox {
|
|
870
|
+
padding: 12px 14px;
|
|
871
|
+
border: 2px solid var(--color-primary-element);
|
|
872
|
+
border-radius: var(--border-radius-element, 8px);
|
|
873
|
+
background: var(--color-primary-element-light);
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
.cn-action-dialog__checkbox-text {
|
|
877
|
+
display: flex;
|
|
878
|
+
flex-direction: column;
|
|
879
|
+
gap: 2px;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
.cn-action-dialog__checkbox-label {
|
|
883
|
+
font-size: 15px;
|
|
884
|
+
font-weight: 600;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
.cn-action-dialog__checkbox .cn-action-dialog__muted {
|
|
888
|
+
font-size: 13px;
|
|
889
|
+
}
|
|
890
|
+
|
|
732
891
|
.cn-action-dialog__field {
|
|
733
892
|
display: flex;
|
|
734
893
|
flex-direction: column;
|
|
@@ -1690,9 +1690,10 @@ export default {
|
|
|
1690
1690
|
},
|
|
1691
1691
|
|
|
1692
1692
|
/**
|
|
1693
|
-
*
|
|
1694
|
-
*
|
|
1693
|
+
* Deprecated and ignored: the board user-settings dialog draws no
|
|
1694
|
+
* line above its title (DqPersoonlijkeInstellingen).
|
|
1695
1695
|
*
|
|
1696
|
+
* @deprecated
|
|
1696
1697
|
* @type {string}
|
|
1697
1698
|
*/
|
|
1698
1699
|
userSettingsEyebrow: {
|
|
@@ -1701,10 +1702,10 @@ export default {
|
|
|
1701
1702
|
},
|
|
1702
1703
|
|
|
1703
1704
|
/**
|
|
1704
|
-
*
|
|
1705
|
-
*
|
|
1706
|
-
* settings."
|
|
1705
|
+
* Deprecated and ignored: the board user-settings dialog draws no
|
|
1706
|
+
* sentence under its title (DqPersoonlijkeInstellingen).
|
|
1707
1707
|
*
|
|
1708
|
+
* @deprecated
|
|
1708
1709
|
* @type {string}
|
|
1709
1710
|
*/
|
|
1710
1711
|
userSettingsSubtitle: {
|
|
@@ -3205,8 +3206,8 @@ export default {
|
|
|
3205
3206
|
|
|
3206
3207
|
/**
|
|
3207
3208
|
* The props of the user-settings dialog: `NcAppSettingsDialog`'s, or
|
|
3208
|
-
* under the board look CnUserSettingsDialog's (
|
|
3209
|
-
* the
|
|
3209
|
+
* under the board look CnUserSettingsDialog's (title, the app's name
|
|
3210
|
+
* for the note under the sections, and the Nextcloud links).
|
|
3210
3211
|
*
|
|
3211
3212
|
* @spec openspec/changes/user-settings-board-dialog/specs/user-settings-board-dialog/spec.md#requirement-the-user-settings-dialog-takes-the-board-look
|
|
3212
3213
|
* @return {object}
|
|
@@ -46,9 +46,9 @@
|
|
|
46
46
|
:class="{ 'cn-assign-dialog__toggle--on': flags[toggle.field] }"
|
|
47
47
|
:data-testid="`cn-assign-dialog-toggle-${toggle.field}`">
|
|
48
48
|
<NcCheckboxRadioSwitch :modelValue="flags[toggle.field] === true" @update:modelValue="flags[toggle.field] = $event">
|
|
49
|
-
<span class="cn-assign-dialog__toggle-title">{{
|
|
49
|
+
<span class="cn-assign-dialog__toggle-title">{{ fill(toggle.label) }}</span>
|
|
50
50
|
</NcCheckboxRadioSwitch>
|
|
51
|
-
<span v-if="toggle.note" class="cn-assign-dialog__hint cn-assign-dialog__toggle-note">{{
|
|
51
|
+
<span v-if="toggle.note" class="cn-assign-dialog__hint cn-assign-dialog__toggle-note">{{ fill(toggle.note) }}</span>
|
|
52
52
|
</div>
|
|
53
53
|
|
|
54
54
|
<p v-if="refusal"
|
|
@@ -144,7 +144,8 @@ export function withTypedAssignee(found, typed, label) {
|
|
|
144
144
|
* sent as `field` (default `note`); `false` drops it. `rows` is the text
|
|
145
145
|
* area's height in lines (default 3, as board DqOverdragen draws it).
|
|
146
146
|
* - `toggles`: `[{ field, label, note? }]`: switches that start off and are
|
|
147
|
-
* sent as booleans
|
|
147
|
+
* sent as booleans; `label` and `note` are templates filled from the record
|
|
148
|
+
* ("{initiatorDisplayName} then gets a message").
|
|
148
149
|
* - `confirm`: `{ label?, icon?, request?: { method?, url, body? }, action? }`.
|
|
149
150
|
* `request` sends the choice itself (`body` templates filled from the
|
|
150
151
|
* record); `action` dispatches a manifest action with the choice as its
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
:class="frameClass"
|
|
11
11
|
:style="frameStyle"
|
|
12
12
|
@closing="onClosing">
|
|
13
|
-
<CnDialogHeader v-if="isBoard"
|
|
13
|
+
<CnDialogHeader v-if="isBoard && !headless"
|
|
14
14
|
:title="name"
|
|
15
15
|
:eyebrow="eyebrow"
|
|
16
16
|
:subtitle="subtitle"
|
|
@@ -74,6 +74,11 @@ export default {
|
|
|
74
74
|
eyebrow: { type: String, default: '' },
|
|
75
75
|
/** Sentence under the title (board look). */
|
|
76
76
|
subtitle: { type: String, default: '' },
|
|
77
|
+
/**
|
|
78
|
+
* Leave out the board header: the body draws its own title and close
|
|
79
|
+
* button (CnUserSettingsDialog). `name` stays the accessible name.
|
|
80
|
+
*/
|
|
81
|
+
headless: { type: Boolean, default: false },
|
|
77
82
|
},
|
|
78
83
|
|
|
79
84
|
emits: ['closing'],
|
|
@@ -340,6 +340,7 @@
|
|
|
340
340
|
:currentUserId="currentSavedViewsUserId"
|
|
341
341
|
:counts="savedViewCounts"
|
|
342
342
|
:selectedViewId="appliedSavedViewId"
|
|
343
|
+
:chips="savedViewChips"
|
|
343
344
|
@apply="onApplySavedView"
|
|
344
345
|
@saveRequest="showSaveViewDialog = true" />
|
|
345
346
|
<!-- Native Export menu (opt-in via `allowExport` + schema.exportable):
|
|
@@ -2316,6 +2317,19 @@ export default {
|
|
|
2316
2317
|
default: null,
|
|
2317
2318
|
},
|
|
2318
2319
|
|
|
2320
|
+
/**
|
|
2321
|
+
* Whether the board look draws the saved views as chips in the toolbar
|
|
2322
|
+
* (manifest `config.savedViewChips`). `false` keeps only Save view,
|
|
2323
|
+
* for a page whose views live in the side panel's Weergaven tab
|
|
2324
|
+
* (DqZaken, DqZakenSorteren, DqZakenKaart, DqWerkbord, DqFilterBeheren).
|
|
2325
|
+
*
|
|
2326
|
+
* @spec openspec/changes/saved-view-chips-switch/specs/saved-views/spec.md#requirement-an-index-page-can-leave-the-saved-view-chips-out-of-the-toolbar
|
|
2327
|
+
*/
|
|
2328
|
+
savedViewChips: {
|
|
2329
|
+
type: Boolean,
|
|
2330
|
+
default: true,
|
|
2331
|
+
},
|
|
2332
|
+
|
|
2319
2333
|
/**
|
|
2320
2334
|
* Where the quick-filter chips sit (manifest `config.quickFilterPlacement`).
|
|
2321
2335
|
* `''` (the default) as before. `'toolbar'` keeps them on one row with
|
|
@@ -353,6 +353,7 @@ import { buildExportUrl } from '../../utils/indexExportHelpers.js'
|
|
|
353
353
|
import { followLinkClick, openRowTarget, resolveHref } from '../../utils/linkNavigation.js'
|
|
354
354
|
import { createActionState } from '../../utils/listCreateAction.js'
|
|
355
355
|
import { objectDisplayName, objectFieldValue } from '../../utils/objectName.js'
|
|
356
|
+
import { readPath } from '../../utils/readPath.js'
|
|
356
357
|
import { moveIndex, renumberRows, rowIdOf } from '../../utils/reorderRows.js'
|
|
357
358
|
import { dropOptionalUnresolved, hasUnresolvedTokens, resolveFilterTokens } from '../../utils/resolveFilterTokens.js'
|
|
358
359
|
import { markNewTabHandled } from '../../utils/rowAuxClick.js'
|
|
@@ -497,13 +498,15 @@ export default {
|
|
|
497
498
|
* `hideHeader: true` drops the table's column header row (CnDataTable's
|
|
498
499
|
* `hideHeader`), for a short list that reads as rows, not as a table.
|
|
499
500
|
*
|
|
501
|
+
* `rowIdField` (a dot-path) names the row field holding the id
|
|
502
|
+
* `rowRoute` opens, for a row that links to another record.
|
|
500
503
|
* `footnote` is a line under the table, through the host translate.
|
|
501
504
|
*
|
|
502
505
|
* `export` (`{ label?, format?, group? }`) offers the filtered list as a
|
|
503
506
|
* download in the card's header; `group` shows it to one group only.
|
|
504
507
|
*
|
|
505
508
|
* @spec openspec/changes/zuiddrecht-pixel-gaps/specs/zuiddrecht-pixel-gaps/spec.md#requirement-small-opt-ins-from-the-decidiq-and-learniq-lanes
|
|
506
|
-
* @type {{register?: string, schema?: string, filter?: object, sort?: {field?: string, dir?: string}, limit?: number, extend?: Array<string>, columns?: Array, rowActions?: Array<object>, dropZone?: object, upload?: boolean, groupBy?: string, groupLabel?: string, groupLabelResolve?: {register: string, schema: string, labelField?: string}, selectable?: boolean, bulkActions?: Array<object>, sortable?: boolean, facet?: {field: string, label?: string}, rowRoute?: string, prompt?: string, emptyText?: string, footnote?: string, viewAllRoute?: string, viewAllQuery?: object, hideHeader?: boolean, export?: {label?: string, format?: ('csv'|'excel'), group?: string}}}
|
|
509
|
+
* @type {{register?: string, schema?: string, filter?: object, sort?: {field?: string, dir?: string}, limit?: number, extend?: Array<string>, columns?: Array, rowActions?: Array<object>, dropZone?: object, upload?: boolean, groupBy?: string, groupLabel?: string, groupLabelResolve?: {register: string, schema: string, labelField?: string}, selectable?: boolean, bulkActions?: Array<object>, sortable?: boolean, facet?: {field: string, label?: string}, rowRoute?: string, rowIdField?: string, prompt?: string, emptyText?: string, footnote?: string, viewAllRoute?: string, viewAllQuery?: object, hideHeader?: boolean, export?: {label?: string, format?: ('csv'|'excel'), group?: string}}}
|
|
507
510
|
*/
|
|
508
511
|
content: {
|
|
509
512
|
type: Object,
|
|
@@ -2145,7 +2148,9 @@ export default {
|
|
|
2145
2148
|
*/
|
|
2146
2149
|
openRowRoute(row, event) {
|
|
2147
2150
|
const route = this.content.rowRoute
|
|
2148
|
-
const
|
|
2151
|
+
const field = this.content.rowIdField
|
|
2152
|
+
const raw = field ? readPath(row, field) : row && (row.id || (row['@self'] && row['@self'].id))
|
|
2153
|
+
const id = typeof raw === 'string' || typeof raw === 'number' ? String(raw) : ''
|
|
2149
2154
|
if (route && id && this.$router) {
|
|
2150
2155
|
markNewTabHandled(event, openRowTarget(event, { name: route, params: { id } }, this.$router))
|
|
2151
2156
|
}
|