Skip to content

Commit

Permalink
Merge pull request #154 from mikecao/dev
Browse files Browse the repository at this point in the history
v0.32.0
  • Loading branch information
mikecao authored Sep 14, 2020
2 parents dd7081c + 38ec91c commit d6877af
Show file tree
Hide file tree
Showing 56 changed files with 879 additions and 205 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ Or with MySQL support:
docker pull ghcr.io/mikecao/umami:mysql-latest
```


## Getting updates

To get the latest features, simply do a pull, install any new dependencies, and rebuild:
Expand Down
1 change: 1 addition & 0 deletions assets/calendar-alt.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/list-ul.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions components/WebsiteDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export default function WebsiteDetails({ websiteId }) {

const BackButton = () => (
<Button
key="back-button"
className={styles.backButton}
icon={<Arrow />}
size="xsmall"
Expand Down
4 changes: 2 additions & 2 deletions components/WebsiteList.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import useFetch from 'hooks/useFetch';
import Arrow from 'assets/arrow-right.svg';
import styles from './WebsiteList.module.css';

export default function WebsiteList() {
export default function WebsiteList({ userId }) {
const router = useRouter();
const { data } = useFetch('/api/websites');
const { data } = useFetch('/api/websites', { userId });

if (!data) {
return null;
Expand Down
6 changes: 6 additions & 0 deletions components/common/Button.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export default function Button({
className,
tooltip,
tooltipId,
disabled = false,
onClick = () => {},
...props
}) {
return (
Expand All @@ -27,7 +29,11 @@ export default function Button({
[styles.xsmall]: size === 'xsmall',
[styles.action]: variant === 'action',
[styles.danger]: variant === 'danger',
[styles.light]: variant === 'light',
[styles.disabled]: disabled,
})}
disabled={disabled}
onClick={!disabled ? onClick : null}
{...props}
>
{icon && <Icon icon={icon} size={size} />}
Expand Down
40 changes: 33 additions & 7 deletions components/common/Button.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
}

.button:hover {
background: #eaeaea;
background: var(--gray200);
}

.button:active {
Expand All @@ -38,19 +38,45 @@
}

.action {
color: var(--gray50) !important;
background: var(--gray900) !important;
color: var(--gray50);
background: var(--gray900);
}

.action:hover {
background: var(--gray800) !important;
background: var(--gray800);
}

.danger {
color: var(--gray50) !important;
background: var(--red500) !important;
color: var(--gray50);
background: var(--red500);
}

.danger:hover {
background: var(--red400) !important;
background: var(--red400);
}

.light {
background: var(--gray50);
}

.light:hover {
background: var(--gray75);
}

.button:disabled {
cursor: default;
color: var(--gray500);
background: var(--gray75);
}

.button:disabled:active {
color: var(--gray500);
}

.button:disabled:hover {
background: var(--gray75);
}

.button.light:disabled {
background: var(--gray50);
}
261 changes: 261 additions & 0 deletions components/common/Calendar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
import React, { useState } from 'react';
import classNames from 'classnames';
import {
startOfWeek,
startOfMonth,
startOfYear,
endOfMonth,
addDays,
subDays,
addYears,
subYears,
addMonths,
setMonth,
setYear,
isSameDay,
isBefore,
isAfter,
} from 'date-fns';
import Button from './Button';
import useLocale from 'hooks/useLocale';
import { dateFormat } from 'lib/lang';
import { chunk } from 'lib/array';
import Chevron from 'assets/chevron-down.svg';
import Cross from 'assets/times.svg';
import styles from './Calendar.module.css';
import Icon from './Icon';

export default function Calendar({ date, minDate, maxDate, onChange }) {
const [locale] = useLocale();
const [selectMonth, setSelectMonth] = useState(false);
const [selectYear, setSelectYear] = useState(false);

const month = dateFormat(date, 'MMMM', locale);
const year = date.getFullYear();

function toggleMonthSelect() {
setSelectYear(false);
setSelectMonth(state => !state);
}

function toggleYearSelect() {
setSelectMonth(false);
setSelectYear(state => !state);
}

function handleChange(value) {
setSelectMonth(false);
setSelectYear(false);
if (value) {
onChange(value);
}
}

return (
<div className={styles.calendar}>
<div className={styles.header}>
<div>{date.getDate()}</div>
<div
className={classNames(styles.selector, { [styles.open]: selectMonth })}
onClick={toggleMonthSelect}
>
{month}
<Icon className={styles.icon} icon={selectMonth ? <Cross /> : <Chevron />} size="small" />
</div>
<div
className={classNames(styles.selector, { [styles.open]: selectYear })}
onClick={toggleYearSelect}
>
{year}
<Icon className={styles.icon} icon={selectYear ? <Cross /> : <Chevron />} size="small" />
</div>
</div>
{!selectMonth && !selectYear && (
<DaySelector
date={date}
minDate={minDate}
maxDate={maxDate}
locale={locale}
onSelect={handleChange}
/>
)}
{selectMonth && (
<MonthSelector
date={date}
minDate={minDate}
maxDate={maxDate}
locale={locale}
onSelect={handleChange}
onClose={toggleMonthSelect}
/>
)}
{selectYear && (
<YearSelector
date={date}
minDate={minDate}
maxDate={maxDate}
onSelect={handleChange}
onClose={toggleYearSelect}
/>
)}
</div>
);
}

const DaySelector = ({ date, minDate, maxDate, locale, onSelect }) => {
const startWeek = startOfWeek(date);
const startMonth = startOfMonth(date);
const startDay = subDays(startMonth, startMonth.getDay() + 1);
const month = date.getMonth();
const year = date.getFullYear();

const daysOfWeek = [];
for (let i = 0; i < 7; i++) {
daysOfWeek.push(addDays(startWeek, i));
}

const days = [];
for (let i = 0; i < 35; i++) {
days.push(addDays(startDay, i));
}

return (
<table>
<thead>
<tr>
{daysOfWeek.map((day, i) => (
<th key={i} className={locale}>
{dateFormat(day, 'EEE', locale)}
</th>
))}
</tr>
</thead>
<tbody>
{chunk(days, 7).map((week, i) => (
<tr key={i}>
{week.map((day, j) => {
const disabled = isBefore(day, minDate) || isAfter(day, maxDate);
return (
<td
key={j}
className={classNames({
[styles.selected]: isSameDay(date, day),
[styles.faded]: day.getMonth() !== month || day.getFullYear() !== year,
[styles.disabled]: disabled,
})}
onClick={!disabled ? () => onSelect(day) : null}
>
{day.getDate()}
</td>
);
})}
</tr>
))}
</tbody>
</table>
);
};

const MonthSelector = ({ date, minDate, maxDate, locale, onSelect }) => {
const start = startOfYear(date);
const months = [];
for (let i = 0; i < 12; i++) {
months.push(addMonths(start, i));
}

function handleSelect(value) {
onSelect(setMonth(date, value));
}

return (
<table>
<tbody>
{chunk(months, 3).map((row, i) => (
<tr key={i}>
{row.map((month, j) => {
const disabled =
isBefore(endOfMonth(month), minDate) || isAfter(startOfMonth(month), maxDate);
return (
<td
key={j}
className={classNames(locale, {
[styles.selected]: month.getMonth() === date.getMonth(),
[styles.disabled]: disabled,
})}
onClick={!disabled ? () => handleSelect(month.getMonth()) : null}
>
{dateFormat(month, 'MMMM', locale)}
</td>
);
})}
</tr>
))}
</tbody>
</table>
);
};

const YearSelector = ({ date, minDate, maxDate, onSelect }) => {
const [currentDate, setCurrentDate] = useState(date);
const year = date.getFullYear();
const currentYear = currentDate.getFullYear();
const minYear = minDate.getFullYear();
const maxYear = maxDate.getFullYear();
const years = [];
for (let i = 0; i < 15; i++) {
years.push(currentYear - 7 + i);
}

function handleSelect(value) {
onSelect(setYear(date, value));
}

function handlePrevClick() {
setCurrentDate(state => subYears(state, 15));
}

function handleNextClick() {
setCurrentDate(state => addYears(state, 15));
}

return (
<div className={styles.pager}>
<Button
icon={<Chevron />}
size="small"
className={styles.left}
onClick={handlePrevClick}
disabled={years[0] <= minYear}
variant="light"
/>
<table>
<tbody>
{chunk(years, 5).map((row, i) => (
<tr key={i}>
{row.map((n, j) => (
<td
key={j}
className={classNames({
[styles.selected]: n === year,
[styles.disabled]: n < minYear || n > maxYear,
})}
onClick={() => (n < minYear || n > maxYear ? null : handleSelect(n))}
>
{n}
</td>
))}
</tr>
))}
</tbody>
</table>
<Button
icon={<Chevron />}
size="small"
className={styles.right}
onClick={handleNextClick}
disabled={years[years.length - 1] > maxYear}
variant="light"
/>
</div>
);
};
Loading

1 comment on commit d6877af

@vercel
Copy link

@vercel vercel bot commented on d6877af Sep 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.