loco_motion-rails 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +880 -0
  4. data/app/components/daisy/actions/button_component.html.haml +7 -0
  5. data/app/components/daisy/actions/button_component.rb +52 -0
  6. data/app/components/daisy/actions/dropdown_component.html.haml +13 -0
  7. data/app/components/daisy/actions/dropdown_component.rb +48 -0
  8. data/app/components/daisy/actions/modal_component.html.haml +31 -0
  9. data/app/components/daisy/actions/modal_component.rb +92 -0
  10. data/app/components/daisy/actions/swap_component.html.haml +17 -0
  11. data/app/components/daisy/actions/swap_component.rb +56 -0
  12. data/app/components/daisy/actions/theme_controller_component.html.haml +5 -0
  13. data/app/components/daisy/actions/theme_controller_component.rb +8 -0
  14. data/app/components/daisy/data_display/accordion_component.html.haml +4 -0
  15. data/app/components/daisy/data_display/accordion_component.rb +82 -0
  16. data/app/components/daisy/data_display/avatar_component.html.haml +9 -0
  17. data/app/components/daisy/data_display/avatar_component.rb +60 -0
  18. data/app/components/daisy/data_display/badge_component.html.haml +2 -0
  19. data/app/components/daisy/data_display/badge_component.rb +27 -0
  20. data/app/components/daisy/data_display/card_component.html.haml +17 -0
  21. data/app/components/daisy/data_display/card_component.rb +53 -0
  22. data/app/components/daisy/data_display/carousel_component.html.haml +3 -0
  23. data/app/components/daisy/data_display/carousel_component.rb +17 -0
  24. data/app/components/daisy/data_display/chat_component.html.haml +14 -0
  25. data/app/components/daisy/data_display/chat_component.rb +38 -0
  26. data/app/components/daisy/data_display/collapse_component.html.haml +13 -0
  27. data/app/components/daisy/data_display/collapse_component.rb +37 -0
  28. data/app/components/daisy/data_display/countdown_component.html.haml +24 -0
  29. data/app/components/daisy/data_display/countdown_component.rb +70 -0
  30. data/app/components/daisy/data_display/countdown_controller.js +78 -0
  31. data/app/components/daisy/data_display/diff_component.html.haml +6 -0
  32. data/app/components/daisy/data_display/diff_component.rb +25 -0
  33. data/app/components/daisy/data_display/kbd_component.html.haml +2 -0
  34. data/app/components/daisy/data_display/kbd_component.rb +21 -0
  35. data/app/components/daisy/data_display/stat_component.html.haml +27 -0
  36. data/app/components/daisy/data_display/stat_component.rb +41 -0
  37. data/app/components/daisy/data_display/table_component.html.haml +14 -0
  38. data/app/components/daisy/data_display/table_component.rb +148 -0
  39. data/app/components/daisy/data_display/timeline_component.html.haml +7 -0
  40. data/app/components/daisy/data_display/timeline_component.rb +8 -0
  41. data/app/components/daisy/data_display/timeline_event_component.html.haml +28 -0
  42. data/app/components/daisy/data_display/timeline_event_component.rb +47 -0
  43. data/app/components/daisy/feedback/alert_component.html.haml +8 -0
  44. data/app/components/daisy/feedback/alert_component.rb +19 -0
  45. data/app/components/daisy/layout/join_component.rb +15 -0
  46. data/app/components/daisy/navigation/bottom_nav_component.rb +59 -0
  47. data/app/components/daisy/navigation/breadcrumbs_component.html.haml +7 -0
  48. data/app/components/daisy/navigation/breadcrumbs_component.rb +15 -0
  49. data/app/components/daisy/navigation/link_component.html.haml +4 -0
  50. data/app/components/daisy/navigation/link_component.rb +34 -0
  51. data/app/components/daisy/navigation/menu_component.html.haml +3 -0
  52. data/app/components/daisy/navigation/menu_component.rb +49 -0
  53. data/app/components/daisy/navigation/navbar_component.html.haml +4 -0
  54. data/app/components/daisy/navigation/navbar_component.rb +12 -0
  55. data/app/components/daisy/navigation/steps_component.rb +40 -0
  56. data/app/components/daisy/navigation/tabs_component.html.haml +4 -0
  57. data/app/components/daisy/navigation/tabs_component.rb +107 -0
  58. data/app/components/hero/icon_component.rb +18 -0
  59. data/lib/daisy/helpers.rb +61 -0
  60. data/lib/daisy.rb +19 -0
  61. data/lib/loco_motion/base_component.rb +371 -0
  62. data/lib/loco_motion/basic_component.rb +18 -0
  63. data/lib/loco_motion/component_config.rb +165 -0
  64. data/lib/loco_motion/engine.rb +8 -0
  65. data/lib/loco_motion/errors.rb +33 -0
  66. data/lib/loco_motion.rb +48 -0
  67. metadata +408 -0
@@ -0,0 +1,13 @@
1
+ = part(:component) do
2
+ - if @checkbox
3
+ %input{ type: "checkbox" }
4
+
5
+ - if has_title?
6
+ - if title?
7
+ = title
8
+ - elsif @simple_title.present?
9
+ = part(:title) do
10
+ = @simple_title
11
+
12
+ = part(:wrapper) do
13
+ = content
@@ -0,0 +1,37 @@
1
+ class Daisy::DataDisplay::CollapseComponent < LocoMotion.configuration.base_component_class
2
+ define_parts :title, :wrapper
3
+
4
+ renders_one :title
5
+
6
+ attr_reader :simple_title, :checkbox
7
+
8
+ def initialize(*args, **kws, &block)
9
+ super
10
+
11
+ @simple_title = config_option(:title)
12
+ @checkbox = config_option(:checkbox, true)
13
+ end
14
+
15
+ def before_render
16
+ setup_component
17
+ setup_title
18
+ setup_wrapper
19
+ end
20
+
21
+ def setup_component
22
+ add_css(:component, "collapse")
23
+ add_html(:component, { tabindex: 0 }) unless @checkbox
24
+ end
25
+
26
+ def setup_title
27
+ add_css(:title, "collapse-title")
28
+ end
29
+
30
+ def setup_wrapper
31
+ add_css(:wrapper, "collapse-content")
32
+ end
33
+
34
+ def has_title?
35
+ title? || @simple_title.present?
36
+ end
37
+ end
@@ -0,0 +1,24 @@
1
+ = part(:component) do
2
+ - if dparts[:days]
3
+ = part(:days) do
4
+ %span{style: "--value: #{dparts[:days]}"}
5
+
6
+ = days_separator
7
+
8
+ - if dparts[:hours]
9
+ = part(:hours) do
10
+ %span{style: "--value: #{dparts[:hours]}"}
11
+
12
+ = hours_separator
13
+
14
+ - if dparts[:minutes]
15
+ = part(:minutes) do
16
+ %span{style: "--value: #{dparts[:minutes]}"}
17
+
18
+ = minutes_separator
19
+
20
+ - if dparts[:seconds]
21
+ = part(:seconds) do
22
+ %span{style: "--value: #{dparts[:seconds]}"}
23
+
24
+ = seconds_separator
@@ -0,0 +1,70 @@
1
+ class Daisy::DataDisplay::CountdownComponent < LocoMotion.configuration.base_component_class
2
+
3
+ define_parts :days, :hours, :minutes, :seconds
4
+ define_modifiers :words, :letters
5
+
6
+ def initialize(*args, **kws, &block)
7
+ super
8
+
9
+ @duration = config_option(:duration, args[0])
10
+ @separator = config_option(:separator, ":")
11
+ @parts_css = config_option(:parts_css)
12
+ @parts_html = config_option(:parts_html)
13
+ end
14
+
15
+ def before_render
16
+ add_stimulus_controller(:component, "countdown")
17
+
18
+ add_css(:component, "flex")
19
+ add_css(:component, "[:where(&)]:gap-x-2") if modifiers.include?(:words)
20
+
21
+ %i(days hours minutes seconds).each do |part|
22
+ default_html = {
23
+ data: {
24
+ # Note: We can't use nested hashes here because the Rails content_tag
25
+ # helper is stupid and won't traverse them.
26
+ "countdown-target": part
27
+ }
28
+ }
29
+
30
+ add_css(part, "countdown")
31
+ add_css(part, "[:where(&)]:gap-x-1") if modifiers.include?(:words)
32
+ add_css(part, @parts_css) if @parts_css
33
+
34
+ add_html(part, default_html)
35
+ add_html(part, @parts_html) if @parts_html
36
+ end
37
+ end
38
+
39
+ def dparts
40
+ @duration&.parts || {}
41
+ end
42
+
43
+ def days_separator
44
+ return unless dparts[:days] && dparts[:hours]
45
+ return "days" if modifiers.include?(:words)
46
+ return "d" if modifiers.include?(:letters)
47
+ return @separator
48
+ end
49
+
50
+ def hours_separator
51
+ return unless dparts[:hours] && dparts[:minutes]
52
+ return "hours" if modifiers.include?(:words)
53
+ return "h" if modifiers.include?(:letters)
54
+ return @separator
55
+ end
56
+
57
+ def minutes_separator
58
+ return unless dparts[:minutes] && dparts[:seconds]
59
+ return "minutes" if modifiers.include?(:words)
60
+ return "m" if modifiers.include?(:letters)
61
+ return @separator
62
+ end
63
+
64
+ def seconds_separator
65
+ return unless dparts[:seconds]
66
+ return "seconds" if modifiers.include?(:words)
67
+ return "s" if modifiers.include?(:letters)
68
+ return nil # No default separator for seconds
69
+ end
70
+ end
@@ -0,0 +1,78 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ export default class extends Controller {
4
+ static targets = ["days", "hours", "minutes", "seconds"]
5
+
6
+ connect() {
7
+ this.days = this.getPartValue("days")
8
+ this.hours = this.getPartValue("hours")
9
+ this.minutes = this.getPartValue("minutes")
10
+ this.seconds = this.getPartValue("seconds")
11
+
12
+ this.initialSeconds =
13
+ this.days * 24 * 60 * 60 +
14
+ this.hours * 60 * 60 +
15
+ this.minutes * 60 +
16
+ this.seconds
17
+
18
+ this.totalSeconds = this.initialSeconds
19
+
20
+ this.startCountdown()
21
+ }
22
+
23
+ getPartValue(part) {
24
+ let target = null
25
+
26
+ switch (part) {
27
+ case "days":
28
+ target = this.hasDaysTarget ? this.daysTarget : null
29
+ break
30
+ case "hours":
31
+ target = this.hasHoursTarget ? this.hoursTarget : null
32
+ break
33
+ case "minutes":
34
+ target = this.hasMinutesTarget ? this.minutesTarget : null
35
+ break
36
+ case "seconds":
37
+ target = this.hasSecondsTarget ? this.secondsTarget : null
38
+ break
39
+ }
40
+
41
+ return target?.querySelector("span")?.style?.getPropertyValue("--value") || 0
42
+ }
43
+
44
+ startCountdown() {
45
+ this.interval = setInterval(() => {
46
+ this.totalSeconds--
47
+
48
+ if (this.totalSeconds <= 0) {
49
+ clearInterval(this.interval)
50
+ }
51
+
52
+ this.updateCountdown()
53
+ }, 1000)
54
+ }
55
+
56
+ updateCountdown() {
57
+ let days = Math.floor(this.totalSeconds / (60 * 60 * 24))
58
+ let hours = Math.floor((this.totalSeconds % (60 * 60 * 24)) / (60 * 60))
59
+ let minutes = Math.floor((this.totalSeconds % (60 * 60)) / 60)
60
+ let seconds = Math.floor(this.totalSeconds % 60)
61
+
62
+ if (this.hasDaysTarget) {
63
+ this.daysTarget?.querySelector("span")?.style?.setProperty("--value", days)
64
+ }
65
+
66
+ if (this.hasHoursTarget) {
67
+ this.hoursTarget?.querySelector("span")?.style?.setProperty("--value", hours)
68
+ }
69
+
70
+ if (this.hasMinutesTarget) {
71
+ this.minutesTarget?.querySelector("span")?.style?.setProperty("--value", minutes)
72
+ }
73
+
74
+ if (this.hasSecondsTarget) {
75
+ this.secondsTarget?.querySelector("span")?.style?.setProperty("--value", seconds)
76
+ }
77
+ }
78
+ }
@@ -0,0 +1,6 @@
1
+ = part(:component) do
2
+ - items.each.with_index do |item, index|
3
+ - item.set_index(index + 1)
4
+ = item
5
+
6
+ = part(:resizer)
@@ -0,0 +1,25 @@
1
+ class Daisy::DataDisplay::DiffComponent < LocoMotion.configuration.base_component_class
2
+ class ItemComponent < LocoMotion::BasicComponent
3
+ def set_index(index)
4
+ @index = index
5
+ end
6
+
7
+ def before_render
8
+ # Because we're using the same component for many items, we need to
9
+ # manually specify the item class names to make sure Tailwind picks them
10
+ # up and includes them in the final CSS.
11
+ #
12
+ # diff-item-1 diff-item-2
13
+ add_css(:component, "diff-item-#{@index}")
14
+ end
15
+ end
16
+
17
+ define_part :resizer
18
+
19
+ renders_many :items, ItemComponent
20
+
21
+ def before_render
22
+ add_css(:component, "diff")
23
+ add_css(:resizer, "diff-resizer")
24
+ end
25
+ end
@@ -0,0 +1,2 @@
1
+ = part(:component) do
2
+ = content
@@ -0,0 +1,21 @@
1
+ # This is the Kbd (Keyboard) component.
2
+ class Daisy::DataDisplay::KbdComponent < LocoMotion.configuration.base_component_class
3
+ set_component_name :kbd
4
+
5
+ # Create a new kbd component.
6
+ def initialize(*args, **kws, &block)
7
+ super
8
+
9
+ set_tag_name(:component, :span)
10
+ end
11
+
12
+ def before_render
13
+ setup_component
14
+ end
15
+
16
+ private
17
+
18
+ def setup_component
19
+ add_css(:component, "kbd")
20
+ end
21
+ end
@@ -0,0 +1,27 @@
1
+ = part(:component) do
2
+
3
+ - if figure? || @icon.present? || @src.present?
4
+ = part(:figure) do
5
+ - if figure?
6
+ = figure
7
+ - if @icon.present?
8
+ = heroicon_tag(@icon, class: "inline-block h-8 w-8 stroke-current")
9
+ - if @src.present?
10
+ = daisy_avatar(src: @src)
11
+
12
+
13
+ - if title?
14
+ = title
15
+ - elsif @simple_title.present?
16
+ = part(:title) do
17
+ = @simple_title
18
+
19
+ = part(:value) do
20
+ = content
21
+
22
+ - if description?
23
+ = description
24
+ - elsif @simple_description.present?
25
+ = part(:description) do
26
+ = @simple_description
27
+
@@ -0,0 +1,41 @@
1
+ # This is the stats component.
2
+ class Daisy::DataDisplay::StatComponent < LocoMotion.configuration.base_component_class
3
+ set_component_name :stat
4
+
5
+ define_parts :title, :value, :description, :figure
6
+
7
+ renders_one :title
8
+ renders_one :description
9
+ renders_one :figure
10
+
11
+ attr_reader :simple_title
12
+ attr_reader :simple_description
13
+
14
+ # Create a new stat component.
15
+ def initialize(*args, **kws, &block)
16
+ super
17
+
18
+ @simple_title = config_option(:title)
19
+ @simple_description = config_option(:description)
20
+ @src = config_option(:src)
21
+ @icon = config_option(:icon)
22
+ end
23
+
24
+ def before_render
25
+ setup_component
26
+ end
27
+
28
+ private
29
+
30
+ def setup_component
31
+ add_css(:component, "stat")
32
+ add_css(:title, "stat-title")
33
+ add_css(:value, "stat-value")
34
+ add_css(:description, "stat-desc")
35
+ add_css(:figure, "stat-figure")
36
+
37
+ if @src.nil?
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,14 @@
1
+ = part(:component) do
2
+ -# If we have sections, show them
3
+ - if sections?
4
+ - sections.each do |section|
5
+ = section
6
+ - else
7
+ = head if head?
8
+
9
+ - if body?
10
+ = body
11
+ - else
12
+ %tbody
13
+ - rows.each do |row|
14
+ = row
@@ -0,0 +1,148 @@
1
+ #
2
+ # The Table component is used to render HTML tables with rows, columns, and headers.
3
+ #
4
+ # @!parse class Daisy::DataDisplay::TableComponent < LocoMotion::BaseComponent; end
5
+ class Daisy::DataDisplay::TableComponent < LocoMotion.configuration.base_component_class
6
+
7
+ class HeadColumnComponent < LocoMotion::BasicComponent
8
+ def before_render
9
+ set_tag_name :component, :th
10
+ end
11
+ end
12
+
13
+ class HeadComponent < LocoMotion::BasicComponent
14
+ renders_many :columns, HeadColumnComponent
15
+
16
+ def before_render
17
+ set_tag_name :component, :thead
18
+ end
19
+
20
+ def call
21
+ part(:component) do
22
+ content_tag(:tr) do
23
+ columns.each do |column|
24
+ concat(column)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ class BodyColumnComponent < LocoMotion::BasicComponent
32
+ def before_render
33
+ set_tag_name :component, :td
34
+ end
35
+ end
36
+
37
+ class BodyRowComponent < LocoMotion::BasicComponent
38
+ renders_many :columns, BodyColumnComponent
39
+
40
+ def before_render
41
+ set_tag_name :component, :tr
42
+ end
43
+
44
+ def call
45
+ part(:component) do
46
+ columns.each do |column|
47
+ concat(column)
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ class BodyComponent < LocoMotion::BasicComponent
54
+ renders_many :rows, BodyRowComponent
55
+
56
+ def before_render
57
+ set_tag_name :component, :tbody
58
+ end
59
+
60
+ def call
61
+ part(:component) do
62
+ content_tag(:tr) do
63
+ rows.each do |row|
64
+ concat(row)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ class SectionComponent < LocoMotion::BasicComponent
72
+ renders_one :head, HeadComponent
73
+ renders_one :body, BodyComponent
74
+ renders_many :rows, BodyRowComponent
75
+
76
+ def before_render
77
+ set_tag_name :component, :section
78
+ end
79
+
80
+ def call
81
+ # Sections can't be rendered inside a <table> tag, so we don't render the
82
+ # typical `part(:component)` here.
83
+ concat(head) if head?
84
+
85
+ if body?
86
+ concat(body)
87
+ else
88
+ content_tag(:tbody) do
89
+ rows.each do |row|
90
+ concat(row)
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+
97
+ renders_one :head, HeadComponent
98
+ renders_one :body, BodyComponent
99
+ renders_many :rows, BodyRowComponent
100
+ renders_many :sections, SectionComponent
101
+
102
+ set_component_name :table
103
+
104
+ #
105
+ # Instantiate a new Table component. This component takes no content, but
106
+ # requires you to utilize the optional <code>head</code> slot, and one of the
107
+ # <code>body</code> or <code>rows</code> slots.
108
+ #
109
+ # @example
110
+ # = daisy_table do |table|
111
+ # - table.head do |head|
112
+ # - head.with_column do
113
+ # Column 1
114
+ # - head.with_column do
115
+ # Column 2
116
+ #
117
+ # - table.with_row do |row|
118
+ # - row.with_column do
119
+ # Row 1 - Column 1
120
+ # - row.with_column do
121
+ # Row 1 - Column 2
122
+ # - table.with_row do |row|
123
+ # - row.with_column do
124
+ # Row 2 - Column 1
125
+ # - row.with_column do
126
+ # Row 2 - Column 2
127
+ #
128
+ # For more complex tables, you can use the <code>section</code> slot which
129
+ # takes a <code>head</code> and <code>body</code> slot allowing you to build a
130
+ # table with multiple headers and bodies.
131
+ #
132
+ # Please see the demo for more examples of usage.
133
+ #
134
+ def initialize(*args, **kws, &block)
135
+ super
136
+ end
137
+
138
+ def before_render
139
+ setup_component
140
+ end
141
+
142
+ private
143
+
144
+ def setup_component
145
+ set_tag_name(:component, :table)
146
+ add_css(:component, "table")
147
+ end
148
+ end
@@ -0,0 +1,7 @@
1
+ = part(:component) do
2
+ - events.each.with_index do |event, index|
3
+ - event.set_event_index(index)
4
+ - event.set_events_length(events.length)
5
+ = event
6
+
7
+ = content
@@ -0,0 +1,8 @@
1
+ class Daisy::DataDisplay::TimelineComponent < LocoMotion.configuration.base_component_class
2
+ renders_many :events, Daisy::DataDisplay::TimelineEventComponent
3
+
4
+ def before_render
5
+ set_tag_name(:component, :ul)
6
+ add_css(:component, "timeline")
7
+ end
8
+ end
@@ -0,0 +1,28 @@
1
+ = part(:component) do
2
+ - if @event_index.present? && @event_index > 0
3
+ = part(:separator)
4
+
5
+ - if start?
6
+ = start
7
+ - elsif @simple_start
8
+ = part(:start) do
9
+ = @simple_start
10
+
11
+ - if middle?
12
+ = middle
13
+ - elsif @simple_middle
14
+ = part(:middle) do
15
+ = @simple_middle
16
+ - elsif @simple_middle_icon
17
+ = part(:middle) do
18
+ = hero_icon(@simple_middle_icon, **rendered_html(:simple_middle_icon))
19
+
20
+ - if end?
21
+ - # end is a reserved word in Ruby, so we have to use send to call it
22
+ = self.send(:end)
23
+ - elsif @simple_end
24
+ = part(:end) do
25
+ = @simple_end
26
+
27
+ - if @events_length.present? && @event_index.present? && @event_index < @events_length - 1
28
+ = part(:separator)
@@ -0,0 +1,47 @@
1
+ class Daisy::DataDisplay::TimelineEventComponent < LocoMotion.configuration.base_component_class
2
+ renders_one :start, LocoMotion::BasicComponent.build(css: "timeline-start")
3
+ renders_one :middle, LocoMotion::BasicComponent.build(css: "timeline-middle")
4
+ renders_one :end, LocoMotion::BasicComponent.build(css: "timeline-end")
5
+
6
+ define_parts :start, :middle, :middle_icon, :end, :separator
7
+
8
+ #
9
+ # middle and middle_icon are mutually exclusive, middle takes presedence
10
+ #
11
+ def initialize(*args, **kws, &block)
12
+ super(*args, **kws, &block)
13
+
14
+ @event_index = nil
15
+ @events_length = nil
16
+
17
+ @simple_start = config_option(:start)
18
+ @simple_middle = config_option(:middle)
19
+ @simple_middle_icon = config_option(:middle_icon)
20
+ @simple_end = config_option(:end)
21
+ end
22
+
23
+ def before_render
24
+ set_tag_name(:component, :li)
25
+
26
+ setup_parts
27
+ setup_separator
28
+ end
29
+
30
+ def setup_parts
31
+ add_css(:start, "timeline-start")
32
+ add_css(:middle, "timeline-middle")
33
+ add_css(:end, "timeline-end")
34
+ end
35
+
36
+ def setup_separator
37
+ set_tag_name(:separator, :hr)
38
+ end
39
+
40
+ def set_event_index(index)
41
+ @event_index = index
42
+ end
43
+
44
+ def set_events_length(length)
45
+ @events_length = length
46
+ end
47
+ end
@@ -0,0 +1,8 @@
1
+ = part(:component) do
2
+ - # If we have an icon, assume we want to wrap the content
3
+ - if @icon.present?
4
+ = hero_icon(@icon, **rendered_html(:icon))
5
+ = part(:content_wrapper) do
6
+ = content
7
+ - else
8
+ = content
@@ -0,0 +1,19 @@
1
+ #
2
+ # The AlertComponent displays an important message to users.
3
+ #
4
+ class Daisy::Feedback::AlertComponent < LocoMotion.configuration.base_component_class
5
+ define_parts :icon, :content_wrapper
6
+
7
+ def initialize(*args, **kws, &block)
8
+ super
9
+
10
+ @icon = config_option(:icon)
11
+ end
12
+
13
+ def before_render
14
+ add_css(:component, "alert")
15
+ add_html(:component, { role: "alert" })
16
+
17
+ add_css(:icon, "[:where(&)]:size-8")
18
+ end
19
+ end
@@ -0,0 +1,15 @@
1
+ class Daisy::Layout::JoinComponent < LocoMotion.configuration.base_component_class
2
+ renders_many :items
3
+
4
+ def before_render
5
+ add_css(:component, "join")
6
+ end
7
+
8
+ def call
9
+ part(:component) do
10
+ items.each do |item|
11
+ concat(item)
12
+ end
13
+ end
14
+ end
15
+ end