@conduction/nextcloud-vue 0.1.0-beta.1 → 0.1.0-beta.11

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 (208) hide show
  1. package/README.md +226 -0
  2. package/css/index.css +5 -0
  3. package/dist/nextcloud-vue.cjs.js +79416 -7715
  4. package/dist/nextcloud-vue.cjs.js.map +1 -1
  5. package/dist/nextcloud-vue.css +3583 -504
  6. package/dist/nextcloud-vue.esm.js +79343 -7692
  7. package/dist/nextcloud-vue.esm.js.map +1 -1
  8. package/l10n/en.json +164 -0
  9. package/l10n/nl.json +164 -0
  10. package/package.json +104 -63
  11. package/src/components/CnActionsBar/CnActionsBar.vue +254 -0
  12. package/src/components/CnActionsBar/index.js +1 -0
  13. package/src/components/CnAdvancedFormDialog/CnAdvancedFormDialog.vue +570 -0
  14. package/src/components/CnAdvancedFormDialog/CnDataTab.vue +217 -0
  15. package/src/components/CnAdvancedFormDialog/CnMetadataTab.vue +121 -0
  16. package/src/components/CnAdvancedFormDialog/CnPropertiesTab.vue +422 -0
  17. package/src/components/CnAdvancedFormDialog/CnPropertyValueCell.vue +247 -0
  18. package/src/components/CnAdvancedFormDialog/index.js +1 -0
  19. package/src/components/CnCard/CnCard.vue +415 -0
  20. package/src/components/CnCard/index.js +1 -0
  21. package/src/components/CnCardGrid/CnCardGrid.vue +156 -152
  22. package/src/components/CnCardGrid/index.js +1 -1
  23. package/src/components/CnCellRenderer/CnCellRenderer.vue +132 -132
  24. package/src/components/CnCellRenderer/index.js +1 -1
  25. package/src/components/CnChartWidget/CnChartWidget.vue +346 -0
  26. package/src/components/CnChartWidget/index.js +1 -0
  27. package/src/components/CnConfigurationCard/CnConfigurationCard.vue +77 -77
  28. package/src/components/CnConfigurationCard/index.js +1 -1
  29. package/src/components/CnContextMenu/CnContextMenu.vue +142 -0
  30. package/src/components/CnContextMenu/index.js +1 -0
  31. package/src/components/CnCopyDialog/CnCopyDialog.vue +266 -0
  32. package/src/components/CnCopyDialog/index.js +1 -0
  33. package/src/components/CnDashboardGrid/CnDashboardGrid.vue +229 -0
  34. package/src/components/CnDashboardGrid/index.js +1 -0
  35. package/src/components/CnDashboardPage/CnDashboardPage.vue +397 -0
  36. package/src/components/CnDashboardPage/index.js +1 -0
  37. package/src/components/CnDataTable/CnDataTable.vue +362 -354
  38. package/src/components/CnDataTable/index.js +1 -1
  39. package/src/components/CnDeleteDialog/CnDeleteDialog.vue +177 -0
  40. package/src/components/CnDeleteDialog/index.js +1 -0
  41. package/src/components/CnDetailCard/CnDetailCard.vue +225 -0
  42. package/src/components/CnDetailCard/index.js +1 -0
  43. package/src/components/CnDetailGrid/CnDetailGrid.vue +256 -0
  44. package/src/components/CnDetailGrid/index.js +1 -0
  45. package/src/components/CnDetailPage/CnDetailPage.vue +432 -0
  46. package/src/components/CnDetailPage/index.js +1 -0
  47. package/src/components/CnFacetSidebar/CnFacetSidebar.vue +234 -223
  48. package/src/components/CnFacetSidebar/index.js +1 -1
  49. package/src/components/CnFilterBar/CnFilterBar.vue +153 -152
  50. package/src/components/CnFilterBar/index.js +1 -1
  51. package/src/components/CnFormDialog/CnFormDialog.vue +1047 -0
  52. package/src/components/CnFormDialog/index.js +1 -0
  53. package/src/components/CnIcon/CnIcon.vue +89 -0
  54. package/src/components/CnIcon/index.js +1 -0
  55. package/src/components/CnIndexPage/CnIndexPage.vue +980 -682
  56. package/src/components/CnIndexPage/index.js +1 -1
  57. package/src/components/CnIndexSidebar/CnIndexSidebar.vue +536 -0
  58. package/src/components/CnIndexSidebar/index.js +1 -0
  59. package/src/components/CnInfoWidget/CnInfoWidget.vue +219 -0
  60. package/src/components/CnInfoWidget/index.js +1 -0
  61. package/src/components/CnItemCard/CnItemCard.vue +134 -0
  62. package/src/components/CnItemCard/index.js +1 -0
  63. package/src/components/CnJsonViewer/CnJsonViewer.vue +312 -0
  64. package/src/components/CnJsonViewer/index.js +1 -0
  65. package/src/components/CnKpiGrid/CnKpiGrid.vue +93 -89
  66. package/src/components/CnKpiGrid/index.js +1 -1
  67. package/src/components/CnMassActionBar/CnMassActionBar.vue +161 -160
  68. package/src/components/CnMassActionBar/index.js +1 -1
  69. package/src/components/CnMassCopyDialog/CnMassCopyDialog.vue +327 -320
  70. package/src/components/CnMassCopyDialog/index.js +1 -1
  71. package/src/components/CnMassDeleteDialog/CnMassDeleteDialog.vue +245 -238
  72. package/src/components/CnMassDeleteDialog/index.js +1 -1
  73. package/src/components/CnMassExportDialog/CnMassExportDialog.vue +191 -190
  74. package/src/components/CnMassExportDialog/index.js +1 -1
  75. package/src/components/CnMassImportDialog/CnMassImportDialog.vue +494 -491
  76. package/src/components/CnMassImportDialog/index.js +1 -1
  77. package/src/components/CnNoteCard/CnNoteCard.vue +149 -0
  78. package/src/components/CnNoteCard/index.js +1 -0
  79. package/src/components/CnNotesCard/CnNotesCard.vue +416 -0
  80. package/src/components/CnNotesCard/index.js +1 -0
  81. package/src/components/CnObjectCard/CnObjectCard.vue +294 -292
  82. package/src/components/CnObjectCard/index.js +1 -1
  83. package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +854 -0
  84. package/src/components/CnObjectDataWidget/index.js +1 -0
  85. package/src/components/CnObjectMetadataWidget/CnObjectMetadataWidget.vue +289 -0
  86. package/src/components/CnObjectMetadataWidget/index.js +1 -0
  87. package/src/components/CnObjectSidebar/CnAuditTrailTab.vue +369 -0
  88. package/src/components/CnObjectSidebar/CnFilesTab.vue +287 -0
  89. package/src/components/CnObjectSidebar/CnNotesTab.vue +250 -0
  90. package/src/components/CnObjectSidebar/CnObjectSidebar.vue +255 -0
  91. package/src/components/CnObjectSidebar/CnTagsTab.vue +259 -0
  92. package/src/components/CnObjectSidebar/CnTasksTab.vue +483 -0
  93. package/src/components/CnObjectSidebar/index.js +6 -0
  94. package/src/components/CnPageHeader/CnPageHeader.vue +61 -0
  95. package/src/components/CnPageHeader/index.js +1 -0
  96. package/src/components/CnPagination/CnPagination.vue +253 -252
  97. package/src/components/CnPagination/index.js +1 -1
  98. package/src/components/CnProgressBar/CnProgressBar.vue +262 -0
  99. package/src/components/CnProgressBar/index.js +1 -0
  100. package/src/components/CnRegisterMapping/CnRegisterMapping.vue +793 -0
  101. package/src/components/CnRegisterMapping/index.js +1 -0
  102. package/src/components/CnRowActions/CnRowActions.vue +95 -73
  103. package/src/components/CnRowActions/index.js +1 -1
  104. package/src/components/CnSchemaFormDialog/CnSchemaConfigurationTab.vue +226 -0
  105. package/src/components/CnSchemaFormDialog/CnSchemaFormDialog.vue +788 -0
  106. package/src/components/CnSchemaFormDialog/CnSchemaPropertiesTab.vue +305 -0
  107. package/src/components/CnSchemaFormDialog/CnSchemaPropertyActions.vue +1398 -0
  108. package/src/components/CnSchemaFormDialog/CnSchemaSecurityTab.vue +236 -0
  109. package/src/components/CnSchemaFormDialog/index.js +1 -0
  110. package/src/components/CnSettingsCard/CnSettingsCard.vue +92 -92
  111. package/src/components/CnSettingsCard/index.js +1 -1
  112. package/src/components/CnSettingsSection/CnSettingsSection.vue +267 -266
  113. package/src/components/CnSettingsSection/index.js +1 -1
  114. package/src/components/CnStatsBlock/CnStatsBlock.vue +437 -366
  115. package/src/components/CnStatsBlock/index.js +1 -1
  116. package/src/components/CnStatsPanel/CnStatsPanel.vue +321 -0
  117. package/src/components/CnStatsPanel/index.js +1 -0
  118. package/src/components/CnStatusBadge/CnStatusBadge.vue +90 -77
  119. package/src/components/CnStatusBadge/index.js +1 -1
  120. package/src/components/CnTabbedFormDialog/CnTabbedFormDialog.vue +545 -0
  121. package/src/components/CnTabbedFormDialog/index.js +1 -0
  122. package/src/components/CnTableWidget/CnTableWidget.vue +333 -0
  123. package/src/components/CnTableWidget/index.js +1 -0
  124. package/src/components/CnTasksCard/CnTasksCard.vue +374 -0
  125. package/src/components/CnTasksCard/index.js +1 -0
  126. package/src/components/CnTileWidget/CnTileWidget.vue +159 -0
  127. package/src/components/CnTileWidget/index.js +1 -0
  128. package/src/components/CnTimelineStages/CnTimelineStages.vue +294 -0
  129. package/src/components/CnTimelineStages/index.js +1 -0
  130. package/src/components/CnUserActionMenu/CnUserActionMenu.vue +436 -0
  131. package/src/components/CnUserActionMenu/index.js +1 -0
  132. package/src/components/CnVersionInfoCard/CnVersionInfoCard.vue +313 -312
  133. package/src/components/CnVersionInfoCard/index.js +1 -1
  134. package/src/components/CnWidgetRenderer/CnWidgetRenderer.vue +180 -0
  135. package/src/components/CnWidgetRenderer/index.js +1 -0
  136. package/src/components/CnWidgetWrapper/CnWidgetWrapper.vue +248 -0
  137. package/src/components/CnWidgetWrapper/index.js +1 -0
  138. package/src/components/index.js +57 -25
  139. package/src/composables/index.js +5 -3
  140. package/src/composables/useContextMenu.js +126 -0
  141. package/src/composables/useDashboardView.js +286 -0
  142. package/src/composables/useDetailView.js +290 -132
  143. package/src/composables/useListView.js +364 -153
  144. package/src/composables/useSubResource.js +142 -142
  145. package/src/constants/metadata.js +30 -0
  146. package/src/css/CnSchemaFormDialog.css +546 -0
  147. package/src/css/__sample_nextcloud_tokens.css +110 -0
  148. package/src/css/actions-bar.css +54 -0
  149. package/src/css/badge.css +83 -51
  150. package/src/css/card.css +129 -128
  151. package/src/css/context-menu.css +20 -0
  152. package/src/css/dashboard.css +70 -0
  153. package/src/css/detail-page.css +235 -0
  154. package/src/css/detail.css +68 -68
  155. package/src/css/index-page.css +44 -0
  156. package/src/css/index-sidebar.css +193 -0
  157. package/src/css/index.css +17 -8
  158. package/src/css/layout.css +90 -90
  159. package/src/css/page-header.css +35 -0
  160. package/src/css/pagination.css +72 -72
  161. package/src/css/table.css +142 -143
  162. package/src/css/timeline-stages.css +220 -0
  163. package/src/css/utilities.css +46 -46
  164. package/src/index.js +95 -50
  165. package/src/l10n/index.js +12 -0
  166. package/src/mixins/gridLayout.js +118 -0
  167. package/src/store/createCrudStore.d.ts +350 -0
  168. package/src/store/createCrudStore.js +413 -0
  169. package/src/store/createSubResourcePlugin.js +125 -135
  170. package/src/store/index.js +4 -3
  171. package/src/store/pluginMerge.js +55 -0
  172. package/src/store/plugins/auditTrails.js +357 -17
  173. package/src/store/plugins/files.js +250 -186
  174. package/src/store/plugins/index.js +8 -4
  175. package/src/store/plugins/lifecycle.js +180 -180
  176. package/src/store/plugins/logs.d.ts +22 -0
  177. package/src/store/plugins/logs.js +172 -0
  178. package/src/store/plugins/registerMapping.js +195 -0
  179. package/src/store/plugins/relations.js +68 -68
  180. package/src/store/plugins/search.js +385 -0
  181. package/src/store/plugins/selection.js +104 -0
  182. package/src/store/useObjectStore.js +793 -625
  183. package/src/types/auditTrail.d.ts +32 -32
  184. package/src/types/file.d.ts +23 -23
  185. package/src/types/index.d.ts +67 -35
  186. package/src/types/notification.d.ts +36 -36
  187. package/src/types/object.d.ts +40 -40
  188. package/src/types/organisation.d.ts +41 -41
  189. package/src/types/register.d.ts +25 -25
  190. package/src/types/schema.d.ts +39 -39
  191. package/src/types/shared.d.ts +79 -79
  192. package/src/types/source.d.ts +14 -14
  193. package/src/types/task.d.ts +31 -31
  194. package/src/utils/errors.js +96 -96
  195. package/src/utils/getTheme.js +9 -0
  196. package/src/utils/headers.js +80 -44
  197. package/src/utils/id.js +13 -0
  198. package/src/utils/index.js +4 -3
  199. package/src/utils/schema.js +423 -287
  200. package/src/utils/widgetVisibility.js +162 -0
  201. package/src/components/CnDetailViewLayout/CnDetailViewLayout.vue +0 -88
  202. package/src/components/CnDetailViewLayout/index.js +0 -1
  203. package/src/components/CnEmptyState/CnEmptyState.vue +0 -78
  204. package/src/components/CnEmptyState/index.js +0 -1
  205. package/src/components/CnListViewLayout/CnListViewLayout.vue +0 -80
  206. package/src/components/CnListViewLayout/index.js +0 -1
  207. package/src/components/CnViewModeToggle/CnViewModeToggle.vue +0 -77
  208. package/src/components/CnViewModeToggle/index.js +0 -1
@@ -1,223 +1,234 @@
1
- <template>
2
- <div class="cn-facet-sidebar">
3
- <div class="cn-facet-sidebar__header">
4
- <h3 class="cn-facet-sidebar__title">{{ title }}</h3>
5
- <NcButton
6
- v-if="hasActiveFilters"
7
- type="tertiary"
8
- class="cn-facet-sidebar__clear"
9
- @click="$emit('clear-all')">
10
- {{ clearLabel }}
11
- </NcButton>
12
- </div>
13
-
14
- <!-- Loading state -->
15
- <div v-if="loading" class="cn-facet-sidebar__loading">
16
- <NcLoadingIcon :size="20" />
17
- </div>
18
-
19
- <!-- Filter groups -->
20
- <div v-else class="cn-facet-sidebar__filters">
21
- <div
22
- v-for="filter in effectiveFilters"
23
- :key="filter.key"
24
- class="cn-facet-sidebar__group">
25
- <label class="cn-facet-sidebar__label">{{ filter.label }}</label>
26
-
27
- <!-- Checkbox filter (boolean) -->
28
- <NcCheckboxRadioSwitch
29
- v-if="filter.type === 'checkbox'"
30
- :checked="getFilterValue(filter.key) === true"
31
- @update:checked="onFilterChange(filter.key, $event)">
32
- {{ filter.label }}
33
- </NcCheckboxRadioSwitch>
34
-
35
- <!-- Select filter (enum / facet values) -->
36
- <NcSelect
37
- v-else-if="filter.type === 'select'"
38
- class="cn-facet-sidebar__select"
39
- :value="getSelectedOptions(filter)"
40
- :options="getFilterOptions(filter)"
41
- :placeholder="filter.label"
42
- :input-label="filter.label"
43
- :multiple="true"
44
- :clearable="true"
45
- @input="onSelectChange(filter.key, $event)" />
46
-
47
- <!-- Text filter (fallback) -->
48
- <NcTextField
49
- v-else
50
- :value="getFilterValue(filter.key) || ''"
51
- :placeholder="filter.label"
52
- :label="filter.label"
53
- @update:value="onFilterChange(filter.key, $event)" />
54
- </div>
55
- </div>
56
- </div>
57
- </template>
58
-
59
- <script>
60
- import { NcButton, NcSelect, NcTextField, NcCheckboxRadioSwitch, NcLoadingIcon } from '@nextcloud/vue'
61
- import { filtersFromSchema } from '../../utils/schema.js'
62
-
63
- /**
64
- * CnFacetSidebar Auto-generated faceted search sidebar from schema properties.
65
- *
66
- * Reads properties marked `facetable: true` in the schema and generates
67
- * appropriate filter widgets. Accepts live facet data from the API for
68
- * dynamic option values with counts.
69
- *
70
- * @example
71
- * <CnFacetSidebar
72
- * :schema="schema"
73
- * :facet-data="facetData"
74
- * :active-filters="filters"
75
- * @filter-change="onFilterChange"
76
- * @clear-all="clearFilters" />
77
- */
78
- export default {
79
- name: 'CnFacetSidebar',
80
-
81
- components: {
82
- NcButton,
83
- NcSelect,
84
- NcTextField,
85
- NcCheckboxRadioSwitch,
86
- NcLoadingIcon,
87
- },
88
-
89
- props: {
90
- /** Schema definition — reads facetable properties */
91
- schema: {
92
- type: Object,
93
- required: true,
94
- },
95
- /** Live facet data from API: { fieldName: { values: [{value, count}] } } */
96
- facetData: {
97
- type: Object,
98
- default: () => ({}),
99
- },
100
- /** Current active filters: { fieldName: value | [values] } */
101
- activeFilters: {
102
- type: Object,
103
- default: () => ({}),
104
- },
105
- /** Whether facet data is loading */
106
- loading: {
107
- type: Boolean,
108
- default: false,
109
- },
110
- /** Sidebar title */
111
- title: {
112
- type: String,
113
- default: 'Filters',
114
- },
115
- /** Clear all button label */
116
- clearLabel: {
117
- type: String,
118
- default: 'Clear all',
119
- },
120
- },
121
-
122
- computed: {
123
- effectiveFilters() {
124
- return filtersFromSchema(this.schema)
125
- },
126
-
127
- hasActiveFilters() {
128
- return Object.values(this.activeFilters).some((v) => {
129
- if (Array.isArray(v)) return v.length > 0
130
- return v !== null && v !== undefined && v !== '' && v !== false
131
- })
132
- },
133
- },
134
-
135
- methods: {
136
- getFilterValue(key) {
137
- return this.activeFilters[key] ?? null
138
- },
139
-
140
- getFilterOptions(filter) {
141
- // Use facet data if available (live values with counts)
142
- const facet = this.facetData[filter.key]
143
- if (facet?.values?.length > 0) {
144
- return facet.values.map((v) => ({
145
- id: v.value,
146
- label: v.count !== undefined ? `${v.value} (${v.count})` : String(v.value),
147
- }))
148
- }
149
- // Fall back to static enum options from schema
150
- return filter.options || []
151
- },
152
-
153
- getSelectedOptions(filter) {
154
- const value = this.getFilterValue(filter.key)
155
- if (!value) return []
156
- const values = Array.isArray(value) ? value : [value]
157
- const options = this.getFilterOptions(filter)
158
- return values.map((v) => options.find((o) => o.id === v) || { id: v, label: String(v) })
159
- },
160
-
161
- onSelectChange(key, selected) {
162
- const values = selected ? selected.map((o) => o.id) : []
163
- this.$emit('filter-change', { key, values })
164
- },
165
-
166
- onFilterChange(key, value) {
167
- this.$emit('filter-change', { key, values: value })
168
- },
169
- },
170
- }
171
- </script>
172
-
173
- <style scoped>
174
- .cn-facet-sidebar {
175
- padding: 16px;
176
- border-right: 1px solid var(--color-border);
177
- min-width: 240px;
178
- max-width: 300px;
179
- }
180
-
181
- .cn-facet-sidebar__header {
182
- display: flex;
183
- justify-content: space-between;
184
- align-items: center;
185
- margin-bottom: 16px;
186
- }
187
-
188
- .cn-facet-sidebar__title {
189
- margin: 0;
190
- font-size: 15px;
191
- font-weight: 600;
192
- }
193
-
194
- .cn-facet-sidebar__loading {
195
- display: flex;
196
- justify-content: center;
197
- padding: 20px;
198
- }
199
-
200
- .cn-facet-sidebar__filters {
201
- display: flex;
202
- flex-direction: column;
203
- gap: 16px;
204
- }
205
-
206
- .cn-facet-sidebar__group {
207
- display: flex;
208
- flex-direction: column;
209
- gap: 4px;
210
- }
211
-
212
- .cn-facet-sidebar__label {
213
- font-size: 12px;
214
- font-weight: 500;
215
- color: var(--color-text-maxcontrast);
216
- text-transform: uppercase;
217
- letter-spacing: 0.3px;
218
- }
219
-
220
- .cn-facet-sidebar__select {
221
- width: 100%;
222
- }
223
- </style>
1
+ <template>
2
+ <div class="cn-facet-sidebar">
3
+ <div class="cn-facet-sidebar__header">
4
+ <h3 class="cn-facet-sidebar__title">
5
+ {{ title }}
6
+ </h3>
7
+ <NcButton
8
+ v-if="hasActiveFilters"
9
+ type="tertiary"
10
+ class="cn-facet-sidebar__clear"
11
+ @click="$emit('clear-all')">
12
+ {{ clearLabel }}
13
+ </NcButton>
14
+ </div>
15
+
16
+ <!-- Loading state -->
17
+ <div v-if="loading" class="cn-facet-sidebar__loading">
18
+ <NcLoadingIcon :size="20" />
19
+ </div>
20
+
21
+ <!-- Filter groups -->
22
+ <div v-else class="cn-facet-sidebar__filters">
23
+ <div
24
+ v-for="filter in effectiveFilters"
25
+ :key="filter.key"
26
+ class="cn-facet-sidebar__group">
27
+ <label class="cn-facet-sidebar__label">{{ filter.label }}</label>
28
+
29
+ <!-- Checkbox filter (boolean) -->
30
+ <NcCheckboxRadioSwitch
31
+ v-if="filter.type === 'checkbox'"
32
+ :checked="getFilterValue(filter.key) === true"
33
+ @update:checked="onFilterChange(filter.key, $event)">
34
+ {{ filter.label }}
35
+ </NcCheckboxRadioSwitch>
36
+
37
+ <!-- Select filter (enum / facet values) -->
38
+ <NcSelect
39
+ v-else-if="filter.type === 'select'"
40
+ class="cn-facet-sidebar__select"
41
+ :value="getSelectedOptions(filter)"
42
+ :options="getFilterOptions(filter)"
43
+ :placeholder="filter.label"
44
+ :input-label="filter.label"
45
+ :multiple="true"
46
+ :clearable="true"
47
+ @input="onSelectChange(filter.key, $event)" />
48
+
49
+ <!-- Text filter (fallback) -->
50
+ <NcTextField
51
+ v-else
52
+ :value="getFilterValue(filter.key) || ''"
53
+ :placeholder="filter.label"
54
+ :label="filter.label"
55
+ @update:value="onFilterChange(filter.key, $event)" />
56
+ </div>
57
+ </div>
58
+ </div>
59
+ </template>
60
+
61
+ <script>
62
+ import { translate as t } from '@nextcloud/l10n'
63
+ import { NcButton, NcSelect, NcTextField, NcCheckboxRadioSwitch, NcLoadingIcon } from '@nextcloud/vue'
64
+ import { filtersFromSchema } from '../../utils/schema.js'
65
+
66
+ /**
67
+ * CnFacetSidebar Auto-generated faceted search sidebar from schema properties.
68
+ *
69
+ * Reads properties marked `facetable: true` in the schema and generates
70
+ * appropriate filter widgets. Accepts live facet data from the API for
71
+ * dynamic option values with counts.
72
+ *
73
+ * @example
74
+ * <CnFacetSidebar
75
+ * :schema="schema"
76
+ * :facet-data="facetData"
77
+ * :active-filters="filters"
78
+ * @filter-change="onFilterChange"
79
+ * @clear-all="clearFilters" />
80
+ */
81
+ export default {
82
+ name: 'CnFacetSidebar',
83
+
84
+ components: {
85
+ NcButton,
86
+ NcSelect,
87
+ NcTextField,
88
+ NcCheckboxRadioSwitch,
89
+ NcLoadingIcon,
90
+ },
91
+
92
+ props: {
93
+ /** Schema definition — reads facetable properties */
94
+ schema: {
95
+ type: Object,
96
+ required: true,
97
+ },
98
+ /** Live facet data from API: { fieldName: { values: [{value, count}] } } */
99
+ facetData: {
100
+ type: Object,
101
+ default: () => ({}),
102
+ },
103
+ /** Current active filters: { fieldName: value | [values] } */
104
+ activeFilters: {
105
+ type: Object,
106
+ default: () => ({}),
107
+ },
108
+ /** Whether facet data is loading */
109
+ loading: {
110
+ type: Boolean,
111
+ default: false,
112
+ },
113
+ /** Sidebar title */
114
+ title: {
115
+ type: String,
116
+ default: () => t('nextcloud-vue', 'Filters'),
117
+ },
118
+ /** Clear all button label */
119
+ clearLabel: {
120
+ type: String,
121
+ default: () => t('nextcloud-vue', 'Clear all'),
122
+ },
123
+ /**
124
+ * Whether the current user is an admin.
125
+ * When false, schema properties with `adminOnly: true` are hidden from filters.
126
+ */
127
+ userIsAdmin: {
128
+ type: Boolean,
129
+ default: true,
130
+ },
131
+ },
132
+
133
+ computed: {
134
+ effectiveFilters() {
135
+ return filtersFromSchema(this.schema, { isAdmin: this.userIsAdmin })
136
+ },
137
+
138
+ hasActiveFilters() {
139
+ return Object.values(this.activeFilters).some((v) => {
140
+ if (Array.isArray(v)) return v.length > 0
141
+ return v !== null && v !== undefined && v !== '' && v !== false
142
+ })
143
+ },
144
+ },
145
+
146
+ methods: {
147
+ getFilterValue(key) {
148
+ return this.activeFilters[key] ?? null
149
+ },
150
+
151
+ getFilterOptions(filter) {
152
+ // Use facet data if available (live values with counts)
153
+ const facet = this.facetData[filter.key]
154
+ if (facet?.values?.length > 0) {
155
+ return facet.values.map((v) => ({
156
+ id: v.value,
157
+ label: v.count !== undefined ? `${v.value} (${v.count})` : String(v.value),
158
+ }))
159
+ }
160
+ // Fall back to static enum options from schema
161
+ return filter.options || []
162
+ },
163
+
164
+ getSelectedOptions(filter) {
165
+ const value = this.getFilterValue(filter.key)
166
+ if (!value) return []
167
+ const values = Array.isArray(value) ? value : [value]
168
+ const options = this.getFilterOptions(filter)
169
+ return values.map((v) => options.find((o) => o.id === v) || { id: v, label: String(v) })
170
+ },
171
+
172
+ onSelectChange(key, selected) {
173
+ const values = selected ? selected.map((o) => o.id) : []
174
+ this.$emit('filter-change', { key, values })
175
+ },
176
+
177
+ onFilterChange(key, value) {
178
+ this.$emit('filter-change', { key, values: value })
179
+ },
180
+ },
181
+ }
182
+ </script>
183
+
184
+ <style scoped>
185
+ .cn-facet-sidebar {
186
+ padding: 16px;
187
+ border-right: 1px solid var(--color-border);
188
+ min-width: 240px;
189
+ max-width: 300px;
190
+ }
191
+
192
+ .cn-facet-sidebar__header {
193
+ display: flex;
194
+ justify-content: space-between;
195
+ align-items: center;
196
+ margin-bottom: 16px;
197
+ }
198
+
199
+ .cn-facet-sidebar__title {
200
+ margin: 0;
201
+ font-size: 15px;
202
+ font-weight: 600;
203
+ }
204
+
205
+ .cn-facet-sidebar__loading {
206
+ display: flex;
207
+ justify-content: center;
208
+ padding: 20px;
209
+ }
210
+
211
+ .cn-facet-sidebar__filters {
212
+ display: flex;
213
+ flex-direction: column;
214
+ gap: 16px;
215
+ }
216
+
217
+ .cn-facet-sidebar__group {
218
+ display: flex;
219
+ flex-direction: column;
220
+ gap: 4px;
221
+ }
222
+
223
+ .cn-facet-sidebar__label {
224
+ font-size: 12px;
225
+ font-weight: 500;
226
+ color: var(--color-text-maxcontrast);
227
+ text-transform: uppercase;
228
+ letter-spacing: 0.3px;
229
+ }
230
+
231
+ .cn-facet-sidebar__select {
232
+ width: 100%;
233
+ }
234
+ </style>
@@ -1 +1 @@
1
- export { default as CnFacetSidebar } from './CnFacetSidebar.vue'
1
+ export { default as CnFacetSidebar } from './CnFacetSidebar.vue'