197 lines
5.7 KiB
Text
197 lines
5.7 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="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/pojoaque.min.css"/>
|
|
<script nonce={ templ.GetNonce(ctx) } src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/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>
|
|
}
|
|
}
|