page TimePickercomponent { seo { title = "TimePicker component" description = "Theme-aware, responsive time picker component." } view {
W WRNexusJS
forms component

TimePicker

Theme-aware, responsive time picker component.

@wrnexus/ui 0.5.026 props0 slots7 events

Usage

Components are auto-discovered. Use the component directly in any .wrn view:

<TimePicker
  size="default"
  color="primary"
  id="id"
  name="name"
  label="Time"
/>

Legacy mount name: data-component="TimePicker".

Props and attributes

PropTypeRequirementDefault
sizestringOptional"default"
colorstringOptional"primary"
idstringOptional""
namestringOptional""
labelstringOptional"Time"
hiddenLabelbooleanOptionalfalse
valuestringOptional""
placeholderstringOptional""
variantstringOptional"normal"
iconstringOptional"icon-[lucide--clock-3]"
iconPositionstringOptional"end"
helperTextstringOptional""
cornerHintstringOptional""
errorstringOptional""
inlinebooleanOptionalfalse
minstringOptional""
maxstringOptional""
stepstringOptional""
formatstringOptional"24"
minuteStepnumberOptional5
hoursstringOptional["00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23"]
minutesstringOptional["00", "05", "10", "15", "20", "25", "30", "35", "40", "45", "50", "55"]
readonlybooleanOptionalfalse
disabledbooleanOptionalfalse
requiredbooleanOptionalfalse
classstringOptional""

Slots

This component does not declare a slot.

Events

  • input
  • change
  • focus
  • blur
  • open
  • close
  • invalid

Source contract

Installed source: components/TimePicker.wrn

component TimePicker {
  props {
    @event input = function
    @event change = function
    @event focus = function
    @event blur = function
    @event open = function
    @event close = function
    @event invalid = function
    size = "default"
    color = "primary"
    id = ""
    name = ""
    label = "Time"
    hiddenLabel = false
    value = ""
    placeholder = ""
    variant = "normal"
    icon = "icon-[lucide--clock-3]"
    iconPosition = "end"
    helperText = ""
    cornerHint = ""
    error = ""
    inline = false
    min = ""
    max = ""
    step = ""
    format = "24"
    minuteStep = 5
    hours = ["00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23"]
    minutes = ["00", "05", "10", "15", "20", "25", "30", "35", "40", "45", "50", "55"]
    readonly = false
    disabled = false
    required = false
    class = ""
  }
  state currentValue = value
  state selectedHour = value ? value.split(":")[0] : "00"
  state selectedMinute = value ? value.split(":")[1] : "00"
  state expanded = false
  functions {
    function commit(part, nextValue, sourceEvent, next, detail) { if (part === "hour") { selectedHour = String(nextValue).padStart(2, "0") } if (part === "minute") { selectedMinute = String(nextValue).padStart(2, "0") } next = selectedHour + ":" + selectedMinute; if ((min && next < min) || (max && next > max)) { return } currentValue = next; detail = { value: currentValue, hour: selectedHour, minute: selectedMinute, name: name, sourceEvent: sourceEvent }; $emit("input", detail); $emit("change", detail) }
    function selectTimePart(sourceEvent, root, input, parts, hourValue, minuteValue, next, trigger, detail) { root = sourceEvent.currentTarget.closest(".wire-next--time-picker"); input = root.querySelector(".wire-next__time-value"); parts = String(input.value || value || "00:00").split(":"); hourValue = sourceEvent.currentTarget.dataset.part === "hour" ? sourceEvent.currentTarget.dataset.value : parts[0]; minuteValue = sourceEvent.currentTarget.dataset.part === "minute" ? sourceEvent.currentTarget.dataset.value : parts[1]; next = hourValue + ":" + minuteValue; if ((min && next < min) || (max && next > max)) { return } currentValue = next; input.setAttribute("value", next); trigger = root.querySelector(".wire-next__time-trigger span"); if (trigger) { trigger.replaceChildren(next) } sourceEvent.currentTarget.setAttribute("aria-pressed", "true"); detail = { value: next, hour: hourValue, minute: minuteValue, name: name, sourceEvent: sourceEvent }; $emit("input", detail); $emit("change", detail) }
    function openPicker(sourceEvent) { expanded = true; $emit("open", { value: currentValue, name: name, sourceEvent: sourceEvent }) }
    function closePicker(sourceEvent) { expanded = false; $emit("close", { value: currentValue, name: name, sourceEvent: sourceEvent }) }
    function handleFocus(sourceEvent) { $emit("focus", { value: currentValue, name: name, sourceEvent: sourceEvent }) }
    function handleBlur(sourceEvent) { $emit("blur", { value: currentValue, name: name, sourceEvent: sourceEvent }) }
    function handleInvalid(sourceEvent) { $emit("invalid", { name: name, message: sourceEvent.currentTarget.validationMessage, sourceEvent: sourceEvent }) }
  }
  view {
        <div {...attrs} class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--field wire-next--time-picker {class}" data-variant="{variant}" data-inline="{inline}" data-floating="{variant === 'floating'}" data-invalid="{error ? 'true' : 'false'}" data-expanded="{expanded}">
        <div class="wire-next__field-heading">
        <label class="{hiddenLabel ? 'wire-next__sr-only' : ''}" for="{id || name}">{label}</label>{#if cornerHint}<span class="wire-next__field-hint">{cornerHint}</span>{/if}</div>
        <div class="wire-next__field-control" data-icon-position="{iconPosition}">
        {#if icon}<span class="{icon} wire-next__field-icon" aria-hidden="true">
        </span>{/if}
        <input id="{id || name}" class="wire-next__sr-only wire-next__time-value" type="text" name="{name}" value="{currentValue}" required="{required}" readonly aria-hidden="true" tabindex="-1" @invalid="handleInvalid(event)" />
        <button type="button" class="wire-next__time-trigger" disabled="{disabled || readonly}" aria-haspopup="dialog" aria-expanded="{expanded}" @click="openPicker(event)" @focus="handleFocus(event)" @blur="handleBlur(event)">
        <span>{currentValue || placeholder || "Select time"}</span>
        <i class="{icon}" aria-hidden="true">
        </i>
        </button>
        </div>
        <div class="wire-next__time-panel" role="dialog" aria-label="{label}">
        <div>
        <strong>Hour</strong>
        <div class="wire-next__time-options">{#each hours as hour}<button type="button" data-part="hour" data-value="{hour}" aria-pressed="{String(hour) === selectedHour}" @click="selectTimePart(event)">{hour}</button>{/each}</div>
        </div>
        <div>
        <strong>Minute</strong>
        <div class="wire-next__time-options">{#each minutes as minute}<button type="button" data-part="minute" data-value="{minute}" aria-pressed="{String(minute) === selectedMinute}" @click="selectTimePart(event)">{minute}</button>{/each}</div>
        </div>
        <button type="button" class="wire-next__time-done" @click="closePicker(event)">Done</button>
        </div>
      {#if helperText}<small class="wire-next__field-help">{helperText}</small>{/if}
      <small class="wire-next__field-error" data-error="{name}">{error}</small>
        </div>
    }
}
} }