Skip to content

Commit d8351aa

Browse files
authored
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
1 parent 741a640 commit d8351aa

File tree

9 files changed

+76
-59
lines changed

9 files changed

+76
-59
lines changed

assets/css/compiled/main.css

+30-30
Original file line numberDiff line numberDiff line change
@@ -1545,7 +1545,7 @@ video {
15451545
--tw-text-opacity: 1;
15461546
color: rgb(156 163 175 / var(--tw-text-opacity));
15471547
}
1548-
.content :where(pre):not(:where(.code-block pre, [class~=not-prose],[class~=not-prose] *)) {
1548+
.content :where(pre):not(:where(.hextra-code-block pre, [class~=not-prose],[class~=not-prose] *)) {
15491549
margin-bottom: 1rem;
15501550
overflow-x: auto;
15511551
border-radius: 0.75rem;
@@ -1559,23 +1559,23 @@ video {
15591559
}
15601560
@media (prefers-contrast: more) {
15611561

1562-
.content :where(pre):not(:where(.code-block pre, [class~=not-prose],[class~=not-prose] *)) {
1562+
.content :where(pre):not(:where(.hextra-code-block pre, [class~=not-prose],[class~=not-prose] *)) {
15631563
border-width: 1px;
15641564
border-color: hsl(var(--primary-hue) var(--primary-saturation) 24% / 0.2);
15651565
--tw-contrast: contrast(1.5);
15661566
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);
15671567
}
15681568
}
1569-
:is(html[class~="dark"] .content :where(pre):not(:where(.code-block pre, [class~=not-prose],[class~=not-prose] *))) {
1569+
:is(html[class~="dark"] .content :where(pre):not(:where(.hextra-code-block pre, [class~=not-prose],[class~=not-prose] *))) {
15701570
background-color: hsl(var(--primary-hue) var(--primary-saturation) 77% / 0.1);
15711571
}
15721572
@media (prefers-contrast: more) {
15731573

1574-
:is(html[class~="dark"] .content :where(pre):not(:where(.code-block pre, [class~=not-prose],[class~=not-prose] *))) {
1574+
:is(html[class~="dark"] .content :where(pre):not(:where(.hextra-code-block pre, [class~=not-prose],[class~=not-prose] *))) {
15751575
border-color: hsl(var(--primary-hue) var(--primary-saturation) 94% / 0.4);
15761576
}
15771577
}
1578-
.content :where(code):not(:where(.code-block code, [class~=not-prose],[class~=not-prose] *)) {
1578+
.content :where(code):not(:where(.hextra-code-block code, [class~=not-prose],[class~=not-prose] *)) {
15791579
overflow-wrap: break-word;
15801580
border-radius: 0.375rem;
15811581
border-width: 1px;
@@ -1589,38 +1589,38 @@ video {
15891589
padding-right: .25em;
15901590
font-size: .9em;
15911591
}
1592-
:is(html[class~="dark"] .content :where(code):not(:where(.code-block code, [class~=not-prose],[class~=not-prose] *))) {
1592+
:is(html[class~="dark"] .content :where(code):not(:where(.hextra-code-block code, [class~=not-prose],[class~=not-prose] *))) {
15931593
border-color: rgb(255 255 255 / 0.1);
15941594
background-color: rgb(255 255 255 / 0.1);
15951595
}
1596-
.content :where(table):not(:where(.code-block table, [class~=not-prose],[class~=not-prose] *)) {
1596+
.content :where(table):not(:where(.hextra-code-block table, [class~=not-prose],[class~=not-prose] *)) {
15971597
margin-top: 1.5rem;
15981598
display: block;
15991599
overflow-x: auto;
16001600
padding: 0px;
16011601
}
1602-
.content :where(table):not(:where(.code-block table, [class~=not-prose],[class~=not-prose] *)):first-child {
1602+
.content :where(table):not(:where(.hextra-code-block table, [class~=not-prose],[class~=not-prose] *)):first-child {
16031603
margin-top: 0px;
16041604
}
1605-
.content :where(table):not(:where(.code-block table, [class~=not-prose],[class~=not-prose] *)) tr {
1605+
.content :where(table):not(:where(.hextra-code-block table, [class~=not-prose],[class~=not-prose] *)) tr {
16061606
margin: 0px;
16071607
border-top-width: 1px;
16081608
--tw-border-opacity: 1;
16091609
border-color: rgb(209 213 219 / var(--tw-border-opacity));
16101610
padding: 0px;
16111611
}
1612-
.content :where(table):not(:where(.code-block table, [class~=not-prose],[class~=not-prose] *)) tr:nth-child(even) {
1612+
.content :where(table):not(:where(.hextra-code-block table, [class~=not-prose],[class~=not-prose] *)) tr:nth-child(even) {
16131613
--tw-bg-opacity: 1;
16141614
background-color: rgb(243 244 246 / var(--tw-bg-opacity));
16151615
}
1616-
:is(html[class~="dark"] .content :where(table):not(:where(.code-block table, [class~=not-prose],[class~=not-prose] *)) tr) {
1616+
:is(html[class~="dark"] .content :where(table):not(:where(.hextra-code-block table, [class~=not-prose],[class~=not-prose] *)) tr) {
16171617
--tw-border-opacity: 1;
16181618
border-color: rgb(75 85 99 / var(--tw-border-opacity));
16191619
}
1620-
:is(html[class~="dark"] .content :where(table):not(:where(.code-block table, [class~=not-prose],[class~=not-prose] *)) tr):nth-child(even) {
1620+
:is(html[class~="dark"] .content :where(table):not(:where(.hextra-code-block table, [class~=not-prose],[class~=not-prose] *)) tr):nth-child(even) {
16211621
background-color: rgb(75 85 99 / 0.2);
16221622
}
1623-
.content :where(table):not(:where(.code-block table, [class~=not-prose],[class~=not-prose] *)) th {
1623+
.content :where(table):not(:where(.hextra-code-block table, [class~=not-prose],[class~=not-prose] *)) th {
16241624
margin: 0px;
16251625
border-width: 1px;
16261626
--tw-border-opacity: 1;
@@ -1631,11 +1631,11 @@ video {
16311631
padding-bottom: 0.5rem;
16321632
font-weight: 600;
16331633
}
1634-
:is(html[class~="dark"] .content :where(table):not(:where(.code-block table, [class~=not-prose],[class~=not-prose] *)) th) {
1634+
:is(html[class~="dark"] .content :where(table):not(:where(.hextra-code-block table, [class~=not-prose],[class~=not-prose] *)) th) {
16351635
--tw-border-opacity: 1;
16361636
border-color: rgb(75 85 99 / var(--tw-border-opacity));
16371637
}
1638-
.content :where(table):not(:where(.code-block table, [class~=not-prose],[class~=not-prose] *)) td {
1638+
.content :where(table):not(:where(.hextra-code-block table, [class~=not-prose],[class~=not-prose] *)) td {
16391639
margin: 0px;
16401640
border-width: 1px;
16411641
--tw-border-opacity: 1;
@@ -1645,7 +1645,7 @@ video {
16451645
padding-top: 0.5rem;
16461646
padding-bottom: 0.5rem;
16471647
}
1648-
:is(html[class~="dark"] .content :where(table):not(:where(.code-block table, [class~=not-prose],[class~=not-prose] *)) td) {
1648+
:is(html[class~="dark"] .content :where(table):not(:where(.hextra-code-block table, [class~=not-prose],[class~=not-prose] *)) td) {
16491649
--tw-border-opacity: 1;
16501650
border-color: rgb(75 85 99 / var(--tw-border-opacity));
16511651
}
@@ -1707,11 +1707,11 @@ video {
17071707
border-color: rgb(255 255 255 / 0.1);
17081708
background-color: rgb(255 255 255 / 0.1);
17091709
}
1710-
.content :where(pre.mermaid):not(:where(.code-block pre, [class~=not-prose],[class~=not-prose] *)) {
1710+
.content :where(pre.mermaid):not(:where(.hextra-code-block pre, [class~=not-prose],[class~=not-prose] *)) {
17111711
border-radius: 0px;
17121712
background-color: transparent;
17131713
}
1714-
:is(html[class~="dark"] .content :where(pre.mermaid):not(:where(.code-block pre, [class~=not-prose],[class~=not-prose] *))) {
1714+
:is(html[class~="dark"] .content :where(pre.mermaid):not(:where(.hextra-code-block pre, [class~=not-prose],[class~=not-prose] *))) {
17151715
background-color: transparent;
17161716
}
17171717
.content :where(img):not(:where([class~=not-prose],[class~=not-prose] *)) {
@@ -2117,11 +2117,11 @@ article details > summary::before {
21172117
.dark .highlight .chroma .gl { text-decoration: underline }
21182118
/* TextWhitespace */
21192119
.dark .highlight .chroma .w { color: #6e7681 }
2120-
.code-block {
2120+
.hextra-code-block {
21212121
font-size: .9em;
21222122
line-height: 1.25rem;
21232123
}
2124-
.code-block pre {
2124+
.hextra-code-block pre {
21252125
overflow-x: auto;
21262126
background-color: hsl(var(--primary-hue) var(--primary-saturation) 39% / 0.05);
21272127
font-size: .9em;
@@ -2131,23 +2131,23 @@ article details > summary::before {
21312131
}
21322132
@media (prefers-contrast: more) {
21332133

2134-
.code-block pre {
2134+
.hextra-code-block pre {
21352135
border-width: 1px;
21362136
border-color: hsl(var(--primary-hue) var(--primary-saturation) 24% / 0.2);
21372137
--tw-contrast: contrast(1.5);
21382138
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);
21392139
}
21402140
}
2141-
:is(html[class~="dark"] .code-block pre) {
2141+
:is(html[class~="dark"] .hextra-code-block pre) {
21422142
background-color: hsl(var(--primary-hue) var(--primary-saturation) 77% / 0.1);
21432143
}
21442144
@media (prefers-contrast: more) {
21452145

2146-
:is(html[class~="dark"] .code-block pre) {
2146+
:is(html[class~="dark"] .hextra-code-block pre) {
21472147
border-color: hsl(var(--primary-hue) var(--primary-saturation) 94% / 0.4);
21482148
}
21492149
}
2150-
.code-block .filename {
2150+
.hextra-code-block .filename {
21512151
position: absolute;
21522152
top: 0px;
21532153
z-index: 1;
@@ -2166,24 +2166,24 @@ article details > summary::before {
21662166
--tw-text-opacity: 1;
21672167
color: rgb(55 65 81 / var(--tw-text-opacity));
21682168
}
2169-
:is(html[class~="dark"] .code-block .filename) {
2169+
:is(html[class~="dark"] .hextra-code-block .filename) {
21702170
background-color: hsl(var(--primary-hue) var(--primary-saturation) 77% / 0.1);
21712171
--tw-text-opacity: 1;
21722172
color: rgb(229 231 235 / var(--tw-text-opacity));
21732173
}
2174-
.code-block .filename + pre:not(.lntable pre) {
2174+
.hextra-code-block .filename + pre:not(.lntable pre) {
21752175
/* Override padding for code blocks with filename but no highlight */
21762176
padding-top: 3rem;
21772177
}
2178-
.code-block pre:not(.lntable pre) {
2178+
.hextra-code-block pre:not(.lntable pre) {
21792179
margin-bottom: 1rem;
21802180
border-radius: 0.75rem;
21812181
padding-left: 1rem;
21822182
padding-right: 1rem;
21832183
padding-top: 1rem;
21842184
padding-bottom: 1rem;
21852185
}
2186-
.code-block div:nth-of-type(2) pre {
2186+
.hextra-code-block div:nth-of-type(2) pre {
21872187
padding-top: 3rem;
21882188
padding-bottom: 1rem;
21892189
}
@@ -2542,13 +2542,13 @@ nav .search-wrapper {
25422542
@supports (
25432543
((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px)))
25442544
) {
2545-
.code-copy-btn {
2545+
.hextra-hextra-code-copy-btn {
25462546
--tw-bg-opacity: .85;
25472547
--tw-backdrop-blur: blur(12px);
25482548
-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);
25492549
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);
25502550
}
2551-
:is(html[class~="dark"] .code-copy-btn) {
2551+
:is(html[class~="dark"] .hextra-hextra-code-copy-btn) {
25522552
--tw-bg-opacity: 0.8;
25532553
}
25542554
}

assets/css/components/code-copy.css

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
@supports (
22
(-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))
33
) {
4-
.code-copy-btn {
4+
.hextra-hextra-code-copy-btn {
55
@apply hx-backdrop-blur-md hx-bg-opacity-[.85] dark:hx-bg-opacity-80;
66
}
77
}

assets/css/highlight.css

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
@import "chroma/light.css";
33
@import "chroma/dark.css";
44

5-
.code-block {
5+
.hextra-code-block {
66
@apply hx-text-[.9em] hx-leading-5;
77

88
pre {
@@ -19,11 +19,11 @@
1919
}
2020
}
2121

22-
.code-block pre:not(.lntable pre) {
22+
.hextra-code-block pre:not(.lntable pre) {
2323
@apply hx-px-4 hx-mb-4 hx-py-4 hx-rounded-xl;
2424
}
2525

26-
.code-block div:nth-of-type(2) pre {
26+
.hextra-code-block div:nth-of-type(2) pre {
2727
@apply hx-pt-12 hx-pb-4;
2828
}
2929

assets/css/typography.css

+4-4
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@
2626
:where(blockquote):not(:where([class~=not-prose],[class~=not-prose] *)) {
2727
@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;
2828
}
29-
:where(pre):not(:where(.code-block pre, [class~=not-prose],[class~=not-prose] *)) {
29+
:where(pre):not(:where(.hextra-code-block pre, [class~=not-prose],[class~=not-prose] *)) {
3030
@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;
3131
}
32-
:where(code):not(:where(.code-block code, [class~=not-prose],[class~=not-prose] *)) {
32+
:where(code):not(:where(.hextra-code-block code, [class~=not-prose],[class~=not-prose] *)) {
3333
@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;
3434
}
35-
:where(table):not(:where(.code-block table, [class~=not-prose],[class~=not-prose] *)) {
35+
:where(table):not(:where(.hextra-code-block table, [class~=not-prose],[class~=not-prose] *)) {
3636
@apply hx-block hx-overflow-x-auto hx-mt-6 hx-p-0 first:hx-mt-0;
3737

3838
tr {
@@ -66,7 +66,7 @@
6666
:where(kbd):not(:where([class~=not-prose],[class~=not-prose] *)) {
6767
@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;
6868
}
69-
:where(pre.mermaid):not(:where(.code-block pre, [class~=not-prose],[class~=not-prose] *)) {
69+
:where(pre.mermaid):not(:where(.hextra-code-block pre, [class~=not-prose],[class~=not-prose] *)) {
7070
@apply hx-bg-transparent hx-rounded-none dark:hx-bg-transparent;
7171
}
7272
:where(img):not(:where([class~=not-prose],[class~=not-prose] *)) {

assets/js/code-copy.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ document.addEventListener('DOMContentLoaded', function () {
2525
return svg;
2626
}
2727

28-
document.querySelectorAll('.code-copy-btn').forEach(function (button) {
28+
document.querySelectorAll('.hextra-code-copy-btn').forEach(function (button) {
2929
// Add copy and success icons
3030
button.querySelector('.copy-icon')?.appendChild(getCopyIcon());
3131
button.querySelector('.success-icon')?.appendChild(getSuccessIcon());

exampleSite/hugo_stats.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@
8787
"before:hx-transition-transform",
8888
"before:hx-w-px",
8989
"chroma",
90-
"code-block",
9190
"code-copy-btn",
9291
"content",
9392
"contrast-more:dark:hover:hx-border-gray-50",
@@ -211,6 +210,9 @@
211210
"hamburger-menu",
212211
"hextra-card",
213212
"hextra-cards",
213+
"hextra-code-block",
214+
"hextra-code-copy-btn",
215+
"hextra-code-copy-btn-container",
214216
"hextra-feature-card",
215217
"hextra-filetree",
216218
"hextra-filetree-folder",
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,12 @@
11
{{- $class := .Attributes.class | default "" -}}
22
{{- $filename := .Attributes.filename | default "" -}}
33
{{- $lang := .Attributes.lang | default .Type -}}
4-
{{- $copyCode := (T "copyCode") | default "Copy code" -}}
54

65

7-
<div class="code-block hx-relative hx-mt-6 first:hx-mt-0 hx-group/code">
8-
{{- if $filename -}}
9-
<div class="filename" dir="auto">{{ $filename }}</div>
10-
{{- end -}}
11-
{{- if transform.CanHighlight $lang -}}
12-
<div>{{- highlight .Inner $lang .Options -}}</div>
13-
{{- else -}}
14-
<pre><code>{{ .Inner }}</code></pre>
15-
{{- end -}}
16-
<div class="hx-opacity-0 hx-transition group-hover/code:hx-opacity-100 hx-flex hx-gap-1 hx-absolute hx-m-[11px] hx-right-0 {{ if $filename }}hx-top-8{{ else }}hx-top-0{{ end }}">
17-
<button
18-
class="code-copy-btn hx-group/copybtn hx-transition-all active:hx-opacity-50 hx-bg-primary-700/5 hx-border hx-border-black/5 hx-text-gray-600 hover:hx-text-gray-900 hx-rounded-md hx-p-1.5 dark:hx-bg-primary-300/10 dark:hx-border-white/10 dark:hx-text-gray-400 dark:hover:hx-text-gray-50"
19-
title="{{ $copyCode }}"
20-
>
21-
<div class="copy-icon group-[.copied]/copybtn:hx-hidden hx-pointer-events-none hx-h-4 hx-w-4"></div>
22-
<div class="success-icon hx-hidden group-[.copied]/copybtn:hx-block hx-pointer-events-none hx-h-4 hx-w-4"></div>
23-
</button>
24-
</div>
6+
<div class="hextra-code-block hx-relative hx-mt-6 first:hx-mt-0 hx-group/code">
7+
{{ partial "components/codeblock" (dict "filename" $filename "lang" $lang "content" .Inner "options" .Options) }}
8+
9+
{{- if or (eq site.Params.highlight.copy.enable nil) (site.Params.highlight.copy.enable) }}
10+
{{- partialCached "components/codeblock-copy-button" (dict "filename" $filename) $filename }}
11+
{{ end }}
2512
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{{/* TODO: remove filename variable */}}
2+
{{- $filename := .filename | default "" -}}
3+
{{- $display := site.Params.highlight.copy.display | default "hover" -}}
4+
{{- $copyCode := (T "copyCode") | default "Copy code" -}}
5+
6+
7+
<div class="hextra-code-copy-btn-container {{ if eq $display `hover` }}hx-opacity-0{{ end }} hx-transition group-hover/code:hx-opacity-100 hx-flex hx-gap-1 hx-absolute hx-m-[11px] hx-right-0 {{ if $filename }}hx-top-8{{ else }}hx-top-0{{ end }}">
8+
<button
9+
class="hextra-code-copy-btn hx-group/copybtn hx-transition-all active:hx-opacity-50 hx-bg-primary-700/5 hx-border hx-border-black/5 hx-text-gray-600 hover:hx-text-gray-900 hx-rounded-md hx-p-1.5 dark:hx-bg-primary-300/10 dark:hx-border-white/10 dark:hx-text-gray-400 dark:hover:hx-text-gray-50"
10+
title="{{ $copyCode }}"
11+
>
12+
<div class="copy-icon group-[.copied]/copybtn:hx-hidden hx-pointer-events-none hx-h-4 hx-w-4"></div>
13+
<div class="success-icon hx-hidden group-[.copied]/copybtn:hx-block hx-pointer-events-none hx-h-4 hx-w-4"></div>
14+
</button>
15+
</div>
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{{ $filename := .filename | default "" -}}
2+
{{ $lang := .lang | default "" }}
3+
{{ $content := .content }}
4+
{{ $options := .options | default (dict) }}
5+
6+
{{- if $filename -}}
7+
<div class="filename" dir="auto">{{ $filename }}</div>
8+
{{- end -}}
9+
{{- if transform.CanHighlight $lang -}}
10+
<div>{{- highlight $content $lang $options -}}</div>
11+
{{- else -}}
12+
<pre><code>{{ $content }}</code></pre>
13+
{{- end -}}

0 commit comments

Comments
 (0)