maquina-components 0.4.3 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +16 -0
  3. data/app/assets/stylesheets/drawer.css +120 -0
  4. data/app/assets/tailwind/maquina_components_engine/engine.css +1 -0
  5. data/app/helpers/maquina_components/breadcrumbs_helper.rb +2 -2
  6. data/app/helpers/maquina_components/drawer_helper.rb +19 -0
  7. data/app/helpers/maquina_components/icons_helper.rb +10 -1
  8. data/app/javascript/controllers/breadcrumb_controller.js +24 -3
  9. data/app/javascript/controllers/drawer_controller.js +253 -0
  10. data/app/javascript/controllers/drawer_trigger_controller.js +28 -0
  11. data/app/views/components/_breadcrumbs.html.erb +5 -2
  12. data/app/views/components/_drawer.html.erb +31 -0
  13. data/app/views/components/drawer/_close.html.erb +14 -0
  14. data/app/views/components/drawer/_content.html.erb +7 -0
  15. data/app/views/components/drawer/_footer.html.erb +7 -0
  16. data/app/views/components/drawer/_header.html.erb +12 -0
  17. data/app/views/components/drawer/_provider.html.erb +14 -0
  18. data/app/views/components/drawer/_trigger.html.erb +14 -0
  19. data/lib/generators/maquina_components/install/templates/maquina_components_helper.rb.tt +7 -0
  20. data/lib/generators/maquina_components/scaffold_templates/scaffold_templates_generator.rb +40 -0
  21. data/lib/generators/maquina_components/scaffold_templates/templates/_form.html.erb.tt +49 -0
  22. data/lib/generators/maquina_components/scaffold_templates/templates/edit.html.erb.tt +29 -0
  23. data/lib/generators/maquina_components/scaffold_templates/templates/index.html.erb.tt +58 -0
  24. data/lib/generators/maquina_components/scaffold_templates/templates/new.html.erb.tt +26 -0
  25. data/lib/generators/maquina_components/scaffold_templates/templates/partial.html.erb.tt +17 -0
  26. data/lib/generators/maquina_components/scaffold_templates/templates/show.html.erb.tt +53 -0
  27. data/lib/maquina_components/version.rb +1 -1
  28. metadata +20 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b709941be68c552761043a38b7359796b7bc644af13827fbd47b2f40eab90fb9
4
- data.tar.gz: 931d38949a68e47881e9dc417786a412e5fb16ed02a23539d44259f452249913
3
+ metadata.gz: d5288e1d8b1501f99a2b94c8d296e4babbe5e1d9d42f41d6f969dc1742e78c48
4
+ data.tar.gz: f4b31f8e38c025b05fc9a12a8c16d13b1fc62cfee10ce4596711a935203c39c4
5
5
  SHA512:
6
- metadata.gz: 3b063e95e3ea3c7d4fd649b75cd00696ac33ac88025c3ebab14bea256f7456005a81b8e0c7f3b70f8904f73cc987c4d5cc22a707cbef48f3d10dafa21a7d9ec1
7
- data.tar.gz: 80b31cd01a92351baedec4a87509b494c6dbfe09cb42bb71c7a59444cca0cc48e4b020684d9c450c1b02568a352f0b2781654c5333a84a0c5af7ea38940f91a6
6
+ metadata.gz: 0c9977c27048fb814c4158033afa8bcfd538a257531d94cfe07a9f35dc623dda73503d430fea93947f026caf55ab9b8237423a010af0677f2d637b3abebd6138
7
+ data.tar.gz: 51c6744241d753e6b022c481fead7ad3b47725ac52cddf43a655f78a94c066f69b412a6f4f347a93c8e6935221ba743a6807035ebd86c1ffdd036605a6c30b80
data/README.md CHANGED
@@ -76,6 +76,8 @@ This will:
76
76
  - Add theme variables (light + dark mode)
77
77
  - Create a helper file for icon customization
78
78
 
79
+ All engine helpers (`icon_for`, `sidebar_open?`, `toast_flash_messages`, etc.) are automatically available in your views without any additional configuration.
80
+
79
81
  ### 3. Use Components
80
82
 
81
83
  ```erb
@@ -117,6 +119,19 @@ bin/rails generate maquina_components:install --skip-theme
117
119
  bin/rails generate maquina_components:install --skip-helper
118
120
  ```
119
121
 
122
+ ### Scaffold Templates
123
+
124
+ Copy ERB scaffold templates into your application so Rails' built-in `scaffold`
125
+ generator produces maquina_components-styled views:
126
+
127
+ ```bash
128
+ bin/rails generate maquina_components:scaffold_templates
129
+ ```
130
+
131
+ This installs `index`, `show`, `new`, `edit`, `_form`, and partial templates to
132
+ `lib/templates/erb/scaffold/`. After running it, `rails generate scaffold ModelName field:type`
133
+ generates views built with maquina_components. Customize the templates as needed.
134
+
120
135
  **Prerequisite:** [tailwindcss-rails](https://github.com/rails/tailwindcss-rails) must be installed first.
121
136
 
122
137
  ---
@@ -155,6 +170,7 @@ bin/rails generate maquina_components:install --skip-helper
155
170
  | **Calendar** | Inline date picker with single/range selection | [Calendar](https://maquina.app/documentation/components/calendar/) |
156
171
  | **Combobox** | Searchable dropdown with keyboard navigation | [Combobox](https://maquina.app/documentation/components/combobox/) |
157
172
  | **Date Picker** | Popover-based date selection | [Date Picker](https://maquina.app/documentation/components/date-picker/) |
173
+ | **Drawer** | Slide-out panel with overlay and keyboard shortcut | [Drawer](https://maquina.app/documentation/components/drawer/) |
158
174
  | **Toggle Group** | Single/multiple selection button group | [Toggle Group](https://maquina.app/documentation/components/toggle-group/) |
159
175
 
160
176
  ### Feedback Components
@@ -0,0 +1,120 @@
1
+ /* ===== Drawer Component Styles ===== */
2
+
3
+ /* ===== Provider (Wrapper) ===== */
4
+ [data-component="drawer"] {
5
+ @apply flex w-full;
6
+ }
7
+
8
+ /* ===== Drawer Root ===== */
9
+ [data-drawer-part="root"] {
10
+ @apply block text-foreground;
11
+ }
12
+
13
+ /* Prevent transitions on initial load */
14
+ [data-drawer-part="root"].drawer-loading,
15
+ [data-drawer-part="root"].drawer-loading * {
16
+ transition: none !important;
17
+ }
18
+
19
+ /* ===== Drawer Backdrop (overlay) ===== */
20
+ [data-drawer-part="backdrop"] {
21
+ @apply fixed inset-0 z-40 hidden bg-black opacity-0;
22
+ transition: opacity 300ms;
23
+ }
24
+
25
+ [data-drawer-part="backdrop"][data-state="visible"] {
26
+ @apply block opacity-60;
27
+ }
28
+
29
+ /* ===== Drawer Panel (fixed positioned) ===== */
30
+ [data-drawer-part="panel"] {
31
+ @apply fixed inset-y-0 z-50 flex h-full w-80 flex-col;
32
+ @apply bg-background shadow-lg;
33
+ transition: right 300ms ease-in-out, left 300ms ease-in-out;
34
+ }
35
+
36
+ [data-drawer-part="root"][data-side="left"] [data-drawer-part="panel"] {
37
+ @apply left-0;
38
+ right: auto;
39
+ }
40
+
41
+ [data-drawer-part="root"][data-side="right"] [data-drawer-part="panel"] {
42
+ @apply right-0;
43
+ left: auto;
44
+ }
45
+
46
+ /* Closed state - slide out */
47
+ [data-drawer-part="root"][data-side="left"][data-state="closed"] [data-drawer-part="panel"] {
48
+ @apply -translate-x-full;
49
+ }
50
+
51
+ [data-drawer-part="root"][data-side="right"][data-state="closed"] [data-drawer-part="panel"] {
52
+ @apply translate-x-full;
53
+ }
54
+
55
+ /* ===== Drawer Inner Content Wrapper ===== */
56
+ [data-drawer-part="inner"] {
57
+ @apply flex h-full w-full flex-col;
58
+ }
59
+
60
+ /* ===== Drawer Header ===== */
61
+ [data-drawer-part="header"] {
62
+ @apply flex shrink-0 items-center justify-between gap-2 border-b px-4 py-3;
63
+ border-color: var(--border);
64
+ }
65
+
66
+ /* ===== Drawer Content (scrollable area) ===== */
67
+ [data-drawer-part="content"] {
68
+ @apply flex min-h-0 flex-1 flex-col gap-2 overflow-auto p-4;
69
+ }
70
+
71
+ /* ===== Drawer Footer ===== */
72
+ [data-drawer-part="footer"] {
73
+ @apply flex shrink-0 flex-col gap-2 border-t p-4;
74
+ border-color: var(--border);
75
+ }
76
+
77
+ /* ===== Drawer Trigger ===== */
78
+
79
+ [data-drawer-part="trigger"]:hover {
80
+ background-color: var(--accent);
81
+ color: var(--accent-foreground);
82
+ }
83
+
84
+ /* ===== Drawer Title ===== */
85
+ [data-drawer-part="title"] {
86
+ @apply text-lg font-semibold;
87
+ }
88
+
89
+ /* ===== Drawer Description ===== */
90
+ [data-drawer-part="description"] {
91
+ @apply text-sm text-muted-foreground;
92
+ }
93
+
94
+ /* ===== Drawer Close Button ===== */
95
+ [data-drawer-part="close"] {
96
+ @apply inline-flex items-center justify-center rounded-md p-1;
97
+ @apply outline-hidden ring-ring focus-visible:ring-2;
98
+ @apply transition-colors;
99
+ color: var(--foreground);
100
+ }
101
+
102
+ [data-drawer-part="close"]:hover {
103
+ background-color: var(--accent);
104
+ color: var(--accent-foreground);
105
+ }
106
+
107
+ [data-drawer-part="close"] svg {
108
+ @apply size-4;
109
+ }
110
+
111
+ /* ===== Drawer Section ===== */
112
+ [data-drawer-part="section"] {
113
+ @apply flex flex-col gap-2;
114
+ }
115
+
116
+ /* ===== Drawer Separator ===== */
117
+ [data-drawer-part="separator"] {
118
+ @apply mx-4 my-2 w-auto;
119
+ background-color: var(--border);
120
+ }
@@ -7,6 +7,7 @@
7
7
  @import "../../stylesheets/card.css";
8
8
  @import "../../stylesheets/combobox.css";
9
9
  @import "../../stylesheets/date_picker.css";
10
+ @import "../../stylesheets/drawer.css";
10
11
  @import "../../stylesheets/dropdown_menu.css";
11
12
  @import "../../stylesheets/empty.css";
12
13
  @import "../../stylesheets/form.css";
@@ -44,8 +44,8 @@ module MaquinaComponents
44
44
  # "Button"
45
45
  # )
46
46
  #
47
- def responsive_breadcrumbs(links = {}, current_page = nil, css_classes: "")
48
- render "components/breadcrumbs", css_classes: css_classes, responsive: true do
47
+ def responsive_breadcrumbs(links = {}, current_page = nil, css_classes: "", collapse_after: 0)
48
+ render "components/breadcrumbs", css_classes: css_classes, responsive: true, collapse_after: collapse_after do
49
49
  render "components/breadcrumbs/list" do
50
50
  build_breadcrumb_items(links, current_page, responsive: true)
51
51
  end
@@ -0,0 +1,19 @@
1
+ module MaquinaComponents
2
+ module DrawerHelper
3
+ def drawer_state(cookie_name = "drawer_state")
4
+ cookie_value = cookies[cookie_name]
5
+
6
+ return :open if cookie_value.nil?
7
+
8
+ (cookie_value == "true") ? :open : :closed
9
+ end
10
+
11
+ def drawer_open?(cookie_name = "drawer_state")
12
+ drawer_state(cookie_name) == :open
13
+ end
14
+
15
+ def drawer_closed?(cookie_name = "drawer_state")
16
+ drawer_state(cookie_name) == :closed
17
+ end
18
+ end
19
+ end
@@ -26,8 +26,17 @@ module MaquinaComponents
26
26
  private
27
27
 
28
28
  def apply_icon_options(svg, options)
29
+ return svg.html_safe unless options&.any?
30
+
29
31
  css_classes = options[:class]
30
- svg = svg.gsub('class="', "class=\"#{css_classes} ") if css_classes
32
+ if css_classes&.is_a?(String)
33
+ escaped_css_classes = ERB::Util.html_escape(css_classes)
34
+ svg = if /\bclass\s*=\s*(['"])(.*?)\1/.match?(svg)
35
+ svg.sub(/\bclass\s*=\s*(['"])(.*?)\1/, "class=\\1#{escaped_css_classes}\\1")
36
+ else
37
+ svg.sub(/<svg(?=(\s|>))/, "<svg class=\"#{escaped_css_classes}\"")
38
+ end
39
+ end
31
40
 
32
41
  if options[:stroke_width]
33
42
  svg = svg.gsub('stroke-width="2"', "stroke-width=\"#{options[:stroke_width]}\"")
@@ -2,6 +2,7 @@ import { Controller } from "@hotwired/stimulus"
2
2
 
3
3
  export default class extends Controller {
4
4
  static targets = ["item", "ellipsis", "ellipsisSeparator"]
5
+ static values = { collapseAfter: { type: Number, default: 0 } }
5
6
 
6
7
  connect() {
7
8
  this._dropdown = null
@@ -59,15 +60,35 @@ export default class extends Controller {
59
60
  if (sep) sep.classList.remove('hidden')
60
61
  })
61
62
 
63
+ // Count-based collapsing: force-collapse when total items exceed threshold
64
+ // items are middle targets only; total visible = middle targets + first + last (2)
65
+ const totalItems = items.length + 2
66
+ if (this.collapseAfterValue > 0 && totalItems > this.collapseAfterValue) {
67
+ ellipsis.classList.remove('hidden')
68
+ if (ellipsisSeparator) ellipsisSeparator.classList.remove('hidden')
69
+
70
+ // collapseAfterValue includes first + last, so middle budget = collapseAfterValue - 2
71
+ const maxMiddleVisible = Math.max(this.collapseAfterValue - 2, 0)
72
+ let visibleMiddle = items.length
73
+
74
+ for (let i = items.length - 1; i >= 0 && visibleMiddle > maxMiddleVisible; i--) {
75
+ items[i].classList.add('hidden')
76
+ const sep = this._adjacentSeparator(items[i])
77
+ if (sep) sep.classList.add('hidden')
78
+ visibleMiddle--
79
+ }
80
+ }
81
+
62
82
  // Check overflow using scrollWidth vs clientWidth
63
83
  if (list.scrollWidth > list.clientWidth) {
64
84
  ellipsis.classList.remove('hidden')
65
85
  if (ellipsisSeparator) ellipsisSeparator.classList.remove('hidden')
66
86
 
67
87
  // Hide middle items one at a time until it fits
68
- for (let i = items.length - 1; i >= 0; i--) {
69
- items[i].classList.add('hidden')
70
- const sep = this._adjacentSeparator(items[i])
88
+ const visibleItems = items.filter(item => !item.classList.contains('hidden'))
89
+ for (let i = visibleItems.length - 1; i >= 0; i--) {
90
+ visibleItems[i].classList.add('hidden')
91
+ const sep = this._adjacentSeparator(visibleItems[i])
71
92
  if (sep) sep.classList.add('hidden')
72
93
  if (list.scrollWidth <= list.clientWidth) break
73
94
  }
@@ -0,0 +1,253 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+
3
+ /**
4
+ * Drawer Controller
5
+ *
6
+ * Manages drawer state (slide-out panel), keyboard shortcuts, and persistence.
7
+ * Works with drawer_trigger_controller via Stimulus outlets.
8
+ *
9
+ * @example
10
+ * <div data-controller="drawer" data-outlet="drawer">
11
+ * <!-- drawer content -->
12
+ * </div>
13
+ */
14
+ export default class extends Controller {
15
+ static values = {
16
+ open: { type: Boolean, default: false },
17
+ defaultOpen: { type: Boolean, default: false },
18
+ cookieName: { type: String, default: "drawer_state" },
19
+ cookieMaxAge: { type: Number, default: 60 * 60 * 24 * 365 },
20
+ keyboardShortcut: { type: String, default: "d" }
21
+ }
22
+
23
+ static targets = ["drawer", "container", "backdrop"]
24
+
25
+ initialize() {
26
+ const cookieValue = this.getCookie(this.cookieNameValue)
27
+ this.openValue = cookieValue !== null
28
+ ? cookieValue === "true"
29
+ : this.defaultOpenValue
30
+
31
+ this._morphing = false
32
+ }
33
+
34
+ connect() {
35
+ this.resizeHandler = this.debounce(this.checkScreenSize.bind(this), 150)
36
+ window.addEventListener("resize", this.resizeHandler)
37
+
38
+ this.boundTeardown = this.teardown.bind(this)
39
+ this.boundBeforeMorphElement = this.beforeMorphElement.bind(this)
40
+ this.boundHandleMorph = this.handleMorph.bind(this)
41
+ document.addEventListener("turbo:before-cache", this.boundTeardown)
42
+ document.addEventListener("turbo:before-morph-element", this.boundBeforeMorphElement)
43
+ document.addEventListener("turbo:morph", this.boundHandleMorph)
44
+
45
+ this.updateStateImmediate()
46
+ }
47
+
48
+ disconnect() {
49
+ window.removeEventListener("resize", this.resizeHandler)
50
+ document.removeEventListener("turbo:before-cache", this.boundTeardown)
51
+ document.removeEventListener("turbo:before-morph-element", this.boundBeforeMorphElement)
52
+ document.removeEventListener("turbo:morph", this.boundHandleMorph)
53
+ }
54
+
55
+ // ============================================================================
56
+ // Turbo Cache Teardown
57
+ // ============================================================================
58
+
59
+ teardown() {
60
+ this.openValue = false
61
+
62
+ if (this.hasBackdropTarget) {
63
+ this.backdropTarget.setAttribute("data-state", "hidden")
64
+ this.backdropTarget.classList.add("hidden")
65
+ }
66
+
67
+ this.unlockScroll()
68
+ }
69
+
70
+ beforeMorphElement(event) {
71
+ if (event.target === this.element) {
72
+ this._morphing = true
73
+ }
74
+ }
75
+
76
+ handleMorph() {
77
+ const cookieValue = this.getCookie(this.cookieNameValue)
78
+
79
+ if (cookieValue !== null) {
80
+ this.openValue = cookieValue === "true"
81
+ }
82
+
83
+ if (this.hasDrawerTarget) {
84
+ this.drawerTarget.classList.remove("drawer-loading")
85
+ }
86
+
87
+ this.updateState()
88
+ this._morphing = false
89
+ }
90
+
91
+ // ============================================================================
92
+ // Keyboard Shortcut Actions
93
+ // ============================================================================
94
+
95
+ toggleWithKeyboard(event) {
96
+ this.toggle()
97
+ }
98
+
99
+ // ============================================================================
100
+ // State Management
101
+ // ============================================================================
102
+
103
+ openValueChanged(new_value, old_value) {
104
+ if (new_value === old_value) return
105
+ if (this._morphing) return
106
+
107
+ this.updateState()
108
+ this.persistState()
109
+ this.dispatchStateChange()
110
+ }
111
+
112
+ updateState() {
113
+ if (!this.hasDrawerTarget) return
114
+
115
+ const isOpen = this.openValue
116
+ const state = isOpen ? "open" : "closed"
117
+
118
+ this.drawerTarget.setAttribute("data-state", state)
119
+
120
+ if (this.hasBackdropTarget) {
121
+ const backdropState = isOpen ? "visible" : "hidden"
122
+ this.backdropTarget.setAttribute("data-state", backdropState)
123
+
124
+ if (backdropState === "visible") {
125
+ this.backdropTarget.classList.remove("hidden")
126
+ } else {
127
+ setTimeout(() => {
128
+ if (this.backdropTarget.getAttribute("data-state") === "hidden") {
129
+ this.backdropTarget.classList.add("hidden")
130
+ }
131
+ }, 300)
132
+ }
133
+ }
134
+
135
+ if (isOpen) {
136
+ this.lockScroll()
137
+ } else {
138
+ this.unlockScroll()
139
+ }
140
+ }
141
+
142
+ updateStateImmediate() {
143
+ if (!this.hasDrawerTarget) return
144
+
145
+ this.updateState()
146
+
147
+ requestAnimationFrame(() => {
148
+ this.drawerTarget.classList.remove("drawer-loading")
149
+ })
150
+ }
151
+
152
+ // ============================================================================
153
+ // Public Actions
154
+ // ============================================================================
155
+
156
+ toggle() {
157
+ this.openValue = !this.openValue
158
+ }
159
+
160
+ open() {
161
+ this.openValue = true
162
+ }
163
+
164
+ close() {
165
+ this.openValue = false
166
+ }
167
+
168
+ backdropClick(event) {
169
+ if (this.openValue) {
170
+ this.close()
171
+ }
172
+ }
173
+
174
+ // ============================================================================
175
+ // Persistence
176
+ // ============================================================================
177
+
178
+ persistState() {
179
+ this.setCookie(
180
+ this.cookieNameValue,
181
+ this.openValue.toString(),
182
+ this.cookieMaxAgeValue
183
+ )
184
+ }
185
+
186
+ getCookie(name) {
187
+ const value = `; ${document.cookie}`
188
+ const parts = value.split(`; ${name}=`)
189
+ if (parts.length === 2) {
190
+ return parts.pop().split(";").shift()
191
+ }
192
+ return null
193
+ }
194
+
195
+ setCookie(name, value, maxAge) {
196
+ document.cookie = `${name}=${value}; path=/; max-age=${maxAge}; SameSite=Lax`
197
+ }
198
+
199
+ // ============================================================================
200
+ // Scroll Lock
201
+ // ============================================================================
202
+
203
+ lockScroll() {
204
+ this.scrollPosition = window.pageYOffset
205
+ document.body.style.overflow = "hidden"
206
+ document.body.style.position = "fixed"
207
+ document.body.style.top = `-${this.scrollPosition}px`
208
+ document.body.style.width = "100%"
209
+ }
210
+
211
+ unlockScroll() {
212
+ document.body.style.overflow = ""
213
+ document.body.style.position = ""
214
+ document.body.style.top = ""
215
+ document.body.style.width = ""
216
+ if (this.scrollPosition !== undefined) {
217
+ window.scrollTo(0, this.scrollPosition)
218
+ }
219
+ }
220
+
221
+ // ============================================================================
222
+ // Events
223
+ // ============================================================================
224
+
225
+ dispatchStateChange() {
226
+ this.dispatch("stateChanged", {
227
+ detail: {
228
+ open: this.openValue,
229
+ state: this.openValue ? "open" : "closed"
230
+ },
231
+ bubbles: true
232
+ })
233
+ }
234
+
235
+ // ============================================================================
236
+ // Utilities
237
+ // ============================================================================
238
+
239
+ checkScreenSize() {
240
+ }
241
+
242
+ debounce(func, wait) {
243
+ let timeout
244
+ return function executedFunction(...args) {
245
+ const later = () => {
246
+ clearTimeout(timeout)
247
+ func(...args)
248
+ }
249
+ clearTimeout(timeout)
250
+ timeout = setTimeout(later, wait)
251
+ }
252
+ }
253
+ }
@@ -0,0 +1,28 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+
3
+ /**
4
+ * Drawer Trigger Controller
5
+ *
6
+ * Triggers drawer toggle via Stimulus outlets.
7
+ * Can be placed anywhere on the page - finds drawer via outlet selector.
8
+ *
9
+ * @example
10
+ * <button
11
+ * data-controller="drawer-trigger"
12
+ * data-action="click->drawer-trigger#triggerClick"
13
+ * data-drawer-trigger-drawer-outlet="[data-outlet='drawer']"
14
+ * >
15
+ * Toggle
16
+ * </button>
17
+ */
18
+ export default class extends Controller {
19
+ static outlets = ["drawer"]
20
+
21
+ triggerClick() {
22
+ if (this.hasDrawerOutlet) {
23
+ this.drawerOutlets.forEach(outlet => {
24
+ outlet.toggle()
25
+ })
26
+ }
27
+ }
28
+ }
@@ -1,10 +1,13 @@
1
- <%# locals: (css_classes: "", responsive: false, **html_options) %>
1
+ <%# locals: (css_classes: "", responsive: false, collapse_after: 0, **html_options) %>
2
2
  <% merged_data = (html_options.delete(:data) || {}).merge(
3
3
  component: :breadcrumbs
4
4
  )
5
-
5
+
6
6
  if responsive
7
7
  merged_data[:controller] = "breadcrumb"
8
+ if collapse_after > 0
9
+ merged_data[:breadcrumb_collapse_after_value] = collapse_after
10
+ end
8
11
  end %>
9
12
 
10
13
  <nav
@@ -0,0 +1,31 @@
1
+ <%# locals: (id: nil, state: :closed, side: :right, css_classes: "", **html_options) %>
2
+ <% stable_id = id || "drawer-#{side}"
3
+ merged_data = (html_options.delete(:data) || {}).merge(
4
+ drawer_part: :root,
5
+ drawer_target: "drawer",
6
+ state: state,
7
+ side: side
8
+ ) %>
9
+ <aside
10
+ id="<%= stable_id %>"
11
+ class="group peer drawer-loading <%= css_classes %>"
12
+ <%= tag.attributes(data: merged_data, **html_options) %>
13
+ >
14
+ <%# Backdrop overlay %>
15
+ <div
16
+ id="<%= stable_id %>-backdrop"
17
+ data-drawer-part="backdrop"
18
+ data-drawer-target="backdrop"
19
+ data-action="click->drawer#backdropClick"
20
+ ></div>
21
+ <%# Drawer container (fixed positioned) %>
22
+ <div
23
+ id="<%= stable_id %>-panel"
24
+ data-drawer-part="panel"
25
+ >
26
+ <%# Inner drawer content wrapper %>
27
+ <div id="<%= stable_id %>-inner" data-drawer-part="inner">
28
+ <%= yield %>
29
+ </div>
30
+ </div>
31
+ </aside>
@@ -0,0 +1,14 @@
1
+ <%# locals: (show_icon: true, css_classes: "btn btn-ghost", **html_options) %>
2
+ <% merged_data = (html_options.delete(:data) || {}).merge(
3
+ drawer_part: :close,
4
+ action: "click->drawer#close"
5
+ ) %>
6
+ <%= content_tag :button, type: :button, class: css_classes, data: merged_data, **html_options do %>
7
+ <% if show_icon %>
8
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
9
+ <path d="M18 6 6 18"></path>
10
+ <path d="m6 6 12 12"></path>
11
+ </svg>
12
+ <% end %>
13
+ <%= yield %>
14
+ <% end %>
@@ -0,0 +1,7 @@
1
+ <%# locals: (css_classes: "", **html_options) %>
2
+ <% merged_data = (html_options.delete(:data) || {}).merge(
3
+ drawer_part: :content
4
+ ) %>
5
+ <%= content_tag :div, class: css_classes, data: merged_data, **html_options do %>
6
+ <%= yield %>
7
+ <% end %>
@@ -0,0 +1,7 @@
1
+ <%# locals: (css_classes: "", **html_options) %>
2
+ <% merged_data = (html_options.delete(:data) || {}).merge(
3
+ drawer_part: :footer
4
+ ) %>
5
+ <%= content_tag :div, class: css_classes, data: merged_data, **html_options do %>
6
+ <%= yield %>
7
+ <% end %>
@@ -0,0 +1,12 @@
1
+ <%# locals: (show_close: true, css_classes: "", **html_options) %>
2
+ <% merged_data = (html_options.delete(:data) || {}).merge(
3
+ drawer_part: :header
4
+ ) %>
5
+ <%= content_tag :div, class: css_classes, data: merged_data, **html_options do %>
6
+ <div class="flex-1">
7
+ <%= yield %>
8
+ </div>
9
+ <% if show_close %>
10
+ <%= render "components/drawer/close", css_classes: "btn btn-ghost btn-icon-sm" %>
11
+ <% end %>
12
+ <% end %>
@@ -0,0 +1,14 @@
1
+ <%# locals: (id: nil, default_open: false, css_classes: "", cookie_name: "drawer_state", keyboard_shortcut: "d", **html_options) %>
2
+ <% merged_data = (html_options.delete(:data) || {}).merge(
3
+ component: :drawer,
4
+ controller: "drawer",
5
+ outlet: "drawer",
6
+ drawer_default_open_value: default_open,
7
+ drawer_open_value: default_open,
8
+ drawer_cookie_name_value: cookie_name,
9
+ drawer_keyboard_shortcut_value: keyboard_shortcut,
10
+ action: "keydown.meta+#{keyboard_shortcut}@window->drawer#toggleWithKeyboard keydown.ctrl+#{keyboard_shortcut}@window->drawer#toggleWithKeyboard"
11
+ ) %>
12
+ <%= content_tag :div, id: id || "drawer-provider", class: css_classes, data: merged_data, **html_options do %>
13
+ <%= yield %>
14
+ <% end %>
@@ -0,0 +1,14 @@
1
+ <%# locals: (variant: :default, size: :default, icon_name: nil, css_classes: "", **html_options) %>
2
+ <% merged_data = (html_options.delete(:data) || {}).merge(
3
+ drawer_part: :trigger,
4
+ controller: "drawer-trigger",
5
+ action: "click->drawer-trigger#triggerClick",
6
+ drawer_trigger_drawer_outlet: "[data-outlet='drawer']"
7
+ ) %>
8
+ <% button_data = merged_data.merge(
9
+ component: "button"
10
+ ) %>
11
+ <%= content_tag :button, type: :button, class: css_classes, data: button_data, **html_options do %>
12
+ <%= icon_for(icon_name) if icon_name %>
13
+ <%= yield %>
14
+ <% end %>
@@ -6,9 +6,16 @@
6
6
  # Override methods here to integrate with your application's icon system,
7
7
  # sidebar state management, and other customizations.
8
8
  #
9
+ # Engine helper modules are included below to make their methods available
10
+ # in your views (icon_for, sidebar_open?, toast_flash_messages, etc.).
11
+ #
9
12
  # Documentation: https://github.com/maquina-app/maquina_components
10
13
  #
11
14
  module MaquinaComponentsHelper
15
+ include MaquinaComponents::IconsHelper
16
+ include MaquinaComponents::SidebarHelper
17
+ include MaquinaComponents::ToastHelper
18
+
12
19
  # Icon Override
13
20
  #
14
21
  # Override this method to use your own icon system (Heroicons, Lucide, etc.)
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/base"
4
+
5
+ module MaquinaComponents
6
+ module Generators
7
+ class ScaffoldTemplatesGenerator < Rails::Generators::Base
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ desc "Copy maquina_components scaffold templates to your application"
11
+
12
+ TEMPLATE_FILES = %w[
13
+ _form.html.erb.tt
14
+ edit.html.erb.tt
15
+ index.html.erb.tt
16
+ new.html.erb.tt
17
+ partial.html.erb.tt
18
+ show.html.erb.tt
19
+ ].freeze
20
+
21
+ def copy_templates
22
+ TEMPLATE_FILES.each do |filename|
23
+ copy_file filename, "lib/templates/erb/scaffold/#{filename}"
24
+ end
25
+ end
26
+
27
+ def show_post_install_message
28
+ say ""
29
+ say "Scaffold templates installed!", :green
30
+ say ""
31
+ say "Rails will now use maquina_components when you run:"
32
+ say " rails generate scaffold ModelName field:type"
33
+ say ""
34
+ say "Templates are in lib/templates/erb/scaffold/"
35
+ say "Feel free to customize them for your application."
36
+ say ""
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,49 @@
1
+ <%%= form_with(model: <%= model_resource_name %>, class: "space-y-4") do |form| %>
2
+ <%% if <%= singular_table_name %>.errors.any? %>
3
+ <%%= render "components/alert", variant: :destructive do %>
4
+ <%%= render "components/alert/title", text: "<%%= pluralize(<%= singular_table_name %>.errors.count, 'error') %> prohibited this <%= singular_table_name %> from being saved:" %>
5
+ <%%= render "components/alert/description" do %>
6
+ <ul class="list-disc list-inside mt-2">
7
+ <%% <%= singular_table_name %>.errors.each do |error| %>
8
+ <li><%%= error.full_message %></li>
9
+ <%% end %>
10
+ </ul>
11
+ <%% end %>
12
+ <%% end %>
13
+ <%% end %>
14
+
15
+ <% attributes.each do |attribute| -%>
16
+ <div class="space-y-2">
17
+ <% if attribute.password_digest? -%>
18
+ <%%= form.label :password %>
19
+ <%%= form.password_field :password, data: { component: "input" } %>
20
+ </div>
21
+
22
+ <div class="space-y-2">
23
+ <%%= form.label :password_confirmation %>
24
+ <%%= form.password_field :password_confirmation, data: { component: "input" } %>
25
+ <% elsif attribute.attachments? -%>
26
+ <%%= form.label :<%= attribute.column_name %> %>
27
+ <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, multiple: true, data: { component: "input" } %>
28
+ <% elsif attribute.field_type == :text_area -%>
29
+ <%%= form.label :<%= attribute.column_name %> %>
30
+ <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, data: { component: "textarea" } %>
31
+ <% elsif attribute.field_type == :check_box -%>
32
+ <div class="flex items-center gap-2">
33
+ <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, data: { component: "checkbox" } %>
34
+ <%%= form.label :<%= attribute.column_name %> %>
35
+ </div>
36
+ <% elsif attribute.field_type == :select -%>
37
+ <%%= form.label :<%= attribute.column_name %> %>
38
+ <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, [], {}, data: { component: "select" } %>
39
+ <% else -%>
40
+ <%%= form.label :<%= attribute.column_name %> %>
41
+ <%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, data: { component: "input" } %>
42
+ <% end -%>
43
+ </div>
44
+
45
+ <% end -%>
46
+ <div class="flex items-center gap-4 pt-4">
47
+ <%%= form.submit data: { component: "button", variant: "primary" } %>
48
+ </div>
49
+ <%% end %>
@@ -0,0 +1,29 @@
1
+ <%% content_for :title, "Editing <%= human_name.downcase %>" %>
2
+
3
+ <%% content_for :breadcrumbs do %>
4
+ <%%= render "components/breadcrumbs" do %>
5
+ <%%= render "components/breadcrumbs/list" do %>
6
+ <%%= render "components/breadcrumbs/item" do %>
7
+ <%%= render "components/breadcrumbs/link", href: <%= index_helper(type: :path) %>, text: "<%= human_name.pluralize %>" %>
8
+ <%% end %>
9
+ <%%= render "components/breadcrumbs/item" do %>
10
+ <%%= render "components/breadcrumbs/link", href: <%= model_resource_name(prefix: "@") %>, text: "<%= human_name %>" %>
11
+ <%% end %>
12
+ <%%= render "components/breadcrumbs/item" do %>
13
+ <%%= render "components/breadcrumbs/page", text: "Edit" %>
14
+ <%% end %>
15
+ <%% end %>
16
+ <%% end %>
17
+ <%% end %>
18
+
19
+ <div class="space-y-6">
20
+ <%%= render "components/card" do %>
21
+ <%%= render "components/card/header" do %>
22
+ <%%= render "components/card/title", text: "Editing <%= human_name.downcase %>" %>
23
+ <%% end %>
24
+
25
+ <%%= render "components/card/content" do %>
26
+ <%%= render "form", <%= singular_table_name %>: @<%= singular_table_name %> %>
27
+ <%% end %>
28
+ <%% end %>
29
+ </div>
@@ -0,0 +1,58 @@
1
+ <%% content_for :title, "<%= human_name.pluralize %>" %>
2
+
3
+ <div class="space-y-6">
4
+ <%%= render "components/card" do %>
5
+ <%%= render "components/card/header", layout: :row do %>
6
+ <div>
7
+ <%%= render "components/card/title", text: "<%= human_name.pluralize %>" %>
8
+ <%%= render "components/card/description", text: "<%%= @<%= plural_table_name %>.size %> record(s)" %>
9
+ </div>
10
+ <%%= render "components/card/action" do %>
11
+ <%%= link_to "New <%= human_name.downcase %>", <%= new_helper(type: :path) %>, data: { component: "button", variant: "primary", size: "sm" } %>
12
+ <%% end %>
13
+ <%% end %>
14
+
15
+ <%%= render "components/card/content" do %>
16
+ <%% if @<%= plural_table_name %>.any? %>
17
+ <%%= render "components/table" do %>
18
+ <%%= render "components/table/header" do %>
19
+ <%%= render "components/table/row" do %>
20
+ <% attributes.reject(&:password_digest?).each do |attribute| -%>
21
+ <%%= render "components/table/head" do %><%= attribute.human_name %><%% end %>
22
+ <% end -%>
23
+ <%%= render "components/table/head" do %><%% end %>
24
+ <%% end %>
25
+ <%% end %>
26
+ <%%= render "components/table/body" do %>
27
+ <%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %>
28
+ <%%= render "components/table/row" do %>
29
+ <% attributes.reject(&:password_digest?).each do |attribute| -%>
30
+ <% if attribute.attachment? -%>
31
+ <%%= render "components/table/cell" do %><%%= link_to <%= singular_table_name %>.<%= attribute.column_name %>.filename, <%= singular_table_name %>.<%= attribute.column_name %> if <%= singular_table_name %>.<%= attribute.column_name %>.attached? %><%% end %>
32
+ <% elsif attribute.attachments? -%>
33
+ <%%= render "components/table/cell" do %><%%= <%= singular_table_name %>.<%= attribute.column_name %>.count %> file(s)<%% end %>
34
+ <% else -%>
35
+ <%%= render "components/table/cell" do %><%%= <%= singular_table_name %>.<%= attribute.column_name %> %><%% end %>
36
+ <% end -%>
37
+ <% end -%>
38
+ <%%= render "components/table/cell" do %>
39
+ <%%= link_to "Show this <%= human_name.downcase %>", <%= model_resource_name(singular_table_name) %>, data: { component: "button", variant: "ghost", size: "sm" } %>
40
+ <%% end %>
41
+ <%% end %>
42
+ <%% end %>
43
+ <%% end %>
44
+ <%% end %>
45
+ <%% else %>
46
+ <%%= render "components/empty" do %>
47
+ <%%= render "components/empty/header" do %>
48
+ <%%= render "components/empty/title", text: "No <%= human_name.pluralize.downcase %> yet" %>
49
+ <%%= render "components/empty/description", text: "Get started by creating a new <%= human_name.downcase %>." %>
50
+ <%% end %>
51
+ <%%= render "components/empty/content" do %>
52
+ <%%= link_to "New <%= human_name.downcase %>", <%= new_helper(type: :path) %>, data: { component: "button", variant: "primary" } %>
53
+ <%% end %>
54
+ <%% end %>
55
+ <%% end %>
56
+ <%% end %>
57
+ <%% end %>
58
+ </div>
@@ -0,0 +1,26 @@
1
+ <%% content_for :title, "New <%= human_name.downcase %>" %>
2
+
3
+ <%% content_for :breadcrumbs do %>
4
+ <%%= render "components/breadcrumbs" do %>
5
+ <%%= render "components/breadcrumbs/list" do %>
6
+ <%%= render "components/breadcrumbs/item" do %>
7
+ <%%= render "components/breadcrumbs/link", href: <%= index_helper(type: :path) %>, text: "<%= human_name.pluralize %>" %>
8
+ <%% end %>
9
+ <%%= render "components/breadcrumbs/item" do %>
10
+ <%%= render "components/breadcrumbs/page", text: "New" %>
11
+ <%% end %>
12
+ <%% end %>
13
+ <%% end %>
14
+ <%% end %>
15
+
16
+ <div class="space-y-6">
17
+ <%%= render "components/card" do %>
18
+ <%%= render "components/card/header" do %>
19
+ <%%= render "components/card/title", text: "New <%= human_name.downcase %>" %>
20
+ <%% end %>
21
+
22
+ <%%= render "components/card/content" do %>
23
+ <%%= render "form", <%= singular_table_name %>: @<%= singular_table_name %> %>
24
+ <%% end %>
25
+ <%% end %>
26
+ </div>
@@ -0,0 +1,17 @@
1
+ <div id="<%%= dom_id <%= singular_name %> %>">
2
+ <% attributes.reject(&:password_digest?).each do |attribute| -%>
3
+ <div>
4
+ <strong class="text-sm font-medium text-muted-foreground"><%= attribute.human_name %>:</strong>
5
+ <% if attribute.attachment? -%>
6
+ <%%= link_to <%= singular_name %>.<%= attribute.column_name %>.filename, <%= singular_name %>.<%= attribute.column_name %> if <%= singular_name %>.<%= attribute.column_name %>.attached? %>
7
+ <% elsif attribute.attachments? -%>
8
+ <%% <%= singular_name %>.<%= attribute.column_name %>.each do |<%= attribute.singular_name %>| %>
9
+ <div><%%= link_to <%= attribute.singular_name %>.filename, <%= attribute.singular_name %> %></div>
10
+ <%% end %>
11
+ <% else -%>
12
+ <%%= <%= singular_name %>.<%= attribute.column_name %> %>
13
+ <% end -%>
14
+ </div>
15
+
16
+ <% end -%>
17
+ </div>
@@ -0,0 +1,53 @@
1
+ <%% content_for :title, "<%= human_name %>" %>
2
+
3
+ <%% content_for :breadcrumbs do %>
4
+ <%%= render "components/breadcrumbs" do %>
5
+ <%%= render "components/breadcrumbs/list" do %>
6
+ <%%= render "components/breadcrumbs/item" do %>
7
+ <%%= render "components/breadcrumbs/link", href: <%= index_helper(type: :path) %>, text: "<%= human_name.pluralize %>" %>
8
+ <%% end %>
9
+ <%%= render "components/breadcrumbs/item" do %>
10
+ <%%= render "components/breadcrumbs/page", text: "<%= human_name %>" %>
11
+ <%% end %>
12
+ <%% end %>
13
+ <%% end %>
14
+ <%% end %>
15
+
16
+ <div class="space-y-6">
17
+ <%%= render "components/card" do %>
18
+ <%%= render "components/card/header", layout: :row do %>
19
+ <div>
20
+ <%%= render "components/card/title", text: "<%= human_name %>" %>
21
+ </div>
22
+ <%%= render "components/card/action" do %>
23
+ <%%= link_to "Edit this <%= human_name.downcase %>", <%= edit_helper(type: :path) %>, data: { component: "button", variant: "outline", size: "sm" } %>
24
+ <%% end %>
25
+ <%% end %>
26
+
27
+ <%%= render "components/card/content" do %>
28
+ <dl class="grid grid-cols-1 gap-4 sm:grid-cols-2">
29
+ <% attributes.reject(&:password_digest?).each do |attribute| -%>
30
+ <div>
31
+ <dt class="text-sm font-medium text-muted-foreground"><%= attribute.human_name %></dt>
32
+ <% if attribute.attachment? -%>
33
+ <dd class="mt-1"><%%= link_to @<%= singular_table_name %>.<%= attribute.column_name %>.filename, @<%= singular_table_name %>.<%= attribute.column_name %> if @<%= singular_table_name %>.<%= attribute.column_name %>.attached? %></dd>
34
+ <% elsif attribute.attachments? -%>
35
+ <dd class="mt-1">
36
+ <%% @<%= singular_table_name %>.<%= attribute.column_name %>.each do |<%= attribute.singular_name %>| %>
37
+ <div><%%= link_to <%= attribute.singular_name %>.filename, <%= attribute.singular_name %> %></div>
38
+ <%% end %>
39
+ </dd>
40
+ <% else -%>
41
+ <dd class="mt-1"><%%= @<%= singular_table_name %>.<%= attribute.column_name %> %></dd>
42
+ <% end -%>
43
+ </div>
44
+ <% end -%>
45
+ </dl>
46
+ <%% end %>
47
+
48
+ <%%= render "components/card/footer" do %>
49
+ <%%= link_to "Back to <%= human_name.pluralize.downcase %>", <%= index_helper(type: :path) %>, data: { component: "button", variant: "ghost" } %>
50
+ <%%= button_to "Destroy this <%= human_name.downcase %>", <%= model_resource_name(prefix: "@") %>, method: :delete, data: { component: "button", variant: "destructive" } %>
51
+ <%% end %>
52
+ <%% end %>
53
+ </div>
@@ -1,3 +1,3 @@
1
1
  module MaquinaComponents
2
- VERSION = "0.4.3"
2
+ VERSION = "0.5.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: maquina-components
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mario Alberto Chávez
@@ -55,6 +55,7 @@ files:
55
55
  - app/assets/stylesheets/card.css
56
56
  - app/assets/stylesheets/combobox.css
57
57
  - app/assets/stylesheets/date_picker.css
58
+ - app/assets/stylesheets/drawer.css
58
59
  - app/assets/stylesheets/dropdown_menu.css
59
60
  - app/assets/stylesheets/empty.css
60
61
  - app/assets/stylesheets/form.css
@@ -69,6 +70,7 @@ files:
69
70
  - app/helpers/maquina_components/breadcrumbs_helper.rb
70
71
  - app/helpers/maquina_components/calendar_helper.rb
71
72
  - app/helpers/maquina_components/combobox_helper.rb
73
+ - app/helpers/maquina_components/drawer_helper.rb
72
74
  - app/helpers/maquina_components/dropdown_menu_helper.rb
73
75
  - app/helpers/maquina_components/empty_helper.rb
74
76
  - app/helpers/maquina_components/icons_helper.rb
@@ -81,6 +83,8 @@ files:
81
83
  - app/javascript/controllers/calendar_controller.js
82
84
  - app/javascript/controllers/combobox_controller.js
83
85
  - app/javascript/controllers/date_picker_controller.js
86
+ - app/javascript/controllers/drawer_controller.js
87
+ - app/javascript/controllers/drawer_trigger_controller.js
84
88
  - app/javascript/controllers/dropdown_menu_controller.js
85
89
  - app/javascript/controllers/menu_button_controller.js
86
90
  - app/javascript/controllers/sidebar_controller.js
@@ -95,6 +99,7 @@ files:
95
99
  - app/views/components/_card.html.erb
96
100
  - app/views/components/_combobox.html.erb
97
101
  - app/views/components/_date_picker.html.erb
102
+ - app/views/components/_drawer.html.erb
98
103
  - app/views/components/_dropdown.html.erb
99
104
  - app/views/components/_dropdown_menu.html.erb
100
105
  - app/views/components/_empty.html.erb
@@ -133,6 +138,12 @@ files:
133
138
  - app/views/components/combobox/_option.html.erb
134
139
  - app/views/components/combobox/_separator.html.erb
135
140
  - app/views/components/combobox/_trigger.html.erb
141
+ - app/views/components/drawer/_close.html.erb
142
+ - app/views/components/drawer/_content.html.erb
143
+ - app/views/components/drawer/_footer.html.erb
144
+ - app/views/components/drawer/_header.html.erb
145
+ - app/views/components/drawer/_provider.html.erb
146
+ - app/views/components/drawer/_trigger.html.erb
136
147
  - app/views/components/dropdown_menu/_content.html.erb
137
148
  - app/views/components/dropdown_menu/_group.html.erb
138
149
  - app/views/components/dropdown_menu/_item.html.erb
@@ -180,6 +191,13 @@ files:
180
191
  - lib/generators/maquina_components/install/install_generator.rb
181
192
  - lib/generators/maquina_components/install/templates/maquina_components_helper.rb.tt
182
193
  - lib/generators/maquina_components/install/templates/theme.css.tt
194
+ - lib/generators/maquina_components/scaffold_templates/scaffold_templates_generator.rb
195
+ - lib/generators/maquina_components/scaffold_templates/templates/_form.html.erb.tt
196
+ - lib/generators/maquina_components/scaffold_templates/templates/edit.html.erb.tt
197
+ - lib/generators/maquina_components/scaffold_templates/templates/index.html.erb.tt
198
+ - lib/generators/maquina_components/scaffold_templates/templates/new.html.erb.tt
199
+ - lib/generators/maquina_components/scaffold_templates/templates/partial.html.erb.tt
200
+ - lib/generators/maquina_components/scaffold_templates/templates/show.html.erb.tt
183
201
  - lib/maquina-components.rb
184
202
  - lib/maquina_components/engine.rb
185
203
  - lib/maquina_components/version.rb
@@ -205,7 +223,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
205
223
  - !ruby/object:Gem::Version
206
224
  version: '0'
207
225
  requirements: []
208
- rubygems_version: 4.0.3
226
+ rubygems_version: 3.6.9
209
227
  specification_version: 4
210
228
  summary: ERB, TailwindCSS, and StimulusJS UI components based on Shadcn/UI.
211
229
  test_files: []