@justeattakeaway/pie-pagination 0.0.0-stage → 0.1.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/README.md CHANGED
@@ -1,3 +1,137 @@
1
- # Temporary Holding Version
1
+ # @justeattakeaway/pie-pagination
2
+ [Source Code](https://github.com/justeattakeaway/pie/tree/main/packages/components/pie-pagination) | [Design Documentation](https://pie.design/components/pagination) | [NPM](https://www.npmjs.com/package/@justeattakeaway/pie-pagination)
2
3
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
4
+ <p>
5
+ <a href="https://www.npmjs.com/@justeattakeaway/pie-pagination">
6
+ <img alt="GitHub Workflow Status" src="https://img.shields.io/npm/v/@justeattakeaway/pie-pagination.svg">
7
+ </a>
8
+ </p>
9
+
10
+ `@justeattakeaway/pie-pagination` is a Web Component built using the Lit library. It offers a simple and accessible `pagination` component for web applications.
11
+
12
+ ## Table of Contents
13
+
14
+ - [Installation](#installation)
15
+ - [Documentation](#documentation)
16
+ - [Properties](#properties)
17
+ - [Slots](#slots)
18
+ - [CSS Variables](#css-variables)
19
+ - [Events](#events)
20
+ - [Usage Examples](#usage-examples)
21
+ - [Questions and Support](#questions-and-support)
22
+ - [Contributing](#contributing)
23
+
24
+ ## Installation
25
+
26
+ > To install any of our web components in your application, we would suggest following the [getting started guide](https://webc.pie.design/?path=/docs/introduction-getting-started--docs) to set up your project.
27
+
28
+ Ideally, you should install the component using the **`@justeattakeaway/pie-webc`** package, which includes all of the components. Or you can install the individual component package.
29
+
30
+ ## Documentation
31
+
32
+ ### Properties
33
+ | Prop | Options | Description | Default |
34
+ |---------------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|
35
+ | `currentPage` | `number`| The currently active page (1-based). This component is fully controlled, so it never updates this value itself; the consumer is responsible for updating it in response to the `pie-pagination-page-change` event. | `1` |
36
+ | `totalPages` | `number`| The total number of pages to render. | `1` |
37
+ | `disabled` | `boolean`| When true, all pagination controls are disabled. | `false` |
38
+ | `aria` | `object`| The ARIA attributes for the pagination landmark. Offers a `label` used for the `aria-label` of the wrapping `<nav>` element. | `undefined` |
39
+
40
+ ### Slots
41
+ This component does not use any slots.
42
+
43
+ ### CSS Variables
44
+ This component does not expose any CSS variables.
45
+
46
+ ### Events
47
+ | Event | Type | Description |
48
+ |-------------------------------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
49
+ | `pie-pagination-page-change` | `CustomEvent` | Fired when the user interacts with any pagination control. The event detail contains `{ currentPage }`, the page the user wants to navigate to. |
50
+ | `pie-pagination-prev` | `CustomEvent` | Fired when the user clicks the "previous" control. The event detail contains `{ currentPage }`. This event fires **in addition to** `pie-pagination-page-change`. |
51
+ | `pie-pagination-next` | `CustomEvent` | Fired when the user clicks the "next" control. The event detail contains `{ currentPage }`. This event fires **in addition to** `pie-pagination-page-change`. |
52
+
53
+ ## Usage Examples
54
+
55
+ **For HTML:**
56
+
57
+ ```js
58
+ // import as module into a js file e.g. main.js
59
+ import '@justeattakeaway/pie-webc/components/pagination.js'
60
+ ```
61
+
62
+ ```html
63
+ <!-- pass js file into <script> tag -->
64
+
65
+ <script type="module" src="/main.js"></script>
66
+ ```
67
+
68
+ ```html
69
+ <pie-pagination currentPage="2" totalPages="10"></pie-pagination>
70
+
71
+ <script>
72
+ const pagination = document.querySelector('pie-pagination');
73
+
74
+ pagination.addEventListener('pie-pagination-page-change', (event) => {
75
+ // The component is controlled — update the prop yourself
76
+ pagination.currentPage = event.detail.currentPage;
77
+ });
78
+ </script>
79
+ ```
80
+
81
+ The `pie-pagination-prev` and `pie-pagination-next` events let you hook into the previous/next controls specifically. They fire in addition to `pie-pagination-page-change`, so you can use either one:
82
+
83
+ ```html
84
+ <pie-pagination currentPage="2" totalPages="10"></pie-pagination>
85
+
86
+ <script>
87
+ const pagination = document.querySelector('pie-pagination');
88
+
89
+ pagination.addEventListener('pie-pagination-prev', (event) => {
90
+ console.log('previous clicked, requested page:', event.detail.currentPage);
91
+ pagination.currentPage = event.detail.currentPage;
92
+ });
93
+
94
+ pagination.addEventListener('pie-pagination-next', (event) => {
95
+ console.log('next clicked, requested page:', event.detail.currentPage);
96
+ pagination.currentPage = event.detail.currentPage;
97
+ });
98
+ </script>
99
+ ```
100
+
101
+ **For Native JS Applications, Vue, Angular, Svelte etc.:**
102
+
103
+ ```js
104
+ // Vue templates (using Nuxt 3)
105
+ import '@justeattakeaway/pie-webc/components/pagination.js'
106
+ ```
107
+
108
+ **For React Applications:**
109
+
110
+ ```jsx
111
+ import { PiePagination } from '@justeattakeaway/pie-webc/react/pagination.js';
112
+
113
+ <PiePagination
114
+ currentPage={currentPage}
115
+ totalPages={totalPages}
116
+ onPiePaginationPageChange={(event) => setCurrentPage(event.detail.currentPage)}
117
+ />
118
+ ```
119
+
120
+ The `pie-pagination-prev` and `pie-pagination-next` events are exposed as `onPiePaginationPrev` and `onPiePaginationNext`:
121
+
122
+ ```jsx
123
+ <PiePagination
124
+ currentPage={currentPage}
125
+ totalPages={totalPages}
126
+ onPiePaginationPrev={(event) => setCurrentPage(event.detail.currentPage)}
127
+ onPiePaginationNext={(event) => setCurrentPage(event.detail.currentPage)}
128
+ />
129
+ ```
130
+
131
+ ## Questions and Support
132
+
133
+ If you work at Just Eat Takeaway.com, please contact us on **#help-designsystem**. Otherwise, please raise an issue on [Github](https://github.com/justeattakeaway/pie/issues).
134
+
135
+ ## Contributing
136
+
137
+ Check out our [contributing guide](https://github.com/justeattakeaway/pie/wiki/Contributing-Guide) for more information on [local development](https://github.com/justeattakeaway/pie/wiki/Contributing-Guide#local-development) and how to run specific [component tests](https://github.com/justeattakeaway/pie/wiki/Contributing-Guide#testing).
@@ -0,0 +1,255 @@
1
+ {
2
+ "schemaVersion": "1.0.0",
3
+ "readme": "",
4
+ "modules": [
5
+ {
6
+ "kind": "javascript-module",
7
+ "path": "src/defs-react.js",
8
+ "declarations": [],
9
+ "exports": []
10
+ },
11
+ {
12
+ "kind": "javascript-module",
13
+ "path": "src/defs.js",
14
+ "declarations": [
15
+ {
16
+ "kind": "variable",
17
+ "name": "ON_PAGE_CHANGE_EVENT",
18
+ "type": {
19
+ "text": "string"
20
+ },
21
+ "default": "'pie-pagination-page-change'",
22
+ "description": "The event name dispatched when the user interacts with a pagination control.\nThe component is fully controlled: it never changes `currentPage` itself, it\nonly tells the consumer which page the user wants to navigate to."
23
+ },
24
+ {
25
+ "kind": "variable",
26
+ "name": "ON_PREV_EVENT",
27
+ "type": {
28
+ "text": "string"
29
+ },
30
+ "default": "'pie-pagination-prev'",
31
+ "description": "The event name dispatched when the user clicks the \"previous\" control."
32
+ },
33
+ {
34
+ "kind": "variable",
35
+ "name": "ON_NEXT_EVENT",
36
+ "type": {
37
+ "text": "string"
38
+ },
39
+ "default": "'pie-pagination-next'",
40
+ "description": "The event name dispatched when the user clicks the \"next\" control."
41
+ },
42
+ {
43
+ "kind": "variable",
44
+ "name": "defaultProps",
45
+ "type": {
46
+ "text": "DefaultProps"
47
+ },
48
+ "default": "{ currentPage: 1, totalPages: 1, disabled: false, }"
49
+ }
50
+ ],
51
+ "exports": [
52
+ {
53
+ "kind": "js",
54
+ "name": "ON_PAGE_CHANGE_EVENT",
55
+ "declaration": {
56
+ "name": "ON_PAGE_CHANGE_EVENT",
57
+ "module": "src/defs.js"
58
+ }
59
+ },
60
+ {
61
+ "kind": "js",
62
+ "name": "ON_PREV_EVENT",
63
+ "declaration": {
64
+ "name": "ON_PREV_EVENT",
65
+ "module": "src/defs.js"
66
+ }
67
+ },
68
+ {
69
+ "kind": "js",
70
+ "name": "ON_NEXT_EVENT",
71
+ "declaration": {
72
+ "name": "ON_NEXT_EVENT",
73
+ "module": "src/defs.js"
74
+ }
75
+ },
76
+ {
77
+ "kind": "js",
78
+ "name": "defaultProps",
79
+ "declaration": {
80
+ "name": "defaultProps",
81
+ "module": "src/defs.js"
82
+ }
83
+ }
84
+ ]
85
+ },
86
+ {
87
+ "kind": "javascript-module",
88
+ "path": "src/index.js",
89
+ "declarations": [
90
+ {
91
+ "kind": "class",
92
+ "description": "",
93
+ "name": "PiePagination",
94
+ "members": [
95
+ {
96
+ "kind": "field",
97
+ "name": "aria",
98
+ "type": {
99
+ "text": "PaginationProps['aria']"
100
+ },
101
+ "privacy": "public",
102
+ "attribute": "aria"
103
+ },
104
+ {
105
+ "kind": "field",
106
+ "name": "currentPage",
107
+ "privacy": "public",
108
+ "attribute": "currentPage"
109
+ },
110
+ {
111
+ "kind": "field",
112
+ "name": "totalPages",
113
+ "privacy": "public",
114
+ "attribute": "totalPages"
115
+ },
116
+ {
117
+ "kind": "field",
118
+ "name": "disabled",
119
+ "privacy": "public",
120
+ "attribute": "disabled"
121
+ },
122
+ {
123
+ "kind": "method",
124
+ "name": "_emitPageChange",
125
+ "privacy": "private",
126
+ "parameters": [
127
+ {
128
+ "name": "currentPage",
129
+ "type": {
130
+ "text": "number"
131
+ }
132
+ }
133
+ ],
134
+ "description": "Emits the page change event with the page the user wants to navigate to.\nThe component never updates `currentPage` itself."
135
+ },
136
+ {
137
+ "kind": "method",
138
+ "name": "_handlePreviousClick",
139
+ "privacy": "private",
140
+ "description": "Handles a click on the \"previous\" control."
141
+ },
142
+ {
143
+ "kind": "method",
144
+ "name": "_handleNextClick",
145
+ "privacy": "private",
146
+ "description": "Handles a click on the \"next\" control."
147
+ },
148
+ {
149
+ "kind": "method",
150
+ "name": "_handlePageClick",
151
+ "privacy": "private",
152
+ "parameters": [
153
+ {
154
+ "name": "page",
155
+ "type": {
156
+ "text": "number"
157
+ }
158
+ }
159
+ ],
160
+ "description": "Handles a click on a page number control."
161
+ },
162
+ {
163
+ "kind": "method",
164
+ "name": "_renderChevronIcon",
165
+ "privacy": "private",
166
+ "parameters": [
167
+ {
168
+ "name": "direction",
169
+ "type": {
170
+ "text": "'previous' | 'next'"
171
+ }
172
+ }
173
+ ],
174
+ "description": "Renders the chevron icon for the previous/next controls, mirroring it for RTL."
175
+ }
176
+ ],
177
+ "events": [
178
+ {
179
+ "type": {
180
+ "text": "CustomEvent"
181
+ },
182
+ "description": "when the user interacts with a pagination control.",
183
+ "name": "pie-pagination-page-change"
184
+ },
185
+ {
186
+ "type": {
187
+ "text": "CustomEvent"
188
+ },
189
+ "description": "when the user clicks the \"previous\" control.",
190
+ "name": "pie-pagination-prev"
191
+ },
192
+ {
193
+ "type": {
194
+ "text": "CustomEvent"
195
+ },
196
+ "description": "when the user clicks the \"next\" control.",
197
+ "name": "pie-pagination-next"
198
+ }
199
+ ],
200
+ "attributes": [
201
+ {
202
+ "name": "aria",
203
+ "type": {
204
+ "text": "PaginationProps['aria']"
205
+ },
206
+ "fieldName": "aria"
207
+ },
208
+ {
209
+ "name": "currentPage",
210
+ "fieldName": "currentPage"
211
+ },
212
+ {
213
+ "name": "totalPages",
214
+ "fieldName": "totalPages"
215
+ },
216
+ {
217
+ "name": "disabled",
218
+ "fieldName": "disabled"
219
+ }
220
+ ],
221
+ "mixins": [
222
+ {
223
+ "name": "RtlMixin",
224
+ "package": "@justeattakeaway/pie-webc-core"
225
+ }
226
+ ],
227
+ "superclass": {
228
+ "name": "PieElement",
229
+ "package": "@justeattakeaway/pie-webc-core/src/internals/PieElement"
230
+ },
231
+ "tagName": "pie-pagination",
232
+ "customElement": true
233
+ }
234
+ ],
235
+ "exports": [
236
+ {
237
+ "kind": "js",
238
+ "name": "*",
239
+ "declaration": {
240
+ "name": "*",
241
+ "module": "src/defs"
242
+ }
243
+ },
244
+ {
245
+ "kind": "js",
246
+ "name": "PiePagination",
247
+ "declaration": {
248
+ "name": "PiePagination",
249
+ "module": "src/index.js"
250
+ }
251
+ }
252
+ ]
253
+ }
254
+ ]
255
+ }
@@ -0,0 +1,111 @@
1
+ import { ComponentDefaultProps } from '@justeattakeaway/pie-webc-core';
2
+ import { CSSResult } from 'lit';
3
+ import { GenericConstructor } from '@justeattakeaway/pie-webc-core';
4
+ import { PieElement } from '@justeattakeaway/pie-webc-core/src/internals/PieElement';
5
+ import { PropertyValues } from 'lit';
6
+ import { RTLInterface } from '@justeattakeaway/pie-webc-core';
7
+ import { TemplateResult } from 'lit-html';
8
+
9
+ declare type AriaProps = {
10
+ label?: string;
11
+ };
12
+
13
+ export declare type DefaultProps = ComponentDefaultProps<PaginationProps, 'currentPage' | 'totalPages' | 'disabled'>;
14
+
15
+ export declare const defaultProps: DefaultProps;
16
+
17
+ /**
18
+ * The event name dispatched when the user clicks the "next" control.
19
+ * @constant
20
+ */
21
+ export declare const ON_NEXT_EVENT = "pie-pagination-next";
22
+
23
+ /**
24
+ * The event name dispatched when the user interacts with a pagination control.
25
+ * The component is fully controlled: it never changes `currentPage` itself, it
26
+ * only tells the consumer which page the user wants to navigate to.
27
+ * @constant
28
+ */
29
+ export declare const ON_PAGE_CHANGE_EVENT = "pie-pagination-page-change";
30
+
31
+ /**
32
+ * The event name dispatched when the user clicks the "previous" control.
33
+ * @constant
34
+ */
35
+ export declare const ON_PREV_EVENT = "pie-pagination-prev";
36
+
37
+ export declare interface PaginationProps {
38
+ /**
39
+ * The ARIA attributes for the pagination landmark. Offers a `label` used for
40
+ * the `aria-label` of the wrapping `<nav>` element.
41
+ */
42
+ aria?: AriaProps;
43
+ /**
44
+ * The currently active page (1-based). This component is fully controlled, so
45
+ * it never updates this value itself; the consumer is responsible for updating
46
+ * it in response to the `pie-pagination-page-change` event.
47
+ * @default 1
48
+ */
49
+ currentPage?: number;
50
+ /**
51
+ * The total number of pages to render.
52
+ * @default 1
53
+ */
54
+ totalPages?: number;
55
+ /**
56
+ * When true, all pagination controls are disabled.
57
+ * @default false
58
+ */
59
+ disabled?: boolean;
60
+ }
61
+
62
+ /**
63
+ * @tagname pie-pagination
64
+ * @event {CustomEvent} pie-pagination-page-change - when the user interacts with a pagination control.
65
+ * @event {CustomEvent} pie-pagination-prev - when the user clicks the "previous" control.
66
+ * @event {CustomEvent} pie-pagination-next - when the user clicks the "next" control.
67
+ */
68
+ export declare class PiePagination extends PiePagination_base implements PaginationProps {
69
+ aria: PaginationProps['aria'];
70
+ currentPage: number;
71
+ totalPages: number;
72
+ disabled: boolean;
73
+ protected updated(changedProperties: PropertyValues<this>): void;
74
+ /**
75
+ * Emits the page change event with the page the user wants to navigate to.
76
+ * The component never updates `currentPage` itself.
77
+ *
78
+ * @private
79
+ */
80
+ private _emitPageChange;
81
+ /**
82
+ * Handles a click on the "previous" control.
83
+ *
84
+ * @private
85
+ */
86
+ private _handlePreviousClick;
87
+ /**
88
+ * Handles a click on the "next" control.
89
+ *
90
+ * @private
91
+ */
92
+ private _handleNextClick;
93
+ /**
94
+ * Handles a click on a page number control.
95
+ *
96
+ * @private
97
+ */
98
+ private _handlePageClick;
99
+ /**
100
+ * Renders the chevron icon for the previous/next controls, mirroring it for RTL.
101
+ *
102
+ * @private
103
+ */
104
+ private _renderChevronIcon;
105
+ render(): TemplateResult<1>;
106
+ static styles: CSSResult;
107
+ }
108
+
109
+ declare const PiePagination_base: GenericConstructor<RTLInterface> & typeof PieElement;
110
+
111
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,160 @@
1
+ import { LitElement as u, html as p, unsafeCSS as x } from "lit";
2
+ import { property as g } from "lit/decorators.js";
3
+ import { classMap as f } from "lit/directives/class-map.js";
4
+ import { ifDefined as P } from "lit/directives/if-defined.js";
5
+ import { RtlMixin as y, dispatchCustomEvent as b, safeCustomElement as z } from "@justeattakeaway/pie-webc-core";
6
+ import "@justeattakeaway/pie-icon-button";
7
+ import "@justeattakeaway/pie-icons-webc/dist/IconChevronLeft.js";
8
+ import "@justeattakeaway/pie-icons-webc/dist/IconChevronRight.js";
9
+ const v = class v extends u {
10
+ willUpdate() {
11
+ this.getAttribute("v") || this.setAttribute("v", v.v);
12
+ }
13
+ };
14
+ v.v = "0.1.0";
15
+ let m = v;
16
+ const _ = "*,*:after,*:before{box-sizing:inherit}:host{display:inline-block}.c-pagination{display:inline-block}.c-pagination-list{display:flex;align-items:center;gap:var(--dt-spacing-b);margin:0;padding:0;list-style:none}.c-pagination-item{display:flex;align-items:center}.c-pagination-control{--btn-border-radius: var(--dt-radius-rounded-e);--btn-font-family: var(--dt-font-interactive-l-family);--btn-font-weight: var(--dt-font-interactive-l-weight);--int-states-mixin-bg-color: var(--dt-color-interactive-brand);--btn-text-color: var(--dt-color-content-interactive-light-solid);--btn-padding-vertical-xsmall: 6px;--btn-padding-vertical-small-expressive: 8px;--btn-padding-vertical-small-productive: 10px;--btn-padding-vertical-medium: 12px;--btn-padding-vertical-large: 16px;--btn-padding-horizontal-small: var(--dt-spacing-b);--btn-padding-horizontal-medium: var(--dt-spacing-d);--btn-padding-horizontal-large: var(--dt-spacing-e);position:relative;display:inline-flex;gap:var(--dt-spacing-b);align-items:center;justify-content:center;border:none;border-radius:var(--btn-border-radius);outline:none;background-color:var(--int-states-mixin-bg-color);cursor:pointer;-webkit-user-select:none;user-select:none;text-decoration:none;font-family:var(--btn-font-family);font-size:var(--btn-font-size);font-weight:var(--btn-font-weight);color:var(--btn-text-color);line-height:var(--btn-line-height);inline-size:var(--btn-inline-size)}.c-pagination-control:focus-visible{box-shadow:0 0 0 2px var(--dt-color-focus-inner),0 0 0 4px var(--dt-color-focus-outer);outline:none}.c-pagination-control{--btn-font-size: calc(var(--dt-font-interactive-xs-size) * 1px);--btn-line-height: calc(var(--dt-font-interactive-xs-line-height) * 1px);--btn-icon-size: 16px;--icon-size-override: 16px;padding:var(--btn-padding-vertical-xsmall) var(--btn-padding-horizontal-small)}@media(min-width:769px){.c-pagination-control.c-button--responsive{--btn-font-size: calc(var(--dt-font-interactive-s-size) * 1px);--btn-line-height: calc(var(--dt-font-interactive-s-line-height) * 1px);--btn-icon-size: 20px;--icon-size-override: 20px;padding:var(--btn-padding-vertical-small-productive) var(--btn-padding-horizontal-medium)}.c-pagination-control.c-button--responsive.c-button--expressive{--btn-font-size: calc(var(--dt-font-interactive-l-size) * 1px);--btn-line-height: calc(var(--dt-font-interactive-l-line-height) * 1px);--btn-icon-size: 20px;--icon-size-override: 20px;padding:var(--btn-padding-vertical-small-expressive) var(--btn-padding-horizontal-medium)}}.c-pagination-control.c-button--outline,.c-pagination-control.c-button--outline-inverse{padding:calc(var(--btn-padding-vertical-xsmall) - 1px) var(--btn-padding-horizontal-small)}@media(min-width:769px){.c-pagination-control.c-button--outline.c-button--responsive,.c-pagination-control.c-button--outline-inverse.c-button--responsive{padding:calc(var(--btn-padding-vertical-small-productive) - 1px) var(--btn-padding-horizontal-medium)}.c-pagination-control.c-button--outline.c-button--responsive.c-button--expressive,.c-pagination-control.c-button--outline-inverse.c-button--responsive.c-button--expressive{padding:calc(var(--btn-padding-vertical-small-expressive) - 1px) var(--btn-padding-horizontal-medium)}}.c-pagination-control{--int-states-mixin-bg-color: transparent;--btn-text-color: var(--dt-color-content-interactive-secondary-solid)}@media(hover:hover){.c-pagination-control:hover:not(:disabled,.is-disabled,.is-dismissible){--hover-modifier: calc(-1 * var(--dt-color-hover-01));--hover-modifier: var(--dt-color-hover-01);--int-states-mixin-bg-color: hsl(var(--dt-color-black-h), var(--dt-color-black-s), var(--dt-color-black-l), var(--hover-modifier));--int-states-mixin-bg-color: hsl(var(--dt-color-transparent-h), var(--dt-color-transparent-s), calc(var(--dt-color-transparent-l) + var(--hover-modifier)))}}.c-pagination-control:active:not(:disabled,.is-disabled,.is-dismissible),.c-pagination-control.is-loading:not(:disabled,.is-disabled){--active-modifier: calc(-1 * var(--dt-color-active-01));--active-modifier: var(--dt-color-active-01);--int-states-mixin-bg-color: hsl(var(--dt-color-black-h), var(--dt-color-black-s), var(--dt-color-black-l), var(--active-modifier));--int-states-mixin-bg-color: hsl(var(--dt-color-transparent-h), var(--dt-color-transparent-s), calc(var(--dt-color-transparent-l) + var(--active-modifier)))}@supports (background-color: color-mix(in srgb,black,white)){@media(hover:hover){.c-pagination-control:hover:not(:disabled,.is-disabled,.is-dismissible){--int-states-mixin-bg-color: color-mix(in srgb, var(--dt-color-hover-01-bg) var(--dt-color-hover-01), var(--dt-color-transparent))}}.c-pagination-control:active:not(:disabled,.is-disabled,.is-dismissible),.c-pagination-control.is-loading:not(:disabled,.is-disabled){--int-states-mixin-bg-color: color-mix(in srgb, var(--dt-color-active-01-bg) var(--dt-color-active-01), var(--dt-color-transparent))}}.c-pagination-control{inline-size:32px;block-size:32px;padding:0;--btn-font-weight: var(--dt-font-body-s-weight)}.c-pagination-control.c-pagination-control--current{--int-states-mixin-bg-color: var(--dt-color-interactive-primary);--btn-text-color: var(--dt-color-content-interactive-primary)}@media(hover:hover){.c-pagination-control.c-pagination-control--current:hover:not(:disabled,.is-disabled,.is-dismissible){--hover-modifier: var(--dt-color-hover-02);--int-states-mixin-bg-color: hsl(var(--dt-color-interactive-primary-h), var(--dt-color-interactive-primary-s), calc(var(--dt-color-interactive-primary-l) + var(--hover-modifier)))}}.c-pagination-control.c-pagination-control--current:active:not(:disabled,.is-disabled,.is-dismissible),.c-pagination-control.c-pagination-control--current.is-loading:not(:disabled,.is-disabled){--active-modifier: var(--dt-color-active-02);--int-states-mixin-bg-color: hsl(var(--dt-color-interactive-primary-h), var(--dt-color-interactive-primary-s), calc(var(--dt-color-interactive-primary-l) + var(--active-modifier)))}@supports (background-color: color-mix(in srgb,black,white)){@media(hover:hover){.c-pagination-control.c-pagination-control--current:hover:not(:disabled,.is-disabled,.is-dismissible){--int-states-mixin-bg-color: color-mix(in srgb, var(--dt-color-hover-02-bg) var(--dt-color-hover-02), var(--dt-color-interactive-primary))}}.c-pagination-control.c-pagination-control--current:active:not(:disabled,.is-disabled,.is-dismissible),.c-pagination-control.c-pagination-control--current.is-loading:not(:disabled,.is-disabled){--int-states-mixin-bg-color: color-mix(in srgb, var(--dt-color-active-02-bg) var(--dt-color-active-02), var(--dt-color-interactive-primary))}}.c-pagination-control.c-pagination-control--current{--btn-font-weight: var(--dt-font-body-strong-s-weight)}.c-pagination-control:disabled{--int-states-mixin-bg-color: transparent;--btn-text-color: var(--dt-color-content-disabled-solid);cursor:not-allowed}", w = "pie-pagination-page-change", $ = "pie-pagination-prev", k = "pie-pagination-next", h = {
17
+ currentPage: 1,
18
+ totalPages: 1,
19
+ disabled: !1
20
+ };
21
+ var C = Object.defineProperty, N = Object.getOwnPropertyDescriptor, l = (t, i, n, e) => {
22
+ for (var a = e > 1 ? void 0 : e ? N(i, n) : i, r = t.length - 1, s; r >= 0; r--)
23
+ (s = t[r]) && (a = (e ? s(i, n, a) : s(a)) || a);
24
+ return e && a && C(i, n, a), a;
25
+ };
26
+ let o = class extends y(m) {
27
+ constructor() {
28
+ super(...arguments), this.currentPage = h.currentPage, this.totalPages = h.totalPages, this.disabled = h.disabled;
29
+ }
30
+ updated(t) {
31
+ t.has("totalPages") && this.totalPages < 1 && console.warn(`pie-pagination: "totalPages" must be at least 1, received "${this.totalPages}".`), t.has("currentPage") && this.currentPage < 1 && console.warn(`pie-pagination: "currentPage" must be at least 1, received "${this.currentPage}".`), t.has("currentPage") && this.currentPage > this.totalPages && console.warn(`pie-pagination: "currentPage" (${this.currentPage}) cannot be greater than "totalPages" (${this.totalPages}).`);
32
+ }
33
+ /**
34
+ * Emits the page change event with the page the user wants to navigate to.
35
+ * The component never updates `currentPage` itself.
36
+ *
37
+ * @private
38
+ */
39
+ _emitPageChange(t) {
40
+ b(this, w, { currentPage: t });
41
+ }
42
+ /**
43
+ * Handles a click on the "previous" control.
44
+ *
45
+ * @private
46
+ */
47
+ _handlePreviousClick() {
48
+ if (this.disabled || this.currentPage <= 1) return;
49
+ const t = this.currentPage - 1;
50
+ b(this, $, { currentPage: t }), this._emitPageChange(t);
51
+ }
52
+ /**
53
+ * Handles a click on the "next" control.
54
+ *
55
+ * @private
56
+ */
57
+ _handleNextClick() {
58
+ if (this.disabled || this.currentPage >= this.totalPages) return;
59
+ const t = this.currentPage + 1;
60
+ b(this, k, { currentPage: t }), this._emitPageChange(t);
61
+ }
62
+ /**
63
+ * Handles a click on a page number control.
64
+ *
65
+ * @private
66
+ */
67
+ _handlePageClick(t) {
68
+ this.disabled || t === this.currentPage || this._emitPageChange(t);
69
+ }
70
+ /**
71
+ * Renders the chevron icon for the previous/next controls, mirroring it for RTL.
72
+ *
73
+ * @private
74
+ */
75
+ _renderChevronIcon(t) {
76
+ return (t === "previous" ? !this.isRTL : this.isRTL) ? p`<icon-chevron-left></icon-chevron-left>` : p`<icon-chevron-right></icon-chevron-right>`;
77
+ }
78
+ render() {
79
+ const {
80
+ aria: t,
81
+ currentPage: i,
82
+ disabled: n,
83
+ totalPages: e
84
+ } = this, a = Array.from({ length: Math.max(e, 0) }, (c, d) => d + 1), r = n || i <= 1, s = n || i >= e;
85
+ return p`
86
+ <nav
87
+ class="c-pagination"
88
+ aria-label="${(t == null ? void 0 : t.label) ?? "Pagination"}"
89
+ data-test-id="pie-pagination">
90
+ <ol class="c-pagination-list" data-test-id="pie-pagination-list">
91
+ <li class="c-pagination-item">
92
+ <pie-icon-button
93
+ data-test-id="pie-pagination-previous"
94
+ size="xsmall"
95
+ variant="ghost-secondary"
96
+ ?disabled="${r}"
97
+ .aria="${{ label: "Previous page" }}"
98
+ @click="${this._handlePreviousClick}">
99
+ ${this._renderChevronIcon("previous")}
100
+ </pie-icon-button>
101
+ </li>
102
+
103
+ ${a.map((c) => {
104
+ const d = c === i;
105
+ return p`
106
+ <li class="c-pagination-item">
107
+ <button
108
+ type="button"
109
+ class="${f({
110
+ "c-pagination-control": !0,
111
+ "c-pagination-control--current": d
112
+ })}"
113
+ data-test-id="pie-pagination-page"
114
+ ?disabled="${n}"
115
+ aria-current="${P(d ? "page" : void 0)}"
116
+ aria-label="Page ${c}"
117
+ @click="${() => this._handlePageClick(c)}">
118
+ ${c}
119
+ </button>
120
+ </li>`;
121
+ })}
122
+
123
+ <li class="c-pagination-item">
124
+ <pie-icon-button
125
+ data-test-id="pie-pagination-next"
126
+ size="xsmall"
127
+ variant="ghost-secondary"
128
+ ?disabled="${s}"
129
+ .aria="${{ label: "Next page" }}"
130
+ @click="${this._handleNextClick}">
131
+ ${this._renderChevronIcon("next")}
132
+ </pie-icon-button>
133
+ </li>
134
+ </ol>
135
+ </nav>`;
136
+ }
137
+ };
138
+ o.styles = x(_);
139
+ l([
140
+ g({ type: Object })
141
+ ], o.prototype, "aria", 2);
142
+ l([
143
+ g({ type: Number })
144
+ ], o.prototype, "currentPage", 2);
145
+ l([
146
+ g({ type: Number })
147
+ ], o.prototype, "totalPages", 2);
148
+ l([
149
+ g({ type: Boolean })
150
+ ], o.prototype, "disabled", 2);
151
+ o = l([
152
+ z("pie-pagination")
153
+ ], o);
154
+ export {
155
+ k as ON_NEXT_EVENT,
156
+ w as ON_PAGE_CHANGE_EVENT,
157
+ $ as ON_PREV_EVENT,
158
+ o as PiePagination,
159
+ h as defaultProps
160
+ };
@@ -0,0 +1,122 @@
1
+ import { ComponentDefaultProps } from '@justeattakeaway/pie-webc-core';
2
+ import { CSSResult } from 'lit';
3
+ import { GenericConstructor } from '@justeattakeaway/pie-webc-core';
4
+ import { PieElement } from '@justeattakeaway/pie-webc-core/src/internals/PieElement';
5
+ import { PropertyValues } from 'lit';
6
+ import * as React_2 from 'react';
7
+ import { RTLInterface } from '@justeattakeaway/pie-webc-core';
8
+ import { TemplateResult } from 'lit-html';
9
+
10
+ declare type AriaProps = {
11
+ label?: string;
12
+ };
13
+
14
+ export declare type DefaultProps = ComponentDefaultProps<PaginationProps, 'currentPage' | 'totalPages' | 'disabled'>;
15
+
16
+ export declare const defaultProps: DefaultProps;
17
+
18
+ /**
19
+ * The event name dispatched when the user clicks the "next" control.
20
+ * @constant
21
+ */
22
+ export declare const ON_NEXT_EVENT = "pie-pagination-next";
23
+
24
+ /**
25
+ * The event name dispatched when the user interacts with a pagination control.
26
+ * The component is fully controlled: it never changes `currentPage` itself, it
27
+ * only tells the consumer which page the user wants to navigate to.
28
+ * @constant
29
+ */
30
+ export declare const ON_PAGE_CHANGE_EVENT = "pie-pagination-page-change";
31
+
32
+ /**
33
+ * The event name dispatched when the user clicks the "previous" control.
34
+ * @constant
35
+ */
36
+ export declare const ON_PREV_EVENT = "pie-pagination-prev";
37
+
38
+ export declare interface PaginationProps {
39
+ /**
40
+ * The ARIA attributes for the pagination landmark. Offers a `label` used for
41
+ * the `aria-label` of the wrapping `<nav>` element.
42
+ */
43
+ aria?: AriaProps;
44
+ /**
45
+ * The currently active page (1-based). This component is fully controlled, so
46
+ * it never updates this value itself; the consumer is responsible for updating
47
+ * it in response to the `pie-pagination-page-change` event.
48
+ * @default 1
49
+ */
50
+ currentPage?: number;
51
+ /**
52
+ * The total number of pages to render.
53
+ * @default 1
54
+ */
55
+ totalPages?: number;
56
+ /**
57
+ * When true, all pagination controls are disabled.
58
+ * @default false
59
+ */
60
+ disabled?: boolean;
61
+ }
62
+
63
+ export declare const PiePagination: React_2.ForwardRefExoticComponent<React_2.PropsWithChildren<Omit<React_2.PropsWithoutRef<PaginationProps>, "children">> & React_2.RefAttributes<PiePagination_2> & PiePaginationEvents & ReactBaseType>;
64
+
65
+ /**
66
+ * @tagname pie-pagination
67
+ * @event {CustomEvent} pie-pagination-page-change - when the user interacts with a pagination control.
68
+ * @event {CustomEvent} pie-pagination-prev - when the user clicks the "previous" control.
69
+ * @event {CustomEvent} pie-pagination-next - when the user clicks the "next" control.
70
+ */
71
+ declare class PiePagination_2 extends PiePagination_base implements PaginationProps {
72
+ aria: PaginationProps['aria'];
73
+ currentPage: number;
74
+ totalPages: number;
75
+ disabled: boolean;
76
+ protected updated(changedProperties: PropertyValues<this>): void;
77
+ /**
78
+ * Emits the page change event with the page the user wants to navigate to.
79
+ * The component never updates `currentPage` itself.
80
+ *
81
+ * @private
82
+ */
83
+ private _emitPageChange;
84
+ /**
85
+ * Handles a click on the "previous" control.
86
+ *
87
+ * @private
88
+ */
89
+ private _handlePreviousClick;
90
+ /**
91
+ * Handles a click on the "next" control.
92
+ *
93
+ * @private
94
+ */
95
+ private _handleNextClick;
96
+ /**
97
+ * Handles a click on a page number control.
98
+ *
99
+ * @private
100
+ */
101
+ private _handlePageClick;
102
+ /**
103
+ * Renders the chevron icon for the previous/next controls, mirroring it for RTL.
104
+ *
105
+ * @private
106
+ */
107
+ private _renderChevronIcon;
108
+ render(): TemplateResult<1>;
109
+ static styles: CSSResult;
110
+ }
111
+
112
+ declare const PiePagination_base: GenericConstructor<RTLInterface> & typeof PieElement;
113
+
114
+ declare type PiePaginationEvents = {
115
+ onPiePaginationPageChange?: (event: CustomEvent) => void;
116
+ onPiePaginationPrev?: (event: CustomEvent) => void;
117
+ onPiePaginationNext?: (event: CustomEvent) => void;
118
+ };
119
+
120
+ declare type ReactBaseType = React_2.HTMLAttributes<HTMLElement>;
121
+
122
+ export { }
package/dist/react.js ADDED
@@ -0,0 +1,25 @@
1
+ import * as a from "react";
2
+ import { createComponent as i } from "@lit/react";
3
+ import { PiePagination as e } from "./index.js";
4
+ import { ON_NEXT_EVENT as r, ON_PAGE_CHANGE_EVENT as m, ON_PREV_EVENT as N, defaultProps as E } from "./index.js";
5
+ const n = i({
6
+ displayName: "PiePagination",
7
+ elementClass: e,
8
+ react: a,
9
+ tagName: "pie-pagination",
10
+ events: {
11
+ onPiePaginationPageChange: "pie-pagination-page-change",
12
+ // when the user interacts with a pagination control.
13
+ onPiePaginationPrev: "pie-pagination-prev",
14
+ // when the user clicks the "previous" control.
15
+ onPiePaginationNext: "pie-pagination-next"
16
+ // when the user clicks the "next" control.
17
+ }
18
+ }), P = n;
19
+ export {
20
+ r as ON_NEXT_EVENT,
21
+ m as ON_PAGE_CHANGE_EVENT,
22
+ N as ON_PREV_EVENT,
23
+ P as PiePagination,
24
+ E as defaultProps
25
+ };
package/package.json CHANGED
@@ -1,6 +1,59 @@
1
1
  {
2
2
  "name": "@justeattakeaway/pie-pagination",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "PIE design system pagination built using web components",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/justeattakeaway/pie",
8
+ "directory": "packages/components/pie-pagination"
9
+ },
10
+ "homepage": "https://webc.pie.design/?path=/docs/components-pagination--overview",
11
+ "type": "module",
12
+ "main": "dist/index.js",
13
+ "module": "dist/index.js",
14
+ "types": "dist/index.d.ts",
15
+ "files": [
16
+ "custom-elements.json",
17
+ "src",
18
+ "dist",
19
+ "**/*.d.ts"
20
+ ],
21
+ "pieMetadata": {
22
+ "componentStatus": "alpha",
23
+ "replaces": {
24
+ "snacks": []
25
+ }
26
+ },
27
+ "scripts": {
28
+ "build": "run -T vite build",
29
+ "build:react-wrapper": "run -T build-react-wrapper",
30
+ "create:manifest": "run -T cem analyze --litelement",
31
+ "lint:scripts": "run -T eslint .",
32
+ "lint:scripts:fix": "yarn lint:scripts --fix",
33
+ "lint:style": "run -T stylelint ./src/**/*.{css,scss}",
34
+ "lint:style:fix": "yarn lint:style --fix",
35
+ "watch": "run -T vite build --watch",
36
+ "test:browsers": "run -T playwright test -c ./playwright-lit.config.ts",
37
+ "test:browsers:ci": "yarn test:browsers",
38
+ "test:visual": "run -T cross-env-shell PERCY_TOKEN=${PERCY_TOKEN_PIE_PAGINATION} percy exec --allowed-hostname cloudfront.net -- run -T playwright test -c ./playwright-lit-visual.config.ts",
39
+ "test:visual:ci": "yarn test:visual"
40
+ },
41
+ "author": "Just Eat Takeaway.com - Design System Team",
42
+ "license": "Apache-2.0",
43
+ "devDependencies": {
44
+ "@justeattakeaway/pie-components-config": "0.21.5",
45
+ "@justeattakeaway/pie-css": "1.9.1",
46
+ "@justeattakeaway/pie-monorepo-utils": "0.9.12",
47
+ "@justeattakeaway/pie-wrapper-react": "0.14.5"
48
+ },
49
+ "customElements": "custom-elements.json",
50
+ "sideEffects": [
51
+ "dist/*.js"
52
+ ],
53
+ "dependencies": {
54
+ "@justeattakeaway/pie-button": "1.15.1",
55
+ "@justeattakeaway/pie-icon-button": "2.8.1",
56
+ "@justeattakeaway/pie-icons-webc": "1.29.5",
57
+ "@justeattakeaway/pie-webc-core": "22.0.1"
58
+ }
59
+ }
@@ -0,0 +1,3 @@
1
+ import type React from 'react';
2
+
3
+ export type ReactBaseType = React.HTMLAttributes<HTMLElement>;
package/src/defs.ts ADDED
@@ -0,0 +1,61 @@
1
+ import { type ComponentDefaultProps } from '@justeattakeaway/pie-webc-core';
2
+
3
+ /**
4
+ * The event name dispatched when the user interacts with a pagination control.
5
+ * The component is fully controlled: it never changes `currentPage` itself, it
6
+ * only tells the consumer which page the user wants to navigate to.
7
+ * @constant
8
+ */
9
+ export const ON_PAGE_CHANGE_EVENT = 'pie-pagination-page-change';
10
+
11
+ /**
12
+ * The event name dispatched when the user clicks the "previous" control.
13
+ * @constant
14
+ */
15
+ export const ON_PREV_EVENT = 'pie-pagination-prev';
16
+
17
+ /**
18
+ * The event name dispatched when the user clicks the "next" control.
19
+ * @constant
20
+ */
21
+ export const ON_NEXT_EVENT = 'pie-pagination-next';
22
+
23
+ type AriaProps = {
24
+ label?: string;
25
+ };
26
+
27
+ export interface PaginationProps {
28
+ /**
29
+ * The ARIA attributes for the pagination landmark. Offers a `label` used for
30
+ * the `aria-label` of the wrapping `<nav>` element.
31
+ */
32
+ aria?: AriaProps;
33
+
34
+ /**
35
+ * The currently active page (1-based). This component is fully controlled, so
36
+ * it never updates this value itself; the consumer is responsible for updating
37
+ * it in response to the `pie-pagination-page-change` event.
38
+ * @default 1
39
+ */
40
+ currentPage?: number;
41
+
42
+ /**
43
+ * The total number of pages to render.
44
+ * @default 1
45
+ */
46
+ totalPages?: number;
47
+
48
+ /**
49
+ * When true, all pagination controls are disabled.
50
+ * @default false
51
+ */
52
+ disabled?: boolean;
53
+ }
54
+
55
+ export type DefaultProps = ComponentDefaultProps<PaginationProps, 'currentPage' | 'totalPages' | 'disabled'>;
56
+
57
+ export const defaultProps: DefaultProps = {
58
+ currentPage: 1,
59
+ totalPages: 1,
60
+ disabled: false,
61
+ };
package/src/index.ts ADDED
@@ -0,0 +1,200 @@
1
+ import {
2
+ html, unsafeCSS, type PropertyValues,
3
+ } from 'lit';
4
+ import { property } from 'lit/decorators.js';
5
+ import { classMap } from 'lit/directives/class-map.js';
6
+ import { ifDefined } from 'lit/directives/if-defined.js';
7
+
8
+ import { PieElement } from '@justeattakeaway/pie-webc-core/src/internals/PieElement';
9
+ import {
10
+ dispatchCustomEvent,
11
+ RtlMixin,
12
+ safeCustomElement,
13
+ } from '@justeattakeaway/pie-webc-core';
14
+
15
+ import '@justeattakeaway/pie-icon-button';
16
+ import '@justeattakeaway/pie-icons-webc/dist/IconChevronLeft.js';
17
+ import '@justeattakeaway/pie-icons-webc/dist/IconChevronRight.js';
18
+
19
+ import styles from './pagination.scss?inline';
20
+ import {
21
+ type PaginationProps,
22
+ defaultProps,
23
+ ON_PAGE_CHANGE_EVENT,
24
+ ON_PREV_EVENT,
25
+ ON_NEXT_EVENT,
26
+ } from './defs';
27
+
28
+ // Valid values available to consumers
29
+ export * from './defs';
30
+
31
+ const componentSelector = 'pie-pagination';
32
+
33
+ /**
34
+ * @tagname pie-pagination
35
+ * @event {CustomEvent} pie-pagination-page-change - when the user interacts with a pagination control.
36
+ * @event {CustomEvent} pie-pagination-prev - when the user clicks the "previous" control.
37
+ * @event {CustomEvent} pie-pagination-next - when the user clicks the "next" control.
38
+ */
39
+ @safeCustomElement('pie-pagination')
40
+ export class PiePagination extends RtlMixin(PieElement) implements PaginationProps {
41
+ @property({ type: Object })
42
+ public aria: PaginationProps['aria'];
43
+
44
+ @property({ type: Number })
45
+ public currentPage = defaultProps.currentPage;
46
+
47
+ @property({ type: Number })
48
+ public totalPages = defaultProps.totalPages;
49
+
50
+ @property({ type: Boolean })
51
+ public disabled = defaultProps.disabled;
52
+
53
+ protected updated (changedProperties: PropertyValues<this>): void {
54
+ if (changedProperties.has('totalPages') && this.totalPages < 1) {
55
+ console.warn(`pie-pagination: "totalPages" must be at least 1, received "${this.totalPages}".`);
56
+ }
57
+
58
+ if (changedProperties.has('currentPage') && this.currentPage < 1) {
59
+ console.warn(`pie-pagination: "currentPage" must be at least 1, received "${this.currentPage}".`);
60
+ }
61
+
62
+ if (changedProperties.has('currentPage') && this.currentPage > this.totalPages) {
63
+ console.warn(`pie-pagination: "currentPage" (${this.currentPage}) cannot be greater than "totalPages" (${this.totalPages}).`);
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Emits the page change event with the page the user wants to navigate to.
69
+ * The component never updates `currentPage` itself.
70
+ *
71
+ * @private
72
+ */
73
+ private _emitPageChange (currentPage: number) {
74
+ dispatchCustomEvent(this, ON_PAGE_CHANGE_EVENT, { currentPage });
75
+ }
76
+
77
+ /**
78
+ * Handles a click on the "previous" control.
79
+ *
80
+ * @private
81
+ */
82
+ private _handlePreviousClick () {
83
+ if (this.disabled || this.currentPage <= 1) return;
84
+
85
+ const requestedPage = this.currentPage - 1;
86
+
87
+ dispatchCustomEvent(this, ON_PREV_EVENT, { currentPage: requestedPage });
88
+ this._emitPageChange(requestedPage);
89
+ }
90
+
91
+ /**
92
+ * Handles a click on the "next" control.
93
+ *
94
+ * @private
95
+ */
96
+ private _handleNextClick () {
97
+ if (this.disabled || this.currentPage >= this.totalPages) return;
98
+
99
+ const requestedPage = this.currentPage + 1;
100
+
101
+ dispatchCustomEvent(this, ON_NEXT_EVENT, { currentPage: requestedPage });
102
+ this._emitPageChange(requestedPage);
103
+ }
104
+
105
+ /**
106
+ * Handles a click on a page number control.
107
+ *
108
+ * @private
109
+ */
110
+ private _handlePageClick (page: number) {
111
+ if (this.disabled || page === this.currentPage) return;
112
+ this._emitPageChange(page);
113
+ }
114
+
115
+ /**
116
+ * Renders the chevron icon for the previous/next controls, mirroring it for RTL.
117
+ *
118
+ * @private
119
+ */
120
+ private _renderChevronIcon (direction: 'previous' | 'next') {
121
+ const pointsLeft = direction === 'previous' ? !this.isRTL : this.isRTL;
122
+
123
+ return pointsLeft
124
+ ? html`<icon-chevron-left></icon-chevron-left>`
125
+ : html`<icon-chevron-right></icon-chevron-right>`;
126
+ }
127
+
128
+ render () {
129
+ const {
130
+ aria, currentPage, disabled, totalPages,
131
+ } = this;
132
+
133
+ const pages = Array.from({ length: Math.max(totalPages, 0) }, (_, index) => index + 1);
134
+ const isPreviousDisabled = disabled || currentPage <= 1;
135
+ const isNextDisabled = disabled || currentPage >= totalPages;
136
+
137
+ return html`
138
+ <nav
139
+ class="c-pagination"
140
+ aria-label="${aria?.label ?? 'Pagination'}"
141
+ data-test-id="pie-pagination">
142
+ <ol class="c-pagination-list" data-test-id="pie-pagination-list">
143
+ <li class="c-pagination-item">
144
+ <pie-icon-button
145
+ data-test-id="pie-pagination-previous"
146
+ size="xsmall"
147
+ variant="ghost-secondary"
148
+ ?disabled="${isPreviousDisabled}"
149
+ .aria="${{ label: 'Previous page' }}"
150
+ @click="${this._handlePreviousClick}">
151
+ ${this._renderChevronIcon('previous')}
152
+ </pie-icon-button>
153
+ </li>
154
+
155
+ ${pages.map((page) => {
156
+ const isCurrent = page === currentPage;
157
+ const classes = {
158
+ 'c-pagination-control': true,
159
+ 'c-pagination-control--current': isCurrent,
160
+ };
161
+
162
+ return html`
163
+ <li class="c-pagination-item">
164
+ <button
165
+ type="button"
166
+ class="${classMap(classes)}"
167
+ data-test-id="pie-pagination-page"
168
+ ?disabled="${disabled}"
169
+ aria-current="${ifDefined(isCurrent ? 'page' : undefined)}"
170
+ aria-label="Page ${page}"
171
+ @click="${() => this._handlePageClick(page)}">
172
+ ${page}
173
+ </button>
174
+ </li>`;
175
+ })}
176
+
177
+ <li class="c-pagination-item">
178
+ <pie-icon-button
179
+ data-test-id="pie-pagination-next"
180
+ size="xsmall"
181
+ variant="ghost-secondary"
182
+ ?disabled="${isNextDisabled}"
183
+ .aria="${{ label: 'Next page' }}"
184
+ @click="${this._handleNextClick}">
185
+ ${this._renderChevronIcon('next')}
186
+ </pie-icon-button>
187
+ </li>
188
+ </ol>
189
+ </nav>`;
190
+ }
191
+
192
+ // Renders a `CSSResult` generated from SCSS by Vite
193
+ static styles = unsafeCSS(styles);
194
+ }
195
+
196
+ declare global {
197
+ interface HTMLElementTagNameMap {
198
+ [componentSelector]: PiePagination;
199
+ }
200
+ }
@@ -0,0 +1,53 @@
1
+ @use '@justeattakeaway/pie-css/scss' as p;
2
+ @use '@justeattakeaway/pie-css/scss/mixins/components/button' as btn;
3
+
4
+ :host {
5
+ display: inline-block;
6
+ }
7
+
8
+ .c-pagination {
9
+ display: inline-block;
10
+ }
11
+
12
+ .c-pagination-list {
13
+ display: flex;
14
+ align-items: center;
15
+ gap: var(--dt-spacing-b);
16
+ margin: 0;
17
+ padding: 0;
18
+ list-style: none;
19
+ }
20
+
21
+ .c-pagination-item {
22
+ display: flex;
23
+ align-items: center;
24
+ }
25
+
26
+ // Page number control.
27
+ // Built from the shared PIE button mixins so it inherits the same typography,
28
+ // interactive states and focus ring as pie-button, but sized as a fixed
29
+ // 32x32 circular control to match the Figma specification.
30
+ .c-pagination-control {
31
+ @include btn.button-base($include-icon-selector: false);
32
+ @include btn.button-size('xsmall');
33
+ @include btn.button-variant('ghost');
34
+
35
+ inline-size: 32px;
36
+ block-size: 32px;
37
+ padding: 0;
38
+
39
+ --btn-font-weight: var(--dt-font-body-s-weight);
40
+
41
+ &.c-pagination-control--current {
42
+ @include btn.button-variant('primary-alternative');
43
+
44
+ --btn-font-weight: var(--dt-font-body-strong-s-weight);
45
+ }
46
+
47
+ &:disabled {
48
+ --int-states-mixin-bg-color: transparent;
49
+ --btn-text-color: var(--dt-color-content-disabled-solid);
50
+
51
+ cursor: not-allowed;
52
+ }
53
+ }
package/src/react.ts ADDED
@@ -0,0 +1,29 @@
1
+ import * as React from 'react';
2
+ import { createComponent, type EventName } from '@lit/react';
3
+ import { PiePagination as PiePaginationLit } from './index';
4
+ import { type PaginationProps } from './defs';
5
+
6
+ export * from './defs';
7
+
8
+ const PiePaginationReact = createComponent({
9
+ displayName: 'PiePagination',
10
+ elementClass: PiePaginationLit,
11
+ react: React,
12
+ tagName: 'pie-pagination',
13
+ events: {
14
+ onPiePaginationPageChange: 'pie-pagination-page-change' as EventName<CustomEvent>, // when the user interacts with a pagination control.
15
+ onPiePaginationPrev: 'pie-pagination-prev' as EventName<CustomEvent>, // when the user clicks the "previous" control.
16
+ onPiePaginationNext: 'pie-pagination-next' as EventName<CustomEvent>, // when the user clicks the "next" control.
17
+ },
18
+ });
19
+
20
+ type ReactBaseType = React.HTMLAttributes<HTMLElement>;
21
+
22
+ type PiePaginationEvents = {
23
+ onPiePaginationPageChange?: (event: CustomEvent) => void;
24
+ onPiePaginationPrev?: (event: CustomEvent) => void;
25
+ onPiePaginationNext?: (event: CustomEvent) => void;
26
+ };
27
+
28
+ export const PiePagination = PiePaginationReact as React.ForwardRefExoticComponent<React.PropsWithChildren<Omit<React.PropsWithoutRef<PaginationProps>, 'children'>>
29
+ & React.RefAttributes<PiePaginationLit> & PiePaginationEvents & ReactBaseType>;