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.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +349 -138
  3. data/app/assets/images/maquina.svg +1 -0
  4. data/app/assets/stylesheets/alert.css +143 -0
  5. data/app/assets/stylesheets/badge.css +145 -0
  6. data/app/assets/stylesheets/breadcrumbs.css +163 -0
  7. data/app/assets/stylesheets/card.css +128 -0
  8. data/app/assets/stylesheets/dropdown_menu.css +248 -0
  9. data/app/assets/stylesheets/empty.css +133 -0
  10. data/app/assets/stylesheets/form.css +617 -0
  11. data/app/assets/stylesheets/header.css +61 -0
  12. data/app/assets/stylesheets/maquina_components.css +143 -64
  13. data/app/assets/stylesheets/pagination.css +154 -0
  14. data/app/assets/stylesheets/sidebar.css +477 -0
  15. data/app/assets/stylesheets/table.css +205 -0
  16. data/app/assets/stylesheets/toggle_group.css +151 -0
  17. data/app/assets/tailwind/maquina_components_engine/engine.css +16 -0
  18. data/app/helpers/maquina_components/breadcrumbs_helper.rb +118 -0
  19. data/app/helpers/maquina_components/dropdown_menu_helper.rb +249 -0
  20. data/app/helpers/maquina_components/empty_helper.rb +102 -0
  21. data/app/helpers/{components → maquina_components}/icons_helper.rb +40 -3
  22. data/app/helpers/maquina_components/pagination_helper.rb +153 -0
  23. data/app/helpers/maquina_components/sidebar_helper.rb +63 -0
  24. data/app/helpers/maquina_components/table_helper.rb +144 -0
  25. data/app/helpers/maquina_components/toggle_group_helper.rb +172 -0
  26. data/app/javascript/controllers/breadcrumb_controller.js +71 -0
  27. data/app/javascript/controllers/dropdown_menu_controller.js +203 -0
  28. data/app/javascript/controllers/menu_button_controller.js +59 -0
  29. data/app/javascript/controllers/sidebar_controller.js +316 -0
  30. data/app/javascript/controllers/sidebar_trigger_controller.js +32 -0
  31. data/app/javascript/controllers/toggle_group_controller.js +178 -0
  32. data/app/views/components/_alert.html.erb +11 -10
  33. data/app/views/components/_badge.html.erb +10 -0
  34. data/app/views/components/_breadcrumbs.html.erb +16 -0
  35. data/app/views/components/_card.html.erb +4 -8
  36. data/app/views/components/_dropdown.html.erb +25 -0
  37. data/app/views/components/_dropdown_menu.html.erb +9 -0
  38. data/app/views/components/_empty.html.erb +10 -0
  39. data/app/views/components/_header.html.erb +8 -0
  40. data/app/views/components/_menu_button.html.erb +44 -0
  41. data/app/views/components/_pagination.html.erb +12 -33
  42. data/app/views/components/_separator.html.erb +11 -0
  43. data/app/views/components/_sidebar.html.erb +30 -20
  44. data/app/views/components/_simple_table.html.erb +49 -0
  45. data/app/views/components/_table.html.erb +21 -0
  46. data/app/views/components/_toggle_group.html.erb +24 -0
  47. data/app/views/components/alert/_description.html.erb +6 -0
  48. data/app/views/components/alert/_title.html.erb +6 -0
  49. data/app/views/components/breadcrumbs/_ellipsis.html.erb +9 -0
  50. data/app/views/components/breadcrumbs/_item.html.erb +8 -0
  51. data/app/views/components/breadcrumbs/_link.html.erb +8 -0
  52. data/app/views/components/breadcrumbs/_list.html.erb +8 -0
  53. data/app/views/components/breadcrumbs/_page.html.erb +8 -0
  54. data/app/views/components/breadcrumbs/_separator.html.erb +17 -0
  55. data/app/views/components/card/_action.html.erb +6 -0
  56. data/app/views/components/card/_content.html.erb +9 -0
  57. data/app/views/components/card/_description.html.erb +6 -0
  58. data/app/views/components/card/_footer.html.erb +17 -0
  59. data/app/views/components/card/_header.html.erb +9 -0
  60. data/app/views/components/card/_title.html.erb +9 -0
  61. data/app/views/components/dropdown_menu/_content.html.erb +20 -0
  62. data/app/views/components/dropdown_menu/_group.html.erb +12 -0
  63. data/app/views/components/dropdown_menu/_item.html.erb +29 -0
  64. data/app/views/components/dropdown_menu/_label.html.erb +13 -0
  65. data/app/views/components/dropdown_menu/_separator.html.erb +11 -0
  66. data/app/views/components/dropdown_menu/_shortcut.html.erb +12 -0
  67. data/app/views/components/dropdown_menu/_trigger.html.erb +24 -0
  68. data/app/views/components/empty/_content.html.erb +8 -0
  69. data/app/views/components/empty/_description.html.erb +12 -0
  70. data/app/views/components/empty/_header.html.erb +8 -0
  71. data/app/views/components/empty/_media.html.erb +13 -0
  72. data/app/views/components/empty/_title.html.erb +12 -0
  73. data/app/views/components/pagination/_content.html.erb +8 -0
  74. data/app/views/components/pagination/_ellipsis.html.erb +28 -0
  75. data/app/views/components/pagination/_item.html.erb +8 -0
  76. data/app/views/components/pagination/_link.html.erb +23 -0
  77. data/app/views/components/pagination/_next.html.erb +57 -0
  78. data/app/views/components/pagination/_previous.html.erb +57 -0
  79. data/app/views/components/sidebar/_content.html.erb +8 -0
  80. data/app/views/components/sidebar/_footer.html.erb +8 -0
  81. data/app/views/components/sidebar/_group.html.erb +12 -0
  82. data/app/views/components/sidebar/_header.html.erb +8 -0
  83. data/app/views/components/sidebar/_inset.html.erb +8 -0
  84. data/app/views/components/sidebar/_menu.html.erb +8 -0
  85. data/app/views/components/sidebar/_menu_button.html.erb +14 -0
  86. data/app/views/components/sidebar/_menu_item.html.erb +7 -0
  87. data/app/views/components/sidebar/_menu_link.html.erb +32 -0
  88. data/app/views/components/sidebar/_provider.html.erb +16 -0
  89. data/app/views/components/sidebar/_trigger.html.erb +12 -0
  90. data/app/views/components/stats/_stats_card.html.erb +100 -0
  91. data/app/views/components/stats/_stats_grid.html.erb +38 -0
  92. data/app/views/components/table/_body.html.erb +5 -0
  93. data/app/views/components/table/_caption.html.erb +5 -0
  94. data/app/views/components/table/_cell.html.erb +5 -0
  95. data/app/views/components/table/_footer.html.erb +5 -0
  96. data/app/views/components/table/_head.html.erb +8 -0
  97. data/app/views/components/table/_header.html.erb +8 -0
  98. data/app/views/components/table/_row.html.erb +8 -0
  99. data/app/views/components/toggle_group/_item.html.erb +19 -0
  100. data/config/importmap.rb +1 -0
  101. data/lib/generators/maquina_components/install/USAGE +39 -0
  102. data/lib/generators/maquina_components/install/install_generator.rb +123 -0
  103. data/lib/generators/maquina_components/install/templates/maquina_components_helper.rb.tt +68 -0
  104. data/lib/generators/maquina_components/install/templates/theme.css.tt +179 -0
  105. data/lib/maquina_components/engine.rb +10 -0
  106. data/lib/maquina_components/version.rb +1 -1
  107. metadata +116 -12
  108. data/app/helpers/components/pagination_helper.rb +0 -15
  109. data/app/views/components/_card_content.html.erb +0 -5
  110. data/app/views/components/_card_header.html.erb +0 -8
  111. data/app/views/components/_sidebar_content.html.erb +0 -8
  112. data/app/views/components/_sidebar_group.html.erb +0 -42
  113. data/app/views/components/_sidebar_header.html.erb +0 -3
@@ -1,99 +1,178 @@
1
- @utility button-base {
2
- display: inline-flex;
3
- align-items: center;
4
- justify-content: center;
5
- gap: 0.5rem;
6
- /* gap-2 */
7
- white-space: nowrap;
8
- border-radius: 0.375rem;
9
- /* rounded-md */
10
- font-size: 0.875rem;
11
- /* text-sm */
12
- font-weight: 500;
13
- /* font-medium */
14
- height: 2.25rem;
15
- /* h-9 */
16
- padding: 0.5rem 1rem;
17
- /* px-4 py-2 */
18
- transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
19
- transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
20
- transition-duration: 150ms;
1
+ /* Base animation utilities */
2
+ @utility animate-in {
3
+ animation-duration: var(--duration-normal);
4
+ animation-timing-function: var(--ease-out);
5
+ animation-fill-mode: forwards;
6
+ }
7
+
8
+ @utility animate-out {
9
+ animation-duration: var(--duration-normal);
10
+ animation-timing-function: var(--ease-in);
11
+ animation-fill-mode: forwards;
12
+ }
13
+
14
+ /* Fade animations */
15
+ @keyframes fade-in-0 {
16
+ from {
17
+ opacity: 0;
18
+ }
19
+
20
+ to {
21
+ opacity: 1;
22
+ }
23
+ }
24
+
25
+ @keyframes fade-out-0 {
26
+ from {
27
+ opacity: 1;
28
+ }
29
+
30
+ to {
31
+ opacity: 0;
32
+ }
33
+ }
34
+
35
+ @utility fade-in-0 {
36
+ animation-name: fade-in-0;
37
+ }
38
+
39
+ @utility fade-out-0 {
40
+ animation-name: fade-out-0;
41
+ }
21
42
 
22
- &:focus-visible {
23
- outline: none;
24
- ring-width: 1px;
25
- ring-color: var(--color-ring);
43
+ /* Zoom animations */
44
+ @keyframes zoom-in-95 {
45
+ from {
46
+ transform: scale(0.95);
47
+ opacity: 0;
26
48
  }
27
49
 
28
- &:disabled {
29
- pointer-events: none;
30
- opacity: 0.5;
50
+ to {
51
+ transform: scale(1);
52
+ opacity: 1;
53
+ }
54
+ }
55
+
56
+ @keyframes zoom-out-95 {
57
+ from {
58
+ transform: scale(1);
59
+ opacity: 1;
31
60
  }
32
61
 
33
- & svg {
34
- pointer-events: none;
35
- width: 1rem;
36
- /* size-4 */
37
- height: 1rem;
38
- /* size-4 */
39
- flex-shrink: 0;
62
+ to {
63
+ transform: scale(0.95);
64
+ opacity: 0;
40
65
  }
41
66
  }
42
67
 
43
- /* Form */
44
- .form-base {
45
- @apply flex flex-col w-full gap-6;
68
+ @utility zoom-in-95 {
69
+ animation-name: zoom-in-95;
46
70
  }
47
71
 
48
- .form-group {
49
- @apply flex flex-col gap-2;
72
+ @utility zoom-out-95 {
73
+ animation-name: zoom-out-95;
50
74
  }
51
75
 
52
- .form-label {
53
- @apply text-foreground text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70;
76
+ /* Slide animations with specific measurements */
77
+ @keyframes slide-in-from-top-2 {
78
+ from {
79
+ transform: translateY(-0.5rem);
80
+ opacity: 0;
81
+ }
82
+
83
+ to {
84
+ transform: translateY(0);
85
+ opacity: 1;
86
+ }
54
87
  }
55
88
 
56
- .form-input {
57
- @apply flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm;
89
+ @keyframes slide-in-from-bottom-2 {
90
+ from {
91
+ transform: translateY(0.5rem);
92
+ opacity: 0;
93
+ }
94
+
95
+ to {
96
+ transform: translateY(0);
97
+ opacity: 1;
98
+ }
58
99
  }
59
100
 
60
- .form-select {
61
- @apply col-start-1 row-start-1 w-full appearance-none rounded-md bg-transparent py-1.5 pl-3 pr-8 text-base text-foreground border border-input shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 sm:text-sm/6;
101
+ @keyframes slide-in-from-left-2 {
102
+ from {
103
+ transform: translateX(-0.5rem);
104
+ opacity: 0;
105
+ }
106
+
107
+ to {
108
+ transform: translateX(0);
109
+ opacity: 1;
110
+ }
62
111
  }
63
112
 
64
- .form-select-group {
65
- @apply grid grid-cols-1;
113
+ @keyframes slide-in-from-right-2 {
114
+ from {
115
+ transform: translateX(0.5rem);
116
+ opacity: 0;
117
+ }
66
118
 
67
- & svg {
68
- @apply pointer-events-none col-start-1 row-start-1 mr-2 size-5 self-center justify-self-end text-muted-foreground sm:size-4;
119
+ to {
120
+ transform: translateX(0);
121
+ opacity: 1;
69
122
  }
70
123
  }
71
124
 
72
- .form-help-text {
73
- @apply text-sm text-muted-foreground;
125
+ @utility slide-in-from-top-2 {
126
+ animation-name: slide-in-from-top-2;
127
+ }
128
+
129
+ @utility slide-in-from-bottom-2 {
130
+ animation-name: slide-in-from-bottom-2;
131
+ }
132
+
133
+ @utility slide-in-from-left-2 {
134
+ animation-name: slide-in-from-left-2;
74
135
  }
75
136
 
76
- .form-error-text {
77
- @apply text-sm text-destructive;
137
+ @utility slide-in-from-right-2 {
138
+ animation-name: slide-in-from-right-2;
78
139
  }
79
140
 
80
- /* Buttons */
81
- .button-primary {
82
- @apply button-base bg-primary text-primary-foreground hover:bg-primary/90;
141
+ /* Duration modifiers */
142
+ @utility duration-fast {
143
+ animation-duration: var(--duration-fast);
83
144
  }
84
145
 
85
- .button-secondary {
86
- @apply button-base bg-secondary text-secondary-foreground hover:bg-secondary/80;
146
+ @utility duration-normal {
147
+ animation-duration: var(--duration-normal);
87
148
  }
88
149
 
89
- .button-ghost {
90
- @apply button-base hover:bg-secondary hover:text-secondary-foreground;
150
+ @utility duration-slow {
151
+ animation-duration: var(--duration-slow);
91
152
  }
92
153
 
93
- .button-outline {
94
- @apply button-base border border-border bg-background shadow-sm hover:bg-secondary/40 hover:text-secondary-foreground;
154
+ /* Sidebar */
155
+
156
+ .sidebar-primary-button {
157
+ @apply flex w-full items-center gap-2 overflow-hidden rounded-md p-2
158
+ text-left outline-hidden ring-sidebar-ring transition-[width,height,padding]
159
+ focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50
160
+ group-has-data-[sidebar=menu-action]/menu-item:pr-8
161
+ aria-disabled:pointer-events-none aria-disabled:opacity-50
162
+ group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2!
163
+ [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 h-8 text-sm
164
+ bg-primary text-primary-foreground hover:bg-primary/90
165
+ hover:text-primary-foreground min-w-8 duration-200 ease-linear;
95
166
  }
96
167
 
97
- .button-destructive {
98
- @apply button-base bg-destructive text-destructive shadow-sm hover:bg-destructive/90;
168
+ .sidebar-outline-button {
169
+ @apply inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md
170
+ text-sm font-medium transition-all disabled:pointer-events-none
171
+ disabled:opacity-50 [&_svg]:pointer-events-none
172
+ [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none
173
+ focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]
174
+ aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40
175
+ aria-invalid:border-destructive border bg-background shadow-xs hover:bg-accent
176
+ hover:text-accent-foreground dark:bg-input/30 dark:border-input
177
+ dark:hover:bg-input/50 size-8 group-data-[collapsible=icon]:opacity-0;
99
178
  }
@@ -0,0 +1,154 @@
1
+ /* ===== Pagination Component Styles ===== */
2
+ /*
3
+ * Navigation for paginated content following shadcn/ui patterns.
4
+ * Uses data attributes for styling to avoid inline utility classes.
5
+ * Fully compatible with dark mode via CSS variables.
6
+ *
7
+ * Structure:
8
+ * - pagination (nav root)
9
+ * - content (ul list)
10
+ * - item (li wrapper)
11
+ * - link / previous / next / ellipsis
12
+ */
13
+
14
+ /* ===== Root Container ===== */
15
+ [data-component="pagination"] {
16
+ display: flex;
17
+ justify-content: center;
18
+ width: 100%;
19
+ @apply mx-auto;
20
+ }
21
+
22
+ /* ===== Content List ===== */
23
+ [data-pagination-part="content"] {
24
+ display: flex;
25
+ flex-direction: row;
26
+ align-items: center;
27
+ @apply gap-1;
28
+ list-style: none;
29
+ margin: 0;
30
+ padding: 0;
31
+ }
32
+
33
+ /* ===== Item Wrapper ===== */
34
+ [data-pagination-part="item"] {
35
+ /* No specific styles needed, just a wrapper */
36
+ }
37
+
38
+ /* ===== Shared Button/Link Base Styles ===== */
39
+ [data-pagination-part="link"],
40
+ [data-pagination-part="previous"],
41
+ [data-pagination-part="next"] {
42
+ display: inline-flex;
43
+ align-items: center;
44
+ justify-content: center;
45
+ white-space: nowrap;
46
+ @apply rounded-md text-sm font-medium;
47
+ @apply transition-colors duration-150;
48
+
49
+ /* Size */
50
+ @apply h-9 min-w-9 px-3;
51
+
52
+ /* Colors - ghost style */
53
+ background-color: transparent;
54
+ color: var(--foreground);
55
+
56
+ /* Remove default link/button styles */
57
+ text-decoration: none;
58
+ border: none;
59
+ cursor: pointer;
60
+ }
61
+
62
+ /* ===== Page Number Links ===== */
63
+ [data-pagination-part="link"]:hover:not([aria-disabled="true"]) {
64
+ background-color: var(--accent);
65
+ color: var(--accent-foreground);
66
+ }
67
+
68
+ [data-pagination-part="link"][data-active="true"] {
69
+ border: 1px solid var(--border);
70
+ background-color: var(--background);
71
+ color: var(--foreground);
72
+ }
73
+
74
+ [data-pagination-part="link"][data-active="true"]:hover {
75
+ background-color: var(--accent);
76
+ color: var(--accent-foreground);
77
+ }
78
+
79
+ /* ===== Previous/Next Navigation ===== */
80
+ [data-pagination-part="previous"],
81
+ [data-pagination-part="next"] {
82
+ @apply gap-1 px-2.5;
83
+ }
84
+
85
+ [data-pagination-part="previous"]:hover:not([aria-disabled="true"]),
86
+ [data-pagination-part="next"]:hover:not([aria-disabled="true"]) {
87
+ background-color: var(--accent);
88
+ color: var(--accent-foreground);
89
+ }
90
+
91
+ /* Hide labels on small screens */
92
+ @media (max-width: 640px) {
93
+ [data-pagination-part="previous"] span,
94
+ [data-pagination-part="next"] span {
95
+ @apply sr-only;
96
+ }
97
+
98
+ [data-pagination-part="previous"],
99
+ [data-pagination-part="next"] {
100
+ @apply px-2;
101
+ }
102
+ }
103
+
104
+ /* ===== Ellipsis ===== */
105
+ [data-pagination-part="ellipsis"] {
106
+ display: flex;
107
+ align-items: center;
108
+ justify-content: center;
109
+ @apply h-9 w-9;
110
+ color: var(--muted-foreground);
111
+ }
112
+
113
+ /* ===== Disabled State ===== */
114
+ [data-pagination-part="link"][aria-disabled="true"],
115
+ [data-pagination-part="previous"][aria-disabled="true"],
116
+ [data-pagination-part="next"][aria-disabled="true"] {
117
+ @apply opacity-50 pointer-events-none cursor-not-allowed;
118
+ }
119
+
120
+ /* ===== Focus Visible ===== */
121
+ [data-pagination-part="link"]:focus-visible,
122
+ [data-pagination-part="previous"]:focus-visible,
123
+ [data-pagination-part="next"]:focus-visible {
124
+ @apply outline-none;
125
+ box-shadow: 0 0 0 2px var(--background),
126
+ 0 0 0 4px var(--ring);
127
+ }
128
+
129
+ /* ===== Icon Sizing ===== */
130
+ [data-pagination-part="previous"] svg,
131
+ [data-pagination-part="next"] svg,
132
+ [data-pagination-part="ellipsis"] svg {
133
+ @apply size-4 shrink-0;
134
+ }
135
+
136
+ /* ===== Screen Reader Only ===== */
137
+ .sr-only {
138
+ position: absolute;
139
+ width: 1px;
140
+ height: 1px;
141
+ padding: 0;
142
+ margin: -1px;
143
+ overflow: hidden;
144
+ clip: rect(0, 0, 0, 0);
145
+ white-space: nowrap;
146
+ border-width: 0;
147
+ }
148
+
149
+ /* ===== Dark Mode ===== */
150
+ /*
151
+ * Dark mode is handled automatically through CSS variables.
152
+ * The theme variables change based on the .dark class on html/body.
153
+ * No additional dark mode styles needed here.
154
+ */