amber_component 1.0.0 → 1.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 +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +152 -103
- data/amber_component.gemspec +0 -3
- data/assets/javascripts/amber_component/stimulus_loading.js +30 -0
- data/lib/amber_component/configuration.rb +43 -0
- data/lib/amber_component/railtie.rb +3 -1
- data/lib/amber_component/version.rb +1 -1
- data/lib/amber_component.rb +20 -0
- data/lib/generators/amber_component/install_generator.rb +85 -2
- data/lib/generators/amber_component_generator.rb +35 -0
- data/lib/generators/templates/controller.js.erb +12 -0
- data/lib/generators/templates/view.haml.erb +5 -1
- data/lib/generators/templates/view.html.erb.erb +4 -1
- data/lib/generators/templates/view.slim.erb +5 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3fe50a4aaf1482572196e8ae5fd9fd7e2f04ef1ee07b9b5275b0454623e54a6d
|
4
|
+
data.tar.gz: 17c1e2848263efac38a029621d26a8d2eae5b4b3d47d2c9eaf8e040b14fe3eae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e566cb7199d6d8c890081557ed20ac02410085992b036b6a603e909a62ac6fb6af0e853910ab02ac087fd8e3bcaf398d59496e7172ac1233d843206785b15509
|
7
|
+
data.tar.gz: 3b8bb6257ba09f3f6fbf98c68a128ab99adfa289e8ee3d674de744052b4b69f7b06c0e5f43a5e7bb4ae54533a78728fea15ba2534b947b4e730869b5e09b8932
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -9,7 +9,7 @@
|
|
9
9
|
|
10
10
|
# AmberComponent
|
11
11
|
|
12
|
-
AmberComponent is a simple component library which seamlessly hooks into your Rails project and allows you to create simple backend components
|
12
|
+
AmberComponent is a simple component library which seamlessly hooks into your Rails project and allows you to create simple backend components which consist of a Ruby controller, view, stylesheet and even a JavaScript controller (using [Stimulus](https://stimulus.hotwired.dev/)).
|
13
13
|
|
14
14
|
Created by [Garbus Beach](https://github.com/garbusbeach) and [Mateusz Drewniak](https://github.com/Verseth).
|
15
15
|
|
@@ -33,16 +33,37 @@ If you're using a Rails application there's an installation generator that you s
|
|
33
33
|
$ bin/rails generate amber_component:install
|
34
34
|
```
|
35
35
|
|
36
|
+
Amber component supports [Stimulus](https://stimulus.hotwired.dev/) to make your components
|
37
|
+
reactive using JavaScript.
|
38
|
+
|
39
|
+
If you want to use stimulus you should install this gem with the `--stimulus` flag
|
40
|
+
|
41
|
+
```sh
|
42
|
+
$ bin/rails generate amber_component:install --stimulus
|
43
|
+
```
|
44
|
+
|
36
45
|
## Usage
|
37
46
|
|
38
|
-
|
47
|
+
### Components
|
39
48
|
|
40
|
-
Components are located under `app/components`.
|
49
|
+
Components are located under `app/components`. And their tests under `test/components`.
|
41
50
|
|
42
51
|
Every component consists of:
|
43
52
|
- a Ruby file which defines its properties, encapsulates logic and may implement helper methods (like a controller)
|
44
53
|
- a view/template file (html.erb, haml, slim etc.)
|
45
54
|
- a style file (css, scss, sass etc.)
|
55
|
+
- [optional] a JavaScript file with a Stimulus controller (if you installed the gem with `--stimulus`)
|
56
|
+
|
57
|
+
```
|
58
|
+
app/components/
|
59
|
+
├─ [name]_component.rb
|
60
|
+
└─ [name]_component/
|
61
|
+
├─ style.css # may be .sass or .scss
|
62
|
+
├─ view.html.erb
|
63
|
+
└─ controller.js # if stimulus is configured
|
64
|
+
test/components/
|
65
|
+
└─ [name]_component_test.rb
|
66
|
+
```
|
46
67
|
|
47
68
|
An individual component which implements a button may look like this.
|
48
69
|
|
@@ -57,7 +78,9 @@ end
|
|
57
78
|
```html
|
58
79
|
<!-- app/components/button_component/view.html.erb -->
|
59
80
|
|
60
|
-
<div class="button_component"
|
81
|
+
<div class="button_component"
|
82
|
+
data-controller="button-component"
|
83
|
+
data-action="click->button-component#greet">
|
61
84
|
<%= label %>
|
62
85
|
</div>
|
63
86
|
```
|
@@ -76,6 +99,25 @@ end
|
|
76
99
|
}
|
77
100
|
```
|
78
101
|
|
102
|
+
If you used the `--stimulus` option when installing the gem, a JS controller will be generated as well.
|
103
|
+
```js
|
104
|
+
// app/components/button_component/controller.js
|
105
|
+
|
106
|
+
import { Controller } from "@hotwired/stimulus"
|
107
|
+
|
108
|
+
// Read more about Stimulus here https://stimulus.hotwired.dev/
|
109
|
+
export default class extends Controller {
|
110
|
+
connect() {
|
111
|
+
console.log("Stimulus controller 'button-component' is connected!")
|
112
|
+
}
|
113
|
+
|
114
|
+
greet() {
|
115
|
+
alert("Hi there!")
|
116
|
+
}
|
117
|
+
}
|
118
|
+
|
119
|
+
```
|
120
|
+
|
79
121
|
You can render this component in other components or in a Rails view.
|
80
122
|
|
81
123
|
```html
|
@@ -97,34 +139,69 @@ ButtonComponent.call label: 'Click me!'
|
|
97
139
|
#=> '<div class="button_component">Click me!</div>'
|
98
140
|
```
|
99
141
|
|
100
|
-
###
|
142
|
+
### Components with namespaces
|
101
143
|
|
102
|
-
|
103
|
-
of all ActionView helpers and Rails route helpers.
|
144
|
+
Components may be defined inside multiple modules/namespaces.
|
104
145
|
|
105
|
-
|
146
|
+
```ruby
|
147
|
+
# app/components/sign_up/button_component.rb
|
106
148
|
|
107
|
-
|
108
|
-
|
149
|
+
class SignUp::ButtonComponent < AmberComponent::Base
|
150
|
+
prop :label, required: true
|
151
|
+
end
|
152
|
+
```
|
109
153
|
|
110
|
-
|
111
|
-
|
112
|
-
<%= f.text_field :first_name %>
|
154
|
+
```html
|
155
|
+
<!-- app/components/sign_up/button_component/view.html.erb -->
|
113
156
|
|
114
|
-
|
115
|
-
|
157
|
+
<div class="sign_up_button_component">
|
158
|
+
<%= label %>
|
159
|
+
</div>
|
160
|
+
```
|
116
161
|
|
117
|
-
|
118
|
-
|
162
|
+
```scss
|
163
|
+
// app/components/sign_up/button_component/style.scss
|
119
164
|
|
120
|
-
|
121
|
-
|
165
|
+
.sign_up_button_component {
|
166
|
+
background-color: indigo;
|
167
|
+
border-radius: 1rem;
|
168
|
+
transition-duration: 500ms;
|
122
169
|
|
123
|
-
|
124
|
-
|
170
|
+
&:hover {
|
171
|
+
background-color: blue;
|
172
|
+
}
|
173
|
+
}
|
174
|
+
```
|
125
175
|
|
126
|
-
|
127
|
-
|
176
|
+
You can render such a component by calling the `::call` method
|
177
|
+
on its class, or by using the helper method defined on its parent module.
|
178
|
+
|
179
|
+
```ruby
|
180
|
+
SignUp::ButtonComponent.call label: 'Sign up!'
|
181
|
+
SignUp.button_component label: 'Sign up!'
|
182
|
+
```
|
183
|
+
|
184
|
+
### Generating Components
|
185
|
+
|
186
|
+
You can generate new components by running
|
187
|
+
|
188
|
+
```sh
|
189
|
+
$ bin/rails generate component [name]
|
190
|
+
```
|
191
|
+
|
192
|
+
Name of the component may be PascalCased like `FooBar` or snake_cased `foo_bar`
|
193
|
+
|
194
|
+
This will generate a new component in `app/components/[name]_component.rb` along with a view, stylesheet, test file and a stimulus controller (if configured).
|
195
|
+
|
196
|
+
```
|
197
|
+
app/components/
|
198
|
+
├─ [name]_component.rb
|
199
|
+
└─ [name]_component/
|
200
|
+
├─ style.css
|
201
|
+
├─ view.html.erb
|
202
|
+
└─ controller.js # if stimulus is configured
|
203
|
+
test/components/
|
204
|
+
└─ [name]_component_test.rb
|
128
205
|
```
|
129
206
|
|
130
207
|
### Component properties
|
@@ -155,39 +232,6 @@ CommentComponent.call body: 'Foo bar', author: User.first
|
|
155
232
|
comment_component body: 'Foo bar', author: User.first
|
156
233
|
```
|
157
234
|
|
158
|
-
### Overriding prop getters and setters
|
159
|
-
|
160
|
-
Getters and setters for properties are
|
161
|
-
defined in a module which means that you can override them and call them with `super`.
|
162
|
-
|
163
|
-
```ruby
|
164
|
-
# app/components/priority_icon_component.rb
|
165
|
-
|
166
|
-
class PriorityIconComponent < ApplicationComponent
|
167
|
-
PriorityStruct = Struct.new :icon, :color
|
168
|
-
|
169
|
-
PRIORITY_MAP = {
|
170
|
-
low: PriorityStruct.new('fa-solid fa-chevrons-down', 'green'),
|
171
|
-
medium: PriorityStruct.new('fa-solid fa-chevron-up', 'yellow'),
|
172
|
-
high: PriorityStruct.new('fa-solid fa-chevrons-up', 'red')
|
173
|
-
}
|
174
|
-
|
175
|
-
prop :severity, default: -> { :low }
|
176
|
-
|
177
|
-
def severity=(val)
|
178
|
-
# super will call the original
|
179
|
-
# implementation of the setter
|
180
|
-
super(PRIORITY_MAP[val])
|
181
|
-
end
|
182
|
-
end
|
183
|
-
```
|
184
|
-
|
185
|
-
```html
|
186
|
-
<!-- app/components/priority_icon_component/view.html.erb -->
|
187
|
-
|
188
|
-
<i style="color: <%= severity&.color %>;" class="<%= severity&.icon %>"></i>
|
189
|
-
```
|
190
|
-
|
191
235
|
### Helper methods
|
192
236
|
|
193
237
|
Defining helper methods which are available
|
@@ -239,6 +283,39 @@ end
|
|
239
283
|
</div>
|
240
284
|
```
|
241
285
|
|
286
|
+
### Overriding prop getters and setters
|
287
|
+
|
288
|
+
Getters and setters for properties are
|
289
|
+
defined in a module which means that you can override them and call them with `super`.
|
290
|
+
|
291
|
+
```ruby
|
292
|
+
# app/components/priority_icon_component.rb
|
293
|
+
|
294
|
+
class PriorityIconComponent < ApplicationComponent
|
295
|
+
PriorityStruct = Struct.new :icon, :color
|
296
|
+
|
297
|
+
PRIORITY_MAP = {
|
298
|
+
low: PriorityStruct.new('fa-solid fa-chevrons-down', 'green'),
|
299
|
+
medium: PriorityStruct.new('fa-solid fa-chevron-up', 'yellow'),
|
300
|
+
high: PriorityStruct.new('fa-solid fa-chevrons-up', 'red')
|
301
|
+
}
|
302
|
+
|
303
|
+
prop :severity, default: -> { :low }
|
304
|
+
|
305
|
+
def severity=(val)
|
306
|
+
# super will call the original
|
307
|
+
# implementation of the setter
|
308
|
+
super(PRIORITY_MAP[val])
|
309
|
+
end
|
310
|
+
end
|
311
|
+
```
|
312
|
+
|
313
|
+
```html
|
314
|
+
<!-- app/components/priority_icon_component/view.html.erb -->
|
315
|
+
|
316
|
+
<i style="color: <%= severity&.color %>;" class="<%= severity&.icon %>"></i>
|
317
|
+
```
|
318
|
+
|
242
319
|
### Nested components
|
243
320
|
|
244
321
|
It's possible to nest components or provide
|
@@ -304,64 +381,36 @@ In general `block_given?` will return `true` when a block/nested content is pres
|
|
304
381
|
You can use it to render content conditionally based on
|
305
382
|
whether nested content is present.
|
306
383
|
|
307
|
-
###
|
308
|
-
|
309
|
-
Components may be defined inside multiple modules/namespaces.
|
310
|
-
|
311
|
-
```ruby
|
312
|
-
# app/components/sign_up/button_component.rb
|
313
|
-
|
314
|
-
class SignUp::ButtonComponent < AmberComponent::Base
|
315
|
-
prop :label, required: true
|
316
|
-
end
|
317
|
-
```
|
318
|
-
|
319
|
-
```html
|
320
|
-
<!-- app/components/sign_up/button_component/view.html.erb -->
|
321
|
-
|
322
|
-
<div class="sign_up_button_component">
|
323
|
-
<%= label %>
|
324
|
-
</div>
|
325
|
-
```
|
326
|
-
|
327
|
-
```scss
|
328
|
-
// app/components/sign_up/button_component/style.scss
|
384
|
+
### Rails helpers inside component templates
|
329
385
|
|
330
|
-
|
331
|
-
|
332
|
-
border-radius: 1rem;
|
333
|
-
transition-duration: 500ms;
|
386
|
+
Component views/template files can make use
|
387
|
+
of all ActionView helpers and Rails route helpers.
|
334
388
|
|
335
|
-
|
336
|
-
background-color: blue;
|
337
|
-
}
|
338
|
-
}
|
339
|
-
```
|
389
|
+
This makes component views very flexible and convenient.
|
340
390
|
|
341
|
-
|
342
|
-
|
391
|
+
```erb
|
392
|
+
<!-- app/components/login_form_component/view.html.erb -->
|
343
393
|
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
```
|
394
|
+
<%= form_with url: sign_up_path, class: "login_form_component" do |f| %>
|
395
|
+
<%= f.label :first_name %>
|
396
|
+
<%= f.text_field :first_name %>
|
348
397
|
|
349
|
-
|
398
|
+
<%= f.label :last_name %>
|
399
|
+
<%= f.text_field :last_name %>
|
350
400
|
|
351
|
-
|
401
|
+
<%= f.label :email, "Email Address" %>
|
402
|
+
<%= f.text_field :email %>
|
352
403
|
|
353
|
-
|
354
|
-
|
355
|
-
```
|
404
|
+
<%= f.label :password %>
|
405
|
+
<%= f.password_field :password %>
|
356
406
|
|
357
|
-
|
407
|
+
<%= f.label :password_confirmation, "Confirm Password" %>
|
408
|
+
<%= f.password_field :password_confirmation %>
|
358
409
|
|
359
|
-
|
360
|
-
|
410
|
+
<%= f.submit "Create account" %>
|
411
|
+
<% end %>
|
361
412
|
```
|
362
413
|
|
363
|
-
This will generate a new component in `app/components/foo_bar_component.rb` along with a view, stylesheet and test file.
|
364
|
-
|
365
414
|
### Testing Components
|
366
415
|
|
367
416
|
### Rails
|
data/amber_component.gemspec
CHANGED
@@ -43,7 +43,4 @@ require_relative "lib/amber_component/version"
|
|
43
43
|
spec.add_dependency "activesupport", ">= 6"
|
44
44
|
spec.add_dependency "memery", ">= 1.4.1"
|
45
45
|
spec.add_dependency "tilt", ">= 2.0.10"
|
46
|
-
|
47
|
-
# For more information and examples about making a new gem, check out our
|
48
|
-
# guide at: https://bundler.io/guides/creating_gem.html
|
49
46
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
const registeredControllers = {}
|
2
|
+
|
3
|
+
export function eagerLoadAmberComponentControllers(application) {
|
4
|
+
const paths = Object.keys(parseImportmapJson()).filter(path => path.match(new RegExp(`/controller$`)))
|
5
|
+
paths.forEach(path => registerControllerFromPath(path, application))
|
6
|
+
}
|
7
|
+
|
8
|
+
function parseImportmapJson() {
|
9
|
+
return JSON.parse(document.querySelector("script[type=importmap]").text).imports
|
10
|
+
}
|
11
|
+
|
12
|
+
function registerControllerFromPath(path, application) {
|
13
|
+
const name = path
|
14
|
+
.replace("/controller", "")
|
15
|
+
.replace(/\//g, "--")
|
16
|
+
.replace(/_/g, "-")
|
17
|
+
|
18
|
+
if (!(name in registeredControllers)) {
|
19
|
+
import(path)
|
20
|
+
.then(module => registerController(name, module, application))
|
21
|
+
.catch(error => console.error(`Failed to register controller: ${name} (${path})`, error))
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
function registerController(name, module, application) {
|
26
|
+
if (!(name in registeredControllers)) {
|
27
|
+
application.register(name, module.default)
|
28
|
+
registeredControllers[name] = true
|
29
|
+
}
|
30
|
+
}
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ::AmberComponent
|
4
|
+
# Object which stores configuration options
|
5
|
+
# for this gem.
|
6
|
+
class Configuration
|
7
|
+
# @return [Array<Symbol>]
|
8
|
+
STIMULUS_INTEGRATIONS = %i[importmap jsbundling webpack esbuild rollup].freeze
|
9
|
+
|
10
|
+
# How Stimulus.js is bundled in this app.
|
11
|
+
# Possible values: `[nil, :importmap, :jsbundling, :webpack, :esbuild, :rollup]`
|
12
|
+
# `nil` indicates that stimulus should not be used (default behaviour).
|
13
|
+
#
|
14
|
+
# @return [Symbol, nil]
|
15
|
+
attr_reader :stimulus
|
16
|
+
|
17
|
+
# How Stimulus.js is bundled in this app.
|
18
|
+
# Possible values: `[nil, :importmap, :jsbundling, :webpack, :esbuild, :rollup]`
|
19
|
+
# `nil` indicates that stimulus should not be used (default behaviour).
|
20
|
+
#
|
21
|
+
# @param val [Symbol, String, nil]
|
22
|
+
def stimulus=(val)
|
23
|
+
val = val&.to_sym
|
24
|
+
unless val.nil? || STIMULUS_INTEGRATIONS.include?(val)
|
25
|
+
raise(::ArgumentError,
|
26
|
+
"Invalid value for `stimulus` bundling. " \
|
27
|
+
"Received #{val.inspect}, expected one of #{STIMULUS_INTEGRATIONS.inspect}")
|
28
|
+
end
|
29
|
+
|
30
|
+
@stimulus = val
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [Boolean]
|
34
|
+
def stimulus?
|
35
|
+
!@stimulus.nil?
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [Boolean]
|
39
|
+
def stimulus_importmap?
|
40
|
+
@stimulus == :importmap
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -4,8 +4,10 @@ module ::AmberComponent
|
|
4
4
|
# Class which hooks into Rails
|
5
5
|
# and configures the application.
|
6
6
|
class Railtie < ::Rails::Railtie
|
7
|
-
initializer 'amber_component.
|
7
|
+
initializer 'amber_component.assets' do |app|
|
8
8
|
app.config.assets.paths << (app.root / 'app' / 'components')
|
9
|
+
app.config.assets.paths << (ROOT_GEM_PATH / 'assets' / 'javascripts')
|
10
|
+
app.config.assets.precompile += %w[amber_component/stimulus_loading.js]
|
9
11
|
|
10
12
|
next if ::Rails.env.production?
|
11
13
|
|
data/lib/amber_component.rb
CHANGED
@@ -2,7 +2,11 @@
|
|
2
2
|
|
3
3
|
require 'active_support'
|
4
4
|
require 'active_support/core_ext'
|
5
|
+
require 'pathname'
|
5
6
|
|
7
|
+
require_relative 'amber_component/configuration'
|
8
|
+
|
9
|
+
# Root module of the `amber_component` gem.
|
6
10
|
module ::AmberComponent
|
7
11
|
class Error < ::StandardError; end
|
8
12
|
class MissingPropsError < Error; end
|
@@ -13,6 +17,22 @@ module ::AmberComponent
|
|
13
17
|
class EmptyViewError < Error; end
|
14
18
|
class UnknownViewTypeError < Error; end
|
15
19
|
class MultipleViewsError < Error; end
|
20
|
+
|
21
|
+
# @return [Pathname]
|
22
|
+
ROOT_GEM_PATH = ::Pathname.new ::File.expand_path('..', __dir__)
|
23
|
+
|
24
|
+
class << self
|
25
|
+
# @return [Configuration]
|
26
|
+
def configuration
|
27
|
+
@configuration ||= Configuration.new
|
28
|
+
end
|
29
|
+
|
30
|
+
# @yieldparam [Configuration]
|
31
|
+
# @return [void]
|
32
|
+
def configure
|
33
|
+
yield configuration
|
34
|
+
end
|
35
|
+
end
|
16
36
|
end
|
17
37
|
|
18
38
|
require_relative 'amber_component/version'
|
@@ -10,8 +10,14 @@ module ::AmberComponent
|
|
10
10
|
desc 'Install the AmberComponent gem'
|
11
11
|
source_root ::File.expand_path('templates', __dir__)
|
12
12
|
|
13
|
-
#
|
14
|
-
|
13
|
+
# @return [Array<Symbol>]
|
14
|
+
STIMULUS_INTEGRATIONS = %i[stimulus importmap jsbundling webpack esbuild rollup].freeze
|
15
|
+
|
16
|
+
class_option :stimulus,
|
17
|
+
desc: "Configure the app to use Stimulus.js wih components to make them interactive " \
|
18
|
+
"[options: importmap (default), jsbundling, webpack, esbuild, rollup]"
|
19
|
+
|
20
|
+
def setup
|
15
21
|
copy_file 'application_component.rb', 'app/components/application_component.rb'
|
16
22
|
copy_file 'application_component_test_case.rb', 'test/application_component_test_case.rb'
|
17
23
|
append_file 'test/test_helper.rb', "require_relative 'application_component_test_case'"
|
@@ -23,10 +29,87 @@ module ::AmberComponent
|
|
23
29
|
require_components_css_in 'app/assets/stylesheets/application.css.sass'
|
24
30
|
require_components_css_in 'app/assets/stylesheets/application.scss.sass'
|
25
31
|
require_components_css_in 'app/assets/stylesheets/application.sass.scss'
|
32
|
+
configure_stimulus
|
26
33
|
end
|
27
34
|
|
28
35
|
private
|
29
36
|
|
37
|
+
def configure_stimulus
|
38
|
+
stimulus = options[:stimulus]&.to_sym
|
39
|
+
return unless stimulus
|
40
|
+
|
41
|
+
case stimulus
|
42
|
+
when :stimulus
|
43
|
+
if defined?(::Jsbundling)
|
44
|
+
stimulus_integration = :jsbundling
|
45
|
+
configure_stimulus_jsbundling
|
46
|
+
else
|
47
|
+
stimulus_integration = :importmap
|
48
|
+
configure_stimulus_importmap
|
49
|
+
end
|
50
|
+
when :importmap
|
51
|
+
stimulus_integration = :importmap
|
52
|
+
configure_stimulus_importmap
|
53
|
+
when :jsbundling, :webpack, :esbuild, :rollup
|
54
|
+
stimulus_integration = :jsbundling
|
55
|
+
configure_stimulus_jsbundling
|
56
|
+
end
|
57
|
+
|
58
|
+
create_file 'config/initializers/amber_component.rb', <<~RUBY
|
59
|
+
# frozen_string_literal: true
|
60
|
+
|
61
|
+
::AmberComponent.configure do |c|
|
62
|
+
c.stimulus = :#{stimulus_integration}
|
63
|
+
end
|
64
|
+
RUBY
|
65
|
+
end
|
66
|
+
|
67
|
+
def configure_stimulus_importmap
|
68
|
+
install_importmap
|
69
|
+
install_stimulus
|
70
|
+
append_file 'config/importmap.rb', <<~RUBY
|
71
|
+
pin "@amber_component/stimulus_loading", to: "amber_component/stimulus_loading.js", preload: true
|
72
|
+
pin_all_from "app/components"
|
73
|
+
RUBY
|
74
|
+
append_file 'app/javascript/controllers/index.js', <<~JS
|
75
|
+
import { eagerLoadAmberComponentControllers } from "@amber_component/stimulus_loading"
|
76
|
+
eagerLoadAmberComponentControllers(application)
|
77
|
+
JS
|
78
|
+
append_file 'app/assets/config/manifest.js', %(//= link_tree ../../components .js\n)
|
79
|
+
end
|
80
|
+
|
81
|
+
def configure_stimulus_jsbundling
|
82
|
+
install_stimulus
|
83
|
+
append_file 'app/javascript/application.js', %(import "./controllers/components"\n)
|
84
|
+
create_file 'app/javascript/controllers/components.js', <<~JS
|
85
|
+
// This file has been created by `amber_component` and will
|
86
|
+
// register all stimulus controllers from your components
|
87
|
+
import { application } from "./application"
|
88
|
+
JS
|
89
|
+
end
|
90
|
+
|
91
|
+
# @return [void]
|
92
|
+
def install_importmap
|
93
|
+
return if ::File.exist?('config/importmap.rb') && defined?(::Importmap)
|
94
|
+
|
95
|
+
unless defined?(::Importmap)
|
96
|
+
system 'gem install importmap-rails'
|
97
|
+
gem 'importmap-rails'
|
98
|
+
system 'bundle install'
|
99
|
+
end
|
100
|
+
rake 'importmap:install'
|
101
|
+
end
|
102
|
+
|
103
|
+
# @return [void]
|
104
|
+
def install_stimulus
|
105
|
+
return if defined?(::Stimulus)
|
106
|
+
|
107
|
+
system 'gem install stimulus-rails'
|
108
|
+
gem 'stimulus-rails'
|
109
|
+
system 'bundle install'
|
110
|
+
rake 'stimulus:install'
|
111
|
+
end
|
112
|
+
|
30
113
|
# @param file_name [String]
|
31
114
|
# @return [void]
|
32
115
|
def require_components_css_in(file_name)
|
@@ -40,8 +40,10 @@ class AmberComponentGenerator < ::Rails::Generators::NamedBase
|
|
40
40
|
template 'component_test.rb.erb', "test/components/#{file_path}_test.rb"
|
41
41
|
create_stylesheet
|
42
42
|
create_view
|
43
|
+
create_stimulus_controller
|
43
44
|
end
|
44
45
|
|
46
|
+
# @return [String]
|
45
47
|
def file_name
|
46
48
|
name = super
|
47
49
|
return name if name.end_with? '_component'
|
@@ -51,6 +53,29 @@ class AmberComponentGenerator < ::Rails::Generators::NamedBase
|
|
51
53
|
|
52
54
|
private
|
53
55
|
|
56
|
+
# @return [Boolean]
|
57
|
+
def stimulus?
|
58
|
+
::AmberComponent.configuration.stimulus?
|
59
|
+
end
|
60
|
+
|
61
|
+
# @return [Boolean]
|
62
|
+
def stimulus_importmap?
|
63
|
+
::AmberComponent.configuration.stimulus_importmap?
|
64
|
+
end
|
65
|
+
|
66
|
+
# @return [void]
|
67
|
+
def create_stimulus_controller
|
68
|
+
return unless stimulus?
|
69
|
+
|
70
|
+
template 'controller.js.erb', "app/components/#{file_path}/controller.js"
|
71
|
+
return if stimulus_importmap?
|
72
|
+
|
73
|
+
append_file 'app/javascript/controllers/components.js', <<~JS
|
74
|
+
import #{stimulus_controller_class_name} from "../../components/#{file_path}/controller"
|
75
|
+
application.register("#{stimulus_controller_id}", #{stimulus_controller_class_name})
|
76
|
+
JS
|
77
|
+
end
|
78
|
+
|
54
79
|
# @return [void]
|
55
80
|
def create_view
|
56
81
|
case @view_format
|
@@ -73,4 +98,14 @@ class AmberComponentGenerator < ::Rails::Generators::NamedBase
|
|
73
98
|
template 'style.css.erb', "app/components/#{file_path}/style.css"
|
74
99
|
end
|
75
100
|
end
|
101
|
+
|
102
|
+
# @return [String]
|
103
|
+
def stimulus_controller_id
|
104
|
+
file_path.gsub('_', '-').gsub('/', '--')
|
105
|
+
end
|
106
|
+
|
107
|
+
# @return [String]
|
108
|
+
def stimulus_controller_class_name
|
109
|
+
file_path.gsub('/', '_').camelize
|
110
|
+
end
|
76
111
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
// Read more about Stimulus here https://stimulus.hotwired.dev/
|
4
|
+
export default class extends Controller {
|
5
|
+
connect() {
|
6
|
+
console.log("Stimulus controller '<%= stimulus_controller_id %>' is connected!")
|
7
|
+
}
|
8
|
+
|
9
|
+
greet() {
|
10
|
+
alert("Hi there!")
|
11
|
+
}
|
12
|
+
}
|
@@ -1,9 +1,13 @@
|
|
1
|
-
.<%= singular_table_name %>
|
1
|
+
.<%= singular_table_name %><%= %({ data: { controller: "#{stimulus_controller_id}" } }) if stimulus? %>
|
2
2
|
%h1
|
3
3
|
Hello from
|
4
4
|
%b
|
5
5
|
<%= class_name %>
|
6
6
|
, initialized at:
|
7
7
|
= @time
|
8
|
+
<%- if stimulus? %>
|
9
|
+
%button{ data: { action: "click-><%= stimulus_controller_id %>#greet" }
|
10
|
+
Greet me
|
11
|
+
<% end %>
|
8
12
|
%p
|
9
13
|
= description
|
@@ -1,7 +1,10 @@
|
|
1
|
-
<div class=
|
1
|
+
<div class="<%= singular_table_name %>"<%= %( data-controller="#{stimulus_controller_id}") if stimulus? %>>
|
2
2
|
<h1>
|
3
3
|
Hello from <b><%= class_name %></b>, initialized at: <%%= @time %>
|
4
4
|
</h1>
|
5
|
+
<%- if stimulus? %>
|
6
|
+
<button data-action="click-><%= stimulus_controller_id %>#greet">Greet me</button>
|
7
|
+
<% end %>
|
5
8
|
<p>
|
6
9
|
<%%= description %>
|
7
10
|
</p>
|
@@ -1,6 +1,10 @@
|
|
1
|
-
div.<%= singular_table_name %>
|
1
|
+
div.<%= singular_table_name %><%= %( data-controller="#{stimulus_controller_id}") if stimulus? %>
|
2
2
|
h1
|
3
3
|
| Hello from
|
4
4
|
b <%= class_name %>
|
5
5
|
| , initialized at: #{@time}
|
6
|
+
<%- if stimulus? %>
|
7
|
+
button data-action="click-><%= stimulus_controller_id %>#greet"
|
8
|
+
| Greet me
|
9
|
+
<% end %>
|
6
10
|
p = description
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: amber_component
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ruby-Amber
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date: 2022-11-
|
13
|
+
date: 2022-11-13 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: actionview
|
@@ -106,11 +106,13 @@ files:
|
|
106
106
|
- README.md
|
107
107
|
- Rakefile
|
108
108
|
- amber_component.gemspec
|
109
|
+
- assets/javascripts/amber_component/stimulus_loading.js
|
109
110
|
- banner.png
|
110
111
|
- icon.png
|
111
112
|
- lib/amber_component.rb
|
112
113
|
- lib/amber_component/assets.rb
|
113
114
|
- lib/amber_component/base.rb
|
115
|
+
- lib/amber_component/configuration.rb
|
114
116
|
- lib/amber_component/helpers.rb
|
115
117
|
- lib/amber_component/helpers/class_helper.rb
|
116
118
|
- lib/amber_component/helpers/component_helper.rb
|
@@ -133,6 +135,7 @@ files:
|
|
133
135
|
- lib/generators/component_generator.rb
|
134
136
|
- lib/generators/templates/component.rb.erb
|
135
137
|
- lib/generators/templates/component_test.rb.erb
|
138
|
+
- lib/generators/templates/controller.js.erb
|
136
139
|
- lib/generators/templates/style.css.erb
|
137
140
|
- lib/generators/templates/style.sass.erb
|
138
141
|
- lib/generators/templates/style.scss.erb
|