Skip to content

Commit 52dc869

Browse files
committed
chore: initial commit
0 parents  commit 52dc869

File tree

28 files changed

+571
-0
lines changed

28 files changed

+571
-0
lines changed

.github/FUNDING.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
ko_fi: razonyang
2+
custom:
3+
- https://paypal.me/razonyang

.github/workflows/gh-pages.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: GitHub Pages
2+
on:
3+
push:
4+
branches:
5+
- main
6+
permissions:
7+
contents: write
8+
jobs:
9+
build-and-deploy:
10+
concurrency: ci-${{ github.ref }}
11+
runs-on: ubuntu-latest
12+
steps:
13+
- name: Checkout 🛎️
14+
uses: actions/checkout@v3
15+
with:
16+
submodules: true
17+
fetch-depth: 0
18+
19+
- name: Setup Hugo
20+
uses: peaceiris/actions-hugo@v2
21+
with:
22+
hugo-version: 'latest'
23+
extended: true
24+
25+
- name: Build
26+
run: cd exampleSite && hugo --minify --gc
27+
28+
- name: Deploy 🚀
29+
uses: peaceiris/actions-gh-pages@v3
30+
with:
31+
github_token: ${{ secrets.GITHUB_TOKEN }}
32+
publish_dir: ./exampleSite/public

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
public/
2+
resources/
3+
.hugo_build.lock
4+
node_modules/
5+
package-lock.json

.prettierrc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"bracketSameLine": true,
3+
"goTemplateBracketSpacing": true,
4+
"overrides": [
5+
{
6+
"files": [
7+
"*.html"
8+
],
9+
"options": {
10+
"parser": "go-template",
11+
"bracketSameLine": true
12+
}
13+
}
14+
]
15+
}

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2023 Hugo Modules Authors
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Hugo Code Block Panel Module
2+
3+
A simple code block panel for Hugo, [preview it online](https://projects.razonyang.com/hugo-mod-code-block-panel/).
4+
5+
## Features
6+
7+
- Expand toggle.
8+
- Copy code.
9+
- Line number toggle.
10+
- Wrap toggle.
11+
12+
## Installation
13+
14+
### 1.Import the Module
15+
16+
```toml
17+
[[module.imports]]
18+
path = "github.com/hugomods/code-block-panel"
19+
```
20+
21+
### 2. Tweak Configuration
22+
23+
The module requires the following configuration to be set as corresponding values.
24+
25+
```toml
26+
[markup.highlight]
27+
lineNos = true
28+
lineNumbersInTable = false
29+
```
30+
31+
### 3. Import CSS
32+
33+
You'll need import two SCSS files first.
34+
35+
```scss
36+
// assets/main.scss
37+
@import "snackbar/scss/index"; // used to show the result of copying code.
38+
@import "code-block-panel/scss/index";
39+
```
40+
41+
> The import paths is relative to the `assets` folder.
42+
43+
And then transform it to CSS in partial.
44+
45+
```go
46+
{{ $css := resources.Get "main.scss" }}
47+
{{ $css = $css | toCSS }}
48+
<link rel="stylesheet" href="{{ $css.RelPermalink }}" />
49+
```
50+
51+
### 4. Import JS
52+
53+
Import JS by using Hugo Pipe.
54+
55+
```go
56+
{{ $js := resources.Get "main.ts" | js.Build }}
57+
{{ $codeJS := partialCached "code-block-panel/assets/js-resource" . }}
58+
{{ $js = slice $js $searchJS | resources.Concat "js/main.js" }}
59+
<script src="{{ $js.RelPermalink }}" defer></script>
60+
```
61+
62+
Or import JS via partial.
63+
64+
```go
65+
{{ partialCached "code-block-panel/assets/js" . }}
66+
```
67+
68+
## Parameters
69+
70+
```toml
71+
[params.code_block_panel]
72+
max_lines = 10
73+
# ...
74+
```
75+
76+
| Name | Type | Default | Description
77+
|---|---|---|---
78+
| `line_nos` | Boolean | `true` | Show/Hide the line numbers by default.
79+
| `max_lines` | Number | `10` | The max visible lines.
80+
| `wrap` | Boolean | `false` | When `true`, wrap the code by default.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { default as params } from '@params'
2+
import Translator from "mods/i18n/translator"
3+
4+
const i18n = new Translator(params.i18n, params.defaultLang)
5+
6+
export default i18n
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import Panel from './panel';
2+
3+
(() => {
4+
'use strict'
5+
6+
document.addEventListener('DOMContentLoaded', () => {
7+
document.querySelectorAll('pre code').forEach((code) => {
8+
(new Panel(code as HTMLElement)).init()
9+
})
10+
})
11+
12+
})()
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import { default as params } from '@params';
2+
import snackbar from 'mods/snackbar/js/index.ts';
3+
import i18n from './i18n';
4+
5+
export default class Panel {
6+
private pre: HTMLElement
7+
8+
private wrapper: HTMLElement
9+
10+
private ele: HTMLElement
11+
12+
constructor(private code: HTMLElement) {
13+
}
14+
15+
init() {
16+
if (!params.line_nos) {
17+
this.code.classList.add('code-no-ln')
18+
}
19+
if (params.wrap) {
20+
this.code.classList.add('code-wrap')
21+
}
22+
23+
this.pre = this.code.parentElement as HTMLElement
24+
25+
this.ele = document.createElement('div')
26+
this.ele.className = 'code-block-panel'
27+
28+
this.wrapper = document.createElement('div')
29+
this.wrapper.className = 'code-block-panel-wrapper'
30+
this.wrapper.appendChild(this.ele)
31+
32+
this.maxLines()
33+
this.language()
34+
this.lineNoButton()
35+
this.wrapButton()
36+
this.expandButton()
37+
this.copyButton()
38+
39+
this.pre.appendChild(this.wrapper)
40+
}
41+
42+
// Returns the lines of code block.
43+
private lines(): Array<HTMLElement> {
44+
return Array.from(this.code.querySelectorAll(':scope > span'))
45+
}
46+
47+
private maxHeight
48+
49+
private maxLines() {
50+
const lines = this.lines()
51+
if (params.max_lines > 0 && lines.length > params.max_lines) {
52+
const offsetTop = lines[params.max_lines - 1].offsetTop
53+
if (offsetTop > 0) {
54+
this.pre.style.maxHeight = this.maxHeight = offsetTop + 'px'
55+
}
56+
}
57+
}
58+
59+
// Show the code language.
60+
private language() {
61+
const lang = this.code.getAttribute('data-lang')
62+
if (!lang || lang === 'fallback') {
63+
return
64+
}
65+
66+
const e = document.createElement('span')
67+
e.className = 'code-block-lang'
68+
e.innerText = lang
69+
this.pre.appendChild(e)
70+
}
71+
72+
private button(name: string, callback: CallableFunction): HTMLButtonElement {
73+
const btn = document.createElement('button')
74+
btn.className = 'code-block-action code-block-action-' + name
75+
btn.innerHTML = params.icons[name]
76+
btn.addEventListener('click', () => {
77+
callback()
78+
})
79+
return btn
80+
}
81+
82+
private copyButton() {
83+
const btn = this.button('copy', () => {
84+
this.copy()
85+
})
86+
this.ele.appendChild(btn)
87+
}
88+
89+
private copy() {
90+
const clone = this.code.cloneNode(true) as HTMLElement;
91+
// remove line numbers.
92+
clone.querySelectorAll('.ln, :scope > span > span:first-child').forEach((ln) => {
93+
ln.remove();
94+
});
95+
navigator.clipboard.writeText(clone.innerText).then(() => {
96+
snackbar.add(i18n.translate('copied'))
97+
}).catch((err) => {
98+
snackbar.add(i18n.translate('copy_failed'))
99+
console.error(err)
100+
})
101+
}
102+
103+
private wrapButton() {
104+
const btn = this.button('wrap', () => {
105+
this.toggleClass('code-wrap')
106+
})
107+
this.ele.appendChild(btn)
108+
}
109+
110+
private toggleClass(className: string) {
111+
112+
if (this.code.classList.contains(className)) {
113+
this.code.classList.remove(className)
114+
return
115+
}
116+
117+
this.code.classList.add(className)
118+
}
119+
120+
private lineNoButton() {
121+
const btn = this.button('ln', () => {
122+
this.toggleClass('code-no-ln')
123+
})
124+
this.ele.appendChild(btn)
125+
}
126+
127+
private expandButton() {
128+
const btn = this.button('expand', () => {
129+
this.expand()
130+
})
131+
this.ele.appendChild(btn)
132+
}
133+
134+
private expand() {
135+
if (!this.pre.style.maxHeight && !this.maxHeight) {
136+
return
137+
}
138+
139+
if (this.pre.style.maxHeight) {
140+
this.pre.style.maxHeight = ''
141+
return
142+
}
143+
144+
this.pre.style.maxHeight = this.maxHeight
145+
}
146+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
.highlight {
2+
position: relative;
3+
4+
&:hover,
5+
&:focus {
6+
.code-block-panel {
7+
display: block;
8+
}
9+
}
10+
11+
pre {
12+
max-height: 560px;
13+
overflow: auto;
14+
padding: 0.75rem 0.5rem;
15+
16+
code {
17+
word-break: break-all;
18+
19+
&.code-no-ln {
20+
.ln,
21+
> span > span:first-child {
22+
display: none;
23+
}
24+
}
25+
26+
&.code-wrap {
27+
white-space: pre-wrap !important;
28+
}
29+
}
30+
31+
.code-block-lang {
32+
font-style: italic;
33+
font-weight: bold;
34+
opacity: 0.5;
35+
position: absolute;
36+
right: 1.25rem;
37+
text-transform: uppercase;
38+
top: 0.25rem;
39+
}
40+
}
41+
}
42+
43+
.code-block-panel-wrapper {
44+
position: absolute;
45+
top: 0.5rem;
46+
right: 1rem;
47+
}
48+
49+
.code-block-panel {
50+
display: none;
51+
position: sticky;
52+
}
53+
54+
.code-block-action {
55+
background: #323232;
56+
border: none;
57+
border-radius: 0.25rem;
58+
color: white;
59+
cursor: pointer;
60+
display: inline-flex;
61+
margin: 0 0.25rem;
62+
padding: 0.25rem;
63+
64+
svg {
65+
vertical-align: -0.125rem;
66+
}
67+
}

0 commit comments

Comments
 (0)