diff --git a/README.md b/README.md index 1ac2571..dc40fac 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,24 @@ class MyPagedThing extends LitElement { customElements.define('my-paged-thing', MyPagedThing); ``` +If you're using Shoelace in your own project and you want to avoid loading two different copies, import like this: + +```js +import '@shoelace-style/shoelace/dist/components/switch/switch.js'; +import '@shoelace-style/shoelace/dist/components/icon/icon.js'; +import '@lalomartins/shoestring-pagination/shoestring-pagination.js'; +``` + +Otherwise, if you _want_ to use the bundled Shoelace, do this somewhere in your project (just once): + +```js +import {setBasePath} '@lalomartins/shoestring-pagination'; +// … +setBasePath("node_modules/@lalomartins/shoestring-pagination/node_modules/@shoelace-style/shoelace/dist/"); +``` + +(You can also use CDNed Shoelace if you prefer.) + ## API Attributes: @@ -83,6 +101,29 @@ npm i @lalomartins/shoestring-pagination You don't need to import Shoelace on your own code if you're not using it, but you _do_ need to load your own CSS, whether it's from CDN, or from `node_modules`, or copying it into your build. You can check the [Shoelace install instructions](https://shoelace.style/getting-started/installation) for details. +## CDN + +```html + + + + +``` + +If the arrows don't show up, tweak your import order to make sure you have the Shoelace base path set before Pagination loads. + ## Screenshots ![Light mode](./docs/Screen%20Shot%202023-12-24%20at%2019.34.27.png) diff --git a/cdn/shoestring-pagination.js b/cdn/shoestring-pagination.js new file mode 100644 index 0000000..caf0467 --- /dev/null +++ b/cdn/shoestring-pagination.js @@ -0,0 +1,204 @@ +/** + * @license + * Copyright 2032 Lalo Martins + * SPDX-License-Identifier: MIT + */ + +import { + LitElement, + css, + html, +} from 'https://cdn.jsdelivr.net/npm/lit@3.0.0/+esm'; + +/** + * A web component for pagination that uses Shoelace buttons for consistent UI. + * + * @element shoestring-pagination + * + * @dependency sl-button + * @dependency sl-icon-button + * @dependency sl-visually-hidden + * + * @attr {Number} current - The current page + * @attr {Number} total - Total number of items + * @attr {Number} [page-size=10] - How many items are in a page + * @attr {Number} [surrounding-pages=2] - How many pages to display before and after current at most + * @attr {Boolean} hide-on-single-page - If set, and all items fit in one page, hide the element + * + * @fires page-change - Indicates when the page changes; value is in `event.detail.page` + */ +export class Pagination extends LitElement { + static properties = { + current: { + type: Number, + }, + pageSize: { + type: Number, + attribute: 'page-size', + }, + surroundingPages: { + type: Number, + attribute: 'surrounding-pages', + }, + total: { + type: Number, + }, + hideOnSinglePage: { + type: Boolean, + attribute: 'hide-on-single-page', + }, + }; + + static styles = css` + :host { + display: block; + } + + nav { + display: flex; + align-items: center; + } + `; + + constructor() { + super(); + this.current = 1; + this.pageSize = 10; + this.surroundingPages = 2; + this.total = 1; + this.hideOnSinglePage = false; + } + + generatePages() { + const arr = []; + for ( + let i = Math.max(1, this.current - this.surroundingPages); + i <= Math.min(this.totalPages, this.current + this.surroundingPages); + i++ + ) { + arr.push(i); + } + + return arr; + } + + _dispatchPrev() { + const event = new CustomEvent('page-change', { + bubbles: true, + composed: true, + detail: {page: this.current - 1}, + }); + + this.dispatchEvent(event); + } + + _dispatchNext() { + const event = new CustomEvent('page-change', { + bubbles: true, + composed: true, + detail: {page: this.current + 1}, + }); + + this.dispatchEvent(event); + } + + render() { + this.totalPages = Math.ceil(this.total / this.pageSize); + + if (this.hideOnSinglePage && this.totalPages < 2) { + return html` + + + + `; + } + + const pages = this.generatePages(); + return html` + + `; + } +} +customElements.define('shoestring-pagination', Pagination); +export default Pagination; + +export class PaginationPageButton extends LitElement { + static properties = {page: {type: Number}, current: {type: Boolean}}; + + constructor() { + super(); + this.page = 1; + this.current = false; + } + + _dispatch() { + const event = new CustomEvent('page-change', { + bubbles: true, + composed: true, + detail: {page: this.page}, + }); + + this.dispatchEvent(event); + } + + render() { + return html` + ${this.page} + `; + } +} +customElements.define( + 'shoestring-pagination-page-button', + PaginationPageButton +); diff --git a/dev/index.html b/dev/index.html index 4f371ef..ebf9fd5 100644 --- a/dev/index.html +++ b/dev/index.html @@ -16,7 +16,7 @@ /> @@ -26,6 +26,10 @@ /> +