Skip to content

Commit

Permalink
#88 date picker
Browse files Browse the repository at this point in the history
  • Loading branch information
io53 committed Mar 6, 2024
1 parent 747d446 commit 2fd1ab2
Show file tree
Hide file tree
Showing 8 changed files with 297 additions and 30 deletions.
39 changes: 37 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@
"@testing-library/jest-dom": "^6.1.4",
"@testing-library/react": "^14.1.0",
"@testing-library/user-event": "^14.5.1",
"date-fns": "^3.2.0",
"framer-motion": "^10.16.5",
"i18next": "^23.7.6",
"jspdf": "^2.5.1",
"localforage": "^1.10.0",
"react": "^18.2.0",
"react-day-picker": "^8.10.0",
"react-dom": "^18.2.0",
"react-i18next": "^13.5.0",
"react-icons": "^4.12.0",
Expand Down
112 changes: 112 additions & 0 deletions src/components/DatePicker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import React, { useState } from 'react';

import { DayPicker } from 'react-day-picker';
import { enGB, fi, sv, de, fr, pl } from 'date-fns/locale';
import 'react-day-picker/dist/style.css';
import i18next, { t } from 'i18next';

const css = `
.my-selected:not([disabled]) {
background: #35AD9FB3;
}
.my-selected:hover:not([disabled]) {
color: #35AD9F;
}
.my-today {
font-weight: bold;
}
.rdp-day_range_middle {
background: #35AD9F33 !important;
}
.rdp-day_range_start {
border-top-right-radius: 100% !important;
border-bottom-right-radius: 100% !important;
}
.rdp-day_range_end {
border-top-left-radius: 100% !important;
border-bottom-left-radius: 100% !important;
}
`;

function ddmm(ts) {
return ts.getDate() + "." + (ts.getMonth() + 1) + "." + (ts.getFullYear())
}

export default function DatePicker(props) {
let local = enGB;
let lng = i18next.language
if (lng === "sv") local = sv
if (lng === "fi") local = fi
if (lng === "de") local = de
if (lng === "fr") local = fr
if (lng === "pl") local = pl
const defaultSelected = {
from: null,
to: null
};
const [range, setRange] = useState(defaultSelected);
const [currentMonth, setCurrentMonth] = useState(new Date());

const setBgStyle = () => {
let cells = window.document.querySelectorAll('.rdp-cell')
for (let i = 0; i < cells.length; i++) {
cells[i].style.backgroundColor = '';
};
let startNodes = window.document.querySelectorAll('.rdp-day_range_start');
for (let i = 0; i < startNodes.length; i++) {
let child = startNodes[i];
child.parentNode.style.backgroundColor = '#35AD9F33';
child.parentNode.style.borderTopLeftRadius = '50%';
child.parentNode.style.borderBottomLeftRadius = '50%';
}
let endNodes = window.document.querySelectorAll('.rdp-day_range_end');
for (let i = 0; i < endNodes.length; i++) {
let child = endNodes[i];
child.parentNode.style.backgroundColor = '#35AD9F33';
child.parentNode.style.borderTopRightRadius = '50%';
child.parentNode.style.borderBottomRightRadius = '50%';
}
setCurrentMonth(currentMonth)
}

let footerProps = {
style: {
textAlign: "left",
}
}
let footer = <p {...footerProps}>{t("pick_first_day")}</p>;
if (range?.from) {
if (!range.to) {
footer = <p {...footerProps}>{ddmm(range.from)}</p>;
} else if (range.to) {
footer = (
<p {...footerProps}>
{ddmm(range.from)}{ddmm(range.to)}
</p>
);
}
}

return (
<>
<style>{css}</style>
<DayPicker
mode="range"
locale={local}
selected={range}
modifiersClassNames={{
selected: 'my-selected',
today: 'my-today'
}}
footer={footer}
onSelect={val => {
setRange(val)
props.onChange(val)
//setBgStyle()
}}
defaultMonth={new Date()}
disabled={d => d.getTime() > new Date().getTime()}
/>
</>
);
}
126 changes: 102 additions & 24 deletions src/components/DurationPicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,17 @@ import {
MenuButton,
Button,
MenuDivider,
Popover,
PopoverContent,
PopoverAnchor,
PopoverBody,
Box,
} from "@chakra-ui/react"
import { MdArrowDropDown } from "react-icons/md"
import { AiOutlineCalendar } from "react-icons/ai"
import { useTranslation } from 'react-i18next';
import DatePicker from "./DatePicker";
import { useState } from "react";

var timespans = [
{ k: "1", t: "hour", v: 1 },
Expand All @@ -34,41 +42,111 @@ export function getTimespan(value) {
return ts;
};

function ddmm(ts) {
return ts.getDate() + "." + (ts.getMonth() + 1) + "." + (ts.getFullYear())
}

const detailedSubText = {
fontFamily: "mulish",
fontSize: "14px",
}

export default function DurationPicker(props) {
const { t } = useTranslation();
const [lastCustom, setLastCustom] = useState(null)
const [custom, setCustom] = useState(null)
const [showPicker, setShowPicker] = useState(false)
const [showDropdown, setShowDropdown] = useState(false)
var ts = getTimespan(props.value)
var renderTimespans = timespans;

if (props.dashboard) renderTimespans = renderTimespans.filter(x => x.v <= 24 * 7)
if (props.showMaxHours !== undefined) renderTimespans = renderTimespans.filter(x => x.v <= props.showMaxHours)
return (
<Menu autoSelect={false} strategy="fixed" placement="bottom-end">
<MenuButton as={Button}
rightIcon={<MdArrowDropDown size={26} className="buttonSideIcon" style={{ marginLeft: -10, marginRight: -8 }} />}
variant="ddl"
className="durationPicker"
style={{ ...detailedSubText }}
isDisabled={props.disabled || renderTimespans.length === 0}
borderRadius='4px'>
{ts.k} {t(ts.t).toLowerCase()}
</MenuButton>
<MenuList zIndex={2}>
{renderTimespans.map((x, i) => {
let divider = <></>
let borderStyle = {};
if (i === 0) borderStyle = { borderTopLeftRadius: 6, borderTopRightRadius: 6 }
if (i === renderTimespans.length - 1) borderStyle = { borderBottomLeftRadius: 6, borderBottomRightRadius: 6 }
else divider = <MenuDivider />
return <div key={x.v+"p"}>
<MenuItem key={x.v} className={ts.v === x.v ? "menuActive" : undefined} style={{ ...detailedSubText, ...borderStyle }} onClick={() => props.onChange(x.v)}>{x.k} {t(x.t).toLowerCase()}</MenuItem>
{divider}
</div>
})}
</MenuList>
</Menu>
<>
{/*
<Modal isOpen={showPicker} onClose={() => setShowPicker(false)}>
<ModalOverlay />
<ModalContent maxWidth={"360px"}>
<ModalBody>
<DatePicker onChange={setLastCustom} />
</ModalBody>
<ModalFooter>
<Button variant='ghost' mr={3} onClick={() => setShowPicker(false)}>
Close
</Button>
<Button onClick={() => {
setCustom(lastCustom)
setShowPicker(false)
lastCustom.from.setHours(0, 0, 0, 0)
lastCustom.to.setHours(23, 59, 59, 999)
props.onChange(lastCustom)
}}>Set</Button>
</ModalFooter>
</ModalContent>
</Modal>
*/}

<Menu autoSelect={false} strategy="fixed" placement="bottom-end" isOpen={showDropdown} onClose={() => setShowDropdown(false)}>
<Popover isOpen={showPicker} placement="bottom-end" onClose={() => setShowPicker(false)}>
<PopoverAnchor>
<MenuButton as={Button}
rightIcon={<MdArrowDropDown size={26} className="buttonSideIcon" onClick={e => setShowPicker(false) || setShowDropdown(!showPicker)} style={{ marginRight: -8, marginLeft: 4 }} />}
leftIcon={!props.dashboard && <AiOutlineCalendar size={26} className="buttonSideIcon" onClick={e => setShowDropdown(false) || setShowPicker(!showPicker)} style={{ marginRight: 6, marginLeft: -8 }} />}
variant="ddl"
className="durationPicker"
style={{ ...detailedSubText }}
disabled={props.disabled}
borderRadius='4px'>
{custom ? (
<>
{ddmm(custom.from) + " - " + ddmm(custom.to)}
</>
) : (
<>
{ts.k} {t(ts.t).toLowerCase()}
</>
)}
</MenuButton>
</PopoverAnchor>
<PopoverContent>
<PopoverBody>
<DatePicker onChange={setLastCustom} />
<Box textAlign="right">
<Button variant='ghost' mr={3} onClick={() => setShowPicker(false)}>
{t("close")}
</Button>
<Button isDisabled={!lastCustom || !lastCustom.from || !lastCustom.to} onClick={() => {
setCustom(lastCustom)
setShowPicker(false)
lastCustom.from.setHours(0, 0, 0, 0)
lastCustom.to.setHours(23, 59, 59, 999)
props.onChange(lastCustom)
}}>{t("set")}</Button>
</Box>
</PopoverBody>
</PopoverContent>
</Popover>
<MenuList zIndex={2}>
{renderTimespans.map((x, i) => {
let divider = <></>
let borderStyle = {};
if (i === 0) borderStyle = { borderTopLeftRadius: 6, borderTopRightRadius: 6 }
if (i === renderTimespans.length - 1) borderStyle = { borderBottomLeftRadius: 6, borderBottomRightRadius: 6 }
else divider = <MenuDivider />
return <div key={x.v + "p"}>
<MenuItem key={x.v} className={!custom && ts.v === x.v ? "menuActive" : undefined} style={{ ...detailedSubText, ...borderStyle }}
onClick={() => {
setCustom(null)
props.onChange(x.v)
}}>
{x.k} {t(x.t).toLowerCase()}
</MenuItem>
{divider}
</div>
})}
</MenuList>
</Menu>
</>
)
}
2 changes: 1 addition & 1 deletion src/components/Graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ class Graph extends Component {
return dataKeyChanged || this.props.data.length !== nextProps.data.length || dataUpdated
}
getXRange() {
return [this.props.from / 1000, new Date().getTime() / 1000]
return [this.props.from / 1000, this.props.to ? this.props.to / 1000 : new Date().getTime() / 1000]
}
resize = (e) => {
if (window.innerWidth === screenW) return;
Expand Down
4 changes: 4 additions & 0 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,8 @@ code {

.u-select {
background: rgba(0, 0, 0, 0.2) !important;
}

.rdp {
--rdp-background-color: #5e8d88a0 !important;
}
Loading

0 comments on commit 2fd1ab2

Please sign in to comment.