amber_component 0.0.2 → 0.0.3
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/.rubocop.yml +1 -1
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -1
- data/CONTRIBUTING.md +87 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +16 -91
- data/README.md +25 -42
- data/Rakefile +17 -1
- data/amber_component.gemspec +4 -2
- data/banner.png +0 -0
- data/docs/.bundle/config +2 -0
- data/docs/.gitignore +5 -0
- data/docs/404.html +25 -0
- data/docs/Gemfile +37 -0
- data/docs/Gemfile.lock +89 -0
- data/docs/README.md +19 -0
- data/docs/_config.yml +148 -0
- data/docs/_data/amber_component.yml +3 -0
- data/docs/_sass/_variables.scss +2 -0
- data/docs/_sass/color_schemes/amber_component.scss +11 -0
- data/docs/_sass/custom/custom.scss +60 -0
- data/docs/api/index.md +8 -0
- data/docs/assets/images/logo_wide.png +0 -0
- data/docs/changelog/index.md +8 -0
- data/docs/favicon.ico +0 -0
- data/docs/getting_started/index.md +8 -0
- data/docs/getting_started/installation.md +7 -0
- data/docs/getting_started/ruby_support.md +7 -0
- data/docs/getting_started/wireframes.md +7 -0
- data/docs/index.md +17 -0
- data/docs/introduction/basic_usage.md +7 -0
- data/docs/introduction/index.md +8 -0
- data/docs/introduction/why_amber_component.md +7 -0
- data/docs/resources/index.md +8 -0
- data/docs/styles/index.md +8 -0
- data/docs/styles/usage.md +7 -0
- data/docs/views/index.md +8 -0
- data/docs/views/usage.md +7 -0
- data/icon.png +0 -0
- data/lib/amber_component/assets.rb +59 -0
- data/lib/amber_component/base.rb +66 -434
- data/lib/amber_component/helpers/class_helper.rb +22 -0
- data/lib/amber_component/helpers/component_helper.rb +18 -0
- data/lib/amber_component/helpers/css_helper.rb +25 -0
- data/lib/amber_component/helpers.rb +11 -0
- data/lib/amber_component/railtie.rb +21 -0
- data/lib/amber_component/rendering.rb +53 -0
- data/lib/amber_component/template_handler/erb.rb +17 -0
- data/lib/amber_component/template_handler.rb +26 -0
- data/lib/amber_component/version.rb +1 -1
- data/lib/amber_component/views.rb +198 -0
- data/lib/amber_component.rb +6 -2
- data/lib/generators/amber_component/install_generator.rb +23 -0
- data/lib/generators/amber_component/templates/application_component.rb +13 -0
- data/lib/generators/amber_component_generator.rb +24 -0
- data/lib/generators/templates/component.rb.erb +9 -0
- data/lib/generators/templates/component_test.rb.erb +9 -0
- data/lib/generators/templates/style.css.erb +3 -0
- data/lib/generators/templates/view.html.erb +3 -0
- metadata +85 -19
- data/.kanbn/index.md +0 -23
- data/.kanbn/tasks/add-instance-variables-to-view-when-block-given-markdown.md +0 -9
- data/.kanbn/tasks/bind-clas-to-action-view-method-call-example-component-data-data-without-calling-any-method.md +0 -9
- data/.kanbn/tasks/bind-scoped-css-to-head-of-doc.md +0 -10
- data/.kanbn/tasks/check-if-we-need-full-rails-gem-pack.md +0 -9
- data/.kanbn/tasks/simple-proto.md +0 -10
- data/.kanbn/tasks/verify-if-template-is-haml-or-erb.md +0 -9
- data/PLANS.md +0 -17
- data/lib/amber_component/helper.rb +0 -8
- data/lib/amber_component/style_injector.rb +0 -52
- data/sig/amber_components.rbs +0 -4
data/lib/amber_component/base.rb
CHANGED
@@ -3,226 +3,102 @@
|
|
3
3
|
require 'set'
|
4
4
|
require 'erb'
|
5
5
|
require 'tilt'
|
6
|
-
require 'active_model/callbacks'
|
7
6
|
require 'memery'
|
7
|
+
require 'active_model/callbacks'
|
8
|
+
require 'action_view'
|
8
9
|
|
9
|
-
require_relative './style_injector'
|
10
|
-
|
11
|
-
# Abstract class which serves as a base
|
12
|
-
# for all Amber Components.
|
13
|
-
#
|
14
|
-
# There are a few life cycle callbacks that can be defined.
|
15
|
-
# The same way as in `ActiveRecord` models and `ActionController` controllers.
|
16
|
-
#
|
17
|
-
# - before_render
|
18
|
-
# - around_render
|
19
|
-
# - after_render
|
20
|
-
# - before_initialize
|
21
|
-
# - around_initialize
|
22
|
-
# - after_initialize
|
23
|
-
#
|
24
|
-
# class ButtonComponent < ::AmberComponent::Base
|
25
|
-
# # You can provide a Symbol of the method that should be called
|
26
|
-
# before_render :before_render_method
|
27
|
-
# # Or provide a block that will be executed
|
28
|
-
# after_initialize do
|
29
|
-
# # Your code here
|
30
|
-
# end
|
31
|
-
#
|
32
|
-
# def before_render_method
|
33
|
-
# # Your code here
|
34
|
-
# end
|
35
|
-
# end
|
36
|
-
#
|
37
|
-
#
|
38
|
-
# @abstract Create a subclass to define a new component.
|
39
10
|
module ::AmberComponent
|
40
|
-
class
|
11
|
+
# Abstract class which serves as a base
|
12
|
+
# for all Amber Components.
|
13
|
+
#
|
14
|
+
# There are a few life cycle callbacks that can be defined.
|
15
|
+
# The same way as in `ActiveRecord` models and `ActionController` controllers.
|
16
|
+
#
|
17
|
+
# - before_render
|
18
|
+
# - around_render
|
19
|
+
# - after_render
|
20
|
+
# - before_initialize
|
21
|
+
# - around_initialize
|
22
|
+
# - after_initialize
|
23
|
+
#
|
24
|
+
# class ButtonComponent < ::AmberComponent::Base
|
25
|
+
# # You can provide a Symbol of the method that should be called
|
26
|
+
# before_render :before_render_method
|
27
|
+
# # Or provide a block that will be executed
|
28
|
+
# after_initialize do
|
29
|
+
# # Your code here
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# def before_render_method
|
33
|
+
# # Your code here
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
#
|
38
|
+
# @abstract Create a subclass to define a new component.
|
39
|
+
class Base < ::ActionView::Base
|
40
|
+
# for defining callback such as `after_initialize`
|
41
41
|
extend ::ActiveModel::Callbacks
|
42
|
+
extend Helpers::ClassHelper
|
42
43
|
|
43
|
-
include
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
# View types with built-in embedded Ruby
|
51
|
-
#
|
52
|
-
# @return [Set<Symbol>]
|
53
|
-
VIEW_TYPES_WITH_RUBY = ::Set[:erb, :haml, :slim].freeze
|
54
|
-
# @return [Set<Symbol>]
|
55
|
-
ALLOWED_VIEW_TYPES = ::Set[:erb, :haml, :slim, :html, :md, :markdown].freeze
|
56
|
-
# @return [Set<Symbol>]
|
57
|
-
ALLOWED_STYLE_TYPES = ::Set[:sass, :scss, :less].freeze
|
44
|
+
include Helpers::CssHelper
|
45
|
+
include Views::InstanceMethods
|
46
|
+
extend Views::ClassMethods
|
47
|
+
include Assets::InstanceMethods
|
48
|
+
extend Assets::ClassMethods
|
49
|
+
include Rendering::InstanceMethods
|
50
|
+
extend Rendering::ClassMethods
|
58
51
|
|
59
52
|
class << self
|
60
53
|
include ::Memery
|
61
54
|
|
62
|
-
|
63
|
-
# @return [String]
|
64
|
-
def run(**kwargs, &block)
|
65
|
-
comp = new(**kwargs)
|
66
|
-
|
67
|
-
comp.render(&block)
|
68
|
-
end
|
69
|
-
|
70
|
-
alias call run
|
71
|
-
|
72
|
-
|
73
|
-
# @return [String]
|
74
|
-
memoize def asset_dir_path
|
75
|
-
class_const_name = name.split('::').last
|
76
|
-
component_file_path, = module_parent.const_source_location(class_const_name)
|
77
|
-
|
78
|
-
component_file_path.delete_suffix('.rb')
|
79
|
-
end
|
80
|
-
|
81
|
-
# @return [String]
|
82
|
-
def view_path
|
83
|
-
asset_path view_file_name
|
84
|
-
end
|
85
|
-
|
86
|
-
# @return [String, nil]
|
87
|
-
def view_file_name
|
88
|
-
files = asset_file_names(VIEW_FILE_REGEXP)
|
89
|
-
raise MultipleViews, "More than one view file for `#{name}` found!" if files.length > 1
|
90
|
-
|
91
|
-
files.first
|
92
|
-
end
|
93
|
-
|
94
|
-
# @return [Symbol]
|
95
|
-
def view_type
|
96
|
-
(view_file_name.split('.')[1..].reject { _1.match?(/erb/) }.last || 'erb')&.to_sym
|
97
|
-
end
|
98
|
-
|
99
|
-
# @return [String]
|
100
|
-
def style_path
|
101
|
-
asset_path style_file_name
|
102
|
-
end
|
103
|
-
|
104
|
-
# @return [String, nil]
|
105
|
-
def style_file_name
|
106
|
-
files = asset_file_names(STYLE_FILE_REGEXP)
|
107
|
-
raise MultipleStyles, "More than one style file for `#{name}` found!" if files.length > 1
|
108
|
-
|
109
|
-
files.first
|
110
|
-
end
|
111
|
-
|
112
|
-
# @return [Symbol]
|
113
|
-
def style_type
|
114
|
-
(style_file_name.split('.')[1..].reject { _1.match?(/erb/) }.last || 'erb')&.to_sym
|
115
|
-
end
|
55
|
+
memoize :asset_dir_path
|
116
56
|
|
117
57
|
# Memoize these methods in production
|
118
|
-
if
|
58
|
+
if ::ENV['RAILS_ENV'] == 'production'
|
119
59
|
memoize :view_path
|
120
60
|
memoize :view_file_name
|
121
61
|
memoize :view_type
|
122
|
-
|
123
|
-
memoize :style_path
|
124
|
-
memoize :style_file_name
|
125
|
-
memoize :style_type
|
126
|
-
end
|
127
|
-
|
128
|
-
# Register an inline view by returning a String from the passed block.
|
129
|
-
#
|
130
|
-
# Usage:
|
131
|
-
#
|
132
|
-
# view do
|
133
|
-
# <<~ERB
|
134
|
-
# <h1>
|
135
|
-
# Hello <%= @name %>
|
136
|
-
# </h1>
|
137
|
-
# ERB
|
138
|
-
# end
|
139
|
-
#
|
140
|
-
# or:
|
141
|
-
#
|
142
|
-
# view :haml do
|
143
|
-
# <<~HAML
|
144
|
-
# %h1
|
145
|
-
# Hello
|
146
|
-
# = @name
|
147
|
-
# HAML
|
148
|
-
# end
|
149
|
-
#
|
150
|
-
# @param type [Symbol]
|
151
|
-
# @return [void]
|
152
|
-
def view(type = :erb, &block)
|
153
|
-
@method_view = TypedContent.new(type: type, content: block)
|
154
|
-
end
|
155
|
-
|
156
|
-
# ERB/Haml/Slim view registered through the `view` method.
|
157
|
-
#
|
158
|
-
# @return [TypedContent]
|
159
|
-
attr_reader :method_view
|
160
|
-
|
161
|
-
# Register an inline style by returning a String from the passed block.
|
162
|
-
#
|
163
|
-
# Usage:
|
164
|
-
#
|
165
|
-
# style do
|
166
|
-
# '.my-class { color: red; }'
|
167
|
-
# end
|
168
|
-
#
|
169
|
-
# or:
|
170
|
-
#
|
171
|
-
# style :sass do
|
172
|
-
# <<~SASS
|
173
|
-
# .my-class
|
174
|
-
# color: red
|
175
|
-
# SASS
|
176
|
-
# end
|
177
|
-
#
|
178
|
-
# @param type [Symbol]
|
179
|
-
# @return [void]
|
180
|
-
def style(type = :css, &block)
|
181
|
-
@method_style = TypedContent.new(type: type, content: block)
|
182
62
|
end
|
183
63
|
|
184
|
-
# CSS/SCSS/Sass styles registered through the `style` method.
|
185
|
-
#
|
186
|
-
# @return [TypedContent]
|
187
|
-
attr_reader :method_style
|
188
|
-
|
189
64
|
private
|
190
65
|
|
191
66
|
# @param subclass [Class]
|
192
67
|
# @return [void]
|
193
68
|
def inherited(subclass)
|
69
|
+
super
|
194
70
|
# @type [Module]
|
195
|
-
parent_module = subclass.module_parent
|
196
71
|
method_body = proc do |**kwargs, &block|
|
197
|
-
subclass.
|
72
|
+
subclass.render(**kwargs, &block)
|
198
73
|
end
|
74
|
+
parent_module = subclass.module_parent
|
199
75
|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
# @return [String, nil]
|
207
|
-
def asset_path(file_name)
|
208
|
-
return unless file_name
|
76
|
+
if parent_module.equal?(::Object)
|
77
|
+
method_name = subclass.name
|
78
|
+
define_helper_method(subclass, Helpers::ComponentHelper, method_name, method_body)
|
79
|
+
define_helper_method(subclass, Helpers::ComponentHelper, method_name.underscore, method_body)
|
80
|
+
return
|
81
|
+
end
|
209
82
|
|
210
|
-
|
83
|
+
method_name = subclass.const_name
|
84
|
+
define_helper_method(subclass, parent_module.singleton_class, method_name, method_body)
|
85
|
+
define_helper_method(subclass, parent_module.singleton_class, method_name.underscore, method_body)
|
211
86
|
end
|
212
87
|
|
213
|
-
#
|
214
|
-
#
|
215
|
-
#
|
216
|
-
# @param
|
217
|
-
|
218
|
-
|
219
|
-
return [] unless ::File.directory?(asset_dir_path)
|
88
|
+
# @param component [Class]
|
89
|
+
# @param mod [Module, Class]
|
90
|
+
# @param method_name [String, Symbol]
|
91
|
+
# @param body [Proc]
|
92
|
+
def define_helper_method(component, mod, method_name, body)
|
93
|
+
mod.define_method(method_name, &body)
|
220
94
|
|
221
|
-
::
|
222
|
-
next unless ::File.file?(::File.join(asset_dir_path, file))
|
95
|
+
return if ::ENV['RAILS_ENV'] == 'production'
|
223
96
|
|
224
|
-
|
225
|
-
|
97
|
+
::Warning.warn <<~WARN if mod.instance_methods.include?(method_name)
|
98
|
+
#{caller(0, 1).first}: warning:
|
99
|
+
`#{component}` shadows the name of an already existing `#{mod}` method `#{method_name}`.
|
100
|
+
Consider renaming this component, because the original method will be overwritten.
|
101
|
+
WARN
|
226
102
|
end
|
227
103
|
end
|
228
104
|
|
@@ -235,20 +111,6 @@ module ::AmberComponent
|
|
235
111
|
end
|
236
112
|
end
|
237
113
|
|
238
|
-
# @return [String]
|
239
|
-
def render(&block)
|
240
|
-
run_callbacks :render do
|
241
|
-
element = inject_views(&block)
|
242
|
-
styles = inject_styles
|
243
|
-
element += styles unless styles.nil?
|
244
|
-
element.html_safe
|
245
|
-
end
|
246
|
-
end
|
247
|
-
|
248
|
-
def render_in(context)
|
249
|
-
byebug
|
250
|
-
end
|
251
|
-
|
252
114
|
private
|
253
115
|
|
254
116
|
# @param kwargs [Hash{Symbol => Object}]
|
@@ -258,235 +120,5 @@ module ::AmberComponent
|
|
258
120
|
instance_variable_set("@#{key}", value)
|
259
121
|
end
|
260
122
|
end
|
261
|
-
|
262
|
-
# Helper method to render view from string or with other provided type.
|
263
|
-
#
|
264
|
-
# Usage:
|
265
|
-
#
|
266
|
-
# render_custom_view('<h1>Hello World</h1>')
|
267
|
-
#
|
268
|
-
# or:
|
269
|
-
#
|
270
|
-
# render_custom_view content: '**Hello World**', type: 'md'
|
271
|
-
#
|
272
|
-
# @param style [TypedContent, Hash{Symbol => String, Symbol, Proc}, String]
|
273
|
-
# @return [String, nil]
|
274
|
-
def render_custom_view(view, &block)
|
275
|
-
return '' unless view
|
276
|
-
return view if view.is_a?(::String)
|
277
|
-
|
278
|
-
view = TypedContent.wrap(view)
|
279
|
-
type = view.type
|
280
|
-
content = view.to_s
|
281
|
-
|
282
|
-
if content.empty?
|
283
|
-
raise EmptyView, <<~ERR.squish
|
284
|
-
Custom view for `#{self.class}` from view method cannot be empty!
|
285
|
-
ERR
|
286
|
-
end
|
287
|
-
|
288
|
-
unless ALLOWED_VIEW_TYPES.include? type
|
289
|
-
raise UnknownViewType, <<~ERR.squish
|
290
|
-
Unknown view type for `#{self.class}` from view method!
|
291
|
-
Check return value of param type in `view :[type] do`
|
292
|
-
ERR
|
293
|
-
end
|
294
|
-
|
295
|
-
unless VIEW_TYPES_WITH_RUBY.include? type
|
296
|
-
# first render the content with ERB if the
|
297
|
-
# type does not support embedding Ruby by default
|
298
|
-
content = render_string(content, :erb, block)
|
299
|
-
end
|
300
|
-
|
301
|
-
render_string(content, type, block)
|
302
|
-
end
|
303
|
-
|
304
|
-
# @return [String]
|
305
|
-
def render_view_from_file(&block)
|
306
|
-
view_path = self.class.view_path
|
307
|
-
return '' if view_path.nil? || !::File.file?(view_path)
|
308
|
-
|
309
|
-
content = ::File.read(view_path)
|
310
|
-
type = self.class.view_type
|
311
|
-
|
312
|
-
unless VIEW_TYPES_WITH_RUBY.include? type
|
313
|
-
content = render_string(content, :erb, block)
|
314
|
-
end
|
315
|
-
|
316
|
-
render_string(content, type, block)
|
317
|
-
end
|
318
|
-
|
319
|
-
# Method returning view from method in class file.
|
320
|
-
# Usage:
|
321
|
-
#
|
322
|
-
# view do
|
323
|
-
# <<~HTML
|
324
|
-
# <h1>
|
325
|
-
# Hello <%= @name %>
|
326
|
-
# </h1>
|
327
|
-
# HTML
|
328
|
-
# end
|
329
|
-
#
|
330
|
-
# or:
|
331
|
-
#
|
332
|
-
# view :haml do
|
333
|
-
# <<~HAML
|
334
|
-
# %h1
|
335
|
-
# Hello
|
336
|
-
# = @name
|
337
|
-
# HAML
|
338
|
-
# end
|
339
|
-
#
|
340
|
-
# @return [String]
|
341
|
-
def render_class_method_view(&block)
|
342
|
-
render_custom_view(self.class.method_view, &block)
|
343
|
-
end
|
344
|
-
|
345
|
-
# Method returning view from params in view.
|
346
|
-
# Usage:
|
347
|
-
#
|
348
|
-
# <%= ExampleComponent data: data, view: "<h1>Hello #{@name}</h1>" %>
|
349
|
-
#
|
350
|
-
# or:
|
351
|
-
#
|
352
|
-
# <%= ExampleComponent data: data, view: { content: "<h1>Hello #{@name}</h1>", type: 'erb' } %>
|
353
|
-
#
|
354
|
-
# @return [String]
|
355
|
-
def render_view_from_inline(&block)
|
356
|
-
data = \
|
357
|
-
if @view.is_a? String
|
358
|
-
TypedContent.new(
|
359
|
-
type: :erb,
|
360
|
-
content: @view
|
361
|
-
)
|
362
|
-
else
|
363
|
-
@view
|
364
|
-
end
|
365
|
-
|
366
|
-
render_custom_view(data, &block)
|
367
|
-
end
|
368
|
-
|
369
|
-
# @return [String]
|
370
|
-
def inject_views(&block)
|
371
|
-
view_from_file = render_view_from_file(&block)
|
372
|
-
view_from_method = render_class_method_view(&block)
|
373
|
-
view_from_inline = render_view_from_inline(&block)
|
374
|
-
|
375
|
-
view_content = view_from_file unless view_from_file.empty?
|
376
|
-
view_content = view_from_method unless view_from_method.empty?
|
377
|
-
view_content = view_from_inline unless view_from_inline.empty?
|
378
|
-
|
379
|
-
if view_content.nil? || view_content.empty?
|
380
|
-
raise ViewFileNotFound, "View for `#{self.class}` could not be found!"
|
381
|
-
end
|
382
|
-
|
383
|
-
view_content
|
384
|
-
end
|
385
|
-
|
386
|
-
# Helper method to render style from css string or with other provided type.
|
387
|
-
#
|
388
|
-
# Usage:
|
389
|
-
#
|
390
|
-
# render_custom_style('.my-class { color: red; }')
|
391
|
-
#
|
392
|
-
# or:
|
393
|
-
#
|
394
|
-
# render_custom_style style: '.my-class { color: red; }', type: 'sass'
|
395
|
-
#
|
396
|
-
# @param style [TypedContent, Hash{Symbol => Symbol, String, Proc}, String]
|
397
|
-
# @return [String, nil]
|
398
|
-
def render_custom_style(style)
|
399
|
-
return '' unless style
|
400
|
-
return style if style.is_a?(::String)
|
401
|
-
|
402
|
-
style = TypedContent.wrap(style)
|
403
|
-
type = style.type
|
404
|
-
content = style.to_s
|
405
|
-
|
406
|
-
if content.empty?
|
407
|
-
raise EmptyStyle, <<~ERR.squish
|
408
|
-
Custom style for `#{self.class}` from style method cannot be empty!
|
409
|
-
ERR
|
410
|
-
end
|
411
|
-
|
412
|
-
unless ALLOWED_STYLE_TYPES.include? type
|
413
|
-
raise UnknownStyleType, <<~ERR.squish
|
414
|
-
Unknown style type for `#{self.class}` from style method!
|
415
|
-
Check return value of param type in `style :[type] do`
|
416
|
-
ERR
|
417
|
-
end
|
418
|
-
|
419
|
-
# first render the content with ERB
|
420
|
-
content = render_string(content, :erb)
|
421
|
-
|
422
|
-
render_string(content, type)
|
423
|
-
end
|
424
|
-
|
425
|
-
# Method returning style from file (style.(css|sass|scss|less)) if exists.
|
426
|
-
#
|
427
|
-
# @return [String]
|
428
|
-
def render_style_from_file
|
429
|
-
style_path = self.class.style_path
|
430
|
-
return '' unless style_path
|
431
|
-
|
432
|
-
content = ::File.read(style_path)
|
433
|
-
type = self.class.style_type
|
434
|
-
|
435
|
-
return content if type == :css
|
436
|
-
|
437
|
-
content = render_string(content, :erb)
|
438
|
-
render_string(content, type)
|
439
|
-
end
|
440
|
-
|
441
|
-
# Method returning style from method in class file.
|
442
|
-
# Usage:
|
443
|
-
#
|
444
|
-
# style do
|
445
|
-
# '.my-class { color: red; }'
|
446
|
-
# end
|
447
|
-
#
|
448
|
-
# or:
|
449
|
-
#
|
450
|
-
# style :sass do
|
451
|
-
# <<~SASS
|
452
|
-
# .my-class
|
453
|
-
# color: red
|
454
|
-
# SASS
|
455
|
-
# end
|
456
|
-
#
|
457
|
-
# @return [String]
|
458
|
-
def render_class_method_style
|
459
|
-
render_custom_style(self.class.method_style)
|
460
|
-
end
|
461
|
-
|
462
|
-
# Method returning style from params in view.
|
463
|
-
# Usage:
|
464
|
-
#
|
465
|
-
# <%= ExampleComponent data: data, style: '.my-class { color: red; }' %>
|
466
|
-
#
|
467
|
-
# or:
|
468
|
-
#
|
469
|
-
# <%= ExampleComponent data: data, style: {style: '.my-class { color: red; }', type: 'sass'} %>
|
470
|
-
#
|
471
|
-
# @return [String]
|
472
|
-
def render_style_from_inline
|
473
|
-
render_custom_style(@style)
|
474
|
-
end
|
475
|
-
|
476
|
-
# @param content [String]
|
477
|
-
# @param type [Symbol]
|
478
|
-
# @param block [Proc, nil]
|
479
|
-
# @return [String]
|
480
|
-
def render_string(content, type, block = nil)
|
481
|
-
::Tilt[type].new { content }.render(self, &block)
|
482
|
-
end
|
483
|
-
|
484
|
-
# @return [String]
|
485
|
-
def inject_styles
|
486
|
-
style_content = render_style_from_file + render_class_method_style + render_style_from_inline
|
487
|
-
return if style_content.empty?
|
488
|
-
|
489
|
-
::AmberComponent::StyleInjector.inject(style_content)
|
490
|
-
end
|
491
123
|
end
|
492
124
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ::AmberComponent
|
4
|
+
module Helpers
|
5
|
+
# Adds class-specific utilities.
|
6
|
+
module ClassHelper
|
7
|
+
# Name of the constant this class/module is saved in (in the parent module).
|
8
|
+
#
|
9
|
+
# @return [String]
|
10
|
+
def const_name
|
11
|
+
name.split('::').last
|
12
|
+
end
|
13
|
+
|
14
|
+
# Get the exact place where this class/module has been defined.
|
15
|
+
#
|
16
|
+
# @return [Array(String, Integer), Array(Boolean, Integer)] File path followed by line number.
|
17
|
+
def source_location
|
18
|
+
module_parent.const_source_location const_name
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'action_view'
|
4
|
+
|
5
|
+
module ::AmberComponent
|
6
|
+
module Helpers
|
7
|
+
# Contains methods for quickly rendering
|
8
|
+
# components defined under the root namespace `Object`.
|
9
|
+
module ComponentHelper
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class ::ActionView::Base
|
15
|
+
# Add those convenience methods to all
|
16
|
+
# controllers and views.
|
17
|
+
include ::AmberComponent::Helpers::ComponentHelper
|
18
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'action_view'
|
4
|
+
|
5
|
+
module ::AmberComponent
|
6
|
+
module Helpers
|
7
|
+
# Adds a few utility methods for working with CSS
|
8
|
+
# inside components.
|
9
|
+
module CssHelper
|
10
|
+
# Helper method which creates a name for a css class or id
|
11
|
+
# which is scoped to the current component class.
|
12
|
+
#
|
13
|
+
# self.class #=> Navigation::DropdownMenuComponent
|
14
|
+
# css_id(:list_item) #=> "navigation-dropdown_menu_component--list_item"
|
15
|
+
#
|
16
|
+
# @param name [String, Symbol]
|
17
|
+
# @return [String]
|
18
|
+
def css_identifier(name)
|
19
|
+
"#{self.class.name.underscore.gsub('/', '-')}--#{name.to_s.underscore}"
|
20
|
+
end
|
21
|
+
|
22
|
+
alias css_id css_identifier
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ::AmberComponent
|
4
|
+
# Contains all helpers added by `amber_component`
|
5
|
+
module Helpers
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
require_relative 'helpers/component_helper'
|
10
|
+
require_relative 'helpers/class_helper'
|
11
|
+
require_relative 'helpers/css_helper'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ::AmberComponent
|
4
|
+
# Class which hooks into Rails
|
5
|
+
# and configures the application.
|
6
|
+
class Railtie < ::Rails::Railtie
|
7
|
+
initializer 'amber_component.initialization' do |app|
|
8
|
+
app.config.assets.paths << (app.root / 'app' / 'components')
|
9
|
+
|
10
|
+
next if ::Rails.env.production?
|
11
|
+
|
12
|
+
components_root = app.root / 'app' / 'components'
|
13
|
+
component_paths = ::Dir[components_root / '**' / '*.rb']
|
14
|
+
app.config.eager_load_paths += component_paths
|
15
|
+
|
16
|
+
::ActiveSupport::Reloader.to_prepare do
|
17
|
+
component_paths.each { |file| require_dependency(components_root / file) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ::AmberComponent
|
4
|
+
# Provides universal methods for rendering components.
|
5
|
+
module Rendering
|
6
|
+
# Class methods for rendering.
|
7
|
+
module ClassMethods
|
8
|
+
# @param kwargs [Hash{Symbol => Object}]
|
9
|
+
# @return [String]
|
10
|
+
def render(**kwargs, &block)
|
11
|
+
comp = new(**kwargs)
|
12
|
+
|
13
|
+
comp.render(&block)
|
14
|
+
end
|
15
|
+
|
16
|
+
alias call render
|
17
|
+
end
|
18
|
+
|
19
|
+
# Instance methods for rendering.
|
20
|
+
module InstanceMethods
|
21
|
+
# @return [String]
|
22
|
+
def render(&block)
|
23
|
+
run_callbacks :render do
|
24
|
+
element = render_view(&block)
|
25
|
+
# styles = inject_styles
|
26
|
+
# element += styles unless styles.nil?
|
27
|
+
element.html_safe
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Method used internally by Rails to render an object
|
32
|
+
# passed to the `render` method.
|
33
|
+
#
|
34
|
+
# render MyComponent.new(some: :attribute)
|
35
|
+
#
|
36
|
+
# @param _context [ActionView::Base]
|
37
|
+
# @return [String]
|
38
|
+
def render_in(_context)
|
39
|
+
render
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
# @param content [String]
|
45
|
+
# @param type [Symbol]
|
46
|
+
# @param block [Proc, nil]
|
47
|
+
# @return [String]
|
48
|
+
def render_string(content, type, block = nil)
|
49
|
+
TemplateHandler.render_from_string(self, content, type, block)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|