@conduction/nextcloud-vue 0.1.0-beta.2 → 0.1.0-beta.4
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 -226
- package/css/index.css +5 -0
- package/dist/nextcloud-vue.cjs.js +60455 -8755
- package/dist/nextcloud-vue.cjs.js.map +1 -1
- package/dist/nextcloud-vue.css +2062 -528
- package/dist/nextcloud-vue.esm.js +60411 -8731
- package/dist/nextcloud-vue.esm.js.map +1 -1
- package/package.json +75 -61
- package/src/components/CnActionsBar/CnActionsBar.vue +235 -225
- package/src/components/CnActionsBar/index.js +1 -1
- package/src/components/CnAdvancedFormDialog/CnAdvancedFormDialog.vue +579 -0
- package/src/components/CnAdvancedFormDialog/CnDataTab.vue +217 -0
- package/src/components/CnAdvancedFormDialog/CnMetadataTab.vue +121 -0
- package/src/components/CnAdvancedFormDialog/CnPropertiesTab.vue +418 -0
- package/src/components/CnAdvancedFormDialog/CnPropertyValueCell.vue +247 -0
- package/src/components/CnAdvancedFormDialog/index.js +1 -0
- package/src/components/CnCardGrid/CnCardGrid.vue +152 -152
- package/src/components/CnCardGrid/index.js +1 -1
- package/src/components/CnCellRenderer/CnCellRenderer.vue +132 -132
- package/src/components/CnCellRenderer/index.js +1 -1
- package/src/components/CnChartWidget/CnChartWidget.vue +320 -0
- package/src/components/CnChartWidget/index.js +1 -0
- package/src/components/CnConfigurationCard/CnConfigurationCard.vue +77 -77
- package/src/components/CnConfigurationCard/index.js +1 -1
- package/src/components/CnDashboardGrid/CnDashboardGrid.vue +225 -0
- package/src/components/CnDashboardGrid/index.js +1 -0
- package/src/components/CnDashboardPage/CnDashboardPage.vue +390 -0
- package/src/components/CnDashboardPage/index.js +1 -0
- package/src/components/CnDataTable/CnDataTable.vue +349 -349
- package/src/components/CnDataTable/index.js +1 -1
- package/src/components/CnDetailCard/CnDetailCard.vue +214 -0
- package/src/components/CnDetailCard/index.js +1 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +281 -0
- package/src/components/CnDetailPage/index.js +1 -0
- package/src/components/CnFacetSidebar/CnFacetSidebar.vue +231 -223
- package/src/components/CnFacetSidebar/index.js +1 -1
- package/src/components/CnFilterBar/CnFilterBar.vue +152 -152
- package/src/components/CnFilterBar/index.js +1 -1
- package/src/components/CnIcon/CnIcon.vue +89 -89
- package/src/components/CnIcon/index.js +1 -1
- package/src/components/CnIndexPage/CnIndexPage.vue +874 -816
- package/src/components/CnIndexPage/index.js +1 -1
- package/src/components/CnIndexSidebar/CnIndexSidebar.vue +503 -484
- package/src/components/CnIndexSidebar/index.js +1 -1
- package/src/components/CnItemCard/CnItemCard.vue +132 -0
- package/src/components/CnItemCard/index.js +1 -0
- package/src/components/CnKpiGrid/CnKpiGrid.vue +89 -89
- package/src/components/CnKpiGrid/index.js +1 -1
- package/src/components/CnMassActionBar/CnMassActionBar.vue +160 -160
- package/src/components/CnMassActionBar/index.js +1 -1
- package/src/components/CnMassCopyDialog/CnMassCopyDialog.vue +320 -320
- package/src/components/CnMassCopyDialog/index.js +1 -1
- package/src/components/CnMassDeleteDialog/CnMassDeleteDialog.vue +238 -238
- package/src/components/CnMassDeleteDialog/index.js +1 -1
- package/src/components/CnMassExportDialog/CnMassExportDialog.vue +190 -190
- package/src/components/CnMassExportDialog/index.js +1 -1
- package/src/components/CnMassImportDialog/CnMassImportDialog.vue +491 -491
- package/src/components/CnMassImportDialog/index.js +1 -1
- package/src/components/CnNoteCard/CnNoteCard.vue +149 -0
- package/src/components/CnNoteCard/index.js +1 -0
- package/src/components/CnNotesCard/CnNotesCard.vue +413 -0
- package/src/components/CnNotesCard/index.js +1 -0
- package/src/components/CnObjectCard/CnObjectCard.vue +292 -292
- package/src/components/CnObjectCard/index.js +1 -1
- package/src/components/CnObjectSidebar/CnObjectSidebar.vue +876 -0
- package/src/components/CnObjectSidebar/index.js +1 -0
- package/src/components/CnPageHeader/CnPageHeader.vue +57 -57
- package/src/components/CnPageHeader/index.js +1 -1
- package/src/components/CnPagination/CnPagination.vue +252 -252
- package/src/components/CnPagination/index.js +1 -1
- package/src/components/CnRowActions/CnRowActions.vue +73 -73
- package/src/components/CnRowActions/index.js +1 -1
- package/src/components/CnSchemaFormDialog/CnSchemaConfigurationTab.vue +226 -0
- package/src/components/CnSchemaFormDialog/CnSchemaFormDialog.vue +787 -0
- package/src/components/CnSchemaFormDialog/CnSchemaPropertiesTab.vue +305 -0
- package/src/components/CnSchemaFormDialog/CnSchemaPropertyActions.vue +1398 -0
- package/src/components/CnSchemaFormDialog/CnSchemaSecurityTab.vue +236 -0
- package/src/components/CnSchemaFormDialog/index.js +1 -0
- package/src/components/CnSettingsCard/CnSettingsCard.vue +92 -92
- package/src/components/CnSettingsCard/index.js +1 -1
- package/src/components/CnSettingsSection/CnSettingsSection.vue +266 -266
- package/src/components/CnSettingsSection/index.js +1 -1
- package/src/components/CnStatsBlock/CnStatsBlock.vue +420 -366
- package/src/components/CnStatsBlock/index.js +1 -1
- package/src/components/CnStatusBadge/CnStatusBadge.vue +77 -77
- package/src/components/CnStatusBadge/index.js +1 -1
- package/src/components/CnTabbedFormDialog/CnTabbedFormDialog.vue +540 -0
- package/src/components/CnTabbedFormDialog/index.js +1 -0
- package/src/components/CnTasksCard/CnTasksCard.vue +373 -0
- package/src/components/CnTasksCard/index.js +1 -0
- package/src/components/CnTileWidget/CnTileWidget.vue +159 -0
- package/src/components/CnTileWidget/index.js +1 -0
- package/src/components/CnTimelineStages/CnTimelineStages.vue +292 -0
- package/src/components/CnTimelineStages/index.js +1 -0
- package/src/components/CnUserActionMenu/CnUserActionMenu.vue +435 -0
- package/src/components/CnUserActionMenu/index.js +1 -0
- package/src/components/CnVersionInfoCard/CnVersionInfoCard.vue +312 -312
- package/src/components/CnVersionInfoCard/index.js +1 -1
- package/src/components/CnWidgetRenderer/CnWidgetRenderer.vue +180 -0
- package/src/components/CnWidgetRenderer/index.js +1 -0
- package/src/components/CnWidgetWrapper/CnWidgetWrapper.vue +211 -0
- package/src/components/CnWidgetWrapper/index.js +1 -0
- package/src/components/index.js +43 -29
- package/src/composables/index.js +4 -3
- package/src/composables/useDashboardView.js +240 -0
- package/src/composables/useDetailView.js +289 -132
- package/src/composables/useListView.js +363 -153
- package/src/composables/useSubResource.js +142 -142
- package/src/constants/metadata.js +30 -30
- package/src/css/CnSchemaFormDialog.css +546 -0
- package/src/css/__sample_nextcloud_tokens.css +110 -0
- package/src/css/actions-bar.css +48 -48
- package/src/css/badge.css +51 -51
- package/src/css/card.css +128 -128
- package/src/css/dashboard.css +70 -0
- package/src/css/detail-page.css +168 -0
- package/src/css/detail.css +68 -68
- package/src/css/index-page.css +44 -32
- package/src/css/index-sidebar.css +193 -187
- package/src/css/index.css +16 -12
- package/src/css/layout.css +90 -90
- package/src/css/page-header.css +33 -33
- package/src/css/pagination.css +72 -72
- package/src/css/table.css +142 -142
- package/src/css/timeline-stages.css +218 -0
- package/src/css/utilities.css +46 -46
- package/src/index.js +72 -53
- package/src/store/createSubResourcePlugin.js +135 -135
- package/src/store/index.js +3 -3
- package/src/store/plugins/auditTrails.js +17 -17
- package/src/store/plugins/files.js +250 -186
- package/src/store/plugins/index.js +7 -5
- package/src/store/plugins/lifecycle.js +180 -180
- package/src/store/plugins/relations.js +68 -68
- package/src/store/plugins/search.js +372 -0
- package/src/store/plugins/selection.js +104 -0
- package/src/store/useObjectStore.js +829 -686
- package/src/types/auditTrail.d.ts +32 -32
- package/src/types/file.d.ts +23 -23
- package/src/types/index.d.ts +35 -35
- package/src/types/notification.d.ts +36 -36
- package/src/types/object.d.ts +40 -40
- package/src/types/organisation.d.ts +41 -41
- package/src/types/register.d.ts +25 -25
- package/src/types/schema.d.ts +39 -39
- package/src/types/shared.d.ts +79 -79
- package/src/types/source.d.ts +14 -14
- package/src/types/task.d.ts +31 -31
- package/src/utils/errors.js +96 -96
- package/src/utils/headers.js +68 -50
- package/src/utils/id.js +13 -0
- package/src/utils/index.js +3 -3
- package/src/utils/schema.js +422 -419
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as CnObjectSidebar } from './CnObjectSidebar.vue'
|
|
@@ -1,57 +1,57 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="cn-page-header">
|
|
3
|
-
<div v-if="icon || $slots.icon" class="cn-page-header__icon">
|
|
4
|
-
<slot name="icon">
|
|
5
|
-
<CnIcon :name="icon" :size="iconSize" />
|
|
6
|
-
</slot>
|
|
7
|
-
</div>
|
|
8
|
-
<div class="cn-page-header__text">
|
|
9
|
-
<h1 class="cn-page-header__title">{{ title }}</h1>
|
|
10
|
-
<p v-if="description" class="cn-page-header__description">{{ description }}</p>
|
|
11
|
-
</div>
|
|
12
|
-
<slot name="extra" />
|
|
13
|
-
</div>
|
|
14
|
-
</template>
|
|
15
|
-
|
|
16
|
-
<script>
|
|
17
|
-
import { CnIcon } from '../CnIcon/index.js'
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* CnPageHeader — Reusable page header with optional icon, title, and description.
|
|
21
|
-
*
|
|
22
|
-
* @example
|
|
23
|
-
* <CnPageHeader title="Clients" description="Manage your clients" icon="AccountGroup" />
|
|
24
|
-
*/
|
|
25
|
-
export default {
|
|
26
|
-
name: 'CnPageHeader',
|
|
27
|
-
|
|
28
|
-
components: {
|
|
29
|
-
CnIcon,
|
|
30
|
-
},
|
|
31
|
-
|
|
32
|
-
props: {
|
|
33
|
-
/** Page title text */
|
|
34
|
-
title: {
|
|
35
|
-
type: String,
|
|
36
|
-
required: true,
|
|
37
|
-
},
|
|
38
|
-
/** Optional description shown below the title */
|
|
39
|
-
description: {
|
|
40
|
-
type: String,
|
|
41
|
-
default: '',
|
|
42
|
-
},
|
|
43
|
-
/** Optional MDI icon name (rendered via CnIcon) */
|
|
44
|
-
icon: {
|
|
45
|
-
type: String,
|
|
46
|
-
default: '',
|
|
47
|
-
},
|
|
48
|
-
/** Icon size in pixels */
|
|
49
|
-
iconSize: {
|
|
50
|
-
type: Number,
|
|
51
|
-
default: 28,
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
}
|
|
55
|
-
</script>
|
|
56
|
-
|
|
57
|
-
<!-- Styles in css/page-header.css -->
|
|
1
|
+
<template>
|
|
2
|
+
<div class="cn-page-header">
|
|
3
|
+
<div v-if="icon || $slots.icon" class="cn-page-header__icon">
|
|
4
|
+
<slot name="icon">
|
|
5
|
+
<CnIcon :name="icon" :size="iconSize" />
|
|
6
|
+
</slot>
|
|
7
|
+
</div>
|
|
8
|
+
<div class="cn-page-header__text">
|
|
9
|
+
<h1 class="cn-page-header__title">{{ title }}</h1>
|
|
10
|
+
<p v-if="description" class="cn-page-header__description">{{ description }}</p>
|
|
11
|
+
</div>
|
|
12
|
+
<slot name="extra" />
|
|
13
|
+
</div>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<script>
|
|
17
|
+
import { CnIcon } from '../CnIcon/index.js'
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* CnPageHeader — Reusable page header with optional icon, title, and description.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* <CnPageHeader title="Clients" description="Manage your clients" icon="AccountGroup" />
|
|
24
|
+
*/
|
|
25
|
+
export default {
|
|
26
|
+
name: 'CnPageHeader',
|
|
27
|
+
|
|
28
|
+
components: {
|
|
29
|
+
CnIcon,
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
props: {
|
|
33
|
+
/** Page title text */
|
|
34
|
+
title: {
|
|
35
|
+
type: String,
|
|
36
|
+
required: true,
|
|
37
|
+
},
|
|
38
|
+
/** Optional description shown below the title */
|
|
39
|
+
description: {
|
|
40
|
+
type: String,
|
|
41
|
+
default: '',
|
|
42
|
+
},
|
|
43
|
+
/** Optional MDI icon name (rendered via CnIcon) */
|
|
44
|
+
icon: {
|
|
45
|
+
type: String,
|
|
46
|
+
default: '',
|
|
47
|
+
},
|
|
48
|
+
/** Icon size in pixels */
|
|
49
|
+
iconSize: {
|
|
50
|
+
type: Number,
|
|
51
|
+
default: 28,
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<!-- Styles in css/page-header.css -->
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { default as CnPageHeader } from './CnPageHeader.vue'
|
|
1
|
+
export { default as CnPageHeader } from './CnPageHeader.vue'
|
|
@@ -1,252 +1,252 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div v-if="totalPages > 1 || totalItems > minItemsToShow" class="cn-pagination">
|
|
3
|
-
<!-- Page info -->
|
|
4
|
-
<div class="cn-pagination__info">
|
|
5
|
-
<span class="cn-pagination__page-info">
|
|
6
|
-
{{ pageInfoText }}
|
|
7
|
-
</span>
|
|
8
|
-
</div>
|
|
9
|
-
|
|
10
|
-
<!-- Page navigation -->
|
|
11
|
-
<div v-if="totalPages > 1" class="cn-pagination__nav">
|
|
12
|
-
<NcButton
|
|
13
|
-
:disabled="currentPage === 1"
|
|
14
|
-
@click="changePage(1)">
|
|
15
|
-
{{ firstLabel }}
|
|
16
|
-
</NcButton>
|
|
17
|
-
|
|
18
|
-
<NcButton
|
|
19
|
-
:disabled="currentPage === 1"
|
|
20
|
-
@click="changePage(currentPage - 1)">
|
|
21
|
-
{{ previousLabel }}
|
|
22
|
-
</NcButton>
|
|
23
|
-
|
|
24
|
-
<div class="cn-pagination__numbers">
|
|
25
|
-
<template v-for="page in visiblePages">
|
|
26
|
-
<span v-if="page === '...'" :key="'ellipsis-' + page" class="cn-pagination__ellipsis">...</span>
|
|
27
|
-
<NcButton
|
|
28
|
-
v-else
|
|
29
|
-
:key="page"
|
|
30
|
-
:type="page === currentPage ? 'primary' : 'secondary'"
|
|
31
|
-
:disabled="page === currentPage"
|
|
32
|
-
@click="changePage(page)">
|
|
33
|
-
{{ page }}
|
|
34
|
-
</NcButton>
|
|
35
|
-
</template>
|
|
36
|
-
</div>
|
|
37
|
-
|
|
38
|
-
<NcButton
|
|
39
|
-
:disabled="currentPage === totalPages"
|
|
40
|
-
@click="changePage(currentPage + 1)">
|
|
41
|
-
{{ nextLabel }}
|
|
42
|
-
</NcButton>
|
|
43
|
-
|
|
44
|
-
<NcButton
|
|
45
|
-
:disabled="currentPage === totalPages"
|
|
46
|
-
@click="changePage(totalPages)">
|
|
47
|
-
{{ lastLabel }}
|
|
48
|
-
</NcButton>
|
|
49
|
-
</div>
|
|
50
|
-
|
|
51
|
-
<!-- Page size selector -->
|
|
52
|
-
<div class="cn-pagination__page-size">
|
|
53
|
-
<label :for="pageSizeId">{{ itemsPerPageLabel }}</label>
|
|
54
|
-
<NcSelect
|
|
55
|
-
:input-id="pageSizeId"
|
|
56
|
-
class="cn-pagination__page-size-select"
|
|
57
|
-
:value="currentPageSizeOption"
|
|
58
|
-
:options="pageSizeOptions"
|
|
59
|
-
:clearable="false"
|
|
60
|
-
:input-label="itemsPerPageLabel"
|
|
61
|
-
@option:selected="changePageSize" />
|
|
62
|
-
</div>
|
|
63
|
-
</div>
|
|
64
|
-
</template>
|
|
65
|
-
|
|
66
|
-
<script>
|
|
67
|
-
import { NcButton, NcSelect } from '@nextcloud/vue'
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* CnPagination — Full pagination with page numbers, navigation, and page size selector.
|
|
71
|
-
*
|
|
72
|
-
* Extracted from OpenRegister's PaginationComponent. Zero store dependencies.
|
|
73
|
-
* Supports First/Previous/Next/Last buttons, smart page number display with
|
|
74
|
-
* ellipsis, and configurable page size.
|
|
75
|
-
*
|
|
76
|
-
* NL Design tokens used:
|
|
77
|
-
* - Inherits from cn-pagination CSS class (see css/pagination.css)
|
|
78
|
-
*
|
|
79
|
-
* @example
|
|
80
|
-
* <CnPagination
|
|
81
|
-
* :current-page="page"
|
|
82
|
-
* :total-pages="totalPages"
|
|
83
|
-
* :total-items="totalItems"
|
|
84
|
-
* :current-page-size="limit"
|
|
85
|
-
* @page-changed="onPageChange"
|
|
86
|
-
* @page-size-changed="onPageSizeChange" />
|
|
87
|
-
*/
|
|
88
|
-
export default {
|
|
89
|
-
name: 'CnPagination',
|
|
90
|
-
|
|
91
|
-
components: {
|
|
92
|
-
NcButton,
|
|
93
|
-
NcSelect,
|
|
94
|
-
},
|
|
95
|
-
|
|
96
|
-
props: {
|
|
97
|
-
/** Current page number (1-based) */
|
|
98
|
-
currentPage: {
|
|
99
|
-
type: Number,
|
|
100
|
-
default: 1,
|
|
101
|
-
},
|
|
102
|
-
/** Total number of pages */
|
|
103
|
-
totalPages: {
|
|
104
|
-
type: Number,
|
|
105
|
-
default: 1,
|
|
106
|
-
},
|
|
107
|
-
/** Total number of items across all pages */
|
|
108
|
-
totalItems: {
|
|
109
|
-
type: Number,
|
|
110
|
-
default: 0,
|
|
111
|
-
},
|
|
112
|
-
/** Current items per page */
|
|
113
|
-
currentPageSize: {
|
|
114
|
-
type: Number,
|
|
115
|
-
default: 20,
|
|
116
|
-
},
|
|
117
|
-
/** Available page size options */
|
|
118
|
-
pageSizeOptions: {
|
|
119
|
-
type: Array,
|
|
120
|
-
default: () => [
|
|
121
|
-
{ value: 10, label: '10' },
|
|
122
|
-
{ value: 20, label: '20' },
|
|
123
|
-
{ value: 50, label: '50' },
|
|
124
|
-
{ value: 100, label: '100' },
|
|
125
|
-
{ value: 250, label: '250' },
|
|
126
|
-
{ value: 500, label: '500' },
|
|
127
|
-
{ value: 1000, label: '1000' },
|
|
128
|
-
],
|
|
129
|
-
},
|
|
130
|
-
/** Minimum items before pagination is shown */
|
|
131
|
-
minItemsToShow: {
|
|
132
|
-
type: Number,
|
|
133
|
-
default: 10,
|
|
134
|
-
},
|
|
135
|
-
/** Label for "First" button */
|
|
136
|
-
firstLabel: {
|
|
137
|
-
type: String,
|
|
138
|
-
default: 'First',
|
|
139
|
-
},
|
|
140
|
-
/** Label for "Previous" button */
|
|
141
|
-
previousLabel: {
|
|
142
|
-
type: String,
|
|
143
|
-
default: 'Previous',
|
|
144
|
-
},
|
|
145
|
-
/** Label for "Next" button */
|
|
146
|
-
nextLabel: {
|
|
147
|
-
type: String,
|
|
148
|
-
default: 'Next',
|
|
149
|
-
},
|
|
150
|
-
/** Label for "Last" button */
|
|
151
|
-
lastLabel: {
|
|
152
|
-
type: String,
|
|
153
|
-
default: 'Last',
|
|
154
|
-
},
|
|
155
|
-
/** Label for "Items per page:" */
|
|
156
|
-
itemsPerPageLabel: {
|
|
157
|
-
type: String,
|
|
158
|
-
default: 'Items per page:',
|
|
159
|
-
},
|
|
160
|
-
/**
|
|
161
|
-
* Page info format string. Use {current} and {total} as placeholders.
|
|
162
|
-
* @example "Page {current} of {total}"
|
|
163
|
-
*/
|
|
164
|
-
pageInfoFormat: {
|
|
165
|
-
type: String,
|
|
166
|
-
default: 'Page {current} of {total}',
|
|
167
|
-
},
|
|
168
|
-
},
|
|
169
|
-
|
|
170
|
-
computed: {
|
|
171
|
-
pageSizeId() {
|
|
172
|
-
return 'cn-page-size-' + this._uid
|
|
173
|
-
},
|
|
174
|
-
|
|
175
|
-
currentPageSizeOption() {
|
|
176
|
-
return this.pageSizeOptions.find(
|
|
177
|
-
(option) => option.value === this.currentPageSize,
|
|
178
|
-
) || this.pageSizeOptions[1]
|
|
179
|
-
},
|
|
180
|
-
|
|
181
|
-
pageInfoText() {
|
|
182
|
-
return this.pageInfoFormat
|
|
183
|
-
.replace('{current}', this.currentPage)
|
|
184
|
-
.replace('{total}', this.totalPages)
|
|
185
|
-
},
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Calculate visible page numbers with ellipsis for large page counts.
|
|
189
|
-
* Shows up to 7 page numbers at a time.
|
|
190
|
-
*/
|
|
191
|
-
visiblePages() {
|
|
192
|
-
const current = this.currentPage
|
|
193
|
-
const total = this.totalPages
|
|
194
|
-
const pages = []
|
|
195
|
-
|
|
196
|
-
if (total <= 7) {
|
|
197
|
-
for (let i = 1; i <= total; i++) {
|
|
198
|
-
pages.push(i)
|
|
199
|
-
}
|
|
200
|
-
} else {
|
|
201
|
-
pages.push(1)
|
|
202
|
-
|
|
203
|
-
if (current <= 4) {
|
|
204
|
-
for (let i = 2; i <= 5; i++) {
|
|
205
|
-
pages.push(i)
|
|
206
|
-
}
|
|
207
|
-
pages.push('...')
|
|
208
|
-
pages.push(total)
|
|
209
|
-
} else if (current >= total - 3) {
|
|
210
|
-
pages.push('...')
|
|
211
|
-
for (let i = total - 4; i <= total; i++) {
|
|
212
|
-
pages.push(i)
|
|
213
|
-
}
|
|
214
|
-
} else {
|
|
215
|
-
pages.push('...')
|
|
216
|
-
for (let i = current - 1; i <= current + 1; i++) {
|
|
217
|
-
pages.push(i)
|
|
218
|
-
}
|
|
219
|
-
pages.push('...')
|
|
220
|
-
pages.push(total)
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
return pages
|
|
225
|
-
},
|
|
226
|
-
},
|
|
227
|
-
|
|
228
|
-
methods: {
|
|
229
|
-
/**
|
|
230
|
-
* Navigate to a specific page.
|
|
231
|
-
* @param {number} page Target page number
|
|
232
|
-
*/
|
|
233
|
-
changePage(page) {
|
|
234
|
-
if (page !== this.currentPage && page >= 1 && page <= this.totalPages) {
|
|
235
|
-
/** @event page-changed Emitted when page changes. Payload: new page number. */
|
|
236
|
-
this.$emit('page-changed', page)
|
|
237
|
-
}
|
|
238
|
-
},
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Change the page size.
|
|
242
|
-
* @param {object} option Selected page size option { value, label }
|
|
243
|
-
*/
|
|
244
|
-
changePageSize(option) {
|
|
245
|
-
if (option.value !== this.currentPageSize) {
|
|
246
|
-
/** @event page-size-changed Emitted when page size changes. Payload: new page size. */
|
|
247
|
-
this.$emit('page-size-changed', option.value)
|
|
248
|
-
}
|
|
249
|
-
},
|
|
250
|
-
},
|
|
251
|
-
}
|
|
252
|
-
</script>
|
|
1
|
+
<template>
|
|
2
|
+
<div v-if="totalPages > 1 || totalItems > minItemsToShow" class="cn-pagination">
|
|
3
|
+
<!-- Page info -->
|
|
4
|
+
<div class="cn-pagination__info">
|
|
5
|
+
<span class="cn-pagination__page-info">
|
|
6
|
+
{{ pageInfoText }}
|
|
7
|
+
</span>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<!-- Page navigation -->
|
|
11
|
+
<div v-if="totalPages > 1" class="cn-pagination__nav">
|
|
12
|
+
<NcButton
|
|
13
|
+
:disabled="currentPage === 1"
|
|
14
|
+
@click="changePage(1)">
|
|
15
|
+
{{ firstLabel }}
|
|
16
|
+
</NcButton>
|
|
17
|
+
|
|
18
|
+
<NcButton
|
|
19
|
+
:disabled="currentPage === 1"
|
|
20
|
+
@click="changePage(currentPage - 1)">
|
|
21
|
+
{{ previousLabel }}
|
|
22
|
+
</NcButton>
|
|
23
|
+
|
|
24
|
+
<div class="cn-pagination__numbers">
|
|
25
|
+
<template v-for="page in visiblePages">
|
|
26
|
+
<span v-if="page === '...'" :key="'ellipsis-' + page" class="cn-pagination__ellipsis">...</span>
|
|
27
|
+
<NcButton
|
|
28
|
+
v-else
|
|
29
|
+
:key="page"
|
|
30
|
+
:type="page === currentPage ? 'primary' : 'secondary'"
|
|
31
|
+
:disabled="page === currentPage"
|
|
32
|
+
@click="changePage(page)">
|
|
33
|
+
{{ page }}
|
|
34
|
+
</NcButton>
|
|
35
|
+
</template>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<NcButton
|
|
39
|
+
:disabled="currentPage === totalPages"
|
|
40
|
+
@click="changePage(currentPage + 1)">
|
|
41
|
+
{{ nextLabel }}
|
|
42
|
+
</NcButton>
|
|
43
|
+
|
|
44
|
+
<NcButton
|
|
45
|
+
:disabled="currentPage === totalPages"
|
|
46
|
+
@click="changePage(totalPages)">
|
|
47
|
+
{{ lastLabel }}
|
|
48
|
+
</NcButton>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<!-- Page size selector -->
|
|
52
|
+
<div class="cn-pagination__page-size">
|
|
53
|
+
<label :for="pageSizeId">{{ itemsPerPageLabel }}</label>
|
|
54
|
+
<NcSelect
|
|
55
|
+
:input-id="pageSizeId"
|
|
56
|
+
class="cn-pagination__page-size-select"
|
|
57
|
+
:value="currentPageSizeOption"
|
|
58
|
+
:options="pageSizeOptions"
|
|
59
|
+
:clearable="false"
|
|
60
|
+
:input-label="itemsPerPageLabel"
|
|
61
|
+
@option:selected="changePageSize" />
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
</template>
|
|
65
|
+
|
|
66
|
+
<script>
|
|
67
|
+
import { NcButton, NcSelect } from '@nextcloud/vue'
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* CnPagination — Full pagination with page numbers, navigation, and page size selector.
|
|
71
|
+
*
|
|
72
|
+
* Extracted from OpenRegister's PaginationComponent. Zero store dependencies.
|
|
73
|
+
* Supports First/Previous/Next/Last buttons, smart page number display with
|
|
74
|
+
* ellipsis, and configurable page size.
|
|
75
|
+
*
|
|
76
|
+
* NL Design tokens used:
|
|
77
|
+
* - Inherits from cn-pagination CSS class (see css/pagination.css)
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* <CnPagination
|
|
81
|
+
* :current-page="page"
|
|
82
|
+
* :total-pages="totalPages"
|
|
83
|
+
* :total-items="totalItems"
|
|
84
|
+
* :current-page-size="limit"
|
|
85
|
+
* @page-changed="onPageChange"
|
|
86
|
+
* @page-size-changed="onPageSizeChange" />
|
|
87
|
+
*/
|
|
88
|
+
export default {
|
|
89
|
+
name: 'CnPagination',
|
|
90
|
+
|
|
91
|
+
components: {
|
|
92
|
+
NcButton,
|
|
93
|
+
NcSelect,
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
props: {
|
|
97
|
+
/** Current page number (1-based) */
|
|
98
|
+
currentPage: {
|
|
99
|
+
type: Number,
|
|
100
|
+
default: 1,
|
|
101
|
+
},
|
|
102
|
+
/** Total number of pages */
|
|
103
|
+
totalPages: {
|
|
104
|
+
type: Number,
|
|
105
|
+
default: 1,
|
|
106
|
+
},
|
|
107
|
+
/** Total number of items across all pages */
|
|
108
|
+
totalItems: {
|
|
109
|
+
type: Number,
|
|
110
|
+
default: 0,
|
|
111
|
+
},
|
|
112
|
+
/** Current items per page */
|
|
113
|
+
currentPageSize: {
|
|
114
|
+
type: Number,
|
|
115
|
+
default: 20,
|
|
116
|
+
},
|
|
117
|
+
/** Available page size options */
|
|
118
|
+
pageSizeOptions: {
|
|
119
|
+
type: Array,
|
|
120
|
+
default: () => [
|
|
121
|
+
{ value: 10, label: '10' },
|
|
122
|
+
{ value: 20, label: '20' },
|
|
123
|
+
{ value: 50, label: '50' },
|
|
124
|
+
{ value: 100, label: '100' },
|
|
125
|
+
{ value: 250, label: '250' },
|
|
126
|
+
{ value: 500, label: '500' },
|
|
127
|
+
{ value: 1000, label: '1000' },
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
/** Minimum items before pagination is shown */
|
|
131
|
+
minItemsToShow: {
|
|
132
|
+
type: Number,
|
|
133
|
+
default: 10,
|
|
134
|
+
},
|
|
135
|
+
/** Label for "First" button */
|
|
136
|
+
firstLabel: {
|
|
137
|
+
type: String,
|
|
138
|
+
default: 'First',
|
|
139
|
+
},
|
|
140
|
+
/** Label for "Previous" button */
|
|
141
|
+
previousLabel: {
|
|
142
|
+
type: String,
|
|
143
|
+
default: 'Previous',
|
|
144
|
+
},
|
|
145
|
+
/** Label for "Next" button */
|
|
146
|
+
nextLabel: {
|
|
147
|
+
type: String,
|
|
148
|
+
default: 'Next',
|
|
149
|
+
},
|
|
150
|
+
/** Label for "Last" button */
|
|
151
|
+
lastLabel: {
|
|
152
|
+
type: String,
|
|
153
|
+
default: 'Last',
|
|
154
|
+
},
|
|
155
|
+
/** Label for "Items per page:" */
|
|
156
|
+
itemsPerPageLabel: {
|
|
157
|
+
type: String,
|
|
158
|
+
default: 'Items per page:',
|
|
159
|
+
},
|
|
160
|
+
/**
|
|
161
|
+
* Page info format string. Use {current} and {total} as placeholders.
|
|
162
|
+
* @example "Page {current} of {total}"
|
|
163
|
+
*/
|
|
164
|
+
pageInfoFormat: {
|
|
165
|
+
type: String,
|
|
166
|
+
default: 'Page {current} of {total}',
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
computed: {
|
|
171
|
+
pageSizeId() {
|
|
172
|
+
return 'cn-page-size-' + this._uid
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
currentPageSizeOption() {
|
|
176
|
+
return this.pageSizeOptions.find(
|
|
177
|
+
(option) => option.value === this.currentPageSize,
|
|
178
|
+
) || this.pageSizeOptions[1]
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
pageInfoText() {
|
|
182
|
+
return this.pageInfoFormat
|
|
183
|
+
.replace('{current}', this.currentPage)
|
|
184
|
+
.replace('{total}', this.totalPages)
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Calculate visible page numbers with ellipsis for large page counts.
|
|
189
|
+
* Shows up to 7 page numbers at a time.
|
|
190
|
+
*/
|
|
191
|
+
visiblePages() {
|
|
192
|
+
const current = this.currentPage
|
|
193
|
+
const total = this.totalPages
|
|
194
|
+
const pages = []
|
|
195
|
+
|
|
196
|
+
if (total <= 7) {
|
|
197
|
+
for (let i = 1; i <= total; i++) {
|
|
198
|
+
pages.push(i)
|
|
199
|
+
}
|
|
200
|
+
} else {
|
|
201
|
+
pages.push(1)
|
|
202
|
+
|
|
203
|
+
if (current <= 4) {
|
|
204
|
+
for (let i = 2; i <= 5; i++) {
|
|
205
|
+
pages.push(i)
|
|
206
|
+
}
|
|
207
|
+
pages.push('...')
|
|
208
|
+
pages.push(total)
|
|
209
|
+
} else if (current >= total - 3) {
|
|
210
|
+
pages.push('...')
|
|
211
|
+
for (let i = total - 4; i <= total; i++) {
|
|
212
|
+
pages.push(i)
|
|
213
|
+
}
|
|
214
|
+
} else {
|
|
215
|
+
pages.push('...')
|
|
216
|
+
for (let i = current - 1; i <= current + 1; i++) {
|
|
217
|
+
pages.push(i)
|
|
218
|
+
}
|
|
219
|
+
pages.push('...')
|
|
220
|
+
pages.push(total)
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return pages
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
|
|
228
|
+
methods: {
|
|
229
|
+
/**
|
|
230
|
+
* Navigate to a specific page.
|
|
231
|
+
* @param {number} page Target page number
|
|
232
|
+
*/
|
|
233
|
+
changePage(page) {
|
|
234
|
+
if (page !== this.currentPage && page >= 1 && page <= this.totalPages) {
|
|
235
|
+
/** @event page-changed Emitted when page changes. Payload: new page number. */
|
|
236
|
+
this.$emit('page-changed', page)
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Change the page size.
|
|
242
|
+
* @param {object} option Selected page size option { value, label }
|
|
243
|
+
*/
|
|
244
|
+
changePageSize(option) {
|
|
245
|
+
if (option.value !== this.currentPageSize) {
|
|
246
|
+
/** @event page-size-changed Emitted when page size changes. Payload: new page size. */
|
|
247
|
+
this.$emit('page-size-changed', option.value)
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
}
|
|
252
|
+
</script>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { default as CnPagination } from './CnPagination.vue'
|
|
1
|
+
export { default as CnPagination } from './CnPagination.vue'
|