Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/CaiJimmy/hugo-theme-stack.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/assets
diff options
context:
space:
mode:
authorJimmy Cai <github@jimmycai.com>2022-10-29 18:14:19 +0300
committerGitHub <noreply@github.com>2022-10-29 18:14:19 +0300
commitb03390d237427a2b9cfee55949cb1da62f1a6856 (patch)
treefd7da57cfcebc07d16483f3e9c3d71aee3562138 /assets
parent8a597a5c9c861d2bce22a5e06c7667cd9c8e263a (diff)
Revert "Merge branch 'canary' into master" (#712)
Revert "Merge branch 'canary' into master (#711)" This reverts commit 8a597a5c9c861d2bce22a5e06c7667cd9c8e263a.
Diffstat (limited to 'assets')
-rw-r--r--assets/scss/partials/article.scss1
-rw-r--r--assets/scss/partials/highlight/dark.scss2
-rw-r--r--assets/scss/partials/layout/article.scss48
-rw-r--r--assets/scss/partials/layout/search.scss4
-rw-r--r--assets/scss/partials/sidebar.scss6
-rw-r--r--assets/ts/codeblock.ts28
-rw-r--r--assets/ts/color.ts63
-rw-r--r--assets/ts/gallery.ts240
-rw-r--r--assets/ts/main.ts69
9 files changed, 322 insertions, 139 deletions
diff --git a/assets/scss/partials/article.scss b/assets/scss/partials/article.scss
index a041e9b..f085ff0 100644
--- a/assets/scss/partials/article.scss
+++ b/assets/scss/partials/article.scss
@@ -192,6 +192,7 @@
.article-preview {
font-size: 1.4rem;
color: var(--card-text-color-tertiary);
+ margin-top: 10px;
line-height: 1.5;
}
}
diff --git a/assets/scss/partials/highlight/dark.scss b/assets/scss/partials/highlight/dark.scss
index 3ea6c56..0d3f330 100644
--- a/assets/scss/partials/highlight/dark.scss
+++ b/assets/scss/partials/highlight/dark.scss
@@ -11,4 +11,4 @@ $text-color: $color;
$name-color: #a6e22e;
$literal-color: #e6db74;
-@import "common.scss"; \ No newline at end of file
+@import "common.scss";
diff --git a/assets/scss/partials/layout/article.scss b/assets/scss/partials/layout/article.scss
index ba06c11..97c7e77 100644
--- a/assets/scss/partials/layout/article.scss
+++ b/assets/scss/partials/layout/article.scss
@@ -286,12 +286,10 @@
line-height: 1.428571429;
word-break: break-all;
padding: var(--card-padding);
-
// keep Codeblocks LTR
[dir="rtl"] & {
direction: ltr;
}
-
code {
color: unset;
border: none;
@@ -305,11 +303,15 @@
padding: var(--card-padding);
position: relative;
+ &:hover {
+ .copyCodeButton {
+ opacity: 1;
+ }
+ }
// keep Codeblocks LTR
[dir="rtl"] & {
direction: ltr;
}
-
pre {
margin: initial;
padding: 0;
@@ -318,30 +320,20 @@
}
}
- .codeblock {
- header {
- background-color: var(--card-background-selected);
- padding: 5px var(--card-padding);
- display: flex;
- justify-content: space-between;
- box-shadow: var(--shadow-l1);
-
- span {
- text-transform: uppercase;
- font-weight: bold;
- color: var(--card-text-color-secondary);
- }
- }
-
- .codeblock-copy {
- cursor: pointer;
- background-color: transparent;
- border: none;
- padding: 8px 16px;
- color: var(--card-text-color-secondary);
- font-size: 14px;
- font-weight: bold;
- }
+ .copyCodeButton {
+ position: absolute;
+ top: calc(var(--card-padding));
+ right: calc(var(--card-padding));
+ background: var(--card-background);
+ border: none;
+ box-shadow: var(--shadow-l2);
+ border-radius: var(--tag-border-radius);
+ padding: 8px 16px;
+ color: var(--card-text-color-main);
+ cursor: pointer;
+ font-size: 14px;
+ opacity: 0;
+ transition: opacity 0.3s ease;
}
.table-wrapper {
@@ -410,7 +402,7 @@
/// Negative margins
blockquote,
figure,
- .codeblock,
+ .highlight,
pre,
.gallery,
.video-wrapper,
diff --git a/assets/scss/partials/layout/search.scss b/assets/scss/partials/layout/search.scss
index cbf15b7..89cdcef 100644
--- a/assets/scss/partials/layout/search.scss
+++ b/assets/scss/partials/layout/search.scss
@@ -31,7 +31,6 @@
input {
padding: 40px 20px 20px;
- padding-inline-end: var(--button-size);
border-radius: var(--card-border-radius);
background-color: var(--card-background);
box-shadow: var(--shadow-l1);
@@ -79,4 +78,5 @@
height: 20px;
}
}
-}
+
+} \ No newline at end of file
diff --git a/assets/scss/partials/sidebar.scss b/assets/scss/partials/sidebar.scss
index 75332a7..95310ca 100644
--- a/assets/scss/partials/sidebar.scss
+++ b/assets/scss/partials/sidebar.scss
@@ -11,6 +11,7 @@
flex-direction: column;
flex-shrink: 0;
align-self: stretch;
+ gap: var(--sidebar-element-separation);
max-width: none;
width: 100%;
position: relative;
@@ -64,11 +65,6 @@
}
}
}
-
- .social-menu,
- .menu {
- margin-top: var(--sidebar-element-separation);
- }
}
.right-sidebar {
diff --git a/assets/ts/codeblock.ts b/assets/ts/codeblock.ts
deleted file mode 100644
index 08c3328..0000000
--- a/assets/ts/codeblock.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * Copy button for code blocks
-*/
-export default () => {
- const copyButtons = document.querySelectorAll('.codeblock-copy');
- copyButtons.forEach(button => {
- const codeblockID = button.getAttribute('data-id'),
- copyText = button.textContent,
- copiedText = button.getAttribute('data-copied-text');
- if (!codeblockID) return;
- button.addEventListener('click', (e) => {
- e.preventDefault();
- const codeblock = document.getElementById(codeblockID) as HTMLElement;
- if (!codeblockID) return;
- navigator.clipboard.writeText(codeblock.textContent)
- .then(() => {
- button.textContent = copiedText;
- setTimeout(() => {
- button.textContent = copyText;
- }, 1000);
- })
- .catch(err => {
- alert(err)
- console.log('Something went wrong', err);
- });
- }, false);
- });
-} \ No newline at end of file
diff --git a/assets/ts/color.ts b/assets/ts/color.ts
new file mode 100644
index 0000000..50581d1
--- /dev/null
+++ b/assets/ts/color.ts
@@ -0,0 +1,63 @@
+interface colorScheme {
+ hash: string, /// Regenerate color scheme when the image hash is changed
+ DarkMuted: {
+ hex: string,
+ rgb: Number[],
+ bodyTextColor: string
+ },
+ Vibrant: {
+ hex: string,
+ rgb: Number[],
+ bodyTextColor: string
+ }
+}
+
+let colorsCache: { [key: string]: colorScheme } = {};
+
+if (localStorage.hasOwnProperty('StackColorsCache')) {
+ try {
+ colorsCache = JSON.parse(localStorage.getItem('StackColorsCache'));
+ }
+ catch (e) {
+ colorsCache = {};
+ }
+}
+
+async function getColor(key: string, hash: string, imageURL: string) {
+ if (!key) {
+ /**
+ * If no key is provided, do not cache the result
+ */
+ return await Vibrant.from(imageURL).getPalette();
+ }
+
+ if (!colorsCache.hasOwnProperty(key) || colorsCache[key].hash !== hash) {
+ /**
+ * If key is provided, but not found in cache, or the hash mismatches => Regenerate color scheme
+ */
+ const palette = await Vibrant.from(imageURL).getPalette();
+
+ colorsCache[key] = {
+ hash: hash,
+ Vibrant: {
+ hex: palette.Vibrant.hex,
+ rgb: palette.Vibrant.rgb,
+ bodyTextColor: palette.Vibrant.bodyTextColor
+ },
+ DarkMuted: {
+ hex: palette.DarkMuted.hex,
+ rgb: palette.DarkMuted.rgb,
+ bodyTextColor: palette.DarkMuted.bodyTextColor
+ }
+ }
+
+ /* Save the result in localStorage */
+ localStorage.setItem('StackColorsCache', JSON.stringify(colorsCache));
+ }
+
+ return colorsCache[key];
+}
+
+export {
+ getColor
+} \ No newline at end of file
diff --git a/assets/ts/gallery.ts b/assets/ts/gallery.ts
index e0124d1..9840f1e 100644
--- a/assets/ts/gallery.ts
+++ b/assets/ts/gallery.ts
@@ -1,92 +1,186 @@
-const wrap = (figures: HTMLElement[]) => {
- const galleryContainer = document.createElement('div');
- galleryContainer.className = 'gallery';
+declare global {
+ interface Window {
+ PhotoSwipe: any;
+ PhotoSwipeUI_Default: any
+ }
+}
- const parentNode = figures[0].parentNode,
- first = figures[0];
+interface PhotoSwipeItem {
+ w: number;
+ h: number;
+ src: string;
+ msrc: string;
+ title?: string;
+ el: HTMLElement;
+}
- parentNode.insertBefore(galleryContainer, first)
+class StackGallery {
+ private galleryUID: number;
+ private items: PhotoSwipeItem[] = [];
+
+ constructor(container: HTMLElement, galleryUID = 1) {
+ if (window.PhotoSwipe == undefined || window.PhotoSwipeUI_Default == undefined) {
+ console.error("PhotoSwipe lib not loaded.");
+ return;
+ }
- for (const figure of figures) {
- galleryContainer.appendChild(figure);
+ this.galleryUID = galleryUID;
+
+ StackGallery.createGallery(container);
+ this.loadItems(container);
+ this.bindClick();
}
-}
-export default (container: HTMLElement) => {
- /// The process of wrapping image with figure tag is done using JavaScript instead of only Hugo markdown render hook
- /// because it can not detect whether image is being wrapped by a link or not
- /// and it lead to a invalid HTML construction (<a><figure><img></figure></a>)
- const images = container.querySelectorAll('img.gallery-image') as NodeListOf<HTMLImageElement>;
- for (const img of Array.from(images)) {
- /// Images are wrapped with figure tag if the paragraph has only images without texts
- /// This is done to allow inline images within paragraphs
- const paragraph = img.closest('p');
-
- if (!paragraph || !container.contains(paragraph)) continue;
-
- if (paragraph.textContent.trim() == '') {
- /// Once we insert figcaption, this check no longer works
- /// So we add a class to paragraph to mark it
- paragraph.classList.add('no-text');
+ private loadItems(container: HTMLElement) {
+ this.items = [];
+
+ const figures = container.querySelectorAll('figure.gallery-image');
+
+ for (const el of figures) {
+ const figcaption = el.querySelector('figcaption'),
+ img = el.querySelector('img');
+
+ let aux: PhotoSwipeItem = {
+ w: parseInt(img.getAttribute('width')),
+ h: parseInt(img.getAttribute('height')),
+ src: img.src,
+ msrc: img.getAttribute('data-thumb') || img.src,
+ el: el
+ }
+
+ if (figcaption) {
+ aux.title = figcaption.innerHTML;
+ }
+
+ this.items.push(aux);
}
+ }
+
+ public static createGallery(container: HTMLElement) {
+ /// The process of wrapping image with figure tag is done using JavaScript instead of only Hugo markdown render hook
+ /// because it can not detect whether image is being wrapped by a link or not
+ /// and it lead to a invalid HTML construction (<a><figure><img></figure></a>)
+
+ const images = container.querySelectorAll('img.gallery-image');
+ for (const img of Array.from(images)) {
+ /// Images are wrapped with figure tag if the paragraph has only images without texts
+ /// This is done to allow inline images within paragraphs
+ const paragraph = img.closest('p');
+
+ if (!paragraph || !container.contains(paragraph)) continue;
+
+ if (paragraph.textContent.trim() == '') {
+ /// Once we insert figcaption, this check no longer works
+ /// So we add a class to paragraph to mark it
+ paragraph.classList.add('no-text');
+ }
+
+ let isNewLineImage = paragraph.classList.contains('no-text');
+ if (!isNewLineImage) continue;
- let isNewLineImage = paragraph.classList.contains('no-text');
- if (!isNewLineImage) continue;
+ const hasLink = img.parentElement.tagName == 'A';
- const hasLink = img.parentElement.tagName == 'A';
+ let el: HTMLElement = img;
+ /// Wrap image with figure tag, with flex-grow and flex-basis values extracted from img's data attributes
+ const figure = document.createElement('figure');
+ figure.style.setProperty('flex-grow', img.getAttribute('data-flex-grow') || '1');
+ figure.style.setProperty('flex-basis', img.getAttribute('data-flex-basis') || '0');
+ if (hasLink) {
+ /// Wrap <a> if it exists
+ el = img.parentElement;
+ }
+ el.parentElement.insertBefore(figure, el);
+ figure.appendChild(el);
- let el: HTMLElement = img;
- /// Wrap image with figure tag, with flex-grow and flex-basis values extracted from img's data attributes
- const figure = document.createElement('figure');
- figure.style.setProperty('flex-grow', img.getAttribute('data-flex-grow') || '1');
- figure.style.setProperty('flex-basis', img.getAttribute('data-flex-basis') || '0');
- if (hasLink) {
- /// Wrap <a> if it exists
- el = img.parentElement;
+ /// Add figcaption if it exists
+ if (img.hasAttribute('alt')) {
+ const figcaption = document.createElement('figcaption');
+ figcaption.innerText = img.getAttribute('alt');
+ figure.appendChild(figcaption);
+ }
+
+ /// Wrap img tag with <a> tag if image was not wrapped by <a> tag
+ if (!hasLink) {
+ figure.className = 'gallery-image';
+
+ const a = document.createElement('a');
+ a.href = img.src;
+ a.setAttribute('target', '_blank');
+ img.parentNode.insertBefore(a, img);
+ a.appendChild(img);
+ }
}
- el.parentElement.insertBefore(figure, el);
- figure.appendChild(el);
-
- /// Add figcaption if it exists
- if (img.hasAttribute('alt')) {
- const figcaption = document.createElement('figcaption');
- figcaption.innerText = img.getAttribute('alt');
- figure.appendChild(figcaption);
+
+ const figuresEl = container.querySelectorAll('figure.gallery-image');
+
+ let currentGallery = [];
+
+ for (const figure of figuresEl) {
+ if (!currentGallery.length) {
+ /// First iteration
+ currentGallery = [figure];
+ }
+ else if (figure.previousElementSibling === currentGallery[currentGallery.length - 1]) {
+ /// Adjacent figures
+ currentGallery.push(figure);
+ }
+ else if (currentGallery.length) {
+ /// End gallery
+ StackGallery.wrap(currentGallery);
+ currentGallery = [figure];
+ }
}
- /// Wrap img tag with <a> tag if image was not wrapped by <a> tag
- if (!hasLink) {
- figure.className = 'gallery-image';
-
- const a = document.createElement('a');
- a.href = img.src;
- a.setAttribute('target', '_blank');
- a.setAttribute('data-pswp-width', img.width.toString());
- a.setAttribute('data-pswp-height', img.height.toString());
- img.parentNode.insertBefore(a, img);
- a.appendChild(img);
+ if (currentGallery.length > 0) {
+ StackGallery.wrap(currentGallery);
}
}
- const figuresEl = container.querySelectorAll('figure.gallery-image') as NodeListOf<HTMLElement>;
- let currentGallery = [];
- for (const figure of Array.from(figuresEl)) {
- if (!currentGallery.length) {
- /// First iteration
- currentGallery = [figure];
- }
- else if (figure.previousElementSibling === currentGallery[currentGallery.length - 1]) {
- /// Adjacent figures
- currentGallery.push(figure);
- }
- else if (currentGallery.length) {
- /// End gallery
- wrap(currentGallery);
- currentGallery = [figure];
+ /**
+ * Wrap adjacent figure tags with div.gallery
+ * @param figures
+ */
+ public static wrap(figures: HTMLElement[]) {
+ const galleryContainer = document.createElement('div');
+ galleryContainer.className = 'gallery';
+
+ const parentNode = figures[0].parentNode,
+ first = figures[0];
+
+ parentNode.insertBefore(galleryContainer, first)
+
+ for (const figure of figures) {
+ galleryContainer.appendChild(figure);
}
}
- if (currentGallery.length > 0) {
- wrap(currentGallery);
+ public open(index: number) {
+ const pswp = document.querySelector('.pswp') as HTMLDivElement;
+ const ps = new window.PhotoSwipe(pswp, window.PhotoSwipeUI_Default, this.items, {
+ index: index,
+ galleryUID: this.galleryUID,
+ getThumbBoundsFn: (index) => {
+ const thumbnail = this.items[index].el.getElementsByTagName('img')[0],
+ pageYScroll = window.pageYOffset || document.documentElement.scrollTop,
+ rect = thumbnail.getBoundingClientRect();
+
+ return { x: rect.left, y: rect.top + pageYScroll, w: rect.width };
+ }
+ });
+
+ ps.init();
}
-}; \ No newline at end of file
+
+ private bindClick() {
+ for (const [index, item] of this.items.entries()) {
+ const a = item.el.querySelector('a');
+
+ a.addEventListener('click', (e) => {
+ e.preventDefault();
+ this.open(index);
+ })
+ }
+ }
+}
+
+export default StackGallery; \ No newline at end of file
diff --git a/assets/ts/main.ts b/assets/ts/main.ts
index 0668c7c..f3160ae 100644
--- a/assets/ts/main.ts
+++ b/assets/ts/main.ts
@@ -5,7 +5,8 @@
* @website: https://jimmycai.com
* @link: https://github.com/CaiJimmy/hugo-theme-stack
*/
-import StackCodeBlock from "ts/codeblock";
+import StackGallery from "ts/gallery";
+import { getColor } from 'ts/color';
import menu from 'ts/menu';
import createElement from 'ts/createElement';
import StackColorScheme from 'ts/colorScheme';
@@ -21,12 +22,76 @@ let Stack = {
const articleContent = document.querySelector('.article-content') as HTMLElement;
if (articleContent) {
+ new StackGallery(articleContent);
setupSmoothAnchors();
setupScrollspy();
}
+ /**
+ * Add linear gradient background to tile style article
+ */
+ const articleTile = document.querySelector('.article-list--tile');
+ if (articleTile) {
+ let observer = new IntersectionObserver(async (entries, observer) => {
+ entries.forEach(entry => {
+ if (!entry.isIntersecting) return;
+ observer.unobserve(entry.target);
+
+ const articles = entry.target.querySelectorAll('article.has-image');
+ articles.forEach(async articles => {
+ const image = articles.querySelector('img'),
+ imageURL = image.src,
+ key = image.getAttribute('data-key'),
+ hash = image.getAttribute('data-hash'),
+ articleDetails: HTMLDivElement = articles.querySelector('.article-details');
+
+ const colors = await getColor(key, hash, imageURL);
+
+ articleDetails.style.background = `
+ linear-gradient(0deg,
+ rgba(${colors.DarkMuted.rgb[0]}, ${colors.DarkMuted.rgb[1]}, ${colors.DarkMuted.rgb[2]}, 0.5) 0%,
+ rgba(${colors.Vibrant.rgb[0]}, ${colors.Vibrant.rgb[1]}, ${colors.Vibrant.rgb[2]}, 0.75) 100%)`;
+ })
+ })
+ });
+
+ observer.observe(articleTile)
+ }
+
+
+ /**
+ * Add copy button to code block
+ */
+ const highlights = document.querySelectorAll('.article-content div.highlight');
+ const copyText = `Copy`,
+ copiedText = `Copied!`;
+
+ highlights.forEach(highlight => {
+ const copyButton = document.createElement('button');
+ copyButton.innerHTML = copyText;
+ copyButton.classList.add('copyCodeButton');
+ highlight.appendChild(copyButton);
+
+ const codeBlock = highlight.querySelector('code[data-lang]');
+ if (!codeBlock) return;
+
+ copyButton.addEventListener('click', () => {
+ navigator.clipboard.writeText(codeBlock.textContent)
+ .then(() => {
+ copyButton.textContent = copiedText;
+
+ setTimeout(() => {
+ copyButton.textContent = copyText;
+ }, 1000);
+ })
+ .catch(err => {
+ alert(err)
+ console.log('Something went wrong', err);
+ });
+ });
+ });
+
new StackColorScheme(document.getElementById('dark-mode-toggle'));
- StackCodeBlock();
}
}