better_ui 0.1.0 → 0.1.1

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 (118) 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/field/component.html.erb +27 -0
  26. data/app/components/better_ui/general/field/component.rb +37 -0
  27. data/app/components/better_ui/general/heading/component.html.erb +22 -0
  28. data/app/components/better_ui/general/heading/component.rb +257 -0
  29. data/app/components/better_ui/general/icon/component.html.erb +7 -0
  30. data/app/components/better_ui/general/icon/component.rb +239 -0
  31. data/app/components/better_ui/general/input/checkbox/component.html.erb +5 -0
  32. data/app/components/better_ui/general/input/checkbox/component.rb +238 -0
  33. data/app/components/better_ui/general/input/datetime/component.html.erb +5 -0
  34. data/app/components/better_ui/general/input/datetime/component.rb +223 -0
  35. data/app/components/better_ui/general/input/radio/component.html.erb +5 -0
  36. data/app/components/better_ui/general/input/radio/component.rb +230 -0
  37. data/app/components/better_ui/general/input/select/component.html.erb +16 -0
  38. data/app/components/better_ui/general/input/select/component.rb +184 -0
  39. data/app/components/better_ui/general/input/select/select_component.html.erb +5 -0
  40. data/app/components/better_ui/general/input/select/select_component.rb +37 -0
  41. data/app/components/better_ui/general/input/text/component.html.erb +5 -0
  42. data/app/components/better_ui/general/input/text/component.rb +171 -0
  43. data/app/components/better_ui/general/input/textarea/component.html.erb +5 -0
  44. data/app/components/better_ui/general/input/textarea/component.rb +166 -0
  45. data/app/components/better_ui/general/link/component.html.erb +18 -0
  46. data/app/components/better_ui/general/link/component.rb +258 -0
  47. data/app/components/better_ui/general/panel/component.html.erb +28 -0
  48. data/app/components/better_ui/general/panel/component.rb +249 -0
  49. data/app/components/better_ui/general/progress/component.html.erb +11 -0
  50. data/app/components/better_ui/general/progress/component.rb +160 -0
  51. data/app/components/better_ui/general/spinner/component.html.erb +35 -0
  52. data/app/components/better_ui/general/spinner/component.rb +93 -0
  53. data/app/components/better_ui/general/table/component.html.erb +5 -0
  54. data/app/components/better_ui/general/table/component.rb +217 -0
  55. data/app/components/better_ui/general/table/tbody_component.html.erb +3 -0
  56. data/app/components/better_ui/general/table/tbody_component.rb +30 -0
  57. data/app/components/better_ui/general/table/td_component.html.erb +3 -0
  58. data/app/components/better_ui/general/table/td_component.rb +44 -0
  59. data/app/components/better_ui/general/table/tfoot_component.html.erb +3 -0
  60. data/app/components/better_ui/general/table/tfoot_component.rb +28 -0
  61. data/app/components/better_ui/general/table/th_component.html.erb +6 -0
  62. data/app/components/better_ui/general/table/th_component.rb +51 -0
  63. data/app/components/better_ui/general/table/thead_component.html.erb +3 -0
  64. data/app/components/better_ui/general/table/thead_component.rb +28 -0
  65. data/app/components/better_ui/general/table/tr_component.html.erb +3 -0
  66. data/app/components/better_ui/general/table/tr_component.rb +30 -0
  67. data/app/components/better_ui/general/tag/component.html.erb +3 -0
  68. data/app/components/better_ui/general/tag/component.rb +104 -0
  69. data/app/components/better_ui/general/tooltip/component.html.erb +7 -0
  70. data/app/components/better_ui/general/tooltip/component.rb +239 -0
  71. data/app/helpers/better_ui/application/components/card/card_helper.rb +96 -0
  72. data/app/helpers/better_ui/application/components/card.rb +11 -0
  73. data/app/helpers/better_ui/application/components/main/main_helper.rb +64 -0
  74. data/app/helpers/better_ui/application/components/navbar/navbar_helper.rb +77 -0
  75. data/app/helpers/better_ui/application/components/sidebar/sidebar_helper.rb +51 -0
  76. data/app/helpers/better_ui/application_helper.rb +42 -179
  77. data/app/helpers/better_ui/general/components/alert/alert_helper.rb +57 -0
  78. data/app/helpers/better_ui/general/components/avatar/avatar_helper.rb +29 -0
  79. data/app/helpers/better_ui/general/components/badge/badge_helper.rb +53 -0
  80. data/app/helpers/better_ui/general/components/breadcrumb/breadcrumb_helper.rb +37 -0
  81. data/app/helpers/better_ui/general/components/button/button_helper.rb +65 -0
  82. data/app/helpers/better_ui/general/components/container/container_helper.rb +60 -0
  83. data/app/helpers/better_ui/general/components/divider/divider_helper.rb +63 -0
  84. data/app/helpers/better_ui/general/components/field/field_helper.rb +26 -0
  85. data/app/helpers/better_ui/general/components/heading/heading_helper.rb +72 -0
  86. data/app/helpers/better_ui/general/components/icon/icon_helper.rb +16 -0
  87. data/app/helpers/better_ui/general/components/input/checkbox/checkbox_helper.rb +81 -0
  88. data/app/helpers/better_ui/general/components/input/datetime/datetime_helper.rb +91 -0
  89. data/app/helpers/better_ui/general/components/input/radio/radio_helper.rb +79 -0
  90. data/app/helpers/better_ui/general/components/input/radio_group/radio_group_helper.rb +124 -0
  91. data/app/helpers/better_ui/general/components/input/select/select_helper.rb +70 -0
  92. data/app/helpers/better_ui/general/components/input/text/text_helper.rb +138 -0
  93. data/app/helpers/better_ui/general/components/input/textarea/textarea_helper.rb +73 -0
  94. data/app/helpers/better_ui/general/components/link/link_helper.rb +89 -0
  95. data/app/helpers/better_ui/general/components/panel/panel_helper.rb +83 -0
  96. data/app/helpers/better_ui/general/components/progress/progress_helper.rb +53 -0
  97. data/app/helpers/better_ui/general/components/spinner/spinner_helper.rb +19 -0
  98. data/app/helpers/better_ui/general/components/table/table_helper.rb +53 -0
  99. data/app/helpers/better_ui/general/components/table/tbody_helper.rb +13 -0
  100. data/app/helpers/better_ui/general/components/table/td_helper.rb +19 -0
  101. data/app/helpers/better_ui/general/components/table/tfoot_helper.rb +13 -0
  102. data/app/helpers/better_ui/general/components/table/th_helper.rb +19 -0
  103. data/app/helpers/better_ui/general/components/table/thead_helper.rb +13 -0
  104. data/app/helpers/better_ui/general/components/table/tr_helper.rb +13 -0
  105. data/app/helpers/better_ui/general/components/tag/tag_helper.rb +26 -0
  106. data/app/helpers/better_ui/general/components/tooltip/tooltip_helper.rb +60 -0
  107. data/app/views/layouts/better_ui/application.html.erb +6 -124
  108. data/config/initializers/lookbook.rb +23 -0
  109. data/config/routes.rb +0 -8
  110. data/lib/better_ui/engine.rb +5 -19
  111. data/lib/better_ui/railtie.rb +20 -0
  112. data/lib/better_ui/version.rb +1 -1
  113. data/lib/better_ui.rb +4 -20
  114. metadata +131 -28
  115. data/app/controllers/better_ui/docs_controller.rb +0 -41
  116. data/app/views/better_ui/docs/component.html.erb +0 -365
  117. data/app/views/better_ui/docs/index.html.erb +0 -100
  118. 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>