katalyst-tables 2.6.0.beta → 3.0.0.beta1

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -1
  3. data/README.md +1 -23
  4. data/app/assets/stylesheets/katalyst/tables/_index.scss +1 -0
  5. data/app/assets/stylesheets/katalyst/tables/_ordinal.scss +38 -0
  6. data/app/assets/stylesheets/katalyst/tables/_table.scss +123 -0
  7. data/app/assets/stylesheets/katalyst/tables/typed-columns/_boolean.scss +4 -0
  8. data/app/assets/stylesheets/katalyst/tables/typed-columns/_currency.scss +5 -0
  9. data/app/assets/stylesheets/katalyst/tables/typed-columns/_date.scss +4 -0
  10. data/app/assets/stylesheets/katalyst/tables/typed-columns/_datetime.scss +4 -0
  11. data/app/assets/stylesheets/katalyst/tables/typed-columns/_index.scss +5 -0
  12. data/app/assets/stylesheets/katalyst/tables/typed-columns/_number.scss +5 -0
  13. data/app/components/concerns/katalyst/tables/body/typed_columns.rb +132 -0
  14. data/app/components/concerns/katalyst/tables/has_table_content.rb +1 -1
  15. data/app/components/concerns/katalyst/tables/header/typed_columns.rb +179 -0
  16. data/app/components/concerns/katalyst/tables/orderable.rb +1 -1
  17. data/app/components/concerns/katalyst/tables/selectable.rb +2 -1
  18. data/app/components/katalyst/table_component.rb +13 -1
  19. data/app/components/katalyst/tables/body/attachment_component.rb +58 -0
  20. data/app/components/katalyst/tables/body/boolean_component.rb +14 -0
  21. data/app/components/katalyst/tables/body/currency_component.rb +29 -0
  22. data/app/components/katalyst/tables/body/date_component.rb +64 -0
  23. data/app/components/katalyst/tables/body/date_time_component.rb +57 -0
  24. data/app/components/katalyst/tables/body/link_component.rb +40 -0
  25. data/app/components/katalyst/tables/body/number_component.rb +22 -0
  26. data/app/components/katalyst/tables/body/rich_text_component.rb +18 -0
  27. data/app/components/katalyst/tables/body_cell_component.rb +9 -1
  28. data/app/components/katalyst/tables/body_row_component.rb +9 -2
  29. data/app/components/katalyst/tables/empty_caption_component.rb +1 -1
  30. data/app/components/katalyst/tables/header/attachment_component.rb +15 -0
  31. data/app/components/katalyst/tables/header/boolean_component.rb +15 -0
  32. data/app/components/katalyst/tables/header/currency_component.rb +15 -0
  33. data/app/components/katalyst/tables/header/date_component.rb +15 -0
  34. data/app/components/katalyst/tables/header/date_time_component.rb +15 -0
  35. data/app/components/katalyst/tables/header/link_component.rb +15 -0
  36. data/app/components/katalyst/tables/header/number_component.rb +15 -0
  37. data/app/components/katalyst/tables/header/rich_text_component.rb +15 -0
  38. data/app/components/katalyst/tables/header_cell_component.rb +35 -3
  39. data/app/components/katalyst/tables/header_row_component.rb +3 -2
  40. data/app/components/katalyst/tables/selectable/form_component.html.erb +4 -2
  41. data/app/controllers/concerns/katalyst/tables/backend.rb +2 -2
  42. data/app/helpers/katalyst/tables/frontend.rb +2 -2
  43. data/app/models/concerns/katalyst/tables/collection/pagination.rb +8 -1
  44. data/app/models/concerns/katalyst/tables/collection/sorting.rb +3 -3
  45. data/app/models/katalyst/tables/collection/sort_form.rb +26 -8
  46. data/config/locales/tables.en.yml +6 -0
  47. metadata +33 -9
  48. data/app/components/concerns/katalyst/tables/turbo_replaceable.rb +0 -79
  49. data/app/components/katalyst/turbo/pagy_nav_component.rb +0 -23
  50. data/app/components/katalyst/turbo/table_component.rb +0 -45
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ using Katalyst::HtmlAttributes::HasHtmlAttributes
4
+
5
+ module Katalyst
6
+ module Tables
7
+ module Body
8
+ # Shows an attachment
9
+ #
10
+ # The value is expected to be an ActiveStorage attachment
11
+ #
12
+ # If it is representable, shows as a image tag using a default variant named :thumb.
13
+ #
14
+ # Otherwise shows as a link to download.
15
+ class AttachmentComponent < BodyCellComponent
16
+ def initialize(table, record, attribute, variant: :thumb, **options)
17
+ super(table, record, attribute, **options)
18
+
19
+ @variant = variant
20
+ end
21
+
22
+ def rendered_value
23
+ representation
24
+ end
25
+
26
+ def representation
27
+ if value.try(:variable?) && named_variant.present?
28
+ image_tag(value.variant(@variant))
29
+ elsif value.try(:attached?)
30
+ filename.to_s
31
+ else
32
+ ""
33
+ end
34
+ end
35
+
36
+ def filename
37
+ value.blob.filename
38
+ end
39
+
40
+ # Utility for accessing the path Rails provides for retrieving the
41
+ # attachment for use in cells. Example:
42
+ # <% row.attachment :file do |cell| %>
43
+ # <%= link_to "Download", cell.internal_path %>
44
+ # <% end %>
45
+ def internal_path
46
+ rails_blob_path(value, disposition: :attachment)
47
+ end
48
+
49
+ private
50
+
51
+ # Find the reflective variant by name (i.e. :thumb by default)
52
+ def named_variant
53
+ object.attachment_reflections[@attribute.to_s].named_variants[@variant.to_sym]
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Katalyst
4
+ module Tables
5
+ module Body
6
+ # Shows Yes/No for boolean values
7
+ class BooleanComponent < BodyCellComponent
8
+ def rendered_value
9
+ value ? "Yes" : "No"
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ using Katalyst::HtmlAttributes::HasHtmlAttributes
4
+
5
+ module Katalyst
6
+ module Tables
7
+ module Body
8
+ # Formats the value as a money value
9
+ #
10
+ # The value is expected to be in cents.
11
+ # Adds a class to the cell to allow for custom styling
12
+ class CurrencyComponent < BodyCellComponent
13
+ def initialize(table, record, attribute, options: {}, **html_attributes)
14
+ super(table, record, attribute, **html_attributes)
15
+
16
+ @options = options
17
+ end
18
+
19
+ def rendered_value
20
+ value.present? ? number_to_currency(value / 100.0, @options) : ""
21
+ end
22
+
23
+ def default_html_attributes
24
+ super.merge_html(class: "type-currency")
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Katalyst
4
+ module Tables
5
+ module Body
6
+ # Formats the value as a date
7
+ # @param format [String] date format, defaults to :table
8
+ # @param relative [Boolean] if true, the date may be(if within 5 days) shown as a relative date
9
+ class DateComponent < BodyCellComponent
10
+ def initialize(table, record, attribute, format: :table, relative: true, **options)
11
+ super(table, record, attribute, **options)
12
+
13
+ @format = format
14
+ @relative = relative
15
+ end
16
+
17
+ def value
18
+ super&.to_date
19
+ end
20
+
21
+ def rendered_value
22
+ @relative ? relative_time : absolute_time
23
+ end
24
+
25
+ private
26
+
27
+ def absolute_time
28
+ value.present? ? I18n.l(value, format: @format) : ""
29
+ end
30
+
31
+ def relative_time
32
+ if value.blank?
33
+ ""
34
+ else
35
+ days_ago_in_words(value)&.capitalize || absolute_time
36
+ end
37
+ end
38
+
39
+ def default_html_attributes
40
+ @relative && value.present? && days_ago_in_words(value).present? ? { title: absolute_time } : {}
41
+ end
42
+
43
+ def days_ago_in_words(value)
44
+ from_time = value.to_time
45
+ to_time = Date.current.to_time
46
+ distance_in_days = ((to_time - from_time) / (24.0 * 60.0 * 60.0)).round
47
+
48
+ case distance_in_days
49
+ when 0
50
+ "today"
51
+ when 1
52
+ "yesterday"
53
+ when -1
54
+ "tomorrow"
55
+ when 2..5
56
+ "#{distance_in_days} days ago"
57
+ when -5..-2
58
+ "#{distance_in_days.abs} days from now"
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Katalyst
4
+ module Tables
5
+ module Body
6
+ # Formats the value as a datetime
7
+ # @param format [String] datetime format, defaults to :admin
8
+ # @param relative [Boolean] if true, the datetime may be(if today) shown as a relative date/time
9
+ class DateTimeComponent < BodyCellComponent
10
+ include ActionView::Helpers::DateHelper
11
+
12
+ def initialize(table, record, attribute, format: :table, relative: true, **options)
13
+ super(table, record, attribute, **options)
14
+
15
+ @format = format
16
+ @relative = relative
17
+ end
18
+
19
+ def value
20
+ super&.to_datetime
21
+ end
22
+
23
+ def rendered_value
24
+ @relative ? relative_time : absolute_time
25
+ end
26
+
27
+ private
28
+
29
+ def absolute_time
30
+ value.present? ? I18n.l(value, format: @format) : ""
31
+ end
32
+
33
+ def today?
34
+ value&.to_date == Date.current
35
+ end
36
+
37
+ def relative_time
38
+ return "" if value.blank?
39
+
40
+ if today?
41
+ if value > DateTime.current
42
+ "#{distance_of_time_in_words(value, DateTime.current)} from now".capitalize
43
+ else
44
+ "#{distance_of_time_in_words(value, DateTime.current)} ago".capitalize
45
+ end
46
+ else
47
+ absolute_time
48
+ end
49
+ end
50
+
51
+ def default_html_attributes
52
+ @relative && today? ? { title: absolute_time } : {}
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ using Katalyst::HtmlAttributes::HasHtmlAttributes
4
+
5
+ module Katalyst
6
+ module Tables
7
+ module Body
8
+ # Displays a link to the record
9
+ # The link text is the value of the attribute
10
+ # @see Koi::Tables::BodyRowComponent#link
11
+ class LinkComponent < BodyCellComponent
12
+ def initialize(table, record, attribute, url:, link: {}, **options)
13
+ super(table, record, attribute, **options)
14
+
15
+ @url = url
16
+ @link_options = link
17
+ end
18
+
19
+ def call
20
+ content # ensure content is set before rendering options
21
+
22
+ link = content.present? && url.present? ? link_to(content, url, @link_options) : content.to_s
23
+ content_tag(@type, link, **html_attributes)
24
+ end
25
+
26
+ def url
27
+ case @url
28
+ when Symbol
29
+ # helpers are not available until the component is rendered
30
+ @url = helpers.public_send(@url, record)
31
+ when Proc
32
+ @url = @url.call(record)
33
+ else
34
+ @url
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ using Katalyst::HtmlAttributes::HasHtmlAttributes
4
+
5
+ module Katalyst
6
+ module Tables
7
+ module Body
8
+ # Formats the value as a number
9
+ #
10
+ # Adds a class to the cell to allow for custom styling
11
+ class NumberComponent < BodyCellComponent
12
+ def rendered_value
13
+ value.present? ? number_to_human(value) : ""
14
+ end
15
+
16
+ def default_html_attributes
17
+ super.merge_html(class: "type-number")
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ using Katalyst::HtmlAttributes::HasHtmlAttributes
4
+
5
+ module Katalyst
6
+ module Tables
7
+ module Body
8
+ # Displays the plain text for rich text content
9
+ #
10
+ # Adds a title attribute to allow for hover over display of the full content
11
+ class RichTextComponent < BodyCellComponent
12
+ def default_html_attributes
13
+ { title: value.to_plain_text }
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -18,7 +18,7 @@ module Katalyst
18
18
 
19
19
  def before_render
20
20
  # fallback if no content block is given
21
- with_content(value.to_s) unless content?
21
+ with_content(rendered_value) unless content?
22
22
  end
23
23
 
24
24
  def call
@@ -36,6 +36,14 @@ module Katalyst
36
36
  @record.public_send(@attribute)
37
37
  end
38
38
 
39
+ def rendered_value
40
+ value.to_s
41
+ end
42
+
43
+ def to_s
44
+ value.to_s
45
+ end
46
+
39
47
  def inspect
40
48
  "#<#{self.class.name} attribute: #{@attribute.inspect}, value: #{value.inspect}>"
41
49
  end
@@ -4,6 +4,7 @@ module Katalyst
4
4
  module Tables
5
5
  class BodyRowComponent < ViewComponent::Base # :nodoc:
6
6
  include Katalyst::HtmlAttributes
7
+ include Body::TypedColumns
7
8
 
8
9
  renders_many :columns, ->(component) { component }
9
10
 
@@ -24,8 +25,8 @@ module Katalyst
24
25
  end
25
26
  end
26
27
 
27
- def cell(attribute, **options, &block)
28
- with_column(@table.body_cell_component.new(@table, @record, attribute, **options), &block)
28
+ def cell(attribute, **, &)
29
+ with_column(@table.body_cell_component.new(@table, @record, attribute, **), &)
29
30
  end
30
31
 
31
32
  def header?
@@ -36,6 +37,12 @@ module Katalyst
36
37
  true
37
38
  end
38
39
 
40
+ def default_html_attributes
41
+ return {} unless @table.generate_ids?
42
+
43
+ { id: dom_id(@record) }
44
+ end
45
+
39
46
  def inspect
40
47
  "#<#{self.class.name} record: #{record.inspect}>"
41
48
  end
@@ -24,7 +24,7 @@ module Katalyst
24
24
  end
25
25
 
26
26
  def plural_human_model_name
27
- human = @table.model_name&.human || @table.object_name.to_s.humanize
27
+ human = @table.model_name&.human || @table.object_name&.to_s&.humanize || "record"
28
28
  human.pluralize.downcase
29
29
  end
30
30
 
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ using Katalyst::HtmlAttributes::HasHtmlAttributes
4
+
5
+ module Katalyst
6
+ module Tables
7
+ module Header
8
+ class AttachmentComponent < HeaderCellComponent
9
+ def default_html_attributes
10
+ super.merge_html(class: "type-attachment")
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ using Katalyst::HtmlAttributes::HasHtmlAttributes
4
+
5
+ module Katalyst
6
+ module Tables
7
+ module Header
8
+ class BooleanComponent < HeaderCellComponent
9
+ def default_html_attributes
10
+ super.merge_html(class: "type-boolean")
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ using Katalyst::HtmlAttributes::HasHtmlAttributes
4
+
5
+ module Katalyst
6
+ module Tables
7
+ module Header
8
+ class CurrencyComponent < HeaderCellComponent
9
+ def default_html_attributes
10
+ super.merge_html(class: "type-currency")
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ using Katalyst::HtmlAttributes::HasHtmlAttributes
4
+
5
+ module Katalyst
6
+ module Tables
7
+ module Header
8
+ class DateComponent < HeaderCellComponent
9
+ def default_html_attributes
10
+ super.merge_html(class: "type-date")
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ using Katalyst::HtmlAttributes::HasHtmlAttributes
4
+
5
+ module Katalyst
6
+ module Tables
7
+ module Header
8
+ class DateTimeComponent < HeaderCellComponent
9
+ def default_html_attributes
10
+ super.merge_html(class: "type-datetime")
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ using Katalyst::HtmlAttributes::HasHtmlAttributes
4
+
5
+ module Katalyst
6
+ module Tables
7
+ module Header
8
+ class LinkComponent < HeaderCellComponent
9
+ def default_html_attributes
10
+ super.merge_html(class: "type-link")
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ using Katalyst::HtmlAttributes::HasHtmlAttributes
4
+
5
+ module Katalyst
6
+ module Tables
7
+ module Header
8
+ class NumberComponent < HeaderCellComponent
9
+ def default_html_attributes
10
+ super.merge_html(class: "type-number")
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ using Katalyst::HtmlAttributes::HasHtmlAttributes
4
+
5
+ module Katalyst
6
+ module Tables
7
+ module Header
8
+ class RichTextComponent < HeaderCellComponent
9
+ def default_html_attributes
10
+ super.merge_html(class: "type-rich-text")
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ using Katalyst::HtmlAttributes::HasHtmlAttributes
4
+
3
5
  module Katalyst
4
6
  module Tables
5
7
  class HeaderCellComponent < ViewComponent::Base # :nodoc:
@@ -9,19 +11,20 @@ module Katalyst
9
11
 
10
12
  delegate :object_name, :collection, :sorting, to: :@table
11
13
 
12
- def initialize(table, attribute, label: nil, link: {}, **html_attributes)
14
+ def initialize(table, attribute, label: nil, link: {}, width: nil, **html_attributes)
13
15
  super(**html_attributes)
14
16
 
15
17
  @table = table
16
18
  @attribute = attribute
17
19
  @value = label
20
+ @width = width
18
21
  @link_attributes = link
19
22
  end
20
23
 
21
24
  def call
22
25
  tag.th(**html_attributes) do
23
26
  if sortable?(@attribute)
24
- link_to(value, sort_url(@attribute), **@link_attributes)
27
+ link_to(value, sort_url(@attribute), **link_attributes)
25
28
  else
26
29
  value
27
30
  end
@@ -47,7 +50,7 @@ module Katalyst
47
50
  end
48
51
 
49
52
  def inspect
50
- "#<#{self.class.name} attribute: #{@attribute.inspect}, value: #{value.inspect}>"
53
+ "#<#{self.class.name} attribute: #{@attribute.inspect}, value: #{@value.inspect}>"
51
54
  end
52
55
 
53
56
  # Backwards compatibility with tables 1.0
@@ -55,7 +58,36 @@ module Katalyst
55
58
 
56
59
  private
57
60
 
61
+ def width_class
62
+ case @width
63
+ when :xs
64
+ "width-xs"
65
+ when :s
66
+ "width-s"
67
+ when :m
68
+ "width-m"
69
+ when :l
70
+ "width-l"
71
+ else
72
+ ""
73
+ end
74
+ end
75
+
76
+ def link_attributes
77
+ { data: { turbo_action: "replace" } }.merge_html(@link_attributes)
78
+ end
79
+
58
80
  def default_html_attributes
81
+ sort_data.merge(width_data)
82
+ end
83
+
84
+ def width_data
85
+ return {} unless @width
86
+
87
+ { class: width_class }
88
+ end
89
+
90
+ def sort_data
59
91
  return {} unless sorting&.supports?(collection, @attribute)
60
92
 
61
93
  { data: { sort: sorting.status(@attribute) } }
@@ -4,6 +4,7 @@ module Katalyst
4
4
  module Tables
5
5
  class HeaderRowComponent < ViewComponent::Base # :nodoc:
6
6
  include Katalyst::HtmlAttributes
7
+ include Header::TypedColumns
7
8
 
8
9
  renders_many :columns, ->(component) { component }
9
10
 
@@ -24,8 +25,8 @@ module Katalyst
24
25
  end
25
26
  end
26
27
 
27
- def cell(attribute, **options, &block)
28
- with_column(@table.header_cell_component.new(@table, attribute, link: @link_attributes, **options), &block)
28
+ def cell(attribute, **, &)
29
+ with_column(@table.header_cell_component.new(@table, attribute, link: @link_attributes, **), &)
29
30
  end
30
31
 
31
32
  def header?
@@ -1,7 +1,9 @@
1
1
  <%= form_with(method: :patch,
2
- id: id,
2
+ id:,
3
3
  class: "tables--selection--form",
4
- data: { controller: form_controller },
4
+ data: { controller: form_controller,
5
+ turbo_action: "replace",
6
+ turbo_permanent: "" },
5
7
  html: { hidden: "" }) do |form| %>
6
8
  <p class="tables--selection--summary">
7
9
  <span data-<%= form_target("count") %>>0</span>
@@ -21,8 +21,8 @@ module Katalyst
21
21
  column, direction = params[:sort]&.split
22
22
  direction = "asc" unless SortForm::DIRECTIONS.include?(direction)
23
23
 
24
- SortForm.new(column: column,
25
- direction: direction)
24
+ SortForm.new(column:,
25
+ direction:)
26
26
  .apply(collection)
27
27
  end
28
28
 
@@ -4,9 +4,9 @@ module Katalyst
4
4
  module Tables
5
5
  # View Helper for generating HTML tables. Include in your ApplicationHelper, or similar.
6
6
  module Frontend
7
- def table_with(collection:, component: nil, **options, &block)
7
+ def table_with(collection:, component: nil, **, &)
8
8
  component ||= default_table_component_class
9
- render(component.new(collection: collection, **options), &block)
9
+ render(component.new(collection:, **), &)
10
10
  end
11
11
 
12
12
  private
@@ -35,7 +35,14 @@ module Katalyst
35
35
  end
36
36
 
37
37
  def paginate_options
38
- @paginate.is_a?(Hash) ? @paginate : {}
38
+ opts = @paginate.is_a?(Hash) ? @paginate : {}
39
+ opts = opts.dup
40
+ opts[:anchor_string] ||= anchor_string
41
+ opts
42
+ end
43
+
44
+ def anchor_string
45
+ "data-turbo-action=\"replace\""
39
46
  end
40
47
 
41
48
  class Paginate # :nodoc:
@@ -22,16 +22,16 @@ module Katalyst
22
22
  end
23
23
 
24
24
  def initialize(sorting: config.sorting, **options)
25
- @sorting = SortForm.parse(sorting) if sorting
25
+ @sorting = SortForm.parse(sorting, default: sorting) if sorting
26
26
 
27
- super(sort: sorting, **options) # set default sort based on config
27
+ super(sort: @sorting.to_param, **options) # set default sort based on config
28
28
  end
29
29
 
30
30
  def sort=(value)
31
31
  return unless @sorting
32
32
 
33
33
  # update internal proxy
34
- @sorting = SortForm.parse(value, default: attribute_was(:sort))
34
+ @sorting = SortForm.parse(value, default: @sorting.default)
35
35
 
36
36
  # update attribute based on normalized value
37
37
  super(@sorting.to_param)