katalyst-tables 2.6.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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