loco_motion-rails 0.0.8 → 0.5.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/README.md +62 -14
- data/app/components/daisy/actions/button_component.html.haml +2 -2
- data/app/components/daisy/actions/button_component.rb +98 -59
- data/app/components/daisy/actions/dropdown_component.html.haml +1 -2
- data/app/components/daisy/actions/dropdown_component.rb +7 -10
- data/app/components/daisy/actions/modal_component.html.haml +10 -8
- data/app/components/daisy/actions/modal_component.rb +6 -6
- data/app/components/daisy/actions/swap_component.rb +13 -9
- data/app/components/daisy/actions/theme_controller.js +113 -0
- data/app/components/daisy/actions/theme_controller_component.rb +58 -17
- data/app/components/daisy/actions/theme_preview_component.html.haml +5 -0
- data/app/components/daisy/actions/theme_preview_component.rb +68 -0
- data/app/components/daisy/data_display/accordion_component.html.haml +0 -1
- data/app/components/daisy/data_display/accordion_component.rb +10 -3
- data/app/components/daisy/data_display/avatar_component.html.haml +1 -1
- data/app/components/daisy/data_display/avatar_component.rb +17 -7
- data/app/components/daisy/data_display/badge_component.rb +122 -4
- data/app/components/daisy/data_display/card_component.html.haml +1 -1
- data/app/components/daisy/data_display/card_component.rb +20 -6
- data/app/components/daisy/data_display/chat_component.rb +2 -2
- data/app/components/daisy/data_display/collapse_component.rb +9 -5
- data/app/components/daisy/data_display/countdown_component.rb +15 -5
- data/app/components/daisy/data_display/figure_component.rb +8 -3
- data/app/components/daisy/data_display/kbd_component.rb +13 -4
- data/app/components/daisy/data_display/list_component.html.haml +5 -0
- data/app/components/daisy/data_display/list_component.rb +82 -0
- data/app/components/daisy/data_display/list_item_component.rb +39 -0
- data/app/components/daisy/data_display/stat_component.html.haml +5 -6
- data/app/components/daisy/data_display/stat_component.rb +21 -8
- data/app/components/daisy/data_display/status_component.rb +47 -0
- data/app/components/daisy/data_display/timeline_component.rb +1 -1
- data/app/components/daisy/data_input/cally_component.html.haml +14 -0
- data/app/components/daisy/data_input/cally_component.rb +182 -0
- data/app/components/daisy/data_input/cally_input_component.html.haml +5 -0
- data/app/components/daisy/data_input/cally_input_component.rb +165 -0
- data/app/components/daisy/data_input/cally_input_controller.js +235 -0
- data/app/components/daisy/data_input/checkbox_component.html.haml +20 -0
- data/app/components/daisy/data_input/checkbox_component.rb +106 -0
- data/app/components/daisy/data_input/fieldset_component.html.haml +8 -0
- data/app/components/daisy/data_input/fieldset_component.rb +57 -0
- data/app/components/daisy/data_input/file_input_component.rb +98 -0
- data/app/components/daisy/data_input/filter_component.html.haml +3 -0
- data/app/components/daisy/data_input/filter_component.rb +221 -0
- data/app/components/daisy/data_input/label_component.rb +84 -0
- data/app/components/daisy/data_input/radio_button_component.rb +87 -0
- data/app/components/daisy/data_input/range_component.rb +95 -0
- data/app/components/daisy/data_input/rating_component.html.haml +11 -0
- data/app/components/daisy/data_input/rating_component.rb +139 -0
- data/app/components/daisy/data_input/select_component.html.haml +27 -0
- data/app/components/daisy/data_input/select_component.rb +320 -0
- data/app/components/daisy/data_input/text_area_component.rb +127 -0
- data/app/components/daisy/data_input/text_input_component.html.haml +27 -0
- data/app/components/daisy/data_input/text_input_component.rb +142 -0
- data/app/components/daisy/data_input/toggle_component.rb +48 -0
- data/app/components/daisy/feedback/alert_component.html.haml +1 -1
- data/app/components/daisy/feedback/alert_component.rb +86 -2
- data/app/components/daisy/feedback/loading_component.rb +10 -3
- data/app/components/daisy/feedback/skeleton_component.rb +1 -1
- data/app/components/daisy/layout/divider_component.rb +4 -2
- data/app/components/daisy/layout/drawer_component.html.haml +0 -1
- data/app/components/daisy/layout/footer_component.rb +6 -6
- data/app/components/daisy/mockup/device_component.rb +15 -18
- data/app/components/daisy/navigation/breadcrumbs_component.html.haml +0 -1
- data/app/components/daisy/navigation/breadcrumbs_component.rb +84 -9
- data/app/components/daisy/navigation/dock_component.rb +146 -0
- data/app/components/daisy/navigation/link_component.rb +18 -9
- data/app/components/daisy/navigation/menu_component.rb +15 -9
- data/app/components/daisy/navigation/navbar_component.html.haml +1 -1
- data/app/components/daisy/navigation/navbar_component.rb +2 -13
- data/app/components/daisy/navigation/steps_component.rb +6 -6
- data/app/components/daisy/navigation/tabs_component.html.haml +0 -1
- data/app/components/daisy/navigation/tabs_component.rb +26 -16
- data/app/components/hero/icon_component.rb +15 -5
- data/app/helpers/daisy/form_builder_helper.rb +186 -0
- data/app/views/examples/daisy/data_input/filters.html.haml +62 -0
- data/lib/daisy.rb +5 -0
- data/lib/hero.rb +1 -1
- data/lib/loco_motion/base_component.rb +53 -3
- data/lib/loco_motion/component_config.rb +1 -0
- data/lib/loco_motion/concerns/iconable_component.rb +134 -0
- data/lib/loco_motion/concerns/labelable_component.rb +142 -0
- data/lib/loco_motion/concerns/linkable_component.rb +40 -0
- data/lib/loco_motion/concerns/tippable_component.rb +25 -10
- data/lib/loco_motion/engine.rb +6 -0
- data/lib/loco_motion/helpers.rb +38 -17
- data/lib/loco_motion/patches/view_component/slot_loco_parent_patch.rb +37 -0
- data/lib/loco_motion/patches/view_component/slotable_default_patch.rb +21 -0
- data/lib/loco_motion/version.rb +1 -1
- data/lib/loco_motion.rb +12 -2
- metadata +93 -21
- data/app/components/daisy/actions/theme_controller_component.html.haml +0 -5
- data/app/components/daisy/layout/artboard_component.rb +0 -59
- data/app/components/daisy/navigation/bottom_nav_component.rb +0 -138
@@ -1,16 +1,21 @@
|
|
1
1
|
class LocoMotion::BaseComponent < ViewComponent::Base
|
2
2
|
|
3
3
|
SELF_CLOSING_TAGS = %i[area base br col embed hr img input keygen link meta param source track wbr].freeze
|
4
|
+
EMPTY_PART_IGNORED_TAGS = %i[textarea].freeze
|
4
5
|
|
5
|
-
include
|
6
|
+
include RailsHeroicon::Helper
|
6
7
|
|
7
8
|
class_attribute :component_name
|
8
9
|
class_attribute :component_parts, default: { component: {} }
|
9
10
|
class_attribute :valid_modifiers, default: []
|
10
11
|
class_attribute :valid_sizes, default: []
|
11
12
|
|
13
|
+
# Hooks for concerns to register initialization and setup methods
|
14
|
+
class_attribute :component_initializers, default: []
|
15
|
+
class_attribute :component_setups, default: []
|
16
|
+
|
12
17
|
#
|
13
|
-
# Return the current
|
18
|
+
# Return the current configuration of this component.
|
14
19
|
#
|
15
20
|
# @return LocoMotion::ComponentConfig
|
16
21
|
#
|
@@ -30,6 +35,24 @@ class LocoMotion::BaseComponent < ViewComponent::Base
|
|
30
35
|
|
31
36
|
# Create our config object
|
32
37
|
@config = LocoMotion::ComponentConfig.new(self, **kws, &block)
|
38
|
+
|
39
|
+
# Run registered initializer hooks from concerns
|
40
|
+
self.class.component_initializers.each { |initializer| send(initializer) }
|
41
|
+
|
42
|
+
# Allow certain components to skip styling if they are being inherited
|
43
|
+
@skip_styling = config_option(:skip_styling, false)
|
44
|
+
|
45
|
+
# Allow manual passing of the loco parent on init if it's not auto-set
|
46
|
+
# via slots
|
47
|
+
@loco_parent = kws[:loco_parent] if kws.key?(:loco_parent)
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# Run registered setup hooks from concerns before rendering.
|
52
|
+
#
|
53
|
+
def before_render
|
54
|
+
# Note: ViewComponent::Base does not define before_render, so no super call needed.
|
55
|
+
self.class.component_setups.each { |setup| send(setup) }
|
33
56
|
end
|
34
57
|
|
35
58
|
#
|
@@ -86,6 +109,26 @@ class LocoMotion::BaseComponent < ViewComponent::Base
|
|
86
109
|
end
|
87
110
|
end
|
88
111
|
|
112
|
+
#
|
113
|
+
# Register an instance method to be called during component initialization.
|
114
|
+
#
|
115
|
+
# @param method_name [Symbol] The name of the instance method to call.
|
116
|
+
#
|
117
|
+
def self.register_component_initializer(method_name)
|
118
|
+
# Ensure we don't modify the parent class's array directly
|
119
|
+
self.component_initializers += [method_name.to_sym]
|
120
|
+
end
|
121
|
+
|
122
|
+
#
|
123
|
+
# Register an instance method to be called before component rendering.
|
124
|
+
#
|
125
|
+
# @param method_name [Symbol] The name of the instance method to call.
|
126
|
+
#
|
127
|
+
def self.register_component_setup(method_name)
|
128
|
+
# Ensure we don't modify the parent class's array directly
|
129
|
+
self.component_setups += [method_name.to_sym]
|
130
|
+
end
|
131
|
+
|
89
132
|
#
|
90
133
|
# Defines a single modifier of this component. Modifiers control certain
|
91
134
|
# rendering aspects of the component.
|
@@ -225,12 +268,18 @@ class LocoMotion::BaseComponent < ViewComponent::Base
|
|
225
268
|
tag(tag_name, **rendered_html(part_name))
|
226
269
|
else
|
227
270
|
content_tag(tag_name, **rendered_html(part_name)) do
|
228
|
-
|
271
|
+
empty_part_content(tag_name)
|
229
272
|
end
|
230
273
|
end
|
231
274
|
end
|
232
275
|
end
|
233
276
|
|
277
|
+
def empty_part_content(tag_name)
|
278
|
+
unless EMPTY_PART_IGNORED_TAGS.include?(tag_name.to_sym)
|
279
|
+
"<!-- Empty Part Block //-->".html_safe
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
234
283
|
#
|
235
284
|
# Returns the user-provided or component-default HTML tag-name.
|
236
285
|
#
|
@@ -366,6 +415,7 @@ class LocoMotion::BaseComponent < ViewComponent::Base
|
|
366
415
|
"@valid_sizes=#{valid_sizes.inspect}",
|
367
416
|
"@config=#{@config.inspect}",
|
368
417
|
"@component_parts=#{parts.inspect}",
|
418
|
+
"@loco_parent=#{loco_parent.inspect}",
|
369
419
|
].join(" ") + ">"
|
370
420
|
end
|
371
421
|
end
|
@@ -40,6 +40,7 @@ class LocoMotion::ComponentConfig
|
|
40
40
|
@parts[:component][:user_tag_name] = kws[:tag_name] if kws[:tag_name]
|
41
41
|
@parts[:component][:user_css].push(kws[:css]) if kws[:css]
|
42
42
|
@parts[:component][:user_html].deep_merge!(kws[:html]) if kws[:html]
|
43
|
+
@parts[:component][:user_stimulus_controllers].push(kws[:controller]) if kws[:controller]
|
43
44
|
@parts[:component][:user_stimulus_controllers].push(kws[:controllers]) if kws[:controllers]
|
44
45
|
end
|
45
46
|
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require "active_support/concern"
|
2
|
+
|
3
|
+
module LocoMotion
|
4
|
+
module Concerns
|
5
|
+
#
|
6
|
+
# The IconableComponent concern provides functionality for components that
|
7
|
+
# display icons. It supports both left and right icons and allows for
|
8
|
+
# customization of their CSS classes and HTML attributes.
|
9
|
+
#
|
10
|
+
module IconableComponent
|
11
|
+
extend ActiveSupport::Concern
|
12
|
+
|
13
|
+
included do |base|
|
14
|
+
base.register_component_initializer(:_initialize_iconable_component)
|
15
|
+
base.register_component_setup(:_setup_iconable_component)
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
#
|
21
|
+
# Initialize icon-related options.
|
22
|
+
#
|
23
|
+
# @option kws icon [String] The name of Hero icon to render. This is an
|
24
|
+
# alias of `left_icon`.
|
25
|
+
#
|
26
|
+
# @option kws icon_css [String] The CSS classes to apply to the icon. This
|
27
|
+
# is an alias of `left_icon_css`.
|
28
|
+
#
|
29
|
+
# @option kws icon_html [Hash] Additional HTML attributes to apply to the
|
30
|
+
# icon. This is an alias of `left_icon_html`.
|
31
|
+
#
|
32
|
+
# @option kws left_icon [String] The name of Hero icon to render to the
|
33
|
+
# left of the content.
|
34
|
+
#
|
35
|
+
# @option kws left_icon_css [String] The CSS classes to apply to the left
|
36
|
+
# icon.
|
37
|
+
#
|
38
|
+
# @option kws left_icon_html [Hash] Additional HTML attributes to apply to
|
39
|
+
# the left icon.
|
40
|
+
#
|
41
|
+
# @option kws right_icon [String] The name of Hero icon to render to the
|
42
|
+
# right of the content.
|
43
|
+
#
|
44
|
+
# @option kws right_icon_css [String] The CSS classes to apply to the right
|
45
|
+
# icon.
|
46
|
+
#
|
47
|
+
# @option kws right_icon_html [Hash] Additional HTML attributes to apply to
|
48
|
+
# the right icon.
|
49
|
+
#
|
50
|
+
def _initialize_iconable_component
|
51
|
+
@icon = config_option(:icon)
|
52
|
+
@icon_css = config_option(:icon_css, default_icon_size)
|
53
|
+
@icon_options = config_option(:icon_options, {})
|
54
|
+
@icon_html = config_option(:icon_html, {})
|
55
|
+
|
56
|
+
@left_icon = config_option(:left_icon, @icon)
|
57
|
+
@left_icon_css = config_option(:left_icon_css, @icon_css)
|
58
|
+
@left_icon_options = config_option(:left_icon_options, @icon_html)
|
59
|
+
@left_icon_html = config_option(:left_icon_html, @icon_html)
|
60
|
+
|
61
|
+
@right_icon = config_option(:right_icon)
|
62
|
+
@right_icon_css = config_option(:right_icon_css, @icon_css)
|
63
|
+
@right_icon_options = config_option(:right_icon_options, {})
|
64
|
+
@right_icon_html = config_option(:right_icon_html, @icon_html)
|
65
|
+
end
|
66
|
+
|
67
|
+
#
|
68
|
+
# Configure CSS classes for a component with icons.
|
69
|
+
# This adds necessary classes for proper icon spacing and alignment.
|
70
|
+
#
|
71
|
+
def _setup_iconable_component
|
72
|
+
if @icon || @left_icon || @right_icon
|
73
|
+
add_css(:component, "where:inline-flex where:items-center where:gap-2")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def default_icon_size
|
78
|
+
"where:size-5"
|
79
|
+
end
|
80
|
+
|
81
|
+
public # Ensure these helper methods remain public
|
82
|
+
|
83
|
+
#
|
84
|
+
# Returns the HTML attributes for the left icon.
|
85
|
+
#
|
86
|
+
# @return [Hash] HTML attributes for the left icon
|
87
|
+
#
|
88
|
+
def left_icon_html
|
89
|
+
{ class: @left_icon_css }.merge(@left_icon_html)
|
90
|
+
end
|
91
|
+
|
92
|
+
#
|
93
|
+
# Returns the HTML attributes for the right icon.
|
94
|
+
#
|
95
|
+
# @return [Hash] HTML attributes for the right icon
|
96
|
+
#
|
97
|
+
def right_icon_html
|
98
|
+
{ class: @right_icon_css }.merge(@right_icon_html)
|
99
|
+
end
|
100
|
+
|
101
|
+
#
|
102
|
+
# Determines if any icons are present in the component.
|
103
|
+
#
|
104
|
+
# @return [Boolean] true if any icons are configured, false otherwise
|
105
|
+
#
|
106
|
+
def has_icons?
|
107
|
+
@left_icon.present? || @right_icon.present?
|
108
|
+
end
|
109
|
+
|
110
|
+
#
|
111
|
+
# Renders the left icon as a Hero::IconComponent instance.
|
112
|
+
#
|
113
|
+
# @return [String] The rendered HTML for the icon
|
114
|
+
#
|
115
|
+
def render_left_icon
|
116
|
+
return unless @left_icon.present?
|
117
|
+
|
118
|
+
hero_icon(@left_icon, css: @left_icon_css, html: @left_icon_html, **@left_icon_options)
|
119
|
+
end
|
120
|
+
alias_method :render_icon, :render_left_icon
|
121
|
+
|
122
|
+
#
|
123
|
+
# Renders the right icon using a hero icon.
|
124
|
+
#
|
125
|
+
# @return [String] The rendered HTML for the icon
|
126
|
+
#
|
127
|
+
def render_right_icon
|
128
|
+
return unless @right_icon.present?
|
129
|
+
|
130
|
+
hero_icon(@right_icon, css: @right_icon_css, html: @right_icon_html, **@right_icon_options)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LocoMotion
|
4
|
+
module Concerns
|
5
|
+
#
|
6
|
+
# Can be included in relevant components to add labeling functionality.
|
7
|
+
# This adds support for start, end, and floating labels that can either be
|
8
|
+
# provided as plain text or customized via slots.
|
9
|
+
#
|
10
|
+
# @loco_example Basic usage with a start label
|
11
|
+
# class MyInputComponent < LocoMotion::BaseComponent
|
12
|
+
# include LocoMotion::Concerns::LabelableComponent
|
13
|
+
# # component implementation ...
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# = daisy_my_input(name: "username", start: "Username")
|
17
|
+
#
|
18
|
+
# @loco_example With an end label (useful for checkboxes/radios)
|
19
|
+
# = daisy_checkbox(name: "terms", end: "I agree to the terms")
|
20
|
+
#
|
21
|
+
# @loco_example With a floating label
|
22
|
+
# = daisy_text_input(name: "email", floating: "Email Address")
|
23
|
+
#
|
24
|
+
# @loco_example Using a custom slot for the label
|
25
|
+
# = daisy_text_input(name: "password") do |input|
|
26
|
+
# - input.with_floating do
|
27
|
+
# Password
|
28
|
+
# %span.text-red-500 *
|
29
|
+
#
|
30
|
+
module LabelableComponent
|
31
|
+
extend ActiveSupport::Concern
|
32
|
+
|
33
|
+
#
|
34
|
+
# Called when the module is included in a component class.
|
35
|
+
# Sets up the necessary parts & slots for custom label content.
|
36
|
+
#
|
37
|
+
included do
|
38
|
+
define_parts :label_wrapper, :start, :end, :floating
|
39
|
+
|
40
|
+
renders_one :start
|
41
|
+
renders_one :end
|
42
|
+
renders_one :floating
|
43
|
+
|
44
|
+
# NOTE: We DO NOT define attr_reader properties here because it can
|
45
|
+
# cause confusion / problems with the parts and slots.
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Initializes the component and sets up the label options.
|
50
|
+
#
|
51
|
+
# @param instance_args [Array] Positional arguments passed to the component
|
52
|
+
#
|
53
|
+
# @param instance_kws [Hash] Keyword arguments passed to the component
|
54
|
+
#
|
55
|
+
# @option instance_kws [String, nil] :start Text to display in the start
|
56
|
+
# label position
|
57
|
+
#
|
58
|
+
# @option instance_kws [String, nil] :end Text to display in the end
|
59
|
+
# label position
|
60
|
+
#
|
61
|
+
# @option instance_kws [String, nil] :floating Text to display in the
|
62
|
+
# floating label position
|
63
|
+
#
|
64
|
+
# @option instance_kws [String, nil] :placeholder The input's placeholder
|
65
|
+
# text. If not provided and `floating_placeholder` is set, it will use
|
66
|
+
# that value.
|
67
|
+
#
|
68
|
+
# @option instance_kws [String, nil] :floating_placeholder Text to use for
|
69
|
+
# both the floating label and the input placeholder. This is a
|
70
|
+
# convenience option that sets both the `floating` and `placeholder`
|
71
|
+
# options to the same value. Both `floating` and `placeholder` take
|
72
|
+
# precedence over `floating_placeholder`.
|
73
|
+
#
|
74
|
+
# @param instance_block [Proc] Block passed to the component for rendering
|
75
|
+
# custom content
|
76
|
+
#
|
77
|
+
def initialize(*instance_args, **instance_kws, &instance_block)
|
78
|
+
super(*instance_args, **instance_kws, &instance_block)
|
79
|
+
|
80
|
+
@floating_placeholder = config_option(:floating_placeholder)
|
81
|
+
|
82
|
+
@start = config_option(:start)
|
83
|
+
@end = config_option(:end)
|
84
|
+
@floating = config_option(:floating, @floating_placeholder)
|
85
|
+
@placeholder = config_option(:placeholder, @floating_placeholder)
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
# Sets up the tag names for the label parts before rendering the component.
|
90
|
+
# This method is called automatically during the component rendering
|
91
|
+
# lifecycle.
|
92
|
+
#
|
93
|
+
# Note that CSS classes for labels must be handled by the implementing
|
94
|
+
# component since requirements differ for each type of input component.
|
95
|
+
#
|
96
|
+
def before_render
|
97
|
+
super
|
98
|
+
|
99
|
+
set_tag_name(:label_wrapper, :label)
|
100
|
+
set_tag_name(:start, :span)
|
101
|
+
set_tag_name(:end, :span)
|
102
|
+
set_tag_name(:floating, :span)
|
103
|
+
end
|
104
|
+
|
105
|
+
#
|
106
|
+
# Checks if any type of label is present.
|
107
|
+
#
|
108
|
+
# @return [Boolean] true if any label is present, false otherwise
|
109
|
+
#
|
110
|
+
def has_any_label?
|
111
|
+
has_start_label? || has_end_label? || has_floating_label?
|
112
|
+
end
|
113
|
+
|
114
|
+
#
|
115
|
+
# Checks if a start label is present.
|
116
|
+
#
|
117
|
+
# @return [Boolean] true if start label is present, false otherwise
|
118
|
+
#
|
119
|
+
def has_start_label?
|
120
|
+
start? || @start || config_option(:start).present?
|
121
|
+
end
|
122
|
+
|
123
|
+
#
|
124
|
+
# Checks if an end label is present.
|
125
|
+
#
|
126
|
+
# @return [Boolean] true if end label is present, false otherwise
|
127
|
+
#
|
128
|
+
def has_end_label?
|
129
|
+
end? || @end || config_option(:end).present?
|
130
|
+
end
|
131
|
+
|
132
|
+
#
|
133
|
+
# Checks if a floating label is present.
|
134
|
+
#
|
135
|
+
# @return [Boolean] true if floating label is present, false otherwise
|
136
|
+
#
|
137
|
+
def has_floating_label?
|
138
|
+
floating? || @floating || config_option(:floating).present?
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "active_support/concern"
|
2
|
+
|
3
|
+
module LocoMotion
|
4
|
+
module Concerns
|
5
|
+
#
|
6
|
+
# Include this module to enable link functionality in a component.
|
7
|
+
# When an href is provided, the component will render as an <a> tag.
|
8
|
+
#
|
9
|
+
module LinkableComponent
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
|
12
|
+
included do |base|
|
13
|
+
base.register_component_initializer(:_initialize_linkable_component)
|
14
|
+
base.register_component_setup(:_setup_linkable_component)
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
#
|
20
|
+
# Initialize link-related options.
|
21
|
+
#
|
22
|
+
def _initialize_linkable_component
|
23
|
+
@href = config_option(:href)
|
24
|
+
@target = config_option(:target)
|
25
|
+
@title = config_option(:title)
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# Sets the component's tag to <a> if an href is provided and configures the
|
30
|
+
# appropriate HTML attributes.
|
31
|
+
#
|
32
|
+
def _setup_linkable_component
|
33
|
+
if @href
|
34
|
+
set_tag_name(:component, :a)
|
35
|
+
add_html(:component, { href: @href, target: @target, title: @title })
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -1,24 +1,39 @@
|
|
1
|
+
require "active_support/concern"
|
2
|
+
|
1
3
|
module LocoMotion
|
2
4
|
module Concerns
|
3
5
|
#
|
4
|
-
# Can be included in relevant components to allow a
|
6
|
+
# Can be included in relevant components to allow a `tip` attribute that
|
5
7
|
# automatically adds the `tooltip` CSS class and the `data-tip` attribute
|
6
8
|
# to the component.
|
7
9
|
#
|
8
10
|
module TippableComponent
|
11
|
+
extend ActiveSupport::Concern
|
12
|
+
|
13
|
+
included do |base|
|
14
|
+
base.register_component_initializer(:_initialize_tippable_component)
|
15
|
+
base.register_component_setup(:_setup_tippable_component)
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
9
20
|
#
|
10
|
-
#
|
11
|
-
# the `data-tip` attribute to the component if the `tip` attribute is
|
12
|
-
# present.
|
21
|
+
# Initialize tooltip-related options.
|
13
22
|
#
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
tip = config_option(:tip)
|
23
|
+
# @option kws tip [String] The tooltip text to display when hovering
|
24
|
+
#
|
25
|
+
def _initialize_tippable_component
|
26
|
+
@tip = config_option(:tip)
|
27
|
+
end
|
18
28
|
|
19
|
-
|
29
|
+
#
|
30
|
+
# Configure tooltip functionality for the component.
|
31
|
+
# Adds the `tooltip` CSS class and the `data-tip` attribute if a tip is provided.
|
32
|
+
#
|
33
|
+
def _setup_tippable_component
|
34
|
+
if @tip
|
20
35
|
add_css(:component, "tooltip")
|
21
|
-
add_html(:component, { data: { tip: tip } })
|
36
|
+
add_html(:component, { data: { tip: @tip } })
|
22
37
|
end
|
23
38
|
end
|
24
39
|
end
|
data/lib/loco_motion/engine.rb
CHANGED
@@ -4,5 +4,11 @@ module LocoMotion
|
|
4
4
|
|
5
5
|
config.autoload_paths << "#{root}/app"
|
6
6
|
config.autoload_paths << "#{root}/lib"
|
7
|
+
|
8
|
+
initializer "loco_motion.form_builder_extensions" do
|
9
|
+
ActiveSupport.on_load(:action_view) do
|
10
|
+
require_relative "../../app/helpers/daisy/form_builder_helper"
|
11
|
+
end
|
12
|
+
end
|
7
13
|
end
|
8
14
|
end
|
data/lib/loco_motion/helpers.rb
CHANGED
@@ -14,24 +14,42 @@ module LocoMotion
|
|
14
14
|
"Daisy::Actions::ThemeControllerComponent" => { names: "theme_controller", group: "Actions", title: "Theme Controllers", example: "theme_controllers" },
|
15
15
|
|
16
16
|
# Data
|
17
|
-
"Daisy::DataDisplay::AccordionComponent" => { names: "accordion", group: "Data", title: "Accordions", example: "accordions" },
|
18
|
-
"Daisy::DataDisplay::AvatarComponent" => { names: "avatar", group: "Data", title: "Avatars", example: "avatars" },
|
19
|
-
"Daisy::DataDisplay::BadgeComponent" => { names: "badge", group: "Data", title: "Badges", example: "badges" },
|
20
|
-
"Daisy::DataDisplay::CardComponent" => { names: "card", group: "Data", title: "Cards", example: "cards" },
|
21
|
-
"Daisy::DataDisplay::CarouselComponent" => { names: "carousel", group: "Data", title: "Carousels", example: "carousels" },
|
22
|
-
"Daisy::DataDisplay::ChatComponent" => { names: "chat", group: "Data", title: "Chat Bubbles", example: "chat_bubbles" },
|
23
|
-
"Daisy::DataDisplay::CollapseComponent" => { names: "collapse", group: "Data", title: "Collapses", example: "collapses" },
|
24
|
-
"Daisy::DataDisplay::CountdownComponent" => { names: "countdown", group: "Data", title: "Countdowns", example: "countdowns" },
|
25
|
-
"Daisy::DataDisplay::DiffComponent" => { names: "diff", group: "Data", title: "Diffs", example: "diffs" },
|
26
|
-
"Daisy::DataDisplay::FigureComponent" => { names: "figure", group: "Data", title: "Figures", example: "figures" },
|
27
|
-
"Daisy::DataDisplay::KbdComponent" => { names: "kbd", group: "Data", title: "Keyboard (KBD)", example: "kbds" },
|
28
|
-
"Daisy::DataDisplay::
|
29
|
-
"Daisy::DataDisplay::
|
30
|
-
"Daisy::DataDisplay::
|
17
|
+
"Daisy::DataDisplay::AccordionComponent" => { names: "accordion", group: "Data Display", title: "Accordions", example: "accordions" },
|
18
|
+
"Daisy::DataDisplay::AvatarComponent" => { names: "avatar", group: "Data Display", title: "Avatars", example: "avatars" },
|
19
|
+
"Daisy::DataDisplay::BadgeComponent" => { names: "badge", group: "Data Display", title: "Badges", example: "badges" },
|
20
|
+
"Daisy::DataDisplay::CardComponent" => { names: "card", group: "Data Display", title: "Cards", example: "cards" },
|
21
|
+
"Daisy::DataDisplay::CarouselComponent" => { names: "carousel", group: "Data Display", title: "Carousels", example: "carousels" },
|
22
|
+
"Daisy::DataDisplay::ChatComponent" => { names: "chat", group: "Data Display", title: "Chat Bubbles", example: "chat_bubbles" },
|
23
|
+
"Daisy::DataDisplay::CollapseComponent" => { names: "collapse", group: "Data Display", title: "Collapses", example: "collapses" },
|
24
|
+
"Daisy::DataDisplay::CountdownComponent" => { names: "countdown", group: "Data Display", title: "Countdowns", example: "countdowns" },
|
25
|
+
"Daisy::DataDisplay::DiffComponent" => { names: "diff", group: "Data Display", title: "Diffs", example: "diffs" },
|
26
|
+
"Daisy::DataDisplay::FigureComponent" => { names: "figure", group: "Data Display", title: "Figures", example: "figures" },
|
27
|
+
"Daisy::DataDisplay::KbdComponent" => { names: "kbd", group: "Data Display", title: "Keyboard (KBD)", example: "kbds" },
|
28
|
+
"Daisy::DataDisplay::ListComponent" => { names: "list", group: "Data Display", title: "Lists", example: "lists" },
|
29
|
+
"Daisy::DataDisplay::StatComponent" => { names: "stat", group: "Data Display", title: "Stats", example: "stats" },
|
30
|
+
"Daisy::DataDisplay::StatusComponent" => { names: "status", group: "Data Display", title: "Statuses", example: "statuses" },
|
31
|
+
"Daisy::DataDisplay::TableComponent" => { names: "table", group: "Data Display", title: "Tables", example: "tables" },
|
32
|
+
"Daisy::DataDisplay::TimelineComponent" => { names: "timeline", group: "Data Display", title: "Timelines", example: "timelines" },
|
33
|
+
|
34
|
+
# Data Input
|
35
|
+
"Daisy::DataInput::CallyComponent" => { names: "cally", group: "Data Input", title: "Calendars", example: "calendars" },
|
36
|
+
"Daisy::DataInput::CallyInputComponent" => { names: "cally_input", group: "Data Input", title: "Cally Inputs", example: "cally_inputs" },
|
37
|
+
"Daisy::DataInput::CheckboxComponent" => { names: "checkbox", group: "Data Input", title: "Checkboxes", example: "checkboxes" },
|
38
|
+
"Daisy::DataInput::FileInputComponent" => { names: "file_input", group: "Data Input", title: "File Inputs", example: "file_inputs" },
|
39
|
+
"Daisy::DataInput::FieldsetComponent" => { names: "fieldset", group: "Data Input", title: "Fieldsets", example: "fieldsets" },
|
40
|
+
"Daisy::DataInput::FilterComponent" => { names: "filter", group: "Data Input", title: "Filters", example: "filters" },
|
41
|
+
"Daisy::DataInput::LabelComponent" => { names: "label", group: "Data Input", title: "Labels", example: "labels" },
|
42
|
+
"Daisy::DataInput::RadioButtonComponent" => { names: "radio", group: "Data Input", title: "Radio Buttons", example: "radio_buttons" },
|
43
|
+
"Daisy::DataInput::RangeComponent" => { names: "range", group: "Data Input", title: "Ranges", example: "ranges" },
|
44
|
+
"Daisy::DataInput::RatingComponent" => { names: "rating", group: "Data Input", title: "Ratings", example: "ratings" },
|
45
|
+
"Daisy::DataInput::SelectComponent" => { names: "select", group: "Data Input", title: "Selects", example: "selects" },
|
46
|
+
"Daisy::DataInput::TextInputComponent" => { names: ["input", "text_input"], group: "Data Input", title: "Text Inputs", example: "text_inputs" },
|
47
|
+
"Daisy::DataInput::TextAreaComponent" => { names: "text_area", group: "Data Input", title: "Text Areas", example: "text_areas" },
|
48
|
+
"Daisy::DataInput::ToggleComponent" => { names: "toggle", group: "Data Input", title: "Toggles", example: "toggles" },
|
31
49
|
|
32
50
|
# Navigation
|
33
51
|
"Daisy::Navigation::BreadcrumbsComponent" => { names: "breadcrumbs", group: "Navigation", title: "Breadcrumbs", example: "breadcrumbs" },
|
34
|
-
"Daisy::Navigation::
|
52
|
+
"Daisy::Navigation::DockComponent" => { names: "dock", group: "Navigation", title: "Dock", example: "docks" },
|
35
53
|
"Daisy::Navigation::LinkComponent" => { names: "link", group: "Navigation", title: "Links", example: "links" },
|
36
54
|
"Daisy::Navigation::MenuComponent" => { names: "menu", group: "Navigation", title: "Menus", example: "menus" },
|
37
55
|
"Daisy::Navigation::NavbarComponent" => { names: "navbar", group: "Navigation", title: "Navbars", example: "navbars" },
|
@@ -49,7 +67,6 @@ module LocoMotion
|
|
49
67
|
"Daisy::Feedback::TooltipComponent" => { names: ["tooltip", "tip"], group: "Feedback", title: "Tooltips", example: "tooltips" },
|
50
68
|
|
51
69
|
# Layout
|
52
|
-
"Daisy::Layout::ArtboardComponent" => { names: "artboard", group: "Layout", title: "Artboards", example: "artboards" },
|
53
70
|
"Daisy::Layout::DividerComponent" => { names: "divider", group: "Layout", title: "Dividers", example: "dividers" },
|
54
71
|
"Daisy::Layout::DrawerComponent" => { names: "drawer", group: "Layout", title: "Drawers", example: "drawers" },
|
55
72
|
"Daisy::Layout::FooterComponent" => { names: "footer", group: "Layout", title: "Footers", example: "footers" },
|
@@ -80,6 +97,10 @@ module LocoMotion
|
|
80
97
|
end
|
81
98
|
|
82
99
|
def component_example_path(component_name)
|
100
|
+
"/examples/#{component_name}"
|
101
|
+
end
|
102
|
+
|
103
|
+
def component_partial_path(component_name)
|
83
104
|
comp = COMPONENTS[component_name]
|
84
105
|
|
85
106
|
comp_split = component_name.split("::")
|
@@ -91,6 +112,6 @@ module LocoMotion
|
|
91
112
|
"/examples/#{framework}/#{section_path}#{example}"
|
92
113
|
end
|
93
114
|
|
94
|
-
module_function :component_example_path
|
115
|
+
module_function :component_example_path, :component_partial_path
|
95
116
|
end
|
96
117
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# Monkey patch ViewComponent::Slot to save the parent component
|
2
|
+
|
3
|
+
module LocoMotion
|
4
|
+
module Patches
|
5
|
+
module ViewComponent
|
6
|
+
module SlotPatch
|
7
|
+
|
8
|
+
# Set the loco parent any time the instance changes
|
9
|
+
def __vc_component_instance=(instance)
|
10
|
+
# Call the original implementation
|
11
|
+
super
|
12
|
+
|
13
|
+
# And set the Loco parent
|
14
|
+
set_loco_parent
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
set_loco_parent
|
19
|
+
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
def set_loco_parent(parent = @parent)
|
24
|
+
return if parent.nil?
|
25
|
+
return if @__vc_component_instance.nil?
|
26
|
+
return if @__vc_component_instance.loco_parent.present?
|
27
|
+
|
28
|
+
@__vc_component_instance.set_loco_parent(parent)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Apply the patch
|
37
|
+
::ViewComponent::Slot.prepend(LocoMotion::Patches::ViewComponent::SlotPatch)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Monkey patch ViewComponent::SlotableDefault to modify get_slot behavior
|
2
|
+
|
3
|
+
module LocoMotion
|
4
|
+
module Patches
|
5
|
+
module ViewComponent
|
6
|
+
module SlotableDefaultPatch
|
7
|
+
# Override get_slot method
|
8
|
+
def get_slot(slot_name)
|
9
|
+
# ensure content is loaded so slots will be defined
|
10
|
+
content unless content_evaluated?
|
11
|
+
|
12
|
+
# Call the original implementation
|
13
|
+
super
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Apply the patch
|
21
|
+
::ViewComponent::SlotableDefault.prepend(LocoMotion::Patches::ViewComponent::SlotableDefaultPatch)
|
data/lib/loco_motion/version.rb
CHANGED
data/lib/loco_motion.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
require "rails"
|
2
2
|
require "haml-rails"
|
3
|
-
require "
|
3
|
+
require "rails_heroicon"
|
4
4
|
|
5
|
-
require Gem::Specification.find_by_name("
|
5
|
+
require Gem::Specification.find_by_name("rails_heroicon").gem_dir + "/lib/rails_heroicon/helper.rb"
|
6
6
|
|
7
7
|
require "view_component"
|
8
8
|
|
@@ -14,5 +14,15 @@ require "loco_motion/basic_component"
|
|
14
14
|
require "loco_motion/engine"
|
15
15
|
require "loco_motion/helpers"
|
16
16
|
|
17
|
+
# Load patches
|
18
|
+
require "loco_motion/patches/view_component/slotable_default_patch"
|
19
|
+
require "loco_motion/patches/view_component/slot_loco_parent_patch"
|
20
|
+
|
17
21
|
require "hero"
|
18
22
|
require "daisy"
|
23
|
+
|
24
|
+
begin
|
25
|
+
require "pry" if Rails.env.development?
|
26
|
+
rescue LoadError
|
27
|
+
# Don't throw an error, pry should really only be used while debugging locally
|
28
|
+
end
|