Skip to main content

Misc Utilities

Small, focused helpers used across the frontend. All are importable directly from their respective modules under $lib/utils/.


debounce(func, wait)$lib/utils/debounce.js

Returns a debounced version of func that delays invoking it until wait milliseconds have elapsed since the last call. Repeated calls within the window reset the timer.

import {debounce} from '$lib/utils/debounce.js';

const search = debounce((query: string) => fetchResults(query), 300);
input.addEventListener('input', (e) => search(e.target.value));

buildStorageFileUrl(fileIdentifier)$lib/utils/storageFileProxy.js

Builds the proxied URL for a file stored in the backend file storage. The browser fetches the file through the HAWKI backend rather than hitting the storage provider directly. Returns null when fileIdentifier is falsy.

import {buildStorageFileUrl} from '$lib/utils/storageFileProxy.js';

const url = buildStorageFileUrl(attachment.file_identifier);
if (url) {
img.src = url;
}

Use this wherever an attachment or user-uploaded file needs to be displayed or downloaded.


getFileIconSvg(extension)$lib/utils/fileIconSvg.js

Returns a data:image/svg+xml,... URL for a file-type icon badge. The icon is a document shape with the uppercased extension label centred on a colour-coded banner. The banner colour is derived deterministically from the extension string, so the same extension always produces the same colour.

import {getFileIconSvg} from '$lib/utils/fileIconSvg.js';

img.src = getFileIconSvg('pdf'); // orange-ish banner, 'PDF' label
img.src = getFileIconSvg('docx'); // different colour, 'DOCX' label

Use this for attachment thumbnails when a file has no image preview.


growTransition$lib/utils/transitions/growTransition.js

A Svelte CSS transition that expands or collapses an element by animating its height or width from 0 to its natural size, fading opacity and scaling padding/margin proportionally so the element doesn't jump.

<script lang="ts">
import { growTransition } from '$lib/utils/transitions/growTransition.js';
let visible = $state(false);
</script>

<!-- Vertical grow (default) -->
{#if visible}
<div transition:growTransition>…</div>
{/if}

<!-- Horizontal grow, enter only -->
<span in:growTransition={{mode: 'horizontal'}}>…</span>
ParameterValuesDefaultDescription
direction'in' | 'out''in'Enter uses a gentle spring overshoot; leave uses cubicOut.
mode'vertical' | 'horizontal''vertical'Which dimension to animate.

Component Prop Type Helpers — $lib/utils/utils.js

Four TypeScript utility types for working with bits-ui primitive props:

import type {
WithoutChild,
WithoutChildren,
WithoutChildrenOrChild,
WithElementRef
} from '$lib/utils/utils.js';
TypePurpose
WithoutChild<T>Strips the child snippet prop from a bits-ui props type
WithoutChildren<T>Strips the children snippet prop
WithoutChildrenOrChild<T>Strips both
WithElementRef<T, U>Adds an optional ref binding so a parent can hold a reference to the underlying DOM element
// Expose a ref on a wrapped primitive
import type {WithElementRef} from '$lib/utils/utils.js';
import type {HTMLButtonAttributes} from 'svelte/elements';

interface Props extends WithElementRef<HTMLButtonAttributes, HTMLButtonElement> {
}

const {ref = $bindable(), ...rest}: Props = $props();
<button bind:this={ref} {...rest} />