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

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 (54) hide show
  1. package/README.md +226 -0
  2. package/dist/nextcloud-vue.cjs.js +7039 -2409
  3. package/dist/nextcloud-vue.cjs.js.map +1 -1
  4. package/dist/nextcloud-vue.css +237 -52
  5. package/dist/nextcloud-vue.esm.js +7012 -2386
  6. package/dist/nextcloud-vue.esm.js.map +1 -1
  7. package/package.json +2 -4
  8. package/src/components/CnActionsBar/CnActionsBar.vue +225 -0
  9. package/src/components/CnActionsBar/index.js +1 -0
  10. package/src/components/CnCopyDialog/CnCopyDialog.vue +250 -0
  11. package/src/components/CnCopyDialog/index.js +1 -0
  12. package/src/components/CnDataTable/CnDataTable.vue +0 -5
  13. package/src/components/CnDeleteDialog/CnDeleteDialog.vue +170 -0
  14. package/src/components/CnDeleteDialog/index.js +1 -0
  15. package/src/components/CnFormDialog/CnFormDialog.vue +629 -0
  16. package/src/components/CnFormDialog/index.js +1 -0
  17. package/src/components/CnIcon/CnIcon.vue +89 -0
  18. package/src/components/CnIcon/index.js +1 -0
  19. package/src/components/CnIndexPage/CnIndexPage.vue +434 -300
  20. package/src/components/CnIndexSidebar/CnIndexSidebar.vue +484 -0
  21. package/src/components/CnIndexSidebar/index.js +1 -0
  22. package/src/components/CnPageHeader/CnPageHeader.vue +57 -0
  23. package/src/components/CnPageHeader/index.js +1 -0
  24. package/src/components/CnRegisterMapping/CnRegisterMapping.vue +792 -0
  25. package/src/components/CnRegisterMapping/index.js +1 -0
  26. package/src/components/index.js +8 -4
  27. package/src/constants/metadata.js +30 -0
  28. package/src/css/actions-bar.css +48 -0
  29. package/src/css/badge.css +4 -4
  30. package/src/css/card.css +23 -23
  31. package/src/css/detail.css +13 -13
  32. package/src/css/index-page.css +32 -0
  33. package/src/css/index-sidebar.css +187 -0
  34. package/src/css/index.css +4 -0
  35. package/src/css/layout.css +14 -14
  36. package/src/css/page-header.css +33 -0
  37. package/src/css/pagination.css +12 -12
  38. package/src/css/table.css +21 -22
  39. package/src/css/utilities.css +2 -2
  40. package/src/index.js +11 -8
  41. package/src/store/plugins/index.js +1 -0
  42. package/src/store/plugins/registerMapping.js +185 -0
  43. package/src/store/useObjectStore.js +122 -61
  44. package/src/utils/headers.js +7 -1
  45. package/src/utils/index.js +1 -1
  46. package/src/utils/schema.js +133 -1
  47. package/src/components/CnDetailViewLayout/CnDetailViewLayout.vue +0 -88
  48. package/src/components/CnDetailViewLayout/index.js +0 -1
  49. package/src/components/CnEmptyState/CnEmptyState.vue +0 -78
  50. package/src/components/CnEmptyState/index.js +0 -1
  51. package/src/components/CnListViewLayout/CnListViewLayout.vue +0 -80
  52. package/src/components/CnListViewLayout/index.js +0 -1
  53. package/src/components/CnViewModeToggle/CnViewModeToggle.vue +0 -77
  54. package/src/components/CnViewModeToggle/index.js +0 -1
@@ -6,9 +6,9 @@
6
6
  display: flex;
7
7
  justify-content: space-between;
8
8
  align-items: center;
9
- gap: 15px;
10
- margin-top: 30px;
11
- padding: 20px;
9
+ gap: calc(4 * var(--default-grid-baseline));
10
+ margin-top: calc(8 * var(--default-grid-baseline));
11
+ padding: calc(5 * var(--default-grid-baseline));
12
12
  flex-wrap: nowrap;
13
13
  }
14
14
 
@@ -26,7 +26,7 @@
26
26
  .cn-pagination__nav {
27
27
  display: flex;
28
28
  align-items: center;
29
- gap: 10px;
29
+ gap: calc(2 * var(--default-grid-baseline));
30
30
  flex-grow: 1;
31
31
  justify-content: center;
32
32
  }
@@ -34,11 +34,11 @@
34
34
  .cn-pagination__numbers {
35
35
  display: flex;
36
36
  align-items: center;
37
- gap: 5px;
37
+ gap: var(--default-grid-baseline);
38
38
  }
39
39
 
40
40
  .cn-pagination__ellipsis {
41
- padding: 0 5px;
41
+ padding: 0 var(--default-grid-baseline);
42
42
  color: var(--color-text-maxcontrast);
43
43
  font-size: 0.9rem;
44
44
  }
@@ -46,7 +46,7 @@
46
46
  .cn-pagination__page-size {
47
47
  display: flex;
48
48
  align-items: center;
49
- gap: 10px;
49
+ gap: calc(2 * var(--default-grid-baseline));
50
50
  flex-shrink: 0;
51
51
  min-width: 0;
52
52
  }
@@ -63,10 +63,10 @@
63
63
  }
64
64
 
65
65
  /* Backwards compatibility aliases */
66
- .viewPagination { display: flex; justify-content: space-between; align-items: center; gap: 15px; margin-top: 30px; padding: 20px; flex-wrap: nowrap; }
66
+ .viewPagination { display: flex; justify-content: space-between; align-items: center; gap: calc(4 * var(--default-grid-baseline)); margin-top: calc(8 * var(--default-grid-baseline)); padding: calc(5 * var(--default-grid-baseline)); flex-wrap: nowrap; }
67
67
  .viewPaginationInfo { display: flex; align-items: center; flex-shrink: 0; }
68
68
  .viewPageInfo { color: var(--color-text-maxcontrast); font-size: 0.9rem; }
69
- .viewPaginationNav { display: flex; align-items: center; gap: 10px; flex-grow: 1; justify-content: center; }
70
- .viewPaginationNumbers { display: flex; align-items: center; gap: 5px; }
71
- .viewPaginationEllipsis { padding: 0 5px; color: var(--color-text-maxcontrast); font-size: 0.9rem; }
72
- .viewPaginationPageSize { display: flex; align-items: center; gap: 10px; flex-shrink: 0; min-width: 0; }
69
+ .viewPaginationNav { display: flex; align-items: center; gap: calc(2 * var(--default-grid-baseline)); flex-grow: 1; justify-content: center; }
70
+ .viewPaginationNumbers { display: flex; align-items: center; gap: var(--default-grid-baseline); }
71
+ .viewPaginationEllipsis { padding: 0 var(--default-grid-baseline); color: var(--color-text-maxcontrast); font-size: 0.9rem; }
72
+ .viewPaginationPageSize { display: flex; align-items: center; gap: calc(2 * var(--default-grid-baseline)); flex-shrink: 0; min-width: 0; }
package/src/css/table.css CHANGED
@@ -4,12 +4,12 @@
4
4
 
5
5
  /* Table Container */
6
6
  .cn-table-container {
7
- background: var(--nldesign-component-table-background-color, var(--color-main-background));
8
- border-radius: var(--nldesign-border-radius, var(--border-radius));
7
+ background: var(--color-main-background);
8
+ border-radius: var(--border-radius);
9
9
  overflow: hidden;
10
10
  box-shadow: 0 2px 4px var(--color-box-shadow);
11
- border: 1px solid var(--nldesign-component-table-border-color, var(--color-border));
12
- margin-bottom: 20px;
11
+ border: 1px solid var(--color-border);
12
+ margin-bottom: calc(5 * var(--default-grid-baseline));
13
13
  }
14
14
 
15
15
  .cn-table-container.cn-table-container--scrollable {
@@ -21,28 +21,27 @@
21
21
  .cn-data-table {
22
22
  width: 100%;
23
23
  border-collapse: collapse;
24
- background-color: var(--nldesign-component-table-background-color, var(--color-main-background));
24
+ background-color: var(--color-main-background);
25
25
  }
26
26
 
27
27
  .cn-data-table th,
28
28
  .cn-data-table td {
29
- padding: var(--nldesign-component-table-cell-padding-block, 12px)
30
- var(--nldesign-component-table-cell-padding-inline, 12px);
29
+ padding: calc(3 * var(--default-grid-baseline));
31
30
  text-align: left;
32
- border-bottom: 1px solid var(--nldesign-component-table-border-color, var(--color-border));
31
+ border-bottom: 1px solid var(--color-border);
33
32
  vertical-align: middle;
34
33
  }
35
34
 
36
35
  .cn-data-table th {
37
- background: var(--nldesign-component-table-header-background-color, var(--color-background-dark));
38
- font-weight: var(--nldesign-component-table-header-font-weight, 500);
36
+ background: var(--color-background-dark);
37
+ font-weight: 500;
39
38
  color: var(--color-text-maxcontrast);
40
39
  }
41
40
 
42
41
  .cn-data-table th.cn-table-header--sortable {
43
42
  cursor: pointer;
44
43
  user-select: none;
45
- transition: background-color 0.2s ease;
44
+ transition: background-color var(--animation-quick) ease;
46
45
  }
47
46
 
48
47
  .cn-data-table th.cn-table-header--sortable:hover {
@@ -50,19 +49,19 @@
50
49
  }
51
50
 
52
51
  .cn-table-sort-indicator {
53
- margin-left: 4px;
52
+ margin-left: var(--default-grid-baseline);
54
53
  opacity: 0.6;
55
54
  }
56
55
 
57
56
  /* Row States */
58
57
  .cn-table-row {
59
- border-bottom: 1px solid var(--nldesign-component-table-border-color, var(--color-border));
58
+ border-bottom: 1px solid var(--color-border);
60
59
  cursor: pointer;
61
- transition: background-color 0.2s ease;
60
+ transition: background-color var(--animation-quick) ease;
62
61
  }
63
62
 
64
63
  .cn-table-row:hover {
65
- background: var(--nldesign-component-table-row-hover-background-color, var(--color-background-hover));
64
+ background: var(--color-background-hover);
66
65
  }
67
66
 
68
67
  .cn-table-row--selected {
@@ -109,18 +108,18 @@
109
108
  /* Loading State */
110
109
  .cn-table-loading {
111
110
  text-align: center;
112
- padding: 50px;
111
+ padding: calc(12 * var(--default-grid-baseline));
113
112
  }
114
113
 
115
114
  .cn-table-loading p {
116
- margin-top: 20px;
115
+ margin-top: calc(5 * var(--default-grid-baseline));
117
116
  color: var(--color-text-maxcontrast);
118
117
  }
119
118
 
120
119
  /* Empty State (inside table) */
121
120
  .cn-table-empty td {
122
121
  text-align: center;
123
- padding: 48px 24px;
122
+ padding: calc(12 * var(--default-grid-baseline)) calc(6 * var(--default-grid-baseline));
124
123
  color: var(--color-text-maxcontrast);
125
124
  }
126
125
 
@@ -128,12 +127,12 @@
128
127
  Backwards compatibility aliases
129
128
  (legacy OpenRegister CSS class names)
130
129
  ======================================== */
131
- .viewTableContainer { background: var(--nldesign-component-table-background-color, var(--color-main-background)); border-radius: var(--nldesign-border-radius, var(--border-radius)); overflow: hidden; box-shadow: 0 2px 4px var(--color-box-shadow); border: 1px solid var(--nldesign-component-table-border-color, var(--color-border)); margin-bottom: 20px; }
130
+ .viewTableContainer { background: var(--color-main-background); border-radius: var(--border-radius); overflow: hidden; box-shadow: 0 2px 4px var(--color-box-shadow); border: 1px solid var(--color-border); margin-bottom: calc(5 * var(--default-grid-baseline)); }
132
131
  .viewTableContainer.scrollable { max-height: 400px; overflow-y: auto; }
133
- .viewTable { width: 100%; border-collapse: collapse; background-color: var(--nldesign-component-table-background-color, var(--color-main-background)); }
134
- .viewTable th, .viewTable td { padding: 12px; text-align: left; border-bottom: 1px solid var(--color-border); vertical-align: middle; }
132
+ .viewTable { width: 100%; border-collapse: collapse; background-color: var(--color-main-background); }
133
+ .viewTable th, .viewTable td { padding: calc(3 * var(--default-grid-baseline)); text-align: left; border-bottom: 1px solid var(--color-border); vertical-align: middle; }
135
134
  .viewTable th { background: var(--color-background-hover); font-weight: 500; color: var(--color-text-maxcontrast); background-color: var(--color-background-dark); }
136
- .viewTableRow { border-bottom: 1px solid var(--color-border); cursor: pointer; transition: background-color 0.2s ease; }
135
+ .viewTableRow { border-bottom: 1px solid var(--color-border); cursor: pointer; transition: background-color var(--animation-quick) ease; }
137
136
  .viewTableRow:hover { background: var(--color-background-hover); }
138
137
  .viewTableRow.active, .viewTableRowSelected { background: var(--color-primary-light); }
139
138
  .tableColumnCheckbox { width: 50px; text-align: center; }
@@ -21,11 +21,11 @@
21
21
 
22
22
  /* Vue Transition Animations */
23
23
  .cn-slide-fade-enter-active {
24
- transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
24
+ transition: all var(--animation-slow) cubic-bezier(0.4, 0, 0.2, 1);
25
25
  }
26
26
 
27
27
  .cn-slide-fade-leave-active {
28
- transition: all 0.3s cubic-bezier(0.4, 0, 1, 1);
28
+ transition: all var(--animation-quick) cubic-bezier(0.4, 0, 1, 1);
29
29
  }
30
30
 
31
31
  .cn-slide-fade-enter,
package/src/index.js CHANGED
@@ -1,11 +1,11 @@
1
+ // CSS — auto-imported so consumers get styles with components
2
+ import './css/index.css'
3
+
1
4
  // Components
2
5
  export {
3
6
  CnDataTable,
4
7
  CnFilterBar,
5
- CnListViewLayout,
6
- CnDetailViewLayout,
7
8
  CnStatusBadge,
8
- CnEmptyState,
9
9
  CnPagination,
10
10
  CnSettingsCard,
11
11
  CnSettingsSection,
@@ -16,15 +16,20 @@ export {
16
16
  CnObjectCard,
17
17
  CnCardGrid,
18
18
  CnFacetSidebar,
19
- CnViewModeToggle,
20
19
  CnRowActions,
21
20
  CnIndexPage,
22
21
  CnMassActionBar,
22
+ CnDeleteDialog,
23
+ CnCopyDialog,
24
+ CnFormDialog,
23
25
  CnMassDeleteDialog,
24
26
  CnMassCopyDialog,
25
27
  CnKpiGrid,
26
28
  CnMassExportDialog,
27
29
  CnMassImportDialog,
30
+ CnIndexSidebar,
31
+ CnRegisterMapping,
32
+ registerIcons,
28
33
  } from './components/index.js'
29
34
 
30
35
  // Store
@@ -37,6 +42,7 @@ export {
37
42
  relationsPlugin,
38
43
  filesPlugin,
39
44
  lifecyclePlugin,
45
+ registerMappingPlugin,
40
46
  } from './store/plugins/index.js'
41
47
 
42
48
  // Composables
@@ -44,7 +50,4 @@ export { useListView, useDetailView, useSubResource } from './composables/index.
44
50
 
45
51
  // Utilities
46
52
  export { buildHeaders, buildQueryString, parseResponseError, networkError, genericError } from './utils/index.js'
47
- export { columnsFromSchema, formatValue, filtersFromSchema } from './utils/index.js'
48
-
49
- // CSS (consumers should import separately)
50
- // import '@conduction/nextcloud-vue/src/css/index.css'
53
+ export { columnsFromSchema, formatValue, filtersFromSchema, fieldsFromSchema } from './utils/index.js'
@@ -2,3 +2,4 @@ export { auditTrailsPlugin } from './auditTrails.js'
2
2
  export { relationsPlugin } from './relations.js'
3
3
  export { filesPlugin } from './files.js'
4
4
  export { lifecyclePlugin } from './lifecycle.js'
5
+ export { registerMappingPlugin } from './registerMapping.js'
@@ -0,0 +1,185 @@
1
+ import { buildHeaders } from '../../utils/headers.js'
2
+
3
+ /**
4
+ * Register mapping plugin for the object store.
5
+ *
6
+ * Adds state and actions for fetching OpenRegister registers and their
7
+ * schemas, used by CnRegisterMapping to populate dropdowns.
8
+ *
9
+ * State: registers, registerSchemas, registersLoading, registersError
10
+ * Actions: fetchRegisters, fetchSchemasForRegister, clearRegisterMapping
11
+ * Getters: getRegisters, registerOptions, schemaOptions, isRegistersLoading, getRegistersError
12
+ *
13
+ * @return {Function} Plugin factory
14
+ *
15
+ * @example
16
+ * const useStore = createObjectStore('object', {
17
+ * plugins: [registerMappingPlugin()],
18
+ * })
19
+ * const store = useStore()
20
+ * await store.fetchRegisters()
21
+ * const options = store.registerOptions // [{ label, value }]
22
+ * const schemas = store.schemaOptions('5') // [{ label, value }]
23
+ */
24
+ export function registerMappingPlugin() {
25
+ return {
26
+ name: 'RegisterMapping',
27
+
28
+ state: () => ({
29
+ /** @type {Array} All available registers from OpenRegister */
30
+ registers: [],
31
+ /** @type {Object<string, Array>} Schemas keyed by register ID */
32
+ registerSchemas: {},
33
+ /** @type {boolean} Whether registers are being fetched */
34
+ registersLoading: false,
35
+ /** @type {string|null} Error message from last fetch */
36
+ registersError: null,
37
+ }),
38
+
39
+ getters: {
40
+ /** @return {Array} Raw register list */
41
+ getRegisters: (state) => state.registers,
42
+
43
+ /** @return {boolean} Whether registers are loading */
44
+ isRegistersLoading: (state) => state.registersLoading,
45
+
46
+ /** @return {string|null} Last error */
47
+ getRegistersError: (state) => state.registersError,
48
+
49
+ /**
50
+ * Registers as NcSelect-compatible options.
51
+ *
52
+ * @return {Array<{label: string, value: string}>}
53
+ */
54
+ registerOptions: (state) => state.registers.map((r) => ({
55
+ label: r.title || r.slug || `Register ${r.id}`,
56
+ value: String(r.id),
57
+ })),
58
+ },
59
+
60
+ actions: {
61
+ /**
62
+ * Get schemas for a register as NcSelect options.
63
+ *
64
+ * @param {string|number} registerId Register ID
65
+ * @return {Array<{label: string, value: string}>}
66
+ */
67
+ schemaOptions(registerId) {
68
+ const id = String(registerId)
69
+ return (this.registerSchemas[id] || []).map((s) => ({
70
+ label: s.title || s.slug || `Schema ${s.id}`,
71
+ value: String(s.id),
72
+ }))
73
+ },
74
+
75
+ /**
76
+ * Fetch all registers from OpenRegister with expanded schemas.
77
+ *
78
+ * @param {boolean} [withSchemas=true] Include schemas in response
79
+ * @return {Promise<Array>} Fetched registers
80
+ */
81
+ async fetchRegisters(withSchemas = true) {
82
+ this.registersLoading = true
83
+ this.registersError = null
84
+
85
+ try {
86
+ let url = '/apps/openregister/api/registers'
87
+ if (withSchemas) {
88
+ url += '?_extend[]=schemas'
89
+ }
90
+
91
+ const response = await fetch(url, {
92
+ method: 'GET',
93
+ headers: buildHeaders(),
94
+ })
95
+
96
+ if (!response.ok) {
97
+ this.registersError = `Failed to fetch registers: ${response.statusText}`
98
+ return []
99
+ }
100
+
101
+ const data = await response.json()
102
+ const results = data.results || data
103
+ this.registers = Array.isArray(results) ? results : []
104
+
105
+ // Cache expanded schemas by register ID
106
+ if (withSchemas) {
107
+ for (const reg of this.registers) {
108
+ if (Array.isArray(reg.schemas) && reg.schemas.length > 0) {
109
+ this.registerSchemas = {
110
+ ...this.registerSchemas,
111
+ [String(reg.id)]: reg.schemas.filter(
112
+ (s) => s && typeof s === 'object' && s.id,
113
+ ),
114
+ }
115
+ }
116
+ }
117
+ }
118
+
119
+ return this.registers
120
+ } catch (error) {
121
+ this.registersError = error.message || 'Network error fetching registers'
122
+ return []
123
+ } finally {
124
+ this.registersLoading = false
125
+ }
126
+ },
127
+
128
+ /**
129
+ * Fetch schemas for a specific register.
130
+ * Returns cached data if already fetched via expanded registers.
131
+ *
132
+ * @param {string|number} registerId Register ID
133
+ * @return {Promise<Array>} Schemas for the register
134
+ */
135
+ async fetchSchemasForRegister(registerId) {
136
+ const id = String(registerId)
137
+
138
+ // Return cached if available and non-empty
139
+ if (this.registerSchemas[id]?.length > 0) {
140
+ return this.registerSchemas[id]
141
+ }
142
+
143
+ // Check registers array for expanded schemas
144
+ const register = this.registers.find((r) => String(r.id) === id)
145
+ if (register?.schemas?.length > 0) {
146
+ const schemas = register.schemas.filter(
147
+ (s) => s && typeof s === 'object' && s.id,
148
+ )
149
+ if (schemas.length > 0) {
150
+ this.registerSchemas = { ...this.registerSchemas, [id]: schemas }
151
+ return schemas
152
+ }
153
+ }
154
+
155
+ // Fetch from API as fallback
156
+ try {
157
+ const response = await fetch(
158
+ `/apps/openregister/api/registers/${id}?_extend[]=schemas`,
159
+ { method: 'GET', headers: buildHeaders() },
160
+ )
161
+ if (!response.ok) return []
162
+
163
+ const data = await response.json()
164
+ const schemas = (data.schemas || []).filter(
165
+ (s) => s && typeof s === 'object' && s.id,
166
+ )
167
+ this.registerSchemas = { ...this.registerSchemas, [id]: schemas }
168
+ return schemas
169
+ } catch {
170
+ return []
171
+ }
172
+ },
173
+
174
+ /**
175
+ * Clear all register mapping state.
176
+ */
177
+ clearRegisterMapping() {
178
+ this.registers = []
179
+ this.registerSchemas = {}
180
+ this.registersLoading = false
181
+ this.registersError = null
182
+ },
183
+ },
184
+ }
185
+ }