better_ui 0.1.0 → 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 (142) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +225 -119
  4. data/app/assets/stylesheets/better_ui/application.css +0 -356
  5. data/app/components/better_ui/application/card/component.html.erb +20 -0
  6. data/app/components/better_ui/application/card/component.rb +214 -0
  7. data/app/components/better_ui/application/main/component.html.erb +9 -0
  8. data/app/components/better_ui/application/main/component.rb +123 -0
  9. data/app/components/better_ui/application/navbar/component.html.erb +92 -0
  10. data/app/components/better_ui/application/navbar/component.rb +136 -0
  11. data/app/components/better_ui/application/sidebar/component.html.erb +190 -0
  12. data/app/components/better_ui/application/sidebar/component.rb +129 -0
  13. data/app/components/better_ui/general/alert/component.html.erb +32 -0
  14. data/app/components/better_ui/general/alert/component.rb +242 -0
  15. data/app/components/better_ui/general/avatar/component.html.erb +20 -0
  16. data/app/components/better_ui/general/avatar/component.rb +301 -0
  17. data/app/components/better_ui/general/badge/component.html.erb +23 -0
  18. data/app/components/better_ui/general/badge/component.rb +248 -0
  19. data/app/components/better_ui/general/breadcrumb/component.html.erb +15 -0
  20. data/app/components/better_ui/general/breadcrumb/component.rb +187 -0
  21. data/app/components/better_ui/general/button/component.html.erb +34 -0
  22. data/app/components/better_ui/general/button/component.rb +214 -0
  23. data/app/components/better_ui/general/divider/component.html.erb +10 -0
  24. data/app/components/better_ui/general/divider/component.rb +226 -0
  25. data/app/components/better_ui/general/dropdown/component.html.erb +14 -0
  26. data/app/components/better_ui/general/dropdown/component.rb +219 -0
  27. data/app/components/better_ui/general/dropdown/divider_component.html.erb +1 -0
  28. data/app/components/better_ui/general/dropdown/divider_component.rb +41 -0
  29. data/app/components/better_ui/general/dropdown/item_component.html.erb +6 -0
  30. data/app/components/better_ui/general/dropdown/item_component.rb +118 -0
  31. data/app/components/better_ui/general/field/component.html.erb +27 -0
  32. data/app/components/better_ui/general/field/component.rb +37 -0
  33. data/app/components/better_ui/general/heading/component.html.erb +22 -0
  34. data/app/components/better_ui/general/heading/component.rb +257 -0
  35. data/app/components/better_ui/general/icon/component.html.erb +7 -0
  36. data/app/components/better_ui/general/icon/component.rb +239 -0
  37. data/app/components/better_ui/general/input/checkbox/component.html.erb +5 -0
  38. data/app/components/better_ui/general/input/checkbox/component.rb +238 -0
  39. data/app/components/better_ui/general/input/datetime/component.html.erb +5 -0
  40. data/app/components/better_ui/general/input/datetime/component.rb +223 -0
  41. data/app/components/better_ui/general/input/radio/component.html.erb +5 -0
  42. data/app/components/better_ui/general/input/radio/component.rb +230 -0
  43. data/app/components/better_ui/general/input/select/component.html.erb +16 -0
  44. data/app/components/better_ui/general/input/select/component.rb +184 -0
  45. data/app/components/better_ui/general/input/select/select_component.html.erb +5 -0
  46. data/app/components/better_ui/general/input/select/select_component.rb +37 -0
  47. data/app/components/better_ui/general/input/text/component.html.erb +5 -0
  48. data/app/components/better_ui/general/input/text/component.rb +171 -0
  49. data/app/components/better_ui/general/input/textarea/component.html.erb +5 -0
  50. data/app/components/better_ui/general/input/textarea/component.rb +166 -0
  51. data/app/components/better_ui/general/link/component.html.erb +18 -0
  52. data/app/components/better_ui/general/link/component.rb +258 -0
  53. data/app/components/better_ui/general/modal/component.html.erb +42 -0
  54. data/app/components/better_ui/general/modal/component.rb +165 -0
  55. data/app/components/better_ui/general/pagination/component.html.erb +85 -0
  56. data/app/components/better_ui/general/pagination/component.rb +216 -0
  57. data/app/components/better_ui/general/panel/component.html.erb +28 -0
  58. data/app/components/better_ui/general/panel/component.rb +249 -0
  59. data/app/components/better_ui/general/progress/component.html.erb +11 -0
  60. data/app/components/better_ui/general/progress/component.rb +160 -0
  61. data/app/components/better_ui/general/spinner/component.html.erb +35 -0
  62. data/app/components/better_ui/general/spinner/component.rb +93 -0
  63. data/app/components/better_ui/general/table/component.html.erb +5 -0
  64. data/app/components/better_ui/general/table/component.rb +217 -0
  65. data/app/components/better_ui/general/table/tbody_component.html.erb +3 -0
  66. data/app/components/better_ui/general/table/tbody_component.rb +30 -0
  67. data/app/components/better_ui/general/table/td_component.html.erb +3 -0
  68. data/app/components/better_ui/general/table/td_component.rb +44 -0
  69. data/app/components/better_ui/general/table/tfoot_component.html.erb +3 -0
  70. data/app/components/better_ui/general/table/tfoot_component.rb +28 -0
  71. data/app/components/better_ui/general/table/th_component.html.erb +6 -0
  72. data/app/components/better_ui/general/table/th_component.rb +51 -0
  73. data/app/components/better_ui/general/table/thead_component.html.erb +3 -0
  74. data/app/components/better_ui/general/table/thead_component.rb +28 -0
  75. data/app/components/better_ui/general/table/tr_component.html.erb +3 -0
  76. data/app/components/better_ui/general/table/tr_component.rb +30 -0
  77. data/app/components/better_ui/general/tabs/component.html.erb +3 -0
  78. data/app/components/better_ui/general/tabs/component.rb +102 -0
  79. data/app/components/better_ui/general/tabs/panel_component.html.erb +3 -0
  80. data/app/components/better_ui/general/tabs/panel_component.rb +37 -0
  81. data/app/components/better_ui/general/tabs/tab_component.html.erb +13 -0
  82. data/app/components/better_ui/general/tabs/tab_component.rb +111 -0
  83. data/app/components/better_ui/general/tag/component.html.erb +3 -0
  84. data/app/components/better_ui/general/tag/component.rb +104 -0
  85. data/app/components/better_ui/general/tooltip/component.html.erb +7 -0
  86. data/app/components/better_ui/general/tooltip/component.rb +239 -0
  87. data/app/helpers/better_ui/application/components/card/card_helper.rb +96 -0
  88. data/app/helpers/better_ui/application/components/card.rb +11 -0
  89. data/app/helpers/better_ui/application/components/main/main_helper.rb +64 -0
  90. data/app/helpers/better_ui/application/components/navbar/navbar_helper.rb +77 -0
  91. data/app/helpers/better_ui/application/components/sidebar/sidebar_helper.rb +51 -0
  92. data/app/helpers/better_ui/application_helper.rb +51 -179
  93. data/app/helpers/better_ui/general/components/alert/alert_helper.rb +57 -0
  94. data/app/helpers/better_ui/general/components/avatar/avatar_helper.rb +29 -0
  95. data/app/helpers/better_ui/general/components/badge/badge_helper.rb +53 -0
  96. data/app/helpers/better_ui/general/components/breadcrumb/breadcrumb_helper.rb +37 -0
  97. data/app/helpers/better_ui/general/components/button/button_helper.rb +65 -0
  98. data/app/helpers/better_ui/general/components/container/container_helper.rb +60 -0
  99. data/app/helpers/better_ui/general/components/divider/divider_helper.rb +63 -0
  100. data/app/helpers/better_ui/general/components/dropdown/divider_helper.rb +32 -0
  101. data/app/helpers/better_ui/general/components/dropdown/dropdown_helper.rb +79 -0
  102. data/app/helpers/better_ui/general/components/dropdown/item_helper.rb +62 -0
  103. data/app/helpers/better_ui/general/components/field/field_helper.rb +26 -0
  104. data/app/helpers/better_ui/general/components/heading/heading_helper.rb +72 -0
  105. data/app/helpers/better_ui/general/components/icon/icon_helper.rb +16 -0
  106. data/app/helpers/better_ui/general/components/input/checkbox/checkbox_helper.rb +81 -0
  107. data/app/helpers/better_ui/general/components/input/datetime/datetime_helper.rb +91 -0
  108. data/app/helpers/better_ui/general/components/input/radio/radio_helper.rb +79 -0
  109. data/app/helpers/better_ui/general/components/input/radio_group/radio_group_helper.rb +124 -0
  110. data/app/helpers/better_ui/general/components/input/select/select_helper.rb +70 -0
  111. data/app/helpers/better_ui/general/components/input/text/text_helper.rb +138 -0
  112. data/app/helpers/better_ui/general/components/input/textarea/textarea_helper.rb +73 -0
  113. data/app/helpers/better_ui/general/components/link/link_helper.rb +89 -0
  114. data/app/helpers/better_ui/general/components/modal/modal_helper.rb +95 -0
  115. data/app/helpers/better_ui/general/components/pagination/pagination_helper.rb +82 -0
  116. data/app/helpers/better_ui/general/components/panel/panel_helper.rb +83 -0
  117. data/app/helpers/better_ui/general/components/progress/progress_helper.rb +53 -0
  118. data/app/helpers/better_ui/general/components/spinner/spinner_helper.rb +19 -0
  119. data/app/helpers/better_ui/general/components/table/table_helper.rb +53 -0
  120. data/app/helpers/better_ui/general/components/table/tbody_helper.rb +13 -0
  121. data/app/helpers/better_ui/general/components/table/td_helper.rb +19 -0
  122. data/app/helpers/better_ui/general/components/table/tfoot_helper.rb +13 -0
  123. data/app/helpers/better_ui/general/components/table/th_helper.rb +19 -0
  124. data/app/helpers/better_ui/general/components/table/thead_helper.rb +13 -0
  125. data/app/helpers/better_ui/general/components/table/tr_helper.rb +13 -0
  126. data/app/helpers/better_ui/general/components/tabs/panel_helper.rb +62 -0
  127. data/app/helpers/better_ui/general/components/tabs/tab_helper.rb +55 -0
  128. data/app/helpers/better_ui/general/components/tabs/tabs_helper.rb +62 -0
  129. data/app/helpers/better_ui/general/components/tag/tag_helper.rb +26 -0
  130. data/app/helpers/better_ui/general/components/tooltip/tooltip_helper.rb +60 -0
  131. data/app/views/layouts/better_ui/application.html.erb +6 -124
  132. data/config/initializers/lookbook.rb +23 -0
  133. data/config/routes.rb +0 -8
  134. data/lib/better_ui/engine.rb +5 -19
  135. data/lib/better_ui/railtie.rb +20 -0
  136. data/lib/better_ui/version.rb +1 -1
  137. data/lib/better_ui.rb +4 -20
  138. metadata +155 -28
  139. data/app/controllers/better_ui/docs_controller.rb +0 -41
  140. data/app/views/better_ui/docs/component.html.erb +0 -365
  141. data/app/views/better_ui/docs/index.html.erb +0 -100
  142. data/app/views/better_ui/docs/show.html.erb +0 -60
@@ -13,359 +13,3 @@
13
13
  *= require_tree .
14
14
  *= require_self
15
15
  */
16
-
17
- /*
18
- * Better UI - Componenti UI per Rails
19
- *
20
- *= require_self
21
- */
22
-
23
- :root {
24
- /* Colori principali */
25
- --better-ui-primary: #007bff;
26
- --better-ui-secondary: #6c757d;
27
- --better-ui-success: #28a745;
28
- --better-ui-danger: #dc3545;
29
- --better-ui-warning: #ffc107;
30
- --better-ui-info: #17a2b8;
31
-
32
- /* Colori di sfondo */
33
- --better-ui-bg-light: #ffffff;
34
- --better-ui-bg-dark: #343a40;
35
-
36
- /* Colori di testo */
37
- --better-ui-text-dark: #343a40;
38
- --better-ui-text-light: #f8f9fa;
39
-
40
- /* Bordi */
41
- --better-ui-border-color: #dee2e6;
42
- --better-ui-border-radius: 0.25rem;
43
-
44
- /* Spaziature */
45
- --better-ui-spacer: 1rem;
46
- --better-ui-spacer-sm: 0.5rem;
47
- --better-ui-spacer-lg: 1.5rem;
48
-
49
- /* Transizioni */
50
- --better-ui-transition: all 0.2s ease-in-out;
51
- }
52
-
53
- /*
54
- * Button
55
- */
56
- .better-ui-button {
57
- display: inline-block;
58
- font-weight: 400;
59
- text-align: center;
60
- white-space: nowrap;
61
- vertical-align: middle;
62
- user-select: none;
63
- border: 1px solid transparent;
64
- padding: var(--better-ui-spacer-sm) var(--better-ui-spacer);
65
- font-size: 1rem;
66
- line-height: 1.5;
67
- border-radius: var(--better-ui-border-radius);
68
- transition: var(--better-ui-transition);
69
- cursor: pointer;
70
- }
71
-
72
- .better-ui-button:focus {
73
- outline: 0;
74
- box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
75
- }
76
-
77
- .better-ui-button-primary {
78
- color: var(--better-ui-text-light);
79
- background-color: var(--better-ui-primary);
80
- border-color: var(--better-ui-primary);
81
- }
82
-
83
- .better-ui-button-primary:hover {
84
- background-color: #0069d9;
85
- border-color: #0062cc;
86
- }
87
-
88
- .better-ui-button-secondary {
89
- color: var(--better-ui-text-light);
90
- background-color: var(--better-ui-secondary);
91
- border-color: var(--better-ui-secondary);
92
- }
93
-
94
- .better-ui-button-secondary:hover {
95
- background-color: #5a6268;
96
- border-color: #545b62;
97
- }
98
-
99
- .better-ui-button-success {
100
- color: var(--better-ui-text-light);
101
- background-color: var(--better-ui-success);
102
- border-color: var(--better-ui-success);
103
- }
104
-
105
- .better-ui-button-success:hover {
106
- background-color: #218838;
107
- border-color: #1e7e34;
108
- }
109
-
110
- .better-ui-button-danger {
111
- color: var(--better-ui-text-light);
112
- background-color: var(--better-ui-danger);
113
- border-color: var(--better-ui-danger);
114
- }
115
-
116
- .better-ui-button-danger:hover {
117
- background-color: #c82333;
118
- border-color: #bd2130;
119
- }
120
-
121
- .better-ui-button:disabled {
122
- opacity: 0.65;
123
- cursor: not-allowed;
124
- }
125
-
126
- /*
127
- * Alert
128
- */
129
- .better-ui-alert {
130
- position: relative;
131
- padding: var(--better-ui-spacer);
132
- margin-bottom: var(--better-ui-spacer);
133
- border: 1px solid transparent;
134
- border-radius: var(--better-ui-border-radius);
135
- transition: var(--better-ui-transition);
136
- }
137
-
138
- .better-ui-alert-message {
139
- margin-right: 2rem;
140
- }
141
-
142
- .better-ui-alert-close {
143
- position: absolute;
144
- top: var(--better-ui-spacer-sm);
145
- right: var(--better-ui-spacer-sm);
146
- background: transparent;
147
- border: none;
148
- font-size: 1.25rem;
149
- font-weight: 700;
150
- line-height: 1;
151
- cursor: pointer;
152
- opacity: 0.5;
153
- transition: var(--better-ui-transition);
154
- }
155
-
156
- .better-ui-alert-close:hover {
157
- opacity: 1;
158
- }
159
-
160
- .better-ui-alert-info {
161
- color: #0c5460;
162
- background-color: #d1ecf1;
163
- border-color: #bee5eb;
164
- }
165
-
166
- .better-ui-alert-success {
167
- color: #155724;
168
- background-color: #d4edda;
169
- border-color: #c3e6cb;
170
- }
171
-
172
- .better-ui-alert-warning {
173
- color: #856404;
174
- background-color: #fff3cd;
175
- border-color: #ffeeba;
176
- }
177
-
178
- .better-ui-alert-danger {
179
- color: #721c24;
180
- background-color: #f8d7da;
181
- border-color: #f5c6cb;
182
- }
183
-
184
- .better-ui-alert-closing {
185
- opacity: 0;
186
- transform: translateY(-10px);
187
- }
188
-
189
- /*
190
- * Card
191
- */
192
- .better-ui-card {
193
- position: relative;
194
- display: flex;
195
- flex-direction: column;
196
- min-width: 0;
197
- word-wrap: break-word;
198
- background-color: var(--better-ui-bg-light);
199
- background-clip: border-box;
200
- border: 1px solid var(--better-ui-border-color);
201
- border-radius: var(--better-ui-border-radius);
202
- margin-bottom: var(--better-ui-spacer);
203
- }
204
-
205
- .better-ui-card-header {
206
- padding: 0.75rem 1.25rem;
207
- margin-bottom: 0;
208
- background-color: rgba(0, 0, 0, 0.03);
209
- border-bottom: 1px solid var(--better-ui-border-color);
210
- }
211
-
212
- .better-ui-card-title {
213
- margin: 0;
214
- font-size: 1.25rem;
215
- font-weight: 500;
216
- }
217
-
218
- .better-ui-card-body {
219
- flex: 1 1 auto;
220
- padding: 1.25rem;
221
- }
222
-
223
- .better-ui-card-footer {
224
- padding: 0.75rem 1.25rem;
225
- background-color: rgba(0, 0, 0, 0.03);
226
- border-top: 1px solid var(--better-ui-border-color);
227
- }
228
-
229
- /*
230
- * Tabs
231
- */
232
- .better-ui-tabs {
233
- margin-bottom: var(--better-ui-spacer);
234
- }
235
-
236
- .better-ui-tab-list {
237
- display: flex;
238
- flex-wrap: wrap;
239
- padding-left: 0;
240
- margin-bottom: 0;
241
- list-style: none;
242
- border-bottom: 1px solid var(--better-ui-border-color);
243
- }
244
-
245
- .better-ui-tab-item {
246
- display: block;
247
- padding: 0.5rem 1rem;
248
- border: 1px solid transparent;
249
- border-top-left-radius: var(--better-ui-border-radius);
250
- border-top-right-radius: var(--better-ui-border-radius);
251
- cursor: pointer;
252
- background: none;
253
- margin-bottom: -1px;
254
- }
255
-
256
- .better-ui-tab-item:hover {
257
- border-color: #e9ecef #e9ecef var(--better-ui-border-color);
258
- }
259
-
260
- .better-ui-tab-active {
261
- color: var(--better-ui-primary);
262
- background-color: var(--better-ui-bg-light);
263
- border-color: var(--better-ui-border-color) var(--better-ui-border-color) var(--better-ui-bg-light);
264
- }
265
-
266
- .better-ui-tab-content {
267
- padding: var(--better-ui-spacer);
268
- border: 1px solid var(--better-ui-border-color);
269
- border-top: 0;
270
- border-bottom-right-radius: var(--better-ui-border-radius);
271
- border-bottom-left-radius: var(--better-ui-border-radius);
272
- }
273
-
274
- /*
275
- * Modal
276
- */
277
- .better-ui-modal {
278
- position: fixed;
279
- top: 0;
280
- left: 0;
281
- width: 100%;
282
- height: 100%;
283
- z-index: 1050;
284
- display: none;
285
- overflow: hidden;
286
- align-items: center;
287
- justify-content: center;
288
- }
289
-
290
- .better-ui-modal-background {
291
- position: fixed;
292
- top: 0;
293
- left: 0;
294
- width: 100%;
295
- height: 100%;
296
- background-color: rgba(0, 0, 0, 0.5);
297
- opacity: 0;
298
- transition: opacity 0.3s ease;
299
- }
300
-
301
- .better-ui-modal-background-visible {
302
- opacity: 1;
303
- }
304
-
305
- .better-ui-modal-dialog {
306
- position: relative;
307
- width: 100%;
308
- max-width: 500px;
309
- margin: 1.75rem auto;
310
- background-color: var(--better-ui-bg-light);
311
- border-radius: var(--better-ui-border-radius);
312
- box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
313
- transform: translateY(-20px);
314
- opacity: 0;
315
- transition: transform 0.3s ease, opacity 0.3s ease;
316
- }
317
-
318
- .better-ui-modal-dialog-visible {
319
- transform: translateY(0);
320
- opacity: 1;
321
- }
322
-
323
- .better-ui-modal-header {
324
- display: flex;
325
- align-items: flex-start;
326
- justify-content: space-between;
327
- padding: 1rem;
328
- border-bottom: 1px solid var(--better-ui-border-color);
329
- }
330
-
331
- .better-ui-modal-title {
332
- margin: 0;
333
- font-size: 1.25rem;
334
- font-weight: 500;
335
- }
336
-
337
- .better-ui-modal-close {
338
- background: transparent;
339
- border: 0;
340
- font-size: 1.5rem;
341
- font-weight: 700;
342
- line-height: 1;
343
- color: var(--better-ui-text-dark);
344
- opacity: 0.5;
345
- cursor: pointer;
346
- }
347
-
348
- .better-ui-modal-close:hover {
349
- opacity: 1;
350
- }
351
-
352
- .better-ui-modal-body {
353
- position: relative;
354
- padding: 1rem;
355
- }
356
-
357
- .better-ui-modal-footer {
358
- display: flex;
359
- align-items: center;
360
- justify-content: flex-end;
361
- padding: 1rem;
362
- border-top: 1px solid var(--better-ui-border-color);
363
- }
364
-
365
- .better-ui-modal-footer > .better-ui-button {
366
- margin-left: 0.5rem;
367
- }
368
-
369
- body.better-ui-modal-open {
370
- overflow: hidden;
371
- }
@@ -0,0 +1,20 @@
1
+ <div class="<%= card_classes %>" <%= options.map { |k, v| "#{k}=\"#{v}\"" }.join(' ').html_safe %>>
2
+ <p class="<%= title_classes %>"><%= title %></p>
3
+ <div class="mt-2 flex items-baseline justify-between">
4
+ <p class="<%= value_classes %>"><%= value %></p>
5
+ <% if show_trend? %>
6
+ <span class="<%= trend_classes %>">
7
+ <% if trend_up? %>
8
+ <svg xmlns="http://www.w3.org/2000/svg" class="<%= trend_icon_size %>" fill="none" viewBox="0 0 24 24" stroke="currentColor">
9
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 10l7-7m0 0l7 7m-7-7v18" />
10
+ </svg>
11
+ <% else %>
12
+ <svg xmlns="http://www.w3.org/2000/svg" class="<%= trend_icon_size %>" fill="none" viewBox="0 0 24 24" stroke="currentColor">
13
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 14l-7 7m0 0l-7-7m7 7V3" />
14
+ </svg>
15
+ <% end %>
16
+ <%= change %>
17
+ </span>
18
+ <% end %>
19
+ </div>
20
+ </div>
@@ -0,0 +1,214 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterUi
4
+ module Application
5
+ module Card
6
+ class Component < ViewComponent::Base
7
+ # Costanti per le classi CSS
8
+ CARD_THEME = {
9
+ default: 'bg-gray-900 border-gray-800',
10
+ white: 'bg-white border-gray-200',
11
+ red: 'bg-red-50 border-red-200',
12
+ rose: 'bg-rose-50 border-rose-200',
13
+ orange: 'bg-orange-50 border-orange-200',
14
+ green: 'bg-green-50 border-green-200',
15
+ blue: 'bg-blue-50 border-blue-200',
16
+ yellow: 'bg-yellow-50 border-yellow-200',
17
+ violet: 'bg-violet-50 border-violet-200',
18
+ purple: 'bg-purple-50 border-purple-200'
19
+ }.freeze
20
+
21
+ CARD_SIZE = {
22
+ small: 'p-4',
23
+ medium: 'p-6',
24
+ large: 'p-8'
25
+ }.freeze
26
+
27
+ CARD_ROUNDED = {
28
+ none: 'rounded-none',
29
+ small: 'rounded-md',
30
+ medium: 'rounded-lg',
31
+ large: 'rounded-xl',
32
+ full: 'rounded-2xl'
33
+ }.freeze
34
+
35
+ CARD_SHADOW = {
36
+ none: 'shadow-none',
37
+ small: 'shadow-sm',
38
+ medium: 'shadow-md',
39
+ large: 'shadow-lg'
40
+ }.freeze
41
+
42
+ CARD_TREND_COLOR = {
43
+ green: 'text-green-600',
44
+ red: 'text-red-600',
45
+ blue: 'text-blue-600',
46
+ yellow: 'text-yellow-600',
47
+ purple: 'text-purple-600',
48
+ indigo: 'text-indigo-600',
49
+ gray: 'text-gray-600'
50
+ }.freeze
51
+
52
+ CARD_TITLE_SIZE = {
53
+ small: 'text-xs',
54
+ medium: 'text-sm',
55
+ large: 'text-base'
56
+ }.freeze
57
+
58
+ CARD_VALUE_SIZE = {
59
+ small: 'text-lg',
60
+ medium: 'text-2xl',
61
+ large: 'text-3xl'
62
+ }.freeze
63
+
64
+ def initialize(
65
+ title:,
66
+ value:,
67
+ trend: nil,
68
+ change: nil,
69
+ color: :green,
70
+ theme: :default,
71
+ size: :medium,
72
+ rounded: :medium,
73
+ shadow: :small,
74
+ classes: nil,
75
+ **options
76
+ )
77
+ @title = title
78
+ @value = value
79
+ @trend = trend
80
+ @change = change
81
+ @color = color
82
+ @theme = theme
83
+ @size = size
84
+ @rounded = rounded
85
+ @shadow = shadow
86
+ @classes = classes
87
+ @options = options
88
+
89
+ validate_params
90
+ end
91
+
92
+ private
93
+
94
+ attr_reader :title, :value, :trend, :change, :color, :theme, :size, :rounded, :shadow, :classes, :options
95
+
96
+ def validate_params
97
+ validate_theme
98
+ validate_size
99
+ validate_rounded
100
+ validate_shadow
101
+ validate_trend
102
+ validate_color
103
+ end
104
+
105
+ def validate_theme
106
+ return if CARD_THEME.key?(theme)
107
+
108
+ raise ArgumentError, "Invalid theme: #{theme}. Valid options: #{CARD_THEME.keys.join(', ')}"
109
+ end
110
+
111
+ def validate_size
112
+ return if CARD_SIZE.key?(size)
113
+
114
+ raise ArgumentError, "Invalid size: #{size}. Valid options: #{CARD_SIZE.keys.join(', ')}"
115
+ end
116
+
117
+ def validate_rounded
118
+ return if CARD_ROUNDED.key?(rounded)
119
+
120
+ raise ArgumentError, "Invalid rounded: #{rounded}. Valid options: #{CARD_ROUNDED.keys.join(', ')}"
121
+ end
122
+
123
+ def validate_shadow
124
+ return if CARD_SHADOW.key?(shadow)
125
+
126
+ raise ArgumentError, "Invalid shadow: #{shadow}. Valid options: #{CARD_SHADOW.keys.join(', ')}"
127
+ end
128
+
129
+ def validate_trend
130
+ return if trend.nil? || [:up, :down].include?(trend)
131
+
132
+ raise ArgumentError, "Invalid trend: #{trend}. Valid options: :up, :down, nil"
133
+ end
134
+
135
+ def validate_color
136
+ return if CARD_TREND_COLOR.key?(color)
137
+
138
+ raise ArgumentError, "Invalid color: #{color}. Valid options: #{CARD_TREND_COLOR.keys.join(', ')}"
139
+ end
140
+
141
+ def card_classes
142
+ base_classes = [
143
+ CARD_THEME[theme],
144
+ CARD_SIZE[size],
145
+ CARD_ROUNDED[rounded],
146
+ CARD_SHADOW[shadow],
147
+ 'border'
148
+ ]
149
+
150
+ base_classes << classes if classes.present?
151
+ base_classes.compact.join(' ')
152
+ end
153
+
154
+ def title_classes
155
+ base_classes = [
156
+ CARD_TITLE_SIZE[size],
157
+ 'font-medium'
158
+ ]
159
+
160
+ if theme == :default
161
+ base_classes << 'text-gray-300'
162
+ else
163
+ base_classes << 'text-gray-500'
164
+ end
165
+
166
+ base_classes.join(' ')
167
+ end
168
+
169
+ def value_classes
170
+ base_classes = [
171
+ CARD_VALUE_SIZE[size],
172
+ 'font-semibold'
173
+ ]
174
+
175
+ if theme == :default
176
+ base_classes << 'text-white'
177
+ else
178
+ base_classes << 'text-gray-900'
179
+ end
180
+
181
+ base_classes.join(' ')
182
+ end
183
+
184
+ def trend_classes
185
+ base_classes = [
186
+ CARD_TREND_COLOR[color],
187
+ 'text-sm font-medium flex items-center'
188
+ ]
189
+
190
+ base_classes.join(' ')
191
+ end
192
+
193
+ def show_trend?
194
+ trend.present? && change.present?
195
+ end
196
+
197
+ def trend_up?
198
+ trend == :up
199
+ end
200
+
201
+ def trend_icon_size
202
+ case size
203
+ when :small
204
+ 'h-3 w-3 mr-0.5'
205
+ when :medium
206
+ 'h-4 w-4 mr-0.5'
207
+ when :large
208
+ 'h-5 w-5 mr-1'
209
+ end
210
+ end
211
+ end
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,9 @@
1
+ <main class="w-full fixed <%= get_layout_class %> <%= get_padding_class(padding) %><%= classes ? ' ' + classes : '' %>">
2
+ <div class="h-[calc(100vh-81px)] p-4">
3
+ <div class="h-full w-full bg-white p-4 <%= get_padding_class(inner_padding) %> <%= get_rounded_class %> <%= get_shadow_class %> overflow-y-auto">
4
+ <div class="h-full overflow-y-auto [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none]">
5
+ <%= content %>
6
+ </div>
7
+ </div>
8
+ </div>
9
+ </main>