forked from paul/ruby_snippets
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstimulus_data.rb
128 lines (109 loc) · 3.61 KB
/
stimulus_data.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# frozen_string_literal: true
# Author: Paul Sadauskas<[email protected]>
# License: MIT
class StimulusData
# Simplifies complicated Stimulus data attributes in HTML tags, which can be particularly
# annoying with non-trivial controller names.
#
# It can also be chained using the "with" method, to provide different attributes on different
# elements, excluding the `data-controller` attribute on all but the first.
#
# Examples:
#
# The following examples start with the following defined somewhere
#
# stimulus_data = StimulusData.new("loader", values: {url: "/messages"})
#
# @example Using ERB
#
# <div <%= stimulus_data %>/>
# #=> <div data-controller="loader" data-loader-url-value="/messages"/>
#
# @example Using Rails TagBuilder/TagHelper
#
# tag.div(data: stimulus_data)
# #=> <div data-controller="loader" data-loader-url-value="/messages"/>
#
# @example Using HAML/Slim
#
# %div{data: stimulus_data}
# #=> <div data-controller="loader" data-loader-url-value="/messages"/>
#
# @example Using #with
#
# %div{data: stimulus_data}
# %div.spinner{data: stimulus_data.with(target: "spinner"}
# %button{data: stimulus_data.with(action: "showSpinner"}
# %button{data: stimulus_data.with(action: "hideSpinner"}
#
# #=>
# <div data-controller="loader" data-loader-url-value="/messages">
# <div class="spinner" data-loader-target="spinner"></div>
# <button data-action="loader#showSpinner"/>
# <button data-action="loader#hideSpinner"/>
# </div>
#
attr_reader :controller, :actions, :target, :values, :outlets
def initialize(controller,
action: nil, actions: [],
target: nil, values: {},
outlets: {},
include_controller: true)
@controller = controller_name(controller)
@actions = actions.push(action).compact
@target = target
@values = values
@outlets = outlets
@include_controller = include_controller
end
def with(action: nil, actions: [], target: nil, values: {}, outlets: {})
self.class.new(controller, include_controller: false,
action:, actions:, target:, values:, outlets:)
end
def data
data = {}
data["controller"] = controller if @include_controller
if actions.present?
data["action"] = actions.map do |action|
event, method = action.split("->")
event, method = nil, event if method.nil?
[[event, controller].compact.join("->"), method].join("#")
end.join(" ")
end
data["#{controller}-target"] = target.to_s.camelize(:lower) if target
outlets.each do |outlet, selector|
data["#{controller}-#{outlet.to_s.dasherize}-outlet"] = selector
end
values.each do |key, val|
data["#{controller}-#{key.to_s.dasherize}-value"] = val
end
data.compact
end
alias_method :to_h, :data
def to_s
ActionView::Helpers::TagBuilder.new(nil).attributes(data:)
end
def inspect
"#<StimulusData #{data.inspect}>"
end
# Lie and pretend we're a hash to anything that's checking
# This lets HAML/Slim process the attributes like a Hash
def is_a?(other)
return true if other == Hash
super(other)
end
delegate :each_pair, to: :to_h
private
def controller_name(controller)
return kebab(controller.stimulus_controller) if controller.respond_to?(:stimulus_controller)
case controller
when Class then kebab(controller.name)
when Symbol, String then kebab(controller)
else
kebab(controller.class.name)
end
end
def kebab(str)
str.to_s.underscore.dasherize.gsub("/", "--")
end
end