mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-15 20:30:33 +00:00
feat(webapp): import resource UI
This commit is contained in:
12
packages/webapp/src/components/Dropzone/Dropzone.module.css
Normal file
12
packages/webapp/src/components/Dropzone/Dropzone.module.css
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
|
||||
.root {
|
||||
padding: 20px;
|
||||
border: 2px dotted #c5cbd3;
|
||||
border-radius: 6px;
|
||||
min-height: 200px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #fff;
|
||||
position: relative;
|
||||
}
|
||||
266
packages/webapp/src/components/Dropzone/Dropzone.tsx
Normal file
266
packages/webapp/src/components/Dropzone/Dropzone.tsx
Normal file
@@ -0,0 +1,266 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import clsx from 'classnames';
|
||||
import {
|
||||
Accept,
|
||||
DropEvent,
|
||||
FileError,
|
||||
FileRejection,
|
||||
FileWithPath,
|
||||
useDropzone,
|
||||
} from 'react-dropzone-esm';
|
||||
import { DropzoneProvider } from './DropzoneProvider';
|
||||
import { DropzoneAccept, DropzoneIdle, DropzoneReject } from './DropzoneStatus';
|
||||
import { Box } from '../Layout';
|
||||
import styles from './Dropzone.module.css';
|
||||
import { CloudLoadingIndicator } from '../Indicator';
|
||||
|
||||
export type DropzoneStylesNames = 'root' | 'inner';
|
||||
export type DropzoneVariant = 'filled' | 'light';
|
||||
export type DropzoneCssVariables = {
|
||||
root:
|
||||
| '--dropzone-radius'
|
||||
| '--dropzone-accept-color'
|
||||
| '--dropzone-accept-bg'
|
||||
| '--dropzone-reject-color'
|
||||
| '--dropzone-reject-bg';
|
||||
};
|
||||
|
||||
export interface DropzoneProps {
|
||||
/** Key of `theme.colors` or any valid CSS color to set colors of `Dropzone.Accept`, `theme.primaryColor` by default */
|
||||
acceptColor?: MantineColor;
|
||||
|
||||
/** Key of `theme.colors` or any valid CSS color to set colors of `Dropzone.Reject`, `'red'` by default */
|
||||
rejectColor?: MantineColor;
|
||||
|
||||
/** Key of `theme.radius` or any valid CSS value to set `border-radius`, numbers are converted to rem, `theme.defaultRadius` by default */
|
||||
radius?: MantineRadius;
|
||||
|
||||
/** Determines whether files capturing should be disabled, `false` by default */
|
||||
disabled?: boolean;
|
||||
|
||||
/** Called when any files are dropped to the dropzone */
|
||||
onDropAny?: (files: FileWithPath[], fileRejections: FileRejection[]) => void;
|
||||
|
||||
/** Called when valid files are dropped to the dropzone */
|
||||
onDrop: (files: FileWithPath[]) => void;
|
||||
|
||||
/** Called when dropped files do not meet file restrictions */
|
||||
onReject?: (fileRejections: FileRejection[]) => void;
|
||||
|
||||
/** Determines whether a loading overlay should be displayed over the dropzone, `false` by default */
|
||||
loading?: boolean;
|
||||
|
||||
/** Mime types of the files that dropzone can accepts. By default, dropzone accepts all file types. */
|
||||
accept?: Accept | string[];
|
||||
|
||||
/** A ref function which when called opens the file system file picker */
|
||||
openRef?: React.ForwardedRef<() => void | undefined>;
|
||||
|
||||
/** Determines whether multiple files can be dropped to the dropzone or selected from file system picker, `true` by default */
|
||||
multiple?: boolean;
|
||||
|
||||
/** Maximum file size in bytes */
|
||||
maxSize?: number;
|
||||
|
||||
/** Name of the form control. Submitted with the form as part of a name/value pair. */
|
||||
name?: string;
|
||||
|
||||
/** Maximum number of files that can be picked at once */
|
||||
maxFiles?: number;
|
||||
|
||||
/** Set to autofocus the root element */
|
||||
autoFocus?: boolean;
|
||||
|
||||
/** If `false`, disables click to open the native file selection dialog */
|
||||
activateOnClick?: boolean;
|
||||
|
||||
/** If `false`, disables drag 'n' drop */
|
||||
activateOnDrag?: boolean;
|
||||
|
||||
/** If `false`, disables Space/Enter to open the native file selection dialog. Note that it also stops tracking the focus state. */
|
||||
activateOnKeyboard?: boolean;
|
||||
|
||||
/** If `false`, stops drag event propagation to parents */
|
||||
dragEventsBubbling?: boolean;
|
||||
|
||||
/** Called when the `dragenter` event occurs */
|
||||
onDragEnter?: (event: React.DragEvent<HTMLElement>) => void;
|
||||
|
||||
/** Called when the `dragleave` event occurs */
|
||||
onDragLeave?: (event: React.DragEvent<HTMLElement>) => void;
|
||||
|
||||
/** Called when the `dragover` event occurs */
|
||||
onDragOver?: (event: React.DragEvent<HTMLElement>) => void;
|
||||
|
||||
/** Called when user closes the file selection dialog with no selection */
|
||||
onFileDialogCancel?: () => void;
|
||||
|
||||
/** Called when user opens the file selection dialog */
|
||||
onFileDialogOpen?: () => void;
|
||||
|
||||
/** If `false`, allow dropped items to take over the current browser window */
|
||||
preventDropOnDocument?: boolean;
|
||||
|
||||
/** Set to true to use the File System Access API to open the file picker instead of using an <input type="file"> click event, defaults to true */
|
||||
useFsAccessApi?: boolean;
|
||||
|
||||
/** Use this to provide a custom file aggregator */
|
||||
getFilesFromEvent?: (
|
||||
event: DropEvent,
|
||||
) => Promise<Array<File | DataTransferItem>>;
|
||||
|
||||
/** Custom validation function. It must return null if there's no errors. */
|
||||
validator?: <T extends File>(file: T) => FileError | FileError[] | null;
|
||||
|
||||
/** Determines whether pointer events should be enabled on the inner element, `false` by default */
|
||||
enablePointerEvents?: boolean;
|
||||
|
||||
/** Props passed down to the Loader component */
|
||||
loaderProps?: LoaderProps;
|
||||
|
||||
/** Props passed down to the internal Input component */
|
||||
inputProps?: React.InputHTMLAttributes<HTMLInputElement>;
|
||||
}
|
||||
|
||||
export type DropzoneFactory = Factory<{
|
||||
props: DropzoneProps;
|
||||
ref: HTMLDivElement;
|
||||
stylesNames: DropzoneStylesNames;
|
||||
vars: DropzoneCssVariables;
|
||||
staticComponents: {
|
||||
Accept: typeof DropzoneAccept;
|
||||
Idle: typeof DropzoneIdle;
|
||||
Reject: typeof DropzoneReject;
|
||||
};
|
||||
}>;
|
||||
|
||||
const defaultProps: Partial<DropzoneProps> = {
|
||||
loading: false,
|
||||
multiple: true,
|
||||
maxSize: Infinity,
|
||||
autoFocus: false,
|
||||
activateOnClick: true,
|
||||
activateOnDrag: true,
|
||||
dragEventsBubbling: true,
|
||||
activateOnKeyboard: true,
|
||||
useFsAccessApi: true,
|
||||
variant: 'light',
|
||||
rejectColor: 'red',
|
||||
};
|
||||
|
||||
export const Dropzone = (_props: DropzoneProps) => {
|
||||
const {
|
||||
// classNames,
|
||||
// className,
|
||||
// style,
|
||||
// styles,
|
||||
// unstyled,
|
||||
// vars,
|
||||
radius,
|
||||
disabled,
|
||||
loading,
|
||||
multiple,
|
||||
maxSize,
|
||||
accept,
|
||||
children,
|
||||
onDropAny,
|
||||
onDrop,
|
||||
onReject,
|
||||
openRef,
|
||||
name,
|
||||
maxFiles,
|
||||
autoFocus,
|
||||
activateOnClick,
|
||||
activateOnDrag,
|
||||
dragEventsBubbling,
|
||||
activateOnKeyboard,
|
||||
onDragEnter,
|
||||
onDragLeave,
|
||||
onDragOver,
|
||||
onFileDialogCancel,
|
||||
onFileDialogOpen,
|
||||
preventDropOnDocument,
|
||||
useFsAccessApi,
|
||||
getFilesFromEvent,
|
||||
validator,
|
||||
rejectColor,
|
||||
acceptColor,
|
||||
enablePointerEvents,
|
||||
loaderProps,
|
||||
inputProps,
|
||||
// mod,
|
||||
classNames,
|
||||
...others
|
||||
} = {
|
||||
...defaultProps,
|
||||
..._props,
|
||||
};
|
||||
|
||||
const { getRootProps, getInputProps, isDragAccept, isDragReject, open } =
|
||||
useDropzone({
|
||||
onDrop: onDropAny,
|
||||
onDropAccepted: onDrop,
|
||||
onDropRejected: onReject,
|
||||
disabled: disabled || loading,
|
||||
accept: Array.isArray(accept)
|
||||
? accept.reduce((r, key) => ({ ...r, [key]: [] }), {})
|
||||
: accept,
|
||||
multiple,
|
||||
maxSize,
|
||||
maxFiles,
|
||||
autoFocus,
|
||||
noClick: !activateOnClick,
|
||||
noDrag: !activateOnDrag,
|
||||
noDragEventsBubbling: !dragEventsBubbling,
|
||||
noKeyboard: !activateOnKeyboard,
|
||||
onDragEnter,
|
||||
onDragLeave,
|
||||
onDragOver,
|
||||
onFileDialogCancel,
|
||||
onFileDialogOpen,
|
||||
preventDropOnDocument,
|
||||
useFsAccessApi,
|
||||
validator,
|
||||
...(getFilesFromEvent ? { getFilesFromEvent } : null),
|
||||
});
|
||||
|
||||
const isIdle = !isDragAccept && !isDragReject;
|
||||
|
||||
return (
|
||||
<DropzoneProvider
|
||||
value={{ accept: isDragAccept, reject: isDragReject, idle: isIdle }}
|
||||
>
|
||||
<Box
|
||||
{...getRootProps({
|
||||
className: clsx(styles.root, classNames?.root),
|
||||
})}
|
||||
// {...getStyles('root', { focusable: true })}
|
||||
{...others}
|
||||
mod={[
|
||||
{
|
||||
accept: isDragAccept,
|
||||
reject: isDragReject,
|
||||
idle: isIdle,
|
||||
loading,
|
||||
'activate-on-click': activateOnClick,
|
||||
},
|
||||
// mod,
|
||||
]}
|
||||
>
|
||||
<input {...getInputProps(inputProps)} name={name} />
|
||||
<div
|
||||
data-enable-pointer-events={enablePointerEvents || undefined}
|
||||
className={classNames?.content}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</Box>
|
||||
</DropzoneProvider>
|
||||
);
|
||||
};
|
||||
|
||||
Dropzone.displayName = '@mantine/dropzone/Dropzone';
|
||||
Dropzone.Accept = DropzoneAccept;
|
||||
Dropzone.Idle = DropzoneIdle;
|
||||
Dropzone.Reject = DropzoneReject;
|
||||
12
packages/webapp/src/components/Dropzone/DropzoneProvider.tsx
Normal file
12
packages/webapp/src/components/Dropzone/DropzoneProvider.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import { createSafeContext } from './create-safe-context';
|
||||
|
||||
export interface DropzoneContextValue {
|
||||
idle: boolean;
|
||||
accept: boolean;
|
||||
reject: boolean;
|
||||
}
|
||||
|
||||
export const [DropzoneProvider, useDropzoneContext] =
|
||||
createSafeContext<DropzoneContextValue>(
|
||||
'Dropzone component was not found in tree',
|
||||
);
|
||||
36
packages/webapp/src/components/Dropzone/DropzoneStatus.tsx
Normal file
36
packages/webapp/src/components/Dropzone/DropzoneStatus.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import React, { cloneElement } from 'react';
|
||||
import { upperFirst } from 'lodash';
|
||||
import { DropzoneContextValue, useDropzoneContext } from './DropzoneProvider';
|
||||
import { isElement } from '@/utils/is-element';
|
||||
|
||||
export interface DropzoneStatusProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
type DropzoneStatusComponent = React.FC<DropzoneStatusProps>;
|
||||
|
||||
function createDropzoneStatus(status: keyof DropzoneContextValue) {
|
||||
const Component: DropzoneStatusComponent = (props) => {
|
||||
const { children, ...others } = props;
|
||||
|
||||
const ctx = useDropzoneContext();
|
||||
const _children = isElement(children) ? children : <span>{children}</span>;
|
||||
|
||||
if (ctx[status]) {
|
||||
return cloneElement(_children as JSX.Element, others);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
Component.displayName = `@bigcapital/core/dropzone/${upperFirst(status)}`;
|
||||
|
||||
return Component;
|
||||
}
|
||||
|
||||
export const DropzoneAccept = createDropzoneStatus('accept');
|
||||
export const DropzoneReject = createDropzoneStatus('reject');
|
||||
export const DropzoneIdle = createDropzoneStatus('idle');
|
||||
|
||||
export type DropzoneAcceptProps = DropzoneStatusProps;
|
||||
export type DropzoneRejectProps = DropzoneStatusProps;
|
||||
export type DropzoneIdleProps = DropzoneStatusProps;
|
||||
@@ -0,0 +1,25 @@
|
||||
import React, { createContext, useContext } from 'react';
|
||||
|
||||
export function createSafeContext<ContextValue>(errorMessage: string) {
|
||||
const Context = createContext<ContextValue | null>(null);
|
||||
|
||||
const useSafeContext = () => {
|
||||
const ctx = useContext(Context);
|
||||
|
||||
if (ctx === null) {
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
return ctx;
|
||||
};
|
||||
|
||||
const Provider = ({
|
||||
children,
|
||||
value,
|
||||
}: {
|
||||
value: ContextValue;
|
||||
children: React.ReactNode;
|
||||
}) => <Context.Provider value={value}>{children}</Context.Provider>;
|
||||
|
||||
return [Provider, useSafeContext] as const;
|
||||
}
|
||||
1
packages/webapp/src/components/Dropzone/index.ts
Normal file
1
packages/webapp/src/components/Dropzone/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './Dropzone';
|
||||
39
packages/webapp/src/components/Dropzone/mine-types.ts
Normal file
39
packages/webapp/src/components/Dropzone/mine-types.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
export const MIME_TYPES = {
|
||||
// Images
|
||||
png: 'image/png',
|
||||
gif: 'image/gif',
|
||||
jpeg: 'image/jpeg',
|
||||
svg: 'image/svg+xml',
|
||||
webp: 'image/webp',
|
||||
avif: 'image/avif',
|
||||
heic: 'image/heic',
|
||||
|
||||
// Documents
|
||||
mp4: 'video/mp4',
|
||||
zip: 'application/zip',
|
||||
csv: 'text/csv',
|
||||
pdf: 'application/pdf',
|
||||
doc: 'application/msword',
|
||||
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
xls: 'application/vnd.ms-excel',
|
||||
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
ppt: 'application/vnd.ms-powerpoint',
|
||||
pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
exe: 'application/vnd.microsoft.portable-executable',
|
||||
} as const;
|
||||
|
||||
export const IMAGE_MIME_TYPE = [
|
||||
MIME_TYPES.png,
|
||||
MIME_TYPES.gif,
|
||||
MIME_TYPES.jpeg,
|
||||
MIME_TYPES.svg,
|
||||
MIME_TYPES.webp,
|
||||
MIME_TYPES.avif,
|
||||
MIME_TYPES.heic,
|
||||
];
|
||||
|
||||
export const PDF_MIME_TYPE = [MIME_TYPES.pdf];
|
||||
export const MS_WORD_MIME_TYPE = [MIME_TYPES.doc, MIME_TYPES.docx];
|
||||
export const MS_EXCEL_MIME_TYPE = [MIME_TYPES.xls, MIME_TYPES.xlsx];
|
||||
export const MS_POWERPOINT_MIME_TYPE = [MIME_TYPES.ppt, MIME_TYPES.pptx];
|
||||
export const EXE_MIME_TYPE = [MIME_TYPES.exe];
|
||||
111
packages/webapp/src/components/Stepper/Stepper.tsx
Normal file
111
packages/webapp/src/components/Stepper/Stepper.tsx
Normal file
@@ -0,0 +1,111 @@
|
||||
// @ts-nocheck
|
||||
import { cloneElement } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { toArray } from 'lodash';
|
||||
import { Box } from '../Layout';
|
||||
import { StepperCompleted } from './StepperCompleted';
|
||||
import { StepperStep } from './StepperStep';
|
||||
import { StepperStepState } from './types';
|
||||
|
||||
export interface StepperProps {
|
||||
/** <Stepper.Step /> components */
|
||||
children: React.ReactNode;
|
||||
|
||||
/** Index of the active step */
|
||||
active: number;
|
||||
|
||||
/** Called when step is clicked */
|
||||
onStepClick?: (stepIndex: number) => void;
|
||||
|
||||
/** Determines whether next steps can be selected, `true` by default **/
|
||||
allowNextStepsSelect?: boolean;
|
||||
|
||||
classNames?: Record<string, string>;
|
||||
}
|
||||
|
||||
export function Stepper({
|
||||
active,
|
||||
onStepClick,
|
||||
children,
|
||||
classNames,
|
||||
}: StepperProps) {
|
||||
const convertedChildren = toArray(children) as React.ReactElement[];
|
||||
const _children = convertedChildren.filter(
|
||||
(child) => child.type !== StepperCompleted,
|
||||
);
|
||||
const completedStep = convertedChildren.find(
|
||||
(item) => item.type === StepperCompleted,
|
||||
);
|
||||
const items = _children.reduce<React.ReactElement[]>((acc, item, index) => {
|
||||
const state =
|
||||
active === index
|
||||
? StepperStepState.Progress
|
||||
: active > index
|
||||
? StepperStepState.Completed
|
||||
: StepperStepState.Inactive;
|
||||
|
||||
const shouldAllowSelect = () => {
|
||||
if (typeof onStepClick !== 'function') {
|
||||
return false;
|
||||
}
|
||||
if (typeof item.props.allowStepSelect === 'boolean') {
|
||||
return item.props.allowStepSelect;
|
||||
}
|
||||
return state === 'stepCompleted' || allowNextStepsSelect;
|
||||
};
|
||||
const isStepSelectionEnabled = shouldAllowSelect();
|
||||
|
||||
acc.push(
|
||||
cloneElement(item, {
|
||||
key: index,
|
||||
step: index + 1,
|
||||
state,
|
||||
onClick: () => isStepSelectionEnabled && onStepClick?.(index),
|
||||
allowStepClick: isStepSelectionEnabled,
|
||||
}),
|
||||
);
|
||||
if (index !== _children.length - 1) {
|
||||
acc.push(
|
||||
<StepSeparator
|
||||
data-active={index < active || undefined}
|
||||
key={`separator-${index}`}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
const stepContent = _children[active]?.props?.children;
|
||||
const completedContent = completedStep?.props?.children;
|
||||
const content =
|
||||
active > _children.length - 1 ? completedContent : stepContent;
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<StepsItems>{items}</StepsItems>
|
||||
<StepsContent className={classNames?.content}>{content} </StepsContent>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
Stepper.Step = StepperStep;
|
||||
Stepper.Completed = StepperCompleted;
|
||||
Stepper.displayName = '@bigcapital/core/stepper';
|
||||
|
||||
const StepsItems = styled(Box)`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
`;
|
||||
const StepsContent = styled(Box)`
|
||||
margin-top: 16px;
|
||||
margin-bottom: 8px;
|
||||
`;
|
||||
const StepSeparator = styled.div`
|
||||
flex: 1;
|
||||
display: block;
|
||||
border-color: #c5cbd3;
|
||||
border-top-style: solid;
|
||||
border-top-width: 1px;
|
||||
`;
|
||||
@@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
|
||||
export interface StepperCompletedProps {
|
||||
/** Label content */
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export const StepperCompleted: React.FC<StepperCompletedProps> = () => null;
|
||||
StepperCompleted.displayName = '@bigcapital/core/StepperCompleted';
|
||||
102
packages/webapp/src/components/Stepper/StepperStep.tsx
Normal file
102
packages/webapp/src/components/Stepper/StepperStep.tsx
Normal file
@@ -0,0 +1,102 @@
|
||||
// @ts-nocheck
|
||||
import { StepperStepState } from './types';
|
||||
import styled from 'styled-components';
|
||||
import { Icon } from '../Icon';
|
||||
|
||||
interface StepperStepProps {
|
||||
label: string;
|
||||
description?: string;
|
||||
children: React.ReactNode;
|
||||
step?: number;
|
||||
active?: boolean;
|
||||
state?: StepperStepState;
|
||||
allowStepClick?: boolean;
|
||||
}
|
||||
|
||||
export function StepperStep({
|
||||
label,
|
||||
description,
|
||||
step,
|
||||
active,
|
||||
state,
|
||||
children,
|
||||
}: StepperStepProps) {
|
||||
return (
|
||||
<StepButton>
|
||||
<StepIconWrap>
|
||||
<StepIcon
|
||||
isCompleted={state === StepperStepState.Completed}
|
||||
isActive={state === StepperStepState.Progress}
|
||||
>
|
||||
{state === StepperStepState.Completed && (
|
||||
<Icon icon={'done'} iconSize={24} />
|
||||
)}
|
||||
<StepIconText>{step}</StepIconText>
|
||||
</StepIcon>
|
||||
</StepIconWrap>
|
||||
|
||||
<StepTextWrap>
|
||||
<StepTitle
|
||||
isCompleted={state === StepperStepState.Completed}
|
||||
isActive={state === StepperStepState.Progress}
|
||||
>
|
||||
{label}
|
||||
</StepTitle>
|
||||
{description && (
|
||||
<StepDescription
|
||||
isCompleted={state === StepperStepState.Completed}
|
||||
isActive={state === StepperStepState.Progress}
|
||||
>
|
||||
{description}
|
||||
</StepDescription>
|
||||
)}
|
||||
</StepTextWrap>
|
||||
</StepButton>
|
||||
);
|
||||
}
|
||||
|
||||
const StepButton = styled.button`
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
border: 0;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
text-align: left;
|
||||
`;
|
||||
|
||||
const StepIcon = styled.span`
|
||||
display: block;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
display: block;
|
||||
line-height: 24px;
|
||||
border-radius: 24px;
|
||||
text-align: center;
|
||||
background-color: ${(props) =>
|
||||
props.isCompleted || props.isActive ? 'rgb(0, 82, 204)' : '#9e9e9e'};
|
||||
color: #fff;
|
||||
margin: auto;
|
||||
font-size: 12px;
|
||||
`;
|
||||
|
||||
const StepTitle = styled.div`
|
||||
color: ${(props) =>
|
||||
props.isCompleted || props.isActive ? 'rgb(0, 82, 204)' : '#738091'};
|
||||
`;
|
||||
const StepDescription = styled.div`
|
||||
font-size: 12px;
|
||||
margin-top: 10px;
|
||||
color: ${(props) =>
|
||||
props.isCompleted || props.isActive ? 'rgb(0, 82, 204)' : '#738091'};
|
||||
`;
|
||||
|
||||
const StepIconWrap = styled.div`
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
const StepTextWrap = styled.div`
|
||||
text-align: left;
|
||||
`;
|
||||
|
||||
const StepIconText = styled.div``;
|
||||
1
packages/webapp/src/components/Stepper/index.ts
Normal file
1
packages/webapp/src/components/Stepper/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './Stepper';
|
||||
7
packages/webapp/src/components/Stepper/types.ts
Normal file
7
packages/webapp/src/components/Stepper/types.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
|
||||
export enum StepperStepState {
|
||||
Progress = 'stepProgress',
|
||||
Completed = 'stepCompleted',
|
||||
Inactive = 'stepInactive',
|
||||
}
|
||||
Reference in New Issue
Block a user