feat(combobox): implement ComboboxComponent
This commit is contained in:
parent
bea8cba6ce
commit
85024174d4
7 changed files with 227 additions and 1 deletions
|
@ -621,3 +621,18 @@ textarea::placeholder {
|
||||||
background-color: $white;
|
background-color: $white;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fr-menu__list {
|
||||||
|
padding: $default-spacer;
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: 300px;
|
||||||
|
|
||||||
|
.fr-menu__item {
|
||||||
|
list-style-type: none;
|
||||||
|
margin-bottom: $default-spacer;
|
||||||
|
|
||||||
|
&[aria-selected] {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
65
app/components/dsfr/combobox_component.rb
Normal file
65
app/components/dsfr/combobox_component.rb
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
class Dsfr::ComboboxComponent < ApplicationComponent
|
||||||
|
def initialize(form: nil, options:, selected: nil, allows_custom_value: false, **html_options)
|
||||||
|
@form, @options, @selected, @allows_custom_value, @html_options = form, options, selected, allows_custom_value, html_options
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :form, :options, :selected, :allows_custom_value
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def name
|
||||||
|
@html_options[:name]
|
||||||
|
end
|
||||||
|
|
||||||
|
def form_id
|
||||||
|
@html_options[:form_id]
|
||||||
|
end
|
||||||
|
|
||||||
|
def html_input_options
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
autocomplete: 'off',
|
||||||
|
spellcheck: 'false',
|
||||||
|
id: input_id,
|
||||||
|
class: input_class,
|
||||||
|
value: input_value,
|
||||||
|
'aria-expanded': 'false',
|
||||||
|
'aria-describedby': @html_options[:describedby]
|
||||||
|
}.compact
|
||||||
|
end
|
||||||
|
|
||||||
|
def input_id
|
||||||
|
@html_options[:id]
|
||||||
|
end
|
||||||
|
|
||||||
|
def input_class
|
||||||
|
"#{@html_options[:class].presence || ''} fr-select"
|
||||||
|
end
|
||||||
|
|
||||||
|
def input_value
|
||||||
|
selected.present? ? options_with_values.find { _1.last == selected }&.first : nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def list_id
|
||||||
|
input_id.present? ? "#{input_id}-list" : nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def options_with_values
|
||||||
|
options.map { _1.is_a?(Array) ? _1 : [_1, _1] }
|
||||||
|
end
|
||||||
|
|
||||||
|
def options_json
|
||||||
|
options_with_values.map { |(label, value)| { label:, value: } }.to_json
|
||||||
|
end
|
||||||
|
|
||||||
|
def hints_json
|
||||||
|
{
|
||||||
|
empty: t(".sr.results", count: 0),
|
||||||
|
one: t(".sr.results", count: 1),
|
||||||
|
many: t(".sr.results", count: 2),
|
||||||
|
oneWithLabel: t(".sr.results_with_label", count: 1),
|
||||||
|
manyWithLabel: t(".sr.results_with_label", count: 2),
|
||||||
|
selected: t(".sr.selected", count: 2)
|
||||||
|
}.to_json
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,10 @@
|
||||||
|
en:
|
||||||
|
sr:
|
||||||
|
results:
|
||||||
|
zero: No result
|
||||||
|
one: 1 result
|
||||||
|
other: "{count} results"
|
||||||
|
results_with_label:
|
||||||
|
one: "1 result. {label} is the top result – press Enter to activate"
|
||||||
|
other: "{count} results. {label} is the top result – press Enter to activate"
|
||||||
|
selected: "{label} selected"
|
|
@ -0,0 +1,10 @@
|
||||||
|
fr:
|
||||||
|
sr:
|
||||||
|
results:
|
||||||
|
zero: Aucun résultat
|
||||||
|
one: 1 résultat
|
||||||
|
other: "{count} résultats"
|
||||||
|
results_with_label:
|
||||||
|
one: "1 résultat. {label} est le premier résultat – appuyez sur Entrée pour sélectionner"
|
||||||
|
other: "{count} résultats. {label} est le premier résultat – appuyez sur Entrée pour sélectionner"
|
||||||
|
selected: "{label} sélectionné"
|
|
@ -0,0 +1,13 @@
|
||||||
|
.fr-ds-combobox{ data: { controller: 'combobox', allows_custom_value: allows_custom_value } }
|
||||||
|
.fr-ds-combobox-input
|
||||||
|
%input{ **html_input_options }
|
||||||
|
- if form
|
||||||
|
= form.hidden_field name, value: selected, form: form_id
|
||||||
|
- else
|
||||||
|
%input{ type: 'hidden', name:, value: selected, form: form_id }
|
||||||
|
.fr-menu
|
||||||
|
%ul.fr-menu__list.hidden{ role: 'listbox', hidden: true, id: list_id, data: { turbo_force: :browser, options: options_json, selected:, hints: hints_json } }
|
||||||
|
.sr-only{ aria: { live: 'polite', atomic: 'true' }, data: { turbo_force: :browser } }
|
||||||
|
%template
|
||||||
|
%li.fr-menu__item{ role: 'option' }
|
||||||
|
%slot{ name: 'label' }
|
|
@ -14,6 +14,7 @@
|
||||||
= render partial: "layouts/favicons"
|
= render partial: "layouts/favicons"
|
||||||
|
|
||||||
= vite_client_tag
|
= vite_client_tag
|
||||||
|
= vite_react_refresh_tag
|
||||||
= vite_javascript_tag 'application'
|
= vite_javascript_tag 'application'
|
||||||
|
|
||||||
= preload_link_tag(asset_url("Marianne-Regular.woff2"))
|
= preload_link_tag(asset_url("Marianne-Regular.woff2"))
|
||||||
|
@ -22,7 +23,7 @@
|
||||||
= vite_stylesheet_tag 'main', media: 'all'
|
= vite_stylesheet_tag 'main', media: 'all'
|
||||||
= stylesheet_link_tag 'application', media: 'all'
|
= stylesheet_link_tag 'application', media: 'all'
|
||||||
|
|
||||||
%body{ class: browser.platform.ios? ? 'ios' : nil }
|
%body{ class: browser.platform.ios? ? 'ios' : nil, data: { controller: 'turbo' } }
|
||||||
.page-wrapper
|
.page-wrapper
|
||||||
%main.m-6
|
%main.m-6
|
||||||
= content_for?(:content) ? yield(:content) : yield
|
= content_for?(:content) ? yield(:content) : yield
|
||||||
|
|
112
spec/components/previews/dsfr/combobox_component_preview.rb
Normal file
112
spec/components/previews/dsfr/combobox_component_preview.rb
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
class Dsfr::ComboboxComponentPreview < ViewComponent::Preview
|
||||||
|
OPTIONS = [
|
||||||
|
'Cheddar',
|
||||||
|
'Brie',
|
||||||
|
'Mozzarella',
|
||||||
|
'Gouda',
|
||||||
|
'Swiss',
|
||||||
|
'Parmesan',
|
||||||
|
'Feta',
|
||||||
|
'Blue cheese',
|
||||||
|
'Camembert',
|
||||||
|
'Monterey Jack',
|
||||||
|
'Roquefort',
|
||||||
|
'Provolone',
|
||||||
|
'Colby',
|
||||||
|
'Havarti',
|
||||||
|
'Ricotta',
|
||||||
|
'Pepper Jack',
|
||||||
|
'Muenster',
|
||||||
|
'Fontina',
|
||||||
|
'Limburger',
|
||||||
|
'Asiago',
|
||||||
|
'Cottage cheese',
|
||||||
|
'Emmental',
|
||||||
|
'Mascarpone',
|
||||||
|
'Taleggio',
|
||||||
|
'Gruyere',
|
||||||
|
'Edam',
|
||||||
|
'Pecorino Romano',
|
||||||
|
'Manchego',
|
||||||
|
'Halloumi',
|
||||||
|
'Jarlsberg',
|
||||||
|
'Munster',
|
||||||
|
'Stilton',
|
||||||
|
'Gorgonzola',
|
||||||
|
'Queso blanco',
|
||||||
|
'Queso fresco',
|
||||||
|
'Queso de bola',
|
||||||
|
'Queso de cabra',
|
||||||
|
'Queso panela',
|
||||||
|
'Queso Oaxaca',
|
||||||
|
'Queso Chihuahua',
|
||||||
|
'Queso manchego',
|
||||||
|
'Queso de bola',
|
||||||
|
'Queso de bola de cabra',
|
||||||
|
'Queso de bola de vaca',
|
||||||
|
'Queso de bola de oveja',
|
||||||
|
'Queso de bola de mezcla',
|
||||||
|
'Queso de bola de leche cruda',
|
||||||
|
'Queso de bola de leche pasteurizada',
|
||||||
|
'Queso de bola de leche de cabra',
|
||||||
|
'Queso de bola de leche de vaca',
|
||||||
|
'Queso de bola de leche de oveja',
|
||||||
|
'Queso de bola de leche de mezcla',
|
||||||
|
'Burrata',
|
||||||
|
'Scamorza',
|
||||||
|
'Caciocavallo',
|
||||||
|
'Provolone piccante',
|
||||||
|
'Pecorino sardo',
|
||||||
|
'Pecorino toscano',
|
||||||
|
'Pecorino siciliano',
|
||||||
|
'Pecorino calabrese',
|
||||||
|
'Pecorino moliterno',
|
||||||
|
'Pecorino di fossa',
|
||||||
|
'Pecorino di filiano',
|
||||||
|
'Pecorino di pienza',
|
||||||
|
'Pecorino di grotta',
|
||||||
|
'Pecorino di capra',
|
||||||
|
'Pecorino di mucca',
|
||||||
|
'Pecorino di pecora',
|
||||||
|
'Pecorino di bufala',
|
||||||
|
'Cacio di bosco',
|
||||||
|
'Cacio di roma',
|
||||||
|
'Cacio di fossa',
|
||||||
|
'Cacio di tricarico',
|
||||||
|
'Cacio di cavallo',
|
||||||
|
'Cacio di capra',
|
||||||
|
'Cacio di mucca',
|
||||||
|
'Cacio di pecora',
|
||||||
|
'Cacio di bufala',
|
||||||
|
'Taleggio di capra',
|
||||||
|
'Taleggio di mucca',
|
||||||
|
'Taleggio di pecora',
|
||||||
|
'Taleggio di bufala',
|
||||||
|
'Bel Paese',
|
||||||
|
'Crescenza',
|
||||||
|
'Stracchino',
|
||||||
|
'Robiola',
|
||||||
|
'Toma',
|
||||||
|
'Bra',
|
||||||
|
'Castelmagno',
|
||||||
|
'Raschera',
|
||||||
|
'Montasio',
|
||||||
|
'Piave',
|
||||||
|
'Bitto',
|
||||||
|
'Quartirolo Lombardo',
|
||||||
|
'Formaggella del Luinese',
|
||||||
|
'Formaggella della Val Vigezzo',
|
||||||
|
'Formaggella della Valle Grana',
|
||||||
|
'Formaggella della Val Bognanco',
|
||||||
|
'Formaggella della Val d’Intelvi',
|
||||||
|
'Formaggella della Val Gerola'
|
||||||
|
]
|
||||||
|
|
||||||
|
def simple_select_with_options
|
||||||
|
render Dsfr::ComboboxComponent.new(name: :value, options: OPTIONS, selected: OPTIONS.sample, id: 'simple-select', class: 'width-33')
|
||||||
|
end
|
||||||
|
|
||||||
|
def simple_select_with_options_and_allows_custom_value
|
||||||
|
render Dsfr::ComboboxComponent.new(name: :value, options: OPTIONS, selected: OPTIONS.sample, id: 'simple-select', class: 'width-33', allows_custom_value: true)
|
||||||
|
end
|
||||||
|
end
|
Loading…
Add table
Reference in a new issue