katalyst-koi 4.5.0.beta.1 → 4.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/builds/koi/admin.css +34 -4
- data/app/assets/builds/koi/admin.css.map +1 -1
- data/app/assets/stylesheets/koi/components/_index-table.scss +54 -3
- data/app/components/koi/summary_list/attachment_component.rb +11 -1
- data/app/components/koi/summary_list/base.rb +15 -1
- data/app/components/koi/summary_list/item_component.rb +0 -7
- data/app/components/koi/summary_list/rich_text_component.rb +0 -3
- data/app/components/koi/summary_list_component.rb +14 -14
- data/app/components/koi/tables/body.rb +117 -32
- data/app/components/koi/tables/body_row_component.rb +116 -18
- data/app/components/koi/tables/header.rb +51 -1
- data/app/components/koi/tables/header_cell_component.rb +30 -0
- data/app/components/koi/tables/header_row_component.rb +182 -20
- data/app/helpers/koi/date_helper.rb +18 -28
- data/lib/generators/koi/admin_controller/templates/controller.rb.tt +6 -7
- data/lib/generators/koi/admin_controller/templates/controller_spec.rb.tt +4 -4
- data/lib/generators/koi/admin_views/admin_views_generator.rb +6 -4
- data/lib/generators/koi/admin_views/templates/_record.html+row.erb.tt +1 -1
- data/lib/koi/config.rb +3 -0
- data/lib/koi/form_builder.rb +2 -2
- metadata +4 -4
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
using Katalyst::HtmlAttributes::HasHtmlAttributes
|
4
|
+
|
3
5
|
module Koi
|
4
6
|
module Tables
|
5
7
|
module Body
|
@@ -11,47 +13,117 @@ module Koi
|
|
11
13
|
end
|
12
14
|
|
13
15
|
# Formats the value as a date
|
14
|
-
#
|
16
|
+
# @param format [String] date format, defaults to :admin
|
17
|
+
# @param relative [Boolean] if true, the date may be(if within 5 days) shown as a relative date
|
15
18
|
class DateComponent < BodyCellComponent
|
16
|
-
|
19
|
+
include Koi::DateHelper
|
20
|
+
|
21
|
+
def initialize(table, record, attribute, format: :admin, relative: true, **options)
|
17
22
|
super(table, record, attribute, **options)
|
18
23
|
|
19
|
-
@format
|
24
|
+
@format = format
|
25
|
+
@relative = relative
|
26
|
+
end
|
27
|
+
|
28
|
+
def value
|
29
|
+
super&.to_date
|
20
30
|
end
|
21
31
|
|
22
32
|
def rendered_value
|
23
|
-
|
33
|
+
@relative ? relative_time : absolute_time
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def absolute_time
|
39
|
+
value.present? ? I18n.l(value, format: @format) : ""
|
40
|
+
end
|
41
|
+
|
42
|
+
def relative_time
|
43
|
+
if value.blank?
|
44
|
+
""
|
45
|
+
else
|
46
|
+
days_ago_in_words(value)&.capitalize || absolute_time
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def default_html_attributes
|
51
|
+
@relative && value.present? && days_ago_in_words(value).present? ? { title: absolute_time } : {}
|
24
52
|
end
|
25
53
|
end
|
26
54
|
|
27
55
|
# Formats the value as a datetime
|
28
|
-
#
|
56
|
+
# @param format [String] datetime format, defaults to :admin
|
57
|
+
# @param relative [Boolean] if true, the datetime may be(if today) shown as a relative date/time
|
29
58
|
class DatetimeComponent < BodyCellComponent
|
30
|
-
|
59
|
+
include ActionView::Helpers::DateHelper
|
60
|
+
|
61
|
+
def initialize(table, record, attribute, format: :admin, relative: true, **options)
|
31
62
|
super(table, record, attribute, **options)
|
32
63
|
|
33
|
-
@format
|
64
|
+
@format = format
|
65
|
+
@relative = relative
|
66
|
+
end
|
67
|
+
|
68
|
+
def value
|
69
|
+
super&.to_datetime
|
34
70
|
end
|
35
71
|
|
36
72
|
def rendered_value
|
37
|
-
|
73
|
+
@relative ? relative_time : absolute_time
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def absolute_time
|
79
|
+
value.present? ? I18n.l(value, format: @format) : ""
|
80
|
+
end
|
81
|
+
|
82
|
+
def today?
|
83
|
+
value.to_date == Date.current
|
84
|
+
end
|
85
|
+
|
86
|
+
def relative_time
|
87
|
+
return "" if value.blank?
|
88
|
+
|
89
|
+
if today?
|
90
|
+
if value > DateTime.current
|
91
|
+
"#{distance_of_time_in_words(value, DateTime.current)} from now".capitalize
|
92
|
+
else
|
93
|
+
"#{distance_of_time_in_words(value, DateTime.current)} ago".capitalize
|
94
|
+
end
|
95
|
+
else
|
96
|
+
absolute_time
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def default_html_attributes
|
101
|
+
@relative && today? ? { title: absolute_time } : {}
|
38
102
|
end
|
39
103
|
end
|
40
104
|
|
41
105
|
# Formats the value as a money value
|
42
|
-
#
|
106
|
+
#
|
107
|
+
# The value is expected to be in cents.
|
43
108
|
# Adds a class to the cell to allow for custom styling
|
44
|
-
class
|
109
|
+
class CurrencyComponent < BodyCellComponent
|
110
|
+
def initialize(table, record, attribute, options: {}, **html_attributes)
|
111
|
+
super(table, record, attribute, **html_attributes)
|
112
|
+
|
113
|
+
@options = options
|
114
|
+
end
|
115
|
+
|
45
116
|
def rendered_value
|
46
|
-
value.present? ? number_to_currency(value / 100.0) : ""
|
117
|
+
value.present? ? number_to_currency(value / 100.0, @options) : ""
|
47
118
|
end
|
48
119
|
|
49
120
|
def default_html_attributes
|
50
|
-
|
121
|
+
super.merge_html(class: "type-currency")
|
51
122
|
end
|
52
123
|
end
|
53
124
|
|
54
125
|
# Formats the value as a number
|
126
|
+
#
|
55
127
|
# Adds a class to the cell to allow for custom styling
|
56
128
|
class NumberComponent < BodyCellComponent
|
57
129
|
def rendered_value
|
@@ -59,51 +131,58 @@ module Koi
|
|
59
131
|
end
|
60
132
|
|
61
133
|
def default_html_attributes
|
62
|
-
|
134
|
+
super.merge_html(class: "type-number")
|
63
135
|
end
|
64
136
|
end
|
65
137
|
|
66
138
|
# Displays the plain text for rich text content
|
139
|
+
#
|
67
140
|
# Adds a title attribute to allow for hover over display of the full content
|
68
141
|
class RichTextComponent < BodyCellComponent
|
69
|
-
def rendered_value
|
70
|
-
value.to_plain_text
|
71
|
-
end
|
72
|
-
|
73
142
|
def default_html_attributes
|
74
|
-
{ title:
|
143
|
+
{ title: value.to_plain_text }
|
75
144
|
end
|
76
145
|
end
|
77
146
|
|
78
147
|
# Displays a link to the record
|
79
148
|
# The link text is the value of the attribute
|
80
|
-
# @
|
149
|
+
# @see Koi::Tables::BodyRowComponent#link
|
81
150
|
class LinkComponent < BodyCellComponent
|
82
|
-
def initialize(table, record, attribute, url:
|
83
|
-
super(table, record, attribute, **
|
151
|
+
def initialize(table, record, attribute, url:, link: {}, **options)
|
152
|
+
super(table, record, attribute, **options)
|
84
153
|
|
85
|
-
@
|
154
|
+
@url = url
|
155
|
+
@link_options = link
|
86
156
|
end
|
87
157
|
|
88
158
|
def call
|
89
159
|
content # ensure content is set before rendering options
|
90
160
|
|
91
|
-
link = content.present? && url.present? ? link_to(
|
161
|
+
link = content.present? && url.present? ? link_to(content, url, @link_options) : content.to_s
|
92
162
|
content_tag(@type, link, **html_attributes)
|
93
163
|
end
|
94
164
|
|
95
165
|
def url
|
96
|
-
@url
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
166
|
+
case @url
|
167
|
+
when Symbol
|
168
|
+
# helpers are not available until the component is rendered
|
169
|
+
@url = helpers.public_send(@url, record)
|
170
|
+
when Proc
|
171
|
+
@url = @url.call(record)
|
172
|
+
else
|
173
|
+
@url
|
174
|
+
end
|
101
175
|
end
|
102
176
|
end
|
103
177
|
|
104
|
-
# Shows
|
105
|
-
#
|
106
|
-
|
178
|
+
# Shows an attachment
|
179
|
+
#
|
180
|
+
# The value is expected to be an ActiveStorage attachment
|
181
|
+
#
|
182
|
+
# If it is representable, shows as a image tag using a default variant named :thumb.
|
183
|
+
#
|
184
|
+
# Otherwise shows as a link to download.
|
185
|
+
class AttachmentComponent < BodyCellComponent
|
107
186
|
def initialize(table, record, attribute, variant: :thumb, **options)
|
108
187
|
super(table, record, attribute, **options)
|
109
188
|
|
@@ -111,7 +190,13 @@ module Koi
|
|
111
190
|
end
|
112
191
|
|
113
192
|
def rendered_value
|
114
|
-
|
193
|
+
if value.try(:representable?)
|
194
|
+
image_tag(@variant.nil? ? value : value.variant(@variant))
|
195
|
+
elsif value.try(:attached?)
|
196
|
+
link_to value.blob.filename, rails_blob_path(value, disposition: :attachment)
|
197
|
+
else
|
198
|
+
""
|
199
|
+
end
|
115
200
|
end
|
116
201
|
end
|
117
202
|
end
|
@@ -4,40 +4,138 @@ module Koi
|
|
4
4
|
module Tables
|
5
5
|
# Custom body row component, in order to override the default body cell component
|
6
6
|
class BodyRowComponent < Katalyst::Tables::BodyRowComponent
|
7
|
-
|
8
|
-
|
7
|
+
# Generates a column from boolean values rendered as "Yes" or "No".
|
8
|
+
#
|
9
|
+
# @param method [Symbol] the method to call on the record
|
10
|
+
# @param attributes [Hash] HTML attributes to be added to the cell
|
11
|
+
# @param block [Proc] optional block to alter the cell content
|
12
|
+
# @return [void]
|
13
|
+
#
|
14
|
+
# @example Render a boolean column indicating whether the record is active
|
15
|
+
# <% row.boolean :active %> # => <td>Yes</td>
|
16
|
+
def boolean(method, **attributes, &block)
|
17
|
+
with_column(Body::BooleanComponent.new(@table, @record, method, **attributes), &block)
|
9
18
|
end
|
10
19
|
|
11
|
-
|
12
|
-
|
20
|
+
# Generates a column from date values rendered using I18n.l.
|
21
|
+
# The default format is :admin, but it can be overridden.
|
22
|
+
#
|
23
|
+
# @param method [Symbol] the method to call on the record
|
24
|
+
# @param format [Symbol] the I18n date format to use when rendering
|
25
|
+
# @param attributes [Hash] HTML attributes to be added to the cell tag
|
26
|
+
# @param block [Proc] optional block to alter the cell content
|
27
|
+
# @return [void]
|
28
|
+
#
|
29
|
+
# @example Render a date column describing when the record was created
|
30
|
+
# <% row.date :created_at %> # => <td>29 Feb 2024</td>
|
31
|
+
def date(method, format: :admin, **attributes, &block)
|
32
|
+
with_column(Body::DateComponent.new(@table, @record, method, format:, **attributes), &block)
|
13
33
|
end
|
14
34
|
|
15
|
-
|
16
|
-
|
35
|
+
# Generates a column from datetime values rendered using I18n.l.
|
36
|
+
# The default format is :admin, but it can be overridden.
|
37
|
+
#
|
38
|
+
# @param method [Symbol] the method to call on the record
|
39
|
+
# @param format [Symbol] the I18n datetime format to use when rendering
|
40
|
+
# @param attributes [Hash] HTML attributes to be added to the cell tag
|
41
|
+
# @param block [Proc] optional block to alter the cell content
|
42
|
+
# @return [void]
|
43
|
+
#
|
44
|
+
# @example Render a datetime column describing when the record was created
|
45
|
+
# <% row.datetime :created_at %> # => <td>29 Feb 2024, 5:00pm</td>
|
46
|
+
def datetime(method, format: :admin, **attributes, &block)
|
47
|
+
with_column(Body::DatetimeComponent.new(@table, @record, method, format:, **attributes), &block)
|
17
48
|
end
|
18
49
|
|
19
|
-
|
20
|
-
|
50
|
+
# Generates a column from numeric values formatted appropriately.
|
51
|
+
#
|
52
|
+
# @param method [Symbol] the method to call on the record
|
53
|
+
# @param attributes [Hash] HTML attributes to be added to the cell tag
|
54
|
+
# @param block [Proc] optional block to alter the cell content
|
55
|
+
# @return [void]
|
56
|
+
#
|
57
|
+
# @example Render the number of comments on a post
|
58
|
+
# <% row.number :comment_count %> # => <td>0</td>
|
59
|
+
def number(method, **attributes, &block)
|
60
|
+
with_column(Body::NumberComponent.new(@table, @record, method, **attributes), &block)
|
21
61
|
end
|
22
62
|
|
23
|
-
|
24
|
-
|
63
|
+
# Generates a column from numeric values rendered using `number_to_currency`.
|
64
|
+
#
|
65
|
+
# @param method [Symbol] the method to call on the record
|
66
|
+
# @param options [Hash] options to be passed to `number_to_currency`
|
67
|
+
# @param attributes [Hash] HTML attributes to be added to the cell tag
|
68
|
+
# @param block [Proc] optional block to alter the cell content
|
69
|
+
# @return [void]
|
70
|
+
#
|
71
|
+
# @example Render a currency column for the price of a product
|
72
|
+
# <% row.currency :price %> # => <td>$3.50</td>
|
73
|
+
def currency(method, options: {}, **attributes, &block)
|
74
|
+
with_column(Body::CurrencyComponent.new(@table, @record, method, options:, **attributes), &block)
|
25
75
|
end
|
26
76
|
|
27
|
-
|
28
|
-
|
77
|
+
# Generates a column containing HTML markup.
|
78
|
+
#
|
79
|
+
# @param method [Symbol] the method to call on the record
|
80
|
+
# @param attributes [Hash] HTML attributes to be added to the cell tag
|
81
|
+
# @param block [Proc] optional block to alter the cell content
|
82
|
+
# @return [void]
|
83
|
+
#
|
84
|
+
# @note This method assumes that the method returns HTML-safe content.
|
85
|
+
# If the content is not HTML-safe, it will be escaped.
|
86
|
+
#
|
87
|
+
# @example Render a description column containing HTML markup
|
88
|
+
# <% row.rich_text :description %> # => <td><em>Emphasis</em></td>
|
89
|
+
def rich_text(method, **attributes, &block)
|
90
|
+
with_column(Body::RichTextComponent.new(@table, @record, method, **attributes), &block)
|
29
91
|
end
|
30
92
|
|
31
|
-
|
32
|
-
|
93
|
+
# Generates a column that links to the record's show page (by default).
|
94
|
+
#
|
95
|
+
# @param method [Symbol] the method to call on the record
|
96
|
+
# @param link [Hash] options to be passed to the link_to helper
|
97
|
+
# @option opts [Hash, Array, String, Symbol] :url ([:admin, object]) options for url_for,
|
98
|
+
# or a symbol to be passed to the route helper
|
99
|
+
# @param attributes [Hash] HTML attributes to be added to the cell tag
|
100
|
+
# @param block [Proc] optional block to alter the cell content
|
101
|
+
# @return [void]
|
102
|
+
#
|
103
|
+
# @example Render a column containing the record's title, linked to its show page
|
104
|
+
# <% row.link :title %> # => <td><a href="/admin/post/15">About us</a></td>
|
105
|
+
# @example Render a column containing the record's title, linked to its edit page
|
106
|
+
# <% row.link :title, url: :edit_admin_post_path do |cell| %>
|
107
|
+
# Edit <%= cell %>
|
108
|
+
# <% end %>
|
109
|
+
# # => <td><a href="/admin/post/15/edit">Edit About us</a></td>
|
110
|
+
def link(method, url: [:admin, @record], link: {}, **attributes, &block)
|
111
|
+
with_column(Body::LinkComponent.new(@table, @record, method, url:, link:, **attributes), &block)
|
33
112
|
end
|
34
113
|
|
35
|
-
|
36
|
-
|
114
|
+
# Generates a column that renders the contents as text.
|
115
|
+
#
|
116
|
+
# @param method [Symbol] the method to call on the record
|
117
|
+
# @param attributes [Hash] HTML attributes to be added to the cell tag
|
118
|
+
# @param block [Proc] optional block to alter the cell content
|
119
|
+
# @return [void]
|
120
|
+
#
|
121
|
+
# @example Render a column containing the record's title
|
122
|
+
# <% row.text :title %> # => <td>About us</td>
|
123
|
+
def text(method, **attributes, &block)
|
124
|
+
with_column(BodyCellComponent.new(@table, @record, method, **attributes), &block)
|
37
125
|
end
|
38
126
|
|
39
|
-
|
40
|
-
|
127
|
+
# Generates a column that renders an ActiveStorage attachment as a downloadable link.
|
128
|
+
#
|
129
|
+
# @param method [Symbol] the method to call on the record
|
130
|
+
# @param variant [Symbol] the variant to use when rendering the image (default :thumb)
|
131
|
+
# @param attributes [Hash] HTML attributes to be added to the cell tag
|
132
|
+
# @param block [Proc] optional block to alter the cell content
|
133
|
+
# @return [void]
|
134
|
+
#
|
135
|
+
# @example Render a column containing a download link to the record's background image
|
136
|
+
# <% row.attachment :background %> # => <td><a href="...">background.png</a></td>
|
137
|
+
def attachment(method, variant: :thumb, **attributes, &block)
|
138
|
+
with_column(Body::AttachmentComponent.new(@table, @record, method, variant:, **attributes), &block)
|
41
139
|
end
|
42
140
|
end
|
43
141
|
end
|
@@ -1,11 +1,61 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
using Katalyst::HtmlAttributes::HasHtmlAttributes
|
4
|
+
|
3
5
|
module Koi
|
4
6
|
module Tables
|
5
7
|
module Header
|
6
8
|
class NumberComponent < HeaderCellComponent
|
7
9
|
def default_html_attributes
|
8
|
-
|
10
|
+
super.merge_html(class: "type-number")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class CurrencyComponent < HeaderCellComponent
|
15
|
+
def default_html_attributes
|
16
|
+
super.merge_html(class: "type-currency")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class BooleanComponent < HeaderCellComponent
|
21
|
+
def default_html_attributes
|
22
|
+
super.merge_html(class: "type-boolean")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class DateComponent < HeaderCellComponent
|
27
|
+
def default_html_attributes
|
28
|
+
super.merge_html(class: "type-date")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class DateTimeComponent < HeaderCellComponent
|
33
|
+
def default_html_attributes
|
34
|
+
super.merge_html(class: "type-datetime")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class LinkComponent < HeaderCellComponent
|
39
|
+
def default_html_attributes
|
40
|
+
super.merge_html(class: "type-link")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class TextComponent < HeaderCellComponent
|
45
|
+
def default_html_attributes
|
46
|
+
super.merge_html(class: "type-text")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class ImageComponent < HeaderCellComponent
|
51
|
+
def default_html_attributes
|
52
|
+
super.merge_html(class: "type-image")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class AttachmentComponent < HeaderCellComponent
|
57
|
+
def default_html_attributes
|
58
|
+
super.merge_html(class: "type-attachment")
|
9
59
|
end
|
10
60
|
end
|
11
61
|
end
|
@@ -1,8 +1,38 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
using Katalyst::HtmlAttributes::HasHtmlAttributes
|
4
|
+
|
3
5
|
module Koi
|
4
6
|
module Tables
|
5
7
|
class HeaderCellComponent < Katalyst::Tables::HeaderCellComponent
|
8
|
+
attr_reader :width
|
9
|
+
|
10
|
+
def initialize(table, attribute, label: nil, link: {}, width: nil, **html_attributes)
|
11
|
+
@width = width
|
12
|
+
|
13
|
+
super(table, attribute, label:, link:, **html_attributes)
|
14
|
+
end
|
15
|
+
|
16
|
+
def default_html_attributes
|
17
|
+
super.merge_html(class: width_class)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def width_class
|
23
|
+
case width
|
24
|
+
when :xs
|
25
|
+
"width-xs"
|
26
|
+
when :s
|
27
|
+
"width-s"
|
28
|
+
when :m
|
29
|
+
"width-m"
|
30
|
+
when :l
|
31
|
+
"width-l"
|
32
|
+
else
|
33
|
+
""
|
34
|
+
end
|
35
|
+
end
|
6
36
|
end
|
7
37
|
end
|
8
38
|
end
|