stimulus_tag_helper 0.1.0
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/LICENSE.txt +21 -0
- data/README.md +133 -0
- data/lib/stimulus_tag_helper.rb +134 -0
- data/lib/stimulus_tag_helper/stimulus_action.rb +39 -0
- data/lib/stimulus_tag_helper/stimulus_controller_builder.rb +31 -0
- data/lib/stimulus_tag_helper/version.rb +5 -0
- metadata +68 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b6abf8489a80e3128ea3a0d0a81b935e5b1b2303e0e911af50069ddaad5d84d8
|
4
|
+
data.tar.gz: 45e22a4a2b76553502ffdf0cca5b49b24f59c3e6f8139f57052b159a0e3a45b0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 72427f49698c21330c73061f040fdd1c4eb7a630dd41b15555dc703234771dd614a00d9ade29258d88d4ffc74b33fa4f23c88d24230d62dc2daaa7770440f58e
|
7
|
+
data.tar.gz: 06c29df33bf224027b006d059ef0631693d7be19a92dddba137a004c774ebb8a91c2408d90a5f5061ba1130f2b700109b2997de8c2d9fe80077d2811332396c7
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2021 Anton Topchii
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
# stimulus_tag_helper
|
2
|
+
|
3
|
+
[](https://github.com/crawler/stimulus_tag_helper/actions)
|
4
|
+
|
5
|
+
[](https://rubygems.org/gems/stimulus_tag_helper)
|
6
|
+
|
7
|
+
## WARING!
|
8
|
+
|
9
|
+
This gem is in the proof of concept stage. I made it for myself, and it is not yet covered with tests and the documentation.
|
10
|
+
|
11
|
+
## Description
|
12
|
+
|
13
|
+
The stimulusjs is a great js framework for the HTML that you already have, but once u start using it the HTML becomes pretty big as you need to extend it with the attributes that your required to tight it with controllers. So I made this modest (even more modest than the stimulus itself) helper. Currently, I use it with the Slim(http://slim-lang.com/). So for now examples will be in it.
|
14
|
+
|
15
|
+
---
|
16
|
+
|
17
|
+
- [Quick start](#quick-start)
|
18
|
+
- [Examples](#examples)
|
19
|
+
- [Support](#support)
|
20
|
+
- [License](#license)
|
21
|
+
- [Code of conduct](#code-of-conduct)
|
22
|
+
- [Contribution guide](#contribution-guide)
|
23
|
+
|
24
|
+
## Quick start
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
gem 'stimulus_tag_helper', github: 'crawler/stimulus_tag_helper'
|
28
|
+
```
|
29
|
+
|
30
|
+
### In the rails Controller
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
helper StimulusTagHelper
|
34
|
+
```
|
35
|
+
|
36
|
+
### In the ViewComponent
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
include StimulusTagHelper
|
40
|
+
```
|
41
|
+
|
42
|
+
## Examples
|
43
|
+
|
44
|
+
Some examples is taken from the real app so may be not very)
|
45
|
+
|
46
|
+
### "Hello, Stimulus" controller from the stimulusjs homepage
|
47
|
+
|
48
|
+
```slim
|
49
|
+
= stimulus_controller("hello", tag: "div") do |sc|
|
50
|
+
= text_field_tag('', '', **sc.target("name"))
|
51
|
+
= button_tag('Greet', type: 'button', **sc.action("click->greet"))
|
52
|
+
span[*sc.target("output")]
|
53
|
+
```
|
54
|
+
|
55
|
+
Please note that to support nesting, and the stimulus 2.0 attributes notation, the actions and the targets will be prefixed with controller name
|
56
|
+
|
57
|
+
So example above will became:
|
58
|
+
|
59
|
+
```html
|
60
|
+
<div data-controller="hello">
|
61
|
+
<input type="text" name="" id="" value="" data-hello-target="name" />
|
62
|
+
<button name="button" type="button" data-action="click->hello#greet">Greet</button>
|
63
|
+
<span data-hello-target="output"></span>
|
64
|
+
</div>
|
65
|
+
```
|
66
|
+
|
67
|
+
|
68
|
+
## "Slideshow" define controller without tag rendering
|
69
|
+
|
70
|
+
(buttons messed up for the demonstation purposes)
|
71
|
+
|
72
|
+
```slim
|
73
|
+
= stimulus_controller("slideshow") do |sc|
|
74
|
+
div[*sc.controller_attribute]
|
75
|
+
= button_tag ' ← ', type: 'button', **sc.action("previous")
|
76
|
+
button[*sc.action("next")]
|
77
|
+
' →
|
78
|
+
div[*sc.target("slide")] 🐵
|
79
|
+
div[*sc.target("slide")] 🙈
|
80
|
+
div[*sc.target("slide")] 🙉
|
81
|
+
div[*sc.target("slide")] 🙊
|
82
|
+
```
|
83
|
+
|
84
|
+
Became:
|
85
|
+
|
86
|
+
```html
|
87
|
+
<div data-controller="slideshow">
|
88
|
+
<button name="button" type="button" data-action="slideshow#previous"> ← </button>
|
89
|
+
<button data-action="slideshow#next"> → </button>
|
90
|
+
<div data-slideshow-target="slide">🐵</div>
|
91
|
+
<div data-slideshow-target="slide">🙈</div>
|
92
|
+
<div data-slideshow-target="slide">🙉</div>
|
93
|
+
<div data-slideshow-target="slide">🙊</div>
|
94
|
+
</div>
|
95
|
+
```
|
96
|
+
|
97
|
+
### Nested controllers
|
98
|
+
|
99
|
+
```slim
|
100
|
+
= stimulus_controller( \
|
101
|
+
"flash", tag: "div", class: "flash flash_global", values: { "page-cached" => page_will_be_cached?, src: flash_path },
|
102
|
+
actions: %w[flash-message:connect->registerMessage flash-message:disconnect->removeMessage] \
|
103
|
+
) do |fc|
|
104
|
+
- unless page_will_be_cached?
|
105
|
+
- flash.keys.each do |type|
|
106
|
+
.flash__message
|
107
|
+
= stimulus_controller( \
|
108
|
+
"flash-message",
|
109
|
+
tag: "div", class: "flash-message flash-message_type_#{type}",
|
110
|
+
actions: %w[animationend->next click->hide] \
|
111
|
+
) do |fmc|
|
112
|
+
.flash-message__text = flash[type]
|
113
|
+
button.flash-message__dismiss-button[aria-label="Close flash message" *fmc.action("dismiss")]
|
114
|
+
= svg_sprite "icons/common.svg#common-times", class: "icon"
|
115
|
+
|
116
|
+
```
|
117
|
+
|
118
|
+
|
119
|
+
## Support
|
120
|
+
|
121
|
+
If you want to report a bug, or have ideas, feedback or questions about the gem, [let me know via GitHub issues](https://github.com/crawler/stimulus_tag_helper/issues/new) and I will do my best to provide a helpful answer. Happy hacking!
|
122
|
+
|
123
|
+
## License
|
124
|
+
|
125
|
+
The gem is available as open source under the terms of the [MIT License](LICENSE.txt).
|
126
|
+
|
127
|
+
## Code of conduct
|
128
|
+
|
129
|
+
Everyone interacting in this project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](CODE_OF_CONDUCT.md).
|
130
|
+
|
131
|
+
## Contribution guide
|
132
|
+
|
133
|
+
Pull requests are welcome!
|
@@ -0,0 +1,134 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StimulusTagHelper
|
4
|
+
autoload :VERSION, "stimulus_tag_helper/version"
|
5
|
+
autoload :StimulusAction, "stimulus_tag_helper/stimulus_action"
|
6
|
+
autoload :StimulusControllerBuilder, "stimulus_tag_helper/stimulus_controller_builder"
|
7
|
+
|
8
|
+
def self.base_properties
|
9
|
+
%i[controller values classes targets target actions]
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.aliases_map
|
13
|
+
{ values: :value, classes: :class, actions: :action }
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.alias_properties
|
17
|
+
@alias_properties ||= aliases_map.values
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.property_names
|
21
|
+
@property_names ||= base_properties + alias_properties
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.prevent_duplicates(properties_set, name)
|
25
|
+
alias_name = StimulusTagHelper.aliases_map[name]
|
26
|
+
return unless alias_name && (properties_set[alias_name])
|
27
|
+
|
28
|
+
raise(ArgumentError, <<~STRING)
|
29
|
+
"#{name} and #{alias_name} can't be passed at same time"
|
30
|
+
STRING
|
31
|
+
end
|
32
|
+
|
33
|
+
def stimulus_controller(identifier, tag: nil, **args, &block)
|
34
|
+
raise(ArgumentError, "Missing block") unless block
|
35
|
+
|
36
|
+
builder = StimulusControllerBuilder.new(identifier: identifier, template: self)
|
37
|
+
|
38
|
+
return capture(builder, &block) unless tag
|
39
|
+
|
40
|
+
stimulus_controller_tag(identifier, tag: tag, **args) do
|
41
|
+
capture(builder, &block)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def stimulus_controller_tag(identifier, tag:, data: {}, **args, &block)
|
46
|
+
data.merge!(stimulus_controllers_property(identifier))
|
47
|
+
# class option goes to the tag
|
48
|
+
data.merge!(stimulus_properties(identifier, args.extract!(*StimulusTagHelper.property_names - %i[class])))
|
49
|
+
tag_builder.tag_string(tag, **args.merge(data: data), &block)
|
50
|
+
end
|
51
|
+
|
52
|
+
def stimulus_properties(identifier, props)
|
53
|
+
{}.tap do |data|
|
54
|
+
StimulusTagHelper.property_names.each do |name|
|
55
|
+
next unless props[name]
|
56
|
+
|
57
|
+
params = Array.wrap(props[name]).unshift(identifier)
|
58
|
+
kwparams = params.last.is_a?(Hash) ? params.pop : {}
|
59
|
+
StimulusTagHelper.prevent_duplicates(props, name)
|
60
|
+
name = name.to_s
|
61
|
+
property = name.pluralize == name ? "properties" : "property"
|
62
|
+
data.merge!(public_send("stimulus_#{name}_#{property}", *params, **kwparams))
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def stimulus_controllers_property(*identifiers)
|
68
|
+
{ controller: Array.wrap(identifiers).join(" ") }
|
69
|
+
end
|
70
|
+
|
71
|
+
alias stimulus_controller_property stimulus_controllers_property
|
72
|
+
|
73
|
+
def stimulus_values_properties(identifier, **values)
|
74
|
+
{}.tap do |properties|
|
75
|
+
values.each_pair do |name, value|
|
76
|
+
properties["#{identifier}-#{name}-value"] = value
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
alias stimulus_value_property stimulus_values_properties
|
82
|
+
|
83
|
+
def stimulus_classes_properties(identifier, **classes)
|
84
|
+
{}.tap do |properties|
|
85
|
+
classes.each_pair do |name, value|
|
86
|
+
properties["#{identifier}-#{name}-class"] = value
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
alias stimulus_class_property stimulus_classes_properties
|
92
|
+
|
93
|
+
def stimulus_targets_properties(identifier, *targets)
|
94
|
+
{}.tap do |properties|
|
95
|
+
targets.each do |target|
|
96
|
+
properties.merge!(stimulus_target_property(identifier, target))
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def stimulus_target_property(identifier, name)
|
102
|
+
{ "#{identifier}-target" => name }
|
103
|
+
end
|
104
|
+
|
105
|
+
def stimulus_actions_properties(identifier, *actions_params)
|
106
|
+
{
|
107
|
+
"action" => actions_params.map { |action_params| stimulus_action_value(identifier, action_params) }.join(" ")
|
108
|
+
}
|
109
|
+
end
|
110
|
+
|
111
|
+
alias stimulus_action_property stimulus_actions_properties
|
112
|
+
|
113
|
+
def stimulus_action_value(identifier, action_params_or_action_str)
|
114
|
+
if action_params_or_action_str.is_a?(String)
|
115
|
+
action_params_or_action_str = StimulusAction.parse(action_params_or_action_str)
|
116
|
+
end
|
117
|
+
|
118
|
+
StimulusAction.new(identifier: identifier, **action_params_or_action_str).to_s
|
119
|
+
end
|
120
|
+
|
121
|
+
property_names.each do |name|
|
122
|
+
name = name.to_s
|
123
|
+
attribute, property = name.pluralize == name ? %w[attributes properties] : %w[attribute property]
|
124
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
125
|
+
def stimulus_#{name}_#{attribute}(...) # def stimulus_value_attribute(...)
|
126
|
+
{ data: stimulus_#{name}_#{property}(...) } # { data: stimulus_value_property(...) }
|
127
|
+
end # end
|
128
|
+
RUBY
|
129
|
+
end
|
130
|
+
|
131
|
+
def stimulus_attribute(...)
|
132
|
+
{ data: stimulus_properties(...) }
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StimulusTagHelper
|
4
|
+
class StimulusAction
|
5
|
+
STANDARD_PARSER =
|
6
|
+
/^(?<void>(?<event>.+?)(?<void>@(?<target>window|document))?->)?(?<identifier>.+?)(?<void>#(?<method>[^:]+?))(?<void>:(?<options>.+))?$/.freeze
|
7
|
+
NO_CONTROLLER_PARSER =
|
8
|
+
/^(?<void>(?<event>.+?)?(?<void>@(?<target>window|document))?->)?(?<method>[^#:]+?)(?<void>:(?<options>.+))?$/.freeze
|
9
|
+
class_attribute :parts, default: %i[identifier method event target options]
|
10
|
+
attr_reader(*parts)
|
11
|
+
|
12
|
+
# event is nil to let stimulusjs decide default event for the element
|
13
|
+
def initialize(identifier:, method:, event: nil, target: nil, options: nil)
|
14
|
+
parts.each do |part|
|
15
|
+
instance_variable_set(:"@#{part}", binding.local_variable_get(part))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def event_part
|
20
|
+
"#{event}#{"@#{target}" if target}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def handler_part
|
24
|
+
"#{identifier}##{method}#{":#{options}" if options}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
"#{event_part.presence&.+'->'}#{handler_part}".html_safe
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.parse(str)
|
32
|
+
parsed =
|
33
|
+
str.include?("#") ? STANDARD_PARSER.match(str) : NO_CONTROLLER_PARSER.match(str)
|
34
|
+
raise(ArgumentError, "can't parse action string #{str}") unless parsed
|
35
|
+
|
36
|
+
parsed.named_captures.except("void").symbolize_keys
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StimulusTagHelper
|
4
|
+
class StimulusControllerBuilder
|
5
|
+
def initialize(identifier:, template:)
|
6
|
+
@identifier = identifier
|
7
|
+
@template = template
|
8
|
+
end
|
9
|
+
|
10
|
+
StimulusTagHelper.property_names.each do |name|
|
11
|
+
name = name.to_s
|
12
|
+
attribute, property = name.pluralize == name ? %w[attributes properties] : %w[attribute property]
|
13
|
+
|
14
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
15
|
+
def #{name}_#{attribute}(...) # def value_attribute(...)
|
16
|
+
{ data: #{name}_#{property}(...) } # { data: value_property(...) }
|
17
|
+
end # end
|
18
|
+
|
19
|
+
alias #{name} #{name}_#{attribute} # alias value value_attribute
|
20
|
+
|
21
|
+
def #{name}_#{property}(*args, **kws) # def value_property(*args, **kws)
|
22
|
+
@template.stimulus_#{name}_#{property}(*args.unshift(@identifier), **kws) # stimulus_value_property(@identifier, *args, **kws)
|
23
|
+
end # end
|
24
|
+
RUBY
|
25
|
+
end
|
26
|
+
|
27
|
+
def attributes(props)
|
28
|
+
{ data: @template.stimulus_properties(@identifier, props) }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: stimulus_tag_helper
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Anton Topchii
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-06-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 6.0.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 6.0.0
|
27
|
+
description:
|
28
|
+
email:
|
29
|
+
- player1@infinitevoid.net
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- LICENSE.txt
|
35
|
+
- README.md
|
36
|
+
- lib/stimulus_tag_helper.rb
|
37
|
+
- lib/stimulus_tag_helper/stimulus_action.rb
|
38
|
+
- lib/stimulus_tag_helper/stimulus_controller_builder.rb
|
39
|
+
- lib/stimulus_tag_helper/version.rb
|
40
|
+
homepage: https://github.com/crawler/stimulus_tag_helper
|
41
|
+
licenses:
|
42
|
+
- MIT
|
43
|
+
metadata:
|
44
|
+
bug_tracker_uri: https://github.com/crawler/stimulus_tag_helper/issues
|
45
|
+
changelog_uri: https://github.com/crawler/stimulus_tag_helper/releases
|
46
|
+
source_code_uri: https://github.com/crawler/stimulus_tag_helper
|
47
|
+
homepage_uri: https://github.com/crawler/stimulus_tag_helper
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options: []
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: 2.7.0
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
requirements: []
|
63
|
+
rubygems_version: 3.2.15
|
64
|
+
signing_key:
|
65
|
+
specification_version: 4
|
66
|
+
summary: A form_for like, compact and elegant way to define stimulus attributes in
|
67
|
+
your views.
|
68
|
+
test_files: []
|