Skip to content

Commit a181ac4

Browse files
zubeydecivelekzzacharo
authored andcommitted
facets: add year range facet
1 parent be0a7ba commit a181ac4

File tree

13 files changed

+3638
-3351
lines changed

13 files changed

+3638
-3351
lines changed

package-lock.json

Lines changed: 1681 additions & 3350 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"peerDependencies": {
2626
"@babel/runtime": "^7.9.0",
2727
"axios": "^1.7.7",
28+
"luxon": "^3.5.0",
2829
"lodash": "^4.17.0",
2930
"qs": "^6.8.0",
3031
"react": "^16.13.0",
@@ -34,7 +35,10 @@
3435
"redux": "^4.0.0",
3536
"redux-thunk": "^2.3.0",
3637
"semantic-ui-css": "^2.5.0",
37-
"semantic-ui-react": "^2.1.0"
38+
"semantic-ui-react": "^2.1.0",
39+
"@visx/scale": "^3.12.0",
40+
"@visx/shape": "^3.12.0",
41+
"@visx/responsive": "^3.12.0"
3842
},
3943
"devDependencies": {
4044
"@babel/cli": "^7.11.0",
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
/*
2+
* This file is part of React-SearchKit.
3+
* Copyright (C) 2026 CERN.
4+
*
5+
* React-SearchKit is free software; you can redistribute it and/or modify it
6+
* under the terms of the MIT License; see LICENSE file for more details.
7+
*/
8+
9+
import PropTypes from "prop-types";
10+
import React, { Component, useContext } from "react";
11+
import Overridable from "react-overridable";
12+
import { Input } from "semantic-ui-react";
13+
import { AppContext } from "../ReactSearchKit";
14+
15+
const DEFAULT_PLACEHOLDERS = { YYYY: "YYYY", MM: "MM", DD: "DD" };
16+
17+
const getPartConfig = (part, placeholders = {}) => {
18+
const placeholder = placeholders[part] ?? DEFAULT_PLACEHOLDERS[part] ?? part;
19+
20+
switch (part) {
21+
case "YYYY":
22+
return { placeholder, maxLength: 4, width: "8.5ch" };
23+
case "MM":
24+
return { placeholder, maxLength: 2, width: "7ch" };
25+
case "DD":
26+
return { placeholder, maxLength: 2, width: "7ch" };
27+
default:
28+
return { placeholder, maxLength: 4, width: "8.5ch" };
29+
}
30+
};
31+
32+
const getFieldName = (prefix, part) => {
33+
if (part === "YYYY") return `${prefix}Year`;
34+
if (part === "MM") return `${prefix}Month`;
35+
if (part === "DD") return `${prefix}Day`;
36+
return "";
37+
};
38+
39+
class DateRangeInputs extends Component {
40+
handlePartChange = (field, maxLength) => (event) => {
41+
const { onPartChange } = this.props;
42+
onPartChange(event.target.value, field, maxLength);
43+
};
44+
45+
render() {
46+
const {
47+
format,
48+
values,
49+
disabled,
50+
onPartBlur,
51+
overridableId,
52+
toLabel,
53+
placeholders,
54+
} = this.props;
55+
56+
return (
57+
<DateRangeInputsElement
58+
format={format}
59+
values={values}
60+
disabled={disabled}
61+
onPartChange={this.handlePartChange}
62+
onPartBlur={onPartBlur}
63+
overridableId={overridableId}
64+
toLabel={toLabel}
65+
placeholders={placeholders}
66+
/>
67+
);
68+
}
69+
}
70+
DateRangeInputs.propTypes = {
71+
format: PropTypes.oneOf(["YYYY", "YYYY-MM", "YYYY-MM-DD"]).isRequired,
72+
values: PropTypes.shape({
73+
fromYear: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
74+
fromMonth: PropTypes.string,
75+
fromDay: PropTypes.string,
76+
toYear: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
77+
toMonth: PropTypes.string,
78+
toDay: PropTypes.string,
79+
}).isRequired,
80+
disabled: PropTypes.bool.isRequired,
81+
onPartChange: PropTypes.func.isRequired,
82+
onPartBlur: PropTypes.func.isRequired,
83+
overridableId: PropTypes.string,
84+
toLabel: PropTypes.string,
85+
placeholders: PropTypes.shape({
86+
YYYY: PropTypes.string,
87+
MM: PropTypes.string,
88+
DD: PropTypes.string,
89+
}),
90+
};
91+
92+
DateRangeInputs.defaultProps = {
93+
overridableId: "",
94+
toLabel: "to",
95+
placeholders: undefined,
96+
};
97+
98+
const DateRangeInputsElement = ({
99+
format,
100+
values,
101+
disabled,
102+
onPartChange,
103+
onPartBlur,
104+
overridableId,
105+
toLabel,
106+
placeholders,
107+
}) => {
108+
const { buildUID } = useContext(AppContext);
109+
const parts = format.split("-");
110+
const isStacked = format !== "YYYY";
111+
const toText = toLabel ?? "to";
112+
const mergedPlaceholders = placeholders ?? {};
113+
114+
const renderParts = (side) =>
115+
parts.map((part) => {
116+
const field = getFieldName(side, part);
117+
const config = getPartConfig(part, mergedPlaceholders);
118+
119+
return (
120+
<Input
121+
key={`${side}-${part}`}
122+
size="mini"
123+
placeholder={config.placeholder}
124+
value={values[field] ?? ""}
125+
disabled={disabled}
126+
inputMode="numeric"
127+
onChange={onPartChange(field, config.maxLength)}
128+
onBlur={onPartBlur}
129+
style={{ width: config.width }}
130+
/>
131+
);
132+
});
133+
134+
return (
135+
<Overridable
136+
id={buildUID("RangeFacet.DateInputs.Layout", overridableId)}
137+
format={format}
138+
values={values}
139+
disabled={disabled}
140+
>
141+
<div className={`searchkit-daterange-inputs${isStacked ? " is-stacked" : ""}`}>
142+
{isStacked ? (
143+
<>
144+
<div className="searchkit-daterange-inputs-row">
145+
{renderParts("from")}
146+
<span>{toText}</span>
147+
</div>
148+
<div className="searchkit-daterange-inputs-row">{renderParts("to")}</div>
149+
</>
150+
) : (
151+
<>
152+
{["from", "to"].map((side, sideIndex) => (
153+
<React.Fragment key={side}>
154+
{renderParts(side)}
155+
{sideIndex === 0 && <span>{toText}</span>}
156+
</React.Fragment>
157+
))}
158+
</>
159+
)}
160+
</div>
161+
</Overridable>
162+
);
163+
};
164+
165+
DateRangeInputsElement.propTypes = {
166+
format: PropTypes.string.isRequired,
167+
values: PropTypes.object.isRequired,
168+
disabled: PropTypes.bool.isRequired,
169+
onPartChange: PropTypes.func.isRequired,
170+
onPartBlur: PropTypes.func.isRequired,
171+
overridableId: PropTypes.string,
172+
toLabel: PropTypes.string,
173+
placeholders: PropTypes.shape({
174+
YYYY: PropTypes.string,
175+
MM: PropTypes.string,
176+
DD: PropTypes.string,
177+
}),
178+
};
179+
180+
DateRangeInputsElement.defaultProps = {
181+
overridableId: "",
182+
toLabel: "to",
183+
placeholders: undefined,
184+
};
185+
186+
export default DateRangeInputs;

0 commit comments

Comments
 (0)