@skhemata/skhemata-blog
Version:
Skhemata Blog Web Component. This web component provides several sub components in addition to main component, allowing featured blogs, blog listing and blog post display.
254 lines (222 loc) • 7.83 kB
text/typescript
/* eslint-disable lit-a11y/anchor-is-valid */
/**
*
* Lit Blog List Element
*
* */
// Import litelement base class, html helper function & typescript decorators
import { SkhemataBase, html, CSSResult, property } from '@skhemata/skhemata-base';
// Import custom style elements
import { faUser, faCalendarAlt } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@riovir/wc-fontawesome';
import { stringToHtml } from '@skhemata/skhemata-base/dist/directives/stringToHtml';
import { decodeHtmlEntities } from '@skhemata/skhemata-base/dist/directives/decodeHtmlEntities';
import { SkhemataBlogFeaturedStyle } from '../style/SkhemataBlogFeaturedStyle';
import { SkhemataBlogSharedStyle } from '../style/SkhemataBlogSharedStyle';
import { translationEngDefault } from '../translation/SkhemataBlogFeatured/eng';
// Import custom element directives
// Import element dependencies
import './SkhemataBlogSearch';
export class SkhemataBlogFeatured extends SkhemataBase {
// Property decorator (requires TypeScript or Babel)
// Attributes that can be passed into different elements
apiWordpress = {
url: ''
};
blogPagePath = '';
postsPerPage = 3;
searchedBlogPosts = '';
private blogPosts = [];
totalPages = 0;
totalCount = 0;
translationData = {
eng: translationEngDefault,
};
static get styles() {
return <CSSResult[]>[
...super.styles,
SkhemataBlogFeaturedStyle,
SkhemataBlogSharedStyle,
];
}
static get scopedElements() {
return {
'fa-icon': FontAwesomeIcon,
};
}
constructor() {
// Always call super() first
super();
window.addEventListener(
'hashchange',
() => {
this.getPosts();
},
false
);
}
navigate(slug: string) {
this.dispatchEvent(
new CustomEvent('navigate', {
detail: {
slug,
},
})
);
}
/**
* Implement `render` to define a template for your element.
* Use JS template literals
*/
protected render() {
return html`
<div class="blog-list">
${this.blogPosts.map(
(post: any) => html`
<div class="blog-item card">
<button class="featured" =${() => this.navigate(post.slug)}>
<div class="card-content">
${post._embedded['wp:featuredmedia']
? html`
<figure class="image feature-img">
<img
src="${post._embedded['wp:featuredmedia']['0']
.source_url}"
alt="featured"
/>
</figure>
`
: null}
<div class="article-title-container p-lr">
<h2 class="blog-title title is-5 article-title">
${decodeHtmlEntities(post.title.rendered)}
</h2>
</div>
<div class="article-info p-lr">
<div class="article-author">
<fa-icon
style="margin-right: 0.5rem;"
.icon=${faUser}
></fa-icon>
${post._embedded.author['0'].name}
</div>
<div class="article-date">
<fa-icon
style="margin-right: 0.5rem;"
.icon=${faCalendarAlt}
></fa-icon>
${this.formatDate(post.date)}
</div>
</div>
<div class="article-description p-lr">
${post.excerpt.rendered.length > 250
? stringToHtml(
`${post.excerpt.rendered.substring(0, 200)}...`
)
: stringToHtml(post.excerpt.rendered.substring(0, 200))}
</div>
<div class="article-read-more">
<a class="has-text title is-6">
${this.getStr('SkhemataBlogFeatured.readMore')}
</a>
</div>
</div>
</button>
</div>
`
)}
</div>
`;
}
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()}`;
};
/**
* Implement firstUpdated to perform one-time work after
* the element’s template has been created.
*/
async firstUpdated() {
await super.firstUpdated();
this.getPosts();
}
/**
* Fetch Posts from WP REST API
*/
private 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');
let searchParams = '';
let categoryParams = '';
if (search && search.length > 3) {
searchParams = `&search=${search}`;
}
fetch(
`${this.apiWordpress.url}/categories/?search=featured-article`
)
.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(categories => {
categoryParams = `&categories=${categories[0].id}`;
fetch(
`${this.apiWordpress.url}/posts?_embed${searchParams}${categoryParams}&per_page=${this.postsPerPage}`
)
.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.blogPosts = data.map(SkhemataBlogFeatured.formatCategories);
}
});
});
}
/**
* Format wp categories
*/
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].taxonomy === 'category' : false
);
[formattedData.categories] = filteredCategories;
}
return formattedData;
}
}