/** * * Lit Blog List Element * * */ // Import litelement base class, html helper function & typescript decorators import { html, css, CSSResult, SkhemataBase, property } from '@skhemata/skhemata-base'; import { faCalendarAlt, faFolder, faTag, } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@riovir/wc-fontawesome'; // Import custom element directives import { stringToHtml } from '@skhemata/skhemata-base/dist/directives/stringToHtml'; import { customDateFormat } from '@skhemata/skhemata-base/dist/directives/customDateFormat'; import { decodeHtmlEntities } from '@skhemata/skhemata-base/dist/directives/decodeHtmlEntities'; import { SkhemataBlogListStyle } from '../style/SkhemataBlogListStyle'; import { SkhemataBlogSharedStyle } from '../style/SkhemataBlogSharedStyle'; import { translationEngDefault } from '../translation/SkhemataBlogList/eng'; // Import element dependencies import './SkhemataBlogSearch'; export class SkhemataBlogList extends SkhemataBase { // Property decorator (requires TypeScript or Babel) // Attributes that can be passed into different elements @property({ type: Object, attribute: 'api-wordpress' }) apiWordpress = { url: '' }; @property({ type: String, attribute: 'blog-page-path' }) blogPagePath = ''; @property({ type: Number, attribute: 'posts-per-page' }) postsPerPage = 4; @property({ type: String, attribute: 'pager-type' }) pagerType = "infinite"; @property({ type: Number}) currentPage = 1; @property({ type: String }) searchedBlogPosts = ''; @property({ type: Array }) private blogPosts: any = []; @property({ type: Array }) private blogFeatures: any = []; @property({ type: Number }) totalPages = 0; @property({ type: Number }) totalCount = 0; @property({ type: Number }) maxLoadCount = 0; @property({ type: Number }) private count = 1; @property({ type: Object }) translationData = { eng: translationEngDefault, }; static get styles() { return [ super.styles, SkhemataBlogListStyle, SkhemataBlogSharedStyle, css` .traditional-pager { text-align: center; } ` ]; } static get scopedElements() { return { 'fa-icon': FontAwesomeIcon, }; } constructor() { // Always call super() first super(); window.addEventListener( 'popstate', () => { if(this.pagerType === "traditional") { this.loadPostPage(); } else { this.getPosts(); } }, false ); } willUpdate(changedProperties: Map){ if(changedProperties.has('apiWordpress')){ this.getPosts(); } if(changedProperties.has('postsPerPage')){ this.getPosts(); } if(changedProperties.has('pagerType')){ this.getPosts(); } super.willUpdate(changedProperties); } /** * dispatch navigate event * @param slug post slug */ navigateToPost(slug: string) { this.dispatchEvent( new CustomEvent('navigate', { detail: { slug, }, composed: true, bubbles: true, }) ); window.dispatchEvent(new CustomEvent('clearblogsearch')); } /** * Implement `render` to define a template for your element. * Use JS template literals */ protected render() { let previous = html``; let next = html``; let previousPages = html``; let nextPages = html``; let count = 1; const params = new URLSearchParams(window.location.search); let page = 1; const leadingTrailingCount = 3; const pParam = params.get('p'); if(pParam != null && !Number.isNaN(pParam) ) { const pageParam = parseInt(pParam, 10); page = pageParam; } previous = html` `; if (page <= 1) { previous = html``; } next = html` `; if (page >= this.totalPages) { next = html``; } for (let index = page; index > 1; index -= 1) { if(count > leadingTrailingCount) { break; } previousPages = html`${previousPages}`; count += 1; } count = 1; for (let index = page; index < this.totalPages; index += 1) { if(count > leadingTrailingCount) { break; } nextPages = html`${nextPages}`; count += 1; } let pagination = html``; if (this.totalPages > 1) { pagination = html`
${previous}${previousPages}${nextPages}${next}
`; } return html` ${this.blogFeatures.map( (post: any) => html` ` )}
${this.blogPosts.map( (post: any) => html`
avatar
By { if (e.keyCode === '13') this.filterPostsBy(post._embedded?.author[0].id, 'a'); }} >${post._embedded?.author[0].name}
${stringToHtml(post.excerpt.rendered)}
${this.getStr('SkhemataBlogList.categories')}: ${post.categories ? post.categories .filter((obj: any) => { if (!obj.name.includes('Featured Articles')) { return obj; } return false; }) .map( (item: any, index: Number, arr: any) => html`
{ if (e.keyCode === '13') this.filterPostsBy(item.id, 'c'); }} > ${item.name}
${arr.length > 1 && arr.length - 1 !== index ? html`, ` : html``} ` ) : ''}
${this.getStr('SkhemataBlogList.tags')}: ${post._embedded['wp:term'][1] ? post._embedded['wp:term'][1].map( (item: any, index: Number, arr: any) => html`
{ if (e.keyCode === '13') this.filterPostsBy(item.id, 't'); }} > ${item.name}
${arr.length > 1 && arr.length - 1 !== index ? html`, ` : html``} ` ) : ''}
` )}
${(this.pagerType === "traditional") ? pagination : html`
${this.count < this.maxLoadCount ? html`` : ``}
` } `; } /** * Implement firstUpdated to perform one-time work after * the element’s template has been created. */ async firstUpdated() { await super.firstUpdated(); this.getFeatures(); this.getPosts(); } /** * Fetch Featured Posts from WP REST API */ private getFeatures() { // Use fetch method to make a request // https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch const params = new URLSearchParams(window.location.search); const search = params.get('s'); let searchParams = ''; let categoryParams = ''; if (search && search.length > 3) { searchParams = `&search=${search}`; } fetch( `${this.apiWordpress.url}/categories/?search=featured-articles-landing` ) .then(response => { if(!response.ok){ return; } this.totalPages = Number(response.headers.get('X-WP-TotalPages')); this.totalCount = Number(response.headers.get('X-WP-Total')); const contentType = response.headers.get('Content-Type'); // Check if response header content type is json if (contentType && contentType.includes('application/json')) { return response.json(); } return new TypeError('The format is not JSON.'); // Throw error if above condition isn't met // throw new TypeError('The format is not JSON.'); }) .then(categories => { if(categories?.length > 0){ categoryParams = `&categories=${categories[0].id}`; fetch( `${this.apiWordpress.url}/posts?_embed${searchParams}${categoryParams}&filter[orderby]=date&order=desc` ) .then(response => { this.totalPages = Number(response.headers.get('X-WP-TotalPages')); this.totalCount = Number(response.headers.get('X-WP-Total')); const contentType = response.headers.get('Content-Type'); // Check if response header content type is json if (contentType && contentType.includes('application/json')) { return response.json(); } // Throw error if above condition isn't met // throw new TypeError('The format is not JSON.'); }) .then(data => { if (typeof data !== 'undefined') { // Loop through data // data.forEach((element: any) => // SkhemataBlogFeatured.formatCategories(element) // ); this.blogFeatures = data.map(SkhemataBlogList.formatCategories); } }); } }) } /** * Fetch Posts from WP REST API */ private async getPosts() { // Use fetch method to make a request // https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch const params = new URLSearchParams(window.location.search); const search = params.get('s'); const category = params.get('c'); const author = params.get('a'); const tag = params.get('t'); let searchParams = ''; let categoryParams = ''; let authorParams = ''; let tagParams = ''; const orderbyParam = `&orderby=relevance`; if (search && search.length > 3) { searchParams = `&search=${search}${orderbyParam}`; } if (category) { categoryParams = `&categories=${category}`; } if (author) { authorParams = `&author=${author}`; } if (tag) { tagParams = `&tags=${tag}`; } await fetch( `${this.apiWordpress.url}/posts?_embed${searchParams}${categoryParams}${authorParams}${tagParams}&per_page=${this.postsPerPage}` ) .then(response => { if(!response.ok){ return; } this.totalPages = Number(response.headers.get('X-WP-TotalPages')); this.totalCount = Number(response.headers.get('X-WP-Total')); const contentType = response.headers.get('Content-Type'); this.maxLoadCount = Math.ceil(this.totalCount / 10); // Check if response header content type is json if (contentType && contentType.includes('application/json')) { return response.json(); } // Throw error if above condition isn't met // throw new TypeError('The format is not JSON.'); }) .then(data => { if (typeof data !== 'undefined') { this.blogPosts = data.map(SkhemataBlogList.formatCategories); } }).catch(() => { this.blogPosts = []; }); } /** * Load more posts event handler */ private loadMorePosts() { this.count += 1; const params = new URLSearchParams(window.location.search); const search = params.get('s'); const category = params.get('c'); const author = params.get('a'); const tag = params.get('t'); let tagParams = ''; let searchParams = ''; let categoryParams = ''; let authorParams = ''; const orderbyParam = `&orderby=relevance`; if (search && search.length > 3) { searchParams = `&search=${search}${orderbyParam}`; } if (category) { categoryParams = `&categories=${category}`; } if (author) { authorParams = `&author=${author}`; } if (tag) { tagParams = `&tags=${tag}`; } fetch( `${this.apiWordpress.url}/posts?_embed&page=${this.count}${searchParams}${categoryParams}${authorParams}${tagParams}&per_page=${this.postsPerPage}` ) .then(response => response.json()) .then(data => { const posts: [] = data; this.blogPosts = [ ...this.blogPosts, ...posts.map(SkhemataBlogList.formatCategories), ]; }); } private static formatCategories(data: any) { // Filter out all the categories of the post const formattedData = data; if (data && data._embedded) { const filteredCategories = data._embedded['wp:term'].filter( (term: any, index: any) => term.length > 0 && term[index] ? term[index].taxonomy === 'category' : false ); [formattedData.categories] = filteredCategories; } return formattedData; } private formatDate = (date: string) => { const dateObj = new Date(date); const month = new Intl.DateTimeFormat('en-US', { month: 'short' }).format( dateObj ); return `${month} ${dateObj.getDate()} ${dateObj.getFullYear()}`; }; /** * Filters post based on query params * @param id tag/category/author id * @param queryId query param id */ filterPostsBy(id: string, queryId: string) { const params = new URLSearchParams(window.location.search); if (params.get(queryId) === id) { params.delete(queryId); } else { params.set(queryId, id); } window.history.pushState( {}, '', decodeURIComponent(`${this.blogPagePath}?${params.toString()}`) ); window.scrollTo({ top: 0 }); window.dispatchEvent(new Event('popstate')); } cleanExcerpt = (excerpt: string = '') => excerpt.replace(/.*?.<\/span>/g, ''); private goToButtonPage(event: any) { let page = 1; const buttonValue = event.target.attributes["page-value"].value; if(buttonValue != null) { page = parseInt(buttonValue, 10); } this.setPageNumber(page); } private setPageNumber(page: any) { let setPage = page; if(setPage < 1) { setPage = 1; } if(setPage > this.totalPages) { setPage = this.totalPages; } const params = new URLSearchParams(window.location.search); params.set('p', setPage); this.currentPage = setPage; window.history.pushState( {}, '', `/${this.blogPagePath}?${params.toString()}` ); window.dispatchEvent(new Event('popstate')); } private loadPostPage() { const params = new URLSearchParams(window.location.search); const page = params.get('p'); const search = params.get('s'); const category = params.get('c'); const author = params.get('a'); const tag = params.get('t'); let tagParams = ''; let searchParams = ''; let categoryParams = ''; let authorParams = ''; const orderbyParam = `&orderby=relevance`; if (search && search.length > 3) { searchParams = `&search=${search}${orderbyParam}`; } if (category) { categoryParams = `&categories=${category}`; } if (author) { authorParams = `&author=${author}`; } if (tag) { tagParams = `&tags=${tag}`; } fetch( `${this.apiWordpress.url}/posts?_embed&page=${page}${searchParams}${categoryParams}${authorParams}${tagParams}&per_page=${this.postsPerPage}` ) .then(response => { if(response.status === 200) { this.totalPages = Number(response.headers.get('X-WP-TotalPages')); this.totalCount = Number(response.headers.get('X-WP-Total')); const contentType = response.headers.get('Content-Type'); this.maxLoadCount = Math.ceil(this.totalCount/10); // Check if response header content type is json if (contentType && contentType.includes('application/json')) { return response.json(); } // Throw error if above condition isn't met throw new TypeError('The format is not JSON.'); } else { return response.json(); } }) .then(response => { // Set page to 1 if invalid page number. if(response.code && response.code === "rest_post_invalid_page_number") { this.setPageNumber(1); } else if (!response.code && typeof response !== 'undefined') { this.blogPosts = response.map(SkhemataBlogList.formatCategories); } }); } }