shoelace-rails 0.6.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/app/helpers/shoelace/components/error_wrappable.rb +21 -0
- data/app/helpers/shoelace/components/sl_checkbox.rb +40 -0
- data/app/helpers/shoelace/components/sl_collection_radio_buttons.rb +47 -0
- data/app/helpers/shoelace/components/sl_color_picker.rb +30 -0
- data/app/helpers/shoelace/components/sl_input.rb +33 -0
- data/app/helpers/shoelace/components/sl_radio_button.rb +20 -0
- data/app/helpers/shoelace/components/sl_range.rb +22 -0
- data/app/helpers/shoelace/components/sl_select.rb +77 -0
- data/app/helpers/shoelace/components/sl_switch.rb +23 -0
- data/app/helpers/shoelace/components/sl_textarea.rb +20 -0
- data/app/helpers/shoelace/form_builder.rb +8 -0
- data/app/helpers/shoelace/sl_form_builder.rb +111 -0
- data/app/helpers/shoelace/sl_form_helper.rb +139 -0
- data/lib/shoelace/rails/version.rb +1 -1
- data/lib/shoelace/railtie.rb +2 -4
- data/test/helpers/form_builder/sl_checkbox_test.rb +45 -0
- data/test/helpers/form_builder/sl_color_picker_test.rb +26 -0
- data/test/helpers/form_builder/sl_input_test.rb +126 -0
- data/test/helpers/form_builder/sl_radio_group_test.rb +63 -0
- data/test/helpers/form_builder/sl_range_test.rb +34 -0
- data/test/helpers/form_builder/sl_select_test.rb +320 -0
- data/test/helpers/form_builder/sl_switch_test.rb +26 -0
- data/test/helpers/form_builder/sl_textarea_test.rb +72 -0
- data/test/helpers/form_builder_test.rb +16 -0
- data/test/helpers/form_helper_test.rb +3 -442
- data/test/helpers/translation_test.rb +2 -4
- data/test/test_helper.rb +22 -0
- metadata +25 -4
- data/app/helpers/shoelace/form_helper.rb +0 -408
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 43e6f607b577eb1c8619ca818ad1e1ad29077d948789579a109a740273b1ba6e
|
4
|
+
data.tar.gz: 266799cb74342d48ac0fb97e9fd9a902c84fe5dfc468c5f5fdf7c8ec98872759
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e11ac8b6752b1c8cb9303b972008f088e57e56dfb022c36d6ff4ae5784102df6a0afc1143101fc497c1926ff6f494608d13738759347c9f767676c6fc76c0589
|
7
|
+
data.tar.gz: 6c8f7506829f93b98e673701cf290e5f116a8f283f5abb42c2507f94c248b12223795e7670530dadd1d812e5f3999fa7bccc212f8c85a69a0b3da8a32231e542
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
## Unreleased
|
2
|
+
|
3
|
+
#### 🚨 Breaking Changes
|
4
|
+
|
5
|
+
- Deprecate `config.shoelace.invalid_input_class_name` in favor of `data-invalid` and `data-use-invalid`
|
6
|
+
|
7
|
+
#### ⭐️ Features
|
8
|
+
|
9
|
+
- Add `invalid` and `data-invalid` to the `Shoelace::FormBuilder`
|
10
|
+
- Add the ability to specify a method for rendering slot for input components
|
11
|
+
- Add `FormBuilder#date_field` method
|
12
|
+
|
1
13
|
## v0.6.2
|
2
14
|
|
3
15
|
#### 🐞Bug Fixes
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shoelace
|
4
|
+
module Components
|
5
|
+
module ErrorWrappable
|
6
|
+
def error_wrapping(html_tag)
|
7
|
+
if object_has_errors? && field_error_proc
|
8
|
+
@template_object.instance_exec(html_tag, self, &field_error_proc)
|
9
|
+
else
|
10
|
+
html_tag
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def field_error_proc
|
17
|
+
Shoelace::SlFormBuilder.field_error_proc
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './error_wrappable'
|
4
|
+
|
5
|
+
module Shoelace
|
6
|
+
module Components
|
7
|
+
class SlCheckbox < ActionView::Helpers::Tags::CheckBox #:nodoc:
|
8
|
+
include ErrorWrappable
|
9
|
+
|
10
|
+
def render(&block)
|
11
|
+
options = @options.stringify_keys
|
12
|
+
options["value"] = @checked_value
|
13
|
+
options["checked"] = true if input_checked?(options)
|
14
|
+
options["invalid"] = options["data-invalid"] = "" if object_has_errors?
|
15
|
+
label = options.delete("label")
|
16
|
+
|
17
|
+
if options["multiple"]
|
18
|
+
add_default_name_and_id_for_value(@checked_value, options)
|
19
|
+
options.delete("multiple")
|
20
|
+
else
|
21
|
+
add_default_name_and_id(options)
|
22
|
+
end
|
23
|
+
|
24
|
+
include_hidden = options.delete("include_hidden") { true }
|
25
|
+
|
26
|
+
sl_checkbox_tag = if block_given?
|
27
|
+
@template_object.content_tag('sl-checkbox', '', options, &block)
|
28
|
+
else
|
29
|
+
@template_object.content_tag('sl-checkbox', label, options)
|
30
|
+
end
|
31
|
+
|
32
|
+
if include_hidden
|
33
|
+
hidden_field_for_checkbox(options) + sl_checkbox_tag
|
34
|
+
else
|
35
|
+
sl_checkbox_tag
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './error_wrappable'
|
4
|
+
|
5
|
+
module Shoelace
|
6
|
+
module Components
|
7
|
+
class SlCollectionRadioButtons < ActionView::Helpers::Tags::CollectionRadioButtons #:nodoc:
|
8
|
+
include ErrorWrappable
|
9
|
+
|
10
|
+
class RadioButtonBuilder < Builder # :nodoc:
|
11
|
+
def label(*)
|
12
|
+
text
|
13
|
+
end
|
14
|
+
|
15
|
+
def radio_button(extra_html_options = {}, &block)
|
16
|
+
html_options = extra_html_options.merge(@input_html_options)
|
17
|
+
html_options[:skip_default_ids] = false
|
18
|
+
@template_object.sl_radio_button(@object_name, @method_name, @value, html_options, &block)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def render(&block)
|
23
|
+
render_collection_for(RadioButtonBuilder, &block)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def render_collection(&block)
|
29
|
+
html_options = @html_options.stringify_keys
|
30
|
+
html_options["value"] = value
|
31
|
+
add_default_name_and_id(html_options)
|
32
|
+
html_options["label"] = @options[:label].presence
|
33
|
+
html_options["invalid"] = html_options["data-invalid"] = "" if object_has_errors?
|
34
|
+
|
35
|
+
@template_object.content_tag('sl-radio-group', html_options) { super(&block) }
|
36
|
+
end
|
37
|
+
|
38
|
+
def hidden_field
|
39
|
+
''.html_safe
|
40
|
+
end
|
41
|
+
|
42
|
+
def render_component(builder)
|
43
|
+
builder.radio_button { builder.label }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './error_wrappable'
|
4
|
+
|
5
|
+
module Shoelace
|
6
|
+
module Components
|
7
|
+
class SlColorPicker < ActionView::Helpers::Tags::ColorField #:nodoc:
|
8
|
+
RGB_VALUE_REGEX = /#[0-9a-fA-F]{6}/.freeze
|
9
|
+
|
10
|
+
include ErrorWrappable
|
11
|
+
|
12
|
+
def field_type; nil; end
|
13
|
+
|
14
|
+
def tag(tag_name, *args, &block)
|
15
|
+
tag_name.to_s == 'input' ? content_tag('sl-color-picker', '', *args, &block) : super
|
16
|
+
end
|
17
|
+
|
18
|
+
def render
|
19
|
+
@options["invalid"] = @options["data-invalid"] = "" if object_has_errors?
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def validate_color_string(string)
|
26
|
+
string.downcase if RGB_VALUE_REGEX.match?(string)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './error_wrappable'
|
4
|
+
|
5
|
+
module Shoelace
|
6
|
+
module Components
|
7
|
+
class SlInput < ActionView::Helpers::Tags::TextField #:nodoc:
|
8
|
+
include ErrorWrappable
|
9
|
+
|
10
|
+
attr_reader :field_type
|
11
|
+
|
12
|
+
def initialize(field_type, *args)
|
13
|
+
super(*args)
|
14
|
+
@field_type = field_type
|
15
|
+
end
|
16
|
+
|
17
|
+
def render(&block)
|
18
|
+
options = @options.stringify_keys
|
19
|
+
|
20
|
+
value = options.fetch("value") { value_before_type_cast }
|
21
|
+
options["value"] = value if value.present?
|
22
|
+
|
23
|
+
options["size"] = options["maxlength"] unless options.key?("size")
|
24
|
+
options["type"] ||= field_type
|
25
|
+
options["invalid"] = options["data-invalid"] = "" if object_has_errors?
|
26
|
+
|
27
|
+
add_default_name_and_id(options)
|
28
|
+
|
29
|
+
@template_object.content_tag('sl-input', '', options, &block)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './error_wrappable'
|
4
|
+
|
5
|
+
module Shoelace
|
6
|
+
module Components
|
7
|
+
class SlRadioButton < ActionView::Helpers::Tags::RadioButton #:nodoc:
|
8
|
+
include ErrorWrappable
|
9
|
+
|
10
|
+
def render(&block)
|
11
|
+
options = @options.stringify_keys
|
12
|
+
options["value"] = @tag_value
|
13
|
+
add_default_name_and_id_for_value(@tag_value, options)
|
14
|
+
options.delete("name")
|
15
|
+
|
16
|
+
@template_object.content_tag('sl-radio', '', options.except("type"), &block)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './error_wrappable'
|
4
|
+
|
5
|
+
module Shoelace
|
6
|
+
module Components
|
7
|
+
class SlRange < ActionView::Helpers::Tags::NumberField #:nodoc:
|
8
|
+
include ErrorWrappable
|
9
|
+
|
10
|
+
def field_type; nil; end
|
11
|
+
|
12
|
+
def render
|
13
|
+
@options["invalid"] = @options["data-invalid"] = "" if object_has_errors?
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
def tag(tag_name, *args, &block)
|
18
|
+
tag_name.to_s == 'input' ? content_tag('sl-range', '', *args, &block) : super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './error_wrappable'
|
4
|
+
|
5
|
+
module Shoelace
|
6
|
+
module Components
|
7
|
+
module SlSelectRenderable
|
8
|
+
EMPTY = "".html_safe.freeze
|
9
|
+
|
10
|
+
def select_content_tag(option_tags, _options, html_options)
|
11
|
+
html_options = html_options.stringify_keys
|
12
|
+
html_options['value'] ||= value
|
13
|
+
html_options["invalid"] = html_options["data-invalid"] = "" if object_has_errors?
|
14
|
+
add_default_name_and_id(html_options)
|
15
|
+
|
16
|
+
@template_object.content_tag("sl-select", html_options) do
|
17
|
+
"".html_safe.tap do |html|
|
18
|
+
html.safe_concat(option_tags)
|
19
|
+
html.safe_concat(@template_object.capture(&slot) || EMPTY) if slot
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class SlSelect < ActionView::Helpers::Tags::Select #:nodoc:
|
26
|
+
include ErrorWrappable
|
27
|
+
include SlSelectRenderable
|
28
|
+
|
29
|
+
attr_reader :slot
|
30
|
+
|
31
|
+
def initialize(object_name, method_name, template_object, choices, options, html_options, slot)
|
32
|
+
@slot = slot
|
33
|
+
super(object_name, method_name, template_object, choices, options, html_options)
|
34
|
+
end
|
35
|
+
|
36
|
+
def grouped_options_for_select(grouped_options, options)
|
37
|
+
@template_object.grouped_sl_options_for_select(grouped_options, options)
|
38
|
+
end
|
39
|
+
|
40
|
+
def options_for_select(container, options = nil)
|
41
|
+
@template_object.sl_options_for_select(container, options)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class SlCollectionSelect < ActionView::Helpers::Tags::CollectionSelect #:nodoc:
|
46
|
+
include ErrorWrappable
|
47
|
+
include SlSelectRenderable
|
48
|
+
|
49
|
+
attr_reader :slot
|
50
|
+
|
51
|
+
def initialize(object_name, method_name, template_object, collection, value_method, text_method, options, html_options, slot)
|
52
|
+
@slot = slot
|
53
|
+
super(object_name, method_name, template_object, collection, value_method, text_method, options, html_options)
|
54
|
+
end
|
55
|
+
|
56
|
+
def options_from_collection_for_select(collection, value_method, text_method, selected = nil)
|
57
|
+
@template_object.sl_options_from_collection_for_select(collection, value_method, text_method, selected)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class SlGroupedCollectionSelect < ActionView::Helpers::Tags::GroupedCollectionSelect #:nodoc:
|
62
|
+
include ErrorWrappable
|
63
|
+
include SlSelectRenderable
|
64
|
+
|
65
|
+
attr_reader :slot
|
66
|
+
|
67
|
+
def initialize(object_name, method_name, template_object, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options, slot)
|
68
|
+
@slot = slot
|
69
|
+
super(object_name, method_name, template_object, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
|
70
|
+
end
|
71
|
+
|
72
|
+
def option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, selected_key = nil)
|
73
|
+
@template_object.sl_option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, selected_key)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './error_wrappable'
|
4
|
+
|
5
|
+
module Shoelace
|
6
|
+
module Components
|
7
|
+
class SlSwitch < ActionView::Helpers::Tags::NumberField #:nodoc:
|
8
|
+
include ErrorWrappable
|
9
|
+
|
10
|
+
def field_type; nil; end
|
11
|
+
|
12
|
+
def render(&block)
|
13
|
+
options = @options.stringify_keys
|
14
|
+
options["value"] = options.fetch("value") { value_before_type_cast }
|
15
|
+
add_default_name_and_id(options)
|
16
|
+
label = options.delete('label').presence
|
17
|
+
options["invalid"] = options["data-invalid"] = "" if object_has_errors?
|
18
|
+
|
19
|
+
content_tag('sl-switch', label, options, &block)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './error_wrappable'
|
4
|
+
|
5
|
+
module Shoelace
|
6
|
+
module Components
|
7
|
+
class SlTextarea < ActionView::Helpers::Tags::NumberField #:nodoc:
|
8
|
+
include ErrorWrappable
|
9
|
+
|
10
|
+
def render(&block)
|
11
|
+
options = @options.stringify_keys
|
12
|
+
options["value"] = options.fetch("value") { value_before_type_cast }
|
13
|
+
options["invalid"] = options["data-invalid"] = "" if object_has_errors?
|
14
|
+
add_default_name_and_id(options)
|
15
|
+
|
16
|
+
@template_object.content_tag("sl-textarea", '', options, &block)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './components/sl_checkbox'
|
4
|
+
require_relative './components/sl_collection_radio_buttons'
|
5
|
+
require_relative './components/sl_color_picker'
|
6
|
+
require_relative './components/sl_input'
|
7
|
+
require_relative './components/sl_radio_button'
|
8
|
+
require_relative './components/sl_range'
|
9
|
+
require_relative './components/sl_select'
|
10
|
+
require_relative './components/sl_switch'
|
11
|
+
require_relative './components/sl_textarea'
|
12
|
+
|
13
|
+
module Shoelace
|
14
|
+
class SlFormBuilder < ActionView::Helpers::FormBuilder #:nodoc:
|
15
|
+
# Same concept as the Rails field_error_proc, but for Shoelace components. Set to `nil` to avoid messing up the
|
16
|
+
# Shoelace components by default.
|
17
|
+
cattr_accessor :field_error_proc
|
18
|
+
|
19
|
+
# Similar concept to the Rails field_error_proc, but for Shoelace's commonly used `help-text slot`.
|
20
|
+
cattr_accessor :default_input_slot_method
|
21
|
+
|
22
|
+
{
|
23
|
+
date: :date,
|
24
|
+
email: :email,
|
25
|
+
number: :number,
|
26
|
+
password: :password,
|
27
|
+
search: :search,
|
28
|
+
telephone: :tel,
|
29
|
+
phone: :tel,
|
30
|
+
text: :text,
|
31
|
+
url: :url
|
32
|
+
}.each do |field_type, field_class|
|
33
|
+
# def email_field(method, **options, &block)
|
34
|
+
# slot = wrap_with_default_slot(method, &block)
|
35
|
+
# Components::SlInput.new(:email, object_name, method, @template, options.with_defaults(label: label_text(method))).render(&block)
|
36
|
+
# end
|
37
|
+
eval <<-RUBY, nil, __FILE__, __LINE__ + 1
|
38
|
+
def #{field_type}_field(method, **options, &block)
|
39
|
+
slot = wrap_with_default_slot(method, &block)
|
40
|
+
Components::SlInput.new(:#{field_class}, object_name, method, @template, options.with_defaults(object: @object, label: label_text(method))).render(&slot)
|
41
|
+
end
|
42
|
+
RUBY
|
43
|
+
end
|
44
|
+
|
45
|
+
def color_field(method, **options)
|
46
|
+
Components::SlColorPicker.new(object_name, method, @template, options.with_defaults(object: @object, label: label_text(method))).render
|
47
|
+
end
|
48
|
+
alias color_picker color_field
|
49
|
+
|
50
|
+
def range_field(method, **options)
|
51
|
+
Components::SlRange.new(object_name, method, @template, options.with_defaults(object: @object, label: label_text(method))).render
|
52
|
+
end
|
53
|
+
alias range range_field
|
54
|
+
|
55
|
+
def switch_field(method, **options, &block)
|
56
|
+
if block_given?
|
57
|
+
Components::SlSwitch.new(object_name, method, @template, options.with_defaults(object: @object)).render(&block)
|
58
|
+
else
|
59
|
+
Components::SlSwitch.new(object_name, method, @template, options.with_defaults(object: @object, label: label_text(method))).render(&block)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
alias switch switch_field
|
63
|
+
|
64
|
+
def text_area(method, **options, &block)
|
65
|
+
Components::SlTextarea.new(object_name, method, @template, options.with_defaults(object: @object, label: label_text(method), resize: 'auto')).render(&block)
|
66
|
+
end
|
67
|
+
|
68
|
+
def check_box(method, options = {}, checked_value = "1", unchecked_value = "0", &block)
|
69
|
+
Components::SlCheckbox.new(object_name, method, @template, checked_value, unchecked_value, options.with_defaults(label: label_text(method)).merge(object: @object)).render(&block)
|
70
|
+
end
|
71
|
+
|
72
|
+
def select(method, choices = nil, options = {}, html_options = {}, &block)
|
73
|
+
slot = wrap_with_default_slot(method, &block)
|
74
|
+
Components::SlSelect.new(object_name, method, @template, choices, options.with_defaults(object: @object), html_options.with_defaults(label: label_text(method)), slot).render
|
75
|
+
end
|
76
|
+
|
77
|
+
def collection_select(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
|
78
|
+
slot = wrap_with_default_slot(method, &block)
|
79
|
+
Components::SlCollectionSelect.new(object_name, method, @template, collection, value_method, text_method, options.with_defaults(object: @object), html_options.with_defaults(label: label_text(method)), slot).render
|
80
|
+
end
|
81
|
+
|
82
|
+
def grouped_collection_select(method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {}, &block)
|
83
|
+
slot = wrap_with_default_slot(method, &block)
|
84
|
+
Components::SlGroupedCollectionSelect.new(object_name, method, @template, collection, group_method, group_label_method, option_key_method, option_value_method, options.with_defaults(object: @object), html_options.with_defaults(label: label_text(method)), slot).render
|
85
|
+
end
|
86
|
+
|
87
|
+
def collection_radio_buttons(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
|
88
|
+
Components::SlCollectionRadioButtons.new(object_name, method, @template, collection, value_method, text_method, options.with_defaults(object: @object, label: label_text(method)), html_options).render(&block)
|
89
|
+
end
|
90
|
+
|
91
|
+
def submit(value = nil, options = {})
|
92
|
+
value, options = nil, value if value.is_a?(Hash)
|
93
|
+
|
94
|
+
@template.sl_submit_tag(value || submit_default_value, **options)
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def label_text(method, tag_value = nil)
|
100
|
+
::ActionView::Helpers::Tags::Label::LabelBuilder.new(@template, object_name.to_s, method.to_s, object, tag_value).translation
|
101
|
+
end
|
102
|
+
|
103
|
+
def wrap_with_default_slot(method_name, &user_defined_slot)
|
104
|
+
if default_input_slot_method && !block_given?
|
105
|
+
-> { @template.method(default_input_slot_method).call(object, method_name) }
|
106
|
+
else
|
107
|
+
user_defined_slot
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './sl_form_builder'
|
4
|
+
|
5
|
+
module Shoelace
|
6
|
+
module SlFormHelper
|
7
|
+
ShoelaceFormBuilder = SlFormBuilder
|
8
|
+
deprecate_constant :ShoelaceFormBuilder
|
9
|
+
|
10
|
+
DEFAULT_FORM_PARAMETERS = { builder: SlFormBuilder }
|
11
|
+
DIVIDER_TAG = "<sl-divider></sl-divider>".html_safe
|
12
|
+
|
13
|
+
private_constant :DEFAULT_FORM_PARAMETERS, :DIVIDER_TAG
|
14
|
+
|
15
|
+
def sl_form_for(*args, **options, &block)
|
16
|
+
form_for(*args, **DEFAULT_FORM_PARAMETERS, **options, &block)
|
17
|
+
end
|
18
|
+
|
19
|
+
def sl_form_with(**args, &block)
|
20
|
+
form_with(**args, **DEFAULT_FORM_PARAMETERS, &block)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Creates a submit button with the text value as the caption, with the +submit+ attribute.
|
24
|
+
def sl_submit_tag(value = 'Save changes', **options)
|
25
|
+
options = options.deep_stringify_keys
|
26
|
+
tag_options = { "type" => "submit", "variant" => "primary" }.update(options)
|
27
|
+
set_default_disable_with(value, tag_options)
|
28
|
+
|
29
|
+
content_tag('sl-button', value, tag_options)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Creates a shoelace text field; use these text fields to input smaller chunks of text like a username or a search
|
33
|
+
# query.
|
34
|
+
#
|
35
|
+
# For the properties available on this tag, please refer to the official documentation:
|
36
|
+
# https://shoelace.style/components/input?id=properties
|
37
|
+
#
|
38
|
+
def sl_text_field_tag(name, value = nil, **options, &block)
|
39
|
+
content_tag('sl-input', '', { "type" => "text", "name" => name, "id" => sanitize_to_id(name), "value" => value }.update(options.stringify_keys), &block)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns a string of +<sl-option>+ tags, like +options_for_select+, but prepends a +<small>+ tag to
|
43
|
+
# each group.
|
44
|
+
def grouped_sl_options_for_select(grouped_options, options)
|
45
|
+
body = "".html_safe
|
46
|
+
|
47
|
+
grouped_options.each_with_index do |container, index|
|
48
|
+
label, values = container
|
49
|
+
|
50
|
+
body.safe_concat(DIVIDER_TAG) if index > 0
|
51
|
+
body.safe_concat(content_tag("small", label)) if label.present?
|
52
|
+
body.safe_concat(sl_options_for_select(values, options))
|
53
|
+
end
|
54
|
+
|
55
|
+
body
|
56
|
+
end
|
57
|
+
|
58
|
+
# Accepts an enumerable (hash, array, enumerable, your type) and returns a string of +sl-option+ tags. Given
|
59
|
+
# an enumerable where the elements respond to +first+ and +last+ (such as a two-element array), the “lasts” serve
|
60
|
+
# as option values and the “firsts” as option text.
|
61
|
+
def sl_options_for_select(enumerable, options = nil)
|
62
|
+
return enumerable if String === enumerable
|
63
|
+
|
64
|
+
selected, disabled = extract_selected_and_disabled(options).map { |r| Array(r).map(&:to_s) }
|
65
|
+
|
66
|
+
enumerable.map do |element|
|
67
|
+
html_attributes = option_html_attributes(element)
|
68
|
+
text, value = option_text_and_value(element).map(&:to_s)
|
69
|
+
|
70
|
+
html_attributes[:checked] ||= selected.include?(value)
|
71
|
+
html_attributes[:disabled] ||= disabled.include?(value)
|
72
|
+
html_attributes[:value] = value
|
73
|
+
|
74
|
+
tag_builder.content_tag_string('sl-option', text, html_attributes)
|
75
|
+
end.join("\n").html_safe
|
76
|
+
end
|
77
|
+
|
78
|
+
def sl_option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, selected_key = nil)
|
79
|
+
body = "".html_safe
|
80
|
+
|
81
|
+
collection.each_with_index do |group, index|
|
82
|
+
option_tags = sl_options_from_collection_for_select(value_for_collection(group, group_method), option_key_method, option_value_method, selected_key)
|
83
|
+
|
84
|
+
body.safe_concat(DIVIDER_TAG) if index > 0
|
85
|
+
body.safe_concat(content_tag("small", value_for_collection(group, group_label_method)))
|
86
|
+
body.safe_concat(option_tags)
|
87
|
+
end
|
88
|
+
|
89
|
+
body
|
90
|
+
end
|
91
|
+
|
92
|
+
# Returns a string of +<sl-option>+ tags compiled by iterating over the collection and assigning the result of
|
93
|
+
# a call to the +value_method+ as the option value and the +text_method+ as the option text.
|
94
|
+
def sl_options_from_collection_for_select(collection, value_method, text_method, selected = nil)
|
95
|
+
options = collection.map do |element|
|
96
|
+
[value_for_collection(element, text_method), value_for_collection(element, value_method), option_html_attributes(element)]
|
97
|
+
end
|
98
|
+
|
99
|
+
selected, disabled = extract_selected_and_disabled(selected)
|
100
|
+
|
101
|
+
select_deselect = {
|
102
|
+
selected: extract_values_from_collection(collection, value_method, selected),
|
103
|
+
disabled: extract_values_from_collection(collection, value_method, disabled)
|
104
|
+
}
|
105
|
+
|
106
|
+
sl_options_for_select(options, select_deselect)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns a +<sl-radio>+ tag for accessing a specified attribute (identified by method) on an object assigned to
|
110
|
+
# the template (identified by object). If the current value of method is +tag_value+ the radio button will be
|
111
|
+
# checked.
|
112
|
+
#
|
113
|
+
# To force the radio button to be checked pass checked: true in the options hash. You may pass HTML options there
|
114
|
+
# as well.
|
115
|
+
def sl_radio_button(object_name, method, tag_value, options = {}, &block)
|
116
|
+
Components::SlRadioButton.new(object_name, method, self, tag_value, options).render(&block)
|
117
|
+
end
|
118
|
+
|
119
|
+
{
|
120
|
+
email: :email,
|
121
|
+
number: :number,
|
122
|
+
password: :password,
|
123
|
+
search: :search,
|
124
|
+
telephone: :tel,
|
125
|
+
phone: :tel,
|
126
|
+
url: :url
|
127
|
+
}.each do |field_type, field_class|
|
128
|
+
# def sl_email_field_tag(method, **options, &block)
|
129
|
+
# sl_text_field_tag(name, value, options.merge(type: :email))
|
130
|
+
# end
|
131
|
+
eval <<-RUBY, nil, __FILE__, __LINE__ + 1
|
132
|
+
# Creates a text field of type “#{field_type}”.
|
133
|
+
def sl_#{field_type}_field_tag(method, **options, &block)
|
134
|
+
sl_text_field_tag(name, value, options.merge(type: :#{field_class}))
|
135
|
+
end
|
136
|
+
RUBY
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
data/lib/shoelace/railtie.rb
CHANGED
@@ -30,7 +30,7 @@ module Shoelace
|
|
30
30
|
# Path to the shoelace assets.
|
31
31
|
config.shoelace.dist_path = "node_modules/@shoelace-style/shoelace/dist"
|
32
32
|
|
33
|
-
#
|
33
|
+
# Deprecated.
|
34
34
|
config.shoelace.invalid_input_class_name = nil
|
35
35
|
|
36
36
|
initializer "shoelace.use_rack_middleware" do |app|
|
@@ -45,9 +45,7 @@ module Shoelace
|
|
45
45
|
end
|
46
46
|
|
47
47
|
initializer "shoelace.form_helper" do |app|
|
48
|
-
|
49
|
-
Shoelace.invalid_input_class_name = app.config.shoelace.invalid_input_class_name
|
50
|
-
end
|
48
|
+
# This exists only for maintaining backwards compatibility with the `invalid_input_class_name` option.
|
51
49
|
end
|
52
50
|
|
53
51
|
rake_tasks do
|