stimulus_plumbers 0.2.7 → 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.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +59 -0
  3. data/README.md +60 -41
  4. data/app/assets/javascripts/stimulus-plumbers/stimulus-plumbers-controllers.es.js +760 -237
  5. data/app/assets/javascripts/stimulus-plumbers/stimulus-plumbers-controllers.umd.js +1 -1
  6. data/lib/stimulus_plumbers/components/action_list/item.rb +27 -0
  7. data/lib/stimulus_plumbers/components/action_list/section.rb +21 -0
  8. data/lib/stimulus_plumbers/components/action_list.rb +23 -0
  9. data/lib/stimulus_plumbers/components/avatar.rb +72 -0
  10. data/lib/stimulus_plumbers/components/button/group.rb +17 -0
  11. data/lib/stimulus_plumbers/components/button.rb +27 -0
  12. data/lib/stimulus_plumbers/components/calendar/month/turbo/days_of_month.rb +2 -2
  13. data/lib/stimulus_plumbers/components/calendar/month/turbo/days_of_week.rb +2 -2
  14. data/lib/stimulus_plumbers/components/calendar/month/turbo.rb +55 -0
  15. data/lib/stimulus_plumbers/components/calendar.rb +33 -0
  16. data/lib/stimulus_plumbers/components/card/section.rb +25 -0
  17. data/lib/stimulus_plumbers/components/card.rb +27 -0
  18. data/lib/stimulus_plumbers/components/combobox/autocomplete.rb +53 -0
  19. data/lib/stimulus_plumbers/components/combobox/date.rb +50 -0
  20. data/lib/stimulus_plumbers/components/combobox/dropdown.rb +38 -0
  21. data/lib/stimulus_plumbers/components/combobox/options/option.rb +34 -0
  22. data/lib/stimulus_plumbers/components/combobox/options/option_group.rb +29 -0
  23. data/lib/stimulus_plumbers/components/combobox/options.rb +59 -0
  24. data/lib/stimulus_plumbers/components/combobox/popover.rb +20 -0
  25. data/lib/stimulus_plumbers/components/combobox/time/drum.rb +37 -0
  26. data/lib/stimulus_plumbers/components/combobox/time.rb +120 -0
  27. data/lib/stimulus_plumbers/components/combobox/trigger.rb +38 -0
  28. data/lib/stimulus_plumbers/components/combobox.rb +59 -0
  29. data/lib/stimulus_plumbers/components/date_picker/navigation.rb +1 -1
  30. data/lib/stimulus_plumbers/components/date_picker/navigator.rb +1 -1
  31. data/lib/stimulus_plumbers/components/icon.rb +49 -0
  32. data/lib/stimulus_plumbers/components/popover/builder.rb +25 -0
  33. data/lib/stimulus_plumbers/components/popover.rb +26 -0
  34. data/lib/stimulus_plumbers/form/builder.rb +64 -17
  35. data/lib/stimulus_plumbers/form/{field_component.rb → field.rb} +13 -11
  36. data/lib/stimulus_plumbers/form/fields/combobox.rb +41 -0
  37. data/lib/stimulus_plumbers/form/fields/error.rb +14 -0
  38. data/lib/stimulus_plumbers/form/fields/group.rb +14 -0
  39. data/lib/stimulus_plumbers/form/fields/hint.rb +14 -0
  40. data/lib/stimulus_plumbers/form/fields/label.rb +21 -0
  41. data/lib/stimulus_plumbers/form/fields/password.rb +55 -0
  42. data/lib/stimulus_plumbers/form/fields/renderer.rb +16 -21
  43. data/lib/stimulus_plumbers/form/fields/search.rb +54 -0
  44. data/lib/stimulus_plumbers/form/fields/select.rb +8 -2
  45. data/lib/stimulus_plumbers/form/fields/submit.rb +23 -0
  46. data/lib/stimulus_plumbers/form/fields/text.rb +12 -4
  47. data/lib/stimulus_plumbers/helpers/action_list_helper.rb +2 -2
  48. data/lib/stimulus_plumbers/helpers/avatar_helper.rb +2 -2
  49. data/lib/stimulus_plumbers/helpers/button_helper.rb +2 -2
  50. data/lib/stimulus_plumbers/helpers/calendar_helper.rb +1 -1
  51. data/lib/stimulus_plumbers/helpers/calendar_turbo_helper.rb +1 -1
  52. data/lib/stimulus_plumbers/helpers/card_helper.rb +2 -2
  53. data/lib/stimulus_plumbers/helpers/combobox_helper.rb +74 -0
  54. data/lib/stimulus_plumbers/helpers/popover_helper.rb +2 -2
  55. data/lib/stimulus_plumbers/helpers.rb +2 -2
  56. data/lib/stimulus_plumbers/plumber/base.rb +20 -0
  57. data/lib/stimulus_plumbers/plumber/dispatcher.rb +111 -0
  58. data/lib/stimulus_plumbers/plumber/html_options.rb +51 -0
  59. data/lib/stimulus_plumbers/plumber/renderer.rb +89 -0
  60. data/lib/stimulus_plumbers/themes/base.rb +9 -15
  61. data/lib/stimulus_plumbers/themes/schema/ranges.rb +5 -5
  62. data/lib/stimulus_plumbers/themes/schema.rb +97 -0
  63. data/lib/stimulus_plumbers/themes/tailwind/calendar.rb +48 -2
  64. data/lib/stimulus_plumbers/themes/tailwind/combobox.rb +75 -0
  65. data/lib/stimulus_plumbers/themes/tailwind/form.rb +10 -6
  66. data/lib/stimulus_plumbers/themes/tailwind_theme.rb +2 -0
  67. data/lib/stimulus_plumbers/version.rb +1 -1
  68. data/lib/stimulus_plumbers.rb +41 -14
  69. metadata +42 -23
  70. data/lib/stimulus_plumbers/components/action_list/renderer.rb +0 -47
  71. data/lib/stimulus_plumbers/components/avatar/renderer.rb +0 -74
  72. data/lib/stimulus_plumbers/components/button/renderer.rb +0 -33
  73. data/lib/stimulus_plumbers/components/calendar/month/turbo/renderer.rb +0 -57
  74. data/lib/stimulus_plumbers/components/calendar/renderer.rb +0 -35
  75. data/lib/stimulus_plumbers/components/card/renderer.rb +0 -41
  76. data/lib/stimulus_plumbers/components/date_picker/renderer.rb +0 -82
  77. data/lib/stimulus_plumbers/components/icon/renderer.rb +0 -51
  78. data/lib/stimulus_plumbers/components/plumber/base.rb +0 -22
  79. data/lib/stimulus_plumbers/components/plumber/dispatcher.rb +0 -113
  80. data/lib/stimulus_plumbers/components/plumber/html_options.rb +0 -34
  81. data/lib/stimulus_plumbers/components/plumber/renderer.rb +0 -91
  82. data/lib/stimulus_plumbers/components/popover/renderer.rb +0 -46
  83. data/lib/stimulus_plumbers/helpers/date_picker_helper.rb +0 -17
  84. data/lib/stimulus_plumbers/themes/action_list.rb +0 -14
  85. data/lib/stimulus_plumbers/themes/avatar.rb +0 -14
  86. data/lib/stimulus_plumbers/themes/button.rb +0 -18
  87. data/lib/stimulus_plumbers/themes/calendar.rb +0 -15
  88. data/lib/stimulus_plumbers/themes/card.rb +0 -12
  89. data/lib/stimulus_plumbers/themes/form.rb +0 -30
  90. data/lib/stimulus_plumbers/themes/layout.rb +0 -12
@@ -1,113 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # :markup: markdown
4
-
5
- module StimulusPlumbers
6
- module Components
7
- module Plumber
8
- module Dispatcher
9
- class MethodCall
10
- attr_reader :method_name, :args, :kwargs
11
-
12
- def initialize(method_name, *args, **kwargs)
13
- @method_name = method_name
14
- @args = args
15
- @kwargs = kwargs
16
- validate!
17
- end
18
-
19
- def call(target)
20
- raise NotImplementedError, "#{method_name.inspect} not implemented" unless target.respond_to?(method_name, true)
21
-
22
- method_call = target.method(method_name)
23
- accepts_args = method_call.arity.negative? ? args : args.take(method_call.arity)
24
- accepts_kwargs = method_call.parameters.any? { |type, _| %i[key keyreq keyrest].include?(type) }
25
- accepts_kwargs ? method_call.call(*accepts_args, **kwargs) : method_call.call(*accepts_args)
26
- end
27
-
28
- private
29
-
30
- def validate!
31
- return if method_name.is_a?(String) || method_name.is_a?(Symbol)
32
-
33
- raise ArgumentError, "invalid method name: #{method_name.inspect}"
34
- end
35
- end
36
-
37
- class InstanceExec
38
- attr_reader :block, :args, :kwargs
39
-
40
- def initialize(block, *args, **kwargs)
41
- @block = block
42
- @args = args
43
- @kwargs = kwargs
44
- validate!
45
- end
46
-
47
- def call(target)
48
- accepts_args = block.arity.negative? ? args : args.take(block.arity)
49
- accepts_kwargs = block.parameters.any? { |type, _| %i[key keyreq keyrest].include?(type) }
50
- if accepts_kwargs
51
- target.instance_exec(
52
- *accepts_args,
53
- **kwargs,
54
- &block
55
- )
56
- else
57
- target.instance_exec(*accepts_args, &block)
58
- end
59
- end
60
-
61
- private
62
-
63
- def validate!
64
- raise ArgumentError, "invalid block: #{block.inspect}" unless block.is_a?(Proc)
65
- end
66
- end
67
-
68
- class KlassProxy
69
- attr_reader :klass, :method_name, :args, :kwargs, :init_args, :init_kwargs
70
-
71
- def initialize(klass, method_name, *args, init_args: [], init_kwargs: {}, **kwargs)
72
- @klass = klass
73
- @method_name = method_name
74
- @args = args
75
- @kwargs = kwargs
76
- @init_args = init_args
77
- @init_kwargs = init_kwargs
78
- validate!
79
- end
80
-
81
- def call(_target)
82
- klass.new(*init_args, **init_kwargs).public_send(method_name, *args, **kwargs)
83
- end
84
-
85
- private
86
-
87
- def validate!
88
- raise ArgumentError, "invalid class: #{klass.inspect}" unless klass.is_a?(Module)
89
- return if method_name.is_a?(String) || method_name.is_a?(Symbol)
90
-
91
- raise ArgumentError, "invalid method name: #{method_name.inspect}"
92
- end
93
- end
94
-
95
- def self.build(callable, *args, method_name: nil, init_args: [], init_kwargs: {}, **kwargs)
96
- case callable
97
- when Symbol
98
- MethodCall.new(callable, *args, **kwargs)
99
- when Proc
100
- InstanceExec.new(callable, *args, **kwargs)
101
- when Module
102
- KlassProxy.new(callable, method_name, *args, init_args: init_args, init_kwargs: init_kwargs, **kwargs)
103
- when String
104
- klass = callable.safe_constantize
105
- raise ArgumentError, "could not resolve class from: #{callable.inspect}" unless klass
106
-
107
- KlassProxy.new(klass, method_name, *args, init_args: init_args, init_kwargs: init_kwargs, **kwargs)
108
- end
109
- end
110
- end
111
- end
112
- end
113
- end
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "active_support/concern"
4
-
5
- module StimulusPlumbers
6
- module Components
7
- module Plumber
8
- module HtmlOptions
9
- extend ActiveSupport::Concern
10
-
11
- def merge_html_options(*hashes)
12
- classes = hashes.flat_map { |h| [h[:class], h[:classes]] }
13
- rest = hashes.map { |h| h.except(:class, :classes) }.reduce({}, :deep_merge)
14
- class_value = merge_string_option(*classes).presence
15
- class_value ? rest.merge(class: class_value) : rest
16
- end
17
-
18
- def merge_string_option(*parts, delimiter: " ")
19
- tokens = parts.flat_map { |part| normalize_part(part, delimiter) }
20
- tokens.compact.uniq.join(delimiter)
21
- end
22
-
23
- def normalize_part(value, delimiter)
24
- case value
25
- when String then value.present? ? value.split(delimiter) : []
26
- when Hash then value.filter_map { |key, val| key if val }
27
- when Array then [merge_string_option(*value).presence]
28
- else []
29
- end
30
- end
31
- end
32
- end
33
- end
34
- end
@@ -1,91 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "active_support/concern"
4
-
5
- module StimulusPlumbers
6
- module Components
7
- module Plumber
8
- module Renderer
9
- extend ActiveSupport::Concern
10
-
11
- included do
12
- class_attribute :renderers, instance_writer: false, default: {}
13
- end
14
-
15
- module ClassMethods
16
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
17
- def renders(method_name, with: nil, &block)
18
- raise ArgumentError, "method_name must be Symbol" unless method_name.is_a?(Symbol)
19
- raise ArgumentError, "provide either with: or a block" if !with.nil? && block_given?
20
-
21
- with = block if block_given?
22
-
23
- with_proc_or_symbol = with.is_a?(Proc) || with.is_a?(Symbol)
24
- with_klazz = with.is_a?(Module) || with.is_a?(String)
25
- raise ArgumentError, "with: must be a Symbol/Proc/Class" unless with_proc_or_symbol || with_klazz
26
-
27
- self.renderers = renderers.merge(method_name => with)
28
- ActiveSupport.version >= "7.2" ? generate_renderer_method(method_name) : eval_renderer_method(method_name)
29
- end
30
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
31
-
32
- private
33
-
34
- def generated_renderer_methods
35
- @generated_renderer_methods ||= Module.new.tap { |mod| prepend mod }
36
- end
37
-
38
- def eval_renderer_method(method_name)
39
- generated_renderer_methods.module_eval(<<-RUBY, __FILE__, __LINE__ + 1)
40
- # def method_name(*args, **kwargs)
41
- # renderer = renderers.fetch(:method_name, {})
42
- #
43
- # unless renderer.present?
44
- # raise ArgumentError, "#method_name not found in renderer" unless defined?(super)
45
- # super
46
- # end
47
- #
48
- # dispatcher = StimulusPlumbers::Components::Plumber::Dispatcher.build(
49
- # renderer, *args, method_name: :#{method_name}, init_args: [template], **kwargs
50
- # )
51
- # raise ArgumentError, "invalid renderer, got: \#{renderer.inspect}" unless dispatcher
52
- #
53
- # dispatcher.call(self)
54
- # end
55
- #{renderer_method_template(method_name)}
56
- RUBY
57
- end
58
-
59
- def generate_renderer_method(method_name)
60
- require "active_support/code_generator"
61
- ActiveSupport::CodeGenerator.batch(generated_renderer_methods, __FILE__, __LINE__) do |owner|
62
- owner.define_cached_method(method_name, namespace: :plumber_renderers) do |batch|
63
- batch << renderer_method_template(method_name)
64
- end
65
- end
66
- end
67
-
68
- def renderer_method_template(method_name)
69
- <<-RUBY
70
- def #{method_name}(*args, **kwargs)
71
- renderer = renderers.fetch(:#{method_name}, {})
72
-
73
- unless renderer.present?
74
- raise ArgumentError, "##{method_name} not found in renderer" unless defined?(super)
75
- super
76
- end
77
-
78
- dispatcher = StimulusPlumbers::Components::Plumber::Dispatcher.build(
79
- renderer, *args, method_name: :#{method_name}, init_args: [template], **kwargs
80
- )
81
- raise ArgumentError, "invalid renderer, got: \#{renderer.inspect}" unless dispatcher
82
-
83
- dispatcher.call(self)
84
- end
85
- RUBY
86
- end
87
- end
88
- end
89
- end
90
- end
91
- end
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module StimulusPlumbers
4
- module Components
5
- module Popover
6
- class Builder
7
- attr_reader :activator_html, :content_html
8
-
9
- def initialize(template)
10
- @template = template
11
- @activator_html = "".html_safe
12
- @content_html = "".html_safe
13
- end
14
-
15
- def activator(&block)
16
- @activator_html = @template.capture(&block)
17
- end
18
-
19
- def content(&block)
20
- @content_html = @template.capture(&block)
21
- end
22
- end
23
-
24
- class Renderer < Plumber::Base
25
- def popover(interactive: true, **kwargs, &block)
26
- html_options = merge_html_options(
27
- { classes: theme.resolve(:popover).fetch(:classes, "") },
28
- kwargs
29
- )
30
-
31
- builder = Builder.new(template)
32
- template.capture(builder, &block)
33
-
34
- template.content_tag(:div, **html_options) do
35
- wrapped_content = if interactive
36
- template.content_tag(:template, builder.content_html)
37
- else
38
- builder.content_html
39
- end
40
- template.safe_join([builder.activator_html, wrapped_content])
41
- end
42
- end
43
- end
44
- end
45
- end
46
- end
@@ -1,17 +0,0 @@
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
@@ -1,14 +0,0 @@
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
@@ -1,14 +0,0 @@
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
@@ -1,18 +0,0 @@
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
@@ -1,15 +0,0 @@
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
@@ -1,12 +0,0 @@
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
@@ -1,30 +0,0 @@
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
@@ -1,12 +0,0 @@
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