loco_motion-rails 0.0.6

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.
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