Input Elements

Toggle Lock

A compact locking checkbox. Name its action with aria-label and add optional long-delay help. The app decides what locking means. This basic example disables the Scale input while locked.

×

/routes/forms/toggles-spec/+page.svelte

<script lang="ts">
	import { NumberInput, ToggleLock } from '@leon8ix/jhana-ui';
	import { tooltip } from '@leon8ix/jhana-ui/actions';

	let value = $state(1);
	let disabled = $state(false);
</script>

<div class="fields" style:grid-template-columns="minmax(0, max-content) auto">
	<NumberInput label="Scale" unit="×" bind:value min={0.1} step={0.1} {disabled} />
	<div class="field-accessory">
		<ToggleLock
			aria-label="Lock scale"
			bind:checked={disabled}
			{@attach tooltip('Prevent changes to the scale.', { delay: 'long' })}
		/>
	</div>
</div>

Toggle Link

Keep related Width and Height fields equal and place the link in a compact accessory column. This example captures their current ratio when linked. Changes to either dimension then update the other.

mm
mm

/routes/forms/toggles-spec/+page.svelte

<script lang="ts">
	import { NumberInput, ToggleLink } from '@leon8ix/jhana-ui';
	import { tooltip } from '@leon8ix/jhana-ui/actions';
	import { rnd } from '@leon8ix/utils';
	import type { InputEvent } from '@leon8ix/utils/dom';

	let v1: number | null = $state(210);
	let v2: number | null = $state(297);
	let ratio: number | null = $state(null);

	function link(e: InputEvent) {
		if (!e.currentTarget.checked) ratio = null;
		else ratio = (v1 || (v1 = 1)) / (v2 || (v2 = 1));
	}
	function getNum(e: InputEvent) {
		const val = e.currentTarget.value;
		if (!val.length) return null;
		const num = Number(val);
		return isNaN(num) ? null : num;
	}
	function setV1(e: InputEvent) {
		const val = getNum(e);
		v1 = val;
		if (ratio) v2 = val ? val / ratio : null;
	}
	function setV2(e: InputEvent) {
		const val = getNum(e);
		v2 = val;
		if (ratio) v1 = val ? val * ratio : null;
	}
</script>

<div class="linked-fields">
	<div class="fields equal">
		<NumberInput label="Width" unit="mm" min={1} value={v1 && rnd(v1, 4)} oninput={setV1} />
		<div class="field-accessory">
			<ToggleLink
				aria-label="Keep aspect ratio"
				checked={!!ratio}
				oninput={link}
				{@attach tooltip('Keep the current width-to-height ratio.', { delay: 'long' })}
			/>
		</div>
		<NumberInput label="Height" unit="mm" min={1} value={v2 && rnd(v2, 4)} oninput={setV2} />
	</div>
</div>

<style>
	.linked-fields {
		container-type: inline-size;
	}
	.fields {
		grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
	}
	@container (max-width: 22em) {
		.fields {
			grid-template-columns: minmax(0, 1fr);
		}
		.field-accessory {
			justify-items: center;
		}
	}
</style>

Scaling controls

These are Infinipose's Source file bleed and Target dimensions rows, with translated labels and the same units, compact link/lock accessories and long-delay help. This composition sample only updates the individual values and checkbox states. The document scaling and bleed calculations belong to the app.

Source file bleed
mm
mm
mm
Target dimensions
×
mm
mm

/routes/forms/toggles-spec/+page.svelte

<script lang="ts">
	import { NumberInput, ToggleLink, ToggleLock } from '@leon8ix/jhana-ui';
	import { tooltip } from '@leon8ix/jhana-ui/actions';

	let linkBleed = $state(true);
	let linkSize = $state(true);
	let locked = $state(false);
	type Config = Record<'bleed' | 'bleedX' | 'bleedY' | 'scale' | 'width' | 'height', number | null>;
	let config: Config = $state({ bleed: 2, bleedX: 2, bleedY: 2, scale: 1, width: 210, height: 297 });
</script>

<div class="stack linked-settings">
	<fieldset class="field-group">
		<legend>Source file bleed</legend>
		<div class="fields equal">
			<NumberInput label="Bleed" unit="mm" bind:value={config.bleed} min={0} />
			<div class="field-accessory">
				<ToggleLock
					aria-label="Keep bleed when scaling"
					bind:checked={locked}
					{@attach tooltip('Keep the bleed constant when scaling the page.', { delay: 'long' })}
				/>
			</div>
			<NumberInput label="X" unit="mm" bind:value={config.bleedX} min={0} />
			<div class="field-accessory">
				<ToggleLink
					aria-label="Link bleed axes"
					bind:checked={linkBleed}
					{@attach tooltip('Change the bleed on both axes together.', { delay: 'long' })}
				/>
			</div>
			<NumberInput label="Y" unit="mm" bind:value={config.bleedY} min={0} />
		</div>
	</fieldset>
	<fieldset class="field-group">
		<legend>Target dimensions</legend>
		<div class="fields equal">
			<NumberInput label="Scale" unit="×" bind:value={config.scale} step={0.1} min={0.1} />
			<div class="field-accessory">
				<ToggleLock
					aria-label="Keep scale when changing bleed"
					checked={!locked}
					oninput={() => (locked = !locked)}
					{@attach tooltip('Keep the scale constant when changing the bleed.', { delay: 'long' })}
				/>
			</div>
			<NumberInput label="Width" unit="mm" bind:value={config.width} min={1} />
			<div class="field-accessory">
				<ToggleLink
					aria-label="Keep aspect ratio"
					bind:checked={linkSize}
					{@attach tooltip('Keep the width-to-height ratio of the source page.', { delay: 'long' })}
				/>
			</div>
			<NumberInput label="Height" unit="mm" bind:value={config.height} min={1} />
		</div>
	</fieldset>
</div>

<style>
	.linked-settings {
		container-type: inline-size;
	}
	.fields {
		grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr) auto minmax(0, 1fr);
	}
	@container (max-width: 28em) {
		.fields {
			grid-template-columns: minmax(0, 1fr);
		}
		.field-accessory {
			justify-items: center;
		}
	}
</style>