review code of gdpr banner

This commit is contained in:
Massimo Maggioni 2022-02-18 22:46:09 +01:00
parent 081040572a
commit e89e2d3750
17 changed files with 231 additions and 125 deletions

View File

@ -22,3 +22,17 @@ body {
scrollbar-color: var(--scrollbar-thumb) transparent;
}
/**/
/* scrollbar styles for Chromium */
::-webkit-scrollbar {
height: auto;
}
::-webkit-scrollbar-thumb {
background-color: var(--scrollbar-thumb);
}
::-webkit-scrollbar-track {
background-color: transparent;
}
/**/

View File

@ -123,7 +123,6 @@
}
.article-page.has-toc {
.left-sidebar {
display: none;
}
@ -395,6 +394,41 @@
}
}
.highlight {
background-color: var(--pre-background-color);
padding: var(--card-padding);
position: relative;
&:hover {
.copyCodeButton {
opacity: 1;
}
}
pre {
margin: initial;
padding: 0;
margin: 0;
width: auto;
}
}
.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 {
padding: 0 var(--card-padding);
overflow-x: auto;
@ -449,6 +483,7 @@
/// Negative margins
blockquote,
figure,
.highlight,
pre,
.gallery,
.video-wrapper,
@ -458,30 +493,4 @@
margin-right: calc((var(--card-padding)) * -1);
width: calc(100% + var(--card-padding) * 2);
}
.highlight {
position: relative;
&:hover {
.copyCodeButton {
opacity: 1;
}
}
}
.copyCodeButton {
position: absolute;
top: calc(var(--card-padding));
right: 0;
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;
}
}

View File

@ -1,19 +1,6 @@
$defaultTagBackgrounds: #8ea885, #df7988, #0177b8, #ffb900, #6b69d6;
$defaultTagColors: #fff, #fff, #fff, #fff, #fff;
[data-scheme="light"] {
--pre-text-color: #272822;
--pre-background-color: #fafafa;
@import "partials/highlight/light.scss";
}
[data-scheme="dark"] {
color-scheme: dark;
--pre-text-color: #f8f8f2;
--pre-background-color: #272822;
@import "partials/highlight/dark.scss";
}
/*
* Global style
*/
@ -46,7 +33,7 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff;
--accent-color-darker: #bdc3c7;
--accent-color-text: #000;
--body-text-color: rgba(255, 255, 255, 0.7);
--scrollbar-thumb: #424242;
--scrollbar-thumb: hsl(0, 0%, 40%);
--scrollbar-track: var(--body-background);
}
}
@ -160,3 +147,15 @@ $defaultTagColors: #fff, #fff, #fff, #fff, #fff;
--shadow-l4: 0px 24px 32px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), 0px 4px 8px rgba(0, 0, 0, 0.04),
0px 0px 1px rgba(0, 0, 0, 0.04);
}
[data-scheme="light"] {
--pre-text-color: #272822;
--pre-background-color: #fafafa;
@import "partials/highlight/light.scss";
}
[data-scheme="dark"] {
--pre-text-color: #f8f8f2;
--pre-background-color: #272822;
@import "partials/highlight/dark.scss";
}

View File

@ -57,6 +57,60 @@ class StackGallery {
}
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');
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 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);
/// 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);
}
}
const figuresEl = container.querySelectorAll('figure.gallery-image');
let currentGallery = [];

View File

@ -62,20 +62,21 @@ let Stack = {
/**
* Add copy button to code block
*/
const codeBlocks = document.querySelectorAll('.article-content > div.highlight');
const highlights = document.querySelectorAll('.article-content div.highlight');
const copyText = `Copy`,
copiedText = `Copied!`;
codeBlocks.forEach(codeBlock => {
highlights.forEach(highlight => {
const copyButton = document.createElement('button');
copyButton.innerHTML = copyText;
copyButton.classList.add('copyCodeButton');
codeBlock.appendChild(copyButton);
highlight.appendChild(copyButton);
const pre = codeBlock.getElementsByTagName('pre');
const code = pre[0].textContent;
const codeBlock = highlight.querySelector('code[data-lang]');
if (!codeBlock) return;
copyButton.addEventListener('click', () => {
navigator.clipboard.writeText(code)
navigator.clipboard.writeText(codeBlock.textContent)
.then(() => {
copyButton.textContent = copiedText;

View File

@ -1,7 +1,7 @@
plugins:
- name: gtag
enabled: true
functional: false
optional: true
title: Google Analytics
description: This code gives us insight into the number of people that visit our website, where they are from and what they are clicking on. We follow the guidelines of the Italian Government.
html_src: gdpr/gtag.html

View File

@ -4,6 +4,8 @@ theme: hugo-theme-stack
paginate: 5
title: Example Site
# Change it to your Disqus shortname before using
disqusShortname: hugo-theme-stack
@ -29,9 +31,10 @@ params:
rssFullContent: true
favicon:
GDPRbanner:
enabled: true
onlyfunctional: false
# mode: nocookie, functional, consent
GDPR:
enable: true
mode: consent
footer:
since: 2020
@ -193,13 +196,13 @@ menu:
name: GitHub
url: https://github.com/CaiJimmy/hugo-theme-stack
params:
icon: brand-github
icon: brand-github
- identifier: twitter
name: Twitter
url: https://twitter.com
params:
icon: brand-twitter
icon: brand-twitter
related:
includeNewer: true

View File

@ -162,3 +162,7 @@ X<sup>n</sup> + Y<sup>n</sup> = Z<sup>n</sup>
Press <kbd><kbd>CTRL</kbd>+<kbd>ALT</kbd>+<kbd>Delete</kbd></kbd> to end the session.
Most <mark>salamanders</mark> are nocturnal, and hunt for insects, worms, and other small creatures.
## Hyperlinked image
[![Google](https://www.google.com/images/branding/googlelogo/1x/googlelogo_light_color_272x92dp.png)](https://google.com)

View File

@ -36,3 +36,7 @@ Hugo ships with several [Built-in Shortcodes](https://gohugo.io/content-manageme
## bilibilibi Shortcode
{{< bilibili av498363026 >}}
## Gist Shortcode
{{< gist spf13 7896402 >}}

2
go.mod
View File

@ -1,3 +1,3 @@
module github.com/CaiJimmy/hugo-theme-stack
module github.com/CaiJimmy/hugo-theme-stack/v3
go 1.12

View File

@ -63,8 +63,8 @@ search:
other: "#PAGES_COUNT pages (#TIME_SECONDS seconds)"
footer:
GDPRsettings:
modify: Manage GDPR settings
GDPR:
consent: Manage GDPR settings
functional: This website use only functional cookies
nocookie: This website doesn't use any cookie and doesn't collect any personal data

View File

@ -62,8 +62,8 @@ search:
other: "#PAGES_COUNT pagine (#TIME_SECONDS secondi)"
footer:
GDPRsettings:
other: Gestisci il consenso GDPR
GDPR:
consent: Gestisci il consenso GDPR
functional: Questo sito web utilizza solo cookie funzionali
nocookie: Questo sito web non usa cookie e non raccoglie dati personali

View File

@ -25,22 +25,17 @@
{{- end -}}
{{- end -}}
<figure
<img src="{{ $Permalink }}"
{{ with $Width }}width="{{ . }}"{{ end }}
{{ with $Height }}height="{{ . }}"{{ end }}
{{ with $Srcset }}srcset="{{ . }}"{{ end }}
loading="lazy"
{{ with $alt }}
alt="{{ . }}"
{{ end }}
{{ if $galleryImage }}
class="gallery-image"
style="
flex-grow: {{ div (mul $image.Width 100) $image.Height }};
flex-basis: {{ div (mul $image.Width 240) $image.Height }}px"
{{ end }}>
<a href="{{ $Permalink }}" {{ if $galleryImage }}data-size="{{ $image.Width }}x{{ $image.Height }}"{{ end }}>
<img src="{{ $Permalink }}"
{{ with $Width }}width="{{ . }}"{{ end }}
{{ with $Height }}height="{{ . }}"{{ end }}
{{ with $Srcset }}srcset="{{ . }}"{{ end }}
loading="lazy"
{{ with $alt }}alt="{{ . }}"{{ end }}>
</a>
{{ with $alt }}
<figcaption>{{ . | markdownify }}</figcaption>
data-flex-grow="{{ div (mul $image.Width 100) $image.Height }}"
data-flex-basis="{{ div (mul $image.Width 240) $image.Height }}px"
{{ end }}
</figure>
>

View File

@ -1,3 +1,3 @@
{{ if and .Site.Params.GDPRbanner.enabled (not .Site.Params.GDPRbanner.onlyfunctional) }}
{{ if and .Site.Params.GDPR.enable (not (eq .Site.Params.GDPR.mode "nocookie")) }}
{{ partial "gdpr/banner.html" . }}
{{ end }}

View File

@ -1,6 +1,5 @@
{{- $ThemeVersion := "3.7.0" -}}
{{- $ThemeVersion := "3.8.0" -}}
<footer class="site-footer">
<section class="copyright">
&copy;
{{ if and (.Site.Params.footer.since) (ne .Site.Params.footer.since (int (now.Format "2006"))) }}
@ -9,17 +8,19 @@
{{ now.Format "2006" }} {{ .Site.Title }}
</section>
{{ if .Site.Params.GDPR.enable }}
<section class="GDPRbanner">
{{ if .Site.Params.GDPRbanner.enabled }}
{{ if not .Site.Params.GDPRbanner.onlyfunctional }}
<a class="manage-consent" href="#manage-consent">{{ T "footer.GDPRsettings.modify" }}</a>
{{ if (eq .Site.Params.GDPR.mode "consent") }}
<a class="manage-consent" href="#manage-consent">{{ T "footer.GDPR.consent" }}</a>
{{ else }}
{{ T "footer.GDPRsettings.functional" }}
{{ if (eq .Site.Params.GDPR.mode "functional") }}
{{ T "footer.GDPR.functional" }}
{{ else }}
{{ T "footer.GDPR.nocookie" }}
{{ end }}
{{ end }}
{{ else }}
{{ T "footer.GDPRsettings.nocookie" }}
{{ end }}
</section>
{{ end }}
<section class="powerby">
{{ with .Site.Params.footer.customText }}

View File

@ -1,5 +1,24 @@
<!-- GDPR banner visual style -->
<style>
<!-- if all enabled plugins are not optional instead use mode = functionl in config file-->
{{ range $index, $plugin := .Site.Data.gdpr.plugins }}
{{ if and $plugin.enabled (not $plugin.optional) }}
{{- errorf "All enabled plugins are not optional, use mode = functional in config file" -}}
{{ end }}
{{ end }}
<!-- import and execute scripts because all are functional -->
{{ if eq .Site.Params.GDPR.mode "functional" }}
{{ range $index, $plugin := .Site.Data.gdpr.plugins }}
{{ if $plugin.enabled }}
{{ partial $plugin.html_src }}
<script type="text/javascript" src="/js/{{ $plugin.js_src }}"></script>
{{ end }}
{{ end }}
{{ end }}
<!-- import and execute scripts based on user consent -->
{{ if eq .Site.Params.GDPR.mode "consent" }}
<!-- GDPR banner visual style -->
<style>
#consent-notice {padding: 1rem 1rem; display: none; text-align: center; position: fixed; bottom: 0; width: calc(100% - 2rem); background: #222; color: rgba(255,255,255,0.8);}
#consent-notice span {margin-right: 1rem;}
#consent-notice button {cursor: pointer; display: inline-block; width: auto;}
@ -22,53 +41,55 @@
#consent-overlay button.btn.approve-consent {background: rgba(255,255,255,1); font-weight: normal;}
@media (max-width: 767px) {
#consent-overlay > div {padding: 1.75rem 1rem;}
#consent-notice span {display: block; padding-top: 3px; margin-bottom: 1.5rem;}
#consent-notice button.btn {position: relative; bottom: 4px;}
#consent-overlay > div {padding: 1.75rem 1rem;}
#consent-notice span {display: block; padding-top: 3px; margin-bottom: 1.5rem;}
#consent-notice button.btn {position: relative; bottom: 4px;}
}
</style>
</style>
<!-- import scripts: full if functional, partially if depends on user consent -->
{{ range $index, $plugin := .Site.Data.gdpr.plugins }}
{{ if $plugin.enabled }}
{{ "<!-- " }} {{ $plugin.name }} {{ "start -->" }}
{{ partial $plugin.html_src }}
{{ if $plugin.functional }}
<script type="text/javascript" src="/js/{{ $plugin.js_src }}"></script>
{{ end }}
{{ "<!-- " }} {{ $plugin.name }} {{ "end -->" }}
{{ end }}
{{ end }}
<!-- preferences dialog -->
<div id="consent-notice"><span>We would like to use <a class="manage-consent" href="#manage-consent">third party code</a> to improve the functionality of this website.</span><button class="btn manage-consent">Manage preferences</button><button class="btn deny-consent">Deny</button><button class="btn approve-consent">Allow</button></div>
<div id="consent-overlay">
<!-- preferences dialog -->
<div id="consent-notice">
<span>We would like to use <a class="manage-consent" href="#manage-consent">third party code</a> to improve the functionality of this website.</span>
<button class="btn manage-consent">Manage preferences</button>
<button class="btn deny-consent">Deny</button>
<button class="btn approve-consent">Allow</button>
</div>
<div id="consent-overlay">
<div>
{{ range $index, $plugin := .Site.Data.gdpr.plugins }}
{{ if $plugin.enabled }}
<div>
<input type="checkbox" id="item{{ $index }}" value="1" name="item{{ $index }}" {{ if $plugin.functional }}checked disabled{{ end }} />
<label for="item{{ $index }}">
<h3>{{ $plugin.title }}{{ if $plugin.functional}} (functional){{ end }}</h3>
<p>{{ $plugin.description }}</p>
</label>
{{ range $index, $plugin := .Site.Data.gdpr.plugins }}
{{ if $plugin.enabled }}
<div>
<input type="checkbox" id="item{{ $index }}" value="1" name="item{{ $index }}" {{ if not $plugin.optional }}checked disabled{{ end }} />
<label for="item{{ $index }}">
<h3>{{ $plugin.title }}{{ if not $plugin.optional}} (functional){{ end }}</h3>
<p>{{ $plugin.description }}</p>
</label>
</div>
{{ end }}
{{ end }}
<div>
<button id="save-consent" class="btn save-consent" data-consentvalue="{{ range $index, $plugin := .Site.Data.gdpr.plugins }}{{ if $plugin.enabled }}{{ else }}0{{ end }}{{ end }}">Save preferences</button>
<button class="btn approve-consent">Allow all</button>
<button id="save-consent" class="btn save-consent" data-consentvalue="{{ range $index, $plugin := .Site.Data.gdpr.plugins }}{{ if $plugin.enabled }}{{ else }}0{{ end }}{{ end }}">Save preferences</button>
<button class="btn approve-consent">Allow all</button>
</div>
</div>
</div>
</div>
<!-- execute user consent scripts -->
<script>
<!-- import scripts library with no really action -->
{{ range $index, $plugin := .Site.Data.gdpr.plugins }}
{{ if $plugin.enabled }}
{{ partial $plugin.html_src }}
{{ if not $plugin.optional }}
<script type="text/javascript" src="/js/{{ $plugin.js_src }}"></script>
{{ end }}
{{ end }}
{{ end }}
<!-- really execute scripts based on user consent -->
<script>
const scripts = [];
{{ range $index, $plugin := .Site.Data.gdpr.plugins }}
{{ if and $plugin.enabled (not $plugin.functional) }}
scripts[{{ $index }}] = "/js/{{ $plugin.js_src }}";
{{ if and $plugin.enabled ($plugin.optional) }}
scripts[{{ $index }}] = "/js/{{ $plugin.js_src }}";
{{ end }}
{{ end }}
@ -183,4 +204,5 @@
this.classList.toggle('active');
}
});
</script>
</script>
{{ end }}

View File

@ -21,7 +21,7 @@
<link rel="shortcut icon" href="{{ . }}" />
{{ end }}
{{ if not .Site.Params.GDPRbanner.enabled }}
{{ if not .Site.Params.GDPR.enable }}
{{- template "_internal/google_analytics.html" . -}}
{{ end }}
{{- partial "head/custom.html" . -}}