@synergy-design-system/mcp 2.14.1 → 2.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/metadata/checksum.txt +1 -1
- package/metadata/packages/assets/CHANGELOG.md +10 -0
- package/metadata/packages/components/components/syn-header/component.ts +1 -1
- package/metadata/packages/components/components/syn-pagination/component.angular.ts +201 -0
- package/metadata/packages/components/components/syn-pagination/component.react.ts +56 -0
- package/metadata/packages/components/components/syn-pagination/component.styles.ts +128 -0
- package/metadata/packages/components/components/syn-pagination/component.ts +452 -0
- package/metadata/packages/components/components/syn-pagination/component.vue +144 -0
- package/metadata/packages/components/static/CHANGELOG.md +15 -0
- package/metadata/packages/tokens/CHANGELOG.md +10 -0
- package/metadata/packages/tokens/dark.css +1 -1
- package/metadata/packages/tokens/index.js +1 -1
- package/metadata/packages/tokens/light.css +1 -1
- package/metadata/packages/tokens/sick2018_dark.css +1 -1
- package/metadata/packages/tokens/sick2018_light.css +1 -1
- package/metadata/packages/tokens/sick2025_dark.css +1 -1
- package/metadata/packages/tokens/sick2025_light.css +1 -1
- package/metadata/static/components/syn-pagination/docs.md +102 -0
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.15.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#1240](https://github.com/synergy-design-system/synergy-design-system/pull/1240) [`06c29d4`](https://github.com/synergy-design-system/synergy-design-system/commit/06c29d4be7b9297d34919646cfd71394abdc6f88) Thanks [@schilchSICKAG](https://github.com/schilchSICKAG)! - Released on: 2026-04-22
|
|
8
|
+
|
|
9
|
+
feat: ✨ Add new component `<syn-pagination>` (#684)
|
|
10
|
+
|
|
11
|
+
Adds a new component `<syn-pagination>` that may be used to paginate large datasets.
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- Updated dependencies [[`06c29d4`](https://github.com/synergy-design-system/synergy-design-system/commit/06c29d4be7b9297d34919646cfd71394abdc6f88)]:
|
|
16
|
+
- @synergy-design-system/assets@2.1.0
|
|
17
|
+
|
|
3
18
|
## 2.14.1
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
package/metadata/checksum.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
ca762755c51a403df85afe19edcbb5e0
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#1240](https://github.com/synergy-design-system/synergy-design-system/pull/1240) [`06c29d4`](https://github.com/synergy-design-system/synergy-design-system/commit/06c29d4be7b9297d34919646cfd71394abdc6f88) Thanks [@schilchSICKAG](https://github.com/schilchSICKAG)! - Released on: 2026-04-22
|
|
8
|
+
|
|
9
|
+
feat: ✨ Add new component `<syn-pagination>` (#684)
|
|
10
|
+
|
|
11
|
+
Adds a new component `<syn-pagination>` that may be used to paginate large datasets.
|
|
12
|
+
|
|
3
13
|
## 2.0.3
|
|
4
14
|
|
|
5
15
|
### Patch Changes
|
|
@@ -6,7 +6,7 @@ import SynergyElement from '../../internal/synergy-element.js';
|
|
|
6
6
|
import { HasSlotController } from '../../internal/slot.js';
|
|
7
7
|
import componentStyles from '../../styles/component.styles.js';
|
|
8
8
|
import styles from './header.styles.js';
|
|
9
|
-
import SynIcon from '../icon/icon.js';
|
|
9
|
+
import SynIcon from '../icon/icon.component.js';
|
|
10
10
|
import type SynSideNav from '../side-nav/side-nav.component.js';
|
|
11
11
|
import { LocalizeController } from '../../utilities/localize.js';
|
|
12
12
|
import { watch } from '../../internal/watch.js';
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------
|
|
2
|
+
// 🔒 AUTOGENERATED @synergy-design-system/angular wrappers for @synergy-design-system/components
|
|
3
|
+
// Please do not edit this file directly!
|
|
4
|
+
// It will get recreated when running pnpm build.
|
|
5
|
+
// ---------------------------------------------------------------------
|
|
6
|
+
import {
|
|
7
|
+
Component,
|
|
8
|
+
ElementRef,
|
|
9
|
+
NgZone,
|
|
10
|
+
Input,
|
|
11
|
+
Output,
|
|
12
|
+
EventEmitter,
|
|
13
|
+
AfterContentInit,
|
|
14
|
+
} from '@angular/core';
|
|
15
|
+
import type { SynPagination } from '@synergy-design-system/components';
|
|
16
|
+
import type { SynPaginationPageChangedEvent } from '@synergy-design-system/components';
|
|
17
|
+
import type { SynPaginationPageSizeChangedEvent } from '@synergy-design-system/components';
|
|
18
|
+
import '@synergy-design-system/components/components/pagination/pagination.js';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @summary <syn-pagination /> provides page navigation, direct page input, and page-size selection for large data sets.
|
|
22
|
+
*
|
|
23
|
+
* @documentation https://synergy-design-system.github.io/?path=/docs/components-syn-pagination--docs
|
|
24
|
+
* @status stable
|
|
25
|
+
* @since 3.12.0
|
|
26
|
+
*
|
|
27
|
+
* @event syn-pagination-page-changed - Emitted when the current page changes
|
|
28
|
+
* @event syn-pagination-page-size-changed - Emitted when the page size changes
|
|
29
|
+
*
|
|
30
|
+
* @csspart base - The component's base wrapper.
|
|
31
|
+
* @csspart divider - The divider element displayed at the top of the pagination component.
|
|
32
|
+
* @csspart page-size-select-wrapper - The wrapper element containing the page size select and page item summary.
|
|
33
|
+
* @csspart page-size-select - The page size select element.
|
|
34
|
+
* @csspart page-item-summary - The text element displaying the current page item range and total items.
|
|
35
|
+
* @csspart page-input-section - The section containing the page number input and total pages display.
|
|
36
|
+
* @csspart page-input - The page number input element.
|
|
37
|
+
* @csspart navigation - The pagination navigation element.
|
|
38
|
+
* @csspart navigation-action - The individual navigation action buttons (first, previous, next, last).
|
|
39
|
+
*
|
|
40
|
+
* @accessibility
|
|
41
|
+
* The entire component is wrapped in a semantic `<nav>` landmark with an `aria-label` for screen reader accessibility.
|
|
42
|
+
* Use the `aria-label` attribute to provide a unique, descriptive label when multiple pagination controls exist on the page.
|
|
43
|
+
* Example: `<syn-pagination aria-label="Search results pagination"></syn-pagination>`
|
|
44
|
+
*/
|
|
45
|
+
@Component({
|
|
46
|
+
selector: 'syn-pagination',
|
|
47
|
+
standalone: true,
|
|
48
|
+
template: '<ng-content></ng-content>',
|
|
49
|
+
})
|
|
50
|
+
export class SynPaginationComponent {
|
|
51
|
+
public nativeElement: SynPagination;
|
|
52
|
+
private _ngZone: NgZone;
|
|
53
|
+
|
|
54
|
+
constructor(e: ElementRef, ngZone: NgZone) {
|
|
55
|
+
this.nativeElement = e.nativeElement;
|
|
56
|
+
this._ngZone = ngZone;
|
|
57
|
+
this.nativeElement.addEventListener(
|
|
58
|
+
'syn-pagination-page-changed',
|
|
59
|
+
(e: SynPaginationPageChangedEvent) => {
|
|
60
|
+
this.synPaginationPageChangedEvent.emit(e);
|
|
61
|
+
},
|
|
62
|
+
);
|
|
63
|
+
this.nativeElement.addEventListener(
|
|
64
|
+
'syn-pagination-page-size-changed',
|
|
65
|
+
(e: SynPaginationPageSizeChangedEvent) => {
|
|
66
|
+
this.synPaginationPageSizeChangedEvent.emit(e);
|
|
67
|
+
},
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* When true, a divider is displayed at the top of the pagination component.
|
|
73
|
+
*/
|
|
74
|
+
@Input()
|
|
75
|
+
set divider(v: '' | SynPagination['divider']) {
|
|
76
|
+
this._ngZone.runOutsideAngular(
|
|
77
|
+
() => (this.nativeElement.divider = v === '' || v),
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
get divider(): SynPagination['divider'] {
|
|
81
|
+
return this.nativeElement.divider;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* When true, the pagination controls are disabled and non-interactive.
|
|
86
|
+
*/
|
|
87
|
+
@Input()
|
|
88
|
+
set disabled(v: '' | SynPagination['disabled']) {
|
|
89
|
+
this._ngZone.runOutsideAngular(
|
|
90
|
+
() => (this.nativeElement.disabled = v === '' || v),
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
get disabled(): SynPagination['disabled'] {
|
|
94
|
+
return this.nativeElement.disabled;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The size of the pagination controls.
|
|
99
|
+
*/
|
|
100
|
+
@Input()
|
|
101
|
+
set size(v: SynPagination['size']) {
|
|
102
|
+
this._ngZone.runOutsideAngular(() => (this.nativeElement.size = v));
|
|
103
|
+
}
|
|
104
|
+
get size(): SynPagination['size'] {
|
|
105
|
+
return this.nativeElement.size;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* The current page number.
|
|
110
|
+
* The default value is 1.
|
|
111
|
+
The component will emit a `syn-pagination-page-changed` event whenever the page changes, allowing you to respond to page changes in your application.
|
|
112
|
+
*/
|
|
113
|
+
@Input()
|
|
114
|
+
set currentPage(v: SynPagination['currentPage']) {
|
|
115
|
+
this._ngZone.runOutsideAngular(() => (this.nativeElement.currentPage = v));
|
|
116
|
+
}
|
|
117
|
+
get currentPage(): SynPagination['currentPage'] {
|
|
118
|
+
return this.nativeElement.currentPage;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* The number of items to display per page.
|
|
123
|
+
* The default value is 25.
|
|
124
|
+
The component will emit a `syn-pagination-page-size-changed` event whenever the page size changes, allowing you to respond to page size changes in your application.
|
|
125
|
+
*/
|
|
126
|
+
@Input()
|
|
127
|
+
set pageSize(v: SynPagination['pageSize']) {
|
|
128
|
+
this._ngZone.runOutsideAngular(() => (this.nativeElement.pageSize = v));
|
|
129
|
+
}
|
|
130
|
+
get pageSize(): SynPagination['pageSize'] {
|
|
131
|
+
return this.nativeElement.pageSize;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* An array of numbers representing the available options for the number of items to display per page.
|
|
136
|
+
* The default value is [10, 25, 50, 100].
|
|
137
|
+
The component will use this array to populate the rows-per-page selector, allowing users to choose from the specified options.
|
|
138
|
+
*/
|
|
139
|
+
@Input()
|
|
140
|
+
set pageSizeOptions(v: SynPagination['pageSizeOptions']) {
|
|
141
|
+
this._ngZone.runOutsideAngular(
|
|
142
|
+
() => (this.nativeElement.pageSizeOptions = v),
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
get pageSizeOptions(): SynPagination['pageSizeOptions'] {
|
|
146
|
+
return this.nativeElement.pageSizeOptions;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Total amount of items.
|
|
151
|
+
* The component will use this value to calculate the total number of pages based on the selected rows per page.
|
|
152
|
+
*/
|
|
153
|
+
@Input()
|
|
154
|
+
set totalItems(v: SynPagination['totalItems']) {
|
|
155
|
+
this._ngZone.runOutsideAngular(() => (this.nativeElement.totalItems = v));
|
|
156
|
+
}
|
|
157
|
+
get totalItems(): SynPagination['totalItems'] {
|
|
158
|
+
return this.nativeElement.totalItems;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* The pagination variant to use.
|
|
163
|
+
* The "full" variant includes comprehensive controls for navigating between pages and adjusting the number of displayed rows,
|
|
164
|
+
while the "compact" variant offers a streamlined interface with essential navigation controls.
|
|
165
|
+
The default value is "full".
|
|
166
|
+
*/
|
|
167
|
+
@Input()
|
|
168
|
+
set variant(v: SynPagination['variant']) {
|
|
169
|
+
this._ngZone.runOutsideAngular(() => (this.nativeElement.variant = v));
|
|
170
|
+
}
|
|
171
|
+
get variant(): SynPagination['variant'] {
|
|
172
|
+
return this.nativeElement.variant;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* An accessible label for the navigation landmark.
|
|
177
|
+
* Customize for multiple paginations on a page.
|
|
178
|
+
*/
|
|
179
|
+
@Input()
|
|
180
|
+
set ariaLabel(v: SynPagination['ariaLabel']) {
|
|
181
|
+
this._ngZone.runOutsideAngular(() => (this.nativeElement.ariaLabel = v));
|
|
182
|
+
}
|
|
183
|
+
get ariaLabel(): SynPagination['ariaLabel'] {
|
|
184
|
+
return this.nativeElement.ariaLabel;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Emitted when the current page changes
|
|
189
|
+
*/
|
|
190
|
+
@Output() synPaginationPageChangedEvent =
|
|
191
|
+
new EventEmitter<SynPaginationPageChangedEvent>();
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Emitted when the page size changes
|
|
195
|
+
*/
|
|
196
|
+
@Output() synPaginationPageSizeChangedEvent =
|
|
197
|
+
new EventEmitter<SynPaginationPageSizeChangedEvent>();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export type { SynPaginationPageChangedEvent } from '@synergy-design-system/components';
|
|
201
|
+
export type { SynPaginationPageSizeChangedEvent } from '@synergy-design-system/components';
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------
|
|
2
|
+
// 🔒 AUTOGENERATED @synergy-design-system/react wrappers for @synergy-design-system/components
|
|
3
|
+
// Please do not edit this file directly!
|
|
4
|
+
// It will get recreated when running pnpm build.
|
|
5
|
+
// ---------------------------------------------------------------------
|
|
6
|
+
import * as React from 'react';
|
|
7
|
+
import { createComponent } from '@lit/react';
|
|
8
|
+
import Component from '@synergy-design-system/components/components/pagination/pagination.component.js';
|
|
9
|
+
|
|
10
|
+
import { type EventName } from '@lit/react';
|
|
11
|
+
import type { SynPaginationPageChangedEvent } from '@synergy-design-system/components';
|
|
12
|
+
import type { SynPaginationPageSizeChangedEvent } from '@synergy-design-system/components';
|
|
13
|
+
|
|
14
|
+
const tagName = 'syn-pagination';
|
|
15
|
+
Component.define('syn-pagination');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @summary <syn-pagination /> provides page navigation, direct page input, and page-size selection for large data sets.
|
|
19
|
+
*
|
|
20
|
+
* @documentation https://synergy-design-system.github.io/?path=/docs/components-syn-pagination--docs
|
|
21
|
+
* @status stable
|
|
22
|
+
* @since 3.12.0
|
|
23
|
+
*
|
|
24
|
+
* @event syn-pagination-page-changed - Emitted when the current page changes
|
|
25
|
+
* @event syn-pagination-page-size-changed - Emitted when the page size changes
|
|
26
|
+
*
|
|
27
|
+
* @csspart base - The component's base wrapper.
|
|
28
|
+
* @csspart divider - The divider element displayed at the top of the pagination component.
|
|
29
|
+
* @csspart page-size-select-wrapper - The wrapper element containing the page size select and page item summary.
|
|
30
|
+
* @csspart page-size-select - The page size select element.
|
|
31
|
+
* @csspart page-item-summary - The text element displaying the current page item range and total items.
|
|
32
|
+
* @csspart page-input-section - The section containing the page number input and total pages display.
|
|
33
|
+
* @csspart page-input - The page number input element.
|
|
34
|
+
* @csspart navigation - The pagination navigation element.
|
|
35
|
+
* @csspart navigation-action - The individual navigation action buttons (first, previous, next, last).
|
|
36
|
+
*
|
|
37
|
+
* @accessibility
|
|
38
|
+
* The entire component is wrapped in a semantic `<nav>` landmark with an `aria-label` for screen reader accessibility.
|
|
39
|
+
* Use the `aria-label` attribute to provide a unique, descriptive label when multiple pagination controls exist on the page.
|
|
40
|
+
* Example: `<syn-pagination aria-label="Search results pagination"></syn-pagination>`
|
|
41
|
+
*/
|
|
42
|
+
export const SynPagination = createComponent({
|
|
43
|
+
displayName: 'SynPagination',
|
|
44
|
+
elementClass: Component,
|
|
45
|
+
events: {
|
|
46
|
+
onSynPaginationPageChanged:
|
|
47
|
+
'syn-pagination-page-changed' as EventName<SynPaginationPageChangedEvent>,
|
|
48
|
+
onSynPaginationPageSizeChanged:
|
|
49
|
+
'syn-pagination-page-size-changed' as EventName<SynPaginationPageSizeChangedEvent>,
|
|
50
|
+
},
|
|
51
|
+
react: React,
|
|
52
|
+
tagName,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
export type { SynPaginationPageChangedEvent } from '@synergy-design-system/components';
|
|
56
|
+
export type { SynPaginationPageSizeChangedEvent } from '@synergy-design-system/components';
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { css } from 'lit';
|
|
2
|
+
|
|
3
|
+
export default css`
|
|
4
|
+
:host {
|
|
5
|
+
display: block;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.pagination {
|
|
9
|
+
--base-font: var(--syn-body-medium-regular);
|
|
10
|
+
--base-gap: var(--syn-spacing-small);
|
|
11
|
+
--navigation-gap: var(--syn-spacing-small);
|
|
12
|
+
--pagination-page-size-option-char-count: 2;
|
|
13
|
+
--pagination-total-pages-char-count: 3;
|
|
14
|
+
|
|
15
|
+
align-items: center;
|
|
16
|
+
display: flex;
|
|
17
|
+
flex-wrap: wrap;
|
|
18
|
+
font: var(--base-font);
|
|
19
|
+
gap: var(--base-gap) var(--syn-spacing-large);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/* Sizes */
|
|
23
|
+
:host([size="small"]) .pagination {
|
|
24
|
+
--base-gap: var(--syn-spacing-x-small);
|
|
25
|
+
--base-font: var(--syn-body-small-regular);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
:host([size="large"]) .pagination {
|
|
29
|
+
--base-gap: var(--syn-spacing-medium);
|
|
30
|
+
--base-font: var(--syn-body-large-regular);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* Divider */
|
|
34
|
+
syn-divider {
|
|
35
|
+
--divider-spacing: var(--syn-spacing-small);
|
|
36
|
+
--spacing: 0 auto var(--divider-spacing);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
:host([size="small"]) syn-divider {
|
|
40
|
+
--divider-spacing: var(--syn-spacing-x-small);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
:host([size="large"]) syn-divider {
|
|
44
|
+
--divider-spacing: var(--syn-spacing-medium);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* Select */
|
|
48
|
+
.pagination__page-size-select-wrapper {
|
|
49
|
+
align-items: center;
|
|
50
|
+
display: flex;
|
|
51
|
+
gap: var(--base-gap);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.pagination__page-size-select::part(form-control) {
|
|
55
|
+
align-items: center;
|
|
56
|
+
display: flex;
|
|
57
|
+
gap: var(--base-gap);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.pagination__page-size-select::part(form-control-label) {
|
|
61
|
+
font: var(--base-font);
|
|
62
|
+
margin-bottom: 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.pagination__page-size-select::part(display-input) {
|
|
66
|
+
width: calc((var(--pagination-page-size-option-char-count) * 1ch) + 1ch);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* Navigation */
|
|
70
|
+
.pagination__navigation {
|
|
71
|
+
align-items: center;
|
|
72
|
+
display: flex;
|
|
73
|
+
flex: 1;
|
|
74
|
+
flex-wrap: nowrap;
|
|
75
|
+
gap: var(--navigation-gap);
|
|
76
|
+
justify-content: end;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.pagination__navigation > section {
|
|
80
|
+
align-items: center;
|
|
81
|
+
display: flex;
|
|
82
|
+
flex-wrap: nowrap;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.pagination__page-input {
|
|
86
|
+
margin-inline-end: var(--navigation-gap);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.pagination__page-input::part(input) {
|
|
90
|
+
text-align: center;
|
|
91
|
+
width: calc((var(--pagination-total-pages-char-count) * 1ch) + 3ch);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Make sure to hide the label of the page input, but keep it accessible for screen readers.
|
|
96
|
+
* We can't use the label slot of syn-input for this, because it would mess with the layout.
|
|
97
|
+
*/
|
|
98
|
+
.pagination__page-input::part(form-control-label) {
|
|
99
|
+
border: 0;
|
|
100
|
+
/* stylelint-disable-next-line property-no-deprecated */
|
|
101
|
+
clip: rect(0, 0, 0, 0);
|
|
102
|
+
height: 1px;
|
|
103
|
+
margin: -1px;
|
|
104
|
+
overflow: hidden;
|
|
105
|
+
padding: 0;
|
|
106
|
+
position: absolute;
|
|
107
|
+
white-space: nowrap;
|
|
108
|
+
width: 1px;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* Compact Version */
|
|
112
|
+
:host([variant="compact"]) .pagination__navigation {
|
|
113
|
+
justify-content: center;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* Adjustments for really small container widths */
|
|
117
|
+
@supports (container-type: inline-size) {
|
|
118
|
+
:host {
|
|
119
|
+
container-type: inline-size;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@container (max-width: 400px) {
|
|
123
|
+
.pagination__navigation {
|
|
124
|
+
justify-content: center;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
`;
|
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type CSSResultGroup,
|
|
3
|
+
type PropertyValues,
|
|
4
|
+
html,
|
|
5
|
+
nothing,
|
|
6
|
+
} from 'lit';
|
|
7
|
+
import { property } from 'lit/decorators.js';
|
|
8
|
+
import { createRef, ref } from 'lit/directives/ref.js';
|
|
9
|
+
import { LocalizeController } from '../../utilities/localize.js';
|
|
10
|
+
import SynergyElement from '../../internal/synergy-element.js';
|
|
11
|
+
import componentStyles from '../../styles/component.styles.js';
|
|
12
|
+
import styles from './pagination.styles.js';
|
|
13
|
+
import SynDivider from '../divider/divider.component.js';
|
|
14
|
+
import SynIconButton from '../icon-button/icon-button.component.js';
|
|
15
|
+
import SynInput from '../input/input.component.js';
|
|
16
|
+
import SynOption from '../option/option.component.js';
|
|
17
|
+
import SynSelect from '../select/select.component.js';
|
|
18
|
+
import {
|
|
19
|
+
calculatePageItemIndices,
|
|
20
|
+
calculateTotalPages,
|
|
21
|
+
clampPage,
|
|
22
|
+
getMaxOptionCharCount,
|
|
23
|
+
getPreviousOrDefault,
|
|
24
|
+
getTotalPages,
|
|
25
|
+
getTotalPagesCharCount,
|
|
26
|
+
isValidNonNegativeInteger,
|
|
27
|
+
isValidPositiveInteger,
|
|
28
|
+
sanitizePageSizeOptions,
|
|
29
|
+
} from './utility.js';
|
|
30
|
+
import type { SynInputEvent } from '../../events/events.js';
|
|
31
|
+
import { enableDefaultSettings } from '../../utilities/defaultSettings/decorator.js';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @summary <syn-pagination /> provides page navigation, direct page input, and page-size selection for large data sets.
|
|
35
|
+
*
|
|
36
|
+
* @documentation https://synergy-design-system.github.io/?path=/docs/components-syn-pagination--docs
|
|
37
|
+
* @status stable
|
|
38
|
+
* @since 3.12.0
|
|
39
|
+
*
|
|
40
|
+
* @event syn-pagination-page-changed - Emitted when the current page changes
|
|
41
|
+
* @event syn-pagination-page-size-changed - Emitted when the page size changes
|
|
42
|
+
*
|
|
43
|
+
* @csspart base - The component's base wrapper.
|
|
44
|
+
* @csspart divider - The divider element displayed at the top of the pagination component.
|
|
45
|
+
* @csspart page-size-select-wrapper - The wrapper element containing the page size select and page item summary.
|
|
46
|
+
* @csspart page-size-select - The page size select element.
|
|
47
|
+
* @csspart page-item-summary - The text element displaying the current page item range and total items.
|
|
48
|
+
* @csspart page-input-section - The section containing the page number input and total pages display.
|
|
49
|
+
* @csspart page-input - The page number input element.
|
|
50
|
+
* @csspart navigation - The pagination navigation element.
|
|
51
|
+
* @csspart navigation-action - The individual navigation action buttons (first, previous, next, last).
|
|
52
|
+
*
|
|
53
|
+
* @accessibility
|
|
54
|
+
* The entire component is wrapped in a semantic `<nav>` landmark with an `aria-label` for screen reader accessibility.
|
|
55
|
+
* Use the `aria-label` attribute to provide a unique, descriptive label when multiple pagination controls exist on the page.
|
|
56
|
+
* Example: `<syn-pagination aria-label="Search results pagination"></syn-pagination>`
|
|
57
|
+
*/
|
|
58
|
+
@enableDefaultSettings('SynPagination')
|
|
59
|
+
export default class SynPagination extends SynergyElement {
|
|
60
|
+
private static readonly DEFAULT_PAGE_SIZE = 25;
|
|
61
|
+
|
|
62
|
+
private static readonly DEFAULT_CURRENT_PAGE = 1;
|
|
63
|
+
|
|
64
|
+
private static readonly DEFAULT_TOTAL_ITEMS = 0;
|
|
65
|
+
|
|
66
|
+
private readonly baseRef = createRef<HTMLElement>();
|
|
67
|
+
|
|
68
|
+
private readonly localize = new LocalizeController(this);
|
|
69
|
+
|
|
70
|
+
static styles: CSSResultGroup = [
|
|
71
|
+
componentStyles,
|
|
72
|
+
styles,
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
static dependencies = {
|
|
76
|
+
'syn-divider': SynDivider,
|
|
77
|
+
'syn-icon-button': SynIconButton,
|
|
78
|
+
'syn-input': SynInput,
|
|
79
|
+
'syn-option': SynOption,
|
|
80
|
+
'syn-select': SynSelect,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* When true, a divider is displayed at the top of the pagination component.
|
|
85
|
+
*/
|
|
86
|
+
@property({ type: Boolean }) divider = false;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* When true, the pagination controls are disabled and non-interactive.
|
|
90
|
+
*/
|
|
91
|
+
@property({ reflect: true, type: Boolean }) disabled = false;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The size of the pagination controls.
|
|
95
|
+
*/
|
|
96
|
+
@property({ reflect: true }) size: 'small' | 'medium' | 'large' = 'medium';
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* The current page number. The default value is 1.
|
|
100
|
+
* The component will emit a `syn-pagination-page-changed` event whenever the page changes, allowing you to respond to page changes in your application.
|
|
101
|
+
*/
|
|
102
|
+
@property({ attribute: 'current-page', reflect: true, type: Number }) currentPage = 1;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* The number of items to display per page. The default value is 25.
|
|
106
|
+
* The component will emit a `syn-pagination-page-size-changed` event whenever the page size changes, allowing you to respond to page size changes in your application.
|
|
107
|
+
*/
|
|
108
|
+
@property({ attribute: 'page-size', reflect: true, type: Number }) pageSize = 25;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* An array of numbers representing the available options for the number of items to display per page. The default value is [10, 25, 50, 100].
|
|
112
|
+
* The component will use this array to populate the rows-per-page selector, allowing users to choose from the specified options.
|
|
113
|
+
*/
|
|
114
|
+
@property({
|
|
115
|
+
attribute: 'page-size-options',
|
|
116
|
+
converter: {
|
|
117
|
+
fromAttribute: (value: string) => value
|
|
118
|
+
.split(',')
|
|
119
|
+
.map(val => {
|
|
120
|
+
const num = parseInt(val.trim(), 10);
|
|
121
|
+
return Number.isSafeInteger(num) ? num : null;
|
|
122
|
+
})
|
|
123
|
+
.filter(Boolean),
|
|
124
|
+
},
|
|
125
|
+
type: Array,
|
|
126
|
+
}) pageSizeOptions: number[] = [10, 25, 50, 100];
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Total amount of items. The component will use this value to calculate the total number of pages based on the selected rows per page.
|
|
130
|
+
*/
|
|
131
|
+
@property({ attribute: 'total-items', reflect: true, type: Number }) totalItems = 0;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* The pagination variant to use. The "full" variant includes comprehensive controls for navigating between pages and adjusting the number of displayed rows,
|
|
135
|
+
* while the "compact" variant offers a streamlined interface with essential navigation controls.
|
|
136
|
+
* The default value is "full".
|
|
137
|
+
*/
|
|
138
|
+
@property({ attribute: 'variant', reflect: true }) variant: 'full' | 'compact' = 'full';
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* An accessible label for the navigation landmark. Customize for multiple paginations on a page.
|
|
142
|
+
*/
|
|
143
|
+
@property({ attribute: 'aria-label' }) ariaLabel = 'Pagination';
|
|
144
|
+
|
|
145
|
+
private pageChangedViaUserInput(e: SynInputEvent) {
|
|
146
|
+
const newPage = (e.target as SynInput).valueAsNumber;
|
|
147
|
+
// Ignore invalid page inputs
|
|
148
|
+
if (!isValidPositiveInteger(newPage)) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
this.updateCurrentPage(newPage);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private navigationClicked(event: MouseEvent, newPage: number) {
|
|
155
|
+
// Blur first so icon-button focus state settles before this render cycle disables it.
|
|
156
|
+
(event.currentTarget as SynIconButton | null)?.blur();
|
|
157
|
+
this.updateCurrentPage(newPage);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
private sanitizeInvalidPropertyValues(changed: PropertyValues<this>) {
|
|
161
|
+
if (changed.has('pageSize') && !isValidPositiveInteger(this.pageSize)) {
|
|
162
|
+
this.pageSize = getPreviousOrDefault(
|
|
163
|
+
changed,
|
|
164
|
+
'pageSize',
|
|
165
|
+
SynPagination.DEFAULT_PAGE_SIZE,
|
|
166
|
+
isValidPositiveInteger,
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (changed.has('currentPage') && !isValidPositiveInteger(this.currentPage)) {
|
|
171
|
+
this.currentPage = getPreviousOrDefault(
|
|
172
|
+
changed,
|
|
173
|
+
'currentPage',
|
|
174
|
+
SynPagination.DEFAULT_CURRENT_PAGE,
|
|
175
|
+
isValidPositiveInteger,
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (changed.has('totalItems') && !isValidNonNegativeInteger(this.totalItems)) {
|
|
180
|
+
this.totalItems = getPreviousOrDefault(
|
|
181
|
+
changed,
|
|
182
|
+
'totalItems',
|
|
183
|
+
SynPagination.DEFAULT_TOTAL_ITEMS,
|
|
184
|
+
isValidNonNegativeInteger,
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (changed.has('pageSizeOptions')) {
|
|
189
|
+
const sanitizedPageSizeOptions = sanitizePageSizeOptions(this.pageSizeOptions);
|
|
190
|
+
const hasChanged = this.pageSizeOptions.length !== sanitizedPageSizeOptions.length
|
|
191
|
+
|| this.pageSizeOptions.some((option, index) => option !== sanitizedPageSizeOptions[index]);
|
|
192
|
+
|
|
193
|
+
if (hasChanged) {
|
|
194
|
+
this.pageSizeOptions = sanitizedPageSizeOptions;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Updates the current page number and emits a "syn-pagination-page-changed" event with the new and previous page numbers.
|
|
201
|
+
* This method is called when the page number is changed via the page input or the navigation buttons.
|
|
202
|
+
*/
|
|
203
|
+
private updateCurrentPage(newPage: number) {
|
|
204
|
+
const totalPages = getTotalPages(this.pageSize, this.totalItems);
|
|
205
|
+
const safeNewPage = clampPage(newPage, totalPages);
|
|
206
|
+
const { currentPage } = this;
|
|
207
|
+
|
|
208
|
+
if (safeNewPage === currentPage) {
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
this.emit('syn-pagination-page-changed', {
|
|
213
|
+
detail: {
|
|
214
|
+
currentPage: safeNewPage,
|
|
215
|
+
previousPage: currentPage,
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
this.currentPage = safeNewPage;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Called when the page size is changed via the page size select.
|
|
224
|
+
* Emits a `syn-pagination-page-size-changed` event with the new and previous page sizes.
|
|
225
|
+
*/
|
|
226
|
+
private pageSizeChanged(e: SynInputEvent) {
|
|
227
|
+
const { currentPage, pageSize } = this;
|
|
228
|
+
const currentTarget = e.target as SynSelect;
|
|
229
|
+
const { value } = currentTarget;
|
|
230
|
+
const nextPageSize = parseInt(value as string, 10);
|
|
231
|
+
|
|
232
|
+
// Validate the new page size before applying it
|
|
233
|
+
if (!Number.isSafeInteger(nextPageSize) || nextPageSize <= 0) return;
|
|
234
|
+
|
|
235
|
+
// When we switch from a smaller value to a larger one,
|
|
236
|
+
// we also have to adjust the current page marker.
|
|
237
|
+
const oldStartIndex = (currentPage - 1) * pageSize + 1;
|
|
238
|
+
const nextTotalPages = getTotalPages(nextPageSize, this.totalItems);
|
|
239
|
+
|
|
240
|
+
const nextPage = clampPage(
|
|
241
|
+
Math.floor((oldStartIndex - 1) / nextPageSize) + 1,
|
|
242
|
+
nextTotalPages,
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
this.pageSize = nextPageSize;
|
|
246
|
+
this.currentPage = nextPage;
|
|
247
|
+
|
|
248
|
+
this.emit('syn-pagination-page-size-changed', {
|
|
249
|
+
detail: {
|
|
250
|
+
currentPageSize: nextPageSize,
|
|
251
|
+
previousPageSize: pageSize,
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
if (nextPage !== currentPage) {
|
|
256
|
+
this.emit('syn-pagination-page-changed', {
|
|
257
|
+
detail: {
|
|
258
|
+
currentPage: nextPage,
|
|
259
|
+
previousPage: currentPage,
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
protected willUpdate(changed: PropertyValues<this>) {
|
|
266
|
+
super.willUpdate(changed);
|
|
267
|
+
|
|
268
|
+
this.sanitizeInvalidPropertyValues(changed);
|
|
269
|
+
|
|
270
|
+
const previousPageSizeOptions = changed.get('pageSizeOptions');
|
|
271
|
+
const hasPreviousPageSizeOptions = Array.isArray(previousPageSizeOptions);
|
|
272
|
+
|
|
273
|
+
if (hasPreviousPageSizeOptions && !this.pageSizeOptions.includes(this.pageSize)) {
|
|
274
|
+
const nextPageSize = this.pageSizeOptions[0];
|
|
275
|
+
const oldStartIndex = (this.currentPage - 1) * this.pageSize + 1;
|
|
276
|
+
const nextTotalPages = getTotalPages(nextPageSize, this.totalItems);
|
|
277
|
+
|
|
278
|
+
const nextPage = clampPage(
|
|
279
|
+
Math.floor((oldStartIndex - 1) / nextPageSize) + 1,
|
|
280
|
+
nextTotalPages,
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
this.pageSize = nextPageSize;
|
|
284
|
+
this.currentPage = nextPage;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (changed.has('currentPage') || changed.has('pageSize') || changed.has('totalItems')) {
|
|
288
|
+
// If the current page exceeds the total number of pages based on the new page size or total items,
|
|
289
|
+
// we need to adjust it to ensure it remains within valid bounds.
|
|
290
|
+
const totalPages = getTotalPages(this.pageSize, this.totalItems);
|
|
291
|
+
const clamped = clampPage(this.currentPage, totalPages);
|
|
292
|
+
if (clamped !== this.currentPage) {
|
|
293
|
+
this.currentPage = clamped;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
protected updated(changed: PropertyValues<this>) {
|
|
299
|
+
super.updated(changed);
|
|
300
|
+
|
|
301
|
+
// When the size of available options or the page size changes,
|
|
302
|
+
// we have to adjust the width of our page size select/input controls.
|
|
303
|
+
if (changed.has('pageSizeOptions') || changed.has('pageSize') || changed.has('totalItems')) {
|
|
304
|
+
const maxOptionCharCount = getMaxOptionCharCount(this.pageSizeOptions);
|
|
305
|
+
const totalPagesCharCount = getTotalPagesCharCount(this.pageSize, this.totalItems);
|
|
306
|
+
const baseElement = this.baseRef.value;
|
|
307
|
+
|
|
308
|
+
if (baseElement) {
|
|
309
|
+
baseElement.style.setProperty('--pagination-page-size-option-char-count', String(maxOptionCharCount));
|
|
310
|
+
baseElement.style.setProperty('--pagination-total-pages-char-count', String(totalPagesCharCount));
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
render() {
|
|
316
|
+
// List of total pages.
|
|
317
|
+
// Used to determine the disabled state of navigation buttons and to calculate the page item indices for display.
|
|
318
|
+
const totalPages = calculateTotalPages(this.totalItems, this.pageSize);
|
|
319
|
+
|
|
320
|
+
// Determine if we're in compact mode to conditionally apply styles and layout adjustments.
|
|
321
|
+
const isCompact = this.variant === 'compact';
|
|
322
|
+
|
|
323
|
+
// Condition to determine if the pagination controls should be disabled.
|
|
324
|
+
const isDisabled = this.disabled || totalPages === 0;
|
|
325
|
+
|
|
326
|
+
// Calculate the start and end item indices for the current page to display in the pagination summary (e.g., "11-20 of 100 items").
|
|
327
|
+
const pageItemIndices = calculatePageItemIndices(this.totalItems, this.pageSize, this.currentPage);
|
|
328
|
+
|
|
329
|
+
// If the current page size is not included in the available options,
|
|
330
|
+
// we select the first option as the default value to ensure the select input displays a valid option.
|
|
331
|
+
const selectedPageSizeOption = this.pageSizeOptions.includes(this.pageSize)
|
|
332
|
+
? this.pageSize
|
|
333
|
+
: this.pageSizeOptions[0];
|
|
334
|
+
|
|
335
|
+
const isFirstPage = this.currentPage === 1;
|
|
336
|
+
const isLastPage = this.currentPage === totalPages;
|
|
337
|
+
|
|
338
|
+
/* eslint-disable @typescript-eslint/unbound-method */
|
|
339
|
+
return html`
|
|
340
|
+
${this.divider ? html`<syn-divider part="divider"></syn-divider>` : nothing}
|
|
341
|
+
<nav
|
|
342
|
+
aria-label=${this.ariaLabel}
|
|
343
|
+
class="pagination"
|
|
344
|
+
part="base"
|
|
345
|
+
${ref(this.baseRef)}
|
|
346
|
+
>
|
|
347
|
+
${!isCompact ? html`
|
|
348
|
+
<div class="pagination__page-size-select-wrapper" part="page-size-select-wrapper">
|
|
349
|
+
<syn-select
|
|
350
|
+
class="pagination__page-size-select"
|
|
351
|
+
?disabled=${isDisabled}
|
|
352
|
+
label=${this.localize.term('paginationItemsPerPage')}
|
|
353
|
+
part="page-size-select"
|
|
354
|
+
value=${selectedPageSizeOption}
|
|
355
|
+
size=${this.size}
|
|
356
|
+
@syn-change=${this.pageSizeChanged}
|
|
357
|
+
>
|
|
358
|
+
${this.pageSizeOptions.map(option => html`
|
|
359
|
+
<syn-option value="${option}">
|
|
360
|
+
${option}
|
|
361
|
+
</syn-option>
|
|
362
|
+
`)}
|
|
363
|
+
</syn-select>
|
|
364
|
+
<!-- /.pagination__page-size-select -->
|
|
365
|
+
|
|
366
|
+
<span part="page-item-summary">
|
|
367
|
+
${this.localize.term(
|
|
368
|
+
'paginationItemSummary',
|
|
369
|
+
pageItemIndices.startIndex,
|
|
370
|
+
pageItemIndices.endIndex,
|
|
371
|
+
this.totalItems,
|
|
372
|
+
)}
|
|
373
|
+
</span>
|
|
374
|
+
<!-- /.pagination__page-item-summary -->
|
|
375
|
+
</div>
|
|
376
|
+
<!-- /.pagination__page-size-select-wrapper -->
|
|
377
|
+
`: nothing}
|
|
378
|
+
|
|
379
|
+
<div class="pagination__navigation" part="navigation">
|
|
380
|
+
<section>
|
|
381
|
+
<syn-icon-button
|
|
382
|
+
@click=${(e: MouseEvent) => this.navigationClicked(e, 1)}
|
|
383
|
+
color="primary"
|
|
384
|
+
?disabled=${isFirstPage || isDisabled}
|
|
385
|
+
label=${this.localize.term('paginationFirstPage')}
|
|
386
|
+
library="system"
|
|
387
|
+
name="first-page"
|
|
388
|
+
part="navigation-action"
|
|
389
|
+
size=${this.size}
|
|
390
|
+
></syn-icon-button>
|
|
391
|
+
|
|
392
|
+
<syn-icon-button
|
|
393
|
+
@click=${(e: MouseEvent) => this.navigationClicked(e, this.currentPage - 1)}
|
|
394
|
+
color="primary"
|
|
395
|
+
?disabled=${isFirstPage || isDisabled}
|
|
396
|
+
label=${this.localize.term('paginationPreviousPage')}
|
|
397
|
+
library="system"
|
|
398
|
+
name="previous-page"
|
|
399
|
+
part="navigation-action"
|
|
400
|
+
size=${this.size}
|
|
401
|
+
></syn-icon-button>
|
|
402
|
+
</section>
|
|
403
|
+
|
|
404
|
+
<section part="page-input-section">
|
|
405
|
+
<syn-input
|
|
406
|
+
class="pagination__page-input"
|
|
407
|
+
?disabled=${isDisabled}
|
|
408
|
+
label=${this.localize.term('paginationInputLabel')}
|
|
409
|
+
max=${totalPages}
|
|
410
|
+
min="1"
|
|
411
|
+
no-spin-buttons
|
|
412
|
+
numeric-strategy="modern"
|
|
413
|
+
part="page-input"
|
|
414
|
+
size=${this.size}
|
|
415
|
+
@syn-change=${this.pageChangedViaUserInput}
|
|
416
|
+
type="number"
|
|
417
|
+
value=${this.currentPage}
|
|
418
|
+
></syn-input>
|
|
419
|
+
|
|
420
|
+
<span>${this.localize.term('paginationOfTotalPages', totalPages)}</span>
|
|
421
|
+
</section>
|
|
422
|
+
|
|
423
|
+
<section>
|
|
424
|
+
<syn-icon-button
|
|
425
|
+
@click=${(e: MouseEvent) => this.navigationClicked(e, this.currentPage + 1)}
|
|
426
|
+
color="primary"
|
|
427
|
+
?disabled=${isLastPage || isDisabled}
|
|
428
|
+
label=${this.localize.term('paginationNextPage')}
|
|
429
|
+
library="system"
|
|
430
|
+
name="next-page"
|
|
431
|
+
part="navigation-action"
|
|
432
|
+
size=${this.size}
|
|
433
|
+
></syn-icon-button>
|
|
434
|
+
|
|
435
|
+
<syn-icon-button
|
|
436
|
+
@click=${(e: MouseEvent) => this.navigationClicked(e, totalPages)}
|
|
437
|
+
color="primary"
|
|
438
|
+
?disabled=${isLastPage || isDisabled}
|
|
439
|
+
label=${this.localize.term('paginationLastPage')}
|
|
440
|
+
library="system"
|
|
441
|
+
name="last-page"
|
|
442
|
+
part="navigation-action"
|
|
443
|
+
size=${this.size}
|
|
444
|
+
></syn-icon-button>
|
|
445
|
+
</section>
|
|
446
|
+
</div>
|
|
447
|
+
<!-- /.pagination__navigation -->
|
|
448
|
+
</nav>
|
|
449
|
+
`;
|
|
450
|
+
/* eslint-enable @typescript-eslint/unbound-method */
|
|
451
|
+
}
|
|
452
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// ---------------------------------------------------------------------
|
|
3
|
+
// 🔒 AUTOGENERATED @synergy-design-system/vue wrappers for @synergy-design-system/components
|
|
4
|
+
// Please do not edit this file directly!
|
|
5
|
+
// It will get recreated when running pnpm build.
|
|
6
|
+
// ---------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @summary <syn-pagination /> provides page navigation, direct page input, and page-size selection for large data sets.
|
|
10
|
+
*
|
|
11
|
+
* @documentation https://synergy-design-system.github.io/?path=/docs/components-syn-pagination--docs
|
|
12
|
+
* @status stable
|
|
13
|
+
* @since 3.12.0
|
|
14
|
+
*
|
|
15
|
+
* @event syn-pagination-page-changed - Emitted when the current page changes
|
|
16
|
+
* @event syn-pagination-page-size-changed - Emitted when the page size changes
|
|
17
|
+
*
|
|
18
|
+
* @csspart base - The component's base wrapper.
|
|
19
|
+
* @csspart divider - The divider element displayed at the top of the pagination component.
|
|
20
|
+
* @csspart page-size-select-wrapper - The wrapper element containing the page size select and page item summary.
|
|
21
|
+
* @csspart page-size-select - The page size select element.
|
|
22
|
+
* @csspart page-item-summary - The text element displaying the current page item range and total items.
|
|
23
|
+
* @csspart page-input-section - The section containing the page number input and total pages display.
|
|
24
|
+
* @csspart page-input - The page number input element.
|
|
25
|
+
* @csspart navigation - The pagination navigation element.
|
|
26
|
+
* @csspart navigation-action - The individual navigation action buttons (first, previous, next, last).
|
|
27
|
+
*
|
|
28
|
+
* @accessibility
|
|
29
|
+
* The entire component is wrapped in a semantic `<nav>` landmark with an `aria-label` for screen reader accessibility.
|
|
30
|
+
* Use the `aria-label` attribute to provide a unique, descriptive label when multiple pagination controls exist on the page.
|
|
31
|
+
* Example: `<syn-pagination aria-label="Search results pagination"></syn-pagination>`
|
|
32
|
+
*/
|
|
33
|
+
import { computed, ref } from 'vue';
|
|
34
|
+
import '@synergy-design-system/components/components/pagination/pagination.js';
|
|
35
|
+
|
|
36
|
+
import type { SynPaginationPageChangedEvent } from '@synergy-design-system/components';
|
|
37
|
+
import type { SynPaginationPageSizeChangedEvent } from '@synergy-design-system/components';
|
|
38
|
+
import type { SynPagination } from '@synergy-design-system/components';
|
|
39
|
+
|
|
40
|
+
// DOM Reference to the element
|
|
41
|
+
const nativeElement = ref<SynPagination>();
|
|
42
|
+
|
|
43
|
+
defineExpose({
|
|
44
|
+
nativeElement,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Map attributes
|
|
48
|
+
const props = defineProps<{
|
|
49
|
+
/**
|
|
50
|
+
* When true, a divider is displayed at the top of the pagination component.
|
|
51
|
+
*/
|
|
52
|
+
divider?: SynPagination['divider'];
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* When true, the pagination controls are disabled and non-interactive.
|
|
56
|
+
*/
|
|
57
|
+
disabled?: SynPagination['disabled'];
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The size of the pagination controls.
|
|
61
|
+
*/
|
|
62
|
+
size?: SynPagination['size'];
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The current page number.
|
|
66
|
+
* The default value is 1.
|
|
67
|
+
The component will emit a `syn-pagination-page-changed` event whenever the page changes, allowing you to respond to page changes in your application.
|
|
68
|
+
*/
|
|
69
|
+
currentPage?: SynPagination['currentPage'];
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* The number of items to display per page.
|
|
73
|
+
* The default value is 25.
|
|
74
|
+
The component will emit a `syn-pagination-page-size-changed` event whenever the page size changes, allowing you to respond to page size changes in your application.
|
|
75
|
+
*/
|
|
76
|
+
pageSize?: SynPagination['pageSize'];
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* An array of numbers representing the available options for the number of items to display per page.
|
|
80
|
+
* The default value is [10, 25, 50, 100].
|
|
81
|
+
The component will use this array to populate the rows-per-page selector, allowing users to choose from the specified options.
|
|
82
|
+
*/
|
|
83
|
+
pageSizeOptions?: SynPagination['pageSizeOptions'];
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Total amount of items.
|
|
87
|
+
* The component will use this value to calculate the total number of pages based on the selected rows per page.
|
|
88
|
+
*/
|
|
89
|
+
totalItems?: SynPagination['totalItems'];
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* The pagination variant to use.
|
|
93
|
+
* The "full" variant includes comprehensive controls for navigating between pages and adjusting the number of displayed rows,
|
|
94
|
+
while the "compact" variant offers a streamlined interface with essential navigation controls.
|
|
95
|
+
The default value is "full".
|
|
96
|
+
*/
|
|
97
|
+
variant?: SynPagination['variant'];
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* An accessible label for the navigation landmark.
|
|
101
|
+
* Customize for multiple paginations on a page.
|
|
102
|
+
*/
|
|
103
|
+
ariaLabel?: SynPagination['ariaLabel'];
|
|
104
|
+
}>();
|
|
105
|
+
|
|
106
|
+
// Make sure prop binding only forwards the props that are actually there.
|
|
107
|
+
// This is needed because :param="param" also adds an empty attribute
|
|
108
|
+
// when using web-components, which breaks optional arguments like size in SynInput
|
|
109
|
+
// @see https://github.com/vuejs/core/issues/5190#issuecomment-1003112498
|
|
110
|
+
const visibleProps = computed(() =>
|
|
111
|
+
Object.fromEntries(
|
|
112
|
+
Object.entries(props).filter(([, value]) => typeof value !== 'undefined'),
|
|
113
|
+
),
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
// Map events
|
|
117
|
+
defineEmits<{
|
|
118
|
+
/**
|
|
119
|
+
* Emitted when the current page changes
|
|
120
|
+
*/
|
|
121
|
+
'syn-pagination-page-changed': [e: SynPaginationPageChangedEvent];
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Emitted when the page size changes
|
|
125
|
+
*/
|
|
126
|
+
'syn-pagination-page-size-changed': [e: SynPaginationPageSizeChangedEvent];
|
|
127
|
+
}>();
|
|
128
|
+
</script>
|
|
129
|
+
|
|
130
|
+
<script lang="ts">
|
|
131
|
+
export type { SynPaginationPageChangedEvent } from '@synergy-design-system/components';
|
|
132
|
+
export type { SynPaginationPageSizeChangedEvent } from '@synergy-design-system/components';
|
|
133
|
+
</script>
|
|
134
|
+
|
|
135
|
+
<template>
|
|
136
|
+
<syn-pagination
|
|
137
|
+
@syn-pagination-page-changed="$emit('syn-pagination-page-changed', $event)"
|
|
138
|
+
@syn-pagination-page-size-changed="
|
|
139
|
+
$emit('syn-pagination-page-size-changed', $event)
|
|
140
|
+
"
|
|
141
|
+
v-bind="visibleProps"
|
|
142
|
+
ref="nativeElement">
|
|
143
|
+
</syn-pagination>
|
|
144
|
+
</template>
|
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.12.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#1240](https://github.com/synergy-design-system/synergy-design-system/pull/1240) [`06c29d4`](https://github.com/synergy-design-system/synergy-design-system/commit/06c29d4be7b9297d34919646cfd71394abdc6f88) Thanks [@schilchSICKAG](https://github.com/schilchSICKAG)! - Released on: 2026-04-22
|
|
8
|
+
|
|
9
|
+
feat: ✨ Add new component `<syn-pagination>` (#684)
|
|
10
|
+
|
|
11
|
+
Adds a new component `<syn-pagination>` that may be used to paginate large datasets.
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- Updated dependencies [[`06c29d4`](https://github.com/synergy-design-system/synergy-design-system/commit/06c29d4be7b9297d34919646cfd71394abdc6f88)]:
|
|
16
|
+
- @synergy-design-system/tokens@3.12.0
|
|
17
|
+
|
|
3
18
|
## 3.11.1
|
|
4
19
|
|
|
5
20
|
### Patch Changes
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.12.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#1240](https://github.com/synergy-design-system/synergy-design-system/pull/1240) [`06c29d4`](https://github.com/synergy-design-system/synergy-design-system/commit/06c29d4be7b9297d34919646cfd71394abdc6f88) Thanks [@schilchSICKAG](https://github.com/schilchSICKAG)! - Released on: 2026-04-22
|
|
8
|
+
|
|
9
|
+
feat: ✨ Add new component `<syn-pagination>` (#684)
|
|
10
|
+
|
|
11
|
+
Adds a new component `<syn-pagination>` that may be used to paginate large datasets.
|
|
12
|
+
|
|
3
13
|
## 3.11.1
|
|
4
14
|
|
|
5
15
|
## 3.11.0
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
## Default
|
|
2
|
+
|
|
3
|
+
The default pagination offers the most comprehensive controls and is optimized for tables, lists, and complex data views. It is intended for use cases where users need to adjust both the number of displayed rows and the active page. The navigation controls allow switching between pages as well as jumping directly to the first or last page.
|
|
4
|
+
|
|
5
|
+
```html
|
|
6
|
+
<syn-pagination
|
|
7
|
+
current-page="1"
|
|
8
|
+
page-size="25"
|
|
9
|
+
page-size-options="10, 25, 50, 100"
|
|
10
|
+
total-items="500"
|
|
11
|
+
size="medium"
|
|
12
|
+
variant="full"
|
|
13
|
+
></syn-pagination>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## With Divider
|
|
19
|
+
|
|
20
|
+
An optional divider can be shown to provide visual separation depending on the layout needs.
|
|
21
|
+
|
|
22
|
+
```html
|
|
23
|
+
<syn-pagination
|
|
24
|
+
divider=""
|
|
25
|
+
current-page="2"
|
|
26
|
+
page-size="25"
|
|
27
|
+
total-items="500"
|
|
28
|
+
size="medium"
|
|
29
|
+
variant="full"
|
|
30
|
+
></syn-pagination>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Disabled
|
|
36
|
+
|
|
37
|
+
Use the disabled attribute to disable all interactive elements like syn-select, syn-input, and the previous and next syn-icon-buttons. This can be useful if you want to prevent the user from entering something again immediately after an entry before the first entry has been processed.
|
|
38
|
+
|
|
39
|
+
```html
|
|
40
|
+
<syn-pagination
|
|
41
|
+
disabled=""
|
|
42
|
+
current-page="1"
|
|
43
|
+
page-size="25"
|
|
44
|
+
total-items="500"
|
|
45
|
+
size="medium"
|
|
46
|
+
variant="full"
|
|
47
|
+
></syn-pagination>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Compact
|
|
53
|
+
|
|
54
|
+
The compact variant focuses on essential pagination controls and fits tight layouts or mobile environments. Use this variant when space is limited or page size stays fixed.
|
|
55
|
+
|
|
56
|
+
```html
|
|
57
|
+
<syn-pagination
|
|
58
|
+
variant="compact"
|
|
59
|
+
current-page="1"
|
|
60
|
+
page-size="25"
|
|
61
|
+
total-items="500"
|
|
62
|
+
size="medium"
|
|
63
|
+
></syn-pagination>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Sizes
|
|
69
|
+
|
|
70
|
+
Use the size attribute to change the pagination size.
|
|
71
|
+
|
|
72
|
+
```html
|
|
73
|
+
<div
|
|
74
|
+
style="
|
|
75
|
+
display: flex;
|
|
76
|
+
flex-direction: column;
|
|
77
|
+
gap: var(--syn-spacing-2x-large);
|
|
78
|
+
"
|
|
79
|
+
>
|
|
80
|
+
<syn-pagination
|
|
81
|
+
current-page="1"
|
|
82
|
+
page-size="25"
|
|
83
|
+
total-items="500"
|
|
84
|
+
size="small"
|
|
85
|
+
variant="full"
|
|
86
|
+
></syn-pagination>
|
|
87
|
+
<syn-pagination
|
|
88
|
+
current-page="1"
|
|
89
|
+
page-size="25"
|
|
90
|
+
total-items="500"
|
|
91
|
+
size="medium"
|
|
92
|
+
variant="full"
|
|
93
|
+
></syn-pagination>
|
|
94
|
+
<syn-pagination
|
|
95
|
+
current-page="1"
|
|
96
|
+
page-size="25"
|
|
97
|
+
total-items="500"
|
|
98
|
+
size="large"
|
|
99
|
+
variant="full"
|
|
100
|
+
></syn-pagination>
|
|
101
|
+
</div>
|
|
102
|
+
```
|
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
11
11
|
"globby": "^16.1.1",
|
|
12
12
|
"zod": "^4.3.6",
|
|
13
|
-
"@synergy-design-system/assets": "2.0
|
|
13
|
+
"@synergy-design-system/assets": "2.1.0"
|
|
14
14
|
},
|
|
15
15
|
"description": "MCP Server for the Synergy Design System",
|
|
16
16
|
"devDependencies": {
|
|
@@ -28,12 +28,12 @@
|
|
|
28
28
|
"serve-handler": "^6.1.7",
|
|
29
29
|
"ts-jest": "^29.4.6",
|
|
30
30
|
"typescript": "^5.9.3",
|
|
31
|
-
"@synergy-design-system/components": "3.
|
|
32
|
-
"@synergy-design-system/docs": "0.1.0",
|
|
31
|
+
"@synergy-design-system/components": "3.12.0",
|
|
33
32
|
"@synergy-design-system/eslint-config-syn": "^0.1.0",
|
|
33
|
+
"@synergy-design-system/docs": "0.1.0",
|
|
34
34
|
"@synergy-design-system/fonts": "1.0.5",
|
|
35
35
|
"@synergy-design-system/styles": "2.0.3",
|
|
36
|
-
"@synergy-design-system/tokens": "^3.
|
|
36
|
+
"@synergy-design-system/tokens": "^3.12.0"
|
|
37
37
|
},
|
|
38
38
|
"exports": {
|
|
39
39
|
".": {
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"directory": "packages/mcp"
|
|
68
68
|
},
|
|
69
69
|
"type": "module",
|
|
70
|
-
"version": "2.
|
|
70
|
+
"version": "2.15.0",
|
|
71
71
|
"scripts": {
|
|
72
72
|
"build": "pnpm run build:ts && pnpm run build:metadata && pnpm build:hash",
|
|
73
73
|
"build:all": "pnpm run build && pnpm run build:storybook",
|