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.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +75 -0
  3. data/app/assets/javascripts/stimulus-plumbers/.keep +0 -0
  4. data/app/assets/stylesheets/stimulus_plumbers/tokens.css +83 -0
  5. data/lib/stimulus_plumbers/components/action_list/renderer.rb +47 -0
  6. data/lib/stimulus_plumbers/components/avatar/renderer.rb +74 -0
  7. data/lib/stimulus_plumbers/components/button/renderer.rb +33 -0
  8. data/lib/stimulus_plumbers/components/calendar/month/turbo/days_of_month.rb +124 -0
  9. data/lib/stimulus_plumbers/components/calendar/month/turbo/days_of_week.rb +36 -0
  10. data/lib/stimulus_plumbers/components/calendar/month/turbo/renderer.rb +57 -0
  11. data/lib/stimulus_plumbers/components/calendar/renderer.rb +35 -0
  12. data/lib/stimulus_plumbers/components/card/renderer.rb +41 -0
  13. data/lib/stimulus_plumbers/components/date_picker/navigation.rb +49 -0
  14. data/lib/stimulus_plumbers/components/date_picker/navigator.rb +31 -0
  15. data/lib/stimulus_plumbers/components/date_picker/renderer.rb +82 -0
  16. data/lib/stimulus_plumbers/components/icon/renderer.rb +51 -0
  17. data/lib/stimulus_plumbers/components/plumber/base.rb +22 -0
  18. data/lib/stimulus_plumbers/components/plumber/dispatcher.rb +113 -0
  19. data/lib/stimulus_plumbers/components/plumber/html_options.rb +34 -0
  20. data/lib/stimulus_plumbers/components/plumber/renderer.rb +91 -0
  21. data/lib/stimulus_plumbers/components/popover/renderer.rb +46 -0
  22. data/lib/stimulus_plumbers/configuration.rb +39 -0
  23. data/lib/stimulus_plumbers/engine.rb +21 -0
  24. data/lib/stimulus_plumbers/form/builder.rb +67 -0
  25. data/lib/stimulus_plumbers/form/field_component.rb +80 -0
  26. data/lib/stimulus_plumbers/form/fields/choice.rb +25 -0
  27. data/lib/stimulus_plumbers/form/fields/file.rb +16 -0
  28. data/lib/stimulus_plumbers/form/fields/renderer.rb +57 -0
  29. data/lib/stimulus_plumbers/form/fields/select.rb +27 -0
  30. data/lib/stimulus_plumbers/form/fields/text.rb +25 -0
  31. data/lib/stimulus_plumbers/form/fields/text_area.rb +16 -0
  32. data/lib/stimulus_plumbers/helpers/action_list_helper.rb +25 -0
  33. data/lib/stimulus_plumbers/helpers/avatar_helper.rb +17 -0
  34. data/lib/stimulus_plumbers/helpers/button_helper.rb +25 -0
  35. data/lib/stimulus_plumbers/helpers/calendar_helper.rb +26 -0
  36. data/lib/stimulus_plumbers/helpers/calendar_turbo_helper.rb +31 -0
  37. data/lib/stimulus_plumbers/helpers/card_helper.rb +21 -0
  38. data/lib/stimulus_plumbers/helpers/date_picker_helper.rb +17 -0
  39. data/lib/stimulus_plumbers/helpers/plumber_helper.rb +15 -0
  40. data/lib/stimulus_plumbers/helpers/popover_helper.rb +17 -0
  41. data/lib/stimulus_plumbers/helpers.rb +25 -0
  42. data/lib/stimulus_plumbers/logger.rb +20 -0
  43. data/lib/stimulus_plumbers/themes/action_list.rb +14 -0
  44. data/lib/stimulus_plumbers/themes/avatar.rb +14 -0
  45. data/lib/stimulus_plumbers/themes/base.rb +73 -0
  46. data/lib/stimulus_plumbers/themes/button.rb +18 -0
  47. data/lib/stimulus_plumbers/themes/calendar.rb +15 -0
  48. data/lib/stimulus_plumbers/themes/card.rb +12 -0
  49. data/lib/stimulus_plumbers/themes/form.rb +30 -0
  50. data/lib/stimulus_plumbers/themes/layout.rb +12 -0
  51. data/lib/stimulus_plumbers/themes/schema/ranges.rb +15 -0
  52. data/lib/stimulus_plumbers/themes/tailwind/action_list.rb +33 -0
  53. data/lib/stimulus_plumbers/themes/tailwind/avatar.rb +52 -0
  54. data/lib/stimulus_plumbers/themes/tailwind/button.rb +89 -0
  55. data/lib/stimulus_plumbers/themes/tailwind/calendar.rb +34 -0
  56. data/lib/stimulus_plumbers/themes/tailwind/card.rb +24 -0
  57. data/lib/stimulus_plumbers/themes/tailwind/form.rb +104 -0
  58. data/lib/stimulus_plumbers/themes/tailwind/layout.rb +25 -0
  59. data/lib/stimulus_plumbers/themes/tailwind_theme.rb +29 -0
  60. data/lib/stimulus_plumbers/version.rb +5 -0
  61. data/lib/stimulus_plumbers.rb +48 -0
  62. 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,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusPlumbers
4
+ module Themes
5
+ module Card
6
+ SCHEMA = {
7
+ card: {}.freeze,
8
+ card_section: {}.freeze
9
+ }.freeze
10
+ end
11
+ end
12
+ 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,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StimulusPlumbers
4
+ module Themes
5
+ module Layout
6
+ SCHEMA = {
7
+ divider: {}.freeze,
8
+ popover: {}.freeze
9
+ }.freeze
10
+ end
11
+ end
12
+ 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