@conduction/nextcloud-vue 2.76.0 → 2.78.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 (173) hide show
  1. package/dist/esm/components/CnAiCompanion/CnAiCompanion.vue2.js +23 -0
  2. package/dist/esm/components/CnAiCompanion/CnAiCompanion.vue2.js.map +1 -1
  3. package/dist/esm/components/CnAiCompanion/CnAiCompanion.vue3.js.map +1 -1
  4. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js +18 -3
  5. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
  6. package/dist/esm/components/CnAppNav/CnAppNav.vue3.js +49 -3
  7. package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
  8. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +2 -1
  9. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
  10. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
  11. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +79 -1
  12. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  13. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +229 -203
  14. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  15. package/dist/esm/components/CnCellRenderer/CnGroupNameCell.vue.js +9 -0
  16. package/dist/esm/components/CnCellRenderer/CnGroupNameCell.vue.js.map +1 -0
  17. package/dist/esm/components/CnCellRenderer/CnGroupNameCell.vue2.js +50 -0
  18. package/dist/esm/components/CnCellRenderer/CnGroupNameCell.vue2.js.map +1 -0
  19. package/dist/esm/components/CnCellRenderer/CnGroupNameCell.vue3.js +13 -0
  20. package/dist/esm/components/CnCellRenderer/CnGroupNameCell.vue3.js.map +1 -0
  21. package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js +37 -0
  22. package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
  23. package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js +2 -2
  24. package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
  25. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +29 -4
  26. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  27. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  28. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +48 -6
  29. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  30. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +90 -51
  31. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  32. package/dist/esm/components/CnDateRangePicker/CnDateRangePicker.vue.js +1 -1
  33. package/dist/esm/components/CnDateRangePicker/CnDateRangePicker.vue2.js +45 -2
  34. package/dist/esm/components/CnDateRangePicker/CnDateRangePicker.vue2.js.map +1 -1
  35. package/dist/esm/components/CnDateRangePicker/CnDateRangePicker.vue3.js.map +1 -1
  36. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +16 -28
  37. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  38. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +12 -24
  39. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  40. package/dist/esm/components/CnFavouriteToggle/CnFavouriteToggle.vue2.js +7 -2
  41. package/dist/esm/components/CnFavouriteToggle/CnFavouriteToggle.vue2.js.map +1 -1
  42. package/dist/esm/components/CnFavouriteToggle/CnFavouriteToggle.vue3.js.map +1 -1
  43. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +114 -1
  44. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  45. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +115 -62
  46. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  47. package/dist/esm/components/CnFollowToggle/CnFollowToggle.vue2.js +89 -7
  48. package/dist/esm/components/CnFollowToggle/CnFollowToggle.vue2.js.map +1 -1
  49. package/dist/esm/components/CnFollowToggle/CnFollowToggle.vue3.js +62 -30
  50. package/dist/esm/components/CnFollowToggle/CnFollowToggle.vue3.js.map +1 -1
  51. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +7 -1
  52. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
  53. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
  54. package/dist/esm/components/CnFormPage/CnFormPage.vue2.js +4 -1
  55. package/dist/esm/components/CnFormPage/CnFormPage.vue2.js.map +1 -1
  56. package/dist/esm/components/CnFormPage/CnFormPage.vue3.js.map +1 -1
  57. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +104 -13
  58. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  59. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +13 -9
  60. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  61. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js +199 -7
  62. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js.map +1 -1
  63. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js +155 -119
  64. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js.map +1 -1
  65. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +6 -0
  66. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  67. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +4 -1
  68. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  69. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +36 -0
  70. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
  71. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js +35 -23
  72. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
  73. package/dist/esm/components/CnStatusBadge/CnStatusBadge.vue2.js +6 -2
  74. package/dist/esm/components/CnStatusBadge/CnStatusBadge.vue2.js.map +1 -1
  75. package/dist/esm/components/CnStatusBadge/CnStatusBadge.vue3.js.map +1 -1
  76. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +13 -0
  77. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
  78. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
  79. package/dist/esm/composables/useFormDraft.js +11 -1
  80. package/dist/esm/composables/useFormDraft.js.map +1 -1
  81. package/dist/esm/composables/useListView.js +4 -0
  82. package/dist/esm/composables/useListView.js.map +1 -1
  83. package/dist/esm/composables/useObjectPresence.js +15 -4
  84. package/dist/esm/composables/useObjectPresence.js.map +1 -1
  85. package/dist/esm/index.js +2 -1
  86. package/dist/esm/index.js.map +1 -1
  87. package/dist/esm/l10n/en.json.js +32 -1
  88. package/dist/esm/l10n/en.json.js.map +1 -1
  89. package/dist/esm/l10n/nl.json.js +32 -1
  90. package/dist/esm/l10n/nl.json.js.map +1 -1
  91. package/dist/esm/nextcloud-vue.css +500 -75
  92. package/dist/esm/store/plugins/interactions.js +20 -10
  93. package/dist/esm/store/plugins/interactions.js.map +1 -1
  94. package/dist/esm/store/useObjectStore.js +17 -0
  95. package/dist/esm/store/useObjectStore.js.map +1 -1
  96. package/dist/esm/utils/badgeVariants.js +32 -0
  97. package/dist/esm/utils/badgeVariants.js.map +1 -0
  98. package/dist/esm/utils/builtInFormatters.js +15 -7
  99. package/dist/esm/utils/builtInFormatters.js.map +1 -1
  100. package/dist/esm/utils/groupAutocomplete.js +63 -1
  101. package/dist/esm/utils/groupAutocomplete.js.map +1 -1
  102. package/dist/esm/utils/lensAvailability.js +90 -0
  103. package/dist/esm/utils/lensAvailability.js.map +1 -0
  104. package/dist/esm/utils/personalLenses.js +13 -6
  105. package/dist/esm/utils/personalLenses.js.map +1 -1
  106. package/dist/esm/utils/recordInteractions.js +31 -13
  107. package/dist/esm/utils/recordInteractions.js.map +1 -1
  108. package/dist/esm/utils/resolveFilterTokens.js +46 -3
  109. package/dist/esm/utils/resolveFilterTokens.js.map +1 -1
  110. package/dist/esm/utils/schema.js +1 -1
  111. package/dist/esm/utils/schema.js.map +1 -1
  112. package/dist/esm/utils/sentinelTokens.js +4 -4
  113. package/dist/esm/utils/sentinelTokens.js.map +1 -1
  114. package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
  115. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  116. package/dist/esm/utils/widgetVisibility.js +46 -1
  117. package/dist/esm/utils/widgetVisibility.js.map +1 -1
  118. package/dist/nextcloud-vue.cjs.js +4389 -2890
  119. package/dist/nextcloud-vue.cjs.js.map +2 -2
  120. package/dist/nextcloud-vue.css +500 -75
  121. package/l10n/en.json +32 -1
  122. package/l10n/nl.json +32 -1
  123. package/package.json +1 -1
  124. package/src/components/CnAiCompanion/CnAiCompanion.vue +23 -0
  125. package/src/components/CnAppNav/CnAppNav.vue +44 -5
  126. package/src/components/CnBoardView/CnBoardView.vue +2 -1
  127. package/src/components/CnCellRenderer/CnCellRenderer.md +1 -0
  128. package/src/components/CnCellRenderer/CnCellRenderer.vue +106 -9
  129. package/src/components/CnCellRenderer/CnGroupNameCell.vue +61 -0
  130. package/src/components/CnChartWidget/CnChartWidget.vue +39 -2
  131. package/src/components/CnDashboardPage/CnDashboardPage.vue +29 -4
  132. package/src/components/CnDataTable/CnDataTable.md +9 -0
  133. package/src/components/CnDataTable/CnDataTable.vue +87 -22
  134. package/src/components/CnDateRangePicker/CnDateRangePicker.vue +44 -1
  135. package/src/components/CnDetailPage/CnDetailPage.vue +10 -27
  136. package/src/components/CnFavouriteToggle/CnFavouriteToggle.vue +7 -2
  137. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +204 -5
  138. package/src/components/CnFollowToggle/CnFollowToggle.vue +118 -13
  139. package/src/components/CnFormDialog/CnFormDialog.vue +7 -1
  140. package/src/components/CnFormPage/CnFormPage.vue +4 -1
  141. package/src/components/CnIndexPage/CnIndexPage.md +36 -0
  142. package/src/components/CnIndexPage/CnIndexPage.vue +112 -19
  143. package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +226 -2
  144. package/src/components/CnObjectSidebar/CnFilesTab.vue +9 -0
  145. package/src/components/CnStatWidget/CnStatWidget.vue +52 -2
  146. package/src/components/CnStatusBadge/CnStatusBadge.vue +6 -2
  147. package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +13 -0
  148. package/src/composables/__tests__/useObjectPresence.spec.js +24 -0
  149. package/src/composables/useFormDraft.js +11 -1
  150. package/src/composables/useListView.js +4 -0
  151. package/src/composables/useObjectPresence.js +15 -4
  152. package/src/css/badge.css +31 -0
  153. package/src/css/kpi-card.css +51 -0
  154. package/src/css/look-board-detail.css +40 -2
  155. package/src/css/look-board-index.css +36 -0
  156. package/src/css/look-board.css +8 -0
  157. package/src/css/table.css +82 -17
  158. package/src/index.js +2 -1
  159. package/src/schemas/app-manifest-v2.schema.json +15 -11
  160. package/src/store/plugins/interactions.js +20 -10
  161. package/src/store/useObjectStore.js +17 -0
  162. package/src/utils/badgeVariants.js +29 -0
  163. package/src/utils/builtInFormatters.js +15 -7
  164. package/src/utils/groupAutocomplete.js +75 -0
  165. package/src/utils/index.js +1 -1
  166. package/src/utils/lensAvailability.js +86 -0
  167. package/src/utils/personalLenses.js +13 -6
  168. package/src/utils/recordInteractions.js +31 -13
  169. package/src/utils/resolveFilterTokens.js +45 -2
  170. package/src/utils/schema.js +1 -1
  171. package/src/utils/sentinelTokens.js +4 -4
  172. package/src/utils/validateManifestV2.compiled.js +2 -2
  173. package/src/utils/widgetVisibility.js +45 -0
@@ -1,21 +1,24 @@
1
1
  import axios__default from '@nextcloud/axios';
2
2
  import { translate } from '@nextcloud/l10n';
3
3
  import { generateUrl } from '@nextcloud/router';
4
- import { NcButton, NcLoadingIcon, NcTextField, NcSelect, NcCheckboxRadioSwitch, NcActionButton } from '@nextcloud/vue';
4
+ import { NcButton, NcLoadingIcon, NcTextField, NcSelect, NcSelectUsers, NcCheckboxRadioSwitch, NcActionButton } from '@nextcloud/vue';
5
5
  import Check from 'vue-material-design-icons/Check.vue';
6
6
  import Close from 'vue-material-design-icons/Close.vue';
7
7
  import ContentSaveOutline from 'vue-material-design-icons/ContentSaveOutline.vue';
8
8
  import InformationOutline from 'vue-material-design-icons/InformationOutline.vue';
9
9
  import Pencil from 'vue-material-design-icons/Pencil.vue';
10
- import __sfc_main$3 from '../CnFormDialog/CnFormDialog.vue.js';
10
+ import __sfc_main$1 from '../CnCellRenderer/CnGroupNameCell.vue.js';
11
+ import __sfc_main$4 from '../CnFormDialog/CnFormDialog.vue.js';
11
12
  import { normalizeLook } from '../../composables/useLook.js';
13
+ import { groupDisplayName, searchNextcloudGroups, loadGroupDisplayName } from '../../utils/groupAutocomplete.js';
12
14
  import { PANEL_ACTION_SINK } from '../../utils/panelActions.js';
13
15
  import { resolveFilterTokens } from '../../utils/resolveFilterTokens.js';
14
16
  import { fieldsFromSchema, formatValue } from '../../utils/schema.js';
15
17
  import { schemaRefSlug } from '../../utils/schemaRefSlug.js';
18
+ import { searchNextcloudUsers, resolveNextcloudUser } from '../../utils/userAutocomplete.js';
16
19
  import __sfc_main from '../CnIcon/CnIcon.vue.js';
17
- import __sfc_main$2 from '../CnObjectMetadataModal/CnObjectMetadataModal.vue.js';
18
- import __sfc_main$1 from '../CnWidgetWrapper/CnWidgetWrapper.vue.js';
20
+ import __sfc_main$3 from '../CnObjectMetadataModal/CnObjectMetadataModal.vue.js';
21
+ import __sfc_main$2 from '../CnWidgetWrapper/CnWidgetWrapper.vue.js';
19
22
  import { useObjectStore } from '../../store/useObjectStore.js';
20
23
 
21
24
  /**
@@ -58,11 +61,13 @@ var script = {
58
61
  NcLoadingIcon,
59
62
  NcTextField,
60
63
  NcSelect,
64
+ NcSelectUsers,
65
+ CnGroupNameCell: __sfc_main$1,
61
66
  NcCheckboxRadioSwitch,
62
67
  NcActionButton,
63
- CnWidgetWrapper: __sfc_main$1,
64
- CnObjectMetadataModal: __sfc_main$2,
65
- CnFormDialog: __sfc_main$3,
68
+ CnWidgetWrapper: __sfc_main$2,
69
+ CnObjectMetadataModal: __sfc_main$3,
70
+ CnFormDialog: __sfc_main$4,
66
71
  ContentSaveOutline,
67
72
  InformationOutline,
68
73
  Pencil,
@@ -437,6 +442,12 @@ var script = {
437
442
  relationOptions: {},
438
443
  /** Whether relation picker options are being fetched. */
439
444
  relationOptionsLoading: false,
445
+ /** Group and user picker options per field key ({ id, label }[]). */
446
+ personOptions: {},
447
+ /** Key of the group or user field whose options are loading, or null. */
448
+ personLoading: null,
449
+ /** Display names of users seen in a user picker, by uid. */
450
+ userLabels: {},
440
451
  /** Whether the user expanded the widget to see every field. */
441
452
  expanded: false,
442
453
  /** Fields that fit the cell while collapsed; null when all fit. */
@@ -1350,6 +1361,11 @@ var script = {
1350
1361
  if (this.isSingleRelationField(field.key)) {
1351
1362
  this.loadRelationOptions(field.key);
1352
1363
  }
1364
+ // Group and user fields pick from Nextcloud's own directory.
1365
+ if (this.isPersonPicker(field)) {
1366
+ this.searchPeople(field, '');
1367
+ this.resolvePersonLabels(field, currentValue);
1368
+ }
1353
1369
 
1354
1370
  this.$nextTick(() => {
1355
1371
  // Focus the editor
@@ -1575,6 +1591,182 @@ var script = {
1575
1591
  return options.find((opt) => opt.id === val) || { id: val, label: String(val) }
1576
1592
  },
1577
1593
 
1594
+ /**
1595
+ * Whether a field picks a Nextcloud group (widget `group` or
1596
+ * `group-multiselect`, from `referenceType: nextcloud-group`).
1597
+ *
1598
+ * @spec openspec/changes/nextcloud-group-surfaces/specs/data-display/spec.md#requirement-the-inline-editor-picks-a-nextcloud-group-or-user
1599
+ * @param {object} field Field descriptor.
1600
+ * @return {boolean}
1601
+ */
1602
+ isGroupPicker(field) {
1603
+ return !!field && (field.widget === 'group' || field.widget === 'group-multiselect')
1604
+ },
1605
+
1606
+ /**
1607
+ * Whether a field picks a Nextcloud user (widget `user` or
1608
+ * `user-multiselect`, from `referenceType: nextcloud-user`).
1609
+ *
1610
+ * @spec openspec/changes/nextcloud-group-surfaces/specs/data-display/spec.md#requirement-the-inline-editor-picks-a-nextcloud-group-or-user
1611
+ * @param {object} field Field descriptor.
1612
+ * @return {boolean}
1613
+ */
1614
+ isUserPicker(field) {
1615
+ return !!field && (field.widget === 'user' || field.widget === 'user-multiselect')
1616
+ },
1617
+
1618
+ /**
1619
+ * Whether a field picks a group or a user.
1620
+ *
1621
+ * @spec openspec/changes/nextcloud-group-surfaces/specs/data-display/spec.md#requirement-the-inline-editor-picks-a-nextcloud-group-or-user
1622
+ * @param {object} field Field descriptor.
1623
+ * @return {boolean}
1624
+ */
1625
+ isPersonPicker(field) {
1626
+ return this.isGroupPicker(field) || this.isUserPicker(field)
1627
+ },
1628
+
1629
+ /**
1630
+ * Whether a group or user field holds a list.
1631
+ *
1632
+ * @spec openspec/changes/nextcloud-group-surfaces/specs/data-display/spec.md#requirement-the-inline-editor-picks-a-nextcloud-group-or-user
1633
+ * @param {object} field Field descriptor.
1634
+ * @return {boolean}
1635
+ */
1636
+ isPersonMultiple(field) {
1637
+ return !!field && (field.widget === 'group-multiselect' || field.widget === 'user-multiselect')
1638
+ },
1639
+
1640
+ /**
1641
+ * The group ids a group field holds, for the display mode.
1642
+ *
1643
+ * @spec openspec/changes/nextcloud-group-surfaces/specs/data-display/spec.md#requirement-the-inline-editor-picks-a-nextcloud-group-or-user
1644
+ * @param {object} field Field descriptor.
1645
+ * @return {string[]}
1646
+ */
1647
+ groupIdsOf(field) {
1648
+ const raw = this.rawOf(field);
1649
+ const list = Array.isArray(raw) ? raw : [raw];
1650
+ return list.filter((v) => typeof v === 'string' && v !== '')
1651
+ },
1652
+
1653
+ /**
1654
+ * The display name of a group id or uid, falling back to the id.
1655
+ *
1656
+ * @spec openspec/changes/nextcloud-group-surfaces/specs/data-display/spec.md#requirement-the-inline-editor-picks-a-nextcloud-group-or-user
1657
+ * @param {object} field Field descriptor.
1658
+ * @param {string} id The group id or uid.
1659
+ * @return {string}
1660
+ */
1661
+ personLabel(field, id) {
1662
+ if (this.isGroupPicker(field)) {
1663
+ return groupDisplayName(id) || String(id)
1664
+ }
1665
+ return this.userLabels[id] || String(id)
1666
+ },
1667
+
1668
+ /**
1669
+ * Search Nextcloud groups or users for a picker. Fails soft (the
1670
+ * helpers return `[]`), so the picker stays usable with what it has.
1671
+ *
1672
+ * @spec openspec/changes/nextcloud-group-surfaces/specs/data-display/spec.md#requirement-the-inline-editor-picks-a-nextcloud-group-or-user
1673
+ * @param {object} field Field descriptor.
1674
+ * @param {string} query The search term.
1675
+ * @return {Promise<void>}
1676
+ */
1677
+ async searchPeople(field, query) {
1678
+ this.personLoading = field.key;
1679
+ try {
1680
+ const options = this.isGroupPicker(field)
1681
+ ? await searchNextcloudGroups(query || '')
1682
+ : await searchNextcloudUsers(query || '');
1683
+ const list = Array.isArray(options) ? options : [];
1684
+ if (this.isUserPicker(field)) {
1685
+ const labels = { ...this.userLabels };
1686
+ for (const opt of list) {
1687
+ labels[opt.id] = opt.label;
1688
+ }
1689
+ this.userLabels = labels;
1690
+ }
1691
+ this.personOptions = { ...this.personOptions, [field.key]: list };
1692
+ } finally {
1693
+ if (this.personLoading === field.key) {
1694
+ this.personLoading = null;
1695
+ }
1696
+ }
1697
+ },
1698
+
1699
+ /**
1700
+ * Look up the display names of the value a group or user field holds,
1701
+ * so the open editor shows names rather than ids.
1702
+ *
1703
+ * @spec openspec/changes/nextcloud-group-surfaces/specs/data-display/spec.md#requirement-the-inline-editor-picks-a-nextcloud-group-or-user
1704
+ * @param {object} field Field descriptor.
1705
+ * @param {unknown} value The held value (an id or a list of ids).
1706
+ * @return {Promise<void>}
1707
+ */
1708
+ async resolvePersonLabels(field, value) {
1709
+ const ids = (Array.isArray(value) ? value : [value])
1710
+ .filter((v) => typeof v === 'string' && v !== '');
1711
+ if (this.isGroupPicker(field)) {
1712
+ await Promise.all(ids.map((gid) => loadGroupDisplayName(gid)));
1713
+ return
1714
+ }
1715
+ const unknown = ids.filter((uid) => !this.userLabels[uid]);
1716
+ const options = await Promise.all(unknown.map((uid) => resolveNextcloudUser(uid)));
1717
+ if (options.length > 0) {
1718
+ const labels = { ...this.userLabels };
1719
+ for (const opt of options) {
1720
+ if (opt && opt.id) {
1721
+ labels[opt.id] = opt.label || opt.id;
1722
+ }
1723
+ }
1724
+ this.userLabels = labels;
1725
+ }
1726
+ },
1727
+
1728
+ /**
1729
+ * The selected option(s) of a group or user picker.
1730
+ *
1731
+ * @spec openspec/changes/nextcloud-group-surfaces/specs/data-display/spec.md#requirement-the-inline-editor-picks-a-nextcloud-group-or-user
1732
+ * @param {object} field Field descriptor.
1733
+ * @return {object|object[]|null}
1734
+ */
1735
+ personSelected(field) {
1736
+ const toOption = (id) => {
1737
+ const label = this.personLabel(field, id);
1738
+ return { id, label, displayName: label }
1739
+ };
1740
+ const v = this.editData[field.key];
1741
+ if (this.isPersonMultiple(field)) {
1742
+ return (Array.isArray(v) ? v : [])
1743
+ .filter((id) => typeof id === 'string' && id !== '')
1744
+ .map(toOption)
1745
+ }
1746
+ return (typeof v === 'string' && v !== '') ? toOption(v) : null
1747
+ },
1748
+
1749
+ /**
1750
+ * Store a group or user choice as the id (or list of ids).
1751
+ *
1752
+ * @spec openspec/changes/nextcloud-group-surfaces/specs/data-display/spec.md#requirement-the-inline-editor-picks-a-nextcloud-group-or-user
1753
+ * @param {object} field Field descriptor.
1754
+ * @param {object|object[]|null} selected The chosen option(s).
1755
+ */
1756
+ onPersonChange(field, selected) {
1757
+ if (this.isPersonMultiple(field)) {
1758
+ const ids = (Array.isArray(selected) ? selected : [])
1759
+ .map((opt) => (opt && typeof opt === 'object' ? opt.id : opt))
1760
+ .filter((id) => typeof id === 'string' && id !== '');
1761
+ this.updateField(field.key, ids);
1762
+ return
1763
+ }
1764
+ if (selected && this.isUserPicker(field) && selected.id) {
1765
+ this.userLabels = { ...this.userLabels, [selected.id]: selected.label || selected.id };
1766
+ }
1767
+ this.updateField(field.key, selected && selected.id ? selected.id : null);
1768
+ },
1769
+
1578
1770
  onSelectChange(field, option) {
1579
1771
  this.updateField(field.key, option ? option.id : null);
1580
1772
  },