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.
- checksums.yaml +4 -4
- data/README.md +16 -0
- data/app/assets/stylesheets/drawer.css +120 -0
- data/app/assets/tailwind/maquina_components_engine/engine.css +1 -0
- data/app/helpers/maquina_components/breadcrumbs_helper.rb +2 -2
- data/app/helpers/maquina_components/drawer_helper.rb +19 -0
- data/app/helpers/maquina_components/icons_helper.rb +10 -1
- data/app/javascript/controllers/breadcrumb_controller.js +24 -3
- data/app/javascript/controllers/drawer_controller.js +253 -0
- data/app/javascript/controllers/drawer_trigger_controller.js +28 -0
- data/app/views/components/_breadcrumbs.html.erb +5 -2
- data/app/views/components/_drawer.html.erb +31 -0
- data/app/views/components/drawer/_close.html.erb +14 -0
- data/app/views/components/drawer/_content.html.erb +7 -0
- data/app/views/components/drawer/_footer.html.erb +7 -0
- data/app/views/components/drawer/_header.html.erb +12 -0
- data/app/views/components/drawer/_provider.html.erb +14 -0
- data/app/views/components/drawer/_trigger.html.erb +14 -0
- data/lib/generators/maquina_components/install/templates/maquina_components_helper.rb.tt +7 -0
- data/lib/generators/maquina_components/scaffold_templates/scaffold_templates_generator.rb +40 -0
- data/lib/generators/maquina_components/scaffold_templates/templates/_form.html.erb.tt +49 -0
- data/lib/generators/maquina_components/scaffold_templates/templates/edit.html.erb.tt +29 -0
- data/lib/generators/maquina_components/scaffold_templates/templates/index.html.erb.tt +58 -0
- data/lib/generators/maquina_components/scaffold_templates/templates/new.html.erb.tt +26 -0
- data/lib/generators/maquina_components/scaffold_templates/templates/partial.html.erb.tt +17 -0
- data/lib/generators/maquina_components/scaffold_templates/templates/show.html.erb.tt +53 -0
- data/lib/maquina_components/version.rb +1 -1
- metadata +20 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d5288e1d8b1501f99a2b94c8d296e4babbe5e1d9d42f41d6f969dc1742e78c48
|
|
4
|
+
data.tar.gz: f4b31f8e38c025b05fc9a12a8c16d13b1fc62cfee10ce4596711a935203c39c4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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,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>
|
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
|
+
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:
|
|
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: []
|