maquina-components 0.1.1 → 0.2.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.
Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +410 -13
  3. data/app/assets/images/maquina.svg +1 -0
  4. data/app/assets/stylesheets/alert.css +143 -0
  5. data/app/assets/stylesheets/badge.css +145 -0
  6. data/app/assets/stylesheets/breadcrumbs.css +163 -0
  7. data/app/assets/stylesheets/card.css +128 -0
  8. data/app/assets/stylesheets/dropdown_menu.css +248 -0
  9. data/app/assets/stylesheets/empty.css +133 -0
  10. data/app/assets/stylesheets/form.css +617 -0
  11. data/app/assets/stylesheets/header.css +61 -0
  12. data/app/assets/stylesheets/maquina_components.css +178 -0
  13. data/app/assets/stylesheets/pagination.css +154 -0
  14. data/app/assets/stylesheets/sidebar.css +477 -0
  15. data/app/assets/stylesheets/table.css +205 -0
  16. data/app/assets/stylesheets/toggle_group.css +151 -0
  17. data/app/assets/tailwind/maquina_components_engine/engine.css +16 -0
  18. data/app/helpers/maquina_components/breadcrumbs_helper.rb +118 -0
  19. data/app/helpers/maquina_components/dropdown_menu_helper.rb +249 -0
  20. data/app/helpers/maquina_components/empty_helper.rb +102 -0
  21. data/app/helpers/maquina_components/icons_helper.rb +161 -0
  22. data/app/helpers/maquina_components/pagination_helper.rb +153 -0
  23. data/app/helpers/maquina_components/sidebar_helper.rb +63 -0
  24. data/app/helpers/maquina_components/table_helper.rb +144 -0
  25. data/app/helpers/maquina_components/toggle_group_helper.rb +172 -0
  26. data/app/javascript/controllers/breadcrumb_controller.js +71 -0
  27. data/app/javascript/controllers/dropdown_menu_controller.js +203 -0
  28. data/app/javascript/controllers/menu_button_controller.js +59 -0
  29. data/app/javascript/controllers/sidebar_controller.js +316 -0
  30. data/app/javascript/controllers/sidebar_trigger_controller.js +32 -0
  31. data/app/javascript/controllers/toggle_group_controller.js +178 -0
  32. data/app/views/components/_alert.html.erb +12 -0
  33. data/app/views/components/_badge.html.erb +10 -0
  34. data/app/views/components/_breadcrumbs.html.erb +16 -0
  35. data/app/views/components/_card.html.erb +6 -0
  36. data/app/views/components/_dropdown.html.erb +25 -0
  37. data/app/views/components/_dropdown_menu.html.erb +9 -0
  38. data/app/views/components/_empty.html.erb +10 -0
  39. data/app/views/components/_header.html.erb +8 -0
  40. data/app/views/components/_menu_button.html.erb +44 -0
  41. data/app/views/components/_pagination.html.erb +13 -0
  42. data/app/views/components/_separator.html.erb +11 -0
  43. data/app/views/components/_sidebar.html.erb +40 -0
  44. data/app/views/components/_simple_table.html.erb +49 -0
  45. data/app/views/components/_table.html.erb +21 -0
  46. data/app/views/components/_toggle_group.html.erb +24 -0
  47. data/app/views/components/alert/_description.html.erb +6 -0
  48. data/app/views/components/alert/_title.html.erb +6 -0
  49. data/app/views/components/breadcrumbs/_ellipsis.html.erb +9 -0
  50. data/app/views/components/breadcrumbs/_item.html.erb +8 -0
  51. data/app/views/components/breadcrumbs/_link.html.erb +8 -0
  52. data/app/views/components/breadcrumbs/_list.html.erb +8 -0
  53. data/app/views/components/breadcrumbs/_page.html.erb +8 -0
  54. data/app/views/components/breadcrumbs/_separator.html.erb +17 -0
  55. data/app/views/components/card/_action.html.erb +6 -0
  56. data/app/views/components/card/_content.html.erb +9 -0
  57. data/app/views/components/card/_description.html.erb +6 -0
  58. data/app/views/components/card/_footer.html.erb +17 -0
  59. data/app/views/components/card/_header.html.erb +9 -0
  60. data/app/views/components/card/_title.html.erb +9 -0
  61. data/app/views/components/dropdown_menu/_content.html.erb +20 -0
  62. data/app/views/components/dropdown_menu/_group.html.erb +12 -0
  63. data/app/views/components/dropdown_menu/_item.html.erb +29 -0
  64. data/app/views/components/dropdown_menu/_label.html.erb +13 -0
  65. data/app/views/components/dropdown_menu/_separator.html.erb +11 -0
  66. data/app/views/components/dropdown_menu/_shortcut.html.erb +12 -0
  67. data/app/views/components/dropdown_menu/_trigger.html.erb +24 -0
  68. data/app/views/components/empty/_content.html.erb +8 -0
  69. data/app/views/components/empty/_description.html.erb +12 -0
  70. data/app/views/components/empty/_header.html.erb +8 -0
  71. data/app/views/components/empty/_media.html.erb +13 -0
  72. data/app/views/components/empty/_title.html.erb +12 -0
  73. data/app/views/components/pagination/_content.html.erb +8 -0
  74. data/app/views/components/pagination/_ellipsis.html.erb +28 -0
  75. data/app/views/components/pagination/_item.html.erb +8 -0
  76. data/app/views/components/pagination/_link.html.erb +23 -0
  77. data/app/views/components/pagination/_next.html.erb +57 -0
  78. data/app/views/components/pagination/_previous.html.erb +57 -0
  79. data/app/views/components/sidebar/_content.html.erb +8 -0
  80. data/app/views/components/sidebar/_footer.html.erb +8 -0
  81. data/app/views/components/sidebar/_group.html.erb +12 -0
  82. data/app/views/components/sidebar/_header.html.erb +8 -0
  83. data/app/views/components/sidebar/_inset.html.erb +8 -0
  84. data/app/views/components/sidebar/_menu.html.erb +8 -0
  85. data/app/views/components/sidebar/_menu_button.html.erb +14 -0
  86. data/app/views/components/sidebar/_menu_item.html.erb +7 -0
  87. data/app/views/components/sidebar/_menu_link.html.erb +32 -0
  88. data/app/views/components/sidebar/_provider.html.erb +16 -0
  89. data/app/views/components/sidebar/_trigger.html.erb +12 -0
  90. data/app/views/components/stats/_stats_card.html.erb +100 -0
  91. data/app/views/components/stats/_stats_grid.html.erb +38 -0
  92. data/app/views/components/table/_body.html.erb +5 -0
  93. data/app/views/components/table/_caption.html.erb +5 -0
  94. data/app/views/components/table/_cell.html.erb +5 -0
  95. data/app/views/components/table/_footer.html.erb +5 -0
  96. data/app/views/components/table/_head.html.erb +8 -0
  97. data/app/views/components/table/_header.html.erb +8 -0
  98. data/app/views/components/table/_row.html.erb +8 -0
  99. data/app/views/components/toggle_group/_item.html.erb +19 -0
  100. data/config/importmap.rb +1 -0
  101. data/lib/generators/maquina_components/install/USAGE +39 -0
  102. data/lib/generators/maquina_components/install/install_generator.rb +123 -0
  103. data/lib/generators/maquina_components/install/templates/maquina_components_helper.rb.tt +68 -0
  104. data/lib/generators/maquina_components/install/templates/theme.css.tt +179 -0
  105. data/lib/maquina_components/engine.rb +10 -0
  106. data/lib/maquina_components/version.rb +1 -1
  107. metadata +121 -5
@@ -0,0 +1,57 @@
1
+ <%# locals: (href: nil, label: nil, disabled: false, show_label: true, css_classes: "", **html_options) %>
2
+ <% is_disabled = disabled || href.nil?
3
+ display_label = label || t("maquina_components.pagination.previous", default: "Previous")
4
+ merged_data = (html_options.delete(:data) || {}).merge("pagination-part": "previous") %>
5
+
6
+ <% if is_disabled %>
7
+ <span
8
+ class="<%= css_classes.presence %>"
9
+ data-pagination-part="previous"
10
+ aria-disabled="true"
11
+ <%= tag.attributes(html_options.except(:data)) %>
12
+ >
13
+ <svg
14
+ xmlns="http://www.w3.org/2000/svg"
15
+ width="24"
16
+ height="24"
17
+ viewBox="0 0 24 24"
18
+ fill="none"
19
+ stroke="currentColor"
20
+ stroke-width="2"
21
+ stroke-linecap="round"
22
+ stroke-linejoin="round"
23
+ aria-hidden="true"
24
+ >
25
+ <path d="m15 18-6-6 6-6" />
26
+ </svg>
27
+
28
+ <% if show_label %>
29
+ <span><%= display_label %></span>
30
+ <% end %>
31
+ </span>
32
+ <% else %>
33
+ <%= link_to href,
34
+ class: css_classes.presence,
35
+ data: merged_data,
36
+ "aria-label": display_label,
37
+ **html_options do %>
38
+ <svg
39
+ xmlns="http://www.w3.org/2000/svg"
40
+ width="24"
41
+ height="24"
42
+ viewBox="0 0 24 24"
43
+ fill="none"
44
+ stroke="currentColor"
45
+ stroke-width="2"
46
+ stroke-linecap="round"
47
+ stroke-linejoin="round"
48
+ aria-hidden="true"
49
+ >
50
+ <path d="m15 18-6-6 6-6" />
51
+ </svg>
52
+
53
+ <% if show_label %>
54
+ <span><%= display_label %></span>
55
+ <% end %>
56
+ <% end %>
57
+ <% end %>
@@ -0,0 +1,8 @@
1
+ <%# locals: (css_classes: "", **html_options) %>
2
+ <% merged_data = (html_options.delete(:data) || {}).merge(
3
+ sidebar_part: :content
4
+ ) %>
5
+
6
+ <%= content_tag :div, class: css_classes, data: merged_data, **html_options do %>
7
+ <%= yield %>
8
+ <% end %>
@@ -0,0 +1,8 @@
1
+ <%# locals: (css_classes: "", **html_options) %>
2
+ <% merged_data = (html_options.delete(:data) || {}).merge(
3
+ sidebar_part: :footer
4
+ ) %>
5
+
6
+ <%= content_tag :div, class: css_classes, data: merged_data, **html_options do %>
7
+ <%= yield %>
8
+ <% end %>
@@ -0,0 +1,12 @@
1
+ <%# locals: (title: nil, css_classes: "", **html_options) %>
2
+ <% merged_data = (html_options.delete(:data) || {}).merge(
3
+ sidebar_part: :group
4
+ ) %>
5
+
6
+ <%= content_tag :div, class: css_classes, data: merged_data, **html_options do %>
7
+ <% if title.present? %>
8
+ <div data-sidebar-part="group-label"><%= title %></div>
9
+ <% end %>
10
+
11
+ <%= yield %>
12
+ <% end %>
@@ -0,0 +1,8 @@
1
+ <%# locals: (css_classes: "", **html_options) %>
2
+ <% merged_data = (html_options.delete(:data) || {}).merge(
3
+ sidebar_part: :header
4
+ ) %>
5
+
6
+ <%= content_tag :div, class: css_classes, data: merged_data, **html_options do %>
7
+ <%= yield %>
8
+ <% end %>
@@ -0,0 +1,8 @@
1
+ <%# locals: (css_classes: "", **html_options) %>
2
+ <% merged_data = (html_options.delete(:data) || {}).merge(
3
+ sidebar_part: :inset
4
+ ) %>
5
+
6
+ <%= content_tag :main, class: css_classes, data: merged_data, **html_options do %>
7
+ <%= yield %>
8
+ <% end %>
@@ -0,0 +1,8 @@
1
+ <%# locals: (css_classes: "", **html_options) %>
2
+ <% merged_data = (html_options.delete(:data) || {}).merge(
3
+ sidebar_part: :menu
4
+ ) %>
5
+
6
+ <%= content_tag :ul, class: css_classes, data: merged_data, **html_options do %>
7
+ <%= yield %>
8
+ <% end %>
@@ -0,0 +1,14 @@
1
+ <%# locals: (title:, url:, icon_name: nil, size: :default, active: false, css_classes: "", **html_options) %>
2
+ <% merged_data = (html_options.delete(:data) || {}).merge(
3
+ sidebar_part: "menu-button",
4
+ size: size,
5
+ active: active
6
+ ) %>
7
+
8
+ <%= link_to url, class: css_classes, data: merged_data, **html_options do %>
9
+ <% if icon_name.present? %>
10
+ <%= icon_for(icon_name) %>
11
+ <% end %>
12
+
13
+ <span><%= title %></span>
14
+ <% end %>
@@ -0,0 +1,7 @@
1
+ <%# locals: (css_classes: "", **html_options) %>
2
+ <% merged_data =
3
+ (html_options.delete(:data) || {}).merge(sidebar_part: "menu-item") %>
4
+
5
+ <%= content_tag :li, class: "group/menu-item #{css_classes}", data: merged_data, **html_options do %>
6
+ <%= yield %>
7
+ <% end %>
@@ -0,0 +1,32 @@
1
+ <%# locals: (url: "#", active: false, title: nil, subtitle: nil, icon: nil, text_icon: nil, icon_classes: "", css_classes: "", **html_options) %>
2
+ <% merged_data = (html_options.delete(:data) || {}).merge(
3
+ sidebar_part: "menu-link",
4
+ active: active
5
+ ) %>
6
+
7
+ <%= link_to url, class: css_classes, data: merged_data, **html_options do %>
8
+ <% if icon.present? || text_icon.present? %>
9
+ <div data-sidebar-part="menu-avatar">
10
+ <% if icon.present? %>
11
+ <%= image_tag icon, class: icon_classes %>
12
+ <% elsif text_icon.present? %>
13
+ <span class="<%= icon_classes %>"><%= text_icon %></span>
14
+ <% end %>
15
+ </div>
16
+ <% end %>
17
+
18
+ <% if title.present? || subtitle.present? %>
19
+ <div
20
+ class="flex flex-col gap-0.5 leading-none"
21
+ data-sidebar-hide-on-collapse
22
+ >
23
+ <% if title.present? %>
24
+ <span class="font-medium"><%= title %></span>
25
+ <% end %>
26
+
27
+ <% if subtitle.present? %>
28
+ <span class="text-xs text-sidebar-foreground/70"><%= subtitle %></span>
29
+ <% end %>
30
+ </div>
31
+ <% end %>
32
+ <% end %>
@@ -0,0 +1,16 @@
1
+ <%# locals: (default_open: true, variant: :inset, css_classes: "", cookie_name: "sidebar_state", keyboard_shortcut: "b", **html_options) %>
2
+ <% merged_data = (html_options.delete(:data) || {}).merge(
3
+ component: :sidebar,
4
+ variant: variant,
5
+ controller: "sidebar",
6
+ outlet: "sidebar",
7
+ sidebar_default_open_value: default_open,
8
+ sidebar_open_value: default_open,
9
+ sidebar_cookie_name_value: cookie_name,
10
+ sidebar_keyboard_shortcut_value: keyboard_shortcut,
11
+ action: "keydown.meta+#{keyboard_shortcut}@window->sidebar#toggleWithKeyboard keydown.ctrl+#{keyboard_shortcut}@window->sidebar#toggleWithKeyboard"
12
+ ) %>
13
+
14
+ <%= content_tag :div, class: css_classes, data: merged_data, **html_options do %>
15
+ <%= yield %>
16
+ <% end %>
@@ -0,0 +1,12 @@
1
+ <%# locals: (icon_name:, css_classes: "", **html_options) %>
2
+ <% merged_data = (html_options.delete(:data) || {}).merge(
3
+ sidebar_part: :trigger,
4
+ controller: "sidebar-trigger",
5
+ action: "click->sidebar-trigger#triggerClick",
6
+ sidebar_trigger_sidebar_outlet: "[data-outlet='sidebar']"
7
+ ) %>
8
+
9
+ <%= button_tag type: :button, class: css_classes, data: merged_data, **html_options do %>
10
+ <%= icon_for(icon_name) %>
11
+ <span class="sr-only">Toggle Sidebar</span>
12
+ <% end %>
@@ -0,0 +1,100 @@
1
+ <%# locals: (title:, value:, icon: nil, icon_class: "", subtitle: nil, value_class: "", container_class: "") -%>
2
+
3
+ <div
4
+ class="<%= class_names("bg-card p-4 rounded-lg shadow-sm border border-border", container_class) %>"
5
+ >
6
+ <div class="flex items-center justify-between">
7
+ <div>
8
+ <p class="text-sm text-muted-foreground"><%= title %></p>
9
+ <p
10
+ class="<%= class_names("text-2xl font-bold", value_class.presence || "text-foreground") %>"
11
+ >
12
+ <%= value %>
13
+ </p>
14
+ <% if subtitle.present? %>
15
+ <p class="text-xs text-muted-foreground mt-1"><%= subtitle %></p>
16
+ <% end %>
17
+ </div>
18
+ <% if icon.present? %>
19
+ <div class="<%= icon_class.presence || "text-muted-foreground" %>">
20
+ <% case icon %>
21
+ <% when "message-square" %>
22
+ <svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
23
+ <path
24
+ stroke-linecap="round"
25
+ stroke-linejoin="round"
26
+ stroke-width="2"
27
+ d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z"
28
+ />
29
+ </svg>
30
+ <% when "arrow-up" %>
31
+ <svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
32
+ <path
33
+ stroke-linecap="round"
34
+ stroke-linejoin="round"
35
+ stroke-width="2"
36
+ d="M7 17L17 7M17 7H7M17 7V17"
37
+ />
38
+ </svg>
39
+ <% when "arrow-down" %>
40
+ <svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
41
+ <path
42
+ stroke-linecap="round"
43
+ stroke-linejoin="round"
44
+ stroke-width="2"
45
+ d="M17 7L7 17M7 17H17M7 17V7"
46
+ />
47
+ </svg>
48
+ <% when "check-circle" %>
49
+ <svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
50
+ <path
51
+ stroke-linecap="round"
52
+ stroke-linejoin="round"
53
+ stroke-width="2"
54
+ d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
55
+ />
56
+ </svg>
57
+ <% when "lightning-bolt" %>
58
+ <svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
59
+ <path
60
+ stroke-linecap="round"
61
+ stroke-linejoin="round"
62
+ stroke-width="2"
63
+ d="M13 10V3L4 14h7v7l9-11h-7z"
64
+ />
65
+ </svg>
66
+ <% when "exclamation-circle" %>
67
+ <svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
68
+ <path
69
+ stroke-linecap="round"
70
+ stroke-linejoin="round"
71
+ stroke-width="2"
72
+ d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
73
+ />
74
+ </svg>
75
+ <% when "clipboard-list" %>
76
+ <svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
77
+ <path
78
+ stroke-linecap="round"
79
+ stroke-linejoin="round"
80
+ stroke-width="2"
81
+ d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"
82
+ />
83
+ </svg>
84
+ <% when "chart-bar" %>
85
+ <svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
86
+ <path
87
+ stroke-linecap="round"
88
+ stroke-linejoin="round"
89
+ stroke-width="2"
90
+ d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
91
+ />
92
+ </svg>
93
+ <% else %>
94
+ <%# Allow custom SVG or icon HTML to be passed %>
95
+ <%= icon %>
96
+ <% end %>
97
+ </div>
98
+ <% end %>
99
+ </div>
100
+ </div>
@@ -0,0 +1,38 @@
1
+ <%# locals: (cards: [], columns: 3, container_class: "", action: nil, action_position: "end") -%>
2
+
3
+ <div
4
+ class="<%= class_names("flex flex-col lg:flex-row lg:items-center gap-4": action.present?) %>"
5
+ >
6
+ <% if action.present? && action_position == "start" %>
7
+ <div class="flex justify-center lg:justify-start">
8
+ <%= action %>
9
+ </div>
10
+ <% end %>
11
+
12
+ <div
13
+ class="<%= class_names(
14
+ "grid gap-4",
15
+ "grid-cols-1",
16
+ "sm:grid-cols-#{columns}",
17
+ container_class,
18
+ "flex-1": action.present?
19
+ ) %>"
20
+ >
21
+ <% cards.each do |card| %>
22
+ <%= render "components/stats_card",
23
+ title: card[:title],
24
+ value: card[:value],
25
+ icon: card[:icon],
26
+ icon_class: card[:icon_class] || "",
27
+ subtitle: card[:subtitle],
28
+ value_class: card[:value_class] || "",
29
+ container_class: card[:container_class] || "" %>
30
+ <% end %>
31
+ </div>
32
+
33
+ <% if action.present? && action_position == "end" %>
34
+ <div class="flex justify-center lg:justify-end">
35
+ <%= action %>
36
+ </div>
37
+ <% end %>
38
+ </div>
@@ -0,0 +1,5 @@
1
+ <%# locals: (css_classes: "", **html_options) %>
2
+ <% merged_data = (html_options.delete(:data) || {}).merge(table_part: :body) %>
3
+ <%= content_tag :tbody, class: css_classes.presence, data: merged_data, **html_options do %>
4
+ <%= yield %>
5
+ <% end %>
@@ -0,0 +1,5 @@
1
+ <%# locals: (css_classes: "", **html_options) %>
2
+ <% merged_data = (html_options.delete(:data) || {}).merge(table_part: :caption) %>
3
+ <%= content_tag :caption, class: css_classes.presence, data: merged_data, **html_options do %>
4
+ <%= yield if block_given? %>
5
+ <% end %>
@@ -0,0 +1,5 @@
1
+ <%# locals: (css_classes: "", **html_options) %>
2
+ <% merged_data = (html_options.delete(:data) || {}).merge(table_part: :cell) %>
3
+ <%= content_tag :td, class: css_classes.presence, data: merged_data, **html_options do %>
4
+ <%= yield if block_given? %>
5
+ <% end %>
@@ -0,0 +1,5 @@
1
+ <%# locals: (css_classes: "", **html_options) %>
2
+ <% merged_data = (html_options.delete(:data) || {}).merge(table_part: :footer) %>
3
+ <%= content_tag :tfoot, class: css_classes.presence, data: merged_data, **html_options do %>
4
+ <%= yield %>
5
+ <% end %>
@@ -0,0 +1,8 @@
1
+ <%# locals: (css_classes: "", scope: "col", **html_options) %>
2
+ <%
3
+ merged_data = (html_options.delete(:data) || {}).merge(table_part: :head)
4
+ html_options[:scope] = scope if scope
5
+ %>
6
+ <%= content_tag :th, class: css_classes.presence, data: merged_data, **html_options do %>
7
+ <%= yield if block_given? %>
8
+ <% end %>
@@ -0,0 +1,8 @@
1
+ <%# locals: (css_classes: "", sticky: false, **html_options) %>
2
+ <%
3
+ merged_data = (html_options.delete(:data) || {}).merge(table_part: :header)
4
+ merged_data[:sticky] = "true" if sticky
5
+ %>
6
+ <%= content_tag :thead, class: css_classes.presence, data: merged_data, **html_options do %>
7
+ <%= yield %>
8
+ <% end %>
@@ -0,0 +1,8 @@
1
+ <%# locals: (css_classes: "", selected: false, **html_options) %>
2
+ <%
3
+ merged_data = (html_options.delete(:data) || {}).merge(table_part: :row)
4
+ merged_data[:state] = :selected if selected
5
+ %>
6
+ <%= content_tag :tr, class: css_classes.presence, data: merged_data, **html_options do %>
7
+ <%= yield %>
8
+ <% end %>
@@ -0,0 +1,19 @@
1
+ <%# locals: (value:, pressed: false, disabled: false, aria_label: nil, css_classes: "", **html_options) %>
2
+ <% merged_data = (html_options.delete(:data) || {}).merge(
3
+ "toggle-group-part": "item",
4
+ "toggle-group-target": "item",
5
+ value: value,
6
+ state: pressed ? "on" : "off",
7
+ action: "click->toggle-group#toggle keydown->toggle-group#handleKeydown"
8
+ ) %>
9
+
10
+ <%= content_tag :button,
11
+ type: "button",
12
+ class: css_classes.presence,
13
+ data: merged_data,
14
+ disabled: disabled,
15
+ "aria-pressed": pressed,
16
+ "aria-label": aria_label,
17
+ **html_options do %>
18
+ <%= yield %>
19
+ <% end %>
@@ -0,0 +1 @@
1
+ pin_all_from MaquinaComponents::Engine.root.join("app/javascript/controllers"), under: "controllers"
@@ -0,0 +1,39 @@
1
+ Description:
2
+ Install maquina_components into your Rails application.
3
+
4
+ This generator will:
5
+ - Add the engine CSS import to your Tailwind application.css
6
+ - Add theme variables (shadcn/ui convention) for customization
7
+ - Create a helper file for icon overrides and customizations
8
+
9
+ Example:
10
+ bin/rails generate maquina_components:install
11
+
12
+ This will:
13
+ insert @import "../builds/tailwind/maquina_components_engine.css";
14
+ append Theme variables to app/assets/tailwind/application.css
15
+ create app/helpers/maquina_components_helper.rb
16
+
17
+ Options:
18
+ --skip-theme Skip adding theme variables (if you already have them)
19
+ --skip-helper Skip creating the helper file
20
+
21
+ Prerequisites:
22
+ - tailwindcss-rails gem installed
23
+ - app/assets/tailwind/application.css exists
24
+
25
+ After Installation:
26
+ 1. Customize theme variables in app/assets/tailwind/application.css
27
+ 2. Override icon helper in app/helpers/maquina_components_helper.rb
28
+ 3. Start using components:
29
+
30
+ <%%= render "components/card" do %>
31
+ <%%= render "components/card/header" do %>
32
+ <%%= render "components/card/title", text: "Hello World" %>
33
+ <%% end %>
34
+ <%% end %>
35
+
36
+ 4. Use data attributes with form helpers:
37
+
38
+ <%%= f.text_field :email, data: { component: "input" } %>
39
+ <%%= f.submit "Save", data: { component: "button", variant: "primary" } %>
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/base"
4
+
5
+ module MaquinaComponents
6
+ module Generators
7
+ class InstallGenerator < Rails::Generators::Base
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ desc "Install maquina_components into your Rails application"
11
+
12
+ class_option :skip_theme, type: :boolean, default: false,
13
+ desc: "Skip adding theme variables to application.css"
14
+ class_option :skip_helper, type: :boolean, default: false,
15
+ desc: "Skip creating the maquina_components helper"
16
+
17
+ def check_tailwindcss_rails
18
+ css_path = "app/assets/tailwind/application.css"
19
+
20
+ unless File.exist?(File.join(destination_root, css_path))
21
+ say_status :warning, "tailwindcss-rails doesn't appear to be installed", :yellow
22
+ say "Please install tailwindcss-rails first:"
23
+ say " bundle add tailwindcss-rails"
24
+ say " rails tailwindcss:install"
25
+ say ""
26
+ end
27
+ end
28
+
29
+ def add_engine_css_import
30
+ css_path = "app/assets/tailwind/application.css"
31
+ full_path = File.join(destination_root, css_path)
32
+
33
+ unless File.exist?(full_path)
34
+ say_status :skip, "#{css_path} not found", :yellow
35
+ return
36
+ end
37
+
38
+ import_line = '@import "../builds/tailwind/maquina_components_engine.css";'
39
+
40
+ if File.read(full_path).include?(import_line)
41
+ say_status :skip, "Engine CSS import already present", :blue
42
+ return
43
+ end
44
+
45
+ # Insert after @import "tailwindcss"; or @import 'tailwindcss'; line
46
+ inject_into_file css_path, after: /@import\s+["']tailwindcss["'];?\n/ do
47
+ "\n#{import_line}\n"
48
+ end
49
+
50
+ say_status :insert, "Added maquina_components engine CSS import", :green
51
+ end
52
+
53
+ def add_theme_variables
54
+ return if options[:skip_theme]
55
+
56
+ css_path = "app/assets/tailwind/application.css"
57
+ full_path = File.join(destination_root, css_path)
58
+
59
+ unless File.exist?(full_path)
60
+ say_status :skip, "#{css_path} not found", :yellow
61
+ return
62
+ end
63
+
64
+ if File.read(full_path).include?("--color-primary:")
65
+ say_status :skip, "Theme variables already present", :blue
66
+ return
67
+ end
68
+
69
+ theme_content = File.read(File.expand_path("templates/theme.css.tt", __dir__))
70
+
71
+ append_to_file css_path, "\n#{theme_content}"
72
+
73
+ say_status :append, "Added theme variables to application.css", :green
74
+ end
75
+
76
+ def create_helper
77
+ return if options[:skip_helper]
78
+
79
+ helper_path = "app/helpers/maquina_components_helper.rb"
80
+ full_path = File.join(destination_root, helper_path)
81
+
82
+ if File.exist?(full_path)
83
+ say_status :skip, "#{helper_path} already exists", :blue
84
+ return
85
+ end
86
+
87
+ template "maquina_components_helper.rb.tt", helper_path
88
+ say_status :create, helper_path, :green
89
+ end
90
+
91
+ def show_post_install_message
92
+ say ""
93
+ say "=" * 60
94
+ say " maquina_components installed successfully!", :green
95
+ say "=" * 60
96
+ say ""
97
+ say "Next steps:"
98
+ say ""
99
+ say "1. Customize theme variables in app/assets/tailwind/application.css"
100
+ say " to match your design system."
101
+ say ""
102
+ say "2. Override the icon helper in app/helpers/maquina_components_helper.rb"
103
+ say " to use your own icon system (Heroicons, Lucide, etc.)."
104
+ say ""
105
+ say "3. Start using components in your views:"
106
+ say ""
107
+ say ' <%= render "components/card" do %>'
108
+ say ' <%= render "components/card/header" do %>'
109
+ say ' <%= render "components/card/title", text: "Hello" %>'
110
+ say " <% end %>"
111
+ say " <% end %>"
112
+ say ""
113
+ say "4. For form elements, use data attributes:"
114
+ say ""
115
+ say ' <%= f.text_field :email, data: { component: "input" } %>'
116
+ say ' <%= f.submit "Save", data: { component: "button", variant: "primary" } %>'
117
+ say ""
118
+ say "Documentation: https://github.com/maquina-app/maquina_components"
119
+ say ""
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ # MaquinaComponents Helper
4
+ #
5
+ # This helper provides customization hooks for maquina_components.
6
+ # Override methods here to integrate with your application's icon system,
7
+ # sidebar state management, and other customizations.
8
+ #
9
+ # Documentation: https://github.com/maquina-app/maquina_components
10
+ #
11
+ module MaquinaComponentsHelper
12
+ # Icon Override
13
+ #
14
+ # Override this method to use your own icon system (Heroicons, Lucide, etc.)
15
+ # The engine's icon_for helper will call this method first.
16
+ #
17
+ # @param name [Symbol] Icon name (e.g., :check, :chevron_right, :home)
18
+ # @param options [Hash] Options hash with :class, :stroke_width, etc.
19
+ # @return [String, nil] SVG string or nil to fall back to engine's icons
20
+ #
21
+ # @example Using Heroicons
22
+ # def main_icon_svg_for(name)
23
+ # heroicon_tag(name, variant: :outline)
24
+ # end
25
+ #
26
+ # @example Using Lucide
27
+ # def main_icon_svg_for(name)
28
+ # lucide_icon(name)
29
+ # end
30
+ #
31
+ # @example Using inline SVG files
32
+ # def main_icon_svg_for(name)
33
+ # file_path = Rails.root.join("app/assets/images/icons/#{name}.svg")
34
+ # File.read(file_path) if File.exist?(file_path)
35
+ # end
36
+ #
37
+ def main_icon_svg_for(name)
38
+ # Return nil to use the engine's built-in icons
39
+ # Override this method to use your own icon system
40
+ nil
41
+ end
42
+
43
+ # Sidebar State Helpers
44
+ #
45
+ # These helpers are re-exported from the engine for convenience.
46
+ # You can override them if you need custom cookie names or behavior.
47
+
48
+ # Returns the current sidebar state from cookies
49
+ # @param cookie_name [String] Cookie name (default: "sidebar_state")
50
+ # @return [Symbol] :expanded or :collapsed
51
+ def app_sidebar_state(cookie_name = "sidebar_state")
52
+ sidebar_state(cookie_name)
53
+ end
54
+
55
+ # Check if sidebar is expanded
56
+ # @param cookie_name [String] Cookie name (default: "sidebar_state")
57
+ # @return [Boolean]
58
+ def app_sidebar_open?(cookie_name = "sidebar_state")
59
+ sidebar_open?(cookie_name)
60
+ end
61
+
62
+ # Check if sidebar is collapsed
63
+ # @param cookie_name [String] Cookie name (default: "sidebar_state")
64
+ # @return [Boolean]
65
+ def app_sidebar_closed?(cookie_name = "sidebar_state")
66
+ sidebar_closed?(cookie_name)
67
+ end
68
+ end