DEV;Add TimePicker component
This commit is contained in:
parent
97c5141773
commit
cab561c77a
20 changed files with 372 additions and 91 deletions
|
@ -31,6 +31,10 @@ import {
|
|||
faFolderOpen,
|
||||
faUserCircle,
|
||||
faUserTie,
|
||||
faClock,
|
||||
faBackspace,
|
||||
faMinus,
|
||||
faPlus,
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
library.add(
|
||||
|
@ -64,5 +68,10 @@ library.add(
|
|||
faArrowAltCircleRight,
|
||||
faUserCircle,
|
||||
faFolderOpen,
|
||||
faFile
|
||||
faFile,
|
||||
faClock,
|
||||
faTimes,
|
||||
faBackspace,
|
||||
faMinus,
|
||||
faPlus
|
||||
);
|
||||
|
|
|
@ -1,13 +1,8 @@
|
|||
import React, { useCallback, useMemo } from 'react';
|
||||
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { format as setFormat } from 'date-fns';
|
||||
|
||||
import { STATUS } from '@treejs/types/common';
|
||||
|
||||
import Button from '../../Button';
|
||||
import Calendar from '../../Calendar/components/Calendar';
|
||||
import { Grid, GridCol } from '../../Grid';
|
||||
import { ModalId } from '../../Modal/types';
|
||||
|
||||
type IProps = {
|
||||
|
@ -15,12 +10,11 @@ type IProps = {
|
|||
format?: ModalId;
|
||||
name: ModalId;
|
||||
onChange: (value: string) => void;
|
||||
onRemove: () => void;
|
||||
value: Date;
|
||||
};
|
||||
|
||||
const DatePickerModal: React.FC<IProps> = (props) => {
|
||||
const { onChange, onRemove, closeModal, name, value, format = 'dd.MM.yyyy' } = props;
|
||||
const { onChange, closeModal, name, value, format = 'dd.MM.yyyy' } = props;
|
||||
|
||||
const setActiveOption = useCallback(
|
||||
(newValue: Date) => {
|
||||
|
@ -32,31 +26,11 @@ const DatePickerModal: React.FC<IProps> = (props) => {
|
|||
[onChange, closeModal, name, format]
|
||||
);
|
||||
|
||||
const removeActiveOption = useCallback((): void => {
|
||||
if (onRemove) {
|
||||
onRemove();
|
||||
}
|
||||
closeModal(name);
|
||||
}, [onRemove, closeModal, name]);
|
||||
|
||||
const date = useMemo(() => {
|
||||
return new Date(value);
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Calendar value={date} onChange={setActiveOption} hover />
|
||||
<Grid cols={4}>
|
||||
<GridCol start={4} end={4}>
|
||||
<Button
|
||||
label={<FontAwesomeIcon icon="trash" />}
|
||||
status={STATUS.warning}
|
||||
onClick={removeActiveOption}
|
||||
/>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
return <Calendar value={date} onChange={setActiveOption} hover />;
|
||||
};
|
||||
|
||||
export default DatePickerModal;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { ModalId } from '@treejs/components/Modal/types';
|
||||
|
@ -44,33 +43,19 @@ const DatePicker: React.FC<IDatePickerFieldProps> = (props) => {
|
|||
}
|
||||
}, []);
|
||||
|
||||
const handleDateRemove = useCallback((): void => {
|
||||
setValue(null);
|
||||
if (onChange) {
|
||||
onChange(null, null);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleOpenModal = useCallback((): void => {
|
||||
addModalComponent(
|
||||
name,
|
||||
<DatePickerModal
|
||||
name={name}
|
||||
value={value}
|
||||
closeModal={handleCloseModal}
|
||||
onChange={handleDateClick}
|
||||
onRemove={handleDateRemove}
|
||||
/>
|
||||
<DatePickerModal name={name} value={value} closeModal={handleCloseModal} onChange={handleDateClick} />
|
||||
);
|
||||
dispatch(openModal({ id: name, title, width: 300 }));
|
||||
}, [name, value, title]);
|
||||
|
||||
return (
|
||||
<div id={name}>
|
||||
<div>
|
||||
<TextField
|
||||
name={name}
|
||||
title={title}
|
||||
rightIcon={<FontAwesomeIcon icon="calendar-alt" />}
|
||||
onClick={handleOpenModal}
|
||||
onFocus={handleOpenModal}
|
||||
onBlur={handleBlur}
|
||||
|
|
|
@ -5,24 +5,26 @@ import cx from 'classnames';
|
|||
|
||||
import { isNilOrEmpty } from '@treejs/utils';
|
||||
|
||||
import { GRID_SIZE_ENUM } from './types';
|
||||
import { GRID_SIZE, VERTICAL_ALIGN } from './types';
|
||||
|
||||
type IGridProps = {
|
||||
children?: React.ReactElement | React.ReactElement[];
|
||||
cols?: Partial<Record<GRID_SIZE_ENUM, number>> | number;
|
||||
className?: string;
|
||||
cols?: Partial<Record<GRID_SIZE, number>> | number;
|
||||
space?: number;
|
||||
vertical?: VERTICAL_ALIGN;
|
||||
};
|
||||
|
||||
const isBiggerDefined = (cols: Partial<Record<GRID_SIZE_ENUM, number>> | number): boolean => {
|
||||
const isBiggerDefined = (cols: Partial<Record<GRID_SIZE, number>> | number): boolean => {
|
||||
if (!isNilOrEmpty(cols) && !is(Number, cols)) {
|
||||
const sizes = keys(cols);
|
||||
let bigger = [];
|
||||
if (includes(GRID_SIZE_ENUM.SM, sizes)) {
|
||||
bigger = [GRID_SIZE_ENUM.MD, GRID_SIZE_ENUM.LG, GRID_SIZE_ENUM.XL];
|
||||
} else if (includes(GRID_SIZE_ENUM.MD, sizes)) {
|
||||
bigger = [GRID_SIZE_ENUM.LG, GRID_SIZE_ENUM.XL];
|
||||
} else if (includes(GRID_SIZE_ENUM.LG, sizes)) {
|
||||
bigger = [GRID_SIZE_ENUM.XL];
|
||||
if (includes(GRID_SIZE.SM, sizes)) {
|
||||
bigger = [GRID_SIZE.MD, GRID_SIZE.LG, GRID_SIZE.XL];
|
||||
} else if (includes(GRID_SIZE.MD, sizes)) {
|
||||
bigger = [GRID_SIZE.LG, GRID_SIZE.XL];
|
||||
} else if (includes(GRID_SIZE.LG, sizes)) {
|
||||
bigger = [GRID_SIZE.XL];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
@ -31,12 +33,23 @@ const isBiggerDefined = (cols: Partial<Record<GRID_SIZE_ENUM, number>> | number)
|
|||
return true;
|
||||
};
|
||||
|
||||
export const Grid = (props: IGridProps): React.ReactElement => {
|
||||
const { cols = 1, space = 4, children } = props;
|
||||
export const Grid: React.FC<IGridProps> = ({
|
||||
cols = 1,
|
||||
space = 4,
|
||||
className,
|
||||
children,
|
||||
vertical,
|
||||
}): React.ReactElement => {
|
||||
return (
|
||||
<div
|
||||
className={cx(
|
||||
'grid',
|
||||
className,
|
||||
{
|
||||
'grid-items-start': vertical === VERTICAL_ALIGN.TOP,
|
||||
'grid-items-center': vertical === VERTICAL_ALIGN.CENTER,
|
||||
'grid-items-end': vertical === VERTICAL_ALIGN.BOTTOM,
|
||||
},
|
||||
ifElse(
|
||||
is(Number),
|
||||
() => `grid-cols-${cols}`,
|
||||
|
@ -64,7 +77,7 @@ Grid.displayName = 'Grid';
|
|||
|
||||
type IGridColProps = {
|
||||
children?: React.ReactElement | React.ReactElement[] | string;
|
||||
colSpan?: Partial<Record<GRID_SIZE_ENUM, number>> | number;
|
||||
colSpan?: Partial<Record<GRID_SIZE, number>> | number;
|
||||
end?: number;
|
||||
start?: number;
|
||||
};
|
||||
|
@ -87,7 +100,7 @@ export const GridCol = (props: IGridColProps): React.ReactElement => {
|
|||
)
|
||||
)(colSpan),
|
||||
{
|
||||
'sm-m:col-span-1': !has(GRID_SIZE_ENUM.SM)(colSpan) && !isNil(colSpan),
|
||||
'sm-m:col-span-1': !has(GRID_SIZE.SM)(colSpan) && !isNil(colSpan),
|
||||
}
|
||||
)}
|
||||
>
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
export enum GRID_SIZE_ENUM {
|
||||
export enum GRID_SIZE {
|
||||
'2XL' = '2xp',
|
||||
LG = 'lg',
|
||||
MD = 'md',
|
||||
SM = 'sm',
|
||||
XL = 'xl',
|
||||
}
|
||||
|
||||
export enum VERTICAL_ALIGN {
|
||||
BOTTOM = 'bottom',
|
||||
CENTER = 'center',
|
||||
TOP = 'top',
|
||||
}
|
||||
|
|
|
@ -2,18 +2,18 @@ import { any, includes, is, keys } from 'ramda';
|
|||
|
||||
import { isNilOrEmpty } from '@treejs/utils';
|
||||
|
||||
import { GRID_SIZE_ENUM } from './types';
|
||||
import { GRID_SIZE } from './types';
|
||||
|
||||
export const isBiggerDefined = (cols: Partial<Record<GRID_SIZE_ENUM, number>> | number): boolean => {
|
||||
export const isBiggerDefined = (cols: Partial<Record<GRID_SIZE, number>> | number): boolean => {
|
||||
if (!isNilOrEmpty(cols) && !is(Number, cols)) {
|
||||
const sizes = keys(cols);
|
||||
let bigger = [];
|
||||
if (includes(GRID_SIZE_ENUM.SM, sizes)) {
|
||||
bigger = [GRID_SIZE_ENUM.MD, GRID_SIZE_ENUM.LG, GRID_SIZE_ENUM.XL];
|
||||
} else if (includes(GRID_SIZE_ENUM.MD, sizes)) {
|
||||
bigger = [GRID_SIZE_ENUM.LG, GRID_SIZE_ENUM.XL];
|
||||
} else if (includes(GRID_SIZE_ENUM.LG, sizes)) {
|
||||
bigger = [GRID_SIZE_ENUM.XL];
|
||||
if (includes(GRID_SIZE.SM, sizes)) {
|
||||
bigger = [GRID_SIZE.MD, GRID_SIZE.LG, GRID_SIZE.XL];
|
||||
} else if (includes(GRID_SIZE.MD, sizes)) {
|
||||
bigger = [GRID_SIZE.LG, GRID_SIZE.XL];
|
||||
} else if (includes(GRID_SIZE.LG, sizes)) {
|
||||
bigger = [GRID_SIZE.XL];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { find, propEq, propOr } from 'ramda';
|
||||
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { ModalId } from '@treejs/components/Modal/types';
|
||||
|
@ -87,7 +86,6 @@ const SelectBox: React.FC<ISelectBoxFieldProps> = (props) => {
|
|||
disabled={disabled}
|
||||
name={name}
|
||||
title={title}
|
||||
rightIcon={<FontAwesomeIcon icon="caret-square-down" />}
|
||||
onClick={handleOpenModal}
|
||||
onFocus={handleOpenModal}
|
||||
onBlur={handleBlur}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import cx from 'classnames';
|
||||
import { toPattern } from 'vanilla-masker';
|
||||
|
||||
|
@ -9,11 +10,11 @@ import { isNilOrEmpty } from '@treejs/utils';
|
|||
|
||||
export type ITextFieldProps<V = string> = {
|
||||
autoFocus?: boolean;
|
||||
clearable?: boolean;
|
||||
defaultValue?: string;
|
||||
disabled?: boolean;
|
||||
mask?: string;
|
||||
readonly?: boolean;
|
||||
rightIcon?: React.ReactElement;
|
||||
status?: STATUS;
|
||||
type?: 'text' | 'password';
|
||||
value?: string;
|
||||
|
@ -30,11 +31,11 @@ const TextField: React.FC<ITextFieldProps> = (props) => {
|
|||
name,
|
||||
title,
|
||||
readonly,
|
||||
rightIcon,
|
||||
status,
|
||||
autoFocus,
|
||||
defaultValue,
|
||||
mask,
|
||||
clearable = true,
|
||||
} = props;
|
||||
|
||||
const inputRef = React.createRef<HTMLInputElement>();
|
||||
|
@ -61,10 +62,6 @@ const TextField: React.FC<ITextFieldProps> = (props) => {
|
|||
setValue(applyMask(props.value));
|
||||
}, [props.value]);
|
||||
|
||||
const setFocus = (): void => {
|
||||
inputRef.current.focus();
|
||||
};
|
||||
|
||||
const handleClick = (e: React.MouseEvent<HTMLInputElement>): void => {
|
||||
if (onClick) {
|
||||
onClick(e.currentTarget.value, e);
|
||||
|
@ -83,6 +80,13 @@ const TextField: React.FC<ITextFieldProps> = (props) => {
|
|||
}
|
||||
};
|
||||
|
||||
const handleClearValue = (e: React.MouseEvent<HTMLInputElement>): void => {
|
||||
setValue(null);
|
||||
if (onChange) {
|
||||
onChange(null, e);
|
||||
}
|
||||
};
|
||||
|
||||
const handleChange = useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
const value = e.currentTarget.value;
|
||||
|
@ -122,9 +126,11 @@ const TextField: React.FC<ITextFieldProps> = (props) => {
|
|||
onChange={handleChange}
|
||||
autoFocus={autoFocus}
|
||||
/>
|
||||
<div className="field-icon" onClick={setFocus}>
|
||||
{rightIcon}
|
||||
{!disabled && clearable && (
|
||||
<div className="field-icon field-icon--clear" onClick={handleClearValue}>
|
||||
<FontAwesomeIcon icon="backspace" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { format, setHours, setMinutes } from 'date-fns';
|
||||
|
||||
import { VERTICAL_ALIGN } from '@treejs/components/Grid/types';
|
||||
import TextField from '@treejs/components/TextField';
|
||||
import { isNilOrEmpty } from '@treejs/utils';
|
||||
|
||||
import Button from '../../Button';
|
||||
import { Grid, GridCol } from '../../Grid';
|
||||
|
||||
type IProps = {
|
||||
onChange: (value: Date) => void;
|
||||
value: Date;
|
||||
};
|
||||
|
||||
const ButtonField: React.FC<{ name: string; onChange: (a: string) => void; title: string; value: string }> = ({
|
||||
title,
|
||||
name,
|
||||
onChange,
|
||||
value,
|
||||
}) => {
|
||||
const handleChange = useCallback(
|
||||
(newValue): any => {
|
||||
onChange(newValue);
|
||||
},
|
||||
[onChange]
|
||||
);
|
||||
|
||||
const increase = useCallback((): any => {
|
||||
const newValue = (Number(value) + 1).toString();
|
||||
handleChange(newValue);
|
||||
}, [value, handleChange]);
|
||||
|
||||
const decrease = useCallback((): any => {
|
||||
const newValue = (Number(value) - 1).toString();
|
||||
handleChange(newValue);
|
||||
}, [value, handleChange]);
|
||||
|
||||
return (
|
||||
<Grid cols={4} vertical={VERTICAL_ALIGN.BOTTOM}>
|
||||
<GridCol colSpan={1}>
|
||||
<Button label={<FontAwesomeIcon icon="minus" />} onClick={decrease} />
|
||||
</GridCol>
|
||||
<GridCol colSpan={2}>
|
||||
<TextField name={name} title={title} clearable={false} value={value} onChange={handleChange} />
|
||||
</GridCol>
|
||||
<GridCol colSpan={1}>
|
||||
<Button label={<FontAwesomeIcon icon="plus" />} onClick={increase} />
|
||||
</GridCol>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
const TimePickerModal: React.FC<IProps> = (props) => {
|
||||
const { onChange } = props;
|
||||
|
||||
const [value, setValue] = useState(props.value);
|
||||
|
||||
useEffect(() => {
|
||||
setValue(props.value);
|
||||
}, [props.value]);
|
||||
|
||||
const dateTime = useMemo(() => {
|
||||
if (!isNilOrEmpty(value)) {
|
||||
return new Date(value);
|
||||
}
|
||||
return new Date();
|
||||
}, [value]);
|
||||
|
||||
const handleChangeHours = useCallback(
|
||||
(hour) => {
|
||||
if (isNilOrEmpty(hour)) {
|
||||
hour = 0;
|
||||
} else if (Number(hour) > 24 || Number(hour) < 0) {
|
||||
hour = 23;
|
||||
}
|
||||
const newHour = setHours(value, hour);
|
||||
setValue(newHour);
|
||||
onChange(newHour);
|
||||
},
|
||||
[value, onChange]
|
||||
);
|
||||
|
||||
const handleChangeMinutes = useCallback(
|
||||
(minute) => {
|
||||
if (isNilOrEmpty(minute)) {
|
||||
minute = 0;
|
||||
} else if (Number(minute) > 60 || Number(minute) < 0) {
|
||||
minute = 59;
|
||||
}
|
||||
const newMinute = setMinutes(value, minute);
|
||||
setValue(newMinute);
|
||||
onChange(newMinute);
|
||||
},
|
||||
[value, onChange]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Grid cols={1}>
|
||||
<GridCol>
|
||||
<ButtonField name="hour" title="Hour" value={format(dateTime, 'H')} onChange={handleChangeHours} />
|
||||
</GridCol>
|
||||
<GridCol>
|
||||
<ButtonField
|
||||
name="minute"
|
||||
title="Minute"
|
||||
value={format(dateTime, 'm')}
|
||||
onChange={handleChangeMinutes}
|
||||
/>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default TimePickerModal;
|
63
packages/components/src/TimePicker/index.tsx
Normal file
63
packages/components/src/TimePicker/index.tsx
Normal file
|
@ -0,0 +1,63 @@
|
|||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import format from 'date-fns/format';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { STATUS } from '@treejs/types/common';
|
||||
import { IHTMLElement } from '@treejs/types/form';
|
||||
import { isNilOrEmpty } from '@treejs/utils';
|
||||
|
||||
import { openModal } from '../Modal/actions';
|
||||
import { addModalComponent } from '../Modal/utils';
|
||||
import TextField from '../TextField';
|
||||
import TimePickerModal from './components/TimePickerModal';
|
||||
|
||||
export type ITimePickerFieldProps<V = string> = {
|
||||
status?: STATUS;
|
||||
value?: string;
|
||||
} & IHTMLElement<V>;
|
||||
|
||||
const TimePicker: React.FC<ITimePickerFieldProps> = (props) => {
|
||||
const { onBlur, onChange, title, status, name } = props;
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const [value, setValue] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
setValue(props.value);
|
||||
}, [props.value]);
|
||||
|
||||
const handleBlur = useCallback((newValue, e): void => {
|
||||
if (onBlur) {
|
||||
onBlur(newValue, e);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleTimeClick = useCallback((newValue): void => {
|
||||
setValue(newValue);
|
||||
if (onChange) {
|
||||
onChange(newValue, null);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleOpenModal = useCallback((): void => {
|
||||
addModalComponent(name, <TimePickerModal value={value} onChange={handleTimeClick} />);
|
||||
dispatch(openModal({ id: name, title, width: 300 }));
|
||||
}, [name, value, title]);
|
||||
|
||||
return (
|
||||
<TextField
|
||||
name={name}
|
||||
title={title}
|
||||
onClick={handleOpenModal}
|
||||
onFocus={handleOpenModal}
|
||||
onBlur={handleBlur}
|
||||
value={!isNilOrEmpty(value) ? format(value, 'H:mm') : null}
|
||||
status={status}
|
||||
readonly
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default TimePicker;
|
|
@ -8,7 +8,7 @@ import { isNilOrEmpty } from '@treejs/utils';
|
|||
|
||||
type IProps = IDatePickerProps<string>;
|
||||
|
||||
const DatePicker = (props: IProps): React.ReactElement => {
|
||||
const DatePickerField = (props: IProps): React.ReactElement => {
|
||||
const { name, title, value } = props;
|
||||
|
||||
const { control, errors } = useFormContext();
|
||||
|
@ -38,4 +38,4 @@ const DatePicker = (props: IProps): React.ReactElement => {
|
|||
);
|
||||
};
|
||||
|
||||
export default DatePicker;
|
||||
export default DatePickerField;
|
||||
|
|
|
@ -27,7 +27,7 @@ const button = (theme) => ({
|
|||
},
|
||||
'&--error': {
|
||||
color: theme('colors.black'),
|
||||
backgroundColor: theme('colors.error'),
|
||||
backgroundColor: theme('colors.error.DEFAULT'),
|
||||
},
|
||||
|
||||
'&:active': {
|
||||
|
|
|
@ -16,7 +16,7 @@ const field = (theme) => ({
|
|||
fontWeight: 300,
|
||||
borderRadius: '2px',
|
||||
'&--error': {
|
||||
borderLeft: `2px solid ${theme('colors.error')}`,
|
||||
borderLeft: `2px solid ${theme('colors.error.DEFAULT')}`,
|
||||
},
|
||||
'&--info': {
|
||||
borderLeft: `2px solid ${theme('colors.info')}`,
|
||||
|
@ -37,17 +37,22 @@ const field = (theme) => ({
|
|||
border: 'none',
|
||||
},
|
||||
},
|
||||
'&read-only': {
|
||||
'&:read-only': {
|
||||
cursor: 'auto',
|
||||
},
|
||||
},
|
||||
'.field-icon': {
|
||||
position: 'absolute',
|
||||
top: '1.75rem',
|
||||
right: '1rem',
|
||||
top: '1.27rem',
|
||||
right: '0',
|
||||
padding: '0.51rem',
|
||||
color: theme('colors.primary.DEFAULT'),
|
||||
fontSize: '1.125rem',
|
||||
lineHeight: '1.75rem',
|
||||
lineHeight: '1.25rem',
|
||||
'&--clear': {
|
||||
borderRadius: '0 2px 2px 0',
|
||||
color: theme('colors.error.dark'),
|
||||
},
|
||||
'input:disabled &': {
|
||||
color: theme('colors.gray.500'),
|
||||
'& svg': {
|
||||
|
@ -62,7 +67,7 @@ const field = (theme) => ({
|
|||
color: theme('colors.gray.500'),
|
||||
},
|
||||
'.field-validationMessage': {
|
||||
color: theme('colors.error'),
|
||||
color: theme('colors.error.DEFAULT'),
|
||||
textAlign: 'left',
|
||||
marginBottom: '0.5rem',
|
||||
fontSize: '0.7rem',
|
||||
|
|
19
packages/styles/src/components/grid.js
Normal file
19
packages/styles/src/components/grid.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
const button = () => ({
|
||||
'.grid': {
|
||||
'&-items-start': {
|
||||
alignItems: 'first baseline',
|
||||
},
|
||||
'&-items-center': {
|
||||
alignItems: 'center',
|
||||
},
|
||||
'&-items-end': {
|
||||
alignItems: 'last baseline',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = ({ addComponents, theme, config }) => {
|
||||
addComponents(button(theme, config));
|
||||
};
|
||||
|
||||
module.exports.button = button;
|
|
@ -29,7 +29,7 @@ const message = (theme) => ({
|
|||
},
|
||||
'&--error': {
|
||||
color: theme('colors.black'),
|
||||
backgroundColor: theme('colors.error'),
|
||||
backgroundColor: theme('colors.error.DEFAULT'),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
--color-success: theme('colors.green.300');
|
||||
--color-warning: theme('colors.yellow.300');
|
||||
--color-error: theme('colors.red.300');
|
||||
--color-error--dark: theme('colors.red.400');
|
||||
|
||||
.purple {
|
||||
--color-primary: theme('colors.purple.400');
|
||||
|
|
|
@ -23,6 +23,7 @@ module.exports = {
|
|||
plugin(require('./components/message')),
|
||||
plugin(require('./components/skeleton')),
|
||||
plugin(require('./components/table')),
|
||||
plugin(require('./components/grid')),
|
||||
],
|
||||
corePlugins: {
|
||||
float: false,
|
||||
|
|
|
@ -27,7 +27,10 @@ module.exports = {
|
|||
info: 'var(--color-info)',
|
||||
success: 'var(--color-success)',
|
||||
warning: 'var(--color-warning)',
|
||||
error: 'var(--color-error)',
|
||||
error: {
|
||||
DEFAULT: 'var(--color-error)',
|
||||
dark: 'var(--color-error--dark)',
|
||||
},
|
||||
},
|
||||
screens: {
|
||||
'lg-m': { min: '768px', max: '1024px' },
|
||||
|
|
|
@ -52,7 +52,7 @@ export const Template = (args) => (
|
|||
|
||||
<Preview>
|
||||
<Story
|
||||
name="Without NTH"
|
||||
name="Without -nth"
|
||||
args={{
|
||||
nthStyle: false,
|
||||
}}
|
||||
|
|
79
stories/components/TimePicker.stories.mdx
Normal file
79
stories/components/TimePicker.stories.mdx
Normal file
|
@ -0,0 +1,79 @@
|
|||
import { action } from '@storybook/addon-actions';
|
||||
import { ArgsTable, Meta, Preview, Story } from '@storybook/addon-docs';
|
||||
|
||||
import Modals from '@treejs/components/Modal/components/Modals';
|
||||
import TimePicker from '@treejs/components/TimePicker';
|
||||
|
||||
import StoreProvider from './Modal/redux';
|
||||
|
||||
<Meta
|
||||
title={'Components/TimePicker'}
|
||||
component={TimePicker}
|
||||
argTypes={{
|
||||
modalProps: {
|
||||
control: {
|
||||
disable: true,
|
||||
},
|
||||
table: {
|
||||
disable: true,
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
# TimePicker
|
||||
|
||||
`import TimePicker from '@treejs/components/TimePicker';`
|
||||
|
||||
### Requirements
|
||||
|
||||
1. Wrapped with redux store provider.
|
||||
|
||||
```tsx dark
|
||||
import { ROOT_REDUCER_NAME } from '@treejs/constants/redux';
|
||||
|
||||
import { MODAL_REDUCER_NAME } from '@treejs/components/Modal/constants';
|
||||
import modalReducer from '@treejs/components/Modal/reducer';
|
||||
|
||||
export const mainReducer = combineReducers({
|
||||
[ROOT_REDUCER_NAME]: combineReducers({
|
||||
[MODAL_REDUCER_NAME]: modalReducer,
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
2. Somewhere in redux store provider must be used `Modals` component
|
||||
from `@treejs/components/Modal/components/Modals`
|
||||
|
||||
<ArgsTable />
|
||||
|
||||
export const Template = (args) => {
|
||||
const { modalProps, ...select } = args;
|
||||
return (
|
||||
<StoreProvider>
|
||||
<TimePicker onChange={action('clicked')} {...select} />
|
||||
<Modals {...modalProps} />
|
||||
</StoreProvider>
|
||||
);
|
||||
};
|
||||
|
||||
## Default
|
||||
|
||||
<Preview>
|
||||
<Story
|
||||
name="Default"
|
||||
height="400px"
|
||||
args={{
|
||||
name: 'datePicker',
|
||||
title: 'Choose time',
|
||||
value: new Date(new Date().setHours(20, 0)),
|
||||
}}
|
||||
argTypes={{
|
||||
value: {
|
||||
control: { type: 'Date' },
|
||||
},
|
||||
}}
|
||||
>
|
||||
{Template.bind({})}
|
||||
</Story>
|
||||
</Preview>
|
Loading…
Add table
Reference in a new issue