Skip to content

Commit

Permalink
feat: wip on pagination component
Browse files Browse the repository at this point in the history
  • Loading branch information
alexgarel committed Apr 19, 2024
1 parent 6042a41 commit 24812e1
Show file tree
Hide file tree
Showing 9 changed files with 340 additions and 1,631 deletions.
1,716 changes: 88 additions & 1,628 deletions frontend/package-lock.json

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@
"author": "Open Food Facts",
"license": "BSD-3-Clause",
"dependencies": {
"lit": "^3.0.0"
"@types/lodash-es": "^4.17.0",
"lit": "^3.0.0",
"lodash-es": "^4.17.21"
},
"devDependencies": {
"@custom-elements-manifest/analyzer": "^0.6.3",
Expand All @@ -57,7 +59,7 @@
"@web/test-runner-playwright": "^0.9.0",
"@webcomponents/webcomponentsjs": "^2.8.0",
"eslint": "^8.15.0",
"lit-analyzer": "^1.2.1",
"lit-analyzer": "^2.0.3",
"prettier": "^2.6.2",
"rimraf": "^3.0.2",
"rollup": "^2.73.0",
Expand Down
6 changes: 5 additions & 1 deletion frontend/public/off.html
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,11 @@
</div>
</div>
</div>
<div><p>FIXME: pagination</p></div>
<div>
<p>FIXME: pagination</p>
<searchalicious-pages search-name="off">
</searchalicious-pages>
</div>
</div>
</div>
</div>
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ export type SearchResultDetail = {
results: Object[];
count: number;
pageCount: number;
pageSize: number;
currentPage: number;
};

export type SearchResultEvent = CustomEvent<SearchResultDetail>;
1 change: 1 addition & 0 deletions frontend/src/search-a-licious.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export {SearchaliciousBar} from './search-bar';
export {SearchaliciousButton} from './search-button';
export {SearchaliciousPages} from './search-pages';
export {SearchaliciousResults} from './search-results';
10 changes: 10 additions & 0 deletions frontend/src/search-ctl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ export const SearchaliciousSearchMixin = <T extends Constructor<LitElement>>(
@property({type: Number, attribute: 'page-size'})
pageSize = 10;

/**
* Number of result per page
*/
@state()
_currentPage?: number;

/**
* Last search page count
*/
Expand Down Expand Up @@ -143,12 +149,16 @@ export const SearchaliciousSearchMixin = <T extends Constructor<LitElement>>(
const data = await response.json();
this._results = data.hits;
this._count = data.count;
this.pageSize = data.page_size;
this._currentPage = data.page;
this._pageCount = data.page_count;
const detail: SearchResultDetail = {
searchName: this.name,
results: this._results!,
count: this._count!,
pageCount: this._pageCount!,
currentPage: this._currentPage!,
pageSize: this.pageSize,
};
// dispatch an event with the results
this.dispatchEvent(
Expand Down
227 changes: 227 additions & 0 deletions frontend/src/search-pages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
import {LitElement, html, css} from 'lit';
import {customElement, property, state} from 'lit/decorators.js';
import {repeat} from 'lit/directives/repeat.js';
import {range} from 'lodash-es';

import {EventRegistrationMixin} from './event-listener-setup';
import {SearchaliciousEvents} from './enums';
import {SearchResultEvent} from './events';

/**
* A component to display pagination for search results.
*/
@customElement('searchalicious-pages')
export class SearchaliciousPages extends EventRegistrationMixin(LitElement) {
static override styles = css`
ul {
list-style-type: none;
}
li {
display: inline-block;
margin: 0 0.5rem;
}
.current button {
font-weight: bold;
}
`;

/**
* the search we display result for,
* this corresponds to `name` attribute of corresponding search-bar
*/
@property({attribute: 'search-name'})
searchName = 'searchalicious';

/**
* Wether or not we should add the jump to first shortcuts
*/
@property({attribute: 'display-first', type: Boolean})
displayFirst = true;

/**
* Wether or not we should add the jump to end shortcuts
*/
@property({attribute: 'display-last', type: Boolean})
displayLast = true;

/**
* How many pages should we display as clickable links / buttons
*/
@property({attribute: 'displayed-pages', type: Number})
displayedPages = 5;

/**
* Do we have an already launched search
*/
@state()
searchLaunched = false;

/**
* Number of pages in current search
*/
@state()
_pageCount?: number;

/**
* Current displayed page number
*/
@state()
_currentPage?: number;

/**
* HTML to display when there are no results
*
* Can be overridden by a "no-results" slot
*/
noResults = html``;

/**
* HTML to display before anysearch is launched
*
* Can be overridden by a "before-search" slot
*/
beforeSearch = html``;

_updatePages(event: Event) {
const detail = (event as SearchResultEvent).detail;
if (detail.searchName === this.searchName) {
this.searchLaunched = true;
this._pageCount = detail.pageCount;
this._currentPage = detail.currentPage;
this.requestUpdate();
}
}

_isFirstPage() {
return !!(this._currentPage && this._currentPage === 1);
}
_isLastPage() {
return !!(
this._currentPage &&
this._pageCount &&
this._currentPage >= this._pageCount
);
}
_hasStartEllipsis() {
return !!(this._currentPage && this._currentPage > this.displayedPages);
}
_hasEndEllipsis() {
return !!(
this._currentPage &&
this._pageCount &&
this._currentPage < this._pageCount - this.displayedPages
);
}
_displayPages() {
if (!this._currentPage || !this._pageCount) {
return [];
}
const start = Math.max(this._currentPage - 1, 1);
const end = Math.min(start + this.displayedPages - 1, this._pageCount);
return range(start, end);
}

override render() {
if (this._pageCount) {
return this.renderPagination();
} else if (this.searchLaunched) {
return html`<slot name="no-results">${this.noResults}</slot>`;
} else {
return html`<slot name="before-search">${this.beforeSearch}</slot>`;
}
}

/**
* Render the pagination widget (when we have pages)
*/
renderPagination() {
return html`
<nav part="nav">
<ul part="pages">
${this.displayFirst
? html` <li @click=${this._firstPage} part="first">
<slot name="first"
><button ?disabled=${this._isFirstPage()}>|&lt;</button></slot
>
</li>`
: ''}
<li @click=${this._prevPage} part="previous">
<slot name="previous"
><button ?disabled=${this._isFirstPage()}>&lt;</button></slot
>
</li>
${this._hasStartEllipsis()
? html` <li part="start-ellipsis">
<slot name="start-ellipsis"></slot>
</li>`
: ''}
${repeat(
this._displayPages(),
(page, _) => html` <li
class="${page === this._currentPage! ? 'current' : ''}"
part="${page === this._currentPage! ? 'current-' : ''}page"
>
<button>${page}</button>
</li>`
)}
${this._hasEndEllipsis()
? html` <li part="end-ellipsis">
<slot name="end-ellipsis"></slot>
</li>`
: ''}
<li @click=${this._nextPage} part="next">
<slot name="next"
><button ?disabled=${this._isLastPage()}>&gt;</button></slot
>
</li>
${this.displayLast
? html` <li @click=${this._lastPage} part="last">
<slot name="last"
><button ?disabled=${this._isLastPage()}>&gt;|</button></slot
>
</li>`
: ''}
</ul>
</nav>
`;
}

_firstPage() {
// FIXME
}

_lastPage() {
// FIXME
}

_prevPage() {
// FIXME
}

_nextPage() {
// FIXME
}

/**
* Connect search event handlers.
*/
override connectedCallback() {
super.connectedCallback();
this.addEventHandler(SearchaliciousEvents.NEW_RESULT, (event) =>
this._updatePages(event)
);
}
// connect to our specific events
override disconnectedCallback() {
super.disconnectedCallback();
this.removeEventHandler(SearchaliciousEvents.NEW_RESULT, (event) =>
this._updatePages(event)
);
}
}

declare global {
interface HTMLElementTagNameMap {
'searchalicious-pages': SearchaliciousPages;
}
}
2 changes: 2 additions & 0 deletions frontend/src/test/search-results_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ suite('searchalicious-results', () => {
results: results,
count: 3,
pageCount: 1,
currentPage: 1,
pageSize: 10,
};
el._displayResults(
new CustomEvent(SearchaliciousEvents.NEW_RESULT, {
Expand Down
1 change: 1 addition & 0 deletions frontend/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"noImplicitThis": true,
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"forceConsistentCasingInFileNames": true,
"noImplicitOverride": true,
Expand Down

0 comments on commit 24812e1

Please sign in to comment.