lesli_view 0.1.0 → 1.0.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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/lib/lesli_view/charts/bar.rb +41 -0
  3. data/lib/lesli_view/charts/general.html.erb +97 -0
  4. data/lib/lesli_view/charts/general.rb +71 -0
  5. data/lib/lesli_view/charts/line.rb +41 -0
  6. data/lib/lesli_view/{element → components}/header.html.erb +10 -2
  7. data/lib/lesli_view/{element → components}/header.rb +4 -3
  8. data/lib/lesli_view/components/header.scss +69 -0
  9. data/lib/lesli_view/components/panel.html.erb +37 -0
  10. data/lib/lesli_view/{element/form.rb → components/panel.rb} +8 -12
  11. data/lib/lesli_view/components/panel.scss +37 -0
  12. data/lib/lesli_view/components/tabs.html.erb +35 -0
  13. data/lib/lesli_view/components/tabs.rb +58 -0
  14. data/lib/lesli_view/components/tabs_spec.rb +0 -0
  15. data/lib/lesli_view/components/timeline.html.erb +23 -0
  16. data/lib/lesli_view/components/timeline.rb +45 -0
  17. data/lib/lesli_view/components/timeline.scss +218 -0
  18. data/lib/lesli_view/{element → components}/toolbar.rb +1 -1
  19. data/lib/lesli_view/components/toolbar.scss +54 -0
  20. data/lib/lesli_view/elements/avatar.html.erb +7 -0
  21. data/lib/lesli_view/elements/avatar.rb +76 -0
  22. data/lib/lesli_view/elements/avatar.scss +50 -0
  23. data/lib/lesli_view/elements/button.html.erb +24 -0
  24. data/lib/lesli_view/{element → elements}/button.rb +26 -5
  25. data/lib/lesli_view/elements/empty.html.erb +8 -0
  26. data/lib/lesli_view/elements/empty.rb +40 -0
  27. data/lib/lesli_view/elements/table.html.erb +48 -0
  28. data/lib/lesli_view/{element → elements}/table.rb +29 -21
  29. data/lib/lesli_view/elements/table.scss +153 -0
  30. data/lib/lesli_view/forms/builder.rb +47 -0
  31. data/lib/lesli_view/forms/builder_horizontal.rb +25 -0
  32. data/lib/lesli_view/forms/fields.rb +144 -0
  33. data/lib/lesli_view/forms/fieldset.rb +15 -0
  34. data/lib/lesli_view/forms/form.scss +87 -0
  35. data/lib/lesli_view/forms/inputs.rb +46 -0
  36. data/lib/lesli_view/layout/container.html.erb +5 -3
  37. data/lib/lesli_view/version.rb +2 -1
  38. data/lib/lesli_view.rb +27 -5
  39. data/readme.md +29 -32
  40. metadata +39 -17
  41. data/lib/lesli_view/element/button.html.erb +0 -12
  42. data/lib/lesli_view/element/form.html.erb +0 -10
  43. data/lib/lesli_view/element/table.full.rb +0 -43
  44. data/lib/lesli_view/element/table.html.erb +0 -39
  45. data/lib/lesli_view/element/table.html.full.erb +0 -101
  46. data/lib/lesli_view/lesli_view.scss +0 -3
  47. /data/lib/lesli_view/{element → components}/toolbar.html.erb +0 -0
@@ -0,0 +1,218 @@
1
+
2
+ .lesli-timelines {
3
+
4
+ .lesli-timeline {
5
+ line-height: 1.4em;
6
+ list-style: none;
7
+ margin: 0;
8
+ padding: 0;
9
+ width: 100%;
10
+ }
11
+ .lesli-timeline h1,
12
+ .lesli-timeline h2,
13
+ .lesli-timeline h3,
14
+ .lesli-timeline h4,
15
+ .lesli-timeline h5,
16
+ .lesli-timeline h6 {
17
+ line-height: inherit;
18
+ }
19
+
20
+ /*----- TIMELINE ITEM -----*/
21
+ .lesli-timeline-item {
22
+ padding-left: 40px;
23
+ position: relative;
24
+ }
25
+ .lesli-timeline-item:last-child {
26
+ padding-bottom: 0;
27
+ }
28
+
29
+ /*----- TIMELINE INFO -----*/
30
+ .lesli-timeline-info {
31
+ font-size: 12px;
32
+ font-weight: 700;
33
+ letter-spacing: 3px;
34
+ margin: 0 0 0.5em 0;
35
+ text-transform: uppercase;
36
+ white-space: nowrap;
37
+ }
38
+
39
+ /*----- TIMELINE MARKER -----*/
40
+ .lesli-timeline-marker {
41
+ position: absolute;
42
+ top: 0;
43
+ bottom: 0;
44
+ left: 0;
45
+ width: 15px;
46
+ }
47
+ .lesli-timeline-marker:before {
48
+ background: #ff6b6b;
49
+ border: 3px solid transparent;
50
+ border-radius: 100%;
51
+ content: "";
52
+ display: block;
53
+ height: 15px;
54
+ position: absolute;
55
+ top: 4px;
56
+ left: 0;
57
+ width: 15px;
58
+ transition: background 0.3s ease-in-out, border 0.3s ease-in-out;
59
+ }
60
+ .lesli-timeline-marker:after {
61
+ content: "";
62
+ width: 3px;
63
+ background: #ccd5db;
64
+ display: block;
65
+ position: absolute;
66
+ top: 24px;
67
+ bottom: 0;
68
+ left: 6px;
69
+ }
70
+ .lesli-timeline-item:last-child .lesli-timeline-marker:after {
71
+ content: none;
72
+ }
73
+
74
+ .lesli-timeline-item:not(.period):hover .lesli-timeline-marker:before {
75
+ background: transparent;
76
+ border: 3px solid #ff6b6b;
77
+ }
78
+
79
+ /*----- TIMELINE CONTENT -----*/
80
+ .lesli-timeline-content {
81
+ padding-bottom: 40px;
82
+ }
83
+ .lesli-timeline-content p:last-child {
84
+ margin-bottom: 0;
85
+ }
86
+
87
+ /*----- TIMELINE PERIOD -----*/
88
+ .period {
89
+ padding: 0;
90
+ }
91
+ .period .lesli-timeline-info {
92
+ display: none;
93
+ }
94
+ .period .lesli-timeline-marker:before {
95
+ background: transparent;
96
+ content: "";
97
+ width: 15px;
98
+ height: auto;
99
+ border: none;
100
+ border-radius: 0;
101
+ top: 0;
102
+ bottom: 30px;
103
+ position: absolute;
104
+ border-top: 3px solid #ccd5db;
105
+ border-bottom: 3px solid #ccd5db;
106
+ }
107
+ .period .lesli-timeline-marker:after {
108
+ content: "";
109
+ height: 32px;
110
+ top: auto;
111
+ }
112
+ .period .lesli-timeline-content {
113
+ padding: 40px 0 70px;
114
+ }
115
+ .period .timeline-title {
116
+ margin: 0;
117
+ }
118
+
119
+ /*----------------------------------------------
120
+ MOD: TIMELINE SPLIT
121
+ ----------------------------------------------*/
122
+ @media (min-width: 768px) {
123
+ .lesli-timeline-split .timeline, .timeline-centered .lesli-timeline {
124
+ display: table;
125
+ }
126
+ .lesli-timeline-split .lesli-timeline-item, .timeline-centered .lesli-timeline-item {
127
+ display: table-row;
128
+ padding: 0;
129
+ }
130
+ .lesli-timeline-split .lesli-timeline-info, .timeline-centered .lesli-timeline-info,
131
+ .lesli-timeline-split .lesli-timeline-marker,
132
+ .timeline-centered .lesli-timeline-marker,
133
+ .lesli-timeline-split .lesli-timeline-content,
134
+ .timeline-centered .lesli-timeline-content,
135
+ .lesli-timeline-split .period .lesli-timeline-info {
136
+ display: table-cell;
137
+ vertical-align: top;
138
+ }
139
+ .lesli-timeline-split .lesli-timeline-marker, .timeline-centered .lesli-timeline-marker {
140
+ position: relative;
141
+ }
142
+ .lesli-timeline-split .lesli-timeline-content, .timeline-centered .lesli-timeline-content {
143
+ padding-left: 30px;
144
+ }
145
+ .lesli-timeline-split .lesli-timeline-info, .timeline-centered .lesli-timeline-info {
146
+ padding-right: 30px;
147
+ }
148
+ .lesli-timeline-split .period .timeline-title, .timeline-centered .period .timeline-title {
149
+ position: relative;
150
+ left: -45px;
151
+ }
152
+ }
153
+
154
+ /*----------------------------------------------
155
+ MOD: TIMELINE CENTERED
156
+ ----------------------------------------------*/
157
+ @media (min-width: 992px) {
158
+ .timeline-centered,
159
+ .timeline-centered .lesli-timeline-item,
160
+ .timeline-centered .lesli-timeline-info,
161
+ .timeline-centered .lesli-timeline-marker,
162
+ .timeline-centered .lesli-timeline-content {
163
+ display: block;
164
+ margin: 0;
165
+ padding: 0;
166
+ }
167
+ .timeline-centered .lesli-timeline-item {
168
+ padding-bottom: 40px;
169
+ overflow: hidden;
170
+ }
171
+ .timeline-centered .lesli-timeline-marker {
172
+ position: absolute;
173
+ left: 50%;
174
+ margin-left: -7.5px;
175
+ }
176
+ .timeline-centered .lesli-timeline-info,
177
+ .timeline-centered .lesli-timeline-content {
178
+ width: 50%;
179
+ }
180
+ .timeline-centered > .lesli-timeline-item:nth-child(odd) .lesli-timeline-info {
181
+ float: left;
182
+ text-align: right;
183
+ padding-right: 30px;
184
+ }
185
+ .timeline-centered > .lesli-timeline-item:nth-child(odd) .lesli-timeline-content {
186
+ float: right;
187
+ text-align: left;
188
+ padding-left: 30px;
189
+ }
190
+ .timeline-centered > .lesli-timeline-item:nth-child(even) .lesli-timeline-info {
191
+ float: right;
192
+ text-align: left;
193
+ padding-left: 30px;
194
+ }
195
+ .timeline-centered > .lesli-timeline-item:nth-child(even) .lesli-timeline-content {
196
+ float: left;
197
+ text-align: right;
198
+ padding-right: 30px;
199
+ }
200
+ .timeline-centered > .lesli-timeline-item.period .lesli-timeline-content {
201
+ float: none;
202
+ padding: 0;
203
+ width: 100%;
204
+ text-align: center;
205
+ }
206
+ .timeline-centered .lesli-timeline-item.period {
207
+ padding: 50px 0 90px;
208
+ }
209
+ .timeline-centered .period .lesli-timeline-marker:after {
210
+ height: 30px;
211
+ bottom: 0;
212
+ top: auto;
213
+ }
214
+ .timeline-centered .period .timeline-title {
215
+ left: auto;
216
+ }
217
+ }
218
+ }
@@ -33,7 +33,7 @@ Building a better future, one line of code at a time.
33
33
  =end
34
34
 
35
35
  module LesliView
36
- module Element
36
+ module Components
37
37
  class Toolbar < ViewComponent::Base
38
38
  attr_reader :search_placeholder, :initial_value, :pagination
39
39
 
@@ -0,0 +1,54 @@
1
+ /*
2
+
3
+ Lesli
4
+
5
+ Copyright (c) 2023, Lesli Technologies, S. A.
6
+
7
+ This program is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ This program is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with this program. If not, see http://www.gnu.org/licenses/.
19
+
20
+ Lesli · Ruby on Rails SaaS development platform.
21
+
22
+ Made with ♥ by https://www.lesli.tech
23
+ Building a better future, one line of code at a time.
24
+
25
+ @contact hello@lesli.tech
26
+ @website https://www.lesli.tech
27
+ @license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
28
+
29
+ // · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
30
+ // ·
31
+ */
32
+
33
+
34
+ // ·
35
+ //@import "../../scss/component";
36
+
37
+
38
+ // ·
39
+ .lesli-toolbar {
40
+
41
+ // General styles
42
+ input,
43
+ .select select {
44
+ border-radius: 6px;
45
+ border: 1px solid lesli-css-color(silver);
46
+ }
47
+
48
+ // search input
49
+ input::placeholder {
50
+ color: lesli-css-color(silver, 500);
51
+ opacity: 1;
52
+ }
53
+
54
+ }
@@ -0,0 +1,7 @@
1
+ <figure class="lesli-element-avatar has-background-grey-lighter" style="<%= avatar_style %>">
2
+ <% if image %>
3
+ <img src="<%= image %>" alt="Avatar image">
4
+ <% else %>
5
+ <span class="has-text-weight-bold <%= font_size %>"><%= letter %></span>
6
+ <% end %>
7
+ </figure>
@@ -0,0 +1,76 @@
1
+ =begin
2
+
3
+ Lesli
4
+
5
+ Copyright (c) 2025, Lesli Technologies, S. A.
6
+
7
+ This program is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ This program is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with this program. If not, see http://www.gnu.org/licenses/.
19
+
20
+ Lesli · Ruby on Rails SaaS Development Framework.
21
+
22
+ Made with ♥ by LesliTech
23
+ Building a better future, one line of code at a time.
24
+
25
+ @contact hello@lesli.tech
26
+ @website https://www.lesli.tech
27
+ @license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
28
+
29
+ // · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
30
+ // ·
31
+ =end
32
+ module LesliView
33
+ module Elements
34
+ class Avatar < ViewComponent::Base
35
+ attr_reader :image, :name, :size, :letter, :font_size, :avatar_style
36
+
37
+ def initialize(image: nil, name: "", size: "medium")
38
+ @image = image
39
+ @name = name
40
+ @size = size
41
+ @letter = ""
42
+ @font_size = ""
43
+ @avatar_style = ""
44
+ calculate_size
45
+ get_letter unless @image
46
+ end
47
+
48
+ private
49
+
50
+ # Calculate the size of the avatar and set styles accordingly
51
+ def calculate_size
52
+ case size
53
+ when "small"
54
+ @font_size = "is-size-5"
55
+ @avatar_style = "height: 60px; width: 60px;"
56
+ when "medium"
57
+ @font_size = "is-size-3"
58
+ @avatar_style = "height: 120px; width: 120px;"
59
+ when "large"
60
+ @font_size = "is-size-1"
61
+ @avatar_style = "height: 180px; width: 180px;"
62
+ else
63
+ raise ArgumentError, "Invalid size: #{size}"
64
+ end
65
+ end
66
+
67
+ # Calculate the initials based on the name
68
+ def get_letter
69
+ return @letter = "" if name.strip.empty?
70
+
71
+ words = name.strip.split(/\s+/)
72
+ @letter = words.first(2).map { |word| word[0].upcase }.join
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,50 @@
1
+ /*
2
+
3
+ Lesli
4
+
5
+ Copyright (c) 2023, Lesli Technologies, S. A.
6
+
7
+ This program is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ This program is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with this program. If not, see http://www.gnu.org/licenses/.
19
+
20
+ Lesli · Ruby on Rails SaaS development platform.
21
+
22
+ Made with ♥ by https://www.lesli.tech
23
+ Building a better future, one line of code at a time.
24
+
25
+ @contact hello@lesli.tech
26
+ @website https://www.lesli.tech
27
+ @license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
28
+
29
+ // · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
30
+ // ·
31
+ */
32
+
33
+ .lesli-element-avatar {
34
+ display: flex;
35
+ justify-content: center;
36
+ align-items: center;
37
+ border-radius: 50%;
38
+ background-color: lesli-css-color(silver, 100);
39
+
40
+ & > img {
41
+ width: 100%;
42
+ height: 100%;
43
+ object-fit: cover;
44
+ border-radius: 50%;
45
+ }
46
+
47
+ & > span {
48
+ color: lesli-css-color(silver, 700);
49
+ }
50
+ }
@@ -0,0 +1,24 @@
1
+ <% tag_name = url.present? ? :a : :button %>
2
+
3
+ <% attributes = {
4
+ class: button_classes,
5
+ data: {}
6
+ } %>
7
+
8
+ <% attributes[:href] = url if url.present? %>
9
+ <% attributes[:data][:turbo_frame] = "_top" if url.present? %>
10
+
11
+ <%# Add Alpine.js properties %>
12
+ <% attributes[:"x-data"] = "" if dispatch %>
13
+ <% attributes[:"@click"] = "$dispatch('#{ dispatch }')" if dispatch %>
14
+
15
+ <%= content_tag(tag_name, **attributes) do %>
16
+ <% if icon %>
17
+ <span class="icon <%= 'is-small' if small %>">
18
+ <span class="material-icons"><%= icon %></span>
19
+ </span>
20
+ <% end %>
21
+ <% unless icon_only? %>
22
+ <span><%= label %></span>
23
+ <% end %>
24
+ <% end %>
@@ -33,11 +33,30 @@ Building a better future, one line of code at a time.
33
33
  =end
34
34
 
35
35
  module LesliView
36
- module Element
36
+ module Elements
37
37
  class Button < ViewComponent::Base
38
- attr_reader :icon, :loading, :solid, :info, :success, :warning, :danger, :small
38
+ attr_reader :label, :url, :icon, :loading, :solid, :info, :success, :warning, :danger, :small, :type, :dispatch
39
39
 
40
- def initialize(icon: nil, loading: false, solid: false, info: false, success: false, warning: false, danger: false, small: false)
40
+ # Adds two numbers together.
41
+ # @param [Integer] a The first number.
42
+ # @param [Integer] b The second number.
43
+ # @return [Integer] The sum of the two numbers.
44
+ def initialize(
45
+ label=nil,
46
+ url:nil,
47
+ icon: nil,
48
+ loading: false,
49
+ solid: false,
50
+ info: false,
51
+ success: false,
52
+ warning: false,
53
+ danger: false,
54
+ small: false,
55
+ type: "button",
56
+ dispatch:nil
57
+ )
58
+ @label = label
59
+ @url = url
41
60
  @icon = icon
42
61
  @loading = loading
43
62
  @solid = solid
@@ -46,6 +65,8 @@ module LesliView
46
65
  @warning = warning
47
66
  @danger = danger
48
67
  @small = small
68
+ @type = type
69
+ @dispatch = dispatch
49
70
  end
50
71
 
51
72
  def button_classes
@@ -64,8 +85,8 @@ module LesliView
64
85
  "is-primary"
65
86
  end
66
87
 
67
- def icon_only?(content_present)
68
- icon && !content_present
88
+ def icon_only?
89
+ icon && !label
69
90
  end
70
91
  end
71
92
  end
@@ -0,0 +1,8 @@
1
+
2
+ <div class="lesli-empty has-text-centered">
3
+ <svg width="184" height="184" viewBox="0 0 184 184" fill="none" xmlns="http://www.w3.org/2000/svg">
4
+ <path d="M145.271 90.6522H142.735V89.528C142.735 83.9793 138.222 79.4655 132.673 79.4655H85.7357C84.255 79.4655 82.1132 78.0942 81.4922 76.7515L76.1878 65.2544C74.1523 60.8499 68.9629 57.5293 64.1128 57.5293H39.7012C34.1524 57.5293 29.6387 62.043 29.6387 67.5918V151.809C29.6387 157.358 34.1524 161.872 39.7012 161.872H53.3459C53.5357 161.883 53.7139 161.932 53.9065 161.932H112.945V153.307H108.831H53.9065C53.7197 153.307 53.6219 153.278 53.5587 153.247C53.527 153.232 53.4897 153.215 53.481 153.206C53.458 153.181 53.412 153.045 53.4609 152.772L63.1985 101.419C63.4027 100.338 64.6849 99.2772 65.7803 99.2772H134.11H142.735H145.271C145.553 99.2772 145.679 99.349 145.697 99.3749C145.714 99.4008 145.766 99.533 145.714 99.809L142.735 115.524L135.979 151.159C135.775 152.24 134.493 153.301 133.395 153.301H121.964V161.926H133.395C138.621 161.926 143.48 157.904 144.455 152.767L154.189 101.413C154.713 98.6648 154.037 95.9163 152.341 93.8693C150.645 91.8281 148.069 90.6522 145.271 90.6522ZM134.11 90.6522H65.7832C60.5593 90.6522 55.7005 94.6743 54.7259 99.8119L44.9883 151.168C44.856 151.864 44.8043 152.56 44.8244 153.247H39.7012C38.922 153.247 38.2637 152.588 38.2637 151.809V67.5918C38.2637 66.8127 38.922 66.1543 39.7012 66.1543H64.1128C65.5934 66.1543 67.7353 67.5257 68.3563 68.8683L73.6607 80.3654C75.6962 84.7699 80.8855 88.0905 85.7357 88.0905H132.673C133.452 88.0905 134.11 88.7489 134.11 89.528V90.6522Z" fill="#BDBDBD"/>
5
+ <path d="M145.7 50.5227L153.201 43.0219C154.805 41.4176 154.739 38.7525 153.054 37.0677C151.369 35.383 148.704 35.3197 147.1 36.9211L139.599 44.422L132.098 36.9211C130.494 35.3169 127.829 35.383 126.144 37.0677C124.459 38.7525 124.396 41.4176 125.997 43.0219L133.498 50.5227L125.997 58.0236C124.393 59.6279 124.459 62.293 126.144 63.9777C127.829 65.6625 130.494 65.7257 132.098 64.1244L139.599 56.6235L147.1 64.1244C148.704 65.7286 151.369 65.6625 153.054 63.9777C154.739 62.293 154.802 59.6279 153.201 58.0236L145.7 50.5227Z" fill="#BDBDBD"/>
6
+ </svg>
7
+ <p class="has-text-grey"><%= @text %></p>
8
+ </div>
@@ -0,0 +1,40 @@
1
+ =begin
2
+
3
+ Lesli
4
+
5
+ Copyright (c) 2025, Lesli Technologies, S. A.
6
+
7
+ This program is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ This program is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with this program. If not, see http://www.gnu.org/licenses/.
19
+
20
+ Lesli · Ruby on Rails SaaS Development Framework.
21
+
22
+ Made with ♥ by LesliTech
23
+ Building a better future, one line of code at a time.
24
+
25
+ @contact hello@lesli.tech
26
+ @website https://www.lesli.tech
27
+ @license GPLv3 http://www.gnu.org/licenses/gpl-3.0.en.html
28
+
29
+ // · ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~ ~·~
30
+ // ·
31
+ =end
32
+ module LesliView
33
+ module Elements
34
+ class Empty < ViewComponent::Base
35
+ def initialize(text: "No data found")
36
+ @text = text
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,48 @@
1
+ <div class="lesli-table-container table-container">
2
+ <table id="<%= id %>" class="table is-fullwidth lesli-table mb-5 <%= class_name %>">
3
+ <% unless headless %>
4
+ <thead>
5
+ <tr>
6
+ <% columns.each do |column| %>
7
+ <th
8
+ width="<%= column[:width] %>"
9
+ class="<%= table_header_class(column) %>"
10
+ data-action="click->table#sort"
11
+ data-field="<%= column[:field] %>">
12
+ <% if column[:sort] %>
13
+ <span class="icon-text">
14
+ <span><%= column[:label] %></span>
15
+ <span class="icon">
16
+ <span class="material-icons"><%= column[:field] == @current_sort ? (current_sort_dir(column) == "asc" ? "arrow_upward" : "arrow_downward") : "sort" %></span>
17
+ </span>
18
+ </span>
19
+ <% else %>
20
+ <%= column[:label] %>
21
+ <% end %>
22
+ </th>
23
+ <% end %>
24
+ </tr>
25
+ </thead>
26
+ <% end %>
27
+ <tbody>
28
+ <% if records %>
29
+ <% records.each_with_index do |record, i| %>
30
+ <tr>
31
+ <% columns.each do |column| %>
32
+ <td class="<%= table_body_class(column) %>">
33
+ <% if link %>
34
+ <%= link_to(record[column[:field]] || "", link.call(record), class: "link", data: { turbo_frame: '_top' }) %>
35
+ <% else %>
36
+ <%= record[column[:field]] %>
37
+ <% end %>
38
+ </td>
39
+ <% end %>
40
+ </tr>
41
+ <% end %>
42
+ <% end %>
43
+ <% rows.each do |row| %>
44
+ <%= row %>
45
+ <% end %>
46
+ </tbody>
47
+ </table>
48
+ </div>
@@ -1,6 +1,4 @@
1
- # frozen_string_literal: true
2
-
3
- =begin
1
+ =begin
4
2
 
5
3
  Lesli
6
4
 
@@ -33,11 +31,13 @@ Building a better future, one line of code at a time.
33
31
  =end
34
32
 
35
33
  module LesliView
36
- module Element
34
+ module Elements
37
35
  class Table < ViewComponent::Base
38
36
  attr_reader :id, :class_name, :pagination, :loading, :headless, :columns, :records, :link
39
37
 
40
- def initialize(id: nil, class_name: "is-striped", pagination: nil, loading: false, headless: false, columns:, records:, link: nil)
38
+ renders_many :rows, "TableRow"
39
+
40
+ def initialize(columns: nil, records: nil, id: nil, class_name: "is-striped", pagination: nil, loading: false, headless: false, link: nil)
41
41
  @id = id
42
42
  @class_name = class_name
43
43
  @pagination = pagination
@@ -56,22 +56,30 @@ module LesliView
56
56
  column[:field] == "id" || column[:align] == "center" ? "has-text-centered" : ""
57
57
  end
58
58
 
59
- def current_sort_dir(column)
60
- @current_sort_dir ||= {}
61
- @current_sort_dir[column[:field]] || "asc"
62
- end
63
-
64
- def toggle_sort_dir(column)
65
- current_sort_dir(column) == "asc" ? "desc" : "asc"
66
- end
67
-
68
- def render_head_slot(column)
69
- content_for?(:"head_#{column[:field]}") ? content_for(:"head_#{column[:field]}") : column[:label]
70
- end
71
-
72
- def detail_active?(record)
73
- record[:detail_active]
59
+ class TableRow < ViewComponent::Base
60
+ renders_many :cells, "TableData"
61
+
62
+ def initialize(css_class: "")
63
+ @css_class = css_class
64
+ end
65
+
66
+ def call
67
+ # safe_joins ensure multiple <td> elements render correctly
68
+ content_tag(:tr, safe_join(cells), class:@css_class)
69
+ end
70
+
71
+ class TableData < ViewComponent::Base
72
+ attr_reader :css_class
73
+
74
+ def initialize(css_class: "")
75
+ @css_class = css_class
76
+ end
77
+
78
+ def call
79
+ content_tag :td, content, class: css_class
80
+ end
81
+ end
74
82
  end
75
- end
83
+ end
76
84
  end
77
85
  end