@conduction/nextcloud-vue 2.97.0 → 2.99.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/CnActionsMenu/CnActionsMenu.vue2.js +12 -0
- package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js +11 -2
- package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +17 -0
- 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/CnBreadcrumbs/CnBreadcrumbs.vue.js +1 -1
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue2.js.map +1 -1
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue3.js +1 -1
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue3.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue.js +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js +106 -2
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue.js +9 -0
- package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue.js.map +1 -0
- package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue2.js +964 -0
- package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue3.js +282 -0
- package/dist/esm/components/CnChecklistRunWidget/CnChecklistRunWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnChecklistRunWidget/index.js +28 -0
- package/dist/esm/components/CnChecklistRunWidget/index.js.map +1 -0
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +1 -0
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +26 -8
- 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/CnDetailPage/CnDetailPage.vue2.js +188 -5
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +2 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue.js +1 -1
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js +3 -3
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js.map +1 -1
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js.map +1 -1
- package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue.js +1 -1
- package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue2.js +27 -0
- package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue2.js.map +1 -1
- package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue3.js +16 -2
- package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +43 -3
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue.js +9 -0
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue.js.map +1 -0
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue2.js +239 -0
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue2.js.map +1 -0
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue3.js +182 -0
- package/dist/esm/components/CnOrderedListField/CnOrderedListField.vue3.js.map +1 -0
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue.js +9 -0
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue.js.map +1 -0
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue2.js +265 -0
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue2.js.map +1 -0
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js +191 -0
- package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js.map +1 -0
- package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue.js +9 -0
- package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue.js.map +1 -0
- package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue2.js +508 -0
- package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue2.js.map +1 -0
- package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue3.js +99 -0
- package/dist/esm/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue3.js.map +1 -0
- package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue.js +1 -1
- package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue2.js +18 -70
- package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue2.js.map +1 -1
- package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue3.js +6 -44
- package/dist/esm/components/CnSettingsPage/CnSettingsPage.vue3.js.map +1 -1
- package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue.js +1 -1
- package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue2.js +83 -9
- package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue3.js +80 -11
- package/dist/esm/components/CnUserSettingsDialog/CnUserSettingsDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +2 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
- package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue.js +9 -0
- package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue.js.map +1 -0
- package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue2.js +472 -0
- package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue3.js +231 -0
- package/dist/esm/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnWorkingCalendarWidget/index.js +27 -0
- package/dist/esm/components/CnWorkingCalendarWidget/index.js.map +1 -0
- package/dist/esm/composables/indexSources.js +238 -2
- package/dist/esm/composables/indexSources.js.map +1 -1
- package/dist/esm/composables/useFlowStore.js +2 -1
- package/dist/esm/composables/useFlowStore.js.map +1 -1
- package/dist/esm/composables/useOrganisationStore.js +115 -0
- package/dist/esm/composables/useOrganisationStore.js.map +1 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +109 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +138 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +1071 -274
- package/dist/esm/utils/builtInFormatters.js +34 -1
- package/dist/esm/utils/builtInFormatters.js.map +1 -1
- package/dist/esm/utils/libraryWidgetKeys.js +2 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/matrixCells.js +158 -0
- package/dist/esm/utils/matrixCells.js.map +1 -0
- package/dist/esm/utils/nextcloudDirectory.js +121 -0
- package/dist/esm/utils/nextcloudDirectory.js.map +1 -0
- package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/esm/utils/workingCalendarMonth.js +85 -0
- package/dist/esm/utils/workingCalendarMonth.js.map +1 -0
- package/dist/nextcloud-vue.cjs.js +14888 -10014
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +1071 -274
- package/l10n/en.json +109 -1
- package/l10n/nl.json +138 -1
- package/package.json +2 -2
- package/scripts/merge-l10n.js +196 -0
- package/src/components/CnActionsMenu/CnActionsMenu.vue +14 -1
- package/src/components/CnAppRoot/CnAppRoot.md +1 -0
- package/src/components/CnAppRoot/CnAppRoot.vue +17 -0
- package/src/components/CnBreadcrumbs/CnBreadcrumbs.vue +1 -0
- package/src/components/CnChartWidget/CnChartWidget.md +2 -1
- package/src/components/CnChartWidget/CnChartWidget.vue +106 -2
- package/src/components/CnChecklistRunWidget/CnChecklistRunWidget.vue +1269 -0
- package/src/components/CnChecklistRunWidget/index.js +27 -0
- package/src/components/CnDashboardPage/CnDashboardPage.vue +1 -0
- package/src/components/CnDataTable/CnDataTable.vue +26 -8
- package/src/components/CnDetailPage/CnDetailPage.md +48 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +189 -5
- package/src/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue +3 -3
- package/src/components/CnFeaturesBoard/CnFeaturesBoard.vue +38 -2
- package/src/components/CnIndexPage/CnIndexPage.md +28 -0
- package/src/components/CnIndexPage/CnIndexPage.vue +43 -3
- package/src/components/CnOrderedListField/CnOrderedListField.vue +458 -0
- package/src/components/CnOrderedListField/index.js +7 -0
- package/src/components/CnSettingsField/CnSettingsField.vue +449 -0
- package/src/components/CnSettingsField/index.js +4 -0
- package/src/components/CnSettingsFieldsSection/CnSettingsFieldsSection.vue +613 -0
- package/src/components/CnSettingsFieldsSection/index.js +4 -0
- package/src/components/CnSettingsPage/CnSettingsPage.vue +6 -86
- package/src/components/CnUserSettingsDialog/CnUserSettingsDialog.vue +139 -10
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +2 -0
- package/src/components/CnWorkingCalendarWidget/CnWorkingCalendarWidget.vue +725 -0
- package/src/components/CnWorkingCalendarWidget/index.js +26 -0
- package/src/components/index.js +5 -0
- package/src/composables/indexSources.js +237 -1
- package/src/composables/useFlowStore.js +2 -1
- package/src/composables/useOrganisationStore.js +111 -0
- package/src/css/badge.css +32 -16
- package/src/css/look-board-detail.css +80 -8
- package/src/css/look-board-index.css +52 -8
- package/src/css/look-board.css +219 -1
- package/src/index.js +5 -0
- package/src/schemas/app-manifest-v2.schema.json +33 -3
- package/src/utils/builtInFormatters.js +33 -0
- package/src/utils/libraryWidgetKeys.js +2 -0
- package/src/utils/matrixCells.js +154 -0
- package/src/utils/nextcloudDirectory.js +117 -0
- package/src/utils/validateManifestV2.compiled.js +3 -3
- package/src/utils/workingCalendarMonth.js +82 -0
|
@@ -172,34 +172,9 @@
|
|
|
172
172
|
:field="field"
|
|
173
173
|
:value="formData[field.key]"
|
|
174
174
|
:onInput="(v) => updateField(field.key, v)">
|
|
175
|
-
<
|
|
176
|
-
|
|
177
|
-
:modelValue="
|
|
178
|
-
@update:modelValue="updateField(field.key, $event)">
|
|
179
|
-
{{ resolveLabel(field.label) }}
|
|
180
|
-
</NcCheckboxRadioSwitch>
|
|
181
|
-
<NcTextField
|
|
182
|
-
v-else-if="field.type === 'number'"
|
|
183
|
-
:label="resolveLabel(field.label)"
|
|
184
|
-
type="number"
|
|
185
|
-
:modelValue="String(fieldValue(field.key, ''))"
|
|
186
|
-
@update:modelValue="updateField(field.key, $event === '' ? null : Number($event))" />
|
|
187
|
-
<NcTextField
|
|
188
|
-
v-else-if="field.type === 'password'"
|
|
189
|
-
:label="resolveLabel(field.label)"
|
|
190
|
-
type="password"
|
|
191
|
-
:modelValue="fieldValue(field.key, '')"
|
|
192
|
-
@update:modelValue="updateField(field.key, $event)" />
|
|
193
|
-
<NcSelect
|
|
194
|
-
v-else-if="field.type === 'enum' && Array.isArray(field.options)"
|
|
195
|
-
:modelValue="selectedOption(field)"
|
|
196
|
-
:options="field.options"
|
|
197
|
-
:inputLabel="resolveLabel(field.label)"
|
|
198
|
-
@update:modelValue="updateField(field.key, optionValue($event))" />
|
|
199
|
-
<NcTextField
|
|
200
|
-
v-else
|
|
201
|
-
:label="resolveLabel(field.label)"
|
|
202
|
-
:modelValue="fieldValue(field.key, '')"
|
|
175
|
+
<CnSettingsField
|
|
176
|
+
:field="{ ...field, label: resolveLabel(field.label) }"
|
|
177
|
+
:modelValue="formData[field.key]"
|
|
203
178
|
@update:modelValue="updateField(field.key, $event)" />
|
|
204
179
|
</slot>
|
|
205
180
|
<small
|
|
@@ -297,14 +272,12 @@ import axios from '@nextcloud/axios'
|
|
|
297
272
|
import { translate as t } from '@nextcloud/l10n'
|
|
298
273
|
import {
|
|
299
274
|
NcButton,
|
|
300
|
-
NcCheckboxRadioSwitch,
|
|
301
275
|
NcLoadingIcon,
|
|
302
|
-
NcSelect,
|
|
303
|
-
NcTextField,
|
|
304
276
|
} from '@nextcloud/vue'
|
|
305
277
|
import ContentSave from 'vue-material-design-icons/ContentSave.vue'
|
|
306
278
|
import CnBuildiqEditButton from '../CnBuildiqEditButton/CnBuildiqEditButton.vue'
|
|
307
279
|
import CnRegisterMapping from '../CnRegisterMapping/CnRegisterMapping.vue'
|
|
280
|
+
import CnSettingsField from '../CnSettingsField/CnSettingsField.vue'
|
|
308
281
|
import CnToggleTableWidget from '../CnToggleTableWidget/CnToggleTableWidget.vue'
|
|
309
282
|
import CnVersionInfoCard from '../CnVersionInfoCard/CnVersionInfoCard.vue'
|
|
310
283
|
import { useLook } from '../../composables/useLook.js'
|
|
@@ -356,7 +329,7 @@ const BUILTIN_SETTINGS_WIDGETS = Object.freeze({
|
|
|
356
329
|
* three body kinds:
|
|
357
330
|
*
|
|
358
331
|
* - `fields[]` — flat form fields (back-compat default).
|
|
359
|
-
* Each field's `type` controls which input renders:
|
|
332
|
+
* Each field's `type` controls which input CnSettingsField renders:
|
|
360
333
|
* - `boolean` → NcCheckboxRadioSwitch
|
|
361
334
|
* - `number` → NcTextField (type=number)
|
|
362
335
|
* - `string` → NcTextField (default)
|
|
@@ -409,11 +382,9 @@ export default {
|
|
|
409
382
|
|
|
410
383
|
components: {
|
|
411
384
|
NcButton,
|
|
412
|
-
NcCheckboxRadioSwitch,
|
|
413
385
|
NcLoadingIcon,
|
|
414
|
-
NcSelect,
|
|
415
|
-
NcTextField,
|
|
416
386
|
ContentSave,
|
|
387
|
+
CnSettingsField,
|
|
417
388
|
CnBuildiqEditButton,
|
|
418
389
|
CnSettingsCard,
|
|
419
390
|
CnSettingsSection,
|
|
@@ -823,57 +794,6 @@ export default {
|
|
|
823
794
|
},
|
|
824
795
|
|
|
825
796
|
methods: {
|
|
826
|
-
// Coalesce a field value to a fallback when null / undefined.
|
|
827
|
-
// Used in template bindings instead of `formData[key] ?? ''` because
|
|
828
|
-
// vue-template-es2015-compiler (the Rollup VuePlugin's parser) does
|
|
829
|
-
// not accept ES2020 nullish-coalescing inside template expressions.
|
|
830
|
-
fieldValue(key, fallback) {
|
|
831
|
-
const v = this.formData[key]
|
|
832
|
-
return (v === null || v === undefined) ? fallback : v
|
|
833
|
-
},
|
|
834
|
-
|
|
835
|
-
// Resolve the currently-selected option for an enum field.
|
|
836
|
-
// `field.options` may be an array of strings/numbers OR an array
|
|
837
|
-
// of `{ label, value }`-shaped objects. NcSelect needs the actual
|
|
838
|
-
// option entry (not the bare value) to render the selection
|
|
839
|
-
// correctly, so this resolver looks up the matching object form
|
|
840
|
-
// when a primitive value is stored. Returns `null` when nothing
|
|
841
|
-
// is selected so NcSelect renders the empty state instead of
|
|
842
|
-
// emitting a Vue warning about an undefined `value` prop.
|
|
843
|
-
selectedOption(field) {
|
|
844
|
-
const v = this.formData[field.key]
|
|
845
|
-
if (v === null || v === undefined) {
|
|
846
|
-
return null
|
|
847
|
-
}
|
|
848
|
-
if (!Array.isArray(field.options)) {
|
|
849
|
-
return v
|
|
850
|
-
}
|
|
851
|
-
for (const opt of field.options) {
|
|
852
|
-
if (opt && typeof opt === 'object') {
|
|
853
|
-
if (opt.value === v) {
|
|
854
|
-
return opt
|
|
855
|
-
}
|
|
856
|
-
} else if (opt === v) {
|
|
857
|
-
return opt
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
|
-
return v
|
|
861
|
-
},
|
|
862
|
-
|
|
863
|
-
// Inverse of `selectedOption` — extract the storable value from
|
|
864
|
-
// whatever NcSelect emits. NcSelect emits the full option entry
|
|
865
|
-
// when options are objects, the primitive when options are
|
|
866
|
-
// primitives, or `null` on clear.
|
|
867
|
-
optionValue(emitted) {
|
|
868
|
-
if (emitted === null || emitted === undefined) {
|
|
869
|
-
return null
|
|
870
|
-
}
|
|
871
|
-
if (typeof emitted === 'object' && 'value' in emitted) {
|
|
872
|
-
return emitted.value
|
|
873
|
-
}
|
|
874
|
-
return emitted
|
|
875
|
-
},
|
|
876
|
-
|
|
877
797
|
cloneInitial() {
|
|
878
798
|
const merged = { ...(this.initialValues || {}) }
|
|
879
799
|
// Collect every section across both modes (flat
|
|
@@ -21,7 +21,24 @@
|
|
|
21
21
|
class="cn-user-settings-dialog__nav"
|
|
22
22
|
:aria-label="navLabel"
|
|
23
23
|
data-testid="cn-user-settings-nav">
|
|
24
|
-
|
|
24
|
+
<!-- `nextcloudLinks`: Nextcloud's own personal settings first, as
|
|
25
|
+
links, then one entry for this app holding its sections. -->
|
|
26
|
+
<ul v-if="ncLinks.length > 0" class="cn-user-settings-dialog__nc-links" data-testid="cn-user-settings-nc-links">
|
|
27
|
+
<li v-for="link in ncLinks" :key="link.key">
|
|
28
|
+
<a :href="link.href" class="cn-user-settings-dialog__nav-link">
|
|
29
|
+
<span v-if="link.icon" class="cn-user-settings-dialog__nav-icon" aria-hidden="true">
|
|
30
|
+
<CnIcon :name="link.icon" :size="18" />
|
|
31
|
+
</span>
|
|
32
|
+
<span>{{ link.label }}</span>
|
|
33
|
+
</a>
|
|
34
|
+
</li>
|
|
35
|
+
</ul>
|
|
36
|
+
<p v-if="ncLinks.length > 0 && appName"
|
|
37
|
+
class="cn-user-settings-dialog__group"
|
|
38
|
+
data-testid="cn-user-settings-app-group">
|
|
39
|
+
{{ appName }}
|
|
40
|
+
</p>
|
|
41
|
+
<ul :class="{ 'cn-user-settings-dialog__group-sections': ncLinks.length > 0 }">
|
|
25
42
|
<li v-for="section in sections" :key="section.id">
|
|
26
43
|
<a
|
|
27
44
|
:href="`#settings-section_${section.id}`"
|
|
@@ -41,6 +58,14 @@
|
|
|
41
58
|
<div class="cn-user-settings-dialog__sections">
|
|
42
59
|
<!-- @slot default NcAppSettingsSection children; each registers itself in the navigation. -->
|
|
43
60
|
<slot />
|
|
61
|
+
<!-- A section's `#icon` also stands before its heading. -->
|
|
62
|
+
<template v-for="section in sections" :key="`heading-icon-${section.id}-${section.name}`">
|
|
63
|
+
<Teleport v-if="section.icon && headingTargets.includes(section.id)" :to="`#settings-section_${section.id}--label`">
|
|
64
|
+
<span class="cn-user-settings-dialog__heading-icon" aria-hidden="true" :data-testid="`cn-user-settings-heading-icon-${section.id}`">
|
|
65
|
+
<CnVNodes :vnodes="section.icon" />
|
|
66
|
+
</span>
|
|
67
|
+
</Teleport>
|
|
68
|
+
</template>
|
|
44
69
|
</div>
|
|
45
70
|
</div>
|
|
46
71
|
<template #actions>
|
|
@@ -56,9 +81,11 @@
|
|
|
56
81
|
|
|
57
82
|
<script>
|
|
58
83
|
import { translate as t } from '@nextcloud/l10n'
|
|
84
|
+
import { generateUrl } from '@nextcloud/router'
|
|
59
85
|
import { NcButton } from '@nextcloud/vue'
|
|
60
|
-
import { markRaw } from 'vue'
|
|
86
|
+
import { cloneVNode, markRaw, ref } from 'vue'
|
|
61
87
|
import CnDialog from '../CnDialog/CnDialog.vue'
|
|
88
|
+
import CnIcon from '../CnIcon/CnIcon.vue'
|
|
62
89
|
|
|
63
90
|
/**
|
|
64
91
|
* The key NcAppSettingsSection injects to register itself with its dialog
|
|
@@ -66,20 +93,26 @@ import CnDialog from '../CnDialog/CnDialog.vue'
|
|
|
66
93
|
*/
|
|
67
94
|
const REGISTRATION_KEY = Symbol.for('NcAppSettingsDialog:registration')
|
|
68
95
|
|
|
96
|
+
/** The key NcAppSettingsSection injects for the legacy design flag. */
|
|
97
|
+
const LEGACY_DESIGN_KEY = Symbol.for('NcAppSettingsDialog:legacy')
|
|
98
|
+
|
|
69
99
|
/** Renders a section's icon vnodes as they were registered. */
|
|
70
100
|
const CnVNodes = {
|
|
71
101
|
name: 'CnVNodes',
|
|
72
102
|
props: { vnodes: { type: [Array, Object], default: null } },
|
|
73
103
|
render() {
|
|
74
|
-
|
|
104
|
+
// A section's icon is drawn twice (navigation and heading), so each draw gets its own copy.
|
|
105
|
+
const list = Array.isArray(this.vnodes) ? this.vnodes : [this.vnodes]
|
|
106
|
+
return list.map((vnode) => (vnode && typeof vnode === 'object' ? cloneVNode(vnode) : vnode))
|
|
75
107
|
},
|
|
76
108
|
}
|
|
77
109
|
|
|
78
110
|
/**
|
|
79
111
|
* CnUserSettingsDialog - the user settings dialog in the board look (board
|
|
80
112
|
* PqPersoonlijk): eyebrow, title and subtitle, a left navigation listing the
|
|
81
|
-
* sections with their icons, the
|
|
82
|
-
* note that changes save straight away and
|
|
113
|
+
* sections with their icons, the current section (the others stay mounted
|
|
114
|
+
* but hidden), and a footer with a note that changes save straight away and
|
|
115
|
+
* a Close button.
|
|
83
116
|
*
|
|
84
117
|
* The sections stay the app's: any `NcAppSettingsSection` in the default slot
|
|
85
118
|
* registers itself with this dialog exactly as it does with
|
|
@@ -93,7 +126,7 @@ const CnVNodes = {
|
|
|
93
126
|
export default {
|
|
94
127
|
name: 'CnUserSettingsDialog',
|
|
95
128
|
|
|
96
|
-
components: { CnDialog, CnVNodes, NcButton },
|
|
129
|
+
components: { CnDialog, CnIcon, CnVNodes, NcButton },
|
|
97
130
|
|
|
98
131
|
provide() {
|
|
99
132
|
return {
|
|
@@ -101,6 +134,9 @@ export default {
|
|
|
101
134
|
registerSection: this.registerSection,
|
|
102
135
|
unregisterSection: this.unregisterSection,
|
|
103
136
|
},
|
|
137
|
+
|
|
138
|
+
// NcAppSettingsSection also asks its dialog whether to use the legacy design.
|
|
139
|
+
[LEGACY_DESIGN_KEY]: ref(false),
|
|
104
140
|
}
|
|
105
141
|
},
|
|
106
142
|
|
|
@@ -113,8 +149,15 @@ export default {
|
|
|
113
149
|
eyebrow: { type: String, default: () => t('nextcloud-vue', 'Your account') },
|
|
114
150
|
/** Sentence under the title. */
|
|
115
151
|
subtitle: { type: String, default: () => t('nextcloud-vue', 'Only for you. Colleagues keep their own settings.') },
|
|
116
|
-
/** The app's name, after the footer note. */
|
|
152
|
+
/** The app's name, after the footer note and as the navigation entry holding its sections. */
|
|
117
153
|
appName: { type: String, default: '' },
|
|
154
|
+
/**
|
|
155
|
+
* Nextcloud's own personal settings, listed first in the navigation as
|
|
156
|
+
* links: `[{ label, section? | href?, icon? }]`. `section` is a
|
|
157
|
+
* personal settings section id (`/settings/user/<section>`). With any,
|
|
158
|
+
* the app's sections sit under one entry named after the app.
|
|
159
|
+
*/
|
|
160
|
+
nextcloudLinks: { type: Array, default: () => [] },
|
|
118
161
|
},
|
|
119
162
|
|
|
120
163
|
emits: ['update:open'],
|
|
@@ -123,11 +166,30 @@ export default {
|
|
|
123
166
|
return {
|
|
124
167
|
sections: [],
|
|
125
168
|
selected: '',
|
|
169
|
+
/** Ids of the sections whose heading is in the page, for the heading icons. */
|
|
170
|
+
headingTargets: [],
|
|
126
171
|
picked: false,
|
|
127
172
|
}
|
|
128
173
|
},
|
|
129
174
|
|
|
130
175
|
computed: {
|
|
176
|
+
/**
|
|
177
|
+
* The Nextcloud links with their URL.
|
|
178
|
+
*
|
|
179
|
+
* @spec openspec/changes/user-settings-nav-groups/specs/user-settings-board-dialog/spec.md#requirement-the-board-user-settings-navigation-can-list-nextclouds-own-sections-first
|
|
180
|
+
* @return {Array<{key: string, label: string, href: string, icon: string}>}
|
|
181
|
+
*/
|
|
182
|
+
ncLinks() {
|
|
183
|
+
return (Array.isArray(this.nextcloudLinks) ? this.nextcloudLinks : [])
|
|
184
|
+
.filter((link) => link && typeof link === 'object' && typeof link.label === 'string' && link.label !== '' && (link.href || link.section))
|
|
185
|
+
.map((link, index) => ({
|
|
186
|
+
key: String(link.section || link.href || index),
|
|
187
|
+
label: link.label,
|
|
188
|
+
href: link.href || generateUrl('/settings/user/{section}', { section: String(link.section) }),
|
|
189
|
+
icon: typeof link.icon === 'string' ? link.icon : '',
|
|
190
|
+
}))
|
|
191
|
+
},
|
|
192
|
+
|
|
131
193
|
/** @return {object} The board's eyebrow: muted, in sentence case. */
|
|
132
194
|
eyebrowStyle() {
|
|
133
195
|
return { '--cn-dialog-eyebrow-transform': 'none', '--cn-dialog-eyebrow-color': 'var(--color-text-maxcontrast)' }
|
|
@@ -150,6 +212,21 @@ export default {
|
|
|
150
212
|
},
|
|
151
213
|
},
|
|
152
214
|
|
|
215
|
+
watch: {
|
|
216
|
+
sections() {
|
|
217
|
+
this.$nextTick(() => {
|
|
218
|
+
this.headingTargets = typeof document === 'undefined'
|
|
219
|
+
? []
|
|
220
|
+
: this.sections.filter((section) => section.icon && document.getElementById(`settings-section_${section.id}--label`)).map((section) => section.id)
|
|
221
|
+
this.showSelected()
|
|
222
|
+
})
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
selected() {
|
|
226
|
+
this.$nextTick(() => this.showSelected())
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
|
|
153
230
|
methods: {
|
|
154
231
|
/**
|
|
155
232
|
* Add a section to the navigation, kept in `order`; a section
|
|
@@ -190,9 +267,31 @@ export default {
|
|
|
190
267
|
selectSection(id) {
|
|
191
268
|
this.picked = true
|
|
192
269
|
this.selected = id
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
270
|
+
this.$nextTick(() => {
|
|
271
|
+
this.showSelected()
|
|
272
|
+
const heading = typeof document !== 'undefined' ? document.getElementById(`settings-section_${id}--label`) : null
|
|
273
|
+
if (heading) {
|
|
274
|
+
heading.setAttribute('tabindex', '-1')
|
|
275
|
+
heading.focus()
|
|
276
|
+
}
|
|
277
|
+
})
|
|
278
|
+
},
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Show the current section and hide the others. They stay mounted,
|
|
282
|
+
* so what a reader typed in one survives a switch.
|
|
283
|
+
*
|
|
284
|
+
* @spec openspec/changes/user-settings-one-section/specs/user-settings-board-dialog/spec.md#requirement-the-board-user-settings-dialog-shows-one-section-at-a-time
|
|
285
|
+
*/
|
|
286
|
+
showSelected() {
|
|
287
|
+
if (typeof document === 'undefined') {
|
|
288
|
+
return
|
|
289
|
+
}
|
|
290
|
+
for (const section of this.sections) {
|
|
291
|
+
const el = document.getElementById(`settings-section_${section.id}`)
|
|
292
|
+
if (el) {
|
|
293
|
+
el.hidden = section.id !== this.selected
|
|
294
|
+
}
|
|
196
295
|
}
|
|
197
296
|
},
|
|
198
297
|
|
|
@@ -294,6 +393,9 @@ export default {
|
|
|
294
393
|
margin: 0 0 12px;
|
|
295
394
|
font-size: 17px;
|
|
296
395
|
font-weight: 700;
|
|
396
|
+
display: flex;
|
|
397
|
+
align-items: center;
|
|
398
|
+
gap: 10px;
|
|
297
399
|
}
|
|
298
400
|
|
|
299
401
|
.cn-user-settings-dialog__note {
|
|
@@ -302,4 +404,31 @@ export default {
|
|
|
302
404
|
font-size: 14px;
|
|
303
405
|
color: var(--color-text-maxcontrast);
|
|
304
406
|
}
|
|
407
|
+
|
|
408
|
+
.cn-user-settings-dialog__nc-links {
|
|
409
|
+
margin-bottom: 2px;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.cn-user-settings-dialog__group {
|
|
413
|
+
margin: 10px 0 2px;
|
|
414
|
+
padding: 0 12px;
|
|
415
|
+
font-size: 15px;
|
|
416
|
+
font-weight: 600;
|
|
417
|
+
color: var(--color-main-text);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
.cn-user-settings-dialog__group-sections {
|
|
421
|
+
padding-inline-start: 12px;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.cn-user-settings-dialog__heading-icon {
|
|
425
|
+
display: inline-flex;
|
|
426
|
+
order: -1;
|
|
427
|
+
color: var(--color-text-maxcontrast);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/* One section at a time: the others are mounted but hidden. */
|
|
431
|
+
.cn-user-settings-dialog__sections :deep(.app-settings-section[hidden]) {
|
|
432
|
+
display: none;
|
|
433
|
+
}
|
|
305
434
|
</style>
|
|
@@ -75,6 +75,8 @@ import '../CnTasksWidget/index.js'
|
|
|
75
75
|
import '../CnSectionsWidget/index.js'
|
|
76
76
|
import '../CnFactsWidget/index.js'
|
|
77
77
|
import '../CnDeviceFramesWidget/index.js'
|
|
78
|
+
import '../CnChecklistRunWidget/index.js'
|
|
79
|
+
import '../CnWorkingCalendarWidget/index.js'
|
|
78
80
|
import '../CnPartyActionsWidget/index.js'
|
|
79
81
|
import '../CnFieldListWidget/index.js'
|
|
80
82
|
import '../CnSettingsFormWidget/index.js'
|