ruby_ui 1.0.0.beta1 → 1.0.0.rc1

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 (78) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +21 -0
  3. data/README.md +85 -0
  4. data/lib/generators/ruby_ui/component_generator.rb +4 -40
  5. data/lib/generators/ruby_ui/dependencies.yml +74 -0
  6. data/lib/generators/ruby_ui/install/install_generator.rb +21 -22
  7. data/lib/generators/ruby_ui/install/templates/ruby_ui.rb.erb +18 -0
  8. data/lib/generators/ruby_ui/install/templates/tailwind.css.erb +156 -0
  9. data/lib/generators/ruby_ui/javascript_utils.rb +21 -0
  10. data/lib/ruby_ui/accordion/accordion_controller.js +97 -0
  11. data/lib/ruby_ui/alert/alert.rb +1 -1
  12. data/lib/ruby_ui/alert_dialog/alert_dialog_content.rb +1 -1
  13. data/lib/ruby_ui/alert_dialog/alert_dialog_controller.js +31 -0
  14. data/lib/ruby_ui/alert_dialog/alert_dialog_footer.rb +1 -1
  15. data/lib/ruby_ui/alert_dialog/alert_dialog_header.rb +1 -1
  16. data/lib/ruby_ui/breadcrumb/breadcrumb.rb +17 -0
  17. data/lib/ruby_ui/breadcrumb/breadcrumb_ellipsis.rb +39 -0
  18. data/lib/ruby_ui/breadcrumb/breadcrumb_item.rb +17 -0
  19. data/lib/ruby_ui/breadcrumb/breadcrumb_link.rb +22 -0
  20. data/lib/ruby_ui/breadcrumb/breadcrumb_list.rb +17 -0
  21. data/lib/ruby_ui/breadcrumb/breadcrumb_page.rb +19 -0
  22. data/lib/ruby_ui/breadcrumb/breadcrumb_separator.rb +38 -0
  23. data/lib/ruby_ui/calendar/calendar_controller.js +249 -0
  24. data/lib/ruby_ui/calendar/calendar_input_controller.js +8 -0
  25. data/lib/ruby_ui/carousel/carousel.rb +44 -0
  26. data/lib/ruby_ui/carousel/carousel_content.rb +23 -0
  27. data/lib/ruby_ui/carousel/carousel_controller.js +60 -0
  28. data/lib/ruby_ui/carousel/carousel_item.rb +23 -0
  29. data/lib/ruby_ui/carousel/carousel_next.rb +48 -0
  30. data/lib/ruby_ui/carousel/carousel_previous.rb +49 -0
  31. data/lib/ruby_ui/chart/chart_controller.js +103 -0
  32. data/lib/ruby_ui/checkbox/checkbox_group_controller.js +21 -0
  33. data/lib/ruby_ui/clipboard/clipboard_controller.js +54 -0
  34. data/lib/ruby_ui/collapsible/collapsible_controller.js +47 -0
  35. data/lib/ruby_ui/combobox/combobox.rb +8 -6
  36. data/lib/ruby_ui/combobox/combobox_checkbox.rb +25 -0
  37. data/lib/ruby_ui/combobox/combobox_controller.js +176 -0
  38. data/lib/ruby_ui/combobox/{combobox_empty.rb → combobox_empty_state.rb} +2 -2
  39. data/lib/ruby_ui/combobox/combobox_item.rb +9 -37
  40. data/lib/ruby_ui/combobox/combobox_list.rb +2 -11
  41. data/lib/ruby_ui/combobox/combobox_list_group.rb +20 -0
  42. data/lib/ruby_ui/combobox/combobox_popover.rb +30 -0
  43. data/lib/ruby_ui/combobox/combobox_radio.rb +26 -0
  44. data/lib/ruby_ui/combobox/combobox_search_input.rb +21 -24
  45. data/lib/ruby_ui/combobox/combobox_toggle_all_checkbox.rb +25 -0
  46. data/lib/ruby_ui/combobox/combobox_trigger.rb +25 -20
  47. data/lib/ruby_ui/command/command_controller.js +136 -0
  48. data/lib/ruby_ui/context_menu/context_menu_controller.js +144 -0
  49. data/lib/ruby_ui/dialog/dialog_content.rb +2 -2
  50. data/lib/ruby_ui/dialog/dialog_controller.js +32 -0
  51. data/lib/ruby_ui/dialog/dialog_footer.rb +1 -1
  52. data/lib/ruby_ui/dialog/dialog_header.rb +1 -1
  53. data/lib/ruby_ui/dropdown_menu/dropdown_menu_controller.js +120 -0
  54. data/lib/ruby_ui/form/form_field_controller.js +61 -0
  55. data/lib/ruby_ui/hover_card/hover_card_controller.js +144 -0
  56. data/lib/ruby_ui/masked_input/masked_input_controller.js +9 -0
  57. data/lib/ruby_ui/popover/popover_controller.js +107 -0
  58. data/lib/ruby_ui/progress/progress.rb +37 -0
  59. data/lib/ruby_ui/radio_button/radio_button.rb +4 -1
  60. data/lib/ruby_ui/select/select_content.rb +1 -1
  61. data/lib/ruby_ui/select/select_controller.js +171 -0
  62. data/lib/ruby_ui/select/select_item_controller.js +11 -0
  63. data/lib/ruby_ui/select/select_value.rb +1 -1
  64. data/lib/ruby_ui/separator/separator.rb +38 -0
  65. data/lib/ruby_ui/sheet/sheet_content.rb +1 -1
  66. data/lib/ruby_ui/sheet/sheet_content_controller.js +7 -0
  67. data/lib/ruby_ui/sheet/sheet_controller.js +9 -0
  68. data/lib/ruby_ui/{combobox/combobox_separator.rb → skeleton/skeleton.rb} +4 -2
  69. data/lib/ruby_ui/switch/switch.rb +24 -0
  70. data/lib/ruby_ui/tabs/tabs_controller.js +45 -0
  71. data/lib/ruby_ui/theme_toggle/theme_toggle_controller.js +30 -0
  72. data/lib/ruby_ui/tooltip/tooltip_controller.js +37 -0
  73. data/lib/ruby_ui.rb +1 -1
  74. metadata +57 -11
  75. data/lib/ruby_ui/combobox/combobox_content.rb +0 -31
  76. data/lib/ruby_ui/combobox/combobox_group.rb +0 -38
  77. data/lib/ruby_ui/combobox/combobox_input.rb +0 -22
  78. data/lib/ruby_ui/combobox/combobox_value.rb +0 -27
@@ -10,7 +10,7 @@ module RubyUI
10
10
 
11
11
  def default_attrs
12
12
  {
13
- class: "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2"
13
+ class: "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 rtl:space-x-reverse"
14
14
  }
15
15
  end
16
16
  end
@@ -10,7 +10,7 @@ module RubyUI
10
10
 
11
11
  def default_attrs
12
12
  {
13
- class: "flex flex-col space-y-2 text-center sm:text-left"
13
+ class: "flex flex-col space-y-2 text-center sm:text-left rtl:sm:text-right"
14
14
  }
15
15
  end
16
16
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyUI
4
+ class Breadcrumb < Base
5
+ def view_template(&)
6
+ nav(**attrs, &)
7
+ end
8
+
9
+ private
10
+
11
+ def default_attrs
12
+ {
13
+ aria: {label: "breadcrumb"}
14
+ }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyUI
4
+ class BreadcrumbEllipsis < Base
5
+ def view_template(&)
6
+ span(**attrs) do
7
+ icon
8
+ span(class: "sr-only") { "More" }
9
+ end
10
+ end
11
+
12
+ private
13
+
14
+ def icon
15
+ svg(
16
+ xmlns: "http://www.w3.org/2000/svg",
17
+ class: "w-4 h-4",
18
+ viewbox: "0 0 24 24",
19
+ fill: "none",
20
+ stroke: "currentColor",
21
+ stroke_width: "2",
22
+ stroke_linecap: "round",
23
+ stroke_linejoin: "round"
24
+ ) do |s|
25
+ s.circle(cx: "12", cy: "12", r: "1")
26
+ s.circle(cx: "19", cy: "12", r: "1")
27
+ s.circle(cx: "5", cy: "12", r: "1")
28
+ end
29
+ end
30
+
31
+ def default_attrs
32
+ {
33
+ aria: {hidden: true},
34
+ class: "flex h-9 w-9 items-center justify-center",
35
+ role: "presentation"
36
+ }
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyUI
4
+ class BreadcrumbItem < Base
5
+ def view_template(&)
6
+ li(**attrs, &)
7
+ end
8
+
9
+ private
10
+
11
+ def default_attrs
12
+ {
13
+ class: "inline-flex items-center gap-1.5"
14
+ }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyUI
4
+ class BreadcrumbLink < Base
5
+ def initialize(href: "#", **attrs)
6
+ @href = href
7
+ super(**attrs)
8
+ end
9
+
10
+ def view_template(&)
11
+ a(href: @href, **attrs, &)
12
+ end
13
+
14
+ private
15
+
16
+ def default_attrs
17
+ {
18
+ class: "transition-colors hover:text-foreground"
19
+ }
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyUI
4
+ class BreadcrumbList < Base
5
+ def view_template(&)
6
+ ol(**attrs, &)
7
+ end
8
+
9
+ private
10
+
11
+ def default_attrs
12
+ {
13
+ class: "flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5"
14
+ }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyUI
4
+ class BreadcrumbPage < Base
5
+ def view_template(&)
6
+ span(**attrs, &)
7
+ end
8
+
9
+ private
10
+
11
+ def default_attrs
12
+ {
13
+ aria: {disabled: true, current: "page"},
14
+ class: "font-normal text-foreground",
15
+ role: "link"
16
+ }
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyUI
4
+ class BreadcrumbSeparator < Base
5
+ def view_template(&block)
6
+ li(**attrs) do
7
+ if block
8
+ block.call
9
+ else
10
+ icon
11
+ end
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def icon
18
+ svg(
19
+ xmlns: "http://www.w3.org/2000/svg",
20
+ class: "w-4 h-4",
21
+ viewbox: "0 0 24 24",
22
+ fill: "none",
23
+ stroke: "currentColor",
24
+ stroke_width: "2",
25
+ stroke_linecap: "round",
26
+ stroke_linejoin: "round"
27
+ ) { |s| s.path(d: "m9 18 6-6-6-6") }
28
+ end
29
+
30
+ def default_attrs
31
+ {
32
+ aria: {hidden: true},
33
+ class: "[&>svg]:w-3.5 [&>svg]:h-3.5",
34
+ role: "presentation"
35
+ }
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,249 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+ import Mustache from "mustache";
3
+
4
+ export default class extends Controller {
5
+ static targets = [
6
+ "calendar",
7
+ "title",
8
+ "weekdaysTemplate",
9
+ "selectedDateTemplate",
10
+ "todayDateTemplate",
11
+ "currentMonthDateTemplate",
12
+ "otherMonthDateTemplate",
13
+ ];
14
+ static values = {
15
+ selectedDate: {
16
+ type: String,
17
+ default: null,
18
+ },
19
+ viewDate: {
20
+ type: String,
21
+ default: new Date().toISOString().slice(0, 10),
22
+ },
23
+ format: {
24
+ type: String,
25
+ default: "yyyy-MM-dd", // Default format
26
+ },
27
+ };
28
+ static outlets = ["ruby-ui--calendar-input"];
29
+
30
+ initialize() {
31
+ this.updateCalendar(); // Initial calendar render
32
+ }
33
+
34
+ nextMonth(e) {
35
+ e.preventDefault();
36
+ this.viewDateValue = this.adjustMonth(1);
37
+ }
38
+
39
+ prevMonth(e) {
40
+ e.preventDefault();
41
+ this.viewDateValue = this.adjustMonth(-1);
42
+ }
43
+
44
+ selectDay(e) {
45
+ e.preventDefault();
46
+ // Set the selected date value
47
+ this.selectedDateValue = e.currentTarget.dataset.day;
48
+ }
49
+
50
+ selectedDateValueChanged(value, prevValue) {
51
+ // update the viewDateValue to the first day of month of the selected date (This will trigger updateCalendar() function)
52
+ const newViewDate = new Date(this.selectedDateValue);
53
+ newViewDate.setDate(2); // set the day to the 2nd (to avoid issues with months with different number of days and timezones)
54
+ this.viewDateValue = newViewDate.toISOString().slice(0, 10);
55
+
56
+ // Re-render the calendar
57
+ this.updateCalendar();
58
+
59
+ // update the input value
60
+ this.rubyUiCalendarInputOutlets.forEach((outlet) => {
61
+ const formattedDate = this.formatDate(this.selectedDate());
62
+ outlet.setValue(formattedDate);
63
+ });
64
+ }
65
+
66
+ viewDateValueChanged(value, prevValue) {
67
+ this.updateCalendar();
68
+ }
69
+
70
+ adjustMonth(adjustment) {
71
+ const date = this.viewDate();
72
+ date.setDate(2); // set the day to the 2nd (to avoid issues with months with different number of days and timezones)
73
+ date.setMonth(date.getMonth() + adjustment);
74
+ return date.toISOString().slice(0, 10);
75
+ }
76
+
77
+ updateCalendar() {
78
+ // Update the title with month and year
79
+ this.titleTarget.textContent = this.monthAndYear();
80
+ this.calendarTarget.innerHTML = this.calendarHTML();
81
+ }
82
+
83
+ calendarHTML() {
84
+ return this.weekdaysTemplateTarget.innerHTML + this.calendarDays();
85
+ }
86
+
87
+ calendarDays() {
88
+ return this.getFullWeeksStartAndEndInMonth()
89
+ .map((week) => this.renderWeek(week))
90
+ .join("");
91
+ }
92
+
93
+ renderWeek(week) {
94
+ const days = week
95
+ .map((day) => {
96
+ return this.renderDay(day);
97
+ })
98
+ .join("");
99
+ return `<tr class="flex w-full mt-2">${days}</tr>`;
100
+ }
101
+
102
+ renderDay(day) {
103
+ const today = new Date();
104
+ let dateHTML = "";
105
+ const data = { day: day, dayDate: day.getDate() };
106
+
107
+ if (day.toDateString() === this.selectedDate().toDateString()) {
108
+ // selectedDate
109
+ // Render the selected date template target innerHTML with Mustache
110
+ dateHTML = Mustache.render(
111
+ this.selectedDateTemplateTarget.innerHTML,
112
+ data,
113
+ );
114
+ } else if (day.toDateString() === today.toDateString()) {
115
+ // todayDate
116
+ dateHTML = Mustache.render(this.todayDateTemplateTarget.innerHTML, data);
117
+ } else if (day.getMonth() === this.viewDate().getMonth()) {
118
+ // currentMonthDate
119
+ dateHTML = Mustache.render(
120
+ this.currentMonthDateTemplateTarget.innerHTML,
121
+ data,
122
+ );
123
+ } else {
124
+ // otherMonthDate
125
+ dateHTML = Mustache.render(
126
+ this.otherMonthDateTemplateTarget.innerHTML,
127
+ data,
128
+ );
129
+ }
130
+ return dateHTML;
131
+ }
132
+
133
+ monthAndYear() {
134
+ const month = this.viewDate().toLocaleString("en-US", { month: "long" });
135
+ const year = this.viewDate().getFullYear();
136
+ return `${month} ${year}`;
137
+ }
138
+
139
+ selectedDate() {
140
+ return new Date(this.selectedDateValue);
141
+ }
142
+
143
+ viewDate() {
144
+ return this.viewDateValue
145
+ ? new Date(this.viewDateValue)
146
+ : this.selectedDate();
147
+ }
148
+
149
+ getFullWeeksStartAndEndInMonth() {
150
+ const month = this.viewDate().getMonth();
151
+ const year = this.viewDate().getFullYear();
152
+
153
+ let weeks = [],
154
+ firstDate = new Date(year, month, 1),
155
+ lastDate = new Date(year, month + 1, 0),
156
+ numDays = lastDate.getDate();
157
+
158
+ let start = 1;
159
+ let end;
160
+ if (firstDate.getDay() === 1) {
161
+ end = 7;
162
+ } else if (firstDate.getDay() === 0) {
163
+ let preMonthEndDay = new Date(year, month, 0);
164
+ start = preMonthEndDay.getDate() - 6 + 1;
165
+ end = 1;
166
+ } else {
167
+ let preMonthEndDay = new Date(year, month, 0);
168
+ start = preMonthEndDay.getDate() + 1 - firstDate.getDay() + 1;
169
+ end = 7 - firstDate.getDay() + 1;
170
+ weeks.push({
171
+ start: start,
172
+ end: end,
173
+ });
174
+ start = end + 1;
175
+ end = end + 7;
176
+ }
177
+ while (start <= numDays) {
178
+ weeks.push({
179
+ start: start,
180
+ end: end,
181
+ });
182
+ start = end + 1;
183
+ end = end + 7;
184
+ end = start === 1 && end === 8 ? 1 : end;
185
+ if (end > numDays && start <= numDays) {
186
+ end = end - numDays;
187
+ weeks.push({
188
+ start: start,
189
+ end: end,
190
+ });
191
+ break;
192
+ }
193
+ }
194
+ // *** the magic starts here
195
+ return weeks.map(({ start, end }, index) => {
196
+ const sub = +(start > end && index === 0);
197
+ return Array.from({ length: 7 }, (_, index) => {
198
+ const date = new Date(year, month - sub, start + index);
199
+ return date;
200
+ });
201
+ });
202
+ }
203
+
204
+ formatDate(date) {
205
+ const format = this.formatValue;
206
+ const day = date.getDate();
207
+ const month = date.getMonth() + 1;
208
+ const year = date.getFullYear();
209
+ const hours = date.getHours();
210
+ const minutes = date.getMinutes();
211
+ const seconds = date.getSeconds();
212
+ const dayOfWeek = date.toLocaleString("en-US", { weekday: "long" });
213
+ const monthName = date.toLocaleString("en-US", { month: "long" });
214
+ const daySuffix = this.getDaySuffix(day);
215
+
216
+ const map = {
217
+ yyyy: year,
218
+ MM: ("0" + month).slice(-2),
219
+ dd: ("0" + day).slice(-2),
220
+ HH: ("0" + hours).slice(-2),
221
+ mm: ("0" + minutes).slice(-2),
222
+ ss: ("0" + seconds).slice(-2),
223
+ EEEE: dayOfWeek,
224
+ MMMM: monthName,
225
+ do: day + daySuffix,
226
+ PPPP: `${dayOfWeek}, ${monthName} ${day}${daySuffix}, ${year}`,
227
+ };
228
+
229
+ const formattedDate = format.replace(
230
+ /yyyy|MM|dd|HH|mm|ss|EEEE|MMMM|do|PPPP/g,
231
+ (matched) => map[matched],
232
+ );
233
+ return formattedDate;
234
+ }
235
+
236
+ getDaySuffix(day) {
237
+ if (day > 3 && day < 21) return "th";
238
+ switch (day % 10) {
239
+ case 1:
240
+ return "st";
241
+ case 2:
242
+ return "nd";
243
+ case 3:
244
+ return "rd";
245
+ default:
246
+ return "th";
247
+ }
248
+ }
249
+ }
@@ -0,0 +1,8 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ // Connects to data-controller="input"
4
+ export default class extends Controller {
5
+ setValue(value) {
6
+ this.element.value = value
7
+ }
8
+ }
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyUI
4
+ class Carousel < Base
5
+ def initialize(orientation: :horizontal, options: {}, **user_attrs)
6
+ @orientation = orientation
7
+ @options = options
8
+
9
+ super(**user_attrs)
10
+ end
11
+
12
+ def view_template(&)
13
+ div(**attrs, &)
14
+ end
15
+
16
+ private
17
+
18
+ def default_attrs
19
+ {
20
+ class: ["relative group", orientation_classes],
21
+ role: "region",
22
+ aria_roledescription: "carousel",
23
+ data: {
24
+ controller: "ruby-ui--carousel",
25
+ ruby_ui__carousel_options_value: default_options.merge(@options).to_json,
26
+ action: %w[
27
+ keydown.right->ruby-ui--carousel#scrollNext:prevent
28
+ keydown.left->ruby-ui--carousel#scrollPrev:prevent
29
+ ]
30
+ }
31
+ }
32
+ end
33
+
34
+ def default_options
35
+ {
36
+ axis: (@orientation == :horizontal) ? "x" : "y"
37
+ }
38
+ end
39
+
40
+ def orientation_classes
41
+ (@orientation == :horizontal) ? "is-horizontal" : "is-vertical"
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyUI
4
+ class CarouselContent < Base
5
+ def view_template(&)
6
+ div(class: "overflow-hidden", data: {ruby_ui__carousel_target: "viewport"}) do
7
+ div(**attrs, &)
8
+ end
9
+ end
10
+
11
+ private
12
+
13
+ def default_attrs
14
+ {
15
+ class: [
16
+ "flex",
17
+ "group-[.is-horizontal]:-ml-4",
18
+ "group-[.is-vertical]:-mt-4 group-[.is-vertical]:flex-col"
19
+ ]
20
+ }
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,60 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+ import EmblaCarousel from 'embla-carousel'
3
+
4
+ const DEFAULT_OPTIONS = {
5
+ loop: true
6
+ }
7
+
8
+ export default class extends Controller {
9
+ static values = {
10
+ options: {
11
+ type: Object,
12
+ default: {},
13
+ }
14
+ }
15
+ static targets = ["viewport", "nextButton", "prevButton"]
16
+
17
+ connect() {
18
+ this.initCarousel(this.#mergedOptions)
19
+ }
20
+
21
+ disconnect() {
22
+ this.destroyCarousel()
23
+ }
24
+
25
+ initCarousel(options, plugins = []) {
26
+ this.carousel = EmblaCarousel(this.viewportTarget, options, plugins)
27
+
28
+ this.carousel.on("init", this.#updateControls.bind(this))
29
+ this.carousel.on("reInit", this.#updateControls.bind(this))
30
+ this.carousel.on("select", this.#updateControls.bind(this))
31
+ }
32
+
33
+ destroyCarousel() {
34
+ this.carousel.destroy()
35
+ }
36
+
37
+ scrollNext() {
38
+ this.carousel.scrollNext()
39
+ }
40
+
41
+ scrollPrev() {
42
+ this.carousel.scrollPrev()
43
+ }
44
+
45
+ #updateControls() {
46
+ this.#toggleButtonsDisabledState(this.nextButtonTargets, !this.carousel.canScrollNext())
47
+ this.#toggleButtonsDisabledState(this.prevButtonTargets, !this.carousel.canScrollPrev())
48
+ }
49
+
50
+ #toggleButtonsDisabledState(buttons, isDisabled) {
51
+ buttons.forEach((button) => button.disabled = isDisabled)
52
+ }
53
+
54
+ get #mergedOptions() {
55
+ return {
56
+ ...DEFAULT_OPTIONS,
57
+ ...this.optionsValue
58
+ }
59
+ }
60
+ }
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyUI
4
+ class CarouselItem < Base
5
+ def view_template(&)
6
+ div(**attrs, &)
7
+ end
8
+
9
+ private
10
+
11
+ def default_attrs
12
+ {
13
+ role: "group",
14
+ aria_roledescription: "slide",
15
+ class: [
16
+ "min-w-0 shrink-0 grow-0 basis-full",
17
+ "group-[.is-horizontal]:pl-4",
18
+ "group-[.is-vertical]:pt-4"
19
+ ]
20
+ }
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RubyUI
4
+ class CarouselNext < Base
5
+ def view_template(&)
6
+ Button(**attrs) do
7
+ icon
8
+ end
9
+ end
10
+
11
+ private
12
+
13
+ def default_attrs
14
+ {
15
+ variant: :outline,
16
+ icon: true,
17
+ class: [
18
+ "absolute h-8 w-8 rounded-full",
19
+ "group-[.is-horizontal]:-right-12 group-[.is-horizontal]:top-1/2 group-[.is-horizontal]:-translate-y-1/2",
20
+ "group-[.is-vertical]:-bottom-12 group-[.is-vertical]:left-1/2 group-[.is-vertical]:-translate-x-1/2 group-[.is-vertical]:rotate-90"
21
+ ],
22
+ disabled: true,
23
+ data: {
24
+ action: "click->ruby-ui--carousel#scrollNext",
25
+ ruby_ui__carousel_target: "nextButton"
26
+ }
27
+ }
28
+ end
29
+
30
+ def icon
31
+ svg(
32
+ width: "24",
33
+ height: "24",
34
+ viewBox: "0 0 24 24",
35
+ fill: "none",
36
+ stroke: "currentColor",
37
+ stroke_width: "2",
38
+ stroke_linecap: "round",
39
+ stroke_linejoin: "round",
40
+ xmlns: "http://www.w3.org/2000/svg",
41
+ class: "w-4 h-4"
42
+ ) do |s|
43
+ s.path(d: "M5 12h14")
44
+ s.path(d: "m12 5 7 7-7 7")
45
+ end
46
+ end
47
+ end
48
+ end