stimulus_plumbers 0.2.8 → 0.2.9
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 +21 -0
- data/README.md +3 -0
- data/app/assets/javascripts/stimulus-plumbers/stimulus-plumbers-controllers.es.js +339 -302
- data/app/assets/javascripts/stimulus-plumbers/stimulus-plumbers-controllers.umd.js +1 -1
- data/lib/stimulus_plumbers/components/action_list/item.rb +27 -0
- data/lib/stimulus_plumbers/components/action_list/section.rb +21 -0
- data/lib/stimulus_plumbers/components/action_list.rb +23 -0
- data/lib/stimulus_plumbers/components/avatar.rb +72 -0
- data/lib/stimulus_plumbers/components/button/group.rb +17 -0
- data/lib/stimulus_plumbers/components/button.rb +27 -0
- data/lib/stimulus_plumbers/components/calendar/month/turbo/days_of_month.rb +2 -2
- data/lib/stimulus_plumbers/components/calendar/month/turbo/days_of_week.rb +2 -2
- data/lib/stimulus_plumbers/components/calendar/month/turbo.rb +55 -0
- data/lib/stimulus_plumbers/components/calendar.rb +33 -0
- data/lib/stimulus_plumbers/components/card/section.rb +25 -0
- data/lib/stimulus_plumbers/components/card.rb +27 -0
- data/lib/stimulus_plumbers/components/combobox/autocomplete.rb +30 -34
- data/lib/stimulus_plumbers/components/combobox/date.rb +16 -18
- data/lib/stimulus_plumbers/components/combobox/dropdown.rb +13 -16
- data/lib/stimulus_plumbers/components/combobox/options/option.rb +34 -0
- data/lib/stimulus_plumbers/components/combobox/options/option_group.rb +29 -0
- data/lib/stimulus_plumbers/components/combobox/options.rb +59 -0
- data/lib/stimulus_plumbers/components/combobox/popover.rb +20 -0
- data/lib/stimulus_plumbers/components/combobox/time/drum.rb +37 -0
- data/lib/stimulus_plumbers/components/combobox/time.rb +32 -15
- data/lib/stimulus_plumbers/components/combobox/trigger.rb +38 -0
- data/lib/stimulus_plumbers/components/combobox.rb +59 -0
- data/lib/stimulus_plumbers/components/date_picker/navigator.rb +1 -1
- data/lib/stimulus_plumbers/components/icon.rb +49 -0
- data/lib/stimulus_plumbers/components/popover/builder.rb +25 -0
- data/lib/stimulus_plumbers/components/popover.rb +26 -0
- data/lib/stimulus_plumbers/form/builder.rb +7 -5
- data/lib/stimulus_plumbers/form/{field_component.rb → field.rb} +1 -1
- data/lib/stimulus_plumbers/form/fields/combobox.rb +1 -1
- data/lib/stimulus_plumbers/form/fields/error.rb +14 -0
- data/lib/stimulus_plumbers/form/fields/group.rb +14 -0
- data/lib/stimulus_plumbers/form/fields/hint.rb +14 -0
- data/lib/stimulus_plumbers/form/fields/label.rb +21 -0
- data/lib/stimulus_plumbers/form/fields/renderer.rb +16 -20
- data/lib/stimulus_plumbers/form/fields/search.rb +23 -9
- data/lib/stimulus_plumbers/form/fields/submit.rb +23 -0
- data/lib/stimulus_plumbers/helpers/action_list_helper.rb +2 -2
- data/lib/stimulus_plumbers/helpers/avatar_helper.rb +2 -2
- data/lib/stimulus_plumbers/helpers/button_helper.rb +2 -2
- data/lib/stimulus_plumbers/helpers/calendar_helper.rb +1 -1
- data/lib/stimulus_plumbers/helpers/calendar_turbo_helper.rb +1 -1
- data/lib/stimulus_plumbers/helpers/card_helper.rb +2 -2
- data/lib/stimulus_plumbers/helpers/combobox_helper.rb +5 -5
- data/lib/stimulus_plumbers/helpers/popover_helper.rb +2 -2
- data/lib/stimulus_plumbers/plumber/base.rb +20 -0
- data/lib/stimulus_plumbers/plumber/dispatcher.rb +111 -0
- data/lib/stimulus_plumbers/plumber/html_options.rb +51 -0
- data/lib/stimulus_plumbers/plumber/renderer.rb +89 -0
- data/lib/stimulus_plumbers/themes/base.rb +9 -15
- data/lib/stimulus_plumbers/themes/schema/ranges.rb +5 -5
- data/lib/stimulus_plumbers/themes/schema.rb +97 -0
- data/lib/stimulus_plumbers/themes/tailwind/calendar.rb +48 -2
- data/lib/stimulus_plumbers/themes/tailwind/combobox.rb +75 -0
- data/lib/stimulus_plumbers/themes/tailwind_theme.rb +2 -0
- data/lib/stimulus_plumbers/version.rb +1 -1
- data/lib/stimulus_plumbers.rb +29 -19
- metadata +33 -25
- data/lib/stimulus_plumbers/components/action_list/renderer.rb +0 -47
- data/lib/stimulus_plumbers/components/avatar/renderer.rb +0 -74
- data/lib/stimulus_plumbers/components/button/renderer.rb +0 -33
- data/lib/stimulus_plumbers/components/calendar/month/turbo/renderer.rb +0 -57
- data/lib/stimulus_plumbers/components/calendar/renderer.rb +0 -35
- data/lib/stimulus_plumbers/components/card/renderer.rb +0 -41
- data/lib/stimulus_plumbers/components/combobox/option.rb +0 -27
- data/lib/stimulus_plumbers/components/combobox/option_group.rb +0 -52
- data/lib/stimulus_plumbers/components/combobox/renderer.rb +0 -78
- data/lib/stimulus_plumbers/components/icon/renderer.rb +0 -51
- data/lib/stimulus_plumbers/components/plumber/base.rb +0 -22
- data/lib/stimulus_plumbers/components/plumber/dispatcher.rb +0 -113
- data/lib/stimulus_plumbers/components/plumber/html_options.rb +0 -53
- data/lib/stimulus_plumbers/components/plumber/renderer.rb +0 -91
- data/lib/stimulus_plumbers/components/popover/renderer.rb +0 -46
- data/lib/stimulus_plumbers/components/time_picker/renderer.rb +0 -38
- data/lib/stimulus_plumbers/themes/base/action_list.rb +0 -14
- data/lib/stimulus_plumbers/themes/base/avatar.rb +0 -14
- data/lib/stimulus_plumbers/themes/base/button.rb +0 -18
- data/lib/stimulus_plumbers/themes/base/calendar.rb +0 -15
- data/lib/stimulus_plumbers/themes/base/card.rb +0 -12
- data/lib/stimulus_plumbers/themes/base/form.rb +0 -34
- data/lib/stimulus_plumbers/themes/base/layout.rb +0 -12
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "action_view/version"
|
|
4
4
|
|
|
5
|
-
require_relative "
|
|
5
|
+
require_relative "field"
|
|
6
6
|
require_relative "fields/choice"
|
|
7
7
|
require_relative "fields/combobox"
|
|
8
8
|
require_relative "fields/file"
|
|
@@ -12,25 +12,27 @@ require_relative "fields/search"
|
|
|
12
12
|
require_relative "fields/select"
|
|
13
13
|
require_relative "fields/text"
|
|
14
14
|
require_relative "fields/text_area"
|
|
15
|
-
require_relative "
|
|
15
|
+
require_relative "fields/submit"
|
|
16
|
+
require_relative "../plumber/html_options"
|
|
16
17
|
|
|
17
18
|
module StimulusPlumbers
|
|
18
19
|
module Form
|
|
19
20
|
class Builder < ActionView::Helpers::FormBuilder
|
|
20
|
-
include
|
|
21
|
+
include Plumber::HtmlOptions
|
|
21
22
|
include Fields::Choice
|
|
22
23
|
include Fields::Combobox
|
|
23
24
|
include Fields::File
|
|
24
25
|
include Fields::Password
|
|
25
26
|
include Fields::Search
|
|
26
27
|
include Fields::Select
|
|
28
|
+
include Fields::Submit
|
|
27
29
|
include Fields::Text
|
|
28
30
|
include Fields::TextArea
|
|
29
31
|
|
|
30
32
|
private
|
|
31
33
|
|
|
32
34
|
def build_field(attribute, form_field_opts, input_id: field_id(attribute))
|
|
33
|
-
|
|
35
|
+
Field.new(
|
|
34
36
|
object: object,
|
|
35
37
|
attribute: attribute,
|
|
36
38
|
input_id: input_id,
|
|
@@ -57,7 +59,7 @@ module StimulusPlumbers
|
|
|
57
59
|
end
|
|
58
60
|
|
|
59
61
|
def extract_options(options)
|
|
60
|
-
[options.except(*
|
|
62
|
+
[options.except(*Field::OPTIONS), options.slice(*Field::OPTIONS)]
|
|
61
63
|
end
|
|
62
64
|
|
|
63
65
|
def field_theme(key, **variants)
|
|
@@ -27,7 +27,7 @@ module StimulusPlumbers
|
|
|
27
27
|
input: { name: field_name(attribute), value: current_value },
|
|
28
28
|
popover: { content: popover }
|
|
29
29
|
)
|
|
30
|
-
wrapper = Components::Combobox
|
|
30
|
+
wrapper = Components::Combobox.new(@template).render(
|
|
31
31
|
base_id: base_id,
|
|
32
32
|
options: opts,
|
|
33
33
|
**field_theme(:form_combobox, error: field.error?),
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Form
|
|
5
|
+
module Fields
|
|
6
|
+
class Error < Plumber::Base
|
|
7
|
+
def render(message:, id:)
|
|
8
|
+
klass = theme.resolve(:form_error).fetch(:classes, "")
|
|
9
|
+
template.content_tag(:p, message, id: id, class: klass.presence, role: "alert")
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Form
|
|
5
|
+
module Fields
|
|
6
|
+
class Group < Plumber::Base
|
|
7
|
+
def render(layout: :stacked, error: false, &block)
|
|
8
|
+
klass = theme.resolve(:form_group, layout: layout, error: error).fetch(:classes, "")
|
|
9
|
+
template.content_tag(:div, class: klass.presence, &block)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Form
|
|
5
|
+
module Fields
|
|
6
|
+
class Hint < Plumber::Base
|
|
7
|
+
def render(text:, id:)
|
|
8
|
+
klass = theme.resolve(:form_details).fetch(:classes, "")
|
|
9
|
+
template.content_tag(:p, text, id: id, class: klass.presence)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Form
|
|
5
|
+
module Fields
|
|
6
|
+
class Label < Plumber::Base
|
|
7
|
+
def render(text:, for_id:, required: false, hidden: false)
|
|
8
|
+
klass = theme.resolve(:form_label, required: required, hidden: hidden).fetch(:classes, "")
|
|
9
|
+
|
|
10
|
+
inner = text.dup.html_safe
|
|
11
|
+
if required
|
|
12
|
+
mark_klass = theme.resolve(:form_required_mark).fetch(:classes, "")
|
|
13
|
+
inner += template.content_tag(:span, "*", "aria-hidden": "true", class: mark_klass.presence)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
template.content_tag(:label, inner, for: for_id, class: klass.presence)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -13,41 +13,37 @@ module StimulusPlumbers
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def call(input_html)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
template.content_tag(:div, class: field_klass) do
|
|
19
|
-
label_html + input_html.html_safe + hint_html + errors_html
|
|
16
|
+
Group.new(template).render(layout: field.layout, error: field.error?) do
|
|
17
|
+
(field_label + input_html.html_safe + field_hint + field_errors).html_safe
|
|
20
18
|
end
|
|
21
19
|
end
|
|
22
20
|
|
|
23
21
|
private
|
|
24
22
|
|
|
25
|
-
def
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
template.content_tag(:label, inner, for: field.input_id, class: klass)
|
|
23
|
+
def field_label
|
|
24
|
+
Label.new(template).render(
|
|
25
|
+
text: field.label_text,
|
|
26
|
+
for_id: field.input_id,
|
|
27
|
+
required: field.required,
|
|
28
|
+
hidden: field.label_hidden?
|
|
29
|
+
)
|
|
35
30
|
end
|
|
36
31
|
|
|
37
|
-
def
|
|
32
|
+
def field_hint
|
|
38
33
|
return "".html_safe unless field.details.present?
|
|
39
34
|
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
Hint.new(template).render(
|
|
36
|
+
text: field.details,
|
|
37
|
+
id: field.hint_id
|
|
38
|
+
)
|
|
42
39
|
end
|
|
43
40
|
|
|
44
|
-
def
|
|
41
|
+
def field_errors
|
|
45
42
|
return "".html_safe if field.errors.none?
|
|
46
43
|
|
|
47
|
-
klass = theme.resolve(:form_error).fetch(:classes, "")
|
|
48
44
|
field.errors.map.with_index(1) do |message, i|
|
|
49
45
|
id = field.errors.one? ? field.error_id : "#{field.error_id}_#{i}"
|
|
50
|
-
template.
|
|
46
|
+
Error.new(template).render(message: message, id: id)
|
|
51
47
|
end.join.html_safe
|
|
52
48
|
end
|
|
53
49
|
end
|
|
@@ -9,14 +9,26 @@ module StimulusPlumbers
|
|
|
9
9
|
clearable = form_field_opts.delete(:clearable) { false }
|
|
10
10
|
field = build_field(attribute, form_field_opts)
|
|
11
11
|
|
|
12
|
-
html_opts = merge_html_options(
|
|
13
|
-
rails_opts,
|
|
14
|
-
field_theme(:form_input, error: field.error?),
|
|
15
|
-
field.html_opts
|
|
16
|
-
)
|
|
17
12
|
input_html = if clearable
|
|
18
|
-
|
|
13
|
+
input_opts = merge_html_options(
|
|
14
|
+
rails_opts,
|
|
15
|
+
field_theme(:form_input, error: field.error?),
|
|
16
|
+
field.html_opts,
|
|
17
|
+
{ "data-input-search-target": "input", inputmode: "search" }
|
|
18
|
+
)
|
|
19
|
+
build_input_group(
|
|
20
|
+
super(attribute, input_opts),
|
|
21
|
+
field,
|
|
22
|
+
trailing: clear_button,
|
|
23
|
+
"data-controller": "input-search",
|
|
24
|
+
role: "search"
|
|
25
|
+
)
|
|
19
26
|
else
|
|
27
|
+
html_opts = merge_html_options(
|
|
28
|
+
rails_opts,
|
|
29
|
+
field_theme(:form_input, error: field.error?),
|
|
30
|
+
field.html_opts
|
|
31
|
+
)
|
|
20
32
|
super(attribute, html_opts)
|
|
21
33
|
end
|
|
22
34
|
|
|
@@ -29,9 +41,11 @@ module StimulusPlumbers
|
|
|
29
41
|
@template.content_tag(
|
|
30
42
|
:button,
|
|
31
43
|
"",
|
|
32
|
-
type:
|
|
33
|
-
class:
|
|
34
|
-
"aria-label":
|
|
44
|
+
type: "button",
|
|
45
|
+
class: field_theme(:form_button_reveal)[:class],
|
|
46
|
+
"aria-label": "Clear search",
|
|
47
|
+
"data-input-search-target": "clear",
|
|
48
|
+
"data-action": "click->input-search#clear"
|
|
35
49
|
)
|
|
36
50
|
end
|
|
37
51
|
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Form
|
|
5
|
+
module Fields
|
|
6
|
+
module Submit
|
|
7
|
+
def submit(value = nil, options = {})
|
|
8
|
+
if value.is_a?(Hash)
|
|
9
|
+
options = value
|
|
10
|
+
value = nil
|
|
11
|
+
end
|
|
12
|
+
value ||= submit_default_value
|
|
13
|
+
variant = options.delete(:variant) { :default }
|
|
14
|
+
@template.tag.input(
|
|
15
|
+
type: "submit",
|
|
16
|
+
value: value,
|
|
17
|
+
**merge_html_options(field_theme(:form_submit, variant: variant), options)
|
|
18
|
+
)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -4,7 +4,7 @@ module StimulusPlumbers
|
|
|
4
4
|
module Helpers
|
|
5
5
|
module ActionListHelper
|
|
6
6
|
def sp_action_list(**html_options, &block)
|
|
7
|
-
action_list_renderer.
|
|
7
|
+
action_list_renderer.render(**html_options, &block)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def sp_action_list_section(title: nil, **html_options, &block)
|
|
@@ -18,7 +18,7 @@ module StimulusPlumbers
|
|
|
18
18
|
private
|
|
19
19
|
|
|
20
20
|
def action_list_renderer
|
|
21
|
-
Components::ActionList
|
|
21
|
+
Components::ActionList.new(self)
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
end
|
|
@@ -4,13 +4,13 @@ module StimulusPlumbers
|
|
|
4
4
|
module Helpers
|
|
5
5
|
module AvatarHelper
|
|
6
6
|
def sp_avatar(name: nil, initials: nil, url: nil, color: nil, size: :md, **html_options, &block)
|
|
7
|
-
avatar_renderer.
|
|
7
|
+
avatar_renderer.render(name: name, initials: initials, url: url, color: color, size: size, **html_options, &block)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
private
|
|
11
11
|
|
|
12
12
|
def avatar_renderer
|
|
13
|
-
Components::Avatar
|
|
13
|
+
Components::Avatar.new(self)
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
16
|
end
|
|
@@ -4,7 +4,7 @@ module StimulusPlumbers
|
|
|
4
4
|
module Helpers
|
|
5
5
|
module ButtonHelper
|
|
6
6
|
def sp_button(content = nil, url: nil, external: false, variant: :primary, size: :md, **html_options, &block)
|
|
7
|
-
button_renderer.
|
|
7
|
+
button_renderer.render(
|
|
8
8
|
content,
|
|
9
9
|
url: url, external: external, variant: variant, size: size, **html_options,
|
|
10
10
|
&block
|
|
@@ -18,7 +18,7 @@ module StimulusPlumbers
|
|
|
18
18
|
private
|
|
19
19
|
|
|
20
20
|
def button_renderer
|
|
21
|
-
Components::Button
|
|
21
|
+
Components::Button.new(self)
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
end
|
|
@@ -4,7 +4,7 @@ module StimulusPlumbers
|
|
|
4
4
|
module Helpers
|
|
5
5
|
module CardHelper
|
|
6
6
|
def sp_card(title: nil, **html_options, &block)
|
|
7
|
-
card_renderer.
|
|
7
|
+
card_renderer.render(title: title, **html_options, &block)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def sp_card_section(title: nil, **html_options, &block)
|
|
@@ -14,7 +14,7 @@ module StimulusPlumbers
|
|
|
14
14
|
private
|
|
15
15
|
|
|
16
16
|
def card_renderer
|
|
17
|
-
Components::Card
|
|
17
|
+
Components::Card.new(self)
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
end
|
|
@@ -9,7 +9,7 @@ module StimulusPlumbers
|
|
|
9
9
|
popover: { content: Components::Combobox::Date.new(self).render(value: value) }
|
|
10
10
|
)
|
|
11
11
|
opts = opts.deep_merge(trigger: { aria_label: label }) if label
|
|
12
|
-
Components::Combobox
|
|
12
|
+
Components::Combobox.new(self).render(
|
|
13
13
|
base_id: sp_dom_id,
|
|
14
14
|
options: opts,
|
|
15
15
|
data: { input_format_type_value: "date" },
|
|
@@ -23,7 +23,7 @@ module StimulusPlumbers
|
|
|
23
23
|
popover: { content: Components::Combobox::Dropdown.new(self).render(options: options, value: value, label: label) }
|
|
24
24
|
)
|
|
25
25
|
opts = opts.deep_merge(trigger: { aria_label: label }) if label
|
|
26
|
-
Components::Combobox
|
|
26
|
+
Components::Combobox.new(self).render(
|
|
27
27
|
base_id: sp_dom_id,
|
|
28
28
|
options: opts,
|
|
29
29
|
**html_options
|
|
@@ -45,12 +45,12 @@ module StimulusPlumbers
|
|
|
45
45
|
}
|
|
46
46
|
)
|
|
47
47
|
opts = opts.deep_merge(trigger: { aria_label: label }) if label
|
|
48
|
-
Components::Combobox
|
|
48
|
+
Components::Combobox.new(self).render(
|
|
49
49
|
base_id: id,
|
|
50
50
|
options: opts,
|
|
51
51
|
data: {
|
|
52
52
|
input_combobox_combobox_dropdown_outlet: "##{popover_id}",
|
|
53
|
-
action: "input->input-combobox#
|
|
53
|
+
action: "input->input-combobox#onInput"
|
|
54
54
|
},
|
|
55
55
|
**html_options
|
|
56
56
|
)
|
|
@@ -62,7 +62,7 @@ module StimulusPlumbers
|
|
|
62
62
|
popover: { content: Components::Combobox::Time.new(self).render(format: format, step: step, value: value) }
|
|
63
63
|
)
|
|
64
64
|
opts = opts.deep_merge(trigger: { aria_label: label }) if label
|
|
65
|
-
Components::Combobox
|
|
65
|
+
Components::Combobox.new(self).render(
|
|
66
66
|
base_id: sp_dom_id,
|
|
67
67
|
options: opts,
|
|
68
68
|
data: { input_format_type_value: "time", input_format_options_value: { format: format }.to_json },
|
|
@@ -4,13 +4,13 @@ module StimulusPlumbers
|
|
|
4
4
|
module Helpers
|
|
5
5
|
module PopoverHelper
|
|
6
6
|
def sp_popover(interactive: true, **html_options, &block)
|
|
7
|
-
popover_renderer.
|
|
7
|
+
popover_renderer.render(interactive: interactive, **html_options, &block)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
private
|
|
11
11
|
|
|
12
12
|
def popover_renderer
|
|
13
|
-
Components::Popover
|
|
13
|
+
Components::Popover.new(self)
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
16
|
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Plumber
|
|
5
|
+
class Base
|
|
6
|
+
include HtmlOptions
|
|
7
|
+
include Renderer
|
|
8
|
+
|
|
9
|
+
attr_reader :template
|
|
10
|
+
|
|
11
|
+
def initialize(template)
|
|
12
|
+
@template = template
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def theme
|
|
16
|
+
StimulusPlumbers.config.theme
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# :markup: markdown
|
|
4
|
+
|
|
5
|
+
module StimulusPlumbers
|
|
6
|
+
module Plumber
|
|
7
|
+
module Dispatcher
|
|
8
|
+
class MethodCall
|
|
9
|
+
attr_reader :method_name, :args, :kwargs
|
|
10
|
+
|
|
11
|
+
def initialize(method_name, *args, **kwargs)
|
|
12
|
+
@method_name = method_name
|
|
13
|
+
@args = args
|
|
14
|
+
@kwargs = kwargs
|
|
15
|
+
validate!
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def call(target)
|
|
19
|
+
raise NotImplementedError, "#{method_name.inspect} not implemented" unless target.respond_to?(method_name, true)
|
|
20
|
+
|
|
21
|
+
method_call = target.method(method_name)
|
|
22
|
+
accepts_args = method_call.arity.negative? ? args : args.take(method_call.arity)
|
|
23
|
+
accepts_kwargs = method_call.parameters.any? { |type, _| %i[key keyreq keyrest].include?(type) }
|
|
24
|
+
accepts_kwargs ? method_call.call(*accepts_args, **kwargs) : method_call.call(*accepts_args)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def validate!
|
|
30
|
+
return if method_name.is_a?(String) || method_name.is_a?(Symbol)
|
|
31
|
+
|
|
32
|
+
raise ArgumentError, "invalid method name: #{method_name.inspect}"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class InstanceExec
|
|
37
|
+
attr_reader :block, :args, :kwargs
|
|
38
|
+
|
|
39
|
+
def initialize(block, *args, **kwargs)
|
|
40
|
+
@block = block
|
|
41
|
+
@args = args
|
|
42
|
+
@kwargs = kwargs
|
|
43
|
+
validate!
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def call(target)
|
|
47
|
+
accepts_args = block.arity.negative? ? args : args.take(block.arity)
|
|
48
|
+
accepts_kwargs = block.parameters.any? { |type, _| %i[key keyreq keyrest].include?(type) }
|
|
49
|
+
if accepts_kwargs
|
|
50
|
+
target.instance_exec(
|
|
51
|
+
*accepts_args,
|
|
52
|
+
**kwargs,
|
|
53
|
+
&block
|
|
54
|
+
)
|
|
55
|
+
else
|
|
56
|
+
target.instance_exec(*accepts_args, &block)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
def validate!
|
|
63
|
+
raise ArgumentError, "invalid block: #{block.inspect}" unless block.is_a?(Proc)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
class KlassProxy
|
|
68
|
+
attr_reader :klass, :method_name, :args, :kwargs, :init_args, :init_kwargs
|
|
69
|
+
|
|
70
|
+
def initialize(klass, method_name, *args, init_args: [], init_kwargs: {}, **kwargs)
|
|
71
|
+
@klass = klass
|
|
72
|
+
@method_name = method_name
|
|
73
|
+
@args = args
|
|
74
|
+
@kwargs = kwargs
|
|
75
|
+
@init_args = init_args
|
|
76
|
+
@init_kwargs = init_kwargs
|
|
77
|
+
validate!
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def call(_target)
|
|
81
|
+
klass.new(*init_args, **init_kwargs).public_send(method_name, *args, **kwargs)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
def validate!
|
|
87
|
+
raise ArgumentError, "invalid class: #{klass.inspect}" unless klass.is_a?(Module)
|
|
88
|
+
return if method_name.is_a?(String) || method_name.is_a?(Symbol)
|
|
89
|
+
|
|
90
|
+
raise ArgumentError, "invalid method name: #{method_name.inspect}"
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def self.build(callable, *args, method_name: nil, init_args: [], init_kwargs: {}, **kwargs)
|
|
95
|
+
case callable
|
|
96
|
+
when Symbol
|
|
97
|
+
MethodCall.new(callable, *args, **kwargs)
|
|
98
|
+
when Proc
|
|
99
|
+
InstanceExec.new(callable, *args, **kwargs)
|
|
100
|
+
when Module
|
|
101
|
+
KlassProxy.new(callable, method_name, *args, init_args: init_args, init_kwargs: init_kwargs, **kwargs)
|
|
102
|
+
when String
|
|
103
|
+
klass = callable.safe_constantize
|
|
104
|
+
raise ArgumentError, "could not resolve class from: #{callable.inspect}" unless klass
|
|
105
|
+
|
|
106
|
+
KlassProxy.new(klass, method_name, *args, init_args: init_args, init_kwargs: init_kwargs, **kwargs)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/concern"
|
|
4
|
+
|
|
5
|
+
module StimulusPlumbers
|
|
6
|
+
module Plumber
|
|
7
|
+
module HtmlOptions
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
def merge_html_options(*hashes)
|
|
11
|
+
classes = hashes.flat_map { |h| [h[:class], h[:classes]] }
|
|
12
|
+
data_hashes = hashes.map { |h| h[:data] || {} }
|
|
13
|
+
rest = hashes.map { |h| h.except(:class, :classes, :data) }.reduce({}, :deep_merge)
|
|
14
|
+
|
|
15
|
+
class_value = merge_string_option(*classes).presence
|
|
16
|
+
merged_data = merge_data_options(*data_hashes)
|
|
17
|
+
|
|
18
|
+
result = class_value ? rest.merge(class: class_value) : rest
|
|
19
|
+
merged_data.present? ? result.merge(data: merged_data) : result
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
STIMULUS_SPACEJOIN_KEYS = %i[controller action].freeze
|
|
23
|
+
|
|
24
|
+
def merge_data_options(*hashes, spacejoin: STIMULUS_SPACEJOIN_KEYS)
|
|
25
|
+
hashes.reduce({}) do |acc, d|
|
|
26
|
+
acc.merge(d) do |key, old_val, new_val|
|
|
27
|
+
if spacejoin.include?(key.to_sym)
|
|
28
|
+
merge_string_option(old_val, new_val).presence || new_val
|
|
29
|
+
else
|
|
30
|
+
new_val
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def merge_string_option(*parts, delimiter: " ")
|
|
37
|
+
tokens = parts.flat_map { |part| normalize_part(part, delimiter) }
|
|
38
|
+
tokens.compact.uniq.join(delimiter)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def normalize_part(value, delimiter)
|
|
42
|
+
case value
|
|
43
|
+
when String then value.present? ? value.split(delimiter) : []
|
|
44
|
+
when Hash then value.filter_map { |key, val| key if val }
|
|
45
|
+
when Array then [merge_string_option(*value).presence]
|
|
46
|
+
else []
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|