stimulus_plumbers 0.2.2
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 +7 -0
- data/README.md +75 -0
- data/app/assets/javascripts/stimulus-plumbers/.keep +0 -0
- data/app/assets/stylesheets/stimulus_plumbers/tokens.css +83 -0
- data/lib/stimulus_plumbers/components/action_list/renderer.rb +47 -0
- data/lib/stimulus_plumbers/components/avatar/renderer.rb +74 -0
- data/lib/stimulus_plumbers/components/button/renderer.rb +33 -0
- data/lib/stimulus_plumbers/components/calendar/month/turbo/days_of_month.rb +124 -0
- data/lib/stimulus_plumbers/components/calendar/month/turbo/days_of_week.rb +36 -0
- data/lib/stimulus_plumbers/components/calendar/month/turbo/renderer.rb +57 -0
- data/lib/stimulus_plumbers/components/calendar/renderer.rb +35 -0
- data/lib/stimulus_plumbers/components/card/renderer.rb +41 -0
- data/lib/stimulus_plumbers/components/date_picker/navigation.rb +49 -0
- data/lib/stimulus_plumbers/components/date_picker/navigator.rb +31 -0
- data/lib/stimulus_plumbers/components/date_picker/renderer.rb +82 -0
- data/lib/stimulus_plumbers/components/icon/renderer.rb +51 -0
- data/lib/stimulus_plumbers/components/plumber/base.rb +22 -0
- data/lib/stimulus_plumbers/components/plumber/dispatcher.rb +113 -0
- data/lib/stimulus_plumbers/components/plumber/html_options.rb +34 -0
- data/lib/stimulus_plumbers/components/plumber/renderer.rb +91 -0
- data/lib/stimulus_plumbers/components/popover/renderer.rb +46 -0
- data/lib/stimulus_plumbers/configuration.rb +39 -0
- data/lib/stimulus_plumbers/engine.rb +21 -0
- data/lib/stimulus_plumbers/form/builder.rb +67 -0
- data/lib/stimulus_plumbers/form/field_component.rb +80 -0
- data/lib/stimulus_plumbers/form/fields/choice.rb +25 -0
- data/lib/stimulus_plumbers/form/fields/file.rb +16 -0
- data/lib/stimulus_plumbers/form/fields/renderer.rb +57 -0
- data/lib/stimulus_plumbers/form/fields/select.rb +27 -0
- data/lib/stimulus_plumbers/form/fields/text.rb +25 -0
- data/lib/stimulus_plumbers/form/fields/text_area.rb +16 -0
- data/lib/stimulus_plumbers/helpers/action_list_helper.rb +25 -0
- data/lib/stimulus_plumbers/helpers/avatar_helper.rb +17 -0
- data/lib/stimulus_plumbers/helpers/button_helper.rb +25 -0
- data/lib/stimulus_plumbers/helpers/calendar_helper.rb +26 -0
- data/lib/stimulus_plumbers/helpers/calendar_turbo_helper.rb +31 -0
- data/lib/stimulus_plumbers/helpers/card_helper.rb +21 -0
- data/lib/stimulus_plumbers/helpers/date_picker_helper.rb +17 -0
- data/lib/stimulus_plumbers/helpers/plumber_helper.rb +15 -0
- data/lib/stimulus_plumbers/helpers/popover_helper.rb +17 -0
- data/lib/stimulus_plumbers/helpers.rb +25 -0
- data/lib/stimulus_plumbers/logger.rb +20 -0
- data/lib/stimulus_plumbers/themes/action_list.rb +14 -0
- data/lib/stimulus_plumbers/themes/avatar.rb +14 -0
- data/lib/stimulus_plumbers/themes/base.rb +73 -0
- data/lib/stimulus_plumbers/themes/button.rb +18 -0
- data/lib/stimulus_plumbers/themes/calendar.rb +15 -0
- data/lib/stimulus_plumbers/themes/card.rb +12 -0
- data/lib/stimulus_plumbers/themes/form.rb +30 -0
- data/lib/stimulus_plumbers/themes/layout.rb +12 -0
- data/lib/stimulus_plumbers/themes/schema/ranges.rb +15 -0
- data/lib/stimulus_plumbers/themes/tailwind/action_list.rb +33 -0
- data/lib/stimulus_plumbers/themes/tailwind/avatar.rb +52 -0
- data/lib/stimulus_plumbers/themes/tailwind/button.rb +89 -0
- data/lib/stimulus_plumbers/themes/tailwind/calendar.rb +34 -0
- data/lib/stimulus_plumbers/themes/tailwind/card.rb +24 -0
- data/lib/stimulus_plumbers/themes/tailwind/form.rb +104 -0
- data/lib/stimulus_plumbers/themes/tailwind/layout.rb +25 -0
- data/lib/stimulus_plumbers/themes/tailwind_theme.rb +29 -0
- data/lib/stimulus_plumbers/version.rb +5 -0
- data/lib/stimulus_plumbers.rb +48 -0
- metadata +129 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Form
|
|
5
|
+
module Fields
|
|
6
|
+
module Choice
|
|
7
|
+
def check_box(attribute, options = {}, checked_value = "1", unchecked_value = "0")
|
|
8
|
+
rails_opts, form_field_opts = extract_options(options)
|
|
9
|
+
form_field_opts[:layout] ||= :inline
|
|
10
|
+
field = build_field(attribute, form_field_opts)
|
|
11
|
+
html_opts = merge_html_options(rails_opts, field_theme(:form_checkbox, error: field.error?), field.html_opts)
|
|
12
|
+
render_field(field, super(attribute, html_opts, checked_value, unchecked_value))
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def radio_button(attribute, tag_value, options = {})
|
|
16
|
+
rails_opts, form_field_opts = extract_options(options)
|
|
17
|
+
form_field_opts[:layout] ||= :inline
|
|
18
|
+
field = build_field(attribute, form_field_opts, input_id: field_id(attribute, tag_value))
|
|
19
|
+
html_opts = merge_html_options(rails_opts, field_theme(:form_radio, error: field.error?), field.html_opts)
|
|
20
|
+
render_field(field, super(attribute, tag_value, html_opts))
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Form
|
|
5
|
+
module Fields
|
|
6
|
+
module File
|
|
7
|
+
def file_field(attribute, options = {})
|
|
8
|
+
rails_opts, form_field_opts = extract_options(options)
|
|
9
|
+
field = build_field(attribute, form_field_opts)
|
|
10
|
+
html_opts = merge_html_options(rails_opts, field_theme(:form_file, error: field.error?), field.html_opts)
|
|
11
|
+
render_field(field, super(attribute, html_opts))
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Form
|
|
5
|
+
module Fields
|
|
6
|
+
class Renderer
|
|
7
|
+
attr_reader :template, :theme, :field
|
|
8
|
+
|
|
9
|
+
def initialize(template, theme, field)
|
|
10
|
+
@template = template
|
|
11
|
+
@theme = theme
|
|
12
|
+
@field = field
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def call(input_html)
|
|
16
|
+
field_klass = theme.resolve(:form_group, layout: field.layout, error: field.error?).fetch(:classes, "")
|
|
17
|
+
|
|
18
|
+
template.content_tag(:div, class: field_klass) do
|
|
19
|
+
label_html + input_html.html_safe + hint_html + errors_html
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def label_html
|
|
26
|
+
klass = theme.resolve(:form_label, required: field.required).fetch(:classes, "")
|
|
27
|
+
klass = "#{klass} sr-only".strip if field.label_hidden?
|
|
28
|
+
|
|
29
|
+
inner = field.label_text.dup.html_safe
|
|
30
|
+
if field.required
|
|
31
|
+
mark_klass = theme.resolve(:form_required_mark).fetch(:classes, "")
|
|
32
|
+
inner += template.content_tag(:span, "*", "aria-hidden": "true", class: mark_klass)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
template.content_tag(:label, inner, for: field.input_id, class: klass)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def hint_html
|
|
39
|
+
return "".html_safe unless field.details.present?
|
|
40
|
+
|
|
41
|
+
klass = theme.resolve(:form_details).fetch(:classes, "")
|
|
42
|
+
template.content_tag(:p, field.details, id: field.hint_id, class: klass)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def errors_html
|
|
46
|
+
return "".html_safe if field.errors.none?
|
|
47
|
+
|
|
48
|
+
klass = theme.resolve(:form_error).fetch(:classes, "")
|
|
49
|
+
field.errors.map.with_index(1) do |message, i|
|
|
50
|
+
id = field.errors.one? ? field.error_id : "#{field.error_id}_#{i}"
|
|
51
|
+
template.content_tag(:p, message, id: id, class: klass, role: "alert")
|
|
52
|
+
end.join.html_safe
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Form
|
|
5
|
+
module Fields
|
|
6
|
+
module Select
|
|
7
|
+
def select(attribute, choices = nil, options = {}, html_options = {})
|
|
8
|
+
rails_opts, form_field_opts = extract_options(options)
|
|
9
|
+
field = build_field(attribute, form_field_opts)
|
|
10
|
+
html_opts = merge_html_options(html_options, field_theme(:form_select, error: field.error?), field.html_opts)
|
|
11
|
+
render_field(field, super(attribute, choices, rails_opts, html_opts))
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def collection_select(attribute, collection, value_method, text_method,
|
|
15
|
+
options = {}, html_options = {})
|
|
16
|
+
rails_opts, form_field_opts = extract_options(options)
|
|
17
|
+
field = build_field(attribute, form_field_opts)
|
|
18
|
+
html_opts = merge_html_options(html_options, field_theme(:form_select, error: field.error?), field.html_opts)
|
|
19
|
+
render_field(
|
|
20
|
+
field,
|
|
21
|
+
super(attribute, collection, value_method, text_method, rails_opts, html_opts)
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Form
|
|
5
|
+
module Fields
|
|
6
|
+
module Text
|
|
7
|
+
FIELD_TYPES = %i[
|
|
8
|
+
text_field email_field url_field telephone_field search_field
|
|
9
|
+
number_field color_field date_field datetime_local_field
|
|
10
|
+
time_field month_field week_field range_field
|
|
11
|
+
password_field
|
|
12
|
+
].freeze
|
|
13
|
+
|
|
14
|
+
FIELD_TYPES.each do |method_name|
|
|
15
|
+
define_method(method_name) do |attribute, options = {}|
|
|
16
|
+
rails_opts, form_field_opts = extract_options(options)
|
|
17
|
+
field = build_field(attribute, form_field_opts)
|
|
18
|
+
html_opts = merge_html_options(rails_opts, field_theme(:form_input, error: field.error?), field.html_opts)
|
|
19
|
+
render_field(field, super(attribute, html_opts))
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Form
|
|
5
|
+
module Fields
|
|
6
|
+
module TextArea
|
|
7
|
+
def text_area(attribute, options = {})
|
|
8
|
+
rails_opts, form_field_opts = extract_options(options)
|
|
9
|
+
field = build_field(attribute, form_field_opts)
|
|
10
|
+
html_opts = merge_html_options(rails_opts, field_theme(:form_textarea, error: field.error?), field.html_opts)
|
|
11
|
+
render_field(field, super(attribute, html_opts))
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Helpers
|
|
5
|
+
module ActionListHelper
|
|
6
|
+
def sp_action_list(**html_options, &block)
|
|
7
|
+
action_list_renderer.list(**html_options, &block)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def sp_action_list_section(title: nil, **html_options, &block)
|
|
11
|
+
action_list_renderer.section(title: title, **html_options, &block)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def sp_action_list_item(content = nil, url: nil, external: false, active: false, **html_options, &block)
|
|
15
|
+
action_list_renderer.item(content, url: url, external: external, active: active, **html_options, &block)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def action_list_renderer
|
|
21
|
+
Components::ActionList::Renderer.new(self)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Helpers
|
|
5
|
+
module AvatarHelper
|
|
6
|
+
def sp_avatar(name: nil, initials: nil, url: nil, color: nil, size: :md, **html_options, &block)
|
|
7
|
+
avatar_renderer.avatar(name: name, initials: initials, url: url, color: color, size: size, **html_options, &block)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def avatar_renderer
|
|
13
|
+
Components::Avatar::Renderer.new(self)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Helpers
|
|
5
|
+
module ButtonHelper
|
|
6
|
+
def sp_button(content = nil, url: nil, external: false, variant: :primary, size: :md, **html_options, &block)
|
|
7
|
+
button_renderer.button(
|
|
8
|
+
content,
|
|
9
|
+
url: url, external: external, variant: variant, size: size, **html_options,
|
|
10
|
+
&block
|
|
11
|
+
)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def sp_button_group(alignment: :left, direction: :row, **html_options, &block)
|
|
15
|
+
button_renderer.group(alignment: alignment, direction: direction, **html_options, &block)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def button_renderer
|
|
21
|
+
Components::Button::Renderer.new(self)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Helpers
|
|
5
|
+
module CalendarHelper
|
|
6
|
+
def sp_calendar_month(date: nil, **html_options, &block)
|
|
7
|
+
if date
|
|
8
|
+
html_options.deep_merge!(
|
|
9
|
+
data: {
|
|
10
|
+
"calendar-month-year-value": date.year,
|
|
11
|
+
"calendar-month-month-value": date.month - 1,
|
|
12
|
+
"calendar-month-day-value": date.day
|
|
13
|
+
}
|
|
14
|
+
)
|
|
15
|
+
end
|
|
16
|
+
calendar_renderer.month(**html_options, &block)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def calendar_renderer
|
|
22
|
+
Components::Calendar::Renderer.new(self)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Helpers
|
|
5
|
+
module CalendarTurboHelper
|
|
6
|
+
def sp_calendar_month_turbo(
|
|
7
|
+
date: Date.today,
|
|
8
|
+
today: Date.today,
|
|
9
|
+
selectable: false,
|
|
10
|
+
selected_date: nil,
|
|
11
|
+
show_other_months: false,
|
|
12
|
+
**html_options
|
|
13
|
+
)
|
|
14
|
+
calendar_month_turbo_renderer.render(
|
|
15
|
+
date: date,
|
|
16
|
+
today: today,
|
|
17
|
+
selectable: selectable,
|
|
18
|
+
selected_date: selected_date,
|
|
19
|
+
show_other_months: show_other_months,
|
|
20
|
+
**html_options
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def calendar_month_turbo_renderer
|
|
27
|
+
Components::Calendar::Month::Turbo::Renderer.new(self)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Helpers
|
|
5
|
+
module CardHelper
|
|
6
|
+
def sp_card(title: nil, **html_options, &block)
|
|
7
|
+
card_renderer.card(title: title, **html_options, &block)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def sp_card_section(title: nil, **html_options, &block)
|
|
11
|
+
card_renderer.section(title: title, **html_options, &block)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def card_renderer
|
|
17
|
+
Components::Card::Renderer.new(self)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Helpers
|
|
5
|
+
module DatePickerHelper
|
|
6
|
+
def sp_date_picker_month(record = nil, attribute = nil, **html_options)
|
|
7
|
+
calendar_id = sp_dom_id(record, [attribute, "date"].filter(&:presence).join("_"))
|
|
8
|
+
calendar_dialog_id = "#{calendar_id}_dialog"
|
|
9
|
+
Components::DatePicker::Renderer.new(self).render(
|
|
10
|
+
calendar_id: calendar_id,
|
|
11
|
+
calendar_dialog_id: calendar_dialog_id,
|
|
12
|
+
**html_options
|
|
13
|
+
)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Helpers
|
|
5
|
+
module PlumberHelper
|
|
6
|
+
def sp_dom_id(record = nil, suffix = nil)
|
|
7
|
+
if record
|
|
8
|
+
dom_id(record, suffix)
|
|
9
|
+
else
|
|
10
|
+
"#{suffix}_#{SecureRandom.hex(8)}"
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Helpers
|
|
5
|
+
module PopoverHelper
|
|
6
|
+
def sp_popover(interactive: true, **html_options, &block)
|
|
7
|
+
popover_renderer.popover(interactive: interactive, **html_options, &block)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def popover_renderer
|
|
13
|
+
Components::Popover::Renderer.new(self)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "helpers/plumber_helper"
|
|
4
|
+
require_relative "helpers/action_list_helper"
|
|
5
|
+
require_relative "helpers/avatar_helper"
|
|
6
|
+
require_relative "helpers/button_helper"
|
|
7
|
+
require_relative "helpers/calendar_helper"
|
|
8
|
+
require_relative "helpers/calendar_turbo_helper"
|
|
9
|
+
require_relative "helpers/card_helper"
|
|
10
|
+
require_relative "helpers/date_picker_helper"
|
|
11
|
+
require_relative "helpers/popover_helper"
|
|
12
|
+
|
|
13
|
+
module StimulusPlumbers
|
|
14
|
+
module Helpers
|
|
15
|
+
include PlumberHelper
|
|
16
|
+
include ActionListHelper
|
|
17
|
+
include AvatarHelper
|
|
18
|
+
include ButtonHelper
|
|
19
|
+
include CalendarHelper
|
|
20
|
+
include CalendarTurboHelper
|
|
21
|
+
include CardHelper
|
|
22
|
+
include DatePickerHelper
|
|
23
|
+
include PopoverHelper
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Logger
|
|
5
|
+
LEVELS = %i[debug info warn error].freeze
|
|
6
|
+
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
LEVELS.each do |level|
|
|
10
|
+
define_method(level) do |message|
|
|
11
|
+
tagged = StimulusPlumbers.config.log_formatter.call(message)
|
|
12
|
+
if defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger
|
|
13
|
+
Rails.logger.public_send(level, tagged)
|
|
14
|
+
else
|
|
15
|
+
warn(tagged)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Themes
|
|
5
|
+
module ActionList
|
|
6
|
+
SCHEMA = {
|
|
7
|
+
action_list_item: {
|
|
8
|
+
active: { default: false, range: Schema::Ranges::BOOL_RANGE }
|
|
9
|
+
}.freeze,
|
|
10
|
+
action_list: {}.freeze
|
|
11
|
+
}.freeze
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Themes
|
|
5
|
+
module Avatar
|
|
6
|
+
SCHEMA = {
|
|
7
|
+
avatar: {
|
|
8
|
+
size: { default: :md, range: Schema::Ranges::SIZE_RANGE },
|
|
9
|
+
color: { default: nil, range: :avatar_color_range }
|
|
10
|
+
}.freeze
|
|
11
|
+
}.freeze
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "schema/ranges"
|
|
4
|
+
require_relative "action_list"
|
|
5
|
+
require_relative "avatar"
|
|
6
|
+
require_relative "button"
|
|
7
|
+
require_relative "calendar"
|
|
8
|
+
require_relative "card"
|
|
9
|
+
require_relative "form"
|
|
10
|
+
require_relative "layout"
|
|
11
|
+
|
|
12
|
+
module StimulusPlumbers
|
|
13
|
+
module Themes
|
|
14
|
+
class Base
|
|
15
|
+
SCHEMA = {
|
|
16
|
+
**ActionList::SCHEMA,
|
|
17
|
+
**Avatar::SCHEMA,
|
|
18
|
+
**Button::SCHEMA,
|
|
19
|
+
**Calendar::SCHEMA,
|
|
20
|
+
**Card::SCHEMA,
|
|
21
|
+
**Form::SCHEMA,
|
|
22
|
+
**Layout::SCHEMA
|
|
23
|
+
}.freeze
|
|
24
|
+
|
|
25
|
+
def name
|
|
26
|
+
@name ||= self.class.name.demodulize.delete_suffix("Theme")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def attribute_names(component)
|
|
30
|
+
SCHEMA.fetch(component, {}).keys
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Resolves presentational classes for a component slot.
|
|
34
|
+
# Returns a Hash with :classes (String) and optionally :style (String).
|
|
35
|
+
# Returns {} when no mapping exists for the given component.
|
|
36
|
+
def resolve(component, **args)
|
|
37
|
+
method_name = :"#{component}_classes"
|
|
38
|
+
unless respond_to?(method_name, true)
|
|
39
|
+
StimulusPlumbers::Logger.warn("#{self.class} has no classes method for component #{component.inspect}")
|
|
40
|
+
return {}
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
send(method_name, **validate_args(component.to_sym, args))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def validate_args(component, args)
|
|
49
|
+
schema = SCHEMA.fetch(component, {})
|
|
50
|
+
args.slice(*schema.keys).each_with_object({}) do |(key, value), result|
|
|
51
|
+
result[key] = coerce_arg(component, key, value, schema[key])
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def coerce_arg(component, key, value, schema)
|
|
56
|
+
return value unless schema
|
|
57
|
+
|
|
58
|
+
range = if schema[:range].is_a?(Symbol)
|
|
59
|
+
respond_to?(schema[:range], true) ? send(schema[:range]) : []
|
|
60
|
+
else
|
|
61
|
+
schema[:range]
|
|
62
|
+
end
|
|
63
|
+
return value if range.empty? || range.include?(value)
|
|
64
|
+
|
|
65
|
+
StimulusPlumbers::Logger.warn(
|
|
66
|
+
"#{component}##{key} received unknown value #{value.inspect}. " \
|
|
67
|
+
"Range: #{schema[:range].inspect}. Falling back to: #{schema[:default].inspect}"
|
|
68
|
+
)
|
|
69
|
+
schema[:default]
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Themes
|
|
5
|
+
module Button
|
|
6
|
+
SCHEMA = {
|
|
7
|
+
button: {
|
|
8
|
+
variant: { default: :primary, range: %i[primary secondary outline destructive ghost link].freeze },
|
|
9
|
+
size: { default: :md, range: Schema::Ranges::SIZE_RANGE }
|
|
10
|
+
}.freeze,
|
|
11
|
+
button_group: {
|
|
12
|
+
alignment: { default: :left, range: Schema::Ranges::ALIGN_RANGE },
|
|
13
|
+
direction: { default: :row, range: Schema::Ranges::DIR_RANGE }
|
|
14
|
+
}.freeze
|
|
15
|
+
}.freeze
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Themes
|
|
5
|
+
module Calendar
|
|
6
|
+
SCHEMA = {
|
|
7
|
+
calendar_day: {
|
|
8
|
+
today: { default: false, range: Schema::Ranges::BOOL_RANGE },
|
|
9
|
+
selected: { default: false, range: Schema::Ranges::BOOL_RANGE },
|
|
10
|
+
outside: { default: false, range: Schema::Ranges::BOOL_RANGE }
|
|
11
|
+
}.freeze
|
|
12
|
+
}.freeze
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Themes
|
|
5
|
+
module Form
|
|
6
|
+
SCHEMA = {
|
|
7
|
+
form_group: {
|
|
8
|
+
layout: { default: :stacked, range: Schema::Ranges::LAYOUT_RANGE },
|
|
9
|
+
error: { default: false, range: Schema::Ranges::BOOL_RANGE }
|
|
10
|
+
}.freeze,
|
|
11
|
+
form_label: { required: { default: false, range: Schema::Ranges::BOOL_RANGE } }.freeze,
|
|
12
|
+
form_required_mark: {}.freeze,
|
|
13
|
+
form_details: {}.freeze,
|
|
14
|
+
form_error: {}.freeze,
|
|
15
|
+
form_input: { error: { default: false, range: Schema::Ranges::BOOL_RANGE } }.freeze,
|
|
16
|
+
form_textarea: { error: { default: false, range: Schema::Ranges::BOOL_RANGE } }.freeze,
|
|
17
|
+
form_file: { error: { default: false, range: Schema::Ranges::BOOL_RANGE } }.freeze,
|
|
18
|
+
form_select: { error: { default: false, range: Schema::Ranges::BOOL_RANGE } }.freeze,
|
|
19
|
+
form_checkbox: { error: { default: false, range: Schema::Ranges::BOOL_RANGE } }.freeze,
|
|
20
|
+
form_radio: { error: { default: false, range: Schema::Ranges::BOOL_RANGE } }.freeze,
|
|
21
|
+
form_actor: { error: { default: false, range: Schema::Ranges::BOOL_RANGE } }.freeze,
|
|
22
|
+
form_input_reveal: {}.freeze,
|
|
23
|
+
form_button_reveal: {}.freeze,
|
|
24
|
+
form_submit: {
|
|
25
|
+
variant: { default: :default, range: %i[default button].freeze }
|
|
26
|
+
}.freeze
|
|
27
|
+
}.freeze
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Themes
|
|
5
|
+
module Schema
|
|
6
|
+
module Ranges
|
|
7
|
+
BOOL_RANGE = [true, false].freeze
|
|
8
|
+
SIZE_RANGE = %i[sm md lg].freeze
|
|
9
|
+
ALIGN_RANGE = %i[left center right top bottom].freeze
|
|
10
|
+
DIR_RANGE = %i[row col].freeze
|
|
11
|
+
LAYOUT_RANGE = %i[stacked inline].freeze
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Themes
|
|
5
|
+
module Tailwind
|
|
6
|
+
module ActionList
|
|
7
|
+
LIST_ITEM_ACTIVE = %w[bg-[--sp-color-primary]/10 text-[--sp-color-primary]].freeze
|
|
8
|
+
LIST_ITEM_BASE = %w[
|
|
9
|
+
flex items-center gap-[--sp-space-2] w-full
|
|
10
|
+
px-[--sp-space-2] py-[--sp-space-1]
|
|
11
|
+
rounded-[--sp-radius-sm] text-[--sp-text-sm]
|
|
12
|
+
cursor-pointer select-none outline-none
|
|
13
|
+
hover:bg-[--sp-color-muted] focus:bg-[--sp-color-muted] focus:text-[--sp-color-fg]
|
|
14
|
+
].freeze
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def action_list_classes
|
|
19
|
+
{ classes: klasses("py-[--sp-space-1]") }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def action_list_item_classes(active: false)
|
|
23
|
+
{
|
|
24
|
+
classes: klasses(
|
|
25
|
+
*LIST_ITEM_BASE,
|
|
26
|
+
*(active ? LIST_ITEM_ACTIVE : [])
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|