Skip to content

Svelte 5 Version now available #9

Open
@evdama

Description

@evdama

I migrated the code to Svelte 5... feel free to update:

<script lang="ts">
  import { tick } from 'svelte'

  let { cols = 20, inputClasses = '', labelClasses = '', onblur = () => {}, options = [], placeholder = '', rows = 2, selectCaret, type = 'text', value = $bindable() } = $props()

  let editing = $state(false)
  let inputEl: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | undefined = $state()
  let selectedIndex = $state(options.findIndex(o => o.value === value))

  let isNumber = $derived(type === 'number')
  let isSelect = $derived(type === 'select')
  let isText = $derived(type === 'text')
  let isTextArea = $derived(type === 'textarea')
  let label = $derived.by(() => {
    if (isNumber) return value === '' ? placeholder : value
    if (isText || isTextArea) return value ? value : placeholder
    return selectedIndex === -1 ? placeholder : options[selectedIndex].label
  })

  const toggle = async () => {
    editing = !editing

    if (editing) {
      await tick()
      inputEl.focus()
    }
  }

  const handleInput = (e: InputEvent) => {
    const target = e.target as HTMLInputElement // Narrowing the type of the target
    value = isNumber ? +target.value : target.value // Using the unary plus operator to cast a string to number if isNumber is true
  }

  const handleEnter = (e: KeyboardEvent) => {
    if (e.key === 'Enter') inputEl.blur()
  }

  const handleBlur = () => {
    toggle()
    onblur?.(value)
  }

  const handleChange = (e: Event) => {
    const target = e.target as HTMLSelectElement // Narrowing the type of the target
    selectedIndex = placeholder ? target.selectedIndex - 1 : target.selectedIndex
    value = options[selectedIndex].value
  }
</script>

{#if editing && (isText || isNumber)}
  <input bind:this={inputEl} class={inputClasses} {type} {value} {placeholder} oninput={handleInput} onkeyup={handleEnter} onblur={handleBlur} />
{:else if editing && isTextArea}
  <textarea bind:this={inputEl} class={inputClasses} {value} {rows} {cols} {placeholder} oninput={handleInput} onblur={handleBlur}></textarea>
{:else if editing && isSelect}
  <select bind:this={inputEl} class={inputClasses} {value} onchange={handleChange} onblur={handleBlur}>
    {#if placeholder}
      <option selected value disabled>{placeholder}</option>
    {/if}

    {#each options as { label, value }}
      <option {value}>{label}</option>
    {/each}
  </select>
{:else}
  <button type="button" class={labelClasses} onclick={toggle}>
    {label}

    {#if isSelect && selectCaret}
      {@render selectCaret()}
    {:else if isSelect}
      <span>&#9660;</span>
    {/if}
  </button>
{/if}

<style>
  button {
    all: unset;
  }
</style>

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions