katalyst-tables 2.6.0 → 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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -1
  3. data/README.md +57 -213
  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/assets/stylesheets/katalyst/tables/_index.scss +1 -0
  9. data/app/assets/stylesheets/katalyst/tables/_ordinal.scss +38 -0
  10. data/app/assets/stylesheets/katalyst/tables/_table.scss +123 -0
  11. data/app/assets/stylesheets/katalyst/tables/typed-columns/_boolean.scss +4 -0
  12. data/app/assets/stylesheets/katalyst/tables/typed-columns/_currency.scss +5 -0
  13. data/app/assets/stylesheets/katalyst/tables/typed-columns/_date.scss +4 -0
  14. data/app/assets/stylesheets/katalyst/tables/typed-columns/_datetime.scss +4 -0
  15. data/app/assets/stylesheets/katalyst/tables/typed-columns/_index.scss +5 -0
  16. data/app/assets/stylesheets/katalyst/tables/typed-columns/_number.scss +5 -0
  17. data/app/components/concerns/katalyst/tables/has_table_content.rb +17 -8
  18. data/app/components/concerns/katalyst/tables/identifiable.rb +51 -0
  19. data/app/components/concerns/katalyst/tables/orderable.rb +35 -105
  20. data/app/components/concerns/katalyst/tables/selectable.rb +18 -74
  21. data/app/components/concerns/katalyst/tables/sortable.rb +51 -17
  22. data/app/components/katalyst/table_component.html.erb +4 -4
  23. data/app/components/katalyst/table_component.rb +277 -47
  24. data/app/components/katalyst/tables/body_row_component.html.erb +5 -0
  25. data/app/components/katalyst/tables/body_row_component.rb +4 -24
  26. data/app/components/katalyst/tables/cell_component.rb +85 -0
  27. data/app/components/katalyst/tables/cells/boolean_component.rb +20 -0
  28. data/app/components/katalyst/tables/cells/currency_component.rb +29 -0
  29. data/app/components/katalyst/tables/cells/date_component.rb +67 -0
  30. data/app/components/katalyst/tables/cells/date_time_component.rb +60 -0
  31. data/app/components/katalyst/tables/cells/number_component.rb +22 -0
  32. data/app/components/katalyst/tables/cells/ordinal_component.rb +44 -0
  33. data/app/components/katalyst/tables/cells/rich_text_component.rb +21 -0
  34. data/app/components/katalyst/tables/cells/select_component.rb +39 -0
  35. data/app/components/katalyst/tables/data.rb +30 -0
  36. data/app/components/katalyst/tables/empty_caption_component.rb +1 -1
  37. data/app/components/katalyst/tables/header_row_component.html.erb +5 -0
  38. data/app/components/katalyst/tables/header_row_component.rb +4 -24
  39. data/app/components/katalyst/tables/label.rb +37 -0
  40. data/app/components/katalyst/tables/orderable/form_component.rb +38 -0
  41. data/app/components/katalyst/tables/selectable/form_component.html.erb +6 -4
  42. data/app/components/katalyst/tables/selectable/form_component.rb +8 -11
  43. data/app/controllers/concerns/katalyst/tables/backend.rb +2 -28
  44. data/app/helpers/katalyst/tables/frontend.rb +48 -2
  45. data/app/javascript/tables/application.js +0 -5
  46. data/app/javascript/tables/orderable/form_controller.js +8 -6
  47. data/app/javascript/tables/orderable/item_controller.js +9 -0
  48. data/app/models/concerns/katalyst/tables/collection/core.rb +6 -1
  49. data/app/models/concerns/katalyst/tables/collection/pagination.rb +8 -1
  50. data/app/models/concerns/katalyst/tables/collection/sorting.rb +85 -17
  51. data/app/models/katalyst/tables/collection/array.rb +38 -0
  52. data/app/models/katalyst/tables/collection/base.rb +4 -0
  53. data/lib/katalyst/tables/config.rb +23 -0
  54. data/lib/katalyst/tables.rb +9 -0
  55. metadata +32 -15
  56. data/app/components/concerns/katalyst/tables/configurable_component.rb +0 -52
  57. data/app/components/concerns/katalyst/tables/turbo_replaceable.rb +0 -79
  58. data/app/components/katalyst/tables/body_cell_component.rb +0 -47
  59. data/app/components/katalyst/tables/header_cell_component.rb +0 -65
  60. data/app/components/katalyst/turbo/pagy_nav_component.rb +0 -23
  61. data/app/components/katalyst/turbo/table_component.rb +0 -45
  62. data/app/helpers/katalyst/tables/frontend/helper.rb +0 -31
  63. data/app/javascript/tables/turbo/collection_controller.js +0 -38
  64. data/app/models/katalyst/tables/collection/sort_form.rb +0 -102
@@ -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 |_|
@@ -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:
@@ -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) 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, **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: attribute_was(:sort))
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
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Katalyst
4
+ module Tables
5
+ module Collection
6
+ # Entry point for creating a collection from an array for use with table components.
7
+ class Array
8
+ include Core
9
+ include Filtering
10
+
11
+ def self.with_params(params)
12
+ new.with_params(params)
13
+ end
14
+
15
+ def model
16
+ items.first&.class || ActiveRecord::Base
17
+ end
18
+
19
+ def model_name
20
+ @model_name ||= items.first&.model_name || ActiveModel::Name.new(Object, nil, "record")
21
+ end
22
+
23
+ def with_params(params)
24
+ # test support
25
+ params = ActionController::Parameters.new(params) unless params.is_a?(ActionController::Parameters)
26
+
27
+ self.attributes = params.permit(self.class.permitted_params)
28
+
29
+ self
30
+ end
31
+
32
+ def inspect
33
+ "#<#{self.class.name} @attributes=#{attributes.inspect} @model_name=\"#{model_name}\" @count=#{items&.count}>"
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -34,6 +34,10 @@ module Katalyst
34
34
  new.with_params(params)
35
35
  end
36
36
 
37
+ def model
38
+ items.model
39
+ end
40
+
37
41
  def model_name
38
42
  @model_name ||= items.model_name.dup.tap do |name|
39
43
  name.param_key = ""
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/configurable"
4
+
5
+ module Katalyst
6
+ module Tables
7
+ class Config
8
+ include ActiveSupport::Configurable
9
+
10
+ config_accessor(:component_extensions) do
11
+ %w[
12
+ Katalyst::Tables::Identifiable
13
+ Katalyst::Tables::Orderable
14
+ Katalyst::Tables::Selectable
15
+ Katalyst::Tables::Sortable
16
+ ]
17
+ end
18
+
19
+ config_accessor(:date_format) { :default }
20
+ config_accessor(:datetime_format) { :default }
21
+ end
22
+ end
23
+ end
@@ -3,10 +3,19 @@
3
3
  require "view_component"
4
4
  require "katalyst/html_attributes"
5
5
 
6
+ require_relative "tables/config"
6
7
  require_relative "tables/engine"
7
8
 
8
9
  module Katalyst
9
10
  module Tables
10
11
  class Error < StandardError; end
12
+
13
+ def self.config
14
+ @config ||= Config.new
15
+ end
16
+
17
+ def self.configure
18
+ yield config
19
+ end
11
20
  end
12
21
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: katalyst-tables
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.6.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Katalyst Interactive
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-04-04 00:00:00.000000000 Z
11
+ date: 2024-05-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: katalyst-html-attributes
@@ -54,48 +54,65 @@ files:
54
54
  - app/assets/builds/katalyst/tables.min.js
55
55
  - app/assets/builds/katalyst/tables.min.js.map
56
56
  - app/assets/config/katalyst-tables.js
57
- - app/components/concerns/katalyst/tables/configurable_component.rb
57
+ - app/assets/stylesheets/katalyst/tables/_index.scss
58
+ - app/assets/stylesheets/katalyst/tables/_ordinal.scss
59
+ - app/assets/stylesheets/katalyst/tables/_table.scss
60
+ - app/assets/stylesheets/katalyst/tables/typed-columns/_boolean.scss
61
+ - app/assets/stylesheets/katalyst/tables/typed-columns/_currency.scss
62
+ - app/assets/stylesheets/katalyst/tables/typed-columns/_date.scss
63
+ - app/assets/stylesheets/katalyst/tables/typed-columns/_datetime.scss
64
+ - app/assets/stylesheets/katalyst/tables/typed-columns/_index.scss
65
+ - app/assets/stylesheets/katalyst/tables/typed-columns/_number.scss
58
66
  - app/components/concerns/katalyst/tables/has_table_content.rb
67
+ - app/components/concerns/katalyst/tables/identifiable.rb
59
68
  - app/components/concerns/katalyst/tables/orderable.rb
60
69
  - app/components/concerns/katalyst/tables/row_renderer.rb
61
70
  - app/components/concerns/katalyst/tables/selectable.rb
62
71
  - app/components/concerns/katalyst/tables/sortable.rb
63
- - app/components/concerns/katalyst/tables/turbo_replaceable.rb
64
72
  - app/components/katalyst/table_component.html.erb
65
73
  - app/components/katalyst/table_component.rb
66
- - app/components/katalyst/tables/body_cell_component.rb
74
+ - app/components/katalyst/tables/body_row_component.html.erb
67
75
  - app/components/katalyst/tables/body_row_component.rb
76
+ - app/components/katalyst/tables/cell_component.rb
77
+ - app/components/katalyst/tables/cells/boolean_component.rb
78
+ - app/components/katalyst/tables/cells/currency_component.rb
79
+ - app/components/katalyst/tables/cells/date_component.rb
80
+ - app/components/katalyst/tables/cells/date_time_component.rb
81
+ - app/components/katalyst/tables/cells/number_component.rb
82
+ - app/components/katalyst/tables/cells/ordinal_component.rb
83
+ - app/components/katalyst/tables/cells/rich_text_component.rb
84
+ - app/components/katalyst/tables/cells/select_component.rb
85
+ - app/components/katalyst/tables/data.rb
68
86
  - app/components/katalyst/tables/empty_caption_component.html.erb
69
87
  - app/components/katalyst/tables/empty_caption_component.rb
70
- - app/components/katalyst/tables/header_cell_component.rb
88
+ - app/components/katalyst/tables/header_row_component.html.erb
71
89
  - app/components/katalyst/tables/header_row_component.rb
90
+ - app/components/katalyst/tables/label.rb
91
+ - app/components/katalyst/tables/orderable/form_component.rb
72
92
  - app/components/katalyst/tables/pagy_nav_component.rb
73
93
  - app/components/katalyst/tables/selectable/form_component.html.erb
74
94
  - app/components/katalyst/tables/selectable/form_component.rb
75
- - app/components/katalyst/turbo/pagy_nav_component.rb
76
- - app/components/katalyst/turbo/table_component.rb
77
95
  - app/controllers/concerns/katalyst/tables/backend.rb
78
96
  - app/helpers/katalyst/tables/frontend.rb
79
- - app/helpers/katalyst/tables/frontend/helper.rb
80
97
  - app/javascript/tables/application.js
81
98
  - app/javascript/tables/orderable/form_controller.js
82
99
  - app/javascript/tables/orderable/item_controller.js
83
100
  - app/javascript/tables/orderable/list_controller.js
84
101
  - app/javascript/tables/selection/form_controller.js
85
102
  - app/javascript/tables/selection/item_controller.js
86
- - app/javascript/tables/turbo/collection_controller.js
87
103
  - app/models/concerns/katalyst/tables/collection/core.rb
88
104
  - app/models/concerns/katalyst/tables/collection/filtering.rb
89
105
  - app/models/concerns/katalyst/tables/collection/has_params.rb
90
106
  - app/models/concerns/katalyst/tables/collection/pagination.rb
91
107
  - app/models/concerns/katalyst/tables/collection/reducers.rb
92
108
  - app/models/concerns/katalyst/tables/collection/sorting.rb
109
+ - app/models/katalyst/tables/collection/array.rb
93
110
  - app/models/katalyst/tables/collection/base.rb
94
111
  - app/models/katalyst/tables/collection/filter.rb
95
- - app/models/katalyst/tables/collection/sort_form.rb
96
112
  - config/importmap.rb
97
113
  - config/locales/tables.en.yml
98
114
  - lib/katalyst/tables.rb
115
+ - lib/katalyst/tables/config.rb
99
116
  - lib/katalyst/tables/engine.rb
100
117
  homepage: https://github.com/katalyst/tables
101
118
  licenses:
@@ -106,7 +123,7 @@ metadata:
106
123
  homepage_uri: https://github.com/katalyst/tables
107
124
  source_code_uri: https://github.com/katalyst/tables
108
125
  changelog_uri: https://github.com/katalyst/tables/blobs/main/CHANGELOG.md
109
- post_install_message:
126
+ post_install_message:
110
127
  rdoc_options: []
111
128
  require_paths:
112
129
  - lib
@@ -121,8 +138,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
121
138
  - !ruby/object:Gem::Version
122
139
  version: '0'
123
140
  requirements: []
124
- rubygems_version: 3.4.19
125
- signing_key:
141
+ rubygems_version: 3.5.9
142
+ signing_key:
126
143
  specification_version: 4
127
144
  summary: HTML table generator for Rails views
128
145
  test_files: []
@@ -1,52 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Katalyst
4
- module Tables
5
- module ConfigurableComponent # :nodoc:
6
- extend ActiveSupport::Concern
7
-
8
- include ActiveSupport::Configurable
9
-
10
- included do
11
- # Workaround: ViewComponent::Base.config is incompatible with ActiveSupport::Configurable
12
- @_config = Class.new(ActiveSupport::Configurable::Configuration).new
13
- end
14
-
15
- class_methods do
16
- # Define a configurable sub-component.
17
- # Sub-components are cached on the table instance. We want to allow run
18
- # time mixins for tables so that we can extend tables with cross-cutting
19
- # concerns that affect multiple sub-components by including the concern
20
- # into the top-level table class. We achieve this by subclassing the
21
- # component as soon as it is created so that when a mixin is added to
22
- # the table class, it can immediately retrieve and modify the
23
- # sub-component class as well without needing to worry about affecting
24
- # other tables.
25
- def config_component(name, component_name: "#{name}_component", default: nil) # rubocop:disable Metrics/MethodLength
26
- config_accessor(name)
27
- config.public_send(:"#{name}=", default)
28
- define_method(component_name) do
29
- return instance_variable_get(:"@#{component_name}") if instance_variable_defined?(:"@#{component_name}")
30
-
31
- klass = config.public_send(name)
32
- component = klass ? self.class.const_get(klass) : nil
33
-
34
- # subclass to allow table-specific extensions
35
- if component
36
- component = Class.new(component)
37
- component.extend(HiddenSubcomponent)
38
- end
39
-
40
- instance_variable_set(:"@#{component_name}", component) if component
41
- end
42
- end
43
- end
44
-
45
- # View Component uses `name` to resolve the template path, so we need to
46
- # hide the subclass from the template resolver.
47
- module HiddenSubcomponent
48
- delegate :name, to: :superclass
49
- end
50
- end
51
- end
52
- end
@@ -1,79 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Katalyst
4
- module Tables
5
- # Adds support for turbo stream replacement to ViewComponents. Components
6
- # that are rendered from a turbo-stream-compatible response will be rendered
7
- # using turbo stream replacement. Components must define `id`.
8
- #
9
- # Turbo stream replacement rendering will only be enabled if the component
10
- # passes `turbo: true` as a constructor option.
11
- module TurboReplaceable
12
- extend ActiveSupport::Concern
13
-
14
- include ::Turbo::StreamsHelper
15
-
16
- # Is turbo rendering enabled for this component?
17
- def turbo?
18
- @turbo
19
- end
20
-
21
- # Are we rendering a turbo stream response?
22
- def turbo_stream_response?
23
- response.media_type.eql?("text/vnd.turbo-stream.html")
24
- end
25
-
26
- def initialize(turbo: true, **options)
27
- super(**options)
28
-
29
- @turbo = turbo
30
- end
31
-
32
- class_methods do
33
- # Redefine the compiler to use our custom compiler.
34
- # Compiler is set on `inherited` so we need to re-set it if it's not the expected type.
35
- def compiler
36
- @vc_compiler = @vc_compiler.is_a?(TurboCompiler) ? @vc_compiler : TurboCompiler.new(self)
37
- end
38
- end
39
-
40
- included do
41
- # ensure that our custom compiler is used, as `inherited` calls `compile` before our module is included.
42
- compile(force: true) if compiled?
43
- end
44
-
45
- # Wraps the default compiler provided by ViewComponent to add turbo support.
46
- class TurboCompiler < ViewComponent::Compiler
47
- private
48
-
49
- def define_render_template_for # rubocop:disable Metrics/MethodLength
50
- super
51
-
52
- redefinition_lock.synchronize do
53
- # Capture the instance method added by the default compiler and
54
- # wrap it in a turbo stream replacement. Take care to ensure that
55
- # subclasses of this component don't break delegation, as each
56
- # subclass of ViewComponent::Base defines its own version of this
57
- # method.
58
- vc_render_template = component_class.instance_method(:render_template_for)
59
- component_class.define_method(:render_template_for) do |variant = nil|
60
- # VC discards the output from this method and uses the buffer
61
- # if both are set. Capture and wrap the output.
62
- content = capture { vc_render_template.bind_call(self, variant) }
63
- # In turbo mode, replace the inner-most element using a turbo
64
- # stream. Note that we only want one turbo stream per component
65
- # from this mechanism, as subclasses may want to concat their
66
- # own additional streams.
67
- if turbo? && turbo_stream_response? && !@streamed
68
- @streamed = true
69
- concat(turbo_stream.replace(id, content))
70
- else
71
- concat(content)
72
- end
73
- end
74
- end
75
- end
76
- end
77
- end
78
- end
79
- end
@@ -1,47 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Katalyst
4
- module Tables
5
- class BodyCellComponent < ViewComponent::Base # :nodoc:
6
- include Katalyst::HtmlAttributes
7
-
8
- attr_reader :record
9
-
10
- def initialize(table, record, attribute, heading: false, **html_attributes)
11
- super(**html_attributes)
12
-
13
- @table = table
14
- @record = record
15
- @attribute = attribute
16
- @type = heading ? :th : :td
17
- end
18
-
19
- def before_render
20
- # fallback if no content block is given
21
- with_content(value.to_s) unless content?
22
- end
23
-
24
- def call
25
- content # ensure content is set before rendering options
26
-
27
- content_tag(@type, content, **html_attributes)
28
- end
29
-
30
- # @return the object for this row.
31
- def object
32
- @record
33
- end
34
-
35
- def value
36
- @record.public_send(@attribute)
37
- end
38
-
39
- def inspect
40
- "#<#{self.class.name} attribute: #{@attribute.inspect}, value: #{value.inspect}>"
41
- end
42
-
43
- # Backwards compatibility with tables 1.0
44
- alias_method :options, :html_attributes=
45
- end
46
- end
47
- end