From 0595aa026dc79a7fe24fe7f7f40a96d3022e2f9f Mon Sep 17 00:00:00 2001 From: Alexey Kuzniecov Date: Tue, 29 Mar 2022 22:52:15 +0300 Subject: [PATCH 01/15] feat: add basic styles for timePiker --- src/components/Layout/search.js | 5 +++ .../Examples/TimeRangePicker/TimeRangeList.js | 42 +++++++++++++++++++ .../TimeRangePicker/TimeRangePicker.js | 26 ++++++++++++ .../TimeRangePicker/TimeRangePickerBody.js | 36 ++++++++++++++++ 4 files changed, 109 insertions(+) create mode 100644 src/pages/Examples/TimeRangePicker/TimeRangeList.js create mode 100644 src/pages/Examples/TimeRangePicker/TimeRangePicker.js create mode 100644 src/pages/Examples/TimeRangePicker/TimeRangePickerBody.js diff --git a/src/components/Layout/search.js b/src/components/Layout/search.js index 789897383..cf1a00ef8 100644 --- a/src/components/Layout/search.js +++ b/src/components/Layout/search.js @@ -1,3 +1,4 @@ +import { TimeRangePicker } from "../../pages/Examples/TimeRangePicker/TimeRangePicker"; import { RefreshButton } from "../RefreshButton"; import { UserProfile } from "../UserProfile/user-profile"; @@ -15,6 +16,10 @@ export function SearchLayout({
{title}
+
+ {/* temporary for example view */} + +
{onRefresh && } {extra} diff --git a/src/pages/Examples/TimeRangePicker/TimeRangeList.js b/src/pages/Examples/TimeRangePicker/TimeRangeList.js new file mode 100644 index 000000000..628c5fcec --- /dev/null +++ b/src/pages/Examples/TimeRangePicker/TimeRangeList.js @@ -0,0 +1,42 @@ +import { v4 as uuidv4 } from "uuid"; + +export const TimeRangeList = ({selected = false}) => { + + const rangeValues = [ + {title: '1 hour', value: 1}, + {title: '2 hours', value: 2}, + {title: '3 hours', value: 3}, + {title: '4 hours', value: 4}, + {title: '5 hours', value: 5}, + {title: '6 hours', value: 6}, + {title: '12 hours', value: 7}, + {title: '24 hours', value: 8}, + {title: '2 days', value: 9}, + {title: '3 days', value: 10}, + {title: '4 days', value: 11}, + {title: '5 days', value: 12}, + {title: '6 days', value: 13}, + {title: '7 days', value: 14}, + {title: '2 weeks', value: 15}, + {title: '3 weeks', value: 16}, + {title: '1 month', value: 17}, + ]; + + return ( +
    + {rangeValues.map((range) => { + const id = uuidv4(); + return
  • + + +
  • + })} +
+ ) +} \ No newline at end of file diff --git a/src/pages/Examples/TimeRangePicker/TimeRangePicker.js b/src/pages/Examples/TimeRangePicker/TimeRangePicker.js new file mode 100644 index 000000000..b5e49a906 --- /dev/null +++ b/src/pages/Examples/TimeRangePicker/TimeRangePicker.js @@ -0,0 +1,26 @@ +import { useState } from "react"; +import { TimeRangePickerBody } from "./TimeRangePickerBody"; + +export const TimeRangePicker = () => { + let [isPickerOpen, setIsPickerOpen] = useState(false); + + return ( +
+
{ + setIsPickerOpen(!isPickerOpen) + } + }> +
+ +
+
+ Time range: 2 hours +
+
+ setIsPickerOpen(false)}/> +
+ ) +}; \ No newline at end of file diff --git a/src/pages/Examples/TimeRangePicker/TimeRangePickerBody.js b/src/pages/Examples/TimeRangePicker/TimeRangePickerBody.js new file mode 100644 index 000000000..f2996f38e --- /dev/null +++ b/src/pages/Examples/TimeRangePicker/TimeRangePickerBody.js @@ -0,0 +1,36 @@ +import { TimeRangeList } from "./TimeRangeList"; + +export const TimeRangePickerBody = ({ isOpen, closePicker }) => { + + const visible = isOpen ? 'visible' : 'invisible'; + + return ( +
+
+
Absolute time range
+
+
+
+
From
+
+ now-2h +
+ +
+
+
To
+
+ now +
+ +
+
+ +
+
+
+ +
+
+ ) +}; \ No newline at end of file From 7ccb3be0399114eca54eaefb4d6dc0c6780fea13 Mon Sep 17 00:00:00 2001 From: Alexey Kuzniecov Date: Thu, 31 Mar 2022 19:25:30 +0300 Subject: [PATCH 02/15] dev: add calendar to time picker, style component, refactor --- package-lock.json | 82 +++++++++++++++++++ package.json | 1 + src/App.js | 2 + src/components/Layout/search.js | 5 -- .../TimeRangePicker/TimeRangeList.js | 0 .../TimeRangePicker/TimeRangePicker.js | 24 ++++++ .../TimeRangePicker/TimeRangePickerBody.js | 77 +++++++++++++++++ src/components/TimeRangePicker/index.css | 31 +++++++ src/icons/close-x.js | 18 ++++ src/icons/mini-calendar.js | 11 +++ src/icons/mini-clock.js | 11 +++ .../TimeRangePicker/TimeRangePicker.js | 26 ------ .../TimeRangePicker/TimeRangePickerBody.js | 36 -------- src/pages/Examples/TimeRangePickerDemo.js | 8 ++ 14 files changed, 265 insertions(+), 67 deletions(-) rename src/{pages/Examples => components}/TimeRangePicker/TimeRangeList.js (100%) create mode 100644 src/components/TimeRangePicker/TimeRangePicker.js create mode 100644 src/components/TimeRangePicker/TimeRangePickerBody.js create mode 100644 src/components/TimeRangePicker/index.css create mode 100644 src/icons/close-x.js create mode 100644 src/icons/mini-calendar.js create mode 100644 src/icons/mini-clock.js delete mode 100644 src/pages/Examples/TimeRangePicker/TimeRangePicker.js delete mode 100644 src/pages/Examples/TimeRangePicker/TimeRangePickerBody.js create mode 100644 src/pages/Examples/TimeRangePickerDemo.js diff --git a/package-lock.json b/package-lock.json index 2c530f0d6..77e94548c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,6 +32,7 @@ "qs": "^6.7.0", "randomstring": "^1.2.1", "react": "^17.0.2", + "react-calendar": "^3.7.0", "react-country-flag": "^2.3.1", "react-dom": "^17.0.2", "react-hook-form": "^7.15.0", @@ -3748,6 +3749,14 @@ "@xtuc/long": "4.2.2" } }, + "node_modules/@wojtekmaj/date-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@wojtekmaj/date-utils/-/date-utils-1.0.3.tgz", + "integrity": "sha512-1VPkkTBk07gMR1fjpBtse4G+oJqpmE+0gUFB0dg3VIL7qJmUVaBoD/vlzMm/jNeOPfvlmerl1lpnsZyBUFIRuw==", + "funding": { + "url": "https://github.com/wojtekmaj/date-utils?sponsor=1" + } + }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -9024,6 +9033,14 @@ "node": ">=6" } }, + "node_modules/get-user-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-user-locale/-/get-user-locale-1.4.0.tgz", + "integrity": "sha512-gQo03lP1OArHLKlnoglqrGGl7b04u2EP9Xutmp72cMdtrrSD7ZgIsCsUKZynYWLDkVJW33Cj3pliP7uP0UonHQ==", + "dependencies": { + "lodash.once": "^4.1.1" + } + }, "node_modules/get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -11841,6 +11858,11 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, "node_modules/lodash.template": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", @@ -12230,6 +12252,14 @@ "readable-stream": "^2.0.1" } }, + "node_modules/merge-class-names": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/merge-class-names/-/merge-class-names-1.4.2.tgz", + "integrity": "sha512-bOl98VzwCGi25Gcn3xKxnR5p/WrhWFQB59MS/aGENcmUc6iSm96yrFDF0XSNurX9qN4LbJm0R9kfvsQ17i8zCw==", + "funding": { + "url": "https://github.com/wojtekmaj/merge-class-names?sponsor=1" + } + }, "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -15561,6 +15591,24 @@ "node": ">=10" } }, + "node_modules/react-calendar": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/react-calendar/-/react-calendar-3.7.0.tgz", + "integrity": "sha512-zkK95zWLWLC6w3O7p3SHx/FJXEyyD2UMd4jr3CrKD+G73N+G5vEwrXxYQCNivIPoFNBjqoyYYGlkHA+TBDPLCw==", + "dependencies": { + "@wojtekmaj/date-utils": "^1.0.2", + "get-user-locale": "^1.2.0", + "merge-class-names": "^1.1.1", + "prop-types": "^15.6.0" + }, + "funding": { + "url": "https://github.com/wojtekmaj/react-calendar?sponsor=1" + }, + "peerDependencies": { + "react": "^16.3.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.3.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-country-flag": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/react-country-flag/-/react-country-flag-2.3.1.tgz", @@ -24346,6 +24394,11 @@ "@xtuc/long": "4.2.2" } }, + "@wojtekmaj/date-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@wojtekmaj/date-utils/-/date-utils-1.0.3.tgz", + "integrity": "sha512-1VPkkTBk07gMR1fjpBtse4G+oJqpmE+0gUFB0dg3VIL7qJmUVaBoD/vlzMm/jNeOPfvlmerl1lpnsZyBUFIRuw==" + }, "@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -28771,6 +28824,14 @@ "pump": "^3.0.0" } }, + "get-user-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-user-locale/-/get-user-locale-1.4.0.tgz", + "integrity": "sha512-gQo03lP1OArHLKlnoglqrGGl7b04u2EP9Xutmp72cMdtrrSD7ZgIsCsUKZynYWLDkVJW33Cj3pliP7uP0UonHQ==", + "requires": { + "lodash.once": "^4.1.1" + } + }, "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -31053,6 +31114,11 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, "lodash.template": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", @@ -31422,6 +31488,11 @@ "readable-stream": "^2.0.1" } }, + "merge-class-names": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/merge-class-names/-/merge-class-names-1.4.2.tgz", + "integrity": "sha512-bOl98VzwCGi25Gcn3xKxnR5p/WrhWFQB59MS/aGENcmUc6iSm96yrFDF0XSNurX9qN4LbJm0R9kfvsQ17i8zCw==" + }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -34190,6 +34261,17 @@ "whatwg-fetch": "^3.4.1" } }, + "react-calendar": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/react-calendar/-/react-calendar-3.7.0.tgz", + "integrity": "sha512-zkK95zWLWLC6w3O7p3SHx/FJXEyyD2UMd4jr3CrKD+G73N+G5vEwrXxYQCNivIPoFNBjqoyYYGlkHA+TBDPLCw==", + "requires": { + "@wojtekmaj/date-utils": "^1.0.2", + "get-user-locale": "^1.2.0", + "merge-class-names": "^1.1.1", + "prop-types": "^15.6.0" + } + }, "react-country-flag": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/react-country-flag/-/react-country-flag-2.3.1.tgz", diff --git a/package.json b/package.json index 2eac2ff23..313116288 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "qs": "^6.7.0", "randomstring": "^1.2.1", "react": "^17.0.2", + "react-calendar": "^3.7.0", "react-country-flag": "^2.3.1", "react-dom": "^17.0.2", "react-hook-form": "^7.15.0", diff --git a/src/App.js b/src/App.js index 3ebaf9981..9f0e51bb8 100644 --- a/src/App.js +++ b/src/App.js @@ -27,6 +27,7 @@ import { DropdownDemoPage } from "./pages/Examples/dropdown-demo"; import { HealthPage } from "./pages/health"; import { TopologySelectorModalPage } from "./pages/Examples/TopologySelectorModalPage/TopologySelectorModalPage"; import { ModalPage } from "./pages/Examples/Modal/modal-page"; +import { TimeRangePickerDemo } from './pages/Examples/TimeRangePickerDemo'; const queryClient = new QueryClient({ defaultOptions: { @@ -121,6 +122,7 @@ export function App() { } /> } /> + } /> } diff --git a/src/components/Layout/search.js b/src/components/Layout/search.js index cf1a00ef8..789897383 100644 --- a/src/components/Layout/search.js +++ b/src/components/Layout/search.js @@ -1,4 +1,3 @@ -import { TimeRangePicker } from "../../pages/Examples/TimeRangePicker/TimeRangePicker"; import { RefreshButton } from "../RefreshButton"; import { UserProfile } from "../UserProfile/user-profile"; @@ -16,10 +15,6 @@ export function SearchLayout({
{title}
-
- {/* temporary for example view */} - -
{onRefresh && } {extra} diff --git a/src/pages/Examples/TimeRangePicker/TimeRangeList.js b/src/components/TimeRangePicker/TimeRangeList.js similarity index 100% rename from src/pages/Examples/TimeRangePicker/TimeRangeList.js rename to src/components/TimeRangePicker/TimeRangeList.js diff --git a/src/components/TimeRangePicker/TimeRangePicker.js b/src/components/TimeRangePicker/TimeRangePicker.js new file mode 100644 index 000000000..5e90fb5d0 --- /dev/null +++ b/src/components/TimeRangePicker/TimeRangePicker.js @@ -0,0 +1,24 @@ +import { useState } from "react"; +import { TimeRangePickerBody } from "./TimeRangePickerBody"; +import "./index.css"; +import { MiniClockIcon } from "../../icons/mini-clock"; + +export const TimeRangePicker = () => { + let [isPickerOpen, setIsPickerOpen] = useState(false); + + return ( +
+
setIsPickerOpen(!isPickerOpen)}> +
+ +
+
+ Time range: 2 hours +
+
+ setIsPickerOpen(false)}/> +
+ ); +}; diff --git a/src/components/TimeRangePicker/TimeRangePickerBody.js b/src/components/TimeRangePicker/TimeRangePickerBody.js new file mode 100644 index 000000000..432074d73 --- /dev/null +++ b/src/components/TimeRangePicker/TimeRangePickerBody.js @@ -0,0 +1,77 @@ +import Calendar from 'react-calendar'; +import { TimeRangeList } from "./TimeRangeList"; +import "./index.css"; +import clsx from "clsx"; +import { useState } from 'react'; +import { MiniCalendarIcon } from '../../icons/mini-calendar'; +import { CloseIconX } from '../../icons/close-x'; + + +export const TimeRangePickerBody = ({ isOpen, closePicker }) => { + + let [showCalendar, setShowCalendar] = useState(false); + + return ( +
+
+
+
Select a time range
+
+ +
+
+
+ +
+
+
+
Absolute time range
+
+
+
+
From
+
+
+ +
+
+ +
+
+
+
+
To
+
+
+ +
+
+ +
+
+
+
+ +
+
+
+ +
+
+ ) +}; \ No newline at end of file diff --git a/src/components/TimeRangePicker/index.css b/src/components/TimeRangePicker/index.css new file mode 100644 index 000000000..b90963cc4 --- /dev/null +++ b/src/components/TimeRangePicker/index.css @@ -0,0 +1,31 @@ +.time-range-picker-widget { + width: fit-content; +} + +.time-range-picker-body { + visibility: hidden; + opacity: 0; + top: calc(100% + 2px); + left: -1px; + width: 550px; + transition: 0.2s ease; +} + +.time-range-picker-body.active { + visibility: visible; + opacity: 1; +} + +.calendar-wrapper { + visibility: hidden; + opacity: 0; + top: calc(0% - 1px); + right: calc(100% + 1px); + width: 270px; + transition: 0.2s ease; +} + +.calendar-wrapper.active { + visibility: visible; + opacity: 1; +} \ No newline at end of file diff --git a/src/icons/close-x.js b/src/icons/close-x.js new file mode 100644 index 000000000..059b7792e --- /dev/null +++ b/src/icons/close-x.js @@ -0,0 +1,18 @@ +import PropTypes from "prop-types"; + +export const CloseIconX = ({ size = "16", color = "#000000" }) => ( + + + +); + +CloseIconX.propTypes = { + size: PropTypes.string, + color: PropTypes.string +}; diff --git a/src/icons/mini-calendar.js b/src/icons/mini-calendar.js new file mode 100644 index 000000000..2fd0384fb --- /dev/null +++ b/src/icons/mini-calendar.js @@ -0,0 +1,11 @@ +export const MiniCalendarIcon = () => ( + + + +); diff --git a/src/icons/mini-clock.js b/src/icons/mini-clock.js new file mode 100644 index 000000000..d7a4c9c6e --- /dev/null +++ b/src/icons/mini-clock.js @@ -0,0 +1,11 @@ +export const MiniClockIcon = () => ( + + + +); diff --git a/src/pages/Examples/TimeRangePicker/TimeRangePicker.js b/src/pages/Examples/TimeRangePicker/TimeRangePicker.js deleted file mode 100644 index b5e49a906..000000000 --- a/src/pages/Examples/TimeRangePicker/TimeRangePicker.js +++ /dev/null @@ -1,26 +0,0 @@ -import { useState } from "react"; -import { TimeRangePickerBody } from "./TimeRangePickerBody"; - -export const TimeRangePicker = () => { - let [isPickerOpen, setIsPickerOpen] = useState(false); - - return ( -
-
{ - setIsPickerOpen(!isPickerOpen) - } - }> -
- -
-
- Time range: 2 hours -
-
- setIsPickerOpen(false)}/> -
- ) -}; \ No newline at end of file diff --git a/src/pages/Examples/TimeRangePicker/TimeRangePickerBody.js b/src/pages/Examples/TimeRangePicker/TimeRangePickerBody.js deleted file mode 100644 index f2996f38e..000000000 --- a/src/pages/Examples/TimeRangePicker/TimeRangePickerBody.js +++ /dev/null @@ -1,36 +0,0 @@ -import { TimeRangeList } from "./TimeRangeList"; - -export const TimeRangePickerBody = ({ isOpen, closePicker }) => { - - const visible = isOpen ? 'visible' : 'invisible'; - - return ( -
-
-
Absolute time range
-
-
-
-
From
-
- now-2h -
- -
-
-
To
-
- now -
- -
-
- -
-
-
- -
-
- ) -}; \ No newline at end of file diff --git a/src/pages/Examples/TimeRangePickerDemo.js b/src/pages/Examples/TimeRangePickerDemo.js new file mode 100644 index 000000000..1ce688c77 --- /dev/null +++ b/src/pages/Examples/TimeRangePickerDemo.js @@ -0,0 +1,8 @@ +import { SearchLayout } from "../../components/Layout"; +import { TimeRangePicker } from "../../components/TimeRangePicker/TimeRangePicker"; + +export const TimeRangePickerDemo = () => ( + + + +); From 64d49d72130bb543d13b7b5c192470ac773dd60e Mon Sep 17 00:00:00 2001 From: Alexey Kuzniecov Date: Sun, 3 Apr 2022 23:42:10 +0300 Subject: [PATCH 03/15] dev: finish styled component, start implement logic --- .../TimeRangePicker/TimeRangeList.js | 55 ++++----- .../TimeRangePicker/TimeRangePicker.js | 27 +++-- .../TimeRangePicker/TimeRangePickerBody.js | 93 +++++++++++---- src/components/TimeRangePicker/index.css | 108 +++++++++++++++++- src/components/TimeRangePicker/index.js | 1 + src/components/TimeRangePicker/rangeValues.js | 31 +++++ src/icons/close-x.js | 18 --- src/icons/mini-calendar.js | 11 -- src/icons/mini-clock.js | 11 -- src/pages/Examples/TimeRangePickerDemo.js | 4 +- 10 files changed, 249 insertions(+), 110 deletions(-) create mode 100644 src/components/TimeRangePicker/index.js create mode 100644 src/components/TimeRangePicker/rangeValues.js delete mode 100644 src/icons/close-x.js delete mode 100644 src/icons/mini-calendar.js delete mode 100644 src/icons/mini-clock.js diff --git a/src/components/TimeRangePicker/TimeRangeList.js b/src/components/TimeRangePicker/TimeRangeList.js index 628c5fcec..c4433e0ec 100644 --- a/src/components/TimeRangePicker/TimeRangeList.js +++ b/src/components/TimeRangePicker/TimeRangeList.js @@ -1,42 +1,29 @@ import { v4 as uuidv4 } from "uuid"; +import { rangeValues } from "./rangeValues"; -export const TimeRangeList = ({selected = false}) => { - - const rangeValues = [ - {title: '1 hour', value: 1}, - {title: '2 hours', value: 2}, - {title: '3 hours', value: 3}, - {title: '4 hours', value: 4}, - {title: '5 hours', value: 5}, - {title: '6 hours', value: 6}, - {title: '12 hours', value: 7}, - {title: '24 hours', value: 8}, - {title: '2 days', value: 9}, - {title: '3 days', value: 10}, - {title: '4 days', value: 11}, - {title: '5 days', value: 12}, - {title: '6 days', value: 13}, - {title: '7 days', value: 14}, - {title: '2 weeks', value: 15}, - {title: '3 weeks', value: 16}, - {title: '1 month', value: 17}, - ]; - +export const TimeRangeList = ({ selected = false }) => { return (
    {rangeValues.map((range) => { const id = uuidv4(); - return
  • - - -
  • + return ( +
  • + + +
  • + ); })}
- ) -} \ No newline at end of file + ); +}; diff --git a/src/components/TimeRangePicker/TimeRangePicker.js b/src/components/TimeRangePicker/TimeRangePicker.js index 5e90fb5d0..4eddf5576 100644 --- a/src/components/TimeRangePicker/TimeRangePicker.js +++ b/src/components/TimeRangePicker/TimeRangePicker.js @@ -1,24 +1,35 @@ +/* eslint-disable jsx-a11y/no-static-element-interactions */ +/* eslint-disable jsx-a11y/click-events-have-key-events */ import { useState } from "react"; +import { FiClock } from "react-icons/fi"; import { TimeRangePickerBody } from "./TimeRangePickerBody"; import "./index.css"; -import { MiniClockIcon } from "../../icons/mini-clock"; export const TimeRangePicker = () => { - let [isPickerOpen, setIsPickerOpen] = useState(false); + const [isPickerOpen, setIsPickerOpen] = useState(false); + const [currentRangeDisplay, setCurrentRangeDisplay] = useState("some-range"); return ( -
+
setIsPickerOpen(!isPickerOpen)}> + onClick={() => + setIsPickerOpen((prevIsPickerValue) => !prevIsPickerValue) + } + >
- +
-
- Time range: 2 hours +
+ Time range: {currentRangeDisplay}
- setIsPickerOpen(false)}/> + setIsPickerOpen(false)} + currentRange={currentRangeDisplay} + setCurrentRange={setCurrentRangeDisplay} + />
); }; diff --git a/src/components/TimeRangePicker/TimeRangePickerBody.js b/src/components/TimeRangePicker/TimeRangePickerBody.js index 432074d73..139a7eaf7 100644 --- a/src/components/TimeRangePicker/TimeRangePickerBody.js +++ b/src/components/TimeRangePicker/TimeRangePickerBody.js @@ -1,36 +1,70 @@ -import Calendar from 'react-calendar'; +import Calendar from "react-calendar"; +import clsx from "clsx"; +import { useState } from "react"; +import { GrClose } from "react-icons/gr"; +import { FaRegCalendarAlt, FaAngleLeft, FaAngleRight } from "react-icons/fa"; +import dayjs from "dayjs"; import { TimeRangeList } from "./TimeRangeList"; import "./index.css"; -import clsx from "clsx"; -import { useState } from 'react'; -import { MiniCalendarIcon } from '../../icons/mini-calendar'; -import { CloseIconX } from '../../icons/close-x'; +export const TimeRangePickerBody = ({ + isOpen, + closePicker, + currentRangeDisplay, + setCurrentRangeDisplay +}) => { + const timeFormat = "YYYY-MM-DD HH:mm"; + const [showCalendar, setShowCalendar] = useState(false); + const [value, setValue] = useState(new Date()); -export const TimeRangePickerBody = ({ isOpen, closePicker }) => { + const onChangeRange = (value) => { + setValue(value); + console.log(value[0].getTime()); + setCurrentRangeDisplay(dayjs(value[0]).format(timeFormat)); + }; - let [showCalendar, setShowCalendar] = useState(false); - return ( -
-
+
+
-
Select a time range
+
Select a time range
- + } + prevLabel={} + className="react-calendar-custom" + tileClassName="react-calendar-custom__tile" + value={value} + onChange={onChangeRange} + />
-
Absolute time range
+
Absolute time range
@@ -40,38 +74,49 @@ export const TimeRangePickerBody = ({ isOpen, closePicker }) => {
-
-
To
+
To
-
- +
- ) -}; \ No newline at end of file + ); +}; diff --git a/src/components/TimeRangePicker/index.css b/src/components/TimeRangePicker/index.css index b90963cc4..f44e63606 100644 --- a/src/components/TimeRangePicker/index.css +++ b/src/components/TimeRangePicker/index.css @@ -8,6 +8,7 @@ top: calc(100% + 2px); left: -1px; width: 550px; + height: 320px; transition: 0.2s ease; } @@ -19,13 +20,114 @@ .calendar-wrapper { visibility: hidden; opacity: 0; - top: calc(0% - 1px); + top: -1px; right: calc(100% + 1px); - width: 270px; transition: 0.2s ease; } .calendar-wrapper.active { visibility: visible; opacity: 1; -} \ No newline at end of file +} + +/*calendar*/ +.react-calendar-custom__tile { + color: #000; + background-color: #f9fafb; + font-size: 14px; + border: 1px solid transparent; +} + +.react-calendar-custom__tile:hover { + position: relative; +} + +.react-calendar-custom { + z-index: 40; + background-color: #f9fafb; + width: 268px; + font-size: 14px; +} + +.react-calendar__navigation { + display: flex; +} + +.react-calendar__navigation__label, +.react-calendar__navigation__arrow, +.react-calendar__navigation { + padding-top: 4px; + background-color: inherit; + color: #000; + border: 0; + font-weight: 500; +} + +.react-calendar__month-view__weekdays { + background-color: inherit; + text-align: center; + color: #230e99; +} + +.react-calendar__month-view__weekdays abbr { + border: 0; + text-decoration: none; + cursor: default; + display: block; + padding: 4px 0 4px 0; + } + +.react-calendar__month-view__days { + background-color: inherit; +} + +.react-calendar__tile, +.react-calendar__tile--now { + margin-bottom: 4px; + background-color: inherit; + height: 26px; +} + +.react-calendar__navigation__label, +.react-calendar__navigation > button:focus, +.time-picker-calendar-tile:focus { + outline: 0; +} + +.react-calendar__tile--active, +.react-calendar__tile--active:hover { + color: #fff; + font-weight: 400; + background: rgb(61, 113, 217); + box-shadow: none; + border: 0px; +} + +.react-calendar__tile--rangeEnd, +.react-calendar__tile--rangeStart { + padding: 0; + border: 0px; + color: #fff; + font-weight: 400; + background: rgb(61, 113, 217); +} + +.react-calendar__tile--rangeEnd abbr, +.react-calendar__tile--rangeStart abbr { + background-color: rgb(61, 113, 217); + border-radius: 100px; + display: block; + padding-top: 2px; + height: 26px; +} + +.react-calendar__tile--rangeStart { + border-top-left-radius: 20px; + border-bottom-left-radius: 20px; +} + +.react-calendar__tile--rangeEnd { + border-top-right-radius: 20px; + border-bottom-right-radius: 20px; +} + diff --git a/src/components/TimeRangePicker/index.js b/src/components/TimeRangePicker/index.js new file mode 100644 index 000000000..900a2ae52 --- /dev/null +++ b/src/components/TimeRangePicker/index.js @@ -0,0 +1 @@ +export { TimeRangePicker } from "./TimeRangePicker"; diff --git a/src/components/TimeRangePicker/rangeValues.js b/src/components/TimeRangePicker/rangeValues.js new file mode 100644 index 000000000..3476a358d --- /dev/null +++ b/src/components/TimeRangePicker/rangeValues.js @@ -0,0 +1,31 @@ +/* eslint-disable prettier/prettier */ +const intervalsInMs = { + y: 31536000000, + M: 2592000000, + w: 604800000, + d: 86400000, + h: 3600000, + m: 60000, + s: 1000, + ms: 1 +}; + +export const rangeValues = [ + { title: "1 hour", from: "now-1h", to: "now", interval: intervalsInMs.h }, + { title: "2 hours", from: "now-2h", to: "now", interval: intervalsInMs.h * 2 }, + { title: "3 hours", from: "now-3h", to: "now", interval: intervalsInMs.h * 3 }, + { title: "4 hours", from: "now-4h", to: "now", interval: intervalsInMs.h * 4 }, + { title: "5 hours", from: "now-5h", to: "now", interval: intervalsInMs.h * 5 }, + { title: "6 hours", from: "now-6h", to: "now", interval: intervalsInMs.h * 6 }, + { title: "12 hours", from: "now-12h", to: "now", interval: intervalsInMs.h * 12 }, + { title: "24 hours", from: "now-24h", to: "now", interval: intervalsInMs.h * 24 }, + { title: "2 days", from: "now-2d", to: "now", interval: intervalsInMs.d * 2 }, + { title: "3 days", from: "now-3d", to: "now", interval: intervalsInMs.d * 3 }, + { title: "4 days", from: "now-4d", to: "now", interval: intervalsInMs.d * 4 }, + { title: "5 days", from: "now-5d", to: "now", interval: intervalsInMs.d * 5 }, + { title: "6 days", from: "now-6d", to: "now", interval: intervalsInMs.d * 6 }, + { title: "7 days", from: "now-7d", to: "now", interval: intervalsInMs.d * 7 }, + { title: "2 weeks", from: "now-2w", to: "now", interval: intervalsInMs.w * 2 }, + { title: "3 weeks", from: "now-3w", to: "now", interval: intervalsInMs.w * 3 }, + { title: "1 month", from: "now-1m", to: "now", interval: intervalsInMs.M } +]; diff --git a/src/icons/close-x.js b/src/icons/close-x.js deleted file mode 100644 index 059b7792e..000000000 --- a/src/icons/close-x.js +++ /dev/null @@ -1,18 +0,0 @@ -import PropTypes from "prop-types"; - -export const CloseIconX = ({ size = "16", color = "#000000" }) => ( - - - -); - -CloseIconX.propTypes = { - size: PropTypes.string, - color: PropTypes.string -}; diff --git a/src/icons/mini-calendar.js b/src/icons/mini-calendar.js deleted file mode 100644 index 2fd0384fb..000000000 --- a/src/icons/mini-calendar.js +++ /dev/null @@ -1,11 +0,0 @@ -export const MiniCalendarIcon = () => ( - - - -); diff --git a/src/icons/mini-clock.js b/src/icons/mini-clock.js deleted file mode 100644 index d7a4c9c6e..000000000 --- a/src/icons/mini-clock.js +++ /dev/null @@ -1,11 +0,0 @@ -export const MiniClockIcon = () => ( - - - -); diff --git a/src/pages/Examples/TimeRangePickerDemo.js b/src/pages/Examples/TimeRangePickerDemo.js index 1ce688c77..1322b8f84 100644 --- a/src/pages/Examples/TimeRangePickerDemo.js +++ b/src/pages/Examples/TimeRangePickerDemo.js @@ -3,6 +3,8 @@ import { TimeRangePicker } from "../../components/TimeRangePicker/TimeRangePicke export const TimeRangePickerDemo = () => ( - +
+ +
); From 270ae9071dff38d68e3f2add2f7d9f93c6b9e544 Mon Sep 17 00:00:00 2001 From: Alexey Kuzniecov Date: Mon, 4 Apr 2022 17:49:14 +0300 Subject: [PATCH 04/15] dev: improve time picker behaviour, refactor --- .../TimeRangePicker/TimeRangeList.js | 76 +++++++++++++-- .../TimeRangePicker/TimeRangePicker.js | 18 ++-- .../TimeRangePicker/TimeRangePickerBody.js | 93 ++++++++++++++++--- src/components/TimeRangePicker/helpers.js | 6 ++ src/components/TimeRangePicker/index.css | 4 + .../TimeRangePicker/rangeOptions.js | 21 +++++ src/components/TimeRangePicker/rangeValues.js | 31 ------- 7 files changed, 187 insertions(+), 62 deletions(-) create mode 100644 src/components/TimeRangePicker/helpers.js create mode 100644 src/components/TimeRangePicker/rangeOptions.js delete mode 100644 src/components/TimeRangePicker/rangeValues.js diff --git a/src/components/TimeRangePicker/TimeRangeList.js b/src/components/TimeRangePicker/TimeRangeList.js index c4433e0ec..e85a0fa41 100644 --- a/src/components/TimeRangePicker/TimeRangeList.js +++ b/src/components/TimeRangePicker/TimeRangeList.js @@ -1,24 +1,72 @@ +/* eslint-disable react/require-default-props */ +/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ +/* eslint-disable jsx-a11y/click-events-have-key-events */ +import PropTypes from "prop-types"; import { v4 as uuidv4 } from "uuid"; -import { rangeValues } from "./rangeValues"; +import clsx from "clsx"; +import dayjs from "dayjs"; +import { rangeOptions } from "./rangeOptions"; +import { getFromValues } from "./helpers"; + +export const TimeRangeList = ({ + closePicker, + rangeDisplayValue, + setRangeDisplayValue, + setRangeValue, + setInputValue, + setCalendarValue, + setShowCalendar +}) => { + const isChecked = (option, value) => { + if (!option || !value) { + return false; + } + + return option.from === value.from && option.to === value.to; + }; + + const setOption = (option) => { + const { from } = option; + const { to } = option; + setRangeDisplayValue({ + from, + to, + display: option.display + }); + setInputValue((prevState) => ({ ...prevState, from, to })); + setRangeValue({ + from: dayjs() + .subtract(...getFromValues(option.from)) + .format(), + to: dayjs().format() + }); + closePicker(); + setShowCalendar(false); + setCalendarValue(null); + }; -export const TimeRangeList = ({ selected = false }) => { return (
    - {rangeValues.map((range) => { + {rangeOptions.map((option) => { const id = uuidv4(); return (
  • setOption(option)} + key={id} + className={clsx( + "option-item py-1 px-2 hover:bg-blue-200 flex justify-between items-center", + { active: isChecked(option, rangeDisplayValue) } + )} > {}} + name="range-checkbox" id={id} />
  • @@ -27,3 +75,13 @@ export const TimeRangeList = ({ selected = false }) => {
); }; + +TimeRangeList.propTypes = { + closePicker: PropTypes.func, + rangeDisplayValue: PropTypes.shape({}), + setRangeDisplayValue: PropTypes.func, + setRangeValue: PropTypes.func, + setInputValue: PropTypes.func, + setCalendarValue: PropTypes.func, + setShowCalendar: PropTypes.func +}; diff --git a/src/components/TimeRangePicker/TimeRangePicker.js b/src/components/TimeRangePicker/TimeRangePicker.js index 4eddf5576..ada224f70 100644 --- a/src/components/TimeRangePicker/TimeRangePicker.js +++ b/src/components/TimeRangePicker/TimeRangePicker.js @@ -4,31 +4,35 @@ import { useState } from "react"; import { FiClock } from "react-icons/fi"; import { TimeRangePickerBody } from "./TimeRangePickerBody"; import "./index.css"; +import { defaultValue } from "./rangeOptions"; export const TimeRangePicker = () => { const [isPickerOpen, setIsPickerOpen] = useState(false); - const [currentRangeDisplay, setCurrentRangeDisplay] = useState("some-range"); + const [rangeDisplayValue, setRangeDisplayValue] = useState({ + ...defaultValue + }); + const [rangeValue, setRangeValue] = useState({}); return (
- setIsPickerOpen((prevIsPickerValue) => !prevIsPickerValue) - } + onClick={() => setIsPickerOpen((prevState) => !prevState)} >
- Time range: {currentRangeDisplay} + Time range: {rangeDisplayValue.display}
setIsPickerOpen(false)} - currentRange={currentRangeDisplay} - setCurrentRange={setCurrentRangeDisplay} + rangeDisplayValue={rangeDisplayValue} + setRangeDisplayValue={setRangeDisplayValue} + rangeValue={rangeValue} + setRangeValue={setRangeValue} />
); diff --git a/src/components/TimeRangePicker/TimeRangePickerBody.js b/src/components/TimeRangePicker/TimeRangePickerBody.js index 139a7eaf7..26cad03e2 100644 --- a/src/components/TimeRangePicker/TimeRangePickerBody.js +++ b/src/components/TimeRangePicker/TimeRangePickerBody.js @@ -1,3 +1,5 @@ +/* eslint-disable react/require-default-props */ +import PropTypes from "prop-types"; import Calendar from "react-calendar"; import clsx from "clsx"; import { useState } from "react"; @@ -6,21 +8,51 @@ import { FaRegCalendarAlt, FaAngleLeft, FaAngleRight } from "react-icons/fa"; import dayjs from "dayjs"; import { TimeRangeList } from "./TimeRangeList"; import "./index.css"; +import { defaultValue } from "./rangeOptions"; export const TimeRangePickerBody = ({ isOpen, closePicker, - currentRangeDisplay, - setCurrentRangeDisplay + rangeDisplayValue, + setRangeDisplayValue, + setRangeValue }) => { - const timeFormat = "YYYY-MM-DD HH:mm"; + const displayTimeFormat = "YYYY-MM-DD HH:mm"; const [showCalendar, setShowCalendar] = useState(false); - const [value, setValue] = useState(new Date()); + const [calendarValue, setCalendarValue] = useState(null); + const [inputValue, setInputValue] = useState({ + from: defaultValue.from, + to: defaultValue.to + }); const onChangeRange = (value) => { - setValue(value); - console.log(value[0].getTime()); - setCurrentRangeDisplay(dayjs(value[0]).format(timeFormat)); + const from = dayjs(value[0]).format(displayTimeFormat); + const to = dayjs(value[1]).format(displayTimeFormat); + + setCalendarValue(value); + setInputValue((prevState) => ({ ...prevState, from, to })); + }; + + const applyTimeRange = () => { + if (inputValue.from.includes("now") || inputValue.to.includes("now")) { + setShowCalendar(false); + closePicker(); + return; + } + + const from = dayjs(inputValue.from).format(displayTimeFormat); + const to = dayjs(inputValue.to).format(displayTimeFormat); + setRangeDisplayValue({ + from, + to, + display: `${from} - ${to}` + }); + setRangeValue({ + from: dayjs(inputValue.from).format(), + to: dayjs(inputValue.to).format() + }); + setShowCalendar(false); + closePicker(); }; return ( @@ -58,7 +90,7 @@ export const TimeRangePickerBody = ({ prevLabel={} className="react-calendar-custom" tileClassName="react-calendar-custom__tile" - value={value} + value={calendarValue} onChange={onChangeRange} />
@@ -71,7 +103,16 @@ export const TimeRangePickerBody = ({
From
- + + setInputValue((prevState) => ({ + ...prevState, + from: e.target.value + })) + } + className="px-1 py-0.5 border border-gray-300 rounded-sm focus:border-blue-50 focus:outline-none focus:ring-2 focus:ring-blue-300" + />
- + + setInputValue((prevState) => ({ + ...prevState, + to: e.target.value + })) + } + className="px-1 py-0.5 border border-gray-300 rounded-sm focus:border-blue-50 focus:outline-none focus:ring-2 focus:ring-blue-300" + />
- +
); }; + +TimeRangePickerBody.propTypes = { + isOpen: PropTypes.bool, + closePicker: PropTypes.bool, + rangeDisplayValue: PropTypes.shape({}), + setRangeDisplayValue: PropTypes.func, + setRangeValue: PropTypes.func +}; diff --git a/src/components/TimeRangePicker/helpers.js b/src/components/TimeRangePicker/helpers.js new file mode 100644 index 000000000..2c4f9a812 --- /dev/null +++ b/src/components/TimeRangePicker/helpers.js @@ -0,0 +1,6 @@ +export const getFromValues = (str) => { + const data = str.replace("now-", ""); + const intervalName = data.slice(-1); + const intervalTime = Number(data.slice(0, -1)); + return [intervalTime, intervalName]; +}; diff --git a/src/components/TimeRangePicker/index.css b/src/components/TimeRangePicker/index.css index f44e63606..1de43d019 100644 --- a/src/components/TimeRangePicker/index.css +++ b/src/components/TimeRangePicker/index.css @@ -30,6 +30,10 @@ opacity: 1; } +.option-item.active { + background-color: #d0e4fc; +} + /*calendar*/ .react-calendar-custom__tile { color: #000; diff --git a/src/components/TimeRangePicker/rangeOptions.js b/src/components/TimeRangePicker/rangeOptions.js new file mode 100644 index 000000000..08348ecd0 --- /dev/null +++ b/src/components/TimeRangePicker/rangeOptions.js @@ -0,0 +1,21 @@ +export const rangeOptions = [ + { display: "1 hour", from: "now-1h", to: "now" }, + { display: "2 hours", from: "now-2h", to: "now" }, + { display: "3 hours", from: "now-3h", to: "now" }, + { display: "4 hours", from: "now-4h", to: "now" }, + { display: "5 hours", from: "now-5h", to: "now" }, + { display: "6 hours", from: "now-6h", to: "now" }, + { display: "12 hours", from: "now-12h", to: "now" }, + { display: "24 hours", from: "now-24h", to: "now" }, + { display: "2 days", from: "now-2d", to: "now" }, + { display: "3 days", from: "now-3d", to: "now" }, + { display: "4 days", from: "now-4d", to: "now" }, + { display: "5 days", from: "now-5d", to: "now" }, + { display: "6 days", from: "now-6d", to: "now" }, + { display: "7 days", from: "now-7d", to: "now" }, + { display: "2 weeks", from: "now-2w", to: "now" }, + { display: "3 weeks", from: "now-3w", to: "now" }, + { display: "1 month", from: "now-1M", to: "now" } +]; + +export const defaultValue = { display: "1 hour", from: "now-1h", to: "now" }; diff --git a/src/components/TimeRangePicker/rangeValues.js b/src/components/TimeRangePicker/rangeValues.js deleted file mode 100644 index 3476a358d..000000000 --- a/src/components/TimeRangePicker/rangeValues.js +++ /dev/null @@ -1,31 +0,0 @@ -/* eslint-disable prettier/prettier */ -const intervalsInMs = { - y: 31536000000, - M: 2592000000, - w: 604800000, - d: 86400000, - h: 3600000, - m: 60000, - s: 1000, - ms: 1 -}; - -export const rangeValues = [ - { title: "1 hour", from: "now-1h", to: "now", interval: intervalsInMs.h }, - { title: "2 hours", from: "now-2h", to: "now", interval: intervalsInMs.h * 2 }, - { title: "3 hours", from: "now-3h", to: "now", interval: intervalsInMs.h * 3 }, - { title: "4 hours", from: "now-4h", to: "now", interval: intervalsInMs.h * 4 }, - { title: "5 hours", from: "now-5h", to: "now", interval: intervalsInMs.h * 5 }, - { title: "6 hours", from: "now-6h", to: "now", interval: intervalsInMs.h * 6 }, - { title: "12 hours", from: "now-12h", to: "now", interval: intervalsInMs.h * 12 }, - { title: "24 hours", from: "now-24h", to: "now", interval: intervalsInMs.h * 24 }, - { title: "2 days", from: "now-2d", to: "now", interval: intervalsInMs.d * 2 }, - { title: "3 days", from: "now-3d", to: "now", interval: intervalsInMs.d * 3 }, - { title: "4 days", from: "now-4d", to: "now", interval: intervalsInMs.d * 4 }, - { title: "5 days", from: "now-5d", to: "now", interval: intervalsInMs.d * 5 }, - { title: "6 days", from: "now-6d", to: "now", interval: intervalsInMs.d * 6 }, - { title: "7 days", from: "now-7d", to: "now", interval: intervalsInMs.d * 7 }, - { title: "2 weeks", from: "now-2w", to: "now", interval: intervalsInMs.w * 2 }, - { title: "3 weeks", from: "now-3w", to: "now", interval: intervalsInMs.w * 3 }, - { title: "1 month", from: "now-1m", to: "now", interval: intervalsInMs.M } -]; From 15bf468db3128ac5fea739060ef4746c85317fa9 Mon Sep 17 00:00:00 2001 From: Alexey Kuzniecov Date: Tue, 5 Apr 2022 22:32:39 +0300 Subject: [PATCH 05/15] feat, fix: improve styles, expanded input possibilities, fix linter problems, refactor --- .../TimeRangePicker/TimeRangeList.js | 35 ++++---- .../TimeRangePicker/TimeRangePicker.js | 43 ++++++++-- .../TimeRangePicker/TimeRangePickerBody.js | 84 ++++++++++++++----- src/components/TimeRangePicker/helpers.js | 20 ++++- src/components/TimeRangePicker/index.css | 15 +++- .../TimeRangePicker/rangeOptions.js | 5 +- src/pages/Examples/TimeRangePickerDemo.js | 2 +- 7 files changed, 153 insertions(+), 51 deletions(-) diff --git a/src/components/TimeRangePicker/TimeRangeList.js b/src/components/TimeRangePicker/TimeRangeList.js index e85a0fa41..f42eb6992 100644 --- a/src/components/TimeRangePicker/TimeRangeList.js +++ b/src/components/TimeRangePicker/TimeRangeList.js @@ -1,12 +1,9 @@ -/* eslint-disable react/require-default-props */ -/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ import PropTypes from "prop-types"; import { v4 as uuidv4 } from "uuid"; import clsx from "clsx"; import dayjs from "dayjs"; import { rangeOptions } from "./rangeOptions"; -import { getFromValues } from "./helpers"; +import { getIntervalData } from "./helpers"; export const TimeRangeList = ({ closePicker, @@ -26,8 +23,7 @@ export const TimeRangeList = ({ }; const setOption = (option) => { - const { from } = option; - const { to } = option; + const { from, to } = option; setRangeDisplayValue({ from, to, @@ -36,7 +32,7 @@ export const TimeRangeList = ({ setInputValue((prevState) => ({ ...prevState, from, to })); setRangeValue({ from: dayjs() - .subtract(...getFromValues(option.from)) + .subtract(...getIntervalData(option.from)) .format(), to: dayjs().format() }); @@ -46,19 +42,20 @@ export const TimeRangeList = ({ }; return ( -
    +
    {rangeOptions.map((option) => { const id = uuidv4(); return ( -
  • setOption(option)} - key={id} + key={option.display} className={clsx( - "option-item py-1 px-2 hover:bg-blue-200 flex justify-between items-center", + "option-item hover:bg-blue-200 flex justify-between items-center w-full", { active: isChecked(option, rangeDisplayValue) } )} > -
  • + ); })} -
+
); }; @@ -85,3 +82,13 @@ TimeRangeList.propTypes = { setCalendarValue: PropTypes.func, setShowCalendar: PropTypes.func }; + +TimeRangeList.defaultProps = { + closePicker: () => {}, + rangeDisplayValue: {}, + setRangeDisplayValue: () => {}, + setRangeValue: () => {}, + setInputValue: () => {}, + setCalendarValue: () => {}, + setShowCalendar: () => {} +}; diff --git a/src/components/TimeRangePicker/TimeRangePicker.js b/src/components/TimeRangePicker/TimeRangePicker.js index ada224f70..debfe79c3 100644 --- a/src/components/TimeRangePicker/TimeRangePicker.js +++ b/src/components/TimeRangePicker/TimeRangePicker.js @@ -1,21 +1,30 @@ -/* eslint-disable jsx-a11y/no-static-element-interactions */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ +import PropTypes from "prop-types"; +import dayjs from "dayjs"; import { useState } from "react"; import { FiClock } from "react-icons/fi"; +import { MdOutlineKeyboardArrowDown } from "react-icons/md"; +import clsx from "clsx"; import { TimeRangePickerBody } from "./TimeRangePickerBody"; import "./index.css"; import { defaultValue } from "./rangeOptions"; -export const TimeRangePicker = () => { +export const TimeRangePicker = ({ onChange }) => { const [isPickerOpen, setIsPickerOpen] = useState(false); const [rangeDisplayValue, setRangeDisplayValue] = useState({ ...defaultValue }); - const [rangeValue, setRangeValue] = useState({}); + // const [rangeValue, setRangeValue] = useState({}); + + const changeRangeValue = (range) => { + const from = dayjs(range.from).toDate(); + const to = dayjs(range.to).toDate(); + onChange(from, to); + }; return ( -
-
+
+
+ +
+ setIsPickerOpen(false)} rangeDisplayValue={rangeDisplayValue} setRangeDisplayValue={setRangeDisplayValue} - rangeValue={rangeValue} - setRangeValue={setRangeValue} + setRangeValue={changeRangeValue} />
); }; + +TimeRangePicker.propTypes = { + onChange: PropTypes.func +}; + +TimeRangePicker.defaultProps = { + onChange: (from, to) => { + console.log(from, to); + } +}; diff --git a/src/components/TimeRangePicker/TimeRangePickerBody.js b/src/components/TimeRangePicker/TimeRangePickerBody.js index 26cad03e2..a05c1292d 100644 --- a/src/components/TimeRangePicker/TimeRangePickerBody.js +++ b/src/components/TimeRangePicker/TimeRangePickerBody.js @@ -1,4 +1,3 @@ -/* eslint-disable react/require-default-props */ import PropTypes from "prop-types"; import Calendar from "react-calendar"; import clsx from "clsx"; @@ -9,6 +8,7 @@ import dayjs from "dayjs"; import { TimeRangeList } from "./TimeRangeList"; import "./index.css"; import { defaultValue } from "./rangeOptions"; +import { createIntervalName, getIntervalData } from "./helpers"; export const TimeRangePickerBody = ({ isOpen, @@ -35,22 +35,53 @@ export const TimeRangePickerBody = ({ const applyTimeRange = () => { if (inputValue.from.includes("now") || inputValue.to.includes("now")) { - setShowCalendar(false); - closePicker(); - return; + let from; + let to; + let display; + + if (inputValue.from === "now") { + from = dayjs().format(); + } else { + from = dayjs() + .subtract(...getIntervalData(inputValue.from)) + .format(); + } + + if (inputValue.to === "now") { + to = dayjs().format(); + display = createIntervalName(...getIntervalData(inputValue.from)); + } else { + to = dayjs() + .subtract(...getIntervalData(inputValue.to)) + .format(); + display = `${dayjs(from).format(displayTimeFormat)} - ${dayjs() + .subtract(...getIntervalData(inputValue.to)) + .format(displayTimeFormat)}`; + } + + setRangeDisplayValue({ + from: inputValue.from, + to: inputValue.to, + display + }); + setRangeValue({ + from, + to + }); + } else { + const from = dayjs(inputValue.from).format(displayTimeFormat); + const to = dayjs(inputValue.to).format(displayTimeFormat); + setRangeDisplayValue({ + from, + to, + display: `${from} to ${to}` + }); + setRangeValue({ + from: dayjs(inputValue.from).format(), + to: dayjs(inputValue.to).format() + }); } - const from = dayjs(inputValue.from).format(displayTimeFormat); - const to = dayjs(inputValue.to).format(displayTimeFormat); - setRangeDisplayValue({ - from, - to, - display: `${from} - ${to}` - }); - setRangeValue({ - from: dayjs(inputValue.from).format(), - to: dayjs(inputValue.to).format() - }); setShowCalendar(false); closePicker(); }; @@ -95,7 +126,7 @@ export const TimeRangePickerBody = ({ />
-
+
Absolute time range
@@ -114,7 +145,7 @@ export const TimeRangePickerBody = ({ className="px-1 py-0.5 border border-gray-300 rounded-sm focus:border-blue-50 focus:outline-none focus:ring-2 focus:ring-blue-300" />
-
+
-
+
+ {/*
+ Recently used absolute ranges +
*/}
-
+
{}, + rangeDisplayValue: {}, + setRangeDisplayValue: () => {}, + setRangeValue: () => {} +}; diff --git a/src/components/TimeRangePicker/helpers.js b/src/components/TimeRangePicker/helpers.js index 2c4f9a812..e418f73a2 100644 --- a/src/components/TimeRangePicker/helpers.js +++ b/src/components/TimeRangePicker/helpers.js @@ -1,6 +1,22 @@ -export const getFromValues = (str) => { - const data = str.replace("now-", ""); +export const getIntervalData = (interval) => { + if (interval === "now" || !interval.includes("now-")) return [0, "h"]; + const data = interval.replace("now-", ""); const intervalName = data.slice(-1); const intervalTime = Number(data.slice(0, -1)); return [intervalTime, intervalName]; }; + +export const createIntervalName = (interval, letter) => { + const dictionary = { + m: "minute", + h: "hour", + d: "day", + w: "week", + M: "month", + y: "year" + }; + + return dictionary[letter] + ? `${interval.toString()} ${dictionary[letter]}${interval > 1 ? "s" : ""}` + : "invalid format"; +}; diff --git a/src/components/TimeRangePicker/index.css b/src/components/TimeRangePicker/index.css index 1de43d019..5330247bb 100644 --- a/src/components/TimeRangePicker/index.css +++ b/src/components/TimeRangePicker/index.css @@ -1,14 +1,23 @@ +.time-picker-main { + width: fit-content; + margin-left: auto; +} + .time-range-picker-widget { width: fit-content; } +.timepicker-arrow-indicator.active { + transform: rotate(180deg); +} + .time-range-picker-body { visibility: hidden; opacity: 0; top: calc(100% + 2px); - left: -1px; - width: 550px; - height: 320px; + right: 0; + width: 480px; + height: 390px; transition: 0.2s ease; } diff --git a/src/components/TimeRangePicker/rangeOptions.js b/src/components/TimeRangePicker/rangeOptions.js index 08348ecd0..b5eaec605 100644 --- a/src/components/TimeRangePicker/rangeOptions.js +++ b/src/components/TimeRangePicker/rangeOptions.js @@ -15,7 +15,10 @@ export const rangeOptions = [ { display: "7 days", from: "now-7d", to: "now" }, { display: "2 weeks", from: "now-2w", to: "now" }, { display: "3 weeks", from: "now-3w", to: "now" }, - { display: "1 month", from: "now-1M", to: "now" } + { display: "1 month", from: "now-1M", to: "now" }, + { display: "3 months", from: "now-3M", to: "now" }, + { display: "6 months", from: "now-6M", to: "now" }, + { display: "1 year", from: "now-1y", to: "now" } ]; export const defaultValue = { display: "1 hour", from: "now-1h", to: "now" }; diff --git a/src/pages/Examples/TimeRangePickerDemo.js b/src/pages/Examples/TimeRangePickerDemo.js index 1322b8f84..a75d0cad4 100644 --- a/src/pages/Examples/TimeRangePickerDemo.js +++ b/src/pages/Examples/TimeRangePickerDemo.js @@ -3,7 +3,7 @@ import { TimeRangePicker } from "../../components/TimeRangePicker/TimeRangePicke export const TimeRangePickerDemo = () => ( -
+
From 98c453e9b4906a0d1fa45e26abd76b463a72e953 Mon Sep 17 00:00:00 2001 From: Alexey Kuzniecov Date: Thu, 7 Apr 2022 00:12:12 +0300 Subject: [PATCH 06/15] feat, fix: add recently ranges, improved the interaction between inputs and the calendar, implement saving the last search conditions, optimized and shortened the code --- .../TimeRangePicker/RecentlyRanges.js | 38 +++ .../TimeRangePicker/TimeRangeList.js | 42 +-- .../TimeRangePicker/TimeRangePicker.js | 44 ++- .../TimeRangePicker/TimeRangePickerBody.js | 257 +++++++++--------- src/components/TimeRangePicker/helpers.js | 79 ++++++ src/components/TimeRangePicker/index.css | 2 +- .../TimeRangePicker/rangeOptions.js | 4 +- 7 files changed, 289 insertions(+), 177 deletions(-) create mode 100644 src/components/TimeRangePicker/RecentlyRanges.js diff --git a/src/components/TimeRangePicker/RecentlyRanges.js b/src/components/TimeRangePicker/RecentlyRanges.js new file mode 100644 index 000000000..091972222 --- /dev/null +++ b/src/components/TimeRangePicker/RecentlyRanges.js @@ -0,0 +1,38 @@ +import PropTypes from "prop-types"; +import dayjs from "dayjs"; +import { displayTimeFormat } from "./rangeOptions"; + +export const RecentlyRanges = ({ recentRanges, applyTimeRange }) => ( +
+
+ Recently used absolute ranges: +
+
+ {recentRanges && + recentRanges.map((range) => ( + + ))} +
+
+); + +RecentlyRanges.propTypes = { + recentRanges: PropTypes.arrayOf(PropTypes.shape({})), + applyTimeRange: PropTypes.func +}; + +RecentlyRanges.defaultProps = { + recentRanges: [], + applyTimeRange: () => {} +}; diff --git a/src/components/TimeRangePicker/TimeRangeList.js b/src/components/TimeRangePicker/TimeRangeList.js index f42eb6992..9792b2790 100644 --- a/src/components/TimeRangePicker/TimeRangeList.js +++ b/src/components/TimeRangePicker/TimeRangeList.js @@ -1,17 +1,13 @@ import PropTypes from "prop-types"; import { v4 as uuidv4 } from "uuid"; import clsx from "clsx"; -import dayjs from "dayjs"; import { rangeOptions } from "./rangeOptions"; -import { getIntervalData } from "./helpers"; +import { storage } from "./helpers"; export const TimeRangeList = ({ closePicker, - rangeDisplayValue, - setRangeDisplayValue, - setRangeValue, - setInputValue, - setCalendarValue, + currentRange, + changeRangeValue, setShowCalendar }) => { const isChecked = (option, value) => { @@ -24,21 +20,13 @@ export const TimeRangeList = ({ const setOption = (option) => { const { from, to } = option; - setRangeDisplayValue({ + changeRangeValue({ from, - to, - display: option.display - }); - setInputValue((prevState) => ({ ...prevState, from, to })); - setRangeValue({ - from: dayjs() - .subtract(...getIntervalData(option.from)) - .format(), - to: dayjs().format() + to }); + storage.setItem("currentRange", { from, to }); closePicker(); setShowCalendar(false); - setCalendarValue(null); }; return ( @@ -52,7 +40,7 @@ export const TimeRangeList = ({ key={option.display} className={clsx( "option-item hover:bg-blue-200 flex justify-between items-center w-full", - { active: isChecked(option, rangeDisplayValue) } + { active: isChecked(option, currentRange) } )} >
-
-
Absolute time range
-
-
-
-
From
-
-
- - setInputValue((prevState) => ({ - ...prevState, - from: e.target.value - })) - } - className="px-1 py-0.5 border border-gray-300 rounded-sm focus:border-blue-50 focus:outline-none focus:ring-2 focus:ring-blue-300" - /> -
-
- +
+
+
Absolute time range
+
+
+
+
From
+
+
+ + setInputValue((prevState) => ({ + ...prevState, + from: e.target.value + })) + } + className="px-1 py-0.5 border border-gray-300 rounded-sm focus:border-blue-50 focus:outline-none focus:ring-2 focus:ring-blue-300" + /> +
+
+ +
-
-
-
To
-
-
- - setInputValue((prevState) => ({ - ...prevState, - to: e.target.value - })) - } - className="px-1 py-0.5 border border-gray-300 rounded-sm focus:border-blue-50 focus:outline-none focus:ring-2 focus:ring-blue-300" - /> -
-
- +
+
To
+
+
+ + setInputValue((prevState) => ({ + ...prevState, + to: e.target.value + })) + } + className="px-1 py-0.5 border border-gray-300 rounded-sm focus:border-blue-50 focus:outline-none focus:ring-2 focus:ring-blue-300" + /> +
+
+ +
+
-
- {/*
- Recently used absolute ranges -
*/} +
@@ -213,15 +212,13 @@ export const TimeRangePickerBody = ({ TimeRangePickerBody.propTypes = { isOpen: PropTypes.bool, closePicker: PropTypes.func, - rangeDisplayValue: PropTypes.shape({}), - setRangeDisplayValue: PropTypes.func, - setRangeValue: PropTypes.func + currentRange: PropTypes.shape({}), + changeRangeValue: PropTypes.func }; TimeRangePickerBody.defaultProps = { isOpen: false, closePicker: () => {}, - rangeDisplayValue: {}, - setRangeDisplayValue: () => {}, - setRangeValue: () => {} + currentRange: {}, + changeRangeValue: () => {} }; diff --git a/src/components/TimeRangePicker/helpers.js b/src/components/TimeRangePicker/helpers.js index e418f73a2..968b09518 100644 --- a/src/components/TimeRangePicker/helpers.js +++ b/src/components/TimeRangePicker/helpers.js @@ -1,3 +1,6 @@ +import dayjs from "dayjs"; +import { displayTimeFormat } from "./rangeOptions"; + export const getIntervalData = (interval) => { if (interval === "now" || !interval.includes("now-")) return [0, "h"]; const data = interval.replace("now-", ""); @@ -20,3 +23,79 @@ export const createIntervalName = (interval, letter) => { ? `${interval.toString()} ${dictionary[letter]}${interval > 1 ? "s" : ""}` : "invalid format"; }; + +export const convertRangeValue = (value, format = "jsDate") => { + if (dayjs(value).isValid()) { + return format === "jsDate" + ? dayjs(value).toDate() + : format === "iso" + ? dayjs(value).toISOString() + : dayjs(value).format(format); + } + if (format === "jsDate") { + return dayjs() + .subtract(...getIntervalData(value)) + .toDate(); + } + if (format === "iso") { + return dayjs() + .subtract(...getIntervalData(value)) + .toISOString(); + } + if (format === "default") { + return dayjs().subtract(...getIntervalData(value)); + } + return dayjs() + .subtract(...getIntervalData(value)) + .format(format); +}; + +export const createValueForInput = (value) => + dayjs(value).isValid() ? dayjs(value).format(displayTimeFormat) : value; + +export const createDisplayValue = (range) => { + if ( + typeof range.from === "string" && + range.from.includes("now") && + typeof range.to === "string" && + range.to === "now" + ) { + return createIntervalName(...getIntervalData(range.from)); + } + if (dayjs(range.from).isValid() && range.to === "now") { + return `${dayjs(range.from).format(displayTimeFormat)} to now`; + } + if (dayjs(range.from).isValid() && dayjs(range.to).isValid()) { + return `${dayjs(range.from).format(displayTimeFormat)} to ${dayjs( + range.to + ).format(displayTimeFormat)}`; + } + if ( + typeof range.from === "string" && + range.from.includes("now") && + typeof range.to === "string" && + range.to.includes("now") + ) { + return `${convertRangeValue( + range.from, + displayTimeFormat + )} to ${convertRangeValue(range.to, displayTimeFormat)}`; + } + return "invalid date"; +}; + +export const storage = { + setItem: (name, item) => { + if (item) { + localStorage.setItem(name, JSON.stringify(item)); + } + }, + + getItem: (name) => { + const item = localStorage.getItem(name); + if (item && item !== "undefined") { + return JSON.parse(item); + } + return null; + } +}; diff --git a/src/components/TimeRangePicker/index.css b/src/components/TimeRangePicker/index.css index 5330247bb..70a1bf9ab 100644 --- a/src/components/TimeRangePicker/index.css +++ b/src/components/TimeRangePicker/index.css @@ -17,7 +17,7 @@ top: calc(100% + 2px); right: 0; width: 480px; - height: 390px; + height: 384px; transition: 0.2s ease; } diff --git a/src/components/TimeRangePicker/rangeOptions.js b/src/components/TimeRangePicker/rangeOptions.js index b5eaec605..f4c294731 100644 --- a/src/components/TimeRangePicker/rangeOptions.js +++ b/src/components/TimeRangePicker/rangeOptions.js @@ -1,3 +1,5 @@ +export const displayTimeFormat = "YYYY-MM-DD HH:mm"; + export const rangeOptions = [ { display: "1 hour", from: "now-1h", to: "now" }, { display: "2 hours", from: "now-2h", to: "now" }, @@ -21,4 +23,4 @@ export const rangeOptions = [ { display: "1 year", from: "now-1y", to: "now" } ]; -export const defaultValue = { display: "1 hour", from: "now-1h", to: "now" }; +export const defaultRange = { display: "1 hour", from: "now-1h", to: "now" }; From c73e3f861991abf223da73679cafc6089877fef1 Mon Sep 17 00:00:00 2001 From: Alexey Kuzniecov Date: Thu, 7 Apr 2022 23:19:52 +0300 Subject: [PATCH 07/15] feat: implement validation --- .../TimeRangePicker/TimeRangePicker.js | 4 +- .../TimeRangePicker/TimeRangePickerBody.js | 133 +++++++++++++----- src/components/TimeRangePicker/helpers.js | 13 +- src/components/TimeRangePicker/index.css | 29 ++++ 4 files changed, 144 insertions(+), 35 deletions(-) diff --git a/src/components/TimeRangePicker/TimeRangePicker.js b/src/components/TimeRangePicker/TimeRangePicker.js index 3c4188f60..e1f9e8794 100644 --- a/src/components/TimeRangePicker/TimeRangePicker.js +++ b/src/components/TimeRangePicker/TimeRangePicker.js @@ -13,7 +13,9 @@ export const TimeRangePicker = ({ onChange }) => { const [currentRange, setCurrentRange] = useState( storage.getItem("currentRange") || defaultRange ); - const [rangeDisplayValue, setRangeDisplayValue] = useState(""); + const [rangeDisplayValue, setRangeDisplayValue] = useState( + createDisplayValue(currentRange) + ); const changeRangeValue = (range) => { const { from, to } = range; diff --git a/src/components/TimeRangePicker/TimeRangePickerBody.js b/src/components/TimeRangePicker/TimeRangePickerBody.js index 4be54fe89..88263c2d9 100644 --- a/src/components/TimeRangePicker/TimeRangePickerBody.js +++ b/src/components/TimeRangePicker/TimeRangePickerBody.js @@ -4,6 +4,7 @@ import clsx from "clsx"; import { useEffect, useState } from "react"; import { GrClose } from "react-icons/gr"; import { FaRegCalendarAlt, FaAngleLeft, FaAngleRight } from "react-icons/fa"; +import { FiAlertTriangle } from "react-icons/fi"; import dayjs from "dayjs"; import { TimeRangeList } from "./TimeRangeList"; import "./index.css"; @@ -26,6 +27,8 @@ export const TimeRangePickerBody = ({ from: createValueForInput(currentRange.from), to: createValueForInput(currentRange.to) }); + const [errorInputFrom, setErrorInputFrom] = useState(null); + const [errorInputTo, setErrorInputTo] = useState(null); const changeRecentRangesList = (range) => { if ( @@ -52,6 +55,8 @@ export const TimeRangePickerBody = ({ setCalendarValue(value); setInputValue((prevState) => ({ ...prevState, from, to })); + setErrorInputFrom(null); + setErrorInputTo(null); }; const applyTimeRange = (range) => { @@ -67,6 +72,34 @@ export const TimeRangePickerBody = ({ closePicker(); }; + const confirmValidRange = (range) => { + if (!errorInputFrom && !errorInputTo) { + applyTimeRange(range); + } + }; + + const validateInputRange = (range) => { + const from = convertRangeValue(range.from, "jsDate"); + const to = convertRangeValue(range.to, "jsDate"); + if (!dayjs(from).isValid()) { + setErrorInputFrom("Invaid date!"); + } else { + setErrorInputFrom(null); + } + if (!dayjs(to).isValid()) { + setErrorInputTo("Invaid date!"); + } else { + setErrorInputTo(null); + } + if (dayjs(from).isValid() && dayjs(to).isValid()) { + if (from > to) { + setErrorInputFrom('"From" can\'t be after "To"'); + } else { + setErrorInputFrom(null); + } + } + }; + useEffect(() => { if ( dayjs(currentRange.from).isValid() && @@ -83,6 +116,8 @@ export const TimeRangePickerBody = ({ from: createValueForInput(currentRange.from), to: createValueForInput(currentRange.to) }); + setErrorInputFrom(null); + setErrorInputTo(null); }, [currentRange]); return ( @@ -125,66 +160,100 @@ export const TimeRangePickerBody = ({ />
-
+
Absolute time range
-
-
+
+
From
-
-
- - setInputValue((prevState) => ({ - ...prevState, - from: e.target.value - })) - } - className="px-1 py-0.5 border border-gray-300 rounded-sm focus:border-blue-50 focus:outline-none focus:ring-2 focus:ring-blue-300" - /> -
-
- +
+
+
+ { + const from = e.target.value; + setInputValue((prevState) => ({ + ...prevState, + from + })); + validateInputRange({ from, to: inputValue.to }); + }} + onClick={() => setShowCalendar(false)} + className={clsx( + "px-1 py-0.5 border border-gray-300 rounded-sm focus:border-blue-50 focus:outline-none focus:ring-2 focus:ring-blue-300", + { "border-red-300": errorInputFrom } + )} + /> +
+
+ +
+ {errorInputFrom && ( +
+
+ +
+
{errorInputFrom}
+
+ )}
-
+
To
+ onChange={(e) => { + const to = e.target.value; setInputValue((prevState) => ({ ...prevState, - to: e.target.value - })) - } - className="px-1 py-0.5 border border-gray-300 rounded-sm focus:border-blue-50 focus:outline-none focus:ring-2 focus:ring-blue-300" + to + })); + validateInputRange({ from: inputValue.from, to }); + }} + onClick={() => setShowCalendar(false)} + className={clsx( + "px-1 py-0.5 border border-gray-300 rounded-sm focus:border-blue-50 focus:outline-none focus:ring-2 focus:ring-blue-300", + { "border-red-300": errorInputTo } + )} />
+ {errorInputTo && ( +
+
+ +
+
{errorInputTo}
+
+ )}
+
+
+
+ } + prevLabel={} + className="react-calendar-custom" + tileClassName="react-calendar-custom__tile" + value={calendarValue} + onChange={onChangeCalendarRange} + /> +
+
+); + +TimePickerCalendar.propTypes = { + calendarValue: PropTypes.arrayOf(PropTypes.shape({})), + onChangeCalendarRange: PropTypes.func, + setShowCalendar: PropTypes.func +}; + +TimePickerCalendar.defaultProps = { + calendarValue: null, + onChangeCalendarRange: () => {}, + setShowCalendar: () => {} +}; diff --git a/src/components/TimeRangePicker/TimePickerInput.js b/src/components/TimeRangePicker/TimePickerInput.js new file mode 100644 index 000000000..6f882e2f0 --- /dev/null +++ b/src/components/TimeRangePicker/TimePickerInput.js @@ -0,0 +1,61 @@ +import PropTypes from "prop-types"; +import { FaRegCalendarAlt } from "react-icons/fa"; +import { FiAlertTriangle } from "react-icons/fi"; +import clsx from "clsx"; +import "./index.css"; + +export const TimePickerInput = ({ + inputValue, + setInputValue, + setShowCalendar, + error +}) => ( +
+
+
+ setInputValue(e.target.value)} + onClick={() => setShowCalendar(false)} + className={clsx( + "px-1 py-0.5 border border-gray-300 rounded-sm focus:border-blue-50 focus:outline-none focus:ring-2 focus:ring-blue-300", + { "border-red-300": error } + )} + /> +
+
+ +
+
+ {error && ( +
+
+ +
+
{error}
+
+ )} +
+); + +TimePickerInput.propTypes = { + inputValue: PropTypes.string, + setInputValue: PropTypes.func, + error: PropTypes.string, + setShowCalendar: PropTypes.func +}; + +TimePickerInput.defaultProps = { + inputValue: "", + setInputValue: () => {}, + error: null, + setShowCalendar: () => {} +}; diff --git a/src/components/TimeRangePicker/TimeRangePickerBody.js b/src/components/TimeRangePicker/TimeRangePickerBody.js index 8adad8f4b..163f2d0f6 100644 --- a/src/components/TimeRangePicker/TimeRangePickerBody.js +++ b/src/components/TimeRangePicker/TimeRangePickerBody.js @@ -1,16 +1,14 @@ import PropTypes from "prop-types"; -import Calendar from "react-calendar"; import clsx from "clsx"; import { useEffect, useState } from "react"; -import { GrClose } from "react-icons/gr"; -import { FaRegCalendarAlt, FaAngleLeft, FaAngleRight } from "react-icons/fa"; -import { FiAlertTriangle } from "react-icons/fi"; import dayjs from "dayjs"; import { TimeRangeList } from "./TimeRangeList"; import "./index.css"; import { storage, createValueForInput, convertRangeValue } from "./helpers"; import { RecentlyRanges } from "./RecentlyRanges"; import { displayTimeFormat } from "./rangeOptions"; +import { TimePickerCalendar } from "./TimePickerCalendar"; +import { TimePickerInput } from "./TimePickerInput"; export const TimeRangePickerBody = ({ isOpen, @@ -23,10 +21,12 @@ export const TimeRangePickerBody = ({ ); const [showCalendar, setShowCalendar] = useState(false); const [calendarValue, setCalendarValue] = useState(null); - const [inputValue, setInputValue] = useState({ - from: createValueForInput(currentRange.from), - to: createValueForInput(currentRange.to) - }); + const [inputValueFrom, setInputValueFrom] = useState( + createValueForInput(currentRange.from) + ); + const [inputValueTo, setInputValueTo] = useState( + createValueForInput(currentRange.to) + ); const [errorInputFrom, setErrorInputFrom] = useState(null); const [errorInputTo, setErrorInputTo] = useState(null); @@ -54,7 +54,8 @@ export const TimeRangePickerBody = ({ const to = dayjs(value[1]).format(displayTimeFormat); setCalendarValue(value); - setInputValue((prevState) => ({ ...prevState, from, to })); + setInputValueFrom(from); + setInputValueTo(to); }; const applyTimeRange = (range) => { @@ -110,15 +111,13 @@ export const TimeRangePickerBody = ({ } else { setCalendarValue(null); } - setInputValue({ - from: createValueForInput(currentRange.from), - to: createValueForInput(currentRange.to) - }); + setInputValueFrom(createValueForInput(currentRange.from)); + setInputValueTo(createValueForInput(currentRange.to)); }, [currentRange]); useEffect(() => { - validateInputRange({ from: inputValue.from, to: inputValue.to }); - }, [inputValue]); + validateInputRange({ from: inputValueFrom, to: inputValueTo }); + }, [inputValueFrom, inputValueTo]); return (
-
-
Select a time range
-
- -
-
-
- } - prevLabel={} - className="react-calendar-custom" - tileClassName="react-calendar-custom__tile" - value={calendarValue} - onChange={onChangeCalendarRange} - /> -
+
@@ -167,89 +144,27 @@ export const TimeRangePickerBody = ({
From
-
-
-
- - setInputValue((prevState) => ({ - ...prevState, - from: e.target.value - })) - } - onClick={() => setShowCalendar(false)} - className={clsx( - "px-1 py-0.5 border border-gray-300 rounded-sm focus:border-blue-50 focus:outline-none focus:ring-2 focus:ring-blue-300", - { "border-red-300": errorInputFrom } - )} - /> -
-
- -
-
- {errorInputFrom && ( -
-
- -
-
{errorInputFrom}
-
- )} -
+
To
-
-
- - setInputValue((prevState) => ({ - ...prevState, - to: e.target.value - })) - } - onClick={() => setShowCalendar(false)} - className={clsx( - "px-1 py-0.5 border border-gray-300 rounded-sm focus:border-blue-50 focus:outline-none focus:ring-2 focus:ring-blue-300", - { "border-red-300": errorInputTo } - )} - /> -
-
- -
-
- {errorInputTo && ( -
-
- -
-
{errorInputTo}
-
- )} +
- Time range: {rangeDisplayValue} + Time range: {updateDisplayValue}
{
setIsPickerOpen(false)} currentRange={currentRange} @@ -70,11 +78,15 @@ export const TimeRangePicker = ({ onChange }) => { }; TimeRangePicker.propTypes = { - onChange: PropTypes.func + onChange: PropTypes.func, + from: PropTypes.shape({}), + to: PropTypes.shape({}) }; TimeRangePicker.defaultProps = { onChange: (from, to) => { console.log("FROM: ", from, "\n", "TO: ", to); - } + }, + from: dayjs(new Date()).subtract(1, "h"), + to: new Date() }; diff --git a/src/components/TimeRangePicker/TimeRangePickerBody.js b/src/components/TimeRangePicker/TimeRangePickerBody.js index 163f2d0f6..81c716c62 100644 --- a/src/components/TimeRangePicker/TimeRangePickerBody.js +++ b/src/components/TimeRangePicker/TimeRangePickerBody.js @@ -1,6 +1,6 @@ import PropTypes from "prop-types"; import clsx from "clsx"; -import { useEffect, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import dayjs from "dayjs"; import { TimeRangeList } from "./TimeRangeList"; import "./index.css"; @@ -11,6 +11,7 @@ import { TimePickerCalendar } from "./TimePickerCalendar"; import { TimePickerInput } from "./TimePickerInput"; export const TimeRangePickerBody = ({ + pickerLeft, isOpen, closePicker, currentRange, @@ -30,54 +31,62 @@ export const TimeRangePickerBody = ({ const [errorInputFrom, setErrorInputFrom] = useState(null); const [errorInputTo, setErrorInputTo] = useState(null); - const changeRecentRangesList = (range) => { - if ( - !recentRanges.find( - (el) => - dayjs(el.from).toISOString() === dayjs(range.from).toISOString() && - dayjs(el.to).toISOString() === dayjs(range.to).toISOString() - ) - ) { - let newRanges; - if (recentRanges.length < 4) { - newRanges = [range, ...recentRanges]; - } else { - newRanges = [range, ...recentRanges.slice(0, 3)]; + const changeRecentRangesList = useCallback( + (range) => { + if ( + !recentRanges.find( + (el) => + dayjs(el.from).toISOString() === dayjs(range.from).toISOString() && + dayjs(el.to).toISOString() === dayjs(range.to).toISOString() + ) + ) { + let newRanges; + if (recentRanges.length < 4) { + newRanges = [range, ...recentRanges]; + } else { + newRanges = [range, ...recentRanges.slice(0, 3)]; + } + setRecentRanges([...newRanges]); + storage.setItem("timePickerRanges", newRanges); } - setRecentRanges([...newRanges]); - storage.setItem("timePickerRanges", newRanges); - } - }; + }, + [recentRanges] + ); - const onChangeCalendarRange = (value) => { + const onChangeCalendarRange = useCallback((value) => { const from = dayjs(value[0]).format(displayTimeFormat); const to = dayjs(value[1]).format(displayTimeFormat); setCalendarValue(value); setInputValueFrom(from); setInputValueTo(to); - }; + }, []); - const applyTimeRange = (range) => { - changeRangeValue(range); - if (dayjs(range.from).isValid() && dayjs(range.to).isValid()) { - changeRecentRangesList({ - from: dayjs(range.from).toISOString(), - to: dayjs(range.to).toISOString() - }); - } - storage.setItem("currentRange", range); - setShowCalendar(false); - closePicker(); - }; + const applyTimeRange = useCallback( + (range) => { + changeRangeValue(range); + if (dayjs(range.from).isValid() && dayjs(range.to).isValid()) { + changeRecentRangesList({ + from: dayjs(range.from).toISOString(), + to: dayjs(range.to).toISOString() + }); + } + setShowCalendar(false); + closePicker(); + }, + [changeRangeValue, changeRecentRangesList, closePicker] + ); - const confirmValidRange = (range) => { - if (!errorInputFrom && !errorInputTo) { - applyTimeRange(range); - } - }; + const confirmValidRange = useCallback( + (range) => { + if (!errorInputFrom && !errorInputTo) { + applyTimeRange(range); + } + }, + [applyTimeRange, errorInputFrom, errorInputTo] + ); - const validateInputRange = (range) => { + const validateInputRange = useCallback((range) => { const from = convertRangeValue(range.from, "jsDate"); const to = convertRangeValue(range.to, "jsDate"); if (!dayjs(from).isValid()) { @@ -97,7 +106,7 @@ export const TimeRangePickerBody = ({ setErrorInputFrom(null); } } - }; + }, []); useEffect(() => { if ( @@ -123,12 +132,13 @@ export const TimeRangePickerBody = ({
600 } )} >
{}, currentRange: {}, diff --git a/src/components/TimeRangePicker/index.css b/src/components/TimeRangePicker/index.css index e74094b30..b85fdd0d5 100644 --- a/src/components/TimeRangePicker/index.css +++ b/src/components/TimeRangePicker/index.css @@ -1,6 +1,6 @@ .time-picker-main { width: fit-content; - margin-left: auto; + /* margin-left: auto; */ } .time-range-picker-widget { @@ -15,12 +15,16 @@ visibility: hidden; opacity: 0; top: calc(100% + 2px); - right: 0; width: 480px; height: 384px; transition: 0.2s ease; } +.time-range-picker-body.alignPickerRight { + right: 0; +} + + .time-range-picker-body.active { visibility: visible; opacity: 1; @@ -34,6 +38,10 @@ transition: 0.2s ease; } +.calendar-wrapper.calendarRight { + right: -70px; +} + .calendar-wrapper.active { visibility: visible; opacity: 1; diff --git a/src/pages/Examples/TimeRangePickerDemo.js b/src/pages/Examples/TimeRangePickerDemo.js index a75d0cad4..59608fde6 100644 --- a/src/pages/Examples/TimeRangePickerDemo.js +++ b/src/pages/Examples/TimeRangePickerDemo.js @@ -3,7 +3,8 @@ import { TimeRangePicker } from "../../components/TimeRangePicker/TimeRangePicke export const TimeRangePickerDemo = () => ( -
+
+
From 2ebe4208709745d29fe9674cf79a1d6a4c7572fc Mon Sep 17 00:00:00 2001 From: Alexey Kuzniecov Date: Mon, 11 Apr 2022 15:23:19 +0300 Subject: [PATCH 11/15] fix: clarify properties of props --- src/components/TimeRangePicker/RecentlyRanges.js | 2 +- src/components/TimeRangePicker/TimePickerCalendar.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/TimeRangePicker/RecentlyRanges.js b/src/components/TimeRangePicker/RecentlyRanges.js index 091972222..e575b3cf5 100644 --- a/src/components/TimeRangePicker/RecentlyRanges.js +++ b/src/components/TimeRangePicker/RecentlyRanges.js @@ -28,7 +28,7 @@ export const RecentlyRanges = ({ recentRanges, applyTimeRange }) => ( ); RecentlyRanges.propTypes = { - recentRanges: PropTypes.arrayOf(PropTypes.shape({})), + recentRanges: PropTypes.arrayOf(PropTypes.object), applyTimeRange: PropTypes.func }; diff --git a/src/components/TimeRangePicker/TimePickerCalendar.js b/src/components/TimeRangePicker/TimePickerCalendar.js index 1ff2f6c54..d6726212f 100644 --- a/src/components/TimeRangePicker/TimePickerCalendar.js +++ b/src/components/TimeRangePicker/TimePickerCalendar.js @@ -40,7 +40,7 @@ export const TimePickerCalendar = ({ ); TimePickerCalendar.propTypes = { - calendarValue: PropTypes.arrayOf(PropTypes.shape({})), + calendarValue: PropTypes.arrayOf(PropTypes.object), onChangeCalendarRange: PropTypes.func, setShowCalendar: PropTypes.func }; From 9e66977ab27e815d470cce0951b1614d123cc1e1 Mon Sep 17 00:00:00 2001 From: Alexey Kuzniecov Date: Fri, 15 Apr 2022 16:15:07 +0300 Subject: [PATCH 12/15] fix: change data flow --- .../TimeRangePicker/TimeRangePicker.js | 42 ++++++++++--------- .../TimeRangePicker/TimeRangePickerBody.js | 8 ++-- src/components/TimeRangePicker/helpers.js | 7 ++++ src/components/TimeRangePicker/index.css | 2 +- .../TimeRangePicker/rangeOptions.js | 4 +- src/pages/Examples/TimeRangePickerDemo.js | 24 +++++++---- 6 files changed, 52 insertions(+), 35 deletions(-) diff --git a/src/components/TimeRangePicker/TimeRangePicker.js b/src/components/TimeRangePicker/TimeRangePicker.js index 86040b469..53a6bdef5 100644 --- a/src/components/TimeRangePicker/TimeRangePicker.js +++ b/src/components/TimeRangePicker/TimeRangePicker.js @@ -1,33 +1,43 @@ import PropTypes from "prop-types"; -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { useCallback, useMemo, useRef, useState } from "react"; import { FiClock } from "react-icons/fi"; import { MdOutlineKeyboardArrowDown } from "react-icons/md"; import clsx from "clsx"; import dayjs from "dayjs"; import { TimeRangePickerBody } from "./TimeRangePickerBody"; import "./index.css"; -import { convertRangeValue, createDisplayValue } from "./helpers"; +import { areDatesSame, convertRangeValue, createDisplayValue } from "./helpers"; export const TimeRangePicker = ({ onChange, from, to }) => { const [isPickerOpen, setIsPickerOpen] = useState(false); - const [currentRange, setCurrentRange] = useState({ from, to }); - const [pickerLeft, setPickerLeft] = useState(0); const pickerRef = useRef(); + const [sentRange, setSentRange] = useState(null); + const [memoRange, setMemoRange] = useState(null); + + const currentRange = useMemo(() => { + if ( + sentRange && + areDatesSame(from, sentRange.from) && + areDatesSame(to, sentRange.to) + ) { + return memoRange; + } + return { from, to }; + }, [from, to]); const updateDisplayValue = useMemo( () => createDisplayValue(currentRange), [currentRange] ); - useEffect(() => { - const pickerPosLeft = pickerRef.current.getBoundingClientRect().left; - setPickerLeft(pickerPosLeft); - }, []); - const changeRangeValue = useCallback( (range) => { const { from, to } = range; - setCurrentRange({ from, to }); + setMemoRange({ from, to }); + setSentRange({ + from: convertRangeValue(from, "jsDate"), + to: convertRangeValue(to, "jsDate") + }); onChange( convertRangeValue(from, "jsDate"), convertRangeValue(to, "jsDate") @@ -36,14 +46,6 @@ export const TimeRangePicker = ({ onChange, from, to }) => { [onChange] ); - useEffect(() => { - const { from, to } = currentRange; - onChange( - convertRangeValue(from, "jsDate"), - convertRangeValue(to, "jsDate") - ); - }, []); - return (
setIsPickerOpen(false)} currentRange={currentRange} @@ -87,6 +89,6 @@ TimeRangePicker.defaultProps = { onChange: (from, to) => { console.log("FROM: ", from, "\n", "TO: ", to); }, - from: dayjs(new Date()).subtract(1, "h"), + from: dayjs(new Date()).subtract(1, "h").toDate(), to: new Date() }; diff --git a/src/components/TimeRangePicker/TimeRangePickerBody.js b/src/components/TimeRangePicker/TimeRangePickerBody.js index 81c716c62..24b249827 100644 --- a/src/components/TimeRangePicker/TimeRangePickerBody.js +++ b/src/components/TimeRangePicker/TimeRangePickerBody.js @@ -11,11 +11,11 @@ import { TimePickerCalendar } from "./TimePickerCalendar"; import { TimePickerInput } from "./TimePickerInput"; export const TimeRangePickerBody = ({ - pickerLeft, isOpen, closePicker, currentRange, - changeRangeValue + changeRangeValue, + pickerRef }) => { const [recentRanges, setRecentRanges] = useState( storage.getItem("timePickerRanges") || [] @@ -128,6 +128,8 @@ export const TimeRangePickerBody = ({ validateInputRange({ from: inputValueFrom, to: inputValueTo }); }, [inputValueFrom, inputValueTo]); + const pickerLeft = pickerRef?.current?.getBoundingClientRect()?.left || 0; + return (
{}, currentRange: {}, diff --git a/src/components/TimeRangePicker/helpers.js b/src/components/TimeRangePicker/helpers.js index 2cffdd2c8..53562063b 100644 --- a/src/components/TimeRangePicker/helpers.js +++ b/src/components/TimeRangePicker/helpers.js @@ -93,6 +93,13 @@ export const createDisplayValue = (range) => { return "invalid date"; }; +export const areDatesSame = (...dates) => { + const date1 = dayjs(dates[0]); + const date2 = dayjs(dates[1]); + if (date1.diff(date2) < 1500) return true; + return false; +}; + export const storage = { setItem: (name, item) => { if (item) { diff --git a/src/components/TimeRangePicker/index.css b/src/components/TimeRangePicker/index.css index b85fdd0d5..a523b73f3 100644 --- a/src/components/TimeRangePicker/index.css +++ b/src/components/TimeRangePicker/index.css @@ -39,7 +39,7 @@ } .calendar-wrapper.calendarRight { - right: -70px; + right: -21px; } .calendar-wrapper.active { diff --git a/src/components/TimeRangePicker/rangeOptions.js b/src/components/TimeRangePicker/rangeOptions.js index f4c294731..2d8c94f49 100644 --- a/src/components/TimeRangePicker/rangeOptions.js +++ b/src/components/TimeRangePicker/rangeOptions.js @@ -1,6 +1,8 @@ export const displayTimeFormat = "YYYY-MM-DD HH:mm"; export const rangeOptions = [ + { display: "10 minutes", from: "now-10m", to: "now" }, + { display: "30 minutes", from: "now-30m", to: "now" }, { display: "1 hour", from: "now-1h", to: "now" }, { display: "2 hours", from: "now-2h", to: "now" }, { display: "3 hours", from: "now-3h", to: "now" }, @@ -22,5 +24,3 @@ export const rangeOptions = [ { display: "6 months", from: "now-6M", to: "now" }, { display: "1 year", from: "now-1y", to: "now" } ]; - -export const defaultRange = { display: "1 hour", from: "now-1h", to: "now" }; diff --git a/src/pages/Examples/TimeRangePickerDemo.js b/src/pages/Examples/TimeRangePickerDemo.js index 59608fde6..680f19c73 100644 --- a/src/pages/Examples/TimeRangePickerDemo.js +++ b/src/pages/Examples/TimeRangePickerDemo.js @@ -1,11 +1,19 @@ +import { useState } from "react"; import { SearchLayout } from "../../components/Layout"; import { TimeRangePicker } from "../../components/TimeRangePicker/TimeRangePicker"; -export const TimeRangePickerDemo = () => ( - -
- - -
-
-); +export const TimeRangePickerDemo = () => { + const [value, setValue] = useState([new Date(), new Date()]); + const onChange = (from, to) => { + setValue([from, to]); + }; + + return ( + +
+ + +
+
+ ); +}; From a5e462d0ecfff77810b61667ffa485e96cd222b2 Mon Sep 17 00:00:00 2001 From: Alexey Kuzniecov Date: Mon, 18 Apr 2022 13:06:08 +0300 Subject: [PATCH 13/15] fix: add correct proptypes, small refactor --- .../TimeRangePicker/RecentlyRanges.js | 29 +++++++++---------- .../TimeRangePicker/TimePickerCalendar.js | 2 +- .../TimeRangePicker/TimeRangeList.js | 4 ++- src/components/TimeRangePicker/helpers.js | 9 +++--- src/components/TimeRangePicker/index.css | 1 - src/pages/Examples/TimeRangePickerDemo.js | 8 +++-- 6 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/components/TimeRangePicker/RecentlyRanges.js b/src/components/TimeRangePicker/RecentlyRanges.js index e575b3cf5..78dbf6652 100644 --- a/src/components/TimeRangePicker/RecentlyRanges.js +++ b/src/components/TimeRangePicker/RecentlyRanges.js @@ -8,27 +8,24 @@ export const RecentlyRanges = ({ recentRanges, applyTimeRange }) => ( Recently used absolute ranges:
- {recentRanges && - recentRanges.map((range) => ( - - ))} + {recentRanges?.map((range) => ( + + ))}
); RecentlyRanges.propTypes = { - recentRanges: PropTypes.arrayOf(PropTypes.object), + recentRanges: PropTypes.arrayOf(PropTypes.objectOf(PropTypes.string)), applyTimeRange: PropTypes.func }; diff --git a/src/components/TimeRangePicker/TimePickerCalendar.js b/src/components/TimeRangePicker/TimePickerCalendar.js index d6726212f..c4c34306e 100644 --- a/src/components/TimeRangePicker/TimePickerCalendar.js +++ b/src/components/TimeRangePicker/TimePickerCalendar.js @@ -40,7 +40,7 @@ export const TimePickerCalendar = ({ ); TimePickerCalendar.propTypes = { - calendarValue: PropTypes.arrayOf(PropTypes.object), + calendarValue: PropTypes.arrayOf(PropTypes.instanceOf(Date)), onChangeCalendarRange: PropTypes.func, setShowCalendar: PropTypes.func }; diff --git a/src/components/TimeRangePicker/TimeRangeList.js b/src/components/TimeRangePicker/TimeRangeList.js index 5f7bb6cca..f6eb77935 100644 --- a/src/components/TimeRangePicker/TimeRangeList.js +++ b/src/components/TimeRangePicker/TimeRangeList.js @@ -65,7 +65,9 @@ export const TimeRangeList = ({ TimeRangeList.propTypes = { closePicker: PropTypes.func, - currentRange: PropTypes.shape({}), + currentRange: PropTypes.objectOf( + PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]) + ), changeRangeValue: PropTypes.func, setShowCalendar: PropTypes.func }; diff --git a/src/components/TimeRangePicker/helpers.js b/src/components/TimeRangePicker/helpers.js index 53562063b..6f6dbe490 100644 --- a/src/components/TimeRangePicker/helpers.js +++ b/src/components/TimeRangePicker/helpers.js @@ -1,4 +1,5 @@ import dayjs from "dayjs"; +import { getLocalItem, setLocalItem } from "../../utils/storage"; import { displayTimeFormat } from "./rangeOptions"; const rangeRegexp = /^now-\d{1,4}[mhdwMy]$/; @@ -96,19 +97,19 @@ export const createDisplayValue = (range) => { export const areDatesSame = (...dates) => { const date1 = dayjs(dates[0]); const date2 = dayjs(dates[1]); - if (date1.diff(date2) < 1500) return true; - return false; + return date1.diff(date2) < 1500; }; export const storage = { setItem: (name, item) => { if (item) { - localStorage.setItem(name, JSON.stringify(item)); + const jsonItem = JSON.stringify(item); + setLocalItem(name, jsonItem); } }, getItem: (name) => { - const item = localStorage.getItem(name); + const item = getLocalItem(name); if (item && item !== "undefined") { return JSON.parse(item); } diff --git a/src/components/TimeRangePicker/index.css b/src/components/TimeRangePicker/index.css index a523b73f3..90518733a 100644 --- a/src/components/TimeRangePicker/index.css +++ b/src/components/TimeRangePicker/index.css @@ -1,6 +1,5 @@ .time-picker-main { width: fit-content; - /* margin-left: auto; */ } .time-range-picker-widget { diff --git a/src/pages/Examples/TimeRangePickerDemo.js b/src/pages/Examples/TimeRangePickerDemo.js index 680f19c73..3f1e2ff86 100644 --- a/src/pages/Examples/TimeRangePickerDemo.js +++ b/src/pages/Examples/TimeRangePickerDemo.js @@ -1,18 +1,22 @@ import { useState } from "react"; import { SearchLayout } from "../../components/Layout"; -import { TimeRangePicker } from "../../components/TimeRangePicker/TimeRangePicker"; +import { TimeRangePicker } from "../../components/TimeRangePicker"; export const TimeRangePickerDemo = () => { const [value, setValue] = useState([new Date(), new Date()]); + const [value2, setValue2] = useState([new Date(), new Date()]); const onChange = (from, to) => { setValue([from, to]); }; + const onChange2 = (from, to) => { + setValue2([from, to]); + }; return (
- +
); From 0da52038d36e8e7ea2539d66caddb7031f3df5c7 Mon Sep 17 00:00:00 2001 From: Alexey Kuzniecov Date: Wed, 20 Apr 2022 16:22:19 +0300 Subject: [PATCH 14/15] fix: fix bug with calendar visibility --- src/components/TimeRangePicker/TimeRangePickerBody.js | 2 +- src/components/TimeRangePicker/index.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/TimeRangePicker/TimeRangePickerBody.js b/src/components/TimeRangePicker/TimeRangePickerBody.js index 24b249827..d901996a8 100644 --- a/src/components/TimeRangePicker/TimeRangePickerBody.js +++ b/src/components/TimeRangePicker/TimeRangePickerBody.js @@ -139,7 +139,7 @@ export const TimeRangePickerBody = ({ >
diff --git a/src/components/TimeRangePicker/index.css b/src/components/TimeRangePicker/index.css index 90518733a..f48872446 100644 --- a/src/components/TimeRangePicker/index.css +++ b/src/components/TimeRangePicker/index.css @@ -56,7 +56,7 @@ .error-range-box { position: relative; - width:fit-content; + width: fit-content; display: flex; align-items: center; margin-top: 6px; From 64d0362ba53c844b050b2304d6980252dc58b8a8 Mon Sep 17 00:00:00 2001 From: Alexey Kuzniecov Date: Wed, 20 Apr 2022 19:19:43 +0300 Subject: [PATCH 15/15] refactor: rewrote styles from css to tilewind --- .../TimeRangePicker/TimePickerInput.js | 2 +- .../TimeRangePicker/TimeRangeList.js | 2 +- .../TimeRangePicker/TimeRangePicker.js | 4 +- .../TimeRangePicker/TimeRangePickerBody.js | 13 +++--- src/components/TimeRangePicker/index.css | 41 +++---------------- src/pages/Examples/TimeRangePickerDemo.js | 11 ++--- 6 files changed, 23 insertions(+), 50 deletions(-) diff --git a/src/components/TimeRangePicker/TimePickerInput.js b/src/components/TimeRangePicker/TimePickerInput.js index 062e42a78..d18bc40b6 100644 --- a/src/components/TimeRangePicker/TimePickerInput.js +++ b/src/components/TimeRangePicker/TimePickerInput.js @@ -34,7 +34,7 @@ export const TimePickerInput = ({
{error && ( -
+
diff --git a/src/components/TimeRangePicker/TimeRangeList.js b/src/components/TimeRangePicker/TimeRangeList.js index f6eb77935..3eb7f0675 100644 --- a/src/components/TimeRangePicker/TimeRangeList.js +++ b/src/components/TimeRangePicker/TimeRangeList.js @@ -42,7 +42,7 @@ export const TimeRangeList = ({ key={option.display} className={clsx( "option-item hover:bg-blue-200 flex justify-between items-center w-full", - { active: isChecked(option, currentRange) } + { "bg-blue-100": isChecked(option, currentRange) } )} >