Implement mastodon comments
This commit is contained in:
parent
aa60dc5e32
commit
69d7cd1da6
@ -13,6 +13,8 @@ module.exports = {
|
||||
copyrightNotice: "© Nathan Upchurch 2022 - 2024",
|
||||
defaultPostImageURL: "/img/vasilina-sirotina-1NMPvajSt9Q-unsplash_copy.avif",
|
||||
defaultPostImageAlt: "The default post image: a close picture of the dark green leaves of a plant.",
|
||||
mastodonHost: "lounge.town",
|
||||
mastodonUser: "nathanu",
|
||||
postlistHeaderText: "Latest Posts",
|
||||
socialLinks: [
|
||||
{
|
||||
|
@ -1,12 +1,13 @@
|
||||
<footer>
|
||||
{% if metadata.copyrightNotice %}<p>{{ metadata.copyrightNotice }}</p>{% endif %}<br>
|
||||
<p>{% if metadata.copyrightNotice %}<span class="copyright-notice">{{ metadata.copyrightNotice }}</span>{% endif %}
|
||||
|
||||
{% if metadata.webrings %}
|
||||
{% for webring in metadata.webrings %}
|
||||
<span class="webring">
|
||||
{% if webring.previousURL %}<a href="{{ webring.previousURL }}">←</a>{% endif %}
|
||||
{% if webring.ringURL %}<a href="{{ webring.ringURL }}">{{ webring.name }}</a>{% endif %}
|
||||
{% if webring.nextURL %}<a href="{{ webring.nextURL }}">→</a>{% endif %}
|
||||
<br>
|
||||
</span>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endif %}</p>
|
||||
</footer>
|
||||
|
@ -3,6 +3,7 @@ layout: layouts/base.njk
|
||||
---
|
||||
{# Only include the syntax highlighter CSS on blog posts #}
|
||||
{%- css %}{% include "public/css/code.css" %}{% endcss %}
|
||||
<article class="post">
|
||||
<h1>{{ title }}</h1>
|
||||
|
||||
<div class="post-metadata">
|
||||
@ -28,5 +29,7 @@ layout: layouts/base.njk
|
||||
</div>
|
||||
|
||||
{{ content | safe }}
|
||||
|
||||
</article>
|
||||
{% include "mastodonComments.njk" %}
|
||||
<h2>Read Next</h2>
|
||||
{% include "nextLast.njk" %}
|
||||
|
94
_includes/mastodonComments.njk
Normal file
94
_includes/mastodonComments.njk
Normal file
@ -0,0 +1,94 @@
|
||||
{% if mastodon_id %}
|
||||
<section id="comment-section">
|
||||
<h2>Comments</h2>
|
||||
<div class="comment-ingress"></div>
|
||||
<div id="comments" data-id="{{ mastodon_id }}">
|
||||
<p>Loading comments...</p>
|
||||
</div>
|
||||
<div class="continue-discussion">
|
||||
<a class="big-link" href="https://{{ metadata.mastodonHost }}/@{{ metadata.mastodonUser }}/{{ mastodon_id }}">Comment by replying to this post on Mastodon »</a>
|
||||
</section>
|
||||
|
||||
<template id="comment-template">
|
||||
<wc-comment
|
||||
author_name=""
|
||||
author_url=""
|
||||
avatar_url=""
|
||||
comment_content=""
|
||||
publish_date=""
|
||||
sharp_corner="">
|
||||
</wc-comment>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const monthMap = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
|
||||
|
||||
const dateSuffixAdder = (date) => {
|
||||
if (date > 9 && date < 20) {
|
||||
return "th";
|
||||
} else {
|
||||
let dateString = date < 10 ? "0" + date : "" + date;
|
||||
if (dateString[1] < 4 && dateString[1] > 0) {
|
||||
return dateString[1] == 1 ? "st" :
|
||||
dateString[1] == 2 ? "nd" :
|
||||
dateString[1] == 3 ? "rd" : null;
|
||||
} else {
|
||||
return "th"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const timeFormatter = (hours, minutes) => {
|
||||
return `${hours < 12 ? hours : hours - 12}:${minutes < 10 ? "0" : ""}${minutes} ${hours < 12 ? "AM" : "PM"}`
|
||||
}
|
||||
|
||||
const renderComment = (comment, target, parentIdm) => {
|
||||
const node = document
|
||||
.querySelector("template#comment-template")
|
||||
.content.cloneNode(true);
|
||||
|
||||
const dateObj = new Date(comment.created_at);
|
||||
|
||||
const dateTime = `${dateObj.getDate()}${dateSuffixAdder(dateObj.getDate())} of ${monthMap[dateObj.getMonth()]}, ${dateObj.getFullYear()}, at ${timeFormatter(dateObj.getHours(), dateObj.getMinutes())}`;
|
||||
|
||||
node.querySelector("wc-comment").setAttribute("author_name", comment.account.display_name);
|
||||
node.querySelector("wc-comment").setAttribute("author_url",
|
||||
`${comment.account.acct == "{{ metadata.mastodonUser }}" ? "https://{{ metadata.mastodonHost }}/@{{ metadata.mastodonUser }}" : comment.account.acct}`);
|
||||
node.querySelector("wc-comment").setAttribute("avatar_url", comment.account.avatar_static);
|
||||
node.querySelector("wc-comment").setAttribute("comment_content", comment.content);
|
||||
node.querySelector("wc-comment").setAttribute("publish_date", dateTime);
|
||||
|
||||
target.appendChild(node);
|
||||
}
|
||||
|
||||
async function renderComments() {
|
||||
const commentsNode = document.querySelector("#comments");
|
||||
|
||||
const mastodonPostId = commentsNode.dataset?.id;
|
||||
|
||||
if (!mastodonPostId) {
|
||||
return;
|
||||
}
|
||||
|
||||
commentsNode.innerHTML = "";
|
||||
|
||||
const originalPost = await fetch(
|
||||
`https://{{ metadata.mastodonHost }}/api/v1/statuses/${mastodonPostId}`
|
||||
);
|
||||
const originalData = await originalPost.json();
|
||||
renderComment(originalData, commentsNode, null);
|
||||
|
||||
const response = await fetch(
|
||||
`https://{{ metadata.mastodonHost }}/api/v1/statuses/${mastodonPostId}/context`
|
||||
);
|
||||
const data = await response.json();
|
||||
const comments = data.descendants;
|
||||
|
||||
comments.forEach((comment) => {
|
||||
renderComment(comment, commentsNode, mastodonPostId);
|
||||
});
|
||||
}
|
||||
|
||||
renderComments();
|
||||
</script>
|
||||
{% endif %}
|
@ -4,6 +4,7 @@
|
||||
<link rel="icon" type="image/x-icon" href="/img/logo_favicon.svg">
|
||||
<meta name="description" content="{{ description or metadata.description }}">
|
||||
<meta name="robots" content="noai, noimageai">
|
||||
<meta name="generator" content="{{ eleventy.generator }}">
|
||||
<link rel="alternate" href="/feed/feed.xml" type="application/atom+xml" title="{{ metadata.title }}">
|
||||
<link rel="alternate" href="/feed/feed.json" type="application/json" title="{{ metadata.title }}">
|
||||
<meta name="generator" content="{{ eleventy.generator }}">
|
||||
<script type="module" src="/js/main.js"></script>
|
||||
|
@ -7,6 +7,7 @@ tags:
|
||||
synopsis: An ASCII cow postulates on the state of science education in the modern world.
|
||||
imageURL: /img/cowsayOfTheDay.avif
|
||||
imageAlt: An ASCII cow with a thought bubble containing the word wut
|
||||
mastodon_id: "111688829907363670"
|
||||
---
|
||||
As a big-old nerd, I spend a lot of time in the terminal on my computer. When you spend a lot of time somewhere, you want it to be comfortable. As a part of making my terminal more homey, I've set it up to give me a random quote each time I start a new session, delivered, of course, by a cow. Here's today's cowsay of the day:
|
||||
|
||||
|
@ -8,6 +8,7 @@ tags:
|
||||
synopsis: Tools like cooked.wiki let us strip away the cruft from online recipes. Is this necessarily a good thing?
|
||||
imageURL: /img/pexels-brigitte-tohm-378008_compressed.webp
|
||||
imageAlt: An oddly rectangular waffle covered in raspberries. It actually looks quite dry and not very nice. Hopefully there's some syrup on the side!
|
||||
mastodon_id: "111812478768090324"
|
||||
---
|
||||
So, about this [cooked.wiki](https://cooked.wiki) thing, believe me when I say I take my fair share in our collective frustration as I find myself skimming through a hugoesque tome on Brayden and Braxlynne’s wiggly teeth in order to reach the ingredients for “Keighleigh’s Extra Easy No-Bake Ten Minute Palmiers (*So Delicious You’ll Snort the Crumbs!*),” but I must admit that the endless complaining about it puts me out a bit. Here’s the thing; as someone who writes for his own personal blog, who plans to someday publish a recipe or two, *the waffling is the point.*
|
||||
|
||||
|
@ -8,6 +8,7 @@ tags:
|
||||
synopsis: My first KDE contribution! Two new high-contrast KMines themes that will arrive with Plasma 6.
|
||||
imageURL: /img/kmines_dark.webp
|
||||
imageAlt: A screenshot of the KMines game window showing a new dark theme.
|
||||
mastodon_id: "111794936518292495"
|
||||
---
|
||||
## Why KMines?
|
||||
Minesweeper is a tragically underrated puzzle game. While I recall examining the mysterious array of gray squares as a child, it wasn't until adulthood that I took the time to learn the rules of the game. Despite my late start, however, I still count minesweeper as a classic. These days, good minesweeper clones are hard to come by. I settled on GNOME's [Mines](https://wiki.gnome.org/Apps/Mines) for a while, but as the look of GTK applications on my QT-based [KDE Plasma Desktop](https://kde.org/plasma-desktop/) sets my teeth on edge, I ditched it for [KMines](https://apps.kde.org/kmines/) in short order. While I enjoyed the game, I found the themes shipped with KMines a bit dated, so I thought I'd make my own.
|
||||
|
@ -7,6 +7,7 @@ tags:
|
||||
synopsis: A conversation with a colleague caused me to consider the consequences of online-only tooling.
|
||||
imageURL: /img/kenny-eliason-uq5RMAZdZG4-unsplash.webp
|
||||
imageAlt: A server rack in the dark with colorful cables draped between the ports of servers and switches.
|
||||
mastodon_id: "111268603361637013"
|
||||
---
|
||||
As part of a project investigating a potential new piece of software, I've been speaking with colleagues and contractors to determine which features they rely on to do their day-to-day tasks, as well as discover any wish-list items for a new platform. In one of these discussions with a colleague, we had covered her relatively simple use case, and moved on to discuss potential features that might be useful. At this juncture, she mentioned, somewhat apologetically, that should a particular workflow be translated to a new platform, it was important to be able to access data and documents offline.
|
||||
|
||||
|
@ -8,6 +8,7 @@ tags:
|
||||
synopsis: Learning about patience through an incense-making miscalculation.
|
||||
imageURL: /img/dragons_blood_incense_copy.avif
|
||||
imageAlt: A small piece of a coreless, Japanese-style incense stick burning in a black cast-iron burner.
|
||||
mastodon_id: "111732713202024407"
|
||||
---
|
||||
Some time ago, maybe a year or so, I extruded a batch of incense sticks from some ingredients I thought might go well together: sandalwood, cinnamon, dragon's blood resin, a touch of Hojari frankincense for acidity, and some tonka bean for sweetness, if I recall correctly. After leaving the sticks to dry overnight, I was disappointed to see that they didn't stay lit; the stick would shrink behind the ember, and it would fizzle out in short order. Even worse, the little scent I was able to detect during the short burn was terrible: acrid and smoky. Dejected, I put the sticks away, returning to attempt to burn a small fragment every few days or so before I lost interest entirely. A few months later, the tube of crooked red incense sticks caught my eye, and I once again attempted to burn a stick. To my surprise, it stayed lit throughout the entire burn. The fragrance had transformed also, from leafy-campfire to a simple, warm, slightly sweet, and medicinal fragrance. While this was enough of an improvement to encourage me to light one every now and then, I remained disappointed that the fragrance was so far from what I'd hoped to achieve. After half-heartedly burning each stick in the little plastic tube that housed them over a period of weeks, the tube disappeared into a basket on the shelf beneath my coffee table amidst a mess of bundled cables and game-controllers, never to be seen again – until just a few days ago.
|
||||
|
||||
|
@ -8,6 +8,7 @@ tags:
|
||||
- Underrated Apps
|
||||
imageURL: /img/qownnotes.webp
|
||||
imageAlt: A screenshot of QOwnNotes showing a note subfolder panel beside markdown editor and preview panels.
|
||||
mastodon_id: "110862579682916657"
|
||||
---
|
||||
[](/img/qownnotes.webp)
|
||||
|
||||
|
@ -8,6 +8,7 @@ tags:
|
||||
synopsis: Breaking down the alternative-diet restaurant experience to offer some perspective and advice to foodservice professionals and proprietors.
|
||||
imageURL: /img/k8-sWEpcc0Rm0U-unsplash.webp
|
||||
imageAlt: An overhead view of a restaurant interior showing guests sitting at tables with white tablecloths, eating pastries.
|
||||
mastodon_id: "111173763912666764"
|
||||
---
|
||||
I've been a vegan for close to a decade. From washing dishes, slinging cocktails, and pouring latte art, to recipe development, hiring, compliance, and multi-location operations, I've also been around the block a few times when it comes to foodservice. So when I tell you that the vast majority of foodservice establishments of any stripe are utter and complete complete nightmares for people with alternative diets, I hope you'll take me at my word.
|
||||
|
||||
|
@ -13,5 +13,5 @@ numberOfLatestPostsToShow: 5
|
||||
|
||||
{% set morePosts = postsCount - numberOfLatestPostsToShow %}
|
||||
{% if morePosts > 0 %}
|
||||
<p>See {{ morePosts }} more post{% if morePosts != 1 %}s{% endif %} in <a href="/blog/">the blog</a>.</p>
|
||||
<a class="big-link" href="/blog/">See {{ morePosts }} more post{% if morePosts != 1 %}s{% endif %} in the blog »</a>
|
||||
{% endif %}
|
||||
|
@ -78,6 +78,8 @@ module.exports = function(eleventyConfig) {
|
||||
eleventyConfig.addPassthroughCopy({ 'public/xsl/*': "/xsl/" });
|
||||
eleventyConfig.addPassthroughCopy({ 'public/img/*': "/img/" });
|
||||
eleventyConfig.addPassthroughCopy({ 'public/robots.txt': "/" });
|
||||
eleventyConfig.addPassthroughCopy({ 'public/js/*': "/js/" });
|
||||
eleventyConfig.addPassthroughCopy({ 'public/js/webComponents/*': "/js/webComponents" });
|
||||
// Copying so that basic.xsl can use it
|
||||
eleventyConfig.addPassthroughCopy({ 'public/css/index.css': "/css/index.css" });
|
||||
eleventyConfig.addPassthroughCopy({ 'public/css/webfonts/*': "/css/webfonts/" });
|
||||
|
@ -1,4 +1,4 @@
|
||||
main > p:not(.nodropcap):first-of-type:first-letter {
|
||||
main > article > p:not(.nodropcap):first-of-type:first-letter {
|
||||
float: left;
|
||||
font-size: 4rem;
|
||||
padding: .5rem .5rem .5rem .5rem;
|
||||
|
@ -24,7 +24,7 @@
|
||||
--icon-filter: none;
|
||||
|
||||
/* Corners */
|
||||
--corner-radius: .3rem;
|
||||
--border-radius: .3rem;
|
||||
|
||||
/* Space & Size */
|
||||
--syntax-tab-size: 2;
|
||||
@ -32,6 +32,7 @@
|
||||
--single-gap: 1rem;
|
||||
--double-gap: 2rem;
|
||||
--triple-gap: 3rem;
|
||||
--quad-gap: 4rem;
|
||||
|
||||
/* Transitions */
|
||||
--transition-normal: all .3s;
|
||||
@ -49,11 +50,28 @@
|
||||
--weight-heavy: 500;
|
||||
--weight-normal: 300;
|
||||
|
||||
/* Links */
|
||||
--link-decoration-thickness: .1rem;
|
||||
|
||||
/* Borders */
|
||||
--border-nav: 1px solid var(--text-color);
|
||||
--border-nav-currentpage: 20px solid var(--contrast-color);
|
||||
--border-nav-hover: 20px solid var(--text-color);
|
||||
--border-thin: 1px solid var(--color-gray-20);
|
||||
|
||||
/* Shadow */
|
||||
--box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
|
||||
|
||||
/* Components */
|
||||
--wc-card-background-color: var(--card-color);
|
||||
--wc-card-border-radius: var(--border-radius);
|
||||
--wc-card-box-shadow: var(--box-shadow);
|
||||
--wc-link-color: var(--text-color);
|
||||
--wc-link-decoration-color: var(--contrast-color);
|
||||
--wc-link-decoration-thickness: var(--link-decoration-thickness);
|
||||
--wc-comment-text-margin: auto auto auto 4rem;
|
||||
--wc-profile-pic-size: 3rem;
|
||||
--wc-profile-pic-border-radius: 10rem;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
@ -64,7 +82,7 @@
|
||||
--contrast-color: #04c49e;
|
||||
|
||||
/* --text-color is assigned to --color-gray-_ above */
|
||||
--text-color-link: var(--contrast-color);
|
||||
--text-color-link: var(--text-color);
|
||||
|
||||
--background-color: #15202b;
|
||||
--logo-filter: none;
|
||||
@ -96,6 +114,7 @@ body {
|
||||
background-color: var(--background-color);
|
||||
color: var(--text-color);
|
||||
font-family: var(--font-family);
|
||||
font-size: 13px;
|
||||
font-variant-Ligatures: normal;
|
||||
font-weight: var(--weight-normal);
|
||||
margin: 0 auto;
|
||||
@ -109,7 +128,7 @@ body {
|
||||
}
|
||||
a {
|
||||
text-decoration-color: var(--contrast-color);
|
||||
text-decoration-thickness: .1rem;
|
||||
text-decoration-thickness: var(--link-decoration-thickness);
|
||||
transition: var(--transition-normal);
|
||||
}
|
||||
/* https://www.a11yproject.com/posts/how-to-hide-content/ */
|
||||
@ -123,8 +142,18 @@ a {
|
||||
width: 1px;
|
||||
}
|
||||
footer {
|
||||
margin-top: var(--triple-gap);
|
||||
padding: var(--single-gap);
|
||||
border-top: var(--border-thin);
|
||||
}
|
||||
footer .copyright-notice {
|
||||
padding-right: var(--single-gap);
|
||||
}
|
||||
footer .webring {
|
||||
display: inline-block;
|
||||
padding-right: var(--single-gap);
|
||||
}
|
||||
footer p {
|
||||
font-size: var(--font-s);
|
||||
}
|
||||
h1, h2, h3 {
|
||||
color: var(--text-color);
|
||||
@ -140,7 +169,7 @@ h1 {
|
||||
h2 {
|
||||
font-size: var(--font-xl);
|
||||
font-weight: var(--weight-extraheavy);
|
||||
margin-top: var(--double-gap);
|
||||
margin: var(--quad-gap) auto 0 auto;
|
||||
}
|
||||
h3 {
|
||||
font-size: var(--font-l);
|
||||
@ -167,7 +196,7 @@ p, li {
|
||||
}
|
||||
figure {
|
||||
margin: 0;
|
||||
padding: var(--single-gap) 0 var(--single-gap) 0;
|
||||
padding: var(--single-gap) 0 0 0;
|
||||
width: 100%;
|
||||
}
|
||||
figure > a > img {
|
||||
@ -182,6 +211,17 @@ figcaption {
|
||||
.page-block {
|
||||
margin-bottom: var(--triple-gap);
|
||||
}
|
||||
.big-link {
|
||||
width: 100%;
|
||||
padding: var(--half-gap);
|
||||
border: var(--border-nav);
|
||||
border-radius: var(--border-radius);
|
||||
margin: var(--single-gap) auto var(--single-gap) auto;
|
||||
transition: var(--transition-normal);
|
||||
}
|
||||
.big-link:hover {
|
||||
border-color: var(--contrast-color);
|
||||
}
|
||||
a[href]:not(.icon-button) {
|
||||
color: var(--text-color-link);
|
||||
}
|
||||
@ -194,9 +234,8 @@ a[href]:active:not(.icon-button) {
|
||||
}
|
||||
.links-nextprev {
|
||||
list-style: none;
|
||||
border-top: var(--border-thin);
|
||||
padding: var(--triple-gap) 0 var(--single-gap) 0;
|
||||
margin-top: var(--triple-gap);
|
||||
padding: 0 0 var(--single-gap) 0;
|
||||
margin-top: var(--single-gap);
|
||||
}
|
||||
|
||||
table {
|
||||
@ -207,6 +246,27 @@ table th {
|
||||
padding-right: 1em;
|
||||
}
|
||||
|
||||
/* Comments */
|
||||
.comment-ingress {
|
||||
margin-bottom: var(--double-gap);
|
||||
}
|
||||
#comment-section h2 {
|
||||
margin: var(--quad-gap) auto 0 auto;
|
||||
}
|
||||
wc-comment::part(author-link) {
|
||||
font-size: var(--font-n);
|
||||
font-weight: var(--weight-extraheavy);
|
||||
text-decoration: none;
|
||||
}
|
||||
wc-comment::part(main) {
|
||||
margin-bottom: var(--double-gap);
|
||||
}
|
||||
wc-comment::part(publish-date) {
|
||||
font-weight: var(--weight-heavy);
|
||||
font-size: var(--font-s);
|
||||
margin-top: -.25rem;
|
||||
}
|
||||
|
||||
/* Code Fences */
|
||||
pre,
|
||||
code {
|
||||
@ -233,7 +293,6 @@ code {
|
||||
/* Header */
|
||||
header {
|
||||
align-items: end;
|
||||
border-bottom: var(--border-thin);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1em .5em;
|
||||
@ -331,17 +390,17 @@ nav ul {
|
||||
.postlist-item {
|
||||
align-items: flex-start;
|
||||
background-color: var(--card-color);
|
||||
border-radius: var(--corner-radius);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: flex-start;
|
||||
margin-bottom: 1em;
|
||||
margin-bottom: var(--double-gap);
|
||||
padding: var(--single-gap) 1.1rem var(--single-gap) 1.1rem;
|
||||
width: 100%;
|
||||
}
|
||||
.post-image-container {
|
||||
border-radius: var(--corner-radius);
|
||||
border-radius: var(--border-radius);
|
||||
margin-right: var(--single-gap);
|
||||
max-height: 15rem;
|
||||
overflow: hidden;
|
||||
@ -421,9 +480,14 @@ a.post-tag:hover {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
/* Article / Post */
|
||||
.post h2 {
|
||||
font-size: var(--font-l);
|
||||
}
|
||||
|
||||
/* Post Metadata */
|
||||
.post-metadata {
|
||||
margin-bottom: var(--triple-gap);
|
||||
margin-bottom: var(--double-gap);
|
||||
margin-top: var(--single-gap);
|
||||
padding: 0 0 0 .4rem;
|
||||
}
|
||||
@ -481,7 +545,7 @@ h2 + .header-anchor {
|
||||
margin-bottom: var(--single-gap);
|
||||
}
|
||||
h3, .post-copy a h3 {
|
||||
font-size: 1rem;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
@ -494,6 +558,11 @@ h2 + .header-anchor {
|
||||
margin-top: var(--single-gap);
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer .webring {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
.nav {
|
||||
flex-flow: row wrap;
|
||||
|
4
public/js/main.js
Normal file
4
public/js/main.js
Normal file
@ -0,0 +1,4 @@
|
||||
import './webComponents/card.js';
|
||||
import './webComponents/profilePic.js';
|
||||
import './webComponents/speechBubble.js';
|
||||
import './webComponents/comment.js';
|
33
public/js/webComponents/card.js
Normal file
33
public/js/webComponents/card.js
Normal file
@ -0,0 +1,33 @@
|
||||
const template = document.createElement('template');
|
||||
|
||||
template.innerHTML = `
|
||||
<style>
|
||||
#card {
|
||||
align-items: flex-start;
|
||||
background-color: var(--wc-card-background-color);
|
||||
border-radius: var(--wc-card-border-radius);
|
||||
box-shadow: var(--wc-card-box-shadow);
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: flex-start;
|
||||
margin-bottom: 1em;
|
||||
padding: var(--single-gap) 1.1rem var(--single-gap) 1.1rem;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="card" part="main">
|
||||
<slot></slot>
|
||||
</div>
|
||||
`
|
||||
|
||||
class card extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this._shadowRoot = this.attachShadow({ 'mode': 'open' });
|
||||
this._shadowRoot.appendChild(template.content.cloneNode(true));
|
||||
}
|
||||
}
|
||||
|
||||
window.customElements.define('wc-card', card);
|
76
public/js/webComponents/comment.js
Normal file
76
public/js/webComponents/comment.js
Normal file
@ -0,0 +1,76 @@
|
||||
const template = document.createElement('template');
|
||||
|
||||
template.innerHTML = `
|
||||
<style>
|
||||
a {
|
||||
color: var(--wc-link-color);
|
||||
text-decoration-color: var(--wc-link-decoration-color);
|
||||
text-decoration-thickness: var(--wc-link-decoration-thickness);
|
||||
}
|
||||
#comment {
|
||||
margin: var(--wc-comment-text-margin);
|
||||
}
|
||||
#comment p {
|
||||
margin: 0 auto 0 auto;
|
||||
}
|
||||
#meta {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
}
|
||||
#meta-text {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
width: 100%;
|
||||
}
|
||||
#meta-text p {
|
||||
margin: 0 1rem 0 1rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<article id="commentContainer" class="blog-comment" part="main">
|
||||
<div id="meta" part="meta">
|
||||
<div>
|
||||
<wc-profile-pic url="" />
|
||||
</div>
|
||||
<div id="meta-text" part="meta-text">
|
||||
<p id="author" part="author">
|
||||
<a id="author-link" part="author-link"></a><span> says:</span>
|
||||
</p>
|
||||
<p id="publish-date" part="publish-date"></p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="comment" part="content">
|
||||
</div>
|
||||
</article>
|
||||
`
|
||||
|
||||
class comment extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this._shadowRoot = this.attachShadow({ 'mode': 'open' });
|
||||
this._shadowRoot.appendChild(template.content.cloneNode(true));
|
||||
this.$comment = this._shadowRoot.querySelector('#commentContainer');
|
||||
}
|
||||
|
||||
static get observedAttributes() {
|
||||
return ['author_name', 'author_url', 'avatar_url', 'comment_content', 'publish_date'];
|
||||
}
|
||||
|
||||
attributeChangedCallback(name, oldVal, newVal) {
|
||||
if (oldVal != newVal) {
|
||||
this[name] = newVal;
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
this.$comment.querySelector('#author-link').innerHTML = this.author_name;
|
||||
this.$comment.querySelector('#author-link').href = this.author_url;
|
||||
this.$comment.querySelector('wc-profile-pic').setAttribute('url', this.avatar_url)
|
||||
this.$comment.querySelector('#comment').innerHTML = this.comment_content;
|
||||
this.$comment.querySelector('#publish-date').innerHTML = this.publish_date;
|
||||
}
|
||||
}
|
||||
|
||||
window.customElements.define('wc-comment', comment);
|
40
public/js/webComponents/profilePic.js
Normal file
40
public/js/webComponents/profilePic.js
Normal file
@ -0,0 +1,40 @@
|
||||
const template = document.createElement('template');
|
||||
|
||||
template.innerHTML = `
|
||||
<style>
|
||||
#profilePic {
|
||||
border-radius: var(--wc-profile-pic-border-radius);
|
||||
width: var(--wc-profile-pic-size);
|
||||
height: var(--wc-profile-pic-size);
|
||||
}
|
||||
</style>
|
||||
|
||||
<img src="" id="profilePic"/>
|
||||
`
|
||||
|
||||
class profilePic extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this._shadowRoot = this.attachShadow({ 'mode': 'open' });
|
||||
this._shadowRoot.appendChild(template.content.cloneNode(true));
|
||||
this.$profilePic = this._shadowRoot.querySelector('#profilePic');
|
||||
}
|
||||
|
||||
static get observedAttributes() {
|
||||
return ['url'];
|
||||
}
|
||||
|
||||
attributeChangedCallback(name, oldVal, newVal) {
|
||||
if (oldVal != newVal) {
|
||||
this[name] = newVal;
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
this.url ? this.$profilePic.src = this.url : null;
|
||||
}
|
||||
}
|
||||
|
||||
window.customElements.define('wc-profile-pic', profilePic);
|
Loading…
x
Reference in New Issue
Block a user