@conduction/nextcloud-vue 2.56.0-beta.62 → 2.56.0-beta.63

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 (155) hide show
  1. package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue.js +10 -0
  2. package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue.js.map +1 -0
  3. package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue2.js +305 -0
  4. package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue2.js.map +1 -0
  5. package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue3.js +38 -0
  6. package/dist/esm/components/CnAddressSearch/CnAddressSearch.vue3.js.map +1 -0
  7. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue.js +10 -0
  8. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue.js.map +1 -0
  9. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue2.js +398 -0
  10. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue2.js.map +1 -0
  11. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue3.js +155 -0
  12. package/dist/esm/components/CnAssignDialog/CnAssignDialog.vue3.js.map +1 -0
  13. package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue.js +9 -0
  14. package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue.js.map +1 -0
  15. package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue2.js +281 -0
  16. package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue2.js.map +1 -0
  17. package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue3.js +137 -0
  18. package/dist/esm/components/CnDecisionListWidget/CnDecisionListWidget.vue3.js.map +1 -0
  19. package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue.js +9 -0
  20. package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue.js.map +1 -0
  21. package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue2.js +225 -0
  22. package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue2.js.map +1 -0
  23. package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue3.js +110 -0
  24. package/dist/esm/components/CnDuplicateWarning/CnDuplicateWarning.vue3.js.map +1 -0
  25. package/dist/esm/components/CnFormDialog/CnFormDialog.vue.js +1 -1
  26. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +19 -3
  27. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
  28. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
  29. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +30 -0
  30. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  31. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +513 -481
  32. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  33. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue.js +9 -0
  34. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue.js.map +1 -0
  35. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue2.js +181 -0
  36. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue2.js.map +1 -0
  37. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue3.js +50 -0
  38. package/dist/esm/components/CnNcDashboardList/CnNcDashboardList.vue3.js.map +1 -0
  39. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue.js +1 -1
  40. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +102 -1
  41. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  42. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +6 -6
  43. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  44. package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue.js +9 -0
  45. package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue.js.map +1 -0
  46. package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue2.js +190 -0
  47. package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue2.js.map +1 -0
  48. package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue3.js +91 -0
  49. package/dist/esm/components/CnPlanWidget/CnPlanWidget.vue3.js.map +1 -0
  50. package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue.js +9 -0
  51. package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue.js.map +1 -0
  52. package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue2.js +64 -0
  53. package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue2.js.map +1 -0
  54. package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue3.js +49 -0
  55. package/dist/esm/components/CnPlanWidget/CnPlanWidgetRow.vue3.js.map +1 -0
  56. package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue.js +9 -0
  57. package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue.js.map +1 -0
  58. package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue2.js +382 -0
  59. package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue2.js.map +1 -0
  60. package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue3.js +285 -0
  61. package/dist/esm/components/CnRedactionEditor/CnRedactionEditor.vue3.js.map +1 -0
  62. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue.js +9 -0
  63. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue.js.map +1 -0
  64. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue2.js +277 -0
  65. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue2.js.map +1 -0
  66. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue3.js +183 -0
  67. package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue3.js.map +1 -0
  68. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +16 -14
  69. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
  70. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +53 -22
  71. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  72. package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue.js +9 -0
  73. package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue.js.map +1 -0
  74. package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue2.js +246 -0
  75. package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue2.js.map +1 -0
  76. package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue3.js +197 -0
  77. package/dist/esm/components/CnWorkQueue/CnWorkQueue.vue3.js.map +1 -0
  78. package/dist/esm/index.js +12 -0
  79. package/dist/esm/index.js.map +1 -1
  80. package/dist/esm/l10n/en.json.js +106 -1
  81. package/dist/esm/l10n/en.json.js.map +1 -1
  82. package/dist/esm/l10n/nl.json.js +105 -1
  83. package/dist/esm/l10n/nl.json.js.map +1 -1
  84. package/dist/esm/library-style.js +1 -1
  85. package/dist/esm/nextcloud-vue.css +495 -61
  86. package/dist/esm/utils/actionsDispatcher.js +6 -1
  87. package/dist/esm/utils/actionsDispatcher.js.map +1 -1
  88. package/dist/esm/utils/builtInModals.js +3 -1
  89. package/dist/esm/utils/builtInModals.js.map +1 -1
  90. package/dist/esm/utils/duplicateWarning.js +62 -0
  91. package/dist/esm/utils/duplicateWarning.js.map +1 -0
  92. package/dist/esm/utils/errors.js +4 -0
  93. package/dist/esm/utils/errors.js.map +1 -1
  94. package/dist/esm/utils/libraryWidgetKeys.js +4 -0
  95. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  96. package/dist/esm/utils/openDuplicateWarning.js +39 -0
  97. package/dist/esm/utils/openDuplicateWarning.js.map +1 -0
  98. package/dist/esm/utils/optionsFrom.js +69 -2
  99. package/dist/esm/utils/optionsFrom.js.map +1 -1
  100. package/dist/esm/utils/planTree.js +57 -0
  101. package/dist/esm/utils/planTree.js.map +1 -0
  102. package/dist/esm/utils/redactionSegments.js +53 -0
  103. package/dist/esm/utils/redactionSegments.js.map +1 -0
  104. package/dist/esm/utils/registerNcDashboardWidgets.js +61 -0
  105. package/dist/esm/utils/registerNcDashboardWidgets.js.map +1 -0
  106. package/dist/esm/utils/searchRefusal.js +62 -0
  107. package/dist/esm/utils/searchRefusal.js.map +1 -0
  108. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  109. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  110. package/dist/nextcloud-vue.cjs.js +17409 -12832
  111. package/dist/nextcloud-vue.cjs.js.map +2 -2
  112. package/dist/nextcloud-vue.css +495 -61
  113. package/l10n/en.json +106 -1
  114. package/l10n/nl.json +105 -1
  115. package/package.json +1 -1
  116. package/src/components/CnAddressSearch/CnAddressSearch.vue +343 -0
  117. package/src/components/CnAddressSearch/index.js +12 -0
  118. package/src/components/CnAssignDialog/CnAssignDialog.vue +525 -0
  119. package/src/components/CnAssignDialog/index.js +12 -0
  120. package/src/components/CnDecisionListWidget/CnDecisionListWidget.vue +388 -0
  121. package/src/components/CnDecisionListWidget/index.js +12 -0
  122. package/src/components/CnDuplicateWarning/CnDuplicateWarning.vue +318 -0
  123. package/src/components/CnDuplicateWarning/index.js +12 -0
  124. package/src/components/CnFormDialog/CnFormDialog.vue +19 -3
  125. package/src/components/CnIndexPage/CnIndexPage.vue +52 -0
  126. package/src/components/CnNcDashboardList/CnNcDashboardList.vue +218 -0
  127. package/src/components/CnNcDashboardList/index.js +12 -0
  128. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +107 -6
  129. package/src/components/CnPlanWidget/CnPlanWidget.vue +246 -0
  130. package/src/components/CnPlanWidget/CnPlanWidgetRow.vue +117 -0
  131. package/src/components/CnPlanWidget/index.js +12 -0
  132. package/src/components/CnRedactionEditor/CnRedactionEditor.vue +655 -0
  133. package/src/components/CnRedactionEditor/index.js +13 -0
  134. package/src/components/CnRetentionWidget/CnRetentionWidget.vue +413 -0
  135. package/src/components/CnRetentionWidget/index.js +12 -0
  136. package/src/components/CnWidgetGrid/builtInWidgets.js +2 -0
  137. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +31 -0
  138. package/src/components/CnWorkQueue/CnWorkQueue.vue +456 -0
  139. package/src/components/CnWorkQueue/index.js +12 -0
  140. package/src/components/index.js +9 -0
  141. package/src/css/index-page.css +24 -0
  142. package/src/index.js +12 -0
  143. package/src/schemas/app-manifest-v2.schema.json +119 -3
  144. package/src/utils/actionsDispatcher.js +6 -1
  145. package/src/utils/builtInModals.js +3 -1
  146. package/src/utils/duplicateWarning.js +59 -0
  147. package/src/utils/errors.js +4 -0
  148. package/src/utils/libraryWidgetKeys.js +4 -0
  149. package/src/utils/openDuplicateWarning.js +35 -0
  150. package/src/utils/optionsFrom.js +68 -1
  151. package/src/utils/planTree.js +54 -0
  152. package/src/utils/redactionSegments.js +50 -0
  153. package/src/utils/registerNcDashboardWidgets.js +57 -0
  154. package/src/utils/searchRefusal.js +59 -0
  155. package/src/utils/validateManifestV2.compiled.js +3 -3
@@ -0,0 +1,10 @@
1
+ import script from './CnAddressSearch.vue2.js';
2
+ export { addressCoordinates, addressText } from './CnAddressSearch.vue2.js';
3
+ import { render } from './CnAddressSearch.vue3.js';
4
+
5
+ script.render = render;
6
+ script.__scopeId = "data-v-5c8ae3f1";
7
+ const __sfc_main = script;
8
+
9
+ export { __sfc_main as default };
10
+ //# sourceMappingURL=CnAddressSearch.vue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnAddressSearch.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;"}
@@ -0,0 +1,305 @@
1
+ import axios__default from '@nextcloud/axios';
2
+ import { t } from '@nextcloud/l10n';
3
+ import { generateUrl } from '@nextcloud/router';
4
+ import { prefixUrl } from '../../utils/headers.js';
5
+ import __sfc_main from '../CnSearchSelect/CnSearchSelect.vue.js';
6
+
7
+ /** How long typing rests before a search runs, in ms. */
8
+ const SEARCH_DELAY = 250;
9
+
10
+ /** The id the current value carries in the option list. */
11
+ const CURRENT_ID = '__cn-address-current';
12
+
13
+ /**
14
+ * WGS84 coordinates from a Locatieserver document: a normalised
15
+ * `location.coordinates` (`[lng, lat]`) or a raw `centroide_ll` WKT point.
16
+ *
17
+ * @param {object|null} doc The document.
18
+ * @return {{lat: number, lng: number}|null} The coordinates, or null.
19
+ */
20
+ function addressCoordinates(doc) {
21
+ if (!doc) {
22
+ return null
23
+ }
24
+ if (Array.isArray(doc.location?.coordinates)) {
25
+ const [lng, lat] = doc.location.coordinates;
26
+ return { lat, lng }
27
+ }
28
+ const match = typeof doc.centroide_ll === 'string' ? doc.centroide_ll.match(/POINT\(([^ ]+) ([^ ]+)\)/) : null;
29
+ return match ? { lng: parseFloat(match[1]), lat: parseFloat(match[2]) } : null
30
+ }
31
+
32
+ /**
33
+ * A Locatieserver document's display text.
34
+ *
35
+ * @param {object|null} doc The document.
36
+ * @return {string} The address text.
37
+ */
38
+ function addressText(doc) {
39
+ return doc ? doc.displayName || doc.weergavenaam || doc.display || '' : ''
40
+ }
41
+
42
+ /**
43
+ * CnAddressSearch — an address field over the PDOK Locatieserver (BAG
44
+ * addresses, streets, places and postcodes): suggestions as the user types,
45
+ * a free-text search when nothing is suggested, and on a pick the full
46
+ * document with its coordinates. Built on CnSearchSelect.
47
+ *
48
+ * Browsers never call api.pdok.nl directly (ADR-022): requests go to a
49
+ * PDOK proxy with `suggest`, `free` and `lookup/{id}` routes. By default
50
+ * that is the `/api/pdok` route of the first installed app among
51
+ * `appCandidates` (integriq, then openconnector); `endpoint` names another
52
+ * base URL. When the proxy answers 404 or 503 the field says address search
53
+ * is unavailable and stays usable, so a form around it can still be saved.
54
+ *
55
+ * `select` emits `{ result, coordinates, address }`: the looked-up document,
56
+ * `{ lat, lng }` or null, and the display text. `update:modelValue` emits
57
+ * the display text, or null when cleared.
58
+ *
59
+ * ```vue
60
+ * <CnAddressSearch v-model="caseObject.address" @select="onAddress" />
61
+ * ```
62
+ */
63
+ var script = {
64
+ name: 'CnAddressSearch',
65
+
66
+ components: { CnSearchSelect: __sfc_main },
67
+
68
+ props: {
69
+ /** The chosen address text. */
70
+ modelValue: {
71
+ type: String,
72
+ default: null,
73
+ },
74
+
75
+ /** Base URL of the PDOK proxy; empty resolves it from `appCandidates`. */
76
+ endpoint: {
77
+ type: String,
78
+ default: '',
79
+ },
80
+
81
+ /** App ids that serve `/api/pdok`, newest first; the first installed one wins. */
82
+ appCandidates: {
83
+ type: Array,
84
+ default: () => ['integriq', 'openconnector'],
85
+ },
86
+
87
+ /** The field's label; defaults to "Search an address". */
88
+ inputLabel: {
89
+ type: String,
90
+ default: '',
91
+ },
92
+
93
+ /** Draw the label above the field. */
94
+ labelOutside: {
95
+ type: Boolean,
96
+ default: false,
97
+ },
98
+
99
+ /** Placeholder; defaults to "Street, postcode or place". */
100
+ placeholder: {
101
+ type: String,
102
+ default: '',
103
+ },
104
+
105
+ /** Offer a clear button. */
106
+ clearable: {
107
+ type: Boolean,
108
+ default: true,
109
+ },
110
+
111
+ /** Disable the field. */
112
+ disabled: {
113
+ type: Boolean,
114
+ default: false,
115
+ },
116
+
117
+ /** The fewest characters a search needs. */
118
+ minLength: {
119
+ type: Number,
120
+ default: 3,
121
+ },
122
+
123
+ /** The most results a free-text search returns. */
124
+ rows: {
125
+ type: Number,
126
+ default: 10,
127
+ },
128
+ },
129
+
130
+ emits: ['update:modelValue', 'select'],
131
+
132
+ data() {
133
+ return {
134
+ options: [],
135
+ searching: false,
136
+ unavailable: false,
137
+ timer: null,
138
+ searchSeq: 0,
139
+ }
140
+ },
141
+
142
+ computed: {
143
+ /**
144
+ * The current value as an option, so the field shows it.
145
+ *
146
+ * @return {object|null}
147
+ */
148
+ selectedOption() {
149
+ return this.modelValue ? { id: CURRENT_ID, label: this.modelValue, icon: 'MapMarkerOutline' } : null
150
+ },
151
+
152
+ /**
153
+ * The suggestions, with the current value kept in the list.
154
+ *
155
+ * @return {Array<object>}
156
+ */
157
+ shownOptions() {
158
+ return this.selectedOption ? [this.selectedOption, ...this.options] : this.options
159
+ },
160
+
161
+ /**
162
+ * The proxy's base URL.
163
+ *
164
+ * @spec openspec/changes/address-search/specs/address-search/spec.md#requirement-an-address-field-over-the-pdok-locatieserver
165
+ * @return {string}
166
+ */
167
+ baseUrl() {
168
+ if (this.endpoint) {
169
+ return this.endpoint.replace(/\/+$/, '')
170
+ }
171
+ const roots = (typeof window !== 'undefined' && window.OC && window.OC.appswebroots) || {};
172
+ const app = this.appCandidates.find((id) => Object.hasOwn(roots, id)) || this.appCandidates[0] || 'integriq';
173
+ return generateUrl(`/apps/${app}/api/pdok`)
174
+ },
175
+ },
176
+
177
+ beforeUnmount() {
178
+ clearTimeout(this.timer);
179
+ },
180
+
181
+ methods: {
182
+ t,
183
+
184
+ /**
185
+ * A document as a picker option.
186
+ *
187
+ * @param {object} doc The Locatieserver document.
188
+ * @param {number} index Its place in the list, for documents without an id.
189
+ * @return {object|null} The option, or null without a text.
190
+ */
191
+ optionOf(doc, index) {
192
+ const label = addressText(doc);
193
+ if (!label) {
194
+ return null
195
+ }
196
+ const kinds = {
197
+ adres: t('nextcloud-vue', 'Address'),
198
+ address: t('nextcloud-vue', 'Address'),
199
+ weg: t('nextcloud-vue', 'Street'),
200
+ woonplaats: t('nextcloud-vue', 'Place'),
201
+ city: t('nextcloud-vue', 'Place'),
202
+ postcode: t('nextcloud-vue', 'Postcode'),
203
+ };
204
+ const municipality = doc.gemeentenaam && !label.includes(doc.gemeentenaam) ? doc.gemeentenaam : '';
205
+ return {
206
+ id: doc.id || `${CURRENT_ID}-${index}`,
207
+ label,
208
+ secondary: [kinds[doc.type] || '', municipality].filter(Boolean).join(' · '),
209
+ icon: 'MapMarkerOutline',
210
+ doc,
211
+ }
212
+ },
213
+
214
+ /**
215
+ * Call the proxy; 404 or 503 marks search unavailable and yields `fallback`.
216
+ *
217
+ * @param {string} path The route under the base URL.
218
+ * @param {object} params The query parameters.
219
+ * @param {Array<object>} fallback What a degraded answer yields.
220
+ * @return {Promise<Array<object>>} The documents.
221
+ */
222
+ async request(path, params, fallback) {
223
+ try {
224
+ const { data } = await axios__default.get(prefixUrl(`${this.baseUrl}/${path}`), { params });
225
+ this.unavailable = false;
226
+ return Array.isArray(data?.docs) ? data.docs : fallback
227
+ } catch (error) {
228
+ const status = error?.response?.status;
229
+ if (status === 404 || status === 503) {
230
+ this.unavailable = true;
231
+ }
232
+ return fallback
233
+ }
234
+ },
235
+
236
+ /**
237
+ * Search after typing rests.
238
+ *
239
+ * @param {string} query The typed text.
240
+ */
241
+ onSearch(query) {
242
+ clearTimeout(this.timer);
243
+ const term = String(query || '').trim();
244
+ if (term.length < this.minLength) {
245
+ this.options = [];
246
+ return
247
+ }
248
+ this.timer = setTimeout(() => this.search(term), SEARCH_DELAY);
249
+ },
250
+
251
+ /**
252
+ * Suggest addresses for the term; without suggestions, search free text.
253
+ *
254
+ * @spec openspec/changes/address-search/specs/address-search/spec.md#requirement-an-address-field-over-the-pdok-locatieserver
255
+ * @param {string} term The search term.
256
+ * @return {Promise<void>}
257
+ */
258
+ async search(term) {
259
+ const seq = ++this.searchSeq;
260
+ this.searching = true;
261
+ let docs = await this.request('suggest', { q: term }, []);
262
+ if (!docs.length && !this.unavailable && seq === this.searchSeq) {
263
+ docs = await this.request('free', { q: term, rows: this.rows }, []);
264
+ }
265
+ if (seq !== this.searchSeq) {
266
+ return
267
+ }
268
+ this.options = docs.map((doc, index) => this.optionOf(doc, index)).filter(Boolean);
269
+ this.searching = false;
270
+ },
271
+
272
+ /**
273
+ * Take the picked suggestion: look up the full document, then emit it.
274
+ *
275
+ * @spec openspec/changes/address-search/specs/address-search/spec.md#requirement-an-address-field-over-the-pdok-locatieserver
276
+ * @param {string|null} id The picked option's id.
277
+ * @return {Promise<void>}
278
+ */
279
+ async onPick(id) {
280
+ if (id === CURRENT_ID) {
281
+ return
282
+ }
283
+ const option = id === null ? null : this.options.find((o) => String(o.id) === String(id));
284
+ if (!option) {
285
+ /** @event update:modelValue The chosen address text, or null when cleared. */
286
+ this.$emit('update:modelValue', null);
287
+ /** @event select `{ result, coordinates, address }` for the chosen address, or null when cleared. */
288
+ this.$emit('select', null);
289
+ return
290
+ }
291
+ let result = option.doc;
292
+ if (option.doc.id) {
293
+ const [full] = await this.request(`lookup/${encodeURIComponent(option.doc.id)}`, {}, []);
294
+ result = full || option.doc;
295
+ }
296
+ const address = addressText(result) || option.label;
297
+ this.options = [];
298
+ this.$emit('update:modelValue', address);
299
+ this.$emit('select', { result, coordinates: addressCoordinates(result), address });
300
+ },
301
+ },
302
+ };
303
+
304
+ export { addressCoordinates, addressText, script as default };
305
+ //# sourceMappingURL=CnAddressSearch.vue2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnAddressSearch.vue2.js","sources":["../../../../src/components/CnAddressSearch/CnAddressSearch.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-address-search\" data-testid=\"cn-address-search\">\n\t\t<CnSearchSelect\n\t\t\t:modelValue=\"selectedOption ? selectedOption.id : null\"\n\t\t\t:options=\"shownOptions\"\n\t\t\t:inputLabel=\"inputLabel || t('nextcloud-vue', 'Search an address')\"\n\t\t\t:labelOutside=\"labelOutside\"\n\t\t\t:placeholder=\"placeholder || t('nextcloud-vue', 'Street, postcode or place')\"\n\t\t\t:loading=\"searching\"\n\t\t\t:filterable=\"false\"\n\t\t\t:clearable=\"clearable\"\n\t\t\t:disabled=\"disabled\"\n\t\t\t@search=\"onSearch\"\n\t\t\t@update:modelValue=\"onPick\" />\n\t\t<p v-if=\"unavailable\"\n\t\t\tclass=\"cn-address-search__note\"\n\t\t\trole=\"status\"\n\t\t\tdata-testid=\"cn-address-search-unavailable\">\n\t\t\t{{ t('nextcloud-vue', 'Address search is not available right now.') }}\n\t\t</p>\n\t</div>\n</template>\n\n<script>\nimport axios from '@nextcloud/axios'\nimport { t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { prefixUrl } from '../../utils/headers.js'\nimport { CnSearchSelect } from '../CnSearchSelect/index.js'\n\n/** How long typing rests before a search runs, in ms. */\nconst SEARCH_DELAY = 250\n\n/** The id the current value carries in the option list. */\nconst CURRENT_ID = '__cn-address-current'\n\n/**\n * WGS84 coordinates from a Locatieserver document: a normalised\n * `location.coordinates` (`[lng, lat]`) or a raw `centroide_ll` WKT point.\n *\n * @param {object|null} doc The document.\n * @return {{lat: number, lng: number}|null} The coordinates, or null.\n */\nexport function addressCoordinates(doc) {\n\tif (!doc) {\n\t\treturn null\n\t}\n\tif (Array.isArray(doc.location?.coordinates)) {\n\t\tconst [lng, lat] = doc.location.coordinates\n\t\treturn { lat, lng }\n\t}\n\tconst match = typeof doc.centroide_ll === 'string' ? doc.centroide_ll.match(/POINT\\(([^ ]+) ([^ ]+)\\)/) : null\n\treturn match ? { lng: parseFloat(match[1]), lat: parseFloat(match[2]) } : null\n}\n\n/**\n * A Locatieserver document's display text.\n *\n * @param {object|null} doc The document.\n * @return {string} The address text.\n */\nexport function addressText(doc) {\n\treturn doc ? doc.displayName || doc.weergavenaam || doc.display || '' : ''\n}\n\n/**\n * CnAddressSearch — an address field over the PDOK Locatieserver (BAG\n * addresses, streets, places and postcodes): suggestions as the user types,\n * a free-text search when nothing is suggested, and on a pick the full\n * document with its coordinates. Built on CnSearchSelect.\n *\n * Browsers never call api.pdok.nl directly (ADR-022): requests go to a\n * PDOK proxy with `suggest`, `free` and `lookup/{id}` routes. By default\n * that is the `/api/pdok` route of the first installed app among\n * `appCandidates` (integriq, then openconnector); `endpoint` names another\n * base URL. When the proxy answers 404 or 503 the field says address search\n * is unavailable and stays usable, so a form around it can still be saved.\n *\n * `select` emits `{ result, coordinates, address }`: the looked-up document,\n * `{ lat, lng }` or null, and the display text. `update:modelValue` emits\n * the display text, or null when cleared.\n *\n * ```vue\n * <CnAddressSearch v-model=\"caseObject.address\" @select=\"onAddress\" />\n * ```\n */\nexport default {\n\tname: 'CnAddressSearch',\n\n\tcomponents: { CnSearchSelect },\n\n\tprops: {\n\t\t/** The chosen address text. */\n\t\tmodelValue: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Base URL of the PDOK proxy; empty resolves it from `appCandidates`. */\n\t\tendpoint: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** App ids that serve `/api/pdok`, newest first; the first installed one wins. */\n\t\tappCandidates: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => ['integriq', 'openconnector'],\n\t\t},\n\n\t\t/** The field's label; defaults to \"Search an address\". */\n\t\tinputLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Draw the label above the field. */\n\t\tlabelOutside: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Placeholder; defaults to \"Street, postcode or place\". */\n\t\tplaceholder: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Offer a clear button. */\n\t\tclearable: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\n\t\t/** Disable the field. */\n\t\tdisabled: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** The fewest characters a search needs. */\n\t\tminLength: {\n\t\t\ttype: Number,\n\t\t\tdefault: 3,\n\t\t},\n\n\t\t/** The most results a free-text search returns. */\n\t\trows: {\n\t\t\ttype: Number,\n\t\t\tdefault: 10,\n\t\t},\n\t},\n\n\temits: ['update:modelValue', 'select'],\n\n\tdata() {\n\t\treturn {\n\t\t\toptions: [],\n\t\t\tsearching: false,\n\t\t\tunavailable: false,\n\t\t\ttimer: null,\n\t\t\tsearchSeq: 0,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The current value as an option, so the field shows it.\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tselectedOption() {\n\t\t\treturn this.modelValue ? { id: CURRENT_ID, label: this.modelValue, icon: 'MapMarkerOutline' } : null\n\t\t},\n\n\t\t/**\n\t\t * The suggestions, with the current value kept in the list.\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\tshownOptions() {\n\t\t\treturn this.selectedOption ? [this.selectedOption, ...this.options] : this.options\n\t\t},\n\n\t\t/**\n\t\t * The proxy's base URL.\n\t\t *\n\t\t * @spec openspec/changes/address-search/specs/address-search/spec.md#requirement-an-address-field-over-the-pdok-locatieserver\n\t\t * @return {string}\n\t\t */\n\t\tbaseUrl() {\n\t\t\tif (this.endpoint) {\n\t\t\t\treturn this.endpoint.replace(/\\/+$/, '')\n\t\t\t}\n\t\t\tconst roots = (typeof window !== 'undefined' && window.OC && window.OC.appswebroots) || {}\n\t\t\tconst app = this.appCandidates.find((id) => Object.hasOwn(roots, id)) || this.appCandidates[0] || 'integriq'\n\t\t\treturn generateUrl(`/apps/${app}/api/pdok`)\n\t\t},\n\t},\n\n\tbeforeUnmount() {\n\t\tclearTimeout(this.timer)\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * A document as a picker option.\n\t\t *\n\t\t * @param {object} doc The Locatieserver document.\n\t\t * @param {number} index Its place in the list, for documents without an id.\n\t\t * @return {object|null} The option, or null without a text.\n\t\t */\n\t\toptionOf(doc, index) {\n\t\t\tconst label = addressText(doc)\n\t\t\tif (!label) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst kinds = {\n\t\t\t\tadres: t('nextcloud-vue', 'Address'),\n\t\t\t\taddress: t('nextcloud-vue', 'Address'),\n\t\t\t\tweg: t('nextcloud-vue', 'Street'),\n\t\t\t\twoonplaats: t('nextcloud-vue', 'Place'),\n\t\t\t\tcity: t('nextcloud-vue', 'Place'),\n\t\t\t\tpostcode: t('nextcloud-vue', 'Postcode'),\n\t\t\t}\n\t\t\tconst municipality = doc.gemeentenaam && !label.includes(doc.gemeentenaam) ? doc.gemeentenaam : ''\n\t\t\treturn {\n\t\t\t\tid: doc.id || `${CURRENT_ID}-${index}`,\n\t\t\t\tlabel,\n\t\t\t\tsecondary: [kinds[doc.type] || '', municipality].filter(Boolean).join(' · '),\n\t\t\t\ticon: 'MapMarkerOutline',\n\t\t\t\tdoc,\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Call the proxy; 404 or 503 marks search unavailable and yields `fallback`.\n\t\t *\n\t\t * @param {string} path The route under the base URL.\n\t\t * @param {object} params The query parameters.\n\t\t * @param {Array<object>} fallback What a degraded answer yields.\n\t\t * @return {Promise<Array<object>>} The documents.\n\t\t */\n\t\tasync request(path, params, fallback) {\n\t\t\ttry {\n\t\t\t\tconst { data } = await axios.get(prefixUrl(`${this.baseUrl}/${path}`), { params })\n\t\t\t\tthis.unavailable = false\n\t\t\t\treturn Array.isArray(data?.docs) ? data.docs : fallback\n\t\t\t} catch (error) {\n\t\t\t\tconst status = error?.response?.status\n\t\t\t\tif (status === 404 || status === 503) {\n\t\t\t\t\tthis.unavailable = true\n\t\t\t\t}\n\t\t\t\treturn fallback\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Search after typing rests.\n\t\t *\n\t\t * @param {string} query The typed text.\n\t\t */\n\t\tonSearch(query) {\n\t\t\tclearTimeout(this.timer)\n\t\t\tconst term = String(query || '').trim()\n\t\t\tif (term.length < this.minLength) {\n\t\t\t\tthis.options = []\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.timer = setTimeout(() => this.search(term), SEARCH_DELAY)\n\t\t},\n\n\t\t/**\n\t\t * Suggest addresses for the term; without suggestions, search free text.\n\t\t *\n\t\t * @spec openspec/changes/address-search/specs/address-search/spec.md#requirement-an-address-field-over-the-pdok-locatieserver\n\t\t * @param {string} term The search term.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync search(term) {\n\t\t\tconst seq = ++this.searchSeq\n\t\t\tthis.searching = true\n\t\t\tlet docs = await this.request('suggest', { q: term }, [])\n\t\t\tif (!docs.length && !this.unavailable && seq === this.searchSeq) {\n\t\t\t\tdocs = await this.request('free', { q: term, rows: this.rows }, [])\n\t\t\t}\n\t\t\tif (seq !== this.searchSeq) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.options = docs.map((doc, index) => this.optionOf(doc, index)).filter(Boolean)\n\t\t\tthis.searching = false\n\t\t},\n\n\t\t/**\n\t\t * Take the picked suggestion: look up the full document, then emit it.\n\t\t *\n\t\t * @spec openspec/changes/address-search/specs/address-search/spec.md#requirement-an-address-field-over-the-pdok-locatieserver\n\t\t * @param {string|null} id The picked option's id.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync onPick(id) {\n\t\t\tif (id === CURRENT_ID) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst option = id === null ? null : this.options.find((o) => String(o.id) === String(id))\n\t\t\tif (!option) {\n\t\t\t\t/** @event update:modelValue The chosen address text, or null when cleared. */\n\t\t\t\tthis.$emit('update:modelValue', null)\n\t\t\t\t/** @event select `{ result, coordinates, address }` for the chosen address, or null when cleared. */\n\t\t\t\tthis.$emit('select', null)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlet result = option.doc\n\t\t\tif (option.doc.id) {\n\t\t\t\tconst [full] = await this.request(`lookup/${encodeURIComponent(option.doc.id)}`, {}, [])\n\t\t\t\tresult = full || option.doc\n\t\t\t}\n\t\t\tconst address = addressText(result) || option.label\n\t\t\tthis.options = []\n\t\t\tthis.$emit('update:modelValue', address)\n\t\t\tthis.$emit('select', { result, coordinates: addressCoordinates(result), address })\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-address-search {\n\twidth: 100%;\n}\n\n.cn-address-search__note {\n\tmargin: 4px 0 0;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: var(--font-size-small, 13px);\n}\n</style>\n"],"names":["CnSearchSelect","axios"],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import { resolveComponent, openBlock, createElementBlock, createVNode, toDisplayString, createCommentVNode } from 'vue';
2
+
3
+ const _hoisted_1 = {
4
+ class: "cn-address-search",
5
+ "data-testid": "cn-address-search"
6
+ };
7
+ const _hoisted_2 = {
8
+ key: 0,
9
+ class: "cn-address-search__note",
10
+ role: "status",
11
+ "data-testid": "cn-address-search-unavailable"
12
+ };
13
+
14
+ function render(_ctx, _cache, $props, $setup, $data, $options) {
15
+ const _component_CnSearchSelect = resolveComponent("CnSearchSelect");
16
+
17
+ return (openBlock(), createElementBlock("div", _hoisted_1, [
18
+ createVNode(_component_CnSearchSelect, {
19
+ modelValue: $options.selectedOption ? $options.selectedOption.id : null,
20
+ options: $options.shownOptions,
21
+ inputLabel: $props.inputLabel || $options.t('nextcloud-vue', 'Search an address'),
22
+ labelOutside: $props.labelOutside,
23
+ placeholder: $props.placeholder || $options.t('nextcloud-vue', 'Street, postcode or place'),
24
+ loading: $data.searching,
25
+ filterable: false,
26
+ clearable: $props.clearable,
27
+ disabled: $props.disabled,
28
+ onSearch: $options.onSearch,
29
+ "onUpdate:modelValue": $options.onPick
30
+ }, null, 8 /* PROPS */, ["modelValue", "options", "inputLabel", "labelOutside", "placeholder", "loading", "clearable", "disabled", "onSearch", "onUpdate:modelValue"]),
31
+ ($data.unavailable)
32
+ ? (openBlock(), createElementBlock("p", _hoisted_2, toDisplayString($options.t('nextcloud-vue', 'Address search is not available right now.')), 1 /* TEXT */))
33
+ : createCommentVNode("v-if", true)
34
+ ]))
35
+ }
36
+
37
+ export { render };
38
+ //# sourceMappingURL=CnAddressSearch.vue3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnAddressSearch.vue3.js","sources":["../../../../src/components/CnAddressSearch/CnAddressSearch.vue?vue&type=template&id=5c8ae3f1&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-address-search\" data-testid=\"cn-address-search\">\n\t\t<CnSearchSelect\n\t\t\t:modelValue=\"selectedOption ? selectedOption.id : null\"\n\t\t\t:options=\"shownOptions\"\n\t\t\t:inputLabel=\"inputLabel || t('nextcloud-vue', 'Search an address')\"\n\t\t\t:labelOutside=\"labelOutside\"\n\t\t\t:placeholder=\"placeholder || t('nextcloud-vue', 'Street, postcode or place')\"\n\t\t\t:loading=\"searching\"\n\t\t\t:filterable=\"false\"\n\t\t\t:clearable=\"clearable\"\n\t\t\t:disabled=\"disabled\"\n\t\t\t@search=\"onSearch\"\n\t\t\t@update:modelValue=\"onPick\" />\n\t\t<p v-if=\"unavailable\"\n\t\t\tclass=\"cn-address-search__note\"\n\t\t\trole=\"status\"\n\t\t\tdata-testid=\"cn-address-search-unavailable\">\n\t\t\t{{ t('nextcloud-vue', 'Address search is not available right now.') }}\n\t\t</p>\n\t</div>\n</template>\n\n<script>\nimport axios from '@nextcloud/axios'\nimport { t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { prefixUrl } from '../../utils/headers.js'\nimport { CnSearchSelect } from '../CnSearchSelect/index.js'\n\n/** How long typing rests before a search runs, in ms. */\nconst SEARCH_DELAY = 250\n\n/** The id the current value carries in the option list. */\nconst CURRENT_ID = '__cn-address-current'\n\n/**\n * WGS84 coordinates from a Locatieserver document: a normalised\n * `location.coordinates` (`[lng, lat]`) or a raw `centroide_ll` WKT point.\n *\n * @param {object|null} doc The document.\n * @return {{lat: number, lng: number}|null} The coordinates, or null.\n */\nexport function addressCoordinates(doc) {\n\tif (!doc) {\n\t\treturn null\n\t}\n\tif (Array.isArray(doc.location?.coordinates)) {\n\t\tconst [lng, lat] = doc.location.coordinates\n\t\treturn { lat, lng }\n\t}\n\tconst match = typeof doc.centroide_ll === 'string' ? doc.centroide_ll.match(/POINT\\(([^ ]+) ([^ ]+)\\)/) : null\n\treturn match ? { lng: parseFloat(match[1]), lat: parseFloat(match[2]) } : null\n}\n\n/**\n * A Locatieserver document's display text.\n *\n * @param {object|null} doc The document.\n * @return {string} The address text.\n */\nexport function addressText(doc) {\n\treturn doc ? doc.displayName || doc.weergavenaam || doc.display || '' : ''\n}\n\n/**\n * CnAddressSearch — an address field over the PDOK Locatieserver (BAG\n * addresses, streets, places and postcodes): suggestions as the user types,\n * a free-text search when nothing is suggested, and on a pick the full\n * document with its coordinates. Built on CnSearchSelect.\n *\n * Browsers never call api.pdok.nl directly (ADR-022): requests go to a\n * PDOK proxy with `suggest`, `free` and `lookup/{id}` routes. By default\n * that is the `/api/pdok` route of the first installed app among\n * `appCandidates` (integriq, then openconnector); `endpoint` names another\n * base URL. When the proxy answers 404 or 503 the field says address search\n * is unavailable and stays usable, so a form around it can still be saved.\n *\n * `select` emits `{ result, coordinates, address }`: the looked-up document,\n * `{ lat, lng }` or null, and the display text. `update:modelValue` emits\n * the display text, or null when cleared.\n *\n * ```vue\n * <CnAddressSearch v-model=\"caseObject.address\" @select=\"onAddress\" />\n * ```\n */\nexport default {\n\tname: 'CnAddressSearch',\n\n\tcomponents: { CnSearchSelect },\n\n\tprops: {\n\t\t/** The chosen address text. */\n\t\tmodelValue: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Base URL of the PDOK proxy; empty resolves it from `appCandidates`. */\n\t\tendpoint: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** App ids that serve `/api/pdok`, newest first; the first installed one wins. */\n\t\tappCandidates: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => ['integriq', 'openconnector'],\n\t\t},\n\n\t\t/** The field's label; defaults to \"Search an address\". */\n\t\tinputLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Draw the label above the field. */\n\t\tlabelOutside: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Placeholder; defaults to \"Street, postcode or place\". */\n\t\tplaceholder: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Offer a clear button. */\n\t\tclearable: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\n\t\t/** Disable the field. */\n\t\tdisabled: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** The fewest characters a search needs. */\n\t\tminLength: {\n\t\t\ttype: Number,\n\t\t\tdefault: 3,\n\t\t},\n\n\t\t/** The most results a free-text search returns. */\n\t\trows: {\n\t\t\ttype: Number,\n\t\t\tdefault: 10,\n\t\t},\n\t},\n\n\temits: ['update:modelValue', 'select'],\n\n\tdata() {\n\t\treturn {\n\t\t\toptions: [],\n\t\t\tsearching: false,\n\t\t\tunavailable: false,\n\t\t\ttimer: null,\n\t\t\tsearchSeq: 0,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The current value as an option, so the field shows it.\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tselectedOption() {\n\t\t\treturn this.modelValue ? { id: CURRENT_ID, label: this.modelValue, icon: 'MapMarkerOutline' } : null\n\t\t},\n\n\t\t/**\n\t\t * The suggestions, with the current value kept in the list.\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\tshownOptions() {\n\t\t\treturn this.selectedOption ? [this.selectedOption, ...this.options] : this.options\n\t\t},\n\n\t\t/**\n\t\t * The proxy's base URL.\n\t\t *\n\t\t * @spec openspec/changes/address-search/specs/address-search/spec.md#requirement-an-address-field-over-the-pdok-locatieserver\n\t\t * @return {string}\n\t\t */\n\t\tbaseUrl() {\n\t\t\tif (this.endpoint) {\n\t\t\t\treturn this.endpoint.replace(/\\/+$/, '')\n\t\t\t}\n\t\t\tconst roots = (typeof window !== 'undefined' && window.OC && window.OC.appswebroots) || {}\n\t\t\tconst app = this.appCandidates.find((id) => Object.hasOwn(roots, id)) || this.appCandidates[0] || 'integriq'\n\t\t\treturn generateUrl(`/apps/${app}/api/pdok`)\n\t\t},\n\t},\n\n\tbeforeUnmount() {\n\t\tclearTimeout(this.timer)\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * A document as a picker option.\n\t\t *\n\t\t * @param {object} doc The Locatieserver document.\n\t\t * @param {number} index Its place in the list, for documents without an id.\n\t\t * @return {object|null} The option, or null without a text.\n\t\t */\n\t\toptionOf(doc, index) {\n\t\t\tconst label = addressText(doc)\n\t\t\tif (!label) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst kinds = {\n\t\t\t\tadres: t('nextcloud-vue', 'Address'),\n\t\t\t\taddress: t('nextcloud-vue', 'Address'),\n\t\t\t\tweg: t('nextcloud-vue', 'Street'),\n\t\t\t\twoonplaats: t('nextcloud-vue', 'Place'),\n\t\t\t\tcity: t('nextcloud-vue', 'Place'),\n\t\t\t\tpostcode: t('nextcloud-vue', 'Postcode'),\n\t\t\t}\n\t\t\tconst municipality = doc.gemeentenaam && !label.includes(doc.gemeentenaam) ? doc.gemeentenaam : ''\n\t\t\treturn {\n\t\t\t\tid: doc.id || `${CURRENT_ID}-${index}`,\n\t\t\t\tlabel,\n\t\t\t\tsecondary: [kinds[doc.type] || '', municipality].filter(Boolean).join(' · '),\n\t\t\t\ticon: 'MapMarkerOutline',\n\t\t\t\tdoc,\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Call the proxy; 404 or 503 marks search unavailable and yields `fallback`.\n\t\t *\n\t\t * @param {string} path The route under the base URL.\n\t\t * @param {object} params The query parameters.\n\t\t * @param {Array<object>} fallback What a degraded answer yields.\n\t\t * @return {Promise<Array<object>>} The documents.\n\t\t */\n\t\tasync request(path, params, fallback) {\n\t\t\ttry {\n\t\t\t\tconst { data } = await axios.get(prefixUrl(`${this.baseUrl}/${path}`), { params })\n\t\t\t\tthis.unavailable = false\n\t\t\t\treturn Array.isArray(data?.docs) ? data.docs : fallback\n\t\t\t} catch (error) {\n\t\t\t\tconst status = error?.response?.status\n\t\t\t\tif (status === 404 || status === 503) {\n\t\t\t\t\tthis.unavailable = true\n\t\t\t\t}\n\t\t\t\treturn fallback\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Search after typing rests.\n\t\t *\n\t\t * @param {string} query The typed text.\n\t\t */\n\t\tonSearch(query) {\n\t\t\tclearTimeout(this.timer)\n\t\t\tconst term = String(query || '').trim()\n\t\t\tif (term.length < this.minLength) {\n\t\t\t\tthis.options = []\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.timer = setTimeout(() => this.search(term), SEARCH_DELAY)\n\t\t},\n\n\t\t/**\n\t\t * Suggest addresses for the term; without suggestions, search free text.\n\t\t *\n\t\t * @spec openspec/changes/address-search/specs/address-search/spec.md#requirement-an-address-field-over-the-pdok-locatieserver\n\t\t * @param {string} term The search term.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync search(term) {\n\t\t\tconst seq = ++this.searchSeq\n\t\t\tthis.searching = true\n\t\t\tlet docs = await this.request('suggest', { q: term }, [])\n\t\t\tif (!docs.length && !this.unavailable && seq === this.searchSeq) {\n\t\t\t\tdocs = await this.request('free', { q: term, rows: this.rows }, [])\n\t\t\t}\n\t\t\tif (seq !== this.searchSeq) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.options = docs.map((doc, index) => this.optionOf(doc, index)).filter(Boolean)\n\t\t\tthis.searching = false\n\t\t},\n\n\t\t/**\n\t\t * Take the picked suggestion: look up the full document, then emit it.\n\t\t *\n\t\t * @spec openspec/changes/address-search/specs/address-search/spec.md#requirement-an-address-field-over-the-pdok-locatieserver\n\t\t * @param {string|null} id The picked option's id.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync onPick(id) {\n\t\t\tif (id === CURRENT_ID) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst option = id === null ? null : this.options.find((o) => String(o.id) === String(id))\n\t\t\tif (!option) {\n\t\t\t\t/** @event update:modelValue The chosen address text, or null when cleared. */\n\t\t\t\tthis.$emit('update:modelValue', null)\n\t\t\t\t/** @event select `{ result, coordinates, address }` for the chosen address, or null when cleared. */\n\t\t\t\tthis.$emit('select', null)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlet result = option.doc\n\t\t\tif (option.doc.id) {\n\t\t\t\tconst [full] = await this.request(`lookup/${encodeURIComponent(option.doc.id)}`, {}, [])\n\t\t\t\tresult = full || option.doc\n\t\t\t}\n\t\t\tconst address = addressText(result) || option.label\n\t\t\tthis.options = []\n\t\t\tthis.$emit('update:modelValue', address)\n\t\t\tthis.$emit('select', { result, coordinates: addressCoordinates(result), address })\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-address-search {\n\twidth: 100%;\n}\n\n.cn-address-search__note {\n\tmargin: 4px 0 0;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: var(--font-size-small, 13px);\n}\n</style>\n"],"names":[],"mappings":";;;AASM,EAAA,KAAK,EAAC,mBAAmB;AAAC,EAAA,aAAW,EAAC,mBAAmB;;;;AAc5D,EAAA,KAAE,EAAA,yBAAA;AACF,EAAA,IAAG,EAAA,QAAA;;;;;;;;eAH2B,CAAA,yBAAA,EAAA;AAV7B,MAAA,UAAU,EAAE,QAAA,CAAA,cAAc,GAAG,QAAA,CAAA,cAAc,CAAC,EAAG,GAAA,IAAA;AAC/C,MAAA,OAAO,EAAE,QAAS,CAAA,YAAA;MAClB,UAAU,EAAE,MAAK,CAAA,UAAA,IAAA,QAAA,CAAA,CAAA,CAAA,eAAA,EAAA,mBAAA,CAAA;AACjB,MAAA,YAAY,EAAE,MAAM,CAAA,YAAA;MACpB,WAAW,EAAE,MAAK,CAAA,WAAA,IAAA,QAAA,CAAA,CAAA,CAAA,eAAA,EAAA,2BAAA,CAAA;AAClB,MAAA,OAAO,EAAE,KAAO,CAAA,SAAA;AAChB,MAAA,UAAU,EAAE,KAAK;eACR,EAAE,MAAO,CAAA,SAAA;AAClB,MAAA,QAAQ,EAAE,MAAQ,CAAA,QAAA;AAClB,MAAA,QAAM,EAAE,QAAG,CAAA,QAAA;AACX,MAAA,qBAAiB,EAAE,QAAM,CAAA,MAAA;;KAClB,KAAW,CAAA,WAAA;;;;;;;;"}
@@ -0,0 +1,10 @@
1
+ import script from './CnAssignDialog.vue2.js';
2
+ export { withTypedAssignee } from './CnAssignDialog.vue2.js';
3
+ import { render } from './CnAssignDialog.vue3.js';
4
+
5
+ script.render = render;
6
+ script.__scopeId = "data-v-9921b726";
7
+ const __sfc_main = script;
8
+
9
+ export { __sfc_main as default };
10
+ //# sourceMappingURL=CnAssignDialog.vue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnAssignDialog.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;"}