katalyst-tables 3.0.0.beta1 → 3.0.0

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -2
  3. data/README.md +56 -190
  4. data/app/assets/builds/katalyst/tables.esm.js +17 -47
  5. data/app/assets/builds/katalyst/tables.js +17 -47
  6. data/app/assets/builds/katalyst/tables.min.js +1 -1
  7. data/app/assets/builds/katalyst/tables.min.js.map +1 -1
  8. data/app/components/concerns/katalyst/tables/has_table_content.rb +17 -8
  9. data/app/components/concerns/katalyst/tables/identifiable.rb +51 -0
  10. data/app/components/concerns/katalyst/tables/orderable.rb +35 -105
  11. data/app/components/concerns/katalyst/tables/selectable.rb +18 -75
  12. data/app/components/concerns/katalyst/tables/sortable.rb +51 -17
  13. data/app/components/katalyst/table_component.html.erb +4 -4
  14. data/app/components/katalyst/table_component.rb +271 -53
  15. data/app/components/katalyst/tables/body_row_component.html.erb +5 -0
  16. data/app/components/katalyst/tables/body_row_component.rb +4 -31
  17. data/app/components/katalyst/tables/cell_component.rb +85 -0
  18. data/app/components/katalyst/tables/{body → cells}/boolean_component.rb +8 -2
  19. data/app/components/katalyst/tables/{body → cells}/currency_component.rb +7 -7
  20. data/app/components/katalyst/tables/{body → cells}/date_component.rb +12 -9
  21. data/app/components/katalyst/tables/{body → cells}/date_time_component.rb +13 -10
  22. data/app/components/katalyst/tables/{body → cells}/number_component.rb +5 -5
  23. data/app/components/katalyst/tables/cells/ordinal_component.rb +44 -0
  24. data/app/components/katalyst/tables/{body → cells}/rich_text_component.rb +8 -5
  25. data/app/components/katalyst/tables/cells/select_component.rb +39 -0
  26. data/app/components/katalyst/tables/data.rb +30 -0
  27. data/app/components/katalyst/tables/header_row_component.html.erb +5 -0
  28. data/app/components/katalyst/tables/header_row_component.rb +4 -25
  29. data/app/components/katalyst/tables/label.rb +37 -0
  30. data/app/components/katalyst/tables/orderable/form_component.rb +38 -0
  31. data/app/components/katalyst/tables/selectable/form_component.html.erb +3 -3
  32. data/app/components/katalyst/tables/selectable/form_component.rb +8 -11
  33. data/app/controllers/concerns/katalyst/tables/backend.rb +2 -28
  34. data/app/helpers/katalyst/tables/frontend.rb +48 -2
  35. data/app/javascript/tables/application.js +0 -5
  36. data/app/javascript/tables/orderable/form_controller.js +8 -6
  37. data/app/javascript/tables/orderable/item_controller.js +9 -0
  38. data/app/models/concerns/katalyst/tables/collection/core.rb +6 -1
  39. data/app/models/concerns/katalyst/tables/collection/sorting.rb +85 -17
  40. data/app/models/katalyst/tables/collection/array.rb +38 -0
  41. data/app/models/katalyst/tables/collection/base.rb +4 -0
  42. data/config/locales/tables.en.yml +0 -6
  43. data/lib/katalyst/tables/config.rb +23 -0
  44. data/lib/katalyst/tables.rb +9 -0
  45. metadata +22 -29
  46. data/app/components/concerns/katalyst/tables/body/typed_columns.rb +0 -132
  47. data/app/components/concerns/katalyst/tables/configurable_component.rb +0 -52
  48. data/app/components/concerns/katalyst/tables/header/typed_columns.rb +0 -179
  49. data/app/components/katalyst/tables/body/attachment_component.rb +0 -58
  50. data/app/components/katalyst/tables/body/link_component.rb +0 -40
  51. data/app/components/katalyst/tables/body_cell_component.rb +0 -55
  52. data/app/components/katalyst/tables/header/attachment_component.rb +0 -15
  53. data/app/components/katalyst/tables/header/boolean_component.rb +0 -15
  54. data/app/components/katalyst/tables/header/currency_component.rb +0 -15
  55. data/app/components/katalyst/tables/header/date_component.rb +0 -15
  56. data/app/components/katalyst/tables/header/date_time_component.rb +0 -15
  57. data/app/components/katalyst/tables/header/link_component.rb +0 -15
  58. data/app/components/katalyst/tables/header/number_component.rb +0 -15
  59. data/app/components/katalyst/tables/header/rich_text_component.rb +0 -15
  60. data/app/components/katalyst/tables/header_cell_component.rb +0 -97
  61. data/app/helpers/katalyst/tables/frontend/helper.rb +0 -31
  62. data/app/javascript/tables/turbo/collection_controller.js +0 -38
  63. data/app/models/katalyst/tables/collection/sort_form.rb +0 -120
@@ -1,20 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- using Katalyst::HtmlAttributes::HasHtmlAttributes
4
-
5
3
  module Katalyst
6
4
  module Tables
7
- module Body
5
+ module Cells
8
6
  # Formats the value as a number
9
7
  #
10
8
  # Adds a class to the cell to allow for custom styling
11
- class NumberComponent < BodyCellComponent
9
+ class NumberComponent < CellComponent
12
10
  def rendered_value
13
11
  value.present? ? number_to_human(value) : ""
14
12
  end
15
13
 
14
+ private
15
+
16
16
  def default_html_attributes
17
- super.merge_html(class: "type-number")
17
+ { class: "type-number" }
18
18
  end
19
19
  end
20
20
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Katalyst
4
+ module Tables
5
+ module Cells
6
+ class OrdinalComponent < CellComponent
7
+ def initialize(primary_key:, **)
8
+ super(**)
9
+
10
+ @primary_key = primary_key
11
+ end
12
+
13
+ def rendered_value
14
+ t("katalyst.tables.orderable.value")
15
+ end
16
+
17
+ private
18
+
19
+ def default_html_attributes
20
+ if @row.header?
21
+ { class: "ordinal" }
22
+ else
23
+ {
24
+ class: "ordinal",
25
+ data: {
26
+ controller: Orderable::ITEM_CONTROLLER,
27
+ "#{Orderable::ITEM_CONTROLLER}-params-value": params.to_json,
28
+ },
29
+ }
30
+ end
31
+ end
32
+
33
+ def params
34
+ {
35
+ id_name: @primary_key,
36
+ id_value: record.public_send(@primary_key),
37
+ index_name: column,
38
+ index_value: record.public_send(column),
39
+ }
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -1,16 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- using Katalyst::HtmlAttributes::HasHtmlAttributes
4
-
5
3
  module Katalyst
6
4
  module Tables
7
- module Body
5
+ module Cells
8
6
  # Displays the plain text for rich text content
9
7
  #
10
8
  # Adds a title attribute to allow for hover over display of the full content
11
- class RichTextComponent < BodyCellComponent
9
+ class RichTextComponent < CellComponent
10
+ private
11
+
12
12
  def default_html_attributes
13
- { title: value.to_plain_text }
13
+ {
14
+ class: "type-rich-text",
15
+ title: (value.to_plain_text unless row.header?),
16
+ }
14
17
  end
15
18
  end
16
19
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Katalyst
4
+ module Tables
5
+ module Cells
6
+ class SelectComponent < CellComponent
7
+ def initialize(params:, form_id:, **)
8
+ super(**)
9
+
10
+ @params = params
11
+ @form_id = form_id
12
+ end
13
+
14
+ def rendered_value
15
+ tag.input(type: :checkbox)
16
+ end
17
+
18
+ private
19
+
20
+ def default_html_attributes
21
+ if @row.header?
22
+ { class: "selection" }
23
+ else
24
+ {
25
+ class: "selection",
26
+ data: {
27
+ controller: Selectable::ITEM_CONTROLLER,
28
+ "#{Selectable::ITEM_CONTROLLER}-params-value" => @params.to_json,
29
+ "#{Selectable::ITEM_CONTROLLER}-#{Selectable::FORM_CONTROLLER}-outlet" => "##{@form_id}",
30
+ action: "change->#{Selectable::ITEM_CONTROLLER}#change",
31
+ turbo_permanent: "",
32
+ },
33
+ }
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Katalyst
4
+ module Tables
5
+ class Data
6
+ def initialize(record:, column:)
7
+ @record = record
8
+ @column = column
9
+ end
10
+
11
+ def value
12
+ return @value if defined?(@value)
13
+
14
+ @value = @record&.public_send(@column)
15
+ end
16
+
17
+ def call
18
+ ActionView::OutputBuffer.new.tap do |output|
19
+ output << value.to_s
20
+ end.to_s
21
+ end
22
+
23
+ alias to_s call
24
+
25
+ def inspect
26
+ "#<#{self.class.name} column: #{@column.inspect}, value: #{value.inspect}>"
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,5 @@
1
+ <%= tag.tr(**html_attributes) do %>
2
+ <% cells.each do |cell| %>
3
+ <%= cell %>
4
+ <% end %>
5
+ <% end %>
@@ -4,29 +4,11 @@ module Katalyst
4
4
  module Tables
5
5
  class HeaderRowComponent < ViewComponent::Base # :nodoc:
6
6
  include Katalyst::HtmlAttributes
7
- include Header::TypedColumns
8
7
 
9
- renders_many :columns, ->(component) { component }
8
+ renders_many :cells, ->(cell) { cell }
10
9
 
11
- def initialize(table, link: {})
12
- super()
13
-
14
- @table = table
15
- @link_attributes = link
16
- end
17
-
18
- def call
19
- content # generate content before rendering
20
-
21
- tag.tr(**html_attributes) do
22
- columns.each do |column|
23
- concat(column.to_s)
24
- end
25
- end
26
- end
27
-
28
- def cell(attribute, **, &)
29
- with_column(@table.header_cell_component.new(@table, attribute, link: @link_attributes, **), &)
10
+ def before_render
11
+ content # ensure content is rendered so html_attributes can be set
30
12
  end
31
13
 
32
14
  def header?
@@ -38,11 +20,8 @@ module Katalyst
38
20
  end
39
21
 
40
22
  def inspect
41
- "#<#{self.class.name} link_attributes: #{@link_attributes.inspect}>"
23
+ "#<#{self.class.name}>"
42
24
  end
43
-
44
- # Backwards compatibility with tables 1.0
45
- alias_method :options, :html_attributes=
46
25
  end
47
26
  end
48
27
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Katalyst
4
+ module Tables
5
+ class Label
6
+ def initialize(collection:, column:, label: nil)
7
+ @collection = collection
8
+ @column = column
9
+ @label = label
10
+ end
11
+
12
+ def value
13
+ return @value if defined?(@value)
14
+
15
+ @value = if !@label.nil?
16
+ @label
17
+ elsif @collection.model.present?
18
+ @collection.model.human_attribute_name(@column)
19
+ else
20
+ @column.to_s.humanize.capitalize
21
+ end
22
+ end
23
+
24
+ def call
25
+ ActionView::OutputBuffer.new.tap do |output|
26
+ output << value.to_s
27
+ end.to_s
28
+ end
29
+
30
+ alias to_s call
31
+
32
+ def inspect
33
+ "#<#{self.class.name} column: #{@column.inspect} value: #{value.inspect}>"
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Katalyst
4
+ module Tables
5
+ module Orderable
6
+ class FormComponent < ViewComponent::Base # :nodoc:
7
+ include Katalyst::Tables::Identifiable::Defaults
8
+
9
+ attr_reader :id, :url
10
+
11
+ # @param collection [Katalyst::Tables::Collection::Core] the collection to render
12
+ # @param url [String] the url to submit the form to (e.g. <resources>_order_path)
13
+ # @param id [String] the id of the form element (defaults to <resources>_order_form)
14
+ # @param scope [String] the base scope to use for form inputs (defaults to order[<resources>])
15
+ def initialize(collection:, url:, id: nil, scope: nil)
16
+ super
17
+
18
+ @id = id || Orderable.default_form_id(collection)
19
+ @url = url
20
+ @scope = scope || Orderable.default_scope(collection)
21
+ end
22
+
23
+ def call
24
+ form_with(id:, url:, method: :patch, data: {
25
+ controller: FORM_CONTROLLER,
26
+ "#{FORM_CONTROLLER}-scope-value": @scope,
27
+ }) do |form|
28
+ form.button(hidden: "")
29
+ end
30
+ end
31
+
32
+ def inspect
33
+ "#<#{self.class.name} id: #{id.inspect}, url: #{url.inspect}>"
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -4,11 +4,11 @@
4
4
  data: { controller: form_controller,
5
5
  turbo_action: "replace",
6
6
  turbo_permanent: "" },
7
- html: { hidden: "" }) do |form| %>
7
+ html: { action: false, hidden: "" }) do |form| %>
8
8
  <p class="tables--selection--summary">
9
9
  <span data-<%= form_target("count") %>>0</span>
10
- <span data-<%= form_target("singular") %> hidden><%= @table.collection.model_name.singular %></span>
11
- <span data-<%= form_target("plural") %>><%= @table.collection.model_name.plural %></span>
10
+ <span data-<%= form_target("singular") %> hidden><%= @collection.model_name.singular %></span>
11
+ <span data-<%= form_target("plural") %>><%= @collection.model_name.plural %></span>
12
12
  selected
13
13
  </p>
14
14
  <%= content %>
@@ -4,24 +4,21 @@ module Katalyst
4
4
  module Tables
5
5
  module Selectable
6
6
  class FormComponent < ViewComponent::Base # :nodoc:
7
+ include Katalyst::Tables::Identifiable::Defaults
8
+
7
9
  attr_reader :id, :primary_key
8
10
 
9
- def initialize(table:,
11
+ # @param collection [Katalyst::Tables::Collection::Core] the collection to render
12
+ # @param id [String] the id of the form element (defaults to <resources>_selection_form)
13
+ # @param primary_key [String] the primary key of the record in the collection (defaults to :id)
14
+ def initialize(collection:,
10
15
  id: nil,
11
16
  primary_key: :id)
12
17
  super
13
18
 
14
- @table = table
15
- @id = id
19
+ @collection = collection
20
+ @id = id || Selectable.default_form_id(collection)
16
21
  @primary_key = primary_key
17
-
18
- if @id.nil?
19
- table_id = table.try(:id)
20
-
21
- raise ArgumentError, "Table selection requires an id" if table_id.nil?
22
-
23
- @id = "#{table_id}_selection"
24
- end
25
22
  end
26
23
 
27
24
  def inspect
@@ -2,35 +2,10 @@
2
2
 
3
3
  module Katalyst
4
4
  module Tables
5
- # Utilities for controllers that are generating collections for visualisation
6
- # in a table view using Katalyst::Tables::Frontend.
7
- #
8
- # Provides `table_sort` for sorting based on column interactions (sort param).
5
+ # Configuration for controllers to specify which TableComponent should be used in associated views.
9
6
  module Backend
10
7
  extend ActiveSupport::Concern
11
8
 
12
- # @deprecated backwards compatibility
13
- class SortForm < Katalyst::Tables::Collection::SortForm
14
- end
15
-
16
- # Sort the given collection by params[:sort], which is set when a user
17
- # interacts with a column header in a frontend table view.
18
- #
19
- # @return [[SortForm, ActiveRecord::Relation]]
20
- def table_sort(collection)
21
- column, direction = params[:sort]&.split
22
- direction = "asc" unless SortForm::DIRECTIONS.include?(direction)
23
-
24
- SortForm.new(column:,
25
- direction:)
26
- .apply(collection)
27
- end
28
-
29
- def self_referred?
30
- request.referer.present? && URI.parse(request.referer).path == request.path
31
- end
32
- alias self_refered? self_referred?
33
-
34
9
  included do
35
10
  class_attribute :_default_table_component, instance_accessor: false
36
11
  end
@@ -39,8 +14,7 @@ module Katalyst
39
14
  # Set the table component to be used as the default for all tables
40
15
  # in the views rendered by this controller and its subclasses.
41
16
  #
42
- # ==== Parameters
43
- # * <tt>component</tt> - Default table component, an instance of +Katalyst::TableComponent+
17
+ # @param component [Class] the table component class to use
44
18
  def default_table_component(component)
45
19
  self._default_table_component = component
46
20
  end
@@ -4,9 +4,55 @@ 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, **, &)
7
+ # Construct a new table component. This entry point supports a large number
8
+ # of options for customizing the table. The most common options are:
9
+ # @param collection [Katalyst::Tables::Collection::Core] the collection to render
10
+ # @param header [Boolean] whether to render the header row (defaults to true, supports options)
11
+ # @param caption [Boolean Hash] whether to render the caption (defaults to true, supports options)
12
+ # @param generate_ids [Boolean] whether to generate dom ids for the table and rows
13
+ #
14
+ # Blocks will receive the table in row-rendering mode (with row and record defined):
15
+ # @yieldparam [Katalyst::TableComponent] the row component to render
16
+ # @yieldparam [Object, nil] the object to render, or nil for header rows
17
+ #
18
+ # If no block is provided when the table is rendered then the table will look for a row partial:
19
+ # @param object_name [Symbol] the name of the object to use for partial rendering
20
+ # (defaults to collection.model_name.i18n_key)
21
+ # @param partial [String] the name of the partial to use for rendering each row
22
+ # (defaults to to_partial_path on the object)
23
+ # @param as [Symbol] the name of the local variable to use for rendering each row
24
+ # (defaults to collection.model_name.param_key)
25
+ #
26
+ # In addition to these options, standard HTML attributes can be passed which will be added to the table tag.
27
+ def table_with(collection:,
28
+ component: nil,
29
+ header: true,
30
+ caption: true,
31
+ generate_ids: false,
32
+ object_name: nil,
33
+ partial: nil,
34
+ as: nil,
35
+ **,
36
+ &)
8
37
  component ||= default_table_component_class
9
- render(component.new(collection:, **), &)
38
+ component = component.new(collection:, header:, caption:, generate_ids:, object_name:, partial:, as:, **)
39
+
40
+ render(component, &)
41
+ end
42
+
43
+ # @param collection [Katalyst::Tables::Collection::Core] the collection to render
44
+ # @param url [String] the url to submit the form to (e.g. <resources>_order_path)
45
+ # @param id [String] the id of the form element (defaults to <resources>_order_form)
46
+ # @param scope [String] the base scope to use for form inputs (defaults to order[<resources>])
47
+ def table_orderable_with(collection:, url:, id: nil, scope: nil, &)
48
+ render(Orderable::FormComponent.new(collection:, url:, id:, scope:))
49
+ end
50
+
51
+ # @param collection [Katalyst::Tables::Collection::Core] the collection to render
52
+ # @param id [String] the id of the form element (defaults to <resources>_selection_form)
53
+ # @param primary_key [String] the primary key of the record in the collection (defaults to :id)
54
+ def table_selection_with(collection:, id: nil, primary_key: :id, &)
55
+ render(Selectable::FormComponent.new(collection:, id:, primary_key:), &)
10
56
  end
11
57
 
12
58
  private
@@ -1,4 +1,3 @@
1
- import TurboCollectionController from "./turbo/collection_controller";
2
1
  import OrderableItemController from "./orderable/item_controller";
3
2
  import OrderableListController from "./orderable/list_controller";
4
3
  import OrderableFormController from "./orderable/form_controller";
@@ -6,10 +5,6 @@ import SelectionFormController from "./selection/form_controller";
6
5
  import SelectionItemController from "./selection/item_controller";
7
6
 
8
7
  const Definitions = [
9
- {
10
- identifier: "tables--turbo--collection",
11
- controllerConstructor: TurboCollectionController,
12
- },
13
8
  {
14
9
  identifier: "tables--orderable--item",
15
10
  controllerConstructor: OrderableItemController,
@@ -1,13 +1,15 @@
1
1
  import { Controller } from "@hotwired/stimulus";
2
2
 
3
3
  export default class OrderableFormController extends Controller {
4
+ static values = { scope: String };
5
+
4
6
  add(item) {
5
- const { id_name, id_value, index_name } = item.paramsValue;
6
- this.element.insertAdjacentHTML(
7
- "beforeend",
8
- `<input type="hidden" name="${id_name}" value="${id_value}" data-generated>
9
- <input type="hidden" name="${index_name}" value="${item.index}" data-generated>`,
10
- );
7
+ item.params(this.scopeValue).forEach(({ name, value }) => {
8
+ this.element.insertAdjacentHTML(
9
+ "beforeend",
10
+ `<input type="hidden" name="${name}" value="${value}" data-generated>`,
11
+ );
12
+ });
11
13
  }
12
14
 
13
15
  submit() {
@@ -48,6 +48,15 @@ export default class OrderableRowController extends Controller {
48
48
  this.index = index;
49
49
  }
50
50
 
51
+ /** Retrieve params for use in the form */
52
+ params(scope) {
53
+ const { id_name, id_value, index_name } = this.paramsValue;
54
+ return [
55
+ { name: `${scope}[${id_value}][${id_name}]`, value: this.id },
56
+ { name: `${scope}[${id_value}][${index_name}]`, value: this.index },
57
+ ];
58
+ }
59
+
51
60
  /**
52
61
  * Restore any visual changes made during drag and remove the drag state.
53
62
  */
@@ -18,7 +18,7 @@ module Katalyst
18
18
  def permitted_params
19
19
  _default_attributes.to_h.each_with_object([]) do |(k, v), h|
20
20
  h << case v
21
- when Array
21
+ when ::Array
22
22
  { k => [] }
23
23
  else
24
24
  k
@@ -39,6 +39,11 @@ module Katalyst
39
39
  clear_changes_information
40
40
  end
41
41
 
42
+ # Collections that do not include Sorting are never sortable.
43
+ def sortable?
44
+ false
45
+ end
46
+
42
47
  def apply(items)
43
48
  @items = items
44
49
  reducers.build do |_|
@@ -14,27 +14,88 @@ module Katalyst
14
14
  module Sorting
15
15
  extend ActiveSupport::Concern
16
16
 
17
- included do
18
- config_accessor :sorting
19
- attr_accessor :sorting
17
+ DIRECTIONS = %w[asc desc].freeze
18
+
19
+ module SortParams
20
+ refine Hash do
21
+ def to_param
22
+ "#{self[:column]} #{self[:direction]}"
23
+ end
24
+ end
25
+
26
+ refine String do
27
+ def to_param
28
+ to_h.to_param
29
+ end
20
30
 
31
+ def to_h
32
+ column, direction = split(/[ +]/, 2)
33
+
34
+ direction = "asc" unless DIRECTIONS.include?(direction)
35
+ { column:, direction: }
36
+ end
37
+ end
38
+ end
39
+
40
+ using SortParams
41
+
42
+ included do
21
43
  attribute :sort, :string
44
+
45
+ attr_reader :default_sort
22
46
  end
23
47
 
24
48
  def initialize(sorting: config.sorting, **options)
25
- @sorting = SortForm.parse(sorting, default: sorting) if sorting
49
+ @default_sort = sorting.to_param if sorting.present?
50
+
51
+ super(sort: @default_sort, **options) # set default sort based on config
52
+ end
26
53
 
27
- super(sort: @sorting.to_param, **options) # set default sort based on config
54
+ def default_sort?
55
+ sort == @default_sort
28
56
  end
29
57
 
58
+ # Returns true if the collection supports sorting on the given column.
59
+ # A column supports sorting if it is a database column or if
60
+ # the collection responds to `order_by_#{column}(direction)`.
61
+ #
62
+ # @param column [String, Symbol]
63
+ # @return [true, false]
64
+ def sortable?(column = nil)
65
+ if column.nil?
66
+ @default_sort.present?
67
+ else
68
+ items.respond_to?(:"order_by_#{column}") || items.model.has_attribute?(column.to_s)
69
+ end
70
+ end
71
+
72
+ # Set the current sort behaviour of the collection.
73
+ #
74
+ # @param value [String, Hash] "column direction", or { column:, direction: }
30
75
  def sort=(value)
31
- return unless @sorting
76
+ super(value.to_param) if @default_sort
77
+ end
78
+
79
+ # Returns the current sort behaviour of the given column, for use as a
80
+ # column heading class in the table view.
81
+ #
82
+ # @param column [String, Symbol] the table column as defined in table_with
83
+ # @return [String] the current sort behaviour of the given column
84
+ def sort_status(column)
85
+ current, direction = sort.to_h.values_at(:column, :direction)
86
+ direction if column.to_s == current
87
+ end
32
88
 
33
- # update internal proxy
34
- @sorting = SortForm.parse(value, default: @sorting.default)
89
+ # Calculates the sort parameter to apply when the given column is toggled.
90
+ #
91
+ # @param column [String, Symbol]
92
+ # @return [String]
93
+ def toggle_sort(column)
94
+ current, direction = sort.to_h.values_at(:column, :direction)
35
95
 
36
- # update attribute based on normalized value
37
- super(@sorting.to_param)
96
+ return "#{column} asc" unless column.to_s == current
97
+
98
+ direction == "asc" ? "#{column} desc" : "#{column} asc"
38
99
  end
39
100
 
40
101
  class Sort # :nodoc:
@@ -44,15 +105,22 @@ module Katalyst
44
105
  @app = app
45
106
  end
46
107
 
108
+ using SortParams
109
+
47
110
  def call(collection)
48
- @collection = @app.call(collection)
49
- @collection.sorting, @collection.items = @collection.sorting.apply(@collection.items) if @collection.sorting
50
- @collection
51
- end
111
+ collection = @app.call(collection)
112
+
113
+ column, direction = collection.sort.to_h.values_at(:column, :direction)
114
+
115
+ return collection if column.nil?
116
+
117
+ if collection.items.respond_to?(:"order_by_#{column}")
118
+ collection.items = collection.items.reorder(nil).public_send(:"order_by_#{column}", direction.to_sym)
119
+ elsif collection.items.model.has_attribute?(column)
120
+ collection.items = collection.items.reorder(column => direction)
121
+ end
52
122
 
53
- # pagy shim
54
- def params
55
- @collection.attributes
123
+ collection
56
124
  end
57
125
  end
58
126
  end