Class: AutocompleteComponent

Inherits:
Funicular::Component
  • Object
show all
Defined in:
lib/components/autocomplete_component.rb

Overview

Incremental-search combobox over a fixed option list.

component Funicular::Plugins::Autocomplete::Component,
        key: "brewery-#{record_id}",   # remount to re-read value:
        options: [{ "id" => 1, "label" => "Masuizumi" }, ...],
        value: selected_id,            # optional preselection
        input_id: "brewery-search",
        input_class: "...",
        placeholder: "Search...",
        empty_label: "No matches",
        limit: 20,
        on_select: ->(option) { ... }  # receives the chosen Hash

The parent owns the option list; filtering is a client-side case-insensitive substring match on "label". The value prop is read once at mount to prefill the input, so give the component a key that changes when the selection context changes.

Instance Method Summary collapse

Instance Method Details

#component_mountedObject



23
24
25
26
# File 'lib/components/autocomplete_component.rb', line 23

def component_mounted
  prefill = selected_label
  set_input(prefill) if prefill
end

#handle_blur(_event) ⇒ Object



37
38
39
40
# File 'lib/components/autocomplete_component.rb', line 37

def handle_blur(_event)
  # Option mousedown fires before blur, so selection still wins.
  patch(open: false)
end

#handle_focus(_event) ⇒ Object



33
34
35
# File 'lib/components/autocomplete_component.rb', line 33

def handle_focus(_event)
  patch(open: true)
end

#handle_input(_event) ⇒ Object



28
29
30
31
# File 'lib/components/autocomplete_component.rb', line 28

def handle_input(_event)
  node = @refs[:query_input]
  patch(query: node ? node[:value].to_s : "", open: true, highlight: 0)
end

#handle_key(event) ⇒ Object



42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/components/autocomplete_component.rb', line 42

def handle_key(event)
  key = event[:key].to_s
  if key == "Escape"
    patch(open: false)
  elsif key == "ArrowDown"
    move_highlight(1)
  elsif key == "ArrowUp"
    move_highlight(-1)
  elsif key == "Enter" && state[:open]
    shown = shown_options
    option = shown[state[:highlight]] || shown[0]
    select_option(option) if option
  end
end

#initialize_stateObject



19
20
21
# File 'lib/components/autocomplete_component.rb', line 19

def initialize_state
  { query: nil, open: false, highlight: 0 }
end

#renderObject



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/components/autocomplete_component.rb', line 64

def render
  div(class: "funicular-autocomplete") do
    input(
      type: "text",
      ref: :query_input,
      id: props[:input_id],
      class: "funicular-autocomplete__input #{props[:input_class]}",
      placeholder: props[:placeholder].to_s,
      autocomplete: "off",
      oninput: :handle_input,
      onfocus: :handle_focus,
      onblur: :handle_blur,
      onkeydown: :handle_key
    )
    if state[:open]
      render_panel
    end
  end
end

#select_option(option) ⇒ Object



57
58
59
60
61
62
# File 'lib/components/autocomplete_component.rb', line 57

def select_option(option)
  patch(query: nil, open: false, highlight: 0)
  set_input(option["label"].to_s)
  callback = props[:on_select]
  callback.call(option) if callback
end