Skip to content

Commit

Permalink
Merge pull request #2186 from MushroomObserver/autocompleters-refacto…
Browse files Browse the repository at this point in the history
…r-js

Autocompleters: CSS and helper refactor
  • Loading branch information
nimmolo committed Jun 21, 2024
2 parents bc2ed6f + c709ed9 commit ec173c0
Show file tree
Hide file tree
Showing 25 changed files with 295 additions and 167 deletions.
2 changes: 1 addition & 1 deletion app/assets/stylesheets/BlackOnWhite.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

$LOGO_BORDER_COLOR: #DDDDDD;
$LEFT_BAR_BORDER_COLOR: #DDDDDD;
$TOP_BAR_BORDER_COLOR: #DDDDDD;
$TOP_BAR_BORDER_COLOR: #EDDDDD;
$LIST_BORDER_COLOR: #DDDDDD;
$BUTTON_HOVER_BORDER_COLOR: #CCCCCC;
$BUTTON_BG_COLOR: #CCCCCC;
Expand Down
59 changes: 37 additions & 22 deletions app/assets/stylesheets/mo/_autocomplete.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,50 @@
// autocompletion
// --------------------------------------------------

div.auto_complete {
position: absolute;
color: $dropdown-link-color;
background-color: $dropdown-bg;
.dropdown-menu.auto_complete {
// position: absolute;
// color: $dropdown-link-color;
// background-color: $dropdown-bg;
margin: 0px;
border: 1px solid $dropdown-border;
// border: 1px solid $dropdown-border;
border-radius: 0;
padding: 0px;
overflow-x: hidden;
display: none;
text-align: left;
z-index: 100;
// display: none;
// text-align: left;
// z-index: 100;
}

ul.virtual_list {
margin: 0px;
border: 0px;
padding: 0px;
list-style: none;
overflow-x: hidden;
}

li.dropdown-item {
margin: 0px;
border: 0px;
white-space: nowrap;
cursor: pointer;

ul {
margin: 0px;
border: 0px;
padding: 0px;
list-style: none;
overflow-x: hidden;
a {
display: block;
padding: 2px 4px;
color: $dropdown-link-color;
text-decoration: none;

li {
margin: 0px;
border: 0px;
padding: 2px 4px 2px 4px;
white-space: nowrap;
cursor: pointer;
&:hover {
color: $dropdown-link-color;
background-color: $dropdown-link-hover-bg;
text-decoration: none;
}
}

li.selected {
color: $dropdown-link-hover-color;
&.active {
a {
color: $dropdown-link-color;
background-color: $dropdown-link-hover-bg;
}
}
Expand Down
8 changes: 5 additions & 3 deletions app/classes/auto_complete.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
################################################################################

class AutoComplete
attr_accessor :string, :matches, :all
attr_accessor :string, :matches, :all, :whole

PUNCTUATION = '[ -\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]'

Expand All @@ -27,11 +27,13 @@ def self.subclass(type)
def initialize(params = {})
self.string = params[:string].to_s.strip_squeeze
self.all = params[:all].present?
self.whole = params[:whole].present?
end

def matching_strings
# just use the first letter of the string to define the matches
self.matches = rough_matches(string[0])
# unless 'whole', use the first letter of the string to define the matches
token = whole ? string : string[0]
self.matches = rough_matches(token)
clean_matches
return matches if all

Expand Down
19 changes: 19 additions & 0 deletions app/classes/auto_complete/for_region.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# frozen_string_literal: true

# This requires Stimulus delaying the fetch until we have a complete word.
class AutoComplete::ForRegion < AutoComplete::ByWord
attr_accessor :reverse

def initialize(params)
super
self.reverse = (params[:format] == "scientific")
end

def rough_matches(words)
words = Location.reverse_name(words) if reverse
matches = Observation.in_region(words).pluck(:where)

matches.map! { |m| Location.reverse_name(m) } if reverse
matches.sort.uniq
end
end
2 changes: 1 addition & 1 deletion app/controllers/autocompleters_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def new

def auto_complete_results
case @type
when "location", "location_containing"
when "location", "location_containing", "region"
params[:format] = @user&.location_format
when "herbarium"
params[:user_id] = @user&.id
Expand Down
36 changes: 24 additions & 12 deletions app/helpers/forms_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -133,30 +133,41 @@ def text_field_with_label(**args)
opts[:class] = "form-control"

wrap_class = form_group_wrap_class(args)
wrap_data = args[:wrap_data] || {}
label_opts = field_label_opts(args)

tag.div(class: wrap_class) do
tag.div(class: wrap_class, data: wrap_data) do
concat(args[:form].label(args[:field], args[:label], label_opts))
concat(args[:between]) if args[:between].present?
concat(args[:form].text_field(args[:field], opts))
concat(args[:append]) if args[:append].present?
end
end

# This allows incoming data attributes to deep_merge with autocompleter's data
# 2023 hack to defeat unhelpful browser autocompletes that get in the way of
# our autocompleter: use the browser standard autocomplete att "one-time-code"
# MO's autocompleter_field is a text_field that fetches suggestions from the
# db for the requested model. (For a textarea, pass textarea: true.) The
# stimulus controller handles keyboard and mouse interactions, does the
# fetching, and draws the dropdown menu. `args` allow incoming data attributes
# to deep_merge with controller data. We attempt to disable browser
# autocomplete via `autocomplete="off"` — the W3C standard API, but it
# has never been honored by Chrome or Safari. Chrome seems to be in a race to
# defeat the evolving hacks by developers to disable inappropriate
# autocompletes, and Safari is not much better - you just can't turn their
# crap off. (documented on SO)
#
def autocompleter_field(**args)
autocompleter_args = {
ac_args = {
placeholder: :start_typing.l, autocomplete: "off",
data: { controller: :autocompleter, autocompleter_target: "input",
autocomplete: args[:autocomplete], separator: args[:separator] }
}.deep_merge(args.except(:autocomplete, :separator, :textarea))
data: { autocompleter_target: "input" }
}.deep_merge(args.except(:type, :separator, :textarea))
ac_args[:class] = class_names("dropdown", args[:class])
ac_args[:wrap_data] = { controller: :autocompleter, type: args[:type],
separator: args[:separator] }

if args[:textarea] == true
text_area_with_label(**autocompleter_args)
text_area_with_label(**ac_args)
else
text_field_with_label(**autocompleter_args)
text_field_with_label(**ac_args)
end
end

Expand All @@ -169,9 +180,10 @@ def text_area_with_label(**args)
opts[:class] += " text-monospace" if args[:monospace].present?

wrap_class = form_group_wrap_class(args)
wrap_data = args[:wrap_data] || {}
label_opts = field_label_opts(args)

tag.div(class: wrap_class) do
tag.div(class: wrap_class, data: wrap_data) do
concat(args[:form].label(args[:field], args[:label], label_opts))
concat(args[:between]) if args[:between].present?
concat(args[:form].text_area(args[:field], opts))
Expand Down Expand Up @@ -464,7 +476,7 @@ def check_for_optional_or_required_note(args)
def separate_field_options_from_args(args, extras = [])
exceptions = [
:form, :field, :label, :class, :width, :inline, :between, :append,
:optional, :required, :monospace, :type
:optional, :required, :monospace, :type, :wrap_data
] + extras

args.clone.except(*exceptions)
Expand Down
Loading

0 comments on commit ec173c0

Please sign in to comment.