Skip to content

Commit

Permalink
Added a date and time picker
Browse files Browse the repository at this point in the history
  • Loading branch information
Ayoub Baàli committed Jan 30, 2021
1 parent 171f921 commit 7246d32
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 56 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"moment": "^2.27.0",
"normalize.css": "^8.0.1",
"react": "16.12.0",
"react-datepicker": "^3.4.1",
"react-dom": "^16.13.1",
"react-flot": "^1.3.0",
"react-keybind": "^0.8.1",
Expand Down
80 changes: 34 additions & 46 deletions webapp/javascript/components/DateRangePicker.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { useDispatch, useSelector } from "react-redux";

import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faClock } from "@fortawesome/free-solid-svg-icons";

import DatePicker from "react-datepicker";
import OutsideClickHandler from "react-outside-click-handler";
import moment from "moment";
import { setDateRange } from "../redux/actions";
import humanReadableRange from "../util/formatDate";

const defaultPresets = [
[
Expand All @@ -30,24 +30,12 @@ const defaultPresets = [
{ label: "Last 5 years", from: "now-5y", until: "now" },
],
];

const multiplierMapping = {
s: "second",
m: "minute",
h: "hour",
d: "day",
w: "week",
M: "month",
y: "year",
};

function DateRangePicker() {
const dispatch = useDispatch();
const from = useSelector((state) => state.from);
const until = useSelector((state) => state.until);

const [opened, setOpened] = useState(false);
const [presets, setPresets] = useState(defaultPresets);
const readableDateForm = humanReadableRange(until, from);

const updateFrom = (from) => {
dispatch(setDateRange(from, until));
Expand All @@ -61,23 +49,6 @@ function DateRangePicker() {
dispatch(setDateRange(from, until));
};

const humanReadableRange = () => {
if (until === "now") {
const m = from.match(/^now-(?<number>\d+)(?<multiplier>\D+)$/);
if (m && multiplierMapping[m.groups.multiplier]) {
let multiplier = multiplierMapping[m.groups.multiplier];
if (m.groups.number > 1) {
multiplier += "s";
}
return `Last ${m.groups.number} ${multiplier}`;
}
}
return `${moment(from * 1000).format("lll")}${moment(
until * 1000
).format("lll")}`;
// return from + " to " +until;
};

const toggleDropdown = () => {
setOpened(!opened);
};
Expand All @@ -94,19 +65,24 @@ function DateRangePicker() {
return (
<div className={opened ? "drp-container opened" : "drp-container"}>
<OutsideClickHandler onOutsideClick={hideDropdown}>
<button className="btn drp-button" onClick={toggleDropdown}>
<button
type="button"
className="btn drp-button"
onClick={toggleDropdown}
>
<FontAwesomeIcon icon={faClock} />
<span>{humanReadableRange()}</span>
<span>{readableDateForm.range}</span>
</button>
<div className="drp-dropdown">
<h4>Quick Presets</h4>
<div className="drp-presets">
{presets.map((arr, i) => (
{defaultPresets.map((arr, i) => (
<div key={`preset-${i + 1}`} className="drp-preset-column">
{arr.map((x) => (
<button
type="button"
className={`drp-preset ${
x.label === humanReadableRange() ? "active" : ""
x.label === readableDateForm.range ? "active" : ""
}`}
key={x.label}
onClick={() => selectPreset(x)}
Expand All @@ -119,25 +95,37 @@ function DateRangePicker() {
</div>
<h4>Custom Date Range</h4>
<div className="drp-calendar-input-group">
<input
<DatePicker
className="followed-by-btn"
onChange={(e) => updateFrom(e.target.value)}
onBlur={updateDateRange}
value={from}
showTimeSelect
dateFormat="MMM d, yyyy h:mm aa"
onChange={(date) => updateFrom(date / 1000)}
onBlur={() => updateDateRange()}
selected={readableDateForm.from}
/>
<button className="drp-calendar-btn btn" onClick={updateDateRange}>
<button
type="button"
className="drp-calendar-btn btn"
onClick={updateDateRange}
>
<FontAwesomeIcon icon={faClock} />
Update
</button>
</div>
<div className="drp-calendar-input-group">
<input
<DatePicker
className="followed-by-btn"
onChange={(e) => updateUntil(e.target.value)}
onBlur={updateDateRange}
value={until}
showTimeSelect
dateFormat="MMM d, yyyy h:mm aa"
onChange={(date) => updateUntil(date / 1000)}
onBlur={() => updateDateRange()}
selected={readableDateForm.until}
/>
<button className="drp-calendar-btn btn" onClick={updateDateRange}>
<button
type="button"
className="drp-calendar-btn btn"
onClick={updateDateRange}
>
<FontAwesomeIcon icon={faClock} />
Update
</button>
Expand Down
41 changes: 41 additions & 0 deletions webapp/javascript/util/formatDate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import moment from "moment";

const multiplierMapping = {
s: "second",
m: "minute",
h: "hour",
d: "day",
w: "week",
M: "month",
y: "year",
};

export default function humanReadableRange(until, from) {
if (until === "now" && typeof from === "string") {
const m = from.match(/^now-(?<number>\d+)(?<multiplier>\D+)$/);
if (m && multiplierMapping[m.groups.multiplier]) {
let multiplier = multiplierMapping[m.groups.multiplier];
if (m.groups.number > 1) {
multiplier += "s";
}
const readableDateForm = {
range: `Last ${m.groups.number} ${multiplier}`,
from: moment().add(-m.groups.number, multiplier).toDate(),
until: moment().toDate(),
};
return readableDateForm;
}
}
const readableDateForm = {
range: `${moment(from * 1000).format("lll")}${
until === "now"
? moment().format("lll")
: moment(until * 1000).format("lll")
}`,
from: moment(from * 1000).toDate(),
until: until === "now" ? moment().toDate() : moment(until * 1000).toDate(),
};

return readableDateForm;
// return from + " to " +until;
}
23 changes: 16 additions & 7 deletions webapp/sass/components/daterangepicker.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
@use "../variables" as *;
@use "../mixins/outline" as *;
/**
* DatePicker Compenent styles
* TODO: adjust styling
*/
@import "../../node_modules/react-datepicker/dist/react-datepicker.css";
@import "../../node_modules/react-datepicker/dist/react-datepicker-cssmodules.css";

.drp-button {
white-space: nowrap;
Expand All @@ -21,18 +27,20 @@
right: 0px;
width: 400px;
padding: 20px 20px 20px 20px;
border: 1px solid rgb(75, 75, 75);
box-shadow: 0 5px 20px rgba(0,0,0,0.9);
border: 1px solid rgb(75, 75, 75);
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.9);
border-radius: 4px;

z-index: 1;
}

h4:nth-child(1), h5:nth-child(1) {
h4:nth-child(1),
h5:nth-child(1) {
margin-top: 0;
}

h4, h5 {
h4,
h5 {
margin: 10px 0;
}

Expand All @@ -53,10 +61,11 @@
border: none;
text-align: left;
padding: 2px 0;
color: rgba(255,255,255,0.66);
color: rgba(255, 255, 255, 0.66);

&:hover, &.active {
color: rgba(255,255,255,1);
&:hover,
&.active {
color: rgba(255, 255, 255, 1);
}
}

Expand Down
63 changes: 60 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3136,6 +3136,14 @@ cosmiconfig@^5.0.0:
js-yaml "^3.13.1"
parse-json "^4.0.0"

create-react-context@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.3.0.tgz#546dede9dc422def0d3fc2fe03afe0bc0f4f7d8c"
integrity sha512-dNldIoSuNSvlTJ7slIKC/ZFGKexBMBrrcc+TTe1NdmROnaASuLPvqpwj9v4XS4uXZ8+YPu0sNmShX2rXI5LNsw==
dependencies:
gud "^1.0.0"
warning "^4.0.3"

cross-spawn@^5.0.1:
version "5.1.0"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
Expand Down Expand Up @@ -3275,6 +3283,11 @@ data-urls@^2.0.0:
whatwg-mimetype "^2.3.0"
whatwg-url "^8.0.0"

date-fns@^2.0.1:
version "2.16.1"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.16.1.tgz#05775792c3f3331da812af253e1a935851d3834b"
integrity sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ==

dateformat@^3.0.0:
version "3.0.3"
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
Expand Down Expand Up @@ -3331,7 +3344,7 @@ decompress-response@^5.0.0:
dependencies:
mimic-response "^2.0.0"

deep-equal@^1.0.1:
deep-equal@^1.0.1, deep-equal@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a"
integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==
Expand Down Expand Up @@ -4732,6 +4745,11 @@ growly@^1.3.0:
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=

gud@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0"
integrity sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==

gzip-size@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462"
Expand Down Expand Up @@ -7363,6 +7381,11 @@ pkg-dir@^5.0.0:
dependencies:
find-up "^5.0.0"

popper.js@^1.14.4:
version "1.16.1"
resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b"
integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==

posix-character-classes@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
Expand Down Expand Up @@ -7536,7 +7559,7 @@ prop-types-exact@^1.2.0:
object.assign "^4.1.0"
reflect.ownkeys "^0.2.0"

prop-types@^15.5.10, prop-types@^15.6.2, prop-types@^15.7.2:
prop-types@^15.5.10, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
Expand Down Expand Up @@ -7632,6 +7655,17 @@ randombytes@^2.1.0:
dependencies:
safe-buffer "^5.1.0"

react-datepicker@^3.4.1:
version "3.4.1"
resolved "https://registry.yarnpkg.com/react-datepicker/-/react-datepicker-3.4.1.tgz#3c8e8989f1ab31a767c17170a2d1318aa3c3b9ef"
integrity sha512-ASyVb7UmVx1vzeITidD7Cr/EXRXhKyjjbSkBndPc1MipYq4rqQ3eMFgvRQzpsXc3JmIMFgICm7nmN6Otc1GE/Q==
dependencies:
classnames "^2.2.6"
date-fns "^2.0.1"
prop-types "^15.7.2"
react-onclickoutside "^6.9.0"
react-popper "^1.3.4"

react-dom@^16.13.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f"
Expand Down Expand Up @@ -7681,6 +7715,11 @@ react-modal@^3.12.1:
react-lifecycles-compat "^3.0.0"
warning "^4.0.3"

react-onclickoutside@^6.9.0:
version "6.10.0"
resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.10.0.tgz#05abb592575b08b4d129003494056b9dff46eeeb"
integrity sha512-7i2L3ef+0ILXpL6P+Hg304eCQswh4jl3ynwR71BSlMU49PE2uk31k8B2GkP6yE9s2D4jTGKnzuSpzWxu4YxfQQ==

react-outside-click-handler@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/react-outside-click-handler/-/react-outside-click-handler-1.3.0.tgz#3831d541ac059deecd38ec5423f81e80ad60e115"
Expand All @@ -7692,6 +7731,19 @@ react-outside-click-handler@^1.3.0:
object.values "^1.1.0"
prop-types "^15.7.2"

react-popper@^1.3.4:
version "1.3.7"
resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-1.3.7.tgz#f6a3471362ef1f0d10a4963673789de1baca2324"
integrity sha512-nmqYTx7QVjCm3WUZLeuOomna138R1luC4EqkW3hxJUrAe+3eNz3oFCLYdnPwILfn0mX1Ew2c3wctrjlUMYYUww==
dependencies:
"@babel/runtime" "^7.1.2"
create-react-context "^0.3.0"
deep-equal "^1.1.1"
popper.js "^1.14.4"
prop-types "^15.6.1"
typed-styles "^0.0.7"
warning "^4.0.2"

react-redux@^7.2.1:
version "7.2.1"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.1.tgz#8dedf784901014db2feca1ab633864dee68ad985"
Expand Down Expand Up @@ -9190,6 +9242,11 @@ type-fest@^0.8.1:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==

typed-styles@^0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/typed-styles/-/typed-styles-0.0.7.tgz#93392a008794c4595119ff62dde6809dbc40a3d9"
integrity sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q==

typedarray-to-buffer@^3.1.5:
version "3.1.5"
resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080"
Expand Down Expand Up @@ -9360,7 +9417,7 @@ walker@^1.0.7, walker@~1.0.5:
dependencies:
makeerror "1.0.x"

warning@^4.0.3:
warning@^4.0.2, warning@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==
Expand Down

0 comments on commit 7246d32

Please sign in to comment.