stimulus_table_filter 0.1.0 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fd1d293fa4373f23cb2eb542feb797e20b73b994564af5422e1bb69cfb370af4
4
- data.tar.gz: 80a795894d382356b03d83ab003727c4dd5e786f81ca140106b33417ecabeab6
3
+ metadata.gz: 495418a93784ca089dccd7fca9636a3bcc89a7e2f42599fc0cb9c6676be34ef7
4
+ data.tar.gz: c7486ef81d34dc5cd0d09ff3f2bd5047105f2810c25a296b960f1cdcaacddba1
5
5
  SHA512:
6
- metadata.gz: d27af095bbddab3441ed10e65215c2e92c2bdbb2d10111ac4ffee02adc7fc9ee0898087019329b43e2b649d8f5119be42639ab2ba150a427ab5be5e9ce0b17f6
7
- data.tar.gz: 8d52a264133b2aed8e2737392b6ba0a86443f35b95be1a0942204e04297cb86b05e5e6100ffcac25a42e81c5a7f122c0632b5cbe8bd8bbdfb7f7d85ad2c577f6
6
+ metadata.gz: f9f256274e5dc8b69d2db008c7547781987d20dd67a0825f0c9e89af99c6b4b715bf6519ba80017073fe35cd6662e8776177ea9f0abd7ff78b8550ffc7e103f4
7
+ data.tar.gz: b61979d520487ce847e45c00d7d66c66c91adb8eaebb54a9a5742947825e666fda4c947fede3bada960eaf1546f4e584d65cae21f07b75c867cfb41310bf4f4b
data/CHANGELOG.md CHANGED
@@ -3,6 +3,18 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
5
 
6
+ ## [0.2.0] - 2026-10-15
7
+
8
+ - Add `table_filter_bar_tag`: a search input and optional filter buttons in one toolbar. It
9
+ raises `StimulusTableFilter::Error` when `filters:` is given without `dimension:`
10
+
11
+ ## [0.1.1] - 2026-08-31
12
+
13
+ - `table_filter_container_tag` accepts a `search:` option and emits
14
+ `data-table-filter-search-value`, matching the documented data-attribute contract
15
+ - README: document that `table_filter_footer_tag` wraps its content in a footer element, a
16
+ `<tr>` and a `<td>`; correct the URL-state and programmatic-API behavior notes
17
+
6
18
  ## [0.1.0] - 2026-08-25
7
19
 
8
20
  Initial release.
@@ -13,4 +25,5 @@ Initial release.
13
25
  - RSpec support: shared examples plus `have_data` / `have_data_target` matchers
14
26
  - CI: RuboCop, RSpec and JavaScript tests across Ruby 3.2–3.4 and Rails 7.0–8.1
15
27
 
28
+ [0.1.1]: https://github.com/icoluccio/stimulus-table-filter/releases/tag/v0.1.1
16
29
  [0.1.0]: https://github.com/icoluccio/stimulus-table-filter/releases/tag/v0.1.0
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- stimulus_table_filter (0.1.0)
4
+ stimulus_table_filter (0.2.0)
5
5
  railties (>= 7.0, < 9)
6
6
 
7
7
  GEM
@@ -285,7 +285,7 @@ CHECKSUMS
285
285
  securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
286
286
  sexp_processor (4.17.5) sha256=ae2b48ba98353d5d465ce8759836b7a05f2e12c5879fcd14d7815b026de32f0e
287
287
  simplecov (1.1.1) sha256=25825ef13f0b2e74694d769817dad6ab8e90131dabdaa666e522fea105521e78
288
- stimulus_table_filter (0.1.0)
288
+ stimulus_table_filter (0.2.0)
289
289
  thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73
290
290
  tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f
291
291
  tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b
data/README.md CHANGED
@@ -110,13 +110,8 @@ A complete filterable table in one view partial:
110
110
 
111
111
  ```erb
112
112
  <%= table_filter_container_tag do %>
113
- <%# Search + filter bar %>
114
- <div>
115
- <%= table_filter_search_tag(placeholder: "Search users…") %>
116
- <%= table_filter_filter_btn_tag("all", "All", dimension: "status") %>
117
- <%= table_filter_filter_btn_tag("active", "Active", dimension: "status") %>
118
- <%= table_filter_filter_btn_tag("archived", "Archived", dimension: "status") %>
119
- </div>
113
+ <%= table_filter_bar_tag(placeholder: "Search users…", dimension: "status",
114
+ filters: { "all" => "All", "active" => "Active", "archived" => "Archived" }) %>
120
115
 
121
116
  <table>
122
117
  <thead>
@@ -176,6 +171,7 @@ the controller reads on connect.
176
171
  | `page_size:` | `nil` | Rows per page (omit or `0` to disable pagination) |
177
172
  | `url_key:` | `nil` | URL param prefix (default `tf`); set per table to namespace multiple tables |
178
173
  | `debounce_ms:` | `nil` | Search debounce in ms (default `0` = immediate) |
174
+ | `search:` | `nil` | Initial search text (emits `data-table-filter-search-value`) |
179
175
  | `tag:` | `:div` | Wrapping HTML element |
180
176
 
181
177
  Every helper accepts `**opts` and passes them through to the underlying tag.
@@ -224,6 +220,28 @@ filters. Omit `filters:` and the filter helpers entirely for a search-and-sort-o
224
220
  Filters rows by matching `data-name`, or `data-searchable` when present. Case-insensitive
225
221
  substring match.
226
222
 
223
+ ### Toolbar
224
+
225
+ `table_filter_bar_tag` renders the search input and, when `filters:` is given, a group of filter
226
+ buttons inside one `<div>`:
227
+
228
+ ```erb
229
+ <%= table_filter_bar_tag(placeholder: "Search users…", dimension: "status",
230
+ filters: { "all" => "All", "active" => "Active" },
231
+ class: "flex gap-2", search: { class: "input" }, group: { class: "join" }, button: { class: "btn" }) %>
232
+ ```
233
+
234
+ | Option | Applies to |
235
+ | --- | --- |
236
+ | `placeholder:` | The search input |
237
+ | `filters:` | `{ value => label }`; one button per entry. Omit it for a search-only bar. No group renders |
238
+ | `dimension:` | The dimension every button acts on. **Required** with `filters:`. Without it the helper raises `StimulusTableFilter::Error` |
239
+ | `search:`, `group:`, `button:` | Extra attributes (`class:`, `data:`, ...) for the input, the button group `<div>` and each button |
240
+ | any other option | The outer `<div>` |
241
+
242
+ It wraps `table_filter_search_tag` and `table_filter_filter_btn_tag`. Call those directly when
243
+ you need a different layout.
244
+
227
245
  ### Filter buttons
228
246
 
229
247
  ```erb
@@ -315,6 +333,9 @@ The default footer shows how many rows match the current filter and search, out
315
333
  <!-- renders: <span>12</span> of <span>87</span> -->
316
334
  ```
317
335
 
336
+ The helper wraps the content in the footer element (a `<tfoot>` by default), a `<tr>`, and a
337
+ `<td>`; `colspan:` applies to that cell and `tag:` replaces only the outer element.
338
+
318
339
  Or build your own footer. The block receives the match-count and total-count spans:
319
340
 
320
341
  ```erb
@@ -399,11 +420,14 @@ Behavior notes:
399
420
  - **Footer stats**: the match count covers every row that passes the active filters and search,
400
421
  across all pages. Count tags populate with the number of matching rows whose dimension value
401
422
  equals the tag's value.
402
- - **URL state**: every filter dimension, sort, direction, page and search sync to
403
- `URLSearchParams` on every change (dimensions as `tf_filter_{dimension}` params) and the
404
- controller restores them on connect. Namespace multiple tables with `data-table-filter-url-key-value`.
423
+ - **URL state**: the controller syncs filter dimensions, sort, direction, page and search to
424
+ `URLSearchParams` when the user changes them (dimensions as `tf_filter_{dimension}` params)
425
+ and restores them on connect without rewriting the URL. Namespace multiple tables with
426
+ `data-table-filter-url-key-value`.
405
427
  - **Event handling**: the controller delegates all events itself; individual elements
406
- need no `data-action`. `setFilter`, `sortBy` and `search` are also callable programmatically.
428
+ need no `data-action`. Call `search()` programmatically. For filters and sort, set the
429
+ Stimulus values and refresh, e.g.
430
+ `controller.filterDimensionsValue = { status: 'active' }; controller.refresh()`.
407
431
 
408
432
  ### RSpec shared examples
409
433
 
@@ -38,6 +38,7 @@ export default class extends Controller {
38
38
 
39
39
  connect() {
40
40
  this.#readUrlState()
41
+ if (this.hasSearchTarget && this.searchValue) this.searchTarget.value = this.searchValue
41
42
  this.element.addEventListener("click", this.#onClick)
42
43
  this.element.addEventListener("input", this.#onInput)
43
44
  this.element.addEventListener("change", this.#onChange)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module StimulusTableFilter
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.0'
5
5
  end
@@ -3,9 +3,9 @@
3
3
  module StimulusTableFilter
4
4
  module ViewHelper
5
5
  def table_filter_container_tag(sort: 'name', dir: 'asc', page: nil, page_size: nil, url_key: nil,
6
- debounce_ms: nil, tag: :div, **opts, &)
6
+ debounce_ms: nil, search: nil, tag: :div, **opts, &)
7
7
  data = container_data(sort:, dir:, page:, page_size:, url_key:,
8
- debounce_ms:).merge(opts.delete(:data) || {})
8
+ debounce_ms:, search:).merge(opts.delete(:data) || {})
9
9
  content_tag(tag, data: data, **opts, &)
10
10
  end
11
11
 
@@ -17,11 +17,12 @@ module StimulusTableFilter
17
17
  base
18
18
  end
19
19
 
20
- def container_data(sort:, dir:, page: nil, page_size: nil, url_key: nil, debounce_ms: nil)
20
+ def container_data(sort:, dir:, page: nil, page_size: nil, url_key: nil, debounce_ms: nil, search: nil)
21
21
  base = base_container_data(sort:, dir:, page:)
22
22
  base = base.merge(table_filter_page_size_value: page_size.to_s) if page_size
23
23
  base = base.merge(table_filter_url_key_value: url_key.to_s) if url_key
24
24
  base = base.merge(table_filter_debounce_ms_value: debounce_ms.to_s) if debounce_ms
25
+ base = base.merge(table_filter_search_value: search.to_s) if search
25
26
  base
26
27
  end
27
28
  end
@@ -4,6 +4,14 @@ module StimulusTableFilter
4
4
  module ViewHelper
5
5
  def table_filter_search_tag(**opts) = tag.input(type: 'search', data: target_data('search', opts), **opts)
6
6
 
7
+ # Raises StimulusTableFilter::Error if +filters+ is given without +dimension+.
8
+ def table_filter_bar_tag(placeholder: nil, filters: {}, dimension: nil, search: {}, group: {}, button: {}, **opts)
9
+ content_tag(:div, **opts) do
10
+ safe_join([table_filter_search_tag(placeholder: placeholder, **search),
11
+ table_filter_button_group(filters, dimension, group, button)])
12
+ end
13
+ end
14
+
7
15
  def table_filter_filter_btn_tag(value, label, dimension: nil, **opts)
8
16
  data = with_data({ filter_btn: value }, opts)
9
17
  data[:filter_dimension] = dimension if dimension
@@ -31,6 +39,18 @@ module StimulusTableFilter
31
39
 
32
40
  private
33
41
 
42
+ def table_filter_button_group(filters, dimension, group, button)
43
+ return if filters.empty?
44
+
45
+ content_tag(:div, table_filter_buttons(filters, dimension, button), **group)
46
+ end
47
+
48
+ def table_filter_buttons(filters, dimension, button)
49
+ raise Error, 'table_filter_bar_tag requires dimension: when filters are given' unless dimension
50
+
51
+ safe_join(filters.map { |value, label| table_filter_filter_btn_tag(value, label, dimension:, **button) })
52
+ end
53
+
34
54
  def option_tag(pair) = content_tag(:option, pair.first, value: pair.last)
35
55
  def render_options(pairs) = safe_join(pairs.map(&method(:option_tag)))
36
56
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stimulus_table_filter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - icoluccio
@@ -101,7 +101,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
101
101
  - !ruby/object:Gem::Version
102
102
  version: '0'
103
103
  requirements: []
104
- rubygems_version: 3.6.9
104
+ rubygems_version: 3.6.7
105
105
  specification_version: 4
106
106
  summary: Client-side table filtering, sorting and search for Rails
107
107
  test_files: []