<script module lang="ts">
	import { tv, type VariantProps } from "tailwind-variants";

	export const inputField = tv({
		base: "flex min-h-9 items-center gap-2 rounded-sm border bg-background-default px-2 py-1 transition-colors",
		variants: {
			error: {
				false:
					"border-border-default focus-within:border-border-strong focus-within:ring-1 focus-within:ring-border-strong",
				true: "border-border-danger bg-background-danger-subtle text-foreground-danger focus-within:border-border-focus-danger focus-within:ring-1 focus-within:ring-border-focus-danger"
			},
			disabled: {
				false: "",
				true: "cursor-not-allowed bg-background-inset text-foreground-disabled"
			}
		},
		defaultVariants: {
			error: false,
			disabled: false
		}
	});

	export type InputFieldVariants = VariantProps<typeof inputField>;
</script>

<script lang="ts">
	import type { Component } from "svelte";
	import type { HTMLInputAttributes, SvelteHTMLElements } from "svelte/elements";
	import Spinner from "./Spinner.svelte";

	interface Props extends Omit<HTMLInputAttributes, "class" | "value"> {
		value?: string;
		error?: boolean;
		disabled?: boolean;
		loading?: boolean;
		iconLeft?: Component<SvelteHTMLElements["svg"]>;
		iconRight?: Component<SvelteHTMLElements["svg"]>;
		/** static, non-editable trailing text shown inside the field, e.g. a domain suffix */
		suffix?: string;
		/** the input itself, for callers that have to focus or measure it */
		element?: HTMLInputElement;
		class?: string;
	}

	let {
		value = $bindable(""),
		error = false,
		disabled = false,
		loading = false,
		iconLeft,
		iconRight,
		suffix,
		element = $bindable(),
		class: className,
		...rest
	}: Props = $props();

	const classes = $derived(inputField({ error, disabled, class: className }));
</script>

<div class={classes}>
	{#if iconLeft}
		{@const IconLeft = iconLeft}
		<IconLeft class="size-4 shrink-0 text-foreground-subtle" aria-hidden="true" />
	{/if}
	<input
		bind:this={element}
		bind:value
		{disabled}
		aria-invalid={error}
		aria-busy={loading}
		class="flex-1 bg-transparent outline-none placeholder:text-foreground-placeholder disabled:cursor-not-allowed mt-px"
		{...rest}
	/>
	{#if suffix}
		<span class="shrink-0 text-foreground-muted select-none">{suffix}</span>
	{/if}
	{#if loading}
		<Spinner class="size-4 shrink-0" />
	{:else if iconRight}
		{@const IconRight = iconRight}
		<IconRight class="size-4 shrink-0 text-foreground-subtle" aria-hidden="true" />
	{/if}
</div>
