feat: unit descriptions with tooltip action and admin textarea

This commit is contained in:
Falkan
2026-03-17 19:55:09 -04:00
parent 9a8b82eca5
commit 15b763e062
4 changed files with 49 additions and 3 deletions

View File

@@ -2,6 +2,7 @@
import type { Big } from 'big.js';
import type { Unit } from '$lib/types';
import { formatBig } from '$lib/format';
import { tooltip } from '$lib/actions/tooltip';
/**
* ConversionResult — displays one conversion result tile.
@@ -65,7 +66,7 @@
{#if isYolo}<span class="yolo-tilde">~</span>{/if}{formattedValue}
</span>
{/if}
<span class="result-label">{unit.labelPlural} ({unit.symbol})</span>
<span class="result-label" use:tooltip={unit.description ?? ''}>{unit.labelPlural} ({unit.symbol})</span>
</div>
<style>

View File

@@ -0,0 +1,40 @@
/**
* Svelte action: show a native `title` tooltip after a 500ms hover delay.
* If text is empty, does nothing.
*/
export function tooltip(node: HTMLElement, text: string) {
let timer: ReturnType<typeof setTimeout> | null = null;
function enter() {
if (!text) return;
timer = setTimeout(() => {
node.title = text;
}, 500);
}
function leave() {
if (timer !== null) {
clearTimeout(timer);
timer = null;
}
node.removeAttribute('title');
}
node.addEventListener('mouseenter', enter);
node.addEventListener('mouseleave', leave);
return {
update(newText: string) {
text = newText;
// If title is currently set, update it immediately
if (node.title) {
node.title = newText || '';
}
},
destroy() {
if (timer !== null) clearTimeout(timer);
node.removeEventListener('mouseenter', enter);
node.removeEventListener('mouseleave', leave);
}
};
}

View File

@@ -464,6 +464,10 @@
<small class="tobase-hint">{toBaseHint}</small>
{/if}
</label>
<label>
Description <small class="muted">(optional — shown as tooltip in converter)</small>
<textarea bind:value={unitForm.description} placeholder="e.g. A unit based on…" rows="2"></textarea>
</label>
<div class="form-actions">
<button onclick={saveUnit}>{unitFormNew ? 'Create' : 'Save'}</button>
{#if !unitFormNew}

View File

@@ -16,7 +16,7 @@ export const POST: RequestHandler = async ({ request }) => {
const body = await request.json().catch(() => null);
if (!body) return json({ error: 'Invalid JSON' }, { status: 400 });
const { id, label, labelPlural, symbol, group, toBase } = body as Partial<Unit>;
const { id, label, labelPlural, symbol, group, toBase, description } = body as Partial<Unit>;
// Validation
if (!id || !KEBAB_RE.test(id)) {
@@ -44,7 +44,8 @@ export const POST: RequestHandler = async ({ request }) => {
labelPlural,
symbol,
group: group ?? null,
toBase
toBase,
...(description ? { description } : {})
};
data.units.push(newUnit);
saveData(data);