@conduction/nextcloud-vue 0.1.0-beta.1 → 0.1.0-beta.3
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.
- package/README.md +226 -0
- package/css/index.css +5 -0
- package/dist/nextcloud-vue.cjs.js +7039 -2409
- package/dist/nextcloud-vue.cjs.js.map +1 -1
- package/dist/nextcloud-vue.css +237 -52
- package/dist/nextcloud-vue.esm.js +7012 -2386
- package/dist/nextcloud-vue.esm.js.map +1 -1
- package/package.json +4 -5
- package/src/components/CnActionsBar/CnActionsBar.vue +225 -0
- package/src/components/CnActionsBar/index.js +1 -0
- package/src/components/CnCopyDialog/CnCopyDialog.vue +250 -0
- package/src/components/CnCopyDialog/index.js +1 -0
- package/src/components/CnDataTable/CnDataTable.vue +0 -5
- package/src/components/CnDeleteDialog/CnDeleteDialog.vue +170 -0
- package/src/components/CnDeleteDialog/index.js +1 -0
- package/src/components/CnFormDialog/CnFormDialog.vue +629 -0
- package/src/components/CnFormDialog/index.js +1 -0
- package/src/components/CnIcon/CnIcon.vue +89 -0
- package/src/components/CnIcon/index.js +1 -0
- package/src/components/CnIndexPage/CnIndexPage.vue +434 -300
- package/src/components/CnIndexSidebar/CnIndexSidebar.vue +484 -0
- package/src/components/CnIndexSidebar/index.js +1 -0
- package/src/components/CnPageHeader/CnPageHeader.vue +57 -0
- package/src/components/CnPageHeader/index.js +1 -0
- package/src/components/CnRegisterMapping/CnRegisterMapping.vue +792 -0
- package/src/components/CnRegisterMapping/index.js +1 -0
- package/src/components/index.js +8 -4
- package/src/composables/useListView.js +254 -45
- package/src/constants/metadata.js +30 -0
- package/src/css/actions-bar.css +48 -0
- package/src/css/badge.css +4 -4
- package/src/css/card.css +23 -23
- package/src/css/detail.css +13 -13
- package/src/css/index-page.css +32 -0
- package/src/css/index-sidebar.css +187 -0
- package/src/css/index.css +4 -0
- package/src/css/layout.css +14 -14
- package/src/css/page-header.css +33 -0
- package/src/css/pagination.css +12 -12
- package/src/css/table.css +21 -22
- package/src/css/utilities.css +2 -2
- package/src/index.js +11 -8
- package/src/store/plugins/index.js +1 -0
- package/src/store/plugins/registerMapping.js +185 -0
- package/src/store/useObjectStore.js +122 -61
- package/src/utils/headers.js +7 -1
- package/src/utils/index.js +1 -1
- package/src/utils/schema.js +133 -1
- package/src/components/CnDetailViewLayout/CnDetailViewLayout.vue +0 -88
- package/src/components/CnDetailViewLayout/index.js +0 -1
- package/src/components/CnEmptyState/CnEmptyState.vue +0 -78
- package/src/components/CnEmptyState/index.js +0 -1
- package/src/components/CnListViewLayout/CnListViewLayout.vue +0 -80
- package/src/components/CnListViewLayout/index.js +0 -1
- package/src/components/CnViewModeToggle/CnViewModeToggle.vue +0 -77
- package/src/components/CnViewModeToggle/index.js +0 -1
package/src/utils/schema.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Schema utility functions for auto-generating table columns, cell formatting,
|
|
3
|
-
* and faceted filter definitions from OpenRegister
|
|
3
|
+
* form field definitions, and faceted filter definitions from OpenRegister
|
|
4
|
+
* schema property definitions.
|
|
4
5
|
*
|
|
5
6
|
* @module utils/schema
|
|
6
7
|
*/
|
|
@@ -236,6 +237,136 @@ function truncateString(str, maxLength) {
|
|
|
236
237
|
return str.substring(0, maxLength) + '...'
|
|
237
238
|
}
|
|
238
239
|
|
|
240
|
+
/**
|
|
241
|
+
* Resolve the form widget type for a JSON Schema property.
|
|
242
|
+
*
|
|
243
|
+
* Resolution priority (first match wins):
|
|
244
|
+
* 1. Explicit `prop.widget` — pass-through custom widget name
|
|
245
|
+
* 2. `prop.enum` → `'select'`
|
|
246
|
+
* 3. Type-based: `boolean` → `'checkbox'`, `integer`/`number` → `'number'`,
|
|
247
|
+
* `array` + `items.enum` → `'multiselect'`, `array` → `'tags'`
|
|
248
|
+
* 4. Format-based: `date-time` → `'datetime'`, `date` → `'date'`,
|
|
249
|
+
* `email` → `'email'`, `uri`/`url` → `'url'`,
|
|
250
|
+
* `markdown`/`textarea` → `'textarea'`
|
|
251
|
+
* 5. Long text: `maxLength > 255` → `'textarea'`
|
|
252
|
+
* 6. Fallback → `'text'`
|
|
253
|
+
*
|
|
254
|
+
* @param {object} prop The schema property definition (type, format, enum, widget, items, maxLength)
|
|
255
|
+
* @return {string} Widget identifier: 'text'|'email'|'url'|'number'|'checkbox'|'select'|'multiselect'|'tags'|'textarea'|'date'|'datetime' or a custom string
|
|
256
|
+
*/
|
|
257
|
+
function resolveWidget(prop) {
|
|
258
|
+
// Explicit widget hint takes priority
|
|
259
|
+
if (prop.widget) return prop.widget
|
|
260
|
+
|
|
261
|
+
// Enum → select
|
|
262
|
+
if (prop.enum) return 'select'
|
|
263
|
+
|
|
264
|
+
const type = prop.type || 'string'
|
|
265
|
+
const format = prop.format || ''
|
|
266
|
+
|
|
267
|
+
// Boolean → switch/checkbox
|
|
268
|
+
if (type === 'boolean') return 'checkbox'
|
|
269
|
+
|
|
270
|
+
// Number types
|
|
271
|
+
if (type === 'integer' || type === 'number') return 'number'
|
|
272
|
+
|
|
273
|
+
// Array types
|
|
274
|
+
if (type === 'array') {
|
|
275
|
+
if (prop.items && prop.items.enum) return 'multiselect'
|
|
276
|
+
return 'tags'
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Format-based widgets
|
|
280
|
+
if (format === 'date-time') return 'datetime'
|
|
281
|
+
if (format === 'date') return 'date'
|
|
282
|
+
if (format === 'email') return 'email'
|
|
283
|
+
if (format === 'uri' || format === 'url') return 'url'
|
|
284
|
+
if (format === 'markdown' || format === 'textarea') return 'textarea'
|
|
285
|
+
|
|
286
|
+
// Long text → textarea
|
|
287
|
+
if (prop.maxLength && prop.maxLength > 255) return 'textarea'
|
|
288
|
+
|
|
289
|
+
return 'text'
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Generate form field definitions from a schema's properties.
|
|
294
|
+
*
|
|
295
|
+
* Reads `schema.properties` and creates field descriptor objects suitable
|
|
296
|
+
* for auto-generating form UIs. Follows the same pattern as
|
|
297
|
+
* `columnsFromSchema()` — filters, sorts, and supports overrides.
|
|
298
|
+
*
|
|
299
|
+
* @param {object} schema The schema object with a `properties` field
|
|
300
|
+
* @param {object} [options] Configuration options
|
|
301
|
+
* @param {string[]} [options.exclude] Property keys to exclude
|
|
302
|
+
* @param {string[]} [options.include] Property keys to include (whitelist mode)
|
|
303
|
+
* @param {object} [options.overrides] Per-key field overrides, e.g. `{ status: { widget: 'select' } }`
|
|
304
|
+
* @param {boolean} [options.includeReadOnly=false] Whether to include readOnly properties
|
|
305
|
+
* @return {Array<{key: string, label: string, description: string, type: string, format: string|null, widget: string, required: boolean, readOnly: boolean, default: *, enum: Array|null, items: object|null, validation: object, order: number}>}
|
|
306
|
+
*/
|
|
307
|
+
export function fieldsFromSchema(schema, options = {}) {
|
|
308
|
+
const { exclude = [], include = null, overrides = {}, includeReadOnly = false } = options
|
|
309
|
+
|
|
310
|
+
if (!schema || !schema.properties) {
|
|
311
|
+
return []
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const requiredKeys = Array.isArray(schema.required) ? schema.required : []
|
|
315
|
+
|
|
316
|
+
const entries = Object.entries(schema.properties)
|
|
317
|
+
.filter(([key, prop]) => {
|
|
318
|
+
// Skip properties marked as not visible
|
|
319
|
+
if (prop.visible === false) return false
|
|
320
|
+
// Skip readOnly properties by default
|
|
321
|
+
if (prop.readOnly === true && !includeReadOnly) return false
|
|
322
|
+
// Apply exclude list
|
|
323
|
+
if (exclude.includes(key)) return false
|
|
324
|
+
// Apply include whitelist
|
|
325
|
+
if (include && !include.includes(key)) return false
|
|
326
|
+
// Skip complex object types (not supported in auto-form)
|
|
327
|
+
if (prop.type === 'object') return false
|
|
328
|
+
return true
|
|
329
|
+
})
|
|
330
|
+
.sort(([keyA, propA], [keyB, propB]) => {
|
|
331
|
+
// Sort by order hint first, then alphabetically
|
|
332
|
+
const orderA = typeof propA.order === 'number' ? propA.order : Infinity
|
|
333
|
+
const orderB = typeof propB.order === 'number' ? propB.order : Infinity
|
|
334
|
+
if (orderA !== orderB) return orderA - orderB
|
|
335
|
+
return keyA.localeCompare(keyB)
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
return entries.map(([key, prop]) => {
|
|
339
|
+
const field = {
|
|
340
|
+
key,
|
|
341
|
+
label: prop.title || key,
|
|
342
|
+
description: prop.description || '',
|
|
343
|
+
type: prop.type || 'string',
|
|
344
|
+
format: prop.format || null,
|
|
345
|
+
widget: resolveWidget(prop),
|
|
346
|
+
required: requiredKeys.includes(key),
|
|
347
|
+
readOnly: prop.readOnly || false,
|
|
348
|
+
default: prop.default !== undefined ? prop.default : null,
|
|
349
|
+
enum: prop.enum || null,
|
|
350
|
+
items: prop.items || null,
|
|
351
|
+
validation: {
|
|
352
|
+
minLength: prop.minLength,
|
|
353
|
+
maxLength: prop.maxLength,
|
|
354
|
+
minimum: prop.minimum,
|
|
355
|
+
maximum: prop.maximum,
|
|
356
|
+
pattern: prop.pattern,
|
|
357
|
+
},
|
|
358
|
+
order: typeof prop.order === 'number' ? prop.order : Infinity,
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Apply per-field overrides
|
|
362
|
+
if (overrides[key]) {
|
|
363
|
+
Object.assign(field, overrides[key])
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return field
|
|
367
|
+
})
|
|
368
|
+
}
|
|
369
|
+
|
|
239
370
|
/**
|
|
240
371
|
* Generate faceted filter definitions from a schema's facetable properties.
|
|
241
372
|
*
|
|
@@ -263,6 +394,7 @@ export function filtersFromSchema(schema) {
|
|
|
263
394
|
const filter = {
|
|
264
395
|
key,
|
|
265
396
|
label: prop.title || key,
|
|
397
|
+
description: prop.description || '',
|
|
266
398
|
propertyType: prop.type || 'string',
|
|
267
399
|
options: [],
|
|
268
400
|
value: null,
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="cn-detail-layout">
|
|
3
|
-
<!-- Header -->
|
|
4
|
-
<div class="cn-detail-layout__header">
|
|
5
|
-
<NcButton @click="$emit('back')">
|
|
6
|
-
<template #icon>
|
|
7
|
-
<ArrowLeft :size="20" />
|
|
8
|
-
</template>
|
|
9
|
-
{{ backLabel }}
|
|
10
|
-
</NcButton>
|
|
11
|
-
|
|
12
|
-
<h2 class="cn-detail-layout__title">
|
|
13
|
-
<slot name="title">{{ title }}</slot>
|
|
14
|
-
</h2>
|
|
15
|
-
|
|
16
|
-
<div class="cn-detail-layout__actions">
|
|
17
|
-
<slot name="actions" />
|
|
18
|
-
</div>
|
|
19
|
-
</div>
|
|
20
|
-
|
|
21
|
-
<!-- Loading state -->
|
|
22
|
-
<div v-if="loading" class="cn-loading-container">
|
|
23
|
-
<NcLoadingIcon :size="32" />
|
|
24
|
-
</div>
|
|
25
|
-
|
|
26
|
-
<!-- Main content -->
|
|
27
|
-
<div v-else class="cn-detail-layout__content">
|
|
28
|
-
<slot />
|
|
29
|
-
</div>
|
|
30
|
-
|
|
31
|
-
<!-- Delete confirmation dialog -->
|
|
32
|
-
<slot name="dialogs" />
|
|
33
|
-
</div>
|
|
34
|
-
</template>
|
|
35
|
-
|
|
36
|
-
<script>
|
|
37
|
-
import { NcButton, NcLoadingIcon } from '@nextcloud/vue'
|
|
38
|
-
import ArrowLeft from 'vue-material-design-icons/ArrowLeft.vue'
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* CnDetailViewLayout — Detail page layout with back button, title, actions, and content.
|
|
42
|
-
*
|
|
43
|
-
* Provides the standard structure for detail/edit views: back navigation,
|
|
44
|
-
* page title, action buttons, and a content area. Supports loading state
|
|
45
|
-
* and a dialogs slot for modals.
|
|
46
|
-
*
|
|
47
|
-
* @example
|
|
48
|
-
* <CnDetailViewLayout
|
|
49
|
-
* title="Client: Acme Corp"
|
|
50
|
-
* :loading="isLoading"
|
|
51
|
-
* @back="goBack">
|
|
52
|
-
* <template #actions>
|
|
53
|
-
* <NcButton @click="edit">Edit</NcButton>
|
|
54
|
-
* <NcButton type="error" @click="confirmDelete">Delete</NcButton>
|
|
55
|
-
* </template>
|
|
56
|
-
* <div class="cn-detail-grid">
|
|
57
|
-
* <div class="cn-detail-item">...</div>
|
|
58
|
-
* </div>
|
|
59
|
-
* </CnDetailViewLayout>
|
|
60
|
-
*/
|
|
61
|
-
export default {
|
|
62
|
-
name: 'CnDetailViewLayout',
|
|
63
|
-
|
|
64
|
-
components: {
|
|
65
|
-
NcButton,
|
|
66
|
-
NcLoadingIcon,
|
|
67
|
-
ArrowLeft,
|
|
68
|
-
},
|
|
69
|
-
|
|
70
|
-
props: {
|
|
71
|
-
/** Page title */
|
|
72
|
-
title: {
|
|
73
|
-
type: String,
|
|
74
|
-
default: '',
|
|
75
|
-
},
|
|
76
|
-
/** Whether data is loading */
|
|
77
|
-
loading: {
|
|
78
|
-
type: Boolean,
|
|
79
|
-
default: false,
|
|
80
|
-
},
|
|
81
|
-
/** Back button label */
|
|
82
|
-
backLabel: {
|
|
83
|
-
type: String,
|
|
84
|
-
default: 'Back',
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
}
|
|
88
|
-
</script>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default as CnDetailViewLayout } from './CnDetailViewLayout.vue'
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<NcEmptyContent :name="title" :description="description">
|
|
3
|
-
<template #icon>
|
|
4
|
-
<slot name="icon">
|
|
5
|
-
<component :is="icon" v-if="icon" :size="64" />
|
|
6
|
-
</slot>
|
|
7
|
-
</template>
|
|
8
|
-
<template v-if="actionLabel" #action>
|
|
9
|
-
<slot name="action">
|
|
10
|
-
<NcButton :type="actionType" @click="$emit('action')">
|
|
11
|
-
{{ actionLabel }}
|
|
12
|
-
</NcButton>
|
|
13
|
-
</slot>
|
|
14
|
-
</template>
|
|
15
|
-
</NcEmptyContent>
|
|
16
|
-
</template>
|
|
17
|
-
|
|
18
|
-
<script>
|
|
19
|
-
import { NcEmptyContent, NcButton } from '@nextcloud/vue'
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* CnEmptyState — Consistent empty state display wrapping NcEmptyContent.
|
|
23
|
-
*
|
|
24
|
-
* Provides a unified empty state pattern with icon, title, description,
|
|
25
|
-
* and optional action button. Used across all list views.
|
|
26
|
-
*
|
|
27
|
-
* @example
|
|
28
|
-
* <CnEmptyState
|
|
29
|
-
* title="No clients yet"
|
|
30
|
-
* description="Create your first client to get started"
|
|
31
|
-
* action-label="New Client"
|
|
32
|
-
* @action="createClient" />
|
|
33
|
-
*
|
|
34
|
-
* @example
|
|
35
|
-
* <!-- With custom icon -->
|
|
36
|
-
* <CnEmptyState title="No results">
|
|
37
|
-
* <template #icon>
|
|
38
|
-
* <Magnify :size="64" />
|
|
39
|
-
* </template>
|
|
40
|
-
* </CnEmptyState>
|
|
41
|
-
*/
|
|
42
|
-
export default {
|
|
43
|
-
name: 'CnEmptyState',
|
|
44
|
-
|
|
45
|
-
components: {
|
|
46
|
-
NcEmptyContent,
|
|
47
|
-
NcButton,
|
|
48
|
-
},
|
|
49
|
-
|
|
50
|
-
props: {
|
|
51
|
-
/** Main title text */
|
|
52
|
-
title: {
|
|
53
|
-
type: String,
|
|
54
|
-
required: true,
|
|
55
|
-
},
|
|
56
|
-
/** Description text below the title */
|
|
57
|
-
description: {
|
|
58
|
-
type: String,
|
|
59
|
-
default: '',
|
|
60
|
-
},
|
|
61
|
-
/** Vue component for the icon (e.g., imported material design icon) */
|
|
62
|
-
icon: {
|
|
63
|
-
type: [Object, null],
|
|
64
|
-
default: null,
|
|
65
|
-
},
|
|
66
|
-
/** Action button label. If empty, no button is shown. */
|
|
67
|
-
actionLabel: {
|
|
68
|
-
type: String,
|
|
69
|
-
default: '',
|
|
70
|
-
},
|
|
71
|
-
/** NcButton type for the action button */
|
|
72
|
-
actionType: {
|
|
73
|
-
type: String,
|
|
74
|
-
default: 'primary',
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
}
|
|
78
|
-
</script>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default as CnEmptyState } from './CnEmptyState.vue'
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="cn-list-layout">
|
|
3
|
-
<!-- Header -->
|
|
4
|
-
<div class="cn-list-layout__header">
|
|
5
|
-
<div class="cn-list-layout__title">
|
|
6
|
-
<h2>{{ title }}</h2>
|
|
7
|
-
<span v-if="totalItems > 0" class="cn-list-layout__count">({{ totalItems }})</span>
|
|
8
|
-
</div>
|
|
9
|
-
<div class="cn-list-layout__actions">
|
|
10
|
-
<slot name="actions" />
|
|
11
|
-
</div>
|
|
12
|
-
</div>
|
|
13
|
-
|
|
14
|
-
<!-- Filters slot -->
|
|
15
|
-
<slot name="filters" />
|
|
16
|
-
|
|
17
|
-
<!-- Loading state -->
|
|
18
|
-
<div v-if="loading" class="cn-loading-container">
|
|
19
|
-
<NcLoadingIcon :size="32" />
|
|
20
|
-
</div>
|
|
21
|
-
|
|
22
|
-
<!-- Main content (table area) -->
|
|
23
|
-
<template v-else>
|
|
24
|
-
<slot />
|
|
25
|
-
</template>
|
|
26
|
-
|
|
27
|
-
<!-- Pagination slot -->
|
|
28
|
-
<slot name="pagination" />
|
|
29
|
-
</div>
|
|
30
|
-
</template>
|
|
31
|
-
|
|
32
|
-
<script>
|
|
33
|
-
import { NcLoadingIcon } from '@nextcloud/vue'
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* CnListViewLayout — Full list page layout wrapping header, filters, table, and pagination.
|
|
37
|
-
*
|
|
38
|
-
* Provides the standard page structure used by every list view: a header with
|
|
39
|
-
* title + action buttons, a filter/search area, the main content (table), and pagination.
|
|
40
|
-
*
|
|
41
|
-
* @example
|
|
42
|
-
* <CnListViewLayout title="Clients" :total-items="clients.length" :loading="isLoading">
|
|
43
|
-
* <template #actions>
|
|
44
|
-
* <NcButton type="primary" @click="createClient">New client</NcButton>
|
|
45
|
-
* </template>
|
|
46
|
-
* <template #filters>
|
|
47
|
-
* <CnFilterBar ... />
|
|
48
|
-
* </template>
|
|
49
|
-
* <CnDataTable :columns="columns" :rows="clients" />
|
|
50
|
-
* <template #pagination>
|
|
51
|
-
* <CnPagination ... />
|
|
52
|
-
* </template>
|
|
53
|
-
* </CnListViewLayout>
|
|
54
|
-
*/
|
|
55
|
-
export default {
|
|
56
|
-
name: 'CnListViewLayout',
|
|
57
|
-
|
|
58
|
-
components: {
|
|
59
|
-
NcLoadingIcon,
|
|
60
|
-
},
|
|
61
|
-
|
|
62
|
-
props: {
|
|
63
|
-
/** Page title */
|
|
64
|
-
title: {
|
|
65
|
-
type: String,
|
|
66
|
-
required: true,
|
|
67
|
-
},
|
|
68
|
-
/** Total items count (shown next to title) */
|
|
69
|
-
totalItems: {
|
|
70
|
-
type: Number,
|
|
71
|
-
default: 0,
|
|
72
|
-
},
|
|
73
|
-
/** Whether data is loading */
|
|
74
|
-
loading: {
|
|
75
|
-
type: Boolean,
|
|
76
|
-
default: false,
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
}
|
|
80
|
-
</script>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default as CnListViewLayout } from './CnListViewLayout.vue'
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="cn-view-mode-toggle" role="group" :aria-label="ariaLabel">
|
|
3
|
-
<NcButton
|
|
4
|
-
:type="value === 'cards' ? 'primary' : 'secondary'"
|
|
5
|
-
:aria-pressed="String(value === 'cards')"
|
|
6
|
-
@click="$emit('input', 'cards')">
|
|
7
|
-
<template #icon>
|
|
8
|
-
<ViewGrid :size="20" />
|
|
9
|
-
</template>
|
|
10
|
-
{{ cardsLabel }}
|
|
11
|
-
</NcButton>
|
|
12
|
-
<NcButton
|
|
13
|
-
:type="value === 'table' ? 'primary' : 'secondary'"
|
|
14
|
-
:aria-pressed="String(value === 'table')"
|
|
15
|
-
@click="$emit('input', 'table')">
|
|
16
|
-
<template #icon>
|
|
17
|
-
<ViewList :size="20" />
|
|
18
|
-
</template>
|
|
19
|
-
{{ tableLabel }}
|
|
20
|
-
</NcButton>
|
|
21
|
-
</div>
|
|
22
|
-
</template>
|
|
23
|
-
|
|
24
|
-
<script>
|
|
25
|
-
import { NcButton } from '@nextcloud/vue'
|
|
26
|
-
import ViewGrid from 'vue-material-design-icons/ViewGrid.vue'
|
|
27
|
-
import ViewList from 'vue-material-design-icons/ViewList.vue'
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* CnViewModeToggle — Cards/Table view mode toggle.
|
|
31
|
-
*
|
|
32
|
-
* @example
|
|
33
|
-
* <CnViewModeToggle v-model="viewMode" />
|
|
34
|
-
*/
|
|
35
|
-
export default {
|
|
36
|
-
name: 'CnViewModeToggle',
|
|
37
|
-
|
|
38
|
-
components: {
|
|
39
|
-
NcButton,
|
|
40
|
-
ViewGrid,
|
|
41
|
-
ViewList,
|
|
42
|
-
},
|
|
43
|
-
|
|
44
|
-
props: {
|
|
45
|
-
/** Current view mode: 'cards' or 'table' */
|
|
46
|
-
value: {
|
|
47
|
-
type: String,
|
|
48
|
-
default: 'table',
|
|
49
|
-
validator: (v) => ['cards', 'table'].includes(v),
|
|
50
|
-
},
|
|
51
|
-
/** Label for cards button */
|
|
52
|
-
cardsLabel: {
|
|
53
|
-
type: String,
|
|
54
|
-
default: 'Cards',
|
|
55
|
-
},
|
|
56
|
-
/** Label for table button */
|
|
57
|
-
tableLabel: {
|
|
58
|
-
type: String,
|
|
59
|
-
default: 'Table',
|
|
60
|
-
},
|
|
61
|
-
/** Aria label for the toggle group */
|
|
62
|
-
ariaLabel: {
|
|
63
|
-
type: String,
|
|
64
|
-
default: 'View mode',
|
|
65
|
-
},
|
|
66
|
-
},
|
|
67
|
-
}
|
|
68
|
-
</script>
|
|
69
|
-
|
|
70
|
-
<style scoped>
|
|
71
|
-
.cn-view-mode-toggle {
|
|
72
|
-
display: inline-flex;
|
|
73
|
-
gap: 0;
|
|
74
|
-
border-radius: var(--border-radius-pill, 20px);
|
|
75
|
-
overflow: hidden;
|
|
76
|
-
}
|
|
77
|
-
</style>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { default as CnViewModeToggle } from './CnViewModeToggle.vue'
|