hashnode-postcard
Version:
Hashnode blogpost cards for your website
453 lines (452 loc) • 13.1 kB
JavaScript
function f(e) {
return e && e.__esModule && Object.prototype.hasOwnProperty.call(e, "default") ? e.default : e;
}
var l = { exports: {} };
function m(e, r, o) {
const t = o || ".";
let s;
{
let a;
switch (typeof e) {
case "string":
if (e.length < (e[0] === "-" ? 5 : 4))
return e;
s = e, a = Number(
t !== "." ? s.replace(t, ".") : s
);
break;
case "number":
s = String(e), a = e, t !== "." && !Number.isInteger(e) && (s = s.replace(".", t));
break;
default:
return e;
}
if (-1e3 < a && a < 1e3 || isNaN(a) || !isFinite(a))
return s;
}
{
const a = s.lastIndexOf(t);
let i;
a > -1 && (i = s.slice(a), s = s.slice(0, a));
const n = y(s, r || ",");
return i && n.push(i), n.join("");
}
}
function y(e, r) {
let o = (e.length - 1) % 3 + 1;
o === 1 && e[0] === "-" && (o = 4);
const t = [
// holds the string parts
e.slice(0, o)
// grab part before the first separator
];
for (; o < e.length; o += 3)
t.push(r, e.substr(o, 3));
return t;
}
function b(e, r) {
return function(o) {
return m(o, e, r);
};
}
l.exports = m;
l.exports.bindWith = b;
var v = l.exports;
const h = /* @__PURE__ */ f(v), c = {
createTemplate(e) {
const { styles: r } = e;
return `
<style>
${r}
</style>
<div class="card">
<div class="author-area"></div>
<div class="blogposts-wrapper">
<div class="blogposts-area">fetching blog posts...</div>
</div>
</div>
`;
},
createAuthorArea(e) {
const { username: r, photo: o, name: t, tagline: s, followers: a, numFollowers: i } = e;
return `
<div class="author-profile-and-text">
${o ? `<a class="flex" href="https://hashnode.com/@${r}">
<img class="author-profile-photo" src="${o}" alt="${t}"/>
</a>` : ""}
<div class="author-details">
<a href="https://hashnode.com/@${r}">
<div class="author-name">
${t}
</div>
</a>
${s ? `<p class="author-tagline">${s}</p>` : ""}
${i ? a === "false" ? "" : `<p class="author-followers">${h(
i
)} followers</p>` : ""}
</div>
</div>
`;
},
createBlogpostCard(e) {
const { username: r, slug: o, title: t, dateAdded: s, totalReactions: a, brief: i, coverImage: n } = e, u = new Date(s);
return `
<a class="post-link"
href="https://${r}.hashnode.dev/${o}"
target="_blank"
rel="noopener noreferrer"
>
<div class="post-card">
<div class="post-card-text">
<div class="post-title">${t}</div>
<div class="post-date-and-reactions">
<div class="post-date">
<svg class="post-svg" viewBox="0 0 512 512">
<path d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm216 248c0 118.7-96.1 216-216 216-118.7 0-216-96.1-216-216 0-118.7 96.1-216 216-216 118.7 0 216 96.1 216 216zm-148.9 88.3l-81.2-59c-3.1-2.3-4.9-5.9-4.9-9.7V116c0-6.6 5.4-12 12-12h14c6.6 0 12 5.4 12 12v146.3l70.5 51.3c5.4 3.9 6.5 11.4 2.6 16.8l-8.2 11.3c-3.9 5.3-11.4 6.5-16.8 2.6z"></path>
</svg>
${u.toDateString()}
</div>
<div class="post-reactions">
<svg class="post-svg" viewBox="0 0 512 512">
<path d="M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z"></path>
</svg>
${h(a)}
</div>
</div>
${i === "false" ? "" : `<p class="post-brief">${i}</p>`}
</div>
${n === "false" ? "" : `<img
class="post-cover-image"
src="${n}"
alt="${t} cover"
/>`}
</div>
</a>
`;
}
}, d = {
default: {
background: {
primary: "#18191a",
secondary: "#303031"
},
foreground: {
primary: "#ffffff",
secondary: "#bdbdbd"
}
},
devto: {
background: {
primary: "#F5F5F5",
secondary: "#FFFFFF"
},
foreground: {
primary: "#090909",
secondary: "#171717"
}
},
"hashnode-light-mode": {
background: {
primary: "#FFFFFF",
secondary: "#F8FAFC"
},
foreground: {
primary: "#334155",
secondary: "#64748B"
}
},
dracula: {
background: {
primary: "#282A36",
secondary: "#383A59"
},
foreground: {
primary: "#BD93F9",
secondary: "#F8F8F2"
}
},
"nord-light": {
background: {
primary: "#FFFFFF",
secondary: "#F2F4F8"
},
foreground: {
primary: "#5E81AC",
secondary: "#4C566A"
}
},
"nord-dark": {
background: {
primary: "#434C5E",
secondary: "#2E3440"
},
foreground: {
primary: "#ECEFF4",
secondary: "#D8DEE9"
}
}
}, w = Object.keys(d) || [], x = (e) => (e ? w.includes(e) || (console.warn(
`selectedTheme's value of "${e}" doesn't match to any of the existing themes, using default theme for now`
), e = "default") : (console.warn("selectedTheme is undefined, using default theme for now"), e = "default"), `
:host {
--primary-bg: ${d[e].background.primary};
--secondary-bg: ${d[e].background.secondary};
--primary-fg: ${d[e].foreground.primary};
--secondary-fg: ${d[e].foreground.secondary};
}
.flex {
display: flex;
}
.card * {
margin: 0;
padding: 0;
}
.card {
line-height: 1.5;
font-family: Arial;
--max-content-width: 48rem;
}
.card a {
text-decoration: none;
}
.author-area {
background: var(--secondary-bg);
padding: 1rem;
}
.author-profile-and-text{
display: flex;
flex-direction: row-reverse;
justify-content: space-between;
align-items: center;
max-width: var(--max-content-width);
margin: 0 auto;
}
.author-profile-photo{
max-width: 92px;
max-height: 92px;
border-radius: 50%;
}
.author-details{
padding-right: 1rem;
}
.author-name {
font-weight: bold;
font-size: 1.5rem;
color: var(--primary-fg);
margin-bottom: .5rem;
}
.author-tagline,
.author-followers {
color: var(--secondary-fg);
}
.author-followers {
margin-top: .5rem;
}
.blogposts-wrapper {
background: var(--primary-bg);
color: var(--primary-fg);
padding: 1.5rem .5rem;
}
.blogposts-area {
display: flex;
flex-direction: column;
gap: 16px;
}
.blogposts-area > a {
display: flex;
color: var(--primary-fg);
}
.blogposts-area > a:hover .post-card {
background: var(--secondary-bg);
border-radius: 5px;
}
.post-card {
width: 100%;
padding: 1rem .5rem;
}
.post-title {
font-size: 1.5rem;
margin-bottom: .75rem;
}
.post-date-and-reactions{
display: flex;
margin-bottom: .75rem;
}
.post-date,
.post-reactions {
font-size: 0.875rem;
color: var(--secondary-fg);
display: flex;
align-items: center;
margin-right: 1rem;
}
.post-svg{
width: 1rem;
height: 1rem;
margin-right: 0.5rem;
fill: currentColor;
}
.post-brief {
font-size: 1rem;
margin-bottom: .75rem;
color: var(--secondary-fg);
}
.post-cover-image{
width: 100%;
border-radius: 5px;
}
screen and (min-width: 768px){
.blogposts-area > a {
display: flex;
max-width: var(--max-content-width);
margin: 0 auto;
}
.post-card {
display: flex;
align-items: center;
}
.post-card-text {
width: 50%;
margin-right: 10%;
}
.post-cover-image{
width: 40%;
height: min-content;
}
}
`), p = {
getUser({ username: e }) {
return `
query GetUser {
user(username: "${e}") {
name
tagline
numFollowers
photo
numPosts
}
}
`;
},
getUserArticles({ username: e }) {
return `
query GetUserArticles($page: Int!) {
user(username: "${e}") {
publication {
posts(page: $page) {
title,
brief,
totalReactions,
slug,
dateAdded,
coverImage
}
}
}
}
`;
}
}, g = {
async fetcher(e) {
return fetch("https://api.hashnode.com/", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(e)
}).then((r) => r.json());
}
};
class F extends HTMLElement {
constructor() {
super(), this._shadowRoot = this.attachShadow({ mode: "open" });
const r = document.createElement("template");
r.innerHTML = c.createTemplate({
styles: x(this.dataset.theme)
}), this._shadowRoot.appendChild(r.content.cloneNode(!0)), this.currentPage = 0, this.postsFetched = [], this._GET_USER = p.getUser({
username: this.dataset.username.toLowerCase()
}), this._GET_USER_ARTICLES = p.getUserArticles({
username: this.dataset.username.toLowerCase()
});
}
async fetchUser(r, o = {}) {
return g.fetcher({
query: r,
variables: o
}).then(({ data: t }) => {
var s;
if (((s = t == null ? void 0 : t.user) == null ? void 0 : s.name) === null) {
this.renderUser(this.dataset.username + " doesn't exist");
return;
}
this.fetchedUser = t.user, this.totalPosts = t.user.numPosts, this.renderUser(this.fetchedUser), this.fetchPosts(this._GET_USER_ARTICLES, { page: this.currentPage });
}).catch((t) => {
console.log(t);
});
}
renderUser(r) {
const o = this._shadowRoot.querySelector(".author-area");
o.innerHTML = c.createAuthorArea({
username: this.dataset.username,
...r
});
}
async fetchPosts(r, o = {}) {
return g.fetcher({ query: r, variables: o }).then(({ data: t }) => {
var a, i;
const s = ((i = (a = t == null ? void 0 : t.user) == null ? void 0 : a.publication) == null ? void 0 : i.posts) || [];
this.posts = s, this.postsFetched = [...this.postsFetched, ...s], this.renderPosts(this.posts);
});
}
renderPosts(r) {
const o = this._shadowRoot.querySelector(".blogposts-wrapper"), t = this._shadowRoot.querySelector(".blogposts-area");
if (this.setHeight(), this.currentPage === 0 && (t.innerHTML = ""), !r.length && this.currentPage === 0) {
t.innerHTML = "no posts found.";
return;
}
const s = this._shadowRoot.querySelector(".blogposts-area-observer");
if (this.currentPage === 0 && this.totalPosts <= 6 && s && s.remove(), r.forEach((a) => {
t.innerHTML += c.createBlogpostCard({
username: this.dataset.username,
...a
});
}), this.totalPosts > this.postsFetched.length) {
if (!s) {
const a = document.createElement("div");
a.classList.add("blogposts-area-observer"), o.appendChild(a), new IntersectionObserver(
(n) => {
n[0].isIntersecting && (this.currentPage += 1, a.innerHTML = "fetching more posts...", this.fetchPosts(this._GET_USER_ARTICLES, {
page: this.currentPage
}));
},
{
threshold: 1
}
).observe(a);
}
} else
s.remove();
}
render() {
this.setWidth(), this.fetchUser(this._GET_USER);
}
connectedCallback() {
this.render();
}
setWidth() {
this.dataset.width && (this.style.display = "inline-block", this.style.width = this.dataset.width);
}
setHeight() {
this.dataset.height && (this._shadowRoot.querySelector(".blogposts-area").style.overflowY = "scroll", this._shadowRoot.querySelector(".blogposts-area").style.maxHeight = this.dataset.height);
}
static get observedAttributes() {
return ["data-width"];
}
attributeChangedCallback(r, o, t) {
r == "data-width" && o != t && (this[r] = t);
}
}
customElements.define("hashnode-postcard", F);
export {
F as HashnodePostcard
};