Class: LocoMotion::BaseComponent
- Inherits:
-
ViewComponent::Base
- Object
- ViewComponent::Base
- LocoMotion::BaseComponent
- Includes:
- Concerns::InspectableComponent, RailsHeroicon::Helper
- Defined in:
- lib/loco_motion/base_component.rb
Direct Known Subclasses
Daisy::Actions::ButtonComponent, Daisy::Actions::DropdownComponent, Daisy::Actions::FabComponent, Daisy::Actions::ModalComponent, Daisy::Actions::SwapComponent, Daisy::Actions::ThemeControllerComponent, Daisy::Actions::ThemePreviewComponent, Daisy::DataDisplay::AccordionComponent, Daisy::DataDisplay::AvatarComponent, Daisy::DataDisplay::BadgeComponent, Daisy::DataDisplay::CardComponent, Daisy::DataDisplay::CarouselComponent, Daisy::DataDisplay::ChatComponent, Daisy::DataDisplay::CollapseComponent, Daisy::DataDisplay::CountdownComponent, Daisy::DataDisplay::DiffComponent, Daisy::DataDisplay::FigureComponent, Daisy::DataDisplay::KbdComponent, Daisy::DataDisplay::ListComponent, Daisy::DataDisplay::ListItemComponent, Daisy::DataDisplay::StatComponent, Daisy::DataDisplay::StatusComponent, Daisy::DataDisplay::TableComponent, Daisy::DataDisplay::TextRotateComponent, Daisy::DataDisplay::TimelineComponent, Daisy::DataDisplay::TimelineEventComponent, Daisy::DataInput::CallyComponent, Daisy::DataInput::CallyComponent::MonthComponent, Daisy::DataInput::CallyInputComponent, Daisy::DataInput::CheckboxComponent, Daisy::DataInput::FieldsetComponent, Daisy::DataInput::FileInputComponent, Daisy::DataInput::FilterComponent, Daisy::DataInput::LabelComponent, Daisy::DataInput::RadioButtonComponent, Daisy::DataInput::RangeComponent, Daisy::DataInput::RatingComponent, Daisy::DataInput::SelectComponent, Daisy::DataInput::TextAreaComponent, Daisy::DataInput::TextInputComponent, Daisy::Feedback::AlertComponent, Daisy::Feedback::LoadingComponent, Daisy::Feedback::ProgressComponent, Daisy::Feedback::RadialProgressComponent, Daisy::Feedback::SkeletonComponent, Daisy::Feedback::ToastComponent, Daisy::Feedback::TooltipComponent, Daisy::Layout::DividerComponent, Daisy::Layout::DrawerComponent, Daisy::Layout::DrawerComponent::Daisy::Layout::DrawerSidebarComponent, Daisy::Layout::FooterComponent, Daisy::Layout::HeroComponent, Daisy::Layout::HoverComponent, Daisy::Layout::HoverGalleryComponent, Daisy::Layout::IndicatorComponent, Daisy::Layout::JoinComponent, Daisy::Layout::StackComponent, Daisy::Mockup::BrowserComponent, Daisy::Mockup::CodeComponent, Daisy::Mockup::CodeComponent::Daisy::Mockup::CodeLineComponent, Daisy::Mockup::DeviceComponent, Daisy::Mockup::FrameComponent, Daisy::Navigation::BreadcrumbsComponent, Daisy::Navigation::BreadcrumbsComponent::Daisy::Navigation::BreadcrumbItemComponent, Daisy::Navigation::DockComponent, Daisy::Navigation::DockComponent::Daisy::Navigation::DockSectionComponent, Daisy::Navigation::LinkComponent, Daisy::Navigation::MenuComponent, Daisy::Navigation::MenuComponent::Daisy::Navigation::MenuItemComponent, Daisy::Navigation::NavbarComponent, Daisy::Navigation::StepsComponent, Daisy::Navigation::StepsComponent::Daisy::Navigation::StepComponent, Daisy::Navigation::TabsComponent, Hero::IconComponent, BasicComponent
Constant Summary collapse
- SELF_CLOSING_TAGS =
%i[area base br col embed hr img input keygen link meta param source track wbr].freeze
- EMPTY_PART_IGNORED_TAGS =
%i[textarea].freeze
Instance Attribute Summary collapse
-
#config ⇒ Object
readonly
Return the current configuration of this component.
-
#loco_parent ⇒ Object
readonly
rubocop:enable Naming/AccessorMethodName.
Class Method Summary collapse
-
.build(*_build_args, **build_kws, &build_block) ⇒ Object
Allows you to bulid a customized version of this component without having to define a new class.
-
.define_modifier(modifier_name) ⇒ Object
Defines a single modifier of this component.
-
.define_modifiers(*modifier_names) ⇒ Object
Define multiple modifiers for this component.
-
.define_part(part_name, part_defaults = {}) ⇒ Object
Defines a new part of this component which can customize CSS, HTML and more.
-
.define_parts(*part_names) ⇒ Object
Convenience method for defining multiple parts at once with no defaults.
-
.define_size(size_name) ⇒ Object
Define a single size of this component.
-
.define_sizes(*size_names) ⇒ Object
Define multiple sizes for this component.
-
.register_component_initializer(method_name) ⇒ Object
Register an instance method to be called during component initialization.
-
.register_component_setup(method_name) ⇒ Object
Register an instance method to be called before component rendering.
-
.renders_many(*args) ⇒ Object
Override the default many slot to render the BasicComponent if no component is provided.
-
.renders_one(*args) ⇒ Object
Override the default slot to render the BasicComponent if no component is provided.
-
.set_component_name(component_name) ⇒ Object
Sets the component name used in CSS generation.
Instance Method Summary collapse
-
#before_render ⇒ Object
Run registered setup hooks from concerns before rendering.
-
#component_ref ⇒ BaseComponent
Returns a reference to this component.
-
#config_option(key, default = nil) ⇒ Object
Retrieve the requested component option, or the desired default if no option was provided.
-
#cssify(content) ⇒ Object
Convert strings, symbols, and arrays of those into a single CSS-like string.
- #empty_part_content(tag_name) ⇒ Object
-
#initialize(*args, **kws, &block) ⇒ BaseComponent
constructor
Create a new instance of a component.
-
#inspect ⇒ Object
Provide some nice output for debugging or other purposes.
-
#part(part_name, &block) ⇒ Object
Renders the given part.
-
#rendered_css(part_name) ⇒ String
Builds a string suitable for the HTML element’s ‘class` attribute for the requested component part.
-
#rendered_data(part_name) ⇒ Hash
Builds the HTML ‘data` attribute.
-
#rendered_html(part_name) ⇒ Hash
Builds a Hash of all of the HTML attributes for the requested component part.
-
#rendered_stimulus_controllers(part_name) ⇒ Object
Builds a list of Stimulus controllers for the HTML ‘data-controller` attribute.
-
#rendered_tag_name(part_name) ⇒ Symbol, String
Returns the user-provided or component-default HTML tag-name.
-
#set_loco_parent(parent) ⇒ Object
Sets the parent component of this component.
-
#strip_spaces(str) ⇒ String
Strip extra whitespace from a given string.
Methods included from Concerns::InspectableComponent
Constructor Details
#initialize(*args, **kws, &block) ⇒ BaseComponent
Create a new instance of a component.
All components accept the following universal options for customizing the rendered HTML. Each is also available in a part-specific form by prefixing it with the part name (e.g. ‘title_css`, `overlay_html`, `wrapper_aria`).
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/loco_motion/base_component.rb', line 60 def initialize(*args, **kws, &block) super # Create our config object @config = LocoMotion::ComponentConfig.new(self, **kws, &block) # Run registered initializer hooks from concerns self.class.component_initializers.each { |initializer| send(initializer) } # Allow certain components to skip styling if they are being inherited @skip_styling = config_option(:skip_styling, false) # Allow manual passing of the loco parent on init if it's not auto-set # via slots @loco_parent = kws[:loco_parent] if kws.key?(:loco_parent) end |
Instance Attribute Details
#config ⇒ Object (readonly)
Return the current configuration of this component.
25 26 27 |
# File 'lib/loco_motion/base_component.rb', line 25 def config @config end |
#loco_parent ⇒ Object (readonly)
rubocop:enable Naming/AccessorMethodName
244 245 246 |
# File 'lib/loco_motion/base_component.rb', line 244 def loco_parent @loco_parent end |
Class Method Details
.build(*_build_args, **build_kws, &build_block) ⇒ Object
Allows you to bulid a customized version of this component without having to define a new class.
220 221 222 223 |
# File 'lib/loco_motion/base_component.rb', line 220 def self.build(*_build_args, **build_kws, &build_block) builder = ComponentBuilder.new(self, build_kws, &build_block) builder.build end |
.define_modifier(modifier_name) ⇒ Object
Defines a single modifier of this component. Modifiers control certain rendering aspects of the component.
167 168 169 |
# File 'lib/loco_motion/base_component.rb', line 167 def self.define_modifier(modifier_name) define_modifiers(modifier_name) end |
.define_modifiers(*modifier_names) ⇒ Object
Define multiple modifiers for this component. Modifiers control certain rendering aspects of the component.
178 179 180 181 182 183 184 185 186 187 |
# File 'lib/loco_motion/base_component.rb', line 178 def self.define_modifiers(*modifier_names) # Note that since we're using Rails' class_attribute method for these, we # must take care not to alter the original object but rather use a setter # (the `+=` in this case) to set the new value so Rails knows not to # override the parent value. # # For example, we cannot use `<<` or `concat` here. self.valid_modifiers ||= [] self.valid_modifiers += modifier_names end |
.define_part(part_name, part_defaults = {}) ⇒ Object
Defines a new part of this component which can customize CSS, HTML and more.
120 121 122 123 124 125 126 127 128 |
# File 'lib/loco_motion/base_component.rb', line 120 def self.define_part(part_name, part_defaults = {}) # Note that since we're using Rails' class_attribute method for these, we # must take care not to alter the original object but rather use a setter # (the `=` in this case) to set the new value so Rails knows not to override # the parent value. # # For example, we cannot use `merge!` or `[part_name] = ` here. self.component_parts = component_parts.merge({ part_name => part_defaults }) end |
.define_parts(*part_names) ⇒ Object
Convenience method for defining multiple parts at once with no defaults.
135 136 137 138 139 |
# File 'lib/loco_motion/base_component.rb', line 135 def self.define_parts(*part_names) (part_names || []).each do |part_name| define_part(part_name) end end |
.define_size(size_name) ⇒ Object
Define a single size of this component. Sizes control how big or small this component will render.
195 196 197 |
# File 'lib/loco_motion/base_component.rb', line 195 def self.define_size(size_name) define_sizes(size_name) end |
.define_sizes(*size_names) ⇒ Object
Define multiple sizes for this component. Sizes control how big or small this component will render.
205 206 207 208 209 210 211 212 213 214 |
# File 'lib/loco_motion/base_component.rb', line 205 def self.define_sizes(*size_names) # Note that since we're using Rails' class_attribute method for these, we # must take care not to alter the original object but rather use a setter # (the `+=` in this case) to set the new value so Rails knows not to # override the parent value. # # For example, we cannot use `<<` or `concat` here. self.valid_sizes ||= [] self.valid_sizes += size_names end |
.register_component_initializer(method_name) ⇒ Object
Register an instance method to be called during component initialization.
146 147 148 149 |
# File 'lib/loco_motion/base_component.rb', line 146 def self.register_component_initializer(method_name) # Ensure we don't modify the parent class's array directly self.component_initializers += [method_name.to_sym] end |
.register_component_setup(method_name) ⇒ Object
Register an instance method to be called before component rendering.
156 157 158 159 |
# File 'lib/loco_motion/base_component.rb', line 156 def self.register_component_setup(method_name) # Ensure we don't modify the parent class's array directly self.component_setups += [method_name.to_sym] end |
.renders_many(*args) ⇒ Object
Override the default many slot to render the BasicComponent if no component is provided.
98 99 100 101 |
# File 'lib/loco_motion/base_component.rb', line 98 def self.renders_many(*args) # If they don't pass extra options, default to BasicComponent args&.size == 1 ? super(*args + [LocoMotion::BasicComponent]) : super end |
.renders_one(*args) ⇒ Object
Override the default slot to render the BasicComponent if no component is provided.
89 90 91 92 |
# File 'lib/loco_motion/base_component.rb', line 89 def self.renders_one(*args) # If they don't pass extra options, default to BasicComponent args&.size == 1 ? super(*args + [LocoMotion::BasicComponent]) : super end |
.set_component_name(component_name) ⇒ Object
Sets the component name used in CSS generation.
rubocop:disable Naming/AccessorMethodName
109 110 111 |
# File 'lib/loco_motion/base_component.rb', line 109 def self.set_component_name(component_name) self.component_name = component_name end |
Instance Method Details
#before_render ⇒ Object
Run registered setup hooks from concerns before rendering.
80 81 82 83 |
# File 'lib/loco_motion/base_component.rb', line 80 def before_render # NOTE: ViewComponent::Base does not define before_render, so no super call needed. self.class.component_setups.each { |setup| send(setup) } end |
#component_ref ⇒ BaseComponent
Returns a reference to this component. Useful for passing a parent component into child components.
231 232 233 |
# File 'lib/loco_motion/base_component.rb', line 231 def component_ref self end |
#config_option(key, default = nil) ⇒ Object
Retrieve the requested component option, or the desired default if no option was provided.
392 393 394 395 396 |
# File 'lib/loco_motion/base_component.rb', line 392 def config_option(key, default = nil) value = @config.[key] value.nil? ? default : value end |
#cssify(content) ⇒ Object
Convert strings, symbols, and arrays of those into a single CSS-like string.
366 367 368 369 370 |
# File 'lib/loco_motion/base_component.rb', line 366 def cssify(content) css = [content].flatten.compact strip_spaces(css.join(" ")) end |
#empty_part_content(tag_name) ⇒ Object
276 277 278 279 280 |
# File 'lib/loco_motion/base_component.rb', line 276 def empty_part_content(tag_name) return if EMPTY_PART_IGNORED_TAGS.include?(tag_name.to_sym) "<!-- Empty Part Block //-->".html_safe end |
#inspect ⇒ Object
Provide some nice output for debugging or other purposes.
400 401 402 403 404 405 406 407 408 409 410 |
# File 'lib/loco_motion/base_component.rb', line 400 def inspect build_inspect_string( "component_name", "valid_modifiers", "valid_sizes", "config", "component_parts", "loco_parent", suffix: ">" ) end |
#part(part_name, &block) ⇒ Object
Renders the given part.
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 |
# File 'lib/loco_motion/base_component.rb', line 249 def part(part_name, &block) # Validate the part_name @config.validate_part(part_name) # Grab the rendered tag name tag_name = rendered_tag_name(part_name) if block_given? content_tag(tag_name, **rendered_html(part_name), &block) else # The `tag()` helper will allow you to pass any tag without a block, but # this isn't valid HTML. In particular, it will render a "self-closing" # <div /> tag which doesn't actually close the div. # # Therefore, we need to pass some kind of block to ensure it closes. We've # choosen a comment to keep the output as clean as possible while still # informing a developer what is happening. if SELF_CLOSING_TAGS.include?(tag_name.to_sym) tag(tag_name, **rendered_html(part_name)) else content_tag(tag_name, **rendered_html(part_name)) do empty_part_content(tag_name) end end end end |
#rendered_css(part_name) ⇒ String
Builds a string suitable for the HTML element’s ‘class` attribute for the requested component part.
303 304 305 306 307 308 |
# File 'lib/loco_motion/base_component.rb', line 303 def rendered_css(part_name) default_css = @config.get_part(part_name)[:default_css] user_css = @config.get_part(part_name)[:user_css] cssify([default_css, user_css]) end |
#rendered_data(part_name) ⇒ Hash
Builds the HTML ‘data` attribute.
337 338 339 340 341 342 343 344 345 |
# File 'lib/loco_motion/base_component.rb', line 337 def rendered_data(part_name) generated_data = {} stimulus_controllers = rendered_stimulus_controllers(part_name) generated_data[:controller] = stimulus_controllers if stimulus_controllers.present? generated_data end |
#rendered_html(part_name) ⇒ Hash
Builds a Hash of all of the HTML attributes for the requested component part.
319 320 321 322 323 324 325 326 327 |
# File 'lib/loco_motion/base_component.rb', line 319 def rendered_html(part_name) default_html = @config.get_part(part_name)[:default_html] || {} user_html = @config.get_part(part_name)[:user_html] || {} { class: rendered_css(part_name), data: rendered_data(part_name) }.deep_merge(default_html).deep_merge(user_html) end |
#rendered_stimulus_controllers(part_name) ⇒ Object
Builds a list of Stimulus controllers for the HTML ‘data-controller` attribute.
@ return [String] A space-separated list of Stimulus controllers.
356 357 358 359 360 361 |
# File 'lib/loco_motion/base_component.rb', line 356 def rendered_stimulus_controllers(part_name) default_controllers = @config.get_part(part_name)[:default_stimulus_controllers] user_controllers = @config.get_part(part_name)[:user_stimulus_controllers] strip_spaces([default_controllers, user_controllers].join(" ")) end |
#rendered_tag_name(part_name) ⇒ Symbol, String
Returns the user-provided or component-default HTML tag-name.
289 290 291 292 293 |
# File 'lib/loco_motion/base_component.rb', line 289 def rendered_tag_name(part_name) part = @config.get_part(part_name) part[:user_tag_name] || part[:default_tag_name] end |
#set_loco_parent(parent) ⇒ Object
Sets the parent component of this component. Enables child components to ask questions of their parent and access parent config.
rubocop:disable Naming/AccessorMethodName
240 241 242 |
# File 'lib/loco_motion/base_component.rb', line 240 def set_loco_parent(parent) @loco_parent = parent end |
#strip_spaces(str) ⇒ String
Strip extra whitespace from a given string.
379 380 381 |
# File 'lib/loco_motion/base_component.rb', line 379 def strip_spaces(str) str.gsub(/ +/, " ").strip end |