Remove Redux from Toaster component (treejs#6) + field visual update
Change-Id: I3aaab206f6d78a628d2e4b1e9ea0422fa39af589
This commit is contained in:
parent
5f2ad09b39
commit
23daaab869
26 changed files with 321 additions and 359 deletions
|
@ -1,9 +1,10 @@
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { STATUS } from '@treejs/types/common';
|
import { STATUS } from '@treejs/types/common';
|
||||||
import { IHTMLElement } from '@treejs/types/form';
|
import { IHTMLElement } from '@treejs/types/form';
|
||||||
|
|
||||||
import Button from '../Button';
|
|
||||||
import CheckSquareIcon from '../Icons/CheckSquare';
|
import CheckSquareIcon from '../Icons/CheckSquare';
|
||||||
import SquareIcon from '../Icons/Square';
|
import SquareIcon from '../Icons/Square';
|
||||||
|
|
||||||
|
@ -24,7 +25,7 @@ const Checkbox: React.FC<ICheckboxFieldProps> = (props) => {
|
||||||
}, [props.checked]);
|
}, [props.checked]);
|
||||||
|
|
||||||
const handleClick = useCallback(
|
const handleClick = useCallback(
|
||||||
(e: React.MouseEvent<HTMLLabelElement>): void => {
|
(e: React.MouseEvent<HTMLElement>): void => {
|
||||||
if (props.onChange) {
|
if (props.onChange) {
|
||||||
props.onChange(!checked, e);
|
props.onChange(!checked, e);
|
||||||
}
|
}
|
||||||
|
@ -42,23 +43,24 @@ const Checkbox: React.FC<ICheckboxFieldProps> = (props) => {
|
||||||
[checked]
|
[checked]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const boxClassName = clsx('text-xl field-checkbox', {
|
||||||
|
'field-checkbox--info': status === STATUS.info,
|
||||||
|
'field-checkbox--warning': status === STATUS.warning,
|
||||||
|
'field-checkbox--error': status === STATUS.error,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="checkbox-container">
|
<div className="field-container checkbox-container">
|
||||||
<label className="cursor-pointer" htmlFor={name} onClick={handleClick}>
|
<input id={name} name={name} className="hidden field-input" type="checkbox" onBlur={handleBlur} />
|
||||||
<div className="block text-black pb-2 field-label">{title}</div>
|
<label className="field-label" htmlFor={name} onClick={handleClick}>
|
||||||
<Button
|
{label}
|
||||||
label={
|
|
||||||
<div className="flex justify-between items-center">
|
|
||||||
{checked ? <CheckSquareIcon fontSize={20} /> : <SquareIcon fontSize={20} />}
|
|
||||||
<b className="ml-2">{label}</b>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
className="m-0 normal-case w-auto font-thin inline"
|
|
||||||
status={status}
|
|
||||||
pressed={checked}
|
|
||||||
/>
|
|
||||||
</label>
|
</label>
|
||||||
<input id={name} name={name} className="hidden" type="checkbox" onBlur={handleBlur} />
|
<div className="flex justify-between items-center ml-2 mr-1" onClick={handleClick}>
|
||||||
|
{checked ? <CheckSquareIcon className={boxClassName} /> : <SquareIcon className={boxClassName} />}
|
||||||
|
</div>
|
||||||
|
<div className="cursor-pointer" onClick={handleClick}>
|
||||||
|
{title}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,17 +12,15 @@ import InfoCircleIcon from '../Icons/InfoCircle';
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
children?: React.ReactElement | string;
|
children?: React.ReactElement | string;
|
||||||
filled?: boolean;
|
|
||||||
status: STATUS;
|
status: STATUS;
|
||||||
title?: string;
|
title?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
function Message(props: IProps) {
|
function Message(props: IProps) {
|
||||||
const { status = STATUS.none, title, children, filled = false } = props;
|
const { status = STATUS.none, title, children } = props;
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={clsx('message', {
|
className={clsx('message', {
|
||||||
'message--filled': filled,
|
|
||||||
'message--none': status === STATUS.none,
|
'message--none': status === STATUS.none,
|
||||||
'message--info': status === STATUS.info,
|
'message--info': status === STATUS.info,
|
||||||
'message--success': status === STATUS.success,
|
'message--success': status === STATUS.success,
|
||||||
|
@ -30,14 +28,14 @@ function Message(props: IProps) {
|
||||||
'message--error': status === STATUS.error,
|
'message--error': status === STATUS.error,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div className="inline-block align-middle mr-4 text-xl">
|
<div className="inline-block align-middle mr-4 text-3xl">
|
||||||
{status === STATUS.info && <InfoCircleIcon />}
|
{status === STATUS.info && <InfoCircleIcon />}
|
||||||
{status === STATUS.success && <CheckCircleIcon />}
|
{status === STATUS.success && <CheckCircleIcon />}
|
||||||
{status === STATUS.warning && <ExclamationTriangleFillIcon />}
|
{status === STATUS.warning && <ExclamationTriangleFillIcon />}
|
||||||
{status === STATUS.error && <ExclamationCircleIcon />}
|
{status === STATUS.error && <ExclamationCircleIcon />}
|
||||||
{status === STATUS.none && <HappyMessageIcon />}
|
{status === STATUS.none && <HappyMessageIcon />}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xl font-normal max-w-full flex-initial">
|
<div className="text-lg font-normal max-w-full flex-initial">
|
||||||
{title}
|
{title}
|
||||||
<div className="text-sm font-base">{children}</div>
|
<div className="text-sm font-base">{children}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import React, { createContext, FC, ReactNode, useMemo, useState } from 'react';
|
import React, { createContext, FC, ReactNode, useMemo, useState } from 'react';
|
||||||
import { omit } from 'ramda';
|
import { omit } from 'ramda';
|
||||||
|
|
||||||
|
import { ToasterId } from '../Toaster/types';
|
||||||
import { Modal, ModalId } from './types';
|
import { Modal, ModalId } from './types';
|
||||||
|
|
||||||
type ModalContextType = {
|
type ModalContextType = {
|
||||||
|
@ -8,7 +9,7 @@ type ModalContextType = {
|
||||||
open: (modalId: ModalId) => void;
|
open: (modalId: ModalId) => void;
|
||||||
opened: ModalId[];
|
opened: ModalId[];
|
||||||
register: (modal: Modal) => void;
|
register: (modal: Modal) => void;
|
||||||
registered: Record<string, Modal>;
|
registered: Record<ToasterId, Modal>;
|
||||||
unregister: (modalId: ModalId) => void;
|
unregister: (modalId: ModalId) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -22,7 +23,7 @@ export const ModalContext = createContext<ModalContextType>({
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ModalWrapper: FC<{ children: ReactNode }> = ({ children }) => {
|
export const ModalWrapper: FC<{ children: ReactNode }> = ({ children }) => {
|
||||||
const [modals, setModals] = useState<Record<string, Modal>>({});
|
const [modals, setModals] = useState<Record<ToasterId, Modal>>({});
|
||||||
const [opened, setOpened] = useState<ModalId[]>([]);
|
const [opened, setOpened] = useState<ModalId[]>([]);
|
||||||
|
|
||||||
const register = (modal: Modal) => {
|
const register = (modal: Modal) => {
|
||||||
|
|
|
@ -2,7 +2,6 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import Button from '@treejs/components/Button';
|
|
||||||
import CircleIcon from '@treejs/components/Icons/Circle';
|
import CircleIcon from '@treejs/components/Icons/Circle';
|
||||||
import CircleFillIcon from '@treejs/components/Icons/CircleFill';
|
import CircleFillIcon from '@treejs/components/Icons/CircleFill';
|
||||||
import { STATUS } from '@treejs/types/common';
|
import { STATUS } from '@treejs/types/common';
|
||||||
|
@ -43,15 +42,21 @@ const RadioButton: React.FC<IRadioButtonFieldProps> = (props) => {
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
const optionElements = useMemo(() => {
|
const boxClassName = clsx('text-xl field-radio', {
|
||||||
|
'field-radio--info': status === STATUS.info,
|
||||||
|
'field-radio--warning': status === STATUS.warning,
|
||||||
|
'field-radio--error': status === STATUS.error,
|
||||||
|
});
|
||||||
|
|
||||||
|
const elements = useMemo(() => {
|
||||||
return options.map(({ code, name }): React.ReactElement => {
|
return options.map(({ code, name }): React.ReactElement => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
key={code}
|
||||||
className={clsx({
|
className={clsx({
|
||||||
'mb-2 block': !horizontal,
|
'mb-2 block': !horizontal,
|
||||||
'inline-block mr-1': horizontal,
|
'inline-block mr-5': horizontal,
|
||||||
})}
|
})}
|
||||||
key={code}
|
|
||||||
>
|
>
|
||||||
{!isNilOrEmpty(OptionRender) ? (
|
{!isNilOrEmpty(OptionRender) ? (
|
||||||
React.cloneElement(OptionRender, {
|
React.cloneElement(OptionRender, {
|
||||||
|
@ -59,28 +64,24 @@ const RadioButton: React.FC<IRadioButtonFieldProps> = (props) => {
|
||||||
value,
|
value,
|
||||||
} as any)
|
} as any)
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<div className="flex items-center field-radio" onClick={handleClick({ code, name })}>
|
||||||
label={
|
{value === code ? (
|
||||||
<div className="flex justify-between items-center">
|
<CircleFillIcon className={boxClassName} />
|
||||||
{value === code ? <CircleFillIcon fontSize={20} /> : <CircleIcon fontSize={20} />}
|
) : (
|
||||||
<b className="ml-2">{name}</b>
|
<CircleIcon className={boxClassName} />
|
||||||
|
)}
|
||||||
|
<div className="ml-1">{name}</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
|
||||||
onClick={handleClick({ code, name })}
|
|
||||||
className="m-0 normal-case w-auto font-thin"
|
|
||||||
pressed={value === code}
|
|
||||||
status={status}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}, [value, options, props.name, status]);
|
}, [value, options, props.name, boxClassName]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="radiobutton-container">
|
<div className="field-container radiobutton-container">
|
||||||
<div className="block text-black pb-2 field-label">{title}</div>
|
<div className="field-label cursor-default mr-2">{title}</div>
|
||||||
{horizontal ? <div className="flex">{optionElements}</div> : optionElements}
|
{elements}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import React, { ReactChild, useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { isNil } from 'ramda';
|
import { isNil } from 'ramda';
|
||||||
|
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
@ -15,7 +15,7 @@ import { MenuItem } from '../types';
|
||||||
import { Item } from './Item';
|
import { Item } from './Item';
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
children: React.ReactChild;
|
children: ReactChild;
|
||||||
components?: {
|
components?: {
|
||||||
footer?: React.ReactElement;
|
footer?: React.ReactElement;
|
||||||
};
|
};
|
||||||
|
|
|
@ -93,9 +93,9 @@ const TextField: React.FC<ITextFieldProps> = (props) => {
|
||||||
|
|
||||||
const handleClearValue = useCallback(
|
const handleClearValue = useCallback(
|
||||||
(e: React.MouseEvent<HTMLInputElement>): void => {
|
(e: React.MouseEvent<HTMLInputElement>): void => {
|
||||||
setValue(null);
|
setValue('');
|
||||||
if (onChange) {
|
if (onChange) {
|
||||||
onChange(null, e);
|
onChange('', e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[onChange]
|
[onChange]
|
||||||
|
@ -114,19 +114,23 @@ const TextField: React.FC<ITextFieldProps> = (props) => {
|
||||||
[disabled, onChange]
|
[disabled, onChange]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const showClearIcon = !disabled && clearable && !isNilOrEmpty(value);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="textfield-container">
|
<div className="field-container">
|
||||||
{title !== undefined && (
|
{title && (
|
||||||
<label className="field-label" htmlFor={name}>
|
<label className="field-label" htmlFor={name}>
|
||||||
{title}
|
{title}
|
||||||
</label>
|
</label>
|
||||||
)}
|
)}
|
||||||
<input
|
<input
|
||||||
className={clsx(className, 'textfield-input masked', {
|
className={clsx(className, 'field-input masked', {
|
||||||
'textfield-input--info': status === STATUS.info,
|
'field-input--info': status === STATUS.info,
|
||||||
'textfield-input--success': status === STATUS.success,
|
'field-input--success': status === STATUS.success,
|
||||||
'textfield-input--warning': status === STATUS.warning,
|
'field-input--warning': status === STATUS.warning,
|
||||||
'textfield-input--error': status === STATUS.error,
|
'field-input--error': status === STATUS.error,
|
||||||
|
'field-input--nolabel': !title,
|
||||||
|
'field-input--clearable': showClearIcon,
|
||||||
})}
|
})}
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
|
@ -142,7 +146,7 @@ const TextField: React.FC<ITextFieldProps> = (props) => {
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
autoFocus={autoFocus}
|
autoFocus={autoFocus}
|
||||||
/>
|
/>
|
||||||
{!disabled && clearable && !isNilOrEmpty(value) && (
|
{showClearIcon && (
|
||||||
<div className="field-icon field-icon--clear" onClick={handleClearValue}>
|
<div className="field-icon field-icon--clear" onClick={handleClearValue}>
|
||||||
<BackspaceIcon />
|
<BackspaceIcon />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
import { Dispatch } from '@reduxjs/toolkit';
|
|
||||||
|
|
||||||
import { STATUS } from '@treejs/types/common';
|
|
||||||
import { isNilOrEmpty } from '@treejs/utils';
|
|
||||||
|
|
||||||
import { TOASTER_TIMEOUT } from '../constants';
|
|
||||||
import { ACTIONS, ToasterId, ToasterOption } from '../types';
|
|
||||||
|
|
||||||
const timeouts: { [key: string]: any } = [];
|
|
||||||
|
|
||||||
export const closeToaster = (name: ToasterId) => (dispatch: Dispatch) => {
|
|
||||||
timeouts[name] = null;
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.REMOVE_TOASTER,
|
|
||||||
payload: {
|
|
||||||
name,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const addToaster =
|
|
||||||
(options: ToasterOption): any =>
|
|
||||||
(dispatch: Dispatch<any>) => {
|
|
||||||
if (!isNilOrEmpty(timeouts[options.name])) {
|
|
||||||
clearTimeout(timeouts[options.name]);
|
|
||||||
}
|
|
||||||
|
|
||||||
timeouts[options.name] = setTimeout(() => {
|
|
||||||
dispatch(closeToaster(options.name));
|
|
||||||
}, options.timeout || TOASTER_TIMEOUT);
|
|
||||||
|
|
||||||
dispatch({
|
|
||||||
type: ACTIONS.ADD_TOASTER,
|
|
||||||
payload: {
|
|
||||||
status: STATUS.none,
|
|
||||||
timeout: TOASTER_TIMEOUT,
|
|
||||||
...options,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,29 +1,23 @@
|
||||||
import React, { useCallback } from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { useDispatch } from 'react-redux';
|
|
||||||
|
|
||||||
import { STATUS } from '@treejs/types/common';
|
|
||||||
|
|
||||||
import Button from '../../Button';
|
import Button from '../../Button';
|
||||||
import { addToaster } from '../actions';
|
import { useToaster } from '../hooks';
|
||||||
import { ToasterState } from '../types';
|
import { Toaster } from '../types';
|
||||||
|
|
||||||
type IProps = {
|
type IProps = {
|
||||||
message: string;
|
label: string;
|
||||||
status?: STATUS;
|
} & Toaster;
|
||||||
title: string;
|
|
||||||
} & ToasterState;
|
|
||||||
|
|
||||||
const AddToaster: React.FC<IProps> = (props) => {
|
const AddToaster: React.FC<IProps> = (props) => {
|
||||||
const { name, title, status, message } = props;
|
const { id, label, title, status, message } = props;
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
const { showToaster } = useToaster();
|
||||||
|
|
||||||
const handleClick = useCallback((): void => {
|
const handleClick = (): void => {
|
||||||
dispatch(addToaster({ name, status, message }));
|
showToaster({ id, status, message, title });
|
||||||
}, []);
|
};
|
||||||
|
|
||||||
return <Button label={title} onClick={handleClick} status={status} />;
|
return <Button label={label} onClick={handleClick} status={status} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AddToaster;
|
export default AddToaster;
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
|
|
||||||
import { isNilOrEmpty } from '@treejs/utils';
|
import { isNilOrEmpty } from '@treejs/utils';
|
||||||
|
|
||||||
import Message from '../../Message';
|
import Message from '../../Message';
|
||||||
import { getToasters } from '../selectors';
|
import { useToaster } from '../hooks';
|
||||||
|
|
||||||
function Toasters() {
|
function Toasters() {
|
||||||
const toasters = useSelector(getToasters);
|
const { toasters } = useToaster();
|
||||||
|
|
||||||
if (isNilOrEmpty(toasters)) {
|
if (isNilOrEmpty(toasters)) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -16,10 +14,10 @@ function Toasters() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed bottom-4 right-6 sm-m:left-6 w-80 sm-m:w-auto z-10">
|
<div className="fixed bottom-4 right-6 sm-m:left-6 w-80 sm-m:w-auto z-10">
|
||||||
{Object.keys(toasters).map((key) => {
|
{Object.keys(toasters).map((toasterId) => {
|
||||||
const toaster = toasters[key];
|
const toaster = toasters[toasterId];
|
||||||
return (
|
return (
|
||||||
<Message key={key} status={toaster.status} filled={toaster.filled}>
|
<Message key={toasterId} status={toaster.status} title={toaster.title}>
|
||||||
{toaster.message}
|
{toaster.message}
|
||||||
</Message>
|
</Message>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
export const TOASTER_REDUCER_NAME = 'toasters';
|
|
||||||
|
|
||||||
export const TOASTER_TIMEOUT = 4000;
|
|
41
packages/components/src/Toaster/context.tsx
Normal file
41
packages/components/src/Toaster/context.tsx
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import React, { createContext, FC, ReactNode, useMemo, useState } from 'react';
|
||||||
|
import { omit } from 'ramda';
|
||||||
|
|
||||||
|
import { Toaster, ToasterId } from './types';
|
||||||
|
|
||||||
|
type ToasterContextType = {
|
||||||
|
hide: (toasterId: ToasterId) => void;
|
||||||
|
show: (toaster: Toaster) => void;
|
||||||
|
toasters: Record<ToasterId, Toaster>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ToasterContext = createContext<ToasterContextType>({
|
||||||
|
toasters: {},
|
||||||
|
show: () => {},
|
||||||
|
hide: () => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ToasterWrapper: FC<{ children: ReactNode }> = ({ children }) => {
|
||||||
|
const [toasters, setToasters] = useState<Record<ToasterId, Toaster>>({});
|
||||||
|
|
||||||
|
const show = (toaster: Toaster) => {
|
||||||
|
setToasters({ ...toasters, [toaster.id]: toaster });
|
||||||
|
};
|
||||||
|
|
||||||
|
const hide = (toasterId: ToasterId) => {
|
||||||
|
setToasters(omit([toasterId], toasters));
|
||||||
|
};
|
||||||
|
|
||||||
|
const value = useMemo(
|
||||||
|
() => ({
|
||||||
|
toasters,
|
||||||
|
show,
|
||||||
|
hide,
|
||||||
|
}),
|
||||||
|
[toasters]
|
||||||
|
);
|
||||||
|
|
||||||
|
return <ToasterContext.Provider value={value}>{children}</ToasterContext.Provider>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ToasterWrapper;
|
35
packages/components/src/Toaster/hooks.ts
Normal file
35
packages/components/src/Toaster/hooks.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import { useContext, useEffect } from 'react';
|
||||||
|
|
||||||
|
import { ToasterContext } from './context';
|
||||||
|
import { Toaster, ToasterId } from './types';
|
||||||
|
|
||||||
|
const TOASTER_TIMEOUT = 4000;
|
||||||
|
|
||||||
|
const timeouts: { [key: ToasterId]: any } = [];
|
||||||
|
|
||||||
|
export const useToaster = () => {
|
||||||
|
const { show, hide, toasters } = useContext(ToasterContext);
|
||||||
|
|
||||||
|
const autoHide = (toaster: Toaster) => {
|
||||||
|
const { id } = toaster;
|
||||||
|
show(toaster);
|
||||||
|
timeouts[id] = setTimeout(() => {
|
||||||
|
hide(id);
|
||||||
|
}, toaster.timeout || TOASTER_TIMEOUT);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(
|
||||||
|
() => () => {
|
||||||
|
Object.keys(timeouts).forEach((timeoutId) => {
|
||||||
|
clearTimeout(timeouts[timeoutId]);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
toasters,
|
||||||
|
showToaster: autoHide,
|
||||||
|
hideToaster: hide,
|
||||||
|
};
|
||||||
|
};
|
|
@ -1,31 +0,0 @@
|
||||||
import { dissoc } from 'ramda';
|
|
||||||
|
|
||||||
import { Action } from '@treejs/types/redux/actions';
|
|
||||||
import { isNilOrEmpty } from '@treejs/utils';
|
|
||||||
|
|
||||||
import { ACTIONS, ToasterReducer, ToasterState } from '../types';
|
|
||||||
|
|
||||||
const initialState: ToasterReducer = {};
|
|
||||||
|
|
||||||
type Payload = ToasterState;
|
|
||||||
|
|
||||||
const reducer = (state: typeof initialState, action: Action<Payload, ACTIONS>): ToasterReducer => {
|
|
||||||
if (isNilOrEmpty(state)) {
|
|
||||||
state = initialState;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action.type === ACTIONS.ADD_TOASTER) {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
[action.payload.name]: {
|
|
||||||
...action.payload,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
} else if (action.type === ACTIONS.REMOVE_TOASTER) {
|
|
||||||
return dissoc(action.payload.name, state);
|
|
||||||
} else {
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default reducer;
|
|
|
@ -1,6 +0,0 @@
|
||||||
import { ROOT_REDUCER_NAME } from '@treejs/constants/redux';
|
|
||||||
|
|
||||||
import { TOASTER_REDUCER_NAME } from '../constants';
|
|
||||||
import { ToasterReducer } from '../types';
|
|
||||||
|
|
||||||
export const getToasters = (state: any): ToasterReducer => state[ROOT_REDUCER_NAME][TOASTER_REDUCER_NAME];
|
|
11
packages/components/src/Toaster/types.ts
Normal file
11
packages/components/src/Toaster/types.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { STATUS } from '@treejs/types/common';
|
||||||
|
|
||||||
|
export type ToasterId = string;
|
||||||
|
|
||||||
|
export type Toaster = {
|
||||||
|
id: ToasterId;
|
||||||
|
message: string;
|
||||||
|
status?: STATUS;
|
||||||
|
timeout?: number;
|
||||||
|
title?: string;
|
||||||
|
};
|
|
@ -1,22 +0,0 @@
|
||||||
import { STATUS } from '@treejs/types/common';
|
|
||||||
|
|
||||||
export enum ACTIONS {
|
|
||||||
ADD_TOASTER = 'TOASTER/ADD_TOASTER',
|
|
||||||
REMOVE_TOASTER = 'TOASTER/REMOVE_TOASTER',
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ToasterId = string;
|
|
||||||
|
|
||||||
export type ToasterState = {
|
|
||||||
filled?: boolean;
|
|
||||||
message: string;
|
|
||||||
name: ToasterId;
|
|
||||||
status?: STATUS;
|
|
||||||
timeout?: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ToasterOption = ToasterState;
|
|
||||||
|
|
||||||
export interface ToasterReducer {
|
|
||||||
[key: string]: ToasterState;
|
|
||||||
}
|
|
|
@ -37,7 +37,7 @@ function Field<F>(props: IProps<F>) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div className="field-container">{render}</div>;
|
return <div className="form-field-container">{render}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Field;
|
export default Field;
|
||||||
|
|
|
@ -26,7 +26,9 @@ const button = (theme) => ({
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '40px',
|
height: '40px',
|
||||||
|
|
||||||
borderRadius: theme('borderRadius.sm'),
|
fontSize: '14px',
|
||||||
|
|
||||||
|
borderRadius: theme('borderRadius.md'),
|
||||||
border: '2px solid',
|
border: '2px solid',
|
||||||
|
|
||||||
textTransform: 'uppercase',
|
textTransform: 'uppercase',
|
||||||
|
|
|
@ -1,120 +1,97 @@
|
||||||
const buildState = ({ borderColor }) => {
|
|
||||||
return {
|
|
||||||
borderColor: borderColor,
|
|
||||||
boxShadow: `0 4px ${borderColor}`,
|
|
||||||
'&:hover:not(:disabled)': {
|
|
||||||
boxShadow: `0 2px ${borderColor}`,
|
|
||||||
transform: 'translateY(2px)',
|
|
||||||
},
|
|
||||||
'&:focus:not(:disabled)': {
|
|
||||||
boxShadow: `0 0 ${borderColor}`,
|
|
||||||
transform: 'translateY(4px)',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const field = (theme) => ({
|
const field = (theme) => ({
|
||||||
'.field-container': {
|
'.form-field-container': {
|
||||||
marginBottom: '1rem',
|
marginBottom: '.5rem',
|
||||||
},
|
},
|
||||||
'.textfield-container': {
|
'.field': {
|
||||||
|
'&-container': {
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
'& svg': {
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
fontSize: '15px',
|
||||||
|
},
|
||||||
|
'&-label': {
|
||||||
|
padding: '0.25rem 1rem 0.25rem 0.5rem',
|
||||||
|
color: theme('colors.field.label.color'),
|
||||||
|
backgroundColor: theme('colors.field.label.bg'),
|
||||||
|
borderRadius: `${theme('borderRadius.md')} 0 0 ${theme('borderRadius.md')}`,
|
||||||
|
border: `2px solid ${theme('colors.field.border')}`,
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
|
// minWidth: '9rem',
|
||||||
},
|
},
|
||||||
'.textfield-input': {
|
'&-input': {
|
||||||
padding: '0.5rem',
|
padding: '0.25rem',
|
||||||
|
|
||||||
"&:not([value=''])": {
|
|
||||||
transform: 'translateY(4px)',
|
|
||||||
boxShadow: 'none',
|
|
||||||
},
|
|
||||||
|
|
||||||
outline: 'none',
|
outline: 'none',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
|
|
||||||
lineHeight: '1.25',
|
|
||||||
|
|
||||||
fontSize: '1rem',
|
|
||||||
fontWeight: 300,
|
fontWeight: 300,
|
||||||
|
|
||||||
boxSizing: 'border-box',
|
boxSizing: 'border-box',
|
||||||
borderRadius: theme('borderRadius.sm'),
|
borderRadius: `0 ${theme('borderRadius.md')} ${theme('borderRadius.md')} 0`,
|
||||||
border: '2px solid',
|
border: '2px solid',
|
||||||
|
borderLeft: 'none',
|
||||||
'&': buildState({
|
'&': {
|
||||||
borderColor: theme('colors.gray.300'),
|
borderColor: theme('colors.field.border'),
|
||||||
}),
|
},
|
||||||
|
'&--error': {
|
||||||
'&--error': buildState({
|
|
||||||
borderColor: theme('colors.error.DEFAULT'),
|
borderColor: theme('colors.error.DEFAULT'),
|
||||||
}),
|
},
|
||||||
'&--info': buildState({
|
'&--info': {
|
||||||
borderColor: theme('colors.info.DEFAULT'),
|
borderColor: theme('colors.info.DEFAULT'),
|
||||||
}),
|
},
|
||||||
'&--warning': buildState({
|
'&--warning': {
|
||||||
borderColor: theme('colors.warning.DEFAULT'),
|
borderColor: theme('colors.warning.DEFAULT'),
|
||||||
}),
|
},
|
||||||
'&--success': buildState({
|
|
||||||
borderColor: theme('colors.success.DEFAULT'),
|
|
||||||
}),
|
|
||||||
|
|
||||||
'&:disabled': {
|
'&:disabled': {
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
border: `2px solid ${theme('colors.whitesmoke')}`,
|
border: `2px solid ${theme('colors.whitesmoke')}`,
|
||||||
'&:empty': {
|
|
||||||
border: 'none',
|
|
||||||
boxShadow: 'none',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
'&:read-only': {
|
'&:read-only': {
|
||||||
cursor: 'auto',
|
cursor: 'auto',
|
||||||
},
|
},
|
||||||
|
'&--nolabel': {
|
||||||
|
borderLeft: '2px solid',
|
||||||
|
borderRadius: theme('borderRadius.md'),
|
||||||
|
},
|
||||||
|
'&--clearable': {
|
||||||
|
borderRadius: 0,
|
||||||
|
borderRight: 'none',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'.checkbox-container': {
|
|
||||||
position: 'relative',
|
|
||||||
marginTop: '1rem',
|
|
||||||
},
|
},
|
||||||
'.radiobutton-container': {
|
'.checkbox-container, .radiobutton-container': {
|
||||||
position: 'relative',
|
'.field-label': {
|
||||||
marginTop: '1rem',
|
borderRadius: theme('borderRadius.md'),
|
||||||
|
},
|
||||||
|
'.field-checkbox, .field-radio': {
|
||||||
|
cursor: 'pointer',
|
||||||
|
'&--error': {
|
||||||
|
color: theme('colors.error.DEFAULT'),
|
||||||
|
},
|
||||||
|
'&--info': {
|
||||||
|
color: theme('colors.info.DEFAULT'),
|
||||||
|
},
|
||||||
|
'&--warning': {
|
||||||
|
color: theme('colors.warning.DEFAULT'),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
'.field-icon': {
|
'.field-icon': {
|
||||||
position: 'absolute',
|
padding: '0.4rem 0.5rem',
|
||||||
top: '1.27rem',
|
color: theme('colors.white'),
|
||||||
right: '0',
|
backgroundColor: theme('colors.field.label.bg'),
|
||||||
padding: '0.51rem',
|
borderRadius: `0 ${theme('borderRadius.md')} ${theme('borderRadius.md')} 0`,
|
||||||
|
border: `2px solid ${theme('colors.field.border')}`,
|
||||||
color: theme('colors.primary.DEFAULT'),
|
cursor: 'pointer',
|
||||||
|
|
||||||
fontSize: '1.125rem',
|
|
||||||
lineHeight: '1.25rem',
|
|
||||||
|
|
||||||
'&--clear': {
|
|
||||||
display: 'none',
|
|
||||||
borderRadius: '0 2px 2px 0',
|
|
||||||
color: theme('colors.error.dark'),
|
|
||||||
},
|
|
||||||
'.field-input:disabled &': {
|
'.field-input:disabled &': {
|
||||||
color: theme('colors.gray.500'),
|
color: theme('colors.gray.500'),
|
||||||
'& svg': {
|
'& svg': {
|
||||||
cursor: 'auto',
|
cursor: 'auto',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'.field-input:hover + &, .field-input + &:hover': {
|
|
||||||
display: 'inline',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'.field-label': {
|
|
||||||
fontSize: '0.8rem',
|
|
||||||
lineHeight: '1.25rem',
|
|
||||||
color: theme('colors.gray.500'),
|
|
||||||
},
|
},
|
||||||
'.field-validationMessage': {
|
'.field-validationMessage': {
|
||||||
color: theme('colors.error.DEFAULT'),
|
color: theme('colors.error.DEFAULT'),
|
||||||
textAlign: 'left',
|
textAlign: 'left',
|
||||||
fontSize: '0.7rem',
|
fontSize: '0.75rem',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,14 @@
|
||||||
|
const buildState = ({ color, borderColor, backgroundColor, iconColor }) => {
|
||||||
|
return {
|
||||||
|
color,
|
||||||
|
borderColor,
|
||||||
|
backgroundColor,
|
||||||
|
'& svg': {
|
||||||
|
color: iconColor ? iconColor : 'white',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const message = (theme) => ({
|
const message = (theme) => ({
|
||||||
'.message': {
|
'.message': {
|
||||||
padding: theme('spacing.4'),
|
padding: theme('spacing.4'),
|
||||||
|
@ -12,40 +23,34 @@ const message = (theme) => ({
|
||||||
backgroundColor: 'white',
|
backgroundColor: 'white',
|
||||||
|
|
||||||
border: '3px solid whitesmoke',
|
border: '3px solid whitesmoke',
|
||||||
borderRadius: theme('borderRadius.sm'),
|
borderRadius: theme('borderRadius.md'),
|
||||||
|
|
||||||
'&--none': {
|
'&--none': buildState({
|
||||||
borderColor: 'transparent',
|
|
||||||
'&..message--filled': {
|
|
||||||
backgroundColor: theme('transparent'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'&--info': {
|
|
||||||
borderColor: theme('colors.info'),
|
|
||||||
'&.message--filled': {
|
|
||||||
backgroundColor: theme('colors.info'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'&--success': {
|
|
||||||
borderColor: theme('colors.success'),
|
|
||||||
'&.message--filled': {
|
|
||||||
backgroundColor: theme('colors.success'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'&--warning': {
|
|
||||||
color: theme('colors.black'),
|
color: theme('colors.black'),
|
||||||
borderColor: theme('colors.warning'),
|
borderColor: theme('colors.gray.300'),
|
||||||
'&.message--filled': {
|
backgroundColor: theme('colors.whitesmoke'),
|
||||||
backgroundColor: theme('colors.warning'),
|
iconColor: theme('colors.gray.700'),
|
||||||
},
|
}),
|
||||||
},
|
'&--info': buildState({
|
||||||
'&--error': {
|
|
||||||
color: theme('colors.black'),
|
color: theme('colors.black'),
|
||||||
borderColor: theme('colors.error.DEFAULT'),
|
borderColor: theme('colors.info.dark'),
|
||||||
'&.message--filled': {
|
backgroundColor: theme('colors.info.DEFAULT'),
|
||||||
|
}),
|
||||||
|
'&--success': buildState({
|
||||||
|
color: theme('colors.black'),
|
||||||
|
borderColor: theme('colors.success.dark'),
|
||||||
|
backgroundColor: theme('colors.success.DEFAULT'),
|
||||||
|
}),
|
||||||
|
'&--warning': buildState({
|
||||||
|
color: theme('colors.black'),
|
||||||
|
borderColor: theme('colors.warning.dark'),
|
||||||
|
backgroundColor: theme('colors.warning.DEFAULT'),
|
||||||
|
}),
|
||||||
|
'&--error': buildState({
|
||||||
|
color: theme('colors.black'),
|
||||||
|
borderColor: theme('colors.error.dark'),
|
||||||
backgroundColor: theme('colors.error.DEFAULT'),
|
backgroundColor: theme('colors.error.DEFAULT'),
|
||||||
},
|
}),
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ const skeleton = (theme) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
fontSize: '14px',
|
fontSize: '16px',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
'&-left': {
|
'&-left': {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
@ -36,8 +36,8 @@ const skeleton = (theme) => ({
|
||||||
'.side': {
|
'.side': {
|
||||||
color: theme('colors.navigation.side.color'),
|
color: theme('colors.navigation.side.color'),
|
||||||
backgroundColor: theme('colors.navigation.side.bg'),
|
backgroundColor: theme('colors.navigation.side.bg'),
|
||||||
borderRadius: theme('borderRadius.sm'),
|
border: `2px solid ${theme('colors.navigation.side.border')}`,
|
||||||
boxShadow: '1px -1px 8px -1px rgba(var(--color-black), 0.4)',
|
borderRadius: theme('borderRadius.md'),
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
paddingTop: '2.5rem',
|
paddingTop: '2.5rem',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
@ -62,14 +62,14 @@ const skeleton = (theme) => ({
|
||||||
width: '2.25rem',
|
width: '2.25rem',
|
||||||
'.menu-item': {
|
'.menu-item': {
|
||||||
'&-left': {
|
'&-left': {
|
||||||
padding: '0.5rem 0 0.5rem 0.25rem',
|
padding: '0.5rem 0 0.5rem 0.125rem',
|
||||||
},
|
},
|
||||||
'&-right': {
|
'&-right': {
|
||||||
padding: '0.5rem 0.25rem 0.5rem 0',
|
padding: '0.5rem 0.25rem 0.5rem 0',
|
||||||
fontSize: '8px',
|
fontSize: '8px',
|
||||||
},
|
},
|
||||||
'&-openIcon': {
|
'&-openIcon': {
|
||||||
fontSize: '8px',
|
fontSize: '0.5rem',
|
||||||
},
|
},
|
||||||
'&-tooltip': {
|
'&-tooltip': {
|
||||||
color: theme('colors.navigation.side.color'),
|
color: theme('colors.navigation.side.color'),
|
||||||
|
@ -90,8 +90,7 @@ const skeleton = (theme) => ({
|
||||||
left: '2.5rem',
|
left: '2.5rem',
|
||||||
padding: '0.25rem 1rem',
|
padding: '0.25rem 1rem',
|
||||||
backgroundColor: theme('colors.black.DEFAULT'),
|
backgroundColor: theme('colors.black.DEFAULT'),
|
||||||
boxShadow: '1px 1px 8px -1px rgba(var(--color-black), 0.4)',
|
borderRadius: theme('borderRadius.md'),
|
||||||
borderRadius: theme('borderRadius.sm'),
|
|
||||||
display: 'none',
|
display: 'none',
|
||||||
},
|
},
|
||||||
'&:hover .menu-item-tooltip': {
|
'&:hover .menu-item-tooltip': {
|
||||||
|
@ -100,7 +99,9 @@ const skeleton = (theme) => ({
|
||||||
},
|
},
|
||||||
'.submenu': {
|
'.submenu': {
|
||||||
'&-container': {
|
'&-container': {
|
||||||
|
borderRadius: theme('borderRadius.md'),
|
||||||
backgroundColor: theme('colors.subnavigation.side.bg'),
|
backgroundColor: theme('colors.subnavigation.side.bg'),
|
||||||
|
border: `2px solid ${theme('colors.navigation.side.bg')}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -113,9 +114,10 @@ const skeleton = (theme) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
marginLeft: '0.25rem',
|
marginLeft: '0.25rem',
|
||||||
|
height: '2.5rem',
|
||||||
backgroundColor: theme('colors.navigation.top.bg'),
|
backgroundColor: theme('colors.navigation.top.bg'),
|
||||||
borderRadius: theme('borderRadius.sm'),
|
border: `2px solid ${theme('colors.navigation.top.border')}`,
|
||||||
boxShadow: '1px 1px 8px -1px rgba(var(--color-black), 0.4)',
|
borderRadius: theme('borderRadius.md'),
|
||||||
'&-toggler': {
|
'&-toggler': {
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
fontSize: '1.75rem',
|
fontSize: '1.75rem',
|
||||||
|
@ -142,12 +144,13 @@ const skeleton = (theme) => ({
|
||||||
},
|
},
|
||||||
'.submenu': {
|
'.submenu': {
|
||||||
'&-container': {
|
'&-container': {
|
||||||
marginTop: '.125rem',
|
zIndex: 1,
|
||||||
|
marginTop: '.5rem',
|
||||||
width: 'max-content',
|
width: 'max-content',
|
||||||
boxShadow: '1px 5px 8px -1px rgba(var(--color-black), 0.4)',
|
|
||||||
borderRadius: '2px',
|
borderRadius: '2px',
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
backgroundColor: theme('colors.subnavigation.top.bg'),
|
backgroundColor: theme('colors.subnavigation.top.bg'),
|
||||||
|
border: `2px solid ${theme('colors.navigation.top.bg')}`,
|
||||||
},
|
},
|
||||||
'&-item': {
|
'&-item': {
|
||||||
margin: '0',
|
margin: '0',
|
||||||
|
@ -162,6 +165,7 @@ const skeleton = (theme) => ({
|
||||||
[`@media (max-width: ${theme('screens.md-m.max')})`]: {
|
[`@media (max-width: ${theme('screens.md-m.max')})`]: {
|
||||||
'.page-main': {
|
'.page-main': {
|
||||||
'.top': {
|
'.top': {
|
||||||
|
height: 'auto',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
'.menu-container': {
|
'.menu-container': {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
|
@ -170,7 +174,7 @@ const skeleton = (theme) => ({
|
||||||
'&-container': {
|
'&-container': {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
boxShadow: 'none',
|
borderRadius: theme('borderRadius.md'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -29,18 +29,24 @@
|
||||||
--color-success--dark: theme('colors.green.400');
|
--color-success--dark: theme('colors.green.400');
|
||||||
--color-warning: theme('colors.yellow.300');
|
--color-warning: theme('colors.yellow.300');
|
||||||
--color-warning--dark: theme('colors.yellow.400');
|
--color-warning--dark: theme('colors.yellow.400');
|
||||||
--color-error: theme('colors.red.300');
|
--color-error: theme('colors.red.400');
|
||||||
--color-error--dark: theme('colors.red.400');
|
--color-error--dark: theme('colors.red.500');
|
||||||
|
|
||||||
--color-navigation-top-color: white;
|
--color-navigation-top-color: white;
|
||||||
--color-navigation-top-bg: #3A6891;
|
--color-navigation-top-bg: #3A6891;
|
||||||
|
--color-navigation-top-border: #213C53;
|
||||||
--color-subnavigation-top-color: white;
|
--color-subnavigation-top-color: white;
|
||||||
--color-subnavigation-top-bg: #477FB1;
|
--color-subnavigation-top-bg: #477FB1;
|
||||||
|
|
||||||
--color-navigation-side-color: white;
|
--color-navigation-side-color: white;
|
||||||
--color-navigation-side-bg: #3B4566;
|
--color-navigation-side-bg: #3B4566;
|
||||||
|
--color-navigation-side-border: #22283B;
|
||||||
--color-subnavigation-side-color: white;
|
--color-subnavigation-side-color: white;
|
||||||
--color-subnavigation-side-bg: #4E5B87;
|
--color-subnavigation-side-bg: #4E5B87;
|
||||||
|
|
||||||
|
--color-field-label-color: black;
|
||||||
|
--color-field-label-bg: theme('colors.gray.300');
|
||||||
|
--color-field-border: theme('colors.gray.400');
|
||||||
}
|
}
|
||||||
|
|
||||||
.purple {
|
.purple {
|
||||||
|
|
|
@ -45,10 +45,12 @@ module.exports = {
|
||||||
navigation: {
|
navigation: {
|
||||||
top: {
|
top: {
|
||||||
color: 'var(--color-navigation-top-color)',
|
color: 'var(--color-navigation-top-color)',
|
||||||
|
border: 'var(--color-navigation-top-border)',
|
||||||
bg: 'var(--color-navigation-top-bg)',
|
bg: 'var(--color-navigation-top-bg)',
|
||||||
},
|
},
|
||||||
side: {
|
side: {
|
||||||
color: 'var(--color-navigation-side-color)',
|
color: 'var(--color-navigation-side-color)',
|
||||||
|
border: 'var(--color-navigation-side-border)',
|
||||||
bg: 'var(--color-navigation-side-bg)',
|
bg: 'var(--color-navigation-side-bg)',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -62,6 +64,13 @@ module.exports = {
|
||||||
bg: 'var(--color-subnavigation-side-bg)',
|
bg: 'var(--color-subnavigation-side-bg)',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
field: {
|
||||||
|
label: {
|
||||||
|
color: 'var(--color-field-label-color)',
|
||||||
|
bg: 'var(--color-field-label-bg)',
|
||||||
|
},
|
||||||
|
border: 'var(--color-field-border)',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
screens: {
|
screens: {
|
||||||
'lg-m': { min: '768px', max: '1024px' },
|
'lg-m': { min: '768px', max: '1024px' },
|
||||||
|
|
|
@ -2,9 +2,9 @@ import { ArgsTable, Meta, Preview, Story } from '@storybook/addon-docs';
|
||||||
|
|
||||||
import AddToaster from '@treejs/components/Toaster/components/AddToaster';
|
import AddToaster from '@treejs/components/Toaster/components/AddToaster';
|
||||||
import Toasters from '@treejs/components/Toaster/components/Toasters';
|
import Toasters from '@treejs/components/Toaster/components/Toasters';
|
||||||
|
import ToasterWrapper from '@treejs/components/Toaster/context';
|
||||||
|
|
||||||
import { STATUS } from '../../packages/types/src/common';
|
import { STATUS } from '../../packages/types/src/common';
|
||||||
import StoreProvider from './Toaster/redux';
|
|
||||||
|
|
||||||
<Meta title={'Components/Toaster/AddToaster'} component={AddToaster} />
|
<Meta title={'Components/Toaster/AddToaster'} component={AddToaster} />
|
||||||
|
|
||||||
|
@ -16,31 +16,14 @@ import StoreProvider from './Toaster/redux';
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
1. Wrapped with redux store provider.
|
1. Use `ToasterWrapper` component for apply Toaster context
|
||||||
|
|
||||||
```tsx dark
|
|
||||||
import { TOASTER_REDUCER_NAME } from '@treejs/components/Toaster/constants';
|
|
||||||
import toasterReducer from '@treejs/components/Toaster/reducer';
|
|
||||||
import { ROOT_REDUCER_NAME } from '@treejs/constants/redux';
|
|
||||||
|
|
||||||
export const mainReducer = combineReducers({
|
|
||||||
[ROOT_REDUCER_NAME]: combineReducers({
|
|
||||||
[TOASTER_REDUCER_NAME]: toasterReducer,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Somewhere in redux store provider must be used `Toasters` component
|
|
||||||
from `@treejs/components/Toaster/components/Toasters`
|
|
||||||
|
|
||||||
<ArgsTable />
|
|
||||||
|
|
||||||
export const Template = (args) => {
|
export const Template = (args) => {
|
||||||
return (
|
return (
|
||||||
<StoreProvider>
|
<ToasterWrapper>
|
||||||
<AddToaster {...args} />
|
<AddToaster {...args} />
|
||||||
<Toasters />
|
<Toasters />
|
||||||
</StoreProvider>
|
</ToasterWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -53,6 +36,7 @@ export const Template = (args) => {
|
||||||
args={{
|
args={{
|
||||||
name: 'none',
|
name: 'none',
|
||||||
title: 'Show toaster',
|
title: 'Show toaster',
|
||||||
|
label: 'Show toaster',
|
||||||
message: 'Toaster message.',
|
message: 'Toaster message.',
|
||||||
status: STATUS.none,
|
status: STATUS.none,
|
||||||
}}
|
}}
|
||||||
|
@ -70,6 +54,7 @@ export const Template = (args) => {
|
||||||
args={{
|
args={{
|
||||||
name: 'info',
|
name: 'info',
|
||||||
title: 'Show toaster',
|
title: 'Show toaster',
|
||||||
|
label: 'Show toaster',
|
||||||
message: 'Toaster message.',
|
message: 'Toaster message.',
|
||||||
status: STATUS.info,
|
status: STATUS.info,
|
||||||
}}
|
}}
|
||||||
|
@ -87,6 +72,7 @@ export const Template = (args) => {
|
||||||
args={{
|
args={{
|
||||||
name: 'success',
|
name: 'success',
|
||||||
title: 'Show toaster',
|
title: 'Show toaster',
|
||||||
|
label: 'Show toaster',
|
||||||
message: 'Toaster message.',
|
message: 'Toaster message.',
|
||||||
status: STATUS.success,
|
status: STATUS.success,
|
||||||
}}
|
}}
|
||||||
|
@ -104,6 +90,7 @@ export const Template = (args) => {
|
||||||
args={{
|
args={{
|
||||||
name: 'warning',
|
name: 'warning',
|
||||||
title: 'Show toaster',
|
title: 'Show toaster',
|
||||||
|
label: 'Show toaster',
|
||||||
message: 'Toaster message.',
|
message: 'Toaster message.',
|
||||||
status: STATUS.warning,
|
status: STATUS.warning,
|
||||||
}}
|
}}
|
||||||
|
@ -121,6 +108,7 @@ export const Template = (args) => {
|
||||||
args={{
|
args={{
|
||||||
name: 'error',
|
name: 'error',
|
||||||
title: 'Show toaster',
|
title: 'Show toaster',
|
||||||
|
label: 'Show toaster',
|
||||||
message: 'Toaster message.',
|
message: 'Toaster message.',
|
||||||
status: STATUS.error,
|
status: STATUS.error,
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -5,8 +5,10 @@ import CustomRouter from '@treejs/components/CustomRouter';
|
||||||
import MinusIcon from '@treejs/components/Icons/Minus';
|
import MinusIcon from '@treejs/components/Icons/Minus';
|
||||||
import PlusIcon from '@treejs/components/Icons/Plus';
|
import PlusIcon from '@treejs/components/Icons/Plus';
|
||||||
import UserIcon from '@treejs/components/Icons/User';
|
import UserIcon from '@treejs/components/Icons/User';
|
||||||
|
import { ModalWrapper } from '@treejs/components/Modal/context';
|
||||||
import Skeleton from '@treejs/components/Skeleton';
|
import Skeleton from '@treejs/components/Skeleton';
|
||||||
|
|
||||||
|
import FormExample from '../form/components/ExampleForm';
|
||||||
import Router from './Skeleton/Router';
|
import Router from './Skeleton/Router';
|
||||||
|
|
||||||
<Meta title={'Components/Sketeton'} component={Skeleton} />
|
<Meta title={'Components/Sketeton'} component={Skeleton} />
|
||||||
|
@ -21,9 +23,12 @@ export const history = createBrowserHistory({ basename: '/' });
|
||||||
|
|
||||||
export const Template = (args) => (
|
export const Template = (args) => (
|
||||||
<CustomRouter history={history}>
|
<CustomRouter history={history}>
|
||||||
|
<ModalWrapper>
|
||||||
<Skeleton {...args} history={history}>
|
<Skeleton {...args} history={history}>
|
||||||
<Router />
|
<Router />
|
||||||
|
<FormExample />
|
||||||
</Skeleton>
|
</Skeleton>
|
||||||
|
</ModalWrapper>
|
||||||
</CustomRouter>
|
</CustomRouter>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -13,27 +13,10 @@ import { Meta } from '@storybook/addon-docs';
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
1. Wrapped with redux store provider.
|
1. Use `ToasterWrapper` component for apply Toaster context
|
||||||
|
|
||||||
|
## Open Toaster
|
||||||
|
|
||||||
```ts dark
|
```ts dark
|
||||||
import { combineReducers } from '@reduxjs/toolkit';
|
showToaster({ id, status, message });
|
||||||
|
|
||||||
import { TOASTER_REDUCER_NAME } from '@treejs/components/Toaster/constants';
|
|
||||||
import toasterReducer from '@treejs/components/Toaster/reducer';
|
|
||||||
import { ROOT_REDUCER_NAME } from '@treejs/constants/redux';
|
|
||||||
|
|
||||||
export const mainReducer = combineReducers({
|
|
||||||
[ROOT_REDUCER_NAME]: combineReducers({
|
|
||||||
[TOASTER_REDUCER_NAME]: toasterReducer,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Somewhere in redux store provider must be used `Toasters` component
|
|
||||||
from `@treejs/components/Toaster/components/Toasters`
|
|
||||||
|
|
||||||
## Redux action
|
|
||||||
|
|
||||||
```ts dark
|
|
||||||
dispatch(addToaster({ name, status, message }));
|
|
||||||
```
|
```
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue