stimulus_plumbers 0.4.8 → 0.4.10
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 +39 -0
- data/README.md +7 -9
- data/app/assets/javascripts/stimulus-plumbers/controllers.manifest.json +367 -23
- data/app/assets/javascripts/stimulus-plumbers/index.es.js +678 -233
- data/app/assets/javascripts/stimulus-plumbers/index.es.js.map +1 -1
- data/app/assets/javascripts/stimulus-plumbers/index.umd.js +1 -1
- data/app/assets/javascripts/stimulus-plumbers/index.umd.js.map +1 -1
- data/app/assets/stylesheets/stimulus_plumbers/tokens.css +58 -53
- data/docs/architecture.md +0 -2
- data/docs/component/avatar.md +1 -0
- data/docs/component/button.md +1 -0
- data/docs/component/calendar.md +7 -16
- data/docs/component/checklist.md +153 -0
- data/docs/component/form.md +27 -5
- data/docs/component/icon.md +7 -5
- data/docs/component/indicator.md +55 -0
- data/docs/component/list.md +2 -3
- data/docs/component/ordered_list.md +118 -0
- data/docs/component/plumber.md +21 -0
- data/docs/component/popover.md +1 -2
- data/docs/component/progress.md +65 -0
- data/docs/component/timeline.md +47 -12
- data/docs/guide.md +21 -0
- data/lib/generators/stimulus_plumbers/install/install_generator.rb +21 -11
- data/lib/stimulus_plumbers/components/button.rb +1 -0
- data/lib/stimulus_plumbers/components/checklist/item/slots.rb +13 -0
- data/lib/stimulus_plumbers/components/checklist/item.rb +64 -0
- data/lib/stimulus_plumbers/components/checklist.rb +50 -0
- data/lib/stimulus_plumbers/components/combobox/typeahead.rb +1 -0
- data/lib/stimulus_plumbers/components/icon.rb +2 -2
- data/lib/stimulus_plumbers/components/indicator.rb +30 -0
- data/lib/stimulus_plumbers/components/link.rb +1 -0
- data/lib/stimulus_plumbers/components/ordered_list/item/slots.rb +13 -0
- data/lib/stimulus_plumbers/components/ordered_list/item.rb +129 -0
- data/lib/stimulus_plumbers/components/ordered_list.rb +24 -0
- data/lib/stimulus_plumbers/components/progress_bar.rb +48 -0
- data/lib/stimulus_plumbers/components/progress_meter.rb +37 -0
- data/lib/stimulus_plumbers/components/progress_ring.rb +40 -0
- data/lib/stimulus_plumbers/components/timeline/event.rb +2 -1
- data/lib/stimulus_plumbers/components/timeline/group.rb +5 -3
- data/lib/stimulus_plumbers/engine.rb +0 -4
- data/lib/stimulus_plumbers/form/builder.rb +4 -0
- data/lib/stimulus_plumbers/form/field.rb +1 -1
- data/lib/stimulus_plumbers/form/fields/inputs/code.rb +86 -0
- data/lib/stimulus_plumbers/form/fields/inputs/credit_card.rb +98 -0
- data/lib/stimulus_plumbers/form/fields/inputs/password.rb +1 -0
- data/lib/stimulus_plumbers/form/fields/inputs/search.rb +1 -0
- data/lib/stimulus_plumbers/form/fields/renderer.rb +2 -0
- data/lib/stimulus_plumbers/generators/css_entrypoint.rb +37 -9
- data/lib/stimulus_plumbers/generators/tokens_directive.rb +9 -5
- data/lib/stimulus_plumbers/helpers/avatar_helper.rb +1 -7
- data/lib/stimulus_plumbers/helpers/button_helper.rb +1 -7
- data/lib/stimulus_plumbers/helpers/calendar_helper.rb +2 -9
- data/lib/stimulus_plumbers/helpers/calendar_turbo_helper.rb +3 -6
- data/lib/stimulus_plumbers/helpers/checklist_helper.rb +11 -0
- data/lib/stimulus_plumbers/helpers/indicator_helper.rb +11 -0
- data/lib/stimulus_plumbers/helpers/ordered_list_helper.rb +11 -0
- data/lib/stimulus_plumbers/helpers/popover_helper.rb +1 -7
- data/lib/stimulus_plumbers/helpers/progress_helper.rb +21 -0
- data/lib/stimulus_plumbers/helpers.rb +8 -0
- data/lib/stimulus_plumbers/themes/base.rb +4 -0
- data/lib/stimulus_plumbers/themes/schema/icon.rb +21 -4
- data/lib/stimulus_plumbers/themes/schema/indicator/ranges.rb +14 -0
- data/lib/stimulus_plumbers/themes/schema.rb +82 -25
- data/lib/stimulus_plumbers/version.rb +1 -1
- data/lib/stimulus_plumbers.rb +10 -0
- data/vendor/ARIA.md +29 -2
- data/vendor/component/manifest.json +215 -0
- data/vendor/controller/docs/calendar.md +4 -7
- data/vendor/controller/docs/checklist.md +45 -0
- data/vendor/controller/docs/combobox.md +1 -31
- data/vendor/controller/docs/input-clearable.md +1 -4
- data/vendor/controller/docs/input-formatter.md +88 -23
- data/vendor/controller/docs/modal.md +1 -4
- data/vendor/controller/docs/popover.md +1 -3
- data/vendor/controller/docs/progress.md +82 -0
- data/vendor/controller/docs/reorderable.md +82 -0
- data/vendor/controller/docs/visibility.md +31 -0
- data/vendor/controller/manifest.json +367 -23
- metadata +27 -1
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Components
|
|
5
|
+
class ProgressMeter < Plumber::Base
|
|
6
|
+
def render(...) = render_meter(...)
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def render_meter(value:, min: 0, max: 100, low: nil, high: nil, optimum: nil, **kwargs)
|
|
11
|
+
attrs = { value: value, min: min, max: max, low: low, high: high, optimum: optimum }.compact
|
|
12
|
+
html_options = merge_html_options(
|
|
13
|
+
theme.resolve(:progress_meter),
|
|
14
|
+
kwargs,
|
|
15
|
+
stimulus_data(value: value, min: min, max: max, low: low, high: high, optimum: optimum),
|
|
16
|
+
attrs
|
|
17
|
+
)
|
|
18
|
+
template.content_tag(:meter, nil, **html_options)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def stimulus_data(value:, min:, max:, low:, high:, optimum:)
|
|
22
|
+
data = {
|
|
23
|
+
controller: "progress",
|
|
24
|
+
"progress-target": "meter",
|
|
25
|
+
"progress-variant-value": "meter",
|
|
26
|
+
"progress-current-value": value,
|
|
27
|
+
"progress-min-value": min,
|
|
28
|
+
"progress-max-value": max
|
|
29
|
+
}
|
|
30
|
+
data["progress-low-value"] = low if low
|
|
31
|
+
data["progress-high-value"] = high if high
|
|
32
|
+
data["progress-optimum-value"] = optimum if optimum
|
|
33
|
+
{ data: data }
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Components
|
|
5
|
+
class ProgressRing < Plumber::Base
|
|
6
|
+
def render(...) = render_ring(...)
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def render_ring(value:, max: 100, min: 0, indeterminate: false, **kwargs)
|
|
11
|
+
icon_options = merge_html_options(
|
|
12
|
+
theme.resolve(:progress_ring),
|
|
13
|
+
kwargs,
|
|
14
|
+
stimulus_data(value: value, min: min, max: max, indeterminate: indeterminate),
|
|
15
|
+
{ role: "progressbar", aria: progress_aria(value: value, min: min, max: max, indeterminate: indeterminate) }
|
|
16
|
+
)
|
|
17
|
+
Components::Icon.new(template).render(name: "progress-ring", **icon_options)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def progress_aria(value:, min:, max:, indeterminate:)
|
|
21
|
+
aria = { valuemin: min, valuemax: max }
|
|
22
|
+
aria[:valuenow] = value unless indeterminate
|
|
23
|
+
aria
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def stimulus_data(value:, min:, max:, indeterminate:)
|
|
27
|
+
{
|
|
28
|
+
data: {
|
|
29
|
+
controller: "progress",
|
|
30
|
+
"progress-variant-value": "ring",
|
|
31
|
+
"progress-current-value": value,
|
|
32
|
+
"progress-min-value": min,
|
|
33
|
+
"progress-max-value": max,
|
|
34
|
+
"progress-indeterminate-value": indeterminate
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -107,11 +107,12 @@ module StimulusPlumbers
|
|
|
107
107
|
if type == :icon && icon_name
|
|
108
108
|
Components::Icon.new(template).render(
|
|
109
109
|
name: icon_name,
|
|
110
|
+
size: :sm,
|
|
110
111
|
classes: theme.resolve(:timeline_item_indicator_icon_slot).fetch(:classes, ""),
|
|
111
112
|
aria: { hidden: "true" }
|
|
112
113
|
)
|
|
113
114
|
else
|
|
114
|
-
template.
|
|
115
|
+
Components::Indicator.new(template).render(type: :dot, variant: :primary)
|
|
115
116
|
end
|
|
116
117
|
end
|
|
117
118
|
|
|
@@ -4,7 +4,8 @@ module StimulusPlumbers
|
|
|
4
4
|
module Components
|
|
5
5
|
class Timeline
|
|
6
6
|
class Group < Plumber::Base
|
|
7
|
-
def render(**kwargs, &block)
|
|
7
|
+
def render(orientation: :vertical, **kwargs, &block)
|
|
8
|
+
@orientation = orientation
|
|
8
9
|
content = template.capture(self, &block)
|
|
9
10
|
html_options = merge_html_options(theme.resolve(:timeline_group), kwargs)
|
|
10
11
|
template.content_tag(:div, content, **html_options)
|
|
@@ -12,7 +13,7 @@ module StimulusPlumbers
|
|
|
12
13
|
|
|
13
14
|
def section(date:, datetime: nil, **kwargs, &block)
|
|
14
15
|
inner_tl = Timeline.new(template)
|
|
15
|
-
inner_tl.instance_variable_set(:@orientation,
|
|
16
|
+
inner_tl.instance_variable_set(:@orientation, @orientation)
|
|
16
17
|
inner_tl.instance_variable_set(:@interactive, false)
|
|
17
18
|
|
|
18
19
|
events_html = template.capture(inner_tl, &block)
|
|
@@ -29,10 +30,11 @@ module StimulusPlumbers
|
|
|
29
30
|
theme.resolve(:timeline_group_section_date),
|
|
30
31
|
datetime ? { datetime: datetime } : {}
|
|
31
32
|
)
|
|
33
|
+
list_attrs = merge_html_options(theme.resolve(:timeline_group_section_list, orientation: @orientation))
|
|
32
34
|
template.safe_join(
|
|
33
35
|
[
|
|
34
36
|
template.content_tag(:time, date, **date_attrs),
|
|
35
|
-
template.content_tag(:ol, events_html, **
|
|
37
|
+
template.content_tag(:ol, events_html, **list_attrs)
|
|
36
38
|
]
|
|
37
39
|
)
|
|
38
40
|
end
|
|
@@ -9,10 +9,6 @@ module StimulusPlumbers
|
|
|
9
9
|
config.autoload_paths << File.expand_path("../stimulus-plumbers", __dir__)
|
|
10
10
|
config.i18n.load_path += Dir[File.expand_path("../../config/locales/*.{rb,yml}", __dir__)]
|
|
11
11
|
|
|
12
|
-
initializer "stimulus_plumbers.assets", after: :set_default_precompile do |app|
|
|
13
|
-
app.config.assets.precompile += %w[stimulus_plumbers/tokens.css] if app.config.respond_to?(:assets)
|
|
14
|
-
end
|
|
15
|
-
|
|
16
12
|
initializer "stimulus_plumbers.helpers" do
|
|
17
13
|
ActiveSupport.on_load(:action_view) do
|
|
18
14
|
include StimulusPlumbers::Helpers
|
|
@@ -11,6 +11,8 @@ require_relative "fields/renderer"
|
|
|
11
11
|
require_relative "fields/fieldset"
|
|
12
12
|
require_relative "fields/inputs/checkbox"
|
|
13
13
|
require_relative "fields/inputs/combobox"
|
|
14
|
+
require_relative "fields/inputs/code"
|
|
15
|
+
require_relative "fields/inputs/credit_card"
|
|
14
16
|
require_relative "fields/inputs/datetime"
|
|
15
17
|
require_relative "fields/inputs/radio"
|
|
16
18
|
require_relative "fields/inputs/file"
|
|
@@ -30,6 +32,8 @@ module StimulusPlumbers
|
|
|
30
32
|
include Plumber::Options::Html
|
|
31
33
|
include Plumber::Options::Aria
|
|
32
34
|
include Fields::Inputs::Checkbox
|
|
35
|
+
include Fields::Inputs::Code
|
|
36
|
+
include Fields::Inputs::CreditCard
|
|
33
37
|
include Fields::Inputs::Datetime
|
|
34
38
|
include Fields::Inputs::Radio
|
|
35
39
|
include Fields::Inputs::File
|
|
@@ -8,7 +8,7 @@ module StimulusPlumbers
|
|
|
8
8
|
class Field < Base
|
|
9
9
|
TYPES = %i[
|
|
10
10
|
text email number url tel color month week range datetime_local
|
|
11
|
-
text_area file password date time select search
|
|
11
|
+
text_area file password date time select search code credit_card
|
|
12
12
|
].freeze
|
|
13
13
|
COLLECTION_TYPES = %i[radio check_box collection_select grouped_collection_select].freeze
|
|
14
14
|
OPTIONS = (Base::OPTIONS + %i[hide_label]).freeze
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Form
|
|
5
|
+
module Fields
|
|
6
|
+
module Inputs
|
|
7
|
+
module Code
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def render_code_input(attribute, html_opts, opts, error, length:, charset: :digits, groups: [], **kwargs)
|
|
11
|
+
floating = kwargs.delete(:floating)
|
|
12
|
+
raise ArgumentError, "floating labels are not supported for code fields" if floating.present?
|
|
13
|
+
|
|
14
|
+
validate_code_options!(length, groups)
|
|
15
|
+
inputmode = kwargs.delete(:inputmode) || (charset.to_sym == :digits ? "numeric" : nil)
|
|
16
|
+
autocomplete = kwargs.delete(:autocomplete) || "one-time-code"
|
|
17
|
+
config = { error: error, length: length, charset: charset, groups: groups,
|
|
18
|
+
autocomplete: autocomplete, inputmode: inputmode, kwargs: kwargs
|
|
19
|
+
}
|
|
20
|
+
render_code_field(attribute, html_opts, opts, config)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def render_code_field(attribute, html_opts, opts, config)
|
|
24
|
+
@template.content_tag(:div, **code_wrapper_options(config)) do
|
|
25
|
+
@template.safe_join(
|
|
26
|
+
[render_code_cells(config), code_input(attribute, html_opts, opts, config)]
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def code_wrapper_options(config)
|
|
32
|
+
merge_html_options(
|
|
33
|
+
theme.resolve(:form_field_input_code, error: config[:error]),
|
|
34
|
+
{ data: {
|
|
35
|
+
controller: "input-formatter",
|
|
36
|
+
input_formatter_format_value: "code",
|
|
37
|
+
input_formatter_options_value: { charset: config[:charset].to_s, length: config[:length] }.to_json,
|
|
38
|
+
input_formatter_groups_value: config[:groups].presence
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def code_input(attribute, html_opts, opts, config)
|
|
45
|
+
input_options = merge_html_options(
|
|
46
|
+
theme.resolve(:form_field_input_code_overlay, error: config[:error]),
|
|
47
|
+
opts,
|
|
48
|
+
html_opts,
|
|
49
|
+
config[:kwargs],
|
|
50
|
+
{ maxlength: config[:length], autocomplete: config[:autocomplete], inputmode: config[:inputmode],
|
|
51
|
+
data: { input_formatter_target: "input", action: formatter_actions }
|
|
52
|
+
}
|
|
53
|
+
)
|
|
54
|
+
@template.text_field(@object_name, attribute, objectify_options(input_options))
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def render_code_cells(config)
|
|
58
|
+
count = config[:groups].present? ? config[:groups].sum : config[:length]
|
|
59
|
+
cell_options = merge_html_options(
|
|
60
|
+
theme.resolve(:form_field_input_code_cell, error: config[:error]),
|
|
61
|
+
{ data: { input_formatter_target: "cell" } }
|
|
62
|
+
)
|
|
63
|
+
@template.content_tag(:div, **merge_html_options(theme.resolve(:form_field_input_code_cells))) do
|
|
64
|
+
@template.safe_join(Array.new(count) { @template.content_tag(:span, nil, **cell_options) })
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def validate_code_options!(length, groups)
|
|
69
|
+
raise ArgumentError, "length must be a positive integer" unless positive_integer?(length)
|
|
70
|
+
return if groups.blank?
|
|
71
|
+
|
|
72
|
+
raise ArgumentError, "groups must be an array of positive integers" unless valid_groups?(groups)
|
|
73
|
+
raise ArgumentError, "groups must add up to length" unless groups.sum == length
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def valid_groups?(groups) = groups.is_a?(Array) && groups.all? { |group| positive_integer?(group) }
|
|
77
|
+
def positive_integer?(value) = value.is_a?(Integer) && value.positive?
|
|
78
|
+
|
|
79
|
+
def formatter_actions
|
|
80
|
+
"input->input-formatter#onInput focus->input-formatter#onFocus blur->input-formatter#onBlur"
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Form
|
|
5
|
+
module Fields
|
|
6
|
+
module Inputs
|
|
7
|
+
module CreditCard
|
|
8
|
+
DEFAULT_GROUPS = [4, 4, 4, 4].freeze
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def render_credit_card_input(attribute, html_opts, opts, error, groups: DEFAULT_GROUPS, floating: nil, **kwargs)
|
|
13
|
+
raise ArgumentError, "floating labels are not supported for credit card fields" if floating.present?
|
|
14
|
+
|
|
15
|
+
normalized_groups = groups.presence || DEFAULT_GROUPS
|
|
16
|
+
inputmode = kwargs.delete(:inputmode) || "numeric"
|
|
17
|
+
autocomplete = kwargs.delete(:autocomplete) || "cc-number"
|
|
18
|
+
validate_credit_card_groups!(normalized_groups)
|
|
19
|
+
length = normalized_groups.sum
|
|
20
|
+
config = { error: error, groups: normalized_groups, length: length,
|
|
21
|
+
autocomplete: autocomplete, inputmode: inputmode, kwargs: kwargs
|
|
22
|
+
}
|
|
23
|
+
render_credit_card_field(attribute, html_opts, opts, config)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def render_credit_card_field(attribute, html_opts, opts, config)
|
|
27
|
+
@template.content_tag(:div, **credit_card_wrapper_options(config)) do
|
|
28
|
+
@template.safe_join(
|
|
29
|
+
[render_credit_card_cells(config), credit_card_input(attribute, html_opts, opts, config)]
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def credit_card_wrapper_options(config)
|
|
35
|
+
merge_html_options(
|
|
36
|
+
theme.resolve(:form_field_input_credit_card, error: config[:error]),
|
|
37
|
+
{ data: {
|
|
38
|
+
controller: "input-formatter",
|
|
39
|
+
input_formatter_format_value: "creditCard",
|
|
40
|
+
input_formatter_groups_value: config[:groups]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def credit_card_input(attribute, html_opts, opts, config)
|
|
47
|
+
input_options = merge_html_options(
|
|
48
|
+
theme.resolve(:form_field_input_credit_card_overlay, error: config[:error]),
|
|
49
|
+
opts,
|
|
50
|
+
html_opts,
|
|
51
|
+
config[:kwargs],
|
|
52
|
+
{ maxlength: config[:length], autocomplete: config[:autocomplete], inputmode: config[:inputmode],
|
|
53
|
+
data: { input_formatter_target: "input", action: formatter_actions }
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
@template.text_field(@object_name, attribute, objectify_options(input_options))
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def render_credit_card_cells(config)
|
|
60
|
+
cells = config[:groups].each_index.flat_map do |index|
|
|
61
|
+
index.zero? ? [render_credit_card_cell(config)] : [render_credit_card_separator, render_credit_card_cell(config)]
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
@template.content_tag(:div, **merge_html_options(theme.resolve(:form_field_input_credit_card_cells))) do
|
|
65
|
+
@template.safe_join(cells)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def render_credit_card_cell(config)
|
|
70
|
+
cell_options = merge_html_options(
|
|
71
|
+
theme.resolve(:form_field_input_credit_card_cell, error: config[:error]),
|
|
72
|
+
{ data: { input_formatter_target: "cell" } }
|
|
73
|
+
)
|
|
74
|
+
@template.content_tag(:span, nil, **cell_options)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def render_credit_card_separator
|
|
78
|
+
separator_options = merge_html_options(
|
|
79
|
+
theme.resolve(:form_field_input_credit_card_separator),
|
|
80
|
+
{ aria: { hidden: true } }
|
|
81
|
+
)
|
|
82
|
+
@template.content_tag(:span, "-", **separator_options)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def validate_credit_card_groups!(groups)
|
|
86
|
+
return if groups.is_a?(Array) && groups.all? { |group| group.is_a?(Integer) && group.positive? }
|
|
87
|
+
|
|
88
|
+
raise ArgumentError, "groups must be an array of positive integers"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def formatter_actions
|
|
92
|
+
"input->input-formatter#onInput focus->input-formatter#onFocus blur->input-formatter#onBlur"
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -18,6 +18,8 @@ module StimulusPlumbers
|
|
|
18
18
|
text_area: :render_text_area_input,
|
|
19
19
|
file: :render_file_input,
|
|
20
20
|
password: :render_password_input,
|
|
21
|
+
code: :render_code_input,
|
|
22
|
+
credit_card: :render_credit_card_input,
|
|
21
23
|
date: :render_combobox_date,
|
|
22
24
|
time: :render_combobox_time,
|
|
23
25
|
select: :render_combobox_dropdown,
|
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "fileutils"
|
|
4
|
+
|
|
3
5
|
module StimulusPlumbers
|
|
4
6
|
module Generators
|
|
5
7
|
module CssEntrypoint
|
|
6
|
-
|
|
8
|
+
STIMULUS_PLUMBERS_CSS_ENTRY = "STIMULUS_PLUMBERS_CSS_ENTRY"
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
# Shared with stimulus_plumbers_tailwind's InstallGenerator — checked in order,
|
|
11
|
+
# each path is another gem/tool's own default entry file. Detection is theme-
|
|
12
|
+
# agnostic: any of these may be the CSS entry regardless of which theme is active.
|
|
13
|
+
ENTRY_CANDIDATES = [
|
|
14
|
+
"app/assets/stylesheets/application.tailwind.css", # tailwindcss-rails 2.x default
|
|
15
|
+
"app/assets/tailwind/application.css", # tailwindcss-rails 3.x+ default
|
|
16
|
+
"app/assets/stylesheets/application.css", # Rails/Propshaft default manifest
|
|
17
|
+
"app/javascript/entrypoints/application.css" # jsbundling-rails (esbuild/webpack) default
|
|
18
|
+
].freeze
|
|
11
19
|
|
|
12
|
-
|
|
20
|
+
def entry_css_file(candidates:, env_var:)
|
|
21
|
+
if ENV[env_var]
|
|
22
|
+
path = File.expand_path(ENV.fetch(env_var, nil))
|
|
13
23
|
return path if File.exist?(path)
|
|
14
24
|
end
|
|
15
25
|
|
|
@@ -18,11 +28,9 @@ module StimulusPlumbers
|
|
|
18
28
|
.find { |f| File.exist?(f) }
|
|
19
29
|
end
|
|
20
30
|
|
|
21
|
-
def warn_entry_css_not_found(candidates:, env_var:, label
|
|
31
|
+
def warn_entry_css_not_found(candidates:, env_var:, label:)
|
|
22
32
|
tried = candidates.map { |c| File.join(destination_root, c) }
|
|
23
|
-
[env_var
|
|
24
|
-
tried.unshift(File.expand_path(ENV[var])) if ENV[var]
|
|
25
|
-
end
|
|
33
|
+
tried.unshift(File.expand_path(ENV[env_var])) if ENV[env_var]
|
|
26
34
|
say "Could not find a #{label} entry file. Tried: #{tried.join(", ")}. " \
|
|
27
35
|
"Set #{env_var}=/path/to/entry.css and re-run.",
|
|
28
36
|
:red
|
|
@@ -42,6 +50,26 @@ module StimulusPlumbers
|
|
|
42
50
|
say "Could not update #{relative_to_destination(css_file)}: #{e.message}. Skipping.", :yellow
|
|
43
51
|
end
|
|
44
52
|
|
|
53
|
+
# CSS copied by an installer belongs to the application from this point on.
|
|
54
|
+
# Keep an existing file intact so a generator rerun never overwrites local
|
|
55
|
+
# customizations (the same policy used by tailwindcss-rails' installer).
|
|
56
|
+
def copy_asset(source, destination)
|
|
57
|
+
path = File.join(destination_root, destination)
|
|
58
|
+
|
|
59
|
+
if File.exist?(path)
|
|
60
|
+
say_status :identical, destination
|
|
61
|
+
return true
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
65
|
+
FileUtils.cp(source, path)
|
|
66
|
+
say_status :create, destination, :green
|
|
67
|
+
true
|
|
68
|
+
rescue Errno::EROFS, Errno::EACCES => e
|
|
69
|
+
say "Could not copy #{destination}: #{e.message}. Skipping.", :yellow
|
|
70
|
+
false
|
|
71
|
+
end
|
|
72
|
+
|
|
45
73
|
# `stale_pattern:` is required — every call site needs stale-import detection,
|
|
46
74
|
# so making it optional would leave an untested "anchor without stale" state.
|
|
47
75
|
def content_edit(content, directive, stale_pattern:, anchor_pattern: nil)
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "pathname"
|
|
4
|
+
|
|
3
5
|
module StimulusPlumbers
|
|
4
6
|
module Generators
|
|
5
7
|
module TokensDirective
|
|
6
|
-
|
|
7
|
-
TOKENS_CSS_REL_PATH = "app/assets/stylesheets/stimulus_plumbers/tokens.css"
|
|
8
|
+
TOKENS_CSS_PATH = "app/assets/stylesheets/stimulus_plumbers/tokens.css"
|
|
8
9
|
|
|
9
10
|
module_function
|
|
10
11
|
|
|
11
|
-
def directive
|
|
12
|
-
|
|
12
|
+
def directive(from:, destination_root:)
|
|
13
|
+
path = Pathname.new(destination_root).join(TOKENS_CSS_PATH)
|
|
14
|
+
rel = path.relative_path_from(Pathname.new(from))
|
|
15
|
+
rel = "./#{rel}" unless rel.to_s.start_with?(".", "/")
|
|
16
|
+
%(@import "#{rel}";)
|
|
13
17
|
end
|
|
14
18
|
|
|
15
19
|
def stale_pattern
|
|
16
|
-
%r{@import "[^"]*#{Regexp.escape(
|
|
20
|
+
%r{@import "[^"]*#{Regexp.escape(TOKENS_CSS_PATH)}";}
|
|
17
21
|
end
|
|
18
22
|
end
|
|
19
23
|
end
|
|
@@ -4,13 +4,7 @@ module StimulusPlumbers
|
|
|
4
4
|
module Helpers
|
|
5
5
|
module AvatarHelper
|
|
6
6
|
def sp_avatar(...)
|
|
7
|
-
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
private
|
|
11
|
-
|
|
12
|
-
def avatar_renderer
|
|
13
|
-
Components::Avatar.new(self)
|
|
7
|
+
Components::Avatar.new(self).render(...)
|
|
14
8
|
end
|
|
15
9
|
end
|
|
16
10
|
end
|
|
@@ -4,18 +4,12 @@ module StimulusPlumbers
|
|
|
4
4
|
module Helpers
|
|
5
5
|
module ButtonHelper
|
|
6
6
|
def sp_button(content = nil, **kwargs, &block)
|
|
7
|
-
|
|
7
|
+
Components::Button.new(self).render(content, **kwargs, &block)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def sp_button_group(...)
|
|
11
11
|
Components::Button::Group.new(self).render(...)
|
|
12
12
|
end
|
|
13
|
-
|
|
14
|
-
private
|
|
15
|
-
|
|
16
|
-
def button_renderer
|
|
17
|
-
Components::Button.new(self)
|
|
18
|
-
end
|
|
19
13
|
end
|
|
20
14
|
end
|
|
21
15
|
end
|
|
@@ -9,20 +9,13 @@ module StimulusPlumbers
|
|
|
9
9
|
kwargs.deep_merge(
|
|
10
10
|
data: {
|
|
11
11
|
"calendar-month-year-value": date.year,
|
|
12
|
-
"calendar-month-month-value": date.month - 1
|
|
13
|
-
"calendar-month-day-value": date.day
|
|
12
|
+
"calendar-month-month-value": date.month - 1
|
|
14
13
|
}
|
|
15
14
|
)
|
|
16
15
|
else
|
|
17
16
|
kwargs
|
|
18
17
|
end
|
|
19
|
-
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
private
|
|
23
|
-
|
|
24
|
-
def calendar_renderer
|
|
25
|
-
Components::Calendar.new(self)
|
|
18
|
+
Components::Calendar.new(self).month(**html_options)
|
|
26
19
|
end
|
|
27
20
|
end
|
|
28
21
|
end
|
|
@@ -31,8 +31,7 @@ module StimulusPlumbers
|
|
|
31
31
|
[
|
|
32
32
|
content_tag(
|
|
33
33
|
:turbo_frame,
|
|
34
|
-
id:
|
|
35
|
-
data: { "combobox-date-target": "monthView" }
|
|
34
|
+
id: "calendar-month-frame"
|
|
36
35
|
) do
|
|
37
36
|
calendar_turbo_renderer.month(
|
|
38
37
|
date: date,
|
|
@@ -48,8 +47,7 @@ module StimulusPlumbers
|
|
|
48
47
|
content_tag(
|
|
49
48
|
:turbo_frame,
|
|
50
49
|
id: "calendar-year-frame",
|
|
51
|
-
hidden: true
|
|
52
|
-
data: { "combobox-date-target": "yearView" }
|
|
50
|
+
hidden: true
|
|
53
51
|
) do
|
|
54
52
|
calendar_turbo_renderer.year(
|
|
55
53
|
date: date,
|
|
@@ -63,8 +61,7 @@ module StimulusPlumbers
|
|
|
63
61
|
content_tag(
|
|
64
62
|
:turbo_frame,
|
|
65
63
|
id: "calendar-decade-frame",
|
|
66
|
-
hidden: true
|
|
67
|
-
data: { "combobox-date-target": "decadeView" }
|
|
64
|
+
hidden: true
|
|
68
65
|
) do
|
|
69
66
|
calendar_turbo_renderer.decade(
|
|
70
67
|
date: date,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Helpers
|
|
5
|
+
module IndicatorHelper
|
|
6
|
+
def sp_indicator(variant: :primary, type: :dot, pulse: false, **kwargs, &block)
|
|
7
|
+
Components::Indicator.new(self).render(type: type, variant: variant, pulse: pulse, **kwargs, &block)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -4,13 +4,7 @@ module StimulusPlumbers
|
|
|
4
4
|
module Helpers
|
|
5
5
|
module PopoverHelper
|
|
6
6
|
def sp_popover(...)
|
|
7
|
-
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
private
|
|
11
|
-
|
|
12
|
-
def popover_renderer
|
|
13
|
-
Components::Popover.new(self)
|
|
7
|
+
Components::Popover.new(self).render(...)
|
|
14
8
|
end
|
|
15
9
|
end
|
|
16
10
|
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Helpers
|
|
5
|
+
module ProgressHelper
|
|
6
|
+
def sp_progress_bar(value:, min: 0, max: 100, indeterminate: false, **kwargs)
|
|
7
|
+
Components::ProgressBar.new(self).render(value: value, min: min, max: max, indeterminate: indeterminate, **kwargs)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def sp_progress_ring(value:, min: 0, max: 100, indeterminate: false, **kwargs)
|
|
11
|
+
Components::ProgressRing.new(self).render(value: value, min: min, max: max, indeterminate: indeterminate, **kwargs)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def sp_progress_meter(value:, min: 0, max: 100, low: nil, high: nil, optimum: nil, **kwargs)
|
|
15
|
+
Components::ProgressMeter.new(self).render(
|
|
16
|
+
value: value, min: min, max: max, low: low, high: high, optimum: optimum, **kwargs
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|