VcShortcut Gem
VcShortcut simplifies the API for rendering ViewComponents and Phlex components in Ruby on Rails applications, reducing verbosity and streamlining component usage.
It also features caching for lookups, ensuring that it operates with minimal overhead and maximal speed.
<%# Instead of: %>
<%= render Admin::Dashboard::TabsComponent.new(style: :compact) do |tabs| %>
<% tabs.with_tab('Sales') { ... } %>
<% tabs.with_divider %>
<% tabs.with_tab('Settings') { ... } %>
<% end %>
<%= render ButtonComponent.new('Sign up', '/sign_up') %>
<%= render Wysiwyg::Toolbar::FontSelectComponent.new %>
<%# You can now also do: %>
<%= vc.button('Sign up', '/sign_up') %>
<%= vc.wysiwyg.toolbar.font_select %>
<%= vc.admin.dashboard.tabs(style: :compact) do |tabs| %>
<% tabs.with_tab('Sales') { ... } %>
<% tabs.with_divider %>
<% tabs.with_tab('Settings') { ... } %>
<% end %>
It works out of the box with just a bundle add vc_shortcut
, and is also highly customizable.
By default, two shortcuts are set up: vc
for rendering and vci
for instantiating components. You can change the shortcut prefix.
For more advanced use-cases, it also allows you to change the lookup logic as well as setup custom shortcuts.
Installation
Add vc_shortcut
to your Gemfile:
bundle add vc_shortcut
Usage
If you're using ViewComponent or Phlex in your Rails app and your component class names end with Component
or ::Component
(which should be the standard), there's nothing else you need to do.
By default, two shortcuts are set up: vc
for rendering and vci
for instantiating components.
You can start using these helpers from any views or from your components.
Example:
### Instead of:
render Wysiwyg::UploadFieldComponent.new(limit: 50.megabytes) do |upload_field|
upload_field. do
render IconComponent.new('file')
end
end
### You can now also do:
vc.wysiwyg.upload_field(limit: 50.megabytes) do |upload_field|
upload_field. do
vc.icon('file')
end
end
### Instead of:
instance = ProgressBarComponent.new(progress: 75)
### You can now also do:
instance = vci.(progress: 75)
Interested in a powerful Rails UI library?
I am working on a super-powerful Rails UI library - components as well as templates & patterns.
Please check this out if you're interested.
Advanced
You can customize things by creating an initializer file at config/initializers/vc_shortcut.rb
.
Customize name of default shortcuts
You can rename the default shortcuts or disable them entirely:
# In config/initializers/vc_shortcut.rb:
VcShortcut.render_shortcut = :vc # Or whatever you prefer
VcShortcut.instantiate_shortcut = :vci # Or whatever you prefer
You can disable a shortcut by setting its value to false
.
Custom Component Lookup
By default, we assume your component class names end with Component
or ::Component
, which is the standard and should cover the majority of cases.
However, if you're doing something non-standard, you can customize the logic that finds a component:
# In config/initializers/vc_shortcut.rb:
VcShortcut.find_component = ->(camelized_name) {
"#{camelized_name}Component".safe_constantize || "#{camelized_name}::Component".safe_constantize
}
For example, if all your components are namespaced under Polaris
and are suffixed with Primitive
instead of Component
, you can set:
# In config/initializers/vc_shortcut.rb:
VcShortcut.find_component = ->(camelized_name) {
"Polaris::#{camelized_name}Primitive".safe_constantize
}
## So instead of:
# `render Polaris::Admin::NavbarPrimitive.new`
# You can now also do:
# `vc.admin.navbar`
Super Advanced
You can take customization one level further if needed:
Registering Additional Custom Shortcuts
# In config/initializers/vc_shortcut.rb:
VcShortcut.register :admin,
find_component: ->(camelized_name) {
"Ui::Admin::#{camelized_name}::Component".safe_constantize
},
process: ->(context) {
context.view_context.render(
context.component.new(*context.call_args, **context.call_kwargs),
&context.call_block
)
}
## So instead of:
# `render Ui::Admin::Navbar::Component.new`
# You can now also do:
# `admin.navbar`
Taking Full Control
You can customize the find process even more by specifying find
instead of find_component
:
# In config/initializers/vc_shortcut.rb:
VcShortcut.register :admin,
find: ->(context) {
# If you return :has_more, we'll assume there's another component coming up in the chain.
# If you return a non-nil value, we'll assume this is the leaf component and move on to call `process`.
# If you return nil, we'll assume nothing was found for the given chain and raise an error.
chain_camelized = context.chain_camelized
component = "Ui::#{chain_camelized}::Component".safe_constantize
next component if component
:has_more if chain_camelized.safe_constantize
},
process: ->(context) {
context.view_context.render(
context.component.new(*context.call_args, **context.call_kwargs),
&context.call_block
)
}
## So instead of:
# `render Ui::Admin::Navbar::Component.new`
# You can now also do:
# `admin.navbar`
The return value of the register
call is a module that you can include in places where you wish to make the helper available. It's, however, automatically included for all your views, view components, and phlex components.
What is context
in the above examples?
context
is provided to the find
and process
proc. It's an object that responds to the following methods:
1. context.chain
:
Chain until now. E.g., if you call vc.admin.button
:
- When processing :admin, chain will be [:admin].
- Then, when processing :button, chain will be [:admin, :button].
2. context.chain_camelized
:
Just does context.chain.join('/').camelize
. So, if chain was [:admin, :buttton], it'd return Admin::Button
.
3. context.component
:
Only set when called in the process
proc. It is the component returned by the find
or find_component
proc.
4. context.call_args
context.call_kwargs
, context.call_block
:
Only set when called in the process
proc. These are the arguments used when calling the shortcut. E.g.:
vc.admin.navbar('Treact', size: :sm, style: :compact) do |navbar|
navbar.with_menu_item(...)
end
Here, context.call_args
will be ['Treact']
, context.call_kwargs
will be { size: :sm, style: :compact }
and context.call_block
will be the block above.
5. context.view_context
:
The view context when the shortcut was called. You can use it to render the component. E.g.:
instance = context.component.new(*context.call_args, **context.call_kwargs)
html = context.view_context.render(instance, &context.call_block)
Contributing
Bug reports and pull requests are welcome on GitHub.
License
The gem is available as open-source under the terms of the MIT License.