From d8351aa432e9458a877c201fed314c02800f25ac Mon Sep 17 00:00:00 2001 From: Xin Date: Thu, 28 Mar 2024 08:32:51 +0100 Subject: [PATCH] feat: allow enable/disable code block copy button (#331) * refactor: move codeblock to a partial component * feat: add flags for code block copy button * allow disable code copy button completely * allow make the copy button always visible * chore: run build:css --- assets/css/compiled/main.css | 60 +++++++++---------- assets/css/components/code-copy.css | 2 +- assets/css/highlight.css | 6 +- assets/css/typography.css | 8 +-- assets/js/code-copy.js | 2 +- exampleSite/hugo_stats.json | 4 +- .../_default/_markup/render-codeblock.html | 25 ++------ .../components/codeblock-copy-button.html | 15 +++++ layouts/partials/components/codeblock.html | 13 ++++ 9 files changed, 76 insertions(+), 59 deletions(-) create mode 100644 layouts/partials/components/codeblock-copy-button.html create mode 100644 layouts/partials/components/codeblock.html diff --git a/assets/css/compiled/main.css b/assets/css/compiled/main.css index aae39d20..a86a80ad 100644 --- a/assets/css/compiled/main.css +++ b/assets/css/compiled/main.css @@ -1545,7 +1545,7 @@ video { --tw-text-opacity: 1; color: rgb(156 163 175 / var(--tw-text-opacity)); } -.content :where(pre):not(:where(.code-block pre, [class~=not-prose],[class~=not-prose] *)) { +.content :where(pre):not(:where(.hextra-code-block pre, [class~=not-prose],[class~=not-prose] *)) { margin-bottom: 1rem; overflow-x: auto; border-radius: 0.75rem; @@ -1559,23 +1559,23 @@ video { } @media (prefers-contrast: more) { - .content :where(pre):not(:where(.code-block pre, [class~=not-prose],[class~=not-prose] *)) { + .content :where(pre):not(:where(.hextra-code-block pre, [class~=not-prose],[class~=not-prose] *)) { border-width: 1px; border-color: hsl(var(--primary-hue) var(--primary-saturation) 24% / 0.2); --tw-contrast: contrast(1.5); filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); } } -:is(html[class~="dark"] .content :where(pre):not(:where(.code-block pre, [class~=not-prose],[class~=not-prose] *))) { +:is(html[class~="dark"] .content :where(pre):not(:where(.hextra-code-block pre, [class~=not-prose],[class~=not-prose] *))) { background-color: hsl(var(--primary-hue) var(--primary-saturation) 77% / 0.1); } @media (prefers-contrast: more) { - :is(html[class~="dark"] .content :where(pre):not(:where(.code-block pre, [class~=not-prose],[class~=not-prose] *))) { + :is(html[class~="dark"] .content :where(pre):not(:where(.hextra-code-block pre, [class~=not-prose],[class~=not-prose] *))) { border-color: hsl(var(--primary-hue) var(--primary-saturation) 94% / 0.4); } } -.content :where(code):not(:where(.code-block code, [class~=not-prose],[class~=not-prose] *)) { +.content :where(code):not(:where(.hextra-code-block code, [class~=not-prose],[class~=not-prose] *)) { overflow-wrap: break-word; border-radius: 0.375rem; border-width: 1px; @@ -1589,38 +1589,38 @@ video { padding-right: .25em; font-size: .9em; } -:is(html[class~="dark"] .content :where(code):not(:where(.code-block code, [class~=not-prose],[class~=not-prose] *))) { +:is(html[class~="dark"] .content :where(code):not(:where(.hextra-code-block code, [class~=not-prose],[class~=not-prose] *))) { border-color: rgb(255 255 255 / 0.1); background-color: rgb(255 255 255 / 0.1); } -.content :where(table):not(:where(.code-block table, [class~=not-prose],[class~=not-prose] *)) { +.content :where(table):not(:where(.hextra-code-block table, [class~=not-prose],[class~=not-prose] *)) { margin-top: 1.5rem; display: block; overflow-x: auto; padding: 0px; } -.content :where(table):not(:where(.code-block table, [class~=not-prose],[class~=not-prose] *)):first-child { +.content :where(table):not(:where(.hextra-code-block table, [class~=not-prose],[class~=not-prose] *)):first-child { margin-top: 0px; } -.content :where(table):not(:where(.code-block table, [class~=not-prose],[class~=not-prose] *)) tr { +.content :where(table):not(:where(.hextra-code-block table, [class~=not-prose],[class~=not-prose] *)) tr { margin: 0px; border-top-width: 1px; --tw-border-opacity: 1; border-color: rgb(209 213 219 / var(--tw-border-opacity)); padding: 0px; } -.content :where(table):not(:where(.code-block table, [class~=not-prose],[class~=not-prose] *)) tr:nth-child(even) { +.content :where(table):not(:where(.hextra-code-block table, [class~=not-prose],[class~=not-prose] *)) tr:nth-child(even) { --tw-bg-opacity: 1; background-color: rgb(243 244 246 / var(--tw-bg-opacity)); } -:is(html[class~="dark"] .content :where(table):not(:where(.code-block table, [class~=not-prose],[class~=not-prose] *)) tr) { +:is(html[class~="dark"] .content :where(table):not(:where(.hextra-code-block table, [class~=not-prose],[class~=not-prose] *)) tr) { --tw-border-opacity: 1; border-color: rgb(75 85 99 / var(--tw-border-opacity)); } -:is(html[class~="dark"] .content :where(table):not(:where(.code-block table, [class~=not-prose],[class~=not-prose] *)) tr):nth-child(even) { +:is(html[class~="dark"] .content :where(table):not(:where(.hextra-code-block table, [class~=not-prose],[class~=not-prose] *)) tr):nth-child(even) { background-color: rgb(75 85 99 / 0.2); } -.content :where(table):not(:where(.code-block table, [class~=not-prose],[class~=not-prose] *)) th { +.content :where(table):not(:where(.hextra-code-block table, [class~=not-prose],[class~=not-prose] *)) th { margin: 0px; border-width: 1px; --tw-border-opacity: 1; @@ -1631,11 +1631,11 @@ video { padding-bottom: 0.5rem; font-weight: 600; } -:is(html[class~="dark"] .content :where(table):not(:where(.code-block table, [class~=not-prose],[class~=not-prose] *)) th) { +:is(html[class~="dark"] .content :where(table):not(:where(.hextra-code-block table, [class~=not-prose],[class~=not-prose] *)) th) { --tw-border-opacity: 1; border-color: rgb(75 85 99 / var(--tw-border-opacity)); } -.content :where(table):not(:where(.code-block table, [class~=not-prose],[class~=not-prose] *)) td { +.content :where(table):not(:where(.hextra-code-block table, [class~=not-prose],[class~=not-prose] *)) td { margin: 0px; border-width: 1px; --tw-border-opacity: 1; @@ -1645,7 +1645,7 @@ video { padding-top: 0.5rem; padding-bottom: 0.5rem; } -:is(html[class~="dark"] .content :where(table):not(:where(.code-block table, [class~=not-prose],[class~=not-prose] *)) td) { +:is(html[class~="dark"] .content :where(table):not(:where(.hextra-code-block table, [class~=not-prose],[class~=not-prose] *)) td) { --tw-border-opacity: 1; border-color: rgb(75 85 99 / var(--tw-border-opacity)); } @@ -1707,11 +1707,11 @@ video { border-color: rgb(255 255 255 / 0.1); background-color: rgb(255 255 255 / 0.1); } -.content :where(pre.mermaid):not(:where(.code-block pre, [class~=not-prose],[class~=not-prose] *)) { +.content :where(pre.mermaid):not(:where(.hextra-code-block pre, [class~=not-prose],[class~=not-prose] *)) { border-radius: 0px; background-color: transparent; } -:is(html[class~="dark"] .content :where(pre.mermaid):not(:where(.code-block pre, [class~=not-prose],[class~=not-prose] *))) { +:is(html[class~="dark"] .content :where(pre.mermaid):not(:where(.hextra-code-block pre, [class~=not-prose],[class~=not-prose] *))) { background-color: transparent; } .content :where(img):not(:where([class~=not-prose],[class~=not-prose] *)) { @@ -2117,11 +2117,11 @@ article details > summary::before { .dark .highlight .chroma .gl { text-decoration: underline } /* TextWhitespace */ .dark .highlight .chroma .w { color: #6e7681 } -.code-block { +.hextra-code-block { font-size: .9em; line-height: 1.25rem; } -.code-block pre { +.hextra-code-block pre { overflow-x: auto; background-color: hsl(var(--primary-hue) var(--primary-saturation) 39% / 0.05); font-size: .9em; @@ -2131,23 +2131,23 @@ article details > summary::before { } @media (prefers-contrast: more) { - .code-block pre { + .hextra-code-block pre { border-width: 1px; border-color: hsl(var(--primary-hue) var(--primary-saturation) 24% / 0.2); --tw-contrast: contrast(1.5); filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); } } -:is(html[class~="dark"] .code-block pre) { +:is(html[class~="dark"] .hextra-code-block pre) { background-color: hsl(var(--primary-hue) var(--primary-saturation) 77% / 0.1); } @media (prefers-contrast: more) { - :is(html[class~="dark"] .code-block pre) { + :is(html[class~="dark"] .hextra-code-block pre) { border-color: hsl(var(--primary-hue) var(--primary-saturation) 94% / 0.4); } } -.code-block .filename { +.hextra-code-block .filename { position: absolute; top: 0px; z-index: 1; @@ -2166,16 +2166,16 @@ article details > summary::before { --tw-text-opacity: 1; color: rgb(55 65 81 / var(--tw-text-opacity)); } -:is(html[class~="dark"] .code-block .filename) { +:is(html[class~="dark"] .hextra-code-block .filename) { background-color: hsl(var(--primary-hue) var(--primary-saturation) 77% / 0.1); --tw-text-opacity: 1; color: rgb(229 231 235 / var(--tw-text-opacity)); } -.code-block .filename + pre:not(.lntable pre) { +.hextra-code-block .filename + pre:not(.lntable pre) { /* Override padding for code blocks with filename but no highlight */ padding-top: 3rem; } -.code-block pre:not(.lntable pre) { +.hextra-code-block pre:not(.lntable pre) { margin-bottom: 1rem; border-radius: 0.75rem; padding-left: 1rem; @@ -2183,7 +2183,7 @@ article details > summary::before { padding-top: 1rem; padding-bottom: 1rem; } -.code-block div:nth-of-type(2) pre { +.hextra-code-block div:nth-of-type(2) pre { padding-top: 3rem; padding-bottom: 1rem; } @@ -2542,13 +2542,13 @@ nav .search-wrapper { @supports ( ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) ) { - .code-copy-btn { + .hextra-hextra-code-copy-btn { --tw-bg-opacity: .85; --tw-backdrop-blur: blur(12px); -webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); } - :is(html[class~="dark"] .code-copy-btn) { + :is(html[class~="dark"] .hextra-hextra-code-copy-btn) { --tw-bg-opacity: 0.8; } } diff --git a/assets/css/components/code-copy.css b/assets/css/components/code-copy.css index 3c020d34..cea990b3 100644 --- a/assets/css/components/code-copy.css +++ b/assets/css/components/code-copy.css @@ -1,7 +1,7 @@ @supports ( (-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px)) ) { - .code-copy-btn { + .hextra-hextra-code-copy-btn { @apply hx-backdrop-blur-md hx-bg-opacity-[.85] dark:hx-bg-opacity-80; } } diff --git a/assets/css/highlight.css b/assets/css/highlight.css index ee6fdb34..0ffa52cb 100644 --- a/assets/css/highlight.css +++ b/assets/css/highlight.css @@ -2,7 +2,7 @@ @import "chroma/light.css"; @import "chroma/dark.css"; -.code-block { +.hextra-code-block { @apply hx-text-[.9em] hx-leading-5; pre { @@ -19,11 +19,11 @@ } } -.code-block pre:not(.lntable pre) { +.hextra-code-block pre:not(.lntable pre) { @apply hx-px-4 hx-mb-4 hx-py-4 hx-rounded-xl; } -.code-block div:nth-of-type(2) pre { +.hextra-code-block div:nth-of-type(2) pre { @apply hx-pt-12 hx-pb-4; } diff --git a/assets/css/typography.css b/assets/css/typography.css index a059feef..03ab3b53 100644 --- a/assets/css/typography.css +++ b/assets/css/typography.css @@ -26,13 +26,13 @@ :where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)) { @apply hx-mt-6 hx-border-gray-300 hx-italic hx-text-gray-700 dark:hx-border-gray-700 dark:hx-text-gray-400 first:hx-mt-0 ltr:hx-border-l-2 ltr:hx-pl-6 rtl:hx-border-r-2 rtl:hx-pr-6; } - :where(pre):not(:where(.code-block pre, [class~=not-prose],[class~=not-prose] *)) { + :where(pre):not(:where(.hextra-code-block pre, [class~=not-prose],[class~=not-prose] *)) { @apply hx-bg-primary-700/5 hx-mb-4 hx-overflow-x-auto hx-rounded-xl hx-font-medium hx-subpixel-antialiased dark:hx-bg-primary-300/10 hx-text-[.9em] contrast-more:hx-border contrast-more:hx-border-primary-900/20 contrast-more:hx-contrast-150 contrast-more:dark:hx-border-primary-100/40 hx-py-4; } - :where(code):not(:where(.code-block code, [class~=not-prose],[class~=not-prose] *)) { + :where(code):not(:where(.hextra-code-block code, [class~=not-prose],[class~=not-prose] *)) { @apply hx-border-black hx-border-opacity-[0.04] hx-bg-opacity-[0.03] hx-bg-black hx-break-words hx-rounded-md hx-border hx-py-0.5 hx-px-[.25em] hx-text-[.9em] dark:hx-border-white/10 dark:hx-bg-white/10; } - :where(table):not(:where(.code-block table, [class~=not-prose],[class~=not-prose] *)) { + :where(table):not(:where(.hextra-code-block table, [class~=not-prose],[class~=not-prose] *)) { @apply hx-block hx-overflow-x-auto hx-mt-6 hx-p-0 first:hx-mt-0; tr { @@ -66,7 +66,7 @@ :where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)) { @apply hx-border-black hx-border-opacity-[0.04] hx-bg-opacity-[0.03] hx-bg-black hx-break-words hx-rounded-md hx-border hx-py-0.5 hx-px-[.25em] hx-text-[.9em] dark:hx-border-white/10 dark:hx-bg-white/10; } - :where(pre.mermaid):not(:where(.code-block pre, [class~=not-prose],[class~=not-prose] *)) { + :where(pre.mermaid):not(:where(.hextra-code-block pre, [class~=not-prose],[class~=not-prose] *)) { @apply hx-bg-transparent hx-rounded-none dark:hx-bg-transparent; } :where(img):not(:where([class~=not-prose],[class~=not-prose] *)) { diff --git a/assets/js/code-copy.js b/assets/js/code-copy.js index c5136782..df516593 100644 --- a/assets/js/code-copy.js +++ b/assets/js/code-copy.js @@ -25,7 +25,7 @@ document.addEventListener('DOMContentLoaded', function () { return svg; } - document.querySelectorAll('.code-copy-btn').forEach(function (button) { + document.querySelectorAll('.hextra-code-copy-btn').forEach(function (button) { // Add copy and success icons button.querySelector('.copy-icon')?.appendChild(getCopyIcon()); button.querySelector('.success-icon')?.appendChild(getSuccessIcon()); diff --git a/exampleSite/hugo_stats.json b/exampleSite/hugo_stats.json index 7e00beac..0eb78017 100644 --- a/exampleSite/hugo_stats.json +++ b/exampleSite/hugo_stats.json @@ -87,7 +87,6 @@ "before:hx-transition-transform", "before:hx-w-px", "chroma", - "code-block", "code-copy-btn", "content", "contrast-more:dark:hover:hx-border-gray-50", @@ -211,6 +210,9 @@ "hamburger-menu", "hextra-card", "hextra-cards", + "hextra-code-block", + "hextra-code-copy-btn", + "hextra-code-copy-btn-container", "hextra-feature-card", "hextra-filetree", "hextra-filetree-folder", diff --git a/layouts/_default/_markup/render-codeblock.html b/layouts/_default/_markup/render-codeblock.html index 2b65e88d..31b3c258 100644 --- a/layouts/_default/_markup/render-codeblock.html +++ b/layouts/_default/_markup/render-codeblock.html @@ -1,25 +1,12 @@ {{- $class := .Attributes.class | default "" -}} {{- $filename := .Attributes.filename | default "" -}} {{- $lang := .Attributes.lang | default .Type -}} -{{- $copyCode := (T "copyCode") | default "Copy code" -}} -
- {{- if $filename -}} -
{{ $filename }}
- {{- end -}} - {{- if transform.CanHighlight $lang -}} -
{{- highlight .Inner $lang .Options -}}
- {{- else -}} -
{{ .Inner }}
- {{- end -}} -
- -
+
+ {{ partial "components/codeblock" (dict "filename" $filename "lang" $lang "content" .Inner "options" .Options) }} + + {{- if or (eq site.Params.highlight.copy.enable nil) (site.Params.highlight.copy.enable) }} + {{- partialCached "components/codeblock-copy-button" (dict "filename" $filename) $filename }} + {{ end }}
diff --git a/layouts/partials/components/codeblock-copy-button.html b/layouts/partials/components/codeblock-copy-button.html new file mode 100644 index 00000000..f6f8b3f3 --- /dev/null +++ b/layouts/partials/components/codeblock-copy-button.html @@ -0,0 +1,15 @@ +{{/* TODO: remove filename variable */}} +{{- $filename := .filename | default "" -}} +{{- $display := site.Params.highlight.copy.display | default "hover" -}} +{{- $copyCode := (T "copyCode") | default "Copy code" -}} + + +
+ +
diff --git a/layouts/partials/components/codeblock.html b/layouts/partials/components/codeblock.html new file mode 100644 index 00000000..f96a74ab --- /dev/null +++ b/layouts/partials/components/codeblock.html @@ -0,0 +1,13 @@ +{{ $filename := .filename | default "" -}} +{{ $lang := .lang | default "" }} +{{ $content := .content }} +{{ $options := .options | default (dict) }} + +{{- if $filename -}} +
{{ $filename }}
+{{- end -}} +{{- if transform.CanHighlight $lang -}} +
{{- highlight $content $lang $options -}}
+{{- else -}} +
{{ $content }}
+{{- end -}}