maquina-components 0.1.2 → 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.
- checksums.yaml +4 -4
- data/README.md +349 -138
- data/app/assets/images/maquina.svg +1 -0
- data/app/assets/stylesheets/alert.css +143 -0
- data/app/assets/stylesheets/badge.css +145 -0
- data/app/assets/stylesheets/breadcrumbs.css +163 -0
- data/app/assets/stylesheets/card.css +128 -0
- data/app/assets/stylesheets/dropdown_menu.css +248 -0
- data/app/assets/stylesheets/empty.css +133 -0
- data/app/assets/stylesheets/form.css +617 -0
- data/app/assets/stylesheets/header.css +61 -0
- data/app/assets/stylesheets/maquina_components.css +143 -64
- data/app/assets/stylesheets/pagination.css +154 -0
- data/app/assets/stylesheets/sidebar.css +477 -0
- data/app/assets/stylesheets/table.css +205 -0
- data/app/assets/stylesheets/toggle_group.css +151 -0
- data/app/assets/tailwind/maquina_components_engine/engine.css +16 -0
- data/app/helpers/maquina_components/breadcrumbs_helper.rb +118 -0
- data/app/helpers/maquina_components/dropdown_menu_helper.rb +249 -0
- data/app/helpers/maquina_components/empty_helper.rb +102 -0
- data/app/helpers/{components → maquina_components}/icons_helper.rb +40 -3
- data/app/helpers/maquina_components/pagination_helper.rb +153 -0
- data/app/helpers/maquina_components/sidebar_helper.rb +63 -0
- data/app/helpers/maquina_components/table_helper.rb +144 -0
- data/app/helpers/maquina_components/toggle_group_helper.rb +172 -0
- data/app/javascript/controllers/breadcrumb_controller.js +71 -0
- data/app/javascript/controllers/dropdown_menu_controller.js +203 -0
- data/app/javascript/controllers/menu_button_controller.js +59 -0
- data/app/javascript/controllers/sidebar_controller.js +316 -0
- data/app/javascript/controllers/sidebar_trigger_controller.js +32 -0
- data/app/javascript/controllers/toggle_group_controller.js +178 -0
- data/app/views/components/_alert.html.erb +11 -10
- data/app/views/components/_badge.html.erb +10 -0
- data/app/views/components/_breadcrumbs.html.erb +16 -0
- data/app/views/components/_card.html.erb +4 -8
- data/app/views/components/_dropdown.html.erb +25 -0
- data/app/views/components/_dropdown_menu.html.erb +9 -0
- data/app/views/components/_empty.html.erb +10 -0
- data/app/views/components/_header.html.erb +8 -0
- data/app/views/components/_menu_button.html.erb +44 -0
- data/app/views/components/_pagination.html.erb +12 -33
- data/app/views/components/_separator.html.erb +11 -0
- data/app/views/components/_sidebar.html.erb +30 -20
- data/app/views/components/_simple_table.html.erb +49 -0
- data/app/views/components/_table.html.erb +21 -0
- data/app/views/components/_toggle_group.html.erb +24 -0
- data/app/views/components/alert/_description.html.erb +6 -0
- data/app/views/components/alert/_title.html.erb +6 -0
- data/app/views/components/breadcrumbs/_ellipsis.html.erb +9 -0
- data/app/views/components/breadcrumbs/_item.html.erb +8 -0
- data/app/views/components/breadcrumbs/_link.html.erb +8 -0
- data/app/views/components/breadcrumbs/_list.html.erb +8 -0
- data/app/views/components/breadcrumbs/_page.html.erb +8 -0
- data/app/views/components/breadcrumbs/_separator.html.erb +17 -0
- data/app/views/components/card/_action.html.erb +6 -0
- data/app/views/components/card/_content.html.erb +9 -0
- data/app/views/components/card/_description.html.erb +6 -0
- data/app/views/components/card/_footer.html.erb +17 -0
- data/app/views/components/card/_header.html.erb +9 -0
- data/app/views/components/card/_title.html.erb +9 -0
- data/app/views/components/dropdown_menu/_content.html.erb +20 -0
- data/app/views/components/dropdown_menu/_group.html.erb +12 -0
- data/app/views/components/dropdown_menu/_item.html.erb +29 -0
- data/app/views/components/dropdown_menu/_label.html.erb +13 -0
- data/app/views/components/dropdown_menu/_separator.html.erb +11 -0
- data/app/views/components/dropdown_menu/_shortcut.html.erb +12 -0
- data/app/views/components/dropdown_menu/_trigger.html.erb +24 -0
- data/app/views/components/empty/_content.html.erb +8 -0
- data/app/views/components/empty/_description.html.erb +12 -0
- data/app/views/components/empty/_header.html.erb +8 -0
- data/app/views/components/empty/_media.html.erb +13 -0
- data/app/views/components/empty/_title.html.erb +12 -0
- data/app/views/components/pagination/_content.html.erb +8 -0
- data/app/views/components/pagination/_ellipsis.html.erb +28 -0
- data/app/views/components/pagination/_item.html.erb +8 -0
- data/app/views/components/pagination/_link.html.erb +23 -0
- data/app/views/components/pagination/_next.html.erb +57 -0
- data/app/views/components/pagination/_previous.html.erb +57 -0
- data/app/views/components/sidebar/_content.html.erb +8 -0
- data/app/views/components/sidebar/_footer.html.erb +8 -0
- data/app/views/components/sidebar/_group.html.erb +12 -0
- data/app/views/components/sidebar/_header.html.erb +8 -0
- data/app/views/components/sidebar/_inset.html.erb +8 -0
- data/app/views/components/sidebar/_menu.html.erb +8 -0
- data/app/views/components/sidebar/_menu_button.html.erb +14 -0
- data/app/views/components/sidebar/_menu_item.html.erb +7 -0
- data/app/views/components/sidebar/_menu_link.html.erb +32 -0
- data/app/views/components/sidebar/_provider.html.erb +16 -0
- data/app/views/components/sidebar/_trigger.html.erb +12 -0
- data/app/views/components/stats/_stats_card.html.erb +100 -0
- data/app/views/components/stats/_stats_grid.html.erb +38 -0
- data/app/views/components/table/_body.html.erb +5 -0
- data/app/views/components/table/_caption.html.erb +5 -0
- data/app/views/components/table/_cell.html.erb +5 -0
- data/app/views/components/table/_footer.html.erb +5 -0
- data/app/views/components/table/_head.html.erb +8 -0
- data/app/views/components/table/_header.html.erb +8 -0
- data/app/views/components/table/_row.html.erb +8 -0
- data/app/views/components/toggle_group/_item.html.erb +19 -0
- data/config/importmap.rb +1 -0
- data/lib/generators/maquina_components/install/USAGE +39 -0
- data/lib/generators/maquina_components/install/install_generator.rb +123 -0
- data/lib/generators/maquina_components/install/templates/maquina_components_helper.rb.tt +68 -0
- data/lib/generators/maquina_components/install/templates/theme.css.tt +179 -0
- data/lib/maquina_components/engine.rb +10 -0
- data/lib/maquina_components/version.rb +1 -1
- metadata +116 -12
- data/app/helpers/components/pagination_helper.rb +0 -15
- data/app/views/components/_card_content.html.erb +0 -5
- data/app/views/components/_card_header.html.erb +0 -8
- data/app/views/components/_sidebar_content.html.erb +0 -8
- data/app/views/components/_sidebar_group.html.erb +0 -42
- data/app/views/components/_sidebar_header.html.erb +0 -3
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Toggle Group Controller
|
|
5
|
+
*
|
|
6
|
+
* Manages a group of toggle buttons with single or multiple selection.
|
|
7
|
+
*
|
|
8
|
+
* @example Single selection
|
|
9
|
+
* <div data-controller="toggle-group"
|
|
10
|
+
* data-toggle-group-type-value="single"
|
|
11
|
+
* data-toggle-group-selected-value='["bold"]'>
|
|
12
|
+
* <button data-toggle-group-target="item" data-value="bold">Bold</button>
|
|
13
|
+
* <button data-toggle-group-target="item" data-value="italic">Italic</button>
|
|
14
|
+
* </div>
|
|
15
|
+
*
|
|
16
|
+
* @example Multiple selection
|
|
17
|
+
* <div data-controller="toggle-group"
|
|
18
|
+
* data-toggle-group-type-value="multiple"
|
|
19
|
+
* data-toggle-group-selected-value='["bold", "italic"]'>
|
|
20
|
+
* ...
|
|
21
|
+
* </div>
|
|
22
|
+
*/
|
|
23
|
+
export default class extends Controller {
|
|
24
|
+
static targets = ["item"]
|
|
25
|
+
|
|
26
|
+
static values = {
|
|
27
|
+
type: { type: String, default: "single" },
|
|
28
|
+
selected: { type: Array, default: [] }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
connect() {
|
|
32
|
+
this.syncItemStates()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Toggle an item's state
|
|
37
|
+
* @param {Event} event - Click event from toggle item
|
|
38
|
+
*/
|
|
39
|
+
toggle(event) {
|
|
40
|
+
const item = event.currentTarget
|
|
41
|
+
if (item.disabled) return
|
|
42
|
+
|
|
43
|
+
const value = item.dataset.value
|
|
44
|
+
const isPressed = item.dataset.state === "on"
|
|
45
|
+
|
|
46
|
+
if (this.typeValue === "single") {
|
|
47
|
+
if (isPressed) {
|
|
48
|
+
this.selectedValue = []
|
|
49
|
+
} else {
|
|
50
|
+
this.selectedValue = [value]
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
if (isPressed) {
|
|
54
|
+
this.selectedValue = this.selectedValue.filter(v => v !== value)
|
|
55
|
+
} else {
|
|
56
|
+
this.selectedValue = [...this.selectedValue, value]
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
this.syncItemStates()
|
|
61
|
+
this.dispatchChange()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Handle keyboard navigation
|
|
66
|
+
* @param {KeyboardEvent} event
|
|
67
|
+
*/
|
|
68
|
+
handleKeydown(event) {
|
|
69
|
+
const item = event.currentTarget
|
|
70
|
+
const items = this.itemTargets.filter(i => !i.disabled)
|
|
71
|
+
const currentIndex = items.indexOf(item)
|
|
72
|
+
|
|
73
|
+
let nextIndex = currentIndex
|
|
74
|
+
|
|
75
|
+
switch (event.key) {
|
|
76
|
+
case "ArrowRight":
|
|
77
|
+
case "ArrowDown":
|
|
78
|
+
event.preventDefault()
|
|
79
|
+
nextIndex = (currentIndex + 1) % items.length
|
|
80
|
+
break
|
|
81
|
+
case "ArrowLeft":
|
|
82
|
+
case "ArrowUp":
|
|
83
|
+
event.preventDefault()
|
|
84
|
+
nextIndex = (currentIndex - 1 + items.length) % items.length
|
|
85
|
+
break
|
|
86
|
+
case "Home":
|
|
87
|
+
event.preventDefault()
|
|
88
|
+
nextIndex = 0
|
|
89
|
+
break
|
|
90
|
+
case "End":
|
|
91
|
+
event.preventDefault()
|
|
92
|
+
nextIndex = items.length - 1
|
|
93
|
+
break
|
|
94
|
+
case " ":
|
|
95
|
+
case "Enter":
|
|
96
|
+
return
|
|
97
|
+
default:
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
items[nextIndex]?.focus()
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Sync visual states with selectedValue
|
|
106
|
+
*/
|
|
107
|
+
syncItemStates() {
|
|
108
|
+
this.itemTargets.forEach(item => {
|
|
109
|
+
const value = item.dataset.value
|
|
110
|
+
const isSelected = this.selectedValue.includes(value)
|
|
111
|
+
|
|
112
|
+
item.dataset.state = isSelected ? "on" : "off"
|
|
113
|
+
item.setAttribute("aria-pressed", isSelected)
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Dispatch change event
|
|
119
|
+
*/
|
|
120
|
+
dispatchChange() {
|
|
121
|
+
const detail = {
|
|
122
|
+
type: this.typeValue,
|
|
123
|
+
value: this.typeValue === "single"
|
|
124
|
+
? (this.selectedValue[0] || null)
|
|
125
|
+
: this.selectedValue
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
this.dispatch("change", { detail })
|
|
129
|
+
|
|
130
|
+
this.element.dispatchEvent(new CustomEvent("toggle-group:change", {
|
|
131
|
+
bubbles: true,
|
|
132
|
+
detail
|
|
133
|
+
}))
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Programmatically select a value
|
|
138
|
+
* @param {string} value - Value to select
|
|
139
|
+
*/
|
|
140
|
+
select(value) {
|
|
141
|
+
if (this.typeValue === "single") {
|
|
142
|
+
this.selectedValue = [value]
|
|
143
|
+
} else if (!this.selectedValue.includes(value)) {
|
|
144
|
+
this.selectedValue = [...this.selectedValue, value]
|
|
145
|
+
}
|
|
146
|
+
this.syncItemStates()
|
|
147
|
+
this.dispatchChange()
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Programmatically deselect a value
|
|
152
|
+
* @param {string} value - Value to deselect
|
|
153
|
+
*/
|
|
154
|
+
deselect(value) {
|
|
155
|
+
this.selectedValue = this.selectedValue.filter(v => v !== value)
|
|
156
|
+
this.syncItemStates()
|
|
157
|
+
this.dispatchChange()
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Clear all selections
|
|
162
|
+
*/
|
|
163
|
+
clear() {
|
|
164
|
+
this.selectedValue = []
|
|
165
|
+
this.syncItemStates()
|
|
166
|
+
this.dispatchChange()
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Get current value(s)
|
|
171
|
+
* @returns {string|string[]|null}
|
|
172
|
+
*/
|
|
173
|
+
getValue() {
|
|
174
|
+
return this.typeValue === "single"
|
|
175
|
+
? (this.selectedValue[0] || null)
|
|
176
|
+
: this.selectedValue
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
<%# locals: (
|
|
2
|
-
<%
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
<%# locals: (variant: :default, icon: nil, css_classes: "", **html_options) %>
|
|
2
|
+
<% merged_data = (html_options.delete(:data) || {}).merge(
|
|
3
|
+
component: :alert,
|
|
4
|
+
variant: variant,
|
|
5
|
+
has_icon: icon.present? || nil
|
|
6
|
+
).compact %>
|
|
7
|
+
|
|
8
|
+
<%= content_tag :div, role: :alert, class: css_classes.presence, data: merged_data, **html_options do %>
|
|
9
|
+
<%= icon_for(icon) if icon.present? %>
|
|
10
|
+
|
|
11
|
+
<div><%= yield %></div>
|
|
11
12
|
<% end %>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<%# locals: (variant: :default, size: :md, css_classes: "", **html_options) %>
|
|
2
|
+
<% merged_data = (html_options.delete(:data) || {}).merge(
|
|
3
|
+
component: :badge,
|
|
4
|
+
variant: variant,
|
|
5
|
+
size: size
|
|
6
|
+
) %>
|
|
7
|
+
|
|
8
|
+
<%= content_tag :span, class: css_classes, data: merged_data, **html_options do %>
|
|
9
|
+
<%= yield %>
|
|
10
|
+
<% end %>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<%# locals: (css_classes: "", responsive: false, **html_options) %>
|
|
2
|
+
<% merged_data = (html_options.delete(:data) || {}).merge(
|
|
3
|
+
component: :breadcrumbs
|
|
4
|
+
)
|
|
5
|
+
|
|
6
|
+
if responsive
|
|
7
|
+
merged_data[:controller] = "breadcrumb"
|
|
8
|
+
end %>
|
|
9
|
+
|
|
10
|
+
<nav
|
|
11
|
+
aria-label="Breadcrumb"
|
|
12
|
+
class="<%= css_classes %>"
|
|
13
|
+
<%= tag.attributes(data: merged_data, **html_options) %>
|
|
14
|
+
>
|
|
15
|
+
<%= yield %>
|
|
16
|
+
</nav>
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
<%# locals: (
|
|
1
|
+
<%# locals: (css_classes: "", **html_options) %>
|
|
2
|
+
<% merged_data = (html_options.delete(:data) || {}).merge(component: :card) %>
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
class="<%= class_names(
|
|
5
|
-
"rounded-xl border border-border bg-card text-card-foreground shadow",
|
|
6
|
-
custom_classes
|
|
7
|
-
) %>"
|
|
8
|
-
>
|
|
4
|
+
<%= content_tag :div, class: css_classes.presence, data: merged_data, **html_options do %>
|
|
9
5
|
<%= yield %>
|
|
10
|
-
|
|
6
|
+
<% end %>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<div
|
|
2
|
+
data-menu-button-target="content"
|
|
3
|
+
class="
|
|
4
|
+
absolute z-10 overflow-hidden border border-border p-1 text-popover-foreground
|
|
5
|
+
min-w-56 max-w-56 content-fit shadow-md bottom-full rounded-lg hidden
|
|
6
|
+
peer-data-[state=closed]/menu-button:animate-out
|
|
7
|
+
peer-data-[state=closed]/menu-button:fade-out-0
|
|
8
|
+
peer-data-[state=open]/menu-button:fade-in-0
|
|
9
|
+
peer-data-[state=closed]/menu-button:zoom-out-95
|
|
10
|
+
peer-data-[state=open]/menu-button:zoom-in-95
|
|
11
|
+
peer-data-[side=bottom]/menu-button:slide-in-from-top-2
|
|
12
|
+
peer-data-[side=left]/menu-button:slide-in-from-right-2
|
|
13
|
+
peer-data-[side=right]/menu-button:slide-in-from-left-2
|
|
14
|
+
peer-data-[side=top]/menu-button:slide-in-from-bottom-2
|
|
15
|
+
"
|
|
16
|
+
role="menu"
|
|
17
|
+
aria-orientation="vertical"
|
|
18
|
+
aria-labelledby="menu-button"
|
|
19
|
+
tabindex="-1"
|
|
20
|
+
>
|
|
21
|
+
<%= yield %>
|
|
22
|
+
</div>
|
|
23
|
+
<!--
|
|
24
|
+
<div role="separator" aria-orientation="horizontal" class="separator"></div>
|
|
25
|
+
-->
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<%# locals: (css_classes: "", **html_options) %>
|
|
2
|
+
<% merged_data = (html_options.delete(:data) || {}).merge(
|
|
3
|
+
component: "dropdown-menu",
|
|
4
|
+
controller: "dropdown-menu"
|
|
5
|
+
) %>
|
|
6
|
+
|
|
7
|
+
<%= content_tag :div, class: css_classes.presence, data: merged_data, **html_options do %>
|
|
8
|
+
<%= yield %>
|
|
9
|
+
<% end %>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<%# locals: (variant: :default, size: :default, css_classes: "", **html_options) %>
|
|
2
|
+
<% merged_data = (html_options.delete(:data) || {}).merge(
|
|
3
|
+
component: :empty,
|
|
4
|
+
variant: variant,
|
|
5
|
+
size: size
|
|
6
|
+
) %>
|
|
7
|
+
|
|
8
|
+
<%= content_tag :div, class: css_classes.presence, data: merged_data, **html_options do %>
|
|
9
|
+
<%= yield %>
|
|
10
|
+
<% end %>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<%# locals: (css_classes: "", **html_options) %>
|
|
2
|
+
<% merged_data = (html_options.delete(:data) || {}).merge(
|
|
3
|
+
component: :header
|
|
4
|
+
) %>
|
|
5
|
+
|
|
6
|
+
<%= content_tag :header, class: css_classes.presence, data: merged_data, **html_options do %>
|
|
7
|
+
<div data-header-part="inner"><%= yield %></div>
|
|
8
|
+
<% end %>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<%# locals: (title: "", subtitle: nil, icon: nil, text_icon: nil, icon_classes: "", submenu: false) %>
|
|
2
|
+
|
|
3
|
+
<ul class="flex w-full min-w-0 flex-col gap-1">
|
|
4
|
+
<li class="group/menu-item relative" data-controller="menu-button">
|
|
5
|
+
<button
|
|
6
|
+
data-state="closed"
|
|
7
|
+
class="
|
|
8
|
+
peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2
|
|
9
|
+
text-left outline-none ring-sidebar-ring transition-[width,height,padding]
|
|
10
|
+
focus-visible:ring-2 active:bg-sidebar-accent
|
|
11
|
+
active:text-sidebar-accent-foreground disabled:pointer-events-none
|
|
12
|
+
disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50
|
|
13
|
+
data-[state=open]:bg-sidebar-accent
|
|
14
|
+
data-[state=open]text-sidebar-accent-foreground
|
|
15
|
+
group-data-[collapsible=icon]:size-8! [&>span:last-child]:truncate
|
|
16
|
+
[&>svg]:size-4 [&>svg]:shrink-0 hover:bg-sidebar-accent
|
|
17
|
+
hover:text-sidebar-accent-foreground h-12 text-sm
|
|
18
|
+
group-data-[collapsible=icon]:p-0!
|
|
19
|
+
", data-action="menu-button#toggle", data-menu-button-target="button" }
|
|
20
|
+
>
|
|
21
|
+
<% if icon.present? %>
|
|
22
|
+
<%= image_tag icon, alt: "maquina", class: "#{icon_classes}" %>
|
|
23
|
+
<% elsif text_icon.present? %>
|
|
24
|
+
<div
|
|
25
|
+
class="
|
|
26
|
+
flex aspect-square size-8 items-center justify-center rounded-lg
|
|
27
|
+
bg-sidebar-primary text-sidebar-primary-foreground
|
|
28
|
+
"
|
|
29
|
+
>
|
|
30
|
+
<span class="<%= icon_classes %>"><%= text_icon %></span>
|
|
31
|
+
</div>
|
|
32
|
+
<% end %>
|
|
33
|
+
<div class="grid flex-1 text-left text-sm leading-tight">
|
|
34
|
+
<span class="truncate font-semibold"><%= title %></span>
|
|
35
|
+
<% if subtitle.present? %>
|
|
36
|
+
<span class="truncate text-xs"><%= subtitle %></span>
|
|
37
|
+
<% end %>
|
|
38
|
+
</div>
|
|
39
|
+
<%= icon_for(:chevron_up_down, class: "ml-auto") if submenu %>
|
|
40
|
+
</button>
|
|
41
|
+
|
|
42
|
+
<%= yield %>
|
|
43
|
+
</li>
|
|
44
|
+
</ul>
|
|
@@ -1,34 +1,13 @@
|
|
|
1
|
-
<%# locals: (
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
<% if pagy.prev.present? %>
|
|
6
|
-
<%= link_to "Previo",
|
|
7
|
-
paginated_path(route_helper, pagy, pagy.prev, param),
|
|
8
|
-
class: "button-ghost",
|
|
9
|
-
data: data %>
|
|
10
|
-
<% else %>
|
|
11
|
-
<button class="button-ghost" disabled>Previo</button>
|
|
12
|
-
<% end %>
|
|
13
|
-
</li>
|
|
14
|
-
<% pagy.series.each do |page| %>
|
|
15
|
-
<li>
|
|
16
|
-
<%= link_to page,
|
|
17
|
-
paginated_path(route_helper, pagy, page, param),
|
|
18
|
-
class: class_names("button-ghost", "button-outline": page.is_a?(String)),
|
|
19
|
-
data: data %>
|
|
20
|
-
</li>
|
|
21
|
-
<% end %>
|
|
1
|
+
<%# locals: (css_classes: "", **html_options) %>
|
|
2
|
+
<% merged_data = (html_options.delete(:data) || {}).merge(
|
|
3
|
+
component: :pagination
|
|
4
|
+
) %>
|
|
22
5
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
<% end %>
|
|
32
|
-
</li>
|
|
33
|
-
</ul>
|
|
34
|
-
</nav>
|
|
6
|
+
<%= content_tag :nav,
|
|
7
|
+
role: "navigation",
|
|
8
|
+
"aria-label": html_options.delete(:"aria-label") || "Pagination",
|
|
9
|
+
class: css_classes.presence,
|
|
10
|
+
data: merged_data,
|
|
11
|
+
**html_options do %>
|
|
12
|
+
<%= yield %>
|
|
13
|
+
<% end %>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<%# locals: (orientation: :horizontal) -%>
|
|
2
|
+
|
|
3
|
+
<div
|
|
4
|
+
data-orientation="<%= orientation %>"
|
|
5
|
+
role="none"
|
|
6
|
+
class="
|
|
7
|
+
bg-border shrink-0 data-[orientation=horizontal]:h-px
|
|
8
|
+
data-[orientation=horizontal]:w-full data-[orientation=vertical]:w-px mx-2
|
|
9
|
+
data-[orientation=vertical]:h-4
|
|
10
|
+
"
|
|
11
|
+
></div>
|
|
@@ -1,29 +1,39 @@
|
|
|
1
|
+
<%# locals: (id: nil, state: :collapsed, collapsible: :offcanvas, variant: :inset, side: :left, css_classes: "", **html_options) %>
|
|
2
|
+
<% random_id = id || "sidebar-#{SecureRandom.hex(6)}"
|
|
3
|
+
|
|
4
|
+
merged_data = (html_options.delete(:data) || {}).merge(
|
|
5
|
+
sidebar_part: :root,
|
|
6
|
+
sidebar_target: "sidebar",
|
|
7
|
+
state: state,
|
|
8
|
+
variant: variant,
|
|
9
|
+
collapsible: collapsible,
|
|
10
|
+
side: side
|
|
11
|
+
) %>
|
|
12
|
+
|
|
1
13
|
<aside
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
data
|
|
5
|
-
data-collapsible=""
|
|
6
|
-
id="sidebar"
|
|
14
|
+
id="<%= random_id %>"
|
|
15
|
+
class="group peer sidebar-loading <%= css_classes %>"
|
|
16
|
+
<%= tag.attributes(data: merged_data, **html_options) %>
|
|
7
17
|
>
|
|
8
|
-
|
|
18
|
+
<%# Sidebar gap (creates space for sidebar on desktop) %>
|
|
19
|
+
<div id="<%= random_id %>-gap" data-sidebar-part="gap"></div>
|
|
20
|
+
|
|
21
|
+
<%# Mobile backdrop overlay %>
|
|
9
22
|
<div
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"
|
|
13
|
-
data-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
<!-- Sidebar container -->
|
|
23
|
+
id="<%= random_id %>-overlay"
|
|
24
|
+
data-sidebar-part="backdrop"
|
|
25
|
+
data-sidebar-target="backdrop"
|
|
26
|
+
data-action="click->sidebar#backdropClick"
|
|
27
|
+
></div>
|
|
28
|
+
|
|
29
|
+
<%# Sidebar container (fixed positioned) %>
|
|
18
30
|
<div
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
transition-[left,right,width] ease-linear md:flex left-0 border-r
|
|
22
|
-
border-sidebar-border group-data-[collapsible=icon]:w-[--sidebar-width-icon]
|
|
23
|
-
"
|
|
31
|
+
id="<%= random_id %>-container"
|
|
32
|
+
data-sidebar-part="container"
|
|
24
33
|
data-sidebar-target="container"
|
|
25
34
|
>
|
|
26
|
-
|
|
35
|
+
<%# Inner sidebar content wrapper %>
|
|
36
|
+
<div id="<%= random_id %>-inner" data-sidebar-part="inner">
|
|
27
37
|
<%= yield %>
|
|
28
38
|
</div>
|
|
29
39
|
</div>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<%# locals: (collection:, columns:, caption: nil, variant: nil, table_variant: nil, empty_message: "No data available", row_id: nil, html_options: {}) %>
|
|
2
|
+
<%= render "components/table", variant: variant, table_variant: table_variant, **html_options do %>
|
|
3
|
+
<% if caption.present? %>
|
|
4
|
+
<%= render "components/table/caption" do %><%= caption %><% end %>
|
|
5
|
+
<% end %>
|
|
6
|
+
|
|
7
|
+
<%= render "components/table/header" do %>
|
|
8
|
+
<%= render "components/table/row" do %>
|
|
9
|
+
<% columns.each do |column| %>
|
|
10
|
+
<%= render "components/table/head", css_classes: table_alignment_class(column[:align]) do %>
|
|
11
|
+
<%= column[:label] %>
|
|
12
|
+
<% end %>
|
|
13
|
+
<% end %>
|
|
14
|
+
<% end %>
|
|
15
|
+
<% end %>
|
|
16
|
+
|
|
17
|
+
<%= render "components/table/body" do %>
|
|
18
|
+
<% if collection.empty? %>
|
|
19
|
+
<%= render "components/table/row" do %>
|
|
20
|
+
<%= render "components/table/cell", colspan: columns.size, data: { empty: "true" } do %>
|
|
21
|
+
<%= empty_message %>
|
|
22
|
+
<% end %>
|
|
23
|
+
<% end %>
|
|
24
|
+
<% else %>
|
|
25
|
+
<% collection.each do |item| %>
|
|
26
|
+
<%
|
|
27
|
+
row_options = {}
|
|
28
|
+
row_options[:id] = "row-#{item.public_send(row_id)}" if row_id && item.respond_to?(row_id)
|
|
29
|
+
%>
|
|
30
|
+
<%= render "components/table/row", **row_options do %>
|
|
31
|
+
<% columns.each do |column| %>
|
|
32
|
+
<%
|
|
33
|
+
value = if column[:key].is_a?(Proc)
|
|
34
|
+
column[:key].call(item)
|
|
35
|
+
elsif item.is_a?(Hash)
|
|
36
|
+
item[column[:key]] || item[column[:key].to_s]
|
|
37
|
+
elsif item.respond_to?(column[:key])
|
|
38
|
+
item.public_send(column[:key])
|
|
39
|
+
end
|
|
40
|
+
%>
|
|
41
|
+
<%= render "components/table/cell", css_classes: table_alignment_class(column[:align]) do %>
|
|
42
|
+
<%= value %>
|
|
43
|
+
<% end %>
|
|
44
|
+
<% end %>
|
|
45
|
+
<% end %>
|
|
46
|
+
<% end %>
|
|
47
|
+
<% end %>
|
|
48
|
+
<% end %>
|
|
49
|
+
<% end %>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<%# locals: (css_classes: "", container: true, variant: nil, table_variant: nil, **html_options) %>
|
|
2
|
+
<%
|
|
3
|
+
# Table data attributes - merge user data with component defaults
|
|
4
|
+
table_data = (html_options.delete(:data) || {}).merge(component: :table)
|
|
5
|
+
table_data[:variant] = table_variant if table_variant
|
|
6
|
+
|
|
7
|
+
# Container data attributes
|
|
8
|
+
container_data = { table_part: :container }
|
|
9
|
+
container_data[:variant] = variant if variant
|
|
10
|
+
%>
|
|
11
|
+
<% if container %>
|
|
12
|
+
<div data-table-part="container"<%= " data-variant=\"#{variant}\"" if variant %>>
|
|
13
|
+
<%= content_tag :table, class: css_classes.presence, data: table_data, **html_options do %>
|
|
14
|
+
<%= yield %>
|
|
15
|
+
<% end %>
|
|
16
|
+
</div>
|
|
17
|
+
<% else %>
|
|
18
|
+
<%= content_tag :table, class: css_classes.presence, data: table_data, **html_options do %>
|
|
19
|
+
<%= yield %>
|
|
20
|
+
<% end %>
|
|
21
|
+
<% end %>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<%# locals: (type: :single, variant: :default, size: :default, value: nil, disabled: false, css_classes: "", **html_options) %>
|
|
2
|
+
<% selected_values = case value
|
|
3
|
+
when Array then value.map(&:to_s)
|
|
4
|
+
when nil then []
|
|
5
|
+
else [value.to_s]
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
merged_data = (html_options.delete(:data) || {}).merge(
|
|
9
|
+
controller: "toggle-group",
|
|
10
|
+
component: "toggle-group",
|
|
11
|
+
variant: variant,
|
|
12
|
+
size: size,
|
|
13
|
+
"toggle-group-type-value": type,
|
|
14
|
+
"toggle-group-selected-value": selected_values.to_json
|
|
15
|
+
) %>
|
|
16
|
+
|
|
17
|
+
<%= content_tag :div,
|
|
18
|
+
role: "group",
|
|
19
|
+
class: css_classes.presence,
|
|
20
|
+
data: merged_data,
|
|
21
|
+
"aria-disabled": (disabled ? "true" : nil),
|
|
22
|
+
**html_options do %>
|
|
23
|
+
<%= yield %>
|
|
24
|
+
<% end %>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<%# locals: (text: nil, css_classes: "", **html_options) %>
|
|
2
|
+
<% merged_data = (html_options.delete(:data) || {}).merge(alert_part: :description) %>
|
|
3
|
+
|
|
4
|
+
<%= content_tag :div, class: css_classes.presence, data: merged_data, **html_options do %>
|
|
5
|
+
<%= text || yield %>
|
|
6
|
+
<% end %>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<%# locals: (text: nil, css_classes: "", **html_options) %>
|
|
2
|
+
<% merged_data = (html_options.delete(:data) || {}).merge(alert_part: :title) %>
|
|
3
|
+
|
|
4
|
+
<%= content_tag :div, class: css_classes.presence, data: merged_data, **html_options do %>
|
|
5
|
+
<%= text || yield %>
|
|
6
|
+
<% end %>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<%# locals: (css_classes: "", **html_options) %>
|
|
2
|
+
<% merged_data = (html_options.delete(:data) || {}).merge(
|
|
3
|
+
breadcrumb_part: :ellipsis
|
|
4
|
+
) %>
|
|
5
|
+
|
|
6
|
+
<%= content_tag :span, role: "presentation", class: css_classes, data: merged_data, **html_options do %>
|
|
7
|
+
<%= icon_for(:ellipsis) %>
|
|
8
|
+
<span class="sr-only">More</span>
|
|
9
|
+
<% end %>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<%# locals: (css_classes: "", **html_options) %>
|
|
2
|
+
<% merged_data = (html_options.delete(:data) || {}).merge(
|
|
3
|
+
breadcrumb_part: :page
|
|
4
|
+
) %>
|
|
5
|
+
|
|
6
|
+
<%= content_tag :span, role: "link", aria: {current: "page", disabled: true}, class: css_classes, data: merged_data, **html_options do %>
|
|
7
|
+
<%= yield %>
|
|
8
|
+
<% end %>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<%# locals: (icon: :chevron_right, css_classes: "", **html_options) %>
|
|
2
|
+
<% merged_data = (html_options.delete(:data) || {}).merge(
|
|
3
|
+
breadcrumb_part: :separator
|
|
4
|
+
) %>
|
|
5
|
+
|
|
6
|
+
<li
|
|
7
|
+
role="presentation"
|
|
8
|
+
aria-hidden="true"
|
|
9
|
+
class="<%= css_classes %>"
|
|
10
|
+
<%= tag.attributes(data: merged_data, **html_options) %>
|
|
11
|
+
>
|
|
12
|
+
<% if icon == :custom %>
|
|
13
|
+
<%= yield %>
|
|
14
|
+
<% else %>
|
|
15
|
+
<%= icon_for(icon) %>
|
|
16
|
+
<% end %>
|
|
17
|
+
</li>
|