@conduction/nextcloud-vue 2.119.0 → 2.121.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.
Files changed (179) hide show
  1. package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue.js +9 -0
  2. package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue.js.map +1 -0
  3. package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue2.js +226 -0
  4. package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue2.js.map +1 -0
  5. package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue3.js +211 -0
  6. package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue3.js.map +1 -0
  7. package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue.js +9 -0
  8. package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue.js.map +1 -0
  9. package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue2.js +64 -0
  10. package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue2.js.map +1 -0
  11. package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue3.js +89 -0
  12. package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue3.js.map +1 -0
  13. package/dist/esm/components/CnIcon/semanticIcons.js +2 -2
  14. package/dist/esm/components/CnIcon/semanticIcons.js.map +1 -1
  15. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +53 -1
  16. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  17. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +546 -523
  18. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  19. package/dist/esm/components/CnIndexPage/useNamedSource.js +9 -0
  20. package/dist/esm/components/CnIndexPage/useNamedSource.js.map +1 -1
  21. package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue.js +9 -0
  22. package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue.js.map +1 -0
  23. package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue2.js +257 -0
  24. package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue2.js.map +1 -0
  25. package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue3.js +266 -0
  26. package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue3.js.map +1 -0
  27. package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue.js +1 -1
  28. package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue2.js +14 -7
  29. package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue2.js.map +1 -1
  30. package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue3.js.map +1 -1
  31. package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue.js +9 -0
  32. package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue.js.map +1 -0
  33. package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue2.js +278 -0
  34. package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue2.js.map +1 -0
  35. package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue3.js +128 -0
  36. package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue3.js.map +1 -0
  37. package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue.js +9 -0
  38. package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue.js.map +1 -0
  39. package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue2.js +214 -0
  40. package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue2.js.map +1 -0
  41. package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue3.js +125 -0
  42. package/dist/esm/components/CnReadinessChecklist/CnReadinessChecklist.vue3.js.map +1 -0
  43. package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue.js +9 -0
  44. package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue.js.map +1 -0
  45. package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue2.js +163 -0
  46. package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue2.js.map +1 -0
  47. package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue3.js +131 -0
  48. package/dist/esm/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue3.js.map +1 -0
  49. package/dist/esm/components/CnSchemaRulesWidget/schemaRules.js +127 -0
  50. package/dist/esm/components/CnSchemaRulesWidget/schemaRules.js.map +1 -0
  51. package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue.js +9 -0
  52. package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue.js.map +1 -0
  53. package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue2.js +82 -0
  54. package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue2.js.map +1 -0
  55. package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue3.js +61 -0
  56. package/dist/esm/components/CnSearchIndexStatus/CnSearchIndexStatus.vue3.js.map +1 -0
  57. package/dist/esm/components/CnSearchIndexStatus/searchIndexStatus.js +60 -0
  58. package/dist/esm/components/CnSearchIndexStatus/searchIndexStatus.js.map +1 -0
  59. package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue.js +10 -0
  60. package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue.js.map +1 -0
  61. package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue2.js +226 -0
  62. package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue2.js.map +1 -0
  63. package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue3.js +126 -0
  64. package/dist/esm/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue3.js.map +1 -0
  65. package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue.js +10 -0
  66. package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue.js.map +1 -0
  67. package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue2.js +944 -0
  68. package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue2.js.map +1 -0
  69. package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue3.js +276 -0
  70. package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue3.js.map +1 -0
  71. package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue.js +9 -0
  72. package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue.js.map +1 -0
  73. package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue2.js +354 -0
  74. package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue2.js.map +1 -0
  75. package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue3.js +392 -0
  76. package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue3.js.map +1 -0
  77. package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue.js +9 -0
  78. package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue.js.map +1 -0
  79. package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue2.js +180 -0
  80. package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue2.js.map +1 -0
  81. package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue3.js +208 -0
  82. package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue3.js.map +1 -0
  83. package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue.js +9 -0
  84. package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue.js.map +1 -0
  85. package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue2.js +244 -0
  86. package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue2.js.map +1 -0
  87. package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue3.js +76 -0
  88. package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue3.js.map +1 -0
  89. package/dist/esm/components/CnStateMachineEditor/stateMachineFields.js +290 -0
  90. package/dist/esm/components/CnStateMachineEditor/stateMachineFields.js.map +1 -0
  91. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue.js +9 -0
  92. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue.js.map +1 -0
  93. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue2.js +448 -0
  94. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue2.js.map +1 -0
  95. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue3.js +303 -0
  96. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue3.js.map +1 -0
  97. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue.js +1 -1
  98. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js +19 -2
  99. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js.map +1 -1
  100. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js +73 -62
  101. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js.map +1 -1
  102. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +6 -0
  103. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
  104. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +42 -32
  105. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  106. package/dist/esm/composables/indexSources.js +116 -16
  107. package/dist/esm/composables/indexSources.js.map +1 -1
  108. package/dist/esm/index.js +9 -0
  109. package/dist/esm/index.js.map +1 -1
  110. package/dist/esm/l10n/en.json.js +207 -1
  111. package/dist/esm/l10n/en.json.js.map +1 -1
  112. package/dist/esm/l10n/nl.json.js +207 -1
  113. package/dist/esm/l10n/nl.json.js.map +1 -1
  114. package/dist/esm/library-style.js +1 -1
  115. package/dist/esm/nextcloud-vue.css +951 -16
  116. package/dist/esm/utils/aiFeatures.js +245 -0
  117. package/dist/esm/utils/aiFeatures.js.map +1 -0
  118. package/dist/esm/utils/aiSuggestionTracker.js +182 -0
  119. package/dist/esm/utils/aiSuggestionTracker.js.map +1 -0
  120. package/dist/esm/utils/libraryWidgetKeys.js +2 -0
  121. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  122. package/dist/esm/utils/objectPermissions.js +552 -0
  123. package/dist/esm/utils/objectPermissions.js.map +1 -0
  124. package/dist/esm/utils/objectTranslations.js +180 -0
  125. package/dist/esm/utils/objectTranslations.js.map +1 -0
  126. package/dist/esm/utils/stateMachineValidation.js +254 -0
  127. package/dist/esm/utils/stateMachineValidation.js.map +1 -0
  128. package/dist/esm/utils/widgetDispatch.js +3 -1
  129. package/dist/esm/utils/widgetDispatch.js.map +1 -1
  130. package/dist/nextcloud-vue.cjs.js +23038 -14567
  131. package/dist/nextcloud-vue.cjs.js.map +2 -2
  132. package/dist/nextcloud-vue.css +951 -16
  133. package/l10n/en.json +207 -1
  134. package/l10n/nl.json +207 -1
  135. package/package.json +1 -1
  136. package/src/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue +416 -0
  137. package/src/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue +136 -0
  138. package/src/components/CnAiFeaturesWidget/__tests__/CnAiFeaturesWidget.spec.js +213 -0
  139. package/src/components/CnAiFeaturesWidget/index.js +14 -0
  140. package/src/components/CnIndexPage/CnIndexPage.vue +74 -3
  141. package/src/components/CnIndexPage/useNamedSource.js +9 -0
  142. package/src/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue +499 -0
  143. package/src/components/CnObjectPermissionsTab/__tests__/CnObjectPermissionsTab.spec.js +186 -0
  144. package/src/components/CnObjectPermissionsTab/index.js +13 -0
  145. package/src/components/CnObjectSidebar/CnObjectSidebar.vue +8 -1
  146. package/src/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue +424 -0
  147. package/src/components/CnObjectTranslationsWidget/__tests__/CnObjectTranslationsWidget.spec.js +189 -0
  148. package/src/components/CnObjectTranslationsWidget/index.js +13 -0
  149. package/src/components/CnReadinessChecklist/CnReadinessChecklist.vue +335 -0
  150. package/src/components/CnReadinessChecklist/index.js +9 -0
  151. package/src/components/CnSchemaRulesWidget/CnSchemaRulesWidget.vue +300 -0
  152. package/src/components/CnSchemaRulesWidget/index.js +10 -0
  153. package/src/components/CnSchemaRulesWidget/schemaRules.js +123 -0
  154. package/src/components/CnSearchIndexStatus/CnSearchIndexStatus.vue +160 -0
  155. package/src/components/CnSearchIndexStatus/index.js +10 -0
  156. package/src/components/CnSearchIndexStatus/searchIndexStatus.js +56 -0
  157. package/src/components/CnShippedConfigurationSection/CnShippedConfigurationSection.vue +328 -0
  158. package/src/components/CnShippedConfigurationSection/index.js +9 -0
  159. package/src/components/CnStateMachineEditor/CnStateMachineEditor.vue +1332 -0
  160. package/src/components/CnStateMachineEditor/CnStateMachineFields.vue +791 -0
  161. package/src/components/CnStateMachineEditor/CnStateMachineNode.vue +442 -0
  162. package/src/components/CnStateMachineEditor/CnStateMachinePanel.vue +326 -0
  163. package/src/components/CnStateMachineEditor/index.js +8 -0
  164. package/src/components/CnStateMachineEditor/stateMachineFields.js +287 -0
  165. package/src/components/CnTimelineWidget/CnTimelineManage.vue +827 -0
  166. package/src/components/CnTimelineWidget/CnTimelineWidget.vue +29 -3
  167. package/src/components/CnWidgetGrid/builtInWidgets.js +6 -0
  168. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +10 -0
  169. package/src/components/index.js +8 -0
  170. package/src/composables/indexSources.js +116 -16
  171. package/src/css/index-page.css +7 -0
  172. package/src/index.js +9 -0
  173. package/src/utils/aiFeatures.js +241 -0
  174. package/src/utils/aiSuggestionTracker.js +178 -0
  175. package/src/utils/libraryWidgetKeys.js +2 -0
  176. package/src/utils/objectPermissions.js +548 -0
  177. package/src/utils/objectTranslations.js +176 -0
  178. package/src/utils/stateMachineValidation.js +250 -0
  179. package/src/utils/widgetDispatch.js +3 -1
@@ -0,0 +1,278 @@
1
+ import { x as showSuccess, s as showError } from '../../node_modules/@nextcloud/dialogs/dist/chunks/index.js';
2
+ import { subscribe, unsubscribe } from '@nextcloud/event-bus';
3
+ import { translate, getLanguage } from '@nextcloud/l10n';
4
+ import { NcButton, NcLoadingIcon, NcTextField } from '@nextcloud/vue';
5
+ import { DEFAULT_API_BASE, completenessOf, STATUS_OUTDATED, registerLanguages, objectWithTranslations, schemaProperties, translationRows, saveTranslatedProperty } from '../../utils/objectTranslations.js';
6
+
7
+ const PAGE_REFRESH = 'cn:page:refresh';
8
+
9
+ let instanceCount = 0;
10
+
11
+ var script = {
12
+ name: 'CnObjectTranslationsWidget',
13
+
14
+ components: { NcButton, NcLoadingIcon, NcTextField },
15
+
16
+ props: {
17
+ /** Register slug or id (detail-context merge). */
18
+ register: { type: String, default: '' },
19
+ /** Schema slug, or the schema object the detail-context merge supplies. */
20
+ schema: { type: [String, Object], default: '' },
21
+ /** The object id (detail-context merge). Falls back to the route's `id`. */
22
+ objectId: { type: String, default: '' },
23
+ /** OpenRegister API root. */
24
+ apiBase: { type: String, default: DEFAULT_API_BASE },
25
+ /** Optional whitelist of properties to offer; empty offers every translatable one. */
26
+ properties: { type: Array, default: () => [] },
27
+ /** Text when nothing is translatable; empty uses the translated default. */
28
+ emptyText: { type: String, default: '' },
29
+ /** Stored widget content blob; its keys are read as the props above. */
30
+ content: { type: Object, default: () => ({}) },
31
+ },
32
+
33
+ data() {
34
+ instanceCount += 1;
35
+ return {
36
+ uid: `cn-object-translations-${instanceCount}`,
37
+ loading: true,
38
+ error: false,
39
+ saving: false,
40
+ languages: [],
41
+ sourceLanguage: '',
42
+ translatable: [],
43
+ titles: {},
44
+ values: {},
45
+ statuses: {},
46
+ drafts: {},
47
+ activeLanguage: '',
48
+ }
49
+ },
50
+
51
+ computed: {
52
+ target() {
53
+ return {
54
+ register: this.register || this.content.register || '',
55
+ schema: this.schema || this.content.schema || '',
56
+ apiBase: this.content.apiBase || this.apiBase,
57
+ }
58
+ },
59
+
60
+ resolvedId() {
61
+ return String(this.objectId || this.content.objectId || this.$route?.params?.id || '')
62
+ },
63
+
64
+ whitelist() {
65
+ const list = this.properties.length > 0 ? this.properties : (this.content.properties || []);
66
+ return Array.isArray(list) ? list.map(String) : []
67
+ },
68
+
69
+ /** The translatable properties OpenRegister reported, narrowed by the whitelist. */
70
+ propertiesShown() {
71
+ return this.whitelist.length === 0
72
+ ? this.translatable
73
+ : this.translatable.filter((property) => this.whitelist.includes(property))
74
+ },
75
+
76
+ panelId() {
77
+ return `${this.uid}-panel`
78
+ },
79
+
80
+ /**
81
+ * One chip per language the REGISTER declares (so a language nobody has
82
+ * started reads "0 of 5"), counting a stale value as missing.
83
+ *
84
+ * @return {Array<object>} The chips, source first.
85
+ */
86
+ chips() {
87
+ return this.languages.map((language) => {
88
+ const { translated, total } = completenessOf({
89
+ properties: this.propertiesShown,
90
+ values: this.values,
91
+ statuses: this.statuses,
92
+ language,
93
+ sourceLanguage: this.sourceLanguage,
94
+ });
95
+ return {
96
+ language,
97
+ name: this.nameOf(language),
98
+ // TRANSLATORS: how many of a record's labels exist in one language
99
+ count: translate('nextcloud-vue', '{translated} of {total}', { translated, total }),
100
+ complete: translated === total,
101
+ }
102
+ })
103
+ },
104
+
105
+ sourceNote() {
106
+ return translate('nextcloud-vue', '{language} is the source. Change a {language} label and its translations are marked out of date.', { language: this.nameOf(this.sourceLanguage) })
107
+ },
108
+
109
+ hasEdits() {
110
+ return this.propertiesShown.some((property) => this.draftOf(property) !== this.storedOf(property))
111
+ },
112
+ },
113
+
114
+ mounted() {
115
+ subscribe(PAGE_REFRESH, this.load);
116
+ this.load();
117
+ },
118
+
119
+ beforeUnmount() {
120
+ unsubscribe(PAGE_REFRESH, this.load);
121
+ },
122
+
123
+ methods: {
124
+ t: translate,
125
+
126
+ /**
127
+ * A language's name in the reader's own language, from the browser
128
+ * (keeps 38 language names out of the catalogue).
129
+ *
130
+ * @param {string} code The BCP 47 tag.
131
+ * @return {string} The name, or the tag.
132
+ */
133
+ nameOf(code) {
134
+ try {
135
+ return new Intl.DisplayNames([getLanguage()], { type: 'language' }).of(code) ?? code
136
+ } catch {
137
+ return code
138
+ }
139
+ },
140
+
141
+ tabId(language) {
142
+ return `${this.uid}-tab-${language}`
143
+ },
144
+
145
+ /**
146
+ * Arrow keys, Home and End move between the language tabs.
147
+ *
148
+ * @param {KeyboardEvent} event The key event.
149
+ */
150
+ onTabKeydown(event) {
151
+ const index = this.languages.indexOf(this.activeLanguage);
152
+ const last = this.languages.length - 1;
153
+ const next = {
154
+ ArrowRight: index >= last ? 0 : index + 1,
155
+ ArrowLeft: index <= 0 ? last : index - 1,
156
+ Home: 0,
157
+ End: last,
158
+ }[event.key];
159
+ if (next === undefined || last < 0) {
160
+ return
161
+ }
162
+ event.preventDefault();
163
+ this.activeLanguage = this.languages[next];
164
+ this.$nextTick(() => {
165
+ const el = document.getElementById(this.tabId(this.activeLanguage));
166
+ if (el && typeof el.focus === 'function') {
167
+ el.focus();
168
+ }
169
+ });
170
+ },
171
+
172
+ labelOf(property) {
173
+ return this.titles[property] ?? property
174
+ },
175
+
176
+ storedOf(property) {
177
+ return this.values?.[property]?.[this.activeLanguage] ?? ''
178
+ },
179
+
180
+ draftOf(property) {
181
+ const typed = this.drafts?.[this.activeLanguage]?.[property];
182
+ return typed === undefined ? this.storedOf(property) : typed
183
+ },
184
+
185
+ setDraft(property, value) {
186
+ const language = this.activeLanguage;
187
+ this.drafts = { ...this.drafts, [language]: { ...(this.drafts[language] ?? {}), [property]: value } };
188
+ },
189
+
190
+ isStale(property) {
191
+ return (this.statuses?.[property]?.[this.activeLanguage] ?? '') === STATUS_OUTDATED
192
+ },
193
+
194
+ /**
195
+ * Read the register languages, the object with every language, the
196
+ * property titles and the sidecar statuses.
197
+ *
198
+ * @return {Promise<void>}
199
+ */
200
+ async load() {
201
+ if (!this.resolvedId) {
202
+ this.loading = false;
203
+ this.error = true;
204
+ return
205
+ }
206
+
207
+ this.loading = true;
208
+ try {
209
+ const [languages, read, definitions] = await Promise.all([
210
+ registerLanguages(this.target),
211
+ objectWithTranslations(this.target, this.resolvedId),
212
+ schemaProperties(this.target),
213
+ ]);
214
+
215
+ const meta = read.languageMeta ?? {};
216
+ this.translatable = Object.keys(meta);
217
+ const first = this.propertiesShown[0] ?? this.translatable[0];
218
+ this.sourceLanguage = meta[first]?.sourceLanguage ?? languages[0] ?? 'nl';
219
+ this.languages = languages.length > 0 ? languages : [this.sourceLanguage];
220
+ this.activeLanguage = this.languages.find((code) => code !== this.sourceLanguage) ?? this.sourceLanguage;
221
+
222
+ this.values = Object.fromEntries(this.translatable.map((property) => {
223
+ const stored = read.object?.[property];
224
+ return [property, { ...(stored !== null && typeof stored === 'object' ? stored : {}) }]
225
+ }));
226
+ this.titles = Object.fromEntries(this.translatable.map((property) => [property, definitions[property]?.title ?? property]));
227
+
228
+ const uuid = String(read.object?.['@self']?.uuid ?? read.object?.id ?? this.resolvedId);
229
+ const statuses = {};
230
+ for (const row of await translationRows(this.target, uuid)) {
231
+ const property = String(row.property ?? '');
232
+ const language = String(row.language ?? '');
233
+ if (property === '' || language === '') {
234
+ continue
235
+ }
236
+ statuses[property] = { ...(statuses[property] ?? {}), [language]: row.status };
237
+ }
238
+ this.statuses = statuses;
239
+
240
+ this.drafts = {};
241
+ this.error = false;
242
+ } catch {
243
+ this.error = true;
244
+ } finally {
245
+ this.loading = false;
246
+ }
247
+ },
248
+
249
+ /**
250
+ * Write every edited property back, each as a whole language map.
251
+ *
252
+ * @return {Promise<void>}
253
+ */
254
+ async save() {
255
+ this.saving = true;
256
+ try {
257
+ for (const property of this.propertiesShown) {
258
+ const draft = this.draftOf(property);
259
+ if (draft === this.storedOf(property)) {
260
+ continue
261
+ }
262
+ const map = { ...(this.values[property] ?? {}), [this.activeLanguage]: draft };
263
+ await saveTranslatedProperty(this.target, this.resolvedId, property, map);
264
+ this.values = { ...this.values, [property]: map };
265
+ }
266
+ this.drafts = { ...this.drafts, [this.activeLanguage]: {} };
267
+ showSuccess(translate('nextcloud-vue', 'The labels are saved.'));
268
+ } catch {
269
+ showError(translate('nextcloud-vue', 'The labels could not be saved.'));
270
+ } finally {
271
+ this.saving = false;
272
+ }
273
+ },
274
+ },
275
+ };
276
+
277
+ export { script as default };
278
+ //# sourceMappingURL=CnObjectTranslationsWidget.vue2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnObjectTranslationsWidget.vue2.js","sources":["../../../../src/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue"],"sourcesContent":["<!--\n SPDX-FileCopyrightText: 2026 Conduction B.V.\n SPDX-License-Identifier: EUPL-1.2\n\n CnObjectTranslationsWidget — an object's translatable properties in every\n language its register serves: one completeness chip per language (a stale\n value counts as missing), a language tab strip, the source language read-only,\n a stale mark per field, and one save that writes each edited property as a\n whole language map. It translates nothing itself.\n-->\n<template>\n\t<div class=\"cn-object-translations\" data-testid=\"cn-object-translations\">\n\t\t<NcLoadingIcon v-if=\"loading\" :size=\"24\" />\n\n\t\t<p v-else-if=\"error\" class=\"cn-object-translations__empty\" role=\"alert\">\n\t\t\t{{ t('nextcloud-vue', 'The translations could not be read. Reload the page to try again.') }}\n\t\t</p>\n\n\t\t<p v-else-if=\"propertiesShown.length === 0\"\n\t\t\tclass=\"cn-object-translations__empty\"\n\t\t\tdata-testid=\"cn-object-translations-none\">\n\t\t\t{{ emptyText || t('nextcloud-vue', 'This item has no labels that can be translated.') }}\n\t\t</p>\n\n\t\t<template v-else>\n\t\t\t<ul class=\"cn-object-translations__chips\" data-testid=\"cn-object-translations-chips\">\n\t\t\t\t<li v-for=\"chip in chips\"\n\t\t\t\t\t:key=\"chip.language\"\n\t\t\t\t\tclass=\"cn-object-translations__chip cn-status-badge cn-status-badge--default\"\n\t\t\t\t\t:class=\"{ 'cn-object-translations__chip--complete': chip.complete }\"\n\t\t\t\t\t:data-language=\"chip.language\">\n\t\t\t\t\t<span class=\"cn-object-translations__chip-name\">{{ chip.name }}</span>\n\t\t\t\t\t<span class=\"cn-object-translations__chip-count\">{{ chip.count }}</span>\n\t\t\t\t</li>\n\t\t\t</ul>\n\n\t\t\t<p class=\"cn-object-translations__source\">\n\t\t\t\t{{ sourceNote }}\n\t\t\t</p>\n\n\t\t\t<div class=\"cn-object-translations__tabs\"\n\t\t\t\trole=\"tablist\"\n\t\t\t\t:aria-label=\"t('nextcloud-vue', 'Languages')\"\n\t\t\t\t@keydown=\"onTabKeydown\">\n\t\t\t\t<NcButton v-for=\"language in languages\"\n\t\t\t\t\t:id=\"tabId(language)\"\n\t\t\t\t\t:key=\"language\"\n\t\t\t\t\t:variant=\"language === activeLanguage ? 'primary' : 'tertiary'\"\n\t\t\t\t\trole=\"tab\"\n\t\t\t\t\t:aria-selected=\"String(language === activeLanguage)\"\n\t\t\t\t\t:aria-controls=\"panelId\"\n\t\t\t\t\t:tabindex=\"language === activeLanguage ? 0 : -1\"\n\t\t\t\t\t:data-language=\"language\"\n\t\t\t\t\t@click=\"activeLanguage = language\">\n\t\t\t\t\t{{ nameOf(language) }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\n\t\t\t<div :id=\"panelId\"\n\t\t\t\trole=\"tabpanel\"\n\t\t\t\tclass=\"cn-object-translations__panel\"\n\t\t\t\t:aria-labelledby=\"tabId(activeLanguage)\">\n\t\t\t\t<div v-for=\"property in propertiesShown\"\n\t\t\t\t\t:key=\"property\"\n\t\t\t\t\tclass=\"cn-object-translations__field\"\n\t\t\t\t\t:data-property=\"property\">\n\t\t\t\t\t<NcTextField :label=\"labelOf(property)\"\n\t\t\t\t\t\t:modelValue=\"draftOf(property)\"\n\t\t\t\t\t\t:disabled=\"activeLanguage === sourceLanguage\"\n\t\t\t\t\t\t@update:modelValue=\"setDraft(property, $event)\" />\n\n\t\t\t\t\t<span v-if=\"isStale(property)\"\n\t\t\t\t\t\tclass=\"cn-object-translations__stale\"\n\t\t\t\t\t\t:data-property=\"property\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Out of date') }}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\n\t\t\t\t<NcButton variant=\"primary\"\n\t\t\t\t\t:disabled=\"saving || !hasEdits\"\n\t\t\t\t\tdata-testid=\"cn-object-translations-save\"\n\t\t\t\t\t@click=\"save\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Save') }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { showError, showSuccess } from '@nextcloud/dialogs'\nimport { subscribe, unsubscribe } from '@nextcloud/event-bus'\nimport { getLanguage, translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcLoadingIcon, NcTextField } from '@nextcloud/vue'\nimport {\n\tcompletenessOf,\n\tDEFAULT_API_BASE,\n\tobjectWithTranslations,\n\tregisterLanguages,\n\tsaveTranslatedProperty,\n\tschemaProperties,\n\tSTATUS_OUTDATED,\n\ttranslationRows,\n} from '../../utils/objectTranslations.js'\n\nconst PAGE_REFRESH = 'cn:page:refresh'\n\nlet instanceCount = 0\n\nexport default {\n\tname: 'CnObjectTranslationsWidget',\n\n\tcomponents: { NcButton, NcLoadingIcon, NcTextField },\n\n\tprops: {\n\t\t/** Register slug or id (detail-context merge). */\n\t\tregister: { type: String, default: '' },\n\t\t/** Schema slug, or the schema object the detail-context merge supplies. */\n\t\tschema: { type: [String, Object], default: '' },\n\t\t/** The object id (detail-context merge). Falls back to the route's `id`. */\n\t\tobjectId: { type: String, default: '' },\n\t\t/** OpenRegister API root. */\n\t\tapiBase: { type: String, default: DEFAULT_API_BASE },\n\t\t/** Optional whitelist of properties to offer; empty offers every translatable one. */\n\t\tproperties: { type: Array, default: () => [] },\n\t\t/** Text when nothing is translatable; empty uses the translated default. */\n\t\temptyText: { type: String, default: '' },\n\t\t/** Stored widget content blob; its keys are read as the props above. */\n\t\tcontent: { type: Object, default: () => ({}) },\n\t},\n\n\tdata() {\n\t\tinstanceCount += 1\n\t\treturn {\n\t\t\tuid: `cn-object-translations-${instanceCount}`,\n\t\t\tloading: true,\n\t\t\terror: false,\n\t\t\tsaving: false,\n\t\t\tlanguages: [],\n\t\t\tsourceLanguage: '',\n\t\t\ttranslatable: [],\n\t\t\ttitles: {},\n\t\t\tvalues: {},\n\t\t\tstatuses: {},\n\t\t\tdrafts: {},\n\t\t\tactiveLanguage: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\ttarget() {\n\t\t\treturn {\n\t\t\t\tregister: this.register || this.content.register || '',\n\t\t\t\tschema: this.schema || this.content.schema || '',\n\t\t\t\tapiBase: this.content.apiBase || this.apiBase,\n\t\t\t}\n\t\t},\n\n\t\tresolvedId() {\n\t\t\treturn String(this.objectId || this.content.objectId || this.$route?.params?.id || '')\n\t\t},\n\n\t\twhitelist() {\n\t\t\tconst list = this.properties.length > 0 ? this.properties : (this.content.properties || [])\n\t\t\treturn Array.isArray(list) ? list.map(String) : []\n\t\t},\n\n\t\t/** The translatable properties OpenRegister reported, narrowed by the whitelist. */\n\t\tpropertiesShown() {\n\t\t\treturn this.whitelist.length === 0\n\t\t\t\t? this.translatable\n\t\t\t\t: this.translatable.filter((property) => this.whitelist.includes(property))\n\t\t},\n\n\t\tpanelId() {\n\t\t\treturn `${this.uid}-panel`\n\t\t},\n\n\t\t/**\n\t\t * One chip per language the REGISTER declares (so a language nobody has\n\t\t * started reads \"0 of 5\"), counting a stale value as missing.\n\t\t *\n\t\t * @return {Array<object>} The chips, source first.\n\t\t */\n\t\tchips() {\n\t\t\treturn this.languages.map((language) => {\n\t\t\t\tconst { translated, total } = completenessOf({\n\t\t\t\t\tproperties: this.propertiesShown,\n\t\t\t\t\tvalues: this.values,\n\t\t\t\t\tstatuses: this.statuses,\n\t\t\t\t\tlanguage,\n\t\t\t\t\tsourceLanguage: this.sourceLanguage,\n\t\t\t\t})\n\t\t\t\treturn {\n\t\t\t\t\tlanguage,\n\t\t\t\t\tname: this.nameOf(language),\n\t\t\t\t\t// TRANSLATORS: how many of a record's labels exist in one language\n\t\t\t\t\tcount: t('nextcloud-vue', '{translated} of {total}', { translated, total }),\n\t\t\t\t\tcomplete: translated === total,\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\tsourceNote() {\n\t\t\treturn t('nextcloud-vue', '{language} is the source. Change a {language} label and its translations are marked out of date.', { language: this.nameOf(this.sourceLanguage) })\n\t\t},\n\n\t\thasEdits() {\n\t\t\treturn this.propertiesShown.some((property) => this.draftOf(property) !== this.storedOf(property))\n\t\t},\n\t},\n\n\tmounted() {\n\t\tsubscribe(PAGE_REFRESH, this.load)\n\t\tthis.load()\n\t},\n\n\tbeforeUnmount() {\n\t\tunsubscribe(PAGE_REFRESH, this.load)\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * A language's name in the reader's own language, from the browser\n\t\t * (keeps 38 language names out of the catalogue).\n\t\t *\n\t\t * @param {string} code The BCP 47 tag.\n\t\t * @return {string} The name, or the tag.\n\t\t */\n\t\tnameOf(code) {\n\t\t\ttry {\n\t\t\t\treturn new Intl.DisplayNames([getLanguage()], { type: 'language' }).of(code) ?? code\n\t\t\t} catch {\n\t\t\t\treturn code\n\t\t\t}\n\t\t},\n\n\t\ttabId(language) {\n\t\t\treturn `${this.uid}-tab-${language}`\n\t\t},\n\n\t\t/**\n\t\t * Arrow keys, Home and End move between the language tabs.\n\t\t *\n\t\t * @param {KeyboardEvent} event The key event.\n\t\t */\n\t\tonTabKeydown(event) {\n\t\t\tconst index = this.languages.indexOf(this.activeLanguage)\n\t\t\tconst last = this.languages.length - 1\n\t\t\tconst next = {\n\t\t\t\tArrowRight: index >= last ? 0 : index + 1,\n\t\t\t\tArrowLeft: index <= 0 ? last : index - 1,\n\t\t\t\tHome: 0,\n\t\t\t\tEnd: last,\n\t\t\t}[event.key]\n\t\t\tif (next === undefined || last < 0) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tevent.preventDefault()\n\t\t\tthis.activeLanguage = this.languages[next]\n\t\t\tthis.$nextTick(() => {\n\t\t\t\tconst el = document.getElementById(this.tabId(this.activeLanguage))\n\t\t\t\tif (el && typeof el.focus === 'function') {\n\t\t\t\t\tel.focus()\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\tlabelOf(property) {\n\t\t\treturn this.titles[property] ?? property\n\t\t},\n\n\t\tstoredOf(property) {\n\t\t\treturn this.values?.[property]?.[this.activeLanguage] ?? ''\n\t\t},\n\n\t\tdraftOf(property) {\n\t\t\tconst typed = this.drafts?.[this.activeLanguage]?.[property]\n\t\t\treturn typed === undefined ? this.storedOf(property) : typed\n\t\t},\n\n\t\tsetDraft(property, value) {\n\t\t\tconst language = this.activeLanguage\n\t\t\tthis.drafts = { ...this.drafts, [language]: { ...(this.drafts[language] ?? {}), [property]: value } }\n\t\t},\n\n\t\tisStale(property) {\n\t\t\treturn (this.statuses?.[property]?.[this.activeLanguage] ?? '') === STATUS_OUTDATED\n\t\t},\n\n\t\t/**\n\t\t * Read the register languages, the object with every language, the\n\t\t * property titles and the sidecar statuses.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync load() {\n\t\t\tif (!this.resolvedId) {\n\t\t\t\tthis.loading = false\n\t\t\t\tthis.error = true\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tthis.loading = true\n\t\t\ttry {\n\t\t\t\tconst [languages, read, definitions] = await Promise.all([\n\t\t\t\t\tregisterLanguages(this.target),\n\t\t\t\t\tobjectWithTranslations(this.target, this.resolvedId),\n\t\t\t\t\tschemaProperties(this.target),\n\t\t\t\t])\n\n\t\t\t\tconst meta = read.languageMeta ?? {}\n\t\t\t\tthis.translatable = Object.keys(meta)\n\t\t\t\tconst first = this.propertiesShown[0] ?? this.translatable[0]\n\t\t\t\tthis.sourceLanguage = meta[first]?.sourceLanguage ?? languages[0] ?? 'nl'\n\t\t\t\tthis.languages = languages.length > 0 ? languages : [this.sourceLanguage]\n\t\t\t\tthis.activeLanguage = this.languages.find((code) => code !== this.sourceLanguage) ?? this.sourceLanguage\n\n\t\t\t\tthis.values = Object.fromEntries(this.translatable.map((property) => {\n\t\t\t\t\tconst stored = read.object?.[property]\n\t\t\t\t\treturn [property, { ...(stored !== null && typeof stored === 'object' ? stored : {}) }]\n\t\t\t\t}))\n\t\t\t\tthis.titles = Object.fromEntries(this.translatable.map((property) => [property, definitions[property]?.title ?? property]))\n\n\t\t\t\tconst uuid = String(read.object?.['@self']?.uuid ?? read.object?.id ?? this.resolvedId)\n\t\t\t\tconst statuses = {}\n\t\t\t\tfor (const row of await translationRows(this.target, uuid)) {\n\t\t\t\t\tconst property = String(row.property ?? '')\n\t\t\t\t\tconst language = String(row.language ?? '')\n\t\t\t\t\tif (property === '' || language === '') {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tstatuses[property] = { ...(statuses[property] ?? {}), [language]: row.status }\n\t\t\t\t}\n\t\t\t\tthis.statuses = statuses\n\n\t\t\t\tthis.drafts = {}\n\t\t\t\tthis.error = false\n\t\t\t} catch {\n\t\t\t\tthis.error = true\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Write every edited property back, each as a whole language map.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync save() {\n\t\t\tthis.saving = true\n\t\t\ttry {\n\t\t\t\tfor (const property of this.propertiesShown) {\n\t\t\t\t\tconst draft = this.draftOf(property)\n\t\t\t\t\tif (draft === this.storedOf(property)) {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tconst map = { ...(this.values[property] ?? {}), [this.activeLanguage]: draft }\n\t\t\t\t\tawait saveTranslatedProperty(this.target, this.resolvedId, property, map)\n\t\t\t\t\tthis.values = { ...this.values, [property]: map }\n\t\t\t\t}\n\t\t\t\tthis.drafts = { ...this.drafts, [this.activeLanguage]: {} }\n\t\t\t\tshowSuccess(t('nextcloud-vue', 'The labels are saved.'))\n\t\t\t} catch {\n\t\t\t\tshowError(t('nextcloud-vue', 'The labels could not be saved.'))\n\t\t\t} finally {\n\t\t\t\tthis.saving = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-object-translations__chips {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 8px;\n\tlist-style: none;\n\tmargin: 0 0 8px;\n\tpadding: 0;\n}\n\n.cn-object-translations__chip {\n\talign-items: center;\n\tbackground: var(--color-background-dark);\n\tdisplay: flex;\n\tgap: 6px;\n}\n\n.cn-object-translations__chip--complete {\n\tbackground: var(--color-success, var(--color-background-dark));\n\t--cn-status-badge-ink: var(--color-primary-text);\n\tcolor: var(--cn-status-badge-ink);\n}\n\n.cn-object-translations__source,\n.cn-object-translations__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tmargin: 0 0 8px;\n}\n\n.cn-object-translations__tabs {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 4px;\n\tmargin-bottom: 8px;\n}\n\n.cn-object-translations__field {\n\talign-items: center;\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 8px;\n}\n\n.cn-object-translations__stale {\n\tcolor: var(--color-warning-text, var(--color-main-text));\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["t"],"mappings":"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"}
@@ -0,0 +1,128 @@
1
+ import { resolveComponent, openBlock, createElementBlock, createBlock, toDisplayString, Fragment, createElementVNode, renderList, normalizeClass, withCtx, createTextVNode, createVNode, createCommentVNode } from 'vue';
2
+
3
+ const _hoisted_1 = {
4
+ class: "cn-object-translations",
5
+ "data-testid": "cn-object-translations"
6
+ };
7
+ const _hoisted_2 = {
8
+ key: 1,
9
+ class: "cn-object-translations__empty",
10
+ role: "alert"
11
+ };
12
+ const _hoisted_3 = {
13
+ key: 2,
14
+ class: "cn-object-translations__empty",
15
+ "data-testid": "cn-object-translations-none"
16
+ };
17
+ const _hoisted_4 = {
18
+ class: "cn-object-translations__chips",
19
+ "data-testid": "cn-object-translations-chips"
20
+ };
21
+ const _hoisted_5 = ["data-language"];
22
+ const _hoisted_6 = { class: "cn-object-translations__chip-name" };
23
+ const _hoisted_7 = { class: "cn-object-translations__chip-count" };
24
+ const _hoisted_8 = { class: "cn-object-translations__source" };
25
+ const _hoisted_9 = ["aria-label"];
26
+ const _hoisted_10 = ["id", "aria-labelledby"];
27
+ const _hoisted_11 = ["data-property"];
28
+ const _hoisted_12 = ["data-property"];
29
+
30
+ function render(_ctx, _cache, $props, $setup, $data, $options) {
31
+ const _component_NcLoadingIcon = resolveComponent("NcLoadingIcon");
32
+ const _component_NcButton = resolveComponent("NcButton");
33
+ const _component_NcTextField = resolveComponent("NcTextField");
34
+
35
+ return (openBlock(), createElementBlock("div", _hoisted_1, [
36
+ ($data.loading)
37
+ ? (openBlock(), createBlock(_component_NcLoadingIcon, {
38
+ key: 0,
39
+ size: 24
40
+ }))
41
+ : ($data.error)
42
+ ? (openBlock(), createElementBlock("p", _hoisted_2, toDisplayString($options.t('nextcloud-vue', 'The translations could not be read. Reload the page to try again.')), 1 /* TEXT */))
43
+ : ($options.propertiesShown.length === 0)
44
+ ? (openBlock(), createElementBlock("p", _hoisted_3, toDisplayString($props.emptyText || $options.t('nextcloud-vue', 'This item has no labels that can be translated.')), 1 /* TEXT */))
45
+ : (openBlock(), createElementBlock(Fragment, { key: 3 }, [
46
+ createElementVNode("ul", _hoisted_4, [
47
+ (openBlock(true), createElementBlock(Fragment, null, renderList($options.chips, (chip) => {
48
+ return (openBlock(), createElementBlock("li", {
49
+ key: chip.language,
50
+ class: normalizeClass(["cn-object-translations__chip cn-status-badge cn-status-badge--default", { 'cn-object-translations__chip--complete': chip.complete }]),
51
+ "data-language": chip.language
52
+ }, [
53
+ createElementVNode("span", _hoisted_6, toDisplayString(chip.name), 1 /* TEXT */),
54
+ createElementVNode("span", _hoisted_7, toDisplayString(chip.count), 1 /* TEXT */)
55
+ ], 10 /* CLASS, PROPS */, _hoisted_5))
56
+ }), 128 /* KEYED_FRAGMENT */))
57
+ ]),
58
+ createElementVNode("p", _hoisted_8, toDisplayString($options.sourceNote), 1 /* TEXT */),
59
+ createElementVNode("div", {
60
+ class: "cn-object-translations__tabs",
61
+ role: "tablist",
62
+ "aria-label": $options.t('nextcloud-vue', 'Languages'),
63
+ onKeydown: _cache[0] || (_cache[0] = (...args) => ($options.onTabKeydown && $options.onTabKeydown(...args)))
64
+ }, [
65
+ (openBlock(true), createElementBlock(Fragment, null, renderList($data.languages, (language) => {
66
+ return (openBlock(), createBlock(_component_NcButton, {
67
+ id: $options.tabId(language),
68
+ key: language,
69
+ variant: language === $data.activeLanguage ? 'primary' : 'tertiary',
70
+ role: "tab",
71
+ "aria-selected": String(language === $data.activeLanguage),
72
+ "aria-controls": $options.panelId,
73
+ tabindex: language === $data.activeLanguage ? 0 : -1,
74
+ "data-language": language,
75
+ onClick: $event => ($data.activeLanguage = language)
76
+ }, {
77
+ default: withCtx(() => [
78
+ createTextVNode(toDisplayString($options.nameOf(language)), 1 /* TEXT */)
79
+ ]),
80
+ _: 2 /* DYNAMIC */
81
+ }, 1032 /* PROPS, DYNAMIC_SLOTS */, ["id", "variant", "aria-selected", "aria-controls", "tabindex", "data-language", "onClick"]))
82
+ }), 128 /* KEYED_FRAGMENT */))
83
+ ], 40 /* PROPS, NEED_HYDRATION */, _hoisted_9),
84
+ createElementVNode("div", {
85
+ id: $options.panelId,
86
+ role: "tabpanel",
87
+ class: "cn-object-translations__panel",
88
+ "aria-labelledby": $options.tabId($data.activeLanguage)
89
+ }, [
90
+ (openBlock(true), createElementBlock(Fragment, null, renderList($options.propertiesShown, (property) => {
91
+ return (openBlock(), createElementBlock("div", {
92
+ key: property,
93
+ class: "cn-object-translations__field",
94
+ "data-property": property
95
+ }, [
96
+ createVNode(_component_NcTextField, {
97
+ label: $options.labelOf(property),
98
+ modelValue: $options.draftOf(property),
99
+ disabled: $data.activeLanguage === $data.sourceLanguage,
100
+ "onUpdate:modelValue": $event => ($options.setDraft(property, $event))
101
+ }, null, 8 /* PROPS */, ["label", "modelValue", "disabled", "onUpdate:modelValue"]),
102
+ ($options.isStale(property))
103
+ ? (openBlock(), createElementBlock("span", {
104
+ key: 0,
105
+ class: "cn-object-translations__stale",
106
+ "data-property": property
107
+ }, toDisplayString($options.t('nextcloud-vue', 'Out of date')), 9 /* TEXT, PROPS */, _hoisted_12))
108
+ : createCommentVNode("v-if", true)
109
+ ], 8 /* PROPS */, _hoisted_11))
110
+ }), 128 /* KEYED_FRAGMENT */)),
111
+ createVNode(_component_NcButton, {
112
+ variant: "primary",
113
+ disabled: $data.saving || !$options.hasEdits,
114
+ "data-testid": "cn-object-translations-save",
115
+ onClick: $options.save
116
+ }, {
117
+ default: withCtx(() => [
118
+ createTextVNode(toDisplayString($options.t('nextcloud-vue', 'Save')), 1 /* TEXT */)
119
+ ]),
120
+ _: 1 /* STABLE */
121
+ }, 8 /* PROPS */, ["disabled", "onClick"])
122
+ ], 8 /* PROPS */, _hoisted_10)
123
+ ], 64 /* STABLE_FRAGMENT */))
124
+ ]))
125
+ }
126
+
127
+ export { render };
128
+ //# sourceMappingURL=CnObjectTranslationsWidget.vue3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnObjectTranslationsWidget.vue3.js","sources":["../../../../src/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue?vue&type=template&id=c25058de&lang.js"],"sourcesContent":["<!--\n SPDX-FileCopyrightText: 2026 Conduction B.V.\n SPDX-License-Identifier: EUPL-1.2\n\n CnObjectTranslationsWidget — an object's translatable properties in every\n language its register serves: one completeness chip per language (a stale\n value counts as missing), a language tab strip, the source language read-only,\n a stale mark per field, and one save that writes each edited property as a\n whole language map. It translates nothing itself.\n-->\n<template>\n\t<div class=\"cn-object-translations\" data-testid=\"cn-object-translations\">\n\t\t<NcLoadingIcon v-if=\"loading\" :size=\"24\" />\n\n\t\t<p v-else-if=\"error\" class=\"cn-object-translations__empty\" role=\"alert\">\n\t\t\t{{ t('nextcloud-vue', 'The translations could not be read. Reload the page to try again.') }}\n\t\t</p>\n\n\t\t<p v-else-if=\"propertiesShown.length === 0\"\n\t\t\tclass=\"cn-object-translations__empty\"\n\t\t\tdata-testid=\"cn-object-translations-none\">\n\t\t\t{{ emptyText || t('nextcloud-vue', 'This item has no labels that can be translated.') }}\n\t\t</p>\n\n\t\t<template v-else>\n\t\t\t<ul class=\"cn-object-translations__chips\" data-testid=\"cn-object-translations-chips\">\n\t\t\t\t<li v-for=\"chip in chips\"\n\t\t\t\t\t:key=\"chip.language\"\n\t\t\t\t\tclass=\"cn-object-translations__chip cn-status-badge cn-status-badge--default\"\n\t\t\t\t\t:class=\"{ 'cn-object-translations__chip--complete': chip.complete }\"\n\t\t\t\t\t:data-language=\"chip.language\">\n\t\t\t\t\t<span class=\"cn-object-translations__chip-name\">{{ chip.name }}</span>\n\t\t\t\t\t<span class=\"cn-object-translations__chip-count\">{{ chip.count }}</span>\n\t\t\t\t</li>\n\t\t\t</ul>\n\n\t\t\t<p class=\"cn-object-translations__source\">\n\t\t\t\t{{ sourceNote }}\n\t\t\t</p>\n\n\t\t\t<div class=\"cn-object-translations__tabs\"\n\t\t\t\trole=\"tablist\"\n\t\t\t\t:aria-label=\"t('nextcloud-vue', 'Languages')\"\n\t\t\t\t@keydown=\"onTabKeydown\">\n\t\t\t\t<NcButton v-for=\"language in languages\"\n\t\t\t\t\t:id=\"tabId(language)\"\n\t\t\t\t\t:key=\"language\"\n\t\t\t\t\t:variant=\"language === activeLanguage ? 'primary' : 'tertiary'\"\n\t\t\t\t\trole=\"tab\"\n\t\t\t\t\t:aria-selected=\"String(language === activeLanguage)\"\n\t\t\t\t\t:aria-controls=\"panelId\"\n\t\t\t\t\t:tabindex=\"language === activeLanguage ? 0 : -1\"\n\t\t\t\t\t:data-language=\"language\"\n\t\t\t\t\t@click=\"activeLanguage = language\">\n\t\t\t\t\t{{ nameOf(language) }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\n\t\t\t<div :id=\"panelId\"\n\t\t\t\trole=\"tabpanel\"\n\t\t\t\tclass=\"cn-object-translations__panel\"\n\t\t\t\t:aria-labelledby=\"tabId(activeLanguage)\">\n\t\t\t\t<div v-for=\"property in propertiesShown\"\n\t\t\t\t\t:key=\"property\"\n\t\t\t\t\tclass=\"cn-object-translations__field\"\n\t\t\t\t\t:data-property=\"property\">\n\t\t\t\t\t<NcTextField :label=\"labelOf(property)\"\n\t\t\t\t\t\t:modelValue=\"draftOf(property)\"\n\t\t\t\t\t\t:disabled=\"activeLanguage === sourceLanguage\"\n\t\t\t\t\t\t@update:modelValue=\"setDraft(property, $event)\" />\n\n\t\t\t\t\t<span v-if=\"isStale(property)\"\n\t\t\t\t\t\tclass=\"cn-object-translations__stale\"\n\t\t\t\t\t\t:data-property=\"property\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'Out of date') }}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\n\t\t\t\t<NcButton variant=\"primary\"\n\t\t\t\t\t:disabled=\"saving || !hasEdits\"\n\t\t\t\t\tdata-testid=\"cn-object-translations-save\"\n\t\t\t\t\t@click=\"save\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'Save') }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { showError, showSuccess } from '@nextcloud/dialogs'\nimport { subscribe, unsubscribe } from '@nextcloud/event-bus'\nimport { getLanguage, translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcLoadingIcon, NcTextField } from '@nextcloud/vue'\nimport {\n\tcompletenessOf,\n\tDEFAULT_API_BASE,\n\tobjectWithTranslations,\n\tregisterLanguages,\n\tsaveTranslatedProperty,\n\tschemaProperties,\n\tSTATUS_OUTDATED,\n\ttranslationRows,\n} from '../../utils/objectTranslations.js'\n\nconst PAGE_REFRESH = 'cn:page:refresh'\n\nlet instanceCount = 0\n\nexport default {\n\tname: 'CnObjectTranslationsWidget',\n\n\tcomponents: { NcButton, NcLoadingIcon, NcTextField },\n\n\tprops: {\n\t\t/** Register slug or id (detail-context merge). */\n\t\tregister: { type: String, default: '' },\n\t\t/** Schema slug, or the schema object the detail-context merge supplies. */\n\t\tschema: { type: [String, Object], default: '' },\n\t\t/** The object id (detail-context merge). Falls back to the route's `id`. */\n\t\tobjectId: { type: String, default: '' },\n\t\t/** OpenRegister API root. */\n\t\tapiBase: { type: String, default: DEFAULT_API_BASE },\n\t\t/** Optional whitelist of properties to offer; empty offers every translatable one. */\n\t\tproperties: { type: Array, default: () => [] },\n\t\t/** Text when nothing is translatable; empty uses the translated default. */\n\t\temptyText: { type: String, default: '' },\n\t\t/** Stored widget content blob; its keys are read as the props above. */\n\t\tcontent: { type: Object, default: () => ({}) },\n\t},\n\n\tdata() {\n\t\tinstanceCount += 1\n\t\treturn {\n\t\t\tuid: `cn-object-translations-${instanceCount}`,\n\t\t\tloading: true,\n\t\t\terror: false,\n\t\t\tsaving: false,\n\t\t\tlanguages: [],\n\t\t\tsourceLanguage: '',\n\t\t\ttranslatable: [],\n\t\t\ttitles: {},\n\t\t\tvalues: {},\n\t\t\tstatuses: {},\n\t\t\tdrafts: {},\n\t\t\tactiveLanguage: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\ttarget() {\n\t\t\treturn {\n\t\t\t\tregister: this.register || this.content.register || '',\n\t\t\t\tschema: this.schema || this.content.schema || '',\n\t\t\t\tapiBase: this.content.apiBase || this.apiBase,\n\t\t\t}\n\t\t},\n\n\t\tresolvedId() {\n\t\t\treturn String(this.objectId || this.content.objectId || this.$route?.params?.id || '')\n\t\t},\n\n\t\twhitelist() {\n\t\t\tconst list = this.properties.length > 0 ? this.properties : (this.content.properties || [])\n\t\t\treturn Array.isArray(list) ? list.map(String) : []\n\t\t},\n\n\t\t/** The translatable properties OpenRegister reported, narrowed by the whitelist. */\n\t\tpropertiesShown() {\n\t\t\treturn this.whitelist.length === 0\n\t\t\t\t? this.translatable\n\t\t\t\t: this.translatable.filter((property) => this.whitelist.includes(property))\n\t\t},\n\n\t\tpanelId() {\n\t\t\treturn `${this.uid}-panel`\n\t\t},\n\n\t\t/**\n\t\t * One chip per language the REGISTER declares (so a language nobody has\n\t\t * started reads \"0 of 5\"), counting a stale value as missing.\n\t\t *\n\t\t * @return {Array<object>} The chips, source first.\n\t\t */\n\t\tchips() {\n\t\t\treturn this.languages.map((language) => {\n\t\t\t\tconst { translated, total } = completenessOf({\n\t\t\t\t\tproperties: this.propertiesShown,\n\t\t\t\t\tvalues: this.values,\n\t\t\t\t\tstatuses: this.statuses,\n\t\t\t\t\tlanguage,\n\t\t\t\t\tsourceLanguage: this.sourceLanguage,\n\t\t\t\t})\n\t\t\t\treturn {\n\t\t\t\t\tlanguage,\n\t\t\t\t\tname: this.nameOf(language),\n\t\t\t\t\t// TRANSLATORS: how many of a record's labels exist in one language\n\t\t\t\t\tcount: t('nextcloud-vue', '{translated} of {total}', { translated, total }),\n\t\t\t\t\tcomplete: translated === total,\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\tsourceNote() {\n\t\t\treturn t('nextcloud-vue', '{language} is the source. Change a {language} label and its translations are marked out of date.', { language: this.nameOf(this.sourceLanguage) })\n\t\t},\n\n\t\thasEdits() {\n\t\t\treturn this.propertiesShown.some((property) => this.draftOf(property) !== this.storedOf(property))\n\t\t},\n\t},\n\n\tmounted() {\n\t\tsubscribe(PAGE_REFRESH, this.load)\n\t\tthis.load()\n\t},\n\n\tbeforeUnmount() {\n\t\tunsubscribe(PAGE_REFRESH, this.load)\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * A language's name in the reader's own language, from the browser\n\t\t * (keeps 38 language names out of the catalogue).\n\t\t *\n\t\t * @param {string} code The BCP 47 tag.\n\t\t * @return {string} The name, or the tag.\n\t\t */\n\t\tnameOf(code) {\n\t\t\ttry {\n\t\t\t\treturn new Intl.DisplayNames([getLanguage()], { type: 'language' }).of(code) ?? code\n\t\t\t} catch {\n\t\t\t\treturn code\n\t\t\t}\n\t\t},\n\n\t\ttabId(language) {\n\t\t\treturn `${this.uid}-tab-${language}`\n\t\t},\n\n\t\t/**\n\t\t * Arrow keys, Home and End move between the language tabs.\n\t\t *\n\t\t * @param {KeyboardEvent} event The key event.\n\t\t */\n\t\tonTabKeydown(event) {\n\t\t\tconst index = this.languages.indexOf(this.activeLanguage)\n\t\t\tconst last = this.languages.length - 1\n\t\t\tconst next = {\n\t\t\t\tArrowRight: index >= last ? 0 : index + 1,\n\t\t\t\tArrowLeft: index <= 0 ? last : index - 1,\n\t\t\t\tHome: 0,\n\t\t\t\tEnd: last,\n\t\t\t}[event.key]\n\t\t\tif (next === undefined || last < 0) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tevent.preventDefault()\n\t\t\tthis.activeLanguage = this.languages[next]\n\t\t\tthis.$nextTick(() => {\n\t\t\t\tconst el = document.getElementById(this.tabId(this.activeLanguage))\n\t\t\t\tif (el && typeof el.focus === 'function') {\n\t\t\t\t\tel.focus()\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\tlabelOf(property) {\n\t\t\treturn this.titles[property] ?? property\n\t\t},\n\n\t\tstoredOf(property) {\n\t\t\treturn this.values?.[property]?.[this.activeLanguage] ?? ''\n\t\t},\n\n\t\tdraftOf(property) {\n\t\t\tconst typed = this.drafts?.[this.activeLanguage]?.[property]\n\t\t\treturn typed === undefined ? this.storedOf(property) : typed\n\t\t},\n\n\t\tsetDraft(property, value) {\n\t\t\tconst language = this.activeLanguage\n\t\t\tthis.drafts = { ...this.drafts, [language]: { ...(this.drafts[language] ?? {}), [property]: value } }\n\t\t},\n\n\t\tisStale(property) {\n\t\t\treturn (this.statuses?.[property]?.[this.activeLanguage] ?? '') === STATUS_OUTDATED\n\t\t},\n\n\t\t/**\n\t\t * Read the register languages, the object with every language, the\n\t\t * property titles and the sidecar statuses.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync load() {\n\t\t\tif (!this.resolvedId) {\n\t\t\t\tthis.loading = false\n\t\t\t\tthis.error = true\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tthis.loading = true\n\t\t\ttry {\n\t\t\t\tconst [languages, read, definitions] = await Promise.all([\n\t\t\t\t\tregisterLanguages(this.target),\n\t\t\t\t\tobjectWithTranslations(this.target, this.resolvedId),\n\t\t\t\t\tschemaProperties(this.target),\n\t\t\t\t])\n\n\t\t\t\tconst meta = read.languageMeta ?? {}\n\t\t\t\tthis.translatable = Object.keys(meta)\n\t\t\t\tconst first = this.propertiesShown[0] ?? this.translatable[0]\n\t\t\t\tthis.sourceLanguage = meta[first]?.sourceLanguage ?? languages[0] ?? 'nl'\n\t\t\t\tthis.languages = languages.length > 0 ? languages : [this.sourceLanguage]\n\t\t\t\tthis.activeLanguage = this.languages.find((code) => code !== this.sourceLanguage) ?? this.sourceLanguage\n\n\t\t\t\tthis.values = Object.fromEntries(this.translatable.map((property) => {\n\t\t\t\t\tconst stored = read.object?.[property]\n\t\t\t\t\treturn [property, { ...(stored !== null && typeof stored === 'object' ? stored : {}) }]\n\t\t\t\t}))\n\t\t\t\tthis.titles = Object.fromEntries(this.translatable.map((property) => [property, definitions[property]?.title ?? property]))\n\n\t\t\t\tconst uuid = String(read.object?.['@self']?.uuid ?? read.object?.id ?? this.resolvedId)\n\t\t\t\tconst statuses = {}\n\t\t\t\tfor (const row of await translationRows(this.target, uuid)) {\n\t\t\t\t\tconst property = String(row.property ?? '')\n\t\t\t\t\tconst language = String(row.language ?? '')\n\t\t\t\t\tif (property === '' || language === '') {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tstatuses[property] = { ...(statuses[property] ?? {}), [language]: row.status }\n\t\t\t\t}\n\t\t\t\tthis.statuses = statuses\n\n\t\t\t\tthis.drafts = {}\n\t\t\t\tthis.error = false\n\t\t\t} catch {\n\t\t\t\tthis.error = true\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Write every edited property back, each as a whole language map.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync save() {\n\t\t\tthis.saving = true\n\t\t\ttry {\n\t\t\t\tfor (const property of this.propertiesShown) {\n\t\t\t\t\tconst draft = this.draftOf(property)\n\t\t\t\t\tif (draft === this.storedOf(property)) {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tconst map = { ...(this.values[property] ?? {}), [this.activeLanguage]: draft }\n\t\t\t\t\tawait saveTranslatedProperty(this.target, this.resolvedId, property, map)\n\t\t\t\t\tthis.values = { ...this.values, [property]: map }\n\t\t\t\t}\n\t\t\t\tthis.drafts = { ...this.drafts, [this.activeLanguage]: {} }\n\t\t\t\tshowSuccess(t('nextcloud-vue', 'The labels are saved.'))\n\t\t\t} catch {\n\t\t\t\tshowError(t('nextcloud-vue', 'The labels could not be saved.'))\n\t\t\t} finally {\n\t\t\t\tthis.saving = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-object-translations__chips {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 8px;\n\tlist-style: none;\n\tmargin: 0 0 8px;\n\tpadding: 0;\n}\n\n.cn-object-translations__chip {\n\talign-items: center;\n\tbackground: var(--color-background-dark);\n\tdisplay: flex;\n\tgap: 6px;\n}\n\n.cn-object-translations__chip--complete {\n\tbackground: var(--color-success, var(--color-background-dark));\n\t--cn-status-badge-ink: var(--color-primary-text);\n\tcolor: var(--cn-status-badge-ink);\n}\n\n.cn-object-translations__source,\n.cn-object-translations__empty {\n\tcolor: var(--color-text-maxcontrast);\n\tmargin: 0 0 8px;\n}\n\n.cn-object-translations__tabs {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 4px;\n\tmargin-bottom: 8px;\n}\n\n.cn-object-translations__field {\n\talign-items: center;\n\tdisplay: flex;\n\tgap: 8px;\n\tmargin-bottom: 8px;\n}\n\n.cn-object-translations__stale {\n\tcolor: var(--color-warning-text, var(--color-main-text));\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createBlock","_toDisplayString","_Fragment","_renderList","_normalizeClass","_createElementVNode","_createTextVNode","_createVNode"],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import script from './CnReadinessChecklist.vue2.js';
2
+ import { render } from './CnReadinessChecklist.vue3.js';
3
+
4
+ script.render = render;
5
+ script.__scopeId = "data-v-4e10a556";
6
+ const __sfc_main = script;
7
+
8
+ export { __sfc_main as default };
9
+ //# sourceMappingURL=CnReadinessChecklist.vue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnReadinessChecklist.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;"}