<script lang="ts">
  interface Props {
    label: string;
    id?: string;
    min: number;
    max: number;
    step: number;
    value: number;
    onChange: (value: number) => void;
    /** Format display value. Defaults to String(value). */
    format?: (value: number) => string;
    disabled?: boolean;
  }

  let {
    label,
    id,
    min,
    max,
    step,
    value,
    onChange,
    format,
    disabled = false,
  }: Props = $props();

  let displayValue = $derived(format ? format(value) : String(value));

  function handleInput(e: Event) {
    const v = parseFloat((e.target as HTMLInputElement).value);
    if (!Number.isNaN(v)) onChange(v);
  }
</script>

<div class="slider-row">
  <label class="slider-label" for={id}>{label}</label>
  <input
    {id}
    type="range"
    class="slider-input"
    {min}
    {max}
    {step}
    {value}
    {disabled}
    oninput={handleInput}
  />
  <span class="slider-val">{displayValue}</span>
</div>

<style>
  .slider-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
  }

  .slider-label {
    font-size: 0.75rem;
    color: var(--ctp-subtext0);
    white-space: nowrap;
    flex-shrink: 0;
  }

  .slider-input {
    flex: 1;
    accent-color: var(--ctp-blue);
  }

  .slider-input:disabled {
    opacity: 0.4;
    cursor: not-allowed;
  }

  .slider-val {
    font-size: 0.8rem;
    color: var(--ctp-text);
    min-width: 3rem;
    text-align: right;
    font-variant-numeric: tabular-nums;
  }
</style>
