Skip to content

Commit

Permalink
cdn support, dedup, and doc clarifications
Browse files Browse the repository at this point in the history
  • Loading branch information
lalomartins committed Dec 25, 2023
1 parent 8fbfd24 commit ef54475
Show file tree
Hide file tree
Showing 6 changed files with 266 additions and 28 deletions.
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@shoelace-style/[email protected]/cdn/themes/light.css"
/>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@shoelace-style/[email protected]/cdn/themes/dark.css"
/>
<script
type="module"
src="https://cdn.jsdelivr.net/npm/@shoelace-style/[email protected]/cdn/shoelace-autoloader.js"
></script>
<script
type="module"
src="https://cdn.jsdelivr.net/npm/@lalomartins/[email protected]/cdn/shoestring-pagination.js"
></script>
```

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)
Expand Down
204 changes: 204 additions & 0 deletions cdn/shoestring-pagination.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
/**
* @license
* Copyright 2032 Lalo Martins
* SPDX-License-Identifier: MIT
*/

import {
LitElement,
css,
html,
} from 'https://cdn.jsdelivr.net/npm/[email protected]/+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`
<sl-visually-hidden>
<nav role="navigation" aria-label="Pagination Navigation">
Pagination not needed as all ${this.total} items fit in one page
</nav>
</sl-visually-hidden>
`;
}

const pages = this.generatePages();
return html`
<nav role="navigation" aria-label="Pagination Navigation">
${this.current === 1
? null
: html`
<sl-icon-button
name="arrow-left"
value=${this.current - 1}
@click=${this._dispatchPrev}
>
</sl-icon-button>
`}
${pages[0] === 1
? null
: html`
<shoestring-pagination-page-button
page=${1}
></shoestring-pagination-page-button>
`}
${pages.map(
(page) =>
html`<shoestring-pagination-page-button
page=${page}
?current=${page === this.current}
></shoestring-pagination-page-button>`
)}
${pages[pages.length - 1] === this.totalPages
? null
: html`
<shoestring-pagination-page-button
page=${this.totalPages}
></shoestring-pagination-page-button>
`}
${this.current === this.totalPages
? null
: html`
<sl-icon-button
name="arrow-right"
value=${this.current + 1}
@click=${this._dispatchNext}
>
</sl-icon-button>
`}
</nav>
`;
}
}
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`<sl-button
variant=${this.current ? 'default' : 'text'}
?disabled=${this.current}
value=${this.page}
@click=${this._dispatch}
>
${this.page}
</sl-button> `;
}
}
customElements.define(
'shoestring-pagination-page-button',
PaginationPageButton
);
31 changes: 8 additions & 23 deletions dev/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
/>
<script
type="module"
src="../node_modules/@shoelace-style/shoelace/dist/components/switch/switch.js"
src="../node_modules/@shoelace-style/shoelace/dist/shoelace.js"
></script>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
Expand All @@ -26,6 +26,10 @@
/>
<script type="module" src="../shoestring-pagination.js"></script>
<script type="module" src="./demo-components.js"></script>
<script
type="module"
src="https://cdn.jsdelivr.net/npm/@lalomartins/[email protected]/cdn/shoestring-theme-selector.js"
></script>
<style>
body > div {
display: flex;
Expand All @@ -42,7 +46,9 @@
<body>
<div>
<h1>Shoestring Pagination demo</h1>
<div id="controls"><sl-switch>Dark mode</sl-switch></div>
<div id="controls">
<shoestring-theme-selector></shoestring-theme-selector>
</div>
</div>
<div>
<shoestring-pagination-demo-block
Expand Down Expand Up @@ -102,26 +108,5 @@ <h1>Shoestring Pagination demo</h1>
total="7"
></shoestring-pagination-demo-block>
</div>

<script>
(function setupDarkMode() {
const switchEl = document.querySelector('#controls sl-switch');
function updateMode(isDark) {
if (isDark) {
document.documentElement.classList.add('sl-theme-dark');
} else {
document.documentElement.classList.remove('sl-theme-dark');
}
}
switchEl.addEventListener('sl-change', (event) => {
updateMode(event.target.checked);
});
const systemDarkMode =
window.matchMedia &&
window.matchMedia('(prefers-color-scheme: dark)').matches;
switchEl.checked = systemDarkMode;
updateMode(systemDarkMode);
})();
</script>
</body>
</html>
11 changes: 11 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* @license
* Copyright 2032 Lalo Martins
* SPDX-License-Identifier: BSD-3-Clause
*/

export {setBasePath} from '@shoelace-style/shoelace/dist/utilities/base-path.js';
import '@shoelace-style/shoelace/dist/components/button/button.js';
import '@shoelace-style/shoelace/dist/components/icon-button/icon-button.js';
import '@shoelace-style/shoelace/dist/components/visually-hidden/visually-hidden.js';
export {ThemeSelector} from './shoestring-theme-selector';
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@lalomartins/shoestring-pagination",
"version": "1.0.1",
"version": "1.0.2",
"description": "A web component for pagination that uses Shoelace buttons for consistent UI",
"main": "shoestring-pagination.js",
"module": "shoestring-pagination.js",
Expand Down
5 changes: 1 addition & 4 deletions shoestring-pagination.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
/**
* @license
* Copyright 2032 Lalo Martins
* SPDX-License-Identifier: BSD-3-Clause
* SPDX-License-Identifier: MIT
*/

import {LitElement, css, html} from 'lit';
import '@shoelace-style/shoelace/dist/components/button/button.js';
import '@shoelace-style/shoelace/dist/components/icon-button/icon-button.js';
import '@shoelace-style/shoelace/dist/components/visually-hidden/visually-hidden.js';

/**
* A web component for pagination that uses Shoelace buttons for consistent UI.
Expand Down

0 comments on commit ef54475

Please sign in to comment.