9960a8fc3e
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>
182 lines
3.3 KiB
Python
182 lines
3.3 KiB
Python
"""Server-side HTML component library.
|
|
|
|
Split into core / primitives / domain / filters submodules; this package
|
|
re-exports the public API so ``from common.components import X`` keeps working.
|
|
"""
|
|
|
|
from common.components.core import (
|
|
BaseComponent,
|
|
Element,
|
|
Fragment,
|
|
HTMLAttribute,
|
|
HTMLTag,
|
|
Media,
|
|
Node,
|
|
Safe,
|
|
_render_element,
|
|
collect_media,
|
|
randomid,
|
|
render,
|
|
)
|
|
from common.components.custom_elements import (
|
|
SelectionFields,
|
|
SessionTimestampButtons,
|
|
register_element,
|
|
)
|
|
from common.components.date_range_picker import (
|
|
DateRangeCalendar,
|
|
DateRangeField,
|
|
DateRangePicker,
|
|
)
|
|
from common.components.domain import (
|
|
GameLink,
|
|
GameStatus,
|
|
GameStatusSelector,
|
|
LinkedPurchase,
|
|
NameWithIcon,
|
|
PriceConverted,
|
|
PurchasePrice,
|
|
SessionDeviceSelector,
|
|
_resolve_name_with_icon,
|
|
)
|
|
from common.components.filters import (
|
|
DeviceFilterBar,
|
|
FilterBar,
|
|
NumberFilter,
|
|
PlatformFilterBar,
|
|
PlayEventFilterBar,
|
|
PurchaseFilterBar,
|
|
SessionFilterBar,
|
|
StringFilter,
|
|
)
|
|
from common.components.primitives import (
|
|
H1,
|
|
A,
|
|
AddForm,
|
|
ButtonGroup,
|
|
DISABLED_CONTROL_CLASS,
|
|
DISABLED_WITHIN_CLASS,
|
|
FormFields,
|
|
Checkbox,
|
|
CsrfInput,
|
|
Div,
|
|
ExternalScript,
|
|
Icon,
|
|
Input,
|
|
Label,
|
|
Li,
|
|
Modal,
|
|
ModuleScript,
|
|
Pill,
|
|
Popover,
|
|
PopoverTruncated,
|
|
Radio,
|
|
SearchField,
|
|
SimpleTable,
|
|
Span,
|
|
StaticScript,
|
|
StyledButton,
|
|
TableHeader,
|
|
TableRow,
|
|
TableTd,
|
|
Td,
|
|
Template,
|
|
Th,
|
|
Tr,
|
|
Ul,
|
|
YearPicker,
|
|
custom_element_builder,
|
|
paginated_table_content,
|
|
)
|
|
from common.components.search_select import (
|
|
DEFAULT_PREFETCH,
|
|
FilterSelect,
|
|
LabeledOption,
|
|
SearchSelect,
|
|
SearchSelectOption,
|
|
searchselect_selected,
|
|
)
|
|
from common.utils import truncate
|
|
|
|
__all__ = [
|
|
"truncate",
|
|
"BaseComponent",
|
|
"register_element",
|
|
"SelectionFields",
|
|
"SessionTimestampButtons",
|
|
"custom_element_builder",
|
|
"Element",
|
|
"Fragment",
|
|
"Media",
|
|
"Node",
|
|
"Safe",
|
|
"collect_media",
|
|
"render",
|
|
"HTMLAttribute",
|
|
"HTMLTag",
|
|
"_render_element",
|
|
"randomid",
|
|
"A",
|
|
"AddForm",
|
|
"DISABLED_CONTROL_CLASS",
|
|
"DISABLED_WITHIN_CLASS",
|
|
"FormFields",
|
|
"StyledButton",
|
|
"ButtonGroup",
|
|
"Checkbox",
|
|
"CsrfInput",
|
|
"Div",
|
|
"ExternalScript",
|
|
"H1",
|
|
"Icon",
|
|
"Input",
|
|
"Modal",
|
|
"ModuleScript",
|
|
"Pill",
|
|
"Popover",
|
|
"PopoverTruncated",
|
|
"Radio",
|
|
"SearchField",
|
|
"DEFAULT_PREFETCH",
|
|
"FilterSelect",
|
|
"LabeledOption",
|
|
"SearchSelect",
|
|
"SearchSelectOption",
|
|
"searchselect_selected",
|
|
"SimpleTable",
|
|
"Span",
|
|
"StaticScript",
|
|
"Label",
|
|
"Li",
|
|
"Td",
|
|
"Th",
|
|
"Tr",
|
|
"Ul",
|
|
"TableHeader",
|
|
"TableRow",
|
|
"TableTd",
|
|
"Template",
|
|
"YearPicker",
|
|
"paginated_table_content",
|
|
"GameLink",
|
|
"GameStatus",
|
|
"GameStatusSelector",
|
|
"LinkedPurchase",
|
|
"NameWithIcon",
|
|
"PriceConverted",
|
|
"PurchasePrice",
|
|
"SessionDeviceSelector",
|
|
"_resolve_name_with_icon",
|
|
"DateRangeCalendar",
|
|
"DateRangeField",
|
|
"DateRangePicker",
|
|
"FilterBar",
|
|
"PurchaseFilterBar",
|
|
"SessionFilterBar",
|
|
"DeviceFilterBar",
|
|
"PlatformFilterBar",
|
|
"PlayEventFilterBar",
|
|
"StringFilter",
|
|
"NumberFilter",
|
|
]
|