feat(filters): replace RangeSlider with Stash-style NumberFilter (#85) (#86)
Django CI/CD / build-and-push (push) Has been cancelled
Django CI/CD / test (push) Has been cancelled

Numeric range filters could only express BETWEEN/GREATER_THAN/LESS_THAN
via the RangeSlider widget — no way to match NULL/missing values (the
original ask in #32) or exact/not-between. The criteria backend already
supported all 8 numeric modifiers + value2, so this is a UI swap.

- Add NumberFilter component, modeled 1:1 on StringFilter: an 8-modifier
  radio grid plus two number inputs, with the second input revealed only
  for BETWEEN/NOT_BETWEEN and both disabled for IS_NULL/NOT_NULL. Initial
  state is server-rendered so the widget never flashes.
- Migrate all 17 numeric range fields (game/session/purchase/playevent)
  to NumberFilter; drop the now-dead min/max aggregate queries.
- filter-bar.ts: serialize numberFields by modifier (mirroring textFields)
  and wire the modifier radios via event delegation on the persistent
  custom element so they survive htmx swaps of the inner bar body. Apply
  the same delegation fix to the existing string filters.
- Remove RangeSlider entirely: component, range-slider.ts, its custom
  element registration/props, and the range-slider e2e tests.

Backward compatible: old slider filters stored {value, value2, modifier},
the same JSON shape NumberFilter reads, so saved presets keep working.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-21 21:31:10 +02:00
committed by GitHub
parent 34563b26d2
commit 9960a8fc3e
16 changed files with 648 additions and 913 deletions
-11
View File
@@ -146,17 +146,6 @@ register_element("selection-fields", "SelectionFields", SelectionFieldsProps)
_SelectionFields = custom_element_builder("selection-fields")
class RangeSliderProps(TypedDict):
min: int
max: int
step: int
mode: str # "range" | "point"
register_element("range-slider", "RangeSlider", RangeSliderProps)
_RangeSlider = custom_element_builder("range-slider")
class DateRangePickerProps(TypedDict):
pass