scanfile/server/web/templui/components/code/code.templ

197 lines
5.6 KiB
Text

// templui component code - version: main installed by templui v0.71.0
package code
import (
"git.jmbit.de/jmb/scanfile/server/web/templui/components/icon"
"git.jmbit.de/jmb/scanfile/server/web/templui/utils"
)
type Size string
const (
SizeSm Size = "sm"
SizeLg Size = "lg"
SizeFull Size = "full"
)
type Props struct {
ID string
Class string
Attrs templ.Attributes
Language string
ShowCopyButton bool
Size Size
CodeClass string
}
templ Code(props ...Props) {
@Script()
{{ var p Props }}
if len(props) > 0 {
{{ p = props[0] }}
}
if p.ID == "" {
{{ p.ID = "code-" + utils.RandomID() }}
}
<div
id={ p.ID }
class={ utils.TwMerge("relative code-component", p.Class) }
data-code-component
{ p.Attrs... }
>
<pre class="overflow-hidden!">
<code
data-code-block
class={
utils.TwMerge(
"language-"+p.Language,
"overflow-y-auto! rounded-md block text-sm max-h-[501px]",
utils.If(p.Size == SizeSm, "max-h-[250px]"),
utils.If(p.Size == SizeLg, "max-h-[1000px]"),
utils.If(p.Size == SizeFull, "max-h-full"),
"hljs-target",
p.CodeClass,
),
}
>
{ children... }
</code>
</pre>
if p.ShowCopyButton {
<button
data-copy-button
type="button"
class="absolute top-2 right-2 hover:bg-gray-500 hover:bg-opacity-30 text-white p-2 rounded"
>
<span data-icon-check class="hidden">
@icon.Check(icon.Props{Size: 14})
</span>
<span data-icon-clipboard>
@icon.Clipboard(icon.Props{Size: 14})
</span>
</button>
}
</div>
}
var handle = templ.NewOnceHandle()
templ Script() {
@handle.Once() {
<link rel="stylesheet" href="/assets/pojoaque.min.css"/>
<script nonce={ templ.GetNonce(ctx) } src="/assets/highlight.min.js"></script>
<script nonce={ templ.GetNonce(ctx) }>
(function() { // IIFE Start
function whenHljsReady(callback, attempt = 1) {
if (typeof hljs !== "undefined") {
callback();
} else if (attempt < 20) { // Retry for a few seconds
setTimeout(() => whenHljsReady(callback, attempt + 1), 100);
} else {
console.error("highlight.js (hljs) failed to load after several attempts.");
}
}
function fallbackCopyText(text, iconCheck, iconClipboard) {
const textArea = document.createElement('textarea');
textArea.value = text;
textArea.style.position = 'fixed';
textArea.style.top = '-9999px';
textArea.style.left = '-9999px';
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
if (document.execCommand('copy')) {
iconCheck.style.display = 'inline';
iconClipboard.style.display = 'none';
setTimeout(() => {
iconCheck.style.display = 'none';
iconClipboard.style.display = 'inline';
}, 2000);
}
} catch (err) {
console.error('Fallback copy failed', err);
} finally {
document.body.removeChild(textArea);
}
}
function initCode(component) {
if (!component || component._codeInitialized) return; // Basic initialized check
const codeBlock = component.querySelector('[data-code-block]');
const copyButton = component.querySelector('[data-copy-button]');
const iconCheck = component.querySelector('[data-icon-check]');
const iconClipboard = component.querySelector('[data-icon-clipboard]');
// Highlight if hljs is available and not already highlighted
if (codeBlock && typeof hljs !== 'undefined') {
if (!codeBlock.classList.contains('hljs')) {
hljs.highlightElement(codeBlock);
}
}
// Setup copy button if elements exist
if (copyButton && codeBlock && iconCheck && iconClipboard) {
// Remove previous listener if any (important for re-initialization)
const oldListener = copyButton._copyListener;
if (oldListener) {
copyButton.removeEventListener('click', oldListener);
}
const newListener = () => {
const codeToCopy = codeBlock.textContent || '';
const showCopied = () => {
iconCheck.style.display = 'inline';
iconClipboard.style.display = 'none';
setTimeout(() => {
iconCheck.style.display = 'none';
iconClipboard.style.display = 'inline';
}, 2000);
};
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(codeToCopy)
.then(showCopied)
.catch(() => fallbackCopyText(codeToCopy, iconCheck, iconClipboard));
} else {
fallbackCopyText(codeToCopy, iconCheck, iconClipboard);
}
};
copyButton.addEventListener('click', newListener);
copyButton._copyListener = newListener; // Store listener for removal
}
component._codeInitialized = true; // Mark as initialized
}
function initAllComponents(root = document) {
if (root instanceof Element && root.matches('[data-code-component]')) {
initCode(root);
}
for (const component of root.querySelectorAll('[data-code-component]')) {
initCode(component);
}
}
const handleHtmxSwap = (event) => {
const target = event.detail.target || event.detail.elt;
if (target instanceof Element) {
whenHljsReady(() => initAllComponents(target));
}
};
initAllComponents();
document.addEventListener('DOMContentLoaded', () => {
whenHljsReady(() => initAllComponents());
});
document.body.addEventListener('htmx:afterSwap', handleHtmxSwap);
document.body.addEventListener('htmx:oobAfterSwap', handleHtmxSwap);
})(); // IIFE End
</script>
}
}