@conduction/nextcloud-vue 2.62.0 → 2.64.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/CnAdminActionCard/CnAdminActionCard.vue.js +10 -0
- package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue.js.map +1 -0
- package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue2.js +189 -0
- package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue2.js.map +1 -0
- package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue3.js +72 -0
- package/dist/esm/components/CnAdminActionCard/CnAdminActionCard.vue3.js.map +1 -0
- package/dist/esm/components/CnAppNav/CnAppNav.vue2.js +425 -39
- package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
- package/dist/esm/components/CnAppNav/CnAppNav.vue3.js +119 -42
- package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +186 -9
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +65 -42
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js +61 -16
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue2.js.map +1 -1
- package/dist/esm/components/CnAuditTrailCard/CnAuditTrailCard.vue3.js.map +1 -1
- package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue2.js +20 -0
- package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue3.js +3 -2
- package/dist/esm/components/CnAuditTrailWidget/CnAuditTrailWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +33 -2
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js.map +1 -1
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js +53 -37
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +29 -0
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +152 -135
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue.js +10 -0
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue.js.map +1 -0
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue2.js +293 -0
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue2.js.map +1 -0
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue3.js +189 -0
- package/dist/esm/components/CnDataTable/CnColumnFilterPopover.vue3.js.map +1 -0
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +253 -9
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +126 -50
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +66 -14
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +23 -14
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +2 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
- package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue2.js +3 -1
- package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue2.js.map +1 -1
- package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue3.js +1 -1
- package/dist/esm/components/CnFieldHelper/CnFieldHelper.vue3.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +234 -9
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +96 -57
- package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js +81 -4
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js +22 -5
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +81 -2
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +7 -3
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/selfModeActions.js +3 -3
- package/dist/esm/components/CnIndexPage/selfModeActions.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +41 -2
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +8 -3
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue2.js +121 -12
- package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue2.js.map +1 -1
- package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue3.js +3 -1
- package/dist/esm/components/CnResourceSelect/CnResourceSelect.vue3.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js +326 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +112 -26
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js +63 -2
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js +96 -37
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStagesWidget/stagesModel.js +5 -2
- package/dist/esm/components/CnStagesWidget/stagesModel.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +52 -2
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js +6 -6
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +14 -2
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +2 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue.js +10 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue.js.map +1 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js +337 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js +102 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnTimelineWidget/dashboardRegistration.js +34 -0
- package/dist/esm/components/CnTimelineWidget/dashboardRegistration.js.map +1 -0
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js +131 -8
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue2.js.map +1 -1
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js +40 -38
- package/dist/esm/components/CnWalkthrough/CnWalkthrough.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +8 -6
- package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue2.js +73 -1
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue2.js.map +1 -1
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue3.js +14 -2
- package/dist/esm/components/CnWidgetWrapper/CnWidgetWrapper.vue3.js.map +1 -1
- package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue2.js +19 -0
- package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue3.js +1 -1
- package/dist/esm/components/CnWizardDialog/CnWizardDialog.vue3.js.map +1 -1
- package/dist/esm/composables/useDependencyCheck.js +65 -0
- package/dist/esm/composables/useDependencyCheck.js.map +1 -0
- package/dist/esm/composables/useWalkthrough.js +218 -6
- package/dist/esm/composables/useWalkthrough.js.map +1 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +45 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +45 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +780 -81
- package/dist/esm/store/useObjectStore.js +8 -0
- package/dist/esm/store/useObjectStore.js.map +1 -1
- package/dist/esm/utils/columnFilters.js +311 -0
- package/dist/esm/utils/columnFilters.js.map +1 -0
- package/dist/esm/utils/groupAutocomplete.js +123 -0
- package/dist/esm/utils/groupAutocomplete.js.map +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js +2 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/pickerOptions.js +198 -0
- package/dist/esm/utils/pickerOptions.js.map +1 -0
- package/dist/esm/utils/schema.js +83 -7
- package/dist/esm/utils/schema.js.map +1 -1
- package/dist/esm/utils/schemaRefSlug.js +19 -3
- package/dist/esm/utils/schemaRefSlug.js.map +1 -1
- package/dist/esm/utils/timelineEvents.js +174 -0
- package/dist/esm/utils/timelineEvents.js.map +1 -0
- package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/esm/utils/walkthroughSignals.js +88 -0
- package/dist/esm/utils/walkthroughSignals.js.map +1 -0
- package/dist/nextcloud-vue.cjs.js +13088 -7937
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +780 -81
- package/l10n/en.json +45 -0
- package/l10n/nl.json +45 -0
- package/package.json +1 -1
- package/src/components/CnAdminActionCard/CnAdminActionCard.vue +234 -0
- package/src/components/CnAdminActionCard/index.js +4 -0
- package/src/components/CnAppNav/CnAppNav.vue +568 -43
- package/src/components/CnAppRoot/CnAppRoot.md +4 -0
- package/src/components/CnAppRoot/CnAppRoot.vue +223 -16
- package/src/components/CnAuditTrailCard/CnAuditTrailCard.vue +61 -16
- package/src/components/CnAuditTrailWidget/CnAuditTrailWidget.vue +22 -1
- package/src/components/CnCellRenderer/CnCellRenderer.vue +33 -2
- package/src/components/CnChoiceCards/CnChoiceCards.vue +56 -31
- package/src/components/CnDashboardPage/CnDashboardPage.vue +45 -4
- package/src/components/CnDataTable/CnColumnFilterPopover.vue +524 -0
- package/src/components/CnDataTable/CnDataTable.md +3 -0
- package/src/components/CnDataTable/CnDataTable.vue +305 -5
- package/src/components/CnDetailPage/CnDetailPage.md +4 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +60 -1
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +2 -2
- package/src/components/CnFieldHelper/CnFieldHelper.vue +4 -2
- package/src/components/CnFormDialog/CnFormDialog.vue +290 -31
- package/src/components/CnHeaderWidget/CnHeaderWidget.vue +116 -6
- package/src/components/CnIndexPage/CnIndexPage.vue +87 -4
- package/src/components/CnIndexPage/selfModeActions.js +3 -3
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +46 -2
- package/src/components/CnResourceSelect/CnResourceSelect.vue +123 -12
- package/src/components/CnSetupWizard/CnSetupWizard.vue +392 -3
- package/src/components/CnStagesWidget/CnStagesWidget.vue +180 -2
- package/src/components/CnStagesWidget/stagesModel.js +5 -2
- package/src/components/CnStatWidget/CnStatWidget.vue +57 -3
- package/src/components/CnTabsWidget/CnTabsWidget.vue +15 -2
- package/src/components/CnTimelineWidget/CnTimelineWidget.vue +477 -0
- package/src/components/CnTimelineWidget/dashboardRegistration.js +32 -0
- package/src/components/CnTimelineWidget/index.js +4 -0
- package/src/components/CnWalkthrough/CnWalkthrough.vue +143 -10
- package/src/components/CnWidgetGrid/builtInWidgets.js +2 -0
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +1 -0
- package/src/components/CnWidgetWrapper/CnWidgetWrapper.md +4 -0
- package/src/components/CnWidgetWrapper/CnWidgetWrapper.vue +107 -1
- package/src/components/CnWizardDialog/CnWizardDialog.vue +20 -1
- package/src/components/index.js +2 -0
- package/src/composables/useDependencyCheck.js +61 -0
- package/src/composables/useWalkthrough.js +217 -5
- package/src/css/dashboard.css +49 -0
- package/src/css/detail-page.css +29 -4
- package/src/css/kpi-card.css +24 -1
- package/src/css/table.css +99 -6
- package/src/index.js +2 -0
- package/src/schemas/app-manifest-v2.schema.json +135 -3
- package/src/store/useObjectStore.js +8 -0
- package/src/utils/columnFilters.js +324 -0
- package/src/utils/groupAutocomplete.js +119 -0
- package/src/utils/libraryWidgetKeys.js +2 -0
- package/src/utils/pickerOptions.js +194 -0
- package/src/utils/schema.js +83 -7
- package/src/utils/schemaRefSlug.js +19 -3
- package/src/utils/timelineEvents.js +171 -0
- package/src/utils/validateManifestV2.compiled.js +2 -2
- package/src/utils/walkthroughSignals.js +94 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { getLanguage } from '@nextcloud/l10n';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Option lists for the language and time zone pickers, and the defaults a
|
|
5
|
+
* schema can ask for with `x-default`.
|
|
6
|
+
*
|
|
7
|
+
* Both pickers store a code (`nl`, `Europe/Amsterdam`) and show a label in
|
|
8
|
+
* the user's own language, so a person picks "Nederlands" and the record
|
|
9
|
+
* stores `nl`.
|
|
10
|
+
*
|
|
11
|
+
* @module utils/pickerOptions
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* ISO 639-1 language codes. `Intl` has no list of languages, so the codes
|
|
17
|
+
* live here; the labels come from `Intl.DisplayNames` at runtime.
|
|
18
|
+
*
|
|
19
|
+
* @type {string[]}
|
|
20
|
+
*/
|
|
21
|
+
const LANGUAGE_CODES = (''
|
|
22
|
+
+ 'aa ab af ak am an ar as av ay az ba be bg bi bm bn bo br bs '
|
|
23
|
+
+ 'ca ce ch co cr cs cu cv cy da de dv dz ee el en eo es et eu '
|
|
24
|
+
+ 'fa ff fi fj fo fr fy ga gd gl gn gu gv ha he hi ho hr ht hu '
|
|
25
|
+
+ 'hy hz ia id ie ig ii ik io is it iu ja jv ka kg ki kj kk kl '
|
|
26
|
+
+ 'km kn ko kr ks ku kv kw ky la lb lg li ln lo lt lu lv mg mh '
|
|
27
|
+
+ 'mi mk ml mn mr ms mt my na nb nd ne ng nl nn no nr nv ny oc '
|
|
28
|
+
+ 'oj om or os pa pi pl ps pt qu rm rn ro ru rw sa sc sd se sg '
|
|
29
|
+
+ 'si sk sl sm sn so sq sr ss st su sv sw ta te tg th ti tk tl '
|
|
30
|
+
+ 'tn to tr ts tt tw ty ug uk ur uz ve vi vo wa wo xh yi yo za '
|
|
31
|
+
+ 'zh zu'
|
|
32
|
+
).split(' ');
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A small fallback for runtimes without `Intl.supportedValuesOf`.
|
|
36
|
+
*
|
|
37
|
+
* @type {string[]}
|
|
38
|
+
*/
|
|
39
|
+
const FALLBACK_TIMEZONES = 'UTC Europe/Amsterdam Europe/Berlin Europe/Brussels Europe/London Europe/Paris America/New_York America/Chicago America/Los_Angeles Asia/Tokyo Australia/Sydney'.split(' ');
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The user's Nextcloud language as a BCP 47 tag (`en_GB` becomes `en-GB`).
|
|
43
|
+
*
|
|
44
|
+
* @return {string} The tag, `en` when Nextcloud reports nothing.
|
|
45
|
+
*/
|
|
46
|
+
function currentLanguageTag() {
|
|
47
|
+
let lang;
|
|
48
|
+
try {
|
|
49
|
+
lang = getLanguage();
|
|
50
|
+
} catch {
|
|
51
|
+
lang = '';
|
|
52
|
+
}
|
|
53
|
+
return String(lang || 'en').replace(/_/g, '-')
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The browser's time zone (IANA id).
|
|
58
|
+
*
|
|
59
|
+
* @return {string} The time zone, `UTC` when the runtime does not say.
|
|
60
|
+
*/
|
|
61
|
+
function currentTimezone() {
|
|
62
|
+
try {
|
|
63
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'
|
|
64
|
+
} catch {
|
|
65
|
+
return 'UTC'
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Cached option lists, keyed by display language. Stable identity matters:
|
|
71
|
+
* the form's select matches the chosen option by reference.
|
|
72
|
+
*
|
|
73
|
+
* @type {Map<string, {codes: string[], labels: object}>}
|
|
74
|
+
*/
|
|
75
|
+
const languageCache = new Map();
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The language picker's codes and labels, labelled in `displayLanguage`.
|
|
79
|
+
*
|
|
80
|
+
* @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md
|
|
81
|
+
* @param {string} [displayLanguage] BCP 47 tag the labels are written in; defaults to the user's language.
|
|
82
|
+
* @return {{codes: string[], labels: object}} Codes sorted by label, and `{ code: label }`.
|
|
83
|
+
*/
|
|
84
|
+
function languageOptions(displayLanguage) {
|
|
85
|
+
const locale = displayLanguage || currentLanguageTag();
|
|
86
|
+
if (languageCache.has(locale)) {
|
|
87
|
+
return languageCache.get(locale)
|
|
88
|
+
}
|
|
89
|
+
let names;
|
|
90
|
+
try {
|
|
91
|
+
names = new Intl.DisplayNames([locale, 'en'], { type: 'language' });
|
|
92
|
+
} catch {
|
|
93
|
+
names = null;
|
|
94
|
+
}
|
|
95
|
+
const labels = {};
|
|
96
|
+
for (const code of LANGUAGE_CODES) {
|
|
97
|
+
let label;
|
|
98
|
+
try {
|
|
99
|
+
label = (names && names.of(code)) || code;
|
|
100
|
+
} catch {
|
|
101
|
+
label = code;
|
|
102
|
+
}
|
|
103
|
+
// `Intl` lower-cases some names ("nederlands" is fine in a sentence,
|
|
104
|
+
// not in a list); capitalise the first letter for the list.
|
|
105
|
+
labels[code] = label.charAt(0).toLocaleUpperCase(locale) + label.slice(1);
|
|
106
|
+
}
|
|
107
|
+
const codes = [...LANGUAGE_CODES].sort((a, b) => labels[a].localeCompare(labels[b], locale));
|
|
108
|
+
const result = { codes, labels };
|
|
109
|
+
languageCache.set(locale, result);
|
|
110
|
+
return result
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** @type {{codes: string[], labels: object}|null} */
|
|
114
|
+
let timezoneCache = null;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* The time zone picker's codes and labels (`Europe/Amsterdam` shows as
|
|
118
|
+
* `Europe/Amsterdam`, underscores as spaces).
|
|
119
|
+
*
|
|
120
|
+
* @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md
|
|
121
|
+
* @return {{codes: string[], labels: object}} IANA ids, and `{ id: label }`.
|
|
122
|
+
*/
|
|
123
|
+
function timezoneOptions() {
|
|
124
|
+
if (timezoneCache) {
|
|
125
|
+
return timezoneCache
|
|
126
|
+
}
|
|
127
|
+
let codes;
|
|
128
|
+
try {
|
|
129
|
+
codes = typeof Intl.supportedValuesOf === 'function' ? Intl.supportedValuesOf('timeZone') : [];
|
|
130
|
+
} catch {
|
|
131
|
+
codes = [];
|
|
132
|
+
}
|
|
133
|
+
if (!codes.length) {
|
|
134
|
+
codes = FALLBACK_TIMEZONES;
|
|
135
|
+
}
|
|
136
|
+
if (!codes.includes('UTC')) {
|
|
137
|
+
codes = ['UTC', ...codes];
|
|
138
|
+
}
|
|
139
|
+
const labels = {};
|
|
140
|
+
for (const code of codes) {
|
|
141
|
+
labels[code] = code.replace(/_/g, ' ');
|
|
142
|
+
}
|
|
143
|
+
timezoneCache = { codes: [...codes], labels };
|
|
144
|
+
return timezoneCache
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Fit a language tag to the property's `pattern`, if it has one.
|
|
149
|
+
*
|
|
150
|
+
* A schema that stores ISO 639-1 (`^[a-z]{2}$`) cannot take `en-GB`, so the
|
|
151
|
+
* tag falls back to its primary subtag. Without a pattern the full tag stays.
|
|
152
|
+
*
|
|
153
|
+
* @param {string} tag A BCP 47 tag.
|
|
154
|
+
* @param {string|undefined} pattern The property's `pattern`.
|
|
155
|
+
* @return {string} The tag as the schema accepts it.
|
|
156
|
+
*/
|
|
157
|
+
function fitLanguageTag(tag, pattern) {
|
|
158
|
+
if (!pattern) {
|
|
159
|
+
return tag
|
|
160
|
+
}
|
|
161
|
+
let re;
|
|
162
|
+
try {
|
|
163
|
+
re = new RegExp(pattern);
|
|
164
|
+
} catch {
|
|
165
|
+
return tag
|
|
166
|
+
}
|
|
167
|
+
if (re.test(tag)) {
|
|
168
|
+
return tag
|
|
169
|
+
}
|
|
170
|
+
const primary = tag.split('-')[0].toLowerCase();
|
|
171
|
+
return re.test(primary) ? primary : tag
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Resolve an `x-default` token to the value a NEW object starts with.
|
|
176
|
+
*
|
|
177
|
+
* - `current-language`: the user's Nextcloud language (BCP 47, fitted to the
|
|
178
|
+
* property's `pattern`).
|
|
179
|
+
* - `current-timezone`: the browser's time zone.
|
|
180
|
+
*
|
|
181
|
+
* @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md
|
|
182
|
+
* @param {string|null} token The `x-default` value.
|
|
183
|
+
* @param {object} [field] The field descriptor (reads `validation.pattern`).
|
|
184
|
+
* @return {string|null} The value, or null for an unknown token.
|
|
185
|
+
*/
|
|
186
|
+
function resolveDefaultToken(token, field = {}) {
|
|
187
|
+
if (token === 'current-language') {
|
|
188
|
+
const pattern = field && field.validation ? field.validation.pattern : undefined;
|
|
189
|
+
return fitLanguageTag(currentLanguageTag(), pattern)
|
|
190
|
+
}
|
|
191
|
+
if (token === 'current-timezone') {
|
|
192
|
+
return currentTimezone()
|
|
193
|
+
}
|
|
194
|
+
return null
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export { LANGUAGE_CODES, currentLanguageTag, currentTimezone, languageOptions, resolveDefaultToken, timezoneOptions };
|
|
198
|
+
//# sourceMappingURL=pickerOptions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pickerOptions.js","sources":["../../../src/utils/pickerOptions.js"],"sourcesContent":["/**\n * Option lists for the language and time zone pickers, and the defaults a\n * schema can ask for with `x-default`.\n *\n * Both pickers store a code (`nl`, `Europe/Amsterdam`) and show a label in\n * the user's own language, so a person picks \"Nederlands\" and the record\n * stores `nl`.\n *\n * @module utils/pickerOptions\n */\n\nimport { getLanguage } from '@nextcloud/l10n'\n\n/**\n * ISO 639-1 language codes. `Intl` has no list of languages, so the codes\n * live here; the labels come from `Intl.DisplayNames` at runtime.\n *\n * @type {string[]}\n */\nexport const LANGUAGE_CODES = (''\n\t+ 'aa ab af ak am an ar as av ay az ba be bg bi bm bn bo br bs '\n\t+ 'ca ce ch co cr cs cu cv cy da de dv dz ee el en eo es et eu '\n\t+ 'fa ff fi fj fo fr fy ga gd gl gn gu gv ha he hi ho hr ht hu '\n\t+ 'hy hz ia id ie ig ii ik io is it iu ja jv ka kg ki kj kk kl '\n\t+ 'km kn ko kr ks ku kv kw ky la lb lg li ln lo lt lu lv mg mh '\n\t+ 'mi mk ml mn mr ms mt my na nb nd ne ng nl nn no nr nv ny oc '\n\t+ 'oj om or os pa pi pl ps pt qu rm rn ro ru rw sa sc sd se sg '\n\t+ 'si sk sl sm sn so sq sr ss st su sv sw ta te tg th ti tk tl '\n\t+ 'tn to tr ts tt tw ty ug uk ur uz ve vi vo wa wo xh yi yo za '\n\t+ 'zh zu'\n).split(' ')\n\n/**\n * A small fallback for runtimes without `Intl.supportedValuesOf`.\n *\n * @type {string[]}\n */\nconst FALLBACK_TIMEZONES = 'UTC Europe/Amsterdam Europe/Berlin Europe/Brussels Europe/London Europe/Paris America/New_York America/Chicago America/Los_Angeles Asia/Tokyo Australia/Sydney'.split(' ')\n\n/**\n * The user's Nextcloud language as a BCP 47 tag (`en_GB` becomes `en-GB`).\n *\n * @return {string} The tag, `en` when Nextcloud reports nothing.\n */\nexport function currentLanguageTag() {\n\tlet lang\n\ttry {\n\t\tlang = getLanguage()\n\t} catch {\n\t\tlang = ''\n\t}\n\treturn String(lang || 'en').replace(/_/g, '-')\n}\n\n/**\n * The browser's time zone (IANA id).\n *\n * @return {string} The time zone, `UTC` when the runtime does not say.\n */\nexport function currentTimezone() {\n\ttry {\n\t\treturn Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'\n\t} catch {\n\t\treturn 'UTC'\n\t}\n}\n\n/**\n * Cached option lists, keyed by display language. Stable identity matters:\n * the form's select matches the chosen option by reference.\n *\n * @type {Map<string, {codes: string[], labels: object}>}\n */\nconst languageCache = new Map()\n\n/**\n * The language picker's codes and labels, labelled in `displayLanguage`.\n *\n * @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md\n * @param {string} [displayLanguage] BCP 47 tag the labels are written in; defaults to the user's language.\n * @return {{codes: string[], labels: object}} Codes sorted by label, and `{ code: label }`.\n */\nexport function languageOptions(displayLanguage) {\n\tconst locale = displayLanguage || currentLanguageTag()\n\tif (languageCache.has(locale)) {\n\t\treturn languageCache.get(locale)\n\t}\n\tlet names\n\ttry {\n\t\tnames = new Intl.DisplayNames([locale, 'en'], { type: 'language' })\n\t} catch {\n\t\tnames = null\n\t}\n\tconst labels = {}\n\tfor (const code of LANGUAGE_CODES) {\n\t\tlet label\n\t\ttry {\n\t\t\tlabel = (names && names.of(code)) || code\n\t\t} catch {\n\t\t\tlabel = code\n\t\t}\n\t\t// `Intl` lower-cases some names (\"nederlands\" is fine in a sentence,\n\t\t// not in a list); capitalise the first letter for the list.\n\t\tlabels[code] = label.charAt(0).toLocaleUpperCase(locale) + label.slice(1)\n\t}\n\tconst codes = [...LANGUAGE_CODES].sort((a, b) => labels[a].localeCompare(labels[b], locale))\n\tconst result = { codes, labels }\n\tlanguageCache.set(locale, result)\n\treturn result\n}\n\n/** @type {{codes: string[], labels: object}|null} */\nlet timezoneCache = null\n\n/**\n * The time zone picker's codes and labels (`Europe/Amsterdam` shows as\n * `Europe/Amsterdam`, underscores as spaces).\n *\n * @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md\n * @return {{codes: string[], labels: object}} IANA ids, and `{ id: label }`.\n */\nexport function timezoneOptions() {\n\tif (timezoneCache) {\n\t\treturn timezoneCache\n\t}\n\tlet codes\n\ttry {\n\t\tcodes = typeof Intl.supportedValuesOf === 'function' ? Intl.supportedValuesOf('timeZone') : []\n\t} catch {\n\t\tcodes = []\n\t}\n\tif (!codes.length) {\n\t\tcodes = FALLBACK_TIMEZONES\n\t}\n\tif (!codes.includes('UTC')) {\n\t\tcodes = ['UTC', ...codes]\n\t}\n\tconst labels = {}\n\tfor (const code of codes) {\n\t\tlabels[code] = code.replace(/_/g, ' ')\n\t}\n\ttimezoneCache = { codes: [...codes], labels }\n\treturn timezoneCache\n}\n\n/**\n * Fit a language tag to the property's `pattern`, if it has one.\n *\n * A schema that stores ISO 639-1 (`^[a-z]{2}$`) cannot take `en-GB`, so the\n * tag falls back to its primary subtag. Without a pattern the full tag stays.\n *\n * @param {string} tag A BCP 47 tag.\n * @param {string|undefined} pattern The property's `pattern`.\n * @return {string} The tag as the schema accepts it.\n */\nfunction fitLanguageTag(tag, pattern) {\n\tif (!pattern) {\n\t\treturn tag\n\t}\n\tlet re\n\ttry {\n\t\tre = new RegExp(pattern)\n\t} catch {\n\t\treturn tag\n\t}\n\tif (re.test(tag)) {\n\t\treturn tag\n\t}\n\tconst primary = tag.split('-')[0].toLowerCase()\n\treturn re.test(primary) ? primary : tag\n}\n\n/**\n * Resolve an `x-default` token to the value a NEW object starts with.\n *\n * - `current-language`: the user's Nextcloud language (BCP 47, fitted to the\n * property's `pattern`).\n * - `current-timezone`: the browser's time zone.\n *\n * @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md\n * @param {string|null} token The `x-default` value.\n * @param {object} [field] The field descriptor (reads `validation.pattern`).\n * @return {string|null} The value, or null for an unknown token.\n */\nexport function resolveDefaultToken(token, field = {}) {\n\tif (token === 'current-language') {\n\t\tconst pattern = field && field.validation ? field.validation.pattern : undefined\n\t\treturn fitLanguageTag(currentLanguageTag(), pattern)\n\t}\n\tif (token === 'current-timezone') {\n\t\treturn currentTimezone()\n\t}\n\treturn null\n}\n"],"names":[],"mappings":"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"}
|
package/dist/esm/utils/schema.js
CHANGED
|
@@ -298,10 +298,13 @@ function truncateString(str, maxLength) {
|
|
|
298
298
|
* surface (CnFormDialog) resolves the reference to a searchable dropdown
|
|
299
299
|
* of the referenced objects (label = human name, value = UUID).
|
|
300
300
|
* 4. Nextcloud user reference: `referenceType: 'nextcloud-user'` (or
|
|
301
|
-
* `format: 'user'`/`'username'`) → `'user'`; an array of such
|
|
301
|
+
* `format: 'user'`/`'username'`/`'nc-user'`) → `'user'`; an array of such
|
|
302
302
|
* properties → `'user-multiselect'`. CnFormDialog resolves these to a
|
|
303
303
|
* searchable dropdown of real Nextcloud users (label = display name,
|
|
304
|
-
* value = UID).
|
|
304
|
+
* value = UID). A Nextcloud group (`referenceType: 'nextcloud-group'` or
|
|
305
|
+
* `format: 'nc-group'`) → `'group'`, an array of groups →
|
|
306
|
+
* `'group-multiselect'`. `format: 'language'` → `'language'` and
|
|
307
|
+
* `format: 'timezone'` → `'timezone'` (both searchable code lists).
|
|
305
308
|
* 5. Type-based: `boolean` → `'checkbox'`, `integer`/`number` → `'number'`,
|
|
306
309
|
* `array` + `items.enum` → `'multiselect'`, `array` → `'tags'`
|
|
307
310
|
* 6. Format-based: `date-time` → `'datetime'`, `date` → `'date'`,
|
|
@@ -369,9 +372,32 @@ function isUserProp(prop) {
|
|
|
369
372
|
return true
|
|
370
373
|
}
|
|
371
374
|
const format = prop.format || '';
|
|
372
|
-
return format === 'user' || format === 'username'
|
|
375
|
+
return format === 'user' || format === 'username' || format === 'nc-user'
|
|
373
376
|
}
|
|
374
377
|
|
|
378
|
+
/**
|
|
379
|
+
* Whether a (single-value) schema property represents a Nextcloud group.
|
|
380
|
+
*
|
|
381
|
+
* A property is a group field when it declares `referenceType: 'nextcloud-group'`
|
|
382
|
+
* or `format: 'nc-group'`. CnFormDialog renders it as a searchable dropdown of
|
|
383
|
+
* Nextcloud groups (label = display name, value = group id).
|
|
384
|
+
*
|
|
385
|
+
* @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md
|
|
386
|
+
* @param {object} prop A schema property definition (or `items` for an array).
|
|
387
|
+
* @return {boolean} True when the property marks a Nextcloud group.
|
|
388
|
+
*/
|
|
389
|
+
function isGroupProp(prop) {
|
|
390
|
+
if (!prop || typeof prop !== 'object') {
|
|
391
|
+
return false
|
|
392
|
+
}
|
|
393
|
+
return prop.referenceType === 'nextcloud-group' || prop.format === 'nc-group'
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md
|
|
398
|
+
* @param {object} prop The schema property definition.
|
|
399
|
+
* @return {string} The widget identifier (see the block above).
|
|
400
|
+
*/
|
|
375
401
|
function resolveWidget(prop) {
|
|
376
402
|
// Explicit widget hint takes priority
|
|
377
403
|
if (prop.widget) {
|
|
@@ -410,6 +436,24 @@ function resolveWidget(prop) {
|
|
|
410
436
|
return 'user-multiselect'
|
|
411
437
|
}
|
|
412
438
|
|
|
439
|
+
// Nextcloud group (`format: 'nc-group'` / `referenceType: 'nextcloud-group'`):
|
|
440
|
+
// a searchable dropdown of groups storing the group id.
|
|
441
|
+
if (isGroupProp(prop)) {
|
|
442
|
+
return 'group'
|
|
443
|
+
}
|
|
444
|
+
if (type === 'array' && isGroupProp(prop.items)) {
|
|
445
|
+
return 'group-multiselect'
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Language and time zone codes: a searchable list instead of a free-text
|
|
449
|
+
// box, so a person picks "Dutch" and the record stores `nl`.
|
|
450
|
+
if (type === 'string' && format === 'language') {
|
|
451
|
+
return 'language'
|
|
452
|
+
}
|
|
453
|
+
if (type === 'string' && format === 'timezone') {
|
|
454
|
+
return 'timezone'
|
|
455
|
+
}
|
|
456
|
+
|
|
413
457
|
// Boolean → switch/checkbox
|
|
414
458
|
if (type === 'boolean') {
|
|
415
459
|
return 'checkbox'
|
|
@@ -588,7 +632,8 @@ function isTenantProperty(key, prop) {
|
|
|
588
632
|
* @param {boolean} [options.includeReadOnly] Whether to include readOnly properties
|
|
589
633
|
* @param {boolean} [options.hideTenant] Drop properties that hold the record's tenant (see `isTenantProperty`). Off by default, so a detail page still shows the tenant; CnFormDialog turns it on because nobody should be asked for it. `overrides[key].hidden === false` keeps one visible.
|
|
590
634
|
* @param {(text: string) => string} [options.translate] Optional display-layer translation function applied to each field's `label` and `description`. Schema property titles/descriptions are authored in English as the canonical source; consumers pass their bound `t()` (via the injected `cnTranslate`) so the rendered field label follows the user's language. When omitted, label/description are the English source strings unchanged (pure, backward-compatible).
|
|
591
|
-
* @
|
|
635
|
+
* @spec openspec/changes/form-pickers-from-schema/specs/schema-utilities/spec.md
|
|
636
|
+
* @return {Array<{key: string, label: string, description: string, descriptionLong: string, type: string, format: string|null, widget: string, required: boolean, readOnly: boolean, default: unknown, enum: Array|null, enumLabels: object|null, items: object|null, referenceType: string|null, referenceSemanticType: string|null, referenceSemanticApp: string|null, reference: {schema: string|number, multiple: boolean, register?: string, labelField?: string}|null, allowCreate: boolean, userPicker: {multiple: boolean}|null, groupPicker: {multiple: boolean}|null, defaultToken: string|null, fillFrom: object|null, validation: object, order: number}>} `description` is the inline helper text (see `splitDescription`); `descriptionLong` carries the property's `x-help` text when it declares one, else the full description when it was too long to render inline, else ''. `enumLabels` maps each raw enum value to its English display label (from the property's `x-enum-labels`), or null.
|
|
592
637
|
*/
|
|
593
638
|
function fieldsFromSchema(schema, options = {}) {
|
|
594
639
|
const { exclude = [], include = null, overrides = {}, includeReadOnly = false, hideTenant = false, translate } = options;
|
|
@@ -664,11 +709,19 @@ function fieldsFromSchema(schema, options = {}) {
|
|
|
664
709
|
|
|
665
710
|
return entries.map(([key, prop]) => {
|
|
666
711
|
const description = splitDescription(prop.description ? tr(prop.description) : '');
|
|
712
|
+
// `x-help` is the long explanation behind the (i) popover. It always
|
|
713
|
+
// shows the popover, while `description` stays the short helper line.
|
|
714
|
+
const help = typeof prop['x-help'] === 'string' && prop['x-help'].trim() !== ''
|
|
715
|
+
? tr(prop['x-help'].trim())
|
|
716
|
+
: '';
|
|
717
|
+
const labelField = prop['x-label-field']
|
|
718
|
+
|| (prop.items && prop.items['x-label-field'])
|
|
719
|
+
|| null;
|
|
667
720
|
const field = {
|
|
668
721
|
key,
|
|
669
722
|
label: tr(prop.title || key),
|
|
670
723
|
description: description.short,
|
|
671
|
-
descriptionLong: description.long,
|
|
724
|
+
descriptionLong: help || description.long,
|
|
672
725
|
type: prop.type || 'string',
|
|
673
726
|
format: prop.format || null,
|
|
674
727
|
widget: resolveWidget(prop),
|
|
@@ -686,6 +739,10 @@ function fieldsFromSchema(schema, options = {}) {
|
|
|
686
739
|
required: requiredKeys.includes(key),
|
|
687
740
|
readOnly: prop.readOnly || false,
|
|
688
741
|
default: prop.default !== undefined ? prop.default : null,
|
|
742
|
+
// A default that depends on who opens the form (`x-default:
|
|
743
|
+
// 'current-language'` / `'current-timezone'`). Resolved by the
|
|
744
|
+
// consuming form on a NEW object only; never on edit.
|
|
745
|
+
defaultToken: typeof prop['x-default'] === 'string' ? prop['x-default'] : null,
|
|
689
746
|
enum: prop.enum || null,
|
|
690
747
|
// Display labels for the enum CODES, keyed by raw value. An enum
|
|
691
748
|
// value is a stored contract value, not display text — several are
|
|
@@ -733,11 +790,23 @@ function fieldsFromSchema(schema, options = {}) {
|
|
|
733
790
|
// form's own; absent, the reference stays same-register (the form's
|
|
734
791
|
// `register` prop is used). Consolidates the ad-hoc caseReference /
|
|
735
792
|
// approvalDecisionId / x-mirror-of variants onto one convention.
|
|
793
|
+
//
|
|
794
|
+
// `x-label-field` names the referenced object's property that labels
|
|
795
|
+
// an option and that an inline create writes the typed term to
|
|
796
|
+
// (`reference.labelField`, default `name` at the consumer).
|
|
736
797
|
reference: (normalizeRef(prop.$ref) !== null)
|
|
737
|
-
? { schema: normalizeRef(prop.$ref), multiple: false, ...(prop['x-external-register'] ? { register: prop['x-external-register'] } : {}) }
|
|
798
|
+
? { schema: normalizeRef(prop.$ref), multiple: false, ...(prop['x-external-register'] ? { register: prop['x-external-register'] } : {}), ...(labelField ? { labelField } : {}) }
|
|
738
799
|
: (prop.type === 'array' && prop.items && normalizeRef(prop.items.$ref) !== null)
|
|
739
|
-
? { schema: normalizeRef(prop.items.$ref), multiple: true, ...((prop.items['x-external-register'] || prop['x-external-register']) ? { register: prop.items['x-external-register'] || prop['x-external-register'] } : {}) }
|
|
800
|
+
? { schema: normalizeRef(prop.items.$ref), multiple: true, ...((prop.items['x-external-register'] || prop['x-external-register']) ? { register: prop.items['x-external-register'] || prop['x-external-register'] } : {}), ...(labelField ? { labelField } : {}) }
|
|
740
801
|
: null,
|
|
802
|
+
// Select OR create (`x-allow-create: true`, on the property or on
|
|
803
|
+
// its `items` for an array): the picker offers "Create" next to the
|
|
804
|
+
// existing objects. Only meaningful on a reference.
|
|
805
|
+
allowCreate: prop['x-allow-create'] === true
|
|
806
|
+
|| (prop.type === 'array' && !!prop.items && prop.items['x-allow-create'] === true),
|
|
807
|
+
// Template copy (`x-fill-from: { formKey: sourceKey }`): choosing a
|
|
808
|
+
// referenced object copies those of its values into this form.
|
|
809
|
+
fillFrom: (prop['x-fill-from'] && typeof prop['x-fill-from'] === 'object') ? prop['x-fill-from'] : null,
|
|
741
810
|
// Nextcloud user reference: when a property marks a NC user
|
|
742
811
|
// (`referenceType: 'nextcloud-user'`, or `format: 'user'`/
|
|
743
812
|
// `'username'`), tag it so CnFormDialog renders a searchable
|
|
@@ -750,6 +819,13 @@ function fieldsFromSchema(schema, options = {}) {
|
|
|
750
819
|
: (prop.type === 'array' && isUserProp(prop.items))
|
|
751
820
|
? { multiple: true }
|
|
752
821
|
: null,
|
|
822
|
+
// Nextcloud group reference (`format: 'nc-group'` or
|
|
823
|
+
// `referenceType: 'nextcloud-group'`): the form stores the group id.
|
|
824
|
+
groupPicker: isGroupProp(prop)
|
|
825
|
+
? { multiple: false }
|
|
826
|
+
: (prop.type === 'array' && isGroupProp(prop.items))
|
|
827
|
+
? { multiple: true }
|
|
828
|
+
: null,
|
|
753
829
|
// Conditional immutability (AD: x-openregister-readonly-when): a
|
|
754
830
|
// property can declare it becomes read-only when another field on the
|
|
755
831
|
// same object holds a given value — e.g. a hybrid app's identity
|