Class: CrudComponents::Presenters::Collection
- Includes:
- ColumnSelection
- Defined in:
- lib/crud_components/presenters/collection.rb
Overview
The single collection local every layout partial receives.
query: :auto (default) → build a Query from the request params and apply it
query: :static → no filter row, no sort links, params ignored
query:
picker: false (default) → no column picker; true → render the gear picked_columns: :auto (default) → read ?cols=; an Array → that exact selection (no param read — the backend resolved it)
Defined Under Namespace
Classes: Group
Constant Summary
Constants inherited from Base
Instance Attribute Summary collapse
-
#fieldset ⇒ Object
readonly
Returns the value of attribute fieldset.
-
#layout ⇒ Object
readonly
Returns the value of attribute layout.
-
#model ⇒ Object
readonly
Returns the value of attribute model.
-
#owner ⇒ Object
readonly
Returns the value of attribute owner.
-
#param_prefix ⇒ Object
readonly
Returns the value of attribute param_prefix.
-
#query ⇒ Object
readonly
Returns the value of attribute query.
-
#structure ⇒ Object
readonly
Returns the value of attribute structure.
Attributes inherited from Base
Instance Method Summary collapse
-
#actions_column? ⇒ Boolean
── actions ──────────────────────────────────────────────────────────.
-
#available_fields ⇒ Object
Every column this user is allowed to see — declared fieldset fields plus the dynamic columns — regardless of the current visibility selection.
-
#cell(field, record) ⇒ Object
── cells ────────────────────────────────────────────────────────────.
-
#cell_context ⇒ Object
Click-to-filter context for value renderers — only when this collection actually has a live query.
- #collection_actions ⇒ Object
-
#column_header(field) ⇒ Object
The
<th>title forfield: a customheader:(a String — html_safe to carry markup — or a view-context block, e.g. a link) replaces the plainhuman_name; otherwise thehuman_nameitself. -
#column_header_actions(field) ⇒ Object
The header actions for
fieldas an Actions presenter (collection-kind: they act on the column's object × — for a :selection action — the ticked rows, not a single row), or nil. -
#column_param_name ⇒ Object
The checkbox param name the picker submits (respects param_prefix).
-
#column_picker? ⇒ Boolean
Whether to offer the column picker UI for this collection.
-
#column_selection_actions? ⇒ Boolean
A visible column may host an
on: :selectionaction in its header (acting on the ticked rows × that column's object). - #columns_count ⇒ Object
- #current_page ⇒ Object
- #custom_actions_partial ⇒ Object
-
#custom_header?(field) ⇒ Boolean
Whether
fieldbrings its own header markup or header actions. - #filter_fields ⇒ Object
- #filter_form_id ⇒ Object
-
#filterable? ⇒ Boolean
── filtering ────────────────────────────────────────────────────────.
-
#filterable_field?(field) ⇒ Boolean
Match by name, not object identity: a prebuilt Query carries its own DynamicField instances (from its extra_fields), while the collection built its own from extra_columns.
- #filtered? ⇒ Boolean
-
#group_open?(group) ⇒ Boolean
Default: every group open below the collapse threshold, only the first above it.
-
#group_toggle_url(group) ⇒ Object
Toggle this group in
?open=, materializing the current open set so the first click on a default view keeps the others as they are. - #grouped? ⇒ Boolean
-
#groups ⇒ Object
The records split into groups, in group order (the relation is ordered by the group key first, so consecutive records form each group).
-
#initialize(view:, records:, fieldset: nil, query: :auto, layout: :table, param_prefix: nil, actions: true, search_bar: true, group_by: nil, extra_columns: nil, picker: false, picked_columns: :auto, extra_actions: nil, except_actions: nil) ⇒ Collection
constructor
A new instance of Collection.
- #label_link_field?(field) ⇒ Boolean
- #label_link_present?(record) ⇒ Boolean
-
#own_param_keys ⇒ Object
Every param key this collection owns (for reset).
-
#page_scope ⇒ Object
The underlying (possibly paginated) relation, for custom layouts that would rather drive their own pager — e.g.
-
#page_url(n) ⇒ Object
A URL for page n that keeps this collection's filters/search/sort and every other collection's params (only our own
pagechanges) — so the pager composes with everything and respectsparam_prefix:. -
#pager_pages(window: 2) ⇒ Object
Page numbers to show, with :gap markers for elided ranges: [1, :gap, 4, 5, 6, :gap, 10].
-
#paginated? ⇒ Boolean
── pagination ───────────────────────────────────────────────────────── We render a footer pager only when the relation handed to us is already paginated — i.e.
-
#picker_preserved_params ⇒ Object
Hidden inputs for the picker's GET form: keep every other param (filters, search, sort, other collections) but drop our own cols (the checkboxes resubmit it) and page (a column change resets paging).
-
#preserved_params ⇒ Object
Hidden inputs for the filter form: keep this collection's sort and every param that belongs to someone else (other prefixes, the page's own params).
- #record_link(record) ⇒ Object
- #records ⇒ Object
-
#reset_url ⇒ Object
Reset clears this collection's filter/search/sort/page params and keeps everyone else's (other prefixes, the page's own params).
- #row_actions(record) ⇒ Object
- #search_param_name ⇒ Object
- #search_value ⇒ Object
-
#searchable? ⇒ Boolean
── header search (?q=) and reset ──────────────────────────────────────.
- #select_form_id ⇒ Object
-
#select_param_name ⇒ Object
The checkbox param name (respects param_prefix) — value is each row's identify_by, resolved back with CrudComponents.selected.
- #select_value(record) ⇒ Object
- #selectable? ⇒ Boolean
-
#selection_actions ⇒ Object
── selection (bulk actions) ────────────────────────────────────────── Selection actions (
action :x, on: :selection) operate on the rows the user ticks. -
#show_pager? ⇒ Boolean
Whether to draw the footer at all — a single page needs no pager.
-
#show_toolbar? ⇒ Boolean
Whether the toolbar (search + collection actions) has anything to show — lets a layout skip an empty header row.
-
#sort_active?(field) ⇒ Boolean
Is this the column the result is currently sorted by?.
-
#sort_direction(field) ⇒ Object
The active sort direction for a column — :asc / :desc, or nil when the result isn't sorted by it.
-
#sort_numeric?(field) ⇒ Boolean
Whether this column sorts numerically (vs alphabetically) — numbers and dates do — so a layout can pick a sort-numeric vs sort-alpha glyph.
- #sort_url(field) ⇒ Object
-
#sortable_field?(field) ⇒ Boolean
── sorting ──────────────────────────────────────────────────────────.
- #static? ⇒ Boolean
- #surface ⇒ Object
- #total_count ⇒ Object
- #total_pages ⇒ Object
-
#trailing_column? ⇒ Boolean
The trailing column exists when something needs to live in it: row actions (in the body cells), the column picker (its gear in the header cell), or the inline filter row (its submit/reset button sits in this column).
Methods included from ColumnSelection
#column_visible?, #field_groups, #fields, #group_heading, #group_icon, #visible_columns
Methods inherited from Base
#ability, #config, #css, #permission_context, #render_cell, #render_filter_control
Constructor Details
#initialize(view:, records:, fieldset: nil, query: :auto, layout: :table, param_prefix: nil, actions: true, search_bar: true, group_by: nil, extra_columns: nil, picker: false, picked_columns: :auto, extra_actions: nil, except_actions: nil) ⇒ Collection
Returns a new instance of Collection.
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/crud_components/presenters/collection.rb', line 17 def initialize(view:, records:, fieldset: nil, query: :auto, layout: :table, param_prefix: nil, actions: true, search_bar: true, group_by: nil, extra_columns: nil, picker: false, picked_columns: :auto, extra_actions: nil, except_actions: nil) super(view: view) unless records.respond_to?(:klass) raise ArgumentError, "crud_collection expects an ActiveRecord relation (e.g. Book.all, @books, or an " \ "authorized scope like Book.accessible_by(current_ability)), got #{records.class}. " \ 'Pass a scope so your authorization and filtering apply before the gem renders.' end relation = records @model = relation.klass @structure = Structure.for(@model) @owner = relation.respond_to?(:proxy_association) ? relation.proxy_association.owner : nil @layout = layout @param_prefix = param_prefix @actions_enabled = actions @extra_actions = Array(extra_actions) @except_actions = Array(except_actions).map(&:to_sym) @search_bar_enabled = # Two orthogonal column-picker knobs (see ColumnSelection): the gear is on # iff `picker`; the selection comes from the param (`:auto`) or verbatim # from the resolved Array — they never both read the param. @picker = picker @picked_columns = normalize_picked_columns(picked_columns) # User-defined columns whose data lives outside the model's table. Built # fresh per request (never on the immutable Structure), so they may carry # the per-page value cache. @dynamic_fields = Array(extra_columns).map { |c| c.to_field(@model) } case query when :static @static = true @fieldset = @structure.fieldset(fieldset || :index) when :auto, nil @fieldset = @structure.fieldset(fieldset || :index) @query = Query.new(@model, view.request.query_parameters, fieldset: @fieldset, ability: ability, param_prefix: param_prefix, extra_fields: @dynamic_fields) relation = @query.apply(relation) when Query @query = query @fieldset = fieldset ? @structure.fieldset(fieldset) : query.fieldset @param_prefix = query.param_prefix else raise ArgumentError, "crud_collection query: expects :auto, :static or a CrudComponents::Query, got #{query.inspect}" end @relation = eager_load(relation) setup_grouping(group_by) if group_by end |
Instance Attribute Details
#fieldset ⇒ Object (readonly)
Returns the value of attribute fieldset.
15 16 17 |
# File 'lib/crud_components/presenters/collection.rb', line 15 def fieldset @fieldset end |
#layout ⇒ Object (readonly)
Returns the value of attribute layout.
15 16 17 |
# File 'lib/crud_components/presenters/collection.rb', line 15 def layout @layout end |
#model ⇒ Object (readonly)
Returns the value of attribute model.
15 16 17 |
# File 'lib/crud_components/presenters/collection.rb', line 15 def model @model end |
#owner ⇒ Object (readonly)
Returns the value of attribute owner.
15 16 17 |
# File 'lib/crud_components/presenters/collection.rb', line 15 def owner @owner end |
#param_prefix ⇒ Object (readonly)
Returns the value of attribute param_prefix.
15 16 17 |
# File 'lib/crud_components/presenters/collection.rb', line 15 def param_prefix @param_prefix end |
#query ⇒ Object (readonly)
Returns the value of attribute query.
15 16 17 |
# File 'lib/crud_components/presenters/collection.rb', line 15 def query @query end |
#structure ⇒ Object (readonly)
Returns the value of attribute structure.
15 16 17 |
# File 'lib/crud_components/presenters/collection.rb', line 15 def structure @structure end |
Instance Method Details
#actions_column? ⇒ Boolean
── actions ──────────────────────────────────────────────────────────
363 364 365 |
# File 'lib/crud_components/presenters/collection.rb', line 363 def actions_column? @actions_enabled && (custom_actions_partial.present? || row_action_definitions.any?) end |
#available_fields ⇒ Object
Every column this user is allowed to see — declared fieldset fields plus
the dynamic columns — regardless of the current visibility selection.
This is what a column picker offers as the universe to choose from.
(fields, column_visible? and the picker knobs come from ColumnSelection.)
87 88 89 90 |
# File 'lib/crud_components/presenters/collection.rb', line 87 def available_fields @available_fields ||= (structure.fieldset_fields(fieldset) + @dynamic_fields).select { |f| f.permitted?() } end |
#cell(field, record) ⇒ Object
── cells ────────────────────────────────────────────────────────────
107 108 109 110 111 112 113 114 |
# File 'lib/crud_components/presenters/collection.rb', line 107 def cell(field, record) html = render_cell(field, record, surface: :collection, cell_context: cell_context) if label_link_field?(field) && (path = record_link(record)) view.link_to(html, path, class: css.record_link, data: { turbo_action: 'advance' }) else html end end |
#cell_context ⇒ Object
Click-to-filter context for value renderers — only when this collection actually has a live query.
118 119 120 121 122 |
# File 'lib/crud_components/presenters/collection.rb', line 118 def cell_context return nil if static? || query.nil? @cell_context ||= CellContext.new(view: view, query: query) end |
#collection_actions ⇒ Object
387 388 389 390 391 392 393 |
# File 'lib/crud_components/presenters/collection.rb', line 387 def collection_actions return nil unless @actions_enabled @collection_actions ||= Actions.new(view: view, subject: model, structure: structure, actions: keep(structure.fieldset_actions(fieldset, on: :collection)), owner: owner) end |
#column_header(field) ⇒ Object
The <th> title for field: a custom header: (a String — html_safe to
carry markup — or a view-context block, e.g. a link) replaces the plain
human_name; otherwise the human_name itself. header: substitutes only
the name — the layout still wraps this in a sort link when the column is
sortable, and appends any header actions.
145 146 147 148 149 150 151 152 |
# File 'lib/crud_components/presenters/collection.rb', line 145 def column_header(field) header = field.header case header when nil then field.human_name when Proc then view.instance_exec(&header) else header end end |
#column_header_actions(field) ⇒ Object
The header actions for field as an Actions presenter (collection-kind:
they act on the column's object × — for a :selection action — the ticked
rows, not a single row), or nil. Each action's path block closes over that
object; the layout renders a :selection action as a select-form submitter
and any other as a link/button.
159 160 161 162 163 164 165 |
# File 'lib/crud_components/presenters/collection.rb', line 159 def column_header_actions(field) return nil unless field.header_actions.any? (@column_header_actions ||= {})[field] ||= Actions.new(view: view, subject: model, structure: structure, actions: field.header_actions, owner: owner) end |
#column_param_name ⇒ Object
The checkbox param name the picker submits (respects param_prefix).
96 |
# File 'lib/crud_components/presenters/collection.rb', line 96 def column_param_name = "#{pn('cols')}[]" |
#column_picker? ⇒ Boolean
Whether to offer the column picker UI for this collection.
93 |
# File 'lib/crud_components/presenters/collection.rb', line 93 def column_picker? = @picker && available_fields.any? |
#column_selection_actions? ⇒ Boolean
A visible column may host an on: :selection action in its header (acting
on the ticked rows × that column's object). Like a toolbar selection
action, it submits the shared select-form — so it needs the checkbox
column + select-form and thus makes the collection selectable.
419 420 421 422 423 |
# File 'lib/crud_components/presenters/collection.rb', line 419 def column_selection_actions? fields.any? do |field| column_header_actions(field)&.items&.any? { |item| item.action.selection? } end end |
#columns_count ⇒ Object
436 437 438 |
# File 'lib/crud_components/presenters/collection.rb', line 436 def columns_count fields.size + (selectable? ? 1 : 0) + (trailing_column? ? 1 : 0) end |
#current_page ⇒ Object
332 |
# File 'lib/crud_components/presenters/collection.rb', line 332 def current_page = @relation.current_page |
#custom_actions_partial ⇒ Object
377 378 379 |
# File 'lib/crud_components/presenters/collection.rb', line 377 def custom_actions_partial fieldset.custom_actions_partial end |
#custom_header?(field) ⇒ Boolean
Whether field brings its own header markup or header actions.
136 137 138 |
# File 'lib/crud_components/presenters/collection.rb', line 136 def custom_header?(field) field.custom_header? end |
#filter_fields ⇒ Object
184 185 186 |
# File 'lib/crud_components/presenters/collection.rb', line 184 def filter_fields @filter_fields ||= static? ? [] : query.filter_fields end |
#filter_form_id ⇒ Object
197 198 199 200 |
# File 'lib/crud_components/presenters/collection.rb', line 197 def filter_form_id suffix = param_prefix ? "_#{param_prefix}" : '' "crud_filter_#{model.model_name.plural}#{suffix}" end |
#filterable? ⇒ Boolean
── filtering ────────────────────────────────────────────────────────
180 181 182 |
# File 'lib/crud_components/presenters/collection.rb', line 180 def filterable? !static? && query && filter_fields.any? end |
#filterable_field?(field) ⇒ Boolean
Match by name, not object identity: a prebuilt Query carries its own
DynamicField instances (from its extra_fields), while the collection built
its own from extra_columns. Same column, different objects — include?
would miss them and silently drop the inline filter/sort (issue #21).
Declared fields are memoized on the Structure, so they share a name too.
193 194 195 |
# File 'lib/crud_components/presenters/collection.rb', line 193 def filterable_field?(field) filter_fields.any? { |f| f.name == field.name } end |
#filtered? ⇒ Boolean
215 216 217 |
# File 'lib/crud_components/presenters/collection.rb', line 215 def filtered? !static? && query&.active? end |
#group_open?(group) ⇒ Boolean
Default: every group open below the collapse threshold, only the first
above it. Once ?open= is set it is authoritative (and may open several).
301 302 303 304 305 306 307 |
# File 'lib/crud_components/presenters/collection.rb', line 301 def group_open?(group) if open_keys.nil? records.size < config.group_collapse_threshold || group == groups.first else open_keys.include?(group.key) end end |
#group_toggle_url(group) ⇒ Object
Toggle this group in ?open=, materializing the current open set so the
first click on a default view keeps the others as they are.
311 312 313 314 315 316 |
# File 'lib/crud_components/presenters/collection.rb', line 311 def group_toggle_url(group) current = groups.select { |g| group_open?(g) }.map(&:key) toggled = current.include?(group.key) ? current - [group.key] : current + [group.key] params = view.request.query_parameters.merge(pn('open') => toggled.join(',')) "#{view.request.path}?#{params.to_query}" end |
#grouped? ⇒ Boolean
289 |
# File 'lib/crud_components/presenters/collection.rb', line 289 def grouped? = !@group_by.nil? |
#groups ⇒ Object
The records split into groups, in group order (the relation is ordered by the group key first, so consecutive records form each group).
293 294 295 296 297 |
# File 'lib/crud_components/presenters/collection.rb', line 293 def groups @groups ||= records.group_by { |r| group_key_for(r) }.map do |key, recs| Group.new(key: key, label: group_label_for(recs.first), records: recs) end end |
#label_link_field?(field) ⇒ Boolean
124 125 126 |
# File 'lib/crud_components/presenters/collection.rb', line 124 def label_link_field?(field) field.name == structure.label_field_name end |
#label_link_present?(record) ⇒ Boolean
175 176 177 |
# File 'lib/crud_components/presenters/collection.rb', line 175 def label_link_present?(record) fields.any? { |f| label_link_field?(f) } && record_link(record).present? end |
#own_param_keys ⇒ Object
Every param key this collection owns (for reset).
243 244 245 246 |
# File 'lib/crud_components/presenters/collection.rb', line 243 def own_param_keys keys = filter_fields.flat_map { |f| [pn(f.name.to_s), pn("#{f.name}_geq"), pn("#{f.name}_leq")] } keys + %w[q sort dir page per].map { |k| pn(k) } end |
#page_scope ⇒ Object
The underlying (possibly paginated) relation, for custom layouts that
would rather drive their own pager — e.g. hand it to kaminari's
paginate helper instead of rendering the gem's _pager.
339 |
# File 'lib/crud_components/presenters/collection.rb', line 339 def page_scope = @relation |
#page_url(n) ⇒ Object
A URL for page n that keeps this collection's filters/search/sort and
every other collection's params (only our own page changes) — so the
pager composes with everything and respects param_prefix:.
344 345 346 347 |
# File 'lib/crud_components/presenters/collection.rb', line 344 def page_url(n) params = view.request.query_parameters.merge(pn('page') => n) "#{view.request.path}?#{params.to_query}" end |
#pager_pages(window: 2) ⇒ Object
Page numbers to show, with :gap markers for elided ranges: [1, :gap, 4, 5, 6, :gap, 10]. Always includes first/last and a window around the current page.
352 353 354 355 356 357 358 359 360 |
# File 'lib/crud_components/presenters/collection.rb', line 352 def pager_pages(window: 2) return [] if total_pages <= 1 shown = ([1, total_pages] + ((current_page - window)..(current_page + window)).to_a) .select { |p| p >= 1 && p <= total_pages }.uniq.sort shown.each_with_index.flat_map do |p, i| (i.positive? && p - shown[i - 1] > 1) ? [:gap, p] : [p] end end |
#paginated? ⇒ Boolean
── pagination ─────────────────────────────────────────────────────────
We render a footer pager only when the relation handed to us is already
paginated — i.e. the host called .page (kaminari / will_paginate, which
decorate the relation). The gem never paginates on its own: no records
arrive limited unless you asked for it. pagy keeps its state in a
separate object, not on the relation, so it can't be detected here —
render pagy_nav yourself.
325 326 327 |
# File 'lib/crud_components/presenters/collection.rb', line 325 def paginated? @relation.respond_to?(:current_page) && @relation.respond_to?(:total_pages) end |
#picker_preserved_params ⇒ Object
Hidden inputs for the picker's GET form: keep every other param (filters, search, sort, other collections) but drop our own cols (the checkboxes resubmit it) and page (a column change resets paging).
101 102 103 104 |
# File 'lib/crud_components/presenters/collection.rb', line 101 def picker_preserved_params drop = [pn('cols'), pn('page')] view.request.query_parameters.reject { |key, _| drop.include?(key) } end |
#preserved_params ⇒ Object
Hidden inputs for the filter form: keep this collection's sort and every param that belongs to someone else (other prefixes, the page's own params). Drop our own filter/search params — the controls themselves resubmit those.
236 237 238 239 240 |
# File 'lib/crud_components/presenters/collection.rb', line 236 def preserved_params own = filter_fields.flat_map { |f| [pn(f.name.to_s), pn("#{f.name}_geq"), pn("#{f.name}_leq")] } own += [pn('q'), pn('page'), pn('per')] view.request.query_parameters.reject { |key, _| own.include?(key) } end |
#record_link(record) ⇒ Object
167 168 169 170 171 172 173 |
# File 'lib/crud_components/presenters/collection.rb', line 167 def record_link(record) @record_links ||= {} return @record_links[record.id] if @record_links.key?(record.id) found = RouteResolver.record_path(view, record, owner: owner) @record_links[record.id] = found&.first end |
#records ⇒ Object
73 74 75 76 77 78 79 80 81 |
# File 'lib/crud_components/presenters/collection.rb', line 73 def records @records ||= begin rows = @relation.to_a # Prime each visible dynamic column's per-page cache once (no N+1); the # cell resolver then reads per row from what `preload:` returned. fields.each { |f| f.preload!(rows) if f.is_a?(Fields::DynamicField) } rows end end |
#reset_url ⇒ Object
Reset clears this collection's filter/search/sort/page params and keeps everyone else's (other prefixes, the page's own params).
227 228 229 230 |
# File 'lib/crud_components/presenters/collection.rb', line 227 def reset_url kept = view.request.query_parameters.reject { |key, _| own_param_keys.include?(key) } kept.any? ? "#{view.request.path}?#{kept.to_query}" : view.request.path end |
#row_actions(record) ⇒ Object
381 382 383 384 385 |
# File 'lib/crud_components/presenters/collection.rb', line 381 def row_actions(record) Actions.new(view: view, subject: record, structure: structure, actions: row_action_definitions, owner: owner, suppress_show: label_link_present?(record)) end |
#search_param_name ⇒ Object
207 208 209 |
# File 'lib/crud_components/presenters/collection.rb', line 207 def search_param_name query.param_name('q') end |
#search_value ⇒ Object
211 212 213 |
# File 'lib/crud_components/presenters/collection.rb', line 211 def search_value query&.value('q') end |
#searchable? ⇒ Boolean
── header search (?q=) and reset ──────────────────────────────────────
203 204 205 |
# File 'lib/crud_components/presenters/collection.rb', line 203 def searchable? @search_bar_enabled && !static? && query && query.searchable? end |
#select_form_id ⇒ Object
425 426 427 428 |
# File 'lib/crud_components/presenters/collection.rb', line 425 def select_form_id suffix = param_prefix ? "_#{param_prefix}" : '' "crud_select_#{model.model_name.plural}#{suffix}" end |
#select_param_name ⇒ Object
The checkbox param name (respects param_prefix) — value is each row's identify_by, resolved back with CrudComponents.selected.
432 |
# File 'lib/crud_components/presenters/collection.rb', line 432 def select_param_name = "#{pn('selected')}[]" |
#select_value(record) ⇒ Object
434 |
# File 'lib/crud_components/presenters/collection.rb', line 434 def select_value(record) = record.public_send(structure.identify_by).to_s |
#selectable? ⇒ Boolean
409 410 411 412 413 |
# File 'lib/crud_components/presenters/collection.rb', line 409 def selectable? return @selectable if defined?(@selectable) @selectable = @actions_enabled && (selection_actions.any? || column_selection_actions?) end |
#selection_actions ⇒ Object
── selection (bulk actions) ──────────────────────────────────────────
Selection actions (action :x, on: :selection) operate on the rows the
user ticks. Rendered as submit buttons that post the checked
selected[] slugs to the action path — no-JS works; the optional
crud-select controller adds select-all / select-group / a live count.
400 401 402 403 404 405 406 407 |
# File 'lib/crud_components/presenters/collection.rb', line 400 def selection_actions return nil unless @actions_enabled @selection_actions ||= Actions.new(view: view, subject: model, structure: structure, actions: keep(structure.fieldset_actions(fieldset, on: :selection)) + @extra_actions.select { |action| action.on == :selection }, owner: owner) end |
#show_pager? ⇒ Boolean
Whether to draw the footer at all — a single page needs no pager.
330 |
# File 'lib/crud_components/presenters/collection.rb', line 330 def show_pager? = paginated? && total_pages > 1 |
#show_toolbar? ⇒ Boolean
Whether the toolbar (search + collection actions) has anything to show — lets a layout skip an empty header row.
221 222 223 |
# File 'lib/crud_components/presenters/collection.rb', line 221 def searchable? || collection_actions&.any? || selection_actions&.any? end |
#sort_active?(field) ⇒ Boolean
Is this the column the result is currently sorted by?
261 262 263 |
# File 'lib/crud_components/presenters/collection.rb', line 261 def sort_active?(field) !sort_direction(field).nil? end |
#sort_direction(field) ⇒ Object
The active sort direction for a column — :asc / :desc, or nil when the
result isn't sorted by it. The presenter holds no icon names: a layout
turns this tri-state into whatever glyph it likes (see _table), pairing
it with sort_numeric? to choose a numeric vs alphabetic icon.
269 270 271 272 273 274 |
# File 'lib/crud_components/presenters/collection.rb', line 269 def sort_direction(field) current, dir = query&.sort_state return nil unless current == field.name.to_s dir.to_s == 'desc' ? :desc : :asc end |
#sort_numeric?(field) ⇒ Boolean
Whether this column sorts numerically (vs alphabetically) — numbers and dates do — so a layout can pick a sort-numeric vs sort-alpha glyph.
278 279 280 |
# File 'lib/crud_components/presenters/collection.rb', line 278 def sort_numeric?(field) field.is_a?(Fields::NumericField) || field.is_a?(Fields::DateField) end |
#sort_url(field) ⇒ Object
253 254 255 256 257 258 |
# File 'lib/crud_components/presenters/collection.rb', line 253 def sort_url(field) current, dir = query.sort_state next_dir = current == field.name.to_s && dir == 'asc' ? 'desc' : 'asc' params = view.request.query_parameters.merge(pn('sort') => field.name.to_s, pn('dir') => next_dir) "#{view.request.path}?#{params.to_query}" end |
#sortable_field?(field) ⇒ Boolean
── sorting ──────────────────────────────────────────────────────────
249 250 251 |
# File 'lib/crud_components/presenters/collection.rb', line 249 def sortable_field?(field) !static? && query && query.sortable_fields.any? { |f| f.name == field.name } end |
#static? ⇒ Boolean
70 |
# File 'lib/crud_components/presenters/collection.rb', line 70 def static? = !!@static |
#surface ⇒ Object
71 |
# File 'lib/crud_components/presenters/collection.rb', line 71 def surface = :collection |
#total_count ⇒ Object
334 |
# File 'lib/crud_components/presenters/collection.rb', line 334 def total_count = @relation.total_count |
#total_pages ⇒ Object
333 |
# File 'lib/crud_components/presenters/collection.rb', line 333 def total_pages = @relation.total_pages |
#trailing_column? ⇒ Boolean
The trailing column exists when something needs to live in it: row actions (in the body cells), the column picker (its gear in the header cell), or the inline filter row (its submit/reset button sits in this column). Without it a filterable table would render filter inputs with no button to apply them. Keeping it here also keeps the header, rows and width all in agreement.
373 374 375 |
# File 'lib/crud_components/presenters/collection.rb', line 373 def trailing_column? actions_column? || column_picker? || filterable? end |