mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-11-27 16:03:19 +03:00
refactor(component): move date-picker to ui, add story, support responsive (#5468)
- move to `component/ui` - add `AFFiNEDatePicker` & `BlocksuiteDatePicker` story - inline mode support - responsive support <picture> <source media="(prefers-color-scheme: dark)" srcset="https://github.com/toeverything/AFFiNE/assets/39363750/320bef49-380f-40a2-b3b2-4b74dd2d8da4"> <img alt="" src="https://github.com/toeverything/AFFiNE/assets/39363750/fc9e7808-02fe-49a1-aa78-aea254fb1f9d"> </picture>
This commit is contained in:
parent
8f80bdb7af
commit
2db3c933fa
@ -1,3 +1,4 @@
|
|||||||
|
import '../src/theme/global.css';
|
||||||
import './preview.css';
|
import './preview.css';
|
||||||
import { ThemeProvider, useTheme } from 'next-themes';
|
import { ThemeProvider, useTheme } from 'next-themes';
|
||||||
import type { ComponentType } from 'react';
|
import type { ComponentType } from 'react';
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
"@emotion/react": "^11.11.1",
|
"@emotion/react": "^11.11.1",
|
||||||
"@emotion/server": "^11.11.0",
|
"@emotion/server": "^11.11.0",
|
||||||
"@emotion/styled": "^11.11.0",
|
"@emotion/styled": "^11.11.0",
|
||||||
|
"@lit/react": "^1.0.2",
|
||||||
"@popperjs/core": "^2.11.8",
|
"@popperjs/core": "^2.11.8",
|
||||||
"@radix-ui/react-avatar": "^1.0.4",
|
"@radix-ui/react-avatar": "^1.0.4",
|
||||||
"@radix-ui/react-collapsible": "^1.0.3",
|
"@radix-ui/react-collapsible": "^1.0.3",
|
||||||
|
@ -1,191 +0,0 @@
|
|||||||
import {
|
|
||||||
ArrowDownSmallIcon,
|
|
||||||
ArrowLeftSmallIcon,
|
|
||||||
ArrowRightSmallIcon,
|
|
||||||
} from '@blocksuite/icons';
|
|
||||||
import dayjs from 'dayjs';
|
|
||||||
import { useCallback, useState } from 'react';
|
|
||||||
import DatePicker from 'react-datepicker';
|
|
||||||
|
|
||||||
import * as styles from './index.css';
|
|
||||||
const months = [
|
|
||||||
'January',
|
|
||||||
'February',
|
|
||||||
'March',
|
|
||||||
'April',
|
|
||||||
'May',
|
|
||||||
'June',
|
|
||||||
'July',
|
|
||||||
'August',
|
|
||||||
'September',
|
|
||||||
'October',
|
|
||||||
'November',
|
|
||||||
'December',
|
|
||||||
];
|
|
||||||
type DatePickerProps = {
|
|
||||||
value?: string;
|
|
||||||
onChange: (value: string) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const AFFiNEDatePicker = (props: DatePickerProps) => {
|
|
||||||
const { value, onChange } = props;
|
|
||||||
const [openMonthPicker, setOpenMonthPicker] = useState(false);
|
|
||||||
const [selectedDate, setSelectedDate] = useState<Date | null>(
|
|
||||||
value ? dayjs(value).toDate() : null
|
|
||||||
);
|
|
||||||
const handleOpenMonthPicker = useCallback(() => {
|
|
||||||
setOpenMonthPicker(true);
|
|
||||||
}, []);
|
|
||||||
const handleCloseMonthPicker = useCallback(() => {
|
|
||||||
setOpenMonthPicker(false);
|
|
||||||
}, []);
|
|
||||||
const handleSelectDate = (date: Date | null) => {
|
|
||||||
if (date) {
|
|
||||||
setSelectedDate(date);
|
|
||||||
onChange(dayjs(date).format('YYYY-MM-DD'));
|
|
||||||
setOpenMonthPicker(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const renderCustomHeader = ({
|
|
||||||
date,
|
|
||||||
decreaseMonth,
|
|
||||||
increaseMonth,
|
|
||||||
prevMonthButtonDisabled,
|
|
||||||
nextMonthButtonDisabled,
|
|
||||||
}: {
|
|
||||||
date: Date;
|
|
||||||
decreaseMonth: () => void;
|
|
||||||
increaseMonth: () => void;
|
|
||||||
prevMonthButtonDisabled: boolean;
|
|
||||||
nextMonthButtonDisabled: boolean;
|
|
||||||
}) => {
|
|
||||||
const selectedYear = dayjs(date).year();
|
|
||||||
const selectedMonth = dayjs(date).month();
|
|
||||||
return (
|
|
||||||
<div className={styles.headerStyle}>
|
|
||||||
<div
|
|
||||||
data-testid="date-picker-current-month"
|
|
||||||
className={styles.mouthStyle}
|
|
||||||
>
|
|
||||||
{months[selectedMonth]}
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
data-testid="date-picker-current-year"
|
|
||||||
className={styles.yearStyle}
|
|
||||||
>
|
|
||||||
{selectedYear}
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
data-testid="month-picker-button"
|
|
||||||
className={styles.arrowDownStyle}
|
|
||||||
onClick={handleOpenMonthPicker}
|
|
||||||
>
|
|
||||||
<ArrowDownSmallIcon />
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
data-testid="date-picker-prev-button"
|
|
||||||
className={styles.arrowLeftStyle}
|
|
||||||
onClick={decreaseMonth}
|
|
||||||
disabled={prevMonthButtonDisabled}
|
|
||||||
>
|
|
||||||
<ArrowLeftSmallIcon />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
data-testid="date-picker-next-button"
|
|
||||||
className={styles.arrowRightStyle}
|
|
||||||
onClick={increaseMonth}
|
|
||||||
disabled={nextMonthButtonDisabled}
|
|
||||||
>
|
|
||||||
<ArrowRightSmallIcon />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
const renderCustomMonthHeader = ({
|
|
||||||
date,
|
|
||||||
decreaseYear,
|
|
||||||
increaseYear,
|
|
||||||
prevYearButtonDisabled,
|
|
||||||
nextYearButtonDisabled,
|
|
||||||
}: {
|
|
||||||
date: Date;
|
|
||||||
decreaseYear: () => void;
|
|
||||||
increaseYear: () => void;
|
|
||||||
prevYearButtonDisabled: boolean;
|
|
||||||
nextYearButtonDisabled: boolean;
|
|
||||||
}) => {
|
|
||||||
const selectedYear = dayjs(date).year();
|
|
||||||
return (
|
|
||||||
<div className={styles.monthHeaderStyle}>
|
|
||||||
<div
|
|
||||||
data-testid="month-picker-current-year"
|
|
||||||
className={styles.monthTitleStyle}
|
|
||||||
>
|
|
||||||
{selectedYear}
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
data-testid="month-picker-prev-button"
|
|
||||||
className={styles.arrowLeftStyle}
|
|
||||||
onClick={decreaseYear}
|
|
||||||
disabled={prevYearButtonDisabled}
|
|
||||||
>
|
|
||||||
<ArrowLeftSmallIcon />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
data-testid="month-picker-next-button"
|
|
||||||
className={styles.arrowRightStyle}
|
|
||||||
onClick={increaseYear}
|
|
||||||
disabled={nextYearButtonDisabled}
|
|
||||||
>
|
|
||||||
<ArrowRightSmallIcon />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<DatePicker
|
|
||||||
onClickOutside={handleCloseMonthPicker}
|
|
||||||
className={styles.inputStyle}
|
|
||||||
calendarClassName={styles.calendarStyle}
|
|
||||||
weekDayClassName={() => styles.weekStyle}
|
|
||||||
dayClassName={() => styles.dayStyle}
|
|
||||||
popperClassName={styles.popperStyle}
|
|
||||||
monthClassName={() => styles.mouthsStyle}
|
|
||||||
selected={selectedDate}
|
|
||||||
onChange={handleSelectDate}
|
|
||||||
showPopperArrow={false}
|
|
||||||
dateFormat="MMM dd"
|
|
||||||
showMonthYearPicker={openMonthPicker}
|
|
||||||
shouldCloseOnSelect={!openMonthPicker}
|
|
||||||
renderCustomHeader={({
|
|
||||||
date,
|
|
||||||
decreaseYear,
|
|
||||||
increaseYear,
|
|
||||||
decreaseMonth,
|
|
||||||
increaseMonth,
|
|
||||||
prevYearButtonDisabled,
|
|
||||||
nextYearButtonDisabled,
|
|
||||||
prevMonthButtonDisabled,
|
|
||||||
nextMonthButtonDisabled,
|
|
||||||
}) =>
|
|
||||||
openMonthPicker
|
|
||||||
? renderCustomMonthHeader({
|
|
||||||
date,
|
|
||||||
decreaseYear,
|
|
||||||
increaseYear,
|
|
||||||
prevYearButtonDisabled,
|
|
||||||
nextYearButtonDisabled,
|
|
||||||
})
|
|
||||||
: renderCustomHeader({
|
|
||||||
date,
|
|
||||||
decreaseMonth,
|
|
||||||
increaseMonth,
|
|
||||||
prevMonthButtonDisabled,
|
|
||||||
nextMonthButtonDisabled,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AFFiNEDatePicker;
|
|
@ -3,6 +3,7 @@ export * from './styles';
|
|||||||
export * from './ui/avatar';
|
export * from './ui/avatar';
|
||||||
export * from './ui/button';
|
export * from './ui/button';
|
||||||
export * from './ui/checkbox';
|
export * from './ui/checkbox';
|
||||||
|
export * from './ui/date-picker';
|
||||||
export * from './ui/divider';
|
export * from './ui/divider';
|
||||||
export * from './ui/empty';
|
export * from './ui/empty';
|
||||||
export * from './ui/input';
|
export * from './ui/input';
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
@import 'react-datepicker/dist/react-datepicker.css';
|
|
||||||
@import './fonts.css';
|
@import './fonts.css';
|
||||||
|
|
||||||
* {
|
* {
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
import type { Meta, StoryFn } from '@storybook/react';
|
||||||
|
|
||||||
|
import { BlocksuiteDatePicker } from './blocksuite-date-picker';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'UI/Date Picker/Blocksuite Date Picker',
|
||||||
|
} satisfies Meta<typeof BlocksuiteDatePicker>;
|
||||||
|
|
||||||
|
export const Basic: StoryFn<typeof BlocksuiteDatePicker> = () => {
|
||||||
|
return (
|
||||||
|
<div style={{ width: 300 }}>
|
||||||
|
<BlocksuiteDatePicker />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,11 @@
|
|||||||
|
// eslint-disable-next-line @typescript-eslint/no-restricted-imports
|
||||||
|
import { DatePicker } from '@blocksuite/blocks/src/_common/components/date-picker/index';
|
||||||
|
import { createComponent } from '@lit/react';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export const BlocksuiteDatePicker = createComponent({
|
||||||
|
tagName: 'date-picker',
|
||||||
|
elementClass: DatePicker,
|
||||||
|
react: React,
|
||||||
|
events: {},
|
||||||
|
});
|
@ -0,0 +1,37 @@
|
|||||||
|
import type { Meta, StoryFn } from '@storybook/react';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import { AFFiNEDatePicker } from '.';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'UI/Date Picker/Date Picker',
|
||||||
|
} satisfies Meta<typeof AFFiNEDatePicker>;
|
||||||
|
|
||||||
|
const _format = 'YYYY-MM-DD';
|
||||||
|
|
||||||
|
const Template: StoryFn<typeof AFFiNEDatePicker> = args => {
|
||||||
|
const [date, setDate] = useState(dayjs().format(_format));
|
||||||
|
return (
|
||||||
|
<div style={{ minHeight: 400, maxWidth: 600, margin: '0 auto' }}>
|
||||||
|
<div style={{ marginBottom: 20 }}>Selected Date: {date}</div>
|
||||||
|
|
||||||
|
<AFFiNEDatePicker
|
||||||
|
value={date}
|
||||||
|
{...args}
|
||||||
|
onChange={e => {
|
||||||
|
setDate(dayjs(e, _format).format(_format));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Basic: StoryFn<typeof AFFiNEDatePicker> = Template.bind(undefined);
|
||||||
|
Basic.args = {};
|
||||||
|
|
||||||
|
export const Inline: StoryFn<typeof AFFiNEDatePicker> =
|
||||||
|
Template.bind(undefined);
|
||||||
|
Inline.args = {
|
||||||
|
inline: true,
|
||||||
|
};
|
257
packages/frontend/component/src/ui/date-picker/date-picker.tsx
Normal file
257
packages/frontend/component/src/ui/date-picker/date-picker.tsx
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
import {
|
||||||
|
ArrowDownSmallIcon,
|
||||||
|
ArrowLeftSmallIcon,
|
||||||
|
ArrowRightSmallIcon,
|
||||||
|
} from '@blocksuite/icons';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import { type HTMLAttributes, useCallback, useState } from 'react';
|
||||||
|
import DatePicker, { type ReactDatePickerProps } from 'react-datepicker';
|
||||||
|
|
||||||
|
import * as styles from './index.css';
|
||||||
|
const months = [
|
||||||
|
'January',
|
||||||
|
'February',
|
||||||
|
'March',
|
||||||
|
'April',
|
||||||
|
'May',
|
||||||
|
'June',
|
||||||
|
'July',
|
||||||
|
'August',
|
||||||
|
'September',
|
||||||
|
'October',
|
||||||
|
'November',
|
||||||
|
'December',
|
||||||
|
];
|
||||||
|
export interface AFFiNEDatePickerProps
|
||||||
|
extends Omit<ReactDatePickerProps, 'onChange'> {
|
||||||
|
value?: string;
|
||||||
|
onChange: (value: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface HeaderLayoutProps extends HTMLAttributes<HTMLDivElement> {
|
||||||
|
length: number;
|
||||||
|
left: React.ReactNode;
|
||||||
|
right: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The `DatePicker` should work with different width
|
||||||
|
* This is a hack to make header's item align with calendar cell's label, **instead of the cell**
|
||||||
|
* @param length: number of items that calendar body row has
|
||||||
|
*/
|
||||||
|
const HeaderLayout = ({
|
||||||
|
length,
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
className,
|
||||||
|
...attrs
|
||||||
|
}: HeaderLayoutProps) => {
|
||||||
|
return (
|
||||||
|
<div className={clsx(styles.row, className)} {...attrs}>
|
||||||
|
{Array.from({ length })
|
||||||
|
.fill(0)
|
||||||
|
.map((_, index) => {
|
||||||
|
const isLeft = index === 0;
|
||||||
|
const isRight = index === length - 1;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
data-is-left={isLeft}
|
||||||
|
data-is-right={isRight}
|
||||||
|
className={styles.headerLayoutCell}
|
||||||
|
>
|
||||||
|
<div className={styles.headerLayoutCellOrigin}>
|
||||||
|
{isLeft ? left : isRight ? right : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AFFiNEDatePicker = ({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
...props
|
||||||
|
}: AFFiNEDatePickerProps) => {
|
||||||
|
const [openMonthPicker, setOpenMonthPicker] = useState(false);
|
||||||
|
const [selectedDate, setSelectedDate] = useState<Date | null>(
|
||||||
|
value ? dayjs(value).toDate() : null
|
||||||
|
);
|
||||||
|
const handleOpenMonthPicker = useCallback(() => {
|
||||||
|
setOpenMonthPicker(true);
|
||||||
|
}, []);
|
||||||
|
const handleCloseMonthPicker = useCallback(() => {
|
||||||
|
setOpenMonthPicker(false);
|
||||||
|
}, []);
|
||||||
|
const handleSelectDate = (date: Date | null) => {
|
||||||
|
if (date) {
|
||||||
|
setSelectedDate(date);
|
||||||
|
onChange(dayjs(date).format('YYYY-MM-DD'));
|
||||||
|
setOpenMonthPicker(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const renderCustomHeader = ({
|
||||||
|
date,
|
||||||
|
decreaseMonth,
|
||||||
|
increaseMonth,
|
||||||
|
prevMonthButtonDisabled,
|
||||||
|
nextMonthButtonDisabled,
|
||||||
|
}: {
|
||||||
|
date: Date;
|
||||||
|
decreaseMonth: () => void;
|
||||||
|
increaseMonth: () => void;
|
||||||
|
prevMonthButtonDisabled: boolean;
|
||||||
|
nextMonthButtonDisabled: boolean;
|
||||||
|
}) => {
|
||||||
|
const selectedYear = dayjs(date).year();
|
||||||
|
const selectedMonth = dayjs(date).month();
|
||||||
|
return (
|
||||||
|
<HeaderLayout
|
||||||
|
length={7}
|
||||||
|
className={styles.headerStyle}
|
||||||
|
left={
|
||||||
|
<div className={styles.headerLabel}>
|
||||||
|
<div
|
||||||
|
data-testid="date-picker-current-month"
|
||||||
|
className={styles.mouthStyle}
|
||||||
|
>
|
||||||
|
{months[selectedMonth]}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
data-testid="date-picker-current-year"
|
||||||
|
className={styles.yearStyle}
|
||||||
|
>
|
||||||
|
{selectedYear}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
data-testid="month-picker-button"
|
||||||
|
className={styles.arrowDownStyle}
|
||||||
|
onClick={handleOpenMonthPicker}
|
||||||
|
>
|
||||||
|
<ArrowDownSmallIcon />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
right={
|
||||||
|
<div className={styles.headerActionWrapper}>
|
||||||
|
<button
|
||||||
|
data-testid="date-picker-prev-button"
|
||||||
|
className={styles.headerAction}
|
||||||
|
onClick={decreaseMonth}
|
||||||
|
disabled={prevMonthButtonDisabled}
|
||||||
|
>
|
||||||
|
<ArrowLeftSmallIcon />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
data-testid="date-picker-next-button"
|
||||||
|
className={styles.headerAction}
|
||||||
|
onClick={increaseMonth}
|
||||||
|
disabled={nextMonthButtonDisabled}
|
||||||
|
>
|
||||||
|
<ArrowRightSmallIcon />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const renderCustomMonthHeader = ({
|
||||||
|
date,
|
||||||
|
decreaseYear,
|
||||||
|
increaseYear,
|
||||||
|
prevYearButtonDisabled,
|
||||||
|
nextYearButtonDisabled,
|
||||||
|
}: {
|
||||||
|
date: Date;
|
||||||
|
decreaseYear: () => void;
|
||||||
|
increaseYear: () => void;
|
||||||
|
prevYearButtonDisabled: boolean;
|
||||||
|
nextYearButtonDisabled: boolean;
|
||||||
|
}) => {
|
||||||
|
const selectedYear = dayjs(date).year();
|
||||||
|
return (
|
||||||
|
<HeaderLayout
|
||||||
|
length={3}
|
||||||
|
className={styles.monthHeaderStyle}
|
||||||
|
left={
|
||||||
|
<div
|
||||||
|
data-testid="month-picker-current-year"
|
||||||
|
className={styles.monthTitleStyle}
|
||||||
|
>
|
||||||
|
{selectedYear}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
right={
|
||||||
|
<div className={styles.headerActionWrapper}>
|
||||||
|
<button
|
||||||
|
data-testid="month-picker-prev-button"
|
||||||
|
className={styles.headerAction}
|
||||||
|
onClick={decreaseYear}
|
||||||
|
disabled={prevYearButtonDisabled}
|
||||||
|
>
|
||||||
|
<ArrowLeftSmallIcon />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
data-testid="month-picker-next-button"
|
||||||
|
className={styles.headerAction}
|
||||||
|
onClick={increaseYear}
|
||||||
|
disabled={nextYearButtonDisabled}
|
||||||
|
>
|
||||||
|
<ArrowRightSmallIcon />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<DatePicker
|
||||||
|
onClickOutside={handleCloseMonthPicker}
|
||||||
|
className={styles.inputStyle}
|
||||||
|
calendarClassName={styles.calendarStyle}
|
||||||
|
weekDayClassName={() => styles.weekStyle}
|
||||||
|
dayClassName={() => styles.dayStyle}
|
||||||
|
popperClassName={styles.popperStyle}
|
||||||
|
monthClassName={() => styles.mouthsStyle}
|
||||||
|
selected={selectedDate}
|
||||||
|
onChange={handleSelectDate}
|
||||||
|
showPopperArrow={false}
|
||||||
|
dateFormat="MMM dd"
|
||||||
|
showMonthYearPicker={openMonthPicker}
|
||||||
|
shouldCloseOnSelect={!openMonthPicker}
|
||||||
|
renderCustomHeader={({
|
||||||
|
date,
|
||||||
|
decreaseYear,
|
||||||
|
increaseYear,
|
||||||
|
decreaseMonth,
|
||||||
|
increaseMonth,
|
||||||
|
prevYearButtonDisabled,
|
||||||
|
nextYearButtonDisabled,
|
||||||
|
prevMonthButtonDisabled,
|
||||||
|
nextMonthButtonDisabled,
|
||||||
|
}) =>
|
||||||
|
openMonthPicker
|
||||||
|
? renderCustomMonthHeader({
|
||||||
|
date,
|
||||||
|
decreaseYear,
|
||||||
|
increaseYear,
|
||||||
|
prevYearButtonDisabled,
|
||||||
|
nextYearButtonDisabled,
|
||||||
|
})
|
||||||
|
: renderCustomHeader({
|
||||||
|
date,
|
||||||
|
decreaseMonth,
|
||||||
|
increaseMonth,
|
||||||
|
prevMonthButtonDisabled,
|
||||||
|
nextMonthButtonDisabled,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AFFiNEDatePicker;
|
@ -1,5 +1,38 @@
|
|||||||
import { globalStyle, style } from '@vanilla-extract/css';
|
import { globalStyle, style } from '@vanilla-extract/css';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* we do not import css from 'react-date-picker' anymore
|
||||||
|
**/
|
||||||
|
globalStyle('.react-datepicker__aria-live', {
|
||||||
|
display: 'none',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const basicCell = style({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
minWidth: '28px',
|
||||||
|
maxWidth: '50px',
|
||||||
|
flex: '1',
|
||||||
|
userSelect: 'none',
|
||||||
|
});
|
||||||
|
export const headerLayoutCell = style([basicCell]);
|
||||||
|
export const headerLayoutCellOrigin = style({
|
||||||
|
width: 0,
|
||||||
|
height: 'fit-content',
|
||||||
|
display: 'flex',
|
||||||
|
selectors: {
|
||||||
|
'[data-is-left="true"] &': {
|
||||||
|
justifyContent: 'flex-start',
|
||||||
|
marginLeft: '-12px',
|
||||||
|
},
|
||||||
|
'[data-is-right="true"] &': {
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
marginRight: '-24px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export const inputStyle = style({
|
export const inputStyle = style({
|
||||||
fontSize: 'var(--affine-font-xs)',
|
fontSize: 'var(--affine-font-xs)',
|
||||||
width: '50px',
|
width: '50px',
|
||||||
@ -16,52 +49,40 @@ export const inputStyle = style({
|
|||||||
});
|
});
|
||||||
export const popperStyle = style({
|
export const popperStyle = style({
|
||||||
boxShadow: 'var(--affine-shadow-2)',
|
boxShadow: 'var(--affine-shadow-2)',
|
||||||
padding: '0 10px',
|
// TODO: for menu offset, need to be optimized
|
||||||
marginTop: '16px',
|
marginTop: '16px',
|
||||||
background: 'var(--affine-background-overlay-panel-color)',
|
background: 'var(--affine-background-overlay-panel-color)',
|
||||||
borderRadius: '12px',
|
borderRadius: '12px',
|
||||||
width: '300px',
|
width: '300px',
|
||||||
|
zIndex: 'var(--affine-z-index-popover)',
|
||||||
});
|
});
|
||||||
|
|
||||||
globalStyle('.react-datepicker__header', {
|
globalStyle('.react-datepicker__header', {
|
||||||
background: 'var(--affine-background-overlay-panel-color)',
|
background: 'none',
|
||||||
border: 'none',
|
border: 'none',
|
||||||
marginBottom: '6px',
|
marginBottom: '8px',
|
||||||
|
});
|
||||||
|
globalStyle('.react-datepicker__header, .react-datepicker__month', {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: '8px',
|
||||||
});
|
});
|
||||||
export const headerStyle = style({
|
export const headerStyle = style({
|
||||||
background: 'var(--affine-background-overlay-panel-color)',
|
|
||||||
border: 'none',
|
border: 'none',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
width: '100%',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
marginBottom: '12px',
|
justifyContent: 'space-between',
|
||||||
padding: '0 14px',
|
width: '100%',
|
||||||
position: 'relative',
|
|
||||||
});
|
});
|
||||||
export const monthHeaderStyle = style({
|
export const monthHeaderStyle = style({
|
||||||
background: 'var(--affine-background-overlay-panel-color)',
|
|
||||||
border: 'none',
|
border: 'none',
|
||||||
display: 'flex',
|
|
||||||
width: '100%',
|
width: '100%',
|
||||||
alignItems: 'center',
|
|
||||||
marginBottom: '18px',
|
marginBottom: '18px',
|
||||||
padding: '0 14px',
|
|
||||||
position: 'relative',
|
|
||||||
'::after': {
|
|
||||||
content: '""',
|
|
||||||
position: 'absolute',
|
|
||||||
width: 'calc(100% - 24px)',
|
|
||||||
height: '1px',
|
|
||||||
background: 'var(--affine-border-color)',
|
|
||||||
bottom: '-18px',
|
|
||||||
left: '12px',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
export const monthTitleStyle = style({
|
export const monthTitleStyle = style({
|
||||||
color: 'var(--affine-text-primary-color)',
|
color: 'var(--affine-text-primary-color)',
|
||||||
fontWeight: '600',
|
fontWeight: '600',
|
||||||
fontSize: 'var(--affine-font-sm)',
|
fontSize: 'var(--affine-font-sm)',
|
||||||
marginLeft: '12px',
|
|
||||||
});
|
});
|
||||||
export const yearStyle = style({
|
export const yearStyle = style({
|
||||||
marginLeft: '8px',
|
marginLeft: '8px',
|
||||||
@ -73,77 +94,92 @@ export const mouthStyle = style({
|
|||||||
color: 'var(--affine-text-primary-color)',
|
color: 'var(--affine-text-primary-color)',
|
||||||
fontWeight: '600',
|
fontWeight: '600',
|
||||||
fontSize: 'var(--affine-font-sm)',
|
fontSize: 'var(--affine-font-sm)',
|
||||||
|
cursor: 'pointer',
|
||||||
|
textAlign: 'center',
|
||||||
});
|
});
|
||||||
export const arrowLeftStyle = style({
|
export const headerLabel = style({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
});
|
||||||
|
export const headerActionWrapper = style({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 24,
|
||||||
|
});
|
||||||
|
export const headerAction = style({
|
||||||
width: '16px',
|
width: '16px',
|
||||||
height: '16px',
|
height: '16px',
|
||||||
textAlign: 'right',
|
|
||||||
position: 'absolute',
|
|
||||||
right: '50px',
|
|
||||||
});
|
});
|
||||||
export const arrowRightStyle = style({
|
|
||||||
width: '16px',
|
globalStyle('.react-datepicker__day-names, .react-datepicker__week', {
|
||||||
height: '16px',
|
display: 'flex',
|
||||||
right: '14px',
|
alignItems: 'center',
|
||||||
position: 'absolute',
|
justifyContent: 'space-between',
|
||||||
|
gap: 8,
|
||||||
});
|
});
|
||||||
export const weekStyle = style({
|
export const row = style({
|
||||||
fontSize: 'var(--affine-font-xs)',
|
display: 'flex',
|
||||||
color: 'var(--affine-text-secondary-color)',
|
alignItems: 'center',
|
||||||
display: 'inline-block',
|
justifyContent: 'space-between',
|
||||||
width: '28px',
|
gap: 8,
|
||||||
height: '28px',
|
|
||||||
lineHeight: '28px',
|
|
||||||
padding: '0 4px',
|
|
||||||
margin: '0px 6px',
|
|
||||||
verticalAlign: 'middle',
|
|
||||||
});
|
});
|
||||||
|
// header day cell
|
||||||
|
export const weekStyle = style([
|
||||||
|
basicCell,
|
||||||
|
{
|
||||||
|
height: '28px',
|
||||||
|
fontSize: 'var(--affine-font-xs)',
|
||||||
|
fontWeight: 500,
|
||||||
|
color: 'var(--affine-text-secondary-color)',
|
||||||
|
},
|
||||||
|
]);
|
||||||
export const calendarStyle = style({
|
export const calendarStyle = style({
|
||||||
background: 'var(--affine-background-overlay-panel-color)',
|
background: 'none',
|
||||||
border: 'none',
|
border: 'none',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
|
padding: '20px',
|
||||||
});
|
});
|
||||||
export const dayStyle = style({
|
|
||||||
fontSize: 'var(--affine-font-xs)',
|
export const dayStyle = style([
|
||||||
color: 'var(--affine-text-primary-color)',
|
basicCell,
|
||||||
display: 'inline-block',
|
{
|
||||||
width: '28px',
|
height: '28px',
|
||||||
height: '28px',
|
fontSize: 'var(--affine-font-xs)',
|
||||||
lineHeight: '28px',
|
color: 'var(--affine-text-primary-color)',
|
||||||
padding: '0 4px',
|
cursor: 'pointer',
|
||||||
margin: '6px 12px 6px 0px',
|
|
||||||
verticalAlign: 'middle',
|
fontWeight: '400',
|
||||||
fontWeight: '400',
|
borderRadius: '8px',
|
||||||
borderRadius: '8px',
|
selectors: {
|
||||||
selectors: {
|
'&:hover': {
|
||||||
'&:hover': {
|
background: 'var(--affine-hover-color)',
|
||||||
background: 'var(--affine-hover-color)',
|
borderRadius: '8px',
|
||||||
borderRadius: '8px',
|
transition: 'background-color 0.3s ease-in-out',
|
||||||
transition: 'background-color 0.3s ease-in-out',
|
},
|
||||||
},
|
'&[aria-selected="true"]': {
|
||||||
'&[aria-selected="true"]': {
|
color: 'var(--affine-black)',
|
||||||
color: 'var(--affine-black)',
|
background: 'var(--affine-hover-color)',
|
||||||
background: 'var(--affine-hover-color)',
|
},
|
||||||
},
|
'&[aria-selected="true"]:hover': {
|
||||||
'&[aria-selected="true"]:hover': {
|
background: 'var(--affine-hover-color)',
|
||||||
background: 'var(--affine-hover-color)',
|
},
|
||||||
},
|
'&[tabindex="0"][aria-selected="false"]': {
|
||||||
'&[tabindex="0"][aria-selected="false"]': {
|
background: 'var(--affine-background-overlay-panel-color)',
|
||||||
background: 'var(--affine-background-overlay-panel-color)',
|
},
|
||||||
},
|
'&.react-datepicker__day--today[aria-selected="false"]': {
|
||||||
'&.react-datepicker__day--today[aria-selected="false"]': {
|
background: 'var(--affine-primary-color)',
|
||||||
background: 'var(--affine-primary-color)',
|
color: 'var(--affine-palette-line-white)',
|
||||||
color: 'var(--affine-palette-line-white)',
|
},
|
||||||
},
|
'&.react-datepicker__day--today[aria-selected="false"]:hover': {
|
||||||
'&.react-datepicker__day--today[aria-selected="false"]:hover': {
|
color: 'var(--affine-black)',
|
||||||
color: 'var(--affine-black)',
|
background: 'var(--affine-hover-color)',
|
||||||
background: 'var(--affine-hover-color)',
|
},
|
||||||
},
|
'&.react-datepicker__day--outside-month[aria-selected="false"]': {
|
||||||
'&.react-datepicker__day--outside-month[aria-selected="false"]': {
|
color: 'var(--affine-text-disable-color)',
|
||||||
color: 'var(--affine-text-disable-color)',
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
]);
|
||||||
export const arrowDownStyle = style({
|
export const arrowDownStyle = style({
|
||||||
width: '16px',
|
width: '16px',
|
||||||
height: '16px',
|
height: '16px',
|
@ -1,5 +1,4 @@
|
|||||||
import { Input, Menu, MenuItem } from '@affine/component';
|
import { AFFiNEDatePicker, Input, Menu, MenuItem } from '@affine/component';
|
||||||
import { AFFiNEDatePicker } from '@affine/component/date-picker';
|
|
||||||
import type { LiteralValue, Tag } from '@affine/env/filter';
|
import type { LiteralValue, Tag } from '@affine/env/filter';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { type ReactNode } from 'react';
|
import { type ReactNode } from 'react';
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { AFFiNEDatePicker } from '@affine/component/date-picker';
|
import { AFFiNEDatePicker } from '@affine/component';
|
||||||
import type { Meta, StoryFn } from '@storybook/react';
|
import type { Meta, StoryFn } from '@storybook/react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
@ -8,7 +8,7 @@ export default {
|
|||||||
parameters: {
|
parameters: {
|
||||||
chromatic: { disableSnapshot: true },
|
chromatic: { disableSnapshot: true },
|
||||||
},
|
},
|
||||||
} satisfies Meta;
|
} satisfies Meta<typeof AFFiNEDatePicker>;
|
||||||
|
|
||||||
export const Default: StoryFn = () => {
|
export const Default: StoryFn = () => {
|
||||||
const [value, setValue] = useState<string>(new Date().toString());
|
const [value, setValue] = useState<string>(new Date().toString());
|
||||||
|
10
yarn.lock
10
yarn.lock
@ -219,6 +219,7 @@ __metadata:
|
|||||||
"@emotion/react": "npm:^11.11.1"
|
"@emotion/react": "npm:^11.11.1"
|
||||||
"@emotion/server": "npm:^11.11.0"
|
"@emotion/server": "npm:^11.11.0"
|
||||||
"@emotion/styled": "npm:^11.11.0"
|
"@emotion/styled": "npm:^11.11.0"
|
||||||
|
"@lit/react": "npm:^1.0.2"
|
||||||
"@popperjs/core": "npm:^2.11.8"
|
"@popperjs/core": "npm:^2.11.8"
|
||||||
"@radix-ui/react-avatar": "npm:^1.0.4"
|
"@radix-ui/react-avatar": "npm:^1.0.4"
|
||||||
"@radix-ui/react-collapsible": "npm:^1.0.3"
|
"@radix-ui/react-collapsible": "npm:^1.0.3"
|
||||||
@ -6488,6 +6489,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@lit/react@npm:^1.0.2":
|
||||||
|
version: 1.0.2
|
||||||
|
resolution: "@lit/react@npm:1.0.2"
|
||||||
|
peerDependencies:
|
||||||
|
"@types/react": 17 || 18
|
||||||
|
checksum: 78bc607f9022ceefa5c714a701b71e444df8d6c88c75b0fe728b7157d3ec00a7fa459bc3c4ce5a4b5315079159c9759be4738e5ade656f8d74e32f729f75efc4
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@lit/reactive-element@npm:^2.0.0":
|
"@lit/reactive-element@npm:^2.0.0":
|
||||||
version: 2.0.2
|
version: 2.0.2
|
||||||
resolution: "@lit/reactive-element@npm:2.0.2"
|
resolution: "@lit/reactive-element@npm:2.0.2"
|
||||||
|
Loading…
Reference in New Issue
Block a user