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:
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