forked from cerc-io/snowballtools-base
Merge pull request #153 from snowball-tools/andrehadianto/T-4935-timeline-component
[T-4935] timeline component
This commit is contained in:
commit
75a048c7a9
@ -101,7 +101,12 @@ const DeployStep = ({
|
|||||||
)}
|
)}
|
||||||
{status === DeployStatus.COMPLETE && (
|
{status === DeployStatus.COMPLETE && (
|
||||||
<div className="flex items-center gap-1.5">
|
<div className="flex items-center gap-1.5">
|
||||||
<CheckRoundFilledIcon className="text-elements-success" size={18} />
|
<div className="w-4.5 h-4.5 grid place-content-center">
|
||||||
|
<CheckRoundFilledIcon
|
||||||
|
className="text-elements-success"
|
||||||
|
size={15}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<FormatMillisecond time={Number(processTime)} />{' '}
|
<FormatMillisecond time={Number(processTime)} />{' '}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -3,17 +3,11 @@ import { CustomIcon, CustomIconProps } from './CustomIcon';
|
|||||||
|
|
||||||
export const CheckRoundFilledIcon = (props: CustomIconProps) => {
|
export const CheckRoundFilledIcon = (props: CustomIconProps) => {
|
||||||
return (
|
return (
|
||||||
<CustomIcon
|
<CustomIcon width="20" height="20" viewBox="0 0 20 20" {...props}>
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
clipRule="evenodd"
|
||||||
d="M12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2ZM15.774 10.1333C16.1237 9.70582 16.0607 9.0758 15.6332 8.72607C15.2058 8.37635 14.5758 8.43935 14.226 8.86679L10.4258 13.5116L9.20711 12.2929C8.81658 11.9024 8.18342 11.9024 7.79289 12.2929C7.40237 12.6834 7.40237 13.3166 7.79289 13.7071L9.79289 15.7071C9.99267 15.9069 10.2676 16.0129 10.5498 15.9988C10.832 15.9847 11.095 15.8519 11.274 15.6333L15.774 10.1333Z"
|
d="M10 0C4.47715 0 0 4.47715 0 10C0 15.5228 4.47715 20 10 20C15.5228 20 20 15.5228 20 10C20 4.47715 15.5228 0 10 0ZM13.774 8.13327C14.1237 7.70582 14.0607 7.0758 13.6332 6.72607C13.2058 6.37635 12.5758 6.43935 12.226 6.86679L8.42576 11.5116L7.20711 10.2929C6.81658 9.9024 6.18342 9.9024 5.79289 10.2929C5.40237 10.6834 5.40237 11.3166 5.79289 11.7071L7.79289 13.7071C7.99267 13.9069 8.26764 14.0129 8.54981 13.9988C8.83199 13.9847 9.09505 13.8519 9.27396 13.6333L13.774 8.13327Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</CustomIcon>
|
</CustomIcon>
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
import { VariantProps, tv } from 'tailwind-variants';
|
||||||
|
|
||||||
|
export const stepTheme = tv({
|
||||||
|
slots: {
|
||||||
|
wrapper: ['relative', 'px-1.5', 'py-1.5', 'flex', 'gap-2', 'items-center'],
|
||||||
|
step: [
|
||||||
|
'bg-base-bg-emphasized',
|
||||||
|
'rounded-full',
|
||||||
|
'w-7',
|
||||||
|
'h-7',
|
||||||
|
'flex',
|
||||||
|
'items-center',
|
||||||
|
'justify-center',
|
||||||
|
'text-elements-mid-em',
|
||||||
|
'shadow-button',
|
||||||
|
'shrink-0',
|
||||||
|
],
|
||||||
|
label: [
|
||||||
|
'text-sm',
|
||||||
|
'font-sans',
|
||||||
|
'text-elements-mid-em',
|
||||||
|
'whitespace-nowrap',
|
||||||
|
],
|
||||||
|
connector: [],
|
||||||
|
},
|
||||||
|
variants: {
|
||||||
|
orientation: {
|
||||||
|
vertical: {
|
||||||
|
connector: ['bg-border-interactive-hovered', 'w-px', 'h-3', 'ml-5'],
|
||||||
|
},
|
||||||
|
horizontal: {
|
||||||
|
connector: ['text-border-interactive-hovered', 'h-3', 'w-3'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
active: {
|
||||||
|
true: {
|
||||||
|
step: ['bg-controls-secondary-hovered', 'text-elements-on-secondary'],
|
||||||
|
label: ['text-elements-high-em'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
completed: {
|
||||||
|
true: {
|
||||||
|
step: ['text-controls-primary'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
orientation: 'vertical',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export type StepTheme = VariantProps<typeof stepTheme>;
|
67
packages/frontend/src/components/shared/Steps/Step/Step.tsx
Normal file
67
packages/frontend/src/components/shared/Steps/Step/Step.tsx
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import React, { useCallback, ComponentPropsWithoutRef } from 'react';
|
||||||
|
import { stepTheme, StepTheme } from './Step.theme';
|
||||||
|
import {
|
||||||
|
CheckRoundFilledIcon,
|
||||||
|
ChevronRight,
|
||||||
|
} from 'components/shared/CustomIcon';
|
||||||
|
|
||||||
|
export interface StepProps extends ComponentPropsWithoutRef<'li'>, StepTheme {
|
||||||
|
/**
|
||||||
|
* The label for the step
|
||||||
|
*/
|
||||||
|
label: string;
|
||||||
|
/**
|
||||||
|
* The index of the step
|
||||||
|
*/
|
||||||
|
index: number;
|
||||||
|
/**
|
||||||
|
* The total number of steps
|
||||||
|
*/
|
||||||
|
currentIndex: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Step = ({
|
||||||
|
label,
|
||||||
|
index,
|
||||||
|
currentIndex,
|
||||||
|
orientation,
|
||||||
|
...props
|
||||||
|
}: StepProps) => {
|
||||||
|
const theme = stepTheme();
|
||||||
|
|
||||||
|
const active = currentIndex === index;
|
||||||
|
const completed = currentIndex > index;
|
||||||
|
|
||||||
|
const renderConnector = useCallback(
|
||||||
|
(index: number) => {
|
||||||
|
if (index === 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div aria-hidden className={theme.connector({ orientation })}>
|
||||||
|
{orientation === 'horizontal' && <ChevronRight size={12} />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[orientation, theme],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{renderConnector(index)}
|
||||||
|
<li className={theme.wrapper()} {...props}>
|
||||||
|
{
|
||||||
|
<div className={theme.step({ active, completed })}>
|
||||||
|
{completed ? (
|
||||||
|
<CheckRoundFilledIcon className="w-full h-full" />
|
||||||
|
) : (
|
||||||
|
index
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<p className={theme.label()}>{label}</p>
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,2 @@
|
|||||||
|
export * from './Step';
|
||||||
|
export * from './Step.theme';
|
18
packages/frontend/src/components/shared/Steps/Steps.theme.ts
Normal file
18
packages/frontend/src/components/shared/Steps/Steps.theme.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { VariantProps, tv } from 'tailwind-variants';
|
||||||
|
|
||||||
|
export const stepsTheme = tv({
|
||||||
|
slots: {
|
||||||
|
root: [],
|
||||||
|
},
|
||||||
|
variants: {
|
||||||
|
orientation: {
|
||||||
|
vertical: { root: ['flex', 'flex-col'] },
|
||||||
|
horizontal: { root: ['flex', 'items-center', 'gap-1'] },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
orientation: 'vertical',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export type StepsTheme = VariantProps<typeof stepsTheme>;
|
42
packages/frontend/src/components/shared/Steps/Steps.tsx
Normal file
42
packages/frontend/src/components/shared/Steps/Steps.tsx
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import React, { Fragment, ComponentPropsWithoutRef } from 'react';
|
||||||
|
import { stepsTheme, StepsTheme } from './Steps.theme';
|
||||||
|
import { Step, StepProps, StepTheme } from './Step';
|
||||||
|
|
||||||
|
interface StepsProps
|
||||||
|
extends ComponentPropsWithoutRef<'ul'>,
|
||||||
|
StepsTheme,
|
||||||
|
Pick<StepTheme, 'orientation'> {
|
||||||
|
/**
|
||||||
|
* The index of the current step
|
||||||
|
*/
|
||||||
|
currentIndex: number;
|
||||||
|
/**
|
||||||
|
* The steps to render
|
||||||
|
*/
|
||||||
|
steps: Pick<StepProps, 'label'>[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Steps = ({
|
||||||
|
currentIndex,
|
||||||
|
steps = [],
|
||||||
|
className,
|
||||||
|
orientation,
|
||||||
|
...props
|
||||||
|
}: StepsProps) => {
|
||||||
|
const theme = stepsTheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ul className={theme.root({ class: className, orientation })} {...props}>
|
||||||
|
{steps.map((step, i) => (
|
||||||
|
<Fragment key={i}>
|
||||||
|
<Step
|
||||||
|
{...step}
|
||||||
|
orientation={orientation}
|
||||||
|
currentIndex={currentIndex}
|
||||||
|
index={i + 1}
|
||||||
|
/>
|
||||||
|
</Fragment>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
};
|
2
packages/frontend/src/components/shared/Steps/index.ts
Normal file
2
packages/frontend/src/components/shared/Steps/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './Steps';
|
||||||
|
export * from './Steps.theme';
|
@ -26,6 +26,7 @@ import {
|
|||||||
import { renderInputs } from './renders/input';
|
import { renderInputs } from './renders/input';
|
||||||
import { RADIO_OPTIONS } from './renders/radio';
|
import { RADIO_OPTIONS } from './renders/radio';
|
||||||
import { SEGMENTED_CONTROLS_OPTIONS } from './renders/segmentedControls';
|
import { SEGMENTED_CONTROLS_OPTIONS } from './renders/segmentedControls';
|
||||||
|
import { renderHorizontalSteps, renderVerticalSteps } from './renders/steps';
|
||||||
import {
|
import {
|
||||||
renderTabWithBadges,
|
renderTabWithBadges,
|
||||||
renderTabs,
|
renderTabs,
|
||||||
@ -56,6 +57,19 @@ const Page: React.FC = () => {
|
|||||||
|
|
||||||
<div className="w-full h border border-gray-200 px-20 my-10" />
|
<div className="w-full h border border-gray-200 px-20 my-10" />
|
||||||
|
|
||||||
|
{/* Steps */}
|
||||||
|
<div className="flex flex-col gap-10 items-center justify-between">
|
||||||
|
<div className="flex flex-col gap-10 items-center justify-between">
|
||||||
|
<h1 className="text-2xl font-bold">Steps</h1>
|
||||||
|
<div className="flex flex-col gap-10 items-center justify-center">
|
||||||
|
{renderVerticalSteps()}
|
||||||
|
{renderHorizontalSteps()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full h border border-gray-200 px-20 my-10" />
|
||||||
|
|
||||||
{/* Tag */}
|
{/* Tag */}
|
||||||
<div className="flex flex-col gap-10 items-center justify-between">
|
<div className="flex flex-col gap-10 items-center justify-between">
|
||||||
<div className="flex flex-col gap-10 items-center justify-between">
|
<div className="flex flex-col gap-10 items-center justify-between">
|
||||||
|
41
packages/frontend/src/pages/components/renders/steps.tsx
Normal file
41
packages/frontend/src/pages/components/renders/steps.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Steps } from 'components/shared/Steps';
|
||||||
|
|
||||||
|
export const renderVerticalSteps = () => {
|
||||||
|
return (
|
||||||
|
<Steps
|
||||||
|
currentIndex={1}
|
||||||
|
steps={[
|
||||||
|
{
|
||||||
|
label: 'Create repository',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Deploy',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: `What's next?`,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const renderHorizontalSteps = () => {
|
||||||
|
return (
|
||||||
|
<Steps
|
||||||
|
orientation="horizontal"
|
||||||
|
currentIndex={1}
|
||||||
|
steps={[
|
||||||
|
{
|
||||||
|
label: 'Create repository',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Deploy',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: `What's next?`,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
@ -8,8 +8,8 @@ import {
|
|||||||
|
|
||||||
import { Avatar } from '@material-tailwind/react';
|
import { Avatar } from '@material-tailwind/react';
|
||||||
|
|
||||||
import Stepper from '../../../../components/Stepper';
|
|
||||||
import templates from '../../../../assets/templates';
|
import templates from '../../../../assets/templates';
|
||||||
|
import { Steps } from 'components/shared/Steps';
|
||||||
|
|
||||||
// TODO: Set dynamic route for template and load details from DB
|
// TODO: Set dynamic route for template and load details from DB
|
||||||
const CreateWithTemplate = () => {
|
const CreateWithTemplate = () => {
|
||||||
@ -62,7 +62,7 @@ const CreateWithTemplate = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-3 w-5/6 p-6">
|
<div className="grid grid-cols-3 w-5/6 p-6">
|
||||||
<div>
|
<div>
|
||||||
<Stepper activeStep={activeStep} stepperValues={stepperValues} />
|
<Steps currentIndex={activeStep} steps={stepperValues} />
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-2">
|
<div className="col-span-2">
|
||||||
<Outlet context={{ template }} />
|
<Outlet context={{ template }} />
|
||||||
|
Loading…
Reference in New Issue
Block a user