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:02:24 +0300
committerGitHub <noreply@github.com>2022-10-29 18:02:24 +0300
commit8a597a5c9c861d2bce22a5e06c7667cd9c8e263a (patch)
treeaa4d68efec6db197b9bb22479177aafe6fc4f91c /assets
parentd60083e055dad5c5c4c0b07568b3df24df8daad7 (diff)
Merge branch 'canary' into master (#711)
* refactor(i18n): simplify the structure of the translation file * chore: prepare repository for v4.0.0-alpha * chore: modify go.mod to v4 * refactor: migrate theme configuration to TOML * fix: exampleSite not using correct theme * feat: add favicon from assets folder * refactor: drop linear grandient background feature remove node-vibrant from dependencies * feat: use Hugo's code block render hook to implement code copy button Now it can have i18n support * refactor: delete color.ts * refactor: delete Emoji support post from example site * refactor: drop support for `hidden` field in front matter * feat: upgrade to PhotoSwipe v5 * chore: bump the required hugo version to 0.100.0 * refactor: remove PhotoSwipe from external.yaml * fix: extra margin in search result * fix: incorrect markdown heading level in example site * refactor: remove some usages of `default` in template No longer needed thanks to Hugo's configuration merge * fix: one line codeblock style in firefox closes https://github.com/CaiJimmy/hugo-theme-stack/issues/564 * feat: add style to new codeblock * feat: i18n support for codeblock copy text * fix(menu): jitter when closing menu It's caused by flexbox gap property, which can't animate * fix(search): long text overflows under the Search icon closes https://github.com/CaiJimmy/hugo-theme-stack/issues/515
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, 139 insertions, 322 deletions
diff --git a/assets/scss/partials/article.scss b/assets/scss/partials/article.scss
index f085ff0..a041e9b 100644
--- a/assets/scss/partials/article.scss
+++ b/assets/scss/partials/article.scss
@@ -192,7 +192,6 @@
.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 0d3f330..3ea6c56 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";
+@import "common.scss"; \ No newline at end of file
diff --git a/assets/scss/partials/layout/article.scss b/assets/scss/partials/layout/article.scss
index 97c7e77..ba06c11 100644
--- a/assets/scss/partials/layout/article.scss
+++ b/assets/scss/partials/layout/article.scss
@@ -286,10 +286,12 @@
line-height: 1.428571429;
word-break: break-all;
padding: var(--card-padding);
+
// keep Codeblocks LTR
[dir="rtl"] & {
direction: ltr;
}
+
code {
color: unset;
border: none;
@@ -303,15 +305,11 @@
padding: var(--card-padding);
position: relative;
- &:hover {
- .copyCodeButton {
- opacity: 1;
- }
- }
// keep Codeblocks LTR
[dir="rtl"] & {
direction: ltr;
}
+
pre {
margin: initial;
padding: 0;
@@ -320,20 +318,30 @@
}
}
- .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;
+ .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;
+ }
}
.table-wrapper {
@@ -402,7 +410,7 @@
/// Negative margins
blockquote,
figure,
- .highlight,
+ .codeblock,
pre,
.gallery,
.video-wrapper,
diff --git a/assets/scss/partials/layout/search.scss b/assets/scss/partials/layout/search.scss
index 89cdcef..cbf15b7 100644
--- a/assets/scss/partials/layout/search.scss
+++ b/assets/scss/partials/layout/search.scss
@@ -31,6 +31,7 @@
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);
@@ -78,5 +79,4 @@
height: 20px;
}
}
-
-} \ No newline at end of file
+}
diff --git a/assets/scss/partials/sidebar.scss b/assets/scss/partials/sidebar.scss
index 95310ca..75332a7 100644
--- a/assets/scss/partials/sidebar.scss
+++ b/assets/scss/partials/sidebar.scss
@@ -11,7 +11,6 @@
flex-direction: column;
flex-shrink: 0;
align-self: stretch;
- gap: var(--sidebar-element-separation);
max-width: none;
width: 100%;
position: relative;
@@ -65,6 +64,11 @@
}
}
}
+
+ .social-menu,
+ .menu {
+ margin-top: var(--sidebar-element-separation);
+ }
}
.right-sidebar {
diff --git a/assets/ts/codeblock.ts b/assets/ts/codeblock.ts
new file mode 100644
index 0000000..08c3328
--- /dev/null
+++ b/assets/ts/codeblock.ts
@@ -0,0 +1,28 @@
+/**
+ * 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
deleted file mode 100644
index 50581d1..0000000
--- a/assets/ts/color.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-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 9840f1e..e0124d1 100644
--- a/assets/ts/gallery.ts
+++ b/assets/ts/gallery.ts
@@ -1,186 +1,92 @@
-declare global {
- interface Window {
- PhotoSwipe: any;
- PhotoSwipeUI_Default: any
- }
-}
+const wrap = (figures: HTMLElement[]) => {
+ const galleryContainer = document.createElement('div');
+ galleryContainer.className = 'gallery';
-interface PhotoSwipeItem {
- w: number;
- h: number;
- src: string;
- msrc: string;
- title?: string;
- el: HTMLElement;
-}
+ const parentNode = figures[0].parentNode,
+ first = figures[0];
-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;
- }
+ parentNode.insertBefore(galleryContainer, first)
- this.galleryUID = galleryUID;
-
- StackGallery.createGallery(container);
- this.loadItems(container);
- this.bindClick();
+ for (const figure of figures) {
+ galleryContainer.appendChild(figure);
}
+}
- 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);
+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');
}
- }
-
- 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;
- const hasLink = img.parentElement.tagName == 'A';
+ let isNewLineImage = paragraph.classList.contains('no-text');
+ if (!isNewLineImage) continue;
- 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);
+ const hasLink = img.parentElement.tagName == 'A';
- /// 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);
- }
+ 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;
}
-
- 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];
- }
+ 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);
}
- if (currentGallery.length > 0) {
- StackGallery.wrap(currentGallery);
+ /// 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);
}
}
- /**
- * 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);
+ 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];
}
- }
-
- 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();
- }
-
- 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);
- })
+ else if (figure.previousElementSibling === currentGallery[currentGallery.length - 1]) {
+ /// Adjacent figures
+ currentGallery.push(figure);
+ }
+ else if (currentGallery.length) {
+ /// End gallery
+ wrap(currentGallery);
+ currentGallery = [figure];
}
}
-}
-export default StackGallery; \ No newline at end of file
+ if (currentGallery.length > 0) {
+ wrap(currentGallery);
+ }
+}; \ No newline at end of file
diff --git a/assets/ts/main.ts b/assets/ts/main.ts
index f3160ae..0668c7c 100644
--- a/assets/ts/main.ts
+++ b/assets/ts/main.ts
@@ -5,8 +5,7 @@
* @website: https://jimmycai.com
* @link: https://github.com/CaiJimmy/hugo-theme-stack
*/
-import StackGallery from "ts/gallery";
-import { getColor } from 'ts/color';
+import StackCodeBlock from "ts/codeblock";
import menu from 'ts/menu';
import createElement from 'ts/createElement';
import StackColorScheme from 'ts/colorScheme';
@@ -22,76 +21,12 @@ 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();
}
}