lesli_view 1.1.2 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/lesli_view/charts/bar.rb +10 -0
- data/lib/lesli_view/charts/general.html.erb +38 -52
- data/lib/lesli_view/charts/general.rb +69 -30
- data/lib/lesli_view/charts/line.rb +16 -0
- data/lib/lesli_view/components/header.html.erb +48 -23
- data/lib/lesli_view/components/header.rb +35 -2
- data/lib/lesli_view/components/toolbar.css +52 -0
- data/lib/lesli_view/components/toolbar.html.erb +36 -21
- data/lib/lesli_view/components/toolbar.rb +41 -4
- data/lib/lesli_view/elements/button.css +357 -0
- data/lib/lesli_view/elements/button.html.erb +15 -12
- data/lib/lesli_view/elements/button.rb +74 -34
- data/lib/lesli_view/elements/empty.html.erb +31 -6
- data/lib/lesli_view/elements/empty.rb +8 -1
- data/lib/lesli_view/elements/table-chart.html.erb +42 -0
- data/lib/lesli_view/elements/table.css +91 -0
- data/lib/lesli_view/elements/table.html.erb +24 -21
- data/lib/lesli_view/elements/table.rb +67 -17
- data/lib/lesli_view/forms/builder.rb +16 -3
- data/lib/lesli_view/forms/builder_horizontal.rb +34 -10
- data/lib/lesli_view/forms/fields.rb +103 -70
- data/lib/lesli_view/forms/fieldset.rb +3 -3
- data/lib/lesli_view/forms/form.css +171 -0
- data/lib/lesli_view/forms/form.scss +2 -54
- data/lib/lesli_view/forms/inputs.rb +27 -15
- data/lib/lesli_view/layout/container.html.erb +8 -2
- data/lib/lesli_view/version.rb +2 -2
- data/lib/lesli_view/widgets/calendar.html.erb +42 -133
- data/lib/lesli_view/widgets/calendar.rb +77 -3
- data/lib/lesli_view/widgets/chart.html.erb +25 -26
- data/lib/lesli_view/widgets/chart.rb +46 -3
- data/lib/lesli_view/widgets/count.html.erb +17 -15
- data/lib/lesli_view/widgets/count.rb +13 -1
- data/lib/lesli_view/widgets/date.html.erb +23 -21
- data/lib/lesli_view/widgets/date.rb +27 -2
- data/lib/lesli_view/widgets/weather.html.erb +40 -38
- data/lib/lesli_view/widgets/weather.rb +41 -2
- data/readme.md +158 -29
- metadata +6 -4
- data/lib/lesli_view/components/header.scss +0 -69
- data/lib/lesli_view/components/toolbar.scss +0 -54
- data/lib/lesli_view/elements/table.scss +0 -153
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/* Tailwind styles for LesliView::Elements::Table. */
|
|
2
|
+
|
|
3
|
+
@layer components {
|
|
4
|
+
.lesli-table-container {
|
|
5
|
+
@apply w-full overflow-x-auto rounded-2xl border border-slate-200 bg-white shadow-sm;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.lesli-table {
|
|
9
|
+
width: 100%;
|
|
10
|
+
min-width: 40rem;
|
|
11
|
+
border-collapse: collapse;
|
|
12
|
+
@apply text-left text-sm text-slate-600;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.lesli-table thead {
|
|
16
|
+
@apply border-b border-slate-200 bg-slate-50;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.lesli-table th {
|
|
20
|
+
@apply whitespace-nowrap px-4 py-3 text-xs font-semibold uppercase tracking-wide text-slate-600;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.lesli-table td {
|
|
24
|
+
@apply border-b border-slate-100 px-4 py-3 align-middle;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.lesli-table tbody tr {
|
|
28
|
+
@apply transition-colors hover:bg-sky-50/50;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.lesli-table tbody tr:last-child td {
|
|
32
|
+
@apply border-b-0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.lesli-table--striped tbody tr:nth-child(even) {
|
|
36
|
+
@apply bg-slate-50/60;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.lesli-table--striped tbody tr:nth-child(even):hover {
|
|
40
|
+
@apply bg-sky-50/70;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.lesli-table--compact th,
|
|
44
|
+
.lesli-table--compact td {
|
|
45
|
+
@apply px-3 py-2;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.lesli-table__cell--center {
|
|
49
|
+
@apply text-center;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.lesli-table__cell--right {
|
|
53
|
+
@apply text-right;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.lesli-table__cell--left {
|
|
57
|
+
@apply text-left;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.lesli-table td.lesli-table__cell--linked {
|
|
61
|
+
@apply p-0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.lesli-table__link {
|
|
65
|
+
@apply block px-4 py-3 text-slate-700 transition-colors hover:text-sky-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-sky-500/40;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.lesli-table--compact .lesli-table__link {
|
|
69
|
+
@apply px-3 py-2;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.lesli-table__row--danger {
|
|
73
|
+
@apply bg-rose-50 text-rose-900;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.lesli-table__row--warning {
|
|
77
|
+
@apply bg-amber-50 text-amber-900;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.lesli-table__row--info {
|
|
81
|
+
@apply bg-sky-50 text-sky-900;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.lesli-table__row--success {
|
|
85
|
+
@apply bg-emerald-50 text-emerald-900;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.lesli-table__empty {
|
|
89
|
+
@apply py-12 text-center text-sm text-slate-400;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -1,24 +1,18 @@
|
|
|
1
|
-
<div class="lesli-table-container
|
|
2
|
-
<table id="<%= id %>" class="
|
|
1
|
+
<div class="lesli-table-container">
|
|
2
|
+
<table id="<%= id %>" class="<%= table_classes %>">
|
|
3
|
+
<% if caption.present? %>
|
|
4
|
+
<caption class="sr-only"><%= caption %></caption>
|
|
5
|
+
<% end %>
|
|
6
|
+
|
|
3
7
|
<% unless headless %>
|
|
4
8
|
<thead>
|
|
5
9
|
<tr>
|
|
6
10
|
<% columns.each do |column| %>
|
|
7
|
-
<th
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
<% if column[:sort] %>
|
|
13
|
-
<span class="icon-text">
|
|
14
|
-
<span><%= column[:label] %></span>
|
|
15
|
-
<span class="icon">
|
|
16
|
-
<span class="material-symbols"><%= 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 %>
|
|
11
|
+
<th
|
|
12
|
+
scope="col"
|
|
13
|
+
width="<%= column[:width] %>"
|
|
14
|
+
class="<%= column_class(column) %>">
|
|
15
|
+
<%= column[:label] %>
|
|
22
16
|
</th>
|
|
23
17
|
<% end %>
|
|
24
18
|
</tr>
|
|
@@ -26,14 +20,15 @@
|
|
|
26
20
|
<% end %>
|
|
27
21
|
<tbody>
|
|
28
22
|
<% if records %>
|
|
29
|
-
<% records.
|
|
23
|
+
<% records.each do |record| %>
|
|
30
24
|
<tr>
|
|
31
25
|
<% columns.each do |column| %>
|
|
32
|
-
|
|
26
|
+
<% value = value_for(record, column) %>
|
|
27
|
+
<td class="<%= class_names(column_class(column), "lesli-table__cell--linked": link.present?) %>">
|
|
33
28
|
<% if link %>
|
|
34
|
-
<%= link_to(
|
|
29
|
+
<%= link_to(value || "", link.call(record), class: "lesli-table__link", data: { turbo_frame: "_top" }) %>
|
|
35
30
|
<% else %>
|
|
36
|
-
<%=
|
|
31
|
+
<%= value %>
|
|
37
32
|
<% end %>
|
|
38
33
|
</td>
|
|
39
34
|
<% end %>
|
|
@@ -43,6 +38,14 @@
|
|
|
43
38
|
<% rows.each do |row| %>
|
|
44
39
|
<%= row %>
|
|
45
40
|
<% end %>
|
|
41
|
+
|
|
42
|
+
<% if empty? %>
|
|
43
|
+
<tr>
|
|
44
|
+
<td class="lesli-table__empty" colspan="<%= [columns.size, 1].max %>">
|
|
45
|
+
<%= empty_text %>
|
|
46
|
+
</td>
|
|
47
|
+
</tr>
|
|
48
|
+
<% end %>
|
|
46
49
|
</tbody>
|
|
47
50
|
</table>
|
|
48
51
|
</div>
|
|
@@ -33,50 +33,100 @@ Building a better future, one line of code at a time.
|
|
|
33
33
|
module LesliView
|
|
34
34
|
module Elements
|
|
35
35
|
class Table < ViewComponent::Base
|
|
36
|
-
attr_reader :id, :class_name, :
|
|
36
|
+
attr_reader :id, :class_name, :headless, :columns, :records, :link, :caption, :empty_text
|
|
37
37
|
|
|
38
38
|
renders_many :rows, "TableRow"
|
|
39
39
|
|
|
40
|
-
def initialize(
|
|
40
|
+
def initialize(
|
|
41
|
+
columns: nil,
|
|
42
|
+
records: nil,
|
|
43
|
+
id: nil,
|
|
44
|
+
class_name: nil,
|
|
45
|
+
headless: false,
|
|
46
|
+
link: nil,
|
|
47
|
+
caption: nil,
|
|
48
|
+
empty: nil,
|
|
49
|
+
striped: true,
|
|
50
|
+
compact: false
|
|
51
|
+
)
|
|
41
52
|
@id = id
|
|
42
53
|
@class_name = class_name
|
|
43
|
-
@pagination = pagination
|
|
44
|
-
@loading = loading
|
|
45
54
|
@headless = headless
|
|
46
|
-
@columns = columns
|
|
55
|
+
@columns = Array(columns).map { |column| column.to_h.symbolize_keys }
|
|
47
56
|
@records = records
|
|
48
57
|
@link = link
|
|
58
|
+
@caption = caption
|
|
59
|
+
@empty_text = empty || I18n.t("lesli_view.table.empty", default: "No records found")
|
|
60
|
+
@striped = striped
|
|
61
|
+
@compact = compact
|
|
49
62
|
end
|
|
50
63
|
|
|
51
|
-
def
|
|
52
|
-
|
|
64
|
+
def table_classes
|
|
65
|
+
class_names(
|
|
66
|
+
"lesli-table",
|
|
67
|
+
("lesli-table--striped" if @striped),
|
|
68
|
+
("lesli-table--compact" if @compact),
|
|
69
|
+
class_name
|
|
70
|
+
)
|
|
53
71
|
end
|
|
54
72
|
|
|
55
|
-
def
|
|
56
|
-
|
|
73
|
+
def column_class(column)
|
|
74
|
+
alignment = column[:align]&.to_sym
|
|
75
|
+
alignment = :center if column[:field].to_s == "id"
|
|
76
|
+
return unless %i[left center right].include?(alignment)
|
|
77
|
+
|
|
78
|
+
"lesli-table__cell--#{alignment}"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def value_for(record, column)
|
|
82
|
+
field = column.fetch(:field)
|
|
83
|
+
field_name = field.to_s
|
|
84
|
+
|
|
85
|
+
if record.respond_to?(:attributes) && record.attributes.key?(field_name)
|
|
86
|
+
record.attributes[field_name]
|
|
87
|
+
elsif record.respond_to?(:key?)
|
|
88
|
+
return record[field] if record.key?(field)
|
|
89
|
+
return record[field_name] if record.key?(field_name)
|
|
90
|
+
return record[field_name.to_sym] if record.key?(field_name.to_sym)
|
|
91
|
+
elsif record.respond_to?(field_name)
|
|
92
|
+
record.public_send(field_name)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def empty?
|
|
97
|
+
records.present? ? records.empty? : rows.empty?
|
|
57
98
|
end
|
|
58
99
|
|
|
59
100
|
class TableRow < ViewComponent::Base
|
|
60
101
|
renders_many :cells, "TableData"
|
|
61
102
|
|
|
62
|
-
|
|
63
|
-
|
|
103
|
+
STATUSES = %w[danger info success warning].freeze
|
|
104
|
+
|
|
105
|
+
def initialize(class_name: nil, status: nil)
|
|
106
|
+
@class_name = class_name
|
|
107
|
+
@status = status.to_s if STATUSES.include?(status.to_s)
|
|
64
108
|
end
|
|
65
109
|
|
|
66
110
|
def call
|
|
67
|
-
|
|
68
|
-
|
|
111
|
+
content_tag(
|
|
112
|
+
:tr,
|
|
113
|
+
safe_join(cells),
|
|
114
|
+
class: class_names(@class_name, @status && "lesli-table__row--#{@status}")
|
|
115
|
+
)
|
|
69
116
|
end
|
|
70
117
|
|
|
71
118
|
class TableData < ViewComponent::Base
|
|
72
|
-
attr_reader :
|
|
119
|
+
attr_reader :class_name
|
|
73
120
|
|
|
74
|
-
def initialize(
|
|
75
|
-
@
|
|
121
|
+
def initialize(class_name: nil, align: nil)
|
|
122
|
+
@class_name = class_names(
|
|
123
|
+
class_name,
|
|
124
|
+
("lesli-table__cell--#{align}" if %i[left center right].include?(align&.to_sym))
|
|
125
|
+
)
|
|
76
126
|
end
|
|
77
127
|
|
|
78
128
|
def call
|
|
79
|
-
content_tag
|
|
129
|
+
content_tag(:td, content, class: class_name)
|
|
80
130
|
end
|
|
81
131
|
end
|
|
82
132
|
end
|
|
@@ -35,9 +35,22 @@ Building a better future, one line of code at a time.
|
|
|
35
35
|
module LesliView
|
|
36
36
|
module Forms
|
|
37
37
|
class Builder < ActionView::Helpers::FormBuilder
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
CATEGORY_ALIASES = {
|
|
39
|
+
alert: :danger,
|
|
40
|
+
error: :danger,
|
|
41
|
+
notice: :info
|
|
42
|
+
}.freeze
|
|
43
|
+
|
|
44
|
+
def css_category(category = nil)
|
|
45
|
+
return if category.blank?
|
|
46
|
+
|
|
47
|
+
normalized_category = CATEGORY_ALIASES.fetch(category.to_sym, category.to_sym)
|
|
48
|
+
"is-#{normalized_category}"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def merge_css_classes(*classes)
|
|
52
|
+
classes.flatten.compact.flat_map { |value| value.to_s.split }.reject(&:blank?).uniq.join(" ")
|
|
53
|
+
end
|
|
41
54
|
|
|
42
55
|
include LesliView::Forms::Fields
|
|
43
56
|
include LesliView::Forms::Inputs
|
|
@@ -1,25 +1,49 @@
|
|
|
1
1
|
module LesliView
|
|
2
2
|
module Forms
|
|
3
3
|
class BuilderHorizontal < Builder
|
|
4
|
-
def field_control(attribute, label: nil, message:nil, category:nil, icon:nil)
|
|
5
|
-
super(attribute, label:label, message:message, category:category, icon:icon, horizontal:true)
|
|
4
|
+
def field_control(attribute, label: nil, message: nil, category: nil, icon: nil, **options)
|
|
5
|
+
super(attribute, label: label, message: message, category: category, icon: icon, horizontal: true, **options)
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
-
def
|
|
9
|
-
super(attribute,
|
|
8
|
+
def field_control_text(attribute, label: nil, message: nil, category: nil, icon: nil, **options)
|
|
9
|
+
super(attribute, label: label, message: message, category: category, icon: icon, horizontal: true, **options)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def field_control_email(attribute, label: nil, message: nil, category: nil, icon: nil, **options)
|
|
13
|
+
super(attribute, label: label, message: message, category: category, icon: icon, horizontal: true, **options)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def field_control_checkbox(attribute, label: nil, message: nil, category: nil, icon: nil, **options)
|
|
17
|
+
super(attribute, label: label, message: message, category: category, icon: icon, horizontal: true, **options)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def field_control_password(attribute, label: nil, message: nil, category: nil, icon: nil, **options)
|
|
21
|
+
super(attribute, label: label, message: message, category: category, icon: icon, horizontal: true, **options)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def field_control_textarea(attribute, label: nil, message: nil, category: nil, icon: nil, **options)
|
|
25
|
+
super(attribute, label: label, message: message, category: category, icon: icon, horizontal: true, **options)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def field_textarea(attribute, **options)
|
|
29
|
+
field_control_textarea(attribute, **options)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def field_select(attribute, choices, options = {}, html_options = {}, label: nil, humanize: true, **field_options)
|
|
33
|
+
field_control_select(attribute, choices, label: label, humanize: humanize, options: options, html_options: html_options, **field_options)
|
|
10
34
|
end
|
|
11
35
|
|
|
12
36
|
def field_control_submit(value = nil, options = {})
|
|
13
37
|
super(value, options, horizontal:true)
|
|
14
38
|
end
|
|
15
39
|
|
|
16
|
-
def field_control_button(value = nil, options = {}, icon:nil,
|
|
17
|
-
super(value, options, icon:icon, horizontal:true)
|
|
18
|
-
end
|
|
40
|
+
def field_control_button(value = nil, options = {}, category: "primary", icon: nil, type: "submit")
|
|
41
|
+
super(value, options, category: category, icon: icon, type: type, horizontal: true)
|
|
42
|
+
end
|
|
19
43
|
|
|
20
|
-
def field_control_select(attribute, choices, label: nil, message:nil, category:nil, icon:nil, humanize:true)
|
|
21
|
-
super(attribute, choices, label:label, message:message, category:category, icon:icon, horizontal:true, humanize:humanize)
|
|
22
|
-
end
|
|
44
|
+
def field_control_select(attribute, choices, label: nil, message: nil, category: nil, icon: nil, humanize: true, options: {}, html_options: {})
|
|
45
|
+
super(attribute, choices, label: label, message: message, category: category, icon: icon, horizontal: true, humanize: humanize, options: options, html_options: html_options)
|
|
46
|
+
end
|
|
23
47
|
end
|
|
24
48
|
end
|
|
25
49
|
end
|
|
@@ -1,67 +1,113 @@
|
|
|
1
1
|
module LesliView
|
|
2
2
|
module Forms
|
|
3
3
|
module Fields
|
|
4
|
-
def field_control(attribute, label: nil, message:nil, category:nil, icon:nil, horizontal: false)
|
|
5
|
-
field_control_text(attribute, label:label, message:message, category:category, icon:icon, horizontal:horizontal)
|
|
4
|
+
def field_control(attribute, label: nil, message: nil, category: nil, icon: nil, horizontal: false, **options)
|
|
5
|
+
field_control_text(attribute, label: label, message: message, category: category, icon: icon, horizontal: horizontal, **options)
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
-
def field_control_checkbox(attribute, label: nil, message:nil, category:nil, icon:nil, horizontal: false)
|
|
8
|
+
def field_control_checkbox(attribute, label: nil, message: nil, category: nil, icon: nil, horizontal: false, **options)
|
|
9
|
+
label_text = label.presence || attribute.to_s.humanize
|
|
10
|
+
checkbox_html = check_box(attribute, options, true, false)
|
|
11
|
+
control_html = @template.content_tag(:label, class: "lesli-form-checkbox-label") do
|
|
12
|
+
@template.safe_join([
|
|
13
|
+
checkbox_html,
|
|
14
|
+
@template.content_tag(:span, label_text)
|
|
15
|
+
])
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
field_control_builder(
|
|
19
|
+
control_html: control_html,
|
|
20
|
+
message_text: message,
|
|
21
|
+
category: category,
|
|
22
|
+
horizontal: horizontal
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def field_control_password(attribute, label: nil, message: nil, category: nil, icon: nil, horizontal: false, **options)
|
|
9
27
|
label_html = label(attribute, label)
|
|
10
|
-
|
|
28
|
+
control_html = password_field(attribute, options)
|
|
11
29
|
|
|
12
30
|
field_control_builder(
|
|
13
31
|
label_html: label_html,
|
|
14
|
-
control_html:
|
|
32
|
+
control_html: control_html,
|
|
33
|
+
message_text: message,
|
|
34
|
+
category: category,
|
|
35
|
+
icon: icon,
|
|
15
36
|
horizontal: horizontal
|
|
16
37
|
)
|
|
17
38
|
end
|
|
18
39
|
|
|
19
|
-
def field_control_text(attribute, label: nil, message:nil, category:nil, icon:nil, horizontal: false)
|
|
40
|
+
def field_control_text(attribute, label: nil, message: nil, category: nil, icon: nil, horizontal: false, **options)
|
|
20
41
|
label_html = label(attribute, label)
|
|
21
|
-
|
|
42
|
+
control_html = text_field(attribute, options)
|
|
22
43
|
|
|
23
44
|
field_control_builder(
|
|
24
45
|
label_html: label_html,
|
|
25
|
-
control_html:
|
|
46
|
+
control_html: control_html,
|
|
47
|
+
message_text: message,
|
|
48
|
+
category: category,
|
|
49
|
+
icon: icon,
|
|
26
50
|
horizontal: horizontal
|
|
27
51
|
)
|
|
28
52
|
end
|
|
29
53
|
|
|
30
|
-
def field_control_email(attribute, label: nil, message:nil, category:nil, icon:nil, horizontal: false)
|
|
54
|
+
def field_control_email(attribute, label: nil, message: nil, category: nil, icon: nil, horizontal: false, **options)
|
|
31
55
|
label_html = label(attribute, label)
|
|
32
|
-
control_html = email_field(attribute)
|
|
56
|
+
control_html = email_field(attribute, options)
|
|
33
57
|
|
|
34
58
|
field_control_builder(
|
|
35
59
|
label_html: label_html,
|
|
36
60
|
control_html: control_html,
|
|
61
|
+
message_text: message,
|
|
62
|
+
category: category,
|
|
37
63
|
horizontal: horizontal,
|
|
38
|
-
icon:icon
|
|
64
|
+
icon: icon
|
|
39
65
|
)
|
|
40
66
|
end
|
|
41
67
|
|
|
42
|
-
def field_control_select(attribute, choices, label: nil, message:nil, category:nil, icon:nil, horizontal: false, humanize:true)
|
|
43
|
-
choices = choices.map { |
|
|
44
|
-
value = @object.send(attribute)
|
|
68
|
+
def field_control_select(attribute, choices, label: nil, message: nil, category: nil, icon: nil, horizontal: false, humanize: true, options: {}, html_options: {})
|
|
69
|
+
choices = choices.map { |text, value| [text.to_s.humanize, value] } if humanize
|
|
45
70
|
label_html = label(attribute, label)
|
|
46
|
-
select_html = select(attribute, choices,
|
|
71
|
+
select_html = select(attribute, choices, options, html_options)
|
|
47
72
|
|
|
48
73
|
field_control_builder(
|
|
49
74
|
label_html: label_html,
|
|
50
|
-
control_html: @template.content_tag(:div, select_html, class: "select
|
|
75
|
+
control_html: @template.content_tag(:div, select_html, class: "lesli-form-select-wrapper"),
|
|
76
|
+
message_text: message,
|
|
77
|
+
category: category,
|
|
78
|
+
icon: icon,
|
|
51
79
|
horizontal: horizontal
|
|
52
80
|
)
|
|
53
81
|
end
|
|
54
82
|
|
|
55
|
-
def field_control_textarea(attribute, label:nil, message:nil, category:nil, icon:nil, horizontal: false)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
control_html = rich_textarea(attribute)
|
|
83
|
+
def field_control_textarea(attribute, label: nil, message: nil, category: nil, icon: nil, horizontal: false, **options)
|
|
84
|
+
label_html = label == false ? nil : label(attribute, label)
|
|
85
|
+
control_html = rich_textarea(attribute, options)
|
|
59
86
|
|
|
60
87
|
field_control_builder(
|
|
61
88
|
label_html: label_html,
|
|
62
89
|
control_html: control_html,
|
|
90
|
+
message_text: message,
|
|
91
|
+
category: category,
|
|
63
92
|
horizontal: horizontal,
|
|
64
|
-
icon:icon
|
|
93
|
+
icon: icon
|
|
94
|
+
)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Backward-compatible shorthand used by older Lesli forms.
|
|
98
|
+
def field_textarea(attribute, **options)
|
|
99
|
+
field_control_textarea(attribute, **options)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def field_select(attribute, choices, options = {}, html_options = {}, label: nil, humanize: true, **field_options)
|
|
103
|
+
field_control_select(
|
|
104
|
+
attribute,
|
|
105
|
+
choices,
|
|
106
|
+
label: label,
|
|
107
|
+
humanize: humanize,
|
|
108
|
+
options: options,
|
|
109
|
+
html_options: html_options,
|
|
110
|
+
**field_options
|
|
65
111
|
)
|
|
66
112
|
end
|
|
67
113
|
|
|
@@ -70,75 +116,62 @@ module LesliView
|
|
|
70
116
|
field_control_builder(control_html: submit_html, horizontal: horizontal)
|
|
71
117
|
end
|
|
72
118
|
|
|
73
|
-
def field_control_button(value = nil, options = {}, category:"primary", icon:nil, type:"submit", horizontal: false)
|
|
74
|
-
|
|
75
|
-
button_classes =
|
|
119
|
+
def field_control_button(value = nil, options = {}, category: "primary", icon: nil, type: "submit", horizontal: false)
|
|
120
|
+
options = options.symbolize_keys
|
|
121
|
+
button_classes = merge_css_classes("lesli-form-button", css_category(category), options.delete(:class))
|
|
76
122
|
|
|
77
|
-
button_html = @template.content_tag(:button, class: button_classes, type: type, **options
|
|
78
|
-
|
|
123
|
+
button_html = @template.content_tag(:button, class: button_classes, type: type, **options) do
|
|
124
|
+
content = []
|
|
79
125
|
if icon.present?
|
|
80
|
-
|
|
126
|
+
content << @template.content_tag(:span, class: "lesli-form-button-icon", aria: { hidden: true }) do
|
|
81
127
|
@template.content_tag(:span, icon, class: "material-symbols")
|
|
82
128
|
end
|
|
83
129
|
end
|
|
84
|
-
|
|
85
|
-
|
|
130
|
+
content << @template.content_tag(:span, value) if value.present?
|
|
131
|
+
@template.safe_join(content)
|
|
86
132
|
end
|
|
87
|
-
field_control_builder(control_html: button_html,
|
|
133
|
+
field_control_builder(control_html: button_html, horizontal: horizontal)
|
|
88
134
|
end
|
|
89
135
|
|
|
90
136
|
def field_control_builder(
|
|
91
|
-
label_html:nil,
|
|
92
|
-
control_html:nil,
|
|
93
|
-
message_text:nil,
|
|
94
|
-
horizontal:false,
|
|
95
|
-
category:nil,
|
|
96
|
-
icon:nil,
|
|
137
|
+
label_html: nil,
|
|
138
|
+
control_html: nil,
|
|
139
|
+
message_text: nil,
|
|
140
|
+
horizontal: false,
|
|
141
|
+
category: nil,
|
|
142
|
+
icon: nil,
|
|
97
143
|
&block
|
|
98
144
|
)
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
help_class = ""
|
|
112
|
-
|
|
113
|
-
@template.content_tag(:div, class: 'lesli-form-field mb-3') do
|
|
114
|
-
@template.content_tag(:div, class: field_classes.join(' ')) do
|
|
115
|
-
@template.content_tag(:div, label_html, class: 'field-label is-normal mb-1') +
|
|
116
|
-
@template.content_tag(:div, class: 'field-body') do
|
|
117
|
-
@template.content_tag(:div, class: 'field') do
|
|
118
|
-
control_html = @template.content_tag(:div, class: control_classes.join(' ')) do
|
|
119
|
-
content = ''.html_safe
|
|
120
|
-
content << (block_given? ? @template.capture(&block) : control_html)
|
|
121
|
-
content << icon_html(icon_left, 'left') if icon_left
|
|
122
|
-
content << icon_html(icon_right, 'right') if icon_right
|
|
123
|
-
content
|
|
124
|
-
end
|
|
125
|
-
control_html + @template.content_tag(:p, message_text, class: "help #{css_category(category)}")
|
|
126
|
-
end
|
|
145
|
+
field_classes = merge_css_classes("lesli-form-field", horizontal && "is-horizontal", css_category(category))
|
|
146
|
+
control_classes = merge_css_classes("lesli-form-control", icon.present? && "has-icon")
|
|
147
|
+
captured_control = block_given? ? @template.capture(&block) : control_html
|
|
148
|
+
|
|
149
|
+
@template.content_tag(:div, class: field_classes) do
|
|
150
|
+
content = []
|
|
151
|
+
content << @template.content_tag(:div, label_html, class: "lesli-form-label-wrapper") if label_html.present?
|
|
152
|
+
content << @template.content_tag(:div, class: "lesli-form-field-body") do
|
|
153
|
+
body = []
|
|
154
|
+
body << @template.content_tag(:div, class: control_classes) do
|
|
155
|
+
@template.safe_join([captured_control, icon_html(icon)].compact)
|
|
127
156
|
end
|
|
157
|
+
if message_text.present?
|
|
158
|
+
body << @template.content_tag(:p, message_text, class: merge_css_classes("lesli-form-help", css_category(category)))
|
|
159
|
+
end
|
|
160
|
+
@template.safe_join(body)
|
|
128
161
|
end
|
|
162
|
+
@template.safe_join(content)
|
|
129
163
|
end
|
|
130
164
|
end
|
|
131
165
|
|
|
132
166
|
private
|
|
133
167
|
|
|
134
|
-
def icon_html(
|
|
135
|
-
return
|
|
168
|
+
def icon_html(icon_name)
|
|
169
|
+
return unless icon_name.present?
|
|
136
170
|
|
|
137
|
-
@template.content_tag(:span, class: "icon
|
|
138
|
-
|
|
139
|
-
@template.content_tag(:span, icon_class, class: "material-symbols-outlined")
|
|
171
|
+
@template.content_tag(:span, class: "lesli-form-icon", aria: { hidden: true }) do
|
|
172
|
+
@template.content_tag(:span, icon_name, class: "material-symbols-outlined")
|
|
140
173
|
end
|
|
141
|
-
end
|
|
174
|
+
end
|
|
142
175
|
end
|
|
143
176
|
end
|
|
144
177
|
end
|
|
@@ -2,11 +2,11 @@ module LesliView
|
|
|
2
2
|
module Forms
|
|
3
3
|
module Fieldset
|
|
4
4
|
def fieldset(legend = "", options = {}, category: nil, &block)
|
|
5
|
-
options
|
|
6
|
-
options[:class]
|
|
5
|
+
options = options.symbolize_keys
|
|
6
|
+
options[:class] = merge_css_classes("lesli-form-fieldset", css_category(category), options[:class])
|
|
7
7
|
|
|
8
8
|
@template.content_tag(:fieldset, options) do
|
|
9
|
-
legend_html = legend.present? ? @template.content_tag(:
|
|
9
|
+
legend_html = legend.present? ? @template.content_tag(:legend, legend, class: "lesli-form-legend") : nil
|
|
10
10
|
@template.safe_join([legend_html, @template.capture(&block)].compact)
|
|
11
11
|
end
|
|
12
12
|
end
|