Files
blog/src/components/NoteVars.svelte
T
Hadi d6d410a2fa Add notes & card styling
Signed-off-by: Hadi <hadi@example.com>
2026-05-04 14:03:48 +02:00

121 lines
3.5 KiB
Svelte

<script lang="ts">
import { onMount, untrack } from "svelte";
interface Props {
vars: string[];
}
const { vars }: Props = $props();
let values = $state<Record<string, string>>(
untrack(() => Object.fromEntries(vars.map((v) => [v, ""]))),
);
let open = $state(false);
let applied = $state(false);
const originals = new Map<Text, string>();
onMount(() => {
const content = document.querySelector(".note-content");
if (!content) return;
const walker = document.createTreeWalker(content, NodeFilter.SHOW_TEXT);
let node: Text | null;
while ((node = walker.nextNode() as Text | null)) {
if (/\$[a-zA-Z_][a-zA-Z0-9_]*/.test(node.nodeValue ?? "")) {
originals.set(node, node.nodeValue!);
}
}
});
function applyVars() {
const sorted = [...vars].sort((a, b) => b.length - a.length);
for (const [node, original] of originals) {
let text = original;
for (const name of sorted) {
const val = values[name];
if (val) {
text = text.replace(
new RegExp(`\\$${name}(?![a-zA-Z0-9_])`, "g"),
val,
);
}
}
node.nodeValue = text;
}
applied = true;
setTimeout(() => (applied = false), 1800);
}
</script>
{#if vars.length > 0}
<button
onclick={() => (open = true)}
class="btn btn-ghost btn-xs font-mono text-base-content/40 hover:text-base-content/70 border border-base-300/50"
>
<span class="font-mono text-[10px] leading-none">$</span>
vars
</button>
{#if open}
<dialog class="modal modal-open">
<div
class="modal-box max-w-sm"
style="background: oklch(10% 0 0); border: 1px solid oklch(71% 0.0863 296.59 / 0.5);"
>
<h3
class="font-mono text-[10px] text-base-content/40 uppercase tracking-widest mb-4"
>
variables
</h3>
<div class="space-y-2.5">
{#each vars as v}
<div class="flex items-center gap-3">
<label
for={`var-${v}`}
class="font-mono text-xs text-primary/70 w-36 shrink-0 truncate"
title={`$${v}`}
>
${v}
</label>
<input
id={`var-${v}`}
type="text"
bind:value={values[v]}
placeholder={`$${v}`}
class="input input-sm flex-1 min-w-0 font-mono text-xs bg-base-300/20 border-base-300/60 text-base-content/80 placeholder:text-base-content/25 focus:border-primary/60"
/>
</div>
{/each}
</div>
<div class="mt-5 flex items-center justify-between">
<span
class="font-mono text-[10px] text-primary/60 transition-opacity duration-300"
class:opacity-0={!applied}
>
✓ applied
</span>
<div class="flex gap-2">
<button
onclick={applyVars}
class="btn btn-ghost btn-xs font-mono text-primary/60 hover:text-primary border border-primary/30"
>
apply
</button>
<button
onclick={() => (open = false)}
class="btn btn-ghost btn-xs font-mono text-base-content/40 hover:text-base-content/70 border border-base-300/50"
>
close
</button>
</div>
</div>
</div>
<button
onclick={() => (open = false)}
class="modal-backdrop"
aria-label="close"
></button>
</dialog>
{/if}
{/if}